diff --git a/pom.xml b/pom.xml
index bb567dc..73fcb3f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -103,14 +103,6 @@
postgresql
42.7.11
-
- org.springframework.boot
- spring-boot-starter-flyway
-
-
- org.flywaydb
- flyway-database-postgresql
-
org.springframework.boot
spring-boot-starter-web
diff --git a/run_service.sh b/run_service.sh
index 837536e..bfe9085 100755
--- a/run_service.sh
+++ b/run_service.sh
@@ -39,7 +39,6 @@ until docker exec "$POSTGRES_CONTAINER" pg_isready -U "$POSTGRES_USER" -d "$POST
sleep 1
done
-# Il database è usa-e-getta: crea lo schema dalle entity e non applica le
-# migration Flyway, che richiedono uno schema base già esistente.
+# Il database è usa-e-getta: crea lo schema direttamente dalle entity.
./mvnw spring-boot:run \
- -Dspring-boot.run.jvmArguments="-Dddl-auto=create-drop -Dspring.flyway.enabled=false"
+ -Dspring-boot.run.jvmArguments="-Dddl-auto=create-drop"
diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/bias/persistence/BiasImpactJobEntity.java b/src/main/java/it/cnr/isti/workflow/manager/executions/bias/persistence/BiasImpactJobEntity.java
index feffa55..27ea4b3 100644
--- a/src/main/java/it/cnr/isti/workflow/manager/executions/bias/persistence/BiasImpactJobEntity.java
+++ b/src/main/java/it/cnr/isti/workflow/manager/executions/bias/persistence/BiasImpactJobEntity.java
@@ -18,6 +18,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
+import org.hibernate.annotations.ColumnDefault;
@Entity
@Table(name = "bias_impact_job_entity", indexes = {
@@ -44,11 +45,13 @@ public class BiasImpactJobEntity {
private String stepId;
@Enumerated(EnumType.STRING)
- @Column(nullable = false)
- private BiasImpactJobKind kind;
+ @Column(nullable = false, length = 32)
+ @ColumnDefault("'ISOLATED_STEP'")
+ @Builder.Default
+ private BiasImpactJobKind kind = BiasImpactJobKind.ISOLATED_STEP;
@Enumerated(EnumType.STRING)
- @Column(nullable = false)
+ @Column(nullable = false, length = 32)
private BiasImpactJobStatus status;
@Column(name = "created_at", nullable = false)
diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/bias/persistence/BiasImpactReportEntity.java b/src/main/java/it/cnr/isti/workflow/manager/executions/bias/persistence/BiasImpactReportEntity.java
index 8c4f64e..3d14ab1 100644
--- a/src/main/java/it/cnr/isti/workflow/manager/executions/bias/persistence/BiasImpactReportEntity.java
+++ b/src/main/java/it/cnr/isti/workflow/manager/executions/bias/persistence/BiasImpactReportEntity.java
@@ -17,6 +17,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
+import org.hibernate.annotations.ColumnDefault;
@Entity
@Table(name = "bias_impact_report_entity", indexes = {
@@ -35,6 +36,7 @@ public class BiasImpactReportEntity {
private String id;
@NotBlank
+ @Column(nullable = false)
private String owner;
@NotBlank
@@ -48,8 +50,10 @@ public class BiasImpactReportEntity {
@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt;
+ @Builder.Default
@Column(name = "raw_outputs_included", nullable = false)
- private boolean rawOutputsIncluded;
+ @ColumnDefault("true")
+ private boolean rawOutputsIncluded = true;
@Column(name = "report_data", columnDefinition = "TEXT")
@Convert(converter = BiasImpactReportConverter.class)
@@ -66,5 +70,6 @@ public class BiasImpactReportEntity {
*/
@Version
@Column(name = "version", nullable = false)
+ @ColumnDefault("0")
private long version;
}
diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/repo/ExecutionEntity.java b/src/main/java/it/cnr/isti/workflow/manager/executions/repo/ExecutionEntity.java
index e2b51f2..7995880 100644
--- a/src/main/java/it/cnr/isti/workflow/manager/executions/repo/ExecutionEntity.java
+++ b/src/main/java/it/cnr/isti/workflow/manager/executions/repo/ExecutionEntity.java
@@ -11,14 +11,29 @@ import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.ForeignKey;
import jakarta.persistence.Id;
+import jakarta.persistence.Index;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
+import org.hibernate.annotations.ColumnDefault;
+import org.hibernate.annotations.OnDelete;
+import org.hibernate.annotations.OnDeleteAction;
@Entity
+@Table(indexes = {
+ @Index(name = "idx_execution_owner_kind", columnList = "owner, execution_kind"),
+ @Index(name = "idx_execution_parent", columnList = "parent_execution_id, parent_step_id, parent_iteration_index"),
+ @Index(name = "idx_execution_project_run", columnList = "owner, project_run_id"),
+ @Index(name = "idx_execution_owner_project", columnList = "owner, project_id")
+})
@Data
@Builder
@NoArgsConstructor
@@ -60,10 +75,22 @@ public class ExecutionEntity {
@Builder.Default
@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 32)
+ @ColumnDefault("'TOP_LEVEL'")
private ExecutionKind executionKind = ExecutionKind.TOP_LEVEL;
+ @Column(name = "parent_execution_id")
private String parentExecutionId;
+ /**
+ * Schema-only association for the self-referencing foreign key. The service continues to use
+ * {@link #parentExecutionId}, so this association is deliberately read-only.
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "parent_execution_id", insertable = false, updatable = false,
+ foreignKey = @ForeignKey(name = "fk_execution_parent"))
+ @OnDelete(action = OnDeleteAction.CASCADE)
+ private ExecutionEntity parentExecution;
+
private String parentStepId;
private Integer parentIterationIndex;
diff --git a/src/main/java/it/cnr/isti/workflow/manager/flows/repo/FlowEntity.java b/src/main/java/it/cnr/isti/workflow/manager/flows/repo/FlowEntity.java
index 5ae4597..6e4ae89 100644
--- a/src/main/java/it/cnr/isti/workflow/manager/flows/repo/FlowEntity.java
+++ b/src/main/java/it/cnr/isti/workflow/manager/flows/repo/FlowEntity.java
@@ -10,6 +10,8 @@ import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
+import jakarta.persistence.Index;
+import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
@@ -18,6 +20,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
+@Table(indexes = @Index(name = "idx_flow_owner_project", columnList = "owner, project_id"))
@NoArgsConstructor
@AllArgsConstructor
@Data
diff --git a/src/main/java/it/cnr/isti/workflow/manager/projects/repo/ProjectEntity.java b/src/main/java/it/cnr/isti/workflow/manager/projects/repo/ProjectEntity.java
index afbd0c8..b54c12c 100644
--- a/src/main/java/it/cnr/isti/workflow/manager/projects/repo/ProjectEntity.java
+++ b/src/main/java/it/cnr/isti/workflow/manager/projects/repo/ProjectEntity.java
@@ -10,6 +10,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
+import jakarta.persistence.Index;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import lombok.AllArgsConstructor;
@@ -19,7 +20,7 @@ import lombok.NoArgsConstructor;
@Entity
@Table(name = "project", uniqueConstraints = @UniqueConstraint(name = "uk_project_owner_name",
- columnNames = { "owner", "name" }))
+ columnNames = { "owner", "name" }), indexes = @Index(name = "idx_project_owner", columnList = "owner"))
@Data
@Builder
@NoArgsConstructor
diff --git a/src/main/java/it/cnr/isti/workflow/manager/vault/repo/UserSecretEntity.java b/src/main/java/it/cnr/isti/workflow/manager/vault/repo/UserSecretEntity.java
index 23cf8f6..b2634ab 100644
--- a/src/main/java/it/cnr/isti/workflow/manager/vault/repo/UserSecretEntity.java
+++ b/src/main/java/it/cnr/isti/workflow/manager/vault/repo/UserSecretEntity.java
@@ -7,16 +7,19 @@ import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
+import jakarta.persistence.Index;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
+import org.hibernate.annotations.ColumnDefault;
@Entity
@Table(name = "user_secret", uniqueConstraints = @UniqueConstraint(name = "uk_user_secret_owner_label",
- columnNames = { "owner", "label" }))
+ columnNames = { "owner", "label" }), indexes = @Index(name = "idx_user_secret_owner_provider_active",
+ columnList = "owner, provider, active"))
@Data
@Builder
@NoArgsConstructor
@@ -30,10 +33,10 @@ public class UserSecretEntity {
@Column(nullable = false)
private String owner;
- @Column(nullable = false)
+ @Column(nullable = false, length = 120)
private String label;
- @Column(nullable = false)
+ @Column(nullable = false, length = 120)
private String provider;
@Column(length = 1000)
@@ -45,7 +48,7 @@ public class UserSecretEntity {
@Column(nullable = false)
private String iv;
- @Column(nullable = false)
+ @Column(nullable = false, length = 64)
private String algorithm;
@Column(nullable = false)
@@ -58,5 +61,6 @@ public class UserSecretEntity {
@Builder.Default
@Column(nullable = false)
+ @ColumnDefault("true")
private boolean active = true;
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 13879d3..6c8bb30 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -7,8 +7,6 @@ spring.datasource.password=${DB_PASSWORD:password}
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=${ddl-auto:validate}
-spring.flyway.baseline-on-migrate=${FLYWAY_BASELINE_ON_MIGRATE:true}
-spring.flyway.baseline-version=${FLYWAY_BASELINE_VERSION:1}
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always
diff --git a/src/main/resources/db/migration/V10__add_bias_report_version.sql b/src/main/resources/db/migration/V10__add_bias_report_version.sql
deleted file mode 100644
index 5d627a7..0000000
--- a/src/main/resources/db/migration/V10__add_bias_report_version.sql
+++ /dev/null
@@ -1,6 +0,0 @@
--- Appending an LLM assessment to a report is a read-modify-write over its JSON, and the model calls
--- in the middle take minutes. Two overlapping assessments both read a report with no history, and
--- whichever saved last kept only its own: the other was silently gone. A version column is what lets
--- the second writer be refused and retried against the row as it actually is.
-ALTER TABLE bias_impact_report_entity
- ADD COLUMN version BIGINT NOT NULL DEFAULT 0;
diff --git a/src/main/resources/db/migration/V2__create_bias_impact_reports.sql b/src/main/resources/db/migration/V2__create_bias_impact_reports.sql
deleted file mode 100644
index 01e6a51..0000000
--- a/src/main/resources/db/migration/V2__create_bias_impact_reports.sql
+++ /dev/null
@@ -1,14 +0,0 @@
-CREATE TABLE bias_impact_report_entity (
- id VARCHAR(255) PRIMARY KEY,
- owner VARCHAR(255) NOT NULL,
- baseline_execution_id VARCHAR(255) NOT NULL,
- biased_execution_id VARCHAR(255),
- created_at TIMESTAMP(6) NOT NULL,
- report_data TEXT
-);
-
-CREATE INDEX idx_bias_report_baseline_owner_created
- ON bias_impact_report_entity (baseline_execution_id, owner, created_at);
-
-CREATE INDEX idx_bias_report_biased_execution
- ON bias_impact_report_entity (biased_execution_id);
diff --git a/src/main/resources/db/migration/V3__harden_bias_experiments.sql b/src/main/resources/db/migration/V3__harden_bias_experiments.sql
deleted file mode 100644
index 0ef98fd..0000000
--- a/src/main/resources/db/migration/V3__harden_bias_experiments.sql
+++ /dev/null
@@ -1,43 +0,0 @@
-ALTER TABLE bias_impact_report_entity
- ADD COLUMN raw_outputs_included BOOLEAN NOT NULL DEFAULT TRUE;
-
-DELETE FROM bias_impact_report_entity
-WHERE biased_execution_id IS NOT NULL
- AND id IN (
- SELECT id
- FROM (
- SELECT id,
- ROW_NUMBER() OVER (
- PARTITION BY baseline_execution_id, biased_execution_id, owner, raw_outputs_included
- ORDER BY created_at DESC, id DESC
- ) AS duplicate_number
- FROM bias_impact_report_entity
- WHERE biased_execution_id IS NOT NULL
- ) ranked_reports
- WHERE duplicate_number > 1
- );
-
-ALTER TABLE bias_impact_report_entity
- ADD CONSTRAINT uk_bias_full_comparison
- UNIQUE (baseline_execution_id, biased_execution_id, owner, raw_outputs_included);
-
-CREATE TABLE bias_impact_job_entity (
- id VARCHAR(255) PRIMARY KEY,
- owner VARCHAR(255) NOT NULL,
- execution_id VARCHAR(255) NOT NULL,
- step_id VARCHAR(255) NOT NULL,
- status VARCHAR(32) NOT NULL,
- created_at TIMESTAMP(6) NOT NULL,
- started_at TIMESTAMP(6),
- completed_at TIMESTAMP(6),
- report_id VARCHAR(255),
- error_code VARCHAR(255),
- error_message TEXT,
- request_data TEXT NOT NULL
-);
-
-CREATE INDEX idx_bias_job_owner_created
- ON bias_impact_job_entity (owner, created_at);
-
-CREATE INDEX idx_bias_job_status
- ON bias_impact_job_entity (status);
diff --git a/src/main/resources/db/migration/V4__add_subflow_execution_relationship.sql b/src/main/resources/db/migration/V4__add_subflow_execution_relationship.sql
deleted file mode 100644
index f5c103a..0000000
--- a/src/main/resources/db/migration/V4__add_subflow_execution_relationship.sql
+++ /dev/null
@@ -1,26 +0,0 @@
-ALTER TABLE execution_entity
- ADD COLUMN execution_kind VARCHAR(32) NOT NULL DEFAULT 'TOP_LEVEL';
-
-ALTER TABLE execution_entity
- ADD COLUMN parent_execution_id VARCHAR(255);
-
-ALTER TABLE execution_entity
- ADD COLUMN parent_step_id VARCHAR(255);
-
-ALTER TABLE execution_entity
- ADD COLUMN parent_iteration_index INTEGER;
-
-ALTER TABLE execution_entity
- ADD COLUMN subflow_role VARCHAR(32);
-
-ALTER TABLE execution_entity
- ADD CONSTRAINT fk_execution_parent
- FOREIGN KEY (parent_execution_id)
- REFERENCES execution_entity (id)
- ON DELETE CASCADE;
-
-CREATE INDEX idx_execution_owner_kind
- ON execution_entity (owner, execution_kind);
-
-CREATE INDEX idx_execution_parent
- ON execution_entity (parent_execution_id, parent_step_id, parent_iteration_index);
diff --git a/src/main/resources/db/migration/V5__create_user_secret_vault.sql b/src/main/resources/db/migration/V5__create_user_secret_vault.sql
deleted file mode 100644
index 7404415..0000000
--- a/src/main/resources/db/migration/V5__create_user_secret_vault.sql
+++ /dev/null
@@ -1,18 +0,0 @@
-CREATE TABLE user_secret (
- id VARCHAR(255) PRIMARY KEY,
- owner VARCHAR(255) NOT NULL,
- label VARCHAR(120) NOT NULL,
- provider VARCHAR(120) NOT NULL,
- description VARCHAR(1000),
- ciphertext TEXT NOT NULL,
- iv VARCHAR(255) NOT NULL,
- algorithm VARCHAR(64) NOT NULL,
- created_at TIMESTAMP(6) NOT NULL,
- updated_at TIMESTAMP(6) NOT NULL,
- last_used_at TIMESTAMP(6),
- active BOOLEAN NOT NULL DEFAULT TRUE,
- CONSTRAINT uk_user_secret_owner_label UNIQUE (owner, label)
-);
-
-CREATE INDEX idx_user_secret_owner_provider_active
- ON user_secret (owner, provider, active);
diff --git a/src/main/resources/db/migration/V6__create_project.sql b/src/main/resources/db/migration/V6__create_project.sql
deleted file mode 100644
index 8d1e2a8..0000000
--- a/src/main/resources/db/migration/V6__create_project.sql
+++ /dev/null
@@ -1,25 +0,0 @@
-CREATE TABLE project (
- id VARCHAR(255) PRIMARY KEY,
- name VARCHAR(255) NOT NULL,
- description VARCHAR(1000),
- owner VARCHAR(255) NOT NULL,
- created_at TIMESTAMP(6) NOT NULL,
- last_update_at TIMESTAMP(6) NOT NULL,
- shared_context TEXT,
- CONSTRAINT uk_project_owner_name UNIQUE (owner, name)
-);
-
-CREATE INDEX idx_project_owner ON project (owner);
-
--- No foreign key on project_id, on purpose. The tests run on H2 with ddl-auto=create-drop and
--- spring.flyway.enabled=false, and Hibernate's validate does not check foreign keys, so a
--- constraint added here would exist only in production and never be exercised anywhere. The
--- cascade is enforced in ProjectService, which deletes a project's flows before the project, and a
--- dangling project_id degrades safely: the project name simply resolves to null.
-
--- project_order is unused for now: it is the stable display order a project run will need, and a
--- nullable column costs nothing today while sparing an ALTER on flow_entity later.
-ALTER TABLE flow_entity ADD COLUMN project_id VARCHAR(255);
-ALTER TABLE flow_entity ADD COLUMN project_order INTEGER;
-
-CREATE INDEX idx_flow_owner_project ON flow_entity (owner, project_id);
diff --git a/src/main/resources/db/migration/V7__add_project_execution_columns.sql b/src/main/resources/db/migration/V7__add_project_execution_columns.sql
deleted file mode 100644
index cad3dcc..0000000
--- a/src/main/resources/db/migration/V7__add_project_execution_columns.sql
+++ /dev/null
@@ -1,11 +0,0 @@
--- Executions carry the project their source flow belonged to, plus the run that started them.
---
--- project_run_id is a new column rather than a reuse of run_group_id: that one means "the rerun
--- history of one flow" (resolveHistoryGroupId keys off source_flow_id first, and run numbering
--- depends on it), so overloading it would both fail to group a project run and corrupt per-flow
--- rerun numbering.
-ALTER TABLE execution_entity ADD COLUMN project_id VARCHAR(255);
-ALTER TABLE execution_entity ADD COLUMN project_run_id VARCHAR(255);
-
-CREATE INDEX idx_execution_project_run ON execution_entity (owner, project_run_id);
-CREATE INDEX idx_execution_owner_project ON execution_entity (owner, project_id);
diff --git a/src/main/resources/db/migration/V8__add_project_run_order.sql b/src/main/resources/db/migration/V8__add_project_run_order.sql
deleted file mode 100644
index 8bdf2ac..0000000
--- a/src/main/resources/db/migration/V8__add_project_run_order.sql
+++ /dev/null
@@ -1,4 +0,0 @@
--- Position of an execution inside its project run. A project run starts one flow at a time in this
--- order, so the sequence has to survive a restart: it is the run's only state, deliberately, rather
--- than a separate run table with a status machine to keep in sync with the executions themselves.
-ALTER TABLE execution_entity ADD COLUMN project_run_order INTEGER;
diff --git a/src/main/resources/db/migration/V9__add_bias_judge_job.sql b/src/main/resources/db/migration/V9__add_bias_judge_job.sql
deleted file mode 100644
index 4280748..0000000
--- a/src/main/resources/db/migration/V9__add_bias_judge_job.sql
+++ /dev/null
@@ -1,18 +0,0 @@
--- A bias job used to be one thing: re-run a step with the intervention active. Asking a model to
--- assess an existing comparison is the second, and it reuses the same queue, recovery and polling
--- rather than growing a parallel one - so the columns that only the first kind fills stop being
--- mandatory.
-ALTER TABLE bias_impact_job_entity
- ADD COLUMN kind VARCHAR(32) NOT NULL DEFAULT 'ISOLATED_STEP';
-
-ALTER TABLE bias_impact_job_entity
- ALTER COLUMN step_id DROP NOT NULL;
-
-ALTER TABLE bias_impact_job_entity
- ALTER COLUMN request_data DROP NOT NULL;
-
-ALTER TABLE bias_impact_job_entity
- ADD COLUMN report_target_id VARCHAR(255);
-
-ALTER TABLE bias_impact_job_entity
- ADD COLUMN judge_data TEXT;
diff --git a/src/test/resources/test.properties b/src/test/resources/test.properties
index 9d90c94..bc8adb9 100644
--- a/src/test/resources/test.properties
+++ b/src/test/resources/test.properties
@@ -5,7 +5,6 @@ spring.datasource.password=
# Use the native H2 dialect for schema generation while keeping MySQL compatibility mode.
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
-spring.flyway.enabled=false
spring.jpa.properties.jakarta.persistence.validation.mode=none
app.db.init.enabled=true
app.assistant.default-model=assistant-test-model