From a1beec4be943697cd99affa38c0266845188053f Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 2 Apr 2025 08:18:40 +0800 Subject: [PATCH] feat(quartz): Added automatic migration of Quartz database --- .../FodyWeavers.xml | 3 + .../LINGYUN.Abp.Quartz.MySqlInstaller.csproj | 30 ++ .../AbpQuartzMySqlInstallerModule.cs | 30 ++ .../MySqlInstaller/QuartzMySqlInstaller.cs | 136 +++++++ .../Quartz/MySqlInstaller/Scripts/Initial.sql | 181 +++++++++ .../FodyWeavers.xml | 3 + ...NGYUN.Abp.Quartz.SqlServerInstaller.csproj | 30 ++ .../AbpQuartzSqlServerInstallerModule.cs | 30 ++ .../QuartzSqlServerInstaller.cs | 136 +++++++ .../SqlServerInstaller/Scripts/Initial.sql | 370 ++++++++++++++++++ 10 files changed, 949 insertions(+) create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/FodyWeavers.xml create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN.Abp.Quartz.MySqlInstaller.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN/Abp/Quartz/MySqlInstaller/AbpQuartzMySqlInstallerModule.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN/Abp/Quartz/MySqlInstaller/QuartzMySqlInstaller.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN/Abp/Quartz/MySqlInstaller/Scripts/Initial.sql create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/FodyWeavers.xml create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN.Abp.Quartz.SqlServerInstaller.csproj create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN/Abp/Quartz/SqlServerInstaller/AbpQuartzSqlServerInstallerModule.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN/Abp/Quartz/SqlServerInstaller/QuartzSqlServerInstaller.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN/Abp/Quartz/SqlServerInstaller/Scripts/Initial.sql diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN.Abp.Quartz.MySqlInstaller.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN.Abp.Quartz.MySqlInstaller.csproj new file mode 100644 index 000000000..7f15d10c2 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN.Abp.Quartz.MySqlInstaller.csproj @@ -0,0 +1,30 @@ + + + + + + + netstandard2.0;netstandard2.1;net8.0;net9.0 + LINGYUN.Abp.Quartz.MySqlInstaller + LINGYUN.Abp.Quartz.MySqlInstaller + false + false + false + + + + + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN/Abp/Quartz/MySqlInstaller/AbpQuartzMySqlInstallerModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN/Abp/Quartz/MySqlInstaller/AbpQuartzMySqlInstallerModule.cs new file mode 100644 index 000000000..b7878f537 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN/Abp/Quartz/MySqlInstaller/AbpQuartzMySqlInstallerModule.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Modularity; +using Volo.Abp.Quartz; +using Volo.Abp.VirtualFileSystem; + +namespace LINGYUN.Abp.Quartz.MySqlInstaller; + +[DependsOn( + typeof(AbpQuartzModule), + typeof(AbpVirtualFileSystemModule))] +public class AbpQuartzMySqlInstallerModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + } + + public async override Task OnPreApplicationInitializationAsync(ApplicationInitializationContext context) + { + // 初始化 Quartz Mysql 数据库 + await context.ServiceProvider + .GetRequiredService() + .InstallAsync(); + } +} \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN/Abp/Quartz/MySqlInstaller/QuartzMySqlInstaller.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN/Abp/Quartz/MySqlInstaller/QuartzMySqlInstaller.cs new file mode 100644 index 000000000..40e6dfb22 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN/Abp/Quartz/MySqlInstaller/QuartzMySqlInstaller.cs @@ -0,0 +1,136 @@ +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using MySqlConnector; +using System; +using System.Data; +using System.Threading.Tasks; +using Volo.Abp.Quartz; +using Volo.Abp.VirtualFileSystem; +using static Quartz.SchedulerBuilder; + +namespace LINGYUN.Abp.Quartz.MySqlInstaller; + +public class QuartzMySqlInstaller +{ + public ILogger Logger { protected get; set; } + + private readonly IVirtualFileProvider _virtualFileProvider; + private readonly AbpQuartzOptions _quartzOptions; + + public QuartzMySqlInstaller( + IVirtualFileProvider virtualFileProvider, + IOptions quartzOptions) + { + _quartzOptions = quartzOptions.Value; + _virtualFileProvider = virtualFileProvider; + + Logger = NullLogger.Instance; + } + + public async virtual Task InstallAsync() + { + var dataSource = _quartzOptions.Properties["quartz.jobStore.dataSource"] ?? AdoProviderOptions.DefaultDataSourceName; + var connectionString = _quartzOptions.Properties[$"quartz.dataSource.{dataSource}.connectionString"]; + var tablePrefix = _quartzOptions.Properties["quartz.jobStore.tablePrefix"] ?? "QRTZ_"; + + if (connectionString.IsNullOrWhiteSpace()) + { + Logger.LogWarning($"Please configure the `{dataSource}` database connection string in `quartz.jobStore.dataSource`!"); + throw new ArgumentNullException(nameof(connectionString)); + } + + Logger.LogInformation("Install Quartz MySql..."); + + var builder = new MySqlConnectionStringBuilder(connectionString); + + var dataBaseName = await CreateDataBaseIfNotExists(builder.Database, builder); + + builder.Database = dataBaseName; + + using var mySqlConnection = new MySqlConnection(builder.ConnectionString); + + if (mySqlConnection.State == ConnectionState.Closed) + { + await mySqlConnection.OpenAsync(); + } + + using (var mySqlCommand = new MySqlCommand("SELECT COUNT(1) FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = @DataBaseName;", mySqlConnection)) + { + mySqlCommand.Parameters.Add("@DataBaseName", MySqlDbType.String).Value = dataBaseName; + + var rowsAffects = await mySqlCommand.ExecuteScalarAsync() as long?; + if (rowsAffects > 0) + { + Logger.LogInformation($"The `{dataBaseName}` tables has already exists."); + return; + } + } + + var sqlScript = await GetInitSqlScript(); + + // USE `${DataBase}` -> USE `Workflow`; + sqlScript = sqlScript.ReplaceFirst("${DataBase}", dataBaseName); + // CREATE TABLE $(TablePrefix)JOB_DETAILS` -> CREATE TABLE QRTZ_JOB_DETAILS; + sqlScript = sqlScript.Replace("${TablePrefix}", tablePrefix); + + using (var mySqlCommand = new MySqlCommand(sqlScript, mySqlConnection)) + { + Logger.LogInformation("The database initialization script `Initial.sql` starts..."); + + await mySqlCommand.ExecuteNonQueryAsync(); + } + + Logger.LogInformation("Database initialization script `Initial.sql` complete!"); + } + + public async virtual Task CreateDataBaseIfNotExists(string dataBase, MySqlConnectionStringBuilder connectionStringBuilder) + { + // 切换主数据库查询数据库是否存在 + connectionStringBuilder.Database = "mysql"; + using var mySqlConnection = new MySqlConnection(connectionStringBuilder.ConnectionString); + if (mySqlConnection.State == ConnectionState.Closed) + { + await mySqlConnection.OpenAsync(); + } + + var checkDataBaseName = ""; + using (var mySqlCommand = new MySqlCommand("SELECT `SCHEMA_NAME` FROM `information_schema`.`SCHEMATA` WHERE `SCHEMA_NAME` = @DataBaseName;", mySqlConnection)) + { + var dataBaseParamter = mySqlCommand.Parameters.Add("DataBaseName", DbType.String); + dataBaseParamter.Value = dataBase; + + checkDataBaseName = await mySqlCommand.ExecuteScalarAsync() as string; + } + + if (checkDataBaseName.IsNullOrWhiteSpace()) + { + using (var mySqlCommand = new MySqlCommand($"CREATE DATABASE `{dataBase}` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci';", mySqlConnection)) + { + await mySqlCommand.ExecuteNonQueryAsync(); + } + } + + return dataBase; + } + + public async virtual Task GetInitSqlScript() + { + var sqlScriptFileInfo = _virtualFileProvider.GetFileInfo("/LINGYUN/Abp/Quartz/MySqlInstaller/Scripts/Initial.sql"); + if (!sqlScriptFileInfo.Exists || sqlScriptFileInfo.IsDirectory) + { + Logger.LogWarning("Please Check that the `Initial.sql` file exists!"); + throw new InvalidOperationException("The `Initial.sql` database initialization script file does not exist or is not valid!"); + } + + var sqlScript = await sqlScriptFileInfo.ReadAsStringAsync(); + if (sqlScript.IsNullOrWhiteSpace()) + { + Logger.LogWarning("The contents of the `Initial.sql` file are empty or invalid!"); + throw new InvalidOperationException("The contents of the `Initial.sql` file are empty or invalid!"); + } + + return sqlScript; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN/Abp/Quartz/MySqlInstaller/Scripts/Initial.sql b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN/Abp/Quartz/MySqlInstaller/Scripts/Initial.sql new file mode 100644 index 000000000..85a70bfff --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.MySqlInstaller/LINGYUN/Abp/Quartz/MySqlInstaller/Scripts/Initial.sql @@ -0,0 +1,181 @@ +# By: Ron Cordell - roncordell +# I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM. + + +# make sure you have UTF-8 collaction for best .NET interoperability +# CREATE DATABASE quartznet CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +USE `${DataBase}`; + +DROP TABLE IF EXISTS ${TablePrefix}FIRED_TRIGGERS; +DROP TABLE IF EXISTS ${TablePrefix}PAUSED_TRIGGER_GRPS; +DROP TABLE IF EXISTS ${TablePrefix}SCHEDULER_STATE; +DROP TABLE IF EXISTS ${TablePrefix}LOCKS; +DROP TABLE IF EXISTS ${TablePrefix}SIMPLE_TRIGGERS; +DROP TABLE IF EXISTS ${TablePrefix}SIMPROP_TRIGGERS; +DROP TABLE IF EXISTS ${TablePrefix}CRON_TRIGGERS; +DROP TABLE IF EXISTS ${TablePrefix}BLOB_TRIGGERS; +DROP TABLE IF EXISTS ${TablePrefix}TRIGGERS; +DROP TABLE IF EXISTS ${TablePrefix}JOB_DETAILS; +DROP TABLE IF EXISTS ${TablePrefix}CALENDARS; + +CREATE TABLE ${TablePrefix}JOB_DETAILS( +SCHED_NAME VARCHAR(120) NOT NULL, +JOB_NAME VARCHAR(200) NOT NULL, +JOB_GROUP VARCHAR(200) NOT NULL, +DESCRIPTION VARCHAR(250) NULL, +JOB_CLASS_NAME VARCHAR(250) NOT NULL, +IS_DURABLE BOOLEAN NOT NULL, +IS_NONCONCURRENT BOOLEAN NOT NULL, +IS_UPDATE_DATA BOOLEAN NOT NULL, +REQUESTS_RECOVERY BOOLEAN NOT NULL, +JOB_DATA BLOB NULL, +PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)) +ENGINE=InnoDB; + +CREATE TABLE ${TablePrefix}TRIGGERS ( +SCHED_NAME VARCHAR(120) NOT NULL, +TRIGGER_NAME VARCHAR(200) NOT NULL, +TRIGGER_GROUP VARCHAR(200) NOT NULL, +JOB_NAME VARCHAR(200) NOT NULL, +JOB_GROUP VARCHAR(200) NOT NULL, +DESCRIPTION VARCHAR(250) NULL, +NEXT_FIRE_TIME BIGINT(19) NULL, +PREV_FIRE_TIME BIGINT(19) NULL, +PRIORITY INTEGER NULL, +TRIGGER_STATE VARCHAR(16) NOT NULL, +TRIGGER_TYPE VARCHAR(8) NOT NULL, +START_TIME BIGINT(19) NOT NULL, +END_TIME BIGINT(19) NULL, +CALENDAR_NAME VARCHAR(200) NULL, +MISFIRE_INSTR SMALLINT(2) NULL, +JOB_DATA BLOB NULL, +PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), +FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) +REFERENCES ${TablePrefix}JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)) +ENGINE=InnoDB; + +CREATE TABLE ${TablePrefix}SIMPLE_TRIGGERS ( +SCHED_NAME VARCHAR(120) NOT NULL, +TRIGGER_NAME VARCHAR(200) NOT NULL, +TRIGGER_GROUP VARCHAR(200) NOT NULL, +REPEAT_COUNT BIGINT(7) NOT NULL, +REPEAT_INTERVAL BIGINT(12) NOT NULL, +TIMES_TRIGGERED BIGINT(10) NOT NULL, +PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), +FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) +REFERENCES ${TablePrefix}TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) +ENGINE=InnoDB; + +CREATE TABLE ${TablePrefix}CRON_TRIGGERS ( +SCHED_NAME VARCHAR(120) NOT NULL, +TRIGGER_NAME VARCHAR(200) NOT NULL, +TRIGGER_GROUP VARCHAR(200) NOT NULL, +CRON_EXPRESSION VARCHAR(120) NOT NULL, +TIME_ZONE_ID VARCHAR(80), +PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), +FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) +REFERENCES ${TablePrefix}TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) +ENGINE=InnoDB; + +CREATE TABLE ${TablePrefix}SIMPROP_TRIGGERS + ( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + STR_PROP_1 VARCHAR(512) NULL, + STR_PROP_2 VARCHAR(512) NULL, + STR_PROP_3 VARCHAR(512) NULL, + INT_PROP_1 INT NULL, + INT_PROP_2 INT NULL, + LONG_PROP_1 BIGINT NULL, + LONG_PROP_2 BIGINT NULL, + DEC_PROP_1 NUMERIC(13,4) NULL, + DEC_PROP_2 NUMERIC(13,4) NULL, + BOOL_PROP_1 BOOLEAN NULL, + BOOL_PROP_2 BOOLEAN NULL, + TIME_ZONE_ID VARCHAR(80) NULL, + PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES ${TablePrefix}TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) +ENGINE=InnoDB; + +CREATE TABLE ${TablePrefix}BLOB_TRIGGERS ( +SCHED_NAME VARCHAR(120) NOT NULL, +TRIGGER_NAME VARCHAR(200) NOT NULL, +TRIGGER_GROUP VARCHAR(200) NOT NULL, +BLOB_DATA BLOB NULL, +PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), +INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP), +FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) +REFERENCES ${TablePrefix}TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) +ENGINE=InnoDB; + +CREATE TABLE ${TablePrefix}CALENDARS ( +SCHED_NAME VARCHAR(120) NOT NULL, +CALENDAR_NAME VARCHAR(200) NOT NULL, +CALENDAR BLOB NOT NULL, +PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)) +ENGINE=InnoDB; + +CREATE TABLE ${TablePrefix}PAUSED_TRIGGER_GRPS ( +SCHED_NAME VARCHAR(120) NOT NULL, +TRIGGER_GROUP VARCHAR(200) NOT NULL, +PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)) +ENGINE=InnoDB; + +CREATE TABLE ${TablePrefix}FIRED_TRIGGERS ( +SCHED_NAME VARCHAR(120) NOT NULL, +ENTRY_ID VARCHAR(140) NOT NULL, +TRIGGER_NAME VARCHAR(200) NOT NULL, +TRIGGER_GROUP VARCHAR(200) NOT NULL, +INSTANCE_NAME VARCHAR(200) NOT NULL, +FIRED_TIME BIGINT(19) NOT NULL, +SCHED_TIME BIGINT(19) NOT NULL, +PRIORITY INTEGER NOT NULL, +STATE VARCHAR(16) NOT NULL, +JOB_NAME VARCHAR(200) NULL, +JOB_GROUP VARCHAR(200) NULL, +IS_NONCONCURRENT BOOLEAN NULL, +REQUESTS_RECOVERY BOOLEAN NULL, +PRIMARY KEY (SCHED_NAME,ENTRY_ID)) +ENGINE=InnoDB; + +CREATE TABLE ${TablePrefix}SCHEDULER_STATE ( +SCHED_NAME VARCHAR(120) NOT NULL, +INSTANCE_NAME VARCHAR(200) NOT NULL, +LAST_CHECKIN_TIME BIGINT(19) NOT NULL, +CHECKIN_INTERVAL BIGINT(19) NOT NULL, +PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)) +ENGINE=InnoDB; + +CREATE TABLE ${TablePrefix}LOCKS ( +SCHED_NAME VARCHAR(120) NOT NULL, +LOCK_NAME VARCHAR(40) NOT NULL, +PRIMARY KEY (SCHED_NAME,LOCK_NAME)) +ENGINE=InnoDB; + +CREATE INDEX IDX_${TablePrefix}J_REQ_RECOVERY ON ${TablePrefix}JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY); +CREATE INDEX IDX_${TablePrefix}J_GRP ON ${TablePrefix}JOB_DETAILS(SCHED_NAME,JOB_GROUP); + +CREATE INDEX IDX_${TablePrefix}T_J ON ${TablePrefix}TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); +CREATE INDEX IDX_${TablePrefix}T_JG ON ${TablePrefix}TRIGGERS(SCHED_NAME,JOB_GROUP); +CREATE INDEX IDX_${TablePrefix}T_C ON ${TablePrefix}TRIGGERS(SCHED_NAME,CALENDAR_NAME); +CREATE INDEX IDX_${TablePrefix}T_G ON ${TablePrefix}TRIGGERS(SCHED_NAME,TRIGGER_GROUP); +CREATE INDEX IDX_${TablePrefix}T_STATE ON ${TablePrefix}TRIGGERS(SCHED_NAME,TRIGGER_STATE); +CREATE INDEX IDX_${TablePrefix}T_N_STATE ON ${TablePrefix}TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); +CREATE INDEX IDX_${TablePrefix}T_N_G_STATE ON ${TablePrefix}TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); +CREATE INDEX IDX_${TablePrefix}T_NEXT_FIRE_TIME ON ${TablePrefix}TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME); +CREATE INDEX IDX_${TablePrefix}T_NFT_ST ON ${TablePrefix}TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); +CREATE INDEX IDX_${TablePrefix}T_NFT_MISFIRE ON ${TablePrefix}TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); +CREATE INDEX IDX_${TablePrefix}T_NFT_ST_MISFIRE ON ${TablePrefix}TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); +CREATE INDEX IDX_${TablePrefix}T_NFT_ST_MISFIRE_GRP ON ${TablePrefix}TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); + +CREATE INDEX IDX_${TablePrefix}FT_TRIG_INST_NAME ON ${TablePrefix}FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME); +CREATE INDEX IDX_${TablePrefix}FT_INST_JOB_REQ_RCVRY ON ${TablePrefix}FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); +CREATE INDEX IDX_${TablePrefix}FT_J_G ON ${TablePrefix}FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); +CREATE INDEX IDX_${TablePrefix}FT_JG ON ${TablePrefix}FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP); +CREATE INDEX IDX_${TablePrefix}FT_T_G ON ${TablePrefix}FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); +CREATE INDEX IDX_${TablePrefix}FT_TG ON ${TablePrefix}FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); + +commit; \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/FodyWeavers.xml b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN.Abp.Quartz.SqlServerInstaller.csproj b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN.Abp.Quartz.SqlServerInstaller.csproj new file mode 100644 index 000000000..e8ae8b21b --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN.Abp.Quartz.SqlServerInstaller.csproj @@ -0,0 +1,30 @@ + + + + + + + netstandard2.0;netstandard2.1;net8.0;net9.0 + LINGYUN.Abp.Quartz.SqlServerInstaller + LINGYUN.Abp.Quartz.SqlServerInstaller + false + false + false + + + + + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN/Abp/Quartz/SqlServerInstaller/AbpQuartzSqlServerInstallerModule.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN/Abp/Quartz/SqlServerInstaller/AbpQuartzSqlServerInstallerModule.cs new file mode 100644 index 000000000..c3c69701e --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN/Abp/Quartz/SqlServerInstaller/AbpQuartzSqlServerInstallerModule.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Modularity; +using Volo.Abp.Quartz; +using Volo.Abp.VirtualFileSystem; + +namespace LINGYUN.Abp.Quartz.SqlServerInstaller; + +[DependsOn( + typeof(AbpQuartzModule), + typeof(AbpVirtualFileSystemModule))] +public class AbpQuartzSqlServerInstallerModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + } + + public async override Task OnPreApplicationInitializationAsync(ApplicationInitializationContext context) + { + // 初始化 Quartz SqlServer 数据库 + await context.ServiceProvider + .GetRequiredService() + .InstallAsync(); + } +} \ No newline at end of file diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN/Abp/Quartz/SqlServerInstaller/QuartzSqlServerInstaller.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN/Abp/Quartz/SqlServerInstaller/QuartzSqlServerInstaller.cs new file mode 100644 index 000000000..5d4ac3166 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN/Abp/Quartz/SqlServerInstaller/QuartzSqlServerInstaller.cs @@ -0,0 +1,136 @@ +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Data; +using System.Threading.Tasks; +using Volo.Abp.Quartz; +using Volo.Abp.VirtualFileSystem; +using static Quartz.SchedulerBuilder; + +namespace LINGYUN.Abp.Quartz.SqlServerInstaller; + +public class QuartzSqlServerInstaller +{ + public ILogger Logger { protected get; set; } + + private readonly IVirtualFileProvider _virtualFileProvider; + private readonly AbpQuartzOptions _quartzOptions; + + public QuartzSqlServerInstaller( + IVirtualFileProvider virtualFileProvider, + IOptions quartzOptions) + { + _quartzOptions = quartzOptions.Value; + _virtualFileProvider = virtualFileProvider; + + Logger = NullLogger.Instance; + } + + public async virtual Task InstallAsync() + { + var dataSource = _quartzOptions.Properties["quartz.jobStore.dataSource"] ?? AdoProviderOptions.DefaultDataSourceName; + var connectionString = _quartzOptions.Properties[$"quartz.dataSource.{dataSource}.connectionString"]; + var tablePrefix = _quartzOptions.Properties["quartz.jobStore.tablePrefix"] ?? "QRTZ_"; + + if (connectionString.IsNullOrWhiteSpace()) + { + Logger.LogWarning($"Please configure the `{dataSource}` database connection string in `quartz.jobStore.dataSource`!"); + throw new ArgumentNullException(nameof(connectionString)); + } + + Logger.LogInformation("Install Quartz SqlServer..."); + + var builder = new SqlConnectionStringBuilder(connectionString); + + var dataBaseName = await CreateDataBaseIfNotExists(builder.InitialCatalog, builder); + + builder.InitialCatalog = dataBaseName; + + using var sqlConnection = new SqlConnection(builder.ConnectionString); + + if (sqlConnection.State == ConnectionState.Closed) + { + await sqlConnection.OpenAsync(); + } + + using (var sqlCommand = new SqlCommand("SELECT COUNT(1) FROM [sys].[objects] WHERE type=N'U'", sqlConnection)) + { + sqlCommand.Parameters.Add("@DataBaseName", SqlDbType.NVarChar).Value = dataBaseName; + + var rowsAffects = await sqlCommand.ExecuteScalarAsync() as long?; + if (rowsAffects > 0) + { + Logger.LogInformation($"The `{dataBaseName}` tables has already exists."); + return; + } + } + + var sqlScript = await GetInitSqlScript(); + + // USE `${DataBase}` -> USE `Workflow`; + sqlScript = sqlScript.ReplaceFirst("${DataBase}", dataBaseName); + // CREATE TABLE $(TablePrefix)JOB_DETAILS` -> CREATE TABLE QRTZ_JOB_DETAILS; + sqlScript = sqlScript.Replace("${TablePrefix}", tablePrefix); + + using (var sqlCommand = new SqlCommand(sqlScript, sqlConnection)) + { + Logger.LogInformation("The database initialization script `Initial.sql` starts..."); + + await sqlCommand.ExecuteNonQueryAsync(); + } + + Logger.LogInformation("Database initialization script `Initial.sql` complete!"); + } + + public async virtual Task CreateDataBaseIfNotExists(string dataBase, SqlConnectionStringBuilder connectionStringBuilder) + { + // 切换主数据库查询数据库是否存在 + connectionStringBuilder.InitialCatalog = "master"; + using var sqlConnection = new SqlConnection(connectionStringBuilder.ConnectionString); + if (sqlConnection.State == ConnectionState.Closed) + { + await sqlConnection.OpenAsync(); + } + + var checkDataBaseName = ""; + using (var sqlCommand = new SqlCommand("SELECT [name] FROM [master].[dbo].[sysdatabases] WHERE [name] = @DataBaseName;", sqlConnection)) + { + var dataBaseParamter = sqlCommand.Parameters.Add("DataBaseName", SqlDbType.NVarChar); + dataBaseParamter.Value = dataBase; + + checkDataBaseName = await sqlCommand.ExecuteScalarAsync() as string; + } + + if (checkDataBaseName.IsNullOrWhiteSpace()) + { + using (var sqlCommand = new SqlCommand($"CREATE DATABASE {dataBase};", sqlConnection)) + { + await sqlCommand.ExecuteNonQueryAsync(); + } + } + + return dataBase; + } + + public async virtual Task GetInitSqlScript() + { + var sqlScriptFileInfo = _virtualFileProvider.GetFileInfo("/LINGYUN/Abp/Quartz/SqlServerInstaller/Scripts/Initial.sql"); + if (!sqlScriptFileInfo.Exists || sqlScriptFileInfo.IsDirectory) + { + Logger.LogWarning("Please Check that the `Initial.sql` file exists!"); + throw new InvalidOperationException("The `Initial.sql` database initialization script file does not exist or is not valid!"); + } + + var sqlScript = await sqlScriptFileInfo.ReadAsStringAsync(); + if (sqlScript.IsNullOrWhiteSpace()) + { + Logger.LogWarning("The contents of the `Initial.sql` file are empty or invalid!"); + throw new InvalidOperationException("The contents of the `Initial.sql` file are empty or invalid!"); + } + + return sqlScript; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN/Abp/Quartz/SqlServerInstaller/Scripts/Initial.sql b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN/Abp/Quartz/SqlServerInstaller/Scripts/Initial.sql new file mode 100644 index 000000000..5c4478d7d --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.Quartz.SqlServerInstaller/LINGYUN/Abp/Quartz/SqlServerInstaller/Scripts/Initial.sql @@ -0,0 +1,370 @@ +-- this script is for SQL Server and Azure SQL + +-- This initializes the database to pristine for Quartz, by first removing any existing Quartz tables +-- and then recreating them from scratch. +-- Should you only require it to create the tables, set @DropDb to 0. + +USE [${DataBase}]; +GO + +DECLARE @DropDb BIT = 1; -- Set this to 0 to skip DROP statements, 1 to include them + +IF @DropDb = 1 +BEGIN + -- drop indexes if they exist and rebuild if current ones + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_J] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_JG] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_C] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_G] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_G_J] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_STATE] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_N_STATE] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_N_G_STATE] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_NEXT_FIRE_TIME] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_NFT_ST] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_NFT_MISFIRE] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_NFT_ST_MISFIRE] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}T_NFT_ST_MISFIRE_GRP] ON [dbo].[${TablePrefix}TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}FT_TRIG_INST_NAME] ON [dbo].[${TablePrefix}FIRED_TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}FT_INST_JOB_REQ_RCVRY] ON [dbo].[${TablePrefix}FIRED_TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}FT_J_G] ON [dbo].[${TablePrefix}FIRED_TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}FT_JG] ON [dbo].[${TablePrefix}FIRED_TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}FT_T_G] ON [dbo].[${TablePrefix}FIRED_TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}FT_TG] ON [dbo].[${TablePrefix}FIRED_TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}FT_G_J] ON [dbo].[${TablePrefix}FIRED_TRIGGERS]; + DROP INDEX IF EXISTS [IDX_${TablePrefix}FT_G_T] ON [dbo].[${TablePrefix}FIRED_TRIGGERS]; + + IF OBJECT_ID(N'[dbo].[FK_${TablePrefix}TRIGGERS_${TablePrefix}JOB_DETAILS]', N'F') IS NOT NULL + ALTER TABLE [dbo].[${TablePrefix}TRIGGERS] DROP CONSTRAINT [FK_${TablePrefix}TRIGGERS_${TablePrefix}JOB_DETAILS]; + + IF OBJECT_ID(N'[dbo].[FK_${TablePrefix}CRON_TRIGGERS_${TablePrefix}TRIGGERS]', N'F') IS NOT NULL + ALTER TABLE [dbo].[${TablePrefix}CRON_TRIGGERS] DROP CONSTRAINT [FK_${TablePrefix}CRON_TRIGGERS_${TablePrefix}TRIGGERS]; + + IF OBJECT_ID(N'[dbo].[FK_${TablePrefix}SIMPLE_TRIGGERS_${TablePrefix}TRIGGERS]', N'F') IS NOT NULL + ALTER TABLE [dbo].[${TablePrefix}SIMPLE_TRIGGERS] DROP CONSTRAINT [FK_${TablePrefix}SIMPLE_TRIGGERS_${TablePrefix}TRIGGERS]; + + IF OBJECT_ID(N'[dbo].[FK_${TablePrefix}SIMPROP_TRIGGERS_${TablePrefix}TRIGGERS]', N'F') IS NOT NULL + ALTER TABLE [dbo].[${TablePrefix}SIMPROP_TRIGGERS] DROP CONSTRAINT [FK_${TablePrefix}SIMPROP_TRIGGERS_${TablePrefix}TRIGGERS]; + + IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_${TablePrefix}JOB_LISTENERS_${TablePrefix}JOB_DETAILS]') AND parent_object_id = OBJECT_ID(N'[dbo].[${TablePrefix}JOB_LISTENERS]')) + ALTER TABLE [dbo].[${TablePrefix}JOB_LISTENERS] DROP CONSTRAINT [FK_${TablePrefix}JOB_LISTENERS_${TablePrefix}JOB_DETAILS]; + + IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_${TablePrefix}TRIGGER_LISTENERS_${TablePrefix}TRIGGERS]') AND parent_object_id = OBJECT_ID(N'[dbo].[${TablePrefix}TRIGGER_LISTENERS]')) + ALTER TABLE [dbo].[${TablePrefix}TRIGGER_LISTENERS] DROP CONSTRAINT [FK_${TablePrefix}TRIGGER_LISTENERS_${TablePrefix}TRIGGERS]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}CALENDARS]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}CALENDARS]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}CRON_TRIGGERS]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}CRON_TRIGGERS]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}BLOB_TRIGGERS]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}BLOB_TRIGGERS]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}FIRED_TRIGGERS]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}FIRED_TRIGGERS]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}PAUSED_TRIGGER_GRPS]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}PAUSED_TRIGGER_GRPS]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}JOB_LISTENERS]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}JOB_LISTENERS]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}SCHEDULER_STATE]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}SCHEDULER_STATE]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}LOCKS]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}LOCKS]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}TRIGGER_LISTENERS]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}TRIGGER_LISTENERS]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}JOB_DETAILS]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}JOB_DETAILS]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}SIMPLE_TRIGGERS]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}SIMPLE_TRIGGERS]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}SIMPROP_TRIGGERS]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}SIMPROP_TRIGGERS]; + + IF OBJECT_ID(N'[dbo].[${TablePrefix}TRIGGERS]', N'U') IS NOT NULL + DROP TABLE [dbo].[${TablePrefix}TRIGGERS]; +END + +CREATE TABLE [dbo].[${TablePrefix}CALENDARS] ( + [SCHED_NAME] nvarchar(120) NOT NULL, + [CALENDAR_NAME] nvarchar(200) NOT NULL, + [CALENDAR] varbinary(max) NOT NULL +); + +CREATE TABLE [dbo].[${TablePrefix}CRON_TRIGGERS] ( + [SCHED_NAME] nvarchar(120) NOT NULL, + [TRIGGER_NAME] nvarchar(150) NOT NULL, + [TRIGGER_GROUP] nvarchar(150) NOT NULL, + [CRON_EXPRESSION] nvarchar(120) NOT NULL, + [TIME_ZONE_ID] nvarchar(80) +); + +CREATE TABLE [dbo].[${TablePrefix}FIRED_TRIGGERS] ( + [SCHED_NAME] nvarchar(120) NOT NULL, + [ENTRY_ID] nvarchar(140) NOT NULL, + [TRIGGER_NAME] nvarchar(150) NOT NULL, + [TRIGGER_GROUP] nvarchar(150) NOT NULL, + [INSTANCE_NAME] nvarchar(200) NOT NULL, + [FIRED_TIME] bigint NOT NULL, + [SCHED_TIME] bigint NOT NULL, + [PRIORITY] int NOT NULL, + [STATE] nvarchar(16) NOT NULL, + [JOB_NAME] nvarchar(150) NULL, + [JOB_GROUP] nvarchar(150) NULL, + [IS_NONCONCURRENT] bit NULL, + [REQUESTS_RECOVERY] bit NULL +); + +CREATE TABLE [dbo].[${TablePrefix}PAUSED_TRIGGER_GRPS] ( + [SCHED_NAME] nvarchar(120) NOT NULL, + [TRIGGER_GROUP] nvarchar(150) NOT NULL +); + +CREATE TABLE [dbo].[${TablePrefix}SCHEDULER_STATE] ( + [SCHED_NAME] nvarchar(120) NOT NULL, + [INSTANCE_NAME] nvarchar(200) NOT NULL, + [LAST_CHECKIN_TIME] bigint NOT NULL, + [CHECKIN_INTERVAL] bigint NOT NULL +); + +CREATE TABLE [dbo].[${TablePrefix}LOCKS] ( + [SCHED_NAME] nvarchar(120) NOT NULL, + [LOCK_NAME] nvarchar(40) NOT NULL +); + +CREATE TABLE [dbo].[${TablePrefix}JOB_DETAILS] ( + [SCHED_NAME] nvarchar(120) NOT NULL, + [JOB_NAME] nvarchar(150) NOT NULL, + [JOB_GROUP] nvarchar(150) NOT NULL, + [DESCRIPTION] nvarchar(250) NULL, + [JOB_CLASS_NAME] nvarchar(250) NOT NULL, + [IS_DURABLE] bit NOT NULL, + [IS_NONCONCURRENT] bit NOT NULL, + [IS_UPDATE_DATA] bit NOT NULL, + [REQUESTS_RECOVERY] bit NOT NULL, + [JOB_DATA] varbinary(max) NULL +); + +CREATE TABLE [dbo].[${TablePrefix}SIMPLE_TRIGGERS] ( + [SCHED_NAME] nvarchar(120) NOT NULL, + [TRIGGER_NAME] nvarchar(150) NOT NULL, + [TRIGGER_GROUP] nvarchar(150) NOT NULL, + [REPEAT_COUNT] int NOT NULL, + [REPEAT_INTERVAL] bigint NOT NULL, + [TIMES_TRIGGERED] int NOT NULL +); + +CREATE TABLE [dbo].[${TablePrefix}SIMPROP_TRIGGERS] ( + [SCHED_NAME] nvarchar(120) NOT NULL, + [TRIGGER_NAME] nvarchar(150) NOT NULL, + [TRIGGER_GROUP] nvarchar(150) NOT NULL, + [STR_PROP_1] nvarchar(512) NULL, + [STR_PROP_2] nvarchar(512) NULL, + [STR_PROP_3] nvarchar(512) NULL, + [INT_PROP_1] int NULL, + [INT_PROP_2] int NULL, + [LONG_PROP_1] bigint NULL, + [LONG_PROP_2] bigint NULL, + [DEC_PROP_1] numeric(13,4) NULL, + [DEC_PROP_2] numeric(13,4) NULL, + [BOOL_PROP_1] bit NULL, + [BOOL_PROP_2] bit NULL, + [TIME_ZONE_ID] nvarchar(80) NULL +); + +CREATE TABLE [dbo].[${TablePrefix}BLOB_TRIGGERS] ( + [SCHED_NAME] nvarchar(120) NOT NULL, + [TRIGGER_NAME] nvarchar(150) NOT NULL, + [TRIGGER_GROUP] nvarchar(150) NOT NULL, + [BLOB_DATA] varbinary(max) NULL +); + +CREATE TABLE [dbo].[${TablePrefix}TRIGGERS] ( + [SCHED_NAME] nvarchar(120) NOT NULL, + [TRIGGER_NAME] nvarchar(150) NOT NULL, + [TRIGGER_GROUP] nvarchar(150) NOT NULL, + [JOB_NAME] nvarchar(150) NOT NULL, + [JOB_GROUP] nvarchar(150) NOT NULL, + [DESCRIPTION] nvarchar(250) NULL, + [NEXT_FIRE_TIME] bigint NULL, + [PREV_FIRE_TIME] bigint NULL, + [PRIORITY] int NULL, + [TRIGGER_STATE] nvarchar(16) NOT NULL, + [TRIGGER_TYPE] nvarchar(8) NOT NULL, + [START_TIME] bigint NOT NULL, + [END_TIME] bigint NULL, + [CALENDAR_NAME] nvarchar(200) NULL, + [MISFIRE_INSTR] int NULL, + [JOB_DATA] varbinary(max) NULL +); +GO + +ALTER TABLE [dbo].[${TablePrefix}CALENDARS] WITH NOCHECK ADD + CONSTRAINT [PK_${TablePrefix}CALENDARS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [CALENDAR_NAME] + ); +GO + +ALTER TABLE [dbo].[${TablePrefix}CRON_TRIGGERS] WITH NOCHECK ADD + CONSTRAINT [PK_${TablePrefix}CRON_TRIGGERS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ); +GO + +ALTER TABLE [dbo].[${TablePrefix}FIRED_TRIGGERS] WITH NOCHECK ADD + CONSTRAINT [PK_${TablePrefix}FIRED_TRIGGERS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [ENTRY_ID] + ); +GO + +ALTER TABLE [dbo].[${TablePrefix}PAUSED_TRIGGER_GRPS] WITH NOCHECK ADD + CONSTRAINT [PK_${TablePrefix}PAUSED_TRIGGER_GRPS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [TRIGGER_GROUP] + ); +GO + +ALTER TABLE [dbo].[${TablePrefix}SCHEDULER_STATE] WITH NOCHECK ADD + CONSTRAINT [PK_${TablePrefix}SCHEDULER_STATE] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [INSTANCE_NAME] + ); +GO + +ALTER TABLE [dbo].[${TablePrefix}LOCKS] WITH NOCHECK ADD + CONSTRAINT [PK_${TablePrefix}LOCKS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [LOCK_NAME] + ); +GO + +ALTER TABLE [dbo].[${TablePrefix}JOB_DETAILS] WITH NOCHECK ADD + CONSTRAINT [PK_${TablePrefix}JOB_DETAILS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [JOB_NAME], + [JOB_GROUP] + ); +GO + +ALTER TABLE [dbo].[${TablePrefix}SIMPLE_TRIGGERS] WITH NOCHECK ADD + CONSTRAINT [PK_${TablePrefix}SIMPLE_TRIGGERS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ); +GO + +ALTER TABLE [dbo].[${TablePrefix}SIMPROP_TRIGGERS] WITH NOCHECK ADD + CONSTRAINT [PK_${TablePrefix}SIMPROP_TRIGGERS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ); +GO + +ALTER TABLE [dbo].[${TablePrefix}TRIGGERS] WITH NOCHECK ADD + CONSTRAINT [PK_${TablePrefix}TRIGGERS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ); +GO + +ALTER TABLE [dbo].[${TablePrefix}BLOB_TRIGGERS] WITH NOCHECK ADD + CONSTRAINT [PK_${TablePrefix}BLOB_TRIGGERS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ); +GO + +ALTER TABLE [dbo].[${TablePrefix}CRON_TRIGGERS] ADD + CONSTRAINT [FK_${TablePrefix}CRON_TRIGGERS_${TablePrefix}TRIGGERS] FOREIGN KEY + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) REFERENCES [dbo].[${TablePrefix}TRIGGERS] ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) ON DELETE CASCADE; +GO + +ALTER TABLE [dbo].[${TablePrefix}SIMPLE_TRIGGERS] ADD + CONSTRAINT [FK_${TablePrefix}SIMPLE_TRIGGERS_${TablePrefix}TRIGGERS] FOREIGN KEY + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) REFERENCES [dbo].[${TablePrefix}TRIGGERS] ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) ON DELETE CASCADE; +GO + +ALTER TABLE [dbo].[${TablePrefix}SIMPROP_TRIGGERS] ADD + CONSTRAINT [FK_${TablePrefix}SIMPROP_TRIGGERS_${TablePrefix}TRIGGERS] FOREIGN KEY + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) REFERENCES [dbo].[${TablePrefix}TRIGGERS] ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) ON DELETE CASCADE; +GO + +ALTER TABLE [dbo].[${TablePrefix}TRIGGERS] ADD + CONSTRAINT [FK_${TablePrefix}TRIGGERS_${TablePrefix}JOB_DETAILS] FOREIGN KEY + ( + [SCHED_NAME], + [JOB_NAME], + [JOB_GROUP] + ) REFERENCES [dbo].[${TablePrefix}JOB_DETAILS] ( + [SCHED_NAME], + [JOB_NAME], + [JOB_GROUP] + ); +GO + +-- Create indexes +CREATE INDEX [IDX_${TablePrefix}T_G_J] ON [dbo].[${TablePrefix}TRIGGERS](SCHED_NAME, JOB_GROUP, JOB_NAME); +CREATE INDEX [IDX_${TablePrefix}T_C] ON [dbo].[${TablePrefix}TRIGGERS](SCHED_NAME, CALENDAR_NAME); + +CREATE INDEX [IDX_${TablePrefix}T_N_G_STATE] ON [dbo].[${TablePrefix}TRIGGERS](SCHED_NAME, TRIGGER_GROUP, TRIGGER_STATE); +CREATE INDEX [IDX_${TablePrefix}T_STATE] ON [dbo].[${TablePrefix}TRIGGERS](SCHED_NAME, TRIGGER_STATE); +CREATE INDEX [IDX_${TablePrefix}T_N_STATE] ON [dbo].[${TablePrefix}TRIGGERS](SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, TRIGGER_STATE); +CREATE INDEX [IDX_${TablePrefix}T_NEXT_FIRE_TIME] ON [dbo].[${TablePrefix}TRIGGERS](SCHED_NAME, NEXT_FIRE_TIME); +CREATE INDEX [IDX_${TablePrefix}T_NFT_ST] ON [dbo].[${TablePrefix}TRIGGERS](SCHED_NAME, TRIGGER_STATE, NEXT_FIRE_TIME); +CREATE INDEX [IDX_${TablePrefix}T_NFT_ST_MISFIRE] ON [dbo].[${TablePrefix}TRIGGERS](SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_STATE); +CREATE INDEX [IDX_${TablePrefix}T_NFT_ST_MISFIRE_GRP] ON [dbo].[${TablePrefix}TRIGGERS](SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_GROUP, TRIGGER_STATE); + +CREATE INDEX [IDX_${TablePrefix}FT_INST_JOB_REQ_RCVRY] ON [dbo].[${TablePrefix}FIRED_TRIGGERS](SCHED_NAME, INSTANCE_NAME, REQUESTS_RECOVERY); +CREATE INDEX [IDX_${TablePrefix}FT_G_J] ON [dbo].[${TablePrefix}FIRED_TRIGGERS](SCHED_NAME, JOB_GROUP, JOB_NAME); +CREATE INDEX [IDX_${TablePrefix}FT_G_T] ON [dbo].[${TablePrefix}FIRED_TRIGGERS](SCHED_NAME, TRIGGER_GROUP, TRIGGER_NAME); +GO \ No newline at end of file