From c90c9300b190fd90967d2dc190de18d671faab1a Mon Sep 17 00:00:00 2001 From: vdiskg <62455066+vdiskg@users.noreply.github.com> Date: Sat, 3 Feb 2024 11:07:18 +0800 Subject: [PATCH] apollo assembly optimization (#5035) * table prefix * assembly config v0.1 * assembly config v0.2 * assembly db init * assembly db init fix * assembly sql * WebSecurity * H2Function * assembly profile * assembly session * ddl-auto=none * DataSource * doc * admin assembly profile * copy assembly sql on build * merge sql conflict * sql comment * sql converter * sql converter delta * sql converter clean * fix assembly sql * fix assembly sql temp * fix assembly sql temp v0.2 * fix assembly sql temp v0.3 * fix assembly sql temp v0.4 * fix assembly sql temp v0.5 * assembly doc * autoGeneratedDeclaration * rename sql-converter module * sql-converter h2 * fix database-discovery * mv sql * mv sql v0.2 * mv sql v0.3 * multi datasource * remove table prefix * update doc * fix init * log apollo datasource initialize * fix apollo datasource initialize * mv h2 sql * fix apollo datasource initialize v0.2 * clean import * fix sql check * fix sql check v0.2 * merge sql * merge sql generated * add sql test temp * add sql test temp 2 * add sql test * sql convert * fix sql-convert * CHANGES.md * fix copyright * clean logger * fix order * fix h2 converter * fix h2 converter v0.2 * fix order v0.2 --------- Co-authored-by: Jason Song --- CHANGES.md | 1 + .../adminservice/AdminServiceApplication.java | 5 +- .../AdminServiceAssemblyConfiguration.java | 39 ++ apollo-assembly/pom.xml | 28 + .../apollo/assembly/ApolloApplication.java | 55 +- .../application-database-discovery.properties | 26 + .../resources/application-github.properties | 47 ++ .../src/main/resources/logback.xml | 4 + .../src/test/resources/application.properties | 25 +- .../biz/ApolloBizAssemblyConfiguration.java | 36 ++ .../biz/service/BizDBPropertySource.java | 2 +- apollo-build-sql-converter/pom.xml | 80 +++ .../sql/converter/ApolloH2ConverterUtil.java | 301 ++++++++++ .../ApolloMysqlDefaultConverterUtil.java | 69 +++ .../sql/converter/ApolloSqlConverter.java | 134 +++++ .../sql/converter/ApolloSqlConverterUtil.java | 412 ++++++++++++++ .../build/sql/converter/SqlStatement.java | 99 ++++ .../build/sql/converter/SqlTemplate.java | 39 ++ .../sql/converter/SqlTemplateContext.java | 70 +++ .../build/sql/converter/SqlTemplateGist.java | 112 ++++ .../ApolloSqlConverterAutoGeneratedTest.java | 219 ++++++++ .../converter/ApolloSqlConverterH2Test.java | 164 ++++++ .../build/sql/converter/TestH2Function.java | 24 + .../apolloconfigdb-v000-v010-after.sql | 30 + .../apolloconfigdb-v000-v010-base.sql | 414 ++++++++++++++ .../apolloconfigdb-v000-v010-before.sql | 30 + .../v000-v010/apolloconfigdb-v000-v010.sql | 22 + .../apolloportaldb-v000-v010-base.sql | 308 +++++++++++ .../v000-v010/apolloportaldb-v000-v010.sql | 22 + .../v040-v050/apolloconfigdb-v040-v050.sql | 30 + .../v040-v050/apolloportaldb-v040-v050.sql | 26 + .../v060-v062/apolloconfigdb-v060-v062.sql | 25 + .../v060-v062/apolloportaldb-v060-v062.sql | 25 + .../v080-v090/apolloportaldb-v080-v090.sql | 44 ++ .../v151-v160/apolloconfigdb-v151-v160.sql | 37 ++ .../v170-v180/apolloconfigdb-v170-v180.sql | 29 + .../v170-v180/apolloportaldb-v170-v180.sql | 24 + .../v180-v190/apolloconfigdb-v180-v190.sql | 74 +++ .../v180-v190/apolloportaldb-v180-v190.sql | 102 ++++ .../apolloconfigdb-v190-v200-after.sql | 88 +++ .../v190-v200/apolloconfigdb-v190-v200.sql | 62 +++ .../apolloportaldb-v190-v200-after.sql | 83 +++ .../v190-v200/apolloportaldb-v190-v200.sql | 55 ++ .../v200-v210/apolloconfigdb-v200-v210.sql | 41 ++ .../v210-v220/apolloconfigdb-v210-v220.sql | 97 ++++ .../v210-v220/apolloportaldb-v210-v220.sql | 83 +++ ...loDataSourceScriptDatabaseInitializer.java | 73 +++ ...ourceScriptDatabaseInitializerFactory.java | 174 ++++++ .../ApolloSqlInitializationProperties.java | 142 +++++ .../ConfigServiceApplication.java | 5 +- .../ConfigServiceAssemblyConfiguration.java | 44 ++ .../apollo/portal/PortalApplication.java | 2 + .../portal/PortalAssemblyConfiguration.java | 53 ++ .../component/RetryableRestTemplate.java | 5 +- .../service/PortalDBPropertySource.java | 2 +- .../src/main/resources/application.yml | 7 - .../src/main/resources/portal.properties | 20 + .../ApolloApplication-Mysql-VM-Options.png | Bin 0 -> 182066 bytes .../ApolloApplication-Overview.png | Bin 0 -> 90120 bytes .../ApolloApplication-Run.png | Bin 0 -> 15230 bytes .../ApolloApplication-VM-Options.png | Bin 0 -> 89402 bytes .../contribution/apollo-development-guide.md | 125 ++--- .../distributed-deployment-guide.md | 8 +- docs/en/deployment/quick-start.md | 181 +++--- ...al-how-to-implement-user-login-function.md | 2 +- .../contribution/apollo-development-guide.md | 120 ++-- .../distributed-deployment-guide.md | 8 +- docs/zh/deployment/quick-start.md | 177 +++--- ...al-how-to-implement-user-login-function.md | 2 +- pom.xml | 1 + .../profiles/h2-default/apolloconfigdb.sql | 501 +++++++++++++++++ .../profiles/h2-default/apolloportaldb.sql | 445 +++++++++++++++ .../v220-v230/apolloconfigdb-v220-v230.sql | 43 ++ .../v220-v230/apolloportaldb-v220-v230.sql | 42 ++ .../apolloconfigdb.sql | 515 +++++++++++++++++ .../apolloportaldb.sql | 458 +++++++++++++++ .../v220-v230/apolloconfigdb-v220-v230.sql | 39 ++ .../v220-v230/apolloportaldb-v220-v230.sql | 37 ++ .../profiles/mysql-default/apolloconfigdb.sql | 520 ++++++++++++++++++ .../profiles/mysql-default/apolloportaldb.sql | 463 ++++++++++++++++ .../v040-v050/apolloconfigdb-v040-v050.sql | 0 .../v040-v050/apolloportaldb-v040-v050.sql | 0 .../v060-v062/apolloconfigdb-v060-v062.sql | 0 .../v060-v062/apolloportaldb-v060-v062.sql | 0 .../v080-v090/apolloportaldb-v080-v090.sql | 0 .../v151-v160/apolloconfigdb-v151-v160.sql | 0 .../v170-v180/apolloconfigdb-v170-v180.sql | 0 .../v170-v180/apolloportaldb-v170-v180.sql | 0 .../v180-v190/apolloconfigdb-v180-v190.sql | 0 .../v180-v190/apolloportaldb-v180-v190.sql | 0 .../apolloconfigdb-v190-v200-after.sql | 0 .../v190-v200/apolloconfigdb-v190-v200.sql | 0 .../apolloportaldb-v190-v200-after.sql | 0 .../v190-v200/apolloportaldb-v190-v200.sql | 0 .../v200-v210/apolloconfigdb-v200-v210.sql | 0 .../v210-v220/apolloconfigdb-v210-v220.sql | 4 +- .../v210-v220/apolloportaldb-v210-v220.sql | 4 +- .../v220-v230/apolloconfigdb-v220-v230.sql | 41 ++ .../v220-v230/apolloportaldb-v220-v230.sql | 39 ++ scripts/sql/{ => src}/apolloconfigdb.sql | 90 +-- scripts/sql/{ => src}/apolloportaldb.sql | 90 +-- .../v220-v230/apolloconfigdb-v220-v230.sql | 10 +- .../v220-v230/apolloportaldb-v220-v230.sql | 23 + .../sql/src/gist/autoGeneratedDeclaration.sql | 24 + scripts/sql/src/gist/h2Function.sql | 22 + scripts/sql/src/gist/setupDatabase.sql | 22 + scripts/sql/src/gist/useDatabase.sql | 19 + 107 files changed, 7902 insertions(+), 442 deletions(-) create mode 100644 apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/AdminServiceAssemblyConfiguration.java create mode 100644 apollo-assembly/src/main/resources/application-database-discovery.properties create mode 100644 apollo-assembly/src/main/resources/application-github.properties create mode 100644 apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/ApolloBizAssemblyConfiguration.java create mode 100644 apollo-build-sql-converter/pom.xml create mode 100644 apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloH2ConverterUtil.java create mode 100644 apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloMysqlDefaultConverterUtil.java create mode 100644 apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverter.java create mode 100644 apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverterUtil.java create mode 100644 apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlStatement.java create mode 100644 apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlTemplate.java create mode 100644 apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlTemplateContext.java create mode 100644 apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlTemplateGist.java create mode 100644 apollo-build-sql-converter/src/test/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverterAutoGeneratedTest.java create mode 100644 apollo-build-sql-converter/src/test/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverterH2Test.java create mode 100644 apollo-build-sql-converter/src/test/java/com/ctrip/framework/apollo/build/sql/converter/TestH2Function.java create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010-after.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010-base.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010-before.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloportaldb-v000-v010-base.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloportaldb-v000-v010.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v040-v050/apolloconfigdb-v040-v050.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v040-v050/apolloportaldb-v040-v050.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v060-v062/apolloconfigdb-v060-v062.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v060-v062/apolloportaldb-v060-v062.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v080-v090/apolloportaldb-v080-v090.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v151-v160/apolloconfigdb-v151-v160.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v170-v180/apolloconfigdb-v170-v180.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v170-v180/apolloportaldb-v170-v180.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v180-v190/apolloconfigdb-v180-v190.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v180-v190/apolloportaldb-v180-v190.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloconfigdb-v190-v200-after.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloconfigdb-v190-v200.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloportaldb-v190-v200-after.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloportaldb-v190-v200.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v200-v210/apolloconfigdb-v200-v210.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v210-v220/apolloconfigdb-v210-v220.sql create mode 100644 apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v210-v220/apolloportaldb-v210-v220.sql create mode 100644 apollo-common/src/main/java/com/ctrip/framework/apollo/common/datasource/ApolloDataSourceScriptDatabaseInitializer.java create mode 100644 apollo-common/src/main/java/com/ctrip/framework/apollo/common/datasource/ApolloDataSourceScriptDatabaseInitializerFactory.java create mode 100644 apollo-common/src/main/java/com/ctrip/framework/apollo/common/datasource/ApolloSqlInitializationProperties.java create mode 100644 apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/ConfigServiceAssemblyConfiguration.java create mode 100644 apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/PortalAssemblyConfiguration.java create mode 100644 apollo-portal/src/main/resources/portal.properties create mode 100644 doc/images/local-development/ApolloApplication-Mysql-VM-Options.png create mode 100644 doc/images/local-development/ApolloApplication-Overview.png create mode 100644 doc/images/local-development/ApolloApplication-Run.png create mode 100644 doc/images/local-development/ApolloApplication-VM-Options.png create mode 100644 scripts/sql/profiles/h2-default/apolloconfigdb.sql create mode 100644 scripts/sql/profiles/h2-default/apolloportaldb.sql create mode 100644 scripts/sql/profiles/h2-default/delta/v220-v230/apolloconfigdb-v220-v230.sql create mode 100644 scripts/sql/profiles/h2-default/delta/v220-v230/apolloportaldb-v220-v230.sql create mode 100644 scripts/sql/profiles/mysql-database-not-specified/apolloconfigdb.sql create mode 100644 scripts/sql/profiles/mysql-database-not-specified/apolloportaldb.sql create mode 100644 scripts/sql/profiles/mysql-database-not-specified/delta/v220-v230/apolloconfigdb-v220-v230.sql create mode 100644 scripts/sql/profiles/mysql-database-not-specified/delta/v220-v230/apolloportaldb-v220-v230.sql create mode 100644 scripts/sql/profiles/mysql-default/apolloconfigdb.sql create mode 100644 scripts/sql/profiles/mysql-default/apolloportaldb.sql rename scripts/sql/{ => profiles/mysql-default}/delta/v040-v050/apolloconfigdb-v040-v050.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v040-v050/apolloportaldb-v040-v050.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v060-v062/apolloconfigdb-v060-v062.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v060-v062/apolloportaldb-v060-v062.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v080-v090/apolloportaldb-v080-v090.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v151-v160/apolloconfigdb-v151-v160.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v170-v180/apolloconfigdb-v170-v180.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v170-v180/apolloportaldb-v170-v180.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v180-v190/apolloconfigdb-v180-v190.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v180-v190/apolloportaldb-v180-v190.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v190-v200/apolloconfigdb-v190-v200-after.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v190-v200/apolloconfigdb-v190-v200.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v190-v200/apolloportaldb-v190-v200-after.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v190-v200/apolloportaldb-v190-v200.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v200-v210/apolloconfigdb-v200-v210.sql (100%) rename scripts/sql/{ => profiles/mysql-default}/delta/v210-v220/apolloconfigdb-v210-v220.sql (98%) rename scripts/sql/{ => profiles/mysql-default}/delta/v210-v220/apolloportaldb-v210-v220.sql (97%) create mode 100644 scripts/sql/profiles/mysql-default/delta/v220-v230/apolloconfigdb-v220-v230.sql create mode 100644 scripts/sql/profiles/mysql-default/delta/v220-v230/apolloportaldb-v220-v230.sql rename scripts/sql/{ => src}/apolloconfigdb.sql (92%) rename scripts/sql/{ => src}/apolloportaldb.sql (90%) rename scripts/sql/{ => src}/delta/v220-v230/apolloconfigdb-v220-v230.sql (78%) create mode 100644 scripts/sql/src/delta/v220-v230/apolloportaldb-v220-v230.sql create mode 100644 scripts/sql/src/gist/autoGeneratedDeclaration.sql create mode 100644 scripts/sql/src/gist/h2Function.sql create mode 100644 scripts/sql/src/gist/setupDatabase.sql create mode 100644 scripts/sql/src/gist/useDatabase.sql diff --git a/CHANGES.md b/CHANGES.md index cccd30b1b50..3029a1fa0c4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ Apollo 2.3.0 * [Fix circular references on LdapAutoConfiguration](https://github.com/apolloconfig/apollo/pull/5055) * [Add comment for clusters and UI display](https://github.com/apolloconfig/apollo/pull/5072) * [Fix the issue that the length of private namespaces are mis-calculated](https://github.com/apolloconfig/apollo/pull/5078) +* [apollo assembly optimization](https://github.com/apolloconfig/apollo/pull/5035) ------------------ diff --git a/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/AdminServiceApplication.java b/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/AdminServiceApplication.java index 3ed43f44a9f..e62e1901da2 100644 --- a/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/AdminServiceApplication.java +++ b/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/AdminServiceApplication.java @@ -21,6 +21,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @@ -29,7 +30,9 @@ @EnableAspectJAutoProxy @Configuration @PropertySource(value = {"classpath:adminservice.properties"}) -@EnableAutoConfiguration +@EnableAutoConfiguration(exclude = { + UserDetailsServiceAutoConfiguration.class, +}) @EnableTransactionManagement @ComponentScan(basePackageClasses = {ApolloCommonConfig.class, ApolloBizConfig.class, diff --git a/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/AdminServiceAssemblyConfiguration.java b/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/AdminServiceAssemblyConfiguration.java new file mode 100644 index 00000000000..0c517375183 --- /dev/null +++ b/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/AdminServiceAssemblyConfiguration.java @@ -0,0 +1,39 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.adminservice; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Profile("assembly") +@Configuration +public class AdminServiceAssemblyConfiguration { + + @Order(101) + @Configuration + static class AdminServiceSecurityConfigurer extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable(); + http.httpBasic(); + } + } +} diff --git a/apollo-assembly/pom.xml b/apollo-assembly/pom.xml index 635e28733c3..adea3607fea 100644 --- a/apollo-assembly/pom.xml +++ b/apollo-assembly/pom.xml @@ -46,6 +46,34 @@ + + org.apache.maven.plugins + maven-resources-plugin + 3.2.0 + + + copy-resources + validate + + copy-resources + + + ${project.build.directory}/classes/META-INF/sql/profiles + + + ${project.parent.basedir}/scripts/sql/profiles + + h2-default/apolloconfigdb.sql + h2-default/apolloportaldb.sql + mysql-database-not-specified/apolloconfigdb.sql + mysql-database-not-specified/apolloportaldb.sql + + + + + + + org.springframework.boot spring-boot-maven-plugin diff --git a/apollo-assembly/src/main/java/com/ctrip/framework/apollo/assembly/ApolloApplication.java b/apollo-assembly/src/main/java/com/ctrip/framework/apollo/assembly/ApolloApplication.java index 79e3477da8f..bdd6a1c1e51 100644 --- a/apollo-assembly/src/main/java/com/ctrip/framework/apollo/assembly/ApolloApplication.java +++ b/apollo-assembly/src/main/java/com/ctrip/framework/apollo/assembly/ApolloApplication.java @@ -23,16 +23,22 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.MDC; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.context.scope.refresh.RefreshScope; import org.springframework.context.ConfigurableApplicationContext; -@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, - HibernateJpaAutoConfiguration.class, ApolloAuditAutoConfiguration.class}) +@SpringBootApplication(exclude = { + DataSourceAutoConfiguration.class, + DataSourceTransactionManagerAutoConfiguration.class, + HibernateJpaAutoConfiguration.class, + ApolloAuditAutoConfiguration.class, +}) public class ApolloApplication { private static final Logger logger = LoggerFactory.getLogger(ApolloApplication.class); @@ -41,39 +47,46 @@ public static void main(String[] args) throws Exception { /** * Common */ + MDC.put("starting_context", "[starting:common] "); + logger.info("commonContext starting..."); ConfigurableApplicationContext commonContext = new SpringApplicationBuilder(ApolloApplication.class).web(WebApplicationType.NONE).run(args); - logger.info(commonContext.getId() + " isActive: " + commonContext.isActive()); + logger.info("commonContext [{}] isActive: {}", commonContext.getId(), commonContext.isActive()); /** * ConfigService */ - if (commonContext.getEnvironment().containsProperty("configservice")) { - ConfigurableApplicationContext configContext = - new SpringApplicationBuilder(ConfigServiceApplication.class).parent(commonContext) - .sources(RefreshScope.class).run(args); - logger.info(configContext.getId() + " isActive: " + configContext.isActive()); - } + MDC.put("starting_context", "[starting:config] "); + logger.info("configContext starting..."); + ConfigurableApplicationContext configContext = + new SpringApplicationBuilder(ConfigServiceApplication.class).parent(commonContext) + .profiles("assembly") + .sources(RefreshScope.class).run(args); + logger.info("configContext [{}] isActive: {}", configContext.getId(), configContext.isActive()); /** * AdminService */ - if (commonContext.getEnvironment().containsProperty("adminservice")) { - ConfigurableApplicationContext adminContext = - new SpringApplicationBuilder(AdminServiceApplication.class).parent(commonContext) - .sources(RefreshScope.class).run(args); - logger.info(adminContext.getId() + " isActive: " + adminContext.isActive()); - } + MDC.put("starting_context", "[starting:admin] "); + logger.info("adminContext starting..."); + ConfigurableApplicationContext adminContext = + new SpringApplicationBuilder(AdminServiceApplication.class).parent(commonContext) + .profiles("assembly") + .sources(RefreshScope.class).run(args); + logger.info("adminContext [{}] isActive: {}", adminContext.getId(), adminContext.isActive()); /** * Portal */ - if (commonContext.getEnvironment().containsProperty("portal")) { - ConfigurableApplicationContext portalContext = - new SpringApplicationBuilder(PortalApplication.class).parent(commonContext) - .sources(RefreshScope.class).run(args); - logger.info(portalContext.getId() + " isActive: " + portalContext.isActive()); - } + MDC.put("starting_context", "[starting:portal] "); + logger.info("portalContext starting..."); + ConfigurableApplicationContext portalContext = + new SpringApplicationBuilder(PortalApplication.class).parent(commonContext) + .profiles("assembly") + .sources(RefreshScope.class).run(args); + logger.info("portalContext [{}] isActive: {}", portalContext.getId(), portalContext.isActive()); + + MDC.clear(); } } diff --git a/apollo-assembly/src/main/resources/application-database-discovery.properties b/apollo-assembly/src/main/resources/application-database-discovery.properties new file mode 100644 index 00000000000..dcdb6f1ab62 --- /dev/null +++ b/apollo-assembly/src/main/resources/application-database-discovery.properties @@ -0,0 +1,26 @@ +# +# Copyright 2024 Apollo 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 +# +# http://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. +# +apollo.eureka.server.enabled=false +eureka.client.enabled=false +spring.cloud.discovery.enabled=false + +apollo.service.registry.enabled=true +apollo.service.registry.cluster=default +apollo.service.registry.heartbeatIntervalInSecond=10 + +apollo.service.discovery.enabled=true +# health check by heartbeat, heartbeat time before 61s ago will be seemed as unhealthy +apollo.service.discovery.healthCheckIntervalInSecond = 61 diff --git a/apollo-assembly/src/main/resources/application-github.properties b/apollo-assembly/src/main/resources/application-github.properties new file mode 100644 index 00000000000..fbdcfe0c70c --- /dev/null +++ b/apollo-assembly/src/main/resources/application-github.properties @@ -0,0 +1,47 @@ +# +# Copyright 2024 Apollo 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 +# +# http://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. +# +# Config DataSource +spring.config-datasource.url=jdbc:h2:mem:~/apollo-config-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE +#spring.config-datasource.username= +#spring.config-datasource.password= +spring.sql.config-init.schema-locations=@@repository@@/profiles/@@platform@@@@suffix@@/apolloconfigdb.sql +spring.sql.config-init.mode=embedded +# Portal DataSource +spring.portal-datasource.url=jdbc:h2:mem:~/apollo-portal-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE +#spring.portal-datasource.username= +#spring.portal-datasource.password= +spring.sql.portal-init.schema-locations=@@repository@@/profiles/@@platform@@@@suffix@@/apolloportaldb.sql +spring.sql.portal-init.mode=embedded + +# Resolve Multi DataSource JMX name conflict +spring.jmx.unique-names=true + +# H2 datasource +spring.jpa.hibernate.ddl-auto=none +spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl +spring.jpa.properties.hibernate.show_sql=false +spring.jpa.properties.hibernate.metadata_builder_contributor=com.ctrip.framework.apollo.common.jpa.SqlFunctionsMetadataBuilderContributor +spring.h2.console.enabled=true +spring.h2.console.settings.web-allow-others=true + +# Sql logging +#logging.level.org.hibernate.SQL=DEBUG + +# Default env +apollo.portal.envs=local + +# Spring session +spring.session.store-type=none diff --git a/apollo-assembly/src/main/resources/logback.xml b/apollo-assembly/src/main/resources/logback.xml index 4f414e65d95..e724e3c8f46 100644 --- a/apollo-assembly/src/main/resources/logback.xml +++ b/apollo-assembly/src/main/resources/logback.xml @@ -16,6 +16,10 @@ ~ --> + + diff --git a/apollo-assembly/src/test/resources/application.properties b/apollo-assembly/src/test/resources/application.properties index 45dc7de7262..56c1e7ae868 100644 --- a/apollo-assembly/src/test/resources/application.properties +++ b/apollo-assembly/src/test/resources/application.properties @@ -13,10 +13,29 @@ # See the License for the specific language governing permissions and # limitations under the License. # -spring.datasource.url = jdbc:h2:mem:~/apolloconfigdb;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1 +# Config DataSource +spring.config-datasource.url=jdbc:h2:mem:~/apollo-config-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE +#spring.config-datasource.username= +#spring.config-datasource.password= +spring.sql.config-init.schema-locations=@@repository@@/profiles/@@platform@@@@suffix@@/apolloconfigdb.sql +spring.sql.config-init.mode=embedded +# Portal DataSource +spring.portal-datasource.url=jdbc:h2:mem:~/apollo-portal-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE +#spring.portal-datasource.username= +#spring.portal-datasource.password= +spring.sql.portal-init.schema-locations=@@repository@@/profiles/@@platform@@@@suffix@@/apolloportaldb.sql +spring.sql.portal-init.mode=embedded + +# Resolve Multi DataSource JMX name conflict +spring.jmx.unique-names=true + +# H2 datasource +spring.jpa.hibernate.ddl-auto=none spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl spring.jpa.properties.hibernate.show_sql=false spring.jpa.properties.hibernate.metadata_builder_contributor=com.ctrip.framework.apollo.common.jpa.SqlFunctionsMetadataBuilderContributor -spring.h2.console.enabled = true +spring.h2.console.enabled=true spring.h2.console.settings.web-allow-others=true -apollo.portal.env= local + +# Default env +apollo.portal.envs=local diff --git a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/ApolloBizAssemblyConfiguration.java b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/ApolloBizAssemblyConfiguration.java new file mode 100644 index 00000000000..7b3fdf88e1d --- /dev/null +++ b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/ApolloBizAssemblyConfiguration.java @@ -0,0 +1,36 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.biz; + +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Profile; + +@Profile("assembly") +@Configuration +public class ApolloBizAssemblyConfiguration { + + @Primary + @ConfigurationProperties(prefix = "spring.config-datasource") + @Bean + public static DataSourceProperties dataSourceProperties() { + return new DataSourceProperties(); + } +} diff --git a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/BizDBPropertySource.java b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/BizDBPropertySource.java index 714a3417962..e77be0d05d4 100644 --- a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/BizDBPropertySource.java +++ b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/BizDBPropertySource.java @@ -66,7 +66,7 @@ public BizDBPropertySource(final ServerConfigRepository serverConfigRepository, @PostConstruct public void runSqlScript() throws Exception { - if (env.acceptsProfiles(Profiles.of("h2"))) { + if (env.acceptsProfiles(Profiles.of("h2")) && !env.acceptsProfiles(Profiles.of("assembly"))) { Resource resource = new ClassPathResource("jpa/configdb.init.h2.sql"); if (resource.exists()) { DatabasePopulatorUtils.execute(new ResourceDatabasePopulator(resource), dataSource); diff --git a/apollo-build-sql-converter/pom.xml b/apollo-build-sql-converter/pom.xml new file mode 100644 index 00000000000..82d91068ec2 --- /dev/null +++ b/apollo-build-sql-converter/pom.xml @@ -0,0 +1,80 @@ + + + + + com.ctrip.framework.apollo + apollo + ${revision} + ../pom.xml + + 4.0.0 + apollo-build-sql-converter + Apollo Build Sql Converter + + + + org.freemarker + freemarker + + + com.h2database + h2 + test + + + org.springframework.boot + spring-boot-starter-jdbc + test + + + + + + sql-converter + + false + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + sql-converter + compile + + java + + + + + com.ctrip.framework.apollo.build.sql.converter.ApolloSqlConverter + + + + + + + diff --git a/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloH2ConverterUtil.java b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloH2ConverterUtil.java new file mode 100644 index 00000000000..dd4bbb8b47a --- /dev/null +++ b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloH2ConverterUtil.java @@ -0,0 +1,301 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.build.sql.converter; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.StringJoiner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ApolloH2ConverterUtil { + + public static void convert(SqlTemplate sqlTemplate, String targetSql, + SqlTemplateContext context) { + + ApolloSqlConverterUtil.ensureDirectories(targetSql); + + String rawText = ApolloSqlConverterUtil.process(sqlTemplate, context); + + List sqlStatements = ApolloSqlConverterUtil.toStatements(rawText); + try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get(targetSql), + StandardCharsets.UTF_8, StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING)) { + for (SqlStatement sqlStatement : sqlStatements) { + String convertedText; + try { + convertedText = convertAssemblyH2Line(sqlStatement); + } catch (Throwable e) { + throw new RuntimeException("convert error: " + sqlStatement.getRawText(), e); + } + bufferedWriter.write(convertedText); + bufferedWriter.write('\n'); + } + + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + + private static final Pattern OPERATION_TABLE_PATTERN = Pattern.compile( + "(?DROP|CREATE|ALTER)\\s+TABLE\\s+(`)?(?[a-zA-Z0-9\\-_]+)(`)?\\s*", + Pattern.CASE_INSENSITIVE); + + private static final Pattern CREATE_INDEX_ON_PATTERN = Pattern.compile( + "CREATE\\s+INDEX\\s+(`)?(?[a-zA-Z0-9\\-_]+)(`)?\\s+ON\\s+(`)?(?[a-zA-Z0-9\\-_]+)(`)?", + Pattern.CASE_INSENSITIVE); + + private static String convertAssemblyH2Line(SqlStatement sqlStatement) { + String convertedText = sqlStatement.getRawText(); + + // TABLE `` + Matcher opTableMatcher = OPERATION_TABLE_PATTERN.matcher(convertedText); + if (opTableMatcher.find()) { + String operation = opTableMatcher.group("operation"); + if ("DROP".equalsIgnoreCase(operation)) { + return ""; + } else if ("CREATE".equalsIgnoreCase(operation)) { + return convertCreateTable(convertedText, sqlStatement, opTableMatcher); + } else if ("ALTER".equalsIgnoreCase(operation)) { + return convertAlterTable(convertedText, sqlStatement, opTableMatcher); + } + } + + // CREATE INDEX `` ON `` + Matcher createIndexOnMatcher = CREATE_INDEX_ON_PATTERN.matcher(convertedText); + if (createIndexOnMatcher.find()) { + String createIndexOnTableName = createIndexOnMatcher.group("tableName"); + // index with table + return convertIndexOnTable(convertedText, createIndexOnTableName, sqlStatement); + } + + // others + return convertedText; + } + + private static String convertCreateTable(String convertedText, SqlStatement sqlStatement, + Matcher opTableMatcher) { + String tableName = opTableMatcher.group("tableName"); + // table config + convertedText = convertTableConfig(convertedText, sqlStatement); + // index with table + convertedText = convertIndexWithTable(convertedText, tableName, sqlStatement); + // column + convertedText = convertColumn(convertedText, sqlStatement); + return convertedText; + } + + private static final Pattern ENGINE_PATTERN = Pattern.compile( + "ENGINE\\s*=\\s*InnoDB", Pattern.CASE_INSENSITIVE); + + private static final Pattern DEFAULT_CHARSET_PATTERN = Pattern.compile( + "DEFAULT\\s+CHARSET\\s*=\\s*utf8mb4", Pattern.CASE_INSENSITIVE); + + private static final Pattern ROW_FORMAT_PATTERN = Pattern.compile( + "ROW_FORMAT\\s*=\\s*DYNAMIC", Pattern.CASE_INSENSITIVE); + + private static String convertTableConfig(String convertedText, SqlStatement sqlStatement) { + Matcher engineMatcher = ENGINE_PATTERN.matcher(convertedText); + if (engineMatcher.find()) { + convertedText = engineMatcher.replaceAll(""); + } + Matcher defaultCharsetMatcher = DEFAULT_CHARSET_PATTERN.matcher(convertedText); + if (defaultCharsetMatcher.find()) { + convertedText = defaultCharsetMatcher.replaceAll(""); + } + Matcher rowFormatMatcher = ROW_FORMAT_PATTERN.matcher(convertedText); + if (rowFormatMatcher.find()) { + convertedText = rowFormatMatcher.replaceAll(""); + } + return convertedText; + } + + private static final Pattern INDEX_NAME_PATTERN = Pattern.compile( + // KEY `AppId_ClusterName_GroupName` + "(KEY\\s*`|KEY\\s+)(?[a-zA-Z0-9\\-_]+)(`)?\\s*" + // (`AppId`,`ClusterName`(191),`NamespaceName`(191)) + + "\\((?" + + "(`)?[a-zA-Z0-9\\-_]+(`)?\\s*(\\([0-9]+\\))?" + + "(," + + "(`)?[a-zA-Z0-9\\-_]+(`)?\\s*(\\([0-9]+\\))?" + + ")*" + + ")\\)", + Pattern.CASE_INSENSITIVE); + + private static String convertIndexWithTable(String convertedText, String tableName, + SqlStatement sqlStatement) { + String[] lines = convertedText.split("\n"); + StringJoiner joiner = new StringJoiner("\n"); + for (String line : lines) { + String convertedLine = line; + if (convertedLine.contains("KEY") || convertedLine.contains("key")) { + // replace index name + // KEY `AppId_ClusterName_GroupName` (`AppId`,`ClusterName`(191),`NamespaceName`(191)) + // -> + // KEY `tableName_AppId_ClusterName_GroupName` (`AppId`,`ClusterName`(191),`NamespaceName`(191)) + Matcher indexNameMatcher = INDEX_NAME_PATTERN.matcher(convertedLine); + if (indexNameMatcher.find()) { + convertedLine = indexNameMatcher.replaceAll( + "KEY `" + tableName + "_${indexName}` (${indexColumns})"); + } + convertedLine = removePrefixIndex(convertedLine); + } + joiner.add(convertedLine); + } + return joiner.toString(); + } + + private static String convertColumn(String convertedText, SqlStatement sqlStatement) { + // convert bit(1) to boolean + // `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal' + // -> + // `IsDeleted` boolean NOT NULL DEFAULT FALSE + if (convertedText.contains("bit(1)")) { + convertedText = convertedText.replace("bit(1)", "boolean"); + } + if (convertedText.contains("b'0'")) { + convertedText = convertedText.replace("b'0'", "FALSE"); + } + if (convertedText.contains("b'1'")) { + convertedText = convertedText.replace("b'1'", "TRUE"); + } + + return convertedText; + } + + private static String convertAlterTable(String convertedText, SqlStatement sqlStatement, + Matcher opTableMatcher) { + String tableName = opTableMatcher.group("tableName"); + // remove first table name + convertedText = opTableMatcher.replaceAll(""); + convertedText = convertAlterTableMulti(convertedText, sqlStatement, tableName); + + return convertedText; + } + + private static final Pattern ADD_COLUMN_PATTERN = Pattern.compile( + "\\s*ADD\\s+COLUMN\\s+(`)?(?[a-zA-Z0-9\\-_]+)(`)?(?.*)[,;]", + Pattern.CASE_INSENSITIVE); + private static final Pattern MODIFY_COLUMN_PATTERN = Pattern.compile( + "\\s*MODIFY\\s+COLUMN\\s+(`)?(?[a-zA-Z0-9\\-_]+)(`)?(?.*)[,;]", + Pattern.CASE_INSENSITIVE); + private static final Pattern CHANGE_PATTERN = Pattern.compile( + "\\s*CHANGE\\s+(`)?(?[a-zA-Z0-9\\-_]+)(`)?\\s+(`)?(?[a-zA-Z0-9\\-_]+)(`)?(?.*)[,;]", + Pattern.CASE_INSENSITIVE); + private static final Pattern DROP_COLUMN_PATTERN = Pattern.compile( + "\\s*DROP\\s+(COLUMN\\s+)?(`)?(?[a-zA-Z0-9\\-_]+)(`)?\\s*[,;]", + Pattern.CASE_INSENSITIVE); + private static final Pattern ADD_KEY_PATTERN = Pattern.compile( + "\\s*ADD\\s+(?(UNIQUE\\s+)?KEY)\\s+(`)?(?[a-zA-Z0-9\\-_]+)(`)?(?.*)[,;]", + Pattern.CASE_INSENSITIVE); + private static final Pattern ADD_INDEX_PATTERN = Pattern.compile( + "\\s*ADD\\s+(?(UNIQUE\\s+)?INDEX)\\s+(`)?(?[a-zA-Z0-9\\-_]+)(`)?(?.*)[,;]", + Pattern.CASE_INSENSITIVE); + private static final Pattern DROP_INDEX_PATTERN = Pattern.compile( + "\\s*DROP\\s+INDEX\\s+(`)?(?[a-zA-Z0-9\\-_]+)(`)?\\s*[,;]", + Pattern.CASE_INSENSITIVE); + + private static String convertAlterTableMulti(String convertedText, SqlStatement sqlStatement, + String tableName) { + Matcher addColumnMatcher = ADD_COLUMN_PATTERN.matcher(convertedText); + if (addColumnMatcher.find()) { + convertedText = addColumnMatcher.replaceAll( + "\nALTER TABLE `" + tableName + "` ADD COLUMN `${columnName}`${subStatement};"); + } + Matcher modifyColumnMatcher = MODIFY_COLUMN_PATTERN.matcher(convertedText); + if (modifyColumnMatcher.find()) { + convertedText = modifyColumnMatcher.replaceAll( + "\nALTER TABLE `" + tableName + "` MODIFY COLUMN `${columnName}`${subStatement};"); + } + Matcher changeMatcher = CHANGE_PATTERN.matcher(convertedText); + if (changeMatcher.find()) { + convertedText = changeMatcher.replaceAll("\nALTER TABLE `" + tableName + + "` CHANGE `${oldColumnName}` `${newColumnName}` ${subStatement};"); + } + + Matcher dropColumnMatcher = DROP_COLUMN_PATTERN.matcher(convertedText); + if (dropColumnMatcher.find()) { + convertedText = dropColumnMatcher.replaceAll( + "\nALTER TABLE `" + tableName + "` DROP `${columnName}`;"); + } + Matcher addKeyMatcher = ADD_KEY_PATTERN.matcher(convertedText); + if (addKeyMatcher.find()) { + convertedText = addKeyMatcher.replaceAll( + "\nALTER TABLE `" + tableName + "` ADD ${indexType} `" + tableName + + "_${indexName}` ${subStatement};"); + convertedText = removePrefixIndex(convertedText); + } + Matcher addIndexMatcher = ADD_INDEX_PATTERN.matcher(convertedText); + if (addIndexMatcher.find()) { + convertedText = addIndexMatcher.replaceAll( + "\nALTER TABLE `" + tableName + "` ADD ${indexType} `" + tableName + + "_${indexName}` ${subStatement};"); + convertedText = removePrefixIndex(convertedText); + } + Matcher dropIndexMatcher = DROP_INDEX_PATTERN.matcher(convertedText); + if (dropIndexMatcher.find()) { + convertedText = dropIndexMatcher.replaceAll( + "\nALTER TABLE `" + tableName + "` DROP INDEX `" + tableName + "_${indexName}`;"); + } + return convertedText; + } + + private static final Pattern CREATE_INDEX_PATTERN = Pattern.compile( + "CREATE\\s+(?(UNIQUE\\s+)?INDEX)\\s+(`)?(?[a-zA-Z0-9\\-_]+)(`)?", + Pattern.CASE_INSENSITIVE); + + private static String convertIndexOnTable(String convertedText, String tableName, + SqlStatement sqlStatement) { + Matcher createIndexMatcher = CREATE_INDEX_PATTERN.matcher(convertedText); + if (createIndexMatcher.find()) { + convertedText = createIndexMatcher.replaceAll( + "CREATE ${indexType} `" + tableName + "_${indexName}`"); + convertedText = removePrefixIndex(convertedText); + } + return convertedText; + } + + private static final Pattern PREFIX_INDEX_PATTERN = Pattern.compile( + "(?\\(" + // other columns + + "((`)?[a-zA-Z0-9\\-_]+(`)?\\s*(\\([0-9]+\\))?,)*)" + // ``(191) + + "(`)?(?[a-zA-Z0-9\\-_]+)(`)?\\s*\\([0-9]+\\)" + // other columns + + "(?(,(`)?[a-zA-Z0-9\\-_]+(`)?\\s*(\\([0-9]+\\))?)*" + + "\\))"); + + private static String removePrefixIndex(String convertedText) { + // convert prefix index + // (`AppId`,`ClusterName`(191),`NamespaceName`(191)) + // -> + // (`AppId`,`ClusterName`,`NamespaceName`) + for (Matcher prefixIndexMatcher = PREFIX_INDEX_PATTERN.matcher(convertedText); + prefixIndexMatcher.find(); + prefixIndexMatcher = PREFIX_INDEX_PATTERN.matcher(convertedText)) { + convertedText = prefixIndexMatcher.replaceAll("${prefix}`${columnName}`${suffix}"); + } + return convertedText; + } +} diff --git a/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloMysqlDefaultConverterUtil.java b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloMysqlDefaultConverterUtil.java new file mode 100644 index 00000000000..3450cd016bb --- /dev/null +++ b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloMysqlDefaultConverterUtil.java @@ -0,0 +1,69 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.build.sql.converter; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; + +public class ApolloMysqlDefaultConverterUtil { + + public static void convert(SqlTemplate sqlTemplate, String targetSql, + SqlTemplateContext context) { + String databaseName; + String srcSql = sqlTemplate.getSrcPath(); + if (srcSql.contains("apolloconfigdb")) { + databaseName = "ApolloConfigDB"; + } else if (srcSql.contains("apolloportaldb")) { + databaseName = "ApolloPortalDB"; + } else { + throw new IllegalArgumentException("unknown database name: " + srcSql); + } + + ApolloSqlConverterUtil.ensureDirectories(targetSql); + + String rawText = ApolloSqlConverterUtil.process(sqlTemplate, context); + + List sqlStatements = ApolloSqlConverterUtil.toStatements(rawText); + try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get(targetSql), + StandardCharsets.UTF_8, StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING)) { + for (SqlStatement sqlStatement : sqlStatements) { + String convertedText = convertMainMysqlLine(sqlStatement, databaseName); + bufferedWriter.write(convertedText); + bufferedWriter.write('\n'); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static String convertMainMysqlLine(SqlStatement sqlStatement, String databaseName) { + String convertedText = sqlStatement.getRawText(); + + convertedText = convertedText.replace("ApolloAssemblyDB", databaseName); + + return convertedText; + } +} diff --git a/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverter.java b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverter.java new file mode 100644 index 00000000000..fac0e5a5d14 --- /dev/null +++ b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverter.java @@ -0,0 +1,134 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.build.sql.converter; + +import freemarker.template.Configuration; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.List; + +public class ApolloSqlConverter { + + public static void main(String[] args) { + String repositoryDir = ApolloSqlConverterUtil.getRepositoryDir(); + String srcDir = repositoryDir + "/scripts/sql/src"; + String targetParentDir = repositoryDir + "/scripts"; + + SqlTemplateGist gists = ApolloSqlConverterUtil.getGists(repositoryDir); + + convert(repositoryDir, srcDir, targetParentDir, gists); + } + + public static List convert(String repositoryDir, String srcDir, String targetParentDir, + SqlTemplateGist gists) { + + Configuration configuration = createConfiguration(srcDir); + + // 'scripts/sql/src/apolloconfigdb.sql' + // 'scripts/sql/src/apolloportaldb.sql' + // 'scripts/sql/src/delta/**/*.sql' + List srcSqlList = ApolloSqlConverterUtil.getSqlList(srcDir); + List templateList = ApolloSqlConverterUtil.toTemplates(srcSqlList, srcDir, + configuration); + + // 'scripts/sql/src' -> 'scripts/sql/profiles/mysql-default' + convertMysqlDefaultList(templateList, srcDir, targetParentDir, gists); + + // 'scripts/sql/src' -> 'scripts/sql/profiles/mysql-database-not-specified' + convertMysqlDatabaseNotSpecifiedList(templateList, srcDir, targetParentDir, gists); + + // 'scripts/sql/src' -> 'scripts/sql/profiles/h2-default' + convertH2DefaultList(templateList, srcDir, targetParentDir, gists); + + return srcSqlList; + } + + private static Configuration createConfiguration(String srcDir) { + Configuration configuration = new Configuration(Configuration.VERSION_2_3_32); + try { + configuration.setDirectoryForTemplateLoading(new File(srcDir)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + configuration.setDefaultEncoding(StandardCharsets.UTF_8.name()); + return configuration; + } + + private static void convertMysqlDefaultList(List templateList, + String srcDir, String targetParentDir, SqlTemplateGist gists) { + String targetDir = targetParentDir + "/sql/profiles/mysql-default"; + + SqlTemplateGist mainMysqlGists = SqlTemplateGist.builder() + .autoGeneratedDeclaration(gists.getAutoGeneratedDeclaration()) + .h2Function("") + .setupDatabase(gists.getSetupDatabase()) + .useDatabase(gists.getUseDatabase()) + .build(); + SqlTemplateContext context = SqlTemplateContext.builder() + .gists(mainMysqlGists) + .build(); + + for (SqlTemplate sqlTemplate : templateList) { + String targetSql = ApolloSqlConverterUtil.replacePath(sqlTemplate.getSrcPath(), srcDir, + targetDir); + ApolloMysqlDefaultConverterUtil.convert(sqlTemplate, targetSql, context); + } + } + + private static void convertMysqlDatabaseNotSpecifiedList(List templateList, + String srcDir, + String targetParentDir, SqlTemplateGist gists) { + String targetDir = targetParentDir + "/sql/profiles/mysql-database-not-specified"; + + SqlTemplateGist mainMysqlGists = SqlTemplateGist.builder() + .autoGeneratedDeclaration(gists.getAutoGeneratedDeclaration()) + .h2Function("") + .setupDatabase("") + .useDatabase("") + .build(); + SqlTemplateContext context = SqlTemplateContext.builder() + .gists(mainMysqlGists) + .build(); + for (SqlTemplate sqlTemplate : templateList) { + String targetSql = ApolloSqlConverterUtil.replacePath(sqlTemplate.getSrcPath(), srcDir, + targetDir); + ApolloMysqlDefaultConverterUtil.convert(sqlTemplate, targetSql, context); + } + } + + private static void convertH2DefaultList(List templateList, String srcDir, + String targetParentDir, SqlTemplateGist gists) { + String targetDir = targetParentDir + "/sql/profiles/h2-default"; + + SqlTemplateGist mainMysqlGists = SqlTemplateGist.builder() + .autoGeneratedDeclaration(gists.getAutoGeneratedDeclaration()) + .h2Function(gists.getH2Function()) + .setupDatabase("") + .useDatabase("") + .build(); + SqlTemplateContext context = SqlTemplateContext.builder() + .gists(mainMysqlGists) + .build(); + for (SqlTemplate sqlTemplate : templateList) { + String targetSql = ApolloSqlConverterUtil.replacePath(sqlTemplate.getSrcPath(), srcDir, + targetDir); + ApolloH2ConverterUtil.convert(sqlTemplate, targetSql, context); + } + } +} diff --git a/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverterUtil.java b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverterUtil.java new file mode 100644 index 00000000000..af49f64655e --- /dev/null +++ b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverterUtil.java @@ -0,0 +1,412 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.build.sql.converter; + +import freemarker.template.Configuration; +import freemarker.template.Template; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UncheckedIOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.StringJoiner; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; + +public class ApolloSqlConverterUtil { + + private static final Pattern BASE_PATTERN = Pattern.compile( + "(apolloconfigdb|apolloportaldb)-v[0-9]{3,}-v[0-9]{3,}-base.sql"); + + private static final Pattern BEFORE_PATTERN = Pattern.compile( + "(apolloconfigdb|apolloportaldb)-v[0-9]{3,}-v[0-9]{3,}-before.sql"); + + private static final Pattern DELTA_PATTERN = Pattern.compile( + "(apolloconfigdb|apolloportaldb)-v[0-9]{3,}-v[0-9]{3,}.sql"); + + private static final Pattern AFTER_PATTERN = Pattern.compile( + "(apolloconfigdb|apolloportaldb)-v[0-9]{3,}-v[0-9]{3,}-after.sql"); + + public static String getRepositoryDir() { + ProtectionDomain protectionDomain = ApolloSqlConverter.class.getProtectionDomain(); + CodeSource codeSource = protectionDomain.getCodeSource(); + URL location = codeSource.getLocation(); + URI uri; + try { + uri = location.toURI(); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e.getLocalizedMessage(), e); + } + Path path = Paths.get(uri); + String unixClassPath = path.toString().replace("\\", "/"); + + if (!unixClassPath.endsWith("/apollo-build-sql-converter/target/classes")) { + throw new IllegalStateException("illegal class path: " + unixClassPath); + } + + return ApolloSqlConverterUtil.replacePath(unixClassPath, + "/apollo-build-sql-converter/target/classes", ""); + } + + public static void ensureDirectories(String targetFilePath) { + Path path = Paths.get(targetFilePath); + Path dirPath = path.getParent(); + try { + Files.createDirectories(dirPath); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public static String process(SqlTemplate sqlTemplate, SqlTemplateContext context) { + Template freemarkerTemplate = sqlTemplate.getTemplate(); + StringWriter writer = new StringWriter(); + try { + freemarkerTemplate.process(context, writer); + } catch (Exception e) { + throw new RuntimeException(e); + } + return writer.toString(); + } + + public static String replacePrefix(String origin, String prefix, String target) { + if (!origin.startsWith(prefix)) { + throw new IllegalArgumentException("illegal file path: " + origin); + } + return origin.replace(prefix, target); + } + + public static String replacePath(String origin, String src, String target) { + if (!origin.contains(src)) { + throw new IllegalArgumentException("illegal file path: " + origin); + } + return origin.replace(src, target); + } + + public static SqlTemplateGist getGists(String repositoryDir) { + String gistDir = repositoryDir + "/scripts/sql/src/gist"; + String autoGeneratedDeclaration = getGist(gistDir + "/autoGeneratedDeclaration.sql"); + String h2Function = getGist(gistDir + "/h2Function.sql"); + String setupDatabase = getGist(gistDir + "/setupDatabase.sql"); + String useDatabase = getGist(gistDir + "/useDatabase.sql"); + return SqlTemplateGist.builder() + .autoGeneratedDeclaration(autoGeneratedDeclaration) + .h2Function(h2Function) + .setupDatabase(setupDatabase) + .useDatabase(useDatabase) + .build(); + } + + private static String getGist(String gistPath) { + StringJoiner joiner = new StringJoiner("\n", "\n", ""); + boolean accept = false; + try (BufferedReader bufferedReader = Files.newBufferedReader(Paths.get(gistPath), + StandardCharsets.UTF_8)) { + for (String line = bufferedReader.readLine(); line != null; + line = bufferedReader.readLine()) { + if (line.contains("@@gist-start@@")) { + accept = true; + continue; + } + if (line.contains("@@gist-end@@")) { + break; + } + if (accept) { + joiner.add(line); + } + } + } catch (IOException e) { + throw new UncheckedIOException("failed to open gistPath " + e.getLocalizedMessage(), e); + } + return joiner.toString(); + } + + public static List getSqlList(String dir, Set ignoreDirs) { + List sqlList = new ArrayList<>(); + if (Files.exists(Paths.get(dir + "/apolloconfigdb.sql"))) { + sqlList.add(dir + "/apolloconfigdb.sql"); + } + if (Files.exists(Paths.get(dir + "/apolloportaldb.sql"))) { + sqlList.add(dir + "/apolloportaldb.sql"); + } + List deltaSqlList = getDeltaSqlList(dir, ignoreDirs); + sqlList.addAll(deltaSqlList); + return sqlList; + } + + public static List getSqlList(String dir) { + return getSqlList(dir, Collections.emptySet()); + } + + public static List list(Path dir) { + List subPathList = new ArrayList<>(); + try (DirectoryStream ds = Files.newDirectoryStream(dir)) { + for (Path path : ds) { + subPathList.add(path); + } + } catch (IOException e) { + throw new UncheckedIOException("failed to open dir " + e.getLocalizedMessage(), e); + } + return subPathList; + } + + public static List listSorted(Path dir, Comparator comparator) { + List subPathList = list(dir); + List sortedSubPathList = new ArrayList<>(subPathList); + sortedSubPathList.sort(comparator); + return sortedSubPathList; + } + + public static List listSorted(Path dir) { + return listSorted(dir, Comparator.comparing(Path::toString)); + } + + public static void deleteDir(Path dir) { + try { + ApolloSqlConverterUtil.deleteDirInternal(dir); + } catch (IOException e) { + throw new UncheckedIOException("failed to delete dir " + e.getLocalizedMessage(), e); + } + } + + private static void deleteDirInternal(Path dir) throws IOException { + if (!Files.exists(dir)) { + return; + } + List files = ApolloSqlConverterUtil.list(dir); + for (Path file : files) { + if (!Files.exists(file)) { + continue; + } + if (Files.isDirectory(file)) { + ApolloSqlConverterUtil.deleteDirInternal(file); + Files.delete(file); + } else { + Files.delete(file); + } + } + } + + public static Comparator deltaSqlComparator() { + return Comparator.comparing(path -> { + String unixPath = path.replace("\\", "/"); + int lastIndex = unixPath.lastIndexOf("/"); + String fileName; + if (lastIndex > 0) { + fileName = unixPath.substring(lastIndex + 1); + } else { + fileName = unixPath; + } + if (!fileName.endsWith(".sql")) { + // not sql file + return path; + } + // sort: base < before < delta < after + if (BASE_PATTERN.matcher(fileName).matches()) { + return "00" + path; + } else if (BEFORE_PATTERN.matcher(fileName).matches()) { + return "30" + path; + } else if (DELTA_PATTERN.matcher(fileName).matches()) { + return "50" + path; + } else if (AFTER_PATTERN.matcher(fileName).matches()) { + return "90" + path; + } else { + throw new IllegalArgumentException("illegal file name: " + fileName); + } + }); + } + + private static List getDeltaSqlList(String dir, Set ignoreDirs) { + Path dirPath = Paths.get(dir + "/delta"); + if (!Files.exists(dirPath)) { + return Collections.emptyList(); + } + List deltaDirList = listSorted(dirPath); + List allDeltaSqlList = new ArrayList<>(); + for (Path deltaDir : deltaDirList) { + if (!Files.isDirectory(deltaDir)) { + continue; + } + if (ignoreDirs.contains(deltaDir.toString().replace("\\", "/"))) { + continue; + } + List deltaFiles = listSorted(deltaDir, + Comparator.comparing(Path::toString, deltaSqlComparator())); + for (Path path : deltaFiles) { + String fileName = path.toString(); + if (fileName.endsWith(".sql")) { + allDeltaSqlList.add(fileName.replace("\\", "/")); + } + } + } + + return allDeltaSqlList; + } + + + public static List toTemplates(List srcSqlList, String srcDir, + Configuration configuration) { + List templateList = new ArrayList<>(srcSqlList.size()); + for (String srcSql : srcSqlList) { + String templateName = ApolloSqlConverterUtil.replacePrefix(srcSql, srcDir + "/", ""); + Template template; + try { + template = configuration.getTemplate(templateName); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + templateList.add(new SqlTemplate(srcSql, template)); + } + return templateList; + } + + public static List toStatements(String rawText) { + List sqlStatementList = new ArrayList<>(); + try (BufferedReader bufferedReader = new BufferedReader(new StringReader(rawText))) { + AtomicReferencerawTextJoinerRef = new AtomicReference<>(new StringJoiner("\n")); + StringBuilder singleLineTextBuilder = new StringBuilder(); + List textLines = new ArrayList<>(); + AtomicBoolean comment = new AtomicBoolean(false); + for (String line = bufferedReader.readLine(); line != null; + line = bufferedReader.readLine()) { + if (line.startsWith("--")) { + commentLine(line, rawTextJoinerRef, singleLineTextBuilder, textLines, comment, + sqlStatementList); + } else { + noCommentLine(line, rawTextJoinerRef, singleLineTextBuilder, textLines, comment, + sqlStatementList); + } + } + if (!textLines.isEmpty()) { + StringJoiner sqlStatementRawTextJoiner = rawTextJoinerRef.get(); + SqlStatement sqlStatement = createStatement( + sqlStatementRawTextJoiner, singleLineTextBuilder, textLines); + sqlStatementList.add(sqlStatement); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return sqlStatementList; + } + + private static void commentLine(String line,AtomicReference rawTextJoinerRef, + StringBuilder singleLineTextBuilder, List textLines, AtomicBoolean comment, + List sqlStatementList) { + + if (!comment.get()) { + comment.set(true); + if (!textLines.isEmpty()) { + StringJoiner sqlStatementRawTextJoiner = rawTextJoinerRef.get(); + SqlStatement sqlStatement = createStatement( + sqlStatementRawTextJoiner, singleLineTextBuilder, textLines); + + resetStatementBuffer(rawTextJoinerRef, singleLineTextBuilder, + textLines); + sqlStatementList.add(sqlStatement); + } + } + StringJoiner sqlStatementRawTextJoiner = rawTextJoinerRef.get(); + // raw text + sqlStatementRawTextJoiner.add(line); + // single line text + singleLineTextBuilder.append(line); + // text lines + textLines.add(line); + } + + private static void noCommentLine(String line, AtomicReferencerawTextJoinerRef , + StringBuilder singleLineTextBuilder, List textLines, + AtomicBoolean comment, List sqlStatementList) { + + if (comment.get()) { + comment.set(false); + if (!textLines.isEmpty()) { + StringJoiner sqlStatementRawTextJoiner = rawTextJoinerRef.get(); + SqlStatement sqlStatement = createStatement( + sqlStatementRawTextJoiner, singleLineTextBuilder, textLines); + + resetStatementBuffer(rawTextJoinerRef, singleLineTextBuilder, + textLines); + sqlStatementList.add(sqlStatement); + } + } + + StringJoiner sqlStatementRawTextJoiner = rawTextJoinerRef.get(); + // ; is the end of a statement + int indexOfSemicolon = line.indexOf(';'); + if (indexOfSemicolon == -1) { + // raw text + sqlStatementRawTextJoiner.add(line); + // single line text + singleLineTextBuilder.append(line); + // text lines + textLines.add(line); + } else { + String lineBeforeSemicolon = line.substring(0, indexOfSemicolon + 1); + // raw text + sqlStatementRawTextJoiner.add(lineBeforeSemicolon); + // single line text + singleLineTextBuilder.append(lineBeforeSemicolon); + // text lines + textLines.add(lineBeforeSemicolon); + + SqlStatement sqlStatement = createStatement( + sqlStatementRawTextJoiner, singleLineTextBuilder, textLines); + + resetStatementBuffer(rawTextJoinerRef, singleLineTextBuilder, + textLines); + + sqlStatementList.add(sqlStatement); + } + } + + private static SqlStatement createStatement(StringJoiner sqlStatementRawTextJoiner, + StringBuilder singleLineTextBuilder, List textLines) { + return SqlStatement.builder() + .rawText(sqlStatementRawTextJoiner.toString()) + .singleLineText(singleLineTextBuilder.toString()) + .textLines(new ArrayList<>(textLines)) + .build(); + } + + private static void resetStatementBuffer( + AtomicReference rawTextJoinerRef, + StringBuilder singleLineTextBuilder, List textLines) { + // raw text + rawTextJoinerRef.set(new StringJoiner("\n")); + // single line text + singleLineTextBuilder.setLength(0); + // text lines + textLines.clear(); + } +} diff --git a/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlStatement.java b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlStatement.java new file mode 100644 index 00000000000..d6f40da1915 --- /dev/null +++ b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlStatement.java @@ -0,0 +1,99 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.build.sql.converter; + +import java.util.Collections; +import java.util.List; +import java.util.StringJoiner; + +public class SqlStatement { + + private final String rawText; + + private final String singleLineText; + + private final List textLines; + + SqlStatement(Builder builder) { + this.rawText = builder.rawText; + this.singleLineText = builder.singleLineText; + this.textLines = builder.textLines; + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + Builder builder = new Builder(); + builder.rawText = this.rawText; + builder.singleLineText = this.singleLineText; + builder.textLines = this.textLines; + return builder; + } + + public String getRawText() { + return this.rawText; + } + + public String getSingleLineText() { + return this.singleLineText; + } + + public List getTextLines() { + return this.textLines; + } + + @Override + public String toString() { + return new StringJoiner(", ", SqlStatement.class.getSimpleName() + "[", "]") + // fields + .add("rawText='" + this.rawText + "'") + .toString(); + } + + public static final class Builder { + + private String rawText; + private String singleLineText; + private List textLines; + + Builder() { + } + + public Builder rawText(String rawText) { + this.rawText = rawText; + return this; + } + + public Builder singleLineText(String singleLineText) { + this.singleLineText = singleLineText; + return this; + } + + public Builder textLines(List textLines) { + this.textLines = textLines == null ? null : + // nonnull + (textLines.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(textLines)); + return this; + } + + public SqlStatement build() { + return new SqlStatement(this); + } + } +} diff --git a/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlTemplate.java b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlTemplate.java new file mode 100644 index 00000000000..bb1682ef3f4 --- /dev/null +++ b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlTemplate.java @@ -0,0 +1,39 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.build.sql.converter; + +import freemarker.template.Template; + +public class SqlTemplate { + + private final String srcPath; + + private final Template template; + + public SqlTemplate(String srcPath, Template template) { + this.srcPath = srcPath; + this.template = template; + } + + public String getSrcPath() { + return this.srcPath; + } + + public Template getTemplate() { + return this.template; + } +} diff --git a/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlTemplateContext.java b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlTemplateContext.java new file mode 100644 index 00000000000..5b0713c8caa --- /dev/null +++ b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlTemplateContext.java @@ -0,0 +1,70 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.build.sql.converter; + +import java.util.StringJoiner; + +public class SqlTemplateContext { + + /** + * sql gist + */ + private final SqlTemplateGist gists; + + SqlTemplateContext(Builder builder) { + this.gists = builder.gists; + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + Builder builder = new Builder(); + builder.gists = this.gists; + return builder; + } + + public SqlTemplateGist getGists() { + return this.gists; + } + + @Override + public String toString() { + return new StringJoiner(", ", SqlTemplateContext.class.getSimpleName() + "[", "]") + // fields + .add("gists=" + this.gists) + .toString(); + } + + public static final class Builder { + + private SqlTemplateGist gists; + + Builder() { + } + + public Builder gists(SqlTemplateGist gists) { + this.gists = gists; + return this; + } + + public SqlTemplateContext build() { + return new SqlTemplateContext(this); + } + } +} diff --git a/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlTemplateGist.java b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlTemplateGist.java new file mode 100644 index 00000000000..f9d0d5f4bf8 --- /dev/null +++ b/apollo-build-sql-converter/src/main/java/com/ctrip/framework/apollo/build/sql/converter/SqlTemplateGist.java @@ -0,0 +1,112 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.build.sql.converter; + +import java.util.StringJoiner; + +public class SqlTemplateGist { + + private final String autoGeneratedDeclaration; + + private final String h2Function; + + private final String setupDatabase; + + private final String useDatabase; + + SqlTemplateGist(Builder builder) { + this.autoGeneratedDeclaration = builder.autoGeneratedDeclaration; + this.h2Function = builder.h2Function; + this.setupDatabase = builder.setupDatabase; + this.useDatabase = builder.useDatabase; + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + Builder builder = new Builder(); + builder.autoGeneratedDeclaration = this.autoGeneratedDeclaration; + builder.h2Function = this.h2Function; + builder.setupDatabase = this.setupDatabase; + builder.useDatabase = this.useDatabase; + return builder; + } + + public String getAutoGeneratedDeclaration() { + return this.autoGeneratedDeclaration; + } + + public String getH2Function() { + return this.h2Function; + } + + public String getSetupDatabase() { + return this.setupDatabase; + } + + public String getUseDatabase() { + return this.useDatabase; + } + + @Override + public String toString() { + return new StringJoiner(", ", SqlTemplateGist.class.getSimpleName() + "[", "]") + // fields + .add("autoGeneratedDeclaration='" + this.autoGeneratedDeclaration + "'") + .add("h2Function='" + this.h2Function + "'") + .add("setupDatabase='" + this.setupDatabase + "'") + .add("useDatabase='" + this.useDatabase + "'") + .toString(); + } + + public static final class Builder { + + private String autoGeneratedDeclaration; + private String h2Function; + private String setupDatabase; + private String useDatabase; + + Builder() { + } + + public Builder autoGeneratedDeclaration(String autoGeneratedDeclaration) { + this.autoGeneratedDeclaration = autoGeneratedDeclaration; + return this; + } + + public Builder h2Function(String h2Function) { + this.h2Function = h2Function; + return this; + } + + public Builder setupDatabase(String setupDatabase) { + this.setupDatabase = setupDatabase; + return this; + } + + public Builder useDatabase(String useDatabase) { + this.useDatabase = useDatabase; + return this; + } + + public SqlTemplateGist build() { + return new SqlTemplateGist(this); + } + } +} diff --git a/apollo-build-sql-converter/src/test/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverterAutoGeneratedTest.java b/apollo-build-sql-converter/src/test/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverterAutoGeneratedTest.java new file mode 100644 index 00000000000..d43454504ca --- /dev/null +++ b/apollo-build-sql-converter/src/test/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverterAutoGeneratedTest.java @@ -0,0 +1,219 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.build.sql.converter; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class ApolloSqlConverterAutoGeneratedTest { + + private static final String GENERATE_TIPS = "mvn compile -pl apollo-build-sql-converter -Psql-converter"; + + /** + * Check sql files in 'scripts/sql/profiles' are auto generated. + */ + @Test + void checkAutoGenerated() { + String repositoryDir = ApolloSqlConverterUtil.getRepositoryDir(); + + String srcDir = repositoryDir + "/scripts/sql/src"; + String checkerParentDir = repositoryDir + "/apollo-build-sql-converter/target/scripts/sql/checker-auto-generated"; + String repositoryParentDir = repositoryDir + "/scripts"; + + // generate checker sql files + ApolloSqlConverterUtil.deleteDir(Paths.get(checkerParentDir)); + SqlTemplateGist gists = ApolloSqlConverterUtil.getGists(repositoryDir); + List srcSqlList = ApolloSqlConverter.convert(repositoryDir, srcDir, checkerParentDir, + gists); + + // compare checker sql files with repository sql files + this.checkSqlList(srcSqlList, srcDir, checkerParentDir, repositoryParentDir); + } + + private void checkSqlList(List srcSqlList, String srcDir, String checkerParentDir, + String repositoryParentDir) { + + // 'scripts/sql/profiles/mysql-default' + this.checkMysqlDefaultList(srcSqlList, srcDir, checkerParentDir, repositoryParentDir); + + // 'scripts/sql/profiles/mysql-database-not-specified' + this.checkMysqlDatabaseNotSpecifiedList(srcSqlList, srcDir, checkerParentDir, + repositoryParentDir); + + // '/scripts/sql/profiles/h2-default' + this.checkH2DefaultList(srcSqlList, srcDir, checkerParentDir, repositoryParentDir); + } + + private void checkMysqlDefaultList(List srcSqlList, String srcDir, + String checkerParentDir, String repositoryParentDir) { + String checkerTargetDir = checkerParentDir + "/sql/profiles/mysql-default"; + String repositoryTargetDir = repositoryParentDir + "/sql/profiles/mysql-default"; + + List checkerSqlList = ApolloSqlConverterUtil.getSqlList(checkerTargetDir); + + Set ignoreDirs = this.getIgnoreDirs(repositoryTargetDir); + List repositorySqlList = ApolloSqlConverterUtil.getSqlList(repositoryTargetDir, + ignoreDirs); + + List redundantSqlList = this.findRedundantSqlList(checkerTargetDir, checkerSqlList, + repositoryTargetDir, repositorySqlList); + Assertions.assertEquals(0, redundantSqlList.size(), + "redundant sql files, please add sql files in 'scripts/sql/src' and then run '" + + GENERATE_TIPS + + "' to generated. Do not edit 'scripts/sql/profiles' manually !!!\npath: " + + redundantSqlList); + + List missingSqlList = this.findMissingSqlList(checkerTargetDir, checkerSqlList, + repositoryTargetDir, repositorySqlList); + Assertions.assertEquals(0, missingSqlList.size(), + "missing sql files, please run '" + GENERATE_TIPS + "' to regenerated\npath: " + + missingSqlList); + + for (String srcSql : srcSqlList) { + String checkerTargetSql = ApolloSqlConverterUtil.replacePath(srcSql, srcDir, + checkerTargetDir); + String repositoryTargetSql = ApolloSqlConverterUtil.replacePath(srcSql, srcDir, + repositoryTargetDir); + + this.doCheck(checkerTargetSql, repositoryTargetSql); + } + } + + private Set getIgnoreDirs(String repositoryTargetDir) { + Set ignoreDirs = new LinkedHashSet<>(); + ignoreDirs.add(repositoryTargetDir + "/delta/v040-v050"); + ignoreDirs.add(repositoryTargetDir + "/delta/v060-v062"); + ignoreDirs.add(repositoryTargetDir + "/delta/v080-v090"); + ignoreDirs.add(repositoryTargetDir + "/delta/v151-v160"); + ignoreDirs.add(repositoryTargetDir + "/delta/v170-v180"); + ignoreDirs.add(repositoryTargetDir + "/delta/v180-v190"); + ignoreDirs.add(repositoryTargetDir + "/delta/v190-v200"); + ignoreDirs.add(repositoryTargetDir + "/delta/v200-v210"); + ignoreDirs.add(repositoryTargetDir + "/delta/v210-v220"); + return ignoreDirs; + } + + private List findRedundantSqlList(String checkerTargetDir, List checkerSqlList, + String repositoryTargetDir, List repositorySqlList) { + // repository - checker + Map missingSqlMap = this.findMissing( + repositoryTargetDir, repositorySqlList, checkerTargetDir, checkerSqlList); + return new ArrayList<>(missingSqlMap.keySet()); + } + + private List findMissingSqlList(String checkerTargetDir, List checkerSqlList, + String repositoryTargetDir, List repositorySqlList) { + // checker - repository + Map missingSqlMap = this.findMissing(checkerTargetDir, checkerSqlList, + repositoryTargetDir, repositorySqlList); + return new ArrayList<>(missingSqlMap.values()); + } + + private Map findMissing(String sourceDir, List sourceSqlList, + String targetDir, List targetSqlList) { + Map missingSqlList = new LinkedHashMap<>(); + Set targetSqlSet = new LinkedHashSet<>(targetSqlList); + for (String sourceSql : sourceSqlList) { + String targetSql = ApolloSqlConverterUtil.replacePath(sourceSql, sourceDir, targetDir); + if (!targetSqlSet.contains(targetSql)) { + missingSqlList.put(sourceSql, targetSql); + } + } + return missingSqlList; + } + + private void doCheck(String checkerTargetSql, String repositoryTargetSql) { + List checkerLines = new ArrayList<>(); + try (BufferedReader bufferedReader = Files.newBufferedReader(Paths.get(checkerTargetSql), + StandardCharsets.UTF_8)) { + for (String line = bufferedReader.readLine(); line != null; + line = bufferedReader.readLine()) { + checkerLines.add(line); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + List repositoryLines = new ArrayList<>(); + try (BufferedReader bufferedReader = Files.newBufferedReader(Paths.get(repositoryTargetSql), + StandardCharsets.UTF_8)) { + for (String line = bufferedReader.readLine(); line != null; + line = bufferedReader.readLine()) { + repositoryLines.add(line); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + for (int i = 0; i < checkerLines.size(); i++) { + String checkerLine = checkerLines.get(i); + int lineNumber = i + 1; + if (i >= repositoryLines.size()) { + Assertions.fail("invalid sql file content, please run '" + GENERATE_TIPS + + "' to regenerated\npath: " + repositoryTargetSql + "(line: " + lineNumber + ")"); + } + String repositoryLine = repositoryLines.get(i); + Assertions.assertEquals(checkerLine, repositoryLine, + "invalid sql file content, please run '" + GENERATE_TIPS + "' to regenerated\npath: " + + repositoryTargetSql + "(line: " + lineNumber + ")"); + } + Assertions.assertEquals(checkerLines.size(), repositoryLines.size(), + "invalid sql file content, please run '" + GENERATE_TIPS + "' to regenerated\npath: " + + repositoryTargetSql+ "(line: " + checkerLines.size() + ")"); + } + + private void checkMysqlDatabaseNotSpecifiedList(List srcSqlList, String srcDir, + String checkerParentDir, String repositoryParentDir) { + String checkerTargetDir = checkerParentDir + "/sql/profiles/mysql-database-not-specified"; + String repositoryTargetDir = repositoryParentDir + "/sql/profiles/mysql-database-not-specified"; + for (String srcSql : srcSqlList) { + String checkerTargetSql = ApolloSqlConverterUtil.replacePath(srcSql, srcDir, + checkerTargetDir); + String repositoryTargetSql = ApolloSqlConverterUtil.replacePath(srcSql, srcDir, + repositoryTargetDir); + + this.doCheck(checkerTargetSql, repositoryTargetSql); + } + } + + private void checkH2DefaultList(List srcSqlList, String srcDir, + String checkerParentDir, String repositoryParentDir) { + String checkerTargetDir = checkerParentDir + "/sql/profiles/h2-default"; + String repositoryTargetDir = repositoryParentDir + "/sql/profiles/h2-default"; + for (String srcSql : srcSqlList) { + String checkerTargetSql = ApolloSqlConverterUtil.replacePath(srcSql, srcDir, + checkerTargetDir); + String repositoryTargetSql = ApolloSqlConverterUtil.replacePath(srcSql, srcDir, + repositoryTargetDir); + + this.doCheck(checkerTargetSql, repositoryTargetSql); + } + } + +} \ No newline at end of file diff --git a/apollo-build-sql-converter/src/test/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverterH2Test.java b/apollo-build-sql-converter/src/test/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverterH2Test.java new file mode 100644 index 00000000000..85276a0f4f0 --- /dev/null +++ b/apollo-build-sql-converter/src/test/java/com/ctrip/framework/apollo/build/sql/converter/ApolloSqlConverterH2Test.java @@ -0,0 +1,164 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.build.sql.converter; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.core.io.PathResource; +import org.springframework.jdbc.datasource.SimpleDriverDataSource; +import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; + +class ApolloSqlConverterH2Test { + + @Test + void checkH2() { + String repositoryDir = ApolloSqlConverterUtil.getRepositoryDir(); + + String srcDir = repositoryDir + "/scripts/sql/src"; + String checkerParentDir = + repositoryDir + "/apollo-build-sql-converter/target/scripts/sql/checker-h2"; + + String testSrcDir = + repositoryDir + "/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test"; + String testCheckerParentDir = + repositoryDir + "/apollo-build-sql-converter/target/scripts/sql/checker-h2-test"; + + // generate checker sql files + ApolloSqlConverterUtil.deleteDir(Paths.get(checkerParentDir)); + SqlTemplateGist gists = ApolloSqlConverterUtil.getGists(repositoryDir); + SqlTemplateGist h2TestGist = gists.toBuilder() + .h2Function("\n" + + "\n" + + "-- H2 Function\n" + + "-- ------------------------------------------------------------\n" + + "CREATE ALIAS IF NOT EXISTS UNIX_TIMESTAMP FOR \"com.ctrip.framework.apollo.build.sql.converter.TestH2Function.unixTimestamp\";\n") + .build(); + List srcSqlList = ApolloSqlConverter.convert(repositoryDir, srcDir, checkerParentDir, + h2TestGist); + List checkerSqlList = new ArrayList<>(srcSqlList.size()); + for (String srcSql : srcSqlList) { + String checkerSql = ApolloSqlConverterUtil.replacePath(srcSql, srcDir, + checkerParentDir + "/sql/profiles/h2-default"); + checkerSqlList.add(checkerSql); + } + + // generate test checker sql files + ApolloSqlConverterUtil.deleteDir(Paths.get(testCheckerParentDir)); + List testSrcSqlList = ApolloSqlConverter.convert(repositoryDir, testSrcDir, + testCheckerParentDir, h2TestGist); + List testCheckerSqlList = new ArrayList<>(testSrcSqlList.size()); + for (String testSrcSql : testSrcSqlList) { + String testCheckerSql = ApolloSqlConverterUtil.replacePath(testSrcSql, testSrcDir, + testCheckerParentDir + "/sql/profiles/h2-default"); + testCheckerSqlList.add(testCheckerSql); + } + + this.checkSort(testSrcSqlList); + + String h2Path = "test-h2"; + this.checkConfigDatabase(h2Path, checkerSqlList, testCheckerSqlList); + + this.checkPortalDatabase(h2Path, checkerSqlList, testCheckerSqlList); + + } + + private void checkSort(List testSrcSqlList) { + int baseIndex = this.getIndex(testSrcSqlList, "apolloconfigdb-v000-v010-base.sql"); + Assertions.assertTrue(baseIndex >= 0); + int beforeIndex = this.getIndex(testSrcSqlList, "apolloconfigdb-v000-v010-before.sql"); + Assertions.assertTrue(beforeIndex >= 0); + int deltaIndex = this.getIndex(testSrcSqlList, "apolloconfigdb-v000-v010.sql"); + Assertions.assertTrue(deltaIndex >= 0); + int afterIndex = this.getIndex(testSrcSqlList, "apolloconfigdb-v000-v010-after.sql"); + Assertions.assertTrue(afterIndex >= 0); + + // base < before < delta < after + Assertions.assertTrue(baseIndex < beforeIndex); + Assertions.assertTrue(beforeIndex < deltaIndex); + Assertions.assertTrue(deltaIndex < afterIndex); + } + + private int getIndex(List srcSqlList, String fileName) { + for (int i = 0; i < srcSqlList.size(); i++) { + String sqlFile = srcSqlList.get(i); + if (sqlFile.endsWith(fileName)) { + return i; + } + } + return -1; + } + + private void checkConfigDatabase(String h2Path, List checkerSqlList, + List testCheckerSqlList) { + SimpleDriverDataSource configDataSource = new SimpleDriverDataSource(); + configDataSource.setUrl("jdbc:h2:mem:~/" + h2Path + + "/apollo-config-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE"); + configDataSource.setDriverClass(org.h2.Driver.class); + + ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); + populator.setContinueOnError(false); + populator.setSeparator(";"); + populator.setSqlScriptEncoding(StandardCharsets.UTF_8.name()); + + for (String sqlFile : testCheckerSqlList) { + if (sqlFile.contains("apolloconfigdb-")) { + populator.addScript(new PathResource(Paths.get(sqlFile))); + } + } + + for (String sqlFile : checkerSqlList) { + if (sqlFile.contains("apolloconfigdb-")) { + populator.addScript(new PathResource(Paths.get(sqlFile))); + } + } + + DatabasePopulatorUtils.execute(populator, configDataSource); + } + + private void checkPortalDatabase(String h2Path, List checkerSqlList, + List testCheckerSqlList) { + SimpleDriverDataSource portalDataSource = new SimpleDriverDataSource(); + portalDataSource.setUrl("jdbc:h2:mem:~/" + h2Path + + "/apollo-portal-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE"); + portalDataSource.setDriverClass(org.h2.Driver.class); + + ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); + populator.setContinueOnError(false); + populator.setSeparator(";"); + populator.setSqlScriptEncoding(StandardCharsets.UTF_8.name()); + + for (String sqlFile : testCheckerSqlList) { + if (sqlFile.contains("apolloportaldb-")) { + populator.addScript(new PathResource(Paths.get(sqlFile))); + } + } + + for (String sqlFile : checkerSqlList) { + if (sqlFile.contains("apolloportaldb-")) { + populator.addScript(new PathResource(Paths.get(sqlFile))); + } + } + + DatabasePopulatorUtils.execute(populator, portalDataSource); + } + +} \ No newline at end of file diff --git a/apollo-build-sql-converter/src/test/java/com/ctrip/framework/apollo/build/sql/converter/TestH2Function.java b/apollo-build-sql-converter/src/test/java/com/ctrip/framework/apollo/build/sql/converter/TestH2Function.java new file mode 100644 index 00000000000..e88984ad0f7 --- /dev/null +++ b/apollo-build-sql-converter/src/test/java/com/ctrip/framework/apollo/build/sql/converter/TestH2Function.java @@ -0,0 +1,24 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.build.sql.converter; + +public class TestH2Function { + + public static long unixTimestamp(java.sql.Timestamp timestamp) { + return timestamp.getTime() / 1000L; + } +} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010-after.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010-after.sql new file mode 100644 index 00000000000..000eaf8db3f --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010-after.sql @@ -0,0 +1,30 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.setupDatabase} + + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010-base.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010-base.sql new file mode 100644 index 00000000000..9448f7851be --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010-base.sql @@ -0,0 +1,414 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.setupDatabase} + +-- Dump of table app +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `App`; + +CREATE TABLE `App` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名', + `OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id', + `OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字', + `OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName', + `OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `AppId` (`AppId`(191)), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `Name` (`Name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用表'; + + + +-- Dump of table appnamespace +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AppNamespace`; + +CREATE TABLE `AppNamespace` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Name` varchar(32) NOT NULL DEFAULT '' COMMENT 'namespace名字,注意,需要全局唯一', + `AppId` varchar(32) NOT NULL DEFAULT '' COMMENT 'app id', + `Format` varchar(32) NOT NULL DEFAULT 'properties' COMMENT 'namespace的format类型', + `IsPublic` bit(1) NOT NULL DEFAULT b'0' COMMENT 'namespace是否为公共', + `Comment` varchar(64) NOT NULL DEFAULT '' COMMENT '注释', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT '' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `Name_AppId` (`Name`,`AppId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用namespace定义'; + + + +-- Dump of table audit +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Audit`; + +CREATE TABLE `Audit` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `EntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名', + `EntityId` int(10) unsigned DEFAULT NULL COMMENT '记录ID', + `OpName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型', + `Comment` varchar(500) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='日志审计表'; + + + +-- Dump of table cluster +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Cluster`; + +CREATE TABLE `Cluster` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Name` varchar(32) NOT NULL DEFAULT '' COMMENT '集群名字', + `AppId` varchar(32) NOT NULL DEFAULT '' COMMENT 'App id', + `ParentClusterId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父cluster', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT '' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_AppId_Name` (`AppId`,`Name`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='集群'; + + + +-- Dump of table commit +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Commit`; + +CREATE TABLE `Commit` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `ChangeSets` longtext NOT NULL COMMENT '修改变更集', + `AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'namespaceName', + `Comment` varchar(500) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `AppId` (`AppId`(191)), + KEY `ClusterName` (`ClusterName`(191)), + KEY `NamespaceName` (`NamespaceName`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='commit 历史表'; + +-- Dump of table grayreleaserule +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `GrayReleaseRule`; + +CREATE TABLE `GrayReleaseRule` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `AppId` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Cluster Name', + `NamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Namespace Name', + `BranchName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'branch name', + `Rules` varchar(16000) DEFAULT '[]' COMMENT '灰度规则', + `ReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '灰度对应的release', + `BranchStatus` tinyint(2) DEFAULT '1' COMMENT '灰度分支状态: 0:删除分支,1:正在使用的规则 2:全量发布', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Namespace` (`AppId`,`ClusterName`,`NamespaceName`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='灰度规则表'; + + +-- Dump of table instance +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Instance`; + +CREATE TABLE `Instance` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `AppId` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `DataCenter` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'Data Center Name', + `Ip` varchar(32) NOT NULL DEFAULT '' COMMENT 'instance ip', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `IX_UNIQUE_KEY` (`AppId`,`ClusterName`,`Ip`,`DataCenter`), + KEY `IX_IP` (`Ip`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='使用配置的应用实例'; + + + +-- Dump of table instanceconfig +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `InstanceConfig`; + +CREATE TABLE `InstanceConfig` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `InstanceId` int(11) unsigned DEFAULT NULL COMMENT 'Instance Id', + `ConfigAppId` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Config App Id', + `ConfigClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Config Cluster Name', + `ConfigNamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Config Namespace Name', + `ReleaseKey` varchar(64) NOT NULL DEFAULT '' COMMENT '发布的Key', + `ReleaseDeliveryTime` timestamp NULL DEFAULT NULL COMMENT '配置获取时间', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `IX_UNIQUE_KEY` (`InstanceId`,`ConfigAppId`,`ConfigNamespaceName`), + KEY `IX_ReleaseKey` (`ReleaseKey`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Valid_Namespace` (`ConfigAppId`,`ConfigClusterName`,`ConfigNamespaceName`,`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用实例的配置信息'; + + + +-- Dump of table item +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Item`; + +CREATE TABLE `Item` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `NamespaceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '集群NamespaceId', + `Key` varchar(128) NOT NULL DEFAULT 'default' COMMENT '配置项Key', + `Value` longtext NOT NULL COMMENT '配置项值', + `Comment` varchar(1024) DEFAULT '' COMMENT '注释', + `LineNum` int(10) unsigned DEFAULT '0' COMMENT '行号', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_GroupId` (`NamespaceId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置项目'; + + + +-- Dump of table namespace +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Namespace`; + +CREATE TABLE `Namespace` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'Cluster Name', + `NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'Namespace Name', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `AppId_ClusterName_NamespaceName` (`AppId`(191),`ClusterName`(191),`NamespaceName`(191)), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='命名空间'; + + + +-- Dump of table namespacelock +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `NamespaceLock`; + +CREATE TABLE `NamespaceLock` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id', + `NamespaceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '集群NamespaceId', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT 'default' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + `IsDeleted` bit(1) DEFAULT b'0' COMMENT '软删除', + PRIMARY KEY (`Id`), + UNIQUE KEY `IX_NamespaceId` (`NamespaceId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='namespace的编辑锁'; + + + +-- Dump of table privilege +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Privilege`; + +CREATE TABLE `Privilege` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'Name', + `PrivilType` varchar(50) NOT NULL DEFAULT 'default' COMMENT 'PrivilType', + `NamespaceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'NamespaceId', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`id`), + KEY `Name_PrivilType_NamespaceId` (`Name`(191),`PrivilType`,`NamespaceId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限'; + + + +-- Dump of table release +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Release`; + +CREATE TABLE `Release` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `ReleaseKey` varchar(64) NOT NULL DEFAULT '' COMMENT '发布的Key', + `Name` varchar(64) NOT NULL DEFAULT 'default' COMMENT '发布名字', + `Comment` varchar(256) DEFAULT NULL COMMENT '发布说明', + `AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'namespaceName', + `Configurations` longtext NOT NULL COMMENT '发布配置', + `IsAbandoned` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否废弃', + `Status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '发布的状态,0:废弃 1:正常 2:灰度', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `AppId_ClusterName_GroupName` (`AppId`(191),`ClusterName`(191),`NamespaceName`(191)), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_ReleaseKey` (`ReleaseKey`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布'; + + +-- Dump of table releasehistory +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ReleaseHistory`; + +CREATE TABLE `ReleaseHistory` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `AppId` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `NamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'namespaceName', + `BranchName` varchar(32) NOT NULL DEFAULT 'default' COMMENT '发布分支名', + `ReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '关联的Release Id', + `PreviousReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '前一次发布的ReleaseId', + `Operation` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '发布类型,0: 普通发布,1: 回滚,2: 灰度发布,3: 灰度规则更新,4: 灰度合并回主分支发布,5: 主分支发布灰度自动发布,6: 主分支回滚灰度自动发布,7: 放弃灰度', + `OperationContext` longtext NOT NULL COMMENT '发布上下文信息', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_Namespace` (`AppId`,`ClusterName`,`NamespaceName`,`BranchName`), + KEY `IX_ReleaseId` (`ReleaseId`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布历史'; + + +-- Dump of table releasemessage +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ReleaseMessage`; + +CREATE TABLE `ReleaseMessage` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Message` varchar(1024) NOT NULL DEFAULT '' COMMENT '发布的消息内容', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Message` (`Message`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布消息'; + + + +-- Dump of table serverconfig +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ServerConfig`; + +CREATE TABLE `ServerConfig` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Key` varchar(64) NOT NULL DEFAULT 'default' COMMENT '配置项Key', + `Cluster` varchar(32) NOT NULL DEFAULT 'default' COMMENT '配置对应的集群,default为不针对特定的集群', + `Value` varchar(2048) NOT NULL DEFAULT 'default' COMMENT '配置项值', + `Comment` varchar(1024) DEFAULT '' COMMENT '注释', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_Key` (`Key`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置服务自身配置'; + +-- Config +-- ------------------------------------------------------------ +INSERT INTO `ServerConfig` (`Key`, `Cluster`, `Value`, `Comment`) +VALUES + ('eureka.service.url', 'default', 'http://localhost:8080/eureka/', 'Eureka服务Url'), + ('namespace.lock.switch', 'default', 'false', '一次发布只能有一个人修改开关'), + ('item.value.length.limit', 'default', '20000', 'item value最大长度限制'), + ('appnamespace.private.enable', 'default', 'false', '是否开启private namespace'), + ('item.key.length.limit', 'default', '128', 'item key 最大长度限制'); + +-- ${gists.autoGeneratedDeclaration} + +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010-before.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010-before.sql new file mode 100644 index 00000000000..000eaf8db3f --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010-before.sql @@ -0,0 +1,30 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.setupDatabase} + + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010.sql new file mode 100644 index 00000000000..88193c89edf --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloconfigdb-v000-v010.sql @@ -0,0 +1,22 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v0.0.0 to v0.1.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloportaldb-v000-v010-base.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloportaldb-v000-v010-base.sql new file mode 100644 index 00000000000..bc3f0ec2021 --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloportaldb-v000-v010-base.sql @@ -0,0 +1,308 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.setupDatabase} + +-- Dump of table app +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `App`; + +CREATE TABLE `App` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名', + `OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id', + `OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字', + `OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName', + `OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `AppId` (`AppId`(191)), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `Name` (`Name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用表'; + + + +-- Dump of table appnamespace +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AppNamespace`; + +CREATE TABLE `AppNamespace` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Name` varchar(32) NOT NULL DEFAULT '' COMMENT 'namespace名字,注意,需要全局唯一', + `AppId` varchar(32) NOT NULL DEFAULT '' COMMENT 'app id', + `Format` varchar(32) NOT NULL DEFAULT 'properties' COMMENT 'namespace的format类型', + `IsPublic` bit(1) NOT NULL DEFAULT b'0' COMMENT 'namespace是否为公共', + `Comment` varchar(64) NOT NULL DEFAULT '' COMMENT '注释', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT '' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `Name_AppId` (`Name`,`AppId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用namespace定义'; + + + +-- Dump of table consumer +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Consumer`; + +CREATE TABLE `Consumer` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名', + `OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id', + `OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字', + `OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName', + `OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `AppId` (`AppId`(191)), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放API消费者'; + + + +-- Dump of table consumeraudit +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ConsumerAudit`; + +CREATE TABLE `ConsumerAudit` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'Consumer Id', + `Uri` varchar(1024) NOT NULL DEFAULT '' COMMENT '访问的Uri', + `Method` varchar(16) NOT NULL DEFAULT '' COMMENT '访问的Method', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_ConsumerId` (`ConsumerId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer审计表'; + + + +-- Dump of table consumerrole +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ConsumerRole`; + +CREATE TABLE `ConsumerRole` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'Consumer Id', + `RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) DEFAULT '' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_RoleId` (`RoleId`), + KEY `IX_ConsumerId_RoleId` (`ConsumerId`,`RoleId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer和role的绑定表'; + + + +-- Dump of table consumertoken +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ConsumerToken`; + +CREATE TABLE `ConsumerToken` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'ConsumerId', + `Token` varchar(128) NOT NULL DEFAULT '' COMMENT 'token', + `Expires` datetime NOT NULL DEFAULT '2099-01-01 00:00:00' COMMENT 'token失效时间', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `IX_Token` (`Token`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer token表'; + +-- Dump of table favorite +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Favorite`; + +CREATE TABLE `Favorite` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `UserId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '收藏的用户', + `AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Position` int(32) NOT NULL DEFAULT '10000' COMMENT '收藏顺序', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `AppId` (`AppId`(191)), + KEY `IX_UserId` (`UserId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COMMENT='应用收藏表'; + +-- Dump of table permission +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Permission`; + +CREATE TABLE `Permission` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `PermissionType` varchar(32) NOT NULL DEFAULT '' COMMENT '权限类型', + `TargetId` varchar(256) NOT NULL DEFAULT '' COMMENT '权限对象类型', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT '' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_TargetId_PermissionType` (`TargetId`(191),`PermissionType`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='permission表'; + + + +-- Dump of table role +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Role`; + +CREATE TABLE `Role` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `RoleName` varchar(256) NOT NULL DEFAULT '' COMMENT 'Role name', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_RoleName` (`RoleName`(191)), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表'; + + + +-- Dump of table rolepermission +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `RolePermission`; + +CREATE TABLE `RolePermission` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id', + `PermissionId` int(10) unsigned DEFAULT NULL COMMENT 'Permission Id', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) DEFAULT '' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_RoleId` (`RoleId`), + KEY `IX_PermissionId` (`PermissionId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色和权限的绑定表'; + + + +-- Dump of table serverconfig +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ServerConfig`; + +CREATE TABLE `ServerConfig` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Key` varchar(64) NOT NULL DEFAULT 'default' COMMENT '配置项Key', + `Value` varchar(2048) NOT NULL DEFAULT 'default' COMMENT '配置项值', + `Comment` varchar(1024) DEFAULT '' COMMENT '注释', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_Key` (`Key`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置服务自身配置'; + + + +-- Dump of table userrole +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `UserRole`; + +CREATE TABLE `UserRole` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `UserId` varchar(128) DEFAULT '' COMMENT '用户身份标识', + `RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) DEFAULT '' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_RoleId` (`RoleId`), + KEY `IX_UserId_RoleId` (`UserId`,`RoleId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户和role的绑定表'; + +-- Config +-- ------------------------------------------------------------ +INSERT INTO `ServerConfig` (`Key`, `Value`, `Comment`) +VALUES + ('apollo.portal.envs', 'dev', '可支持的环境列表'), + ('organizations', '[{\"orgId\":\"TEST1\",\"orgName\":\"样例部门1\"},{\"orgId\":\"TEST2\",\"orgName\":\"样例部门2\"}]', '部门列表'), + ('superAdmin', 'apollo', 'Portal超级管理员'), + ('api.readTimeout', '10000', 'http接口read timeout'), + ('consumer.token.salt', 'someSalt', 'consumer token salt'); + +-- ${gists.autoGeneratedDeclaration} + +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloportaldb-v000-v010.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloportaldb-v000-v010.sql new file mode 100644 index 00000000000..df9f7b68deb --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v000-v010/apolloportaldb-v000-v010.sql @@ -0,0 +1,22 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v0.4.0 to v0.5.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v040-v050/apolloconfigdb-v040-v050.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v040-v050/apolloconfigdb-v040-v050.sql new file mode 100644 index 00000000000..ed7992de6f9 --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v040-v050/apolloconfigdb-v040-v050.sql @@ -0,0 +1,30 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v0.4.0 to v0.5.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +DROP TABLE `Privilege`; +ALTER TABLE `Release` DROP `Status`; +ALTER TABLE `Namespace` ADD KEY `IX_NamespaceName` (`NamespaceName`(191)); +ALTER TABLE `Cluster` ADD KEY `IX_ParentClusterId` (`ParentClusterId`); +ALTER TABLE `AppNamespace` ADD KEY `IX_AppId` (`AppId`); +ALTER TABLE `App` DROP INDEX `Name`; +ALTER TABLE `App` ADD KEY `Name` (`Name`); + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v040-v050/apolloportaldb-v040-v050.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v040-v050/apolloportaldb-v040-v050.sql new file mode 100644 index 00000000000..a2a6108bd4d --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v040-v050/apolloportaldb-v040-v050.sql @@ -0,0 +1,26 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v0.4.0 to v0.5.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +ALTER TABLE `AppNamespace` ADD KEY `IX_AppId` (`AppId`); +ALTER TABLE `App` DROP INDEX `Name`; +ALTER TABLE `App` ADD KEY `Name` (`Name`); + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v060-v062/apolloconfigdb-v060-v062.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v060-v062/apolloconfigdb-v060-v062.sql new file mode 100644 index 00000000000..ce5d7ab8b4b --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v060-v062/apolloconfigdb-v060-v062.sql @@ -0,0 +1,25 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v0.6.0 to v0.6.2 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +ALTER TABLE `App` DROP INDEX `Name`; +CREATE INDEX `IX_NAME` ON App (`Name`(191)); + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v060-v062/apolloportaldb-v060-v062.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v060-v062/apolloportaldb-v060-v062.sql new file mode 100644 index 00000000000..5d4a1dc7091 --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v060-v062/apolloportaldb-v060-v062.sql @@ -0,0 +1,25 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v0.6.0 to v0.6.2 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +ALTER TABLE `App` DROP INDEX `Name`; +CREATE INDEX `IX_NAME` ON App (`Name`(191)); + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v080-v090/apolloportaldb-v080-v090.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v080-v090/apolloportaldb-v080-v090.sql new file mode 100644 index 00000000000..15c56f0d085 --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v080-v090/apolloportaldb-v080-v090.sql @@ -0,0 +1,44 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v0.8.0 to v0.9.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +CREATE TABLE `Users` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Username` varchar(64) NOT NULL DEFAULT 'default' COMMENT '用户名', + `Password` varchar(64) NOT NULL DEFAULT 'default' COMMENT '密码', + `Email` varchar(64) NOT NULL DEFAULT 'default' COMMENT '邮箱地址', + `Enabled` tinyint(4) DEFAULT NULL COMMENT '是否有效', + PRIMARY KEY (`Id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; + +CREATE TABLE `Authorities` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Username` varchar(50) NOT NULL, + `Authority` varchar(50) NOT NULL, + PRIMARY KEY (`Id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT INTO `Users` (`Username`, `Password`, `Email`, `Enabled`) +VALUES + ('apollo', '$2a$10$7r20uS.BQ9uBpf3Baj3uQOZvMVvB1RN3PYoKE94gtz2.WAOuiiwXS', 'apollo@acme.com', 1); + +INSERT INTO `Authorities` (`Username`, `Authority`) VALUES ('apollo', 'ROLE_user'); + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v151-v160/apolloconfigdb-v151-v160.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v151-v160/apolloconfigdb-v151-v160.sql new file mode 100644 index 00000000000..315920637ef --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v151-v160/apolloconfigdb-v151-v160.sql @@ -0,0 +1,37 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v1.5.1 to v1.6.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +CREATE TABLE `AccessKey` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `AppId` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Secret` varchar(128) NOT NULL DEFAULT '' COMMENT 'Secret', + `IsEnabled` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: enabled, 0: disabled', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DataChange_CreatedBy` varchar(32) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(32) NOT NULL DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `AppId` (`AppId`(191)), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问密钥'; + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v170-v180/apolloconfigdb-v170-v180.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v170-v180/apolloconfigdb-v170-v180.sql new file mode 100644 index 00000000000..cc085536d50 --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v170-v180/apolloconfigdb-v170-v180.sql @@ -0,0 +1,29 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v1.7.0 to v1.8.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +alter table `AppNamespace` change AppId AppId varchar(64) NOT NULL DEFAULT 'default' COMMENT 'app id'; +alter table `Cluster` change AppId AppId varchar(64) NOT NULL DEFAULT 'default' COMMENT 'app id'; +alter table `GrayReleaseRule` change AppId AppId varchar(64) NOT NULL DEFAULT 'default' COMMENT 'app id'; +alter table `Instance` change AppId AppId varchar(64) NOT NULL DEFAULT 'default' COMMENT 'app id'; +alter table `InstanceConfig` change ConfigAppId ConfigAppId varchar(64) NOT NULL DEFAULT 'default' COMMENT 'Config App Id'; +alter table `ReleaseHistory` change AppId AppId varchar(64) NOT NULL DEFAULT 'default' COMMENT 'app id'; + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v170-v180/apolloportaldb-v170-v180.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v170-v180/apolloportaldb-v170-v180.sql new file mode 100644 index 00000000000..ffa769906b3 --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v170-v180/apolloportaldb-v170-v180.sql @@ -0,0 +1,24 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v1.7.0 to v1.8.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +alter table `AppNamespace` change AppId AppId varchar(64) NOT NULL DEFAULT 'default' COMMENT 'app id'; + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v180-v190/apolloconfigdb-v180-v190.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v180-v190/apolloconfigdb-v180-v190.sql new file mode 100644 index 00000000000..ec6abbc0220 --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v180-v190/apolloconfigdb-v180-v190.sql @@ -0,0 +1,74 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v1.8.0 to v1.9.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +ALTER TABLE `App` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `AppNamespace` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `Audit` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `Cluster` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `Commit` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `GrayReleaseRule` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `Item` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `Namespace` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `NamespaceLock` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `Release` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `ReleaseHistory` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `ServerConfig` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `AccessKey` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v180-v190/apolloportaldb-v180-v190.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v180-v190/apolloportaldb-v180-v190.sql new file mode 100644 index 00000000000..95dd8a21f9e --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v180-v190/apolloportaldb-v180-v190.sql @@ -0,0 +1,102 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v1.8.0 to v1.9.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +ALTER TABLE `App` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `AppNamespace` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `Consumer` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `ConsumerRole` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `ConsumerToken` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `Favorite` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `Permission` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `Role` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `RolePermission` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `ServerConfig` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `UserRole` + MODIFY COLUMN `DataChange_CreatedBy` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + MODIFY COLUMN `DataChange_LastModifiedBy` VARCHAR(64) DEFAULT '' COMMENT '最后修改人邮箱前缀'; + +ALTER TABLE `Users` + MODIFY COLUMN `Username` varchar(64) NOT NULL DEFAULT 'default' COMMENT '用户登录账户', + ADD COLUMN `UserDisplayName` varchar(512) NOT NULL DEFAULT 'default' COMMENT '用户名称' AFTER `Password`; +UPDATE `Users` SET `UserDisplayName`=`Username` WHERE `UserDisplayName` = 'default'; + +ALTER TABLE `Users` + MODIFY COLUMN `Password` varchar(512) NOT NULL DEFAULT 'default' COMMENT '密码'; +UPDATE `Users` SET `Password` = REPLACE(`Password`, '{nonsensical}', '{placeholder}') WHERE `Password` LIKE '{nonsensical}%'; +-- note: add the {bcrypt} prefix for `Users`.`Password` is not mandatory, and it may break the old version of apollo-portal while upgrading. +-- 注意: 向 `Users`.`Password` 添加 {bcrypt} 是非必须操作, 并且这个操作会导致升级 apollo-portal 集群的过程中旧版的 apollo-portal 无法使用. +-- UPDATE `Users` SET `Password` = CONCAT('{bcrypt}', `Password`) WHERE `Password` NOT LIKE '{%}%'; + +-- spring session (https://github.com/spring-projects/spring-session/blob/faee8f1bdb8822a5653a81eba838dddf224d92d6/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-mysql.sql) +CREATE TABLE SPRING_SESSION ( + PRIMARY_ID CHAR(36) NOT NULL, + SESSION_ID CHAR(36) NOT NULL, + CREATION_TIME BIGINT NOT NULL, + LAST_ACCESS_TIME BIGINT NOT NULL, + MAX_INACTIVE_INTERVAL INT NOT NULL, + EXPIRY_TIME BIGINT NOT NULL, + PRINCIPAL_NAME VARCHAR(100), + CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; + +CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID); +CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME); +CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME); + +CREATE TABLE SPRING_SESSION_ATTRIBUTES ( + SESSION_PRIMARY_ID CHAR(36) NOT NULL, + ATTRIBUTE_NAME VARCHAR(200) NOT NULL, + ATTRIBUTE_BYTES BLOB NOT NULL, + CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME), + CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloconfigdb-v190-v200-after.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloconfigdb-v190-v200-after.sql new file mode 100644 index 00000000000..9ae64ef85bb --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloconfigdb-v190-v200-after.sql @@ -0,0 +1,88 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v1.9.0 to v2.0.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +-- Begin:Create indexes to solve the problem of updating large tables +ALTER TABLE `Commit` ADD INDEX `idx_IsDeleted_DeletedAt` (`IsDeleted`, `DeletedAt`); +ALTER TABLE `Release` ADD INDEX `idx_IsDeleted_DeletedAt` (`IsDeleted`, `DeletedAt`); + +-- the follow DML won't change the `DataChange_LastTime` field +UPDATE `AccessKey` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `App` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `AppNamespace` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `Audit` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `Cluster` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `Commit` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `GrayReleaseRule` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `Item` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `Namespace` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `NamespaceLock` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `Release` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `ReleaseHistory` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `ServerConfig` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; + +-- add UNIQUE CONSTRAINT INDEX for each table +ALTER TABLE `AccessKey` + ADD UNIQUE INDEX `UK_AppId_Secret_DeletedAt` (`AppId`,`Secret`,`DeletedAt`), + DROP INDEX `AppId`; + +ALTER TABLE `App` + ADD UNIQUE INDEX `UK_AppId_DeletedAt` (`AppId`,`DeletedAt`), + DROP INDEX `AppId`; + +ALTER TABLE `AppNamespace` + ADD UNIQUE INDEX `UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`), + DROP INDEX `IX_AppId`; + +-- Ignore TABLE `Audit` + +ALTER TABLE `Cluster` + ADD UNIQUE INDEX `UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`), + DROP INDEX `IX_AppId_Name`; + +-- Ignore TABLE `Commit` + +-- Ignore TABLE `GrayReleaseRule`, add unique index in future + +-- Ignore TABLE `Item`, add unique index in future + +ALTER TABLE `Namespace` + ADD UNIQUE INDEX `UK_AppId_ClusterName_NamespaceName_DeletedAt` (`AppId`(191),`ClusterName`(191),`NamespaceName`(191),`DeletedAt`), + DROP INDEX `AppId_ClusterName_NamespaceName`; + +ALTER TABLE `NamespaceLock` + ADD UNIQUE INDEX `UK_NamespaceId_DeletedAt` (`NamespaceId`,`DeletedAt`), + DROP INDEX `IX_NamespaceId`; + +ALTER TABLE `Release` + ADD UNIQUE INDEX `UK_ReleaseKey_DeletedAt` (`ReleaseKey`,`DeletedAt`), + DROP INDEX `IX_ReleaseKey`; + +-- Ignore TABLE `ReleaseHistory` + +ALTER TABLE `ServerConfig` + ADD UNIQUE INDEX `UK_Key_Cluster_DeletedAt` (`Key`,`Cluster`,`DeletedAt`), + DROP INDEX `IX_Key`; + +-- End:Delete temporarily created indexes +ALTER TABLE `Commit` DROP INDEX `idx_IsDeleted_DeletedAt`; +ALTER TABLE `Release` DROP INDEX `idx_IsDeleted_DeletedAt`; + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloconfigdb-v190-v200.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloconfigdb-v190-v200.sql new file mode 100644 index 00000000000..323afe7486a --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloconfigdb-v190-v200.sql @@ -0,0 +1,62 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v1.9.0 to v2.0.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +ALTER TABLE `App` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `AppNamespace` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `Audit` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `Cluster` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `Commit` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `GrayReleaseRule` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `Item` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`, + ADD INDEX IX_key (`Key`); + +ALTER TABLE `Namespace` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `NamespaceLock` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `Release` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `ReleaseHistory` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `ServerConfig` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `AccessKey` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloportaldb-v190-v200-after.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloportaldb-v190-v200-after.sql new file mode 100644 index 00000000000..7b05e5e115b --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloportaldb-v190-v200-after.sql @@ -0,0 +1,83 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v1.9.0 to v2.0.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +-- the follow DML won't change the `DataChange_LastTime` field +UPDATE `App` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `AppNamespace` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `Consumer` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `ConsumerRole` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `ConsumerToken` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `Favorite` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `Permission` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `Role` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `RolePermission` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `ServerConfig` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; +UPDATE `UserRole` SET `DeletedAt` = -Id, `DataChange_LastTime` = `DataChange_LastTime` WHERE `IsDeleted` = 1 and `DeletedAt` = 0; + +-- add UNIQUE CONSTRAINT INDEX for each table +ALTER TABLE `App` + ADD UNIQUE INDEX `UK_AppId_DeletedAt` (`AppId`,`DeletedAt`), + DROP INDEX `AppId`; + +ALTER TABLE `AppNamespace` + ADD UNIQUE INDEX `UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`), + DROP INDEX `IX_AppId`; + +ALTER TABLE `Consumer` + ADD UNIQUE INDEX `UK_AppId_DeletedAt` (`AppId`,`DeletedAt`), + DROP INDEX `AppId`; + +ALTER TABLE `ConsumerRole` + ADD UNIQUE INDEX `UK_ConsumerId_RoleId_DeletedAt` (`ConsumerId`,`RoleId`,`DeletedAt`), + DROP INDEX `IX_ConsumerId_RoleId`; + +ALTER TABLE `ConsumerToken` + ADD UNIQUE INDEX `UK_Token_DeletedAt` (`Token`,`DeletedAt`), + DROP INDEX `IX_Token`; + +ALTER TABLE `Favorite` + ADD UNIQUE INDEX `UK_UserId_AppId_DeletedAt` (`UserId`,`AppId`,`DeletedAt`), + DROP INDEX `IX_UserId`; + +ALTER TABLE `Permission` + ADD UNIQUE INDEX `UK_TargetId_PermissionType_DeletedAt` (`TargetId`,`PermissionType`,`DeletedAt`), + DROP INDEX `IX_TargetId_PermissionType`; + +ALTER TABLE `Role` + ADD UNIQUE INDEX `UK_RoleName_DeletedAt` (`RoleName`,`DeletedAt`), + DROP INDEX `IX_RoleName`; + +ALTER TABLE `RolePermission` + ADD UNIQUE INDEX `UK_RoleId_PermissionId_DeletedAt` (`RoleId`,`PermissionId`,`DeletedAt`), + DROP INDEX `IX_RoleId`; + +ALTER TABLE `ServerConfig` + ADD UNIQUE INDEX `UK_Key_DeletedAt` (`Key`,`DeletedAt`), + DROP INDEX `IX_Key`; + +ALTER TABLE `UserRole` + ADD UNIQUE INDEX `UK_UserId_RoleId_DeletedAt` (`UserId`,`RoleId`,`DeletedAt`), + DROP INDEX `IX_UserId_RoleId`; + +ALTER TABLE `Users` + ADD UNIQUE INDEX `UK_Username` (`Username`); + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloportaldb-v190-v200.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloportaldb-v190-v200.sql new file mode 100644 index 00000000000..d521cf25a53 --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v190-v200/apolloportaldb-v190-v200.sql @@ -0,0 +1,55 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v1.9.0 to v2.0.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +ALTER TABLE `App` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `AppNamespace` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `Consumer` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `ConsumerRole` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `ConsumerToken` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `Favorite` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `Permission` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `Role` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `RolePermission` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `ServerConfig` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +ALTER TABLE `UserRole` + ADD COLUMN `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds' AFTER `IsDeleted`; + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v200-v210/apolloconfigdb-v200-v210.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v200-v210/apolloconfigdb-v200-v210.sql new file mode 100644 index 00000000000..47bfa07ce82 --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v200-v210/apolloconfigdb-v200-v210.sql @@ -0,0 +1,41 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v2.0.0 to v2.1.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +-- add INDEX for ReleaseHistory table +CREATE INDEX IX_PreviousReleaseId ON ReleaseHistory(PreviousReleaseId); + +ALTER TABLE `Item` + ADD COLUMN `Type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '配置项类型,0: String,1: Number,2: Boolean,3: JSON' AFTER `Key`; + +CREATE TABLE `ServiceRegistry` ( + `Id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ServiceName` VARCHAR(64) NOT NULL COMMENT '服务名', + `Uri` VARCHAR(64) NOT NULL COMMENT '服务地址', + `Cluster` VARCHAR(64) NOT NULL COMMENT '集群,可以用来标识apollo.cluster或者网络分区', + `Metadata` VARCHAR(1024) NOT NULL DEFAULT '{}' COMMENT '元数据,key value结构的json object,为了方面后面扩展功能而不需要修改表结构', + `DataChange_CreatedTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE INDEX `IX_UNIQUE_KEY` (`ServiceName`, `Uri`), + INDEX `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='注册中心'; + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v210-v220/apolloconfigdb-v210-v220.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v210-v220/apolloconfigdb-v210-v220.sql new file mode 100644 index 00000000000..78cc43b4cae --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v210-v220/apolloconfigdb-v210-v220.sql @@ -0,0 +1,97 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v2.1.0 to v2.2.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +ALTER TABLE `App` + MODIFY COLUMN `AppId` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT 'AppID'; + +ALTER TABLE `Commit` + MODIFY COLUMN `AppId` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT 'AppID'; + +ALTER TABLE `Namespace` + MODIFY COLUMN `AppId` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT 'AppID'; + +ALTER TABLE `Release` + MODIFY COLUMN `AppId` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT 'AppID'; + +ALTER TABLE `AccessKey` + MODIFY COLUMN `AppId` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT 'AppID'; + +ALTER TABLE `Commit` + DROP INDEX `AppId`, + ADD INDEX `AppId` (`AppId`); + +ALTER TABLE `Namespace` + DROP INDEX `UK_AppId_ClusterName_NamespaceName_DeletedAt`, + ADD UNIQUE INDEX `UK_AppId_ClusterName_NamespaceName_DeletedAt` (`AppId`,`ClusterName`(191),`NamespaceName`(191),`DeletedAt`); + +ALTER TABLE `Release` + DROP INDEX `AppId_ClusterName_GroupName`, + ADD INDEX `AppId_ClusterName_GroupName` (`AppId`,`ClusterName`(191),`NamespaceName`(191),`DeletedAt`); + +DROP TABLE IF EXISTS `AuditLog`; + +CREATE TABLE `AuditLog` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `TraceId` varchar(32) NOT NULL DEFAULT '' COMMENT '链路全局唯一ID', + `SpanId` varchar(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `ParentSpanId` varchar(32) DEFAULT NULL COMMENT '父跨度ID', + `FollowsFromSpanId` varchar(32) DEFAULT NULL COMMENT '上一个兄弟跨度ID', + `Operator` varchar(64) NOT NULL DEFAULT 'anonymous' COMMENT '操作人', + `OpType` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型', + `OpName` varchar(150) NOT NULL DEFAULT 'default' COMMENT '操作名称', + `Description` varchar(200) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_TraceId` (`TraceId`), + KEY `IX_OpName` (`OpName`), + KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `IX_Operator` (`Operator`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志表'; + + +DROP TABLE IF EXISTS `AuditLogDataInfluence`; + +CREATE TABLE `AuditLogDataInfluence` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `SpanId` char(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `InfluenceEntityId` varchar(50) NOT NULL DEFAULT '0' COMMENT '记录ID', + `InfluenceEntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名', + `FieldName` varchar(50) DEFAULT NULL COMMENT '字段名称', + `FieldOldValue` varchar(500) DEFAULT NULL COMMENT '字段旧值', + `FieldNewValue` varchar(500) DEFAULT NULL COMMENT '字段新值', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_SpanId` (`SpanId`), + KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `IX_EntityId` (`InfluenceEntityId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志数据变动表'; + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v210-v220/apolloportaldb-v210-v220.sql b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v210-v220/apolloportaldb-v210-v220.sql new file mode 100644 index 00000000000..0175694efe1 --- /dev/null +++ b/apollo-build-sql-converter/src/test/resources/META-INF/sql/h2-test/delta/v210-v220/apolloportaldb-v210-v220.sql @@ -0,0 +1,83 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v2.1.0 to v2.2.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + +ALTER TABLE `App` + MODIFY COLUMN `AppId` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT 'AppID'; + +ALTER TABLE `Consumer` + MODIFY COLUMN `AppId` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT 'AppID'; + +ALTER TABLE `Favorite` + MODIFY COLUMN `AppId` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT 'AppID'; + +ALTER TABLE `Favorite` + DROP INDEX `AppId`, + ADD INDEX `AppId` (`AppId`); + +DROP TABLE IF EXISTS `AuditLog`; + +CREATE TABLE `AuditLog` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `TraceId` varchar(32) NOT NULL DEFAULT '' COMMENT '链路全局唯一ID', + `SpanId` varchar(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `ParentSpanId` varchar(32) DEFAULT NULL COMMENT '父跨度ID', + `FollowsFromSpanId` varchar(32) DEFAULT NULL COMMENT '上一个兄弟跨度ID', + `Operator` varchar(64) NOT NULL DEFAULT 'anonymous' COMMENT '操作人', + `OpType` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型', + `OpName` varchar(150) NOT NULL DEFAULT 'default' COMMENT '操作名称', + `Description` varchar(200) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_TraceId` (`TraceId`), + KEY `IX_OpName` (`OpName`), + KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `IX_Operator` (`Operator`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志表'; + + +DROP TABLE IF EXISTS `AuditLogDataInfluence`; + +CREATE TABLE `AuditLogDataInfluence` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `SpanId` char(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `InfluenceEntityId` varchar(50) NOT NULL DEFAULT '0' COMMENT '记录ID', + `InfluenceEntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名', + `FieldName` varchar(50) DEFAULT NULL COMMENT '字段名称', + `FieldOldValue` varchar(500) DEFAULT NULL COMMENT '字段旧值', + `FieldNewValue` varchar(500) DEFAULT NULL COMMENT '字段新值', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_SpanId` (`SpanId`), + KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `IX_EntityId` (`InfluenceEntityId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志数据变动表'; + +-- ${gists.autoGeneratedDeclaration} diff --git a/apollo-common/src/main/java/com/ctrip/framework/apollo/common/datasource/ApolloDataSourceScriptDatabaseInitializer.java b/apollo-common/src/main/java/com/ctrip/framework/apollo/common/datasource/ApolloDataSourceScriptDatabaseInitializer.java new file mode 100644 index 00000000000..e6d3af94857 --- /dev/null +++ b/apollo-common/src/main/java/com/ctrip/framework/apollo/common/datasource/ApolloDataSourceScriptDatabaseInitializer.java @@ -0,0 +1,73 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.common.datasource; + +import java.util.List; +import javax.sql.DataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer; +import org.springframework.boot.sql.init.DatabaseInitializationMode; +import org.springframework.boot.sql.init.DatabaseInitializationSettings; +import org.springframework.jdbc.datasource.AbstractDriverBasedDataSource; +import org.springframework.jdbc.datasource.SimpleDriverDataSource; + +public class ApolloDataSourceScriptDatabaseInitializer extends + DataSourceScriptDatabaseInitializer { + + private static final Logger log = LoggerFactory.getLogger( + ApolloDataSourceScriptDatabaseInitializer.class); + + public ApolloDataSourceScriptDatabaseInitializer(DataSource dataSource, + DatabaseInitializationSettings settings) { + super(dataSource, settings); + if (this.isEnabled(settings)) { + log.info("Apollo DataSource Initialize is enabled"); + if (log.isDebugEnabled()) { + String jdbcUrl = this.getJdbcUrl(dataSource); + log.debug("Initialize jdbc url: {}", jdbcUrl); + List schemaLocations = settings.getSchemaLocations(); + if (!schemaLocations.isEmpty()) { + for (String schemaLocation : schemaLocations) { + log.debug("Initialize Schema Location: {}", schemaLocation); + } + } + } + } else { + log.info("Apollo DataSource Initialize is disabled"); + } + } + + private String getJdbcUrl(DataSource dataSource) { + if (dataSource instanceof AbstractDriverBasedDataSource) { + AbstractDriverBasedDataSource driverBasedDataSource = (AbstractDriverBasedDataSource) dataSource; + return driverBasedDataSource.getUrl(); + } + SimpleDriverDataSource simpleDriverDataSource = DataSourceBuilder.derivedFrom(dataSource) + .type(SimpleDriverDataSource.class) + .build(); + return simpleDriverDataSource.getUrl(); + } + + private boolean isEnabled(DatabaseInitializationSettings settings) { + if (settings.getMode() == DatabaseInitializationMode.NEVER) { + return false; + } + return settings.getMode() == DatabaseInitializationMode.ALWAYS || this.isEmbeddedDatabase(); + } +} diff --git a/apollo-common/src/main/java/com/ctrip/framework/apollo/common/datasource/ApolloDataSourceScriptDatabaseInitializerFactory.java b/apollo-common/src/main/java/com/ctrip/framework/apollo/common/datasource/ApolloDataSourceScriptDatabaseInitializerFactory.java new file mode 100644 index 00000000000..74106334df7 --- /dev/null +++ b/apollo-common/src/main/java/com/ctrip/framework/apollo/common/datasource/ApolloDataSourceScriptDatabaseInitializerFactory.java @@ -0,0 +1,174 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.common.datasource; + +import java.net.URL; +import java.security.CodeSource; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.sql.DataSource; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.boot.jdbc.DatabaseDriver; +import org.springframework.boot.jdbc.init.PlatformPlaceholderDatabaseDriverResolver; +import org.springframework.boot.sql.init.DatabaseInitializationSettings; +import org.springframework.jdbc.datasource.SimpleDriverDataSource; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +public class ApolloDataSourceScriptDatabaseInitializerFactory { + + public static ApolloDataSourceScriptDatabaseInitializer create(DataSource dataSource, + ApolloSqlInitializationProperties properties) { + DataSource determinedDataSource = determineDataSource(dataSource, properties); + DatabaseInitializationSettings settings = getSettings(dataSource, properties); + return new ApolloDataSourceScriptDatabaseInitializer(determinedDataSource, settings); + } + + private static DataSource determineDataSource(DataSource dataSource, + ApolloSqlInitializationProperties properties) { + + String username = properties.getUsername(); + String password = properties.getPassword(); + if (StringUtils.hasText(username) && StringUtils.hasText(password)) { + return DataSourceBuilder.derivedFrom(dataSource) + .username(username) + .password(password) + .type(SimpleDriverDataSource.class) + .build(); + } + return dataSource; + } + + private static DatabaseInitializationSettings getSettings(DataSource dataSource, + ApolloSqlInitializationProperties properties) { + + PlatformPlaceholderDatabaseDriverResolver platformResolver = new PlatformPlaceholderDatabaseDriverResolver().withDriverPlatform( + DatabaseDriver.MARIADB, "mysql"); + + List schemaLocations = resolveLocations(properties.getSchemaLocations(), + platformResolver, + dataSource, properties); + List dataLocations = resolveLocations(properties.getDataLocations(), platformResolver, + dataSource, properties); + + DatabaseInitializationSettings settings = new DatabaseInitializationSettings(); + settings.setSchemaLocations( + scriptLocations(schemaLocations, "schema", properties.getPlatform())); + settings.setDataLocations(scriptLocations(dataLocations, "data", properties.getPlatform())); + settings.setContinueOnError(properties.isContinueOnError()); + settings.setSeparator(properties.getSeparator()); + settings.setEncoding(properties.getEncoding()); + settings.setMode(properties.getMode()); + return settings; + } + + + private static List resolveLocations(Collection locations, + PlatformPlaceholderDatabaseDriverResolver platformResolver, DataSource dataSource, + ApolloSqlInitializationProperties properties) { + + if (CollectionUtils.isEmpty(locations)) { + return null; + } + + Collection convertedLocations = convertRepositoryLocations(locations, dataSource); + if (CollectionUtils.isEmpty(convertedLocations)) { + return null; + } + + String platform = properties.getPlatform(); + if (StringUtils.hasText(platform) && !"all".equals(platform)) { + return platformResolver.resolveAll(platform, convertedLocations.toArray(new String[0])); + } + return platformResolver.resolveAll(dataSource, convertedLocations.toArray(new String[0])); + } + + private static Collection convertRepositoryLocations(Collection locations, + DataSource dataSource) { + if (CollectionUtils.isEmpty(locations)) { + return null; + } + String repositoryDir = findRepositoryDirectory(); + String suffix = findSuffix(dataSource); + List convertedLocations = new ArrayList<>(locations.size()); + for (String location : locations) { + String convertedLocation = convertRepositoryLocation(location, repositoryDir, suffix); + if (StringUtils.hasText(convertedLocation)) { + convertedLocations.add(convertedLocation); + } + } + return convertedLocations; + } + + private static String findSuffix(DataSource dataSource) { + DatabaseDriver databaseDriver = DatabaseDriver.fromDataSource(dataSource); + if (DatabaseDriver.H2.equals(databaseDriver)) { + return "-default"; + } + if (DatabaseDriver.MYSQL.equals(databaseDriver)) { + return "-database-not-specified"; + } + return ""; + } + + private static String findRepositoryDirectory() { + CodeSource codeSource = ApolloDataSourceScriptDatabaseInitializer.class.getProtectionDomain() + .getCodeSource(); + URL location = codeSource != null ? codeSource.getLocation() : null; + if (location == null) { + return null; + } + if ("jar".equals(location.getProtocol())) { + // running with jar + return "classpath:META-INF/sql"; + } + if ("file".equals(location.getProtocol())) { + // running with ide + String locationText = location.toString(); + if (!locationText.endsWith("/apollo-common/target/classes/")) { + throw new IllegalStateException( + "can not determine repository directory from classpath: " + locationText); + } + return locationText.replace("/apollo-common/target/classes/", "/scripts/sql"); + } + return null; + } + + private static String convertRepositoryLocation(String location, String repositoryDir, + String suffix) { + if (!StringUtils.hasText(location)) { + return location; + } + if (!StringUtils.hasText(repositoryDir)) { + // repository dir not found + return null; + } + return location.replace("@@repository@@", repositoryDir).replace("@@suffix@@", suffix); + } + + private static List scriptLocations(List locations, String fallback, + String platform) { + if (locations != null) { + return locations; + } + List fallbackLocations = new ArrayList<>(); + fallbackLocations.add("optional:classpath*:" + fallback + "-" + platform + ".sql"); + fallbackLocations.add("optional:classpath*:" + fallback + ".sql"); + return fallbackLocations; + } +} diff --git a/apollo-common/src/main/java/com/ctrip/framework/apollo/common/datasource/ApolloSqlInitializationProperties.java b/apollo-common/src/main/java/com/ctrip/framework/apollo/common/datasource/ApolloSqlInitializationProperties.java new file mode 100644 index 00000000000..4ce80f4622b --- /dev/null +++ b/apollo-common/src/main/java/com/ctrip/framework/apollo/common/datasource/ApolloSqlInitializationProperties.java @@ -0,0 +1,142 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.common.datasource; + +import java.nio.charset.Charset; +import java.util.List; +import org.springframework.boot.sql.init.DatabaseInitializationMode; + +public class ApolloSqlInitializationProperties { + + /** + * Locations of the schema (DDL) scripts to apply to the database. + */ + private List schemaLocations; + + /** + * Locations of the data (DML) scripts to apply to the database. + */ + private List dataLocations; + + /** + * Platform to use in the default schema or data script locations, schema-${platform}.sql and + * data-${platform}.sql. + */ + private String platform = "all"; + + /** + * Username of the database to use when applying initialization scripts (if different). + */ + private String username; + + /** + * Password of the database to use when applying initialization scripts (if different). + */ + private String password; + + /** + * Whether initialization should continue when an error occurs. + */ + private boolean continueOnError = false; + + /** + * Statement separator in the schema and data scripts. + */ + private String separator = ";"; + + /** + * Encoding of the schema and data scripts. + */ + private Charset encoding; + + /** + * Mode to apply when determining whether initialization should be performed. + */ + private DatabaseInitializationMode mode = DatabaseInitializationMode.EMBEDDED; + + public List getSchemaLocations() { + return this.schemaLocations; + } + + public void setSchemaLocations(List schemaLocations) { + this.schemaLocations = schemaLocations; + } + + public List getDataLocations() { + return this.dataLocations; + } + + public void setDataLocations(List dataLocations) { + this.dataLocations = dataLocations; + } + + public String getPlatform() { + return this.platform; + } + + public void setPlatform(String platform) { + this.platform = platform; + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + public boolean isContinueOnError() { + return this.continueOnError; + } + + public void setContinueOnError(boolean continueOnError) { + this.continueOnError = continueOnError; + } + + public String getSeparator() { + return this.separator; + } + + public void setSeparator(String separator) { + this.separator = separator; + } + + public Charset getEncoding() { + return this.encoding; + } + + public void setEncoding(Charset encoding) { + this.encoding = encoding; + } + + public DatabaseInitializationMode getMode() { + return this.mode; + } + + public void setMode(DatabaseInitializationMode mode) { + this.mode = mode; + } +} diff --git a/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/ConfigServiceApplication.java b/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/ConfigServiceApplication.java index 2906fb37f3d..f7dcd44855c 100644 --- a/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/ConfigServiceApplication.java +++ b/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/ConfigServiceApplication.java @@ -22,6 +22,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @@ -35,7 +36,9 @@ */ @EnableAspectJAutoProxy -@EnableAutoConfiguration +@EnableAutoConfiguration(exclude = { + UserDetailsServiceAutoConfiguration.class, +}) @Configuration @EnableTransactionManagement @PropertySource(value = {"classpath:configservice.properties"}) diff --git a/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/ConfigServiceAssemblyConfiguration.java b/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/ConfigServiceAssemblyConfiguration.java new file mode 100644 index 00000000000..3321019eb1b --- /dev/null +++ b/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/ConfigServiceAssemblyConfiguration.java @@ -0,0 +1,44 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.configservice; + +import com.ctrip.framework.apollo.common.datasource.ApolloDataSourceScriptDatabaseInitializer; +import com.ctrip.framework.apollo.common.datasource.ApolloDataSourceScriptDatabaseInitializerFactory; +import com.ctrip.framework.apollo.common.datasource.ApolloSqlInitializationProperties; +import javax.sql.DataSource; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Profile("assembly") +@Configuration +public class ConfigServiceAssemblyConfiguration { + + @ConfigurationProperties(prefix = "spring.sql.config-init") + @Bean + public static ApolloSqlInitializationProperties apolloSqlInitializationProperties() { + return new ApolloSqlInitializationProperties(); + } + + @Bean + public static ApolloDataSourceScriptDatabaseInitializer apolloDataSourceScriptDatabaseInitializer( + DataSource dataSource, + ApolloSqlInitializationProperties properties) { + return ApolloDataSourceScriptDatabaseInitializerFactory.create(dataSource, properties); + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/PortalApplication.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/PortalApplication.java index 15cfb2e3594..9ce5f88f959 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/PortalApplication.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/PortalApplication.java @@ -24,10 +24,12 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableAspectJAutoProxy @Configuration +@PropertySource(value = {"classpath:portal.properties"}) @EnableAutoConfiguration(exclude = {LdapAutoConfiguration.class}) @EnableTransactionManagement @ComponentScan(basePackageClasses = {ApolloCommonConfig.class, diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/PortalAssemblyConfiguration.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/PortalAssemblyConfiguration.java new file mode 100644 index 00000000000..08b9909b09d --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/PortalAssemblyConfiguration.java @@ -0,0 +1,53 @@ +/* + * Copyright 2024 Apollo 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 + * + * http://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 com.ctrip.framework.apollo.portal; + +import com.ctrip.framework.apollo.common.datasource.ApolloDataSourceScriptDatabaseInitializer; +import com.ctrip.framework.apollo.common.datasource.ApolloDataSourceScriptDatabaseInitializerFactory; +import com.ctrip.framework.apollo.common.datasource.ApolloSqlInitializationProperties; +import javax.sql.DataSource; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Profile; + +@Profile("assembly") +@Configuration +public class PortalAssemblyConfiguration { + + @Primary + @ConfigurationProperties(prefix = "spring.portal-datasource") + @Bean + public static DataSourceProperties dataSourceProperties() { + return new DataSourceProperties(); + } + + @ConfigurationProperties(prefix = "spring.sql.portal-init") + @Bean + public static ApolloSqlInitializationProperties apolloSqlInitializationProperties() { + return new ApolloSqlInitializationProperties(); + } + + @Bean + public static ApolloDataSourceScriptDatabaseInitializer apolloDataSourceScriptDatabaseInitializer( + DataSource dataSource, + ApolloSqlInitializationProperties properties) { + return ApolloDataSourceScriptDatabaseInitializerFactory.create(dataSource, properties); + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/RetryableRestTemplate.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/RetryableRestTemplate.java index bc1d35bc4cf..8125632b803 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/RetryableRestTemplate.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/RetryableRestTemplate.java @@ -32,6 +32,7 @@ import java.net.SocketTimeoutException; import java.util.List; import java.util.Map; +import java.util.Objects; import javax.annotation.PostConstruct; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.HttpHostConnectException; @@ -300,7 +301,9 @@ private T doExecute(HttpMethod method, HttpHeaders extraHeaders, ServiceDTO } private String parseHost(ServiceDTO serviceAddress) { - return serviceAddress.getHomepageUrl() + "/"; + String homepageUrl = serviceAddress.getHomepageUrl(); + Objects.requireNonNull(homepageUrl, "homepageUrl"); + return homepageUrl.endsWith("/") ? homepageUrl : homepageUrl + "/"; } //post,delete,put请求在admin server处理超时情况下不重试 diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/PortalDBPropertySource.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/PortalDBPropertySource.java index 603eb38e745..b0779c04284 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/PortalDBPropertySource.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/PortalDBPropertySource.java @@ -62,7 +62,7 @@ public PortalDBPropertySource(final ServerConfigRepository serverConfigRepositor @PostConstruct public void runSqlScript() throws Exception { - if (env.acceptsProfiles(Profiles.of("h2"))) { + if (env.acceptsProfiles(Profiles.of("h2")) && !env.acceptsProfiles(Profiles.of("assembly"))) { Resource resource = new ClassPathResource("jpa/portaldb.init.h2.sql"); if (resource.exists()) { DatabasePopulatorUtils.execute(new ResourceDatabasePopulator(resource), dataSource); diff --git a/apollo-portal/src/main/resources/application.yml b/apollo-portal/src/main/resources/application.yml index 20f126490ac..9a9f17a56b7 100644 --- a/apollo-portal/src/main/resources/application.yml +++ b/apollo-portal/src/main/resources/application.yml @@ -14,8 +14,6 @@ # limitations under the License. # spring: - application: - name: apollo-portal profiles: active: ${apollo_profile} jpa: @@ -33,7 +31,6 @@ spring: max-file-size: 200MB # import data configs max-request-size: 200MB server: - port: 8070 compression: enabled: true tomcat: @@ -44,10 +41,6 @@ server: # prevent csrf same-site: Lax -logging: - file: - name: /opt/logs/apollo-portal.log - management: health: status: diff --git a/apollo-portal/src/main/resources/portal.properties b/apollo-portal/src/main/resources/portal.properties new file mode 100644 index 00000000000..9edd0ee3b16 --- /dev/null +++ b/apollo-portal/src/main/resources/portal.properties @@ -0,0 +1,20 @@ +# +# Copyright 2024 Apollo 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 +# +# http://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. +# +#Used for apollo-assembly +spring.application.name= apollo-portal +server.port= 8070 +logging.file.name= /opt/logs/apollo-portal.log +spring.jmx.default-domain = apollo-portal diff --git a/doc/images/local-development/ApolloApplication-Mysql-VM-Options.png b/doc/images/local-development/ApolloApplication-Mysql-VM-Options.png new file mode 100644 index 0000000000000000000000000000000000000000..051158ea44befe4ba6e98c2afb38873afa99bca5 GIT binary patch literal 182066 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

JXiH00a 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 literal 0 HcmV?d00001 diff --git a/doc/images/local-development/ApolloApplication-Overview.png b/doc/images/local-development/ApolloApplication-Overview.png new file mode 100644 index 0000000000000000000000000000000000000000..7015b164ccea632efa96e85f84c428c74f5cb183 GIT binary patch literal 90120 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≪=G>kuwh>Bl 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 literal 0 HcmV?d00001 diff --git a/doc/images/local-development/ApolloApplication-Run.png b/doc/images/local-development/ApolloApplication-Run.png new file mode 100644 index 0000000000000000000000000000000000000000..e32852ac56d2565f783f0be4882067f725d12643 GIT binary patch literal 15230 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 JN>KmL{{f4LTcH2| literal 0 HcmV?d00001 diff --git a/doc/images/local-development/ApolloApplication-VM-Options.png b/doc/images/local-development/ApolloApplication-VM-Options.png new file mode 100644 index 0000000000000000000000000000000000000000..73aba7fed56156b72f2d836b3c538cc8e33a54c3 GIT binary patch literal 89402 zcmeFYcT}27_cm;*>85Cm8ucW`hOxJ(QPb>Q0lR4IJ+a1uq^hx=s93N!6tF8AD^a7y z9AA4H%PjWPC4eL+7PI()uJ!2^8ySscMoOOJv^OS+1JofUT&AAi# z;wyDiZw7`3OMiV%y|+Dg_axKS8EE2TN_ut%?7khX=;N%H?_%BKPk>vkJhOE>-W&SSkmuv}rvHxz7fq^cg@!~1)jn&!| zW7b_zW5?F?8j%>JM^b$E@$55F_3UmV<-JSz{O5)5KJF5| zy@N>Z{=}Y*$;A2^5;2ja)}Aˢ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

^YM2!aCh%@08V!)Hx;Fpy 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 literal 0 HcmV?d00001 diff --git a/docs/en/contribution/apollo-development-guide.md b/docs/en/contribution/apollo-development-guide.md index c69b2d1a435..7ecff0111d6 100644 --- a/docs/en/contribution/apollo-development-guide.md +++ b/docs/en/contribution/apollo-development-guide.md @@ -9,7 +9,7 @@ This document describes how to compile and run Apollo locally using the IDE so t Apollo local development requires the following components. 1. Java: 1.8+ -2. MySQL: 5.6.5+ 3. +2. MySQL: 5.6.5+ (If you plan to use H2 in-memory database/H2 file database, then there is no need to prepare MySQL) 3. IDE: No special requirements MySQL is required to create Apollo database and import the base data. @@ -24,13 +24,13 @@ Please refer to [Apollo Configuration Center Design](en/design/apollo-design) fo # II. Local startup -## 2.1 Apollo Config Service and Apollo Admin Service +## 2.1 Apollo Assembly -When we develop locally, we usually start both `apollo-config service` and `apollo-adminservice` in the IDE. +When we develop locally, we usually start `apollo-assembly` in the IDE. -The following is an example of how to start `apollo-configService` and `apollo-adminservice` locally with Intellij Community 2016.2 version. +The following is an example of how to start `apollo-assembly` locally with Intellij Community 2016.2 version. -![ConfigAdminApplication-Overview](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ConfigAdminApplication-Overview.png) +![ApolloApplication-Overview](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ApolloApplication-Overview.png) ### 2.1.1 Create a new running configuration @@ -40,34 +40,50 @@ The following is an example of how to start `apollo-configService` and `apollo-a `com.ctrip.framework.apollo.assembly.ApolloApplication` -> Note: If you want to start `apollo-configservice` and `apollo-adminservice` independently, you can replace Main Class with -> ConfigServiceApplication` and +> Note: If you want to start `apollo-portal`, `apollo-configservice` and `apollo-adminservice` independently, you can replace Main Class with +> `com.ctrip.framework.apollo.portal.PortalApplication` +> `com.ctrip.framework.apollo.configservice.ConfigServiceApplication` > `com.ctrip.framework.apollo.adminservice.AdminServiceApplication` ### 2.1.3 VM options configuration -![ConfigAdminApplication-VM-Options](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ConfigAdminApplication-VM-Options.png) - - -Dapollo_profile=github - -Dspring.datasource.url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8 - -Dspring.datasource.username=root - -Dspring.datasource.password= +![ApolloApplication-VM-Options](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ApolloApplication-VM-Options.png) +``` +-Dapollo_profile=github,auth ->Note 1: replace spring.datasource related configuration with your own database connection information, note that the database is `ApolloConfigDB`. -> ->Note 2: The default log output of the program is /opt/logs/100003171/apollo-assembly.log, if you need to modify the log file path, you can add the `logging.file.name` parameter, as follows. +``` +>Note 1: apollo_profile is specified here as `github` and `auth`, where `github` is a profile required by Apollo for database configuration, and `auth` is added from 0.9.0 to support simple authentication using Spring Security provided by apollo. For more information you can refer to [Portal-implement-user-login-function](en/development/portal-how-to-implement-user-login-function) > ->-Dlogging.file.name=/your-path/apollo-assembly.log +>Note 2: If you plan to use a MySQL database, you need to add `spring.config-datasource.*` related configuration, +> the your-mysql-server:3306 needs to be replaced with the actual mysql server address and port, +> ApolloConfigDB and ApolloPortalDB needs to be replaced with the actual database name, +> apollo-username and apollo-password need to be replaced with the actual username and password + +![ApolloApplication-Mysql-VM-Options](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ApolloApplication-Mysql-VM-Options.png) + +``` +-Dspring.config-datasource.url=jdbc:mysql://your-mysql-server:3306/ApolloConfigDB?useUnicode=true&characterEncoding=UTF8 +-Dspring.config-datasource.username=apollo-username +-Dspring.config-datasource.password=apollo-password -### 2.1.4 Program arguments configuration +-Dspring.portal-datasource.url=jdbc:mysql://your-mysql-server:3306/ApolloPortalDB?useUnicode=true&characterEncoding=UTF8 +-Dspring.portal-datasource.username=apollo-username +-Dspring.portal-datasource.password=apollo-password -`--configservice --adminservice` +``` +The initialization script for the MySQL database can be found in the scripts/sql/profiles/mysql-default directory of this project. +[apolloconfigdb.sql](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/scripts/sql/profiles/mysql-default/apolloconfigdb.sql) +[apolloportaldb.sql](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/scripts/sql/profiles/mysql-default/apolloportaldb.sql) + +>Note 3: The default log output of the program is /opt/logs/100003171/apollo-assembly.log, if you need to modify the log file path, you can add the `logging.file.name` parameter, as follows. +> +>-Dlogging.file.name=/your-path/apollo-assembly.log -### 2.1.5 Run +### 2.1.4 Run Click Run or Debug for the new run configuration. -![ConfigAdminApplication-Run](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ConfigAdminApplication-Run.png) +![ApolloApplication-Run](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ApolloApplication-Run.png) After starting, open [http://localhost:8080](http://localhost:8080) to see that both `apollo-configservice` and `apollo-adminservice` have been started and registered to Eureka. @@ -88,66 +104,23 @@ After starting, open [http://localhost:8080](http://localhost:8080) to see that > ... > } -## 2.2 Apollo-Portal - -The following is an example of how to start `apollo-portal` locally with Intellij Community 2016.2 version. - -![PortalApplication-Overview](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/PortalApplication-Overview.png) - -### 2.2.1 New run configuration - -![NewConfiguration-Application](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/NewConfiguration-Application.png) - -### 2.2.2 Main class configuration - -`com.ctrip.framework.apollo.portal.PortalApplication` - -### 2.2.3 VM options configuration - -![PortalApplication-VM-Options](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/PortalApplication-VM-Options.png) - - -Dapollo_profile=github,auth - -Ddev_meta=http://localhost:8080/ - -Dserver.port=8070 - -Dspring.datasource.url=jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8 - -Dspring.datasource.username=root - -Dspring.datasource.password= - ->Note 1: apollo_profile is specified here as `github` and `auth`, where `github` is a profile required by Apollo for database configuration, and `auth` is added from 0.9.0 to support simple authentication using Spring Security provided by apollo. For more information you can refer to [Portal-implement-user-login-function](en/extension/portal-how-to-implement-user-login-function) -> ->Note 2: spring.datasource related configuration replaced with your own database connection information, note that the database is `ApolloPortalDB `. -> ->Note 3: The default configuration imported in ApolloPortalDB will only show the configuration of DEV environment, so the dev\_meta property is configured here, if you want to show the configuration of other environment locally, you need to add the meta server address of other environment here, such as fat\_meta. -> ->Note 4: Here server.port=8070 is specified because `apollo-configservice` starts on port 8080, so here `apollo-portal` is configured to start on port 8070. -> ->Note 5: The default log output of the program is /opt/logs/100003173/apollo-portal.log. If you need to modify the log file path, you can add the `logging.file.name` parameter as follows. -> ->-Dlogging.file.name=/your-path/apollo-portal.log - -### 2.2.4 Running - -Click Run or Debug for the newly created run configuration. - -![PortalApplication-Run](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/PortalApplication-Run.png) - After starting, open [http://localhost:8070](http://localhost:8070) to see the Apollo Configuration Center interface. ![PortalApplication-Home](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/PortalApplication-Home.png) >Note: If `auth` profile is enabled, the default username is apollo and password is admin -### 2.2.5 Demo application access +### 2.1.5 Demo application access For better development and debugging, we usually create a demo project for our own use. You can refer to [General Application Access Guide](en/portal/apollo-user-guide?id=i-general-application-access-guide) to create your own demo project. -## 2.3 Java sample client startup +## 2.2 Java sample client startup There is a sample client project: [apollo-demo-java](https://github.com/apolloconfig/apollo-demo-java), the following is an example of how to start it locally with Intellij. -### 2.3.1 Configure the project AppId +### 2.2.1 Configure the project AppId When creating a demo project in `2.2.5 Demo Application Access`, the system will ask to fill in a globally unique AppId, which we need to configure into the app.properties file of the `apollo-demo` project: `apollo-demo-java/api-demo/src/main/resources/ META-INF/app.properties`. @@ -163,15 +136,15 @@ If our own demo project uses an AppId of 100004458, then the file content would > More ways to configure AppId can be found in [1.2.1 AppId](en/client/java-sdk-user-guide?id=_121-appid) -### 2.3.2 New run configuration +### 2.2.2 New run configuration ![NewConfiguration-Application](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/NewConfiguration-Application.png) -### 2.3.3 Main class configuration +### 2.2.3 Main class configuration `com.apolloconfig.apollo.demo.api.SimpleApolloConfigDemo` -### 2.3.4 VM options configuration +### 2.2.4 VM options configuration ![apollo-demo-vm-options](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/apollo-demo-vm-options.jpg) @@ -181,11 +154,11 @@ If our own demo project uses an AppId of 100004458, then the file content would > For more ways to configure Apollo Meta Server, please refer to [1.2.2 Apollo Meta Server](en/client/java-sdk-user-guide?id=_122-apollo-meta-server) -### 2.3.5 Overview +### 2.2.5 Overview ![apollo-demo-overview](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/apollo-demo-overview.jpg) -### 2.3.6 Running +### 2.2.6 Running Click Run or Debug on the newly created run configuration. @@ -208,11 +181,11 @@ Enter the value you have configured on the Portal, such as `timeout` in our demo > > -## 2.4 .Net sample client startup +## 2.3 .Net sample client startup The [apollo.net](https://github.com/ctripcorp/apollo.net) project has a sample client project: `ApolloDemo`, here's an example of how to start it locally with VS 2010. -### 2.4.1 Configuring the project AppId +### 2.3.1 Configuring the project AppId When creating a Demo project in `2.2.5 Demo Application Access`, the system will ask to fill in a globally unique AppId, which we need to configure into the App.config file of the `ApolloDemo` project: `apollo.net\ApolloDemo\App.config`. @@ -228,13 +201,13 @@ If our own demo project uses an AppId of 100004458, then the contents of the fil > For public Namespace configurations, the configuration can be obtained without the AppId, but the ability of the application to override the public Namespace configuration is lost. -### 2.4.2 Configuring Service Addresses +### 2.3.2 Configuring Service Addresses Apollo client will get the configuration from different servers for different environments, so we need to configure the server address (Apollo.{ENV}.Meta) in app.config or web.config. Suppose the DEV environment's configuration service (apollo-config service) address is 11.22.33.44, then we will do the following configuration. ![apollo-net-server-url-config](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/apollo-net-server-url-config.png) -### 2.4.3 Running +### 2.3.3 Running Just run `ApolloConfigDemo.cs`. diff --git a/docs/en/deployment/distributed-deployment-guide.md b/docs/en/deployment/distributed-deployment-guide.md index 2c06ee9ce9a..3251d4c5d5e 100644 --- a/docs/en/deployment/distributed-deployment-guide.md +++ b/docs/en/deployment/distributed-deployment-guide.md @@ -219,12 +219,12 @@ You can choose to create it by manually importing SQL or by automatically import #### 2.1.1.1 Manual SQL Import -You can import [apolloportaldb.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/apolloportaldb.sql) through various MySQL clients. +You can import [apolloportaldb.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/profiles/mysql-default/apolloportaldb.sql) through various MySQL clients. Using the native MySQL client as an example. ```sql -source /your_local_path/scripts/sql/apolloportaldb.sql +source /your_local_path/scripts/sql/profiles/mysql-default/apolloportaldb.sql ``` #### 2.1.1.2 Created via Flyway import SQL @@ -254,12 +254,12 @@ You can choose to create it by manually importing SQL or automatically importing #### 2.1.2.1 Importing SQL Manually -You can import [apolloconfigdb.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/apolloconfigdb.sql) through various MySQL clients. +You can import [apolloconfigdb.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/profiles/mysql-default/apolloconfigdb.sql) through various MySQL clients. Using the native MySQL client as an example. ```sql -source /your_local_path/scripts/sql/apolloconfigdb.sql +source /your_local_path/scripts/sql/profiles/mysql-default/apolloconfigdb.sql ``` #### 2.1.2.2 SQL import via Flyway diff --git a/docs/en/deployment/quick-start.md b/docs/en/deployment/quick-start.md index b6277456a47..b4fe384aa8e 100644 --- a/docs/en/deployment/quick-start.md +++ b/docs/en/deployment/quick-start.md @@ -29,7 +29,7 @@ Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode) Windows users please make sure that JAVA_HOME environment variable is set. ## 1.2 MySQL - +* If you plan to use H2 in-memory database/H2 file database, there is no need for MySQL, and you can skip this step. * Version requirement: 5.6.5+ Apollo's table structure uses multiple default declarations for `timestamp`, so version 5.6.5+ is required. @@ -66,103 +66,128 @@ Quick Start is only for local testing, so generally users do not need to downloa 2. Execute `mvn clean package -pl apollo-assembly -am -DskipTests=true` in the root directory. 3. Copy the jar package under apollo-assembly/target and rename it to apollo-all-in-one.jar -# II. Installation steps -## 2.1 Create the database -Apollo server side needs a total of two databases: `ApolloPortalDB` and `ApolloConfigDB`, we have prepared the database, table creation and sample data as sql files respectively, just import the database. - -> Note: If you have already created Apollo database locally, please take care to backup the data. The sql file we prepared will clear the Apollo related tables. - -### 2.1.1 Creating ApolloPortalDB -Just import [sql/apolloportaldb.sql](https://github.com/apolloconfig/apollo-quick-start/blob/master/sql/apolloportaldb.sql) through various MySQL clients. - -The following is an example of a native MySQL client. -```sql -source /your_local_path/sql/apolloportaldb.sql -``` +# II. Initialization and Startup +#### Precautions +1. The Apollo server process needs to use ports 8070, 8080, 8090 respectively, please ensure these three ports are not currently in use. +2. The `github` in the SPRING_PROFILES_ACTIVE environment variable in the script is a required profile, `auth` is a profile that provides simple authentication for the portal, it can be removed if authentication is not required or other authentication methods are used. +## 2.1 Use H2 in-memory database, automatic initialization +No configuration is required, just use the following command to start +> Note: When using the in-memory database, any operation will be lost after the Apollo process restarts +```bash +export SPRING_PROFILES_ACTIVE="github,auth" +unset SPRING_SQL_CONFIG_INIT_MODE +unset SPRING_SQL_PORTAL_INIT_MODE +java -jar apollo-all-in-one.jar -After the successful import, you can verify it by executing the following sql statement. -```sql -select `Id`, `AppId`, `Name` from ApolloPortalDB.App; ``` -| Id | AppId | Name | -| ---- | --------- | ---------- | -| 1 | SampleApp | Sample App | - -### 2.1.2 Creating ApolloConfigDB -You can import [sql/apolloconfigdb.sql](https://github.com/apolloconfig/apollo-quick-start/blob/master/sql/apolloconfigdb.sql) through various MySQL clients. - -The following is an example of a native MySQL client. -```sql -source /your_local_path/sql/apolloconfigdb.sql -``` +## 2.2 Use H2 file database, automatic initialization +#### Precautions +1. The path `~/apollo/apollo-config-db` and `~/apollo/apollo-portal-db` in the environment variable in the script can be replaced with other custom paths, you need to ensure that this path has read and write permissions + +### 2.2.1 First startup +Use the SPRING_SQL_CONFIG_INIT_MODE="always" and SPRING_SQL_PORTAL_INIT_MODE="always" environment variable for initialization at the first startup +```bash +export SPRING_PROFILES_ACTIVE="github,auth" +# config db +export SPRING_SQL_CONFIG_INIT_MODE="always" +export SPRING_CONFIG_DATASOURCE_URL="jdbc:h2:file:~/apollo/apollo-config-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE" +# portal db +export SPRING_SQL_PORTAL_INIT_MODE="always" +export SPRING_PORTAL_DATASOURCE_URL="jdbc:h2:file:~/apollo/apollo-portal-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE" +java -jar apollo-all-in-one.jar -After the successful import, you can verify it by executing the following sql statement. -```sql -select `NamespaceId`, `Key`, `Value`, `Comment` from ApolloConfigDB.Item; ``` -| NamespaceId | Key | Value | Comment | -| ----------- | ------- | ----- | ---------------------------- | -| 1 | timeout | 100 | sample timeout configuration | - -## 2.2 Configuring Database Connection Information -The Apollo server needs to know how to connect to the database you created earlier, so you need to edit [demo.sh](https://github.com/apolloconfig/apollo-quick-start/blob/master/demo.sh) and modify ApolloPortalDB and ApolloConfigDB related database connection string information. -> Note: The filled in user needs to have read and write access to ApolloPortalDB and ApolloConfigDB data. +### 2.2.2 Subsequent startup +Remove the SPRING_SQL_CONFIG_INIT_MODE and SPRING_SQL_PORTAL_INIT_MODE environment variable to avoid repeated initialization at subsequent startup +```bash +export SPRING_PROFILES_ACTIVE="github,auth" +# config db +unset SPRING_SQL_CONFIG_INIT_MODE +export SPRING_CONFIG_DATASOURCE_URL="jdbc:h2:file:~/apollo/apollo-config-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE" +# portal db +unset SPRING_SQL_PORTAL_INIT_MODE +export SPRING_PORTAL_DATASOURCE_URL="jdbc:h2:file:~/apollo/apollo-portal-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE" +java -jar apollo-all-in-one.jar -```sh -#apollo config db info -apollo_config_db_url="jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8&serverTimezone=Asia/Shanghai" -apollo_config_db_username=username -apollo_config_db_password=password (if you don't have a password, just leave it blank) - -# apollo portal db info -apollo_portal_db_url="jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8&serverTimezone=Asia/Shanghai" -apollo_portal_db_username=username -apollo_portal_db_password=password (if you don't have a password, just leave it blank) ``` -> Note: Do not modify other parts of demo.sh - -# III. Start Apollo Configuration Center -## 3.1 Make sure the port is not occupied -The Quick Start script will start 3 services locally, using ports 8070, 8080, 8090 respectively, please make sure these 3 ports are not currently used. +## 2.3 Use mysql database, automatic initialization +#### Precautions +1. The your-mysql-server:3306 in the environment variable in the script needs to be replaced with the actual mysql server address and port, ApolloConfigDB and ApolloPortalDB needs to be replaced with the actual database name +2. The "apollo-username" and "apollo-password" in the environment variables in the script need to fill in the actual username and password + +### 2.3.1 First startup +Use the SPRING_SQL_INIT_MODE="always" environment variable for initialization at the first startup +```bash +export SPRING_PROFILES_ACTIVE="github,auth" +# config db +export SPRING_SQL_CONFIG_INIT_MODE="always" +export SPRING_CONFIG_DATASOURCE_URL="jdbc:mysql://your-mysql-server:3306/ApolloConfigDB?useUnicode=true&characterEncoding=UTF8" +export SPRING_CONFIG_DATASOURCE_USERNAME="apollo-username" +export SPRING_CONFIG_DATASOURCE_PASSWORD="apollo-password" +# portal db +export SPRING_SQL_PORTAL_INIT_MODE="always" +export SPRING_PORTAL_DATASOURCE_URL="jdbc:mysql://your-mysql-server:3306/ApolloPortalDB?useUnicode=true&characterEncoding=UTF8" +export SPRING_PORTAL_DATASOURCE_USERNAME="apollo-username" +export SPRING_PORTAL_DATASOURCE_PASSWORD="apollo-password" +java -jar apollo-all-in-one.jar -For example, under Linux/Mac, you can check with the following command. -```sh -lsof -i:8080 ``` -## 3.2 Execute the startup script -```sh -./demo.sh start -``` +### 2.3.2 Subsequent startup +Remove the SPRING_SQL_CONFIG_INIT_MODE and SPRING_SQL_PORTAL_INIT_MODE environment variable to avoid repeated initialization at subsequent startup +```bash +export SPRING_PROFILES_ACTIVE="github,auth" +# config db +unset SPRING_SQL_CONFIG_INIT_MODE +export SPRING_CONFIG_DATASOURCE_URL="jdbc:mysql://your-mysql-server:3306/ApolloConfigDB?useUnicode=true&characterEncoding=UTF8" +export SPRING_CONFIG_DATASOURCE_USERNAME="apollo-username" +export SPRING_CONFIG_DATASOURCE_PASSWORD="apollo-password" +# portal db +unset SPRING_SQL_PORTAL_INIT_MODE +export SPRING_PORTAL_DATASOURCE_URL="jdbc:mysql://your-mysql-server:3306/ApolloPortalDB?useUnicode=true&characterEncoding=UTF8" +export SPRING_PORTAL_DATASOURCE_USERNAME="apollo-username" +export SPRING_PORTAL_DATASOURCE_PASSWORD="apollo-password" +java -jar apollo-all-in-one.jar -When you see the following output, you've started successfully! -```sh -==== starting service ==== -Service logging file is . /service/apollo-service.log -Started [10768] -Waiting for config service startup ....... -Config service started. You may visit http://localhost:8080 for service status now! -Waiting for admin service startup.... -Admin service started -==== starting portal ==== -Portal logging file is . /portal/apollo-portal.log -Started [10846] -Waiting for portal startup ...... -Portal started. You can visit http://localhost:8070 now! -You can visit now! ``` +## 2.4 Use mysql database, manual initialization + +### 2.4.1 Manually initialize ApolloConfigDB and ApolloPortalDB +You can import [apolloconfigdb.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/profiles/mysql-default/apolloconfigdb.sql) to ApolloConfigDB through various MySQL clients. +You can import [apolloportaldb.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/profiles/mysql-default/apolloportaldb.sql) to ApolloPortalDB through various MySQL clients. + +### 2.4.2 Run +#### Precautions +1. The your-mysql-server:3306 in the environment variable in the script needs to be replaced with the actual mysql server address and port, ApolloConfigDB and ApolloPortalDB needs to be replaced with the actual database name +2. The "apollo-username" and "apollo-password" in the environment variables in the script need to fill in the actual username and password + +```bash +export SPRING_PROFILES_ACTIVE="github,auth" +# config db +unset SPRING_SQL_CONFIG_INIT_MODE +export SPRING_CONFIG_DATASOURCE_URL="jdbc:mysql://your-mysql-server:3306/ApolloConfigDB?useUnicode=true&characterEncoding=UTF8" +export SPRING_CONFIG_DATASOURCE_USERNAME="apollo-username" +export SPRING_CONFIG_DATASOURCE_PASSWORD="apollo-password" +# portal db +unset SPRING_SQL_PORTAL_INIT_MODE +export SPRING_PORTAL_DATASOURCE_URL="jdbc:mysql://your-mysql-server:3306/ApolloPortalDB?useUnicode=true&characterEncoding=UTF8" +export SPRING_PORTAL_DATASOURCE_USERNAME="apollo-username" +export SPRING_PORTAL_DATASOURCE_PASSWORD="apollo-password" +java -jar apollo-all-in-one.jar +``` -## 3.3 Troubleshooting +# III. Additional Startup Instructions +## 3.1 Troubleshooting If you encounter an exception in the startup, you can check the log files in the service and portal directories respectively to troubleshoot the problem. -> Note: During start-up of apollo-configservice, eureka registration failure message will be output in the log, e.g. `com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused`. Note that this is expected because apollo-configservice needs to register the service with Meta Server (itself), but since it is not up yet during the startup process itself, this error is reported. A retry action will be performed later, so it will register properly when the service is up by itself. +> Note: During start-up, eureka registration failure message will be output in the log, e.g. `com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused`. Note that this is expected because apollo-configservice needs to register the service with Meta Server (itself), but since it is not up yet during the startup process itself, this error is reported. A retry action will be performed later, so it will register properly when the service is up by itself. -## 3.4 Note +## 3.2 Note Quick Start is only used to help you quickly experience Apollo project, please refer to: [distributed-deployment-guide](en/deployment/distributed-deployment-guide) for details. diff --git a/docs/en/extension/portal-how-to-implement-user-login-function.md b/docs/en/extension/portal-how-to-implement-user-login-function.md index 80686150e96..13610b4ea9b 100644 --- a/docs/en/extension/portal-how-to-implement-user-login-function.md +++ b/docs/en/extension/portal-how-to-implement-user-login-function.md @@ -10,7 +10,7 @@ Use the following steps. ### 1. Install 0.9.0 or above -> If the previous version is 0.8.0, you need to import [apolloportaldb-v080-v090.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/delta/v080-v090/apolloportaldb-v080-v090.sql) +> If the previous version is 0.8.0, you need to import [apolloportaldb-v080-v090.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/profiles/mysql-default/delta/v080-v090/apolloportaldb-v080-v090.sql) Checking ApolloPortalDB, the `Users` table should already exist and have an initial record. The initial user name is apollo and the password is admin. diff --git a/docs/zh/contribution/apollo-development-guide.md b/docs/zh/contribution/apollo-development-guide.md index 90864e2e4a5..ac6c3c3eb9c 100644 --- a/docs/zh/contribution/apollo-development-guide.md +++ b/docs/zh/contribution/apollo-development-guide.md @@ -6,7 +6,7 @@ Apollo本地开发需要以下组件: 1. Java: 1.8+ -2. MySQL: 5.6.5+ +2. MySQL: 5.6.5+ (如果使用 H2 内存数据库/H2 文件数据库,则无需 MySQL) 3. IDE: 没有特殊要求 其中MySQL需要创建Apollo数据库并导入基础数据。 @@ -19,12 +19,12 @@ Apollo本地开发需要以下组件: 具体请参考[Apollo配置中心设计](zh/design/apollo-design) # 二、本地启动 -## 2.1 Apollo Config Service和Apollo Admin Service -我们在本地开发时,一般会在IDE中同时启动`apollo-configservice`和`apollo-adminservice`。 +## 2.1 Apollo Assembly +我们在本地开发时,一般会在IDE中启动`apollo-assembly`。 -下面以Intellij Community 2016.2版本为例来说明如何在本地启动`apollo-configservice`和`apollo-adminservice`。 +下面以Intellij Community 2016.2版本为例来说明如何在本地启动`apollo-assembly`。 -![ConfigAdminApplication-Overview](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ConfigAdminApplication-Overview.png) +![ApolloApplication-Overview](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ApolloApplication-Overview.png) ### 2.1.1 新建运行配置 ![NewConfiguration-Application](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/NewConfiguration-Application.png) @@ -32,31 +32,48 @@ Apollo本地开发需要以下组件: ### 2.1.2 Main class配置 `com.ctrip.framework.apollo.assembly.ApolloApplication` -> 注:如果希望独立启动`apollo-configservice`和`apollo-adminservice`,可以把Main Class分别换成 -> `com.ctrip.framework.apollo.configservice.ConfigServiceApplication`和 +> 注:如果希望独立启动`apollo-portal`、`apollo-configservice`和`apollo-adminservice`,可以把Main Class分别换成 +> `com.ctrip.framework.apollo.portal.PortalApplication` +> `com.ctrip.framework.apollo.configservice.ConfigServiceApplication` > `com.ctrip.framework.apollo.adminservice.AdminServiceApplication` ### 2.1.3 VM options配置 -![ConfigAdminApplication-VM-Options](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ConfigAdminApplication-VM-Options.png) - - -Dapollo_profile=github - -Dspring.datasource.url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8 - -Dspring.datasource.username=root - -Dspring.datasource.password= +![ApolloApplication-VM-Options](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ApolloApplication-VM-Options.png) +``` +-Dapollo_profile=github,auth ->注1:spring.datasource相关配置替换成你自己的数据库连接信息,注意数据库是`ApolloConfigDB` +``` +>注1:这里指定了apollo_profile是`github`和`auth`,其中`github`是Apollo必须的一个profile,用于数据库的配置,`auth`是从0.9.0新增的,用来支持使用apollo提供的Spring Security简单认证,更多信息可以参考[Portal-实现用户登录功能](zh/development/portal-how-to-implement-user-login-function) > ->注2:程序默认日志输出为/opt/logs/100003171/apollo-assembly.log,如果需要修改日志文件路径,可以增加`logging.file.name`参数,如下: +>注2:如果需要使用 mysql 数据库,添加`spring.config-datasource.*` 和 `spring.portal-datasource.*` 相关配置, +> your-mysql-server:3306 需要替换为实际的 mysql 服务器地址和端口, +> ApolloConfigDB 和 ApolloPortalDB 需要替换为实际的数据库名称, +> apollo-username 和 apollo-password 需要替换为实际的用户名和密码 + +![ApolloApplication-Mysql-VM-Options](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ApolloApplication-Mysql-VM-Options.png) + +``` +-Dspring.config-datasource.url=jdbc:mysql://your-mysql-server:3306/ApolloConfigDB?useUnicode=true&characterEncoding=UTF8 +-Dspring.config-datasource.username=apollo-username +-Dspring.config-datasource.password=apollo-password + +-Dspring.portal-datasource.url=jdbc:mysql://your-mysql-server:3306/ApolloPortalDB?useUnicode=true&characterEncoding=UTF8 +-Dspring.portal-datasource.username=apollo-username +-Dspring.portal-datasource.password=apollo-password + +``` +mysql 数据库初始化脚本见 本项目 scripts/sql/profiles/mysql-default 目录下的文件 +[apolloconfigdb.sql](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/scripts/sql/profiles/mysql-default/apolloconfigdb.sql) +[apolloportaldb.sql](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/scripts/sql/profiles/mysql-default/apolloportaldb.sql) + +>注3:程序默认日志输出为/opt/logs/100003171/apollo-assembly.log,如果需要修改日志文件路径,可以增加`logging.file.name`参数,如下: > >-Dlogging.file.name=/your-path/apollo-assembly.log -### 2.1.4 Program arguments配置 -`--configservice --adminservice` - -### 2.1.5 运行 +### 2.1.4 运行 对新建的运行配置点击Run或Debug皆可。 -![ConfigAdminApplication-Run](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ConfigAdminApplication-Run.png) +![ApolloApplication-Run](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/ApolloApplication-Run.png) 启动完后,打开[http://localhost:8080](http://localhost:8080)可以看到`apollo-configservice`和`apollo-adminservice`都已经启动完成并注册到Eureka。 @@ -77,62 +94,23 @@ Apollo本地开发需要以下组件: > ... > } -## 2.2 Apollo-Portal - -下面以Intellij Community 2016.2版本为例来说明如何在本地启动`apollo-portal`。 - -![PortalApplication-Overview](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/PortalApplication-Overview.png) - -### 2.2.1 新建运行配置 -![NewConfiguration-Application](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/NewConfiguration-Application.png) - -### 2.2.2 Main class配置 -`com.ctrip.framework.apollo.portal.PortalApplication` - -### 2.2.3 VM options配置 -![PortalApplication-VM-Options](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/PortalApplication-VM-Options.png) - - -Dapollo_profile=github,auth - -Ddev_meta=http://localhost:8080/ - -Dserver.port=8070 - -Dspring.datasource.url=jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8 - -Dspring.datasource.username=root - -Dspring.datasource.password= - ->注1:这里指定了apollo_profile是`github`和`auth`,其中`github`是Apollo必须的一个profile,用于数据库的配置,`auth`是从0.9.0新增的,用来支持使用apollo提供的Spring Security简单认证,更多信息可以参考[Portal-实现用户登录功能](zh/extension/portal-how-to-implement-user-login-function) -> ->注2:spring.datasource相关配置替换成你自己的数据库连接信息,注意数据库是`ApolloPortalDB `。 -> ->注3:默认ApolloPortalDB中导入的配置只会展示DEV环境的配置,所以这里配置了dev\_meta属性,如果你希望在本地展示其它环境的配置,需要在这里增加其它环境的meta服务器地址,如fat\_meta。 -> ->注4:这里指定了server.port=8070是因为`apollo-configservice`启动在8080端口,所以这里配置`apollo-portal`启动在8070端口。 -> ->注5:程序默认日志输出为/opt/logs/100003173/apollo-portal.log,如果需要修改日志文件路径,可以增加`logging.file.name`参数,如下: -> ->-Dlogging.file.name=/your-path/apollo-portal.log - -### 2.2.4 运行 -对新建的运行配置点击Run或Debug皆可。 - -![PortalApplication-Run](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/PortalApplication-Run.png) - 启动完后,打开[http://localhost:8070](http://localhost:8070)就可以看到Apollo配置中心界面了。 ![PortalApplication-Home](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/PortalApplication-Home.png) >注:如果启用了`auth` profile的话,默认的用户名是apollo,密码是admin -### 2.2.5 Demo应用接入 +### 2.1.5 Demo应用接入 为了更好的开发和调试,一般我们都会自己创建一个demo项目给自己使用。 可以参考[一、普通应用接入指南](zh/portal/apollo-user-guide#一、普通应用接入指南)创建自己的demo项目。 -## 2.3 Java样例客户端启动 +## 2.2 Java样例客户端启动 仓库中有一个样例客户端的项目:[apollo-demo-java](https://github.com/apolloconfig/apollo-demo-java),下面以Intellij为例来说明如何在本地启动。 -### 2.3.1 配置项目AppId +### 2.2.1 配置项目AppId 在`2.2.5 Demo应用接入`中创建Demo项目时,系统会要求填入一个全局唯一的AppId,我们需要把这个AppId配置到`apollo-demo`项目的app.properties文件中:`apollo-demo-java/api-demo/src/main/resources/META-INF/app.properties`。 ![apollo-demo-app-properties](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/apollo-demo-app-properties.jpg) @@ -147,13 +125,13 @@ Apollo本地开发需要以下组件: > 更多配置AppId的方式可以参考[1.2.1 AppId](zh/client/java-sdk-user-guide#_121-appid) -### 2.3.2 新建运行配置 +### 2.2.2 新建运行配置 ![NewConfiguration-Application](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/NewConfiguration-Application.png) -### 2.3.3 Main class配置 +### 2.2.3 Main class配置 `com.apolloconfig.apollo.demo.api.SimpleApolloConfigDemo` -### 2.3.4 VM options配置 +### 2.2.4 VM options配置 ![apollo-demo-vm-options](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/apollo-demo-vm-options.jpg) -Dapollo.meta=http://localhost:8080 @@ -162,11 +140,11 @@ Apollo本地开发需要以下组件: > 更多配置Apollo Meta Server的方式可以参考[1.2.2 Apollo Meta Server](zh/client/java-sdk-user-guide#_122-apollo-meta-server) -### 2.3.5 概览 +### 2.2.5 概览 ![apollo-demo-overview](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/apollo-demo-overview.jpg) -### 2.3.6 运行 +### 2.2.6 运行 对新建的运行配置点击Run或Debug皆可。 ![apollo-demo-run](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/local-development/apollo-demo-run.png) @@ -188,11 +166,11 @@ Apollo本地开发需要以下组件: > > ``` -## 2.4 .Net样例客户端启动 +## 2.3 .Net样例客户端启动 [apollo.net](https://github.com/ctripcorp/apollo.net)项目中有一个样例客户端的项目:`ApolloDemo`,下面就以VS 2010为例来说明如何在本地启动。 -### 2.4.1 配置项目AppId +### 2.3.1 配置项目AppId 在`2.2.5 Demo应用接入`中创建Demo项目时,系统会要求填入一个全局唯一的AppId,我们需要把这个AppId配置到`ApolloDemo`项目的APP.config文件中:`apollo.net\ApolloDemo\App.config`。 ![apollo-demo-app-config](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/apollo-net-app-config.png) @@ -206,12 +184,12 @@ Apollo本地开发需要以下组件: > 对于公共Namespace的配置,没有AppId也可以获取到配置,但是就失去了应用覆盖公共Namespace配置的能力。 -### 2.4.2 配置服务地址 +### 2.3.2 配置服务地址 Apollo客户端针对不同的环境会从不同的服务器获取配置,所以我们需要在app.config或web.config配置服务器地址(Apollo.{ENV}.Meta)。假设DEV环境的配置服务(apollo-configservice)地址是11.22.33.44,那么我们就做如下配置: ![apollo-net-server-url-config](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/apollo-net-server-url-config.png) -### 2.4.3 运行 +### 2.3.3 运行 运行`ApolloConfigDemo.cs`即可。 启动完后,忽略前面的调试信息,可以看到如下提示: diff --git a/docs/zh/deployment/distributed-deployment-guide.md b/docs/zh/deployment/distributed-deployment-guide.md index eecc6f8b493..4d21e1c80aa 100644 --- a/docs/zh/deployment/distributed-deployment-guide.md +++ b/docs/zh/deployment/distributed-deployment-guide.md @@ -217,11 +217,11 @@ Apollo服务端共需要两个数据库:`ApolloPortalDB`和`ApolloConfigDB`, #### 2.1.1.1 手动导入SQL创建 -通过各种MySQL客户端导入[apolloportaldb.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/apolloportaldb.sql)即可。 +通过各种MySQL客户端导入[apolloportaldb.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/profiles/mysql-default/apolloportaldb.sql)即可。 以MySQL原生客户端为例: ```sql -source /your_local_path/scripts/sql/apolloportaldb.sql +source /your_local_path/scripts/sql/profiles/mysql-default/apolloportaldb.sql ``` #### 2.1.1.2 通过Flyway导入SQL创建 @@ -250,11 +250,11 @@ select `Id`, `Key`, `Value`, `Comment` from `ApolloPortalDB`.`ServerConfig` limi #### 2.1.2.1 手动导入SQL -通过各种MySQL客户端导入[apolloconfigdb.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/apolloconfigdb.sql)即可。 +通过各种MySQL客户端导入[apolloconfigdb.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/profiles/mysql-default/apolloconfigdb.sql)即可。 以MySQL原生客户端为例: ```sql -source /your_local_path/scripts/sql/apolloconfigdb.sql +source /your_local_path/scripts/sql/profiles/mysql-default/apolloconfigdb.sql ``` #### 2.1.2.2 通过Flyway导入SQL diff --git a/docs/zh/deployment/quick-start.md b/docs/zh/deployment/quick-start.md index 9474fc98b0f..a87c15b4149 100644 --- a/docs/zh/deployment/quick-start.md +++ b/docs/zh/deployment/quick-start.md @@ -29,7 +29,7 @@ Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode) Windows用户请确保JAVA_HOME环境变量已经设置。 ## 1.2 MySQL - +* 如果使用 H2 内存数据库/H2 文件数据库,则无需 MySQL,可以跳过此步骤 * 版本要求:5.6.5+ Apollo的表结构对`timestamp`使用了多个default声明,所以需要5.6.5以上版本。 @@ -66,100 +66,129 @@ Quick Start只针对本地测试使用,所以一般用户不需要自己下载 2. 在根目录下执行`mvn clean package -pl apollo-assembly -am -DskipTests=true` 3. 复制apollo-assembly/target下的jar包,rename为apollo-all-in-one.jar -# 二、安装步骤 -## 2.1 创建数据库 -Apollo服务端共需要两个数据库:`ApolloPortalDB`和`ApolloConfigDB`,我们把数据库、表的创建和样例数据都分别准备了sql文件,只需要导入数据库即可。 - -> 注意:如果你本地已经创建过Apollo数据库,请注意备份数据。我们准备的sql文件会清空Apollo相关的表。 +# 二、数据库初始化及启动 +#### 注意事项 +1. apollo 服务端进程需要分别使用8070, 8080, 8090端口,请确保这3个端口当前没有被使用。 +2. 脚本中的 SPRING_PROFILES_ACTIVE 环境变量中的 `github` 是必须的 profile,`auth` 是 portal 提供简单认证的 profile,不需要认证或者使用其它认证方式时可以去掉 -### 2.1.1 创建ApolloPortalDB -通过各种MySQL客户端导入[sql/apolloportaldb.sql](https://github.com/apolloconfig/apollo-quick-start/blob/master/sql/apolloportaldb.sql)即可。 +## 2.1 使用 H2 内存数据库,自动初始化 +无需任何配置,直接使用如下命令启动即可 +> 注:使用内存数据库时,任何操作都会在 apollo 进程重启后丢失 +```bash +export SPRING_PROFILES_ACTIVE="github,auth" +unset SPRING_SQL_CONFIG_INIT_MODE +unset SPRING_SQL_PORTAL_INIT_MODE +java -jar apollo-all-in-one.jar -下面以MySQL原生客户端为例: -```sql -source /your_local_path/sql/apolloportaldb.sql ``` -导入成功后,可以通过执行以下sql语句来验证: -```sql -select `Id`, `AppId`, `Name` from ApolloPortalDB.App; -``` +## 2.2 使用 H2 文件数据库,自动初始化 +#### 注意事项 +1. 脚本中环境变量中的路径 `~/apollo/apollo-config-db` 和 `~/apollo/apollo-portal-db` 可以替换为其它自定义路径,需要保证该路径有读写权限 + +### 2.2.1 首次启动 +首次启动使用 SPRING_SQL_CONFIG_INIT_MODE="always" 和 SPRING_SQL_PORTAL_INIT_MODE="always" 环境变量来进行初始化 +```bash +export SPRING_PROFILES_ACTIVE="github,auth" +# config db +export SPRING_SQL_CONFIG_INIT_MODE="always" +export SPRING_CONFIG_DATASOURCE_URL="jdbc:h2:file:~/apollo/apollo-config-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE" +# portal db +export SPRING_SQL_PORTAL_INIT_MODE="always" +export SPRING_PORTAL_DATASOURCE_URL="jdbc:h2:file:~/apollo/apollo-portal-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE" +java -jar apollo-all-in-one.jar -| Id | AppId | Name | -|----|-----------|------------| -| 1 | SampleApp | Sample App | - -### 2.1.2 创建ApolloConfigDB -通过各种MySQL客户端导入[sql/apolloconfigdb.sql](https://github.com/apolloconfig/apollo-quick-start/blob/master/sql/apolloconfigdb.sql)即可。 - -下面以MySQL原生客户端为例: -```sql -source /your_local_path/sql/apolloconfigdb.sql ``` -导入成功后,可以通过执行以下sql语句来验证: -```sql -select `NamespaceId`, `Key`, `Value`, `Comment` from ApolloConfigDB.Item; -``` -| NamespaceId | Key | Value | Comment | -|-------------|---------|-------|--------------------| -| 1 | timeout | 100 | sample timeout配置 | +### 2.2.2 后续启动 +后续启动去掉 SPRING_SQL_CONFIG_INIT_MODE 和 SPRING_SQL_PORTAL_INIT_MODE 环境变量来避免重复初始化 +```bash +export SPRING_PROFILES_ACTIVE="github,auth" +# config db +unset SPRING_SQL_CONFIG_INIT_MODE +export SPRING_CONFIG_DATASOURCE_URL="jdbc:h2:file:~/apollo/apollo-config-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE" +# portal db +unset SPRING_SQL_PORTAL_INIT_MODE +export SPRING_PORTAL_DATASOURCE_URL="jdbc:h2:file:~/apollo/apollo-portal-db;mode=mysql;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;BUILTIN_ALIAS_OVERRIDE=TRUE;DATABASE_TO_UPPER=FALSE" +java -jar apollo-all-in-one.jar -## 2.2 配置数据库连接信息 -Apollo服务端需要知道如何连接到你前面创建的数据库,所以需要编辑[demo.sh](https://github.com/apolloconfig/apollo-quick-start/blob/master/demo.sh),修改ApolloPortalDB和ApolloConfigDB相关的数据库连接串信息。 +``` -> 注意:填入的用户需要具备对ApolloPortalDB和ApolloConfigDB数据的读写权限。 +## 2.3 使用 mysql 数据库,自动初始化 +#### 注意事项 +1. 脚本环境变量中的 your-mysql-server:3306 需要替换为实际的 mysql 服务器地址和端口,ApolloConfigDB 和 ApolloPortalDB 需要替换为实际的数据库名称 +2. 脚本环境变量中的 "apollo-username" 和 "apollo-password" 需要填写实际的用户名和密码 + +### 2.3.1 首次启动 +首次启动使用 SPRING_SQL_INIT_MODE="always" 环境变量来进行初始化 +```bash +export SPRING_PROFILES_ACTIVE="github,auth" +# config db +export SPRING_SQL_CONFIG_INIT_MODE="always" +export SPRING_CONFIG_DATASOURCE_URL="jdbc:mysql://your-mysql-server:3306/ApolloConfigDB?useUnicode=true&characterEncoding=UTF8" +export SPRING_CONFIG_DATASOURCE_USERNAME="apollo-username" +export SPRING_CONFIG_DATASOURCE_PASSWORD="apollo-password" +# portal db +export SPRING_SQL_PORTAL_INIT_MODE="always" +export SPRING_PORTAL_DATASOURCE_URL="jdbc:mysql://your-mysql-server:3306/ApolloPortalDB?useUnicode=true&characterEncoding=UTF8" +export SPRING_PORTAL_DATASOURCE_USERNAME="apollo-username" +export SPRING_PORTAL_DATASOURCE_PASSWORD="apollo-password" +java -jar apollo-all-in-one.jar -```sh -#apollo config db info -apollo_config_db_url="jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8&serverTimezone=Asia/Shanghai" -apollo_config_db_username=用户名 -apollo_config_db_password=密码(如果没有密码,留空即可) - -# apollo portal db info -apollo_portal_db_url="jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8&serverTimezone=Asia/Shanghai" -apollo_portal_db_username=用户名 -apollo_portal_db_password=密码(如果没有密码,留空即可) ``` -> 注意:不要修改demo.sh的其它部分 - -# 三、启动Apollo配置中心 -## 3.1 确保端口未被占用 -Quick Start脚本会在本地启动3个服务,分别使用8070, 8080, 8090端口,请确保这3个端口当前没有被使用。 +### 2.3.2 后续启动 +后续启动去掉 SPRING_SQL_CONFIG_INIT_MODE 和 SPRING_SQL_PORTAL_INIT_MODE 环境变量来避免重复初始化 +```bash +export SPRING_PROFILES_ACTIVE="github,auth" +# config db +unset SPRING_SQL_CONFIG_INIT_MODE +export SPRING_CONFIG_DATASOURCE_URL="jdbc:mysql://your-mysql-server:3306/ApolloConfigDB?useUnicode=true&characterEncoding=UTF8" +export SPRING_CONFIG_DATASOURCE_USERNAME="apollo-username" +export SPRING_CONFIG_DATASOURCE_PASSWORD="apollo-password" +# portal db +unset SPRING_SQL_PORTAL_INIT_MODE +export SPRING_PORTAL_DATASOURCE_URL="jdbc:mysql://your-mysql-server:3306/ApolloPortalDB?useUnicode=true&characterEncoding=UTF8" +export SPRING_PORTAL_DATASOURCE_USERNAME="apollo-username" +export SPRING_PORTAL_DATASOURCE_PASSWORD="apollo-password" +java -jar apollo-all-in-one.jar -例如,在Linux/Mac下,可以通过如下命令检查: -```sh -lsof -i:8080 ``` -## 3.2 执行启动脚本 -```sh -./demo.sh start -``` +## 2.4 使用 mysql 数据库,手动初始化 + +### 2.4.1 手动初始化 ApolloConfigDB 和 ApolloPortalDB +ApolloConfigDB 通过各种MySQL客户端导入[apolloconfigdb.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/profiles/mysql-default/apolloconfigdb.sql)即可。 +ApolloPortalDB 通过各种MySQL客户端导入[apolloportaldb.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/profiles/mysql-default/apolloportaldb.sql)即可。 + +### 2.4.2 运行 +#### 注意事项 +1. 脚本环境变量中的 your-mysql-server:3306 需要替换为实际的 mysql 服务器地址和端口,ApolloConfigDB 和 ApolloPortalDB 需要替换为实际的数据库名称 +2. 脚本环境变量中的 "apollo-username" 和 "apollo-password" 需要填写实际的用户名和密码 + +```bash +export SPRING_PROFILES_ACTIVE="github,auth" +# config db +unset SPRING_SQL_CONFIG_INIT_MODE +export SPRING_CONFIG_DATASOURCE_URL="jdbc:mysql://your-mysql-server:3306/ApolloConfigDB?useUnicode=true&characterEncoding=UTF8" +export SPRING_CONFIG_DATASOURCE_USERNAME="apollo-username" +export SPRING_CONFIG_DATASOURCE_PASSWORD="apollo-password" +# portal db +unset SPRING_SQL_PORTAL_INIT_MODE +export SPRING_PORTAL_DATASOURCE_URL="jdbc:mysql://your-mysql-server:3306/ApolloPortalDB?useUnicode=true&characterEncoding=UTF8" +export SPRING_PORTAL_DATASOURCE_USERNAME="apollo-username" +export SPRING_PORTAL_DATASOURCE_PASSWORD="apollo-password" +java -jar apollo-all-in-one.jar -当看到如下输出后,就说明启动成功了! -```sh -==== starting service ==== -Service logging file is ./service/apollo-service.log -Started [10768] -Waiting for config service startup....... -Config service started. You may visit http://localhost:8080 for service status now! -Waiting for admin service startup.... -Admin service started -==== starting portal ==== -Portal logging file is ./portal/apollo-portal.log -Started [10846] -Waiting for portal startup...... -Portal started. You can visit http://localhost:8070 now! ``` -## 3.3 异常排查 +# 三、启动额外说明 +## 3.1 异常排查 如果启动遇到了异常,可以分别查看service和portal目录下的log文件排查问题。 -> 注:在启动apollo-configservice的过程中会在日志中输出eureka注册失败的信息,如`com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused`。需要注意的是,这个是预期的情况,因为apollo-configservice需要向Meta Server(它自己)注册服务,但是因为在启动过程中,自己还没起来,所以会报这个错。后面会进行重试的动作,所以等自己服务起来后就会注册正常了。 +> 注:在启动的过程中会在日志中输出eureka注册失败的信息,如`com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused`。需要注意的是,这个是预期的情况,因为apollo-configservice需要向Meta Server(它自己)注册服务,但是因为在启动过程中,自己还没起来,所以会报这个错。后面会进行重试的动作,所以等自己服务起来后就会注册正常了。 -## 3.4 注意 +## 3.2 注意 Quick Start只是用来帮助大家快速体验Apollo项目,具体实际使用时请参考:[分布式部署指南](zh/deployment/distributed-deployment-guide)。 另外需要注意的是Quick Start不支持增加环境,只有通过分布式部署才可以新增环境,同样请参考:[分布式部署指南](zh/deployment/distributed-deployment-guide) diff --git a/docs/zh/extension/portal-how-to-implement-user-login-function.md b/docs/zh/extension/portal-how-to-implement-user-login-function.md index cb406e616e3..ae8d996517b 100644 --- a/docs/zh/extension/portal-how-to-implement-user-login-function.md +++ b/docs/zh/extension/portal-how-to-implement-user-login-function.md @@ -8,7 +8,7 @@ Apollo是配置管理系统,会提供权限管理(Authorization),理论 使用步骤如下: ### 1. 安装0.9.0以上版本 ->如果之前是0.8.0版本,需要导入[apolloportaldb-v080-v090.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/delta/v080-v090/apolloportaldb-v080-v090.sql) +>如果之前是0.8.0版本,需要导入[apolloportaldb-v080-v090.sql](https://github.com/apolloconfig/apollo/blob/master/scripts/sql/profiles/mysql-default/delta/v080-v090/apolloportaldb-v080-v090.sql) 查看ApolloPortalDB,应该已经存在`Users`表,并有一条初始记录。初始用户名是apollo,密码是admin。 diff --git a/pom.xml b/pom.xml index 2869b72a45c..c8535c07671 100644 --- a/pom.xml +++ b/pom.xml @@ -95,6 +95,7 @@ + apollo-build-sql-converter apollo-buildtools apollo-common apollo-biz diff --git a/scripts/sql/profiles/h2-default/apolloconfigdb.sql b/scripts/sql/profiles/h2-default/apolloconfigdb.sql new file mode 100644 index 00000000000..8a24f1c82c9 --- /dev/null +++ b/scripts/sql/profiles/h2-default/apolloconfigdb.sql @@ -0,0 +1,501 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- + +-- H2 Function +-- ------------------------------------------------------------ +CREATE ALIAS IF NOT EXISTS UNIX_TIMESTAMP FOR "com.ctrip.framework.apollo.common.jpa.H2Function.unixTimestamp"; + +-- + +-- Dump of table app +-- ------------------------------------------------------------ + + +CREATE TABLE `App` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名', + `OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id', + `OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字', + `OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName', + `OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `App_UK_AppId_DeletedAt` (`AppId`,`DeletedAt`), + KEY `App_DataChange_LastTime` (`DataChange_LastTime`), + KEY `App_IX_Name` (`Name`) +) COMMENT='应用表'; + + + +-- Dump of table appnamespace +-- ------------------------------------------------------------ + + +CREATE TABLE `AppNamespace` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Name` varchar(32) NOT NULL DEFAULT '' COMMENT 'namespace名字,注意,需要全局唯一', + `AppId` varchar(64) NOT NULL DEFAULT '' COMMENT 'app id', + `Format` varchar(32) NOT NULL DEFAULT 'properties' COMMENT 'namespace的format类型', + `IsPublic` boolean NOT NULL DEFAULT FALSE COMMENT 'namespace是否为公共', + `Comment` varchar(64) NOT NULL DEFAULT '' COMMENT '注释', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `AppNamespace_UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`), + KEY `AppNamespace_Name_AppId` (`Name`,`AppId`), + KEY `AppNamespace_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='应用namespace定义'; + + + +-- Dump of table audit +-- ------------------------------------------------------------ + + +CREATE TABLE `Audit` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `EntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名', + `EntityId` int(10) unsigned DEFAULT NULL COMMENT '记录ID', + `OpName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型', + `Comment` varchar(500) DEFAULT NULL COMMENT '备注', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `Audit_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='日志审计表'; + + + +-- Dump of table cluster +-- ------------------------------------------------------------ + + +CREATE TABLE `Cluster` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Name` varchar(32) NOT NULL DEFAULT '' COMMENT '集群名字', + `AppId` varchar(64) NOT NULL DEFAULT '' COMMENT 'App id', + `ParentClusterId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父cluster', + `Comment` varchar(64) DEFAULT NULL COMMENT '备注', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `Cluster_UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`), + KEY `Cluster_IX_ParentClusterId` (`ParentClusterId`), + KEY `Cluster_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='集群'; + + + +-- Dump of table commit +-- ------------------------------------------------------------ + + +CREATE TABLE `Commit` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `ChangeSets` longtext NOT NULL COMMENT '修改变更集', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'namespaceName', + `Comment` varchar(500) DEFAULT NULL COMMENT '备注', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `Commit_DataChange_LastTime` (`DataChange_LastTime`), + KEY `Commit_AppId` (`AppId`), + KEY `Commit_ClusterName` (`ClusterName`), + KEY `Commit_NamespaceName` (`NamespaceName`) +) COMMENT='commit 历史表'; + +-- Dump of table grayreleaserule +-- ------------------------------------------------------------ + + +CREATE TABLE `GrayReleaseRule` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Cluster Name', + `NamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Namespace Name', + `BranchName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'branch name', + `Rules` varchar(16000) DEFAULT '[]' COMMENT '灰度规则', + `ReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '灰度对应的release', + `BranchStatus` tinyint(2) DEFAULT '1' COMMENT '灰度分支状态: 0:删除分支,1:正在使用的规则 2:全量发布', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `GrayReleaseRule_DataChange_LastTime` (`DataChange_LastTime`), + KEY `GrayReleaseRule_IX_Namespace` (`AppId`,`ClusterName`,`NamespaceName`) +) COMMENT='灰度规则表'; + + +-- Dump of table instance +-- ------------------------------------------------------------ + + +CREATE TABLE `Instance` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `DataCenter` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'Data Center Name', + `Ip` varchar(32) NOT NULL DEFAULT '' COMMENT 'instance ip', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `Instance_IX_UNIQUE_KEY` (`AppId`,`ClusterName`,`Ip`,`DataCenter`), + KEY `Instance_IX_IP` (`Ip`), + KEY `Instance_IX_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='使用配置的应用实例'; + + + +-- Dump of table instanceconfig +-- ------------------------------------------------------------ + + +CREATE TABLE `InstanceConfig` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `InstanceId` int(11) unsigned DEFAULT NULL COMMENT 'Instance Id', + `ConfigAppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'Config App Id', + `ConfigClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Config Cluster Name', + `ConfigNamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Config Namespace Name', + `ReleaseKey` varchar(64) NOT NULL DEFAULT '' COMMENT '发布的Key', + `ReleaseDeliveryTime` timestamp NULL DEFAULT NULL COMMENT '配置获取时间', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `InstanceConfig_IX_UNIQUE_KEY` (`InstanceId`,`ConfigAppId`,`ConfigNamespaceName`), + KEY `InstanceConfig_IX_ReleaseKey` (`ReleaseKey`), + KEY `InstanceConfig_IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `InstanceConfig_IX_Valid_Namespace` (`ConfigAppId`,`ConfigClusterName`,`ConfigNamespaceName`,`DataChange_LastTime`) +) COMMENT='应用实例的配置信息'; + + + +-- Dump of table item +-- ------------------------------------------------------------ + + +CREATE TABLE `Item` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `NamespaceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '集群NamespaceId', + `Key` varchar(128) NOT NULL DEFAULT 'default' COMMENT '配置项Key', + `Type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '配置项类型,0: String,1: Number,2: Boolean,3: JSON', + `Value` longtext NOT NULL COMMENT '配置项值', + `Comment` varchar(1024) DEFAULT '' COMMENT '注释', + `LineNum` int(10) unsigned DEFAULT '0' COMMENT '行号', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `Item_IX_GroupId` (`NamespaceId`), + KEY `Item_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='配置项目'; + + + +-- Dump of table namespace +-- ------------------------------------------------------------ + + +CREATE TABLE `Namespace` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'Cluster Name', + `NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'Namespace Name', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `Namespace_UK_AppId_ClusterName_NamespaceName_DeletedAt` (`AppId`,`ClusterName`,`NamespaceName`,`DeletedAt`), + KEY `Namespace_DataChange_LastTime` (`DataChange_LastTime`), + KEY `Namespace_IX_NamespaceName` (`NamespaceName`) +) COMMENT='命名空间'; + + + +-- Dump of table namespacelock +-- ------------------------------------------------------------ + + +CREATE TABLE `NamespaceLock` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id', + `NamespaceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '集群NamespaceId', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + `IsDeleted` boolean DEFAULT FALSE COMMENT '软删除', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + PRIMARY KEY (`Id`), + UNIQUE KEY `NamespaceLock_UK_NamespaceId_DeletedAt` (`NamespaceId`,`DeletedAt`), + KEY `NamespaceLock_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='namespace的编辑锁'; + + + +-- Dump of table release +-- ------------------------------------------------------------ + + +CREATE TABLE `Release` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `ReleaseKey` varchar(64) NOT NULL DEFAULT '' COMMENT '发布的Key', + `Name` varchar(64) NOT NULL DEFAULT 'default' COMMENT '发布名字', + `Comment` varchar(256) DEFAULT NULL COMMENT '发布说明', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'namespaceName', + `Configurations` longtext NOT NULL COMMENT '发布配置', + `IsAbandoned` boolean NOT NULL DEFAULT FALSE COMMENT '是否废弃', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `Release_UK_ReleaseKey_DeletedAt` (`ReleaseKey`,`DeletedAt`), + KEY `Release_AppId_ClusterName_GroupName` (`AppId`,`ClusterName`,`NamespaceName`), + KEY `Release_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='发布'; + + +-- Dump of table releasehistory +-- ------------------------------------------------------------ + + +CREATE TABLE `ReleaseHistory` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `NamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'namespaceName', + `BranchName` varchar(32) NOT NULL DEFAULT 'default' COMMENT '发布分支名', + `ReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '关联的Release Id', + `PreviousReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '前一次发布的ReleaseId', + `Operation` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '发布类型,0: 普通发布,1: 回滚,2: 灰度发布,3: 灰度规则更新,4: 灰度合并回主分支发布,5: 主分支发布灰度自动发布,6: 主分支回滚灰度自动发布,7: 放弃灰度', + `OperationContext` longtext NOT NULL COMMENT '发布上下文信息', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `ReleaseHistory_IX_Namespace` (`AppId`,`ClusterName`,`NamespaceName`,`BranchName`), + KEY `ReleaseHistory_IX_ReleaseId` (`ReleaseId`), + KEY `ReleaseHistory_IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `ReleaseHistory_IX_PreviousReleaseId` (`PreviousReleaseId`) +) COMMENT='发布历史'; + + +-- Dump of table releasemessage +-- ------------------------------------------------------------ + + +CREATE TABLE `ReleaseMessage` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Message` varchar(1024) NOT NULL DEFAULT '' COMMENT '发布的消息内容', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `ReleaseMessage_DataChange_LastTime` (`DataChange_LastTime`), + KEY `ReleaseMessage_IX_Message` (`Message`) +) COMMENT='发布消息'; + + + +-- Dump of table serverconfig +-- ------------------------------------------------------------ + + +CREATE TABLE `ServerConfig` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Key` varchar(64) NOT NULL DEFAULT 'default' COMMENT '配置项Key', + `Cluster` varchar(32) NOT NULL DEFAULT 'default' COMMENT '配置对应的集群,default为不针对特定的集群', + `Value` varchar(2048) NOT NULL DEFAULT 'default' COMMENT '配置项值', + `Comment` varchar(1024) DEFAULT '' COMMENT '注释', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `ServerConfig_UK_Key_Cluster_DeletedAt` (`Key`,`Cluster`,`DeletedAt`), + KEY `ServerConfig_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='配置服务自身配置'; + +-- Dump of table accesskey +-- ------------------------------------------------------------ + + +CREATE TABLE `AccessKey` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Secret` varchar(128) NOT NULL DEFAULT '' COMMENT 'Secret', + `IsEnabled` boolean NOT NULL DEFAULT FALSE COMMENT '1: enabled, 0: disabled', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `AccessKey_UK_AppId_Secret_DeletedAt` (`AppId`,`Secret`,`DeletedAt`), + KEY `AccessKey_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='访问密钥'; + + +-- Dump of table serviceregistry +-- ------------------------------------------------------------ + + +CREATE TABLE `ServiceRegistry` ( + `Id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ServiceName` VARCHAR(64) NOT NULL COMMENT '服务名', + `Uri` VARCHAR(64) NOT NULL COMMENT '服务地址', + `Cluster` VARCHAR(64) NOT NULL COMMENT '集群,可以用来标识apollo.cluster或者网络分区', + `Metadata` VARCHAR(1024) NOT NULL DEFAULT '{}' COMMENT '元数据,key value结构的json object,为了方面后面扩展功能而不需要修改表结构', + `DataChange_CreatedTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE INDEX `IX_UNIQUE_KEY` (`ServiceName`, `Uri`), + INDEX `IX_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='注册中心'; + +-- Dump of table AuditLog +-- ------------------------------------------------------------ + + +CREATE TABLE `AuditLog` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `TraceId` varchar(32) NOT NULL DEFAULT '' COMMENT '链路全局唯一ID', + `SpanId` varchar(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `ParentSpanId` varchar(32) DEFAULT NULL COMMENT '父跨度ID', + `FollowsFromSpanId` varchar(32) DEFAULT NULL COMMENT '上一个兄弟跨度ID', + `Operator` varchar(64) NOT NULL DEFAULT 'anonymous' COMMENT '操作人', + `OpType` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型', + `OpName` varchar(150) NOT NULL DEFAULT 'default' COMMENT '操作名称', + `Description` varchar(200) DEFAULT NULL COMMENT '备注', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `AuditLog_IX_TraceId` (`TraceId`), + KEY `AuditLog_IX_OpName` (`OpName`), + KEY `AuditLog_IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `AuditLog_IX_Operator` (`Operator`) +) COMMENT='审计日志表'; + +-- Dump of table AuditLogDataInfluence +-- ------------------------------------------------------------ + + +CREATE TABLE `AuditLogDataInfluence` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `SpanId` char(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `InfluenceEntityId` varchar(50) NOT NULL DEFAULT '0' COMMENT '记录ID', + `InfluenceEntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名', + `FieldName` varchar(50) DEFAULT NULL COMMENT '字段名称', + `FieldOldValue` varchar(500) DEFAULT NULL COMMENT '字段旧值', + `FieldNewValue` varchar(500) DEFAULT NULL COMMENT '字段新值', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `AuditLogDataInfluence_IX_SpanId` (`SpanId`), + KEY `AuditLogDataInfluence_IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `AuditLogDataInfluence_IX_EntityId` (`InfluenceEntityId`) +) COMMENT='审计日志数据变动表'; + +-- Config +-- ------------------------------------------------------------ +INSERT INTO `ServerConfig` (`Key`, `Cluster`, `Value`, `Comment`) +VALUES + ('eureka.service.url', 'default', 'http://localhost:8080/eureka/', 'Eureka服务Url,多个service以英文逗号分隔'), + ('namespace.lock.switch', 'default', 'false', '一次发布只能有一个人修改开关'), + ('item.key.length.limit', 'default', '128', 'item key 最大长度限制'), + ('item.value.length.limit', 'default', '20000', 'item value最大长度限制'), + ('config-service.cache.enabled', 'default', 'false', 'ConfigService是否开启缓存,开启后能提高性能,但是会增大内存消耗!'); + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== + +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/scripts/sql/profiles/h2-default/apolloportaldb.sql b/scripts/sql/profiles/h2-default/apolloportaldb.sql new file mode 100644 index 00000000000..e1be67d3d6e --- /dev/null +++ b/scripts/sql/profiles/h2-default/apolloportaldb.sql @@ -0,0 +1,445 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- + +-- H2 Function +-- ------------------------------------------------------------ +CREATE ALIAS IF NOT EXISTS UNIX_TIMESTAMP FOR "com.ctrip.framework.apollo.common.jpa.H2Function.unixTimestamp"; + +-- + +-- Dump of table app +-- ------------------------------------------------------------ + + +CREATE TABLE `App` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名', + `OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id', + `OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字', + `OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName', + `OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `App_UK_AppId_DeletedAt` (`AppId`,`DeletedAt`), + KEY `App_DataChange_LastTime` (`DataChange_LastTime`), + KEY `App_IX_Name` (`Name`) +) COMMENT='应用表'; + + + +-- Dump of table appnamespace +-- ------------------------------------------------------------ + + +CREATE TABLE `AppNamespace` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Name` varchar(32) NOT NULL DEFAULT '' COMMENT 'namespace名字,注意,需要全局唯一', + `AppId` varchar(64) NOT NULL DEFAULT '' COMMENT 'app id', + `Format` varchar(32) NOT NULL DEFAULT 'properties' COMMENT 'namespace的format类型', + `IsPublic` boolean NOT NULL DEFAULT FALSE COMMENT 'namespace是否为公共', + `Comment` varchar(64) NOT NULL DEFAULT '' COMMENT '注释', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `AppNamespace_UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`), + KEY `AppNamespace_Name_AppId` (`Name`,`AppId`), + KEY `AppNamespace_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='应用namespace定义'; + + + +-- Dump of table consumer +-- ------------------------------------------------------------ + + +CREATE TABLE `Consumer` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名', + `OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id', + `OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字', + `OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName', + `OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `Consumer_UK_AppId_DeletedAt` (`AppId`,`DeletedAt`), + KEY `Consumer_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='开放API消费者'; + + + +-- Dump of table consumeraudit +-- ------------------------------------------------------------ + + +CREATE TABLE `ConsumerAudit` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'Consumer Id', + `Uri` varchar(1024) NOT NULL DEFAULT '' COMMENT '访问的Uri', + `Method` varchar(16) NOT NULL DEFAULT '' COMMENT '访问的Method', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `ConsumerAudit_IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `ConsumerAudit_IX_ConsumerId` (`ConsumerId`) +) COMMENT='consumer审计表'; + + + +-- Dump of table consumerrole +-- ------------------------------------------------------------ + + +CREATE TABLE `ConsumerRole` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'Consumer Id', + `RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `ConsumerRole_UK_ConsumerId_RoleId_DeletedAt` (`ConsumerId`,`RoleId`,`DeletedAt`), + KEY `ConsumerRole_IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `ConsumerRole_IX_RoleId` (`RoleId`) +) COMMENT='consumer和role的绑定表'; + + + +-- Dump of table consumertoken +-- ------------------------------------------------------------ + + +CREATE TABLE `ConsumerToken` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'ConsumerId', + `Token` varchar(128) NOT NULL DEFAULT '' COMMENT 'token', + `Expires` datetime NOT NULL DEFAULT '2099-01-01 00:00:00' COMMENT 'token失效时间', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `ConsumerToken_UK_Token_DeletedAt` (`Token`,`DeletedAt`), + KEY `ConsumerToken_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='consumer token表'; + +-- Dump of table favorite +-- ------------------------------------------------------------ + + +CREATE TABLE `Favorite` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `UserId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '收藏的用户', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Position` int(32) NOT NULL DEFAULT '10000' COMMENT '收藏顺序', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `Favorite_UK_UserId_AppId_DeletedAt` (`UserId`,`AppId`,`DeletedAt`), + KEY `Favorite_AppId` (`AppId`), + KEY `Favorite_DataChange_LastTime` (`DataChange_LastTime`) +) AUTO_INCREMENT=23 COMMENT='应用收藏表'; + +-- Dump of table permission +-- ------------------------------------------------------------ + + +CREATE TABLE `Permission` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `PermissionType` varchar(32) NOT NULL DEFAULT '' COMMENT '权限类型', + `TargetId` varchar(256) NOT NULL DEFAULT '' COMMENT '权限对象类型', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `Permission_UK_TargetId_PermissionType_DeletedAt` (`TargetId`,`PermissionType`,`DeletedAt`), + KEY `Permission_IX_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='permission表'; + + + +-- Dump of table role +-- ------------------------------------------------------------ + + +CREATE TABLE `Role` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `RoleName` varchar(256) NOT NULL DEFAULT '' COMMENT 'Role name', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `Role_UK_RoleName_DeletedAt` (`RoleName`,`DeletedAt`), + KEY `Role_IX_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='角色表'; + + + +-- Dump of table rolepermission +-- ------------------------------------------------------------ + + +CREATE TABLE `RolePermission` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id', + `PermissionId` int(10) unsigned DEFAULT NULL COMMENT 'Permission Id', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `RolePermission_UK_RoleId_PermissionId_DeletedAt` (`RoleId`,`PermissionId`,`DeletedAt`), + KEY `RolePermission_IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `RolePermission_IX_PermissionId` (`PermissionId`) +) COMMENT='角色和权限的绑定表'; + + + +-- Dump of table serverconfig +-- ------------------------------------------------------------ + + +CREATE TABLE `ServerConfig` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Key` varchar(64) NOT NULL DEFAULT 'default' COMMENT '配置项Key', + `Value` varchar(2048) NOT NULL DEFAULT 'default' COMMENT '配置项值', + `Comment` varchar(1024) DEFAULT '' COMMENT '注释', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `ServerConfig_UK_Key_DeletedAt` (`Key`,`DeletedAt`), + KEY `ServerConfig_DataChange_LastTime` (`DataChange_LastTime`) +) COMMENT='配置服务自身配置'; + + + +-- Dump of table userrole +-- ------------------------------------------------------------ + + +CREATE TABLE `UserRole` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `UserId` varchar(128) DEFAULT '' COMMENT '用户身份标识', + `RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UserRole_UK_UserId_RoleId_DeletedAt` (`UserId`,`RoleId`,`DeletedAt`), + KEY `UserRole_IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `UserRole_IX_RoleId` (`RoleId`) +) COMMENT='用户和role的绑定表'; + +-- Dump of table Users +-- ------------------------------------------------------------ + + +CREATE TABLE `Users` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Username` varchar(64) NOT NULL DEFAULT 'default' COMMENT '用户登录账户', + `Password` varchar(512) NOT NULL DEFAULT 'default' COMMENT '密码', + `UserDisplayName` varchar(512) NOT NULL DEFAULT 'default' COMMENT '用户名称', + `Email` varchar(64) NOT NULL DEFAULT 'default' COMMENT '邮箱地址', + `Enabled` tinyint(4) DEFAULT NULL COMMENT '是否有效', + PRIMARY KEY (`Id`), + UNIQUE KEY `Users_UK_Username` (`Username`) +) COMMENT='用户表'; + + +-- Dump of table Authorities +-- ------------------------------------------------------------ + + +CREATE TABLE `Authorities` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Username` varchar(64) NOT NULL, + `Authority` varchar(50) NOT NULL, + PRIMARY KEY (`Id`) +) ; + +-- spring session (https://github.com/spring-projects/spring-session/blob/faee8f1bdb8822a5653a81eba838dddf224d92d6/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-mysql.sql) +-- Dump of table SPRING_SESSION +-- ------------------------------------------------------------ + + +CREATE TABLE `SPRING_SESSION` ( + `PRIMARY_ID` char(36) NOT NULL, + `SESSION_ID` char(36) NOT NULL, + `CREATION_TIME` bigint NOT NULL, + `LAST_ACCESS_TIME` bigint NOT NULL, + `MAX_INACTIVE_INTERVAL` int NOT NULL, + `EXPIRY_TIME` bigint NOT NULL, + `PRINCIPAL_NAME` varchar(100) DEFAULT NULL, + PRIMARY KEY (`PRIMARY_ID`), + UNIQUE KEY `SPRING_SESSION_SPRING_SESSION_IX1` (`SESSION_ID`), + KEY `SPRING_SESSION_SPRING_SESSION_IX2` (`EXPIRY_TIME`), + KEY `SPRING_SESSION_SPRING_SESSION_IX3` (`PRINCIPAL_NAME`) +) ; + +-- Dump of table SPRING_SESSION_ATTRIBUTES +-- ------------------------------------------------------------ + + +CREATE TABLE `SPRING_SESSION_ATTRIBUTES` ( + `SESSION_PRIMARY_ID` char(36) NOT NULL, + `ATTRIBUTE_NAME` varchar(200) NOT NULL, + `ATTRIBUTE_BYTES` blob NOT NULL, + PRIMARY KEY (`SESSION_PRIMARY_ID`,`ATTRIBUTE_NAME`), + CONSTRAINT `SPRING_SESSION_ATTRIBUTES_FK` FOREIGN KEY (`SESSION_PRIMARY_ID`) REFERENCES `SPRING_SESSION` (`PRIMARY_ID`) ON DELETE CASCADE +) ; + +-- Dump of table AuditLog +-- ------------------------------------------------------------ + + +CREATE TABLE `AuditLog` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `TraceId` varchar(32) NOT NULL DEFAULT '' COMMENT '链路全局唯一ID', + `SpanId` varchar(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `ParentSpanId` varchar(32) DEFAULT NULL COMMENT '父跨度ID', + `FollowsFromSpanId` varchar(32) DEFAULT NULL COMMENT '上一个兄弟跨度ID', + `Operator` varchar(64) NOT NULL DEFAULT 'anonymous' COMMENT '操作人', + `OpType` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型', + `OpName` varchar(150) NOT NULL DEFAULT 'default' COMMENT '操作名称', + `Description` varchar(200) DEFAULT NULL COMMENT '备注', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `AuditLog_IX_TraceId` (`TraceId`), + KEY `AuditLog_IX_OpName` (`OpName`), + KEY `AuditLog_IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `AuditLog_IX_Operator` (`Operator`) +) COMMENT='审计日志表'; + +-- Dump of table AuditLogDataInfluence +-- ------------------------------------------------------------ + + +CREATE TABLE `AuditLogDataInfluence` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `SpanId` char(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `InfluenceEntityId` varchar(50) NOT NULL DEFAULT '0' COMMENT '记录ID', + `InfluenceEntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名', + `FieldName` varchar(50) DEFAULT NULL COMMENT '字段名称', + `FieldOldValue` varchar(500) DEFAULT NULL COMMENT '字段旧值', + `FieldNewValue` varchar(500) DEFAULT NULL COMMENT '字段新值', + `IsDeleted` boolean NOT NULL DEFAULT FALSE COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `AuditLogDataInfluence_IX_SpanId` (`SpanId`), + KEY `AuditLogDataInfluence_IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `AuditLogDataInfluence_IX_EntityId` (`InfluenceEntityId`) +) COMMENT='审计日志数据变动表'; + +-- Config +-- ------------------------------------------------------------ +INSERT INTO `ServerConfig` (`Key`, `Value`, `Comment`) +VALUES + ('apollo.portal.envs', 'dev', '可支持的环境列表'), + ('organizations', '[{"orgId":"TEST1","orgName":"样例部门1"},{"orgId":"TEST2","orgName":"样例部门2"}]', '部门列表'), + ('superAdmin', 'apollo', 'Portal超级管理员'), + ('api.readTimeout', '10000', 'http接口read timeout'), + ('consumer.token.salt', 'someSalt', 'consumer token salt'), + ('admin.createPrivateNamespace.switch', 'true', '是否允许项目管理员创建私有namespace'), + ('configView.memberOnly.envs', 'pro', '只对项目成员显示配置信息的环境列表,多个env以英文逗号分隔'), + ('apollo.portal.meta.servers', '{}', '各环境Meta Service列表'); + + +INSERT INTO `Users` (`Username`, `Password`, `UserDisplayName`, `Email`, `Enabled`) +VALUES + ('apollo', '$2a$10$7r20uS.BQ9uBpf3Baj3uQOZvMVvB1RN3PYoKE94gtz2.WAOuiiwXS', 'apollo', 'apollo@acme.com', 1); + +INSERT INTO `Authorities` (`Username`, `Authority`) VALUES ('apollo', 'ROLE_user'); + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== + +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/scripts/sql/profiles/h2-default/delta/v220-v230/apolloconfigdb-v220-v230.sql b/scripts/sql/profiles/h2-default/delta/v220-v230/apolloconfigdb-v220-v230.sql new file mode 100644 index 00000000000..ec703d5aa80 --- /dev/null +++ b/scripts/sql/profiles/h2-default/delta/v220-v230/apolloconfigdb-v220-v230.sql @@ -0,0 +1,43 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v2.2.0 to v2.3.0 + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- + +-- H2 Function +-- ------------------------------------------------------------ +CREATE ALIAS IF NOT EXISTS UNIX_TIMESTAMP FOR "com.ctrip.framework.apollo.common.jpa.H2Function.unixTimestamp"; + +-- + +ALTER TABLE `Cluster` ADD COLUMN `Comment` varchar(64) DEFAULT NULL COMMENT '备注'; + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== diff --git a/scripts/sql/profiles/h2-default/delta/v220-v230/apolloportaldb-v220-v230.sql b/scripts/sql/profiles/h2-default/delta/v220-v230/apolloportaldb-v220-v230.sql new file mode 100644 index 00000000000..80971e7e15c --- /dev/null +++ b/scripts/sql/profiles/h2-default/delta/v220-v230/apolloportaldb-v220-v230.sql @@ -0,0 +1,42 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v2.2.0 to v2.3.0 + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- + +-- H2 Function +-- ------------------------------------------------------------ +CREATE ALIAS IF NOT EXISTS UNIX_TIMESTAMP FOR "com.ctrip.framework.apollo.common.jpa.H2Function.unixTimestamp"; + +-- + + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== diff --git a/scripts/sql/profiles/mysql-database-not-specified/apolloconfigdb.sql b/scripts/sql/profiles/mysql-database-not-specified/apolloconfigdb.sql new file mode 100644 index 00000000000..4d2e0c19118 --- /dev/null +++ b/scripts/sql/profiles/mysql-database-not-specified/apolloconfigdb.sql @@ -0,0 +1,515 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- +-- + +-- Dump of table app +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `App`; + +CREATE TABLE `App` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名', + `OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id', + `OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字', + `OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName', + `OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_DeletedAt` (`AppId`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Name` (`Name`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用表'; + + + +-- Dump of table appnamespace +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AppNamespace`; + +CREATE TABLE `AppNamespace` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Name` varchar(32) NOT NULL DEFAULT '' COMMENT 'namespace名字,注意,需要全局唯一', + `AppId` varchar(64) NOT NULL DEFAULT '' COMMENT 'app id', + `Format` varchar(32) NOT NULL DEFAULT 'properties' COMMENT 'namespace的format类型', + `IsPublic` bit(1) NOT NULL DEFAULT b'0' COMMENT 'namespace是否为公共', + `Comment` varchar(64) NOT NULL DEFAULT '' COMMENT '注释', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`), + KEY `Name_AppId` (`Name`,`AppId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用namespace定义'; + + + +-- Dump of table audit +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Audit`; + +CREATE TABLE `Audit` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `EntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名', + `EntityId` int(10) unsigned DEFAULT NULL COMMENT '记录ID', + `OpName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型', + `Comment` varchar(500) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='日志审计表'; + + + +-- Dump of table cluster +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Cluster`; + +CREATE TABLE `Cluster` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Name` varchar(32) NOT NULL DEFAULT '' COMMENT '集群名字', + `AppId` varchar(64) NOT NULL DEFAULT '' COMMENT 'App id', + `ParentClusterId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父cluster', + `Comment` varchar(64) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`), + KEY `IX_ParentClusterId` (`ParentClusterId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='集群'; + + + +-- Dump of table commit +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Commit`; + +CREATE TABLE `Commit` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `ChangeSets` longtext NOT NULL COMMENT '修改变更集', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'namespaceName', + `Comment` varchar(500) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `AppId` (`AppId`), + KEY `ClusterName` (`ClusterName`(191)), + KEY `NamespaceName` (`NamespaceName`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='commit 历史表'; + +-- Dump of table grayreleaserule +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `GrayReleaseRule`; + +CREATE TABLE `GrayReleaseRule` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Cluster Name', + `NamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Namespace Name', + `BranchName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'branch name', + `Rules` varchar(16000) DEFAULT '[]' COMMENT '灰度规则', + `ReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '灰度对应的release', + `BranchStatus` tinyint(2) DEFAULT '1' COMMENT '灰度分支状态: 0:删除分支,1:正在使用的规则 2:全量发布', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Namespace` (`AppId`,`ClusterName`,`NamespaceName`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='灰度规则表'; + + +-- Dump of table instance +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Instance`; + +CREATE TABLE `Instance` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `DataCenter` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'Data Center Name', + `Ip` varchar(32) NOT NULL DEFAULT '' COMMENT 'instance ip', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `IX_UNIQUE_KEY` (`AppId`,`ClusterName`,`Ip`,`DataCenter`), + KEY `IX_IP` (`Ip`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='使用配置的应用实例'; + + + +-- Dump of table instanceconfig +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `InstanceConfig`; + +CREATE TABLE `InstanceConfig` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `InstanceId` int(11) unsigned DEFAULT NULL COMMENT 'Instance Id', + `ConfigAppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'Config App Id', + `ConfigClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Config Cluster Name', + `ConfigNamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Config Namespace Name', + `ReleaseKey` varchar(64) NOT NULL DEFAULT '' COMMENT '发布的Key', + `ReleaseDeliveryTime` timestamp NULL DEFAULT NULL COMMENT '配置获取时间', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `IX_UNIQUE_KEY` (`InstanceId`,`ConfigAppId`,`ConfigNamespaceName`), + KEY `IX_ReleaseKey` (`ReleaseKey`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Valid_Namespace` (`ConfigAppId`,`ConfigClusterName`,`ConfigNamespaceName`,`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用实例的配置信息'; + + + +-- Dump of table item +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Item`; + +CREATE TABLE `Item` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `NamespaceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '集群NamespaceId', + `Key` varchar(128) NOT NULL DEFAULT 'default' COMMENT '配置项Key', + `Type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '配置项类型,0: String,1: Number,2: Boolean,3: JSON', + `Value` longtext NOT NULL COMMENT '配置项值', + `Comment` varchar(1024) DEFAULT '' COMMENT '注释', + `LineNum` int(10) unsigned DEFAULT '0' COMMENT '行号', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_GroupId` (`NamespaceId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置项目'; + + + +-- Dump of table namespace +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Namespace`; + +CREATE TABLE `Namespace` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'Cluster Name', + `NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'Namespace Name', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_ClusterName_NamespaceName_DeletedAt` (`AppId`,`ClusterName`(191),`NamespaceName`(191),`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_NamespaceName` (`NamespaceName`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='命名空间'; + + + +-- Dump of table namespacelock +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `NamespaceLock`; + +CREATE TABLE `NamespaceLock` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id', + `NamespaceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '集群NamespaceId', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + `IsDeleted` bit(1) DEFAULT b'0' COMMENT '软删除', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_NamespaceId_DeletedAt` (`NamespaceId`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='namespace的编辑锁'; + + + +-- Dump of table release +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Release`; + +CREATE TABLE `Release` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `ReleaseKey` varchar(64) NOT NULL DEFAULT '' COMMENT '发布的Key', + `Name` varchar(64) NOT NULL DEFAULT 'default' COMMENT '发布名字', + `Comment` varchar(256) DEFAULT NULL COMMENT '发布说明', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'namespaceName', + `Configurations` longtext NOT NULL COMMENT '发布配置', + `IsAbandoned` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否废弃', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_ReleaseKey_DeletedAt` (`ReleaseKey`,`DeletedAt`), + KEY `AppId_ClusterName_GroupName` (`AppId`,`ClusterName`(191),`NamespaceName`(191)), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布'; + + +-- Dump of table releasehistory +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ReleaseHistory`; + +CREATE TABLE `ReleaseHistory` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `NamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'namespaceName', + `BranchName` varchar(32) NOT NULL DEFAULT 'default' COMMENT '发布分支名', + `ReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '关联的Release Id', + `PreviousReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '前一次发布的ReleaseId', + `Operation` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '发布类型,0: 普通发布,1: 回滚,2: 灰度发布,3: 灰度规则更新,4: 灰度合并回主分支发布,5: 主分支发布灰度自动发布,6: 主分支回滚灰度自动发布,7: 放弃灰度', + `OperationContext` longtext NOT NULL COMMENT '发布上下文信息', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_Namespace` (`AppId`,`ClusterName`,`NamespaceName`,`BranchName`), + KEY `IX_ReleaseId` (`ReleaseId`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_PreviousReleaseId` (`PreviousReleaseId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布历史'; + + +-- Dump of table releasemessage +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ReleaseMessage`; + +CREATE TABLE `ReleaseMessage` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Message` varchar(1024) NOT NULL DEFAULT '' COMMENT '发布的消息内容', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Message` (`Message`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布消息'; + + + +-- Dump of table serverconfig +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ServerConfig`; + +CREATE TABLE `ServerConfig` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Key` varchar(64) NOT NULL DEFAULT 'default' COMMENT '配置项Key', + `Cluster` varchar(32) NOT NULL DEFAULT 'default' COMMENT '配置对应的集群,default为不针对特定的集群', + `Value` varchar(2048) NOT NULL DEFAULT 'default' COMMENT '配置项值', + `Comment` varchar(1024) DEFAULT '' COMMENT '注释', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_Key_Cluster_DeletedAt` (`Key`,`Cluster`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置服务自身配置'; + +-- Dump of table accesskey +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AccessKey`; + +CREATE TABLE `AccessKey` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Secret` varchar(128) NOT NULL DEFAULT '' COMMENT 'Secret', + `IsEnabled` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: enabled, 0: disabled', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_Secret_DeletedAt` (`AppId`,`Secret`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问密钥'; + + +-- Dump of table serviceregistry +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ServiceRegistry`; + +CREATE TABLE `ServiceRegistry` ( + `Id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ServiceName` VARCHAR(64) NOT NULL COMMENT '服务名', + `Uri` VARCHAR(64) NOT NULL COMMENT '服务地址', + `Cluster` VARCHAR(64) NOT NULL COMMENT '集群,可以用来标识apollo.cluster或者网络分区', + `Metadata` VARCHAR(1024) NOT NULL DEFAULT '{}' COMMENT '元数据,key value结构的json object,为了方面后面扩展功能而不需要修改表结构', + `DataChange_CreatedTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE INDEX `IX_UNIQUE_KEY` (`ServiceName`, `Uri`), + INDEX `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='注册中心'; + +-- Dump of table AuditLog +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AuditLog`; + +CREATE TABLE `AuditLog` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `TraceId` varchar(32) NOT NULL DEFAULT '' COMMENT '链路全局唯一ID', + `SpanId` varchar(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `ParentSpanId` varchar(32) DEFAULT NULL COMMENT '父跨度ID', + `FollowsFromSpanId` varchar(32) DEFAULT NULL COMMENT '上一个兄弟跨度ID', + `Operator` varchar(64) NOT NULL DEFAULT 'anonymous' COMMENT '操作人', + `OpType` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型', + `OpName` varchar(150) NOT NULL DEFAULT 'default' COMMENT '操作名称', + `Description` varchar(200) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_TraceId` (`TraceId`), + KEY `IX_OpName` (`OpName`), + KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `IX_Operator` (`Operator`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志表'; + +-- Dump of table AuditLogDataInfluence +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AuditLogDataInfluence`; + +CREATE TABLE `AuditLogDataInfluence` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `SpanId` char(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `InfluenceEntityId` varchar(50) NOT NULL DEFAULT '0' COMMENT '记录ID', + `InfluenceEntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名', + `FieldName` varchar(50) DEFAULT NULL COMMENT '字段名称', + `FieldOldValue` varchar(500) DEFAULT NULL COMMENT '字段旧值', + `FieldNewValue` varchar(500) DEFAULT NULL COMMENT '字段新值', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_SpanId` (`SpanId`), + KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `IX_EntityId` (`InfluenceEntityId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志数据变动表'; + +-- Config +-- ------------------------------------------------------------ +INSERT INTO `ServerConfig` (`Key`, `Cluster`, `Value`, `Comment`) +VALUES + ('eureka.service.url', 'default', 'http://localhost:8080/eureka/', 'Eureka服务Url,多个service以英文逗号分隔'), + ('namespace.lock.switch', 'default', 'false', '一次发布只能有一个人修改开关'), + ('item.key.length.limit', 'default', '128', 'item key 最大长度限制'), + ('item.value.length.limit', 'default', '20000', 'item value最大长度限制'), + ('config-service.cache.enabled', 'default', 'false', 'ConfigService是否开启缓存,开启后能提高性能,但是会增大内存消耗!'); + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== + +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/scripts/sql/profiles/mysql-database-not-specified/apolloportaldb.sql b/scripts/sql/profiles/mysql-database-not-specified/apolloportaldb.sql new file mode 100644 index 00000000000..e6de5a1a4a9 --- /dev/null +++ b/scripts/sql/profiles/mysql-database-not-specified/apolloportaldb.sql @@ -0,0 +1,458 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- +-- + +-- Dump of table app +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `App`; + +CREATE TABLE `App` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名', + `OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id', + `OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字', + `OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName', + `OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_DeletedAt` (`AppId`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Name` (`Name`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用表'; + + + +-- Dump of table appnamespace +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AppNamespace`; + +CREATE TABLE `AppNamespace` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Name` varchar(32) NOT NULL DEFAULT '' COMMENT 'namespace名字,注意,需要全局唯一', + `AppId` varchar(64) NOT NULL DEFAULT '' COMMENT 'app id', + `Format` varchar(32) NOT NULL DEFAULT 'properties' COMMENT 'namespace的format类型', + `IsPublic` bit(1) NOT NULL DEFAULT b'0' COMMENT 'namespace是否为公共', + `Comment` varchar(64) NOT NULL DEFAULT '' COMMENT '注释', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`), + KEY `Name_AppId` (`Name`,`AppId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用namespace定义'; + + + +-- Dump of table consumer +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Consumer`; + +CREATE TABLE `Consumer` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名', + `OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id', + `OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字', + `OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName', + `OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_DeletedAt` (`AppId`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放API消费者'; + + + +-- Dump of table consumeraudit +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ConsumerAudit`; + +CREATE TABLE `ConsumerAudit` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'Consumer Id', + `Uri` varchar(1024) NOT NULL DEFAULT '' COMMENT '访问的Uri', + `Method` varchar(16) NOT NULL DEFAULT '' COMMENT '访问的Method', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_ConsumerId` (`ConsumerId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer审计表'; + + + +-- Dump of table consumerrole +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ConsumerRole`; + +CREATE TABLE `ConsumerRole` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'Consumer Id', + `RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_ConsumerId_RoleId_DeletedAt` (`ConsumerId`,`RoleId`,`DeletedAt`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_RoleId` (`RoleId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer和role的绑定表'; + + + +-- Dump of table consumertoken +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ConsumerToken`; + +CREATE TABLE `ConsumerToken` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'ConsumerId', + `Token` varchar(128) NOT NULL DEFAULT '' COMMENT 'token', + `Expires` datetime NOT NULL DEFAULT '2099-01-01 00:00:00' COMMENT 'token失效时间', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_Token_DeletedAt` (`Token`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer token表'; + +-- Dump of table favorite +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Favorite`; + +CREATE TABLE `Favorite` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `UserId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '收藏的用户', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Position` int(32) NOT NULL DEFAULT '10000' COMMENT '收藏顺序', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_UserId_AppId_DeletedAt` (`UserId`,`AppId`,`DeletedAt`), + KEY `AppId` (`AppId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COMMENT='应用收藏表'; + +-- Dump of table permission +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Permission`; + +CREATE TABLE `Permission` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `PermissionType` varchar(32) NOT NULL DEFAULT '' COMMENT '权限类型', + `TargetId` varchar(256) NOT NULL DEFAULT '' COMMENT '权限对象类型', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_TargetId_PermissionType_DeletedAt` (`TargetId`,`PermissionType`,`DeletedAt`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='permission表'; + + + +-- Dump of table role +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Role`; + +CREATE TABLE `Role` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `RoleName` varchar(256) NOT NULL DEFAULT '' COMMENT 'Role name', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_RoleName_DeletedAt` (`RoleName`,`DeletedAt`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表'; + + + +-- Dump of table rolepermission +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `RolePermission`; + +CREATE TABLE `RolePermission` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id', + `PermissionId` int(10) unsigned DEFAULT NULL COMMENT 'Permission Id', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_RoleId_PermissionId_DeletedAt` (`RoleId`,`PermissionId`,`DeletedAt`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_PermissionId` (`PermissionId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色和权限的绑定表'; + + + +-- Dump of table serverconfig +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ServerConfig`; + +CREATE TABLE `ServerConfig` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Key` varchar(64) NOT NULL DEFAULT 'default' COMMENT '配置项Key', + `Value` varchar(2048) NOT NULL DEFAULT 'default' COMMENT '配置项值', + `Comment` varchar(1024) DEFAULT '' COMMENT '注释', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_Key_DeletedAt` (`Key`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置服务自身配置'; + + + +-- Dump of table userrole +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `UserRole`; + +CREATE TABLE `UserRole` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `UserId` varchar(128) DEFAULT '' COMMENT '用户身份标识', + `RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_UserId_RoleId_DeletedAt` (`UserId`,`RoleId`,`DeletedAt`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_RoleId` (`RoleId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户和role的绑定表'; + +-- Dump of table Users +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Users`; + +CREATE TABLE `Users` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Username` varchar(64) NOT NULL DEFAULT 'default' COMMENT '用户登录账户', + `Password` varchar(512) NOT NULL DEFAULT 'default' COMMENT '密码', + `UserDisplayName` varchar(512) NOT NULL DEFAULT 'default' COMMENT '用户名称', + `Email` varchar(64) NOT NULL DEFAULT 'default' COMMENT '邮箱地址', + `Enabled` tinyint(4) DEFAULT NULL COMMENT '是否有效', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_Username` (`Username`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; + + +-- Dump of table Authorities +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Authorities`; + +CREATE TABLE `Authorities` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Username` varchar(64) NOT NULL, + `Authority` varchar(50) NOT NULL, + PRIMARY KEY (`Id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- spring session (https://github.com/spring-projects/spring-session/blob/faee8f1bdb8822a5653a81eba838dddf224d92d6/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-mysql.sql) +-- Dump of table SPRING_SESSION +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `SPRING_SESSION`; + +CREATE TABLE `SPRING_SESSION` ( + `PRIMARY_ID` char(36) NOT NULL, + `SESSION_ID` char(36) NOT NULL, + `CREATION_TIME` bigint NOT NULL, + `LAST_ACCESS_TIME` bigint NOT NULL, + `MAX_INACTIVE_INTERVAL` int NOT NULL, + `EXPIRY_TIME` bigint NOT NULL, + `PRINCIPAL_NAME` varchar(100) DEFAULT NULL, + PRIMARY KEY (`PRIMARY_ID`), + UNIQUE KEY `SPRING_SESSION_IX1` (`SESSION_ID`), + KEY `SPRING_SESSION_IX2` (`EXPIRY_TIME`), + KEY `SPRING_SESSION_IX3` (`PRINCIPAL_NAME`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; + +-- Dump of table SPRING_SESSION_ATTRIBUTES +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `SPRING_SESSION_ATTRIBUTES`; + +CREATE TABLE `SPRING_SESSION_ATTRIBUTES` ( + `SESSION_PRIMARY_ID` char(36) NOT NULL, + `ATTRIBUTE_NAME` varchar(200) NOT NULL, + `ATTRIBUTE_BYTES` blob NOT NULL, + PRIMARY KEY (`SESSION_PRIMARY_ID`,`ATTRIBUTE_NAME`), + CONSTRAINT `SPRING_SESSION_ATTRIBUTES_FK` FOREIGN KEY (`SESSION_PRIMARY_ID`) REFERENCES `SPRING_SESSION` (`PRIMARY_ID`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; + +-- Dump of table AuditLog +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AuditLog`; + +CREATE TABLE `AuditLog` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `TraceId` varchar(32) NOT NULL DEFAULT '' COMMENT '链路全局唯一ID', + `SpanId` varchar(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `ParentSpanId` varchar(32) DEFAULT NULL COMMENT '父跨度ID', + `FollowsFromSpanId` varchar(32) DEFAULT NULL COMMENT '上一个兄弟跨度ID', + `Operator` varchar(64) NOT NULL DEFAULT 'anonymous' COMMENT '操作人', + `OpType` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型', + `OpName` varchar(150) NOT NULL DEFAULT 'default' COMMENT '操作名称', + `Description` varchar(200) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_TraceId` (`TraceId`), + KEY `IX_OpName` (`OpName`), + KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `IX_Operator` (`Operator`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志表'; + +-- Dump of table AuditLogDataInfluence +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AuditLogDataInfluence`; + +CREATE TABLE `AuditLogDataInfluence` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `SpanId` char(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `InfluenceEntityId` varchar(50) NOT NULL DEFAULT '0' COMMENT '记录ID', + `InfluenceEntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名', + `FieldName` varchar(50) DEFAULT NULL COMMENT '字段名称', + `FieldOldValue` varchar(500) DEFAULT NULL COMMENT '字段旧值', + `FieldNewValue` varchar(500) DEFAULT NULL COMMENT '字段新值', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_SpanId` (`SpanId`), + KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `IX_EntityId` (`InfluenceEntityId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志数据变动表'; + +-- Config +-- ------------------------------------------------------------ +INSERT INTO `ServerConfig` (`Key`, `Value`, `Comment`) +VALUES + ('apollo.portal.envs', 'dev', '可支持的环境列表'), + ('organizations', '[{"orgId":"TEST1","orgName":"样例部门1"},{"orgId":"TEST2","orgName":"样例部门2"}]', '部门列表'), + ('superAdmin', 'apollo', 'Portal超级管理员'), + ('api.readTimeout', '10000', 'http接口read timeout'), + ('consumer.token.salt', 'someSalt', 'consumer token salt'), + ('admin.createPrivateNamespace.switch', 'true', '是否允许项目管理员创建私有namespace'), + ('configView.memberOnly.envs', 'pro', '只对项目成员显示配置信息的环境列表,多个env以英文逗号分隔'), + ('apollo.portal.meta.servers', '{}', '各环境Meta Service列表'); + + +INSERT INTO `Users` (`Username`, `Password`, `UserDisplayName`, `Email`, `Enabled`) +VALUES + ('apollo', '$2a$10$7r20uS.BQ9uBpf3Baj3uQOZvMVvB1RN3PYoKE94gtz2.WAOuiiwXS', 'apollo', 'apollo@acme.com', 1); + +INSERT INTO `Authorities` (`Username`, `Authority`) VALUES ('apollo', 'ROLE_user'); + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== + +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/scripts/sql/profiles/mysql-database-not-specified/delta/v220-v230/apolloconfigdb-v220-v230.sql b/scripts/sql/profiles/mysql-database-not-specified/delta/v220-v230/apolloconfigdb-v220-v230.sql new file mode 100644 index 00000000000..3f659cea710 --- /dev/null +++ b/scripts/sql/profiles/mysql-database-not-specified/delta/v220-v230/apolloconfigdb-v220-v230.sql @@ -0,0 +1,39 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v2.2.0 to v2.3.0 + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- +-- + +ALTER TABLE `Cluster` + ADD COLUMN `Comment` varchar(64) DEFAULT NULL COMMENT '备注'; + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== diff --git a/scripts/sql/profiles/mysql-database-not-specified/delta/v220-v230/apolloportaldb-v220-v230.sql b/scripts/sql/profiles/mysql-database-not-specified/delta/v220-v230/apolloportaldb-v220-v230.sql new file mode 100644 index 00000000000..9f8443856a1 --- /dev/null +++ b/scripts/sql/profiles/mysql-database-not-specified/delta/v220-v230/apolloportaldb-v220-v230.sql @@ -0,0 +1,37 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v2.2.0 to v2.3.0 + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- +-- + + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== diff --git a/scripts/sql/profiles/mysql-default/apolloconfigdb.sql b/scripts/sql/profiles/mysql-default/apolloconfigdb.sql new file mode 100644 index 00000000000..3736ea599f3 --- /dev/null +++ b/scripts/sql/profiles/mysql-default/apolloconfigdb.sql @@ -0,0 +1,520 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- +-- +-- Create Database +-- ------------------------------------------------------------ +CREATE DATABASE IF NOT EXISTS ApolloConfigDB DEFAULT CHARACTER SET = utf8mb4; + +Use ApolloConfigDB; + +-- Dump of table app +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `App`; + +CREATE TABLE `App` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名', + `OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id', + `OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字', + `OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName', + `OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_DeletedAt` (`AppId`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Name` (`Name`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用表'; + + + +-- Dump of table appnamespace +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AppNamespace`; + +CREATE TABLE `AppNamespace` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Name` varchar(32) NOT NULL DEFAULT '' COMMENT 'namespace名字,注意,需要全局唯一', + `AppId` varchar(64) NOT NULL DEFAULT '' COMMENT 'app id', + `Format` varchar(32) NOT NULL DEFAULT 'properties' COMMENT 'namespace的format类型', + `IsPublic` bit(1) NOT NULL DEFAULT b'0' COMMENT 'namespace是否为公共', + `Comment` varchar(64) NOT NULL DEFAULT '' COMMENT '注释', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`), + KEY `Name_AppId` (`Name`,`AppId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用namespace定义'; + + + +-- Dump of table audit +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Audit`; + +CREATE TABLE `Audit` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `EntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名', + `EntityId` int(10) unsigned DEFAULT NULL COMMENT '记录ID', + `OpName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型', + `Comment` varchar(500) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='日志审计表'; + + + +-- Dump of table cluster +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Cluster`; + +CREATE TABLE `Cluster` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Name` varchar(32) NOT NULL DEFAULT '' COMMENT '集群名字', + `AppId` varchar(64) NOT NULL DEFAULT '' COMMENT 'App id', + `ParentClusterId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父cluster', + `Comment` varchar(64) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`), + KEY `IX_ParentClusterId` (`ParentClusterId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='集群'; + + + +-- Dump of table commit +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Commit`; + +CREATE TABLE `Commit` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `ChangeSets` longtext NOT NULL COMMENT '修改变更集', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'namespaceName', + `Comment` varchar(500) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `AppId` (`AppId`), + KEY `ClusterName` (`ClusterName`(191)), + KEY `NamespaceName` (`NamespaceName`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='commit 历史表'; + +-- Dump of table grayreleaserule +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `GrayReleaseRule`; + +CREATE TABLE `GrayReleaseRule` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Cluster Name', + `NamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Namespace Name', + `BranchName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'branch name', + `Rules` varchar(16000) DEFAULT '[]' COMMENT '灰度规则', + `ReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '灰度对应的release', + `BranchStatus` tinyint(2) DEFAULT '1' COMMENT '灰度分支状态: 0:删除分支,1:正在使用的规则 2:全量发布', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Namespace` (`AppId`,`ClusterName`,`NamespaceName`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='灰度规则表'; + + +-- Dump of table instance +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Instance`; + +CREATE TABLE `Instance` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `DataCenter` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'Data Center Name', + `Ip` varchar(32) NOT NULL DEFAULT '' COMMENT 'instance ip', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `IX_UNIQUE_KEY` (`AppId`,`ClusterName`,`Ip`,`DataCenter`), + KEY `IX_IP` (`Ip`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='使用配置的应用实例'; + + + +-- Dump of table instanceconfig +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `InstanceConfig`; + +CREATE TABLE `InstanceConfig` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `InstanceId` int(11) unsigned DEFAULT NULL COMMENT 'Instance Id', + `ConfigAppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'Config App Id', + `ConfigClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Config Cluster Name', + `ConfigNamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Config Namespace Name', + `ReleaseKey` varchar(64) NOT NULL DEFAULT '' COMMENT '发布的Key', + `ReleaseDeliveryTime` timestamp NULL DEFAULT NULL COMMENT '配置获取时间', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `IX_UNIQUE_KEY` (`InstanceId`,`ConfigAppId`,`ConfigNamespaceName`), + KEY `IX_ReleaseKey` (`ReleaseKey`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Valid_Namespace` (`ConfigAppId`,`ConfigClusterName`,`ConfigNamespaceName`,`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用实例的配置信息'; + + + +-- Dump of table item +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Item`; + +CREATE TABLE `Item` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `NamespaceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '集群NamespaceId', + `Key` varchar(128) NOT NULL DEFAULT 'default' COMMENT '配置项Key', + `Type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '配置项类型,0: String,1: Number,2: Boolean,3: JSON', + `Value` longtext NOT NULL COMMENT '配置项值', + `Comment` varchar(1024) DEFAULT '' COMMENT '注释', + `LineNum` int(10) unsigned DEFAULT '0' COMMENT '行号', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_GroupId` (`NamespaceId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置项目'; + + + +-- Dump of table namespace +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Namespace`; + +CREATE TABLE `Namespace` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'Cluster Name', + `NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'Namespace Name', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_ClusterName_NamespaceName_DeletedAt` (`AppId`,`ClusterName`(191),`NamespaceName`(191),`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_NamespaceName` (`NamespaceName`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='命名空间'; + + + +-- Dump of table namespacelock +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `NamespaceLock`; + +CREATE TABLE `NamespaceLock` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id', + `NamespaceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '集群NamespaceId', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + `IsDeleted` bit(1) DEFAULT b'0' COMMENT '软删除', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_NamespaceId_DeletedAt` (`NamespaceId`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='namespace的编辑锁'; + + + +-- Dump of table release +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Release`; + +CREATE TABLE `Release` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `ReleaseKey` varchar(64) NOT NULL DEFAULT '' COMMENT '发布的Key', + `Name` varchar(64) NOT NULL DEFAULT 'default' COMMENT '发布名字', + `Comment` varchar(256) DEFAULT NULL COMMENT '发布说明', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `NamespaceName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'namespaceName', + `Configurations` longtext NOT NULL COMMENT '发布配置', + `IsAbandoned` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否废弃', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_ReleaseKey_DeletedAt` (`ReleaseKey`,`DeletedAt`), + KEY `AppId_ClusterName_GroupName` (`AppId`,`ClusterName`(191),`NamespaceName`(191)), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布'; + + +-- Dump of table releasehistory +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ReleaseHistory`; + +CREATE TABLE `ReleaseHistory` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `ClusterName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'ClusterName', + `NamespaceName` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'namespaceName', + `BranchName` varchar(32) NOT NULL DEFAULT 'default' COMMENT '发布分支名', + `ReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '关联的Release Id', + `PreviousReleaseId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '前一次发布的ReleaseId', + `Operation` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '发布类型,0: 普通发布,1: 回滚,2: 灰度发布,3: 灰度规则更新,4: 灰度合并回主分支发布,5: 主分支发布灰度自动发布,6: 主分支回滚灰度自动发布,7: 放弃灰度', + `OperationContext` longtext NOT NULL COMMENT '发布上下文信息', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_Namespace` (`AppId`,`ClusterName`,`NamespaceName`,`BranchName`), + KEY `IX_ReleaseId` (`ReleaseId`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_PreviousReleaseId` (`PreviousReleaseId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布历史'; + + +-- Dump of table releasemessage +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ReleaseMessage`; + +CREATE TABLE `ReleaseMessage` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Message` varchar(1024) NOT NULL DEFAULT '' COMMENT '发布的消息内容', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Message` (`Message`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布消息'; + + + +-- Dump of table serverconfig +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ServerConfig`; + +CREATE TABLE `ServerConfig` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Key` varchar(64) NOT NULL DEFAULT 'default' COMMENT '配置项Key', + `Cluster` varchar(32) NOT NULL DEFAULT 'default' COMMENT '配置对应的集群,default为不针对特定的集群', + `Value` varchar(2048) NOT NULL DEFAULT 'default' COMMENT '配置项值', + `Comment` varchar(1024) DEFAULT '' COMMENT '注释', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_Key_Cluster_DeletedAt` (`Key`,`Cluster`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置服务自身配置'; + +-- Dump of table accesskey +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AccessKey`; + +CREATE TABLE `AccessKey` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Secret` varchar(128) NOT NULL DEFAULT '' COMMENT 'Secret', + `IsEnabled` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: enabled, 0: disabled', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_Secret_DeletedAt` (`AppId`,`Secret`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问密钥'; + + +-- Dump of table serviceregistry +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ServiceRegistry`; + +CREATE TABLE `ServiceRegistry` ( + `Id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ServiceName` VARCHAR(64) NOT NULL COMMENT '服务名', + `Uri` VARCHAR(64) NOT NULL COMMENT '服务地址', + `Cluster` VARCHAR(64) NOT NULL COMMENT '集群,可以用来标识apollo.cluster或者网络分区', + `Metadata` VARCHAR(1024) NOT NULL DEFAULT '{}' COMMENT '元数据,key value结构的json object,为了方面后面扩展功能而不需要修改表结构', + `DataChange_CreatedTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE INDEX `IX_UNIQUE_KEY` (`ServiceName`, `Uri`), + INDEX `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='注册中心'; + +-- Dump of table AuditLog +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AuditLog`; + +CREATE TABLE `AuditLog` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `TraceId` varchar(32) NOT NULL DEFAULT '' COMMENT '链路全局唯一ID', + `SpanId` varchar(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `ParentSpanId` varchar(32) DEFAULT NULL COMMENT '父跨度ID', + `FollowsFromSpanId` varchar(32) DEFAULT NULL COMMENT '上一个兄弟跨度ID', + `Operator` varchar(64) NOT NULL DEFAULT 'anonymous' COMMENT '操作人', + `OpType` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型', + `OpName` varchar(150) NOT NULL DEFAULT 'default' COMMENT '操作名称', + `Description` varchar(200) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_TraceId` (`TraceId`), + KEY `IX_OpName` (`OpName`), + KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `IX_Operator` (`Operator`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志表'; + +-- Dump of table AuditLogDataInfluence +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AuditLogDataInfluence`; + +CREATE TABLE `AuditLogDataInfluence` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `SpanId` char(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `InfluenceEntityId` varchar(50) NOT NULL DEFAULT '0' COMMENT '记录ID', + `InfluenceEntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名', + `FieldName` varchar(50) DEFAULT NULL COMMENT '字段名称', + `FieldOldValue` varchar(500) DEFAULT NULL COMMENT '字段旧值', + `FieldNewValue` varchar(500) DEFAULT NULL COMMENT '字段新值', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_SpanId` (`SpanId`), + KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `IX_EntityId` (`InfluenceEntityId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志数据变动表'; + +-- Config +-- ------------------------------------------------------------ +INSERT INTO `ServerConfig` (`Key`, `Cluster`, `Value`, `Comment`) +VALUES + ('eureka.service.url', 'default', 'http://localhost:8080/eureka/', 'Eureka服务Url,多个service以英文逗号分隔'), + ('namespace.lock.switch', 'default', 'false', '一次发布只能有一个人修改开关'), + ('item.key.length.limit', 'default', '128', 'item key 最大长度限制'), + ('item.value.length.limit', 'default', '20000', 'item value最大长度限制'), + ('config-service.cache.enabled', 'default', 'false', 'ConfigService是否开启缓存,开启后能提高性能,但是会增大内存消耗!'); + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== + +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/scripts/sql/profiles/mysql-default/apolloportaldb.sql b/scripts/sql/profiles/mysql-default/apolloportaldb.sql new file mode 100644 index 00000000000..264f948a53b --- /dev/null +++ b/scripts/sql/profiles/mysql-default/apolloportaldb.sql @@ -0,0 +1,463 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- +-- +-- Create Database +-- ------------------------------------------------------------ +CREATE DATABASE IF NOT EXISTS ApolloPortalDB DEFAULT CHARACTER SET = utf8mb4; + +Use ApolloPortalDB; + +-- Dump of table app +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `App`; + +CREATE TABLE `App` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名', + `OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id', + `OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字', + `OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName', + `OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_DeletedAt` (`AppId`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_Name` (`Name`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用表'; + + + +-- Dump of table appnamespace +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AppNamespace`; + +CREATE TABLE `AppNamespace` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `Name` varchar(32) NOT NULL DEFAULT '' COMMENT 'namespace名字,注意,需要全局唯一', + `AppId` varchar(64) NOT NULL DEFAULT '' COMMENT 'app id', + `Format` varchar(32) NOT NULL DEFAULT 'properties' COMMENT 'namespace的format类型', + `IsPublic` bit(1) NOT NULL DEFAULT b'0' COMMENT 'namespace是否为公共', + `Comment` varchar(64) NOT NULL DEFAULT '' COMMENT '注释', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_Name_DeletedAt` (`AppId`,`Name`,`DeletedAt`), + KEY `Name_AppId` (`Name`,`AppId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用namespace定义'; + + + +-- Dump of table consumer +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Consumer`; + +CREATE TABLE `Consumer` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Name` varchar(500) NOT NULL DEFAULT 'default' COMMENT '应用名', + `OrgId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '部门Id', + `OrgName` varchar(64) NOT NULL DEFAULT 'default' COMMENT '部门名字', + `OwnerName` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerName', + `OwnerEmail` varchar(500) NOT NULL DEFAULT 'default' COMMENT 'ownerEmail', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_AppId_DeletedAt` (`AppId`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放API消费者'; + + + +-- Dump of table consumeraudit +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ConsumerAudit`; + +CREATE TABLE `ConsumerAudit` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'Consumer Id', + `Uri` varchar(1024) NOT NULL DEFAULT '' COMMENT '访问的Uri', + `Method` varchar(16) NOT NULL DEFAULT '' COMMENT '访问的Method', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_ConsumerId` (`ConsumerId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer审计表'; + + + +-- Dump of table consumerrole +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ConsumerRole`; + +CREATE TABLE `ConsumerRole` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'Consumer Id', + `RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_ConsumerId_RoleId_DeletedAt` (`ConsumerId`,`RoleId`,`DeletedAt`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_RoleId` (`RoleId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer和role的绑定表'; + + + +-- Dump of table consumertoken +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ConsumerToken`; + +CREATE TABLE `ConsumerToken` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `ConsumerId` int(11) unsigned DEFAULT NULL COMMENT 'ConsumerId', + `Token` varchar(128) NOT NULL DEFAULT '' COMMENT 'token', + `Expires` datetime NOT NULL DEFAULT '2099-01-01 00:00:00' COMMENT 'token失效时间', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_Token_DeletedAt` (`Token`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer token表'; + +-- Dump of table favorite +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Favorite`; + +CREATE TABLE `Favorite` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `UserId` varchar(32) NOT NULL DEFAULT 'default' COMMENT '收藏的用户', + `AppId` varchar(64) NOT NULL DEFAULT 'default' COMMENT 'AppID', + `Position` int(32) NOT NULL DEFAULT '10000' COMMENT '收藏顺序', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_UserId_AppId_DeletedAt` (`UserId`,`AppId`,`DeletedAt`), + KEY `AppId` (`AppId`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COMMENT='应用收藏表'; + +-- Dump of table permission +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Permission`; + +CREATE TABLE `Permission` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `PermissionType` varchar(32) NOT NULL DEFAULT '' COMMENT '权限类型', + `TargetId` varchar(256) NOT NULL DEFAULT '' COMMENT '权限对象类型', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_TargetId_PermissionType_DeletedAt` (`TargetId`,`PermissionType`,`DeletedAt`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='permission表'; + + + +-- Dump of table role +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Role`; + +CREATE TABLE `Role` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `RoleName` varchar(256) NOT NULL DEFAULT '' COMMENT 'Role name', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_RoleName_DeletedAt` (`RoleName`,`DeletedAt`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表'; + + + +-- Dump of table rolepermission +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `RolePermission`; + +CREATE TABLE `RolePermission` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id', + `PermissionId` int(10) unsigned DEFAULT NULL COMMENT 'Permission Id', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_RoleId_PermissionId_DeletedAt` (`RoleId`,`PermissionId`,`DeletedAt`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_PermissionId` (`PermissionId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色和权限的绑定表'; + + + +-- Dump of table serverconfig +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `ServerConfig`; + +CREATE TABLE `ServerConfig` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Key` varchar(64) NOT NULL DEFAULT 'default' COMMENT '配置项Key', + `Value` varchar(2048) NOT NULL DEFAULT 'default' COMMENT '配置项值', + `Comment` varchar(1024) DEFAULT '' COMMENT '注释', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_Key_DeletedAt` (`Key`,`DeletedAt`), + KEY `DataChange_LastTime` (`DataChange_LastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置服务自身配置'; + + + +-- Dump of table userrole +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `UserRole`; + +CREATE TABLE `UserRole` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `UserId` varchar(128) DEFAULT '' COMMENT '用户身份标识', + `RoleId` int(10) unsigned DEFAULT NULL COMMENT 'Role Id', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) NOT NULL DEFAULT 'default' COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_UserId_RoleId_DeletedAt` (`UserId`,`RoleId`,`DeletedAt`), + KEY `IX_DataChange_LastTime` (`DataChange_LastTime`), + KEY `IX_RoleId` (`RoleId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户和role的绑定表'; + +-- Dump of table Users +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Users`; + +CREATE TABLE `Users` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Username` varchar(64) NOT NULL DEFAULT 'default' COMMENT '用户登录账户', + `Password` varchar(512) NOT NULL DEFAULT 'default' COMMENT '密码', + `UserDisplayName` varchar(512) NOT NULL DEFAULT 'default' COMMENT '用户名称', + `Email` varchar(64) NOT NULL DEFAULT 'default' COMMENT '邮箱地址', + `Enabled` tinyint(4) DEFAULT NULL COMMENT '是否有效', + PRIMARY KEY (`Id`), + UNIQUE KEY `UK_Username` (`Username`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; + + +-- Dump of table Authorities +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `Authorities`; + +CREATE TABLE `Authorities` ( + `Id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id', + `Username` varchar(64) NOT NULL, + `Authority` varchar(50) NOT NULL, + PRIMARY KEY (`Id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- spring session (https://github.com/spring-projects/spring-session/blob/faee8f1bdb8822a5653a81eba838dddf224d92d6/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-mysql.sql) +-- Dump of table SPRING_SESSION +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `SPRING_SESSION`; + +CREATE TABLE `SPRING_SESSION` ( + `PRIMARY_ID` char(36) NOT NULL, + `SESSION_ID` char(36) NOT NULL, + `CREATION_TIME` bigint NOT NULL, + `LAST_ACCESS_TIME` bigint NOT NULL, + `MAX_INACTIVE_INTERVAL` int NOT NULL, + `EXPIRY_TIME` bigint NOT NULL, + `PRINCIPAL_NAME` varchar(100) DEFAULT NULL, + PRIMARY KEY (`PRIMARY_ID`), + UNIQUE KEY `SPRING_SESSION_IX1` (`SESSION_ID`), + KEY `SPRING_SESSION_IX2` (`EXPIRY_TIME`), + KEY `SPRING_SESSION_IX3` (`PRINCIPAL_NAME`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; + +-- Dump of table SPRING_SESSION_ATTRIBUTES +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `SPRING_SESSION_ATTRIBUTES`; + +CREATE TABLE `SPRING_SESSION_ATTRIBUTES` ( + `SESSION_PRIMARY_ID` char(36) NOT NULL, + `ATTRIBUTE_NAME` varchar(200) NOT NULL, + `ATTRIBUTE_BYTES` blob NOT NULL, + PRIMARY KEY (`SESSION_PRIMARY_ID`,`ATTRIBUTE_NAME`), + CONSTRAINT `SPRING_SESSION_ATTRIBUTES_FK` FOREIGN KEY (`SESSION_PRIMARY_ID`) REFERENCES `SPRING_SESSION` (`PRIMARY_ID`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; + +-- Dump of table AuditLog +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AuditLog`; + +CREATE TABLE `AuditLog` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `TraceId` varchar(32) NOT NULL DEFAULT '' COMMENT '链路全局唯一ID', + `SpanId` varchar(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `ParentSpanId` varchar(32) DEFAULT NULL COMMENT '父跨度ID', + `FollowsFromSpanId` varchar(32) DEFAULT NULL COMMENT '上一个兄弟跨度ID', + `Operator` varchar(64) NOT NULL DEFAULT 'anonymous' COMMENT '操作人', + `OpType` varchar(50) NOT NULL DEFAULT 'default' COMMENT '操作类型', + `OpName` varchar(150) NOT NULL DEFAULT 'default' COMMENT '操作名称', + `Description` varchar(200) DEFAULT NULL COMMENT '备注', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_TraceId` (`TraceId`), + KEY `IX_OpName` (`OpName`), + KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `IX_Operator` (`Operator`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志表'; + +-- Dump of table AuditLogDataInfluence +-- ------------------------------------------------------------ + +DROP TABLE IF EXISTS `AuditLogDataInfluence`; + +CREATE TABLE `AuditLogDataInfluence` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `SpanId` char(32) NOT NULL DEFAULT '' COMMENT '跨度ID', + `InfluenceEntityId` varchar(50) NOT NULL DEFAULT '0' COMMENT '记录ID', + `InfluenceEntityName` varchar(50) NOT NULL DEFAULT 'default' COMMENT '表名', + `FieldName` varchar(50) DEFAULT NULL COMMENT '字段名称', + `FieldOldValue` varchar(500) DEFAULT NULL COMMENT '字段旧值', + `FieldNewValue` varchar(500) DEFAULT NULL COMMENT '字段新值', + `IsDeleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '1: deleted, 0: normal', + `DeletedAt` BIGINT(20) NOT NULL DEFAULT '0' COMMENT 'Delete timestamp based on milliseconds', + `DataChange_CreatedBy` varchar(64) DEFAULT NULL COMMENT '创建人邮箱前缀', + `DataChange_CreatedTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `DataChange_LastModifiedBy` varchar(64) DEFAULT '' COMMENT '最后修改人邮箱前缀', + `DataChange_LastTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + PRIMARY KEY (`Id`), + KEY `IX_SpanId` (`SpanId`), + KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), + KEY `IX_EntityId` (`InfluenceEntityId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志数据变动表'; + +-- Config +-- ------------------------------------------------------------ +INSERT INTO `ServerConfig` (`Key`, `Value`, `Comment`) +VALUES + ('apollo.portal.envs', 'dev', '可支持的环境列表'), + ('organizations', '[{"orgId":"TEST1","orgName":"样例部门1"},{"orgId":"TEST2","orgName":"样例部门2"}]', '部门列表'), + ('superAdmin', 'apollo', 'Portal超级管理员'), + ('api.readTimeout', '10000', 'http接口read timeout'), + ('consumer.token.salt', 'someSalt', 'consumer token salt'), + ('admin.createPrivateNamespace.switch', 'true', '是否允许项目管理员创建私有namespace'), + ('configView.memberOnly.envs', 'pro', '只对项目成员显示配置信息的环境列表,多个env以英文逗号分隔'), + ('apollo.portal.meta.servers', '{}', '各环境Meta Service列表'); + + +INSERT INTO `Users` (`Username`, `Password`, `UserDisplayName`, `Email`, `Enabled`) +VALUES + ('apollo', '$2a$10$7r20uS.BQ9uBpf3Baj3uQOZvMVvB1RN3PYoKE94gtz2.WAOuiiwXS', 'apollo', 'apollo@acme.com', 1); + +INSERT INTO `Authorities` (`Username`, `Authority`) VALUES ('apollo', 'ROLE_user'); + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== + +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/scripts/sql/delta/v040-v050/apolloconfigdb-v040-v050.sql b/scripts/sql/profiles/mysql-default/delta/v040-v050/apolloconfigdb-v040-v050.sql similarity index 100% rename from scripts/sql/delta/v040-v050/apolloconfigdb-v040-v050.sql rename to scripts/sql/profiles/mysql-default/delta/v040-v050/apolloconfigdb-v040-v050.sql diff --git a/scripts/sql/delta/v040-v050/apolloportaldb-v040-v050.sql b/scripts/sql/profiles/mysql-default/delta/v040-v050/apolloportaldb-v040-v050.sql similarity index 100% rename from scripts/sql/delta/v040-v050/apolloportaldb-v040-v050.sql rename to scripts/sql/profiles/mysql-default/delta/v040-v050/apolloportaldb-v040-v050.sql diff --git a/scripts/sql/delta/v060-v062/apolloconfigdb-v060-v062.sql b/scripts/sql/profiles/mysql-default/delta/v060-v062/apolloconfigdb-v060-v062.sql similarity index 100% rename from scripts/sql/delta/v060-v062/apolloconfigdb-v060-v062.sql rename to scripts/sql/profiles/mysql-default/delta/v060-v062/apolloconfigdb-v060-v062.sql diff --git a/scripts/sql/delta/v060-v062/apolloportaldb-v060-v062.sql b/scripts/sql/profiles/mysql-default/delta/v060-v062/apolloportaldb-v060-v062.sql similarity index 100% rename from scripts/sql/delta/v060-v062/apolloportaldb-v060-v062.sql rename to scripts/sql/profiles/mysql-default/delta/v060-v062/apolloportaldb-v060-v062.sql diff --git a/scripts/sql/delta/v080-v090/apolloportaldb-v080-v090.sql b/scripts/sql/profiles/mysql-default/delta/v080-v090/apolloportaldb-v080-v090.sql similarity index 100% rename from scripts/sql/delta/v080-v090/apolloportaldb-v080-v090.sql rename to scripts/sql/profiles/mysql-default/delta/v080-v090/apolloportaldb-v080-v090.sql diff --git a/scripts/sql/delta/v151-v160/apolloconfigdb-v151-v160.sql b/scripts/sql/profiles/mysql-default/delta/v151-v160/apolloconfigdb-v151-v160.sql similarity index 100% rename from scripts/sql/delta/v151-v160/apolloconfigdb-v151-v160.sql rename to scripts/sql/profiles/mysql-default/delta/v151-v160/apolloconfigdb-v151-v160.sql diff --git a/scripts/sql/delta/v170-v180/apolloconfigdb-v170-v180.sql b/scripts/sql/profiles/mysql-default/delta/v170-v180/apolloconfigdb-v170-v180.sql similarity index 100% rename from scripts/sql/delta/v170-v180/apolloconfigdb-v170-v180.sql rename to scripts/sql/profiles/mysql-default/delta/v170-v180/apolloconfigdb-v170-v180.sql diff --git a/scripts/sql/delta/v170-v180/apolloportaldb-v170-v180.sql b/scripts/sql/profiles/mysql-default/delta/v170-v180/apolloportaldb-v170-v180.sql similarity index 100% rename from scripts/sql/delta/v170-v180/apolloportaldb-v170-v180.sql rename to scripts/sql/profiles/mysql-default/delta/v170-v180/apolloportaldb-v170-v180.sql diff --git a/scripts/sql/delta/v180-v190/apolloconfigdb-v180-v190.sql b/scripts/sql/profiles/mysql-default/delta/v180-v190/apolloconfigdb-v180-v190.sql similarity index 100% rename from scripts/sql/delta/v180-v190/apolloconfigdb-v180-v190.sql rename to scripts/sql/profiles/mysql-default/delta/v180-v190/apolloconfigdb-v180-v190.sql diff --git a/scripts/sql/delta/v180-v190/apolloportaldb-v180-v190.sql b/scripts/sql/profiles/mysql-default/delta/v180-v190/apolloportaldb-v180-v190.sql similarity index 100% rename from scripts/sql/delta/v180-v190/apolloportaldb-v180-v190.sql rename to scripts/sql/profiles/mysql-default/delta/v180-v190/apolloportaldb-v180-v190.sql diff --git a/scripts/sql/delta/v190-v200/apolloconfigdb-v190-v200-after.sql b/scripts/sql/profiles/mysql-default/delta/v190-v200/apolloconfigdb-v190-v200-after.sql similarity index 100% rename from scripts/sql/delta/v190-v200/apolloconfigdb-v190-v200-after.sql rename to scripts/sql/profiles/mysql-default/delta/v190-v200/apolloconfigdb-v190-v200-after.sql diff --git a/scripts/sql/delta/v190-v200/apolloconfigdb-v190-v200.sql b/scripts/sql/profiles/mysql-default/delta/v190-v200/apolloconfigdb-v190-v200.sql similarity index 100% rename from scripts/sql/delta/v190-v200/apolloconfigdb-v190-v200.sql rename to scripts/sql/profiles/mysql-default/delta/v190-v200/apolloconfigdb-v190-v200.sql diff --git a/scripts/sql/delta/v190-v200/apolloportaldb-v190-v200-after.sql b/scripts/sql/profiles/mysql-default/delta/v190-v200/apolloportaldb-v190-v200-after.sql similarity index 100% rename from scripts/sql/delta/v190-v200/apolloportaldb-v190-v200-after.sql rename to scripts/sql/profiles/mysql-default/delta/v190-v200/apolloportaldb-v190-v200-after.sql diff --git a/scripts/sql/delta/v190-v200/apolloportaldb-v190-v200.sql b/scripts/sql/profiles/mysql-default/delta/v190-v200/apolloportaldb-v190-v200.sql similarity index 100% rename from scripts/sql/delta/v190-v200/apolloportaldb-v190-v200.sql rename to scripts/sql/profiles/mysql-default/delta/v190-v200/apolloportaldb-v190-v200.sql diff --git a/scripts/sql/delta/v200-v210/apolloconfigdb-v200-v210.sql b/scripts/sql/profiles/mysql-default/delta/v200-v210/apolloconfigdb-v200-v210.sql similarity index 100% rename from scripts/sql/delta/v200-v210/apolloconfigdb-v200-v210.sql rename to scripts/sql/profiles/mysql-default/delta/v200-v210/apolloconfigdb-v200-v210.sql diff --git a/scripts/sql/delta/v210-v220/apolloconfigdb-v210-v220.sql b/scripts/sql/profiles/mysql-default/delta/v210-v220/apolloconfigdb-v210-v220.sql similarity index 98% rename from scripts/sql/delta/v210-v220/apolloconfigdb-v210-v220.sql rename to scripts/sql/profiles/mysql-default/delta/v210-v220/apolloconfigdb-v210-v220.sql index 81183f62c5e..f63cfb6f95d 100644 --- a/scripts/sql/delta/v210-v220/apolloconfigdb-v210-v220.sql +++ b/scripts/sql/profiles/mysql-default/delta/v210-v220/apolloconfigdb-v210-v220.sql @@ -13,7 +13,7 @@ -- See the License for the specific language governing permissions and -- limitations under the License. -- -# delta schema to upgrade apollo config db from v2.1.0 to v2.2.0 +-- delta schema to upgrade apollo config db from v2.1.0 to v2.2.0 Use ApolloConfigDB; @@ -90,4 +90,4 @@ CREATE TABLE `AuditLogDataInfluence` ( KEY `IX_SpanId` (`SpanId`), KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), KEY `IX_EntityId` (`InfluenceEntityId`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志数据变动表'; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志数据变动表'; diff --git a/scripts/sql/delta/v210-v220/apolloportaldb-v210-v220.sql b/scripts/sql/profiles/mysql-default/delta/v210-v220/apolloportaldb-v210-v220.sql similarity index 97% rename from scripts/sql/delta/v210-v220/apolloportaldb-v210-v220.sql rename to scripts/sql/profiles/mysql-default/delta/v210-v220/apolloportaldb-v210-v220.sql index c2c0ab7083c..9f946a4d0c1 100644 --- a/scripts/sql/delta/v210-v220/apolloportaldb-v210-v220.sql +++ b/scripts/sql/profiles/mysql-default/delta/v210-v220/apolloportaldb-v210-v220.sql @@ -13,7 +13,7 @@ -- See the License for the specific language governing permissions and -- limitations under the License. -- -# delta schema to upgrade apollo portal db from v2.1.0 to v2.2.0 +-- delta schema to upgrade apollo portal db from v2.1.0 to v2.2.0 Use ApolloPortalDB; @@ -76,4 +76,4 @@ CREATE TABLE `AuditLogDataInfluence` ( KEY `IX_SpanId` (`SpanId`), KEY `IX_DataChange_CreatedTime` (`DataChange_CreatedTime`), KEY `IX_EntityId` (`InfluenceEntityId`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志数据变动表'; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志数据变动表'; diff --git a/scripts/sql/profiles/mysql-default/delta/v220-v230/apolloconfigdb-v220-v230.sql b/scripts/sql/profiles/mysql-default/delta/v220-v230/apolloconfigdb-v220-v230.sql new file mode 100644 index 00000000000..a1defac266c --- /dev/null +++ b/scripts/sql/profiles/mysql-default/delta/v220-v230/apolloconfigdb-v220-v230.sql @@ -0,0 +1,41 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo config db from v2.2.0 to v2.3.0 + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- +-- +-- Use Database +Use ApolloConfigDB; + +ALTER TABLE `Cluster` + ADD COLUMN `Comment` varchar(64) DEFAULT NULL COMMENT '备注'; + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== diff --git a/scripts/sql/profiles/mysql-default/delta/v220-v230/apolloportaldb-v220-v230.sql b/scripts/sql/profiles/mysql-default/delta/v220-v230/apolloportaldb-v220-v230.sql new file mode 100644 index 00000000000..0ff6f7efb75 --- /dev/null +++ b/scripts/sql/profiles/mysql-default/delta/v220-v230/apolloportaldb-v220-v230.sql @@ -0,0 +1,39 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v2.2.0 to v2.3.0 + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- +-- +-- Use Database +Use ApolloPortalDB; + + +-- +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== diff --git a/scripts/sql/apolloconfigdb.sql b/scripts/sql/src/apolloconfigdb.sql similarity index 92% rename from scripts/sql/apolloconfigdb.sql rename to scripts/sql/src/apolloconfigdb.sql index 301f1ebe838..9f85a8791ce 100644 --- a/scripts/sql/apolloconfigdb.sql +++ b/scripts/sql/src/apolloconfigdb.sql @@ -21,14 +21,12 @@ /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -# Create Database -# ------------------------------------------------------------ -CREATE DATABASE IF NOT EXISTS ApolloConfigDB DEFAULT CHARACTER SET = utf8mb4; +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.setupDatabase} -Use ApolloConfigDB; - -# Dump of table app -# ------------------------------------------------------------ +-- Dump of table app +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `App`; @@ -54,8 +52,8 @@ CREATE TABLE `App` ( -# Dump of table appnamespace -# ------------------------------------------------------------ +-- Dump of table appnamespace +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `AppNamespace`; @@ -80,8 +78,8 @@ CREATE TABLE `AppNamespace` ( -# Dump of table audit -# ------------------------------------------------------------ +-- Dump of table audit +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Audit`; @@ -103,8 +101,8 @@ CREATE TABLE `Audit` ( -# Dump of table cluster -# ------------------------------------------------------------ +-- Dump of table cluster +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Cluster`; @@ -128,8 +126,8 @@ CREATE TABLE `Cluster` ( -# Dump of table commit -# ------------------------------------------------------------ +-- Dump of table commit +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Commit`; @@ -153,8 +151,8 @@ CREATE TABLE `Commit` ( KEY `NamespaceName` (`NamespaceName`(191)) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='commit 历史表'; -# Dump of table grayreleaserule -# ------------------------------------------------------------ +-- Dump of table grayreleaserule +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `GrayReleaseRule`; @@ -179,8 +177,8 @@ CREATE TABLE `GrayReleaseRule` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='灰度规则表'; -# Dump of table instance -# ------------------------------------------------------------ +-- Dump of table instance +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Instance`; @@ -200,8 +198,8 @@ CREATE TABLE `Instance` ( -# Dump of table instanceconfig -# ------------------------------------------------------------ +-- Dump of table instanceconfig +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `InstanceConfig`; @@ -224,8 +222,8 @@ CREATE TABLE `InstanceConfig` ( -# Dump of table item -# ------------------------------------------------------------ +-- Dump of table item +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Item`; @@ -250,8 +248,8 @@ CREATE TABLE `Item` ( -# Dump of table namespace -# ------------------------------------------------------------ +-- Dump of table namespace +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Namespace`; @@ -274,8 +272,8 @@ CREATE TABLE `Namespace` ( -# Dump of table namespacelock -# ------------------------------------------------------------ +-- Dump of table namespacelock +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `NamespaceLock`; @@ -295,8 +293,8 @@ CREATE TABLE `NamespaceLock` ( -# Dump of table release -# ------------------------------------------------------------ +-- Dump of table release +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Release`; @@ -323,8 +321,8 @@ CREATE TABLE `Release` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布'; -# Dump of table releasehistory -# ------------------------------------------------------------ +-- Dump of table releasehistory +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `ReleaseHistory`; @@ -352,8 +350,8 @@ CREATE TABLE `ReleaseHistory` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布历史'; -# Dump of table releasemessage -# ------------------------------------------------------------ +-- Dump of table releasemessage +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `ReleaseMessage`; @@ -368,8 +366,8 @@ CREATE TABLE `ReleaseMessage` ( -# Dump of table serverconfig -# ------------------------------------------------------------ +-- Dump of table serverconfig +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `ServerConfig`; @@ -390,8 +388,8 @@ CREATE TABLE `ServerConfig` ( KEY `DataChange_LastTime` (`DataChange_LastTime`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置服务自身配置'; -# Dump of table accesskey -# ------------------------------------------------------------ +-- Dump of table accesskey +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `AccessKey`; @@ -412,8 +410,8 @@ CREATE TABLE `AccessKey` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问密钥'; -# Dump of table serviceregistry -# ------------------------------------------------------------ +-- Dump of table serviceregistry +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `ServiceRegistry`; @@ -430,8 +428,8 @@ CREATE TABLE `ServiceRegistry` ( INDEX `IX_DataChange_LastTime` (`DataChange_LastTime`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='注册中心'; -# Dump of table AuditLog -# ------------------------------------------------------------ +-- Dump of table AuditLog +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `AuditLog`; @@ -458,8 +456,8 @@ CREATE TABLE `AuditLog` ( KEY `IX_Operator` (`Operator`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志表'; -# Dump of table AuditLogDataInfluence -# ------------------------------------------------------------ +-- Dump of table AuditLogDataInfluence +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `AuditLogDataInfluence`; @@ -483,8 +481,8 @@ CREATE TABLE `AuditLogDataInfluence` ( KEY `IX_EntityId` (`InfluenceEntityId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志数据变动表'; -# Config -# ------------------------------------------------------------ +-- Config +-- ------------------------------------------------------------ INSERT INTO `ServerConfig` (`Key`, `Cluster`, `Value`, `Comment`) VALUES ('eureka.service.url', 'default', 'http://localhost:8080/eureka/', 'Eureka服务Url,多个service以英文逗号分隔'), @@ -493,6 +491,8 @@ VALUES ('item.value.length.limit', 'default', '20000', 'item value最大长度限制'), ('config-service.cache.enabled', 'default', 'false', 'ConfigService是否开启缓存,开启后能提高性能,但是会增大内存消耗!'); +-- ${gists.autoGeneratedDeclaration} + /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; diff --git a/scripts/sql/apolloportaldb.sql b/scripts/sql/src/apolloportaldb.sql similarity index 90% rename from scripts/sql/apolloportaldb.sql rename to scripts/sql/src/apolloportaldb.sql index 8af0c276f0d..323a8191149 100644 --- a/scripts/sql/apolloportaldb.sql +++ b/scripts/sql/src/apolloportaldb.sql @@ -21,14 +21,12 @@ /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -# Create Database -# ------------------------------------------------------------ -CREATE DATABASE IF NOT EXISTS ApolloPortalDB DEFAULT CHARACTER SET = utf8mb4; +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.setupDatabase} -Use ApolloPortalDB; - -# Dump of table app -# ------------------------------------------------------------ +-- Dump of table app +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `App`; @@ -54,8 +52,8 @@ CREATE TABLE `App` ( -# Dump of table appnamespace -# ------------------------------------------------------------ +-- Dump of table appnamespace +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `AppNamespace`; @@ -80,8 +78,8 @@ CREATE TABLE `AppNamespace` ( -# Dump of table consumer -# ------------------------------------------------------------ +-- Dump of table consumer +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Consumer`; @@ -106,8 +104,8 @@ CREATE TABLE `Consumer` ( -# Dump of table consumeraudit -# ------------------------------------------------------------ +-- Dump of table consumeraudit +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `ConsumerAudit`; @@ -125,8 +123,8 @@ CREATE TABLE `ConsumerAudit` ( -# Dump of table consumerrole -# ------------------------------------------------------------ +-- Dump of table consumerrole +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `ConsumerRole`; @@ -148,8 +146,8 @@ CREATE TABLE `ConsumerRole` ( -# Dump of table consumertoken -# ------------------------------------------------------------ +-- Dump of table consumertoken +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `ConsumerToken`; @@ -169,8 +167,8 @@ CREATE TABLE `ConsumerToken` ( KEY `DataChange_LastTime` (`DataChange_LastTime`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='consumer token表'; -# Dump of table favorite -# ------------------------------------------------------------ +-- Dump of table favorite +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Favorite`; @@ -191,8 +189,8 @@ CREATE TABLE `Favorite` ( KEY `DataChange_LastTime` (`DataChange_LastTime`) ) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COMMENT='应用收藏表'; -# Dump of table permission -# ------------------------------------------------------------ +-- Dump of table permission +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Permission`; @@ -213,8 +211,8 @@ CREATE TABLE `Permission` ( -# Dump of table role -# ------------------------------------------------------------ +-- Dump of table role +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Role`; @@ -234,8 +232,8 @@ CREATE TABLE `Role` ( -# Dump of table rolepermission -# ------------------------------------------------------------ +-- Dump of table rolepermission +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `RolePermission`; @@ -257,8 +255,8 @@ CREATE TABLE `RolePermission` ( -# Dump of table serverconfig -# ------------------------------------------------------------ +-- Dump of table serverconfig +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `ServerConfig`; @@ -280,8 +278,8 @@ CREATE TABLE `ServerConfig` ( -# Dump of table userrole -# ------------------------------------------------------------ +-- Dump of table userrole +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `UserRole`; @@ -301,8 +299,8 @@ CREATE TABLE `UserRole` ( KEY `IX_RoleId` (`RoleId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户和role的绑定表'; -# Dump of table Users -# ------------------------------------------------------------ +-- Dump of table Users +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Users`; @@ -318,8 +316,8 @@ CREATE TABLE `Users` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; -# Dump of table Authorities -# ------------------------------------------------------------ +-- Dump of table Authorities +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `Authorities`; @@ -331,8 +329,8 @@ CREATE TABLE `Authorities` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- spring session (https://github.com/spring-projects/spring-session/blob/faee8f1bdb8822a5653a81eba838dddf224d92d6/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-mysql.sql) -# Dump of table SPRING_SESSION -# ------------------------------------------------------------ +-- Dump of table SPRING_SESSION +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `SPRING_SESSION`; @@ -350,8 +348,8 @@ CREATE TABLE `SPRING_SESSION` ( KEY `SPRING_SESSION_IX3` (`PRINCIPAL_NAME`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; -# Dump of table SPRING_SESSION_ATTRIBUTES -# ------------------------------------------------------------ +-- Dump of table SPRING_SESSION_ATTRIBUTES +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `SPRING_SESSION_ATTRIBUTES`; @@ -363,8 +361,8 @@ CREATE TABLE `SPRING_SESSION_ATTRIBUTES` ( CONSTRAINT `SPRING_SESSION_ATTRIBUTES_FK` FOREIGN KEY (`SESSION_PRIMARY_ID`) REFERENCES `SPRING_SESSION` (`PRIMARY_ID`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; -# Dump of table AuditLog -# ------------------------------------------------------------ +-- Dump of table AuditLog +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `AuditLog`; @@ -391,8 +389,8 @@ CREATE TABLE `AuditLog` ( KEY `IX_Operator` (`Operator`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志表'; -# Dump of table AuditLogDataInfluence -# ------------------------------------------------------------ +-- Dump of table AuditLogDataInfluence +-- ------------------------------------------------------------ DROP TABLE IF EXISTS `AuditLogDataInfluence`; @@ -416,12 +414,12 @@ CREATE TABLE `AuditLogDataInfluence` ( KEY `IX_EntityId` (`InfluenceEntityId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审计日志数据变动表'; -# Config -# ------------------------------------------------------------ +-- Config +-- ------------------------------------------------------------ INSERT INTO `ServerConfig` (`Key`, `Value`, `Comment`) VALUES ('apollo.portal.envs', 'dev', '可支持的环境列表'), - ('organizations', '[{\"orgId\":\"TEST1\",\"orgName\":\"样例部门1\"},{\"orgId\":\"TEST2\",\"orgName\":\"样例部门2\"}]', '部门列表'), + ('organizations', '[{"orgId":"TEST1","orgName":"样例部门1"},{"orgId":"TEST2","orgName":"样例部门2"}]', '部门列表'), ('superAdmin', 'apollo', 'Portal超级管理员'), ('api.readTimeout', '10000', 'http接口read timeout'), ('consumer.token.salt', 'someSalt', 'consumer token salt'), @@ -436,9 +434,11 @@ VALUES INSERT INTO `Authorities` (`Username`, `Authority`) VALUES ('apollo', 'ROLE_user'); +-- ${gists.autoGeneratedDeclaration} + /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; \ No newline at end of file +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/scripts/sql/delta/v220-v230/apolloconfigdb-v220-v230.sql b/scripts/sql/src/delta/v220-v230/apolloconfigdb-v220-v230.sql similarity index 78% rename from scripts/sql/delta/v220-v230/apolloconfigdb-v220-v230.sql rename to scripts/sql/src/delta/v220-v230/apolloconfigdb-v220-v230.sql index 412b67f5fd2..b072cf9f311 100644 --- a/scripts/sql/delta/v220-v230/apolloconfigdb-v220-v230.sql +++ b/scripts/sql/src/delta/v220-v230/apolloconfigdb-v220-v230.sql @@ -13,9 +13,13 @@ -- See the License for the specific language governing permissions and -- limitations under the License. -- -# delta schema to upgrade apollo config db from v2.2.0 to v2.3.0 +-- delta schema to upgrade apollo config db from v2.2.0 to v2.3.0 -Use ApolloConfigDB; +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} ALTER TABLE `Cluster` - ADD COLUMN `Comment` varchar(64) DEFAULT NULL COMMENT '备注'; \ No newline at end of file + ADD COLUMN `Comment` varchar(64) DEFAULT NULL COMMENT '备注'; + +-- ${gists.autoGeneratedDeclaration} diff --git a/scripts/sql/src/delta/v220-v230/apolloportaldb-v220-v230.sql b/scripts/sql/src/delta/v220-v230/apolloportaldb-v220-v230.sql new file mode 100644 index 00000000000..cdfa354b3c3 --- /dev/null +++ b/scripts/sql/src/delta/v220-v230/apolloportaldb-v220-v230.sql @@ -0,0 +1,23 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- delta schema to upgrade apollo portal db from v2.2.0 to v2.3.0 + +-- ${gists.autoGeneratedDeclaration} +-- ${gists.h2Function} +-- ${gists.useDatabase} + + +-- ${gists.autoGeneratedDeclaration} diff --git a/scripts/sql/src/gist/autoGeneratedDeclaration.sql b/scripts/sql/src/gist/autoGeneratedDeclaration.sql new file mode 100644 index 00000000000..d3a2fe50f5c --- /dev/null +++ b/scripts/sql/src/gist/autoGeneratedDeclaration.sql @@ -0,0 +1,24 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- @@gist-start@@ +-- =============================================================================== +-- == == +-- == Generated from 'scripts/sql/src/' == +-- == by running 'mvn compile -pl apollo-build-sql-converter -Psql-converter'. == +-- == DO NOT EDIT !!! == +-- == == +-- =============================================================================== +-- @@gist-end@@ diff --git a/scripts/sql/src/gist/h2Function.sql b/scripts/sql/src/gist/h2Function.sql new file mode 100644 index 00000000000..e50d37950f7 --- /dev/null +++ b/scripts/sql/src/gist/h2Function.sql @@ -0,0 +1,22 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- @@gist-start@@ + +-- H2 Function +-- ------------------------------------------------------------ +CREATE ALIAS IF NOT EXISTS UNIX_TIMESTAMP FOR "com.ctrip.framework.apollo.common.jpa.H2Function.unixTimestamp"; + +-- @@gist-end@@ diff --git a/scripts/sql/src/gist/setupDatabase.sql b/scripts/sql/src/gist/setupDatabase.sql new file mode 100644 index 00000000000..8497b44cfec --- /dev/null +++ b/scripts/sql/src/gist/setupDatabase.sql @@ -0,0 +1,22 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- @@gist-start@@ +-- Create Database +-- ------------------------------------------------------------ +CREATE DATABASE IF NOT EXISTS ApolloAssemblyDB DEFAULT CHARACTER SET = utf8mb4; + +Use ApolloAssemblyDB; +-- @@gist-end@@ diff --git a/scripts/sql/src/gist/useDatabase.sql b/scripts/sql/src/gist/useDatabase.sql new file mode 100644 index 00000000000..71f17586d84 --- /dev/null +++ b/scripts/sql/src/gist/useDatabase.sql @@ -0,0 +1,19 @@ +-- +-- Copyright 2024 Apollo 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 +-- +-- http://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. +-- +-- @@gist-start@@ +-- Use Database +Use ApolloAssemblyDB; +-- @@gist-end@@