feat: pin running instances to immutable published definition versions

Replace register/replace with publish so new graphs can ship without rewriting old ones, and keep in-flight work on the version it started with.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
0264408
2026-09-16 10:30:14 +08:00
co-authored by Cursor
parent 8bc628613d
commit 9bda7c417c
35 changed files with 688 additions and 382 deletions
@@ -15,6 +15,8 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -24,36 +26,45 @@ import java.util.Optional;
/** JDBC implementation of the definition storage port; steps and their candidates live in separate tables. */
public final class JdbcProcessDefinitionRepository implements ProcessDefinitionRepository {
private static final String INSERT_PROCESS =
"INSERT INTO ordo_process (id, current_version, name) VALUES (?, ?, ?)";
private static final String UPDATE_PROCESS =
"UPDATE ordo_process SET current_version = ?, name = ? WHERE id = ?";
private static final String LOCK_PROCESS =
"SELECT current_version FROM ordo_process WHERE id = ? FOR UPDATE";
private static final String INSERT_DEFINITION =
"INSERT INTO ordo_process_definition (id, name) VALUES (?, ?)";
"INSERT INTO ordo_process_definition (id, version, name, created_at) VALUES (?, ?, ?, ?)";
private static final String INSERT_STEP =
"INSERT INTO ordo_approval_step (definition_id, step_id, step_name, policy, step_order, kind, action_key,"
+ " due_after, due_then, due_to, due_action) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
"INSERT INTO ordo_approval_step (definition_id, definition_version, step_id, step_name, policy, step_order,"
+ " kind, action_key, due_after, due_then, due_to, due_action)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
private static final String INSERT_CANDIDATE =
"INSERT INTO ordo_step_candidate (definition_id, step_id, candidate, candidate_order) VALUES (?, ?, ?, ?)";
"INSERT INTO ordo_step_candidate (definition_id, definition_version, step_id, candidate, candidate_order)"
+ " VALUES (?, ?, ?, ?, ?)";
private static final String INSERT_TRANSITION =
"INSERT INTO ordo_step_transition (definition_id, from_step_id, to_step_id, condition_key, priority) VALUES (?, ?, ?, ?, ?)";
private static final String UPDATE_DEFINITION_NAME =
"UPDATE ordo_process_definition SET name = ? WHERE id = ?";
private static final String DELETE_TRANSITIONS =
"DELETE FROM ordo_step_transition WHERE definition_id = ?";
private static final String DELETE_CANDIDATES =
"DELETE FROM ordo_step_candidate WHERE definition_id = ?";
private static final String DELETE_STEPS =
"DELETE FROM ordo_approval_step WHERE definition_id = ?";
"INSERT INTO ordo_step_transition (definition_id, definition_version, from_step_id, to_step_id,"
+ " condition_key, priority) VALUES (?, ?, ?, ?, ?, ?)";
private static final String SELECT_DEFINITION =
"SELECT id, name FROM ordo_process_definition WHERE id = ?";
"SELECT name FROM ordo_process_definition WHERE id = ? AND version = ?";
private static final String SELECT_STEPS =
"SELECT step_id, step_name, policy, kind, action_key, due_after, due_then, due_to, due_action"
+ " FROM ordo_approval_step"
+ " WHERE definition_id = ? ORDER BY step_order";
+ " WHERE definition_id = ? AND definition_version = ? ORDER BY step_order";
private static final String SELECT_CANDIDATES =
"SELECT step_id, candidate FROM ordo_step_candidate WHERE definition_id = ? ORDER BY step_id, candidate_order";
"SELECT step_id, candidate FROM ordo_step_candidate"
+ " WHERE definition_id = ? AND definition_version = ? ORDER BY step_id, candidate_order";
private static final String SELECT_TRANSITIONS =
"SELECT from_step_id, to_step_id, condition_key, priority FROM ordo_step_transition "
+ "WHERE definition_id = ? ORDER BY from_step_id, priority";
private static final String SELECT_DEFINITIONS_PAGE =
"SELECT id, name FROM ordo_process_definition ORDER BY id LIMIT ? OFFSET ?";
+ "WHERE definition_id = ? AND definition_version = ? ORDER BY from_step_id, priority";
private static final String SELECT_LATEST_PAGE =
"SELECT p.id, p.current_version, d.name FROM ordo_process p"
+ " JOIN ordo_process_definition d ON d.id = p.id AND d.version = p.current_version"
+ " ORDER BY p.id LIMIT ? OFFSET ?";
private static final String COUNT_PROCESSES = "SELECT COUNT(*) FROM ordo_process";
private static final String SELECT_VERSIONS_PAGE =
"SELECT version, name FROM ordo_process_definition WHERE id = ? ORDER BY version DESC LIMIT ? OFFSET ?";
private static final String COUNT_VERSIONS =
"SELECT COUNT(*) FROM ordo_process_definition WHERE id = ?";
private final JdbcConnectionProvider connectionProvider;
@@ -62,62 +73,51 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
}
@Override
public boolean insertIfAbsent(ProcessDefinition definition) {
public ProcessDefinition publish(ProcessDefinition definition) {
Objects.requireNonNull(definition, "definition must not be null");
Connection connection = connectionProvider.getConnection();
try {
if (!insertDefinitionRow(connection, definition)) {
return false;
Integer current = lockCurrentVersion(connection, definition.id());
if (current == null) {
if (insertProcessRow(connection, definition.id(), 1, definition.name())) {
ProcessDefinition first = definition.withVersion(1);
insertDefinitionVersion(connection, first);
insertGraph(connection, first);
return first;
}
current = lockCurrentVersion(connection, definition.id());
if (current == null) {
throw new JdbcStorageException(
"failed to lock process after concurrent publish: " + definition.id(),
new IllegalStateException("process row missing"));
}
}
insertGraph(connection, definition);
return true;
ProcessDefinition latest = assemble(connection, definition.id(), current);
if (latest.sameGraph(definition)) {
return latest;
}
int next = current + 1;
ProcessDefinition published = definition.withVersion(next);
insertDefinitionVersion(connection, published);
insertGraph(connection, published);
updateProcess(connection, definition.id(), next, definition.name());
return published;
} catch (SQLException e) {
throw new JdbcStorageException("failed to insert definition: " + definition.id(), e);
throw new JdbcStorageException("failed to publish definition: " + definition.id(), e);
} finally {
connectionProvider.close(connection);
}
}
@Override
public void upsert(ProcessDefinition definition) {
Objects.requireNonNull(definition, "definition must not be null");
public Optional<ProcessDefinition> findLatest(String definitionId) {
Connection connection = connectionProvider.getConnection();
try {
// PostgreSQL aborts the current transaction on unique-constraint violations,
// so upsert must not probe existence via a failing INSERT.
if (definitionExists(connection, definition.id())) {
updateDefinitionName(connection, definition);
deleteGraph(connection, definition.id());
} else {
try (PreparedStatement insert = connection.prepareStatement(INSERT_DEFINITION)) {
insert.setString(1, definition.id());
insert.setString(2, definition.name());
insert.executeUpdate();
}
Integer version = currentVersion(connection, definitionId);
if (version == null) {
return Optional.empty();
}
insertGraph(connection, definition);
} catch (SQLException e) {
throw new JdbcStorageException("failed to upsert definition: " + definition.id(), e);
} finally {
connectionProvider.close(connection);
}
}
@Override
public Optional<ProcessDefinition> findById(String definitionId) {
Connection connection = connectionProvider.getConnection();
try {
String name;
try (PreparedStatement selectDefinition = connection.prepareStatement(SELECT_DEFINITION)) {
selectDefinition.setString(1, definitionId);
try (ResultSet resultSet = selectDefinition.executeQuery()) {
if (!resultSet.next()) {
return Optional.empty();
}
name = resultSet.getString("name");
}
}
return Optional.of(assemble(connection, definitionId, name));
return Optional.of(assemble(connection, definitionId, version));
} catch (SQLException e) {
throw new JdbcStorageException("failed to load definition: " + definitionId, e);
} finally {
@@ -125,24 +125,41 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
}
}
@Override
public Optional<ProcessDefinition> find(String definitionId, int version) {
Connection connection = connectionProvider.getConnection();
try {
if (!definitionExists(connection, definitionId, version)) {
return Optional.empty();
}
return Optional.of(assemble(connection, definitionId, version));
} catch (SQLException e) {
throw new JdbcStorageException("failed to load definition: " + definitionId + " version " + version, e);
} finally {
connectionProvider.close(connection);
}
}
@Override
public Page<ProcessDefinition> findAll(PageRequest pageRequest) {
Objects.requireNonNull(pageRequest, "pageRequest must not be null");
Connection connection = connectionProvider.getConnection();
try {
long total = PageSupport.count(connection, "SELECT COUNT(*) FROM ordo_process_definition", List.of());
long total = PageSupport.count(connection, COUNT_PROCESSES, List.of());
List<ProcessDefinition> content = new ArrayList<>();
try (PreparedStatement select = connection.prepareStatement(SELECT_DEFINITIONS_PAGE)) {
try (PreparedStatement select = connection.prepareStatement(SELECT_LATEST_PAGE)) {
select.setInt(1, pageRequest.size());
select.setInt(2, pageRequest.offset());
List<Map.Entry<String, String>> idsAndNames = new ArrayList<>();
List<int[]> versions = new ArrayList<>();
List<String> ids = new ArrayList<>();
try (ResultSet resultSet = select.executeQuery()) {
while (resultSet.next()) {
idsAndNames.add(Map.entry(resultSet.getString("id"), resultSet.getString("name")));
ids.add(resultSet.getString("id"));
versions.add(new int[] {resultSet.getInt("current_version")});
}
}
for (Map.Entry<String, String> idAndName : idsAndNames) {
content.add(assemble(connection, idAndName.getKey(), idAndName.getValue()));
for (int i = 0; i < ids.size(); i++) {
content.add(assemble(connection, ids.get(i), versions.get(i)[0]));
}
}
return new Page<>(content, total, pageRequest.page(), pageRequest.size());
@@ -153,10 +170,52 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
}
}
private static ProcessDefinition assemble(Connection connection, String definitionId, String name) throws SQLException {
@Override
public Page<ProcessDefinition> findVersions(String definitionId, PageRequest pageRequest) {
Objects.requireNonNull(pageRequest, "pageRequest must not be null");
Connection connection = connectionProvider.getConnection();
try {
long total = PageSupport.count(connection, COUNT_VERSIONS, List.of(definitionId));
List<Integer> versionNumbers = new ArrayList<>();
try (PreparedStatement select = connection.prepareStatement(SELECT_VERSIONS_PAGE)) {
select.setString(1, definitionId);
select.setInt(2, pageRequest.size());
select.setInt(3, pageRequest.offset());
try (ResultSet resultSet = select.executeQuery()) {
while (resultSet.next()) {
versionNumbers.add(resultSet.getInt("version"));
}
}
}
List<ProcessDefinition> content = new ArrayList<>();
for (int version : versionNumbers) {
content.add(assemble(connection, definitionId, version));
}
return new Page<>(content, total, pageRequest.page(), pageRequest.size());
} catch (SQLException e) {
throw new JdbcStorageException("failed to list definition versions: " + definitionId, e);
} finally {
connectionProvider.close(connection);
}
}
private static ProcessDefinition assemble(Connection connection, String definitionId, int version)
throws SQLException {
String name;
try (PreparedStatement selectDefinition = connection.prepareStatement(SELECT_DEFINITION)) {
selectDefinition.setString(1, definitionId);
selectDefinition.setInt(2, version);
try (ResultSet resultSet = selectDefinition.executeQuery()) {
if (!resultSet.next()) {
throw new SQLException("definition row missing: " + definitionId + " version " + version);
}
name = resultSet.getString("name");
}
}
List<StepRow> stepRows = new ArrayList<>();
try (PreparedStatement selectSteps = connection.prepareStatement(SELECT_STEPS)) {
selectSteps.setString(1, definitionId);
selectSteps.setInt(2, version);
try (ResultSet resultSet = selectSteps.executeQuery()) {
while (resultSet.next()) {
stepRows.add(ApprovalStepMapper.readRow(resultSet));
@@ -166,6 +225,7 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
Map<String, List<String>> candidatesByStep = new LinkedHashMap<>();
try (PreparedStatement selectCandidates = connection.prepareStatement(SELECT_CANDIDATES)) {
selectCandidates.setString(1, definitionId);
selectCandidates.setInt(2, version);
try (ResultSet resultSet = selectCandidates.executeQuery()) {
while (resultSet.next()) {
candidatesByStep.computeIfAbsent(resultSet.getString("step_id"), key -> new ArrayList<>())
@@ -182,6 +242,7 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
List<StepTransition> transitions = new ArrayList<>();
try (PreparedStatement selectTransitions = connection.prepareStatement(SELECT_TRANSITIONS)) {
selectTransitions.setString(1, definitionId);
selectTransitions.setInt(2, version);
try (ResultSet resultSet = selectTransitions.executeQuery()) {
while (resultSet.next()) {
TransitionRow row = StepTransitionMapper.readRow(resultSet);
@@ -190,23 +251,46 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
}
}
}
return new ProcessDefinition(definitionId, name, steps, transitions);
return new ProcessDefinition(definitionId, version, name, steps, transitions);
}
private static boolean definitionExists(Connection connection, String definitionId) throws SQLException {
private static Integer lockCurrentVersion(Connection connection, String definitionId) throws SQLException {
try (PreparedStatement select = connection.prepareStatement(LOCK_PROCESS)) {
select.setString(1, definitionId);
try (ResultSet resultSet = select.executeQuery()) {
return resultSet.next() ? resultSet.getInt("current_version") : null;
}
}
}
private static Integer currentVersion(Connection connection, String definitionId) throws SQLException {
try (PreparedStatement select = connection.prepareStatement(
"SELECT current_version FROM ordo_process WHERE id = ?")) {
select.setString(1, definitionId);
try (ResultSet resultSet = select.executeQuery()) {
return resultSet.next() ? resultSet.getInt("current_version") : null;
}
}
}
private static boolean definitionExists(Connection connection, String definitionId, int version)
throws SQLException {
try (PreparedStatement select = connection.prepareStatement(SELECT_DEFINITION)) {
select.setString(1, definitionId);
select.setInt(2, version);
try (ResultSet resultSet = select.executeQuery()) {
return resultSet.next();
}
}
}
private static boolean insertDefinitionRow(Connection connection, ProcessDefinition definition) throws SQLException {
try (PreparedStatement insertDefinition = connection.prepareStatement(INSERT_DEFINITION)) {
insertDefinition.setString(1, definition.id());
insertDefinition.setString(2, definition.name());
insertDefinition.executeUpdate();
private static boolean insertProcessRow(Connection connection, String id, int version, String name)
throws SQLException {
try (PreparedStatement insert = connection.prepareStatement(INSERT_PROCESS)) {
insert.setString(1, id);
insert.setInt(2, version);
insert.setString(3, name);
insert.executeUpdate();
return true;
} catch (SQLException e) {
if (isDuplicateKey(e)) {
@@ -216,26 +300,23 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
}
}
private static void updateDefinitionName(Connection connection, ProcessDefinition definition) throws SQLException {
try (PreparedStatement update = connection.prepareStatement(UPDATE_DEFINITION_NAME)) {
update.setString(1, definition.name());
update.setString(2, definition.id());
private static void updateProcess(Connection connection, String id, int version, String name) throws SQLException {
try (PreparedStatement update = connection.prepareStatement(UPDATE_PROCESS)) {
update.setInt(1, version);
update.setString(2, name);
update.setString(3, id);
update.executeUpdate();
}
}
private static void deleteGraph(Connection connection, String definitionId) throws SQLException {
try (PreparedStatement deleteTransitions = connection.prepareStatement(DELETE_TRANSITIONS)) {
deleteTransitions.setString(1, definitionId);
deleteTransitions.executeUpdate();
}
try (PreparedStatement deleteCandidates = connection.prepareStatement(DELETE_CANDIDATES)) {
deleteCandidates.setString(1, definitionId);
deleteCandidates.executeUpdate();
}
try (PreparedStatement deleteSteps = connection.prepareStatement(DELETE_STEPS)) {
deleteSteps.setString(1, definitionId);
deleteSteps.executeUpdate();
private static void insertDefinitionVersion(Connection connection, ProcessDefinition definition)
throws SQLException {
try (PreparedStatement insert = connection.prepareStatement(INSERT_DEFINITION)) {
insert.setString(1, definition.id());
insert.setInt(2, definition.version());
insert.setString(3, definition.name());
insert.setTimestamp(4, Timestamp.from(Instant.now()));
insert.executeUpdate();
}
}
@@ -244,22 +325,23 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
for (ApprovalStep step : definition.steps()) {
try (PreparedStatement insertStep = connection.prepareStatement(INSERT_STEP)) {
insertStep.setString(1, definition.id());
insertStep.setString(2, step.id());
insertStep.setString(3, step.name());
insertStep.setString(4, step.policy().name());
insertStep.setInt(5, stepOrder++);
insertStep.setString(6, step.kind().name());
insertStep.setString(7, step.actionKey());
insertStep.setInt(2, definition.version());
insertStep.setString(3, step.id());
insertStep.setString(4, step.name());
insertStep.setString(5, step.policy().name());
insertStep.setInt(6, stepOrder++);
insertStep.setString(7, step.kind().name());
insertStep.setString(8, step.actionKey());
if (step.due() == null) {
insertStep.setString(8, null);
insertStep.setString(9, null);
insertStep.setString(10, null);
insertStep.setString(11, null);
insertStep.setString(12, null);
} else {
insertStep.setString(8, step.due().after().toString());
insertStep.setString(9, step.due().then().name());
insertStep.setString(10, step.due().to());
insertStep.setString(11, step.due().action());
insertStep.setString(9, step.due().after().toString());
insertStep.setString(10, step.due().then().name());
insertStep.setString(11, step.due().to());
insertStep.setString(12, step.due().action());
}
insertStep.executeUpdate();
}
@@ -267,9 +349,10 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
for (String candidate : step.candidates()) {
try (PreparedStatement insertCandidate = connection.prepareStatement(INSERT_CANDIDATE)) {
insertCandidate.setString(1, definition.id());
insertCandidate.setString(2, step.id());
insertCandidate.setString(3, candidate);
insertCandidate.setInt(4, candidateOrder++);
insertCandidate.setInt(2, definition.version());
insertCandidate.setString(3, step.id());
insertCandidate.setString(4, candidate);
insertCandidate.setInt(5, candidateOrder++);
insertCandidate.executeUpdate();
}
}
@@ -277,10 +360,11 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
for (StepTransition transition : definition.transitions()) {
try (PreparedStatement insertTransition = connection.prepareStatement(INSERT_TRANSITION)) {
insertTransition.setString(1, definition.id());
insertTransition.setString(2, transition.fromStepId());
insertTransition.setString(3, transition.toStepId());
insertTransition.setString(4, transition.conditionKey());
insertTransition.setInt(5, transition.priority());
insertTransition.setInt(2, definition.version());
insertTransition.setString(3, transition.fromStepId());
insertTransition.setString(4, transition.toStepId());
insertTransition.setString(5, transition.conditionKey());
insertTransition.setInt(6, transition.priority());
insertTransition.executeUpdate();
}
}
@@ -22,14 +22,14 @@ import java.util.Optional;
/** JDBC implementation of the instance storage port. */
public final class JdbcProcessInstanceRepository implements ProcessInstanceRepository {
private static final String INSERT_INSTANCE =
"INSERT INTO ordo_process_instance (id, definition_id, initiator, status, context_json, started_at, finished_at)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
"INSERT INTO ordo_process_instance (id, definition_id, definition_version, initiator, status, context_json,"
+ " started_at, finished_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
private static final String UPDATE_INSTANCE =
"UPDATE ordo_process_instance SET status = ?, finished_at = ? WHERE id = ?";
private static final String COMPLETE_IF_RUNNING =
"UPDATE ordo_process_instance SET status = ?, finished_at = ? WHERE id = ? AND status = 'RUNNING'";
private static final String SELECT_INSTANCE =
"SELECT id, definition_id, initiator, status, context_json, started_at, finished_at"
"SELECT id, definition_id, definition_version, initiator, status, context_json, started_at, finished_at"
+ " FROM ordo_process_instance WHERE id = ?";
private static final String EXISTS_RUNNING =
"SELECT 1 FROM ordo_process_instance WHERE definition_id = ? AND status = ? LIMIT 1";
@@ -124,8 +124,8 @@ public final class JdbcProcessInstanceRepository implements ProcessInstanceRepos
try {
long total = PageSupport.count(connection,
"SELECT COUNT(*) FROM ordo_process_instance" + where.sql(), where.params());
String sql = "SELECT id, definition_id, initiator, status, context_json, started_at, finished_at"
+ " FROM ordo_process_instance" + where.sql()
String sql = "SELECT id, definition_id, definition_version, initiator, status, context_json, started_at,"
+ " finished_at FROM ordo_process_instance" + where.sql()
+ " ORDER BY started_at DESC, id DESC LIMIT ? OFFSET ?";
List<ProcessInstance> content = new ArrayList<>();
try (PreparedStatement select = connection.prepareStatement(sql)) {
@@ -16,11 +16,12 @@ public final class ProcessInstanceMapper {
public static void bindInsert(PreparedStatement statement, ProcessInstance instance) throws SQLException {
statement.setString(1, instance.id());
statement.setString(2, instance.definitionId());
statement.setString(3, instance.initiator());
statement.setString(4, instance.status().name());
statement.setString(5, ProcessContextCodec.encode(instance.context()));
statement.setTimestamp(6, Timestamp.from(instance.startedAt()));
statement.setTimestamp(7, instance.finishedAt() == null ? null : Timestamp.from(instance.finishedAt()));
statement.setInt(3, instance.definitionVersion());
statement.setString(4, instance.initiator());
statement.setString(5, instance.status().name());
statement.setString(6, ProcessContextCodec.encode(instance.context()));
statement.setTimestamp(7, Timestamp.from(instance.startedAt()));
statement.setTimestamp(8, instance.finishedAt() == null ? null : Timestamp.from(instance.finishedAt()));
}
public static void bindUpdate(PreparedStatement statement, ProcessInstance instance) throws SQLException {
@@ -35,6 +36,7 @@ public final class ProcessInstanceMapper {
return new ProcessInstance(
resultSet.getString("id"),
resultSet.getString("definition_id"),
resultSet.getInt("definition_version"),
resultSet.getString("initiator"),
ProcessStatus.valueOf(resultSet.getString("status")),
startedAt.toInstant(),
@@ -0,0 +1,96 @@
CREATE TABLE ordo_process (
id VARCHAR(64) PRIMARY KEY,
current_version INTEGER NOT NULL,
name VARCHAR(255) NOT NULL
);
INSERT INTO ordo_process (id, current_version, name)
SELECT id, 1, name FROM ordo_process_definition;
ALTER TABLE ordo_process_instance DROP CONSTRAINT fk_process_instance_definition;
ALTER TABLE ordo_approval_step DROP CONSTRAINT fk_approval_step_definition;
ALTER TABLE ordo_step_candidate DROP CONSTRAINT fk_step_candidate_step;
ALTER TABLE ordo_step_transition DROP CONSTRAINT fk_transition_from;
ALTER TABLE ordo_step_transition DROP CONSTRAINT fk_transition_to;
CREATE TABLE ordo_process_definition_v7 (
id VARCHAR(64) NOT NULL,
version INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id, version)
);
INSERT INTO ordo_process_definition_v7 (id, version, name)
SELECT id, 1, name FROM ordo_process_definition;
DROP TABLE ordo_process_definition;
ALTER TABLE ordo_process_definition_v7 RENAME TO ordo_process_definition;
CREATE TABLE ordo_approval_step_v7 (
definition_id VARCHAR(64) NOT NULL,
definition_version INTEGER NOT NULL,
step_id VARCHAR(64) NOT NULL,
step_name VARCHAR(255) NOT NULL,
step_order INTEGER NOT NULL,
policy VARCHAR(16) NOT NULL,
kind VARCHAR(16) NOT NULL,
action_key VARCHAR(255),
due_after VARCHAR(32),
due_then VARCHAR(16),
due_to VARCHAR(255),
due_action VARCHAR(255),
PRIMARY KEY (definition_id, definition_version, step_id),
CONSTRAINT fk_approval_step_definition FOREIGN KEY (definition_id, definition_version)
REFERENCES ordo_process_definition (id, version)
);
INSERT INTO ordo_approval_step_v7 (definition_id, definition_version, step_id, step_name, step_order, policy, kind,
action_key, due_after, due_then, due_to, due_action)
SELECT definition_id, 1, step_id, step_name, step_order, policy, kind, action_key, due_after, due_then, due_to,
due_action
FROM ordo_approval_step;
DROP TABLE ordo_approval_step;
ALTER TABLE ordo_approval_step_v7 RENAME TO ordo_approval_step;
CREATE TABLE ordo_step_candidate_v7 (
definition_id VARCHAR(64) NOT NULL,
definition_version INTEGER NOT NULL,
step_id VARCHAR(64) NOT NULL,
candidate VARCHAR(255) NOT NULL,
candidate_order INTEGER NOT NULL,
PRIMARY KEY (definition_id, definition_version, step_id, candidate),
CONSTRAINT fk_step_candidate_step FOREIGN KEY (definition_id, definition_version, step_id)
REFERENCES ordo_approval_step (definition_id, definition_version, step_id)
);
INSERT INTO ordo_step_candidate_v7 (definition_id, definition_version, step_id, candidate, candidate_order)
SELECT definition_id, 1, step_id, candidate, candidate_order FROM ordo_step_candidate;
DROP TABLE ordo_step_candidate;
ALTER TABLE ordo_step_candidate_v7 RENAME TO ordo_step_candidate;
CREATE TABLE ordo_step_transition_v7 (
definition_id VARCHAR(64) NOT NULL,
definition_version INTEGER NOT NULL,
from_step_id VARCHAR(64) NOT NULL,
to_step_id VARCHAR(64),
condition_key VARCHAR(255),
priority INTEGER NOT NULL,
PRIMARY KEY (definition_id, definition_version, from_step_id, priority),
CONSTRAINT fk_transition_from FOREIGN KEY (definition_id, definition_version, from_step_id)
REFERENCES ordo_approval_step (definition_id, definition_version, step_id),
CONSTRAINT fk_transition_to FOREIGN KEY (definition_id, definition_version, to_step_id)
REFERENCES ordo_approval_step (definition_id, definition_version, step_id)
);
INSERT INTO ordo_step_transition_v7 (definition_id, definition_version, from_step_id, to_step_id, condition_key, priority)
SELECT definition_id, 1, from_step_id, to_step_id, condition_key, priority FROM ordo_step_transition;
DROP TABLE ordo_step_transition;
ALTER TABLE ordo_step_transition_v7 RENAME TO ordo_step_transition;
ALTER TABLE ordo_process_instance ADD COLUMN definition_version INTEGER DEFAULT 1 NOT NULL;
ALTER TABLE ordo_process_instance ADD CONSTRAINT fk_process_instance_definition
FOREIGN KEY (definition_id, definition_version) REFERENCES ordo_process_definition (id, version);