From c125b7377a2642c8a300a6cd15ff2d365c14f47e Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Wed, 12 May 2021 13:15:14 +0200 Subject: [PATCH 01/43] Fix race condition in reimporting trigger scanner This should improve TestStrings stability. --- .../com/evolveum/midpoint/common/Clock.java | 26 +++++++++---------- .../trigger/TriggerScannerItemProcessor.java | 4 +-- .../TriggerScannerTaskPartExecution.java | 6 ++++- .../test/AbstractModelIntegrationTest.java | 19 ++++++-------- .../wf/impl/other/TestEscalation.java | 2 +- .../task/quartzimpl/run/JobExecutor.java | 2 +- .../midpoint/testing/story/TestStrings.java | 4 +-- 7 files changed, 31 insertions(+), 32 deletions(-) diff --git a/infra/common/src/main/java/com/evolveum/midpoint/common/Clock.java b/infra/common/src/main/java/com/evolveum/midpoint/common/Clock.java index 58897654777..efaa85954b1 100644 --- a/infra/common/src/main/java/com/evolveum/midpoint/common/Clock.java +++ b/infra/common/src/main/java/com/evolveum/midpoint/common/Clock.java @@ -27,8 +27,8 @@ public class Clock { private static final Trace LOGGER = TraceManager.getTrace(Clock.class); - private Long override = null; - private Long overrideOffset = null; + volatile private Long override = null; + volatile private Long overrideOffset = null; public long currentTimeMillis() { long time; @@ -66,11 +66,10 @@ public boolean isFuture(XMLGregorianCalendar date) { } public void override(long overrideTimestamp) { - LOGGER.info("Clock override: {}", override); + Long originalOverride = this.override; this.override = overrideTimestamp; - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Clock current time: {}", currentTimeXMLGregorianCalendar()); - } + LOGGER.info("Clock override changed from {} to {}", originalOverride, this.override); + LOGGER.debug("Clock current time: {}", currentTimeXMLGregorianCalendar()); } public void override(XMLGregorianCalendar overrideTimestamp) { @@ -99,11 +98,13 @@ public void overrideDuration(Duration duration) { * Extends offset on top of existing offset. */ public void overrideDuration(Long offsetMillis) { - if (overrideOffset == null) { - overrideOffset = offsetMillis; + Long originalOverrideOffset = this.overrideOffset; + if (originalOverrideOffset == null) { + this.overrideOffset = offsetMillis; } else { - overrideOffset = overrideOffset + offsetMillis; + this.overrideOffset = originalOverrideOffset + offsetMillis; } + LOGGER.info("Clock override offset changed from {} to {}", originalOverrideOffset, this.overrideOffset); } public void overrideOffset(Long offsetMillis) { @@ -111,12 +112,9 @@ public void overrideOffset(Long offsetMillis) { } public void resetOverride() { - LOGGER.info("Clock override reset"); this.override = null; this.overrideOffset = null; - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Clock current time: {}", currentTimeXMLGregorianCalendar()); - } + LOGGER.info("Clock override and override offset were reset"); + LOGGER.debug("Clock current time: {}", currentTimeXMLGregorianCalendar()); } - } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/trigger/TriggerScannerItemProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/trigger/TriggerScannerItemProcessor.java index 758e9ae1ce4..36bb248adf8 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/trigger/TriggerScannerItemProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/trigger/TriggerScannerItemProcessor.java @@ -91,7 +91,7 @@ private void fireTriggers(PrismObject object, RunningTask workerTask continue; } if (!isHot(timestamp)) { - logger.trace("Trigger {} is not hot (timestamp={}, thisScanTimestamp={}, lastScanTimestamp={}) - skipping also the triggers after that", + logger.debug("Trigger {} is not hot (timestamp={}, thisScanTimestamp={}, lastScanTimestamp={}) - skipping also the triggers after that", trigger, timestamp, taskExecution.getThisScanTimestamp(), taskExecution.getLastScanTimestamp()); return; } @@ -109,7 +109,7 @@ private void fireTriggers(PrismObject object, RunningTask workerTask // during single task handler run.) continue; } - logger.debug("Going to fire trigger {} in {}: id={}", handlerUri, object, trigger.getId()); + logger.debug("Going to fire trigger {} in {}: id={}, ts={}", handlerUri, object, trigger.getId(), timestamp); TriggerHandler handler = taskHandler.getTriggerHandler(handlerUri); if (handler == null) { logger.warn("No registered trigger handler for URI {} in {}", handlerUri, trigger); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/trigger/TriggerScannerTaskPartExecution.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/trigger/TriggerScannerTaskPartExecution.java index 1762fa1f3fb..88e096d4835 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/trigger/TriggerScannerTaskPartExecution.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/trigger/TriggerScannerTaskPartExecution.java @@ -17,6 +17,8 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import javax.xml.datatype.XMLGregorianCalendar; + /** * Single execution of a trigger scanner task part. */ @@ -36,8 +38,10 @@ public TriggerScannerTaskPartExecution(TriggerScannerTaskHandler.TaskExecution t @Override protected ObjectQuery createQuery(OperationResult opResult) { + XMLGregorianCalendar timestamp = taskExecution.getThisScanTimestamp(); + logger.debug("Looking for triggers with timestamps up to {}", timestamp); return getPrismContext().queryFor(ObjectType.class) - .item(F_TRIGGER, F_TIMESTAMP).le(taskExecution.getThisScanTimestamp()) + .item(F_TRIGGER, F_TIMESTAMP).le(timestamp) .build(); } } diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index ce9ba82495a..0cb22468c37 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -5633,18 +5633,15 @@ protected List filter(List records, AuditEve } // Use this when you want to start the task manually. - protected void clearTaskSchedule(String taskOid, File taskFile, OperationResult result) - throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException, FileNotFoundException { + protected void reimportWithNoSchedule(String taskOid, File taskFile, Task opTask, OperationResult result) + throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException, IOException, + ExpressionEvaluationException, CommunicationException, SecurityViolationException, ConfigurationException, + PolicyViolationException { taskManager.suspendAndDeleteTasks(Collections.singletonList(taskOid), 60000L, true, result); - importObjectFromFile(taskFile, result); - taskManager.suspendTasks(Collections.singletonList(taskOid), 60000L, result); - modifySystemObjectInRepo(TaskType.class, taskOid, - prismContext.deltaFor(TaskType.class) - .item(TaskType.F_SCHEDULE).replace() - .item(TaskType.F_BINDING).replace(TaskBindingType.LOOSE) // tightly-bound tasks must have interval set - .asItemDeltas(), - result); - taskManager.resumeTasks(singleton(taskOid), result); + addObject(taskFile, opTask, result, taskObject -> + ((TaskType) taskObject.asObjectable()) + .schedule(null) + .binding(TaskBindingType.LOOSE)); // tightly-bound tasks must have interval set } protected void repoAddObjects(List objects, OperationResult result) diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/other/TestEscalation.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/other/TestEscalation.java index a6cbcf297d2..0e29dee5d1b 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/other/TestEscalation.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/other/TestEscalation.java @@ -187,7 +187,7 @@ public void test200CreateE2ApprovalCase() throws Exception { OperationResult result = getTestOperationResult(); clock.resetOverride(); - clearTaskSchedule(TASK_TRIGGER_SCANNER_OID, TASK_TRIGGER_SCANNER_FILE, result); + reimportWithNoSchedule(TASK_TRIGGER_SCANNER_OID, TASK_TRIGGER_SCANNER_FILE, task, result); // WHEN assignRole(userJackOid, ROLE_E2_OID, task, result); // should start approval process diff --git a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/run/JobExecutor.java b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/run/JobExecutor.java index 0d869448a35..ae530cc1699 100644 --- a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/run/JobExecutor.java +++ b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/run/JobExecutor.java @@ -87,7 +87,7 @@ private void executeInternal(OperationResult result) stateCheck(beans != null, "Task manager beans are not correctly set"); String oid = context.getJobDetail().getKey().getName(); - LOGGER.trace("Starting execution of task {}", oid); + LOGGER.debug("Starting execution of task {}", oid); fetchTheTask(oid, result); diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestStrings.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestStrings.java index 92eb2558592..2defa4e5475 100644 --- a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestStrings.java +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestStrings.java @@ -135,7 +135,7 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti transplantGlobalPolicyRulesAdd(CONFIG_WITH_GLOBAL_RULES_FILE, initTask, initResult); // we prefer running trigger scanner by hand - clearTaskSchedule(TASK_TRIGGER_SCANNER_OID, TASK_TRIGGER_SCANNER_FILE, initResult); + reimportWithNoSchedule(TASK_TRIGGER_SCANNER_OID, TASK_TRIGGER_SCANNER_FILE, initTask, initResult); // and we don't need validity scanner taskManager.suspendAndDeleteTasks(Collections.singletonList(TASK_VALIDITY_SCANNER_OID), 60000L, true, initResult); @@ -843,7 +843,7 @@ public void test208SixDaysLater() throws Exception { // GIVEN clock.resetOverride(); - clearTaskSchedule(TASK_TRIGGER_SCANNER_OID, TASK_TRIGGER_SCANNER_FILE, result); + reimportWithNoSchedule(TASK_TRIGGER_SCANNER_OID, TASK_TRIGGER_SCANNER_FILE, task, result); clock.overrideDuration("P6D"); // WHEN From 70633f0a799c062a039614a86aefe7b3c7f9f6a6 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 12 May 2021 17:57:00 +0200 Subject: [PATCH 02/43] repo-sqale SQL schema: move of m_case+cert tables, org-closure update --- repo/repo-sqale/sql/pgnew-repo.sql | 428 ++++++++++++++++------------- 1 file changed, 235 insertions(+), 193 deletions(-) diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index cb737e8352e..65ee724f40a 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -337,20 +337,6 @@ CREATE TABLE m_ref_object_modify_approver ( CREATE INDEX m_ref_object_modify_approver_targetOid_relation_id_idx ON m_ref_object_modify_approver (targetOid, relation_id); --- stores ObjectType/parentOrgRef -CREATE TABLE m_ref_object_parent_org ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - referenceType ReferenceType GENERATED ALWAYS AS ('OBJECT_PARENT_ORG') STORED, - - -- TODO wouldn't (owner_oid, targetOid, relation_id) perform better for typical queries? - PRIMARY KEY (owner_oid, relation_id, targetOid) -) - INHERITS (m_reference); - --- TODO is this enough? Is target+owner+relation needed too? -CREATE INDEX m_ref_object_parent_org_targetOid_relation_id_idx - ON m_ref_object_parent_org (targetOid, relation_id); - -- stores AssignmentHolderType/roleMembershipRef CREATE TABLE m_ref_role_membership ( owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, @@ -596,168 +582,71 @@ CREATE INDEX m_org_name_orig_idx ON m_org (name_orig); ALTER TABLE m_org ADD CONSTRAINT m_org_name_norm_key UNIQUE (name_norm); CREATE INDEX m_org_displayOrder_idx ON m_org (displayOrder); -/* -CREATE TABLE m_org_closure ( - ancestor_oid UUID NOT NULL, - descendant_oid UUID NOT NULL, - val INTEGER, -- number of distinct paths - PRIMARY KEY (ancestor_oid, descendant_oid) -); - -CREATE INDEX iDescendantAncestor ON m_org_closure (descendant_oid, ancestor_oid); -ALTER TABLE m_org_closure - ADD CONSTRAINT fk_ancestor FOREIGN KEY (ancestor_oid) REFERENCES m_object; -ALTER TABLE m_org_closure - ADD CONSTRAINT fk_descendant FOREIGN KEY (descendant_oid) REFERENCES m_object; -*/ --- endregion - --- region Access Certification object tables --- Represents AccessCertificationDefinitionType, see https://wiki.evolveum.com/display/midPoint/Access+Certification -CREATE TABLE m_access_cert_definition ( - oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_DEFINITION') STORED, - handlerUri_id INTEGER REFERENCES m_uri(id), - lastCampaignStartedTimestamp TIMESTAMPTZ, - lastCampaignClosedTimestamp TIMESTAMPTZ, - ownerRef_targetOid UUID, - ownerRef_targetType ObjectType, - ownerRef_relation_id INTEGER REFERENCES m_uri(id) -) - INHERITS (m_object); - -CREATE TRIGGER m_access_cert_definition_oid_insert_tr BEFORE INSERT ON m_access_cert_definition - FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); -CREATE TRIGGER m_access_cert_definition_update_tr BEFORE UPDATE ON m_access_cert_definition - FOR EACH ROW EXECUTE PROCEDURE before_update_object(); -CREATE TRIGGER m_access_cert_definition_oid_delete_tr AFTER DELETE ON m_access_cert_definition - FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); - -CREATE INDEX m_access_cert_definition_name_orig_idx ON m_access_cert_definition (name_orig); -ALTER TABLE m_access_cert_definition - ADD CONSTRAINT m_access_cert_definition_name_norm_key UNIQUE (name_norm); -CREATE INDEX m_access_cert_definition_ext_idx ON m_access_cert_definition USING gin (ext); - --- TODO not mapped yet -CREATE TABLE m_access_cert_campaign ( - oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_CAMPAIGN') STORED, - definitionRef_targetOid UUID, - definitionRef_targetType ObjectType, - definitionRef_relation_id INTEGER REFERENCES m_uri(id), - endTimestamp TIMESTAMPTZ, - handlerUri_id INTEGER REFERENCES m_uri(id), - iteration INTEGER NOT NULL, - ownerRef_targetOid UUID, - ownerRef_targetType ObjectType, - ownerRef_relation_id INTEGER REFERENCES m_uri(id), - stageNumber INTEGER, - startTimestamp TIMESTAMPTZ, - state INTEGER -) - INHERITS (m_object); - -CREATE TRIGGER m_access_cert_campaign_oid_insert_tr BEFORE INSERT ON m_access_cert_campaign - FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); -CREATE TRIGGER m_access_cert_campaign_update_tr BEFORE UPDATE ON m_access_cert_campaign - FOR EACH ROW EXECUTE PROCEDURE before_update_object(); -CREATE TRIGGER m_access_cert_campaign_oid_delete_tr AFTER DELETE ON m_access_cert_campaign - FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); - -CREATE INDEX m_access_cert_campaign_name_orig_idx ON m_access_cert_campaign (name_orig); -ALTER TABLE m_access_cert_campaign - ADD CONSTRAINT m_access_cert_campaign_name_norm_key UNIQUE (name_norm); -CREATE INDEX m_access_cert_campaign_ext_idx ON m_access_cert_campaign USING gin (ext); - -CREATE TABLE m_access_cert_case ( +-- stores ObjectType/parentOrgRef +CREATE TABLE m_ref_object_parent_org ( owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_CASE') STORED, - administrativeStatus INTEGER, - archiveTimestamp TIMESTAMPTZ, - disableReason TEXT, - disableTimestamp TIMESTAMPTZ, - effectiveStatus INTEGER, - enableTimestamp TIMESTAMPTZ, - validFrom TIMESTAMPTZ, - validTo TIMESTAMPTZ, - validityChangeTimestamp TIMESTAMPTZ, - validityStatus INTEGER, - currentStageOutcome TEXT, - fullObject BYTEA, - iteration INTEGER NOT NULL, - objectRef_targetOid UUID, - objectRef_targetType ObjectType, - objectRef_relation_id INTEGER REFERENCES m_uri(id), - orgRef_targetOid UUID, - orgRef_targetType ObjectType, - orgRef_relation_id INTEGER REFERENCES m_uri(id), - outcome TEXT, - remediedTimestamp TIMESTAMPTZ, - reviewDeadline TIMESTAMPTZ, - reviewRequestedTimestamp TIMESTAMPTZ, - stageNumber INTEGER, - targetRef_targetOid UUID, - targetRef_targetType ObjectType, - targetRef_relation_id INTEGER REFERENCES m_uri(id), - tenantRef_targetOid UUID, - tenantRef_targetType ObjectType, - tenantRef_relation_id INTEGER REFERENCES m_uri(id), + referenceType ReferenceType GENERATED ALWAYS AS ('OBJECT_PARENT_ORG') STORED, - PRIMARY KEY (owner_oid, cid) + -- TODO wouldn't (owner_oid, targetOid, relation_id) perform better for typical queries? + PRIMARY KEY (owner_oid, relation_id, targetOid) ) - INHERITS(m_container); + INHERITS (m_reference); -CREATE TABLE m_access_cert_wi ( - owner_oid UUID NOT NULL, -- PK+FK - acc_cert_case_cid INTEGER NOT NULL, -- PK+FK - containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_WORK_ITEM') STORED, - closeTimestamp TIMESTAMPTZ, - iteration INTEGER NOT NULL, - outcome TEXT, - outputChangeTimestamp TIMESTAMPTZ, - performerRef_targetOid UUID, - performerRef_targetType ObjectType, - performerRef_relation_id INTEGER REFERENCES m_uri(id), - stageNumber INTEGER, +-- TODO is this enough? Is target+owner+relation needed too? +CREATE INDEX m_ref_object_parent_org_targetOid_relation_id_idx + ON m_ref_object_parent_org (targetOid, relation_id); - PRIMARY KEY (owner_oid, acc_cert_case_cid, cid) +-- region org-closure +-- Closure is handled by two views - one materialized (m_org_closure_internal) and one with return +-- rule (m_org_closure). Only the second one is used from the outside. +-- Trigger on m_ref_object_parent_org creates flag that the materialized view must be refreshed +-- (line in +CREATE MATERIALIZED VIEW m_org_closure AS +WITH RECURSIVE org_h ( + ancestor_oid, -- ref.targetoid + descendant_oid --ref.owner_oid + -- paths -- number of different paths, not used for materialized view version + -- TODO depth? if so, cycles must be checked in recursive term +) AS ( + -- gather all organizations with parents + SELECT r.targetoid, r.owner_oid + FROM m_ref_object_parent_org r + WHERE r.owner_type = 'ORG' + UNION + -- generate their parents + SELECT par.targetoid, chi.descendant_oid -- leaving original child there generates closure + FROM m_ref_object_parent_org as par, org_h as chi + WHERE par.owner_oid = chi.ancestor_oid ) - INHERITS(m_container); +SELECT * FROM org_h; -ALTER TABLE m_access_cert_wi - ADD CONSTRAINT m_access_cert_wi_id_fk FOREIGN KEY (owner_oid, acc_cert_case_cid) - REFERENCES m_access_cert_case (owner_oid, cid) - ON DELETE CASCADE; +-- unique index is like PK if it was table +CREATE UNIQUE INDEX m_org_closure_asc_desc_idx + ON m_org_closure (ancestor_oid, descendant_oid); +CREATE INDEX m_org_closure_desc_asc_idx + ON m_org_closure (descendant_oid, ancestor_oid); --- TODO rework to inherit from reference tables -CREATE TABLE m_access_cert_wi_reference ( - owner_oid UUID NOT NULL, -- PK+FK - acc_cert_case_cid INTEGER NOT NULL, -- PK+FK - acc_cert_wi_cid INTEGER NOT NULL, -- PK+FK - targetOid UUID NOT NULL, -- more PK columns... - targetType ObjectType, - relation_id INTEGER NOT NULL REFERENCES m_uri(id), +-- The trigger for m_ref_object_parent_org that flags the view for refresh. +CREATE OR REPLACE FUNCTION m_org_closure_refresh() + RETURNS trigger + LANGUAGE plpgsql +AS $$ +BEGIN + IF TG_OP = 'TRUNCATE' OR OLD.owner_type = 'ORG' OR NEW.owner_type = 'ORG' THEN + REFRESH MATERIALIZED VIEW m_org_closure_internal; + END IF; - -- TODO is the order of last two components optimal for index/query? - PRIMARY KEY (owner_oid, acc_cert_case_cid, acc_cert_wi_cid, relation_id, targetOid) -); + -- after trigger returns null + RETURN NULL; +END $$; -ALTER TABLE m_access_cert_wi_reference - ADD CONSTRAINT m_access_cert_wi_reference_id_fk - FOREIGN KEY (owner_oid, acc_cert_case_cid, acc_cert_wi_cid) - REFERENCES m_access_cert_wi (owner_oid, acc_cert_case_cid, cid) - ON DELETE CASCADE; -/* -CREATE INDEX iCertCampaignNameOrig ON m_access_cert_campaign (name_orig); -ALTER TABLE m_access_cert_campaign ADD CONSTRAINT uc_access_cert_campaign_name UNIQUE (name_norm); -CREATE INDEX iCaseObjectRefTargetOid ON m_access_cert_case (objectRef_targetOid); -CREATE INDEX iCaseTargetRefTargetOid ON m_access_cert_case (targetRef_targetOid); -CREATE INDEX iCaseTenantRefTargetOid ON m_access_cert_case (tenantRef_targetOid); -CREATE INDEX iCaseOrgRefTargetOid ON m_access_cert_case (orgRef_targetOid); -CREATE INDEX iCertDefinitionNameOrig ON m_access_cert_definition (name_orig); -ALTER TABLE m_access_cert_definition ADD CONSTRAINT uc_access_cert_definition_name UNIQUE (name_norm); -CREATE INDEX iCertWorkItemRefTargetOid ON m_access_cert_wi_reference (targetOid); - */ +CREATE TRIGGER m_ref_object_parent_org_refresh_tr + AFTER INSERT OR UPDATE OR DELETE ON m_ref_object_parent_org + FOR EACH ROW EXECUTE PROCEDURE m_org_closure_refresh(); +CREATE TRIGGER m_ref_object_parent_org_trunc_refresh_tr + AFTER TRUNCATE ON m_ref_object_parent_org + FOR EACH STATEMENT EXECUTE PROCEDURE m_org_closure_refresh(); +-- endregion -- endregion -- region OTHER object tables @@ -1117,7 +1006,9 @@ CREATE INDEX m_task_parent_idx ON m_task (parent); CREATE INDEX m_task_objectRef_targetOid_idx ON m_task(objectRef_targetOid); ALTER TABLE m_task ADD CONSTRAINT m_task_taskIdentifier_key UNIQUE (taskIdentifier); CREATE INDEX m_task_dependentTaskIdentifiers_idx ON m_task USING GIN(dependentTaskIdentifiers); +-- endregion +-- region cases -- Represents CaseType, see https://wiki.evolveum.com/display/midPoint/Case+Management CREATE TABLE m_case ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), @@ -1155,13 +1046,191 @@ CREATE INDEX iCaseTypeTargetRefTargetOid ON m_case(targetRef_targetOid); CREATE INDEX iCaseTypeParentRefTargetOid ON m_case(parentRef_targetOid); CREATE INDEX iCaseTypeRequestorRefTargetOid ON m_case(requestorRef_targetOid); CREATE INDEX iCaseTypeCloseTimestamp ON m_case(closeTimestamp); + +CREATE TABLE m_case_wi ( + id INTEGER NOT NULL, + owner_oid UUID NOT NULL, + closeTimestamp TIMESTAMPTZ, + createTimestamp TIMESTAMPTZ, + deadline TIMESTAMPTZ, + originalAssigneeRef_relation VARCHAR(157), + originalAssigneeRef_targetOid UUID, + originalAssigneeRef_targetType INTEGER, + outcome TEXT, + performerRef_relation VARCHAR(157), + performerRef_targetOid UUID, + performerRef_targetType INTEGER, + stageNumber INTEGER, + PRIMARY KEY (owner_oid, id) +); + ALTER TABLE m_case_wi ADD CONSTRAINT fk_case_wi_owner FOREIGN KEY (owner_oid) REFERENCES m_case; + +CREATE TABLE m_case_wi_reference ( + owner_id INTEGER NOT NULL, + owner_owner_oid UUID NOT NULL, + reference_type INTEGER NOT NULL, + relation VARCHAR(157) NOT NULL, + targetOid UUID NOT NULL, + targetType INTEGER, + PRIMARY KEY (owner_owner_oid, owner_id, reference_type, targetOid, relation) +); + ALTER TABLE m_case_wi_reference ADD CONSTRAINT fk_case_wi_reference_owner FOREIGN KEY (owner_owner_oid, owner_id) REFERENCES m_case_wi; +CREATE INDEX iCaseWorkItemRefTargetOid ON m_case_wi_reference (targetOid); */ -- endregion +-- region Access Certification object tables +-- Represents AccessCertificationDefinitionType, see https://wiki.evolveum.com/display/midPoint/Access+Certification +CREATE TABLE m_access_cert_definition ( + oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), + objectType ObjectType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_DEFINITION') STORED, + handlerUri_id INTEGER REFERENCES m_uri(id), + lastCampaignStartedTimestamp TIMESTAMPTZ, + lastCampaignClosedTimestamp TIMESTAMPTZ, + ownerRef_targetOid UUID, + ownerRef_targetType ObjectType, + ownerRef_relation_id INTEGER REFERENCES m_uri(id) +) + INHERITS (m_object); + +CREATE TRIGGER m_access_cert_definition_oid_insert_tr BEFORE INSERT ON m_access_cert_definition + FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); +CREATE TRIGGER m_access_cert_definition_update_tr BEFORE UPDATE ON m_access_cert_definition + FOR EACH ROW EXECUTE PROCEDURE before_update_object(); +CREATE TRIGGER m_access_cert_definition_oid_delete_tr AFTER DELETE ON m_access_cert_definition + FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); + +CREATE INDEX m_access_cert_definition_name_orig_idx ON m_access_cert_definition (name_orig); +ALTER TABLE m_access_cert_definition + ADD CONSTRAINT m_access_cert_definition_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_access_cert_definition_ext_idx ON m_access_cert_definition USING gin (ext); + +-- TODO not mapped yet +CREATE TABLE m_access_cert_campaign ( + oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), + objectType ObjectType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_CAMPAIGN') STORED, + definitionRef_targetOid UUID, + definitionRef_targetType ObjectType, + definitionRef_relation_id INTEGER REFERENCES m_uri(id), + endTimestamp TIMESTAMPTZ, + handlerUri_id INTEGER REFERENCES m_uri(id), + iteration INTEGER NOT NULL, + ownerRef_targetOid UUID, + ownerRef_targetType ObjectType, + ownerRef_relation_id INTEGER REFERENCES m_uri(id), + stageNumber INTEGER, + startTimestamp TIMESTAMPTZ, + state INTEGER +) + INHERITS (m_object); + +CREATE TRIGGER m_access_cert_campaign_oid_insert_tr BEFORE INSERT ON m_access_cert_campaign + FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); +CREATE TRIGGER m_access_cert_campaign_update_tr BEFORE UPDATE ON m_access_cert_campaign + FOR EACH ROW EXECUTE PROCEDURE before_update_object(); +CREATE TRIGGER m_access_cert_campaign_oid_delete_tr AFTER DELETE ON m_access_cert_campaign + FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); + +CREATE INDEX m_access_cert_campaign_name_orig_idx ON m_access_cert_campaign (name_orig); +ALTER TABLE m_access_cert_campaign + ADD CONSTRAINT m_access_cert_campaign_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_access_cert_campaign_ext_idx ON m_access_cert_campaign USING gin (ext); + +CREATE TABLE m_access_cert_case ( + owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_CASE') STORED, + administrativeStatus INTEGER, + archiveTimestamp TIMESTAMPTZ, + disableReason TEXT, + disableTimestamp TIMESTAMPTZ, + effectiveStatus INTEGER, + enableTimestamp TIMESTAMPTZ, + validFrom TIMESTAMPTZ, + validTo TIMESTAMPTZ, + validityChangeTimestamp TIMESTAMPTZ, + validityStatus INTEGER, + currentStageOutcome TEXT, + fullObject BYTEA, + iteration INTEGER NOT NULL, + objectRef_targetOid UUID, + objectRef_targetType ObjectType, + objectRef_relation_id INTEGER REFERENCES m_uri(id), + orgRef_targetOid UUID, + orgRef_targetType ObjectType, + orgRef_relation_id INTEGER REFERENCES m_uri(id), + outcome TEXT, + remediedTimestamp TIMESTAMPTZ, + reviewDeadline TIMESTAMPTZ, + reviewRequestedTimestamp TIMESTAMPTZ, + stageNumber INTEGER, + targetRef_targetOid UUID, + targetRef_targetType ObjectType, + targetRef_relation_id INTEGER REFERENCES m_uri(id), + tenantRef_targetOid UUID, + tenantRef_targetType ObjectType, + tenantRef_relation_id INTEGER REFERENCES m_uri(id), + + PRIMARY KEY (owner_oid, cid) +) + INHERITS(m_container); + +CREATE TABLE m_access_cert_wi ( + owner_oid UUID NOT NULL, -- PK+FK + acc_cert_case_cid INTEGER NOT NULL, -- PK+FK + containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_WORK_ITEM') STORED, + closeTimestamp TIMESTAMPTZ, + iteration INTEGER NOT NULL, + outcome TEXT, + outputChangeTimestamp TIMESTAMPTZ, + performerRef_targetOid UUID, + performerRef_targetType ObjectType, + performerRef_relation_id INTEGER REFERENCES m_uri(id), + stageNumber INTEGER, + + PRIMARY KEY (owner_oid, acc_cert_case_cid, cid) +) + INHERITS(m_container); + +ALTER TABLE m_access_cert_wi + ADD CONSTRAINT m_access_cert_wi_id_fk FOREIGN KEY (owner_oid, acc_cert_case_cid) + REFERENCES m_access_cert_case (owner_oid, cid) + ON DELETE CASCADE; + +-- TODO rework to inherit from reference tables +CREATE TABLE m_access_cert_wi_reference ( + owner_oid UUID NOT NULL, -- PK+FK + acc_cert_case_cid INTEGER NOT NULL, -- PK+FK + acc_cert_wi_cid INTEGER NOT NULL, -- PK+FK + targetOid UUID NOT NULL, -- more PK columns... + targetType ObjectType, + relation_id INTEGER NOT NULL REFERENCES m_uri(id), + + -- TODO is the order of last two components optimal for index/query? + PRIMARY KEY (owner_oid, acc_cert_case_cid, acc_cert_wi_cid, relation_id, targetOid) +); + +ALTER TABLE m_access_cert_wi_reference + ADD CONSTRAINT m_access_cert_wi_reference_id_fk + FOREIGN KEY (owner_oid, acc_cert_case_cid, acc_cert_wi_cid) + REFERENCES m_access_cert_wi (owner_oid, acc_cert_case_cid, cid) + ON DELETE CASCADE; +/* +CREATE INDEX iCertCampaignNameOrig ON m_access_cert_campaign (name_orig); +ALTER TABLE m_access_cert_campaign ADD CONSTRAINT uc_access_cert_campaign_name UNIQUE (name_norm); +CREATE INDEX iCaseObjectRefTargetOid ON m_access_cert_case (objectRef_targetOid); +CREATE INDEX iCaseTargetRefTargetOid ON m_access_cert_case (targetRef_targetOid); +CREATE INDEX iCaseTenantRefTargetOid ON m_access_cert_case (tenantRef_targetOid); +CREATE INDEX iCaseOrgRefTargetOid ON m_access_cert_case (orgRef_targetOid); +CREATE INDEX iCertDefinitionNameOrig ON m_access_cert_definition (name_orig); +ALTER TABLE m_access_cert_definition ADD CONSTRAINT uc_access_cert_definition_name UNIQUE (name_norm); +CREATE INDEX iCertWorkItemRefTargetOid ON m_access_cert_wi_reference (targetOid); + */ +-- endregion + -- region ObjectTemplateType CREATE TABLE m_object_template ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), @@ -1578,31 +1647,6 @@ CREATE TABLE m_audit_resource ( record_id BIGINT NOT NULL, PRIMARY KEY (record_id, resourceOid) ); -CREATE TABLE m_case_wi ( - id INTEGER NOT NULL, - owner_oid UUID NOT NULL, - closeTimestamp TIMESTAMPTZ, - createTimestamp TIMESTAMPTZ, - deadline TIMESTAMPTZ, - originalAssigneeRef_relation VARCHAR(157), - originalAssigneeRef_targetOid UUID, - originalAssigneeRef_targetType INTEGER, - outcome TEXT, - performerRef_relation VARCHAR(157), - performerRef_targetOid UUID, - performerRef_targetType INTEGER, - stageNumber INTEGER, - PRIMARY KEY (owner_oid, id) -); -CREATE TABLE m_case_wi_reference ( - owner_id INTEGER NOT NULL, - owner_owner_oid UUID NOT NULL, - reference_type INTEGER NOT NULL, - relation VARCHAR(157) NOT NULL, - targetOid UUID NOT NULL, - targetType INTEGER, - PRIMARY KEY (owner_owner_oid, owner_id, reference_type, targetOid, relation) -); CREATE TABLE m_ext_item ( id SERIAL NOT NULL, kind INTEGER, @@ -1688,8 +1732,6 @@ CREATE INDEX iAuditResourceOid ON m_audit_resource (resourceOid); CREATE INDEX iAuditResourceOidRecordId ON m_audit_resource (record_id); -CREATE INDEX iCaseWorkItemRefTargetOid - ON m_case_wi_reference (targetOid); ALTER TABLE m_ext_item ADD CONSTRAINT iExtItemDefinition UNIQUE (itemName, itemType, kind); From 14e1a2b3216f65efa1569381abb1b60b62149107 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 12 May 2021 23:14:55 +0200 Subject: [PATCH 03/43] repo-sqale: used for row type parameter instead of This is consistent with most of the other code like mapping, but was forgotten in FlexibleRelationalPathBase, QObject and few more. --- .../evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java | 6 +++--- .../midpoint/repo/sqale/qmodel/object/QObject.java | 8 ++++---- .../midpoint/repo/sqale/qmodel/role/QAbstractRole.java | 6 +++--- .../repo/sqlbase/querydsl/FlexibleRelationalPathBase.java | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java index 29ad0b14bc6..627ed4e21a4 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java @@ -24,7 +24,7 @@ * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QFocus extends QObject { +public class QFocus extends QObject { private static final long serialVersionUID = -535915621882761789L; @@ -120,11 +120,11 @@ public class QFocus extends QObject { public final EnumPath lockoutStatus = createEnum("lockoutStatus", LockoutStatusType.class, LOCKOUT_STATUS); - public QFocus(Class type, String variable) { + public QFocus(Class type, String variable) { this(type, variable, DEFAULT_SCHEMA_NAME, TABLE_NAME); } - public QFocus(Class type, String variable, String schema, String table) { + public QFocus(Class type, String variable, String schema, String table) { super(type, variable, schema, table); } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObject.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObject.java index 4e44fd43147..22ef69a71fe 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObject.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObject.java @@ -25,7 +25,7 @@ * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QObject extends FlexibleRelationalPathBase { +public class QObject extends FlexibleRelationalPathBase { private static final long serialVersionUID = -4174420892574422778L; @@ -129,7 +129,7 @@ public class QObject extends FlexibleRelationalPathBase { public final DateTimePath modifyTimestamp = createInstant("modifyTimestamp", MODIFY_TIMESTAMP); - public final PrimaryKey pk = createPrimaryKey(oid); + public final PrimaryKey pk = createPrimaryKey(oid); public final ForeignKey createChannelIdFk = createForeignKey(createChannelId, QUri.ID.getName()); public final ForeignKey modifyChannelIdFk = @@ -141,11 +141,11 @@ public class QObject extends FlexibleRelationalPathBase { public final ForeignKey tenantRefRelationIdFk = createForeignKey(tenantRefRelationId, QUri.ID.getName()); - public QObject(Class type, String variable) { + public QObject(Class type, String variable) { this(type, variable, DEFAULT_SCHEMA_NAME, TABLE_NAME); } - public QObject(Class type, String variable, String schema, String table) { + public QObject(Class type, String variable, String schema, String table) { super(type, variable, schema, table); } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRole.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRole.java index 91513e08365..75802fc9ce3 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRole.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRole.java @@ -18,7 +18,7 @@ * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QAbstractRole extends QFocus { +public class QAbstractRole extends QFocus { private static final long serialVersionUID = 8559628642680237808L; @@ -52,11 +52,11 @@ public class QAbstractRole extends QFocus { public final BooleanPath requestable = createBoolean("requestable", REQUESTABLE); public final StringPath riskLevel = createString("riskLevel", RISK_LEVEL); - public QAbstractRole(Class type, String variable) { + public QAbstractRole(Class type, String variable) { this(type, variable, DEFAULT_SCHEMA_NAME, TABLE_NAME); } - public QAbstractRole(Class type, String variable, String schema, String table) { + public QAbstractRole(Class type, String variable, String schema, String table) { super(type, variable, schema, table); } } diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/querydsl/FlexibleRelationalPathBase.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/querydsl/FlexibleRelationalPathBase.java index d85ee10a558..26030d00de9 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/querydsl/FlexibleRelationalPathBase.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/querydsl/FlexibleRelationalPathBase.java @@ -69,9 +69,9 @@ * as example, then register the mapping with {@link QueryModelMappingRegistry#register}. * The registration call typically appears in some bean configuration class. * - * @param entity type - typically a pure DTO bean for the table mapped by Q-type + * @param row ("entity bean") type - typically a pure DTO bean for the table mapped by Q-type */ -public class FlexibleRelationalPathBase extends RelationalPathBase { +public class FlexibleRelationalPathBase extends RelationalPathBase { public static final String DEFAULT_SCHEMA_NAME = "PUBLIC"; @@ -80,7 +80,7 @@ public class FlexibleRelationalPathBase extends RelationalPathBase { private final Map> propertyNameToPath = new LinkedHashMap<>(); public FlexibleRelationalPathBase( - Class type, String pathVariable, String schema, String table) { + Class type, String pathVariable, String schema, String table) { super(type, forVariable(pathVariable), schema, table); } From 81fb76fac15339342d5ab686b1f1a8f4bdc845a7 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 12 May 2021 23:15:56 +0200 Subject: [PATCH 04/43] repo-sqale SQL schema: fix of closure table name in refresh function --- repo/repo-sqale/sql/pg-org-experiments.sql | 145 +++++++++++++++++++-- repo/repo-sqale/sql/pgnew-repo.sql | 9 +- 2 files changed, 135 insertions(+), 19 deletions(-) diff --git a/repo/repo-sqale/sql/pg-org-experiments.sql b/repo/repo-sqale/sql/pg-org-experiments.sql index a0176ed1ecf..49a8498c171 100644 --- a/repo/repo-sqale/sql/pg-org-experiments.sql +++ b/repo/repo-sqale/sql/pg-org-experiments.sql @@ -86,22 +86,139 @@ select orgc.parent, orgc.child from orgc orgc where orgc.child = gen_random_uuid() limit 10 ; +EXPLAIN (ANALYZE, VERBOSE, BUFFERS) +select count(*) from m_org_closure where ancestor_oid = '1047bd4e-79ff-466c-802f-c42fc7e3ebaf'; +select * from m_ref_object_parent_org; --- bottom to top, we check for owner type only in the non-recursive term -drop materialized view m_org_closure; -create materialized view m_org_closure as --- EXPLAIN (ANALYZE, VERBOSE, BUFFERS) -with recursive org_h (ancestor, descendant) as not materialized ( +CREATE OR REPLACE FUNCTION m_org_clsr(ancestorOid UUID) + RETURNS TABLE ( + ancestor_oid UUID, -- ref.targetoid + descendant_oid UUID --ref.owner_oid + ) + LANGUAGE plpgsql +AS $$ +DECLARE + flag_val text; +BEGIN + -- No lock here, if the view is OK, we don't want any locking at all. + SELECT value INTO flag_val FROM m_global_metadata WHERE name = 'orgClosureRefreshNeeded'; + IF flag_val = 'true' THEN + CALL m_refresh_org_closure(); + END IF; + + RETURN QUERY SELECT * FROM m_org_closure_internal oci where oci.ancestor_oid = ancestorOid; +END $$; + +select count(*) from m_org_closure_internal; + +select * from m_user +order by oid; + +-- using function m_org_clsr(ancestorOid): 1000 ms for oids, 1400 ms for full rows +-- using m_org_closure with rule: 23 s! no noticeable difference between oid/full select +select oid from m_user u +where u.name_norm like '%45' + and exists (select 1 from + m_ref_object_parent_org pref + join +-- m_org_closure oc on pref.targetoid = oc.descendant_oid + m_org_clsr('4e84dcec-eff6-4f1c-9bad-929e98dea3fa') oc on pref.targetoid = oc.descendant_oid + and pref.owner_oid = u.oid +-- where oc.ancestor_oid = '4e84dcec-eff6-4f1c-9bad-929e98dea3fa' +); + +-- using m_org_closure_internal directly 150 ms for just OIDs (600 ms full rows) +select oid from m_user u +where u.name_norm like '%45' + and exists (select 1 from + m_ref_object_parent_org pref + join + m_org_closure_internal oc on pref.targetoid = oc.descendant_oid + and pref.owner_oid = u.oid + where oc.ancestor_oid = '4e84dcec-eff6-4f1c-9bad-929e98dea3fa' +); + +WITH RECURSIVE org_h ( + ancestor_oid, -- ref.targetoid + descendant_oid --ref.owner_oid +) AS ( -- gather all organizations with parents - select r.targetoid, r.owner_oid - from m_ref_object_parent_org r - where r.owner_type = 'ORG' - union + SELECT r.targetoid, r.owner_oid + FROM m_ref_object_parent_org r + WHERE r.owner_type = 'ORG' + UNION -- generate their parents - select par.targetoid, chi.descendant -- leaving original child there generates closure - from m_ref_object_parent_org as par, org_h as chi - where par.owner_oid = chi.ancestor + SELECT par.targetoid, chi.descendant_oid -- leaving original child there generates closure + FROM m_ref_object_parent_org as par, org_h as chi + WHERE par.owner_oid = chi.ancestor_oid +), +pref as ( + select pref.* from m_ref_object_parent_org pref + join org_h oc on pref.targetoid = oc.descendant_oid + where oc.ancestor_oid = '4e84dcec-eff6-4f1c-9bad-929e98dea3fa' +) +select oid from m_user u +where + u.name_norm like '%45' and + exists (select 1 from pref where pref.owner_oid = u.oid) +; +-- select count(*) from org_h; +-- select * from org_h; +; +select oid from m_user u +where u.name_norm like '%45' + and exists (select 1 from + m_ref_object_parent_org pref + join + org_h oc on pref.targetoid = oc.descendant_oid + and pref.owner_oid = u.oid + where oc.ancestor_oid = '4e84dcec-eff6-4f1c-9bad-929e98dea3fa' +); + +select * from m_ref_object_parent_org + where owner_oid = '0f9badc4-3fc2-4977-aa0a-f08c3576383d'; + +select * from m_org_closure oc + where oc.descendant_oid = '6e732607-609f-46de-9747-1675868dc227'; + +select * from m_org_closure oc + where oc.ancestor_oid = '4e84dcec-eff6-4f1c-9bad-929e98dea3fa'; -- parent +-- where oc.ancestor_oid = '29d933cc-a26c-49c7-afb5-a7b0e39121f2'; -- child +-- where oc.ancestor_oid = '5e3ff03b-7366-4ed5-9d8c-7dc8186b70c6'; -- next child + +select * from m_org_closure oc + where oc.descendant_oid = '29d933cc-a26c-49c7-afb5-a7b0e39121f2'; + +select * from m_global_metadata +; + +refresh materialized view m_org_closure; + +WITH RECURSIVE org_h(ancestor_oid, descendant_oid) AS ( + SELECT r.targetoid, + r.owner_oid + FROM m_ref_object_parent_org r + WHERE r.owner_type = 'ORG'::objecttype + UNION + SELECT par.targetoid, + chi.descendant_oid + FROM m_ref_object_parent_org par, + org_h chi + WHERE par.owner_oid = chi.ancestor_oid ) -select count(*) from org_h; +SELECT count(*) FROM org_h; + +select * from m_org; +select count(*) from m_org; +select count(*) from m_user; +select count(*) from m_org_closure; + +select * from m_org_closure; + +select * from m_object where oid = '62d6f1db-7b97-40de-bfbd-d325020597a0' +; + +select * from m_ref_object_parent_org; -refresh materialized view m_org_closure; \ No newline at end of file +truncate m_ref_object_parent_org; +select * from m_org_closure_internal; \ No newline at end of file diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index 65ee724f40a..2b3564e3a28 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -597,10 +597,9 @@ CREATE INDEX m_ref_object_parent_org_targetOid_relation_id_idx ON m_ref_object_parent_org (targetOid, relation_id); -- region org-closure --- Closure is handled by two views - one materialized (m_org_closure_internal) and one with return --- rule (m_org_closure). Only the second one is used from the outside. --- Trigger on m_ref_object_parent_org creates flag that the materialized view must be refreshed --- (line in +-- Trigger on m_ref_object_parent_org refreshes this view. +-- This is not most performant, but it is *correct* and it's still WIP. + CREATE MATERIALIZED VIEW m_org_closure AS WITH RECURSIVE org_h ( ancestor_oid, -- ref.targetoid @@ -633,7 +632,7 @@ CREATE OR REPLACE FUNCTION m_org_closure_refresh() AS $$ BEGIN IF TG_OP = 'TRUNCATE' OR OLD.owner_type = 'ORG' OR NEW.owner_type = 'ORG' THEN - REFRESH MATERIALIZED VIEW m_org_closure_internal; + REFRESH MATERIALIZED VIEW m_org_closure; END IF; -- after trigger returns null From cca4cb8ea9f384176da70c4910f6fccc46bfc259 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 12 May 2021 23:17:02 +0200 Subject: [PATCH 05/43] repo-sqale: update of querydsl mapping around QOrgClosure Still no Q*Mapping class is needed, this is only for query support. --- .../midpoint/repo/sqale/qmodel/org/MOrg.java | 3 +-- .../repo/sqale/qmodel/org/MOrgClosure.java | 23 +++++++++++++++++++ .../repo/sqale/qmodel/org/QOrgClosure.java | 22 ++++++++++-------- 3 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/org/MOrgClosure.java diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/org/MOrg.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/org/MOrg.java index 09e31cecd8c..26e35ca1b96 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/org/MOrg.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/org/MOrg.java @@ -7,10 +7,9 @@ package com.evolveum.midpoint.repo.sqale.qmodel.org; import com.evolveum.midpoint.repo.sqale.qmodel.role.MAbstractRole; -import com.evolveum.midpoint.repo.sqale.qmodel.role.QRole; /** - * Querydsl "row bean" type related to {@link QRole}. + * Querydsl "row bean" type related to {@link QOrg}. */ public class MOrg extends MAbstractRole { diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/org/MOrgClosure.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/org/MOrgClosure.java new file mode 100644 index 00000000000..3925f623cc4 --- /dev/null +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/org/MOrgClosure.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010-2021 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.repo.sqale.qmodel.org; + +import java.util.UUID; + +/** + * Querydsl "row bean" type related to {@link QOrgClosure}. + */ +public class MOrgClosure { + + public UUID ancestorOid; + public UUID descendantOid; + + @Override + public String toString() { + return "MOrgClosure{" + ancestorOid + " -> " + descendantOid + '}'; + } +} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/org/QOrgClosure.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/org/QOrgClosure.java index 336fa886059..cce18ef83d0 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/org/QOrgClosure.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/org/QOrgClosure.java @@ -12,24 +12,26 @@ import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath; /** - * Querydsl query type for common table expression (CTE) representing org hierarchy on the fly. - * Does not use any backing bean, it is never retrieved directly, only used in the query. + * Querydsl query type for org closure table. + * Can also be used for common table expression (CTE) representing org hierarchy on the fly. * This does not have to be under {@link FlexibleRelationalPathBase}, but is for convenience. */ @SuppressWarnings("unused") -public class QOrgClosure extends FlexibleRelationalPathBase { +public class QOrgClosure extends FlexibleRelationalPathBase { private static final long serialVersionUID = 4406075586720866032L; + public static final String TABLE_NAME = "m_org_closure"; + public static final String DEFAULT_ALIAS_NAME = "orgc"; - public static final ColumnMetadata PARENT = - ColumnMetadata.named("parent").ofType(UuidPath.UUID_TYPE); - public static final ColumnMetadata CHILD = - ColumnMetadata.named("child").ofType(UuidPath.UUID_TYPE); + public static final ColumnMetadata ANCESTOR_OID = + ColumnMetadata.named("ancestor_oid").ofType(UuidPath.UUID_TYPE); + public static final ColumnMetadata DESCENDANT_OID = + ColumnMetadata.named("descendant_oid").ofType(UuidPath.UUID_TYPE); - public final UuidPath parent = createUuid("parent", PARENT); - public final UuidPath child = createUuid("child", CHILD); + public final UuidPath ancestorOid = createUuid("ancestorOid", ANCESTOR_OID); + public final UuidPath descendantOid = createUuid("descendantOid", DESCENDANT_OID); public QOrgClosure() { this(DEFAULT_ALIAS_NAME, DEFAULT_SCHEMA_NAME); @@ -40,6 +42,6 @@ public QOrgClosure(String variable) { } public QOrgClosure(String variable, String schema) { - super(QOrgClosure.class, variable, schema, "orgc"); // not a real table + super(MOrgClosure.class, variable, schema, TABLE_NAME); } } From 3eca7587c9d6014f0837300afc6d33f2937e54f9 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 12 May 2021 23:17:42 +0200 Subject: [PATCH 06/43] repo-sqale: added experimental OrgHierarchyPerfTest, other test tweaks --- .../repo/sqale/func/OrgHierarchyPerfTest.java | 128 ++++++++++++++++++ .../sqale/func/SqaleRepoSearchObjectTest.java | 20 +-- 2 files changed, 138 insertions(+), 10 deletions(-) create mode 100644 repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/OrgHierarchyPerfTest.java diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/OrgHierarchyPerfTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/OrgHierarchyPerfTest.java new file mode 100644 index 00000000000..d1678b99352 --- /dev/null +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/OrgHierarchyPerfTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010-2021 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.repo.sqale.func; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.security.SecureRandom; +import java.util.Arrays; + +import org.jetbrains.annotations.NotNull; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.repo.sqale.SqaleRepoBaseTest; +import com.evolveum.midpoint.repo.sqale.qmodel.focus.QUser; +import com.evolveum.midpoint.repo.sqale.qmodel.org.QOrg; +import com.evolveum.midpoint.repo.sqale.qmodel.org.QOrgClosure; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.SearchResultList; +import com.evolveum.midpoint.schema.SelectorOptions; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.MiscUtil; +import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.prism.xml.ns._public.query_3.QueryType; + +// TODO experimental, maybe temporary +public class OrgHierarchyPerfTest extends SqaleRepoBaseTest { + + public static final SecureRandom RANDOM = new SecureRandom(); + + @BeforeClass + public void initObjects() throws Exception { + OperationResult result = createOperationResult(); + + createOrgsFor(null, 5, 8, result); + + assertThatOperationResult(result).isSuccess(); + } + + private void createOrgsFor( + OrgType parent, int levels, int typicalCountPerLevel, OperationResult result) + throws SchemaException, ObjectAlreadyExistsException { + if (levels == 0) { + createUsersFor(parent, typicalCountPerLevel, result); + return; + } + + int orgs = RANDOM.nextInt(typicalCountPerLevel) + typicalCountPerLevel / 2 + 1; + for (int i = 1; i <= orgs; i++) { + // names use only chars that are preserved by normalization to avoid collision + String name = parent != null ? parent.getName() + "x" + i : "org" + i; + OrgType org = new OrgType(prismContext).name(name); + if (parent != null) { + org.parentOrgRef(parent.getOid(), OrgType.COMPLEX_TYPE); + } + + repositoryService.addObject(org.asPrismObject(), null, result); + createOrgsFor(org, levels - 1, typicalCountPerLevel, result); + } + } + + private void createUsersFor(OrgType parent, int typicalCountPerLevel, OperationResult result) + throws SchemaException, ObjectAlreadyExistsException { + int users = RANDOM.nextInt(typicalCountPerLevel) + typicalCountPerLevel / 2 + 1; + for (int i = 1; i <= users; i++) { + repositoryService.addObject( + new UserType(prismContext).name("user" + parent.getName() + "v" + i) + .metadata(new MetadataType() + .createChannel("create-channel") + .createTimestamp(MiscUtil.asXMLGregorianCalendar(System.currentTimeMillis()))) + .parentOrgRef(parent.getOid(), OrgType.COMPLEX_TYPE) + .asPrismObject(), + null, result); + } + } + + @Test + // TODO + public void test100Xxx() throws Exception { + when("..."); +// OperationResult operationResult = createOperationResult(); +// SearchResultList result = searchObjects(ObjectType.class, +// prismContext.queryFor(ObjectType.class) +// .build(), +// operationResult); + + then("..."); + System.out.println("Orgs: " + count(QOrg.CLASS)); + System.out.println("Org closure: " + count(new QOrgClosure())); + System.out.println("Users: " + count(QUser.class)); + } + + // support methods + @SafeVarargs + @NotNull + private SearchResultList searchObjects( + @NotNull Class type, + ObjectQuery query, + OperationResult operationResult, + SelectorOptions... selectorOptions) + throws SchemaException { + QueryType queryType = prismContext.getQueryConverter().createQueryType(query); + String serializedQuery = prismContext.xmlSerializer().serializeAnyData( + queryType, SchemaConstants.MODEL_EXTENSION_OBJECT_QUERY); + display("QUERY: " + serializedQuery); + + // sanity check if it's re-parsable + assertThat(prismContext.parserFor(serializedQuery).parseRealValue(QueryType.class)) + .isNotNull(); + return repositoryService.searchObjects( + type, + query, + Arrays.asList(selectorOptions), + operationResult) + .map(p -> p.asObjectable()); + } +} diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java index f734650928b..1d8c30c015c 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java @@ -168,7 +168,7 @@ public void testQuerydslCteSimple() { QObjectReference par = QObjectReferenceMapping.getForParentOrg().defaultAlias(); //noinspection unchecked SQLQuery query = sqlRepoContext.newQuery(jdbcSession.connection()) - .withRecursive(orgc, orgc.parent, orgc.child) + .withRecursive(orgc, orgc.ancestorOid, orgc.descendantOid) .as(new SQLQuery<>().union( // non-recursive term: initial select new SQLQuery<>() @@ -178,12 +178,12 @@ public void testQuerydslCteSimple() { // add where here if possible for much faster results (often not possible) .where(), // recursive term: each time add the parents for what we have gathered in orgc until now - new SQLQuery<>().select(par.targetOid, orgc.child) + new SQLQuery<>().select(par.targetOid, orgc.descendantOid) .from(par, orgc) - .where(par.ownerOid.eq(orgc.parent)))) - .select(orgc.parent, orgc.child) + .where(par.ownerOid.eq(orgc.ancestorOid)))) + .select(orgc.ancestorOid, orgc.descendantOid) .from(orgc) - .where(orgc.child.eq(UUID.randomUUID())); + .where(orgc.descendantOid.eq(UUID.randomUUID())); System.out.println("query = " + query); Object o = query.fetchFirst(); System.out.println("o = " + o); @@ -202,7 +202,7 @@ public void testQuerydslCteForUser() { QObjectReference par = QObjectReferenceMapping.getForParentOrg().defaultAlias(); //noinspection unchecked SQLQuery query = sqlRepoContext.newQuery(jdbcSession.connection()) - .withRecursive(orgc, orgc.parent, orgc.child) + .withRecursive(orgc, orgc.ancestorOid, orgc.descendantOid) .as(new SQLQuery<>().union( // non-recursive term: initial select new SQLQuery<>() @@ -212,17 +212,17 @@ public void testQuerydslCteForUser() { // add where here if possible for much faster results (often not possible) .where(), // recursive term: each time add the parents for what we have gathered in orgc until now - new SQLQuery<>().select(par.targetOid, orgc.child) + new SQLQuery<>().select(par.targetOid, orgc.descendantOid) .from(par, orgc) - .where(par.ownerOid.eq(orgc.parent)))) + .where(par.ownerOid.eq(orgc.ancestorOid)))) .select(u) .from(u) .where(u.honorificPrefixNorm.startsWith("x") // query filter condition .and(new SQLQuery<>() .from(orgc) - .where(orgc.child.eq(u.oid) - .and(orgc.parent.eq(orgXOid))) + .where(orgc.descendantOid.eq(u.oid) + .and(orgc.ancestorOid.eq(orgXOid))) .exists())); System.out.println("query = " + query); From c6a7f3235c71e982afc2bd9b3d0182b2f605538d Mon Sep 17 00:00:00 2001 From: Kamil Jires Date: Thu, 13 May 2021 03:02:42 +0200 Subject: [PATCH 07/43] perf-test-process.sh: BUILD_ENV variable implemented (related SQL queries) --- tools/perf-test/perf-test-process.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/perf-test/perf-test-process.sh b/tools/perf-test/perf-test-process.sh index b2ba4197782..3a0cf23bf0e 100644 --- a/tools/perf-test/perf-test-process.sh +++ b/tools/perf-test/perf-test-process.sh @@ -18,7 +18,7 @@ set -eu : "${BUILD_NUMBER:="dev"}" : "${BRANCH:=$(git rev-parse --abbrev-ref HEAD)}" : "${GIT_COMMIT:=$(git show -s --format=%H)}" -: "${ENV:="dev"}" # TODO use this +: "${BUILD_ENV:="dev"}" # TODO use this # backup mkdir -p "${HOME}/perf-out/" @@ -49,16 +49,16 @@ fi # check for commit date in case the date is not already set : "${COMMIT_DATE:=$(git show -s --format=%cI "${GIT_COMMIT}")}" -# load to DB TODO ENV -BUILD_ID=$(psql -tc "select id from mst_build where commit_hash='${GIT_COMMIT}'") +# load to DB +BUILD_ID=$(${PSQL} -tc "select id from mst_build where commit_hash='${GIT_COMMIT}' and env='${BUILD_ENV}'") if [ -n "${BUILD_ID}" ]; then - echo "Results for commit ${GIT_COMMIT} already processed, no action needed." + echo "Results for commit ${GIT_COMMIT} from environment ${BUILD_ENV} already processed, no action needed." exit fi # create new build entry BUILD_ID=$( - "${PSQL}" -qtAX -c "insert into mst_build (build, branch, commit_hash, date) values ('${BUILD_NUMBER}', '${BRANCH}', '${GIT_COMMIT}', '${COMMIT_DATE}') returning id" + "${PSQL}" -qtAX -c "insert into mst_build (build, branch, commit_hash, date, env) values ('${BUILD_NUMBER}', '${BRANCH}', '${GIT_COMMIT}', '${COMMIT_DATE}', '${BUILD_ENV}') returning id" ) echo "BUILD_ID = $BUILD_ID" From 3af1035808b7a13ac33eb32e9f94c83fbcfde89d Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 13 May 2021 16:24:30 +0200 Subject: [PATCH 08/43] repo-sqale: added OrgFilterProcessor with isRoot impl+test --- .../repo/sqale/SqaleQueryContext.java | 11 +- .../sqale/filtering/OrgFilterProcessor.java | 64 +++++++ .../sqale/func/SqaleRepoSearchObjectTest.java | 167 ++++++++---------- .../{func => perf}/OrgHierarchyPerfTest.java | 9 +- .../repo/sqlbase/SqlQueryContext.java | 7 +- .../filtering/ObjectFilterProcessor.java | 26 ++- 6 files changed, 178 insertions(+), 106 deletions(-) create mode 100644 repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java rename repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/{func => perf}/OrgHierarchyPerfTest.java (94%) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleQueryContext.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleQueryContext.java index 1c603666c79..70cfcbeed96 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleQueryContext.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleQueryContext.java @@ -12,7 +12,9 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.prism.query.InOidFilter; +import com.evolveum.midpoint.prism.query.OrgFilter; import com.evolveum.midpoint.repo.sqale.filtering.InOidFilterProcessor; +import com.evolveum.midpoint.repo.sqale.filtering.OrgFilterProcessor; import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTableMapping; import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; import com.evolveum.midpoint.repo.sqlbase.filtering.FilterProcessor; @@ -51,8 +53,13 @@ public SqaleRepoContext repositoryContext() { } @Override - public FilterProcessor createInOidFilter(SqlQueryContext context) { - return new InOidFilterProcessor(context); + public FilterProcessor createInOidFilter() { + return new InOidFilterProcessor(this); + } + + @Override + public FilterProcessor createOrgFilter() { + return new OrgFilterProcessor(this); } public @NotNull Integer searchCachedRelationId(QName qName) { diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java new file mode 100644 index 00000000000..2fb50eda420 --- /dev/null +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010-2021 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.repo.sqale.filtering; + +import com.querydsl.core.types.Predicate; +import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.sql.SQLQuery; + +import com.evolveum.midpoint.prism.query.OrgFilter; +import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReference; +import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; +import com.evolveum.midpoint.repo.sqlbase.QueryException; +import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; +import com.evolveum.midpoint.repo.sqlbase.filtering.FilterProcessor; +import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; + +/** + * Filter processor that resolves {@link OrgFilter}. + */ +public class OrgFilterProcessor implements FilterProcessor { + + private final SqlQueryContext context; + + public OrgFilterProcessor(SqlQueryContext context) { + this.context = context; + } + + @Override + public Predicate process(OrgFilter filter) throws QueryException { + FlexibleRelationalPathBase path = context.root(); + if (!(path instanceof QObject)) { + throw new QueryException("Org filter can only be used for objects," + + " not for path " + path + " of type " + path.getColumns()); + } + + QObject objectPath = (QObject) path; + if (filter.isRoot()) { + var refMapping = QObjectReferenceMapping.getForParentOrg(); + QObjectReference ref = refMapping.newAlias( + context.uniqueAliasName(refMapping.defaultAliasName())); + return new SQLQuery<>().select(Expressions.constant(1)) + .from(ref) + .where(ref.ownerOid.eq(objectPath.oid)) + .notExists(); + } + + if (filter.getOrgRef() == null) { + throw new QueryException("No organization reference defined in the search query."); + } + + if (filter.getOrgRef().getOid() == null) { + throw new QueryException("No oid specified in organization reference " + filter.getOrgRef().debugDump()); + } + + // TODO + throw new QueryException("OrgFilter cannot be applied to the entity: " + path); + } +} diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java index 1d8c30c015c..b72d1aaf025 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java @@ -13,7 +13,6 @@ import java.util.UUID; import javax.xml.namespace.QName; -import com.querydsl.sql.SQLQuery; import org.jetbrains.annotations.NotNull; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -21,12 +20,7 @@ import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.repo.sqale.SqaleRepoBaseTest; -import com.evolveum.midpoint.repo.sqale.qmodel.focus.QUser; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; -import com.evolveum.midpoint.repo.sqale.qmodel.org.QOrgClosure; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReference; -import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; -import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SearchResultList; import com.evolveum.midpoint.schema.SelectorOptions; @@ -40,18 +34,21 @@ public class SqaleRepoSearchObjectTest extends SqaleRepoBaseTest { - private String user1Oid; // typical object - private String task1Oid; // task has more attribute type variability - private String shadow1Oid; // ditto - private String service1Oid; // object with integer attribute - private String org1Oid; + // org structure + private String org1Oid; // one root private String org11Oid; private String org111Oid; private String org112Oid; - private String org2Oid; + private String org2Oid; // second root private String org21Oid; private String orgXOid; // under two orgs + private String user1Oid; // typical object + private String user2Oid; // different user, this one is in org + private String task1Oid; // task has more attribute type variability + private String shadow1Oid; // ditto + private String service1Oid; // object with integer attribute + // other info used in queries private String creatorOid = UUID.randomUUID().toString(); private String modifierOid = UUID.randomUUID().toString(); @@ -62,26 +59,7 @@ public class SqaleRepoSearchObjectTest extends SqaleRepoBaseTest { public void initObjects() throws Exception { OperationResult result = createOperationResult(); - user1Oid = repositoryService.addObject( - new UserType(prismContext).name("user-1") - .metadata(new MetadataType() - .creatorRef(creatorOid, UserType.COMPLEX_TYPE, relation1) - .createChannel("create-channel") - .createTimestamp(MiscUtil.asXMLGregorianCalendar(1L)) - .modifierRef(modifierOid, UserType.COMPLEX_TYPE, relation2) - .modifyChannel("modify-channel") - .modifyTimestamp(MiscUtil.asXMLGregorianCalendar(2L))) - .asPrismObject(), - null, result); - task1Oid = repositoryService.addObject( - new TaskType(prismContext).name("task-1").asPrismObject(), - null, result); - shadow1Oid = repositoryService.addObject( - new ShadowType(prismContext).name("shadow-1").asPrismObject(), - null, result); - service1Oid = repositoryService.addObject( - new ServiceType(prismContext).name("service-1").asPrismObject(), - null, result); + // org structure org1Oid = repositoryService.addObject( new OrgType(prismContext).name("org-1").asPrismObject(), null, result); @@ -115,6 +93,33 @@ public void initObjects() throws Exception { .asPrismObject(), null, result); + // other objects + user1Oid = repositoryService.addObject( + new UserType(prismContext).name("user-1") + .metadata(new MetadataType() + .creatorRef(creatorOid, UserType.COMPLEX_TYPE, relation1) + .createChannel("create-channel") + .createTimestamp(MiscUtil.asXMLGregorianCalendar(1L)) + .modifierRef(modifierOid, UserType.COMPLEX_TYPE, relation2) + .modifyChannel("modify-channel") + .modifyTimestamp(MiscUtil.asXMLGregorianCalendar(2L))) + .asPrismObject(), + null, result); + user2Oid = repositoryService.addObject( + new UserType(prismContext).name("user-2") + .parentOrgRef(orgXOid, OrgType.COMPLEX_TYPE) + .asPrismObject(), + null, result); + task1Oid = repositoryService.addObject( + new TaskType(prismContext).name("task-1").asPrismObject(), + null, result); + shadow1Oid = repositoryService.addObject( + new ShadowType(prismContext).name("shadow-1").asPrismObject(), + null, result); + service1Oid = repositoryService.addObject( + new ServiceType(prismContext).name("service-1").asPrismObject(), + null, result); + assertThatOperationResult(result).isSuccess(); } @@ -160,76 +165,44 @@ public void test110SearchUserByName() throws Exception { // region org filter @Test - public void testQuerydslCteSimple() { - try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession().startReadOnlyTransaction()) { - // CTE query just for the organization closure - QOrgClosure orgc = new QOrgClosure(); - QObjectReference ref = QObjectReferenceMapping.getForParentOrg().defaultAlias(); - QObjectReference par = QObjectReferenceMapping.getForParentOrg().defaultAlias(); - //noinspection unchecked - SQLQuery query = sqlRepoContext.newQuery(jdbcSession.connection()) - .withRecursive(orgc, orgc.ancestorOid, orgc.descendantOid) - .as(new SQLQuery<>().union( - // non-recursive term: initial select - new SQLQuery<>() -// .select(ref.ownerOid, ref.ownerOid) // use this to include identity loops - .select(ref.targetOid, ref.ownerOid) - .from(ref) - // add where here if possible for much faster results (often not possible) - .where(), - // recursive term: each time add the parents for what we have gathered in orgc until now - new SQLQuery<>().select(par.targetOid, orgc.descendantOid) - .from(par, orgc) - .where(par.ownerOid.eq(orgc.ancestorOid)))) - .select(orgc.ancestorOid, orgc.descendantOid) - .from(orgc) - .where(orgc.descendantOid.eq(UUID.randomUUID())); - System.out.println("query = " + query); - Object o = query.fetchFirst(); - System.out.println("o = " + o); - } + public void test200QueryForRootOrganizations() throws SchemaException { + when("searching orgs with is-root filter"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(OrgType.class, + prismContext.queryFor(OrgType.class) + .isRoot() + .build(), + operationResult); + + then("only organizations without any parents are returned"); + assertThat(result).hasSize(2) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(org1Oid, org2Oid); + + and("operation result is success"); + assertThatOperationResult(operationResult).isSuccess(); } @Test - public void testQuerydslCteForUser() { - try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession().startReadOnlyTransaction()) { - // CTE for user with condition that is child of org X - UUID orgXOid = UUID.randomUUID(); + public void test201QueryForRootOrganizationsWithWrongType() throws SchemaException { + // Currently this is "undefined", this does not work in old repo, in new repo it + // checks parent-org refs (not closure). Prism does not complain either. + // First we should fix it on Prism level first, then add type check to OrgFilterProcessor. + when("searching user with is-root filter"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(UserType.class, + prismContext.queryFor(UserType.class) + .isRoot() + .build(), + operationResult); - QUser u = aliasFor(QUser.class); - QOrgClosure orgc = new QOrgClosure(); // vanilla new, this has no mapping - QObjectReference ref = QObjectReferenceMapping.getForParentOrg().defaultAlias(); - QObjectReference par = QObjectReferenceMapping.getForParentOrg().defaultAlias(); - //noinspection unchecked - SQLQuery query = sqlRepoContext.newQuery(jdbcSession.connection()) - .withRecursive(orgc, orgc.ancestorOid, orgc.descendantOid) - .as(new SQLQuery<>().union( - // non-recursive term: initial select - new SQLQuery<>() -// .select(ref.ownerOid, ref.ownerOid) // use this to include identity loops - .select(ref.targetOid, ref.ownerOid) - .from(ref) - // add where here if possible for much faster results (often not possible) - .where(), - // recursive term: each time add the parents for what we have gathered in orgc until now - new SQLQuery<>().select(par.targetOid, orgc.descendantOid) - .from(par, orgc) - .where(par.ownerOid.eq(orgc.ancestorOid)))) - .select(u) - .from(u) - .where(u.honorificPrefixNorm.startsWith("x") - // query filter condition - .and(new SQLQuery<>() - .from(orgc) - .where(orgc.descendantOid.eq(u.oid) - .and(orgc.ancestorOid.eq(orgXOid))) - .exists())); + then("only users without any organizations are returned"); + assertThat(result).hasSize(1) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(user1Oid); - System.out.println("query = " + query); - Object o = query.fetchFirst(); - // just exec check, hardly returns something for random UUID - System.out.println("o = " + o); - } + and("operation result is success"); + assertThatOperationResult(operationResult).isSuccess(); } // TODO // endregion diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/OrgHierarchyPerfTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/perf/OrgHierarchyPerfTest.java similarity index 94% rename from repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/OrgHierarchyPerfTest.java rename to repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/perf/OrgHierarchyPerfTest.java index d1678b99352..8fbc1b93509 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/OrgHierarchyPerfTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/perf/OrgHierarchyPerfTest.java @@ -4,7 +4,7 @@ * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ -package com.evolveum.midpoint.repo.sqale.func; +package com.evolveum.midpoint.repo.sqale.perf; import static org.assertj.core.api.Assertions.assertThat; @@ -43,7 +43,7 @@ public class OrgHierarchyPerfTest extends SqaleRepoBaseTest { public void initObjects() throws Exception { OperationResult result = createOperationResult(); - createOrgsFor(null, 5, 8, result); + createOrgsFor(null, 5, 6, result); assertThatOperationResult(result).isSuccess(); } @@ -56,7 +56,10 @@ private void createOrgsFor( return; } - int orgs = RANDOM.nextInt(typicalCountPerLevel) + typicalCountPerLevel / 2 + 1; + // for roots we want the exact number, so it does not vary wildly + int orgs = parent != null + ? RANDOM.nextInt(typicalCountPerLevel) + typicalCountPerLevel / 2 + 1 + : typicalCountPerLevel; for (int i = 1; i <= orgs; i++) { // names use only chars that are preserved by normalization to avoid collision String name = parent != null ? parent.getName() + "x" + i : "org" + i; diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryContext.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryContext.java index eeda9965227..ec3679e0866 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryContext.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryContext.java @@ -357,7 +357,12 @@ public QName normalizeRelation(QName qName) { return sqlRepoContext.normalizeRelation(qName); } - public FilterProcessor createInOidFilter(SqlQueryContext context) { + public FilterProcessor createInOidFilter() { + // not supported for audit, overridden in repo-sqale + throw new UnsupportedOperationException(); + } + + public FilterProcessor createOrgFilter() { // not supported for audit, overridden in repo-sqale throw new UnsupportedOperationException(); } diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/ObjectFilterProcessor.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/ObjectFilterProcessor.java index 136df9ef56a..789a3cb5e39 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/ObjectFilterProcessor.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/ObjectFilterProcessor.java @@ -14,6 +14,17 @@ import com.evolveum.midpoint.repo.sqlbase.RepositoryException; import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; +/** + * This is a universal/generic filter processor that dispatches to the actual filter processor + * based on the filter type. + * It is used both as an entry point for the root filter of the query, but also when various + * structural filters need to resolve their components (e.g. AND uses this for all its components). + * + * While the subtypes of {@link ObjectFilter} (from Prism API) are all known here, the concrete + * types and implementations in the repository are not all known on the `repo-sqlbase` level. + * That's why some processors are created directly and for others the factory methods + * on the {@link SqlQueryContext} are used and concrete repo (like `repo-sqale` implements them). + */ public class ObjectFilterProcessor implements FilterProcessor { private final SqlQueryContext context; @@ -24,6 +35,7 @@ public ObjectFilterProcessor(SqlQueryContext context) { @Override public Predicate process(ObjectFilter filter) throws RepositoryException { + // To compare with old repo see: QueryInterpreter.findAndCreateRestrictionInternal if (filter instanceof NaryLogicalFilter) { return new NaryLogicalFilterProcessor(context) .process((NaryLogicalFilter) filter); @@ -34,12 +46,20 @@ public Predicate process(ObjectFilter filter) throws RepositoryException { // here are the values applied (ref/property value filters) return new ValueFilterProcessor(context) .process((ValueFilter) filter); -// TODO see QueryInterpreter.findAndCreateRestrictionInternal for uncovered cases -// } else if (filter instanceof OrgFilter) { } else if (filter instanceof InOidFilter) { - return context.createInOidFilter(context) + return context.createInOidFilter() .process((InOidFilter) filter); + } else if (filter instanceof OrgFilter) { + return context.createOrgFilter() + .process((OrgFilter) filter); + } else if (filter instanceof FullTextFilter) { + // TODO + throw new QueryException("TODO filter " + filter); + } else if (filter instanceof TypeFilter) { + // TODO + throw new QueryException("TODO filter " + filter); } else if (filter instanceof AllFilter) { + // TODO throws in old repo, do we want to throw here too? (the same for NoneFilter and UndefinedFilter) return Expressions.asBoolean(true).isTrue(); } else if (filter instanceof NoneFilter) { return Expressions.asBoolean(true).isFalse(); From e69d1c09a9b9afa051e4512d17312b9c74eb242a Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Fri, 14 May 2021 09:22:41 +0200 Subject: [PATCH 09/43] postgresql-4.3-all.sql: removed superfluous warning-causing commit If someone starts the trn before this, it's their call to commit it. --- config/sql/postgresql-4.3-all.sql | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/sql/postgresql-4.3-all.sql b/config/sql/postgresql-4.3-all.sql index bda4ced2ed6..2054ece5422 100644 --- a/config/sql/postgresql-4.3-all.sql +++ b/config/sql/postgresql-4.3-all.sql @@ -1399,5 +1399,3 @@ create index idx_qrtz_ft_j_g on qrtz_fired_triggers(SCHED_NAME,JOB_NAME,JOB_GROU create index idx_qrtz_ft_jg on qrtz_fired_triggers(SCHED_NAME,JOB_GROUP); create index idx_qrtz_ft_t_g on qrtz_fired_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); create index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP); - -commit; From 0e6298ac80a196463d4db35ff67a13d756184cd4 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Fri, 14 May 2021 09:46:19 +0200 Subject: [PATCH 10/43] repo-sql-impl-test: removed unused XMLs with confusing/invalid filters --- .../orgstruct/query-org-struct-org-depth.xml | 23 ------------------- .../query-org-struct-user-unbounded.xml | 23 ------------------- 2 files changed, 46 deletions(-) delete mode 100644 repo/repo-sql-impl-test/src/test/resources/orgstruct/query-org-struct-org-depth.xml delete mode 100644 repo/repo-sql-impl-test/src/test/resources/orgstruct/query-org-struct-user-unbounded.xml diff --git a/repo/repo-sql-impl-test/src/test/resources/orgstruct/query-org-struct-org-depth.xml b/repo/repo-sql-impl-test/src/test/resources/orgstruct/query-org-struct-org-depth.xml deleted file mode 100644 index 16c7763b0d2..00000000000 --- a/repo/repo-sql-impl-test/src/test/resources/orgstruct/query-org-struct-org-depth.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - 2 - - - diff --git a/repo/repo-sql-impl-test/src/test/resources/orgstruct/query-org-struct-user-unbounded.xml b/repo/repo-sql-impl-test/src/test/resources/orgstruct/query-org-struct-user-unbounded.xml deleted file mode 100644 index 1c2e3943011..00000000000 --- a/repo/repo-sql-impl-test/src/test/resources/orgstruct/query-org-struct-user-unbounded.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - unbounded - - - From fdf47ebd68f97e4cf6ba3ebfd7c770837838bdf8 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Fri, 14 May 2021 15:16:28 +0200 Subject: [PATCH 11/43] SqlRepoTestUtil: javadoc simplification, obsolete/noise out, typo fix --- .../midpoint/repo/sql/testing/SqlRepoTestUtil.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/repo/repo-sql-impl-test/src/main/java/com/evolveum/midpoint/repo/sql/testing/SqlRepoTestUtil.java b/repo/repo-sql-impl-test/src/main/java/com/evolveum/midpoint/repo/sql/testing/SqlRepoTestUtil.java index a80385ec2fd..3af6942bc8a 100644 --- a/repo/repo-sql-impl-test/src/main/java/com/evolveum/midpoint/repo/sql/testing/SqlRepoTestUtil.java +++ b/repo/repo-sql-impl-test/src/main/java/com/evolveum/midpoint/repo/sql/testing/SqlRepoTestUtil.java @@ -1,15 +1,13 @@ /* - * Copyright (C) 2010-2020 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ - package com.evolveum.midpoint.repo.sql.testing; import org.testng.AssertJUnit; -import com.evolveum.midpoint.test.util.AbstractSpringTest; import com.evolveum.midpoint.tools.testng.TestMonitor; import com.evolveum.midpoint.tools.testng.TestReportSection; @@ -35,7 +33,7 @@ private static String checkVersionProgressInternal(String prevVersion, String ne return "null next version"; } if (prevVersion == null) { - // anythig is OK + // anything is OK return null; } if (prevVersion.equals(nextVersion)) { @@ -60,11 +58,7 @@ private static String checkVersionProgressInternal(String prevVersion, String ne } /** - * Returns report callback effectively wrapping around "this" at the moment the callback is created. - * This is handy, because the field queryListener may be null at the moment when the results - * are to be processed because of {@link AbstractSpringTest#clearClassFields()} - * and ordering of @After... methods. - *

+ * Returns report callback adding query section to the performance test report. * Note that the section is NOT added if the count of queries is 0. */ public static TestMonitor.ReportCallback createReportCallback(TestQueryListener testQueryListener) { From 63ae1e9201822bb9ede4511da4e864c373bdece9 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Fri, 14 May 2021 22:40:24 +0200 Subject: [PATCH 12/43] OrgFilterProcessor: added support for ONE_LEVEL (isDirectChildOf) + test --- .../sqale/filtering/OrgFilterProcessor.java | 50 ++++++++++--- .../sqale/func/SqaleRepoSearchObjectTest.java | 74 ++++++++++++++++--- 2 files changed, 105 insertions(+), 19 deletions(-) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java index 2fb50eda420..d28438c5308 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java @@ -6,17 +6,20 @@ */ package com.evolveum.midpoint.repo.sqale.filtering; +import java.util.UUID; +import javax.xml.namespace.QName; + import com.querydsl.core.types.Predicate; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.sql.SQLQuery; import com.evolveum.midpoint.prism.query.OrgFilter; +import com.evolveum.midpoint.repo.sqale.SqaleQueryContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReference; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; import com.evolveum.midpoint.repo.sqlbase.QueryException; -import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; import com.evolveum.midpoint.repo.sqlbase.filtering.FilterProcessor; import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; @@ -25,9 +28,9 @@ */ public class OrgFilterProcessor implements FilterProcessor { - private final SqlQueryContext context; + private final SqaleQueryContext context; - public OrgFilterProcessor(SqlQueryContext context) { + public OrgFilterProcessor(SqaleQueryContext context) { this.context = context; } @@ -41,9 +44,7 @@ public Predicate process(OrgFilter filter) throws QueryException { QObject objectPath = (QObject) path; if (filter.isRoot()) { - var refMapping = QObjectReferenceMapping.getForParentOrg(); - QObjectReference ref = refMapping.newAlias( - context.uniqueAliasName(refMapping.defaultAliasName())); + QObjectReference ref = getNewRefAlias(); return new SQLQuery<>().select(Expressions.constant(1)) .from(ref) .where(ref.ownerOid.eq(objectPath.oid)) @@ -54,11 +55,42 @@ public Predicate process(OrgFilter filter) throws QueryException { throw new QueryException("No organization reference defined in the search query."); } - if (filter.getOrgRef().getOid() == null) { + String oidParam = filter.getOrgRef().getOid(); + if (oidParam == null) { throw new QueryException("No oid specified in organization reference " + filter.getOrgRef().debugDump()); } - // TODO - throw new QueryException("OrgFilter cannot be applied to the entity: " + path); + QName relation = filter.getOrgRef().getRelation(); + // null means ANY (not "default") here, so we only search/normalize non-nulls + Integer relationId = relation != null + ? context.repositoryContext().searchCachedRelationId(relation) + : null; + + if (filter.getScope() == OrgFilter.Scope.ONE_LEVEL) { + QObjectReference ref = getNewRefAlias(); + SQLQuery subQuery = new SQLQuery<>().select(Expressions.constant(1)) + .from(ref) + .where(ref.ownerOid.eq(objectPath.oid) + .and(ref.targetOid.eq(UUID.fromString(oidParam)))); + if (relationId != null) { + subQuery.where(ref.relationId.eq(relationId)); + } + return subQuery.exists(); + } else if (filter.getScope() == OrgFilter.Scope.SUBTREE) { + throw new UnsupportedOperationException(); + // TODO + } else if (filter.getScope() == OrgFilter.Scope.ANCESTORS) { + throw new UnsupportedOperationException(); + // TODO + } else { + throw new QueryException("Unknown scope if org filter: " + filter); + } + } + + private QObjectReference getNewRefAlias() { + var refMapping = QObjectReferenceMapping.getForParentOrg(); + QObjectReference ref = refMapping.newAlias( + context.uniqueAliasName(refMapping.defaultAliasName())); + return ref; } } diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java index b72d1aaf025..93bed45acc4 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java @@ -39,6 +39,7 @@ public class SqaleRepoSearchObjectTest extends SqaleRepoBaseTest { private String org11Oid; private String org111Oid; private String org112Oid; + private String org12Oid; private String org2Oid; // second root private String org21Oid; private String orgXOid; // under two orgs @@ -75,7 +76,12 @@ public void initObjects() throws Exception { null, result); org112Oid = repositoryService.addObject( new OrgType(prismContext).name("org-1-1-2") - .parentOrgRef(org11Oid, OrgType.COMPLEX_TYPE) + .parentOrgRef(org11Oid, OrgType.COMPLEX_TYPE, relation1) + .asPrismObject(), + null, result); + org12Oid = repositoryService.addObject( + new OrgType(prismContext).name("org-1-2") + .parentOrgRef(org1Oid, OrgType.COMPLEX_TYPE) .asPrismObject(), null, result); org2Oid = repositoryService.addObject( @@ -88,7 +94,7 @@ public void initObjects() throws Exception { null, result); orgXOid = repositoryService.addObject( new OrgType(prismContext).name("org-X") - .parentOrgRef(org11Oid, OrgType.COMPLEX_TYPE) + .parentOrgRef(org12Oid, OrgType.COMPLEX_TYPE) .parentOrgRef(org21Oid, OrgType.COMPLEX_TYPE, SchemaConstants.ORG_MANAGER) .asPrismObject(), null, result); @@ -108,6 +114,7 @@ public void initObjects() throws Exception { user2Oid = repositoryService.addObject( new UserType(prismContext).name("user-2") .parentOrgRef(orgXOid, OrgType.COMPLEX_TYPE) + .parentOrgRef(org11Oid, OrgType.COMPLEX_TYPE, relation1) .asPrismObject(), null, result); task1Oid = repositoryService.addObject( @@ -155,11 +162,9 @@ public void test110SearchUserByName() throws Exception { operationResult); then("user with the matching name is returned"); + assertThatOperationResult(operationResult).isSuccess(); assertThat(result).hasSize(1); assertThat(result.get(0).getOid()).isEqualTo(user1Oid); - - and("operation result is success"); - assertThatOperationResult(operationResult).isSuccess(); } // endregion @@ -175,12 +180,10 @@ public void test200QueryForRootOrganizations() throws SchemaException { operationResult); then("only organizations without any parents are returned"); + assertThatOperationResult(operationResult).isSuccess(); assertThat(result).hasSize(2) .extracting(o -> o.getOid()) .containsExactlyInAnyOrder(org1Oid, org2Oid); - - and("operation result is success"); - assertThatOperationResult(operationResult).isSuccess(); } @Test @@ -197,14 +200,65 @@ public void test201QueryForRootOrganizationsWithWrongType() throws SchemaExcepti operationResult); then("only users without any organizations are returned"); + assertThatOperationResult(operationResult).isSuccess(); assertThat(result).hasSize(1) .extracting(o -> o.getOid()) .containsExactlyInAnyOrder(user1Oid); + } - and("operation result is success"); + @Test + public void test210QueryForDirectChildrenOrgs() throws SchemaException { + when("searching orgs just under another org"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(OrgType.class, + prismContext.queryFor(OrgType.class) + .isDirectChildOf(org1Oid) + .build(), + operationResult); + + then("only orgs with direct parent-org ref to another org are returned"); assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).hasSize(2) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(org11Oid, org12Oid); } - // TODO + + @Test + public void test211QueryForDirectChildrenOfAnyType() throws SchemaException { + when("searching objects just under an org"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(ObjectType.class, + prismContext.queryFor(ObjectType.class) + .isDirectChildOf(org11Oid) + .build(), + operationResult); + + then("only objects (of any type) with direct parent-org ref to another org are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).hasSize(3) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(org111Oid, org112Oid, user2Oid); + } + + @Test + public void test212QueryForDirectChildrenOfAnyTypeWithRelation() throws SchemaException { + when("searching objects just under an org with specific relation"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(ObjectType.class, + prismContext.queryFor(ObjectType.class) + .isDirectChildOf(prismContext.itemFactory() + .createReferenceValue(org11Oid).relation(relation1)) + .build(), + operationResult); + + then("only objects with direct parent-org ref with specified relation are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).hasSize(2) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(org112Oid, user2Oid); + } + + // TODO child/parent tests // endregion // region special cases From 58b35a6eba41f09b7c554024464aaa1027669243 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Sat, 15 May 2021 08:13:42 +0200 Subject: [PATCH 13/43] OrgFilterProcessor: added isChild support, closure now has o->o rows --- repo/repo-sqale/sql/pgnew-repo.sql | 26 ++++--- .../sqale/filtering/OrgFilterProcessor.java | 32 +++++++-- .../sqale/func/SqaleRepoSearchObjectTest.java | 72 ++++++++++++++++++- 3 files changed, 113 insertions(+), 17 deletions(-) diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index 2b3564e3a28..5c0309da3ed 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -597,22 +597,30 @@ CREATE INDEX m_ref_object_parent_org_targetOid_relation_id_idx ON m_ref_object_parent_org (targetOid, relation_id); -- region org-closure --- Trigger on m_ref_object_parent_org refreshes this view. --- This is not most performant, but it is *correct* and it's still WIP. - +/* +Trigger on m_ref_object_parent_org refreshes this view. +This is not most performant, but it is *correct* and it's still WIP. +Closure contains also identity (org = org) entries because: +* It's easier to do optimized matrix-multiplication based refresh with them later. +* It actually makes some query easier and requires AND instead of OR conditions. +* While the table shows that o => o (=> means "is parent of"), this is not the semantics +of isParent/ChildOf searches and they never return parameter OID as a result. +*/ CREATE MATERIALIZED VIEW m_org_closure AS WITH RECURSIVE org_h ( ancestor_oid, -- ref.targetoid descendant_oid --ref.owner_oid -- paths -- number of different paths, not used for materialized view version - -- TODO depth? if so, cycles must be checked in recursive term + -- depth -- possible later, but cycle detected must be added to the recursive term ) AS ( - -- gather all organizations with parents - SELECT r.targetoid, r.owner_oid - FROM m_ref_object_parent_org r - WHERE r.owner_type = 'ORG' + -- non-recursive term: + -- Gather all organization oids and initialize identity lines (o => o). + SELECT o.oid, o.oid FROM m_org o + -- It's possible to exclude orgs not in parent-org-refs (either owner or target!), + -- but it's not a big deal and makes things simple for JOINs (no outer needed). UNION - -- generate their parents + -- recursive (iterative) term: + -- Generate their parents (anc => desc, that is target => owner), => means "is parent of". SELECT par.targetoid, chi.descendant_oid -- leaving original child there generates closure FROM m_ref_object_parent_org as par, org_h as chi WHERE par.owner_oid = chi.ancestor_oid diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java index d28438c5308..d2ddf13949c 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java @@ -17,6 +17,7 @@ import com.evolveum.midpoint.repo.sqale.SqaleQueryContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.org.QOrgClosure; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReference; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; import com.evolveum.midpoint.repo.sqlbase.QueryException; @@ -45,8 +46,7 @@ public Predicate process(OrgFilter filter) throws QueryException { QObject objectPath = (QObject) path; if (filter.isRoot()) { QObjectReference ref = getNewRefAlias(); - return new SQLQuery<>().select(Expressions.constant(1)) - .from(ref) + return subQuery(ref) .where(ref.ownerOid.eq(objectPath.oid)) .notExists(); } @@ -68,8 +68,7 @@ public Predicate process(OrgFilter filter) throws QueryException { if (filter.getScope() == OrgFilter.Scope.ONE_LEVEL) { QObjectReference ref = getNewRefAlias(); - SQLQuery subQuery = new SQLQuery<>().select(Expressions.constant(1)) - .from(ref) + SQLQuery subQuery = subQuery(ref) .where(ref.ownerOid.eq(objectPath.oid) .and(ref.targetOid.eq(UUID.fromString(oidParam)))); if (relationId != null) { @@ -77,8 +76,16 @@ public Predicate process(OrgFilter filter) throws QueryException { } return subQuery.exists(); } else if (filter.getScope() == OrgFilter.Scope.SUBTREE) { - throw new UnsupportedOperationException(); - // TODO + QObjectReference ref = getNewRefAlias(); + QOrgClosure oc = getNewClosureAlias(); + SQLQuery subQuery = subQuery(ref) + .join(oc).on(oc.descendantOid.eq(ref.targetOid)) + .where(ref.ownerOid.eq(objectPath.oid) + .and(oc.ancestorOid.eq(UUID.fromString(oidParam)))); + if (relationId != null) { + subQuery.where(ref.relationId.eq(relationId)); + } + return subQuery.exists(); } else if (filter.getScope() == OrgFilter.Scope.ANCESTORS) { throw new UnsupportedOperationException(); // TODO @@ -87,10 +94,21 @@ public Predicate process(OrgFilter filter) throws QueryException { } } + private SQLQuery subQuery(FlexibleRelationalPathBase entityPath) { + return new SQLQuery<>().select(Expressions.constant(1)) + .from(entityPath); + } + private QObjectReference getNewRefAlias() { - var refMapping = QObjectReferenceMapping.getForParentOrg(); + QObjectReferenceMapping, MObject> refMapping = + QObjectReferenceMapping.getForParentOrg(); QObjectReference ref = refMapping.newAlias( context.uniqueAliasName(refMapping.defaultAliasName())); return ref; } + + private QOrgClosure getNewClosureAlias() { + return new QOrgClosure( + context.uniqueAliasName(QOrgClosure.DEFAULT_ALIAS_NAME)); + } } diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java index 93bed45acc4..ea632acd784 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java @@ -44,8 +44,10 @@ public class SqaleRepoSearchObjectTest extends SqaleRepoBaseTest { private String org21Oid; private String orgXOid; // under two orgs - private String user1Oid; // typical object + private String user1Oid; // user without org private String user2Oid; // different user, this one is in org + private String user3Oid; // another user in org + private String user4Oid; // another user in org private String task1Oid; // task has more attribute type variability private String shadow1Oid; // ditto private String service1Oid; // object with integer attribute @@ -89,6 +91,7 @@ public void initObjects() throws Exception { null, result); org21Oid = repositoryService.addObject( new OrgType(prismContext).name("org-2-1") + .costCenter("5") .parentOrgRef(org2Oid, OrgType.COMPLEX_TYPE) .asPrismObject(), null, result); @@ -117,6 +120,19 @@ public void initObjects() throws Exception { .parentOrgRef(org11Oid, OrgType.COMPLEX_TYPE, relation1) .asPrismObject(), null, result); + user3Oid = repositoryService.addObject( + new UserType(prismContext).name("user-3") + .costCenter("50") + .parentOrgRef(orgXOid, OrgType.COMPLEX_TYPE) + .parentOrgRef(org21Oid, OrgType.COMPLEX_TYPE, relation1) + .asPrismObject(), + null, result); + user4Oid = repositoryService.addObject( + new UserType(prismContext).name("user-4") + .costCenter("51") + .parentOrgRef(org111Oid, OrgType.COMPLEX_TYPE) + .asPrismObject(), + null, result); task1Oid = repositoryService.addObject( new TaskType(prismContext).name("task-1").asPrismObject(), null, result); @@ -258,6 +274,60 @@ public void test212QueryForDirectChildrenOfAnyTypeWithRelation() throws SchemaEx .containsExactlyInAnyOrder(org112Oid, user2Oid); } + @Test + public void test215QueryForChildrenOfAnyType() throws SchemaException { + when("searching objects anywhere under an org"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(ObjectType.class, + prismContext.queryFor(ObjectType.class) + .isChildOf(org2Oid) + .build(), + operationResult); + + then("all objects under the specified organization are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).hasSize(4) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(org21Oid, orgXOid, user2Oid, user3Oid); + } + + @Test + public void test216QueryForChildrenOfAnyTypeWithRelation() throws SchemaException { + when("searching objects anywhere under an org with specific relation"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(ObjectType.class, + prismContext.queryFor(ObjectType.class) + .isChildOf(prismContext.itemFactory() + .createReferenceValue(org2Oid).relation(relation1)) + .build(), + operationResult); + + then("all objects under the specified organization with specified relation are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).hasSize(1) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(user3Oid); + // user-2 has another parent link with relation1, but not under org-2 + } + + @Test + public void test230QueryForChildrenOfAnyTypeWithAnotherCondition() throws SchemaException { + when("searching objects anywhere under an org"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(FocusType.class, + prismContext.queryFor(FocusType.class) + .isChildOf(org2Oid) + .and().item(FocusType.F_COST_CENTER).startsWith("5") + .build(), + operationResult); + + then("all objects under the specified organization matching other conditions are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).hasSize(2) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(org21Oid, user3Oid); + } + // TODO child/parent tests // endregion From 4915bfbe53c209b778a1fc2e620ec003ebda3921 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Mon, 17 May 2021 09:26:24 +0200 Subject: [PATCH 14/43] repo-sqale: finished org filter with isParentOf support + tests --- .../sqale/filtering/OrgFilterProcessor.java | 13 ++-- .../sqale/func/SqaleRepoSearchObjectTest.java | 69 ++++++++++++++++++- 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java index d2ddf13949c..b0841912543 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java @@ -81,14 +81,18 @@ public Predicate process(OrgFilter filter) throws QueryException { SQLQuery subQuery = subQuery(ref) .join(oc).on(oc.descendantOid.eq(ref.targetOid)) .where(ref.ownerOid.eq(objectPath.oid) - .and(oc.ancestorOid.eq(UUID.fromString(oidParam)))); + .and(oc.ancestorOid.eq(UUID.fromString(oidParam)))); if (relationId != null) { subQuery.where(ref.relationId.eq(relationId)); } return subQuery.exists(); } else if (filter.getScope() == OrgFilter.Scope.ANCESTORS) { - throw new UnsupportedOperationException(); - // TODO + QOrgClosure oc = getNewClosureAlias(); + return subQuery(oc) + .where(oc.ancestorOid.eq(objectPath.oid) + .and(oc.descendantOid.eq(UUID.fromString(oidParam))) + .and(oc.ancestorOid.ne(oc.descendantOid))) + .exists(); } else { throw new QueryException("Unknown scope if org filter: " + filter); } @@ -102,9 +106,8 @@ private SQLQuery subQuery(FlexibleRelationalPathBase entityPath) { private QObjectReference getNewRefAlias() { QObjectReferenceMapping, MObject> refMapping = QObjectReferenceMapping.getForParentOrg(); - QObjectReference ref = refMapping.newAlias( + return refMapping.newAlias( context.uniqueAliasName(refMapping.defaultAliasName())); - return ref; } private QOrgClosure getNewClosureAlias() { diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java index ea632acd784..ebd492ab500 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java @@ -73,7 +73,7 @@ public void initObjects() throws Exception { null, result); org111Oid = repositoryService.addObject( new OrgType(prismContext).name("org-1-1-1") - .parentOrgRef(org11Oid, OrgType.COMPLEX_TYPE) + .parentOrgRef(org11Oid, OrgType.COMPLEX_TYPE, relation2) .asPrismObject(), null, result); org112Oid = repositoryService.addObject( @@ -310,6 +310,73 @@ public void test216QueryForChildrenOfAnyTypeWithRelation() throws SchemaExceptio // user-2 has another parent link with relation1, but not under org-2 } + @Test + public void test220QueryForParents() throws SchemaException { + when("searching parents of an org"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(OrgType.class, + prismContext.queryFor(OrgType.class) + .isParentOf(org112Oid) + .build(), + operationResult); + + then("all ancestors of the specified organization are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).hasSize(2) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(org11Oid, org1Oid); + } + + @Test + public void test221QueryForParentsWithOrgSupertype() throws SchemaException { + when("searching parents of an org using more abstract type"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(ObjectType.class, + prismContext.queryFor(ObjectType.class) + .isParentOf(org112Oid) + .build(), + operationResult); + + then("returns orgs but as supertype instances"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).hasSize(2) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(org11Oid, org1Oid); + } + + @Test + public void test222QueryForParentsUsingOtherType() throws SchemaException { + when("searching parents of an org using unrelated type"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(UserType.class, + prismContext.queryFor(UserType.class) + .isParentOf(org112Oid) + .build(), + operationResult); + + then("returns nothing (officially the behaviour is undefined)"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).isEmpty(); + } + + @Test + public void test223QueryForParentIgnoresRelation() throws SchemaException { + when("searching parents of an org with specified relation"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(OrgType.class, + prismContext.queryFor(OrgType.class) + .isParentOf(prismContext.itemFactory() + .createReferenceValue(org112Oid).relation(relation2)) + .build(), + operationResult); + + then("all ancestors are returned, ignoring provided relation"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).hasSize(2) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(org11Oid, org1Oid); + } + @Test public void test230QueryForChildrenOfAnyTypeWithAnotherCondition() throws SchemaException { when("searching objects anywhere under an org"); From f7c6583777026299f12c4e4594e0f93e9053db1c Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Mon, 17 May 2021 15:42:14 +0200 Subject: [PATCH 15/43] repo-sqale: app controlled lazy refresh of org-closure before query --- repo/repo-sqale/sql/pg-org-experiments.sql | 33 +++------ repo/repo-sqale/sql/pgnew-repo.sql | 73 ++++++++++++++----- .../repo/sqale/SqaleQueryContext.java | 36 ++++++++- .../sqale/filtering/OrgFilterProcessor.java | 2 + .../repo/sqale/perf/OrgHierarchyPerfTest.java | 50 +++++++++---- .../repo/sql/audit/AuditSqlQueryContext.java | 9 ++- .../repo/sqlbase/SqlQueryContext.java | 24 ++++++ .../repo/sqlbase/SqlQueryExecutor.java | 2 + 8 files changed, 171 insertions(+), 58 deletions(-) diff --git a/repo/repo-sqale/sql/pg-org-experiments.sql b/repo/repo-sqale/sql/pg-org-experiments.sql index 49a8498c171..f5ea89f037e 100644 --- a/repo/repo-sqale/sql/pg-org-experiments.sql +++ b/repo/repo-sqale/sql/pg-org-experiments.sql @@ -194,31 +194,22 @@ select * from m_global_metadata refresh materialized view m_org_closure; -WITH RECURSIVE org_h(ancestor_oid, descendant_oid) AS ( - SELECT r.targetoid, - r.owner_oid - FROM m_ref_object_parent_org r - WHERE r.owner_type = 'ORG'::objecttype - UNION - SELECT par.targetoid, - chi.descendant_oid - FROM m_ref_object_parent_org par, - org_h chi - WHERE par.owner_oid = chi.ancestor_oid -) -SELECT count(*) FROM org_h; - select * from m_org; select count(*) from m_org; -select count(*) from m_user; select count(*) from m_org_closure; +select count(*) from m_user; -select * from m_org_closure; +-- Perf test adding orgs: +-- trigger with refresh: orgs/closure: 15125/72892 29m31s, ~8.5 orgs/s (most late addObject took ~220ms) +-- empty trigger: orgs/closure: 14052/67735 (after manual refresh taking ~230 ms) 31s, ~450 orgs/s +-- trigger with mark: orgs/closure: 14573/70225 (after m_refresh_org_closure ~300 ms) 32s, ~455 orgs/s +-- trigger with mark: orgs/closure: 59711/291099 (after refresh ~1.9s), 2m13s, ~450 orgs/s -select * from m_object where oid = '62d6f1db-7b97-40de-bfbd-d325020597a0' -; +select * from m_org o + where not exists (select 1 from m_ref_object_parent_org po where po.owner_oid = o.oid); -select * from m_ref_object_parent_org; +select * from m_org_closure; + +select * FROM m_global_metadata; -truncate m_ref_object_parent_org; -select * from m_org_closure_internal; \ No newline at end of file +CALL m_refresh_org_closure(true); diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index 5c0309da3ed..b791ba8084d 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -121,10 +121,10 @@ CREATE TYPE TimeIntervalStatusType AS ENUM ('BEFORE', 'IN', 'AFTER'); -- select * from pg_available_extensions order by name; DO $$ BEGIN - perform pg_get_functiondef('gen_random_uuid()'::regprocedure); - raise notice 'gen_random_uuid already exists, skipping create EXTENSION pgcrypto'; + PERFORM pg_get_functiondef('gen_random_uuid()'::regprocedure); + RAISE NOTICE 'gen_random_uuid already exists, skipping create EXTENSION pgcrypto'; EXCEPTION WHEN undefined_function THEN - create EXTENSION pgcrypto; + CREATE EXTENSION pgcrypto; END $$; @@ -142,9 +142,9 @@ CREATE OR REPLACE FUNCTION insert_object_oid() AS $$ BEGIN IF NEW.oid IS NOT NULL THEN - insert into m_object_oid values (NEW.oid); + INSERT INTO m_object_oid VALUES (NEW.oid); ELSE - insert into m_object_oid DEFAULT VALUES RETURNING oid INTO NEW.oid; + INSERT INTO m_object_oid DEFAULT VALUES RETURNING oid INTO NEW.oid; END IF; -- before trigger must return NEW row to do something RETURN NEW; @@ -183,7 +183,13 @@ END $$; -- endregion --- region Enumeration/code tables +-- region Enumeration/code/management tables +-- Key -> value config table for internal use. +CREATE TABLE m_global_metadata ( + name TEXT PRIMARY KEY, + value TEXT +); + -- Catalog of often used URIs, typically channels and relation Q-names. -- Never update values of "uri" manually to change URI for some objects -- (unless you really want to migrate old URI to a new one). @@ -614,10 +620,12 @@ WITH RECURSIVE org_h ( -- depth -- possible later, but cycle detected must be added to the recursive term ) AS ( -- non-recursive term: - -- Gather all organization oids and initialize identity lines (o => o). + -- Gather all organization oids from parent-org refs and initialize identity lines (o => o). + -- We don't want the orgs not in org hierarchy, that would require org triggers too. SELECT o.oid, o.oid FROM m_org o - -- It's possible to exclude orgs not in parent-org-refs (either owner or target!), - -- but it's not a big deal and makes things simple for JOINs (no outer needed). + WHERE EXISTS( + SELECT 1 FROM m_ref_object_parent_org r + WHERE r.targetOid = o.oid OR r.owner_oid = o.oid) UNION -- recursive (iterative) term: -- Generate their parents (anc => desc, that is target => owner), => means "is parent of". @@ -634,25 +642,55 @@ CREATE INDEX m_org_closure_desc_asc_idx ON m_org_closure (descendant_oid, ancestor_oid); -- The trigger for m_ref_object_parent_org that flags the view for refresh. -CREATE OR REPLACE FUNCTION m_org_closure_refresh() +CREATE OR REPLACE FUNCTION mark_org_closure_for_refresh() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF TG_OP = 'TRUNCATE' OR OLD.owner_type = 'ORG' OR NEW.owner_type = 'ORG' THEN - REFRESH MATERIALIZED VIEW m_org_closure; + INSERT INTO m_global_metadata VALUES ('orgClosureRefreshNeeded', 'true') + ON CONFLICT (name) DO UPDATE SET value = 'true'; END IF; -- after trigger returns null RETURN NULL; END $$; -CREATE TRIGGER m_ref_object_parent_org_refresh_tr +CREATE TRIGGER m_ref_object_parent_mark_refresh_tr AFTER INSERT OR UPDATE OR DELETE ON m_ref_object_parent_org - FOR EACH ROW EXECUTE PROCEDURE m_org_closure_refresh(); -CREATE TRIGGER m_ref_object_parent_org_trunc_refresh_tr + FOR EACH ROW EXECUTE PROCEDURE mark_org_closure_for_refresh(); +CREATE TRIGGER m_ref_object_parent_mark_refresh_trunc_tr AFTER TRUNCATE ON m_ref_object_parent_org - FOR EACH STATEMENT EXECUTE PROCEDURE m_org_closure_refresh(); + FOR EACH STATEMENT EXECUTE PROCEDURE mark_org_closure_for_refresh(); + +-- This procedure for conditional refresh when needed is called from the application code. +-- The refresh can be forced, e.g. after many changes with triggers off (or just to be sure). +CREATE OR REPLACE PROCEDURE m_refresh_org_closure(force boolean = false) + LANGUAGE plpgsql +AS $$ +DECLARE + flag_val text; +BEGIN + SELECT value INTO flag_val FROM m_global_metadata WHERE name = 'orgClosureRefreshNeeded'; + IF flag_val = 'true' OR force THEN + -- We use advisory session lock only for the check + refresh, then release it immediately. + -- This can still dead-lock two transactions in a single thread on the select/delete combo, + -- (I mean, who would do that?!) but works fine for parallel transactions. + PERFORM pg_advisory_lock(47); + BEGIN + SELECT value INTO flag_val FROM m_global_metadata WHERE name = 'orgClosureRefreshNeeded'; + IF flag_val = 'true' OR force THEN + REFRESH MATERIALIZED VIEW m_org_closure; + DELETE FROM m_global_metadata WHERE name = 'orgClosureRefreshNeeded'; + END IF; + PERFORM pg_advisory_unlock(47); + EXCEPTION WHEN OTHERS THEN + -- Whatever happens we definitely want to release the lock. + PERFORM pg_advisory_unlock(47); + RAISE; + END; + END IF; +END; $$; -- endregion -- endregion @@ -2067,11 +2105,6 @@ create index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP); */ -- region Schema versioning and upgrading -CREATE TABLE m_global_metadata ( - name TEXT PRIMARY KEY, - value TEXT -); - /* Procedure applying a DB schema/data change. Use sequential change numbers to identify the changes. This protects re-execution of the same change on the same database instance. diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleQueryContext.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleQueryContext.java index 70cfcbeed96..c9eef0ca77a 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleQueryContext.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleQueryContext.java @@ -16,6 +16,7 @@ import com.evolveum.midpoint.repo.sqale.filtering.InOidFilterProcessor; import com.evolveum.midpoint.repo.sqale.filtering.OrgFilterProcessor; import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTableMapping; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; import com.evolveum.midpoint.repo.sqlbase.filtering.FilterProcessor; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; @@ -24,6 +25,8 @@ public class SqaleQueryContext, R> extends SqlQueryContext { + private boolean containsOrgFilter = false; + public static , R> SqaleQueryContext from( Class schemaType, SqaleRepoContext sqlRepoContext) { @@ -47,6 +50,13 @@ private SqaleQueryContext( super(entityPath, mapping, sqlRepoContext, query); } + private SqaleQueryContext( + Q entityPath, + SqaleTableMapping mapping, + SqaleQueryContext parentContext) { + super(entityPath, mapping, parentContext); + } + @Override public SqaleRepoContext repositoryContext() { return (SqaleRepoContext) super.repositoryContext(); @@ -66,6 +76,14 @@ public FilterProcessor createOrgFilter() { return repositoryContext().searchCachedRelationId(qName); } + public void markContainsOrgFilter() { + containsOrgFilter = true; + SqaleQueryContext parentContext = parentContext(); + if (parentContext != null) { + parentContext.markContainsOrgFilter(); + } + } + /** * Returns derived {@link SqaleQueryContext} for join or subquery. */ @@ -75,7 +93,21 @@ public FilterProcessor createOrgFilter() { return new SqaleQueryContext<>( newPath, (SqaleTableMapping) newMapping, - repositoryContext(), - sqlQuery); + this); + } + + @Override + public SqaleQueryContext parentContext() { + return (SqaleQueryContext) super.parentContext(); + } + + @Override + public void beforeQuery() { + if (containsOrgFilter) { + try (JdbcSession jdbcSession = repositoryContext().newJdbcSession().startTransaction()) { + jdbcSession.executeStatement("CALL m_refresh_org_closure()"); + jdbcSession.commit(); + } + } } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java index b0841912543..c4b85828440 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/OrgFilterProcessor.java @@ -37,6 +37,8 @@ public OrgFilterProcessor(SqaleQueryContext context) { @Override public Predicate process(OrgFilter filter) throws QueryException { + context.markContainsOrgFilter(); // necessary for lazy refresh of org closure + FlexibleRelationalPathBase path = context.root(); if (!(path instanceof QObject)) { throw new QueryException("Org filter can only be used for objects," diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/perf/OrgHierarchyPerfTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/perf/OrgHierarchyPerfTest.java index 8fbc1b93509..c3d61fa0ae3 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/perf/OrgHierarchyPerfTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/perf/OrgHierarchyPerfTest.java @@ -15,11 +15,13 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.repo.sqale.SqaleRepoBaseTest; import com.evolveum.midpoint.repo.sqale.qmodel.focus.QUser; import com.evolveum.midpoint.repo.sqale.qmodel.org.QOrg; import com.evolveum.midpoint.repo.sqale.qmodel.org.QOrgClosure; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SearchResultList; import com.evolveum.midpoint.schema.SelectorOptions; @@ -43,6 +45,13 @@ public class OrgHierarchyPerfTest extends SqaleRepoBaseTest { public void initObjects() throws Exception { OperationResult result = createOperationResult(); + try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession().startTransaction()) { + jdbcSession.executeStatement("CALL m_refresh_org_closure(true)"); + jdbcSession.commit(); + } + assertThat(count(QOrg.CLASS)).isZero(); + assertThat(count(new QOrgClosure())).isZero(); + createOrgsFor(null, 5, 6, result); assertThatOperationResult(result).isSuccess(); @@ -52,6 +61,7 @@ private void createOrgsFor( OrgType parent, int levels, int typicalCountPerLevel, OperationResult result) throws SchemaException, ObjectAlreadyExistsException { if (levels == 0) { + // typical count is used as max for user generation createUsersFor(parent, typicalCountPerLevel, result); return; } @@ -73,9 +83,9 @@ private void createOrgsFor( } } - private void createUsersFor(OrgType parent, int typicalCountPerLevel, OperationResult result) + private void createUsersFor(OrgType parent, int maxCountPerLevel, OperationResult result) throws SchemaException, ObjectAlreadyExistsException { - int users = RANDOM.nextInt(typicalCountPerLevel) + typicalCountPerLevel / 2 + 1; + int users = RANDOM.nextInt(maxCountPerLevel) + 1; for (int i = 1; i <= users; i++) { repositoryService.addObject( new UserType(prismContext).name("user" + parent.getName() + "v" + i) @@ -89,19 +99,31 @@ private void createUsersFor(OrgType parent, int typicalCountPerLevel, OperationR } @Test - // TODO public void test100Xxx() throws Exception { - when("..."); -// OperationResult operationResult = createOperationResult(); -// SearchResultList result = searchObjects(ObjectType.class, -// prismContext.queryFor(ObjectType.class) -// .build(), -// operationResult); - - then("..."); - System.out.println("Orgs: " + count(QOrg.CLASS)); - System.out.println("Org closure: " + count(new QOrgClosure())); - System.out.println("Users: " + count(QUser.class)); + given("there are orgs and users, closure is not yet updated"); + OperationResult operationResult = createOperationResult(); + display("Orgs: " + count(QOrg.CLASS)); + display("Users: " + count(QUser.class)); + assertThat(count(new QOrgClosure())).isZero(); + OrgType org1x1x1 = searchObjects(OrgType.class, + prismContext.queryFor(OrgType.class) + .item(ObjectType.F_NAME).eq(PolyString.fromOrig("org1x1x1")) + .build(), + operationResult).get(0); + + when("search for user under some org is initiated"); + SearchResultList result = searchObjects(UserType.class, + prismContext.queryFor(UserType.class) + .isChildOf(org1x1x1.getOid()) + .build(), + operationResult); + + then("non-empty result is returned and org closure has been initialized"); + assertThat(result).isNotEmpty(); + assertThat(count(new QOrgClosure())).isPositive(); + display("Orgs: " + count(QOrg.CLASS)); + display("Org closure: " + count(new QOrgClosure())); + display("Users: " + count(QUser.class)); } // support methods diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/AuditSqlQueryContext.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/AuditSqlQueryContext.java index 516d6e65d6c..f304bad3c31 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/AuditSqlQueryContext.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/audit/AuditSqlQueryContext.java @@ -44,10 +44,17 @@ private AuditSqlQueryContext( super(entityPath, mapping, sqlRepoContext, query); } + private AuditSqlQueryContext( + Q entityPath, + QueryTableMapping mapping, + AuditSqlQueryContext parentContext) { + super(entityPath, mapping, parentContext); + } + @Override protected , TR> SqlQueryContext deriveNew(TQ newPath, QueryTableMapping newMapping) { return new AuditSqlQueryContext<>( - newPath, newMapping, repositoryContext(), sqlQuery); + newPath, newMapping, this); } } diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryContext.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryContext.java index ec3679e0866..c86910d050d 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryContext.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryContext.java @@ -75,11 +75,14 @@ public abstract class SqlQueryContext protected final QueryTableMapping entityPathMapping; private final SqlRepoContext sqlRepoContext; + private final SqlQueryContext parent; + protected boolean notFilterUsed = false; // options stored to modify select clause and also to affect mapping protected Collection> options; + /** Constructor for root query context. */ protected SqlQueryContext( Q entityPath, QueryTableMapping mapping, @@ -89,6 +92,19 @@ protected SqlQueryContext( this.entityPathMapping = mapping; this.sqlRepoContext = sqlRepoContext; this.sqlQuery = query; + this.parent = null; + } + + /** Constructor for derived context or sub-context, e.g. JOIN, EXISTS, etc. */ + protected SqlQueryContext( + Q entityPath, + QueryTableMapping mapping, + SqlQueryContext parentContext) { + this.entityPath = entityPath; + this.entityPathMapping = mapping; + this.sqlRepoContext = parentContext.repositoryContext(); + this.sqlQuery = parentContext.sqlQuery; + this.parent = parentContext; } public Q root() { @@ -313,6 +329,10 @@ public SQLQuery sqlQuery() { return sqlQuery; } + public SqlQueryContext parentContext() { + return parent; + } + /** * Returns entity path of this context. */ @@ -366,4 +386,8 @@ public FilterProcessor createOrgFilter() { // not supported for audit, overridden in repo-sqale throw new UnsupportedOperationException(); } + + // before-query hook, empty by default + public void beforeQuery() { + } } diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryExecutor.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryExecutor.java index 4476d443838..77e5947c53e 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryExecutor.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/SqlQueryExecutor.java @@ -46,6 +46,7 @@ public , R> int count( // TODO MID-6319: all options can be applied, just like for list? context.processOptions(options); + context.beforeQuery(); try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession().startReadOnlyTransaction()) { return context.executeCount(jdbcSession.connection()); } @@ -63,6 +64,7 @@ public , R> SearchResultList list( } context.processOptions(options); + context.beforeQuery(); PageOf result; try (JdbcSession jdbcSession = sqlRepoContext.newJdbcSession().startReadOnlyTransaction()) { result = context.executeQuery(jdbcSession.connection()); From 4d3c5845b464f66c191620815d3f007cc77519d1 Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Mon, 17 May 2021 16:36:49 +0200 Subject: [PATCH 16/43] Squale: case --- .../repo/sqale/qmodel/cases/QCaseMapping.java | 33 ++++++++++- .../func/SqaleRepoAddDeleteObjectTest.java | 56 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java index 9ee84f7589a..dbf13db3fbd 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java @@ -8,6 +8,12 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType.*; +import com.evolveum.midpoint.repo.sqale.qmodel.task.MTask; + +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; + +import com.evolveum.midpoint.util.MiscUtil; + import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; @@ -60,5 +66,30 @@ public MCase newRowObject() { return new MCase(); } - // TODO transformation code + @Override + public @NotNull MCase toRowObjectWithoutFullObject( + CaseType acase, JdbcSession jdbcSession) { + MCase row = super.toRowObjectWithoutFullObject(acase, jdbcSession); + + row.state = acase.getState(); + row.closeTimestamp = MiscUtil.asInstant(acase.getCloseTimestamp()); + setReference(acase.getObjectRef(), + o -> row.objectRefTargetOid = o, + t -> row.objectRefTargetType = t, + r -> row.objectRefRelationId = r); + setReference(acase.getParentRef(), + o -> row.parentRefTargetOid = o, + t -> row.parentRefTargetType = t, + r -> row.parentRefRelationId = r); + setReference(acase.getRequestorRef(), + o -> row.requestorRefTargetOid = o, + t -> row.requestorRefTargetType = t, + r -> row.requestorRefRelationId = r); + setReference(acase.getTargetRef(), + o -> row.targetRefTargetOid = o, + t -> row.targetRefTargetType = t, + r -> row.targetRefRelationId = r); + + return row; + } } diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java index b4e51abf1cf..5ae99c81376 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java @@ -18,6 +18,10 @@ import java.util.UUID; import javax.xml.namespace.QName; +import com.evolveum.midpoint.repo.sqale.qmodel.cases.MCase; + +import com.evolveum.midpoint.repo.sqale.qmodel.cases.QCase; + import org.testng.annotations.Test; import com.evolveum.midpoint.repo.api.DeleteObjectResult; @@ -1118,6 +1122,58 @@ public void test840AccessCertificationDefinition() throws Exception { assertThat(row.ownerRefTargetType).isEqualTo(MObjectType.USER); assertCachedUri(row.ownerRefRelationId, relationUri); } + + @Test + public void test850Case() throws Exception { + OperationResult result = createOperationResult(); + + given("case"); + String objectName = "case" + getTestNumber(); + UUID parentOid = UUID.randomUUID(); + QName parentRelation = QName.valueOf("{https://random.org/ns}case-parent-rel"); + UUID objectOid = UUID.randomUUID(); + QName objectRelation = QName.valueOf("{https://random.org/ns}case-object-rel"); + UUID requestorOid = UUID.randomUUID(); + QName requestorRelation = QName.valueOf("{https://random.org/ns}case-requestor-rel"); + UUID targetOid = UUID.randomUUID(); + QName targetRelation = QName.valueOf("{https://random.org/ns}case-target-rel"); + + CaseType acase = new CaseType(prismContext) + .name(objectName) + .state("closed") + .closeTimestamp(MiscUtil.asXMLGregorianCalendar(321L)) + .parentRef(parentOid.toString(), + CaseType.COMPLEX_TYPE, parentRelation) + .objectRef(objectOid.toString(), + RoleType.COMPLEX_TYPE, objectRelation) + .requestorRef(requestorOid.toString(), + UserType.COMPLEX_TYPE, requestorRelation) + .targetRef(targetOid.toString(), + OrgType.COMPLEX_TYPE, targetRelation); + + when("adding it to the repository"); + repositoryService.addObject(acase.asPrismObject(), null, result); + + then("it is stored and relevant attributes are in columns"); + assertThatOperationResult(result).isSuccess(); + + MCase row = selectObjectByOid(QCase.class, acase.getOid()); + assertThat(row.state).isEqualTo("closed"); + assertThat(row.closeTimestamp).isEqualTo(Instant.ofEpochMilli(321)); + assertThat(row.parentRefTargetOid).isEqualTo(parentOid); + assertThat(row.parentRefTargetType).isEqualTo(MObjectType.CASE); + assertCachedUri(row.parentRefRelationId, parentRelation); + assertThat(row.objectRefTargetOid).isEqualTo(objectOid); + assertThat(row.objectRefTargetType).isEqualTo(MObjectType.ROLE); + assertCachedUri(row.objectRefRelationId, objectRelation); + assertThat(row.requestorRefTargetOid).isEqualTo(requestorOid); + assertThat(row.requestorRefTargetType).isEqualTo(MObjectType.USER); + assertCachedUri(row.requestorRefRelationId, requestorRelation); + assertThat(row.targetRefTargetOid).isEqualTo(targetOid); + assertThat(row.targetRefTargetType).isEqualTo(MObjectType.ORG); + assertCachedUri(row.targetRefRelationId, targetRelation); + } + // endregion // region delete tests From f707ddc6b4b6f25a66fee50a03fe362c614d7c80 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Tue, 18 May 2021 11:52:01 +0200 Subject: [PATCH 17/43] old-repo OperationLogger moved to repo-sqlbase, can be reused as-is --- .../repo/sql/SqlRepositoryServiceImpl.java | 4 +-- .../repo/sqlbase}/OperationLogger.java | 36 +++++++++++-------- 2 files changed, 23 insertions(+), 17 deletions(-) rename repo/{repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql => repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase}/OperationLogger.java (80%) diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java index 7559d56c7f5..d9adef62eb4 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java @@ -19,6 +19,7 @@ import javax.annotation.PreDestroy; import javax.xml.namespace.QName; +import com.evolveum.midpoint.repo.sqlbase.OperationLogger; import com.evolveum.midpoint.util.annotation.Experimental; import org.apache.commons.lang3.Validate; @@ -413,7 +414,6 @@ public String addObject( int attempt = 1; int restarts = 0; try { - // TODO use executeAttempts final String operation = "adding"; String proposedOid = object.getOid(); @@ -703,7 +703,7 @@ public ModifyObjectResult modifyObjectDynamically( pm.registerOperationFinish(opHandle, attempt); throw t; } finally { - OperationLogger.logModifyDynamically(type, oid, rv, getOptions, modifyOptions, result); + OperationLogger.logModifyDynamically(type, oid, rv, modifyOptions, result); } } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/OperationLogger.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/OperationLogger.java similarity index 80% rename from repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/OperationLogger.java rename to repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/OperationLogger.java index 51fb6750e8c..4736f08b342 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/OperationLogger.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/OperationLogger.java @@ -1,10 +1,10 @@ /* - * Copyright (c) 2018 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ -package com.evolveum.midpoint.repo.sql; +package com.evolveum.midpoint.repo.sqlbase; import java.util.Collection; @@ -23,10 +23,6 @@ import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -/** - * @author semancik - * - */ public class OperationLogger { private static final Trace LOGGER_OP = TraceManager.getTrace("com.evolveum.midpoint.repo.operation"); @@ -40,25 +36,36 @@ public static void logAdd(PrismObject object, RepoAddO object.debugDump(1)); } - public static void logModify(Class type, String oid, Collection modifications, - ModificationPrecondition precondition, RepoModifyOptions options, OperationResult subResult) { + public static void logModify( + Class type, + String oid, + Collection> modifications, + ModificationPrecondition precondition, + RepoModifyOptions options, + OperationResult subResult) { if (!LOGGER_OP.isDebugEnabled()) { return; } LOGGER_OP.debug("{} modify {} {}{}{}: {}\n{}", PREFIX, type.getSimpleName(), oid, shortDumpOptions(options), - precondition == null ? "" : " precondition="+precondition+", ", + precondition == null ? "" : " precondition=" + precondition + ", ", getStatus(subResult), DebugUtil.debugDump(modifications, 1, false)); } - public static void logModifyDynamically(Class type, String oid, - ModifyObjectResult modifyObjectResult, Collection> getOptions, - RepoModifyOptions modifyOptions, OperationResult result) { + public static void logModifyDynamically( + Class type, + String oid, + ModifyObjectResult modifyObjectResult, + RepoModifyOptions modifyOptions, + OperationResult result) { if (!LOGGER_OP.isDebugEnabled()) { return; } - Collection modifications = modifyObjectResult != null ? modifyObjectResult.getModifications() : null; + Collection> modifications = + modifyObjectResult != null + ? modifyObjectResult.getModifications() + : null; LOGGER_OP.debug("{} modify dynamically {} {}{}: {}\n{}", PREFIX, type.getSimpleName(), oid, shortDumpOptions(modifyOptions), getStatus(result), @@ -86,7 +93,7 @@ private static Object shortDumpOptions(ShortDumpable options) { if (options == null) { return ""; } else { - return " ["+options.shortDump()+"]"; + return " [" + options.shortDump() + "]"; } } @@ -111,5 +118,4 @@ private static String getStatus(OperationResult subResult) { return subResult.getStatus().toString() + ": " + message; } } - } From 6a379ece8570b981f74a8abc7919973ef654df42 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Tue, 18 May 2021 12:04:18 +0200 Subject: [PATCH 18/43] repo: add+overwrite has own key for perf stats + cleanup/generics around --- .../midpoint/repo/api/ModifyObjectResult.java | 8 ++++---- .../midpoint/repo/api/RepositoryService.java | 1 + .../api/perf/OperationPerformanceInformation.java | 13 ++++++------- .../midpoint/repo/sql/SqlRepositoryServiceImpl.java | 9 +++++---- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/ModifyObjectResult.java b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/ModifyObjectResult.java index ec3cf8a413d..62bf4933c3c 100644 --- a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/ModifyObjectResult.java +++ b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/ModifyObjectResult.java @@ -31,7 +31,7 @@ public class ModifyObjectResult { private final PrismObject objectBefore; private final PrismObject objectAfter; - private final Collection modifications; + private final Collection> modifications; /** * Performance record for the current operation. @@ -39,12 +39,12 @@ public class ModifyObjectResult { */ private OperationRecord performanceRecord; - public ModifyObjectResult(Collection modifications) { + public ModifyObjectResult(Collection> modifications) { this(null, null, modifications); } public ModifyObjectResult(PrismObject objectBefore, PrismObject objectAfter, - Collection modifications) { + Collection> modifications) { this.objectBefore = objectBefore; this.objectAfter = objectAfter; this.modifications = modifications; @@ -58,7 +58,7 @@ public PrismObject getObjectAfter() { return objectAfter; } - public Collection getModifications() { + public Collection> getModifications() { return modifications; } diff --git a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/RepositoryService.java b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/RepositoryService.java index eb10f5447de..d72a0f7c693 100644 --- a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/RepositoryService.java +++ b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/RepositoryService.java @@ -112,6 +112,7 @@ public interface RepositoryService { String CLASS_NAME_WITH_DOT = RepositoryService.class.getName() + "."; String OP_ADD_OBJECT = "addObject"; + String OP_ADD_OBJECT_OVERWRITE = "addObjectOverwrite"; // addObject with overwrite option String OP_DELETE_OBJECT = "deleteObject"; String OP_COUNT_OBJECTS = "countObjects"; String OP_MODIFY_OBJECT = "modifyObject"; diff --git a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/perf/OperationPerformanceInformation.java b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/perf/OperationPerformanceInformation.java index 0b7d400b35b..42ed76b0309 100644 --- a/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/perf/OperationPerformanceInformation.java +++ b/repo/repo-api/src/main/java/com/evolveum/midpoint/repo/api/perf/OperationPerformanceInformation.java @@ -1,25 +1,24 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ - package com.evolveum.midpoint.repo.api.perf; +import java.util.Locale; + import com.evolveum.midpoint.util.ShortDumpable; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.xml.ns._public.common.common_3.RepositoryOperationPerformanceInformationType; -import java.util.Locale; - /** - * Experimental. + * Experimental. */ public class OperationPerformanceInformation implements ShortDumpable, Cloneable { private int invocationCount; - private int executionCount; + private int executionCount; // counts each attempt, including retries private long totalTime; private Long minTime; private Long maxTime; @@ -105,7 +104,7 @@ public synchronized void shortDump(StringBuilder sb) { sb.append(invocationCount); sb.append(", total time: ").append(timeInfo(totalTime, minTime, maxTime, invocationCount)); if (totalTime > 0 && executionCount > invocationCount) { - sb.append(String.format(Locale.US, ", wasted time for %d retry/retries: %s (%s)", executionCount-invocationCount, + sb.append(String.format(Locale.US, ", wasted time for %d retry/retries: %s (%s)", executionCount - invocationCount, timeInfo(totalWastedTime, minWastedTime, maxWastedTime, invocationCount), percent(totalWastedTime, totalTime))); } } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java index d9adef62eb4..bf2a46f8032 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlRepositoryServiceImpl.java @@ -19,9 +19,6 @@ import javax.annotation.PreDestroy; import javax.xml.namespace.QName; -import com.evolveum.midpoint.repo.sqlbase.OperationLogger; -import com.evolveum.midpoint.util.annotation.Experimental; - import org.apache.commons.lang3.Validate; import org.hibernate.Session; import org.hibernate.SessionFactory; @@ -44,6 +41,7 @@ import com.evolveum.midpoint.repo.api.query.ObjectFilterExpressionEvaluator; import com.evolveum.midpoint.repo.sql.helpers.*; import com.evolveum.midpoint.repo.sqlbase.ConflictWatcherImpl; +import com.evolveum.midpoint.repo.sqlbase.OperationLogger; import com.evolveum.midpoint.repo.sqlbase.perfmon.SqlPerformanceMonitorImpl; import com.evolveum.midpoint.schema.*; import com.evolveum.midpoint.schema.constants.SchemaConstants; @@ -56,6 +54,7 @@ import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.PrettyPrinter; +import com.evolveum.midpoint.util.annotation.Experimental; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; @@ -410,7 +409,9 @@ public String addObject( .build(); SqlPerformanceMonitorImpl pm = getPerformanceMonitor(); - long opHandle = pm.registerOperationStart(OP_ADD_OBJECT, object.getCompileTimeClass()); + long opHandle = pm.registerOperationStart( + options.isOverwrite() ? OP_ADD_OBJECT_OVERWRITE : OP_ADD_OBJECT, + object.getCompileTimeClass()); int attempt = 1; int restarts = 0; try { From ed889fc101d9d5940fca4a9b5b758975add3169c Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Tue, 18 May 2021 15:34:19 +0200 Subject: [PATCH 19/43] repo-sqale: columns renamed to match M-class attributes (no _) --- repo/repo-sqale/README.adoc | 4 +- repo/repo-sqale/sql/pg-org-experiments.sql | 44 +- repo/repo-sqale/sql/pgnew-eav-vs-json.sql | 54 +- repo/repo-sqale/sql/pgnew-experiments.sql | 28 +- repo/repo-sqale/sql/pgnew-repo.sql | 733 +++++++++--------- .../midpoint/repo/sqale/UriCache.java | 2 +- .../QAccessCertificationDefinition.java | 8 +- .../sqale/qmodel/assignment/QAssignment.java | 42 +- .../repo/sqale/qmodel/cases/QCase.java | 24 +- .../repo/sqale/qmodel/common/QContainer.java | 2 +- .../sqale/qmodel/connector/QConnector.java | 8 +- .../repo/sqale/qmodel/focus/QFocus.java | 4 +- .../sqale/qmodel/focus/QGenericObject.java | 2 +- .../repo/sqale/qmodel/focus/QUser.java | 32 +- .../qmodel/lookuptable/QLookupTableRow.java | 4 +- .../repo/sqale/qmodel/object/QObject.java | 28 +- .../sqale/qmodel/object/QObjectMapping.java | 2 +- .../qmodel/object/QOperationExecution.java | 12 +- .../repo/sqale/qmodel/object/QTrigger.java | 2 +- .../repo/sqale/qmodel/ref/QReference.java | 6 +- .../repo/sqale/qmodel/report/QReportData.java | 6 +- .../repo/sqale/qmodel/resource/QResource.java | 6 +- .../repo/sqale/qmodel/role/QAbstractRole.java | 4 +- .../repo/sqale/qmodel/shadow/QShadow.java | 8 +- .../repo/sqale/qmodel/task/QTask.java | 14 +- 25 files changed, 531 insertions(+), 548 deletions(-) diff --git a/repo/repo-sqale/README.adoc b/repo/repo-sqale/README.adoc index 18eb757f5e6..9d5cfba4da2 100644 --- a/repo/repo-sqale/README.adoc +++ b/repo/repo-sqale/README.adoc @@ -295,11 +295,11 @@ Insert performance measurements: [source,sql] ---- -INSERT INTO m_user (name_norm, name_orig, version) +INSERT INTO m_user (nameNorm, nameOrig, version) VALUES ('user-' || LPAD(r::text, 10, '0'), 'user-' || LPAD(r::text, 10, '0'), 1); ---- -Both name columns are indexed, `name_norm` is also unique. +Both name columns are indexed, `nameNorm` is also unique. Loop is used to INSERT the rows, which is slower than `INSERT from SELECT` with `generate_series`, but closer to real scenario that uses separate statements (although there are no round-trips here). diff --git a/repo/repo-sqale/sql/pg-org-experiments.sql b/repo/repo-sqale/sql/pg-org-experiments.sql index f5ea89f037e..251e120af42 100644 --- a/repo/repo-sqale/sql/pg-org-experiments.sql +++ b/repo/repo-sqale/sql/pg-org-experiments.sql @@ -46,15 +46,15 @@ with recursive org_h ( parent, child ) as ( - select r.targetoid, r.owner_oid from m_reference as r + select r.targetoid, r.ownerOid from m_reference as r where r.reference_type = 0 -- this condition makes it super fast, the same out of CTE is much slower - and r.owner_oid = 'u7:21200-0...-....-....-............' + and r.ownerOid = 'u7:21200-0...-....-....-............' union select par.targetoid, chi.child from m_reference as par, org_h as chi where par.reference_type = 0 - and par.owner_oid = chi.parent + and par.ownerOid = chi.parent ) select distinct parent from org_h -- select * from org_h @@ -72,12 +72,12 @@ set jit = off; -- generated by Querydsl EXPLAIN (ANALYZE, VERBOSE, BUFFERS) with recursive orgc (parent, child) as not materialized ( - (select refpo.targetOid, refpo.owner_oid from m_ref_object_parent_org refpo) + (select refpo.targetOid, refpo.ownerOid from m_ref_object_parent_org refpo) union - (select refpo.targetOid, orgc.child from m_ref_object_parent_org refpo, orgc orgc where refpo.owner_oid = orgc.parent) + (select refpo.targetOid, orgc.child from m_ref_object_parent_org refpo, orgc orgc where refpo.ownerOid = orgc.parent) ) -select po.name_orig, parent, child, - co.name_orig, +select po.nameOrig, parent, child, + co.nameOrig, * from orgc join m_object po on po.oid = parent join m_object co on co.oid = child @@ -93,7 +93,7 @@ select * from m_ref_object_parent_org; CREATE OR REPLACE FUNCTION m_org_clsr(ancestorOid UUID) RETURNS TABLE ( ancestor_oid UUID, -- ref.targetoid - descendant_oid UUID --ref.owner_oid + descendant_oid UUID --ref.ownerOid ) LANGUAGE plpgsql AS $$ @@ -117,40 +117,40 @@ order by oid; -- using function m_org_clsr(ancestorOid): 1000 ms for oids, 1400 ms for full rows -- using m_org_closure with rule: 23 s! no noticeable difference between oid/full select select oid from m_user u -where u.name_norm like '%45' +where u.nameNorm like '%45' and exists (select 1 from m_ref_object_parent_org pref join -- m_org_closure oc on pref.targetoid = oc.descendant_oid m_org_clsr('4e84dcec-eff6-4f1c-9bad-929e98dea3fa') oc on pref.targetoid = oc.descendant_oid - and pref.owner_oid = u.oid + and pref.ownerOid = u.oid -- where oc.ancestor_oid = '4e84dcec-eff6-4f1c-9bad-929e98dea3fa' ); -- using m_org_closure_internal directly 150 ms for just OIDs (600 ms full rows) select oid from m_user u -where u.name_norm like '%45' +where u.nameNorm like '%45' and exists (select 1 from m_ref_object_parent_org pref join m_org_closure_internal oc on pref.targetoid = oc.descendant_oid - and pref.owner_oid = u.oid + and pref.ownerOid = u.oid where oc.ancestor_oid = '4e84dcec-eff6-4f1c-9bad-929e98dea3fa' ); WITH RECURSIVE org_h ( ancestor_oid, -- ref.targetoid - descendant_oid --ref.owner_oid + descendant_oid --ref.ownerOid ) AS ( -- gather all organizations with parents - SELECT r.targetoid, r.owner_oid + SELECT r.targetoid, r.ownerOid FROM m_ref_object_parent_org r - WHERE r.owner_type = 'ORG' + WHERE r.ownerType = 'ORG' UNION -- generate their parents SELECT par.targetoid, chi.descendant_oid -- leaving original child there generates closure FROM m_ref_object_parent_org as par, org_h as chi - WHERE par.owner_oid = chi.ancestor_oid + WHERE par.ownerOid = chi.ancestor_oid ), pref as ( select pref.* from m_ref_object_parent_org pref @@ -159,24 +159,24 @@ pref as ( ) select oid from m_user u where - u.name_norm like '%45' and - exists (select 1 from pref where pref.owner_oid = u.oid) + u.nameNorm like '%45' and + exists (select 1 from pref where pref.ownerOid = u.oid) ; -- select count(*) from org_h; -- select * from org_h; ; select oid from m_user u -where u.name_norm like '%45' +where u.nameNorm like '%45' and exists (select 1 from m_ref_object_parent_org pref join org_h oc on pref.targetoid = oc.descendant_oid - and pref.owner_oid = u.oid + and pref.ownerOid = u.oid where oc.ancestor_oid = '4e84dcec-eff6-4f1c-9bad-929e98dea3fa' ); select * from m_ref_object_parent_org - where owner_oid = '0f9badc4-3fc2-4977-aa0a-f08c3576383d'; + where ownerOid = '0f9badc4-3fc2-4977-aa0a-f08c3576383d'; select * from m_org_closure oc where oc.descendant_oid = '6e732607-609f-46de-9747-1675868dc227'; @@ -206,7 +206,7 @@ select count(*) from m_user; -- trigger with mark: orgs/closure: 59711/291099 (after refresh ~1.9s), 2m13s, ~450 orgs/s select * from m_org o - where not exists (select 1 from m_ref_object_parent_org po where po.owner_oid = o.oid); + where not exists (select 1 from m_ref_object_parent_org po where po.ownerOid = o.oid); select * from m_org_closure; diff --git a/repo/repo-sqale/sql/pgnew-eav-vs-json.sql b/repo/repo-sqale/sql/pgnew-eav-vs-json.sql index 6579775e23b..4feee186568 100644 --- a/repo/repo-sqale/sql/pgnew-eav-vs-json.sql +++ b/repo/repo-sqale/sql/pgnew-eav-vs-json.sql @@ -43,12 +43,12 @@ create table teav ( ALTER TABLE teav ADD CONSTRAINT teav_name_key UNIQUE (name); create table teav_ext_string ( - owner_oid UUID NOT NULL references teav(oid), + ownerOid UUID NOT NULL references teav(oid), key VARCHAR(32) NOT NULL, value VARCHAR(255) NOT NULL, - -- this also covers the index on owner_oid FK - CONSTRAINT teav_ext_string_pk PRIMARY KEY (owner_oid, key, value) + -- this also covers the index on ownerOid FK + CONSTRAINT teav_ext_string_pk PRIMARY KEY (ownerOid, key, value) ); CREATE INDEX teav_ext_string_key_value_idx ON teav_ext_string (key, value); @@ -81,9 +81,9 @@ BEGIN -- EAV INSERT INTO teav (oid, name) VALUES (id, 'user-' || LPAD(r::text, 10, '0')); - INSERT INTO teav_ext_string (owner_oid, key, value) VALUES (id, 'eid', r); + INSERT INTO teav_ext_string (ownerOid, key, value) VALUES (id, 'eid', r); FOREACH v IN ARRAY hobbies LOOP - INSERT INTO teav_ext_string (owner_oid, key, value) + INSERT INTO teav_ext_string (ownerOid, key, value) VALUES (id, 'hobbies', v); END LOOP; ELSEIF r % 10 <= 1 THEN @@ -97,9 +97,9 @@ BEGIN ('{"eid": ' || r || ', "email": "user' || r || '@mycompany.com", "other-key-' || r || '": "other-value-' || r || '"}')::jsonb ); INSERT INTO teav (oid, name) VALUES (id, 'user-' || LPAD(r::text, 10, '0')); - INSERT INTO teav_ext_string (owner_oid, key, value) VALUES (id, 'email', 'user' || r || '@mycompany.com'); - INSERT INTO teav_ext_string (owner_oid, key, value) VALUES (id, 'eid', r); - INSERT INTO teav_ext_string (owner_oid, key, value) VALUES (id, 'other-key-' || r, 'other-value-' || r); + INSERT INTO teav_ext_string (ownerOid, key, value) VALUES (id, 'email', 'user' || r || '@mycompany.com'); + INSERT INTO teav_ext_string (ownerOid, key, value) VALUES (id, 'eid', r); + INSERT INTO teav_ext_string (ownerOid, key, value) VALUES (id, 'other-key-' || r, 'other-value-' || r); ELSE -- these values are used by many entries hobbies := random_pick(ARRAY['eating', 'books', 'music', 'dancing', 'walking', 'jokes', 'video', 'photo'], 0.4); @@ -111,9 +111,9 @@ BEGIN -- EAV INSERT INTO teav (oid, name) VALUES (id, 'user-' || LPAD(r::text, 10, '0')); - INSERT INTO teav_ext_string (owner_oid, key, value) VALUES (id, 'eid', r); + INSERT INTO teav_ext_string (ownerOid, key, value) VALUES (id, 'eid', r); FOREACH v IN ARRAY hobbies LOOP - INSERT INTO teav_ext_string (owner_oid, key, value) + INSERT INTO teav_ext_string (ownerOid, key, value) VALUES (id, 'hobbies', v); END LOOP; END IF; @@ -201,7 +201,7 @@ select * from tjson where UPPER(ext->>'email') LIKE 'USER2%' LIMIT 500; CREATE INDEX teav_ext_string_value_trgm_idx ON teav_ext_string USING gin(value gin_trgm_ops); CREATE INDEX teav_ext_string_value_email_trgm_idx ON teav_ext_string USING gin(value gin_trgm_ops) WHERE key='email'; EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'email' and es.value ILIKE 'USER2%') LIMIT 50; +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'email' and es.value ILIKE 'USER2%') LIMIT 50; select from teav_ext_string ex where es.key = 'email' and es.value ILIKE 'USER2%' limit 500; -- selects @@ -236,30 +236,30 @@ select * from tjson where ext @> '{"eid":5000}'; -- pgbench -r -P 5 -f - -t 5 << "--EOF" select count(*) from teav_ext_string; -- out for curiosity, not practical otherwise select count(*) from teav; -select count(*) from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'hobbies' and es.value = 'video'); -select count(*) from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'hobbies' and es.value = 'sleeping'); -select count(*) from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'email' and es.value LIKE 'user2%'); -select count(*) from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'email' and UPPER(es.value) LIKE 'USER2%'); +select count(*) from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'hobbies' and es.value = 'video'); +select count(*) from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'hobbies' and es.value = 'sleeping'); +select count(*) from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'email' and es.value LIKE 'user2%'); +select count(*) from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'email' and UPPER(es.value) LIKE 'USER2%'); --EOF -- selects -- pgbench -r -P 5 -f - -t 30 << "--EOF" select * from teav limit 500; -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'hobbies' and es.value = 'video') limit 500; -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'hobbies' and es.value = 'video') order by t.oid limit 500; -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'hobbies' and es.value = 'video') and t.oid>'fffe0000-0000-0000-0000-000000000000' order by t.oid limit 500; -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'hobbies' and es.value = 'sleeping') limit 500; -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'hobbies' and es.value = 'sleeping') order by t.oid limit 500; +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'hobbies' and es.value = 'video') limit 500; +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'hobbies' and es.value = 'video') order by t.oid limit 500; +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'hobbies' and es.value = 'video') and t.oid>'fffe0000-0000-0000-0000-000000000000' order by t.oid limit 500; +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'hobbies' and es.value = 'sleeping') limit 500; +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'hobbies' and es.value = 'sleeping') order by t.oid limit 500; --EOF -- pgbench -r -P 5 -f - -t 30 << "--EOF" -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'email' and es.value LIKE 'user2%') limit 500; -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'email' and es.value LIKE 'user2%') order by oid limit 500; -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'email' and es.value LIKE 'user2%') and oid>'fffe0000-0000-0000-0000-000000000000' order by oid limit 500; +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'email' and es.value LIKE 'user2%') limit 500; +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'email' and es.value LIKE 'user2%') order by oid limit 500; +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'email' and es.value LIKE 'user2%') and oid>'fffe0000-0000-0000-0000-000000000000' order by oid limit 500; -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'email' and UPPER(es.value) LIKE 'USER2%') limit 500; -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'email' and UPPER(es.value) LIKE 'USER2%') order by oid limit 500; -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'email' and UPPER(es.value) LIKE 'USER2%') and oid>'fffe0000-0000-0000-0000-000000000000' order by oid limit 500; +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'email' and UPPER(es.value) LIKE 'USER2%') limit 500; +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'email' and UPPER(es.value) LIKE 'USER2%') order by oid limit 500; +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'email' and UPPER(es.value) LIKE 'USER2%') and oid>'fffe0000-0000-0000-0000-000000000000' order by oid limit 500; -select * from teav t where exists (select from teav_ext_string es where es.owner_oid = t.oid and es.key = 'eid' and es.value = '5000'); +select * from teav t where exists (select from teav_ext_string es where es.ownerOid = t.oid and es.key = 'eid' and es.value = '5000'); --EOF diff --git a/repo/repo-sqale/sql/pgnew-experiments.sql b/repo/repo-sqale/sql/pgnew-experiments.sql index e89304b513c..35a2de16a03 100644 --- a/repo/repo-sqale/sql/pgnew-experiments.sql +++ b/repo/repo-sqale/sql/pgnew-experiments.sql @@ -14,20 +14,20 @@ select * from pg_available_extensions order by name; -- DB data initialization (after pgnew-repo.sql) -- one user with random name -INSERT INTO m_user (oid, name_norm, name_orig, version) +INSERT INTO m_user (oid, nameNorm, nameOrig, version) VALUES (gen_random_uuid(), md5(random()::TEXT), md5(random()::TEXT), 1); -INSERT INTO m_user (name_norm, name_orig, createtimestamp, modifytimestamp, version) +INSERT INTO m_user (nameNorm, nameOrig, createtimestamp, modifytimestamp, version) VALUES (md5(random()::TEXT), md5(random()::TEXT), current_timestamp, current_timestamp, 1); select * from m_resource; --- creates new row with generated UUID, repeated run must fail on unique name_norm -insert into m_resource (name_norm, name_orig, version) VALUES ('resource0', 'resource0', 1) RETURNING OID; --- should fail the second time because oid is PK of the table (even with changed name_norm) -insert into m_resource (oid, name_norm, name_orig, version) +-- creates new row with generated UUID, repeated run must fail on unique nameNorm +insert into m_resource (nameNorm, nameOrig, version) VALUES ('resource0', 'resource0', 1) RETURNING OID; +-- should fail the second time because oid is PK of the table (even with changed nameNorm) +insert into m_resource (oid, nameNorm, nameOrig, version) VALUES ('66eb4861-867d-4a41-b6f0-41a3874bd48f', 'resource1', 'resource1', 1); -- this should fail after previous due to cross-table m_object unique constraint -insert into m_user (oid, name_norm, name_orig, version) +insert into m_user (oid, nameNorm, nameOrig, version) VALUES ('66eb4861-867d-4a41-b6f0-41a3874bd48f', 'conflict', 'conflict', 1); -- must fail, update trigger does not allow OID changes update m_object set oid='66eb4861-867d-4a41-b6f0-41a3874bd48e' @@ -45,11 +45,11 @@ SELECT * from m_object_oid where oid not in (SELECT oid FROM m_object); delete from m_object where oid='66eb4861-867d-4a41-b6f0-41a3874bd48f'; -- switch Tx to manual in IDE to avoid autocommit START TRANSACTION; -insert into m_resource (oid, name_norm, name_orig, version) +insert into m_resource (oid, nameNorm, nameOrig, version) VALUES ('66eb4861-867d-4a41-b6f0-41a3874bd48f', 'resource1', 'resource1', 1); START TRANSACTION; - insert into m_user (oid, name_norm, name_orig, version) + insert into m_user (oid, nameNorm, nameOrig, version) VALUES ('66eb4861-867d-4a41-b6f0-41a3874bd48f', 'conflict', 'conflict', 1); commit; commit; @@ -60,7 +60,7 @@ select * from m_object where oid='66eb4861-867d-4a41-b6f0-41a3874bd48f'; -- Delete in two steps without trigger, much faster than normal. SET session_replication_role = replica; -- disables triggers for the current session -- HERE the delete you want, e.g.: -delete from m_user where name_norm > 'user-0001000000'; +delete from m_user where nameNorm > 'user-0001000000'; -- this is the cleanup of unused OIDs DELETE FROM m_object_oid oo WHERE NOT EXISTS (SELECT * from m_object o WHERE o.oid = oo.oid); @@ -79,14 +79,14 @@ select count(*) from m_user; -- vacuum full analyze; -- this requires exclusive lock on processed table and can be very slow, with 1M rows it takes 10s vacuum analyze; -- this is normal operation version (can run in parallel, ~25s/25m rows) -INSERT INTO m_resource (name_norm, name_orig, fullobject, version) +INSERT INTO m_resource (nameNorm, nameOrig, fullobject, version) SELECT 'resource-' || LPAD(r::text, 10, '0'), 'resource-' || LPAD(r::text, 10, '0'), random_bytea(100, 20000), 1 from generate_series(1, 10) as r; -INSERT INTO m_user (name_norm, name_orig, fullobject, ext, policySituations, version) +INSERT INTO m_user (nameNorm, nameOrig, fullobject, ext, policySituations, version) SELECT 'user-' || LPAD(r::text, 10, '0'), 'user-' || LPAD(r::text, 10, '0'), random_bytea(100, 2000), @@ -120,7 +120,7 @@ from generate_series(100001,1000000) as r; EXPLAIN (ANALYZE, VERBOSE, BUFFERS) select oid, policysituations from m_user where policysituations @> '{10}' --- order by name_norm desc +-- order by nameNorm desc ; /* 1k rows @@ -244,7 +244,7 @@ CREATE INDEX m_user_ext_hired2_idx ON m_user ((ext ->> 'hired')) WHERE ext ? 'hi -- see also https://www.postgresql.org/docs/13/functions-json.html some stuff is only for JSONB EXPLAIN (ANALYZE, VERBOSE, BUFFERS) -- select count(*) -select oid, name_norm, ext +select oid, nameNorm, ext from m_user where -- ext?'hobbies' and -- faster, uses GIN index diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index b791ba8084d..7db06af35ca 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -10,6 +10,7 @@ -- FK foreign key, IDX for index, KEY for unique index. -- TR is suffix for triggers. -- Names are generally lowercase (despite prefix/suffixes above in uppercase ;-)). +-- Column names are Java style and match attribute names from M-classes (e.g. MObject). -- -- Other notes: -- TEXT is used instead of VARCHAR, see: https://dba.stackexchange.com/a/21496/157622 @@ -213,7 +214,7 @@ INSERT INTO m_uri (id, uri) -- 1) override OID like this (PK+FK): oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), -- 2) define object type class (change value): objectType ObjectType GENERATED ALWAYS AS ('XY') STORED, -- 3) add three triggers _oid_{insert|update|delete}_tr --- 4) add indexes for name_norm and name_orig columns (name_norm as unique) +-- 4) add indexes for nameOrig and nameNorm columns (nameNorm as unique) -- 5) the rest varies on the concrete table, other indexes or constraints, etc. -- 6) any required FK must be created on the concrete table, even for inherited columns @@ -222,14 +223,14 @@ CREATE TABLE m_object ( oid UUID NOT NULL, -- objectType will be overridden with GENERATED value in concrete table objectType ObjectType NOT NULL, - name_orig TEXT NOT NULL, - name_norm TEXT NOT NULL, + nameOrig TEXT NOT NULL, + nameNorm TEXT NOT NULL, fullObject BYTEA, - tenantRef_targetOid UUID, - tenantRef_targetType ObjectType, - tenantRef_relation_id INTEGER REFERENCES m_uri(id), + tenantRefTargetOid UUID, + tenantRefTargetType ObjectType, + tenantRefRelationId INTEGER REFERENCES m_uri(id), lifecycleState TEXT, -- TODO what is this? how many distinct values? - cid_seq BIGINT NOT NULL DEFAULT 1, -- sequence for container id, next free cid + cidSeq BIGINT NOT NULL DEFAULT 1, -- sequence for container id, next free cid version INTEGER NOT NULL DEFAULT 1, -- complex DB columns, add indexes as needed per concrete table, e.g. see m_user -- TODO compare with [] in JSONB, check performance, indexing, etc. first @@ -238,15 +239,15 @@ CREATE TABLE m_object ( textInfo TEXT[], -- TODO not mapped yet, see RObjectTextInfo#createItemsSet ext JSONB, -- metadata - creatorRef_targetOid UUID, - creatorRef_targetType ObjectType, - creatorRef_relation_id INTEGER REFERENCES m_uri(id), - createChannel_id INTEGER REFERENCES m_uri(id), + creatorRefTargetOid UUID, + creatorRefTargetType ObjectType, + creatorRefRelationId INTEGER REFERENCES m_uri(id), + createChannelId INTEGER REFERENCES m_uri(id), createTimestamp TIMESTAMPTZ, - modifierRef_targetOid UUID, - modifierRef_targetType ObjectType, - modifierRef_relation_id INTEGER REFERENCES m_uri(id), - modifyChannel_id INTEGER REFERENCES m_uri(id), + modifierRefTargetOid UUID, + modifierRefTargetType ObjectType, + modifierRefRelationId INTEGER REFERENCES m_uri(id), + modifyChannelId INTEGER REFERENCES m_uri(id), modifyTimestamp TIMESTAMPTZ, -- these are purely DB-managed metadata, not mapped to in midPoint @@ -263,9 +264,9 @@ CREATE TABLE m_object ( CREATE TABLE m_container ( -- Default OID value is covered by INSERT triggers. No PK defined on abstract tables. -- Owner does not have to be the direct parent of the container. - owner_oid UUID NOT NULL, + ownerOid UUID NOT NULL, -- use like this on the concrete table: - -- owner_oid UUID NOT NULL REFERENCES m_object_oid(oid), + -- ownerOid UUID NOT NULL REFERENCES m_object_oid(oid), -- Container ID, unique in the scope of the whole object (owner). -- While this provides it for sub-tables we will repeat this for clarity, it's part of PK. @@ -274,86 +275,86 @@ CREATE TABLE m_container ( containerType ContainerType NOT NULL, CHECK (FALSE) NO INHERIT - -- add on concrete table (additional columns possible): PRIMARY KEY (owner_oid, cid) + -- add on concrete table (additional columns possible): PRIMARY KEY (ownerOid, cid) ); -- Abstract reference table, for object but also other container references. CREATE TABLE m_reference ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - -- reference_type will be overridden with GENERATED value in concrete table - owner_type ObjectType NOT NULL, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + -- referenceType will be overridden with GENERATED value in concrete table + ownerType ObjectType NOT NULL, referenceType ReferenceType NOT NULL, targetOid UUID NOT NULL, -- soft-references m_object targetType ObjectType NOT NULL, - relation_id INTEGER NOT NULL REFERENCES m_uri(id), + relationId INTEGER NOT NULL REFERENCES m_uri(id), -- prevents inserts to this table, but not to inherited ones; this makes it "abstract" table CHECK (FALSE) NO INHERIT - -- add PK (referenceType is the same per table): PRIMARY KEY (owner_oid, relation_id, targetOid) + -- add PK (referenceType is the same per table): PRIMARY KEY (ownerOid, relationId, targetOid) ); -- Add this index for each sub-table (reference type is not necessary, each sub-table has just one). --- CREATE INDEX m_reference_targetOid_relation_id_idx ON m_reference (targetOid, relation_id); +-- CREATE INDEX m_referenceTargetOidRelationId_idx ON m_reference (targetOid, relationId); -- references related to ObjectType and AssignmentHolderType -- stores AssignmentHolderType/archetypeRef CREATE TABLE m_ref_archetype ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, referenceType ReferenceType GENERATED ALWAYS AS ('ARCHETYPE') STORED, - PRIMARY KEY (owner_oid, relation_id, targetOid) + PRIMARY KEY (ownerOid, relationId, targetOid) ) INHERITS (m_reference); -CREATE INDEX m_ref_archetype_targetOid_relation_id_idx - ON m_ref_archetype (targetOid, relation_id); +CREATE INDEX m_ref_archetypeTargetOidRelationId_idx + ON m_ref_archetype (targetOid, relationId); -- stores AssignmentHolderType/delegatedRef CREATE TABLE m_ref_delegated ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, referenceType ReferenceType GENERATED ALWAYS AS ('DELEGATED') STORED, - PRIMARY KEY (owner_oid, relation_id, targetOid) + PRIMARY KEY (ownerOid, relationId, targetOid) ) INHERITS (m_reference); -CREATE INDEX m_ref_delegated_targetOid_relation_id_idx - ON m_ref_delegated (targetOid, relation_id); +CREATE INDEX m_ref_delegatedTargetOidRelationId_idx + ON m_ref_delegated (targetOid, relationId); -- stores ObjectType/metadata/createApproverRef CREATE TABLE m_ref_object_create_approver ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, referenceType ReferenceType GENERATED ALWAYS AS ('OBJECT_CREATE_APPROVER') STORED, - PRIMARY KEY (owner_oid, relation_id, targetOid) + PRIMARY KEY (ownerOid, relationId, targetOid) ) INHERITS (m_reference); -CREATE INDEX m_ref_object_create_approver_targetOid_relation_id_idx - ON m_ref_object_create_approver (targetOid, relation_id); +CREATE INDEX m_ref_object_create_approverTargetOidRelationId_idx + ON m_ref_object_create_approver (targetOid, relationId); -- stores ObjectType/metadata/modifyApproverRef CREATE TABLE m_ref_object_modify_approver ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, referenceType ReferenceType GENERATED ALWAYS AS ('OBJECT_MODIFY_APPROVER') STORED, - PRIMARY KEY (owner_oid, relation_id, targetOid) + PRIMARY KEY (ownerOid, relationId, targetOid) ) INHERITS (m_reference); -CREATE INDEX m_ref_object_modify_approver_targetOid_relation_id_idx - ON m_ref_object_modify_approver (targetOid, relation_id); +CREATE INDEX m_ref_object_modify_approverTargetOidRelationId_idx + ON m_ref_object_modify_approver (targetOid, relationId); -- stores AssignmentHolderType/roleMembershipRef CREATE TABLE m_ref_role_membership ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, referenceType ReferenceType GENERATED ALWAYS AS ('ROLE_MEMBERSHIP') STORED, - PRIMARY KEY (owner_oid, relation_id, targetOid) + PRIMARY KEY (ownerOid, relationId, targetOid) ) INHERITS (m_reference); -CREATE INDEX m_ref_role_member_targetOid_relation_id_idx - ON m_ref_role_membership (targetOid, relation_id); +CREATE INDEX m_ref_role_memberTargetOidRelationId_idx + ON m_ref_role_membership (targetOid, relationId); -- endregion -- region FOCUS related tables @@ -366,8 +367,8 @@ CREATE TABLE m_focus ( emailAddress TEXT, photo BYTEA, -- will be TOAST-ed if necessary locale TEXT, - locality_orig TEXT, - locality_norm TEXT, + localityOrig TEXT, + localityNorm TEXT, preferredLanguage TEXT, telephoneNumber TEXT, timezone TEXT, @@ -393,33 +394,33 @@ CREATE TABLE m_focus ( -- stores FocusType/personaRef CREATE TABLE m_ref_persona ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, referenceType ReferenceType GENERATED ALWAYS AS ('PERSONA') STORED, - PRIMARY KEY (owner_oid, relation_id, targetOid) + PRIMARY KEY (ownerOid, relationId, targetOid) ) INHERITS (m_reference); -CREATE INDEX m_ref_persona_targetOid_relation_id_idx - ON m_ref_persona (targetOid, relation_id); +CREATE INDEX m_ref_personaTargetOidRelationId_idx + ON m_ref_persona (targetOid, relationId); -- stores FocusType/linkRef ("projection" is newer and better term) CREATE TABLE m_ref_projection ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, referenceType ReferenceType GENERATED ALWAYS AS ('PROJECTION') STORED, - PRIMARY KEY (owner_oid, relation_id, targetOid) + PRIMARY KEY (ownerOid, relationId, targetOid) ) INHERITS (m_reference); -CREATE INDEX m_ref_projection_targetOid_relation_id_idx - ON m_ref_projection (targetOid, relation_id); +CREATE INDEX m_ref_projectionTargetOidRelationId_idx + ON m_ref_projection (targetOid, relationId); -- Represents GenericObjectType, see https://docs.evolveum.com/midpoint/reference/schema/generic-objects/ CREATE TABLE m_generic_object ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('GENERIC_OBJECT') STORED, - genericObjectType_id INTEGER NOT NULL REFERENCES m_uri(id) -- GenericObjectType#objectType + genericObjectTypeId INTEGER NOT NULL REFERENCES m_uri(id) -- GenericObjectType#objectType ) INHERITS (m_focus); @@ -430,10 +431,10 @@ CREATE TRIGGER m_generic_object_update_tr BEFORE UPDATE ON m_generic_object CREATE TRIGGER m_generic_object_oid_delete_tr AFTER DELETE ON m_generic_object FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); --- TODO unique per genericObjectType_id? +-- TODO unique per genericObjectTypeId? -- No indexes for GenericObjectType#objectType were in old repo, what queries are expected? -CREATE INDEX m_generic_object_name_orig_idx ON m_generic_object (name_orig); -ALTER TABLE m_generic_object ADD CONSTRAINT m_generic_object_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_generic_object_nameOrig_idx ON m_generic_object (nameOrig); +ALTER TABLE m_generic_object ADD CONSTRAINT m_generic_object_nameNorm_key UNIQUE (nameNorm); -- endregion -- region USER related tables @@ -441,23 +442,23 @@ ALTER TABLE m_generic_object ADD CONSTRAINT m_generic_object_name_norm_key UNIQU CREATE TABLE m_user ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('USER') STORED, - additionalName_orig TEXT, - additionalName_norm TEXT, + additionalNameOrig TEXT, + additionalNameNorm TEXT, employeeNumber TEXT, - familyName_orig TEXT, - familyName_norm TEXT, - fullName_orig TEXT, - fullName_norm TEXT, - givenName_orig TEXT, - givenName_norm TEXT, - honorificPrefix_orig TEXT, - honorificPrefix_norm TEXT, - honorificSuffix_orig TEXT, - honorificSuffix_norm TEXT, - nickName_orig TEXT, - nickName_norm TEXT, - title_orig TEXT, - title_norm TEXT + familyNameOrig TEXT, + familyNameNorm TEXT, + fullNameOrig TEXT, + fullNameNorm TEXT, + givenNameOrig TEXT, + givenNameNorm TEXT, + honorificPrefixOrig TEXT, + honorificPrefixNorm TEXT, + honorificSuffixOrig TEXT, + honorificSuffixNorm TEXT, + nickNameOrig TEXT, + nickNameNorm TEXT, + titleOrig TEXT, + titleNorm TEXT ) INHERITS (m_focus); @@ -468,13 +469,13 @@ CREATE TRIGGER m_user_update_tr BEFORE UPDATE ON m_user CREATE TRIGGER m_user_oid_delete_tr AFTER DELETE ON m_user FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_user_name_orig_idx ON m_user (name_orig); -ALTER TABLE m_user ADD CONSTRAINT m_user_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_user_nameOrig_idx ON m_user (nameOrig); +ALTER TABLE m_user ADD CONSTRAINT m_user_nameNorm_key UNIQUE (nameNorm); CREATE INDEX m_user_policySituation_idx ON m_user USING GIN(policysituations gin__int_ops); CREATE INDEX m_user_ext_idx ON m_user USING gin (ext); -CREATE INDEX m_user_fullName_orig_idx ON m_user (fullName_orig); -CREATE INDEX m_user_familyName_orig_idx ON m_user (familyName_orig); -CREATE INDEX m_user_givenName_orig_idx ON m_user (givenName_orig); +CREATE INDEX m_user_fullNameOrig_idx ON m_user (fullNameOrig); +CREATE INDEX m_user_familyNameOrig_idx ON m_user (familyNameOrig); +CREATE INDEX m_user_givenNameOrig_idx ON m_user (givenNameOrig); CREATE INDEX m_user_employeeNumber_idx ON m_user (employeeNumber); /* TODO JSON of polystrings? @@ -497,8 +498,8 @@ CREATE TABLE m_abstract_role ( -- will be overridden with GENERATED value in concrete table objectType ObjectType NOT NULL, autoAssignEnabled BOOLEAN, - displayName_orig TEXT, - displayName_norm TEXT, + displayNameOrig TEXT, + displayNameNorm TEXT, identifier TEXT, requestable BOOLEAN, riskLevel TEXT, @@ -528,8 +529,8 @@ CREATE TRIGGER m_role_update_tr BEFORE UPDATE ON m_role CREATE TRIGGER m_role_oid_delete_tr AFTER DELETE ON m_role FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_role_name_orig_idx ON m_role (name_orig); -ALTER TABLE m_role ADD CONSTRAINT m_role_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_role_nameOrig_idx ON m_role (nameOrig); +ALTER TABLE m_role ADD CONSTRAINT m_role_nameNorm_key UNIQUE (nameNorm); -- Represents ServiceType, see https://wiki.evolveum.com/display/midPoint/Service+Account+Management CREATE TABLE m_service ( @@ -546,8 +547,8 @@ CREATE TRIGGER m_service_update_tr BEFORE UPDATE ON m_service CREATE TRIGGER m_service_oid_delete_tr AFTER DELETE ON m_service FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_service_name_orig_idx ON m_service (name_orig); -ALTER TABLE m_service ADD CONSTRAINT m_service_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_service_nameOrig_idx ON m_service (nameOrig); +ALTER TABLE m_service ADD CONSTRAINT m_service_nameNorm_key UNIQUE (nameNorm); -- Represents ArchetypeType, see https://wiki.evolveum.com/display/midPoint/Archetypes CREATE TABLE m_archetype ( @@ -563,8 +564,8 @@ CREATE TRIGGER m_archetype_update_tr BEFORE UPDATE ON m_archetype CREATE TRIGGER m_archetype_oid_delete_tr AFTER DELETE ON m_archetype FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_archetype_name_orig_idx ON m_archetype (name_orig); -ALTER TABLE m_archetype ADD CONSTRAINT m_archetype_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_archetype_nameOrig_idx ON m_archetype (nameOrig); +ALTER TABLE m_archetype ADD CONSTRAINT m_archetype_nameNorm_key UNIQUE (nameNorm); -- endregion -- region Organization hierarchy support @@ -584,23 +585,23 @@ CREATE TRIGGER m_org_update_tr BEFORE UPDATE ON m_org CREATE TRIGGER m_org_oid_delete_tr AFTER DELETE ON m_org FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_org_name_orig_idx ON m_org (name_orig); -ALTER TABLE m_org ADD CONSTRAINT m_org_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_org_nameOrig_idx ON m_org (nameOrig); +ALTER TABLE m_org ADD CONSTRAINT m_org_nameNorm_key UNIQUE (nameNorm); CREATE INDEX m_org_displayOrder_idx ON m_org (displayOrder); -- stores ObjectType/parentOrgRef CREATE TABLE m_ref_object_parent_org ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, referenceType ReferenceType GENERATED ALWAYS AS ('OBJECT_PARENT_ORG') STORED, - -- TODO wouldn't (owner_oid, targetOid, relation_id) perform better for typical queries? - PRIMARY KEY (owner_oid, relation_id, targetOid) + -- TODO wouldn't (ownerOid, targetOid, relationId) perform better for typical queries? + PRIMARY KEY (ownerOid, relationId, targetOid) ) INHERITS (m_reference); -- TODO is this enough? Is target+owner+relation needed too? -CREATE INDEX m_ref_object_parent_org_targetOid_relation_id_idx - ON m_ref_object_parent_org (targetOid, relation_id); +CREATE INDEX m_ref_object_parent_orgTargetOidRelationId_idx + ON m_ref_object_parent_org (targetOid, relationId); -- region org-closure /* @@ -615,7 +616,7 @@ of isParent/ChildOf searches and they never return parameter OID as a result. CREATE MATERIALIZED VIEW m_org_closure AS WITH RECURSIVE org_h ( ancestor_oid, -- ref.targetoid - descendant_oid --ref.owner_oid + descendant_oid --ref.ownerOid -- paths -- number of different paths, not used for materialized view version -- depth -- possible later, but cycle detected must be added to the recursive term ) AS ( @@ -625,13 +626,13 @@ WITH RECURSIVE org_h ( SELECT o.oid, o.oid FROM m_org o WHERE EXISTS( SELECT 1 FROM m_ref_object_parent_org r - WHERE r.targetOid = o.oid OR r.owner_oid = o.oid) + WHERE r.targetOid = o.oid OR r.ownerOid = o.oid) UNION -- recursive (iterative) term: -- Generate their parents (anc => desc, that is target => owner), => means "is parent of". SELECT par.targetoid, chi.descendant_oid -- leaving original child there generates closure FROM m_ref_object_parent_org as par, org_h as chi - WHERE par.owner_oid = chi.ancestor_oid + WHERE par.ownerOid = chi.ancestor_oid ) SELECT * FROM org_h; @@ -647,7 +648,7 @@ CREATE OR REPLACE FUNCTION mark_org_closure_for_refresh() LANGUAGE plpgsql AS $$ BEGIN - IF TG_OP = 'TRUNCATE' OR OLD.owner_type = 'ORG' OR NEW.owner_type = 'ORG' THEN + IF TG_OP = 'TRUNCATE' OR OLD.ownerType = 'ORG' OR NEW.ownerType = 'ORG' THEN INSERT INTO m_global_metadata VALUES ('orgClosureRefreshNeeded', 'true') ON CONFLICT (name) DO UPDATE SET value = 'true'; END IF; @@ -701,9 +702,9 @@ CREATE TABLE m_resource ( objectType ObjectType GENERATED ALWAYS AS ('RESOURCE') STORED, business_administrativeState ResourceAdministrativeStateType, operationalState_lastAvailabilityStatus AvailabilityStatusType, - connectorRef_targetOid UUID, - connectorRef_targetType ObjectType, - connectorRef_relation_id INTEGER REFERENCES m_uri(id) + connectorRefTargetOid UUID, + connectorRefTargetType ObjectType, + connectorRefRelationId INTEGER REFERENCES m_uri(id) ) INHERITS (m_object); @@ -714,31 +715,31 @@ CREATE TRIGGER m_resource_update_tr BEFORE UPDATE ON m_resource CREATE TRIGGER m_resource_oid_delete_tr AFTER DELETE ON m_resource FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_resource_name_orig_idx ON m_resource (name_orig); -ALTER TABLE m_resource ADD CONSTRAINT m_resource_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_resource_nameOrig_idx ON m_resource (nameOrig); +ALTER TABLE m_resource ADD CONSTRAINT m_resource_nameNorm_key UNIQUE (nameNorm); -- stores ResourceType/business/approverRef CREATE TABLE m_ref_resource_business_configuration_approver ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, referenceType ReferenceType GENERATED ALWAYS AS ('RESOURCE_BUSINESS_CONFIGURATION_APPROVER') STORED, - PRIMARY KEY (owner_oid, relation_id, targetOid) + PRIMARY KEY (ownerOid, relationId, targetOid) ) INHERITS (m_reference); -CREATE INDEX m_ref_resource_biz_config_approver_targetOid_relation_id_idx - ON m_ref_resource_business_configuration_approver (targetOid, relation_id); +CREATE INDEX m_ref_resource_biz_config_approverTargetOidRelationId_idx + ON m_ref_resource_business_configuration_approver (targetOid, relationId); -- Represents ShadowType, see https://wiki.evolveum.com/display/midPoint/Shadow+Objects -- and also https://docs.evolveum.com/midpoint/reference/schema/focus-and-projections/ CREATE TABLE m_shadow ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('SHADOW') STORED, - objectClass_id INTEGER REFERENCES m_uri(id), - resourceRef_targetOid UUID, - resourceRef_targetType ObjectType, - resourceRef_relation_id INTEGER REFERENCES m_uri(id), + objectClassId INTEGER REFERENCES m_uri(id), + resourceRefTargetOid UUID, + resourceRefTargetType ObjectType, + resourceRefRelationId INTEGER REFERENCES m_uri(id), intent TEXT, kind ShadowKindType, attemptNumber INTEGER, -- TODO how is this mapped? @@ -762,14 +763,14 @@ CREATE TRIGGER m_shadow_update_tr BEFORE UPDATE ON m_shadow CREATE TRIGGER m_shadow_oid_delete_tr AFTER DELETE ON m_shadow FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_shadow_name_orig_idx ON m_shadow (name_orig); -ALTER TABLE m_shadow ADD CONSTRAINT m_shadow_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_shadow_nameOrig_idx ON m_shadow (nameOrig); +ALTER TABLE m_shadow ADD CONSTRAINT m_shadow_nameNorm_key UNIQUE (nameNorm); CREATE INDEX m_shadow_policySituation_idx ON m_shadow USING GIN(policysituations gin__int_ops); CREATE INDEX m_shadow_ext_idx ON m_shadow USING gin (ext); /* TODO: reconsider, especially boolean things like dead (perhaps WHERE in other indexes?) Also consider partitioning by some of the attributes (class/kind/intent?) -CREATE INDEX iShadowResourceRef ON m_shadow (resourceRef_targetOid); +CREATE INDEX iShadowResourceRef ON m_shadow (resourceRefTargetOid); CREATE INDEX iShadowDead ON m_shadow (dead); CREATE INDEX iShadowKind ON m_shadow (kind); CREATE INDEX iShadowIntent ON m_shadow (intent); @@ -778,7 +779,7 @@ CREATE INDEX iShadowFailedOperationType ON m_shadow (failedOperationType); CREATE INDEX iShadowSyncSituation ON m_shadow (synchronizationSituation); CREATE INDEX iShadowPendingOperationCount ON m_shadow (pendingOperationCount); ALTER TABLE m_shadow ADD CONSTRAINT iPrimaryIdentifierValueWithOC - UNIQUE (primaryIdentifierValue, objectClass, resourceRef_targetOid); + UNIQUE (primaryIdentifierValue, objectClass, resourceRefTargetOid); */ -- Represents NodeType, see https://wiki.evolveum.com/display/midPoint/Managing+cluster+nodes @@ -796,8 +797,8 @@ CREATE TRIGGER m_node_update_tr BEFORE UPDATE ON m_node CREATE TRIGGER m_node_oid_delete_tr AFTER DELETE ON m_node FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_node_name_orig_idx ON m_node (name_orig); -ALTER TABLE m_node ADD CONSTRAINT m_node_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_node_nameOrig_idx ON m_node (nameOrig); +ALTER TABLE m_node ADD CONSTRAINT m_node_nameNorm_key UNIQUE (nameNorm); -- not interested in ext index for this one, this table will be small -- Represents SystemConfigurationType, see https://wiki.evolveum.com/display/midPoint/System+Configuration+Object @@ -846,8 +847,8 @@ CREATE TRIGGER m_object_collection_update_tr BEFORE UPDATE ON m_object_collectio CREATE TRIGGER m_object_collection_oid_delete_tr AFTER DELETE ON m_object_collection FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_object_collection_name_orig_idx ON m_object_collection (name_orig); -ALTER TABLE m_object_collection ADD CONSTRAINT m_object_collection_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_object_collection_nameOrig_idx ON m_object_collection (nameOrig); +ALTER TABLE m_object_collection ADD CONSTRAINT m_object_collection_nameNorm_key UNIQUE (nameNorm); -- Represents DashboardType, see https://wiki.evolveum.com/display/midPoint/Dashboard+configuration CREATE TABLE m_dashboard ( @@ -863,8 +864,8 @@ CREATE TRIGGER m_dashboard_update_tr BEFORE UPDATE ON m_dashboard CREATE TRIGGER m_dashboard_oid_delete_tr AFTER DELETE ON m_dashboard FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_dashboard_name_orig_idx ON m_dashboard (name_orig); -ALTER TABLE m_dashboard ADD CONSTRAINT m_dashboard_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_dashboard_nameOrig_idx ON m_dashboard (nameOrig); +ALTER TABLE m_dashboard ADD CONSTRAINT m_dashboard_nameNorm_key UNIQUE (nameNorm); -- Represents ValuePolicyType CREATE TABLE m_value_policy ( @@ -880,8 +881,8 @@ CREATE TRIGGER m_value_policy_update_tr BEFORE UPDATE ON m_value_policy CREATE TRIGGER m_value_policy_oid_delete_tr AFTER DELETE ON m_value_policy FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_value_policy_name_orig_idx ON m_value_policy (name_orig); -ALTER TABLE m_value_policy ADD CONSTRAINT m_value_policy_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_value_policy_nameOrig_idx ON m_value_policy (nameOrig); +ALTER TABLE m_value_policy ADD CONSTRAINT m_value_policy_nameNorm_key UNIQUE (nameNorm); -- Represents ReportType, see https://wiki.evolveum.com/display/midPoint/Report+Configuration CREATE TABLE m_report ( @@ -899,17 +900,17 @@ CREATE TRIGGER m_report_update_tr BEFORE UPDATE ON m_report CREATE TRIGGER m_report_oid_delete_tr AFTER DELETE ON m_report FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_report_name_orig_idx ON m_report (name_orig); -ALTER TABLE m_report ADD CONSTRAINT m_report_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_report_nameOrig_idx ON m_report (nameOrig); +ALTER TABLE m_report ADD CONSTRAINT m_report_nameNorm_key UNIQUE (nameNorm); -- TODO old repo had index on parent (boolean), does it make sense? if so, which value is sparse? -- Represents ReportDataType, see also m_report above CREATE TABLE m_report_data ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('REPORT_DATA') STORED, - reportRef_targetOid UUID, - reportRef_targetType ObjectType, - reportRef_relation_id INTEGER REFERENCES m_uri(id) + reportRefTargetOid UUID, + reportRefTargetType ObjectType, + reportRefRelationId INTEGER REFERENCES m_uri(id) ) INHERITS (m_object); @@ -920,8 +921,8 @@ CREATE TRIGGER m_report_data_update_tr BEFORE UPDATE ON m_report_data CREATE TRIGGER m_report_data_oid_delete_tr AFTER DELETE ON m_report_data FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_report_data_name_orig_idx ON m_report_data (name_orig); -ALTER TABLE m_report_data ADD CONSTRAINT m_report_data_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_report_data_nameOrig_idx ON m_report_data (nameOrig); +ALTER TABLE m_report_data ADD CONSTRAINT m_report_data_nameNorm_key UNIQUE (nameNorm); -- Represents LookupTableType, see https://wiki.evolveum.com/display/midPoint/Lookup+Tables CREATE TABLE m_lookup_table ( @@ -937,25 +938,25 @@ CREATE TRIGGER m_lookup_table_update_tr BEFORE UPDATE ON m_lookup_table CREATE TRIGGER m_lookup_table_oid_delete_tr AFTER DELETE ON m_lookup_table FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_lookup_table_name_orig_idx ON m_lookup_table (name_orig); -ALTER TABLE m_lookup_table ADD CONSTRAINT m_lookup_table_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_lookup_table_nameOrig_idx ON m_lookup_table (nameOrig); +ALTER TABLE m_lookup_table ADD CONSTRAINT m_lookup_table_nameNorm_key UNIQUE (nameNorm); -- Represents LookupTableRowType, see also m_lookup_table above CREATE TABLE m_lookup_table_row ( - owner_oid UUID NOT NULL REFERENCES m_lookup_table(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_lookup_table(oid) ON DELETE CASCADE, containerType ContainerType GENERATED ALWAYS AS ('LOOKUP_TABLE_ROW') STORED, key TEXT, value TEXT, - label_orig TEXT, - label_norm TEXT, + labelOrig TEXT, + labelNorm TEXT, lastChangeTimestamp TIMESTAMPTZ, - PRIMARY KEY (owner_oid, cid) + PRIMARY KEY (ownerOid, cid) ) INHERITS(m_container); ALTER TABLE m_lookup_table_row - ADD CONSTRAINT m_lookup_table_row_owner_oid_key_key UNIQUE (owner_oid, key); + ADD CONSTRAINT m_lookup_table_row_ownerOid_key_key UNIQUE (ownerOid, key); -- Represents ConnectorType, see https://wiki.evolveum.com/display/midPoint/Identity+Connectors CREATE TABLE m_connector ( @@ -964,10 +965,10 @@ CREATE TABLE m_connector ( connectorBundle TEXT, -- typically a package name connectorType TEXT, -- typically a class name connectorVersion TEXT, - framework_id INTEGER REFERENCES m_uri(id), - connectorHostRef_targetOid UUID, - connectorHostRef_targetType ObjectType, - connectorHostRef_relation_id INTEGER REFERENCES m_uri(id), + frameworkId INTEGER REFERENCES m_uri(id), + connectorHostRefTargetOid UUID, + connectorHostRefTargetType ObjectType, + connectorHostRefRelationId INTEGER REFERENCES m_uri(id), targetSystemTypes TEXT[] -- TODO any strings? cached URIs? ) INHERITS (m_object); @@ -979,8 +980,8 @@ CREATE TRIGGER m_connector_update_tr BEFORE UPDATE ON m_connector CREATE TRIGGER m_connector_oid_delete_tr AFTER DELETE ON m_connector FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_connector_name_orig_idx ON m_connector (name_orig); -ALTER TABLE m_connector ADD CONSTRAINT m_connector_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_connector_nameOrig_idx ON m_connector (nameOrig); +ALTER TABLE m_connector ADD CONSTRAINT m_connector_nameNorm_key UNIQUE (nameNorm); -- TODO array/json in m_connector table -- CREATE TABLE m_connector_target_system ( @@ -1006,8 +1007,8 @@ CREATE TRIGGER m_connector_host_update_tr BEFORE UPDATE ON m_connector_host CREATE TRIGGER m_connector_host_oid_delete_tr AFTER DELETE ON m_connector_host FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_connector_host_name_orig_idx ON m_connector_host (name_orig); -ALTER TABLE m_connector_host ADD CONSTRAINT m_connector_host_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_connector_host_nameOrig_idx ON m_connector_host (nameOrig); +ALTER TABLE m_connector_host ADD CONSTRAINT m_connector_host_nameNorm_key UNIQUE (nameNorm); -- Represents persistent TaskType, see https://wiki.evolveum.com/display/midPoint/Task+Manager CREATE TABLE m_task ( @@ -1019,16 +1020,16 @@ CREATE TABLE m_task ( completionTimestamp TIMESTAMPTZ, executionStatus TaskExecutionStateType, fullResult BYTEA, - handlerUri_id INTEGER REFERENCES m_uri(id), + handlerUriId INTEGER REFERENCES m_uri(id), lastRunStartTimestamp TIMESTAMPTZ, lastRunFinishTimestamp TIMESTAMPTZ, - node TEXT, -- node_id only for information purposes - objectRef_targetOid UUID, - objectRef_targetType ObjectType, - objectRef_relation_id INTEGER REFERENCES m_uri(id), - ownerRef_targetOid UUID, - ownerRef_targetType ObjectType, - ownerRef_relation_id INTEGER REFERENCES m_uri(id), + node TEXT, -- nodeId only for information purposes + objectRefTargetOid UUID, + objectRefTargetType ObjectType, + objectRefRelationId INTEGER REFERENCES m_uri(id), + ownerRefTargetOid UUID, + ownerRefTargetType ObjectType, + ownerRefRelationId INTEGER REFERENCES m_uri(id), parent TEXT, -- value of taskIdentifier recurrence TaskRecurrenceType, resultStatus OperationResultStatusType, @@ -1045,10 +1046,10 @@ CREATE TRIGGER m_task_update_tr BEFORE UPDATE ON m_task CREATE TRIGGER m_task_oid_delete_tr AFTER DELETE ON m_task FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_task_name_orig_idx ON m_task (name_orig); -ALTER TABLE m_task ADD CONSTRAINT m_task_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_task_nameOrig_idx ON m_task (nameOrig); +ALTER TABLE m_task ADD CONSTRAINT m_task_nameNorm_key UNIQUE (nameNorm); CREATE INDEX m_task_parent_idx ON m_task (parent); -CREATE INDEX m_task_objectRef_targetOid_idx ON m_task(objectRef_targetOid); +CREATE INDEX m_task_objectRefTargetOid_idx ON m_task(objectRefTargetOid); ALTER TABLE m_task ADD CONSTRAINT m_task_taskIdentifier_key UNIQUE (taskIdentifier); CREATE INDEX m_task_dependentTaskIdentifiers_idx ON m_task USING GIN(dependentTaskIdentifiers); -- endregion @@ -1060,18 +1061,18 @@ CREATE TABLE m_case ( objectType ObjectType GENERATED ALWAYS AS ('CASE') STORED, state TEXT, closeTimestamp TIMESTAMPTZ, - objectRef_targetOid UUID, - objectRef_targetType ObjectType, - objectRef_relation_id INTEGER REFERENCES m_uri(id), - parentRef_targetOid UUID, - parentRef_targetType ObjectType, - parentRef_relation_id INTEGER REFERENCES m_uri(id), - requestorRef_targetOid UUID, - requestorRef_targetType ObjectType, - requestorRef_relation_id INTEGER REFERENCES m_uri(id), - targetRef_targetOid UUID, - targetRef_targetType ObjectType, - targetRef_relation_id INTEGER REFERENCES m_uri(id) + objectRefTargetOid UUID, + objectRefTargetType ObjectType, + objectRefRelationId INTEGER REFERENCES m_uri(id), + parentRefTargetOid UUID, + parentRefTargetType ObjectType, + parentRefRelationId INTEGER REFERENCES m_uri(id), + requestorRefTargetOid UUID, + requestorRefTargetType ObjectType, + requestorRefRelationId INTEGER REFERENCES m_uri(id), + targetRefTargetOid UUID, + targetRefTargetType ObjectType, + targetRefRelationId INTEGER REFERENCES m_uri(id) ) INHERITS (m_object); @@ -1082,48 +1083,48 @@ CREATE TRIGGER m_case_update_tr BEFORE UPDATE ON m_case CREATE TRIGGER m_case_oid_delete_tr AFTER DELETE ON m_case FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_case_name_orig_idx ON m_case (name_orig); -ALTER TABLE m_case ADD CONSTRAINT m_case_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_case_nameOrig_idx ON m_case (nameOrig); +ALTER TABLE m_case ADD CONSTRAINT m_case_nameNorm_key UNIQUE (nameNorm); /* -CREATE INDEX iCaseTypeObjectRefTargetOid ON m_case(objectRef_targetOid); -CREATE INDEX iCaseTypeTargetRefTargetOid ON m_case(targetRef_targetOid); -CREATE INDEX iCaseTypeParentRefTargetOid ON m_case(parentRef_targetOid); -CREATE INDEX iCaseTypeRequestorRefTargetOid ON m_case(requestorRef_targetOid); +CREATE INDEX iCaseTypeObjectRefTargetOid ON m_case(objectRefTargetOid); +CREATE INDEX iCaseTypeTargetRefTargetOid ON m_case(targetRefTargetOid); +CREATE INDEX iCaseTypeParentRefTargetOid ON m_case(parentRefTargetOid); +CREATE INDEX iCaseTypeRequestorRefTargetOid ON m_case(requestorRefTargetOid); CREATE INDEX iCaseTypeCloseTimestamp ON m_case(closeTimestamp); CREATE TABLE m_case_wi ( id INTEGER NOT NULL, - owner_oid UUID NOT NULL, + ownerOid UUID NOT NULL, closeTimestamp TIMESTAMPTZ, createTimestamp TIMESTAMPTZ, deadline TIMESTAMPTZ, originalAssigneeRef_relation VARCHAR(157), - originalAssigneeRef_targetOid UUID, - originalAssigneeRef_targetType INTEGER, + originalAssigneeRefTargetOid UUID, + originalAssigneeRefTargetType INTEGER, outcome TEXT, performerRef_relation VARCHAR(157), - performerRef_targetOid UUID, - performerRef_targetType INTEGER, + performerRefTargetOid UUID, + performerRefTargetType INTEGER, stageNumber INTEGER, - PRIMARY KEY (owner_oid, id) + PRIMARY KEY (ownerOid, id) ); ALTER TABLE m_case_wi - ADD CONSTRAINT fk_case_wi_owner FOREIGN KEY (owner_oid) REFERENCES m_case; + ADD CONSTRAINT fk_case_wi_owner FOREIGN KEY (ownerOid) REFERENCES m_case; CREATE TABLE m_case_wi_reference ( owner_id INTEGER NOT NULL, - owner_owner_oid UUID NOT NULL, + owner_ownerOid UUID NOT NULL, reference_type INTEGER NOT NULL, relation VARCHAR(157) NOT NULL, targetOid UUID NOT NULL, targetType INTEGER, - PRIMARY KEY (owner_owner_oid, owner_id, reference_type, targetOid, relation) + PRIMARY KEY (owner_ownerOid, owner_id, reference_type, targetOid, relation) ); ALTER TABLE m_case_wi_reference - ADD CONSTRAINT fk_case_wi_reference_owner FOREIGN KEY (owner_owner_oid, owner_id) REFERENCES m_case_wi; + ADD CONSTRAINT fk_case_wi_reference_owner FOREIGN KEY (owner_ownerOid, owner_id) REFERENCES m_case_wi; CREATE INDEX iCaseWorkItemRefTargetOid ON m_case_wi_reference (targetOid); */ -- endregion @@ -1133,12 +1134,12 @@ CREATE INDEX iCaseWorkItemRefTargetOid ON m_case_wi_reference (targetOid); CREATE TABLE m_access_cert_definition ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_DEFINITION') STORED, - handlerUri_id INTEGER REFERENCES m_uri(id), + handlerUriId INTEGER REFERENCES m_uri(id), lastCampaignStartedTimestamp TIMESTAMPTZ, lastCampaignClosedTimestamp TIMESTAMPTZ, - ownerRef_targetOid UUID, - ownerRef_targetType ObjectType, - ownerRef_relation_id INTEGER REFERENCES m_uri(id) + ownerRefTargetOid UUID, + ownerRefTargetType ObjectType, + ownerRefRelationId INTEGER REFERENCES m_uri(id) ) INHERITS (m_object); @@ -1149,24 +1150,24 @@ CREATE TRIGGER m_access_cert_definition_update_tr BEFORE UPDATE ON m_access_cert CREATE TRIGGER m_access_cert_definition_oid_delete_tr AFTER DELETE ON m_access_cert_definition FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_access_cert_definition_name_orig_idx ON m_access_cert_definition (name_orig); +CREATE INDEX m_access_cert_definition_nameOrig_idx ON m_access_cert_definition (nameOrig); ALTER TABLE m_access_cert_definition - ADD CONSTRAINT m_access_cert_definition_name_norm_key UNIQUE (name_norm); + ADD CONSTRAINT m_access_cert_definition_nameNorm_key UNIQUE (nameNorm); CREATE INDEX m_access_cert_definition_ext_idx ON m_access_cert_definition USING gin (ext); -- TODO not mapped yet CREATE TABLE m_access_cert_campaign ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_CAMPAIGN') STORED, - definitionRef_targetOid UUID, - definitionRef_targetType ObjectType, - definitionRef_relation_id INTEGER REFERENCES m_uri(id), + definitionRefTargetOid UUID, + definitionRefTargetType ObjectType, + definitionRefRelationId INTEGER REFERENCES m_uri(id), endTimestamp TIMESTAMPTZ, - handlerUri_id INTEGER REFERENCES m_uri(id), + handlerUriId INTEGER REFERENCES m_uri(id), iteration INTEGER NOT NULL, - ownerRef_targetOid UUID, - ownerRef_targetType ObjectType, - ownerRef_relation_id INTEGER REFERENCES m_uri(id), + ownerRefTargetOid UUID, + ownerRefTargetType ObjectType, + ownerRefRelationId INTEGER REFERENCES m_uri(id), stageNumber INTEGER, startTimestamp TIMESTAMPTZ, state INTEGER @@ -1180,13 +1181,13 @@ CREATE TRIGGER m_access_cert_campaign_update_tr BEFORE UPDATE ON m_access_cert_c CREATE TRIGGER m_access_cert_campaign_oid_delete_tr AFTER DELETE ON m_access_cert_campaign FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_access_cert_campaign_name_orig_idx ON m_access_cert_campaign (name_orig); +CREATE INDEX m_access_cert_campaign_nameOrig_idx ON m_access_cert_campaign (nameOrig); ALTER TABLE m_access_cert_campaign - ADD CONSTRAINT m_access_cert_campaign_name_norm_key UNIQUE (name_norm); + ADD CONSTRAINT m_access_cert_campaign_nameNorm_key UNIQUE (nameNorm); CREATE INDEX m_access_cert_campaign_ext_idx ON m_access_cert_campaign USING gin (ext); CREATE TABLE m_access_cert_case ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_CASE') STORED, administrativeStatus INTEGER, archiveTimestamp TIMESTAMPTZ, @@ -1201,77 +1202,77 @@ CREATE TABLE m_access_cert_case ( currentStageOutcome TEXT, fullObject BYTEA, iteration INTEGER NOT NULL, - objectRef_targetOid UUID, - objectRef_targetType ObjectType, - objectRef_relation_id INTEGER REFERENCES m_uri(id), - orgRef_targetOid UUID, - orgRef_targetType ObjectType, - orgRef_relation_id INTEGER REFERENCES m_uri(id), + objectRefTargetOid UUID, + objectRefTargetType ObjectType, + objectRefRelationId INTEGER REFERENCES m_uri(id), + orgRefTargetOid UUID, + orgRefTargetType ObjectType, + orgRefRelationId INTEGER REFERENCES m_uri(id), outcome TEXT, remediedTimestamp TIMESTAMPTZ, reviewDeadline TIMESTAMPTZ, reviewRequestedTimestamp TIMESTAMPTZ, stageNumber INTEGER, - targetRef_targetOid UUID, - targetRef_targetType ObjectType, - targetRef_relation_id INTEGER REFERENCES m_uri(id), - tenantRef_targetOid UUID, - tenantRef_targetType ObjectType, - tenantRef_relation_id INTEGER REFERENCES m_uri(id), - - PRIMARY KEY (owner_oid, cid) + targetRefTargetOid UUID, + targetRefTargetType ObjectType, + targetRefRelationId INTEGER REFERENCES m_uri(id), + tenantRefTargetOid UUID, + tenantRefTargetType ObjectType, + tenantRefRelationId INTEGER REFERENCES m_uri(id), + + PRIMARY KEY (ownerOid, cid) ) INHERITS(m_container); CREATE TABLE m_access_cert_wi ( - owner_oid UUID NOT NULL, -- PK+FK + ownerOid UUID NOT NULL, -- PK+FK acc_cert_case_cid INTEGER NOT NULL, -- PK+FK containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_WORK_ITEM') STORED, closeTimestamp TIMESTAMPTZ, iteration INTEGER NOT NULL, outcome TEXT, outputChangeTimestamp TIMESTAMPTZ, - performerRef_targetOid UUID, - performerRef_targetType ObjectType, - performerRef_relation_id INTEGER REFERENCES m_uri(id), + performerRefTargetOid UUID, + performerRefTargetType ObjectType, + performerRefRelationId INTEGER REFERENCES m_uri(id), stageNumber INTEGER, - PRIMARY KEY (owner_oid, acc_cert_case_cid, cid) + PRIMARY KEY (ownerOid, acc_cert_case_cid, cid) ) INHERITS(m_container); ALTER TABLE m_access_cert_wi - ADD CONSTRAINT m_access_cert_wi_id_fk FOREIGN KEY (owner_oid, acc_cert_case_cid) - REFERENCES m_access_cert_case (owner_oid, cid) + ADD CONSTRAINT m_access_cert_wi_id_fk FOREIGN KEY (ownerOid, acc_cert_case_cid) + REFERENCES m_access_cert_case (ownerOid, cid) ON DELETE CASCADE; -- TODO rework to inherit from reference tables CREATE TABLE m_access_cert_wi_reference ( - owner_oid UUID NOT NULL, -- PK+FK + ownerOid UUID NOT NULL, -- PK+FK acc_cert_case_cid INTEGER NOT NULL, -- PK+FK acc_cert_wi_cid INTEGER NOT NULL, -- PK+FK targetOid UUID NOT NULL, -- more PK columns... targetType ObjectType, - relation_id INTEGER NOT NULL REFERENCES m_uri(id), + relationId INTEGER NOT NULL REFERENCES m_uri(id), -- TODO is the order of last two components optimal for index/query? - PRIMARY KEY (owner_oid, acc_cert_case_cid, acc_cert_wi_cid, relation_id, targetOid) + PRIMARY KEY (ownerOid, acc_cert_case_cid, acc_cert_wi_cid, relationId, targetOid) ); ALTER TABLE m_access_cert_wi_reference ADD CONSTRAINT m_access_cert_wi_reference_id_fk - FOREIGN KEY (owner_oid, acc_cert_case_cid, acc_cert_wi_cid) - REFERENCES m_access_cert_wi (owner_oid, acc_cert_case_cid, cid) + FOREIGN KEY (ownerOid, acc_cert_case_cid, acc_cert_wi_cid) + REFERENCES m_access_cert_wi (ownerOid, acc_cert_case_cid, cid) ON DELETE CASCADE; /* -CREATE INDEX iCertCampaignNameOrig ON m_access_cert_campaign (name_orig); -ALTER TABLE m_access_cert_campaign ADD CONSTRAINT uc_access_cert_campaign_name UNIQUE (name_norm); -CREATE INDEX iCaseObjectRefTargetOid ON m_access_cert_case (objectRef_targetOid); -CREATE INDEX iCaseTargetRefTargetOid ON m_access_cert_case (targetRef_targetOid); -CREATE INDEX iCaseTenantRefTargetOid ON m_access_cert_case (tenantRef_targetOid); -CREATE INDEX iCaseOrgRefTargetOid ON m_access_cert_case (orgRef_targetOid); -CREATE INDEX iCertDefinitionNameOrig ON m_access_cert_definition (name_orig); -ALTER TABLE m_access_cert_definition ADD CONSTRAINT uc_access_cert_definition_name UNIQUE (name_norm); +CREATE INDEX iCertCampaignNameOrig ON m_access_cert_campaign (nameOrig); +ALTER TABLE m_access_cert_campaign ADD CONSTRAINT uc_access_cert_campaign_name UNIQUE (nameNorm); +CREATE INDEX iCaseObjectRefTargetOid ON m_access_cert_case (objectRefTargetOid); +CREATE INDEX iCaseTargetRefTargetOid ON m_access_cert_case (targetRefTargetOid); +CREATE INDEX iCaseTenantRefTargetOid ON m_access_cert_case (tenantRefTargetOid); +CREATE INDEX iCaseOrgRefTargetOid ON m_access_cert_case (orgRefTargetOid); +CREATE INDEX iCertDefinitionNameOrig ON m_access_cert_definition (nameOrig); +ALTER TABLE m_access_cert_definition ADD CONSTRAINT uc_access_cert_definition_name UNIQUE (nameNorm); CREATE INDEX iCertWorkItemRefTargetOid ON m_access_cert_wi_reference (targetOid); */ -- endregion @@ -1290,20 +1291,20 @@ CREATE TRIGGER m_object_template_update_tr BEFORE UPDATE ON m_object_template CREATE TRIGGER m_object_template_oid_delete_tr AFTER DELETE ON m_object_template FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_object_template_name_orig_idx ON m_object_template (name_orig); -ALTER TABLE m_object_template ADD CONSTRAINT m_object_template_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_object_template_nameOrig_idx ON m_object_template (nameOrig); +ALTER TABLE m_object_template ADD CONSTRAINT m_object_template_nameNorm_key UNIQUE (nameNorm); -- stores ObjectTemplateType/includeRef CREATE TABLE m_ref_include ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, referenceType ReferenceType GENERATED ALWAYS AS ('INCLUDE') STORED, - PRIMARY KEY (owner_oid, relation_id, targetOid) + PRIMARY KEY (ownerOid, relationId, targetOid) ) INHERITS (m_reference); -CREATE INDEX m_ref_include_targetOid_relation_id_idx - ON m_ref_include (targetOid, relation_id); +CREATE INDEX m_ref_includeTargetOidRelationId_idx + ON m_ref_include (targetOid, relationId); -- endregion -- region FunctionLibrary/Sequence/Form tables @@ -1321,8 +1322,8 @@ CREATE TRIGGER m_function_library_update_tr BEFORE UPDATE ON m_function_library CREATE TRIGGER m_function_library_oid_delete_tr AFTER DELETE ON m_function_library FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_function_library_name_orig_idx ON m_function_library (name_orig); -ALTER TABLE m_function_library ADD CONSTRAINT m_function_library_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_function_library_nameOrig_idx ON m_function_library (nameOrig); +ALTER TABLE m_function_library ADD CONSTRAINT m_function_library_nameNorm_key UNIQUE (nameNorm); -- Represents SequenceType, see https://wiki.evolveum.com/display/midPoint/Sequences CREATE TABLE m_sequence ( @@ -1338,8 +1339,8 @@ CREATE TRIGGER m_sequence_update_tr BEFORE UPDATE ON m_sequence CREATE TRIGGER m_sequence_oid_delete_tr AFTER DELETE ON m_sequence FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_sequence_name_orig_idx ON m_sequence (name_orig); -ALTER TABLE m_sequence ADD CONSTRAINT m_sequence_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_sequence_nameOrig_idx ON m_sequence (nameOrig); +ALTER TABLE m_sequence ADD CONSTRAINT m_sequence_nameNorm_key UNIQUE (nameNorm); -- Represents FormType, see https://wiki.evolveum.com/display/midPoint/Custom+forms CREATE TABLE m_form ( @@ -1355,8 +1356,8 @@ CREATE TRIGGER m_form_update_tr BEFORE UPDATE ON m_form CREATE TRIGGER m_form_oid_delete_tr AFTER DELETE ON m_form FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); -CREATE INDEX m_form_name_orig_idx ON m_form (name_orig); -ALTER TABLE m_form ADD CONSTRAINT m_form_name_norm_key UNIQUE (name_norm); +CREATE INDEX m_form_nameOrig_idx ON m_form (nameOrig); +ALTER TABLE m_form ADD CONSTRAINT m_form_nameNorm_key UNIQUE (nameNorm); -- endregion -- region Assignment/Inducement table @@ -1367,30 +1368,30 @@ ALTER TABLE m_form ADD CONSTRAINT m_form_name_norm_key UNIQUE (name_norm); --1 45 (inducements) --0 48756229 CREATE TABLE m_assignment ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, -- this is different from other containers, this is not generated, app must provide it containerType ContainerType NOT NULL CHECK (containerType IN ('ASSIGNMENT', 'INDUCEMENT')), - owner_type ObjectType NOT NULL, + ownerType ObjectType NOT NULL, lifecycleState TEXT, orderValue INTEGER, - orgRef_targetOid UUID, - orgRef_targetType ObjectType, - orgRef_relation_id INTEGER REFERENCES m_uri(id), - targetRef_targetOid UUID, - targetRef_targetType ObjectType, - targetRef_relation_id INTEGER REFERENCES m_uri(id), - tenantRef_targetOid UUID, - tenantRef_targetType ObjectType, - tenantRef_relation_id INTEGER REFERENCES m_uri(id), + orgRefTargetOid UUID, + orgRefTargetType ObjectType, + orgRefRelationId INTEGER REFERENCES m_uri(id), + targetRefTargetOid UUID, + targetRefTargetType ObjectType, + targetRefRelationId INTEGER REFERENCES m_uri(id), + tenantRefTargetOid UUID, + tenantRefTargetType ObjectType, + tenantRefRelationId INTEGER REFERENCES m_uri(id), -- TODO what is this? see RAssignment.getExtension (both extId/Oid) extId INTEGER, extOid TEXT, -- is this UUID too? policySituations INTEGER[], -- soft-references m_uri, add index per table ext JSONB, -- construction - resourceRef_targetOid UUID, - resourceRef_targetType ObjectType, - resourceRef_relation_id INTEGER REFERENCES m_uri(id), + resourceRefTargetOid UUID, + resourceRefTargetType ObjectType, + resourceRefRelationId INTEGER REFERENCES m_uri(id), -- activation administrativeStatus ActivationStatusType, effectiveStatus ActivationStatusType, @@ -1403,18 +1404,18 @@ CREATE TABLE m_assignment ( validityChangeTimestamp TIMESTAMPTZ, archiveTimestamp TIMESTAMPTZ, -- metadata - creatorRef_targetOid UUID, - creatorRef_targetType ObjectType, - creatorRef_relation_id INTEGER REFERENCES m_uri(id), - createChannel_id INTEGER, + creatorRefTargetOid UUID, + creatorRefTargetType ObjectType, + creatorRefRelationId INTEGER REFERENCES m_uri(id), + createChannelId INTEGER, createTimestamp TIMESTAMPTZ, - modifierRef_targetOid UUID, - modifierRef_targetType ObjectType, - modifierRef_relation_id INTEGER REFERENCES m_uri(id), - modifyChannel_id INTEGER, + modifierRefTargetOid UUID, + modifierRefTargetType ObjectType, + modifierRefRelationId INTEGER REFERENCES m_uri(id), + modifyChannelId INTEGER, modifyTimestamp TIMESTAMPTZ, - PRIMARY KEY (owner_oid, cid) + PRIMARY KEY (ownerOid, cid) ) INHERITS(m_container); @@ -1426,51 +1427,51 @@ CREATE INDEX m_assignment_ext_idx ON m_assignment USING gin (ext); -- TODO the same: CREATE INDEX iAssignmentEffective ON m_assignment (effectiveStatus); CREATE INDEX m_assignment_validFrom_idx ON m_assignment (validFrom); CREATE INDEX m_assignment_validTo_idx ON m_assignment (validTo); -CREATE INDEX m_assignment_targetRef_targetOid_idx ON m_assignment (targetRef_targetOid); -CREATE INDEX m_assignment_tenantRef_targetOid_idx ON m_assignment (tenantRef_targetOid); -CREATE INDEX m_assignment_orgRef_targetOid_idx ON m_assignment (orgRef_targetOid); -CREATE INDEX m_assignment_resourceRef_targetOid_idx ON m_assignment (resourceRef_targetOid); +CREATE INDEX m_assignment_targetRefTargetOid_idx ON m_assignment (targetRefTargetOid); +CREATE INDEX m_assignment_tenantRefTargetOid_idx ON m_assignment (tenantRefTargetOid); +CREATE INDEX m_assignment_orgRefTargetOid_idx ON m_assignment (orgRefTargetOid); +CREATE INDEX m_assignment_resourceRefTargetOid_idx ON m_assignment (resourceRefTargetOid); -- stores assignment/metadata/createApproverRef CREATE TABLE m_assignment_ref_create_approver ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, assignment_cid INTEGER NOT NULL, referenceType ReferenceType GENERATED ALWAYS AS ('ASSIGNMENT_CREATE_APPROVER') STORED, - PRIMARY KEY (owner_oid, assignment_cid, referenceType, relation_id, targetOid) + PRIMARY KEY (ownerOid, assignment_cid, referenceType, relationId, targetOid) ) INHERITS (m_reference); ALTER TABLE m_assignment_ref_create_approver ADD CONSTRAINT m_assignment_ref_create_approver_id_fk - FOREIGN KEY (owner_oid, assignment_cid) REFERENCES m_assignment (owner_oid, cid); + FOREIGN KEY (ownerOid, assignment_cid) REFERENCES m_assignment (ownerOid, cid); --- TODO index targetOid, relation_id? +-- TODO index targetOid, relationId? -- stores assignment/metadata/modifyApproverRef CREATE TABLE m_assignment_ref_modify_approver ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, assignment_cid INTEGER NOT NULL, referenceType ReferenceType GENERATED ALWAYS AS ('ASSIGNMENT_MODIFY_APPROVER') STORED, - PRIMARY KEY (owner_oid, assignment_cid, referenceType, relation_id, targetOid) + PRIMARY KEY (ownerOid, assignment_cid, referenceType, relationId, targetOid) ) INHERITS (m_reference); ALTER TABLE m_assignment_ref_modify_approver ADD CONSTRAINT m_assignment_ref_modify_approver_id_fk - FOREIGN KEY (owner_oid, assignment_cid) REFERENCES m_assignment (owner_oid, cid); + FOREIGN KEY (ownerOid, assignment_cid) REFERENCES m_assignment (ownerOid, cid); --- TODO index targetOid, relation_id? +-- TODO index targetOid, relationId? -- endregion -- region Other object containers -- stores ObjectType/trigger (TriggerType) CREATE TABLE m_trigger ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, containerType ContainerType GENERATED ALWAYS AS ('TRIGGER') STORED, - handlerUri_id INTEGER REFERENCES m_uri(id), + handlerUriId INTEGER REFERENCES m_uri(id), timestampValue TIMESTAMPTZ, - PRIMARY KEY (owner_oid, cid) + PRIMARY KEY (ownerOid, cid) ) INHERITS(m_container); @@ -1478,28 +1479,28 @@ CREATE INDEX m_trigger_timestampValue_idx ON m_trigger (timestampValue); -- stores ObjectType/operationExecution (OperationExecutionType) CREATE TABLE m_operation_execution ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, containerType ContainerType GENERATED ALWAYS AS ('OPERATION_EXECUTION') STORED, status OperationResultStatusType, recordType OperationExecutionRecordTypeType, - initiatorRef_targetOid UUID, - initiatorRef_targetType ObjectType, - initiatorRef_relation_id INTEGER REFERENCES m_uri(id), - taskRef_targetOid UUID, - taskRef_targetType ObjectType, - taskRef_relation_id INTEGER REFERENCES m_uri(id), + initiatorRefTargetOid UUID, + initiatorRefTargetType ObjectType, + initiatorRefRelationId INTEGER REFERENCES m_uri(id), + taskRefTargetOid UUID, + taskRefTargetType ObjectType, + taskRefRelationId INTEGER REFERENCES m_uri(id), timestampValue TIMESTAMPTZ, - PRIMARY KEY (owner_oid, cid) + PRIMARY KEY (ownerOid, cid) ) INHERITS(m_container); -CREATE INDEX m_operation_execution_initiatorRef_targetOid_idx - ON m_operation_execution (initiatorRef_targetOid); -CREATE INDEX m_operation_execution_taskRef_targetOid_idx - ON m_operation_execution (taskRef_targetOid); +CREATE INDEX m_operation_execution_initiatorRefTargetOid_idx + ON m_operation_execution (initiatorRefTargetOid); +CREATE INDEX m_operation_execution_taskRefTargetOid_idx + ON m_operation_execution (taskRefTargetOid); CREATE INDEX m_operation_execution_timestampValue_idx ON m_operation_execution (timestampValue); --- TODO: index for owner_oid is part of PK +-- TODO: index for ownerOid is part of PK -- index for status is questionable, don't we want WHERE status = ... to another index instead? -- endregion @@ -1520,43 +1521,43 @@ CREATE TABLE m_ext_item ( /* -- EXPERIMENTAL EAV (first without catalog, so string keys are used) CREATE TABLE m_object_ext_boolean ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid), + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid), ext_item_id VARCHAR(32) NOT NULL, value BOOLEAN NOT NULL, - PRIMARY KEY (owner_oid, ext_item_id, value) + PRIMARY KEY (ownerOid, ext_item_id, value) ); CREATE TABLE m_object_ext_date ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid), + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid), ext_item_id VARCHAR(32) NOT NULL, value TIMESTAMPTZ NOT NULL, - PRIMARY KEY (owner_oid, ext_item_id, value) + PRIMARY KEY (ownerOid, ext_item_id, value) ); CREATE TABLE m_object_ext_long ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid), + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid), ext_item_id VARCHAR(32) NOT NULL, value INTEGER NOT NULL, - PRIMARY KEY (owner_oid, ext_item_id, value) + PRIMARY KEY (ownerOid, ext_item_id, value) ); CREATE TABLE m_object_ext_poly ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid), + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid), ext_item_id VARCHAR(32) NOT NULL, orig TEXT NOT NULL, norm TEXT, - PRIMARY KEY (owner_oid, ext_item_id, orig) + PRIMARY KEY (ownerOid, ext_item_id, orig) ); CREATE TABLE m_object_ext_reference ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid), + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid), ext_item_id VARCHAR(32) NOT NULL, target_oid UUID NOT NULL, - relation_id INTEGER, + relationId INTEGER, targetType INTEGER, - PRIMARY KEY (owner_oid, ext_item_id, target_oid) + PRIMARY KEY (ownerOid, ext_item_id, target_oid) ); CREATE TABLE m_object_ext_string ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid), + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid), ext_item_id VARCHAR(32) NOT NULL, value TEXT NOT NULL, - PRIMARY KEY (owner_oid, ext_item_id, value) + PRIMARY KEY (ownerOid, ext_item_id, value) ); @@ -1569,52 +1570,52 @@ CREATE TABLE m_object_ext_string ( CREATE TABLE m_assignment_ext_boolean ( item_id INTEGER NOT NULL, anyContainer_owner_id INTEGER NOT NULL, - anyContainer_owner_owner_oid UUID NOT NULL, + anyContainer_owner_ownerOid UUID NOT NULL, booleanValue BOOLEAN NOT NULL, - PRIMARY KEY (anyContainer_owner_owner_oid, anyContainer_owner_id, item_id, booleanValue) + PRIMARY KEY (anyContainer_owner_ownerOid, anyContainer_owner_id, item_id, booleanValue) ); CREATE TABLE m_assignment_ext_date ( item_id INTEGER NOT NULL, anyContainer_owner_id INTEGER NOT NULL, - anyContainer_owner_owner_oid UUID NOT NULL, + anyContainer_owner_ownerOid UUID NOT NULL, dateValue TIMESTAMPTZ NOT NULL, - PRIMARY KEY (anyContainer_owner_owner_oid, anyContainer_owner_id, item_id, dateValue) + PRIMARY KEY (anyContainer_owner_ownerOid, anyContainer_owner_id, item_id, dateValue) ); CREATE TABLE m_assignment_ext_long ( item_id INTEGER NOT NULL, anyContainer_owner_id INTEGER NOT NULL, - anyContainer_owner_owner_oid UUID NOT NULL, + anyContainer_owner_ownerOid UUID NOT NULL, longValue BIGINT NOT NULL, - PRIMARY KEY (anyContainer_owner_owner_oid, anyContainer_owner_id, item_id, longValue) + PRIMARY KEY (anyContainer_owner_ownerOid, anyContainer_owner_id, item_id, longValue) ); CREATE TABLE m_assignment_ext_poly ( item_id INTEGER NOT NULL, anyContainer_owner_id INTEGER NOT NULL, - anyContainer_owner_owner_oid UUID NOT NULL, + anyContainer_owner_ownerOid UUID NOT NULL, orig TEXT NOT NULL, norm TEXT, - PRIMARY KEY (anyContainer_owner_owner_oid, anyContainer_owner_id, item_id, orig) + PRIMARY KEY (anyContainer_owner_ownerOid, anyContainer_owner_id, item_id, orig) ); CREATE TABLE m_assignment_ext_reference ( item_id INTEGER NOT NULL, anyContainer_owner_id INTEGER NOT NULL, - anyContainer_owner_owner_oid UUID NOT NULL, + anyContainer_owner_ownerOid UUID NOT NULL, targetoid UUID NOT NULL, relation VARCHAR(157), targetType INTEGER, - PRIMARY KEY (anyContainer_owner_owner_oid, anyContainer_owner_id, item_id, targetoid) + PRIMARY KEY (anyContainer_owner_ownerOid, anyContainer_owner_id, item_id, targetoid) ); CREATE TABLE m_assignment_ext_string ( item_id INTEGER NOT NULL, anyContainer_owner_id INTEGER NOT NULL, - anyContainer_owner_owner_oid UUID NOT NULL, + anyContainer_owner_ownerOid UUID NOT NULL, stringValue TEXT NOT NULL, - PRIMARY KEY (anyContainer_owner_owner_oid, anyContainer_owner_id, item_id, stringValue) + PRIMARY KEY (anyContainer_owner_ownerOid, anyContainer_owner_id, item_id, stringValue) ); CREATE TABLE m_assignment_extension ( owner_id INTEGER NOT NULL, - owner_owner_oid UUID NOT NULL, - PRIMARY KEY (owner_owner_oid, owner_id) + owner_ownerOid UUID NOT NULL, + PRIMARY KEY (owner_ownerOid, owner_id) ); @@ -1626,10 +1627,10 @@ CREATE TABLE m_audit_delta ( deltaOid UUID, deltaType INTEGER, fullResult BYTEA, - objectName_norm TEXT, - objectName_orig TEXT, - resourceName_norm TEXT, - resourceName_orig TEXT, + objectNameNorm TEXT, + objectNameOrig TEXT, + resourceNameNorm TEXT, + resourceNameOrig TEXT, resourceOid UUID, status INTEGER, PRIMARY KEY (record_id, checksum) @@ -1682,8 +1683,8 @@ CREATE TABLE m_audit_ref_value ( name TEXT, oid UUID, record_id BIGINT, - targetName_norm TEXT, - targetName_orig TEXT, + targetNameNorm TEXT, + targetNameOrig TEXT, type TEXT, PRIMARY KEY (id) ); @@ -1701,48 +1702,48 @@ CREATE TABLE m_ext_item ( ); CREATE TABLE m_object_ext_boolean ( item_id INTEGER NOT NULL, - owner_oid UUID NOT NULL, + ownerOid UUID NOT NULL, ownerType INTEGER NOT NULL, booleanValue BOOLEAN NOT NULL, - PRIMARY KEY (owner_oid, ownerType, item_id, booleanValue) + PRIMARY KEY (ownerOid, ownerType, item_id, booleanValue) ); CREATE TABLE m_object_ext_date ( item_id INTEGER NOT NULL, - owner_oid UUID NOT NULL, + ownerOid UUID NOT NULL, ownerType INTEGER NOT NULL, dateValue TIMESTAMPTZ NOT NULL, - PRIMARY KEY (owner_oid, ownerType, item_id, dateValue) + PRIMARY KEY (ownerOid, ownerType, item_id, dateValue) ); CREATE TABLE m_object_ext_long ( item_id INTEGER NOT NULL, - owner_oid UUID NOT NULL, + ownerOid UUID NOT NULL, ownerType INTEGER NOT NULL, longValue BIGINT NOT NULL, - PRIMARY KEY (owner_oid, ownerType, item_id, longValue) + PRIMARY KEY (ownerOid, ownerType, item_id, longValue) ); CREATE TABLE m_object_ext_poly ( item_id INTEGER NOT NULL, - owner_oid UUID NOT NULL, + ownerOid UUID NOT NULL, ownerType INTEGER NOT NULL, orig TEXT NOT NULL, norm TEXT, - PRIMARY KEY (owner_oid, ownerType, item_id, orig) + PRIMARY KEY (ownerOid, ownerType, item_id, orig) ); CREATE TABLE m_object_ext_reference ( item_id INTEGER NOT NULL, - owner_oid UUID NOT NULL, + ownerOid UUID NOT NULL, ownerType INTEGER NOT NULL, targetoid UUID NOT NULL, relation VARCHAR(157), targetType INTEGER, - PRIMARY KEY (owner_oid, ownerType, item_id, targetoid) + PRIMARY KEY (ownerOid, ownerType, item_id, targetoid) ); CREATE TABLE m_object_ext_string ( item_id INTEGER NOT NULL, - owner_oid UUID NOT NULL, + ownerOid UUID NOT NULL, ownerType INTEGER NOT NULL, stringValue TEXT NOT NULL, - PRIMARY KEY (owner_oid, ownerType, item_id, stringValue) + PRIMARY KEY (ownerOid, ownerType, item_id, stringValue) ); CREATE INDEX iAExtensionBoolean @@ -1780,10 +1781,6 @@ CREATE INDEX iAuditResourceOidRecordId ALTER TABLE m_ext_item ADD CONSTRAINT iExtItemDefinition UNIQUE (itemName, itemType, kind); -CREATE INDEX iObjectNameOrig - ON m_object (name_orig); -CREATE INDEX iObjectNameNorm - ON m_object (name_norm); CREATE INDEX iObjectCreateTimestamp ON m_object (createTimestamp); CREATE INDEX iObjectLifecycleState @@ -1800,42 +1797,28 @@ CREATE INDEX iExtensionReference ON m_object_ext_reference (targetoid); CREATE INDEX iExtensionString ON m_object_ext_string (stringValue); -CREATE INDEX iArchetypeNameOrig ON m_archetype(name_orig); -CREATE INDEX iArchetypeNameNorm ON m_archetype(name_norm); CREATE INDEX iFocusAdministrative ON m_focus (administrativeStatus); CREATE INDEX iFocusEffective ON m_focus (effectiveStatus); CREATE INDEX iLocality - ON m_focus (locality_orig); + ON m_focus (localityOrig); CREATE INDEX iFocusValidFrom ON m_focus (validFrom); CREATE INDEX iFocusValidTo ON m_focus (validTo); -CREATE INDEX iFunctionLibraryNameOrig - ON m_function_library (name_orig); -ALTER TABLE m_function_library - ADD CONSTRAINT uc_function_library_name UNIQUE (name_norm); -CREATE INDEX iObjectTemplateNameOrig - ON m_object_template (name_orig); -ALTER TABLE m_object_template - ADD CONSTRAINT uc_object_template_name UNIQUE (name_norm); -CREATE INDEX iSystemConfigurationNameOrig - ON m_system_configuration (name_orig); -ALTER TABLE m_system_configuration - ADD CONSTRAINT uc_system_configuration_name UNIQUE (name_norm); ALTER TABLE m_assignment_ext_boolean - ADD CONSTRAINT fk_a_ext_boolean_owner FOREIGN KEY (anyContainer_owner_owner_oid, anyContainer_owner_id) REFERENCES m_assignment_extension; + ADD CONSTRAINT fk_a_ext_boolean_owner FOREIGN KEY (anyContainer_owner_ownerOid, anyContainer_owner_id) REFERENCES m_assignment_extension; ALTER TABLE m_assignment_ext_date - ADD CONSTRAINT fk_a_ext_date_owner FOREIGN KEY (anyContainer_owner_owner_oid, anyContainer_owner_id) REFERENCES m_assignment_extension; + ADD CONSTRAINT fk_a_ext_date_owner FOREIGN KEY (anyContainer_owner_ownerOid, anyContainer_owner_id) REFERENCES m_assignment_extension; ALTER TABLE m_assignment_ext_long - ADD CONSTRAINT fk_a_ext_long_owner FOREIGN KEY (anyContainer_owner_owner_oid, anyContainer_owner_id) REFERENCES m_assignment_extension; + ADD CONSTRAINT fk_a_ext_long_owner FOREIGN KEY (anyContainer_owner_ownerOid, anyContainer_owner_id) REFERENCES m_assignment_extension; ALTER TABLE m_assignment_ext_poly - ADD CONSTRAINT fk_a_ext_poly_owner FOREIGN KEY (anyContainer_owner_owner_oid, anyContainer_owner_id) REFERENCES m_assignment_extension; + ADD CONSTRAINT fk_a_ext_poly_owner FOREIGN KEY (anyContainer_owner_ownerOid, anyContainer_owner_id) REFERENCES m_assignment_extension; ALTER TABLE m_assignment_ext_reference - ADD CONSTRAINT fk_a_ext_reference_owner FOREIGN KEY (anyContainer_owner_owner_oid, anyContainer_owner_id) REFERENCES m_assignment_extension; + ADD CONSTRAINT fk_a_ext_reference_owner FOREIGN KEY (anyContainer_owner_ownerOid, anyContainer_owner_id) REFERENCES m_assignment_extension; ALTER TABLE m_assignment_ext_string - ADD CONSTRAINT fk_a_ext_string_owner FOREIGN KEY (anyContainer_owner_owner_oid, anyContainer_owner_id) REFERENCES m_assignment_extension; + ADD CONSTRAINT fk_a_ext_string_owner FOREIGN KEY (anyContainer_owner_ownerOid, anyContainer_owner_id) REFERENCES m_assignment_extension; -- These are created manually ALTER TABLE m_assignment_ext_boolean @@ -1862,19 +1845,19 @@ ALTER TABLE m_audit_ref_value ALTER TABLE m_audit_resource ADD CONSTRAINT fk_audit_resource FOREIGN KEY (record_id) REFERENCES m_audit_event; ALTER TABLE m_focus_photo - ADD CONSTRAINT fk_focus_photo FOREIGN KEY (owner_oid) REFERENCES m_focus; + ADD CONSTRAINT fk_focus_photo FOREIGN KEY (ownerOid) REFERENCES m_focus; ALTER TABLE m_object_ext_boolean - ADD CONSTRAINT fk_o_ext_boolean_owner FOREIGN KEY (owner_oid) REFERENCES m_object; + ADD CONSTRAINT fk_o_ext_boolean_owner FOREIGN KEY (ownerOid) REFERENCES m_object; ALTER TABLE m_object_ext_date - ADD CONSTRAINT fk_o_ext_date_owner FOREIGN KEY (owner_oid) REFERENCES m_object; + ADD CONSTRAINT fk_o_ext_date_owner FOREIGN KEY (ownerOid) REFERENCES m_object; ALTER TABLE m_object_ext_long - ADD CONSTRAINT fk_object_ext_long FOREIGN KEY (owner_oid) REFERENCES m_object; + ADD CONSTRAINT fk_object_ext_long FOREIGN KEY (ownerOid) REFERENCES m_object; ALTER TABLE m_object_ext_poly - ADD CONSTRAINT fk_o_ext_poly_owner FOREIGN KEY (owner_oid) REFERENCES m_object; + ADD CONSTRAINT fk_o_ext_poly_owner FOREIGN KEY (ownerOid) REFERENCES m_object; ALTER TABLE m_object_ext_reference - ADD CONSTRAINT fk_o_ext_reference_owner FOREIGN KEY (owner_oid) REFERENCES m_object; + ADD CONSTRAINT fk_o_ext_reference_owner FOREIGN KEY (ownerOid) REFERENCES m_object; ALTER TABLE m_object_ext_string - ADD CONSTRAINT fk_object_ext_string FOREIGN KEY (owner_oid) REFERENCES m_object; + ADD CONSTRAINT fk_object_ext_string FOREIGN KEY (ownerOid) REFERENCES m_object; -- These are created manually ALTER TABLE m_object_ext_boolean @@ -1891,7 +1874,7 @@ ALTER TABLE m_object_ext_string ADD CONSTRAINT fk_o_ext_string_item FOREIGN KEY (item_id) REFERENCES m_ext_item; ALTER TABLE m_object_text_info - ADD CONSTRAINT fk_object_text_info_owner FOREIGN KEY (owner_oid) REFERENCES m_object; + ADD CONSTRAINT fk_object_text_info_owner FOREIGN KEY (ownerOid) REFERENCES m_object; ALTER TABLE m_user_organization ADD CONSTRAINT fk_user_organization FOREIGN KEY (user_oid) REFERENCES m_user; ALTER TABLE m_user_organizational_unit diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/UriCache.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/UriCache.java index 26dde7ce749..d03c55b94e6 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/UriCache.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/UriCache.java @@ -54,7 +54,7 @@ public class UriCache { * Unknown id placeholder, not actually in DB but returned when URI is not in the cache * and returning `null` or throwing exception would not make sense. * Typical case is using it for query predicate when searching for unknown URI should result - * in a condition comparing URI ID attribute (e.g. relation_id) to id that will not be found. + * in a condition comparing URI ID attribute (e.g. relationId) to id that will not be found. * This is completely transient and can be changed if the need arises. */ public static final int UNKNOWN_ID = -1; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinition.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinition.java index 49a065ab6bf..f5205f73b67 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinition.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinition.java @@ -29,7 +29,7 @@ public class QAccessCertificationDefinition extends QObject handlerUriId = createInteger("handlerUriId", HANDLER_URI_ID); public final DateTimePath lastCampaignStartedTimestamp = diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignment.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignment.java index d445d77bd92..6261d9ade99 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignment.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignment.java @@ -40,29 +40,29 @@ public class QAssignment extends QContainer public static final String TABLE_NAME = "m_assignment"; public static final ColumnMetadata OWNER_TYPE = - ColumnMetadata.named("owner_type").ofType(Types.OTHER); + ColumnMetadata.named("ownerType").ofType(Types.OTHER); public static final ColumnMetadata LIFECYCLE_STATE = ColumnMetadata.named("lifecycleState").ofType(Types.VARCHAR); public static final ColumnMetadata ORDER_VALUE = ColumnMetadata.named("orderValue").ofType(Types.INTEGER); public static final ColumnMetadata ORG_REF_TARGET_OID = - ColumnMetadata.named("orgRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("orgRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata ORG_REF_TARGET_TYPE = - ColumnMetadata.named("orgRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("orgRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata ORG_REF_RELATION_ID = - ColumnMetadata.named("orgRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("orgRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata TARGET_REF_TARGET_OID = - ColumnMetadata.named("targetRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("targetRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata TARGET_REF_TARGET_TYPE = - ColumnMetadata.named("targetRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("targetRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata TARGET_REF_RELATION_ID = - ColumnMetadata.named("targetRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("targetRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata TENANT_REF_TARGET_OID = - ColumnMetadata.named("tenantRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("tenantRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata TENANT_REF_TARGET_TYPE = - ColumnMetadata.named("tenantRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("tenantRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata TENANT_REF_RELATION_ID = - ColumnMetadata.named("tenantRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("tenantRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata EXT_ID = ColumnMetadata.named("extId").ofType(Types.INTEGER); // TODO UUID or not? our control or outside? @@ -73,11 +73,11 @@ public class QAssignment extends QContainer public static final ColumnMetadata EXT = ColumnMetadata.named("ext").ofType(JSONB_TYPE); // construction columns public static final ColumnMetadata RESOURCE_REF_TARGET_OID = - ColumnMetadata.named("resourceRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("resourceRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata RESOURCE_REF_TARGET_TYPE = - ColumnMetadata.named("resourceRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("resourceRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata RESOURCE_REF_RELATION_ID = - ColumnMetadata.named("resourceRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("resourceRefRelationId").ofType(Types.INTEGER); // activation columns public static final ColumnMetadata ADMINISTRATIVE_STATUS = ColumnMetadata.named("administrativeStatus").ofType(Types.OTHER); @@ -101,23 +101,23 @@ public class QAssignment extends QContainer ColumnMetadata.named("archiveTimestamp").ofType(Types.TIMESTAMP_WITH_TIMEZONE); // metadata columns public static final ColumnMetadata CREATOR_REF_TARGET_OID = - ColumnMetadata.named("creatorRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("creatorRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata CREATOR_REF_TARGET_TYPE = - ColumnMetadata.named("creatorRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("creatorRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata CREATOR_REF_RELATION_ID = - ColumnMetadata.named("creatorRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("creatorRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata CREATE_CHANNEL_ID = - ColumnMetadata.named("createChannel_id").ofType(Types.INTEGER); + ColumnMetadata.named("createChannelId").ofType(Types.INTEGER); public static final ColumnMetadata CREATE_TIMESTAMP = ColumnMetadata.named("createTimestamp").ofType(Types.TIMESTAMP_WITH_TIMEZONE); public static final ColumnMetadata MODIFIER_REF_TARGET_OID = - ColumnMetadata.named("modifierRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("modifierRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata MODIFIER_REF_TARGET_TYPE = - ColumnMetadata.named("modifierRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("modifierRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata MODIFIER_REF_RELATION_ID = - ColumnMetadata.named("modifierRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("modifierRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata MODIFY_CHANNEL_ID = - ColumnMetadata.named("modifyChannel_id").ofType(Types.INTEGER); + ColumnMetadata.named("modifyChannelId").ofType(Types.INTEGER); public static final ColumnMetadata MODIFY_TIMESTAMP = ColumnMetadata.named("modifyTimestamp").ofType(Types.TIMESTAMP_WITH_TIMEZONE); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCase.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCase.java index 560d9932271..eeacb2eec81 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCase.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCase.java @@ -34,29 +34,29 @@ public class QCase extends QObject { public static final ColumnMetadata CLOSE_TIMESTAMP = ColumnMetadata.named("closeTimestamp").ofType(Types.TIMESTAMP_WITH_TIMEZONE); public static final ColumnMetadata OBJECT_REF_TARGET_OID = - ColumnMetadata.named("objectRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("objectRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata OBJECT_REF_TARGET_TYPE = - ColumnMetadata.named("objectRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("objectRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata OBJECT_REF_RELATION_ID = - ColumnMetadata.named("objectRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("objectRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata PARENT_REF_TARGET_OID = - ColumnMetadata.named("parentRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("parentRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata PARENT_REF_TARGET_TYPE = - ColumnMetadata.named("parentRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("parentRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata PARENT_REF_RELATION_ID = - ColumnMetadata.named("parentRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("parentRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata REQUESTOR_REF_TARGET_OID = - ColumnMetadata.named("requestorRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("requestorRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata REQUESTOR_REF_TARGET_TYPE = - ColumnMetadata.named("requestorRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("requestorRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata REQUESTOR_REF_RELATION_ID = - ColumnMetadata.named("requestorRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("requestorRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata TARGET_REF_TARGET_OID = - ColumnMetadata.named("targetRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("targetRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata TARGET_REF_TARGET_TYPE = - ColumnMetadata.named("targetRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("targetRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata TARGET_REF_RELATION_ID = - ColumnMetadata.named("targetRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("targetRefRelationId").ofType(Types.INTEGER); // attributes diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/QContainer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/QContainer.java index 99b13891be1..a9a6374f79f 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/QContainer.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/QContainer.java @@ -36,7 +36,7 @@ public class QContainer extends FlexibleRelationalPath public static final String TABLE_NAME = "m_container"; public static final ColumnMetadata OWNER_OID = - ColumnMetadata.named("owner_oid").ofType(UuidPath.UUID_TYPE).notNull(); + ColumnMetadata.named("ownerOid").ofType(UuidPath.UUID_TYPE).notNull(); public static final ColumnMetadata CID = ColumnMetadata.named("cid").ofType(Types.BIGINT).notNull(); public static final ColumnMetadata CONTAINER_TYPE = diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnector.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnector.java index 1f44aa3330d..ebeaa3fcca0 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnector.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnector.java @@ -35,13 +35,13 @@ public class QConnector extends QObject { public static final ColumnMetadata CONNECTOR_VERSION = ColumnMetadata.named("connectorVersion").ofType(Types.VARCHAR); public static final ColumnMetadata FRAMEWORK_ID = - ColumnMetadata.named("framework_id").ofType(Types.INTEGER); + ColumnMetadata.named("frameworkId").ofType(Types.INTEGER); public static final ColumnMetadata CONNECTOR_HOST_REF_TARGET_OID = - ColumnMetadata.named("connectorHostRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("connectorHostRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata CONNECTOR_HOST_REF_TARGET_TYPE = - ColumnMetadata.named("connectorHostRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("connectorHostRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata CONNECTOR_HOST_REF_RELATION_ID = - ColumnMetadata.named("connectorHostRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("connectorHostRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata TARGET_SYSTEM_TYPES = ColumnMetadata.named("targetSystemTypes").ofType(Types.ARRAY); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java index 627ed4e21a4..94e6fceaa20 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java @@ -46,9 +46,9 @@ public class QFocus extends QObject { public static final ColumnMetadata LOCALE = ColumnMetadata.named("locale").ofType(Types.VARCHAR); public static final ColumnMetadata LOCALITY_ORIG = - ColumnMetadata.named("locality_orig").ofType(Types.VARCHAR); + ColumnMetadata.named("localityOrig").ofType(Types.VARCHAR); public static final ColumnMetadata LOCALITY_NORM = - ColumnMetadata.named("locality_norm").ofType(Types.VARCHAR); + ColumnMetadata.named("localityNorm").ofType(Types.VARCHAR); public static final ColumnMetadata PREFERRED_LANGUAGE = ColumnMetadata.named("preferredLanguage").ofType(Types.VARCHAR); public static final ColumnMetadata TELEPHONE_NUMBER = diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QGenericObject.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QGenericObject.java index 6c8b567e3d4..4951bcf66fc 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QGenericObject.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QGenericObject.java @@ -22,7 +22,7 @@ public class QGenericObject extends QFocus { public static final String TABLE_NAME = "m_generic_object"; public static final ColumnMetadata GENERIC_OBJECT_TYPE_ID = - ColumnMetadata.named("genericObjectType_id").ofType(Types.INTEGER); + ColumnMetadata.named("genericObjectTypeId").ofType(Types.INTEGER); public final NumberPath genericObjectTypeId = createInteger("genericObjectTypeId", GENERIC_OBJECT_TYPE_ID); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QUser.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QUser.java index ddb13c9f1db..2128595598b 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QUser.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QUser.java @@ -22,39 +22,39 @@ public class QUser extends QFocus { public static final String TABLE_NAME = "m_user"; public static final ColumnMetadata ADDITIONAL_NAME_ORIG = - ColumnMetadata.named("additionalName_orig").ofType(Types.VARCHAR); + ColumnMetadata.named("additionalNameOrig").ofType(Types.VARCHAR); public static final ColumnMetadata ADDITIONAL_NAME_NORM = - ColumnMetadata.named("additionalName_norm").ofType(Types.VARCHAR); + ColumnMetadata.named("additionalNameNorm").ofType(Types.VARCHAR); public static final ColumnMetadata EMPLOYEE_NUMBER = ColumnMetadata.named("employeeNumber").ofType(Types.VARCHAR); public static final ColumnMetadata FAMILY_NAME_ORIG = - ColumnMetadata.named("familyName_orig").ofType(Types.VARCHAR); + ColumnMetadata.named("familyNameOrig").ofType(Types.VARCHAR); public static final ColumnMetadata FAMILY_NAME_NORM = - ColumnMetadata.named("familyName_norm").ofType(Types.VARCHAR); + ColumnMetadata.named("familyNameNorm").ofType(Types.VARCHAR); public static final ColumnMetadata FULL_NAME_ORIG = - ColumnMetadata.named("fullName_orig").ofType(Types.VARCHAR); + ColumnMetadata.named("fullNameOrig").ofType(Types.VARCHAR); public static final ColumnMetadata FULL_NAME_NORM = - ColumnMetadata.named("fullName_norm").ofType(Types.VARCHAR); + ColumnMetadata.named("fullNameNorm").ofType(Types.VARCHAR); public static final ColumnMetadata GIVEN_NAME_ORIG = - ColumnMetadata.named("givenName_orig").ofType(Types.VARCHAR); + ColumnMetadata.named("givenNameOrig").ofType(Types.VARCHAR); public static final ColumnMetadata GIVEN_NAME_NORM = - ColumnMetadata.named("givenName_norm").ofType(Types.VARCHAR); + ColumnMetadata.named("givenNameNorm").ofType(Types.VARCHAR); public static final ColumnMetadata HONORIFIC_PREFIX_ORIG = - ColumnMetadata.named("honorificPrefix_orig").ofType(Types.VARCHAR); + ColumnMetadata.named("honorificPrefixOrig").ofType(Types.VARCHAR); public static final ColumnMetadata HONORIFIC_PREFIX_NORM = - ColumnMetadata.named("honorificPrefix_norm").ofType(Types.VARCHAR); + ColumnMetadata.named("honorificPrefixNorm").ofType(Types.VARCHAR); public static final ColumnMetadata HONORIFIC_SUFFIX_ORIG = - ColumnMetadata.named("honorificSuffix_orig").ofType(Types.VARCHAR); + ColumnMetadata.named("honorificSuffixOrig").ofType(Types.VARCHAR); public static final ColumnMetadata HONORIFIC_SUFFIX_NORM = - ColumnMetadata.named("honorificSuffix_norm").ofType(Types.VARCHAR); + ColumnMetadata.named("honorificSuffixNorm").ofType(Types.VARCHAR); public static final ColumnMetadata NICK_NAME_ORIG = - ColumnMetadata.named("nickName_orig").ofType(Types.VARCHAR); + ColumnMetadata.named("nickNameOrig").ofType(Types.VARCHAR); public static final ColumnMetadata NICK_NAME_NORM = - ColumnMetadata.named("nickName_norm").ofType(Types.VARCHAR); + ColumnMetadata.named("nickNameNorm").ofType(Types.VARCHAR); public static final ColumnMetadata TITLE_ORIG = - ColumnMetadata.named("title_orig").ofType(Types.VARCHAR); + ColumnMetadata.named("titleOrig").ofType(Types.VARCHAR); public static final ColumnMetadata TITLE_NORM = - ColumnMetadata.named("title_norm").ofType(Types.VARCHAR); + ColumnMetadata.named("titleNorm").ofType(Types.VARCHAR); public final StringPath additionalNameOrig = createString("additionalNameOrig", ADDITIONAL_NAME_ORIG); public final StringPath additionalNameNorm = createString("additionalNameNorm", ADDITIONAL_NAME_NORM); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableRow.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableRow.java index b2105a47883..eba15fb9e9b 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableRow.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableRow.java @@ -32,9 +32,9 @@ public class QLookupTableRow extends QContainer { public static final ColumnMetadata VALUE = ColumnMetadata.named("value").ofType(Types.VARCHAR); public static final ColumnMetadata LABEL_NORM = - ColumnMetadata.named("label_norm").ofType(Types.VARCHAR); + ColumnMetadata.named("labelNorm").ofType(Types.VARCHAR); public static final ColumnMetadata LABEL_ORIG = - ColumnMetadata.named("label_orig").ofType(Types.VARCHAR); + ColumnMetadata.named("labelOrig").ofType(Types.VARCHAR); public static final ColumnMetadata LAST_CHANGE_TIMESTAMP = ColumnMetadata.named("lastChangeTimestamp").ofType(Types.TIMESTAMP_WITH_TIMEZONE); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObject.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObject.java index 22ef69a71fe..f9c3b0ef8e1 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObject.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObject.java @@ -40,21 +40,21 @@ public class QObject extends FlexibleRelationalPathBase { public static final ColumnMetadata OBJECT_TYPE = ColumnMetadata.named("objectType").ofType(Types.OTHER).notNull(); public static final ColumnMetadata NAME_ORIG = - ColumnMetadata.named("name_orig").ofType(Types.VARCHAR).notNull(); + ColumnMetadata.named("nameOrig").ofType(Types.VARCHAR).notNull(); public static final ColumnMetadata NAME_NORM = - ColumnMetadata.named("name_norm").ofType(Types.VARCHAR).notNull(); + ColumnMetadata.named("nameNorm").ofType(Types.VARCHAR).notNull(); public static final ColumnMetadata FULL_OBJECT = ColumnMetadata.named("fullObject").ofType(Types.BINARY); public static final ColumnMetadata TENANT_REF_TARGET_OID = - ColumnMetadata.named("tenantRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("tenantRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata TENANT_REF_TARGET_TYPE = - ColumnMetadata.named("tenantRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("tenantRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata TENANT_REF_RELATION_ID = - ColumnMetadata.named("tenantRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("tenantRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata LIFECYCLE_STATE = ColumnMetadata.named("lifecycleState").ofType(Types.VARCHAR); public static final ColumnMetadata CID_SEQ = - ColumnMetadata.named("cid_seq").ofType(Types.BIGINT).notNull(); + ColumnMetadata.named("cidSeq").ofType(Types.BIGINT).notNull(); public static final ColumnMetadata VERSION = ColumnMetadata.named("version").ofType(Types.INTEGER).notNull(); // complex DB fields @@ -65,23 +65,23 @@ public class QObject extends FlexibleRelationalPathBase { public static final ColumnMetadata EXT = ColumnMetadata.named("ext").ofType(JSONB_TYPE); // metadata columns public static final ColumnMetadata CREATOR_REF_TARGET_OID = - ColumnMetadata.named("creatorRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("creatorRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata CREATOR_REF_TARGET_TYPE = - ColumnMetadata.named("creatorRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("creatorRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata CREATOR_REF_RELATION_ID = - ColumnMetadata.named("creatorRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("creatorRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata CREATE_CHANNEL_ID = - ColumnMetadata.named("createChannel_id").ofType(Types.INTEGER); + ColumnMetadata.named("createChannelId").ofType(Types.INTEGER); public static final ColumnMetadata CREATE_TIMESTAMP = ColumnMetadata.named("createTimestamp").ofType(Types.TIMESTAMP_WITH_TIMEZONE); public static final ColumnMetadata MODIFIER_REF_TARGET_OID = - ColumnMetadata.named("modifierRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("modifierRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata MODIFIER_REF_TARGET_TYPE = - ColumnMetadata.named("modifierRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("modifierRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata MODIFIER_REF_RELATION_ID = - ColumnMetadata.named("modifierRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("modifierRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata MODIFY_CHANNEL_ID = - ColumnMetadata.named("modifyChannel_id").ofType(Types.INTEGER); + ColumnMetadata.named("modifyChannelId").ofType(Types.INTEGER); public static final ColumnMetadata MODIFY_TIMESTAMP = ColumnMetadata.named("modifyTimestamp").ofType(Types.TIMESTAMP_WITH_TIMEZONE); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java index d9455d39e05..8fab14efb76 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java @@ -72,7 +72,7 @@ protected QObjectMapping( q -> q.tenantRefTargetType, q -> q.tenantRefRelationId)); addItemMapping(F_LIFECYCLE_STATE, stringMapper(q -> q.lifecycleState)); - // version/cid_seq is not mapped for queries or deltas, it's managed by repo explicitly + // version/cidSeq is not mapped for queries or deltas, it's managed by repo explicitly // TODO mapper for policySituations and subtypes // TODO ext mapping can't be done statically diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QOperationExecution.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QOperationExecution.java index 8aac6c32248..14e3087d56e 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QOperationExecution.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QOperationExecution.java @@ -43,17 +43,17 @@ public class QOperationExecution extends QContainer extends QContainer { public static final String TABLE_NAME = "m_trigger"; public static final ColumnMetadata HANDLER_URI_ID = - ColumnMetadata.named("handlerUri_id").ofType(Types.INTEGER); + ColumnMetadata.named("handlerUriId").ofType(Types.INTEGER); public static final ColumnMetadata TIMESTAMP_VALUE = ColumnMetadata.named("timestampValue").ofType(Types.TIMESTAMP_WITH_TIMEZONE); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QReference.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QReference.java index f46fb4108a4..e9cd3877b36 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QReference.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/QReference.java @@ -38,9 +38,9 @@ public class QReference extends FlexibleRelationalPath public static final String TABLE_NAME = "m_reference"; public static final ColumnMetadata OWNER_OID = - ColumnMetadata.named("owner_oid").ofType(UuidPath.UUID_TYPE).notNull(); + ColumnMetadata.named("ownerOid").ofType(UuidPath.UUID_TYPE).notNull(); public static final ColumnMetadata OWNER_TYPE = - ColumnMetadata.named("owner_type").ofType(Types.OTHER); + ColumnMetadata.named("ownerType").ofType(Types.OTHER); public static final ColumnMetadata REFERENCE_TYPE = ColumnMetadata.named("referenceType").ofType(Types.OTHER).notNull(); public static final ColumnMetadata TARGET_OID = @@ -48,7 +48,7 @@ public class QReference extends FlexibleRelationalPath public static final ColumnMetadata TARGET_TYPE = ColumnMetadata.named("targetType").ofType(Types.OTHER).notNull(); public static final ColumnMetadata RELATION_ID = - ColumnMetadata.named("relation_id").ofType(Types.INTEGER).notNull(); + ColumnMetadata.named("relationId").ofType(Types.INTEGER).notNull(); public final UuidPath ownerOid = createUuid("ownerOid", OWNER_OID); public final EnumPath ownerType = diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportData.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportData.java index dd708410b9d..a423be8f691 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportData.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportData.java @@ -27,11 +27,11 @@ public class QReportData extends QObject { public static final String TABLE_NAME = "m_report_data"; public static final ColumnMetadata REPORT_REF_TARGET_OID = - ColumnMetadata.named("reportRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("reportRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata REPORT_REF_TARGET_TYPE = - ColumnMetadata.named("reportRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("reportRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata REPORT_REF_RELATION_ID = - ColumnMetadata.named("reportRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("reportRefRelationId").ofType(Types.INTEGER); public final UuidPath reportRefTargetOid = createUuid("reportRefTargetOid", REPORT_REF_TARGET_OID); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResource.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResource.java index efa4de2e987..c423c93aae5 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResource.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResource.java @@ -33,11 +33,11 @@ public class QResource extends QObject { public static final ColumnMetadata OPERATIONAL_STATE_LAST_AVAILABILITY_STATUS = ColumnMetadata.named("operationalState_lastAvailabilityStatus").ofType(Types.OTHER); public static final ColumnMetadata CONNECTOR_REF_TARGET_OID = - ColumnMetadata.named("connectorRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("connectorRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata CONNECTOR_REF_TARGET_TYPE = - ColumnMetadata.named("connectorRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("connectorRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata CONNECTOR_REF_RELATION_ID = - ColumnMetadata.named("connectorRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("connectorRefRelationId").ofType(Types.INTEGER); public final EnumPath businessAdministrativeState = createEnum("businessAdministrativeState", ResourceAdministrativeStateType.class, diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRole.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRole.java index 75802fc9ce3..54255bcf1d9 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRole.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRole.java @@ -34,9 +34,9 @@ public class QAbstractRole extends QFocus { public static final ColumnMetadata AUTO_ASSIGN_ENABLED = ColumnMetadata.named("autoAssignEnabled").ofType(Types.BOOLEAN); public static final ColumnMetadata DISPLAY_NAME_ORIG = - ColumnMetadata.named("displayName_orig").ofType(Types.VARCHAR); + ColumnMetadata.named("displayNameOrig").ofType(Types.VARCHAR); public static final ColumnMetadata DISPLAY_NAME_NORM = - ColumnMetadata.named("displayName_norm").ofType(Types.VARCHAR); + ColumnMetadata.named("displayNameNorm").ofType(Types.VARCHAR); public static final ColumnMetadata IDENTIFIER = ColumnMetadata.named("identifier").ofType(Types.VARCHAR); public static final ColumnMetadata REQUESTABLE = diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/QShadow.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/QShadow.java index 9c8a3dba846..4531c09ed7b 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/QShadow.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/QShadow.java @@ -30,13 +30,13 @@ public class QShadow extends QObject { // TODO public static final ColumnMetadata OBJECT_CLASS_ID = - ColumnMetadata.named("objectClass_id").ofType(Types.INTEGER); + ColumnMetadata.named("objectClassId").ofType(Types.INTEGER); public static final ColumnMetadata RESOURCE_REF_TARGET_OID = - ColumnMetadata.named("resourceRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("resourceRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata RESOURCE_REF_TARGET_TYPE = - ColumnMetadata.named("resourceRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("resourceRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata RESOURCE_REF_RELATION_ID = - ColumnMetadata.named("resourceRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("resourceRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata INTENT = ColumnMetadata.named("intent").ofType(Types.VARCHAR); public static final ColumnMetadata KIND = diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTask.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTask.java index 8f6acdca44a..ad9ae14b128 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTask.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTask.java @@ -40,7 +40,7 @@ public class QTask extends QObject { public static final ColumnMetadata FULL_RESULT = ColumnMetadata.named("fullResult").ofType(Types.BINARY); public static final ColumnMetadata HANDLER_URI_ID = - ColumnMetadata.named("handlerUri_id").ofType(Types.INTEGER); + ColumnMetadata.named("handlerUriId").ofType(Types.INTEGER); public static final ColumnMetadata LAST_RUN_START_TIMESTAMP = ColumnMetadata.named("lastRunStartTimestamp").ofType(Types.TIMESTAMP_WITH_TIMEZONE); public static final ColumnMetadata LAST_RUN_FINISH_TIMESTAMP = @@ -48,17 +48,17 @@ public class QTask extends QObject { public static final ColumnMetadata NODE = ColumnMetadata.named("node").ofType(Types.VARCHAR); public static final ColumnMetadata OBJECT_REF_TARGET_OID = - ColumnMetadata.named("objectRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("objectRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata OBJECT_REF_TARGET_TYPE = - ColumnMetadata.named("objectRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("objectRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata OBJECT_REF_RELATION_ID = - ColumnMetadata.named("objectRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("objectRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata OWNER_REF_TARGET_OID = - ColumnMetadata.named("ownerRef_targetOid").ofType(UuidPath.UUID_TYPE); + ColumnMetadata.named("ownerRefTargetOid").ofType(UuidPath.UUID_TYPE); public static final ColumnMetadata OWNER_REF_TARGET_TYPE = - ColumnMetadata.named("ownerRef_targetType").ofType(Types.OTHER); + ColumnMetadata.named("ownerRefTargetType").ofType(Types.OTHER); public static final ColumnMetadata OWNER_REF_RELATION_ID = - ColumnMetadata.named("ownerRef_relation_id").ofType(Types.INTEGER); + ColumnMetadata.named("ownerRefRelationId").ofType(Types.INTEGER); public static final ColumnMetadata PARENT = ColumnMetadata.named("parent").ofType(Types.VARCHAR); public static final ColumnMetadata RECURRENCE = From e0d49d0162c054f2a917683c04d06f579e142b34 Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Tue, 18 May 2021 16:57:58 +0200 Subject: [PATCH 20/43] torso of case work item (for merge) --- repo/repo-sqale/sql/pgnew-repo.sql | 26 ++--- .../repo/sqale/qmodel/cases/QCaseMapping.java | 12 +- .../qmodel/cases/workitem/MCaseWorkItem.java | 31 +++++ .../qmodel/cases/workitem/QCaseWorkItem.java | 103 +++++++++++++++++ .../cases/workitem/QCaseWorkItemMapping.java | 108 ++++++++++++++++++ 5 files changed, 259 insertions(+), 21 deletions(-) create mode 100644 repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/MCaseWorkItem.java create mode 100644 repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItem.java create mode 100644 repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index b791ba8084d..9498ce1bd49 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -1093,19 +1093,19 @@ CREATE INDEX iCaseTypeRequestorRefTargetOid ON m_case(requestorRef_targetOid); CREATE INDEX iCaseTypeCloseTimestamp ON m_case(closeTimestamp); CREATE TABLE m_case_wi ( - id INTEGER NOT NULL, - owner_oid UUID NOT NULL, - closeTimestamp TIMESTAMPTZ, - createTimestamp TIMESTAMPTZ, - deadline TIMESTAMPTZ, - originalAssigneeRef_relation VARCHAR(157), - originalAssigneeRef_targetOid UUID, - originalAssigneeRef_targetType INTEGER, - outcome TEXT, - performerRef_relation VARCHAR(157), - performerRef_targetOid UUID, - performerRef_targetType INTEGER, - stageNumber INTEGER, + id INTEGER NOT NULL, + owner_oid UUID NOT NULL, + closeTimestamp TIMESTAMPTZ, + createTimestamp TIMESTAMPTZ, + deadline TIMESTAMPTZ, + originalAssigneeRefTargetOid UUID, + originalAssigneeRefTargetType ObjectType, + originalAssigneeRefRelationId INTEGER REFERENCES m_uri(id), + outcome TEXT, + performerRefTargetOid UUID, + performerRefTargetType ObjectType, + performerRefRelationId INTEGER REFERENCES m_uri(id), + stageNumber INTEGER, PRIMARY KEY (owner_oid, id) ); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java index dbf13db3fbd..df780cc9273 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java @@ -6,19 +6,15 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.cases; -import static com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType.*; - -import com.evolveum.midpoint.repo.sqale.qmodel.task.MTask; - +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; - import com.evolveum.midpoint.util.MiscUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; import org.jetbrains.annotations.NotNull; -import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; -import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; +import static com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType.*; /** * Mapping between {@link QCase} and {@link CaseType}. diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/MCaseWorkItem.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/MCaseWorkItem.java new file mode 100644 index 00000000000..fbf629657a7 --- /dev/null +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/MCaseWorkItem.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010-2021 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.repo.sqale.qmodel.cases.workitem; + +import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainer; +import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; + +import java.time.Instant; +import java.util.UUID; + +/** + * Querydsl "row bean" type related to {@link QCaseWorkItem}. + */ +public class MCaseWorkItem extends MContainer { + + public Instant closeTimestamp; + public Instant createTimestamp; + public Instant deadline; + public UUID originalAssigneeRefTargetOid; + public MObjectType originalAssigneeRefTargetType; + public Integer originalAssigneeRefRelationId; + public String outcome; + public UUID performerRefTargetOid; + public MObjectType performerRefTargetType; + public Integer performerRefRelationId; + public Integer stageNumber; +} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItem.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItem.java new file mode 100644 index 00000000000..ec24d0b0035 --- /dev/null +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItem.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010-2021 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.repo.sqale.qmodel.cases.workitem; + +import com.evolveum.midpoint.repo.sqale.qmodel.assignment.MAssignment; +import com.evolveum.midpoint.repo.sqale.qmodel.cases.MCase; +import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainer; +import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; +import com.evolveum.midpoint.repo.sqlbase.querydsl.JsonbPath; +import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TimeIntervalStatusType; + +import com.querydsl.core.types.dsl.*; +import com.querydsl.sql.ColumnMetadata; + +import java.sql.Types; +import java.time.Instant; + +import static com.evolveum.midpoint.repo.sqlbase.querydsl.JsonbPath.JSONB_TYPE; + +/** + * Querydsl query type for {@value #TABLE_NAME} table. + */ +@SuppressWarnings("unused") +public class QCaseWorkItem extends QContainer { + + private static final long serialVersionUID = 341727922218837393L; + + /** + * If `QCaseWorkItem.class` is not enough because of generics, try `QCaseWorkItem.CLASS`. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static final Class> CLASS = (Class) QContainer.class; + + public static final String TABLE_NAME = "m_case_wi"; + + public static final ColumnMetadata CLOSE_TIMESTAMP = + ColumnMetadata.named("closeTimestamp").ofType(Types.TIMESTAMP_WITH_TIMEZONE); + public static final ColumnMetadata CREATE_TIMESTAMP = + ColumnMetadata.named("createTimestamp").ofType(Types.TIMESTAMP_WITH_TIMEZONE); + public static final ColumnMetadata DEADLINE = + ColumnMetadata.named("deadline").ofType(Types.TIMESTAMP_WITH_TIMEZONE); + public static final ColumnMetadata ORIGINAL_ASSIGNEE_REF_TARGET_OID = + ColumnMetadata.named("originalAssigneeRefTargetOid").ofType(UuidPath.UUID_TYPE); + public static final ColumnMetadata ORIGINAL_ASSIGNEE_REF_TARGET_TYPE = + ColumnMetadata.named("originalAssigneeRefTargetType").ofType(Types.OTHER); + public static final ColumnMetadata ORIGINAL_ASSIGNEE_REF_RELATION_ID = + ColumnMetadata.named("originalAssigneeRefRelationId").ofType(Types.INTEGER); + public static final ColumnMetadata OUTCOME = + ColumnMetadata.named("outcome").ofType(Types.VARCHAR); + public static final ColumnMetadata PERFORMER_REF_TARGET_OID = + ColumnMetadata.named("performerRefTargetOid").ofType(UuidPath.UUID_TYPE); + public static final ColumnMetadata PERFORMER_REF_TARGET_TYPE = + ColumnMetadata.named("performerRefTargetType").ofType(Types.OTHER); + public static final ColumnMetadata PERFORMER_REF_RELATION_ID = + ColumnMetadata.named("performerRefRelationId").ofType(Types.INTEGER); + public static final ColumnMetadata STAGE_NUMBER = + ColumnMetadata.named("stageNumber").ofType(Types.INTEGER); + + // attributes + + public final DateTimePath closeTimestamp = + createInstant("closeTimestamp", CLOSE_TIMESTAMP); + public final DateTimePath createTimestamp = + createInstant("createTimestamp", CREATE_TIMESTAMP); + public final DateTimePath deadline = + createInstant("deadline", DEADLINE); + public final UuidPath originalAssigneeRefTargetOid = + createUuid("originalAssigneeRefTargetOid", ORIGINAL_ASSIGNEE_REF_TARGET_OID); + public final EnumPath originalAssigneeRefTargetType = + createEnum("originalAssigneeRefTargetType", MObjectType.class, ORIGINAL_ASSIGNEE_REF_TARGET_TYPE); + public final NumberPath originalAssigneeRefRelationId = + createInteger("originalAssigneeRefRelationId", ORIGINAL_ASSIGNEE_REF_RELATION_ID); + public final StringPath outcome = createString("outcome", OUTCOME); + public final UuidPath performerRefTargetOid = + createUuid("performerRefTargetOid", PERFORMER_REF_TARGET_OID); + public final EnumPath performerRefTargetType = + createEnum("performerRefTargetType", MObjectType.class, PERFORMER_REF_TARGET_TYPE); + public final NumberPath performerRefRelationId = + createInteger("performerRefRelationId", PERFORMER_REF_RELATION_ID); + public final NumberPath stageNumber = + createInteger("stageNumber", STAGE_NUMBER); + + + public QCaseWorkItem(String variable) { + this(variable, DEFAULT_SCHEMA_NAME, TABLE_NAME); + } + + public QCaseWorkItem(String variable, String schema, String table) { + super(MCaseWorkItem.class, variable, schema, table); + } + + @Override + public BooleanExpression isOwnedBy(OR ownerRow) { + return ownerOid.eq(ownerRow.oid); + } +} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java new file mode 100644 index 00000000000..01007405659 --- /dev/null +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2010-2021 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.repo.sqale.qmodel.cases.workitem; + +import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; +import com.evolveum.midpoint.repo.sqale.qmodel.cases.MCase; +import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainerType; +import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainerMapping; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.util.MiscUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType; + +import org.jetbrains.annotations.NotNull; + +import static com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType.*; + +/** + * Mapping between {@link QCaseWorkItem} and {@link CaseWorkItemType}. + * + * @param type of the owner row + */ +public class QCaseWorkItemMapping + extends QContainerMapping, MCaseWorkItem, OR> { + + public static final String DEFAULT_ALIAS_NAME = "cswi"; + + private final MContainerType containerType; + + // We can't declare Class>.class, so we cheat a bit. + @SuppressWarnings({ "unchecked", "rawtypes" }) + private QCaseWorkItemMapping( + @NotNull MContainerType containerType, + @NotNull SqaleRepoContext repositoryContext) { + super(QCaseWorkItem.TABLE_NAME, DEFAULT_ALIAS_NAME, + CaseWorkItemType.class, (Class) QCaseWorkItem.class, repositoryContext); + this.containerType = containerType; + + addItemMapping(F_CLOSE_TIMESTAMP, timestampMapper(q -> q.closeTimestamp)); + addItemMapping(F_CREATE_TIMESTAMP, timestampMapper(q -> q.createTimestamp)); + addItemMapping(F_DEADLINE, timestampMapper(q -> q.deadline)); + + addItemMapping(F_ORIGINAL_ASSIGNEE_REF, refMapper( + q -> q.originalAssigneeRefTargetOid, + q -> q.originalAssigneeRefTargetType, + q -> q.originalAssigneeRefRelationId)); + + // TODO: OUTCOME +// addItemMapping(F_OUTCOME, stringMapper(q -> q.outcome)); + + addItemMapping(F_PERFORMER_REF, refMapper( + q -> q.performerRefTargetOid, + q -> q.performerRefTargetType, + q -> q.performerRefRelationId)); + + addItemMapping(F_STAGE_NUMBER, integerMapper(q -> q.stageNumber)); + + } + + @Override + protected QCaseWorkItem newAliasInstance(String alias) { + return new QCaseWorkItem<>(alias); + } + + @Override + public MCaseWorkItem newRowObject() { + MCaseWorkItem row = new MCaseWorkItem(); + row.containerType = this.containerType; + return row; + } + + @Override + public MCaseWorkItem newRowObject(OR ownerRow) { + MCaseWorkItem row = newRowObject(); + row.ownerOid = ownerRow.oid; + return row; + } + + // about duplication see the comment in QObjectMapping.toRowObjectWithoutFullObject + @SuppressWarnings("DuplicatedCode") + @Override + public MCaseWorkItem insert(CaseWorkItemType workItem, OR ownerRow, JdbcSession jdbcSession) { + MCaseWorkItem row = initRowObject(workItem, ownerRow); + + row.closeTimestamp = MiscUtil.asInstant(workItem.getCloseTimestamp()); + row.createTimestamp = MiscUtil.asInstant(workItem.getCreateTimestamp()); + row.deadline = MiscUtil.asInstant(workItem.getDeadline()); + setReference(workItem.getOriginalAssigneeRef(), + o -> row.originalAssigneeRefTargetOid = o, + t -> row.originalAssigneeRefTargetType = t, + r -> row.originalAssigneeRefRelationId = r); + + // TODO: Outcome + + setReference(workItem.getPerformerRef(), + o -> row.performerRefTargetOid = o, + t -> row.performerRefTargetType = t, + r -> row.performerRefRelationId = r); + row.stageNumber = workItem.getStageNumber(); + + insert(row, jdbcSession); + + return row; + } +} From 01f4580e47c62b857b1959cf2bd617c5b7d5bc7e Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Tue, 18 May 2021 19:09:14 +0200 Subject: [PATCH 21/43] Sqale: case->workitem (WIP) --- .../repo/sqale/qmodel/cases/QCaseMapping.java | 28 ++++++++++++++++--- .../cases/workitem/QCaseWorkItemMapping.java | 25 +++++++++++++---- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java index df780cc9273..3d82493033a 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java @@ -6,15 +6,20 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.cases; +import static com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType.*; + +import java.util.List; +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; +import com.evolveum.midpoint.repo.sqale.qmodel.cases.workitem.QCaseWorkItemMapping; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; - -import org.jetbrains.annotations.NotNull; - -import static com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType; /** * Mapping between {@link QCase} and {@link CaseType}. @@ -50,6 +55,10 @@ private QCaseMapping(@NotNull SqaleRepoContext repositoryContext) { q -> q.targetRefTargetOid, q -> q.targetRefTargetType, q -> q.targetRefRelationId)); + + addContainerTableMapping(F_WORK_ITEM, + QCaseWorkItemMapping.init(repositoryContext), + joinOn((o, wi) -> o.oid.eq(wi.ownerOid))); } @Override @@ -88,4 +97,15 @@ public MCase newRowObject() { return row; } + + @Override + public void storeRelatedEntities( + @NotNull MCase row, @NotNull CaseType schemaObject, @NotNull JdbcSession jdbcSession) { + Objects.requireNonNull(row.oid); + + List workItems = schemaObject.getWorkItem(); + if (!workItems.isEmpty()) { + workItems.forEach(t -> QCaseWorkItemMapping.get().insert(t, row, jdbcSession)); + } + } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java index 01007405659..cf440ed0d3d 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java @@ -10,12 +10,16 @@ import com.evolveum.midpoint.repo.sqale.qmodel.cases.MCase; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainerType; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainerMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QTriggerMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType; import org.jetbrains.annotations.NotNull; +import java.util.Objects; + import static com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType.*; /** @@ -28,16 +32,26 @@ public class QCaseWorkItemMapping public static final String DEFAULT_ALIAS_NAME = "cswi"; - private final MContainerType containerType; + private static QCaseWorkItemMapping instance; + + public static QCaseWorkItemMapping init( + @NotNull SqaleRepoContext repositoryContext) { + if (instance == null) { + instance = new QCaseWorkItemMapping<>(repositoryContext); + } + return get(); + } + + public static QCaseWorkItemMapping get() { + //noinspection unchecked + return (QCaseWorkItemMapping) Objects.requireNonNull(instance); + } // We can't declare Class>.class, so we cheat a bit. @SuppressWarnings({ "unchecked", "rawtypes" }) - private QCaseWorkItemMapping( - @NotNull MContainerType containerType, - @NotNull SqaleRepoContext repositoryContext) { + private QCaseWorkItemMapping(@NotNull SqaleRepoContext repositoryContext) { super(QCaseWorkItem.TABLE_NAME, DEFAULT_ALIAS_NAME, CaseWorkItemType.class, (Class) QCaseWorkItem.class, repositoryContext); - this.containerType = containerType; addItemMapping(F_CLOSE_TIMESTAMP, timestampMapper(q -> q.closeTimestamp)); addItemMapping(F_CREATE_TIMESTAMP, timestampMapper(q -> q.createTimestamp)); @@ -68,7 +82,6 @@ protected QCaseWorkItem newAliasInstance(String alias) { @Override public MCaseWorkItem newRowObject() { MCaseWorkItem row = new MCaseWorkItem(); - row.containerType = this.containerType; return row; } From a6ffcfba8198e123613650d17bd1ad4ecfd1e120 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Tue, 18 May 2021 19:53:53 +0200 Subject: [PATCH 22/43] repo-sqale: added perf monitoring to add/get/search/modifyObject --- .../repo/sqale/SqaleRepositoryBeanConfig.java | 2 - .../repo/sqale/SqaleRepositoryService.java | 235 ++++++++++++------ .../repo/sqale/SqaleRepoBaseTest.java | 15 ++ .../func/SqaleRepoAddDeleteObjectTest.java | 30 ++- .../sqale/func/SqaleRepoModifyObjectTest.java | 22 ++ .../sqale/func/SqaleRepoSearchObjectTest.java | 21 ++ .../repo/sqale/func/SqaleRepoSmokeTest.java | 24 ++ 7 files changed, 260 insertions(+), 89 deletions(-) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryBeanConfig.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryBeanConfig.java index bbb52c2541d..b70a1dfb7ed 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryBeanConfig.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryBeanConfig.java @@ -182,11 +182,9 @@ public SqlPerformanceMonitorsCollection sqlPerformanceMonitorsCollection() { @Bean public SqaleRepositoryService repositoryService( SqaleRepoContext sqlRepoContext, - SchemaService schemaService, SqlPerformanceMonitorsCollection sqlPerformanceMonitorsCollection) { return new SqaleRepositoryService( sqlRepoContext, - schemaService, sqlPerformanceMonitorsCollection); } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java index 8613d581234..1f66c9ac0f5 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java @@ -30,7 +30,7 @@ import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.repo.api.*; -import com.evolveum.midpoint.repo.api.perf.PerformanceMonitor; +import com.evolveum.midpoint.repo.api.perf.OperationRecord; import com.evolveum.midpoint.repo.api.query.ObjectFilterExpressionEvaluator; import com.evolveum.midpoint.repo.sqale.operations.AddObjectOperation; import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTableMapping; @@ -46,7 +46,6 @@ import com.evolveum.midpoint.schema.internals.InternalsConfig; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.result.OperationResultStatus; -import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; @@ -55,6 +54,17 @@ /** * Repository implementation based on SQL, JDBC and Querydsl without any ORM. * WORK IN PROGRESS. + * + * Structure of main public methods (WIP): + * - arg checks + * - debug log + * - create op-result, immediately followed by try/catch/finally (see addObject for example) + * - more arg checks :-) (here ore before op result depending on the needs) + * - call to executeMethodName(...) where perf monitor is initialized followed by try/catch/finally + * - finally in main method: + * + * TODO/document?: + * - ignore useNoFetchExtensionValuesInsertion - related to Hibernate, */ public class SqaleRepositoryService implements RepositoryService { @@ -70,7 +80,6 @@ public class SqaleRepositoryService implements RepositoryService { private static final int MAX_CONFLICT_WATCHERS = 10; private final SqaleRepoContext repositoryContext; - private final SchemaService schemaService; private final SqlQueryExecutor sqlQueryExecutor; private final SqlPerformanceMonitorsCollection sqlPerformanceMonitorsCollection; @@ -84,10 +93,8 @@ public class SqaleRepositoryService implements RepositoryService { public SqaleRepositoryService( SqaleRepoContext repositoryContext, - SchemaService schemaService, SqlPerformanceMonitorsCollection sqlPerformanceMonitorsCollection) { this.repositoryContext = repositoryContext; - this.schemaService = schemaService; this.sqlQueryExecutor = new SqlQueryExecutor(repositoryContext); this.sqlPerformanceMonitorsCollection = sqlPerformanceMonitorsCollection; @@ -118,24 +125,9 @@ public SqaleRepositoryService( .addParam("oid", oid) .build(); + PrismObject object = null; try { - PrismObject object; - try (JdbcSession jdbcSession = - repositoryContext.newJdbcSession().startReadOnlyTransaction()) { - //noinspection unchecked - object = (PrismObject) readByOid(jdbcSession, type, oidUuid, options) - .asPrismObject(); - jdbcSession.commit(); - } - - // "objectLocal" is here just to provide effectively final variable for the lambda below -// PrismObject objectLocal = executeAttempts(oid, OP_GET_OBJECT, type, "getting", -// subResult, () -> objectRetriever.getObjectAttempt(type, oid, options, operationResult)); -// object = objectLocal; - invokeConflictWatchers((w) -> w.afterGetObject(object)); - - // TODO both update and get need this? - ObjectTypeUtil.normalizeAllRelations(object, schemaService.relationRegistry()); + object = executeGetObject(type, oidUuid, options); return object; } catch (RuntimeException e) { // TODO what else to catch? throw handledGeneralException(e, operationResult); @@ -144,11 +136,39 @@ public SqaleRepositoryService( throw t; } finally { operationResult.computeStatusIfUnknown(); -// OperationLogger.logGetObject(type, oid, options, object, operationResult); - // TODO some logging + OperationLogger.logGetObject(type, oid, options, object, operationResult); } } + private PrismObject executeGetObject( + Class type, + UUID oidUuid, + Collection> options) + throws SchemaException, ObjectNotFoundException { + PrismObject object; + long opHandle = registerOperationStart(OP_GET_OBJECT, type); + try (JdbcSession jdbcSession = + repositoryContext.newJdbcSession().startReadOnlyTransaction()) { + //noinspection unchecked + object = (PrismObject) readByOid(jdbcSession, type, oidUuid, options) + .asPrismObject(); + jdbcSession.commit(); + } finally { + registerOperationFinish(opHandle, 1); // TODO attempt (separate try from JDBC session) + } + + // TODO attempts on conflict + // "objectLocal" is here just to provide effectively final variable for the lambda below +// PrismObject objectLocal = executeAttempts(oid, OP_GET_OBJECT, type, "getting", +// subResult, () -> objectRetriever.getObjectAttempt(type, oid, options, operationResult)); +// object = objectLocal; + invokeConflictWatchers((w) -> w.afterGetObject(object)); + + // TODO both update and get need this? I believe not. + // ObjectTypeUtil.normalizeAllRelations(object, schemaService.relationRegistry()); + return object; + } + private UUID checkOid(String oid) { Objects.requireNonNull(oid, "OID must not be null"); try { @@ -240,46 +260,53 @@ public String addObject( object.setVersion("1"); } + return executeAddObject(object, options, operationResult); + } catch (Throwable t) { + operationResult.recordFatalError(t); + throw t; + } finally { + operationResult.computeStatusIfUnknown(); + OperationLogger.logAdd(object, options, operationResult); + } + } + + private String executeAddObject( + @NotNull PrismObject object, + @NotNull RepoAddOptions options, + @NotNull OperationResult operationResult) + throws SchemaException, ObjectAlreadyExistsException { + long opHandle = registerOperationStart( + options.isOverwrite() ? OP_ADD_OBJECT_OVERWRITE : OP_ADD_OBJECT, + object); /* old repo code missing in new repo: - SqlPerformanceMonitorImpl pm = getPerformanceMonitor(); - long opHandle = pm.registerOperationStart(OP_ADD_OBJECT, object.getCompileTimeClass()); int attempt = 1; int restarts = 0; - boolean noFetchExtensionValueInsertionForbidden = false; String proposedOid = object.getOid(); while (true) { - try { */ - // TODO use executeAttempts + // TODO use executeAttempts + try { String oid = new AddObjectOperation<>(object, options, operationResult) .execute(repositoryContext); invokeConflictWatchers((w) -> w.afterAddObject(oid, object)); return oid; - /* - } catch (RestartOperationRequestedException ex) { - // special case: we want to restart but we do not want to count these - LOGGER.trace("Restarting because of {}", ex.getMessage()); - restarts++; - if (restarts > RESTART_LIMIT) { - throw new IllegalStateException("Too many operation restarts"); - } - } catch (RuntimeException ex) { - attempt = baseHelper.logOperationAttempt(proposedOid, "adding", attempt, ex, subResult); -// pm.registerOperationNewAttempt(opHandle, attempt); + /* + } catch (RestartOperationRequestedException ex) { + // special case: we want to restart but we do not want to count these + LOGGER.trace("Restarting because of {}", ex.getMessage()); + restarts++; + if (restarts > RESTART_LIMIT) { + throw new IllegalStateException("Too many operation restarts"); } - noFetchExtensionValueInsertionForbidden = true; // todo This is a temporary measure; needs better handling. + } catch (RuntimeException ex) { + attempt = baseHelper.logOperationAttempt(proposedOid, "adding", attempt, ex, subResult); +// pm.registerOperationNewAttempt(opHandle, attempt); } - } finally { -// pm.registerOperationFinish(opHandle, attempt); -// OperationLogger.logAdd(object, options, subResult); TODO logging } - */ - } catch (Throwable t) { - operationResult.recordFatalError(t); - throw t; + */ } finally { - operationResult.computeStatusIfUnknown(); + registerOperationFinish(opHandle, 1); // TODO attempt } } @@ -326,6 +353,13 @@ public ModifyObjectResult modifyObject( UUID oidUuid = checkOid(oid); Objects.requireNonNull(parentResult, "Operation result must not be null."); + if (options == null) { + options = new RepoModifyOptions(); + } + + LOGGER.debug("Modify object type '{}', oid={}, reindex={}", + type.getSimpleName(), oid, options.isForceReindex()); + OperationResult operationResult = parentResult.subresult(OP_NAME_PREFIX + OP_MODIFY_OBJECT) .addQualifier(type.getSimpleName()) .addParam("type", type.getName()) @@ -353,31 +387,7 @@ public ModifyObjectResult modifyObject( logTraceModifications(modifications); - // TODO: THIS is real start of modifyObjectAttempt - try (JdbcSession jdbcSession = repositoryContext.newJdbcSession().startTransaction()) { - RootUpdateContext, MObject> updateContext = - prepareUpdateContext(jdbcSession, type, oidUuid); - PrismObject prismObject = updateContext.getPrismObject(); - if (precondition != null && !precondition.holds(prismObject)) { - jdbcSession.rollback(); - throw new PreconditionViolationException( - "Modification precondition does not hold for " + prismObject); - } - invokeConflictWatchers(w -> w.beforeModifyObject(prismObject)); - - // TODO is modifications cloning unavoidable? see the clone at the start of ObjectUpdater.modifyObjectAttempt - // If cloning will be necessary, do it at the beginning of modifyObjectAttempt, - // especially if called potentially multiple times. - // TODO replaces: RObject rObject = objectDeltaUpdater.modifyObject(type, oid, modifications, prismObject, modifyOptions, session, attemptContext); - PrismObject originalObject = prismObject.clone(); - - modifications = updateContext.execute(modifications); - jdbcSession.commit(); - - LOGGER.trace("OBJECT after:\n{}", prismObject.debugDumpLazily()); - - return new ModifyObjectResult<>(originalObject, prismObject, modifications); - } + return executeModifyObject(type, oidUuid, modifications, precondition); } catch (RepositoryException | RuntimeException e) { throw handledGeneralException(e, operationResult); } catch (Throwable t) { @@ -385,6 +395,39 @@ public ModifyObjectResult modifyObject( throw t; } finally { operationResult.computeStatusIfUnknown(); + OperationLogger.logModify(type, oid, modifications, precondition, options, operationResult); + } + } + + @NotNull + private ModifyObjectResult executeModifyObject( + @NotNull Class type, + @NotNull UUID oidUuid, + @NotNull Collection> modifications, + @Nullable ModificationPrecondition precondition) + throws SchemaException, ObjectNotFoundException, PreconditionViolationException, RepositoryException { + + long opHandle = registerOperationStart(OP_MODIFY_OBJECT, type); + try (JdbcSession jdbcSession = repositoryContext.newJdbcSession().startTransaction()) { + RootUpdateContext, MObject> updateContext = + prepareUpdateContext(jdbcSession, type, oidUuid); + PrismObject prismObject = updateContext.getPrismObject(); + if (precondition != null && !precondition.holds(prismObject)) { + jdbcSession.rollback(); + throw new PreconditionViolationException( + "Modification precondition does not hold for " + prismObject); + } + invokeConflictWatchers(w -> w.beforeModifyObject(prismObject)); + PrismObject originalObject = prismObject.clone(); // for result later + + modifications = updateContext.execute(modifications); + jdbcSession.commit(); + + LOGGER.trace("OBJECT after:\n{}", prismObject.debugDumpLazily()); + + return new ModifyObjectResult<>(originalObject, prismObject, modifications); + } finally { + registerOperationFinish(opHandle, 1); // TODO attempt } } @@ -543,13 +586,7 @@ public int countObjects(Class type, ObjectQuery query, .build(); try { - var queryContext = SqaleQueryContext.from(type, repositoryContext); - SearchResultList result = - sqlQueryExecutor.list(queryContext, query, options); - // TODO see the commented code from old repo lower, problems for each object must be caught - //noinspection unchecked - return result.map( - o -> (PrismObject) o.asPrismObject()); + return executeSearchObject(type, query, options); } catch (RepositoryException | RuntimeException e) { throw handledGeneralException(e, operationResult); } catch (Throwable t) { @@ -560,6 +597,27 @@ public int countObjects(Class type, ObjectQuery query, } } + private SearchResultList> executeSearchObject( + @NotNull Class type, + ObjectQuery query, + Collection> options) + throws RepositoryException, SchemaException { + + long opHandle = registerOperationStart(OP_SEARCH_OBJECTS, type); + try { + SearchResultList result = sqlQueryExecutor.list( + SqaleQueryContext.from(type, repositoryContext), + query, + options); + // TODO see the commented code from old repo lower, problems for each object must be caught + //noinspection unchecked + return result.map( + o -> (PrismObject) o.asPrismObject()); + } finally { + registerOperationFinish(opHandle, 1); // TODO attempt (separate try from JDBC session) + } + } + /* TODO from ObjectRetriever, how to do this per-object Throwable catch + record result? should we smuggle the OperationResult all the way to the mapping call? @@ -810,7 +868,7 @@ public void addDiagnosticInformation(Class type, Strin } @Override - public PerformanceMonitor getPerformanceMonitor() { + public SqlPerformanceMonitorImpl getPerformanceMonitor() { return performanceMonitor; } @@ -849,4 +907,17 @@ private void recordException(@NotNull Throwable ex, OperationResult result, bool result.recordFatalError(message, ex); } } + + private long registerOperationStart(String kind, PrismObject object) { + return performanceMonitor.registerOperationStart(kind, object.getCompileTimeClass()); + } + + private long registerOperationStart(String kind, Class type) { + return performanceMonitor.registerOperationStart(kind, type); + } + + // TODO return will be used probably by modifyObject* + private OperationRecord registerOperationFinish(long opHandle, int attempt) { + return performanceMonitor.registerOperationFinish(opHandle, attempt); + } } diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/SqaleRepoBaseTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/SqaleRepoBaseTest.java index 919575fbcd7..91f0b15f5f9 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/SqaleRepoBaseTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/SqaleRepoBaseTest.java @@ -10,6 +10,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.stream.Stream; import javax.xml.namespace.QName; @@ -24,12 +25,14 @@ import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.Referencable; +import com.evolveum.midpoint.repo.api.perf.OperationPerformanceInformation; import com.evolveum.midpoint.repo.sqale.qmodel.common.QUri; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; import com.evolveum.midpoint.repo.sqale.qmodel.ref.MReference; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.repo.sqlbase.perfmon.SqlPerformanceMonitorImpl; import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; import com.evolveum.midpoint.schema.RelationRegistry; import com.evolveum.midpoint.test.util.AbstractSpringTest; @@ -231,4 +234,16 @@ protected java.util.function.Predicate refRowMatcher( && ref.targetType == targetType && cachedUriById(ref.relationId).equals(QNameUtil.qNameToUri(relation)); } + + protected void assertSingleOperationRecorded(SqlPerformanceMonitorImpl pm, String opKind) { + Map pmAllData = + pm.getGlobalPerformanceInformation().getAllData(); + assertThat(pmAllData).hasSize(1); + Map.Entry perfEntry = + pmAllData.entrySet().iterator().next(); + assertThat(perfEntry.getKey()).startsWith(opKind); + OperationPerformanceInformation operationInfo = perfEntry.getValue(); + assertThat(operationInfo.getInvocationCount()).isEqualTo(1); + assertThat(operationInfo.getExecutionCount()).isEqualTo(1); + } } diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java index 5ae99c81376..58ddd2c4487 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java @@ -18,17 +18,16 @@ import java.util.UUID; import javax.xml.namespace.QName; -import com.evolveum.midpoint.repo.sqale.qmodel.cases.MCase; - -import com.evolveum.midpoint.repo.sqale.qmodel.cases.QCase; - import org.testng.annotations.Test; import com.evolveum.midpoint.repo.api.DeleteObjectResult; +import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.repo.sqale.SqaleRepoBaseTest; import com.evolveum.midpoint.repo.sqale.qmodel.accesscert.MAccessCertificationDefinition; import com.evolveum.midpoint.repo.sqale.qmodel.accesscert.QAccessCertificationDefinition; import com.evolveum.midpoint.repo.sqale.qmodel.assignment.*; +import com.evolveum.midpoint.repo.sqale.qmodel.cases.MCase; +import com.evolveum.midpoint.repo.sqale.qmodel.cases.QCase; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainer; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainerType; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainer; @@ -58,6 +57,7 @@ import com.evolveum.midpoint.repo.sqale.qmodel.task.MTask; import com.evolveum.midpoint.repo.sqale.qmodel.task.QTask; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.repo.sqlbase.perfmon.SqlPerformanceMonitorImpl; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.util.MiscUtil; @@ -65,6 +65,7 @@ import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; public class SqaleRepoAddDeleteObjectTest extends SqaleRepoBaseTest { @@ -191,6 +192,25 @@ public void test111AddSecondObjectWithTheSameOidThrowsObjectAlreadyExists() assertCount(QUser.class, baseCount); } + @Test + public void test150AddOperationUpdatesPerformanceMonitor() + throws ObjectAlreadyExistsException, SchemaException { + OperationResult result = createOperationResult(); + + given("object to add and cleared performance information"); + UserType userType = new UserType(prismContext).name("user" + getTestNumber()); + SqlPerformanceMonitorImpl pm = repositoryService.getPerformanceMonitor(); + pm.clearGlobalPerformanceInformation(); + assertThat(pm.getGlobalPerformanceInformation().getAllData()).isEmpty(); + + when("object is added to the repository"); + repositoryService.addObject(userType.asPrismObject(), null, result); + + then("performance monitor is updated"); + assertThatOperationResult(result).isSuccess(); + assertSingleOperationRecorded(pm, RepositoryService.OP_ADD_OBJECT); + } + @Test public void test200AddObjectWithMultivalueContainers() throws ObjectAlreadyExistsException, SchemaException { @@ -320,7 +340,7 @@ public void test206AddObjectWithMultivalueRefsOnAssignment() String userName = "user" + getTestNumber(); UUID approverRef1 = UUID.randomUUID(); UUID approverRef2 = UUID.randomUUID(); - QName approverRelation = QName.valueOf("{https://random.org/ns}conn-rel"); // TODO + QName approverRelation = QName.valueOf("{https://random.org/ns}conn-rel"); UserType user = new UserType(prismContext) .name(userName) .assignment(new AssignmentType() diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java index eaad0d84937..c375ac60320 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java @@ -23,6 +23,7 @@ import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.polystring.PolyString; +import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.repo.sqale.SqaleRepoBaseTest; import com.evolveum.midpoint.repo.sqale.qmodel.assignment.*; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainerType; @@ -38,6 +39,7 @@ import com.evolveum.midpoint.repo.sqale.qmodel.shadow.QShadow; import com.evolveum.midpoint.repo.sqale.qmodel.task.MTask; import com.evolveum.midpoint.repo.sqale.qmodel.task.QTask; +import com.evolveum.midpoint.repo.sqlbase.perfmon.SqlPerformanceMonitorImpl; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; @@ -2115,6 +2117,26 @@ public void test910ModificationsOfNonexistentObjectFails() throws SchemaExceptio assertThatOperationResult(result).isFatalError(); } + @Test + public void test920ModifyOperationUpdatesPerformanceMonitor() + throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { + OperationResult result = createOperationResult(); + + given("object modification and cleared performance information"); + ObjectDelta delta = prismContext.deltaFor(UserType.class) + .item(UserType.F_EMAIL_ADDRESS).add(getTestNameShort() + "@email.com") + .asObjectDelta(user1Oid); + SqlPerformanceMonitorImpl pm = repositoryService.getPerformanceMonitor(); + pm.clearGlobalPerformanceInformation(); + assertThat(pm.getGlobalPerformanceInformation().getAllData()).isEmpty(); + + when("object is modified in the repository"); + repositoryService.modifyObject(UserType.class, user1Oid, delta.getModifications(), result); + + then("performance monitor is updated"); + assertThatOperationResult(result).isSuccess(); + assertSingleOperationRecorded(pm, RepositoryService.OP_MODIFY_OBJECT); + } @Test public void test990ChangeOfNonPersistedAttributeWorksOk() throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java index ebd492ab500..92f65c4416e 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java @@ -19,8 +19,10 @@ import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.repo.sqale.SqaleRepoBaseTest; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqlbase.perfmon.SqlPerformanceMonitorImpl; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SearchResultList; import com.evolveum.midpoint.schema.SelectorOptions; @@ -411,6 +413,25 @@ public void test900SearchByWholeContainerIsNotPossible() { // even if query was possible this would fail in the actual repo search, which is expected } + + @Test + public void test950SearchOperationUpdatesPerformanceMonitor() throws SchemaException { + OperationResult operationResult = createOperationResult(); + + given("cleared performance information"); + SqlPerformanceMonitorImpl pm = repositoryService.getPerformanceMonitor(); + pm.clearGlobalPerformanceInformation(); + assertThat(pm.getGlobalPerformanceInformation().getAllData()).isEmpty(); + + when("search is called on the repository"); + SearchResultList result = searchObjects(FocusType.class, + prismContext.queryFor(FocusType.class).build(), + operationResult); + + then("performance monitor is updated"); + assertThatOperationResult(operationResult).isSuccess(); + assertSingleOperationRecorded(pm, RepositoryService.OP_SEARCH_OBJECTS); + } // endregion // support methods diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSmokeTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSmokeTest.java index 84ecf5f18eb..309382e380a 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSmokeTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSmokeTest.java @@ -12,7 +12,9 @@ import org.testng.annotations.Test; +import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.repo.api.DeleteObjectResult; +import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.repo.sqale.SqaleRepoBaseTest; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainer; import com.evolveum.midpoint.repo.sqale.qmodel.focus.MUser; @@ -20,6 +22,7 @@ import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QReference; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; +import com.evolveum.midpoint.repo.sqlbase.perfmon.SqlPerformanceMonitorImpl; import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; import com.evolveum.midpoint.repo.sqlbase.querydsl.Jsonb; import com.evolveum.midpoint.schema.result.OperationResult; @@ -59,14 +62,35 @@ public void test000Sanity() { public void test100AddObject() throws ObjectAlreadyExistsException, SchemaException { OperationResult result = createOperationResult(); + when("correct object is added to the repository"); UserType userType = new UserType(prismContext) .name("sanity-user"); sanityUserOid = repositoryService.addObject(userType.asPrismObject(), null, result); + then("added object is assigned OID and operation is success"); assertThat(sanityUserOid).isNotNull(); + assertThat(userType.getOid()).isEqualTo(sanityUserOid); assertThatOperationResult(result).isSuccess(); } + @Test + public void test200GetObject() throws SchemaException, ObjectNotFoundException { + OperationResult result = createOperationResult(); + + given("cleared performance information"); + SqlPerformanceMonitorImpl pm = repositoryService.getPerformanceMonitor(); + pm.clearGlobalPerformanceInformation(); + assertThat(pm.getGlobalPerformanceInformation().getAllData()).isEmpty(); + + when("getObject is called for known OID"); + PrismObject object = + repositoryService.getObject(UserType.class, sanityUserOid, null, result); + + then("object is obtained and performance monitor is updated"); + assertThatOperationResult(result).isSuccess(); + assertSingleOperationRecorded(pm, RepositoryService.OP_GET_OBJECT); + } + @Test(enabled = false) // TODO deleteObject not implemented yet public void test800DeleteObject() throws ObjectNotFoundException { OperationResult result = createOperationResult(); From f28532f1b631ee2d413655f9e254ed883f3099d3 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 19 May 2021 16:13:26 +0200 Subject: [PATCH 23/43] repo-sqale: added overwrite and first tests, ignoring version in add Now addObject ignores provided version, both for initial (1 is set) and for overwrite (updates version + 1). --- .../repo/sqale/SqaleRepositoryService.java | 89 ++++++++++++++++--- .../AddObjectContext.java} | 44 ++++----- .../func/SqaleRepoAddDeleteObjectTest.java | 85 +++++++++++++++++- 3 files changed, 174 insertions(+), 44 deletions(-) rename repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/{operations/AddObjectOperation.java => update/AddObjectContext.java} (79%) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java index 1f66c9ac0f5..56f16622092 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java @@ -26,17 +26,19 @@ import com.evolveum.midpoint.prism.PrismPropertyValue; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ItemDeltaCollectionsUtil; +import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.delta.PropertyDelta; +import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.repo.api.*; import com.evolveum.midpoint.repo.api.perf.OperationRecord; import com.evolveum.midpoint.repo.api.query.ObjectFilterExpressionEvaluator; -import com.evolveum.midpoint.repo.sqale.operations.AddObjectOperation; import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTableMapping; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.update.AddObjectContext; import com.evolveum.midpoint.repo.sqale.update.RootUpdateContext; import com.evolveum.midpoint.repo.sqlbase.*; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; @@ -260,7 +262,11 @@ public String addObject( object.setVersion("1"); } - return executeAddObject(object, options, operationResult); + return object.getOid() == null || !options.isOverwrite() + ? executeAddObject(object, options, operationResult) + : executeOverwriteObject(object, options, operationResult); + } catch (RepositoryException | RuntimeException e) { + throw handledGeneralException(e, operationResult); } catch (Throwable t) { operationResult.recordFatalError(t); throw t; @@ -275,9 +281,7 @@ private String executeAddObject( @NotNull RepoAddOptions options, @NotNull OperationResult operationResult) throws SchemaException, ObjectAlreadyExistsException { - long opHandle = registerOperationStart( - options.isOverwrite() ? OP_ADD_OBJECT_OVERWRITE : OP_ADD_OBJECT, - object); + long opHandle = registerOperationStart(OP_ADD_OBJECT, object); /* old repo code missing in new repo: int attempt = 1; int restarts = 0; @@ -287,8 +291,8 @@ private String executeAddObject( // TODO use executeAttempts try { - String oid = new AddObjectOperation<>(object, options, operationResult) - .execute(repositoryContext); + String oid = new AddObjectContext<>(repositoryContext, object, options, operationResult) + .execute(); invokeConflictWatchers((w) -> w.afterAddObject(oid, object)); return oid; /* @@ -310,6 +314,52 @@ private String executeAddObject( } } + /** Overwrite is more like update than add. */ + private String executeOverwriteObject( + @NotNull PrismObject newObject, + @NotNull RepoAddOptions options, + @NotNull OperationResult operationResult) + throws SchemaException, RepositoryException, ObjectAlreadyExistsException { + + String oid = newObject.getOid(); + UUID oidUuid = UUID.fromString(oid); + long opHandle = registerOperationStart(OP_ADD_OBJECT_OVERWRITE, newObject); + // TODO use executeAttempts + try (JdbcSession jdbcSession = repositoryContext.newJdbcSession().startTransaction()) { + try { + //noinspection ConstantConditions + RootUpdateContext, MObject> updateContext = + prepareUpdateContext(jdbcSession, newObject.getCompileTimeClass(), oidUuid); + PrismObject prismObject = updateContext.getPrismObject(); + // no precondition check for overwrite + + // TODO beforeModify? then afterModify lower too... +// invokeConflictWatchers(w -> w.beforeModifyObject(prismObject)); + newObject.setUserData(RepositoryService.KEY_ORIGINAL_OBJECT, prismObject.clone()); + ObjectDelta delta = prismObject.diff(newObject, EquivalenceStrategy.LITERAL); + Collection> modifications = delta.getModifications(); + + LOGGER.trace("overwriteAddObjectAttempt: originalOid={}, modifications={}", + oid, modifications); + + updateContext.execute(modifications); + + // TODO do we want this conflict watcher or modify one? +// invokeConflictWatchers((w) -> w.afterAddObject(oid, object)); + LOGGER.trace("OBJECT after:\n{}", prismObject.debugDumpLazily()); + } catch (ObjectNotFoundException e) { + // so it is just plain addObject after all + new AddObjectContext<>(repositoryContext, newObject, options, operationResult) + .execute(); + invokeConflictWatchers((w) -> w.afterAddObject(oid, newObject)); + } + jdbcSession.commit(); + return oid; + } finally { + registerOperationFinish(opHandle, 1); // TODO attempt + } + } + @Override @NotNull public ModifyObjectResult modifyObject( @@ -425,6 +475,7 @@ private ModifyObjectResult executeModifyObject( LOGGER.trace("OBJECT after:\n{}", prismObject.debugDumpLazily()); + invokeConflictWatchers((w) -> w.afterModifyObject(prismObject.getOid())); return new ModifyObjectResult<>(originalObject, prismObject, modifications); } finally { registerOperationFinish(opHandle, 1); // TODO attempt @@ -500,13 +551,7 @@ private void logTraceModifications(@NotNull Collection .addParam("oid", oid) .build(); try { - try (JdbcSession jdbcSession = repositoryContext.newJdbcSession().startTransaction()) { - DeleteObjectResult result = deleteObjectAttempt(type, oidUuid, jdbcSession); - invokeConflictWatchers((w) -> w.afterDeleteObject(oid)); - - jdbcSession.commit(); - return result; - } + return executeDeleteObject(type, oid, oidUuid); } catch (RuntimeException e) { throw handledGeneralException(e, operationResult); } catch (Throwable t) { @@ -517,6 +562,22 @@ private void logTraceModifications(@NotNull Collection } } + @NotNull + private DeleteObjectResult executeDeleteObject( + Class type, String oid, UUID oidUuid) throws ObjectNotFoundException { + + long opHandle = registerOperationStart(OP_DELETE_OBJECT, type); + try (JdbcSession jdbcSession = repositoryContext.newJdbcSession().startTransaction()) { + DeleteObjectResult result = deleteObjectAttempt(type, oidUuid, jdbcSession); + invokeConflictWatchers((w) -> w.afterDeleteObject(oid)); + + jdbcSession.commit(); + return result; + } finally { + registerOperationFinish(opHandle, 1); // TODO attempt + } + } + private , R extends MObject> DeleteObjectResult deleteObjectAttempt(Class type, UUID oid, JdbcSession jdbcSession) throws ObjectNotFoundException { diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/operations/AddObjectOperation.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/AddObjectContext.java similarity index 79% rename from repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/operations/AddObjectOperation.java rename to repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/AddObjectContext.java index c0614196cad..076bcc31245 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/operations/AddObjectOperation.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/AddObjectContext.java @@ -4,7 +4,7 @@ * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ -package com.evolveum.midpoint.repo.sqale.operations; +package com.evolveum.midpoint.repo.sqale.update; import java.util.Objects; import java.util.UUID; @@ -24,35 +24,31 @@ import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; -import com.evolveum.midpoint.repo.sqlbase.SqlRepoContext; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -/* -TODO: implementation note: - Typically I'd use "technical" dependencies in a constructor and then the object/options/result - would be parameters of execute(). Unfortunately I don't know how to do that AND capture - the parametric types in the operations object. I could hide it behind this object and then capture - it in another "actual operation" object, but that does not make any sense. - That's why the creation is with actual parameters and execute() takes technical ones (possibly - some richer "context" object later to provide more dependencies if necessary). - The "context" could go to construction too, but than it would be all mixed too much. Sorry. -*/ -public class AddObjectOperation, R extends MObject> { +/** + * Add object operation context; used only for true add, not overwrite which is more like modify. + */ +public class AddObjectContext, R extends MObject> { + private final SqaleRepoContext repositoryContext; private final PrismObject object; private final RepoAddOptions options; private final OperationResult result; - private SqlRepoContext repositoryContext; private Q root; private QObjectMapping rootMapping; private MObjectType objectType; - public AddObjectOperation(@NotNull PrismObject object, - @NotNull RepoAddOptions options, @NotNull OperationResult result) { + public AddObjectContext( + @NotNull SqaleRepoContext repositoryContext, + @NotNull PrismObject object, + @NotNull RepoAddOptions options, + @NotNull OperationResult result) { + this.repositoryContext = repositoryContext; this.object = object; this.options = options; this.result = result; @@ -61,24 +57,20 @@ public AddObjectOperation(@NotNull PrismObject object, /** * Inserts the object provided to the constructor and returns its OID. */ - public String execute(SqaleRepoContext repositoryContext) + public String execute() throws SchemaException, ObjectAlreadyExistsException { try { // TODO utilize options and result - this.repositoryContext = repositoryContext; + object.setVersion("1"); // initial add always uses 1 as version number Class schemaObjectClass = object.getCompileTimeClass(); objectType = MObjectType.fromSchemaType(schemaObjectClass); rootMapping = repositoryContext.getMappingBySchemaType(schemaObjectClass); root = rootMapping.defaultAlias(); - // we don't want CID generation here, because overwrite works different then normal add - if (object.getOid() == null) { return addObjectWithoutOid(); - } else if (options.isOverwrite()) { - return overwriteObject(); } else { - // OID is not null, but it's not overwrite either + // this also handles overwrite after ObjectNotFoundException return addObjectWithOid(); } } catch (QueryException e) { @@ -90,10 +82,6 @@ public String execute(SqaleRepoContext repositoryContext) } } - private String overwriteObject() { - throw new UnsupportedOperationException(); // TODO - } - private String addObjectWithOid() throws SchemaException { long lastCid = new ContainerValueIdGenerator().generateForNewObject(object); try (JdbcSession jdbcSession = repositoryContext.newJdbcSession().startTransaction()) { @@ -150,6 +138,8 @@ private String addObjectWithoutOid() throws SchemaException { } } + // TODO can be static. Should it move to other SQL exception handling code? + /** Throws more specific exception or returns and then original exception should be rethrown. */ private void handlePostgresException(PSQLException psqlException) throws ObjectAlreadyExistsException { diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java index 58ddd2c4487..1a37bd42e91 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java @@ -77,7 +77,8 @@ public void test100AddNamedUserWithoutOidWorksOk() given("user with a name"); String userName = "user" + getTestNumber(); UserType userType = new UserType(prismContext) - .name(userName); + .name(userName) + .version("5"); // version will be ignored and set to 1 when("adding it to the repository"); String returnedOid = repositoryService.addObject(userType.asPrismObject(), null, result); @@ -90,7 +91,7 @@ public void test100AddNamedUserWithoutOidWorksOk() MUser row = selectOne(u, u.nameOrig.eq(userName)); assertThat(row.oid).isEqualTo(UUID.fromString(returnedOid)); assertThat(row.nameNorm).isNotNull(); // normalized name is stored - assertThat(row.version).isEqualTo(1); // initial version is set + assertThat(row.version).isEqualTo(1); // initial version is set, ignoring provided version // read-only column with value generated/stored in the database assertThat(row.objectType).isEqualTo(MObjectType.USER); assertThat(row.subtypes).isNull(); // we don't store empty lists as empty arrays @@ -139,6 +140,65 @@ public void test102AddWithoutOidIgnoresOverwriteOption() assertThat(users.get(0).oid).isNotNull(); } + @Test + public void test105AddWithOverwriteOption() + throws ObjectAlreadyExistsException, SchemaException { + OperationResult result = createOperationResult(); + + given("user already in the repository"); + long baseCount = count(QUser.class); + String userName = "user" + getTestNumber(); + UserType userType = new UserType(prismContext) + .name(userName); + repositoryService.addObject(userType.asPrismObject(), null, result); + assertThat(count(QUser.class)).isEqualTo(baseCount + 1); + + when("adding it to the repository again with overwrite option"); + userType.setFullName(PolyStringType.fromOrig("Overwritten User")); + userType.setVersion("5"); // should be ignored + repositoryService.addObject(userType.asPrismObject(), createOverwrite(), result); + + then("operation is successful"); + assertThatOperationResult(result).isSuccess(); + + and("existing user row is modified/overwritten"); + assertThat(count(QUser.class)).isEqualTo(baseCount + 1); // no change in count + MUser row = selectObjectByOid(QUser.class, userType.getOid()); + assertThat(row.fullNameOrig).isEqualTo("Overwritten User"); + + and("provided version for overwrite is ignored"); + assertThat(row.version).isEqualTo(2); + } + + @Test + public void test106AddWithOverwriteOptionWithNewOidActsLikeNormalAdd() + throws ObjectAlreadyExistsException, SchemaException { + OperationResult result = createOperationResult(); + + given("user with random OID is not in the repository"); + long baseCount = count(QUser.class); + UUID oid = UUID.randomUUID(); + assertThat(selectNullableObjectByOid(QUser.class, oid)).isNull(); + + when("adding it to the repository again with overwrite option"); + String userName = "user" + getTestNumber(); + UserType userType = new UserType(prismContext) + .oid(oid.toString()) + .name(userName) + .version("5"); + repositoryService.addObject(userType.asPrismObject(), createOverwrite(), result); + + then("operation is successful"); + assertThatOperationResult(result).isSuccess(); + + and("existing user row is modified/overwritten"); + assertThat(count(QUser.class)).isEqualTo(baseCount + 1); // no change in count + MUser row = selectObjectByOid(QUser.class, userType.getOid()); + + and("provided version for overwrite is ignored"); + assertThat(row.version).isEqualTo(1); + } + @Test public void test110AddUserWithProvidedOidWorksOk() throws ObjectAlreadyExistsException, SchemaException { @@ -1258,7 +1318,26 @@ public void test911DeleteUserUsingFocusWorksOk() throws Exception { } @Test - public void test920DeleteAllOtherObjects() throws Exception { + public void test920DeleteOperationUpdatesPerformanceMonitor() + throws ObjectNotFoundException { + OperationResult result = createOperationResult(); + + given("object to delete and cleared performance information"); + UUID userOid = randomExistingOid(QUser.class); + SqlPerformanceMonitorImpl pm = repositoryService.getPerformanceMonitor(); + pm.clearGlobalPerformanceInformation(); + assertThat(pm.getGlobalPerformanceInformation().getAllData()).isEmpty(); + + when("object is deleted from the repository"); + repositoryService.deleteObject(FocusType.class, userOid.toString(), result); + + then("performance monitor is updated"); + assertThatOperationResult(result).isSuccess(); + assertSingleOperationRecorded(pm, RepositoryService.OP_DELETE_OBJECT); + } + + @Test + public void test999DeleteAllOtherObjects() throws Exception { // this doesn't follow given-when-then, sorry OperationResult result = createOperationResult(); From 7c15000b5dac67de86474901a4d75d44d97e8c9f Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Wed, 19 May 2021 21:04:58 +0200 Subject: [PATCH 24/43] Add "distinct" as supported REST option --- .../java/com/evolveum/midpoint/schema/GetOperationOptions.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/GetOperationOptions.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/GetOperationOptions.java index f7df94d53fa..e2c8193a606 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/GetOperationOptions.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/GetOperationOptions.java @@ -1103,6 +1103,9 @@ public static GetOperationOptions fromRestOptions(List options, Definiti if (GetOperationOptionsType.F_RESOLVE_NAMES.getLocalPart().equals(option)) { rv.setResolveNames(true); } + if (GetOperationOptionsType.F_DISTINCT.getLocalPart().equals(option)) { + rv.setDistinct(true); + } // Do NOT set executionPhase here! } From f852353229ac20b0b89bea41a5016e24808033e5 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 19 May 2021 22:59:34 +0200 Subject: [PATCH 25/43] perf-test support: added unstructured dump of SQL queries from listener --- .../repo/sql/testing/SqlRepoTestUtil.java | 20 ++++++++- .../midpoint/repo/sql/BaseSQLRepoTest.java | 3 +- .../test/AbstractIntegrationTest.java | 3 +- .../midpoint/tools/testng/TestMonitor.java | 8 +++- .../tools/testng/TestReportSection.java | 41 +++++++++++++++---- 5 files changed, 62 insertions(+), 13 deletions(-) diff --git a/repo/repo-sql-impl-test/src/main/java/com/evolveum/midpoint/repo/sql/testing/SqlRepoTestUtil.java b/repo/repo-sql-impl-test/src/main/java/com/evolveum/midpoint/repo/sql/testing/SqlRepoTestUtil.java index 3af6942bc8a..a9aaa12d5dd 100644 --- a/repo/repo-sql-impl-test/src/main/java/com/evolveum/midpoint/repo/sql/testing/SqlRepoTestUtil.java +++ b/repo/repo-sql-impl-test/src/main/java/com/evolveum/midpoint/repo/sql/testing/SqlRepoTestUtil.java @@ -61,7 +61,8 @@ private static String checkVersionProgressInternal(String prevVersion, String ne * Returns report callback adding query section to the performance test report. * Note that the section is NOT added if the count of queries is 0. */ - public static TestMonitor.ReportCallback createReportCallback(TestQueryListener testQueryListener) { + public static TestMonitor.ReportCallback reportCallbackQuerySummary( + TestQueryListener testQueryListener) { return testMonitor -> { if (testQueryListener.hasNoEntries()) { return; @@ -73,4 +74,21 @@ public static TestMonitor.ReportCallback createReportCallback(TestQueryListener section.addRow("execution-count", testQueryListener.getExecutionCount()); }; } + + /** + * Returns report callback adding detailed query dump section to the performance test report. + * Note that the section is NOT added if the count of queries is 0. + * This section is more for visual comparison/interpretation than for graphing. + */ + public static TestMonitor.ReportCallback reportCallbackQueryList( + TestQueryListener testQueryListener) { + return testMonitor -> { + if (testQueryListener.hasNoEntries()) { + return; + } + + TestReportSection section = testMonitor.addRawReportSection("query-list"); + testQueryListener.getEntries().forEach(e -> section.addRow(e.query)); + }; + } } diff --git a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/BaseSQLRepoTest.java b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/BaseSQLRepoTest.java index 1898d2735ac..672b0de900a 100644 --- a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/BaseSQLRepoTest.java +++ b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/BaseSQLRepoTest.java @@ -174,7 +174,8 @@ public TestMonitor createTestMonitor() { queryListener.clear(); return super.createTestMonitor() .addReportCallback(TestReportUtil::reportGlobalPerfData) - .addReportCallback(SqlRepoTestUtil.createReportCallback(queryListener)); + .addReportCallback(SqlRepoTestUtil.reportCallbackQuerySummary(queryListener)) + .addReportCallback(SqlRepoTestUtil.reportCallbackQueryList(queryListener)); } protected boolean isUsingH2() { diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java index 2b87af3534f..8fce7880e5b 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java @@ -316,7 +316,8 @@ public TestMonitor createTestMonitor() { return super.createTestMonitor() .addReportCallback(TestReportUtil::reportGlobalPerfData) - .addReportCallback(SqlRepoTestUtil.createReportCallback(queryListener)); + .addReportCallback(SqlRepoTestUtil.reportCallbackQuerySummary(queryListener)) + .addReportCallback(SqlRepoTestUtil.reportCallbackQueryList(queryListener)); } protected TracingProfileType getTestMethodTracingProfile() { diff --git a/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/TestMonitor.java b/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/TestMonitor.java index 10e4486f82f..84b05c9eb9c 100644 --- a/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/TestMonitor.java +++ b/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/TestMonitor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2020 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -113,6 +113,12 @@ public TestReportSection addReportSection(String sectionName) { return reportSection; } + public TestReportSection addRawReportSection(String sectionName) { + TestReportSection reportSection = new TestReportSection(sectionName, true); + reportSections.add(reportSection); + return reportSection; + } + public void dumpReport(String testName) { ReportMetadata reportMetadata = new ReportMetadata(testName); String perfReportPrefix = System.getProperty(PERF_REPORT_PREFIX_PROPERTY_NAME); diff --git a/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/TestReportSection.java b/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/TestReportSection.java index 763ca29793a..1e33b318bda 100644 --- a/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/TestReportSection.java +++ b/tools/test-ng/src/main/java/com/evolveum/midpoint/tools/testng/TestReportSection.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2020 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -19,12 +19,18 @@ public class TestReportSection { public static final char ESCAPE_CHAR = '\\'; private final String sectionName; + private final boolean rawSection; // no CSV header, no test name column private final List rows = new ArrayList<>(); private String[] columnNames; public TestReportSection(String sectionName) { + this(sectionName, false); + } + + public TestReportSection(String sectionName, boolean rawSection) { this.sectionName = sectionName; + this.rawSection = rawSection; } /** @@ -43,19 +49,36 @@ public void addRow(Object... row) { * Dumps the output as CSV including section header preceded by an empty line. * * @param testName common test name used as a first column value (named "test") + * unless {@link #rawSection} is `true` in which case it is skipped */ public void dump(String testName, PrintStream out) { - out.print("\n[" + sectionName + "]\ntest"); - for (String columnName : columnNames) { - out.print(SEPARATOR + format(columnName)); - } + if (rawSection) { + out.print("\n[" + sectionName + "]"); + for (Object[] row : rows) { + StringBuilder sb = new StringBuilder(); + for (Object value : row) { + if (sb.length() > 0) { + sb.append(SEPARATOR); + } + sb.append(format(value)); + } + out.print('\n' + sb.toString()); + } + } else { + // normal CSV output + out.print("\n[" + sectionName + "]\ntest"); + for (String columnName : columnNames) { + out.print(SEPARATOR + format(columnName)); + } - for (Object[] row : rows) { - out.print('\n' + format(testName)); - for (Object value : row) { - out.print(SEPARATOR + format(value)); + for (Object[] row : rows) { + out.print('\n' + format(testName)); + for (Object value : row) { + out.print(SEPARATOR + format(value)); + } } } + out.println(); } From d91298cc7b4afd3b6db997550d51abb2d5e3e4e0 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 20 May 2021 09:34:30 +0200 Subject: [PATCH 26/43] repo-sqale: more overwrite related tests, fixed conflictWatchers --- .../repo/sqale/SqaleRepositoryService.java | 6 +- .../repo/sqale/SqaleRepoBaseTest.java | 2 +- .../func/SqaleRepoAddDeleteObjectTest.java | 117 +++++++++++++++++- 3 files changed, 115 insertions(+), 10 deletions(-) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java index 56f16622092..44e57f3da41 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryService.java @@ -333,8 +333,7 @@ private String executeOverwriteObject( PrismObject prismObject = updateContext.getPrismObject(); // no precondition check for overwrite - // TODO beforeModify? then afterModify lower too... -// invokeConflictWatchers(w -> w.beforeModifyObject(prismObject)); + invokeConflictWatchers(w -> w.beforeModifyObject(prismObject)); newObject.setUserData(RepositoryService.KEY_ORIGINAL_OBJECT, prismObject.clone()); ObjectDelta delta = prismObject.diff(newObject, EquivalenceStrategy.LITERAL); Collection> modifications = delta.getModifications(); @@ -344,8 +343,7 @@ private String executeOverwriteObject( updateContext.execute(modifications); - // TODO do we want this conflict watcher or modify one? -// invokeConflictWatchers((w) -> w.afterAddObject(oid, object)); + invokeConflictWatchers((w) -> w.afterModifyObject(oid)); LOGGER.trace("OBJECT after:\n{}", prismObject.debugDumpLazily()); } catch (ObjectNotFoundException e) { // so it is just plain addObject after all diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/SqaleRepoBaseTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/SqaleRepoBaseTest.java index 91f0b15f5f9..7231838f209 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/SqaleRepoBaseTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/SqaleRepoBaseTest.java @@ -241,7 +241,7 @@ protected void assertSingleOperationRecorded(SqlPerformanceMonitorImpl pm, Strin assertThat(pmAllData).hasSize(1); Map.Entry perfEntry = pmAllData.entrySet().iterator().next(); - assertThat(perfEntry.getKey()).startsWith(opKind); + assertThat(perfEntry.getKey()).isEqualTo(opKind); OperationPerformanceInformation operationInfo = perfEntry.getValue(); assertThat(operationInfo.getInvocationCount()).isEqualTo(1); assertThat(operationInfo.getExecutionCount()).isEqualTo(1); diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java index 1a37bd42e91..898115e32b3 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java @@ -141,7 +141,7 @@ public void test102AddWithoutOidIgnoresOverwriteOption() } @Test - public void test105AddWithOverwriteOption() + public void test110AddWithOverwriteOption() throws ObjectAlreadyExistsException, SchemaException { OperationResult result = createOperationResult(); @@ -171,7 +171,7 @@ public void test105AddWithOverwriteOption() } @Test - public void test106AddWithOverwriteOptionWithNewOidActsLikeNormalAdd() + public void test111AddWithOverwriteOptionWithNewOidActsLikeNormalAdd() throws ObjectAlreadyExistsException, SchemaException { OperationResult result = createOperationResult(); @@ -199,8 +199,68 @@ public void test106AddWithOverwriteOptionWithNewOidActsLikeNormalAdd() assertThat(row.version).isEqualTo(1); } + // detailed container tests are from test200 on, this one has overwrite priority :-) @Test - public void test110AddUserWithProvidedOidWorksOk() + public void test112OverwriteWithContainers() + throws ObjectAlreadyExistsException, SchemaException { + OperationResult result = createOperationResult(); + + given("user with container in the repository"); + long baseCount = count(QUser.class); + + UUID assConstructionRef = UUID.randomUUID(); + QName assConstructionRel = QName.valueOf("{https://random.org/ns}const-rel"); + String userName = "user" + getTestNumber(); + UserType user1 = new UserType(prismContext) + .name(userName) + .assignment(new AssignmentType(prismContext) + .id(2L) // assigned CID to make things simple for tracking + .construction(new ConstructionType(prismContext) + .resourceRef(assConstructionRef.toString(), + ResourceType.COMPLEX_TYPE, assConstructionRel))); + repositoryService.addObject(user1.asPrismObject(), null, result); + assertThat(count(QUser.class)).isEqualTo(baseCount + 1); + + UUID userOid = UUID.fromString(user1.getOid()); + QAssignment qa = QAssignmentMapping.getAssignment().defaultAlias(); + List assRows = select(qa, qa.ownerOid.eq(userOid)); + assertThat(assRows).hasSize(1) + // construction/resourceRef is set + .anyMatch(aRow -> aRow.resourceRefTargetOid != null + && aRow.resourceRefTargetType != null + && aRow.resourceRefRelationId != null); + + when("using overwrite with changed container identified by id"); + UserType user2 = new UserType(prismContext) + .oid(user1.getOid()) + .version("5") // should be ignored + .name(userName) + .assignment(new AssignmentType(prismContext) + .id(2L) + // no construction + .targetRef(UUID.randomUUID().toString(), RoleType.COMPLEX_TYPE)); + repositoryService.addObject(user2.asPrismObject(), createOverwrite(), result); + + then("operation is successful"); + assertThatOperationResult(result).isSuccess(); + + and("identified container is modified/overwritten"); + assertThat(count(QUser.class)).isEqualTo(baseCount + 1); // no change in count + MUser row = selectObjectByOid(QUser.class, user2.getOid()); + assRows = select(qa, qa.ownerOid.eq(UUID.fromString(user2.getOid()))); + assertThat(assRows).hasSize(1) + .anyMatch(aRow -> aRow.resourceRefTargetOid == null // removed + && aRow.resourceRefTargetType == null // removed + && aRow.resourceRefRelationId == null // removed + && aRow.targetRefTargetOid != null // added + && aRow.targetRefTargetType == MObjectType.ROLE); + + and("provided version for overwrite is ignored"); + assertThat(row.version).isEqualTo(2); + } + + @Test + public void test120AddUserWithProvidedOidWorksOk() throws ObjectAlreadyExistsException, SchemaException { OperationResult result = createOperationResult(); @@ -227,7 +287,7 @@ public void test110AddUserWithProvidedOidWorksOk() } @Test - public void test111AddSecondObjectWithTheSameOidThrowsObjectAlreadyExists() + public void test121AddSecondObjectWithTheSameOidThrowsObjectAlreadyExists() throws ObjectAlreadyExistsException, SchemaException { OperationResult result = createOperationResult(); @@ -242,7 +302,7 @@ public void test111AddSecondObjectWithTheSameOidThrowsObjectAlreadyExists() long baseCount = count(QUser.class); UserType user2 = new UserType(prismContext) .oid(providedOid.toString()) - .name("user" + getTestNumber() + "-different-name"); + .name("user" + getTestNumber()); then("operation fails and no new user row is created"); assertThatThrownBy(() -> repositoryService.addObject(user2.asPrismObject(), null, result)) @@ -252,6 +312,32 @@ public void test111AddSecondObjectWithTheSameOidThrowsObjectAlreadyExists() assertCount(QUser.class, baseCount); } + @Test + public void test122AddSecondObjectWithTheSameOidWithOverwriteIsOk() + throws ObjectAlreadyExistsException, SchemaException { + OperationResult result = createOperationResult(); + + given("user with provided OID already exists"); + UUID providedOid = UUID.randomUUID(); + UserType user1 = new UserType(prismContext) + .oid(providedOid.toString()) + .name("user" + getTestNumber()); + repositoryService.addObject(user1.asPrismObject(), null, result); + + when("adding it again with overwrite without any changes"); + long baseCount = count(QObject.CLASS); + UserType user2 = new UserType(prismContext) + .oid(providedOid.toString()) + .name("user" + getTestNumber()); + repositoryService.addObject(user2.asPrismObject(), createOverwrite(), result); + + then("operation is success and no changes are made (delta is empty)"); + assertThatOperationResult(result).isSuccess(); + assertCount(QObject.CLASS, baseCount); // no new object was created + MUser row = selectObjectByOid(QUser.class, providedOid); + assertThat(row.version).isEqualTo(1); // version is still initial, no change + } + @Test public void test150AddOperationUpdatesPerformanceMonitor() throws ObjectAlreadyExistsException, SchemaException { @@ -271,6 +357,27 @@ public void test150AddOperationUpdatesPerformanceMonitor() assertSingleOperationRecorded(pm, RepositoryService.OP_ADD_OBJECT); } + @Test + public void test151OverwriteOperationUpdatesPerformanceMonitor() + throws ObjectAlreadyExistsException, SchemaException { + OperationResult result = createOperationResult(); + + given("existing object for overwrite and cleared performance information"); + UserType userType = new UserType(prismContext).name("user" + getTestNumber()); + repositoryService.addObject(userType.asPrismObject(), null, result); + + SqlPerformanceMonitorImpl pm = repositoryService.getPerformanceMonitor(); + pm.clearGlobalPerformanceInformation(); + assertThat(pm.getGlobalPerformanceInformation().getAllData()).isEmpty(); + + when("object is added to the repository"); + repositoryService.addObject(userType.asPrismObject(), createOverwrite(), result); + + then("performance monitor is updated"); + assertThatOperationResult(result).isSuccess(); + assertSingleOperationRecorded(pm, RepositoryService.OP_ADD_OBJECT_OVERWRITE); + } + @Test public void test200AddObjectWithMultivalueContainers() throws ObjectAlreadyExistsException, SchemaException { From e8050dc24742fbe590e2cdbb36e72869e315af43 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 20 May 2021 14:09:38 +0200 Subject: [PATCH 27/43] SqaleRepoModifyObjectTest: test with invalid deltas like assignment/5 This covers seemingly reasonable but naive alternatives like adding, replacing or deleting assignment at the path ending with ID. All throw the same exception immediately during delta construction. --- .../sqale/func/SqaleRepoModifyObjectTest.java | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java index c375ac60320..f9bb90c6d54 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java @@ -1883,7 +1883,7 @@ public void test304MultipleModificationsOfExistingAssignment() } @Test - public void test305DeleteAssignmentByContent() + public void test305DeleteAssignmentByContent() // or by value, or by equality throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { OperationResult result = createOperationResult(); MUser originalRow = selectObjectByOid(QUser.class, user1Oid); @@ -2040,10 +2040,46 @@ public void test312AddingAssignmentWithUsedBytFreeCid() && aRow.orderValue == 1); } - // TODO delete by pattern - as per ItemImpl.remove(V, EquivalenceStrategy) + @Test + public void test320AddingAssignmentWithItemPathEndingWithCidIsIllegal() { + expect("creating delta adding assignment to path ending with CID throws exception"); + assertThatThrownBy( + () -> prismContext.deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT, 5).add( + new AssignmentType(prismContext).order(5)) + .asObjectDelta(user1Oid)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid delta path assignment/5." + + " Delta path must always point to item, not to value"); + } + + @Test + public void test321AddingAssignmentWithItemPathEndingWithCidIsIllegal() { + expect("creating delta replacing assignment at path ending with CID throws exception"); + assertThatThrownBy( + () -> prismContext.deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT, 5).replace( + new AssignmentType(prismContext).order(5)) + .asObjectDelta(user1Oid)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid delta path assignment/5." + + " Delta path must always point to item, not to value"); + } @Test - public void test319DeleteAllAssignments() + public void test322DeleteAssignmentWithItemPathEndingWithCidIsIllegal() { + expect("creating delta deleting assignment path ending with CID throws exception"); + assertThatThrownBy( + () -> prismContext.deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT, 5).delete() + .asObjectDelta(user1Oid)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid delta path assignment/5." + + " Delta path must always point to item, not to value"); + } + + @Test + public void test399DeleteAllAssignments() throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { OperationResult result = createOperationResult(); MUser originalRow = selectObjectByOid(QUser.class, user1Oid); @@ -2137,6 +2173,7 @@ public void test920ModifyOperationUpdatesPerformanceMonitor() assertThatOperationResult(result).isSuccess(); assertSingleOperationRecorded(pm, RepositoryService.OP_MODIFY_OBJECT); } + @Test public void test990ChangeOfNonPersistedAttributeWorksOk() throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { From d2459bf65ca4ee9881fc98d1107f4f3d68179dc7 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 20 May 2021 23:30:22 +0200 Subject: [PATCH 28/43] SqaleRepoSearchObjectTest: added inOid tests --- .../sqale/func/SqaleRepoSearchObjectTest.java | 65 ++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java index 92f65c4416e..d831a02eb81 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java @@ -400,6 +400,69 @@ public void test230QueryForChildrenOfAnyTypeWithAnotherCondition() throws Schema // TODO child/parent tests // endregion + // region other filters: inOid, type, any + @Test + public void test300QueryForObjectsWithInOid() throws SchemaException { + when("searching objects by list of OIDs"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(ObjectType.class, + prismContext.queryFor(ObjectType.class) + .id(user1Oid, task1Oid, org2Oid) + .build(), + operationResult); + + then("all objects with specified OIDs are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).hasSize(3) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(user1Oid, task1Oid, org2Oid); + } + + @Test + public void test301QueryForFocusWithInOid() throws SchemaException { + when("searching focus objects by list of OIDs"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(FocusType.class, + prismContext.queryFor(FocusType.class) + .id(user1Oid, task1Oid, org2Oid) // task will not match, it's not a focus + .build(), + operationResult); + + then("all focus objects with specified OIDs are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).hasSize(2) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(user1Oid, org2Oid); + } + + /* + @Test + public void test310QueryWithTypeFilter() throws SchemaException { + when("query includes type filter"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(ObjectType.class, + prismContext.queryFor(ObjectType.class) + .type(FocusType.class) + .block() + .id(user1Oid, task1Oid, org2Oid) // task will not match, it's not a focus +// .and() TODO I want this +// .item(FocusType.F_COST_CENTER).eq("5") + .endBlock() + .build(), + operationResult); + + then("search is narrowed only to objects of specific type"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).hasSize(2) + .extracting(o -> o.getOid()) + .containsExactlyInAnyOrder(user1Oid, org2Oid); + } + */ + + // TODO TYPE tests + // TODO EXISTS tests + // endregion + // region special cases @Test public void test900SearchByWholeContainerIsNotPossible() { @@ -424,7 +487,7 @@ public void test950SearchOperationUpdatesPerformanceMonitor() throws SchemaExcep assertThat(pm.getGlobalPerformanceInformation().getAllData()).isEmpty(); when("search is called on the repository"); - SearchResultList result = searchObjects(FocusType.class, + searchObjects(FocusType.class, prismContext.queryFor(FocusType.class).build(), operationResult); From 76b503601863cf2e8c416555aa1e6e2739c0820f Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Mon, 24 May 2021 10:30:00 +0200 Subject: [PATCH 29/43] Sqale: m_case -> m_case_wi sans outcome --- repo/repo-sqale/sql/pgnew-repo.sql | 53 +++++------- .../repo/sqale/SqaleRepositoryBeanConfig.java | 3 + .../qmodel/cases/workitem/QCaseWorkItem.java | 22 ++--- .../sqale/qmodel/common/MContainerType.java | 3 +- .../func/SqaleRepoAddDeleteObjectTest.java | 81 ++++++++++++++++++- 5 files changed, 110 insertions(+), 52 deletions(-) diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index 247a93e4777..f3b2b7d6939 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -34,7 +34,8 @@ CREATE TYPE ContainerType AS ENUM ( 'INDUCEMENT', 'LOOKUP_TABLE_ROW', 'OPERATION_EXECUTION', - 'TRIGGER'); + 'TRIGGER', + 'CASE_WORK_ITEM'); CREATE TYPE ObjectType AS ENUM ( 'ABSTRACT_ROLE', @@ -1086,7 +1087,6 @@ CREATE TRIGGER m_case_oid_delete_tr AFTER DELETE ON m_case CREATE INDEX m_case_nameOrig_idx ON m_case (nameOrig); ALTER TABLE m_case ADD CONSTRAINT m_case_nameNorm_key UNIQUE (nameNorm); -/* CREATE INDEX iCaseTypeObjectRefTargetOid ON m_case(objectRefTargetOid); CREATE INDEX iCaseTypeTargetRefTargetOid ON m_case(targetRefTargetOid); CREATE INDEX iCaseTypeParentRefTargetOid ON m_case(parentRefTargetOid); @@ -1094,40 +1094,23 @@ CREATE INDEX iCaseTypeRequestorRefTargetOid ON m_case(requestorRefTargetOid); CREATE INDEX iCaseTypeCloseTimestamp ON m_case(closeTimestamp); CREATE TABLE m_case_wi ( - id INTEGER NOT NULL, - owner_oid UUID NOT NULL, - closeTimestamp TIMESTAMPTZ, - createTimestamp TIMESTAMPTZ, - deadline TIMESTAMPTZ, - originalAssigneeRefTargetOid UUID, - originalAssigneeRefTargetType ObjectType, - originalAssigneeRefRelationId INTEGER REFERENCES m_uri(id), - outcome TEXT, - performerRefTargetOid UUID, - performerRefTargetType ObjectType, - performerRefRelationId INTEGER REFERENCES m_uri(id), - stageNumber INTEGER, - PRIMARY KEY (owner_oid, id) - -); - -ALTER TABLE m_case_wi - ADD CONSTRAINT fk_case_wi_owner FOREIGN KEY (ownerOid) REFERENCES m_case; - -CREATE TABLE m_case_wi_reference ( - owner_id INTEGER NOT NULL, - owner_ownerOid UUID NOT NULL, - reference_type INTEGER NOT NULL, - relation VARCHAR(157) NOT NULL, - targetOid UUID NOT NULL, - targetType INTEGER, - PRIMARY KEY (owner_ownerOid, owner_id, reference_type, targetOid, relation) -); + ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + containerType ContainerType GENERATED ALWAYS AS ('CASE_WORK_ITEM') STORED, + closeTimestamp TIMESTAMPTZ, + createTimestamp TIMESTAMPTZ, + deadline TIMESTAMPTZ, + originalAssigneeRefTargetOid UUID, + originalAssigneeRefTargetType ObjectType, + originalAssigneeRefRelationId INTEGER REFERENCES m_uri(id), + outcome TEXT, + performerRefTargetOid UUID, + performerRefTargetType ObjectType, + performerRefRelationId INTEGER REFERENCES m_uri(id), + stageNumber INTEGER, + PRIMARY KEY (ownerOid, cid) +) + INHERITS(m_container); -ALTER TABLE m_case_wi_reference - ADD CONSTRAINT fk_case_wi_reference_owner FOREIGN KEY (owner_ownerOid, owner_id) REFERENCES m_case_wi; -CREATE INDEX iCaseWorkItemRefTargetOid ON m_case_wi_reference (targetOid); -*/ -- endregion -- region Access Certification object tables diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryBeanConfig.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryBeanConfig.java index b70a1dfb7ed..da541bd47a4 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryBeanConfig.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/SqaleRepositoryBeanConfig.java @@ -8,6 +8,8 @@ import javax.sql.DataSource; +import com.evolveum.midpoint.repo.sqale.qmodel.cases.workitem.QCaseWorkItemMapping; + import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -130,6 +132,7 @@ public SqaleRepoContext sqlRepoContext( .register(AssignmentType.COMPLEX_TYPE, QAssignmentMapping.initAssignment(repositoryContext)) .register(CaseType.COMPLEX_TYPE, QCaseMapping.init(repositoryContext)) + .register(CaseWorkItemType.COMPLEX_TYPE, QCaseWorkItemMapping.init(repositoryContext)) .register(DashboardType.COMPLEX_TYPE, QDashboardMapping.init(repositoryContext)) .register(FocusType.COMPLEX_TYPE, QFocusMapping.init(repositoryContext)) .register(FormType.COMPLEX_TYPE, QFormMapping.init(repositoryContext)) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItem.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItem.java index ec24d0b0035..f240765b17d 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItem.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItem.java @@ -6,23 +6,16 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.cases.workitem; -import com.evolveum.midpoint.repo.sqale.qmodel.assignment.MAssignment; -import com.evolveum.midpoint.repo.sqale.qmodel.cases.MCase; -import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainer; -import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; -import com.evolveum.midpoint.repo.sqlbase.querydsl.JsonbPath; -import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TimeIntervalStatusType; +import java.sql.Types; +import java.time.Instant; import com.querydsl.core.types.dsl.*; import com.querydsl.sql.ColumnMetadata; -import java.sql.Types; -import java.time.Instant; - -import static com.evolveum.midpoint.repo.sqlbase.querydsl.JsonbPath.JSONB_TYPE; +import com.evolveum.midpoint.repo.sqale.qmodel.cases.MCase; +import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainer; +import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; +import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath; /** * Querydsl query type for {@value #TABLE_NAME} table. @@ -36,7 +29,7 @@ public class QCaseWorkItem extends QContainer> CLASS = (Class) QContainer.class; + public static final Class> CLASS = (Class) QCaseWorkItem.class; public static final String TABLE_NAME = "m_case_wi"; @@ -87,7 +80,6 @@ public class QCaseWorkItem extends QContainer stageNumber = createInteger("stageNumber", STAGE_NUMBER); - public QCaseWorkItem(String variable) { this(variable, DEFAULT_SCHEMA_NAME, TABLE_NAME); } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/MContainerType.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/MContainerType.java index c3f71524b78..a7f6f8e154f 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/MContainerType.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/MContainerType.java @@ -16,5 +16,6 @@ public enum MContainerType { INDUCEMENT, // also represented by AssignmentType LOOKUP_TABLE_ROW, OPERATION_EXECUTION, - TRIGGER + TRIGGER, + CASE_WORK_ITEM } diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java index 898115e32b3..8b0f85a484c 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java @@ -28,6 +28,8 @@ import com.evolveum.midpoint.repo.sqale.qmodel.assignment.*; import com.evolveum.midpoint.repo.sqale.qmodel.cases.MCase; import com.evolveum.midpoint.repo.sqale.qmodel.cases.QCase; +import com.evolveum.midpoint.repo.sqale.qmodel.cases.workitem.MCaseWorkItem; +import com.evolveum.midpoint.repo.sqale.qmodel.cases.workitem.QCaseWorkItem; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainer; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainerType; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainer; @@ -1324,6 +1326,19 @@ public void test850Case() throws Exception { QName requestorRelation = QName.valueOf("{https://random.org/ns}case-requestor-rel"); UUID targetOid = UUID.randomUUID(); QName targetRelation = QName.valueOf("{https://random.org/ns}case-target-rel"); + UUID originalAssignee1Oid = UUID.randomUUID(); + QName originalAssignee1Relation = QName.valueOf("{https://random.org/ns}original-assignee1-rel"); + UUID performer1Oid = UUID.randomUUID(); + QName performer1Relation = QName.valueOf("{https://random.org/ns}performer1-rel"); + UUID originalAssignee2Oid = UUID.randomUUID(); + QName originalAssignee2Relation = QName.valueOf("{https://random.org/ns}original-assignee2-rel"); + UUID performer2Oid = UUID.randomUUID(); + QName performer2Relation = QName.valueOf("{https://random.org/ns}performer2-rel"); + + AbstractWorkItemOutputType output1 = new AbstractWorkItemOutputType(prismContext) + .outcome("OUTCOME one"); + AbstractWorkItemOutputType output2 = new AbstractWorkItemOutputType(prismContext) + .outcome("OUTCOME two"); CaseType acase = new CaseType(prismContext) .name(objectName) @@ -1336,7 +1351,31 @@ public void test850Case() throws Exception { .requestorRef(requestorOid.toString(), UserType.COMPLEX_TYPE, requestorRelation) .targetRef(targetOid.toString(), - OrgType.COMPLEX_TYPE, targetRelation); + OrgType.COMPLEX_TYPE, targetRelation) + .beginWorkItem() + .id(41L) + .createTimestamp(MiscUtil.asXMLGregorianCalendar(10000L)) + .closeTimestamp(MiscUtil.asXMLGregorianCalendar(10100L)) + .deadline(MiscUtil.asXMLGregorianCalendar(10200L)) + .originalAssigneeRef(originalAssignee1Oid.toString(), + OrgType.COMPLEX_TYPE, originalAssignee1Relation) + .performerRef(performer1Oid.toString(), + UserType.COMPLEX_TYPE, performer1Relation) + .stageNumber(1) + .output(output1) + .end() + .beginWorkItem() + .id(42L) + .createTimestamp(MiscUtil.asXMLGregorianCalendar(20000L)) + .closeTimestamp(MiscUtil.asXMLGregorianCalendar(20100L)) + .deadline(MiscUtil.asXMLGregorianCalendar(20200L)) + .originalAssigneeRef(originalAssignee2Oid.toString(), + UserType.COMPLEX_TYPE, originalAssignee2Relation) + .performerRef(performer2Oid.toString(), + UserType.COMPLEX_TYPE, performer2Relation) + .stageNumber(2) + .output(output2) + .end(); when("adding it to the repository"); repositoryService.addObject(acase.asPrismObject(), null, result); @@ -1359,6 +1398,46 @@ public void test850Case() throws Exception { assertThat(row.targetRefTargetOid).isEqualTo(targetOid); assertThat(row.targetRefTargetType).isEqualTo(MObjectType.ORG); assertCachedUri(row.targetRefRelationId, targetRelation); + + QCaseWorkItem t = aliasFor(QCaseWorkItem.CLASS); + List wiRows = select(t, t.ownerOid.eq(UUID.fromString(acase.getOid()))); + assertThat(wiRows).hasSize(2); + + wiRows.sort(comparing(tr -> tr.cid)); + + MCaseWorkItem wiRow = wiRows.get(0); + assertThat(wiRow.cid).isEqualTo(41); // assigned in advance + assertThat(wiRow.ownerOid.toString()).isEqualTo(acase.getOid()); + assertThat(wiRow.containerType).isEqualTo(MContainerType.CASE_WORK_ITEM); + assertThat(wiRow.createTimestamp).isEqualTo(Instant.ofEpochMilli(10000)); + assertThat(wiRow.closeTimestamp).isEqualTo(Instant.ofEpochMilli(10100)); + assertThat(wiRow.deadline).isEqualTo(Instant.ofEpochMilli(10200)); + assertThat(wiRow.originalAssigneeRefTargetOid).isEqualTo(originalAssignee1Oid); + assertThat(wiRow.originalAssigneeRefTargetType).isEqualTo(MObjectType.ORG); + assertCachedUri(wiRow.originalAssigneeRefRelationId, originalAssignee1Relation); + // TODO: outcome +// assertThat(wiRow.outcome).isEqualTo("OUTCOME one"); + assertThat(wiRow.performerRefTargetOid).isEqualTo(performer1Oid); + assertThat(wiRow.performerRefTargetType).isEqualTo(MObjectType.USER); + assertCachedUri(wiRow.performerRefRelationId, performer1Relation); + assertThat(wiRow.stageNumber).isEqualTo(1); + + wiRow = wiRows.get(1); + assertThat(wiRow.cid).isEqualTo(42); // assigned in advance + assertThat(wiRow.ownerOid.toString()).isEqualTo(acase.getOid()); + assertThat(wiRow.containerType).isEqualTo(MContainerType.CASE_WORK_ITEM); + assertThat(wiRow.createTimestamp).isEqualTo(Instant.ofEpochMilli(20000)); + assertThat(wiRow.closeTimestamp).isEqualTo(Instant.ofEpochMilli(20100)); + assertThat(wiRow.deadline).isEqualTo(Instant.ofEpochMilli(20200)); + assertThat(wiRow.originalAssigneeRefTargetOid).isEqualTo(originalAssignee2Oid); + assertThat(wiRow.originalAssigneeRefTargetType).isEqualTo(MObjectType.USER); + assertCachedUri(wiRow.originalAssigneeRefRelationId, originalAssignee2Relation); + // TODO: outcome +// assertThat(wiRow.outcome).isEqualTo("OUTCOME two"); + assertThat(wiRow.performerRefTargetOid).isEqualTo(performer2Oid); + assertThat(wiRow.performerRefTargetType).isEqualTo(MObjectType.USER); + assertCachedUri(wiRow.performerRefRelationId, performer2Relation); + assertThat(wiRow.stageNumber).isEqualTo(2); } // endregion From 720e168c1bcd0f34d06cbc433abad7612628c39c Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Mon, 24 May 2021 11:34:48 +0200 Subject: [PATCH 30/43] repo-sqale: pair/peer review of QCaseWorkItem+mapping+test --- repo/repo-sqale/sql/pgnew-repo.sql | 4 +- .../qmodel/cases/workitem/QCaseWorkItem.java | 10 +--- .../cases/workitem/QCaseWorkItemMapping.java | 45 ++++++++--------- .../sqale/qmodel/common/MContainerType.java | 6 +-- .../func/SqaleRepoAddDeleteObjectTest.java | 48 +++++++++---------- 5 files changed, 49 insertions(+), 64 deletions(-) diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index f3b2b7d6939..44e8074e83e 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -31,11 +31,11 @@ CREATE TYPE ContainerType AS ENUM ( 'ACCESS_CERTIFICATION_CASE', 'ACCESS_CERTIFICATION_WORK_ITEM', 'ASSIGNMENT', + 'CASE_WORK_ITEM', 'INDUCEMENT', 'LOOKUP_TABLE_ROW', 'OPERATION_EXECUTION', - 'TRIGGER', - 'CASE_WORK_ITEM'); + 'TRIGGER'); CREATE TYPE ObjectType AS ENUM ( 'ABSTRACT_ROLE', diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItem.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItem.java index f240765b17d..d0d8bb3d7cf 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItem.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItem.java @@ -21,16 +21,10 @@ * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QCaseWorkItem extends QContainer { +public class QCaseWorkItem extends QContainer { private static final long serialVersionUID = 341727922218837393L; - /** - * If `QCaseWorkItem.class` is not enough because of generics, try `QCaseWorkItem.CLASS`. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static final Class> CLASS = (Class) QCaseWorkItem.class; - public static final String TABLE_NAME = "m_case_wi"; public static final ColumnMetadata CLOSE_TIMESTAMP = @@ -89,7 +83,7 @@ public QCaseWorkItem(String variable, String schema, String table) { } @Override - public BooleanExpression isOwnedBy(OR ownerRow) { + public BooleanExpression isOwnedBy(MCase ownerRow) { return ownerOid.eq(ownerRow.oid); } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java index cf440ed0d3d..1f6d41684d2 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java @@ -6,52 +6,44 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.cases.workitem; +import static com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType.*; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.cases.MCase; -import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainerType; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainerMapping; -import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QTriggerMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -import static com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType.*; - /** * Mapping between {@link QCaseWorkItem} and {@link CaseWorkItemType}. - * - * @param type of the owner row */ -public class QCaseWorkItemMapping - extends QContainerMapping, MCaseWorkItem, OR> { +public class QCaseWorkItemMapping + extends QContainerMapping { public static final String DEFAULT_ALIAS_NAME = "cswi"; - private static QCaseWorkItemMapping instance; + private static QCaseWorkItemMapping instance; - public static QCaseWorkItemMapping init( + public static QCaseWorkItemMapping init( @NotNull SqaleRepoContext repositoryContext) { if (instance == null) { - instance = new QCaseWorkItemMapping<>(repositoryContext); + instance = new QCaseWorkItemMapping(repositoryContext); } return get(); } - public static QCaseWorkItemMapping get() { - //noinspection unchecked - return (QCaseWorkItemMapping) Objects.requireNonNull(instance); + public static QCaseWorkItemMapping get() { + return Objects.requireNonNull(instance); } - // We can't declare Class>.class, so we cheat a bit. - @SuppressWarnings({ "unchecked", "rawtypes" }) private QCaseWorkItemMapping(@NotNull SqaleRepoContext repositoryContext) { super(QCaseWorkItem.TABLE_NAME, DEFAULT_ALIAS_NAME, - CaseWorkItemType.class, (Class) QCaseWorkItem.class, repositoryContext); + CaseWorkItemType.class, QCaseWorkItem.class, repositoryContext); addItemMapping(F_CLOSE_TIMESTAMP, timestampMapper(q -> q.closeTimestamp)); addItemMapping(F_CREATE_TIMESTAMP, timestampMapper(q -> q.createTimestamp)); @@ -63,6 +55,7 @@ private QCaseWorkItemMapping(@NotNull SqaleRepoContext repositoryContext) { q -> q.originalAssigneeRefRelationId)); // TODO: OUTCOME + // see QFocusMapping:61, F_OUTPUT/F_OUTCOME // addItemMapping(F_OUTCOME, stringMapper(q -> q.outcome)); addItemMapping(F_PERFORMER_REF, refMapper( @@ -75,8 +68,8 @@ private QCaseWorkItemMapping(@NotNull SqaleRepoContext repositoryContext) { } @Override - protected QCaseWorkItem newAliasInstance(String alias) { - return new QCaseWorkItem<>(alias); + protected QCaseWorkItem newAliasInstance(String alias) { + return new QCaseWorkItem(alias); } @Override @@ -86,7 +79,7 @@ public MCaseWorkItem newRowObject() { } @Override - public MCaseWorkItem newRowObject(OR ownerRow) { + public MCaseWorkItem newRowObject(MCase ownerRow) { MCaseWorkItem row = newRowObject(); row.ownerOid = ownerRow.oid; return row; @@ -95,7 +88,7 @@ public MCaseWorkItem newRowObject(OR ownerRow) { // about duplication see the comment in QObjectMapping.toRowObjectWithoutFullObject @SuppressWarnings("DuplicatedCode") @Override - public MCaseWorkItem insert(CaseWorkItemType workItem, OR ownerRow, JdbcSession jdbcSession) { + public MCaseWorkItem insert(CaseWorkItemType workItem, MCase ownerRow, JdbcSession jdbcSession) { MCaseWorkItem row = initRowObject(workItem, ownerRow); row.closeTimestamp = MiscUtil.asInstant(workItem.getCloseTimestamp()); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/MContainerType.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/MContainerType.java index a7f6f8e154f..8e1237d595f 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/MContainerType.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/MContainerType.java @@ -13,9 +13,9 @@ public enum MContainerType { ACCESS_CERTIFICATION_CASE, ACCESS_CERTIFICATION_WORK_ITEM, ASSIGNMENT, - INDUCEMENT, // also represented by AssignmentType + CASE_WORK_ITEM, + INDUCEMENT, // also represented by AssignmentType, LOOKUP_TABLE_ROW, OPERATION_EXECUTION, - TRIGGER, - CASE_WORK_ITEM + TRIGGER } diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java index 8b0f85a484c..75587be47c9 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java @@ -1352,30 +1352,28 @@ public void test850Case() throws Exception { UserType.COMPLEX_TYPE, requestorRelation) .targetRef(targetOid.toString(), OrgType.COMPLEX_TYPE, targetRelation) - .beginWorkItem() - .id(41L) - .createTimestamp(MiscUtil.asXMLGregorianCalendar(10000L)) - .closeTimestamp(MiscUtil.asXMLGregorianCalendar(10100L)) - .deadline(MiscUtil.asXMLGregorianCalendar(10200L)) - .originalAssigneeRef(originalAssignee1Oid.toString(), - OrgType.COMPLEX_TYPE, originalAssignee1Relation) - .performerRef(performer1Oid.toString(), - UserType.COMPLEX_TYPE, performer1Relation) - .stageNumber(1) - .output(output1) - .end() - .beginWorkItem() - .id(42L) - .createTimestamp(MiscUtil.asXMLGregorianCalendar(20000L)) - .closeTimestamp(MiscUtil.asXMLGregorianCalendar(20100L)) - .deadline(MiscUtil.asXMLGregorianCalendar(20200L)) - .originalAssigneeRef(originalAssignee2Oid.toString(), - UserType.COMPLEX_TYPE, originalAssignee2Relation) - .performerRef(performer2Oid.toString(), - UserType.COMPLEX_TYPE, performer2Relation) - .stageNumber(2) - .output(output2) - .end(); + .workItem(new CaseWorkItemType(prismContext) + .id(41L) + .createTimestamp(MiscUtil.asXMLGregorianCalendar(10000L)) + .closeTimestamp(MiscUtil.asXMLGregorianCalendar(10100L)) + .deadline(MiscUtil.asXMLGregorianCalendar(10200L)) + .originalAssigneeRef(originalAssignee1Oid.toString(), + OrgType.COMPLEX_TYPE, originalAssignee1Relation) + .performerRef(performer1Oid.toString(), + UserType.COMPLEX_TYPE, performer1Relation) + .stageNumber(1) + .output(output1)) + .workItem(new CaseWorkItemType(prismContext) + .id(42L) + .createTimestamp(MiscUtil.asXMLGregorianCalendar(20000L)) + .closeTimestamp(MiscUtil.asXMLGregorianCalendar(20100L)) + .deadline(MiscUtil.asXMLGregorianCalendar(20200L)) + .originalAssigneeRef(originalAssignee2Oid.toString(), + UserType.COMPLEX_TYPE, originalAssignee2Relation) + .performerRef(performer2Oid.toString(), + UserType.COMPLEX_TYPE, performer2Relation) + .stageNumber(2) + .output(output2)); when("adding it to the repository"); repositoryService.addObject(acase.asPrismObject(), null, result); @@ -1399,7 +1397,7 @@ public void test850Case() throws Exception { assertThat(row.targetRefTargetType).isEqualTo(MObjectType.ORG); assertCachedUri(row.targetRefRelationId, targetRelation); - QCaseWorkItem t = aliasFor(QCaseWorkItem.CLASS); + QCaseWorkItem t = aliasFor(QCaseWorkItem.class); List wiRows = select(t, t.ownerOid.eq(UUID.fromString(acase.getOid()))); assertThat(wiRows).hasSize(2); From d3b5f5173cec8deef8f735cc428d45ae9abe2114 Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Mon, 24 May 2021 14:58:08 +0200 Subject: [PATCH 31/43] Case workitem outcome (+tests) --- .../cases/workitem/QCaseWorkItemMapping.java | 12 +++-- .../func/SqaleRepoAddDeleteObjectTest.java | 15 ++---- .../sqale/func/SqaleRepoSearchObjectTest.java | 54 +++++++++++++++++++ 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java index 1f6d41684d2..6cbdac3fd87 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/workitem/QCaseWorkItemMapping.java @@ -10,6 +10,8 @@ import java.util.Objects; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractWorkItemOutputType; + import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; @@ -54,9 +56,8 @@ private QCaseWorkItemMapping(@NotNull SqaleRepoContext repositoryContext) { q -> q.originalAssigneeRefTargetType, q -> q.originalAssigneeRefRelationId)); - // TODO: OUTCOME - // see QFocusMapping:61, F_OUTPUT/F_OUTCOME -// addItemMapping(F_OUTCOME, stringMapper(q -> q.outcome)); + addNestedMapping(F_OUTPUT, AbstractWorkItemOutputType.class) + .addItemMapping(AbstractWorkItemOutputType.F_OUTCOME, stringMapper(q -> q.outcome)); addItemMapping(F_PERFORMER_REF, refMapper( q -> q.performerRefTargetOid, @@ -99,7 +100,10 @@ public MCaseWorkItem insert(CaseWorkItemType workItem, MCase ownerRow, JdbcSessi t -> row.originalAssigneeRefTargetType = t, r -> row.originalAssigneeRefRelationId = r); - // TODO: Outcome + AbstractWorkItemOutputType output = workItem.getOutput(); + if (output != null) { + row.outcome = output.getOutcome(); + } setReference(workItem.getPerformerRef(), o -> row.performerRefTargetOid = o, diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java index 75587be47c9..304c4c9aa23 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java @@ -1335,11 +1335,6 @@ public void test850Case() throws Exception { UUID performer2Oid = UUID.randomUUID(); QName performer2Relation = QName.valueOf("{https://random.org/ns}performer2-rel"); - AbstractWorkItemOutputType output1 = new AbstractWorkItemOutputType(prismContext) - .outcome("OUTCOME one"); - AbstractWorkItemOutputType output2 = new AbstractWorkItemOutputType(prismContext) - .outcome("OUTCOME two"); - CaseType acase = new CaseType(prismContext) .name(objectName) .state("closed") @@ -1362,7 +1357,7 @@ public void test850Case() throws Exception { .performerRef(performer1Oid.toString(), UserType.COMPLEX_TYPE, performer1Relation) .stageNumber(1) - .output(output1)) + .output(new AbstractWorkItemOutputType(prismContext).outcome("OUTCOME one"))) .workItem(new CaseWorkItemType(prismContext) .id(42L) .createTimestamp(MiscUtil.asXMLGregorianCalendar(20000L)) @@ -1373,7 +1368,7 @@ public void test850Case() throws Exception { .performerRef(performer2Oid.toString(), UserType.COMPLEX_TYPE, performer2Relation) .stageNumber(2) - .output(output2)); + .output(new AbstractWorkItemOutputType(prismContext).outcome("OUTCOME two"))); when("adding it to the repository"); repositoryService.addObject(acase.asPrismObject(), null, result); @@ -1413,8 +1408,7 @@ public void test850Case() throws Exception { assertThat(wiRow.originalAssigneeRefTargetOid).isEqualTo(originalAssignee1Oid); assertThat(wiRow.originalAssigneeRefTargetType).isEqualTo(MObjectType.ORG); assertCachedUri(wiRow.originalAssigneeRefRelationId, originalAssignee1Relation); - // TODO: outcome -// assertThat(wiRow.outcome).isEqualTo("OUTCOME one"); + assertThat(wiRow.outcome).isEqualTo("OUTCOME one"); assertThat(wiRow.performerRefTargetOid).isEqualTo(performer1Oid); assertThat(wiRow.performerRefTargetType).isEqualTo(MObjectType.USER); assertCachedUri(wiRow.performerRefRelationId, performer1Relation); @@ -1430,8 +1424,7 @@ public void test850Case() throws Exception { assertThat(wiRow.originalAssigneeRefTargetOid).isEqualTo(originalAssignee2Oid); assertThat(wiRow.originalAssigneeRefTargetType).isEqualTo(MObjectType.USER); assertCachedUri(wiRow.originalAssigneeRefRelationId, originalAssignee2Relation); - // TODO: outcome -// assertThat(wiRow.outcome).isEqualTo("OUTCOME two"); + assertThat(wiRow.outcome).isEqualTo("OUTCOME two"); assertThat(wiRow.performerRefTargetOid).isEqualTo(performer2Oid); assertThat(wiRow.performerRefTargetType).isEqualTo(MObjectType.USER); assertCachedUri(wiRow.performerRefRelationId, performer2Relation); diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java index d831a02eb81..e3fd231a902 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java @@ -13,6 +13,8 @@ import java.util.UUID; import javax.xml.namespace.QName; +import com.evolveum.midpoint.prism.path.ItemPath; + import org.jetbrains.annotations.NotNull; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -53,6 +55,8 @@ public class SqaleRepoSearchObjectTest extends SqaleRepoBaseTest { private String task1Oid; // task has more attribute type variability private String shadow1Oid; // ditto private String service1Oid; // object with integer attribute + private String case1Oid; // Closed case, two work items + private String case2Oid; // other info used in queries private String creatorOid = UUID.randomUUID().toString(); @@ -144,6 +148,30 @@ public void initObjects() throws Exception { service1Oid = repositoryService.addObject( new ServiceType(prismContext).name("service-1").asPrismObject(), null, result); + case1Oid = repositoryService.addObject( + new CaseType(prismContext).name("case-1") + .state("closed") + .closeTimestamp(MiscUtil.asXMLGregorianCalendar(321L)) + .workItem(new CaseWorkItemType(prismContext) + .id(41L) + .createTimestamp(MiscUtil.asXMLGregorianCalendar(10000L)) + .closeTimestamp(MiscUtil.asXMLGregorianCalendar(10100L)) + .deadline(MiscUtil.asXMLGregorianCalendar(10200L)) + .originalAssigneeRef(user3Oid, UserType.COMPLEX_TYPE) + .performerRef(user3Oid, UserType.COMPLEX_TYPE) + .stageNumber(1) + .output(new AbstractWorkItemOutputType(prismContext).outcome("OUTCOME one"))) + .workItem(new CaseWorkItemType(prismContext) + .id(42L) + .createTimestamp(MiscUtil.asXMLGregorianCalendar(20000L)) + .closeTimestamp(MiscUtil.asXMLGregorianCalendar(20100L)) + .deadline(MiscUtil.asXMLGregorianCalendar(20200L)) + .originalAssigneeRef(user1Oid, UserType.COMPLEX_TYPE) + .performerRef(user1Oid, UserType.COMPLEX_TYPE) + .stageNumber(2) + .output(new AbstractWorkItemOutputType(prismContext).outcome("OUTCOME two"))) + .asPrismObject(), + null, result); assertThatOperationResult(result).isSuccess(); } @@ -184,6 +212,32 @@ public void test110SearchUserByName() throws Exception { assertThat(result).hasSize(1); assertThat(result.get(0).getOid()).isEqualTo(user1Oid); } + + @Test + public void test180SearchCaseWorkitemByOutcome() throws Exception { + searchCaseWorkitemByOutcome("OUTCOME one", case1Oid); + searchCaseWorkitemByOutcome("OUTCOME two", case1Oid); + searchCaseWorkitemByOutcome("OUTCOME nonexist", null); + } + + private void searchCaseWorkitemByOutcome(String wiOutcome, String expectedOid) throws Exception { + when("searching case with query for workitem/output/outcome " + wiOutcome); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(CaseType.class, + prismContext.queryFor(CaseType.class) + .item(ItemPath.create(CaseType.F_WORK_ITEM, CaseWorkItemType.F_OUTPUT, AbstractWorkItemOutputType.F_OUTCOME)).eq(wiOutcome) + .build(), + operationResult); + + then("case with the matching workitem outcome is returned"); + assertThatOperationResult(operationResult).isSuccess(); + if (expectedOid == null) { + assertThat(result).hasSize(0); + } else { + assertThat(result).hasSize(1); + assertThat(result.get(0).getOid()).isEqualTo(expectedOid); + } + } // endregion // region org filter From a12d8ede25834db6bee729505c5c12d586c9cca1 Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Mon, 24 May 2021 15:09:20 +0200 Subject: [PATCH 32/43] Sqale: Case index names according to new convention. --- repo/repo-sqale/sql/pgnew-repo.sql | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index 44e8074e83e..baecd02fb6d 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -1087,11 +1087,11 @@ CREATE TRIGGER m_case_oid_delete_tr AFTER DELETE ON m_case CREATE INDEX m_case_nameOrig_idx ON m_case (nameOrig); ALTER TABLE m_case ADD CONSTRAINT m_case_nameNorm_key UNIQUE (nameNorm); -CREATE INDEX iCaseTypeObjectRefTargetOid ON m_case(objectRefTargetOid); -CREATE INDEX iCaseTypeTargetRefTargetOid ON m_case(targetRefTargetOid); -CREATE INDEX iCaseTypeParentRefTargetOid ON m_case(parentRefTargetOid); -CREATE INDEX iCaseTypeRequestorRefTargetOid ON m_case(requestorRefTargetOid); -CREATE INDEX iCaseTypeCloseTimestamp ON m_case(closeTimestamp); +CREATE INDEX m_case_objectRefTargetOid_idx ON m_case(objectRefTargetOid); +CREATE INDEX m_case_targetRefTargetOid_idx ON m_case(targetRefTargetOid); +CREATE INDEX m_case_parentRefTargetOid_idx ON m_case(parentRefTargetOid); +CREATE INDEX m_case_requestorRefTargetOid_idx ON m_case(requestorRefTargetOid); +CREATE INDEX m_case_closeTimestamp_idx ON m_case(closeTimestamp); CREATE TABLE m_case_wi ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, From c6c1cb3da53d917521dc12ffcfabc5f5d822cba0 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Mon, 24 May 2021 15:43:09 +0200 Subject: [PATCH 33/43] repo-sqale: added new case-wi ref types to enums, *_cid cols renamed --- repo/repo-sqale/sql/pgnew-repo.sql | 30 ++++++++++--------- .../assignment/QAssignmentReference.java | 2 +- .../repo/sqale/qmodel/ref/MReferenceType.java | 4 ++- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index baecd02fb6d..039f9ddebbd 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -74,6 +74,8 @@ CREATE TYPE ReferenceType AS ENUM ( 'ARCHETYPE', 'ASSIGNMENT_CREATE_APPROVER', 'ASSIGNMENT_MODIFY_APPROVER', + 'CASE_WI_ASSIGNEE', + 'CASE_WI_CANDIDATE', 'DELEGATED', 'INCLUDE', 'PROJECTION', @@ -1210,7 +1212,7 @@ CREATE TABLE m_access_cert_case ( CREATE TABLE m_access_cert_wi ( ownerOid UUID NOT NULL, -- PK+FK - acc_cert_case_cid INTEGER NOT NULL, -- PK+FK + accCertCaseCid INTEGER NOT NULL, -- PK+FK containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_WORK_ITEM') STORED, closeTimestamp TIMESTAMPTZ, iteration INTEGER NOT NULL, @@ -1221,32 +1223,32 @@ CREATE TABLE m_access_cert_wi ( performerRefRelationId INTEGER REFERENCES m_uri(id), stageNumber INTEGER, - PRIMARY KEY (ownerOid, acc_cert_case_cid, cid) + PRIMARY KEY (ownerOid, accCertCaseCid, cid) ) INHERITS(m_container); ALTER TABLE m_access_cert_wi - ADD CONSTRAINT m_access_cert_wi_id_fk FOREIGN KEY (ownerOid, acc_cert_case_cid) + ADD CONSTRAINT m_access_cert_wi_id_fk FOREIGN KEY (ownerOid, accCertCaseCid) REFERENCES m_access_cert_case (ownerOid, cid) ON DELETE CASCADE; -- TODO rework to inherit from reference tables CREATE TABLE m_access_cert_wi_reference ( ownerOid UUID NOT NULL, -- PK+FK - acc_cert_case_cid INTEGER NOT NULL, -- PK+FK - acc_cert_wi_cid INTEGER NOT NULL, -- PK+FK + accCertCaseCid INTEGER NOT NULL, -- PK+FK + accCertWiCid INTEGER NOT NULL, -- PK+FK targetOid UUID NOT NULL, -- more PK columns... targetType ObjectType, relationId INTEGER NOT NULL REFERENCES m_uri(id), -- TODO is the order of last two components optimal for index/query? - PRIMARY KEY (ownerOid, acc_cert_case_cid, acc_cert_wi_cid, relationId, targetOid) + PRIMARY KEY (ownerOid, accCertCaseCid, accCertWiCid, relationId, targetOid) ); ALTER TABLE m_access_cert_wi_reference ADD CONSTRAINT m_access_cert_wi_reference_id_fk - FOREIGN KEY (ownerOid, acc_cert_case_cid, acc_cert_wi_cid) - REFERENCES m_access_cert_wi (ownerOid, acc_cert_case_cid, cid) + FOREIGN KEY (ownerOid, accCertCaseCid, accCertWiCid) + REFERENCES m_access_cert_wi (ownerOid, accCertCaseCid, cid) ON DELETE CASCADE; /* CREATE INDEX iCertCampaignNameOrig ON m_access_cert_campaign (nameOrig); @@ -1419,30 +1421,30 @@ CREATE INDEX m_assignment_resourceRefTargetOid_idx ON m_assignment (resourceRefT -- stores assignment/metadata/createApproverRef CREATE TABLE m_assignment_ref_create_approver ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - assignment_cid INTEGER NOT NULL, + assignmentCid INTEGER NOT NULL, referenceType ReferenceType GENERATED ALWAYS AS ('ASSIGNMENT_CREATE_APPROVER') STORED, - PRIMARY KEY (ownerOid, assignment_cid, referenceType, relationId, targetOid) + PRIMARY KEY (ownerOid, assignmentCid, referenceType, relationId, targetOid) ) INHERITS (m_reference); ALTER TABLE m_assignment_ref_create_approver ADD CONSTRAINT m_assignment_ref_create_approver_id_fk - FOREIGN KEY (ownerOid, assignment_cid) REFERENCES m_assignment (ownerOid, cid); + FOREIGN KEY (ownerOid, assignmentCid) REFERENCES m_assignment (ownerOid, cid); -- TODO index targetOid, relationId? -- stores assignment/metadata/modifyApproverRef CREATE TABLE m_assignment_ref_modify_approver ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - assignment_cid INTEGER NOT NULL, + assignmentCid INTEGER NOT NULL, referenceType ReferenceType GENERATED ALWAYS AS ('ASSIGNMENT_MODIFY_APPROVER') STORED, - PRIMARY KEY (ownerOid, assignment_cid, referenceType, relationId, targetOid) + PRIMARY KEY (ownerOid, assignmentCid, referenceType, relationId, targetOid) ) INHERITS (m_reference); ALTER TABLE m_assignment_ref_modify_approver ADD CONSTRAINT m_assignment_ref_modify_approver_id_fk - FOREIGN KEY (ownerOid, assignment_cid) REFERENCES m_assignment (ownerOid, cid); + FOREIGN KEY (ownerOid, assignmentCid) REFERENCES m_assignment (ownerOid, cid); -- TODO index targetOid, relationId? -- endregion diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReference.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReference.java index 30e9d09ef7d..7510a2bfc31 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReference.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentReference.java @@ -23,7 +23,7 @@ public class QAssignmentReference extends QReference assignmentCid = createLong("assignmentCid", ASSIGNMENT_CID); diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/MReferenceType.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/MReferenceType.java index 4baa405a29a..cd7e007be07 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/MReferenceType.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ref/MReferenceType.java @@ -28,5 +28,7 @@ public enum MReferenceType { // OTHER REFERENCES ASSIGNMENT_CREATE_APPROVER, - ASSIGNMENT_MODIFY_APPROVER; + ASSIGNMENT_MODIFY_APPROVER, + CASE_WI_ASSIGNEE, + CASE_WI_CANDIDATE, } From fddd755054c1416003960bd16938ca94f14888eb Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Mon, 24 May 2021 20:07:38 +0200 Subject: [PATCH 34/43] *FilterImpl: toString fixes/enhancements, cleanup, some generics --- .../prism/impl/query/ExistsFilterImpl.java | 57 ++++++------ .../prism/impl/query/FullTextFilterImpl.java | 43 ++++----- .../prism/impl/query/InOidFilterImpl.java | 90 ++++++++++--------- .../prism/impl/query/LogicalFilterImpl.java | 35 ++++---- .../prism/impl/query/ObjectQueryImpl.java | 31 +++---- .../prism/impl/query/OrgFilterImpl.java | 30 ++++--- .../prism/impl/query/ValueFilterImpl.java | 10 +-- 7 files changed, 155 insertions(+), 141 deletions(-) diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/ExistsFilterImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/ExistsFilterImpl.java index 2f9b0bee819..6b4bf8c0f13 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/ExistsFilterImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/ExistsFilterImpl.java @@ -1,12 +1,15 @@ /* - * Copyright (c) 2010-2018 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ - package com.evolveum.midpoint.prism.impl.query; +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.match.MatchingRuleRegistry; import com.evolveum.midpoint.prism.path.ItemPath; @@ -16,7 +19,6 @@ import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.PrettyPrinter; import com.evolveum.midpoint.util.exception.SchemaException; -import org.jetbrains.annotations.NotNull; /** * TODO think about creating abstract ItemFilter (ItemRelatedFilter) for this filter and ValueFilter. @@ -27,10 +29,10 @@ public final class ExistsFilterImpl extends ObjectFilterImpl implements ExistsFilter { @NotNull private final ItemPath fullPath; - private final ItemDefinition definition; + private final ItemDefinition definition; private ObjectFilter filter; - private ExistsFilterImpl(@NotNull ItemPath fullPath, ItemDefinition definition, ObjectFilter filter) { + private ExistsFilterImpl(@NotNull ItemPath fullPath, ItemDefinition definition, ObjectFilter filter) { this.fullPath = fullPath; this.definition = definition; this.filter = filter; @@ -65,18 +67,17 @@ protected void performFreeze() { } public static ExistsFilter createExists(ItemPath itemPath, PrismContainerDefinition containerDef, - ObjectFilter filter) throws SchemaException { - ItemDefinition itemDefinition = FilterImplUtil.findItemDefinition(itemPath, containerDef); + ObjectFilter filter) throws SchemaException { + ItemDefinition itemDefinition = FilterImplUtil.findItemDefinition(itemPath, containerDef); return new ExistsFilterImpl(itemPath, itemDefinition, filter); } public static ExistsFilter createExists(ItemPath itemPath, Class clazz, PrismContext prismContext, - ObjectFilter filter) { - ItemDefinition itemDefinition = FilterImplUtil.findItemDefinition(itemPath, clazz, prismContext); + ObjectFilter filter) { + ItemDefinition itemDefinition = FilterImplUtil.findItemDefinition(itemPath, clazz, prismContext); return new ExistsFilterImpl(itemPath, itemDefinition, filter); } - @SuppressWarnings("CloneDoesntCallSuperClone") @Override public ExistsFilterImpl clone() { ObjectFilter f = filter != null ? filter.clone() : null; @@ -111,10 +112,10 @@ public boolean match(PrismContainerValue value, MatchingRuleRegistry matchingRul @Override public void checkConsistence(boolean requireDefinitions) { if (fullPath.isEmpty()) { - throw new IllegalArgumentException("Null or empty path in "+this); + throw new IllegalArgumentException("Null or empty path in " + this); } if (requireDefinitions && definition == null) { - throw new IllegalArgumentException("Null definition in "+this); + throw new IllegalArgumentException("Null definition in " + this); } // null subfilter is legal. It means "ALL". if (filter != null) { @@ -132,7 +133,7 @@ public String debugDump(int indent) { DebugUtil.indentDebugDump(sb, indent + 1); sb.append("DEF: "); if (getDefinition() != null) { - sb.append(getDefinition().toString()); + sb.append(getDefinition()); } else { sb.append("null"); } @@ -145,13 +146,11 @@ public String debugDump(int indent) { @Override public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("EXISTS("); - sb.append(PrettyPrinter.prettyPrint(fullPath)); - sb.append(","); - sb.append(filter); - sb.append(")"); - return sb.toString(); + return "EXISTS(" + + PrettyPrinter.prettyPrint(fullPath) + + ", " + + filter + + ")"; } @Override @@ -164,16 +163,20 @@ public void accept(Visitor visitor) { @Override public boolean equals(Object o, boolean exact) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } ExistsFilterImpl that = (ExistsFilterImpl) o; - if (!fullPath.equals(that.fullPath, exact)) return false; - if (exact) { - if (definition != null ? !definition.equals(that.definition) : that.definition != null) { - return false; - } + if (!fullPath.equals(that.fullPath, exact)) { + return false; + } + if (exact && !Objects.equals(definition, that.definition)) { + return false; } return filter != null ? filter.equals(that.filter, exact) : that.filter == null; } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/FullTextFilterImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/FullTextFilterImpl.java index 727505e3492..aef7c649749 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/FullTextFilterImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/FullTextFilterImpl.java @@ -1,26 +1,25 @@ /* - * Copyright (c) 2010-2018 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ - package com.evolveum.midpoint.prism.impl.query; +import java.util.Arrays; +import java.util.Collection; +import java.util.Objects; + +import com.google.common.collect.ImmutableList; +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; + import com.evolveum.midpoint.prism.ExpressionWrapper; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.match.MatchingRuleRegistry; import com.evolveum.midpoint.prism.query.FullTextFilter; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.SchemaException; -import com.google.common.collect.ImmutableList; -import org.apache.commons.lang.StringUtils; -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Objects; -import java.util.stream.Collectors; public final class FullTextFilterImpl extends ObjectFilterImpl implements FullTextFilter { @@ -35,11 +34,11 @@ private FullTextFilterImpl(ExpressionWrapper expression) { this.expression = expression; } - public static FullTextFilter createFullText(Collection values){ + public static FullTextFilter createFullText(Collection values) { return new FullTextFilterImpl(values); } - public static FullTextFilter createFullText(String... values){ + public static FullTextFilter createFullText(String... values) { return new FullTextFilterImpl(Arrays.asList(values)); } @@ -78,14 +77,14 @@ protected void performFreeze() { @Override public void checkConsistence(boolean requireDefinitions) { if (values == null) { - throw new IllegalArgumentException("Null 'values' in "+this); + throw new IllegalArgumentException("Null 'values' in " + this); } if (values.isEmpty()) { - throw new IllegalArgumentException("No values in "+this); + throw new IllegalArgumentException("No values in " + this); } - for (String value: values) { + for (String value : values) { if (StringUtils.isBlank(value)) { - throw new IllegalArgumentException("Empty value in "+this); + throw new IllegalArgumentException("Empty value in " + this); } } } @@ -98,7 +97,7 @@ public String debugDump(int indent) { if (values != null) { sb.append("\n"); for (String value : values) { - DebugUtil.indentDebugDump(sb, indent+1); + DebugUtil.indentDebugDump(sb, indent + 1); sb.append(value); sb.append("\n"); } @@ -113,7 +112,7 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("FULLTEXT: "); if (values != null) { - sb.append(values.stream().collect(Collectors.joining("; "))); + sb.append(String.join("; ", values)); } return sb.toString(); } @@ -132,8 +131,12 @@ public boolean match(PrismContainerValue value, MatchingRuleRegistry matchingRul @Override public boolean equals(Object o, boolean exact) { - if (this == o) return true; - if (!(o instanceof FullTextFilterImpl)) return false; + if (this == o) { + return true; + } + if (!(o instanceof FullTextFilterImpl)) { + return false; + } FullTextFilterImpl that = (FullTextFilterImpl) o; return Objects.equals(values, that.values) && Objects.equals(expression, that.expression); diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/InOidFilterImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/InOidFilterImpl.java index 03cffa844cb..52f9a47757b 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/InOidFilterImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/InOidFilterImpl.java @@ -1,22 +1,21 @@ /* - * Copyright (c) 2010-2018 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ - package com.evolveum.midpoint.prism.impl.query; import java.util.*; -import com.evolveum.midpoint.prism.ExpressionWrapper; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.query.InOidFilter; import org.apache.commons.lang.StringUtils; +import com.evolveum.midpoint.prism.ExpressionWrapper; +import com.evolveum.midpoint.prism.PrismContainer; import com.evolveum.midpoint.prism.PrismContainerValue; +import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.match.MatchingRuleRegistry; +import com.evolveum.midpoint.prism.query.InOidFilter; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.SchemaException; @@ -24,39 +23,39 @@ public final class InOidFilterImpl extends ObjectFilterImpl implements InOidFilt private List oids; private ExpressionWrapper expression; - private boolean considerOwner; // temporary hack (checks owner OID) + private final boolean considerOwner; private InOidFilterImpl(boolean considerOwner, Collection oids) { this.considerOwner = considerOwner; setOids(oids); } - private InOidFilterImpl(boolean considerOwner, ExpressionWrapper expression){ + private InOidFilterImpl(boolean considerOwner, ExpressionWrapper expression) { this.considerOwner = considerOwner; this.expression = expression; } - public static InOidFilter createInOid(boolean considerOwner, Collection oids){ + public static InOidFilter createInOid(boolean considerOwner, Collection oids) { return new InOidFilterImpl(considerOwner, oids); } - public static InOidFilter createInOid(Collection oids){ + public static InOidFilter createInOid(Collection oids) { return new InOidFilterImpl(false, oids); } - public static InOidFilter createInOid(String... oids){ + public static InOidFilter createInOid(String... oids) { return new InOidFilterImpl(false, Arrays.asList(oids)); } - public static InOidFilter createOwnerHasOidIn(Collection oids){ + public static InOidFilter createOwnerHasOidIn(Collection oids) { return new InOidFilterImpl(true, oids); } - public static InOidFilter createOwnerHasOidIn(String... oids){ + public static InOidFilter createOwnerHasOidIn(String... oids) { return new InOidFilterImpl(true, Arrays.asList(oids)); } - public static InOidFilter createInOid(boolean considerOwner, ExpressionWrapper expression){ + public static InOidFilter createInOid(boolean considerOwner, ExpressionWrapper expression) { return new InOidFilterImpl(considerOwner, expression); } @@ -96,11 +95,11 @@ protected void performFreeze() { @Override public void checkConsistence(boolean requireDefinitions) { if (oids == null) { - throw new IllegalArgumentException("Null oids in "+this); + throw new IllegalArgumentException("Null oids in " + this); } - for (String oid: oids) { + for (String oid : oids) { if (StringUtils.isBlank(oid)) { - throw new IllegalArgumentException("Empty oid in "+this); + throw new IllegalArgumentException("Empty oid in " + this); } } } @@ -109,15 +108,15 @@ public void checkConsistence(boolean requireDefinitions) { public String debugDump(int indent) { StringBuilder sb = new StringBuilder(); DebugUtil.indentDebugDump(sb, indent); - sb.append("IN OID: "); + sb.append("IN OID"); if (considerOwner) { - sb.append("(for owner)"); + sb.append(" (for owner)"); } - sb.append("VALUE:"); + sb.append(": "); if (getOids() != null) { for (String oid : getOids()) { sb.append("\n"); - DebugUtil.indentDebugDump(sb, indent+1); + DebugUtil.indentDebugDump(sb, indent + 1); sb.append(oid); } } else { @@ -125,21 +124,29 @@ public String debugDump(int indent) { } return sb.toString(); - } @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("IN OID: "); - if (getOids() != null){ + sb.append("IN OID"); + if (considerOwner) { + sb.append(" (for owner)"); + } + sb.append(": "); + if (getOids() != null) { + boolean first = true; for (String value : getOids()) { if (value == null) { sb.append("null"); } else { sb.append(value); } - sb.append("; "); + if (first) { + first = false; + } else { + sb.append("; "); + } } } return sb.toString(); @@ -155,7 +162,7 @@ public InOidFilterImpl clone() { @Override public boolean match(PrismContainerValue value, MatchingRuleRegistry matchingRuleRegistry) throws SchemaException { if (value == null) { - return false; // just for sure + return false; // just for sure } // are we a prism object? @@ -163,38 +170,37 @@ public boolean match(PrismContainerValue value, MatchingRuleRegistry matchingRul if (considerOwner) { return false; } - String oid = ((PrismObject) (value.getParent())).getOid(); + String oid = ((PrismObject) (value.getParent())).getOid(); return StringUtils.isNotBlank(oid) && oids != null && oids.contains(oid); } - final PrismContainerValue pcvToConsider; + PrismContainerValue pcvToConsider; if (considerOwner) { if (!(value.getParent() instanceof PrismContainer)) { return false; } - PrismContainer container = (PrismContainer) value.getParent(); - if (!(container.getParent() instanceof PrismContainerValue)) { - return false; - } - pcvToConsider = container.getParent(); + pcvToConsider = ((PrismContainer) value.getParent()).getParent(); } else { pcvToConsider = value; } - return pcvToConsider.getId() != null && oids.contains(pcvToConsider.getId()); + return pcvToConsider != null + && pcvToConsider.getId() != null + && oids.contains(pcvToConsider.getId().toString()); } @Override public boolean equals(Object o, boolean exact) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - InOidFilterImpl that = (InOidFilterImpl) o; - - if (considerOwner != that.considerOwner) return false; - if (oids != null ? !oids.equals(that.oids) : that.oids != null) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { return false; } - return expression != null ? expression.equals(that.expression) : that.expression == null; + + InOidFilterImpl that = (InOidFilterImpl) o; + return considerOwner == that.considerOwner + && Objects.equals(oids, that.oids) + && Objects.equals(expression, that.expression); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/LogicalFilterImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/LogicalFilterImpl.java index ed9eb37e5ca..18cf82cab06 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/LogicalFilterImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/LogicalFilterImpl.java @@ -1,20 +1,20 @@ /* - * Copyright (c) 2010-2018 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ - package com.evolveum.midpoint.prism.impl.query; import java.util.ArrayList; import java.util.List; +import com.google.common.collect.ImmutableList; + import com.evolveum.midpoint.prism.query.LogicalFilter; import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.prism.query.Visitor; import com.evolveum.midpoint.util.DebugUtil; -import com.google.common.collect.ImmutableList; public abstract class LogicalFilterImpl extends ObjectFilterImpl implements LogicalFilter { @@ -22,7 +22,7 @@ public abstract class LogicalFilterImpl extends ObjectFilterImpl implements Logi @Override public List getConditions() { - if (conditions == null){ + if (conditions == null) { conditions = new ArrayList<>(); } return conditions; @@ -64,7 +64,7 @@ protected List getClonedConditions() { return null; } List clonedConditions = new ArrayList<>(conditions.size()); - for (ObjectFilter condition: conditions) { + for (ObjectFilter condition : conditions) { clonedConditions.add(condition.clone()); } return clonedConditions; @@ -78,14 +78,14 @@ public boolean isEmpty() { @Override public void checkConsistence(boolean requireDefinitions) { if (conditions == null) { - throw new IllegalArgumentException("Null conditions in "+this); + throw new IllegalArgumentException("Null conditions in " + this); } if (conditions.isEmpty()) { - throw new IllegalArgumentException("Empty conditions in "+this); + throw new IllegalArgumentException("Empty conditions in " + this); } - for (ObjectFilter condition: conditions) { + for (ObjectFilter condition : conditions) { if (condition == null) { - throw new IllegalArgumentException("Null subfilter in "+this); + throw new IllegalArgumentException("Null subfilter in " + this); } condition.checkConsistence(requireDefinitions); } @@ -94,7 +94,7 @@ public void checkConsistence(boolean requireDefinitions) { @Override public void accept(Visitor visitor) { super.accept(visitor); - for (ObjectFilter condition: getConditions()) { + for (ObjectFilter condition : getConditions()) { condition.accept(visitor); } } @@ -115,9 +115,12 @@ public int hashCode() { @Override public boolean equals(Object obj, boolean exact) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } LogicalFilterImpl other = (LogicalFilterImpl) obj; if (conditions != null) { if (conditions.size() != other.conditions.size()) { @@ -139,7 +142,7 @@ public String debugDump(int indent) { StringBuilder sb = new StringBuilder(); DebugUtil.indentDebugDump(sb, indent); sb.append(getDebugDumpOperationName()).append(":"); - for (ObjectFilter filter : getConditions()){ + for (ObjectFilter filter : getConditions()) { sb.append("\n"); sb.append(filter.debugDump(indent + 1)); } @@ -151,10 +154,10 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getDebugDumpOperationName()); sb.append("("); - for (int i = 0; i < getConditions().size(); i++){ + for (int i = 0; i < getConditions().size(); i++) { sb.append(getConditions().get(i)); if (i != getConditions().size() - 1) { - sb.append(","); + sb.append("; "); // clearer separation than with ',' used in value filters } } sb.append(")"); diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/ObjectQueryImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/ObjectQueryImpl.java index a3c0765f6a2..5a834ca46b3 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/ObjectQueryImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/ObjectQueryImpl.java @@ -1,17 +1,16 @@ /* - * Copyright (c) 2010-2018 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ - package com.evolveum.midpoint.prism.impl.query; +import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; import com.evolveum.midpoint.prism.query.AllFilter; import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.prism.query.ObjectPaging; import com.evolveum.midpoint.prism.query.ObjectQuery; -import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; import com.evolveum.midpoint.util.DebugUtil; public class ObjectQueryImpl implements ObjectQuery { @@ -74,6 +73,7 @@ public static ObjectQuery createObjectQuery(ObjectFilter filter, ObjectPaging pa return query; } + @SuppressWarnings("MethodDoesntCallSuperMethod") public ObjectQueryImpl clone() { ObjectQueryImpl clone = cloneEmpty(); if (this.filter != null) { @@ -128,20 +128,20 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Q{"); if (filter != null) { - sb.append(filter.toString()); - sb.append(","); + sb.append(filter); } else { sb.append("null filter"); } + sb.append(", "); if (paging != null) { - sb.append(paging.toString()); - sb.append(","); + sb.append(paging); } else { sb.append("null paging"); } if (allowPartialResults) { - sb.append(",partial"); + sb.append(", partial"); } + sb.append('}'); return sb.toString(); } @@ -160,9 +160,6 @@ public Integer getOffset() { if (paging == null) { return null; } -// if (paging.getCookie() != null) { -// throw new UnsupportedOperationException("Paging cookie is not supported here."); -// } return paging.getOffset(); } @@ -171,9 +168,6 @@ public Integer getMaxSize() { if (paging == null) { return null; } -// if (paging.getCookie() != null) { -// throw new UnsupportedOperationException("Paging cookie is not supported here."); -// } return paging.getMaxSize(); } @@ -187,8 +181,12 @@ public boolean equivalent(Object o) { } public boolean equals(Object o, boolean exact) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } ObjectQueryImpl that = (ObjectQueryImpl) o; @@ -199,7 +197,6 @@ public boolean equals(Object o, boolean exact) { return false; } return paging != null ? paging.equals(that.paging, exact) : that.paging == null; - } @Override diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/OrgFilterImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/OrgFilterImpl.java index e8a6d9d0da8..1f3c864dc90 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/OrgFilterImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/OrgFilterImpl.java @@ -1,12 +1,13 @@ /* - * Copyright (c) 2010-2018 Evolveum and contributors + * Copyright (C) 2010-2021 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ - package com.evolveum.midpoint.prism.impl.query; +import java.util.Objects; + import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.PrismReferenceValue; import com.evolveum.midpoint.prism.impl.PrismReferenceValueImpl; @@ -80,7 +81,7 @@ protected void performFreeze() { @Override public void checkConsistence(boolean requireDefinitions) { if (!root && baseOrgRef == null) { - throw new IllegalArgumentException("Null baseOrgRef in "+this); + throw new IllegalArgumentException("Null baseOrgRef in " + this); } } @@ -96,18 +97,16 @@ public int hashCode() { @Override public boolean equals(Object obj, boolean exact) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - OrgFilterImpl other = (OrgFilterImpl) obj; - if (baseOrgRef == null) { - if (other.baseOrgRef != null) return false; - } else if (!baseOrgRef.equals(other.baseOrgRef)) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { return false; } - if (scope != other.scope) return false; - if (root != other.root) return false; - return true; + OrgFilterImpl other = (OrgFilterImpl) obj; + return Objects.equals(baseOrgRef, other.baseOrgRef) + && scope == other.scope + && root == other.root; } // Just to make checkstyle happy @@ -143,8 +142,11 @@ public String debugDump(int indent) { public String toString() { StringBuilder sb = new StringBuilder(); sb.append("ORG: "); + if (isRoot()) { + sb.append("ROOT"); + } if (getOrgRef() != null) { - sb.append(getOrgRef().toString()); + sb.append(getOrgRef()); sb.append(", "); } if (getScope() != null) { diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/ValueFilterImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/ValueFilterImpl.java index f9e97b175c4..7d18dce3391 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/ValueFilterImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/query/ValueFilterImpl.java @@ -323,7 +323,7 @@ protected void debugDump(int indent, StringBuilder sb) { sb.append("\n"); DebugUtil.indentDebugDump(sb, indent + 1); sb.append("PATH: "); - sb.append(getFullPath().toString()); + sb.append(getFullPath()); sb.append("\n"); DebugUtil.indentDebugDump(sb, indent + 1); @@ -358,7 +358,7 @@ protected void debugDump(int indent, StringBuilder sb) { sb.append("\n"); DebugUtil.indentDebugDump(sb, indent + 1); sb.append("RIGHT SIDE PATH: "); - sb.append(getFullPath().toString()); + sb.append(getFullPath()); sb.append("\n"); DebugUtil.indentDebugDump(sb, indent + 1); sb.append("RIGHT SIDE DEF: "); @@ -379,15 +379,15 @@ protected void debugDump(int indent, StringBuilder sb) { } protected String toString(StringBuilder sb) { - sb.append(getFullPath().toString()); - sb.append(","); + sb.append(getFullPath()); + sb.append(", "); if (getValues() != null) { for (int i = 0; i < getValues().size(); i++) { PrismValue value = getValues().get(i); if (value == null) { sb.append("null"); } else { - sb.append(value.toString()); + sb.append(value); } if (i != getValues().size() - 1) { sb.append(","); From 95ca85784167ed7fa41f14dfd65de17f0a4b8c13 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Tue, 25 May 2021 11:35:48 +0200 Subject: [PATCH 35/43] repo-sqale: added m_assignment_holder to SQL schema + objectType CHECKs CHECK constraints for objectType were added to prune query plan if "WHERE objecttype='...'" is used, although this is probably obscure condition, I don't plan to use it for TYPE filter either. It's always better to use subtable matching the type. Also "objecttype='FOCUS'" does not select anything, by definition it can't match any row in SQL table as no real row is 'FOCUS'. --- repo/repo-sqale/sql/pgnew-repo.sql | 184 +++++++++++++++++++---------- 1 file changed, 121 insertions(+), 63 deletions(-) diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index 039f9ddebbd..b218c6c8faf 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -215,17 +215,21 @@ INSERT INTO m_uri (id, uri) -- See https://docs.evolveum.com/midpoint/architecture/archive/data-model/midpoint-common-schema/objecttype/ -- Following is recommended for each concrete table (see m_resource for example): -- 1) override OID like this (PK+FK): oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), --- 2) define object type class (change value): objectType ObjectType GENERATED ALWAYS AS ('XY') STORED, +-- 2) define object type class (change value as needed): +-- objectType ObjectType GENERATED ALWAYS AS ('XY') STORED CHECK (objectType = 'XY'), +-- The CHECK part helps with query optimization when the column is uses in WHERE. -- 3) add three triggers _oid_{insert|update|delete}_tr -- 4) add indexes for nameOrig and nameNorm columns (nameNorm as unique) -- 5) the rest varies on the concrete table, other indexes or constraints, etc. -- 6) any required FK must be created on the concrete table, even for inherited columns - CREATE TABLE m_object ( -- Default OID value is covered by INSERT triggers. No PK defined on abstract tables. oid UUID NOT NULL, -- objectType will be overridden with GENERATED value in concrete table - objectType ObjectType NOT NULL, + -- CHECK helps optimizer to avoid this table when different type is asked, mind that + -- WHERE objectType = 'OBJECT' never returns anything (unlike select * from m_object). + -- We don't want this check to be inherited as it would prevent any inserts of other types. + objectType ObjectType NOT NULL CHECK (objectType = 'OBJECT') NO INHERIT, nameOrig TEXT NOT NULL, nameNorm TEXT NOT NULL, fullObject BYTEA, @@ -262,6 +266,16 @@ CREATE TABLE m_object ( ); -- No indexes here, always add indexes and referential constraints on concrete sub-tables. +-- Represents AssignmentHolderType (all objects except shadows) +-- extending m_object, but still abstract, hence the CHECK (false) +CREATE TABLE m_assignment_holder ( + -- objectType will be overridden with GENERATED value in concrete table + objectType ObjectType NOT NULL CHECK (objectType = 'ASSIGNMENT_HOLDER') NO INHERIT, + + CHECK (FALSE) NO INHERIT +) + INHERITS (m_object); + -- Purely abstract table (no entries are allowed). Represents Containerable/PrismContainerValue. -- Allows querying all separately persisted containers, but not necessary for the application. CREATE TABLE m_container ( @@ -302,7 +316,8 @@ CREATE TABLE m_reference ( -- stores AssignmentHolderType/archetypeRef CREATE TABLE m_ref_archetype ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - referenceType ReferenceType GENERATED ALWAYS AS ('ARCHETYPE') STORED, + referenceType ReferenceType GENERATED ALWAYS AS ('ARCHETYPE') STORED + CHECK (referenceType = 'ARCHETYPE'), PRIMARY KEY (ownerOid, relationId, targetOid) ) @@ -314,7 +329,8 @@ CREATE INDEX m_ref_archetypeTargetOidRelationId_idx -- stores AssignmentHolderType/delegatedRef CREATE TABLE m_ref_delegated ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - referenceType ReferenceType GENERATED ALWAYS AS ('DELEGATED') STORED, + referenceType ReferenceType GENERATED ALWAYS AS ('DELEGATED') STORED + CHECK (referenceType = 'DELEGATED'), PRIMARY KEY (ownerOid, relationId, targetOid) ) @@ -326,7 +342,8 @@ CREATE INDEX m_ref_delegatedTargetOidRelationId_idx -- stores ObjectType/metadata/createApproverRef CREATE TABLE m_ref_object_create_approver ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - referenceType ReferenceType GENERATED ALWAYS AS ('OBJECT_CREATE_APPROVER') STORED, + referenceType ReferenceType GENERATED ALWAYS AS ('OBJECT_CREATE_APPROVER') STORED + CHECK (referenceType = 'OBJECT_CREATE_APPROVER'), PRIMARY KEY (ownerOid, relationId, targetOid) ) @@ -338,7 +355,8 @@ CREATE INDEX m_ref_object_create_approverTargetOidRelationId_idx -- stores ObjectType/metadata/modifyApproverRef CREATE TABLE m_ref_object_modify_approver ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - referenceType ReferenceType GENERATED ALWAYS AS ('OBJECT_MODIFY_APPROVER') STORED, + referenceType ReferenceType GENERATED ALWAYS AS ('OBJECT_MODIFY_APPROVER') STORED + CHECK (referenceType = 'OBJECT_MODIFY_APPROVER'), PRIMARY KEY (ownerOid, relationId, targetOid) ) @@ -350,7 +368,8 @@ CREATE INDEX m_ref_object_modify_approverTargetOidRelationId_idx -- stores AssignmentHolderType/roleMembershipRef CREATE TABLE m_ref_role_membership ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - referenceType ReferenceType GENERATED ALWAYS AS ('ROLE_MEMBERSHIP') STORED, + referenceType ReferenceType GENERATED ALWAYS AS ('ROLE_MEMBERSHIP') STORED + CHECK (referenceType = 'ROLE_MEMBERSHIP'), PRIMARY KEY (ownerOid, relationId, targetOid) ) @@ -362,10 +381,10 @@ CREATE INDEX m_ref_role_memberTargetOidRelationId_idx -- region FOCUS related tables -- Represents FocusType (Users, Roles, ...), see https://docs.evolveum.com/midpoint/reference/schema/focus-and-projections/ --- extending m_object, but still abstract, hence DEFAULT for objectType and CHECK (false) +-- extending m_object, but still abstract, hence the CHECK (false) CREATE TABLE m_focus ( - -- will be overridden with GENERATED value in concrete table - objectType ObjectType NOT NULL, + -- objectType will be overridden with GENERATED value in concrete table + objectType ObjectType NOT NULL CHECK (objectType = 'FOCUS') NO INHERIT, costCenter TEXT, emailAddress TEXT, photo BYTEA, -- will be TOAST-ed if necessary @@ -393,12 +412,13 @@ CREATE TABLE m_focus ( CHECK (FALSE) NO INHERIT ) - INHERITS (m_object); + INHERITS (m_assignment_holder); -- stores FocusType/personaRef CREATE TABLE m_ref_persona ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - referenceType ReferenceType GENERATED ALWAYS AS ('PERSONA') STORED, + referenceType ReferenceType GENERATED ALWAYS AS ('PERSONA') STORED + CHECK (referenceType = 'PERSONA'), PRIMARY KEY (ownerOid, relationId, targetOid) ) @@ -410,7 +430,8 @@ CREATE INDEX m_ref_personaTargetOidRelationId_idx -- stores FocusType/linkRef ("projection" is newer and better term) CREATE TABLE m_ref_projection ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - referenceType ReferenceType GENERATED ALWAYS AS ('PROJECTION') STORED, + referenceType ReferenceType GENERATED ALWAYS AS ('PROJECTION') STORED + CHECK (referenceType = 'PROJECTION'), PRIMARY KEY (ownerOid, relationId, targetOid) ) @@ -422,7 +443,8 @@ CREATE INDEX m_ref_projectionTargetOidRelationId_idx -- Represents GenericObjectType, see https://docs.evolveum.com/midpoint/reference/schema/generic-objects/ CREATE TABLE m_generic_object ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('GENERIC_OBJECT') STORED, + objectType ObjectType GENERATED ALWAYS AS ('GENERIC_OBJECT') STORED + CHECK (objectType = 'GENERIC_OBJECT'), genericObjectTypeId INTEGER NOT NULL REFERENCES m_uri(id) -- GenericObjectType#objectType ) INHERITS (m_focus); @@ -444,7 +466,8 @@ ALTER TABLE m_generic_object ADD CONSTRAINT m_generic_object_nameNorm_key UNIQUE -- Represents UserType, see https://docs.evolveum.com/midpoint/architecture/archive/data-model/midpoint-common-schema/usertype/ CREATE TABLE m_user ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('USER') STORED, + objectType ObjectType GENERATED ALWAYS AS ('USER') STORED + CHECK (objectType = 'USER'), additionalNameOrig TEXT, additionalNameNorm TEXT, employeeNumber TEXT, @@ -498,8 +521,8 @@ CREATE TABLE m_user_organizational_unit ( -- region ROLE related tables -- Represents AbstractRoleType, see https://docs.evolveum.com/midpoint/architecture/concepts/abstract-role/ CREATE TABLE m_abstract_role ( - -- will be overridden with GENERATED value in concrete table - objectType ObjectType NOT NULL, + -- objectType will be overridden with GENERATED value in concrete table + objectType ObjectType NOT NULL CHECK (objectType = 'ABSTRACT_ROLE') NO INHERIT, autoAssignEnabled BOOLEAN, displayNameOrig TEXT, displayNameNorm TEXT, @@ -520,7 +543,8 @@ CREATE INDEX iAutoassignEnabled ON m_abstract_role(autoassign_enabled); -- Represents RoleType, see https://docs.evolveum.com/midpoint/architecture/archive/data-model/midpoint-common-schema/roletype/ CREATE TABLE m_role ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('ROLE') STORED, + objectType ObjectType GENERATED ALWAYS AS ('ROLE') STORED + CHECK (objectType = 'ROLE'), roleType TEXT ) INHERITS (m_abstract_role); @@ -538,7 +562,8 @@ ALTER TABLE m_role ADD CONSTRAINT m_role_nameNorm_key UNIQUE (nameNorm); -- Represents ServiceType, see https://wiki.evolveum.com/display/midPoint/Service+Account+Management CREATE TABLE m_service ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('SERVICE') STORED, + objectType ObjectType GENERATED ALWAYS AS ('SERVICE') STORED + CHECK (objectType = 'SERVICE'), displayOrder INTEGER ) INHERITS (m_abstract_role); @@ -557,6 +582,7 @@ ALTER TABLE m_service ADD CONSTRAINT m_service_nameNorm_key UNIQUE (nameNorm); CREATE TABLE m_archetype ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('ARCHETYPE') STORED + CHECK (objectType = 'ARCHETYPE') ) INHERITS (m_abstract_role); @@ -575,7 +601,8 @@ ALTER TABLE m_archetype ADD CONSTRAINT m_archetype_nameNorm_key UNIQUE (nameNorm -- Represents OrgType, see https://docs.evolveum.com/midpoint/architecture/archive/data-model/midpoint-common-schema/orgtype/ CREATE TABLE m_org ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('ORG') STORED, + objectType ObjectType GENERATED ALWAYS AS ('ORG') STORED + CHECK (objectType = 'ORG'), displayOrder INTEGER, tenant BOOLEAN ) @@ -595,7 +622,8 @@ CREATE INDEX m_org_displayOrder_idx ON m_org (displayOrder); -- stores ObjectType/parentOrgRef CREATE TABLE m_ref_object_parent_org ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - referenceType ReferenceType GENERATED ALWAYS AS ('OBJECT_PARENT_ORG') STORED, + referenceType ReferenceType GENERATED ALWAYS AS ('OBJECT_PARENT_ORG') STORED + CHECK (referenceType = 'OBJECT_PARENT_ORG'), -- TODO wouldn't (ownerOid, targetOid, relationId) perform better for typical queries? PRIMARY KEY (ownerOid, relationId, targetOid) @@ -702,14 +730,15 @@ END; $$; -- Represents ResourceType, see https://wiki.evolveum.com/display/midPoint/Resource+Configuration CREATE TABLE m_resource ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('RESOURCE') STORED, + objectType ObjectType GENERATED ALWAYS AS ('RESOURCE') STORED + CHECK (objectType = 'RESOURCE'), business_administrativeState ResourceAdministrativeStateType, operationalState_lastAvailabilityStatus AvailabilityStatusType, connectorRefTargetOid UUID, connectorRefTargetType ObjectType, connectorRefRelationId INTEGER REFERENCES m_uri(id) ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_resource_oid_insert_tr BEFORE INSERT ON m_resource FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -738,7 +767,8 @@ CREATE INDEX m_ref_resource_biz_config_approverTargetOidRelationId_idx -- and also https://docs.evolveum.com/midpoint/reference/schema/focus-and-projections/ CREATE TABLE m_shadow ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('SHADOW') STORED, + objectType ObjectType GENERATED ALWAYS AS ('SHADOW') STORED + CHECK (objectType = 'SHADOW'), objectClassId INTEGER REFERENCES m_uri(id), resourceRefTargetOid UUID, resourceRefTargetType ObjectType, @@ -788,10 +818,11 @@ ALTER TABLE m_shadow ADD CONSTRAINT iPrimaryIdentifierValueWithOC -- Represents NodeType, see https://wiki.evolveum.com/display/midPoint/Managing+cluster+nodes CREATE TABLE m_node ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('NODE') STORED, + objectType ObjectType GENERATED ALWAYS AS ('NODE') STORED + CHECK (objectType = 'NODE'), nodeIdentifier TEXT ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_node_oid_insert_tr BEFORE INSERT ON m_node FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -808,8 +839,9 @@ ALTER TABLE m_node ADD CONSTRAINT m_node_nameNorm_key UNIQUE (nameNorm); CREATE TABLE m_system_configuration ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('SYSTEM_CONFIGURATION') STORED + CHECK (objectType = 'SYSTEM_CONFIGURATION') ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_system_configuration_oid_insert_tr BEFORE INSERT ON m_system_configuration FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -824,8 +856,9 @@ CREATE TRIGGER m_system_configuration_oid_delete_tr AFTER DELETE ON m_system_con CREATE TABLE m_security_policy ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('SECURITY_POLICY') STORED + CHECK (objectType = 'SECURITY_POLICY') ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_security_policy_oid_insert_tr BEFORE INSERT ON m_security_policy FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -840,8 +873,9 @@ CREATE TRIGGER m_security_policy_oid_delete_tr AFTER DELETE ON m_security_policy CREATE TABLE m_object_collection ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('OBJECT_COLLECTION') STORED + CHECK (objectType = 'OBJECT_COLLECTION') ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_object_collection_oid_insert_tr BEFORE INSERT ON m_object_collection FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -857,8 +891,9 @@ ALTER TABLE m_object_collection ADD CONSTRAINT m_object_collection_nameNorm_key CREATE TABLE m_dashboard ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('DASHBOARD') STORED + CHECK (objectType = 'DASHBOARD') ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_dashboard_oid_insert_tr BEFORE INSERT ON m_dashboard FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -874,8 +909,9 @@ ALTER TABLE m_dashboard ADD CONSTRAINT m_dashboard_nameNorm_key UNIQUE (nameNorm CREATE TABLE m_value_policy ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('VALUE_POLICY') STORED + CHECK (objectType = 'VALUE_POLICY') ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_value_policy_oid_insert_tr BEFORE INSERT ON m_value_policy FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -890,11 +926,12 @@ ALTER TABLE m_value_policy ADD CONSTRAINT m_value_policy_nameNorm_key UNIQUE (na -- Represents ReportType, see https://wiki.evolveum.com/display/midPoint/Report+Configuration CREATE TABLE m_report ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('REPORT') STORED, + objectType ObjectType GENERATED ALWAYS AS ('REPORT') STORED + CHECK (objectType = 'REPORT'), orientation OrientationType, parent BOOLEAN ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_report_oid_insert_tr BEFORE INSERT ON m_report FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -910,12 +947,13 @@ ALTER TABLE m_report ADD CONSTRAINT m_report_nameNorm_key UNIQUE (nameNorm); -- Represents ReportDataType, see also m_report above CREATE TABLE m_report_data ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('REPORT_DATA') STORED, + objectType ObjectType GENERATED ALWAYS AS ('REPORT_DATA') STORED + CHECK (objectType = 'REPORT_DATA'), reportRefTargetOid UUID, reportRefTargetType ObjectType, reportRefRelationId INTEGER REFERENCES m_uri(id) ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_report_data_oid_insert_tr BEFORE INSERT ON m_report_data FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -931,8 +969,9 @@ ALTER TABLE m_report_data ADD CONSTRAINT m_report_data_nameNorm_key UNIQUE (name CREATE TABLE m_lookup_table ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('LOOKUP_TABLE') STORED + CHECK (objectType = 'LOOKUP_TABLE') ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_lookup_table_oid_insert_tr BEFORE INSERT ON m_lookup_table FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -947,7 +986,8 @@ ALTER TABLE m_lookup_table ADD CONSTRAINT m_lookup_table_nameNorm_key UNIQUE (na -- Represents LookupTableRowType, see also m_lookup_table above CREATE TABLE m_lookup_table_row ( ownerOid UUID NOT NULL REFERENCES m_lookup_table(oid) ON DELETE CASCADE, - containerType ContainerType GENERATED ALWAYS AS ('LOOKUP_TABLE_ROW') STORED, + containerType ContainerType GENERATED ALWAYS AS ('LOOKUP_TABLE_ROW') STORED + CHECK (containerType = 'LOOKUP_TABLE_ROW'), key TEXT, value TEXT, labelOrig TEXT, @@ -964,7 +1004,8 @@ ALTER TABLE m_lookup_table_row -- Represents ConnectorType, see https://wiki.evolveum.com/display/midPoint/Identity+Connectors CREATE TABLE m_connector ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('CONNECTOR') STORED, + objectType ObjectType GENERATED ALWAYS AS ('CONNECTOR') STORED + CHECK (objectType = 'CONNECTOR'), connectorBundle TEXT, -- typically a package name connectorType TEXT, -- typically a class name connectorVersion TEXT, @@ -974,7 +1015,7 @@ CREATE TABLE m_connector ( connectorHostRefRelationId INTEGER REFERENCES m_uri(id), targetSystemTypes TEXT[] -- TODO any strings? cached URIs? ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_connector_oid_insert_tr BEFORE INSERT ON m_connector FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -997,11 +1038,12 @@ ALTER TABLE m_connector ADD CONSTRAINT m_connector_nameNorm_key UNIQUE (nameNorm -- Represents ConnectorHostType, see https://wiki.evolveum.com/display/midPoint/Connector+Server CREATE TABLE m_connector_host ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('CONNECTOR_HOST') STORED, + objectType ObjectType GENERATED ALWAYS AS ('CONNECTOR_HOST') STORED + CHECK (objectType = 'CONNECTOR_HOST'), hostname TEXT, port TEXT ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_connector_host_oid_insert_tr BEFORE INSERT ON m_connector_host FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -1016,7 +1058,8 @@ ALTER TABLE m_connector_host ADD CONSTRAINT m_connector_host_nameNorm_key UNIQUE -- Represents persistent TaskType, see https://wiki.evolveum.com/display/midPoint/Task+Manager CREATE TABLE m_task ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('TASK') STORED, + objectType ObjectType GENERATED ALWAYS AS ('TASK') STORED + CHECK (objectType = 'TASK'), taskIdentifier TEXT, binding TaskBindingType, category TEXT, @@ -1040,7 +1083,7 @@ CREATE TABLE m_task ( waitingReason TaskWaitingReasonType, dependentTaskIdentifiers TEXT[] -- contains values of taskIdentifier ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_task_oid_insert_tr BEFORE INSERT ON m_task FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -1061,7 +1104,8 @@ CREATE INDEX m_task_dependentTaskIdentifiers_idx ON m_task USING GIN(dependentTa -- Represents CaseType, see https://wiki.evolveum.com/display/midPoint/Case+Management CREATE TABLE m_case ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('CASE') STORED, + objectType ObjectType GENERATED ALWAYS AS ('CASE') STORED + CHECK (objectType = 'CASE'), state TEXT, closeTimestamp TIMESTAMPTZ, objectRefTargetOid UUID, @@ -1077,7 +1121,7 @@ CREATE TABLE m_case ( targetRefTargetType ObjectType, targetRefRelationId INTEGER REFERENCES m_uri(id) ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_case_oid_insert_tr BEFORE INSERT ON m_case FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -1097,7 +1141,8 @@ CREATE INDEX m_case_closeTimestamp_idx ON m_case(closeTimestamp); CREATE TABLE m_case_wi ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - containerType ContainerType GENERATED ALWAYS AS ('CASE_WORK_ITEM') STORED, + containerType ContainerType GENERATED ALWAYS AS ('CASE_WORK_ITEM') STORED + CHECK (containerType = 'CASE_WORK_ITEM'), closeTimestamp TIMESTAMPTZ, createTimestamp TIMESTAMPTZ, deadline TIMESTAMPTZ, @@ -1119,7 +1164,8 @@ CREATE TABLE m_case_wi ( -- Represents AccessCertificationDefinitionType, see https://wiki.evolveum.com/display/midPoint/Access+Certification CREATE TABLE m_access_cert_definition ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_DEFINITION') STORED, + objectType ObjectType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_DEFINITION') STORED + CHECK (objectType = 'ACCESS_CERTIFICATION_DEFINITION'), handlerUriId INTEGER REFERENCES m_uri(id), lastCampaignStartedTimestamp TIMESTAMPTZ, lastCampaignClosedTimestamp TIMESTAMPTZ, @@ -1127,7 +1173,7 @@ CREATE TABLE m_access_cert_definition ( ownerRefTargetType ObjectType, ownerRefRelationId INTEGER REFERENCES m_uri(id) ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_access_cert_definition_oid_insert_tr BEFORE INSERT ON m_access_cert_definition FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -1144,7 +1190,8 @@ CREATE INDEX m_access_cert_definition_ext_idx ON m_access_cert_definition USING -- TODO not mapped yet CREATE TABLE m_access_cert_campaign ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_CAMPAIGN') STORED, + objectType ObjectType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_CAMPAIGN') STORED + CHECK (objectType = 'ACCESS_CERTIFICATION_CAMPAIGN'), definitionRefTargetOid UUID, definitionRefTargetType ObjectType, definitionRefRelationId INTEGER REFERENCES m_uri(id), @@ -1158,7 +1205,7 @@ CREATE TABLE m_access_cert_campaign ( startTimestamp TIMESTAMPTZ, state INTEGER ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_access_cert_campaign_oid_insert_tr BEFORE INSERT ON m_access_cert_campaign FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -1174,7 +1221,8 @@ CREATE INDEX m_access_cert_campaign_ext_idx ON m_access_cert_campaign USING gin CREATE TABLE m_access_cert_case ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_CASE') STORED, + containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_CASE') STORED + CHECK (containerType = 'ACCESS_CERTIFICATION_CASE'), administrativeStatus INTEGER, archiveTimestamp TIMESTAMPTZ, disableReason TEXT, @@ -1213,7 +1261,8 @@ CREATE TABLE m_access_cert_case ( CREATE TABLE m_access_cert_wi ( ownerOid UUID NOT NULL, -- PK+FK accCertCaseCid INTEGER NOT NULL, -- PK+FK - containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_WORK_ITEM') STORED, + containerType ContainerType GENERATED ALWAYS AS ('ACCESS_CERTIFICATION_WORK_ITEM') STORED + CHECK (containerType = 'ACCESS_CERTIFICATION_WORK_ITEM'), closeTimestamp TIMESTAMPTZ, iteration INTEGER NOT NULL, outcome TEXT, @@ -1267,8 +1316,9 @@ CREATE INDEX iCertWorkItemRefTargetOid ON m_access_cert_wi_reference (targetOid) CREATE TABLE m_object_template ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('OBJECT_TEMPLATE') STORED + CHECK (objectType = 'OBJECT_TEMPLATE') ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_object_template_oid_insert_tr BEFORE INSERT ON m_object_template FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -1283,7 +1333,8 @@ ALTER TABLE m_object_template ADD CONSTRAINT m_object_template_nameNorm_key UNIQ -- stores ObjectTemplateType/includeRef CREATE TABLE m_ref_include ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - referenceType ReferenceType GENERATED ALWAYS AS ('INCLUDE') STORED, + referenceType ReferenceType GENERATED ALWAYS AS ('INCLUDE') STORED + CHECK (referenceType = 'INCLUDE'), PRIMARY KEY (ownerOid, relationId, targetOid) ) @@ -1298,8 +1349,9 @@ CREATE INDEX m_ref_includeTargetOidRelationId_idx CREATE TABLE m_function_library ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('FUNCTION_LIBRARY') STORED + CHECK (objectType = 'FUNCTION_LIBRARY') ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_function_library_oid_insert_tr BEFORE INSERT ON m_function_library FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -1315,8 +1367,9 @@ ALTER TABLE m_function_library ADD CONSTRAINT m_function_library_nameNorm_key UN CREATE TABLE m_sequence ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), objectType ObjectType GENERATED ALWAYS AS ('SEQUENCE') STORED + CHECK (objectType = 'SEQUENCE') ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_sequence_oid_insert_tr BEFORE INSERT ON m_sequence FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -1331,9 +1384,10 @@ ALTER TABLE m_sequence ADD CONSTRAINT m_sequence_nameNorm_key UNIQUE (nameNorm); -- Represents FormType, see https://wiki.evolveum.com/display/midPoint/Custom+forms CREATE TABLE m_form ( oid UUID NOT NULL PRIMARY KEY REFERENCES m_object_oid(oid), - objectType ObjectType GENERATED ALWAYS AS ('SEQUENCE') STORED + objectType ObjectType GENERATED ALWAYS AS ('FORM') STORED + CHECK (objectType = 'FORM') ) - INHERITS (m_object); + INHERITS (m_assignment_holder); CREATE TRIGGER m_form_oid_insert_tr BEFORE INSERT ON m_form FOR EACH ROW EXECUTE PROCEDURE insert_object_oid(); @@ -1422,7 +1476,8 @@ CREATE INDEX m_assignment_resourceRefTargetOid_idx ON m_assignment (resourceRefT CREATE TABLE m_assignment_ref_create_approver ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, assignmentCid INTEGER NOT NULL, - referenceType ReferenceType GENERATED ALWAYS AS ('ASSIGNMENT_CREATE_APPROVER') STORED, + referenceType ReferenceType GENERATED ALWAYS AS ('ASSIGNMENT_CREATE_APPROVER') STORED + CHECK (referenceType = 'ASSIGNMENT_CREATE_APPROVER'), PRIMARY KEY (ownerOid, assignmentCid, referenceType, relationId, targetOid) ) @@ -1437,7 +1492,8 @@ ALTER TABLE m_assignment_ref_create_approver ADD CONSTRAINT m_assignment_ref_cre CREATE TABLE m_assignment_ref_modify_approver ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, assignmentCid INTEGER NOT NULL, - referenceType ReferenceType GENERATED ALWAYS AS ('ASSIGNMENT_MODIFY_APPROVER') STORED, + referenceType ReferenceType GENERATED ALWAYS AS ('ASSIGNMENT_MODIFY_APPROVER') STORED + CHECK (referenceType = 'ASSIGNMENT_MODIFY_APPROVER'), PRIMARY KEY (ownerOid, assignmentCid, referenceType, relationId, targetOid) ) @@ -1453,7 +1509,8 @@ ALTER TABLE m_assignment_ref_modify_approver ADD CONSTRAINT m_assignment_ref_mod -- stores ObjectType/trigger (TriggerType) CREATE TABLE m_trigger ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - containerType ContainerType GENERATED ALWAYS AS ('TRIGGER') STORED, + containerType ContainerType GENERATED ALWAYS AS ('TRIGGER') STORED + CHECK (containerType = 'TRIGGER'), handlerUriId INTEGER REFERENCES m_uri(id), timestampValue TIMESTAMPTZ, @@ -1466,7 +1523,8 @@ CREATE INDEX m_trigger_timestampValue_idx ON m_trigger (timestampValue); -- stores ObjectType/operationExecution (OperationExecutionType) CREATE TABLE m_operation_execution ( ownerOid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - containerType ContainerType GENERATED ALWAYS AS ('OPERATION_EXECUTION') STORED, + containerType ContainerType GENERATED ALWAYS AS ('OPERATION_EXECUTION') STORED + CHECK (containerType = 'OPERATION_EXECUTION'), status OperationResultStatusType, recordType OperationExecutionRecordTypeType, initiatorRefTargetOid UUID, From 2a46d0ce83ad4b3c23929cd34eaf74be755abe62 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Tue, 25 May 2021 16:01:01 +0200 Subject: [PATCH 36/43] repo-sqale: QAssignmentHolder fully incorporated into Q-hierarchy This eliminates the need for additional TYPE filter if AssignHolderType is queried (refineAssignmentHolderQuery from old repo). Also, mapping stuff is more clean now, QObject(Mapping) does not contain anything beyond ObjectType mapping. QCaseMapping#storeRelatedEntities fixed, now it calls super.method(). --- .../repo/sqale/qmodel/QObjectTemplate.java | 4 +- .../sqale/qmodel/QObjectTemplateMapping.java | 4 +- .../QAccessCertificationDefinition.java | 4 +- ...QAccessCertificationDefinitionMapping.java | 4 +- .../repo/sqale/qmodel/cases/QCase.java | 4 +- .../repo/sqale/qmodel/cases/QCaseMapping.java | 23 ++- .../sqale/qmodel/connector/QConnector.java | 4 +- .../qmodel/connector/QConnectorHost.java | 4 +- .../connector/QConnectorHostMapping.java | 4 +- .../qmodel/connector/QConnectorMapping.java | 4 +- .../repo/sqale/qmodel/focus/QFocus.java | 4 +- .../sqale/qmodel/focus/QFocusMapping.java | 4 +- .../qmodel/lookuptable/MLookupTable.java | 2 +- .../qmodel/lookuptable/QLookupTable.java | 4 +- .../lookuptable/QLookupTableMapping.java | 4 +- .../repo/sqale/qmodel/node/QNode.java | 4 +- .../repo/sqale/qmodel/node/QNodeMapping.java | 4 +- .../repo/sqale/qmodel/object/MObjectType.java | 3 +- .../qmodel/object/QAssignmentHolder.java | 33 ++++ .../object/QAssignmentHolderMapping.java | 83 +++++++-- .../sqale/qmodel/object/QObjectMapping.java | 35 +--- .../repo/sqale/qmodel/other/QDashboard.java | 4 +- .../sqale/qmodel/other/QDashboardMapping.java | 4 +- .../repo/sqale/qmodel/other/QForm.java | 4 +- .../repo/sqale/qmodel/other/QFormMapping.java | 4 +- .../sqale/qmodel/other/QFunctionLibrary.java | 4 +- .../qmodel/other/QFunctionLibraryMapping.java | 4 +- .../sqale/qmodel/other/QObjectCollection.java | 4 +- .../other/QObjectCollectionMapping.java | 4 +- .../repo/sqale/qmodel/other/QSequence.java | 4 +- .../sqale/qmodel/other/QSequenceMapping.java | 4 +- .../repo/sqale/qmodel/report/QReport.java | 4 +- .../repo/sqale/qmodel/report/QReportData.java | 4 +- .../qmodel/report/QReportDataMapping.java | 4 +- .../sqale/qmodel/report/QReportMapping.java | 4 +- .../repo/sqale/qmodel/resource/QResource.java | 4 +- .../qmodel/resource/QResourceMapping.java | 5 +- .../repo/sqale/qmodel/shadow/QShadow.java | 1 - .../sqale/qmodel/system/QSecurityPolicy.java | 4 +- .../qmodel/system/QSecurityPolicyMapping.java | 4 +- .../qmodel/system/QSystemConfiguration.java | 4 +- .../system/QSystemConfigurationMapping.java | 4 +- .../sqale/qmodel/system/QValuePolicy.java | 4 +- .../qmodel/system/QValuePolicyMapping.java | 4 +- .../repo/sqale/qmodel/task/QTask.java | 4 +- .../repo/sqale/qmodel/task/QTaskMapping.java | 4 +- .../sqale/func/SqaleRepoSearchObjectTest.java | 161 +++++++++++++++++- 47 files changed, 349 insertions(+), 149 deletions(-) create mode 100644 repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QAssignmentHolder.java diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QObjectTemplate.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QObjectTemplate.java index b84083fa0f5..29b55f4e038 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QObjectTemplate.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QObjectTemplate.java @@ -7,13 +7,13 @@ package com.evolveum.midpoint.repo.sqale.qmodel; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QObjectTemplate extends QObject { +public class QObjectTemplate extends QAssignmentHolder { private static final long serialVersionUID = 5428353336949587877L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QObjectTemplateMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QObjectTemplateMapping.java index 7bf93c339ee..316d8fe87c6 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QObjectTemplateMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/QObjectTemplateMapping.java @@ -12,7 +12,7 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTemplateType; @@ -21,7 +21,7 @@ * Mapping between {@link QObjectTemplate} and {@link ObjectTemplateType}. */ public class QObjectTemplateMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "ot"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinition.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinition.java index f5205f73b67..7b843daabb1 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinition.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinition.java @@ -15,14 +15,14 @@ import com.querydsl.sql.ColumnMetadata; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QAccessCertificationDefinition extends QObject { +public class QAccessCertificationDefinition extends QAssignmentHolder { private static final long serialVersionUID = 6073628996722018176L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinitionMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinitionMapping.java index 0c782c0d664..0a306d0e972 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinitionMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/accesscert/QAccessCertificationDefinitionMapping.java @@ -11,7 +11,7 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationDefinitionType; @@ -21,7 +21,7 @@ * and {@link AccessCertificationDefinitionType}. */ public class QAccessCertificationDefinitionMapping - extends QObjectMapping { public static final String DEFAULT_ALIAS_NAME = "acd"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCase.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCase.java index eeacb2eec81..79d0d3fe102 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCase.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCase.java @@ -16,14 +16,14 @@ import com.querydsl.sql.ColumnMetadata; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QCase extends QObject { +public class QCase extends QAssignmentHolder { private static final long serialVersionUID = -5546874425855732858L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java index 3d82493033a..6cd9744e811 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/cases/QCaseMapping.java @@ -9,13 +9,12 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType.*; import java.util.List; -import java.util.Objects; import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.cases.workitem.QCaseWorkItemMapping; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType; @@ -25,7 +24,7 @@ * Mapping between {@link QCase} and {@link CaseType}. */ public class QCaseMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "cs"; @@ -73,24 +72,24 @@ public MCase newRowObject() { @Override public @NotNull MCase toRowObjectWithoutFullObject( - CaseType acase, JdbcSession jdbcSession) { - MCase row = super.toRowObjectWithoutFullObject(acase, jdbcSession); + CaseType schemaObject, JdbcSession jdbcSession) { + MCase row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); - row.state = acase.getState(); - row.closeTimestamp = MiscUtil.asInstant(acase.getCloseTimestamp()); - setReference(acase.getObjectRef(), + row.state = schemaObject.getState(); + row.closeTimestamp = MiscUtil.asInstant(schemaObject.getCloseTimestamp()); + setReference(schemaObject.getObjectRef(), o -> row.objectRefTargetOid = o, t -> row.objectRefTargetType = t, r -> row.objectRefRelationId = r); - setReference(acase.getParentRef(), + setReference(schemaObject.getParentRef(), o -> row.parentRefTargetOid = o, t -> row.parentRefTargetType = t, r -> row.parentRefRelationId = r); - setReference(acase.getRequestorRef(), + setReference(schemaObject.getRequestorRef(), o -> row.requestorRefTargetOid = o, t -> row.requestorRefTargetType = t, r -> row.requestorRefRelationId = r); - setReference(acase.getTargetRef(), + setReference(schemaObject.getTargetRef(), o -> row.targetRefTargetOid = o, t -> row.targetRefTargetType = t, r -> row.targetRefRelationId = r); @@ -101,7 +100,7 @@ public MCase newRowObject() { @Override public void storeRelatedEntities( @NotNull MCase row, @NotNull CaseType schemaObject, @NotNull JdbcSession jdbcSession) { - Objects.requireNonNull(row.oid); + super.storeRelatedEntities(row, schemaObject, jdbcSession); List workItems = schemaObject.getWorkItem(); if (!workItems.isEmpty()) { diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnector.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnector.java index ebeaa3fcca0..d1d8f4d31d0 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnector.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnector.java @@ -15,14 +15,14 @@ import com.querydsl.sql.ColumnMetadata; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QConnector extends QObject { +public class QConnector extends QAssignmentHolder { private static final long serialVersionUID = 6073628996722018176L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorHost.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorHost.java index 46db9898f46..a2a756bf6b6 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorHost.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorHost.java @@ -11,13 +11,13 @@ import com.querydsl.core.types.dsl.StringPath; import com.querydsl.sql.ColumnMetadata; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QConnectorHost extends QObject { +public class QConnectorHost extends QAssignmentHolder { private static final long serialVersionUID = 8908570767190499506L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorHostMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorHostMapping.java index ccf8474a3a6..609d168aaf2 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorHostMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorHostMapping.java @@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorHostType; @@ -20,7 +20,7 @@ * Mapping between {@link QConnectorHost} and {@link ConnectorHostType}. */ public class QConnectorHostMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "conh"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorMapping.java index aebdedbbd45..99c76974d9a 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorMapping.java @@ -11,7 +11,7 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorType; @@ -19,7 +19,7 @@ * Mapping between {@link QConnector} and {@link ConnectorType}. */ public class QConnectorMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "con"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java index 94e6fceaa20..f13b2e90641 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocus.java @@ -15,7 +15,7 @@ import com.querydsl.core.types.dsl.StringPath; import com.querydsl.sql.ColumnMetadata; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.LockoutStatusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.TimeIntervalStatusType; @@ -24,7 +24,7 @@ * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QFocus extends QObject { +public class QFocus extends QAssignmentHolder { private static final long serialVersionUID = -535915621882761789L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocusMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocusMapping.java index 92fb26d92ae..c922fde4528 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocusMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/QFocusMapping.java @@ -14,7 +14,7 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.schema.GetOperationOptions; @@ -30,7 +30,7 @@ * @param row type related to the {@link Q} */ public class QFocusMapping, R extends MFocus> - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "f"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/MLookupTable.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/MLookupTable.java index 6e6aab32bed..dc29a59acf4 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/MLookupTable.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/MLookupTable.java @@ -13,5 +13,5 @@ */ public class MLookupTable extends MObject { - // TODO if no additional fields are needed (e.g. for rows) replace with MObject + // TODO if no additional fields are needed (e.g. for rows) replace with MAssignmentHolder } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTable.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTable.java index f2420f6e0f3..e22ae1f8e4d 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTable.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTable.java @@ -6,13 +6,13 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.lookuptable; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QLookupTable extends QObject { +public class QLookupTable extends QAssignmentHolder { private static final long serialVersionUID = -2040531200445583676L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableMapping.java index a7723179d0f..4caea3c6eec 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/lookuptable/QLookupTableMapping.java @@ -13,7 +13,7 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; @@ -22,7 +22,7 @@ * Mapping between {@link QLookupTable} and {@link LookupTableType}. */ public class QLookupTableMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "lt"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/QNode.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/QNode.java index 7ec429f8bda..85b03c7c263 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/QNode.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/QNode.java @@ -11,13 +11,13 @@ import com.querydsl.core.types.dsl.StringPath; import com.querydsl.sql.ColumnMetadata; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QNode extends QObject { +public class QNode extends QAssignmentHolder { private static final long serialVersionUID = 2042159341967925185L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/QNodeMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/QNodeMapping.java index 397cd366306..49328c31835 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/QNodeMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/node/QNodeMapping.java @@ -11,7 +11,7 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.NodeType; @@ -19,7 +19,7 @@ * Mapping between {@link QNode} and {@link NodeType}. */ public class QNodeMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "nod"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/MObjectType.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/MObjectType.java index bd3fae411a5..90b49e444e3 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/MObjectType.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/MObjectType.java @@ -23,7 +23,6 @@ import com.evolveum.midpoint.repo.sqale.qmodel.focus.QUser; import com.evolveum.midpoint.repo.sqale.qmodel.lookuptable.QLookupTable; import com.evolveum.midpoint.repo.sqale.qmodel.node.QNode; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping.QAssignmentHolder; import com.evolveum.midpoint.repo.sqale.qmodel.other.*; import com.evolveum.midpoint.repo.sqale.qmodel.report.QReport; import com.evolveum.midpoint.repo.sqale.qmodel.report.QReportData; @@ -48,7 +47,7 @@ public enum MObjectType { ACCESS_CERTIFICATION_DEFINITION( QAccessCertificationDefinition.class, AccessCertificationDefinitionType.class), ARCHETYPE(QArchetype.class, ArchetypeType.class), - ASSIGNMENT_HOLDER(QAssignmentHolder.class, AssignmentHolderType.class), + ASSIGNMENT_HOLDER(QAssignmentHolder.CLASS, AssignmentHolderType.class), CASE(QCase.class, CaseType.class), CONNECTOR(QConnector.class, ConnectorType.class), CONNECTOR_HOST(QConnectorHost.class, ConnectorHostType.class), diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QAssignmentHolder.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QAssignmentHolder.java new file mode 100644 index 00000000000..d48a467c21b --- /dev/null +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QAssignmentHolder.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010-2021 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.repo.sqale.qmodel.object; + +/** + * Querydsl query type for {@value #TABLE_NAME} table. + */ +public class QAssignmentHolder extends QObject { + + private static final long serialVersionUID = -8772807624205702543L; + + /** + * If {@code QAssignmentHolder.class} is not enough because of generics, + * try {@code QAssignmentHolder.CLASS}. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static final Class> CLASS = + (Class) QAssignmentHolder.class; + + public static final String TABLE_NAME = "m_assignment_holder"; + + public QAssignmentHolder(Class type, String variable) { + this(type, variable, DEFAULT_SCHEMA_NAME, TABLE_NAME); + } + + public QAssignmentHolder(Class type, String variable, String schema, String table) { + super(type, variable, schema, table); + } +} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QAssignmentHolderMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QAssignmentHolderMapping.java index 170ce467d08..12f5a3eb6e7 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QAssignmentHolderMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QAssignmentHolderMapping.java @@ -6,40 +6,87 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.object; +import static com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType.*; + +import java.util.List; + import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; +import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignmentMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; +import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; /** - * Mapping for {@link AssignmentHolderType} - this is served by M_OBJECT mapping. - * Technically, many mappings should be derived from this, but they are derived from - * {@link QObjectMapping} instead and this class is ignored, except for mapping - just to support - * queries typed to {@link AssignmentHolderType}. + * Mapping between {@link QAssignmentHolder} and {@link AssignmentHolderType}. + * + * @param schema type for the assignment holder object + * @param type of entity path + * @param row type related to the {@link Q} */ -public class QAssignmentHolderMapping extends QObjectMapping< - AssignmentHolderType, - QAssignmentHolderMapping.QAssignmentHolder, - QAssignmentHolderMapping.MAssignmentHolder> { +public class QAssignmentHolderMapping< + S extends AssignmentHolderType, Q extends QAssignmentHolder, R extends MObject> + extends QObjectMapping { public static final String DEFAULT_ALIAS_NAME = "ah"; - public static QAssignmentHolderMapping init(@NotNull SqaleRepoContext repositoryContext) { - return new QAssignmentHolderMapping(repositoryContext); + public static QAssignmentHolderMapping init(@NotNull SqaleRepoContext repositoryContext) { + return new QAssignmentHolderMapping<>(QAssignmentHolder.TABLE_NAME, DEFAULT_ALIAS_NAME, + AssignmentHolderType.class, QAssignmentHolder.CLASS, + repositoryContext); } - protected QAssignmentHolderMapping(@NotNull SqaleRepoContext repositoryContext) { - super(QObject.TABLE_NAME, DEFAULT_ALIAS_NAME, - AssignmentHolderType.class, QAssignmentHolder.class, - repositoryContext); + protected QAssignmentHolderMapping( + @NotNull String tableName, + @NotNull String defaultAliasName, + @NotNull Class schemaType, + @NotNull Class queryType, + @NotNull SqaleRepoContext repositoryContext) { + super(tableName, defaultAliasName, schemaType, queryType, repositoryContext); + + addContainerTableMapping(AssignmentHolderType.F_ASSIGNMENT, + QAssignmentMapping.initAssignment(repositoryContext), + joinOn((o, a) -> o.oid.eq(a.ownerOid))); + + addRefMapping(F_ARCHETYPE_REF, QObjectReferenceMapping.initForArchetype(repositoryContext)); + addRefMapping(F_DELEGATED_REF, QObjectReferenceMapping.initForDelegated(repositoryContext)); + addRefMapping(F_ROLE_MEMBERSHIP_REF, + QObjectReferenceMapping.initForRoleMembership(repositoryContext)); + } + + @Override + protected Q newAliasInstance(String alias) { + //noinspection unchecked + return (Q) new QAssignmentHolder<>(MObject.class, alias); } - public static class MAssignmentHolder extends MObject { + @SuppressWarnings("DuplicatedCode") // activation code duplicated with assignment + @Override + public @NotNull R toRowObjectWithoutFullObject(S schemaObject, JdbcSession jdbcSession) { + R row = super.toRowObjectWithoutFullObject(schemaObject, jdbcSession); + + // TODO + return row; } - public static class QAssignmentHolder extends QObject { - private QAssignmentHolder() { - super(MAssignmentHolder.class, DEFAULT_ALIAS_NAME); + @Override + public void storeRelatedEntities( + @NotNull R row, @NotNull S schemaObject, @NotNull JdbcSession jdbcSession) { + super.storeRelatedEntities(row, schemaObject, jdbcSession); + + List assignments = schemaObject.getAssignment(); + if (!assignments.isEmpty()) { + assignments.forEach(assignment -> + QAssignmentMapping.getAssignment().insert(assignment, row, jdbcSession)); } + + storeRefs(row, schemaObject.getArchetypeRef(), + QObjectReferenceMapping.getForArchetype(), jdbcSession); + storeRefs(row, schemaObject.getDelegatedRef(), + QObjectReferenceMapping.getForDelegated(), jdbcSession); + storeRefs(row, schemaObject.getRoleMembershipRef(), + QObjectReferenceMapping.getForRoleMembership(), jdbcSession); } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java index 8fab14efb76..970a7ee9234 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java @@ -25,7 +25,6 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.SqaleUtils; import com.evolveum.midpoint.repo.sqale.qmodel.SqaleTableMapping; -import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignmentMapping; import com.evolveum.midpoint.repo.sqale.qmodel.common.QUri; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; @@ -35,7 +34,10 @@ import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationExecutionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; /** * Mapping between {@link QObject} and {@link ObjectType}. @@ -102,21 +104,12 @@ protected QObjectMapping( addRefMapping(F_PARENT_ORG_REF, QObjectReferenceMapping.initForParentOrg(repositoryContext)); - addContainerTableMapping(AssignmentHolderType.F_ASSIGNMENT, - QAssignmentMapping.initAssignment(repositoryContext), - joinOn((o, a) -> o.oid.eq(a.ownerOid))); addContainerTableMapping(F_OPERATION_EXECUTION, QOperationExecutionMapping.init(repositoryContext), joinOn((o, trg) -> o.oid.eq(trg.ownerOid))); addContainerTableMapping(F_TRIGGER, QTriggerMapping.init(repositoryContext), joinOn((o, trg) -> o.oid.eq(trg.ownerOid))); - - // AssignmentHolderType - addRefMapping(F_ARCHETYPE_REF, QObjectReferenceMapping.initForArchetype(repositoryContext)); - addRefMapping(F_DELEGATED_REF, QObjectReferenceMapping.initForDelegated(repositoryContext)); - addRefMapping(F_ROLE_MEMBERSHIP_REF, - QObjectReferenceMapping.initForRoleMembership(repositoryContext)); } @Override @@ -264,10 +257,6 @@ public void storeRelatedEntities( storeRefs(row, schemaObject.getParentOrgRef(), QObjectReferenceMapping.getForParentOrg(), jdbcSession); - if (schemaObject instanceof AssignmentHolderType) { - storeAssignmentHolderEntities(row, (AssignmentHolderType) schemaObject, jdbcSession); - } - /* TODO EAV extensions - the relevant code from old repo RObject#copyObjectInformationFromJAXB if (jaxb.getExtension() != null) { copyExtensionOrAttributesFromJAXB(jaxb.getExtension().asPrismContainerValue(), repo, repositoryContext, RObjectExtensionType.EXTENSION, generatorResult); @@ -275,22 +264,6 @@ public void storeRelatedEntities( */ } - private void storeAssignmentHolderEntities( - R row, AssignmentHolderType schemaObject, JdbcSession jdbcSession) { - List assignments = schemaObject.getAssignment(); - if (!assignments.isEmpty()) { - assignments.forEach(assignment -> - QAssignmentMapping.getAssignment().insert(assignment, row, jdbcSession)); - } - - storeRefs(row, schemaObject.getArchetypeRef(), - QObjectReferenceMapping.getForArchetype(), jdbcSession); - storeRefs(row, schemaObject.getDelegatedRef(), - QObjectReferenceMapping.getForDelegated(), jdbcSession); - storeRefs(row, schemaObject.getRoleMembershipRef(), - QObjectReferenceMapping.getForRoleMembership(), jdbcSession); - } - /** * Serializes schema object and sets {@link R#fullObject}. */ diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QDashboard.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QDashboard.java index ab9955be5e2..492c4b02213 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QDashboard.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QDashboard.java @@ -7,13 +7,13 @@ package com.evolveum.midpoint.repo.sqale.qmodel.other; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QDashboard extends QObject { +public class QDashboard extends QAssignmentHolder { private static final long serialVersionUID = -3546780348548754579L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QDashboardMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QDashboardMapping.java index 6b53c75d151..b4983755fe7 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QDashboardMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QDashboardMapping.java @@ -10,14 +10,14 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.xml.ns._public.common.common_3.DashboardType; /** * Mapping between {@link QDashboard} and {@link DashboardType}. */ public class QDashboardMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "d"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QForm.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QForm.java index b71628c07f2..7be16233b97 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QForm.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QForm.java @@ -7,13 +7,13 @@ package com.evolveum.midpoint.repo.sqale.qmodel.other; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QForm extends QObject { +public class QForm extends QAssignmentHolder { private static final long serialVersionUID = -8782193461498358493L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFormMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFormMapping.java index d4d3c5fab43..a45cdd8becc 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFormMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFormMapping.java @@ -10,13 +10,13 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.xml.ns._public.common.common_3.FormType; /** * Mapping between {@link QForm} and {@link FormType}. */ -public class QFormMapping extends QObjectMapping { +public class QFormMapping extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "form"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFunctionLibrary.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFunctionLibrary.java index 420c674b833..bbe0a1a747c 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFunctionLibrary.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFunctionLibrary.java @@ -7,13 +7,13 @@ package com.evolveum.midpoint.repo.sqale.qmodel.other; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QFunctionLibrary extends QObject { +public class QFunctionLibrary extends QAssignmentHolder { private static final long serialVersionUID = -8437964574227439797L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFunctionLibraryMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFunctionLibraryMapping.java index c9840342900..1966958c09f 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFunctionLibraryMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QFunctionLibraryMapping.java @@ -10,14 +10,14 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.xml.ns._public.common.common_3.FunctionLibraryType; /** * Mapping between {@link QFunctionLibrary} and {@link FunctionLibraryType}. */ public class QFunctionLibraryMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "flib"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QObjectCollection.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QObjectCollection.java index 957906296c1..e403462ca05 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QObjectCollection.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QObjectCollection.java @@ -7,13 +7,13 @@ package com.evolveum.midpoint.repo.sqale.qmodel.other; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QObjectCollection extends QObject { +public class QObjectCollection extends QAssignmentHolder { private static final long serialVersionUID = -62230817260259478L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QObjectCollectionMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QObjectCollectionMapping.java index 2a47f1b81d6..c112992510d 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QObjectCollectionMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QObjectCollectionMapping.java @@ -10,14 +10,14 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectCollectionType; /** * Mapping between {@link QObjectCollection} and {@link ObjectCollectionType}. */ public class QObjectCollectionMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "oc"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QSequence.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QSequence.java index a62b5d74436..fefb27dff09 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QSequence.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QSequence.java @@ -7,13 +7,13 @@ package com.evolveum.midpoint.repo.sqale.qmodel.other; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QSequence extends QObject { +public class QSequence extends QAssignmentHolder { private static final long serialVersionUID = 1340389166467004733L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QSequenceMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QSequenceMapping.java index 7a650d183c2..dce9862047b 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QSequenceMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/other/QSequenceMapping.java @@ -10,13 +10,13 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.xml.ns._public.common.common_3.SequenceType; /** * Mapping between {@link QSequence} and {@link SequenceType}. */ -public class QSequenceMapping extends QObjectMapping { +public class QSequenceMapping extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "seq"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReport.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReport.java index cc892dd3295..9ade319cc8a 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReport.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReport.java @@ -12,14 +12,14 @@ import com.querydsl.core.types.dsl.EnumPath; import com.querydsl.sql.ColumnMetadata; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; import com.evolveum.midpoint.xml.ns._public.common.common_3.OrientationType; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QReport extends QObject { +public class QReport extends QAssignmentHolder { private static final long serialVersionUID = -5738006878845987541L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportData.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportData.java index a423be8f691..acb0dfca40c 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportData.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportData.java @@ -13,14 +13,14 @@ import com.querydsl.sql.ColumnMetadata; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QReportData extends QObject { +public class QReportData extends QAssignmentHolder { private static final long serialVersionUID = -544485328996889511L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportDataMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportDataMapping.java index e409993c199..9be4859d293 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportDataMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportDataMapping.java @@ -11,7 +11,7 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.ReportDataType; @@ -19,7 +19,7 @@ * Mapping between {@link QReportData} and {@link ReportDataType}. */ public class QReportDataMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "repout"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportMapping.java index 151312f9b71..01a5a3169ff 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/report/QReportMapping.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.JasperReportEngineConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ReportType; @@ -18,7 +18,7 @@ * Mapping between {@link QReport} and {@link ReportType}. */ public class QReportMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "rep"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResource.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResource.java index c423c93aae5..96c9db6b769 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResource.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResource.java @@ -13,7 +13,7 @@ import com.querydsl.sql.ColumnMetadata; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath; import com.evolveum.midpoint.xml.ns._public.common.common_3.AvailabilityStatusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceAdministrativeStateType; @@ -22,7 +22,7 @@ * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QResource extends QObject { +public class QResource extends QAssignmentHolder { private static final long serialVersionUID = 4311838248823321876L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResourceMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResourceMapping.java index 289871fadc1..dfb5e6eda7e 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResourceMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/resource/QResourceMapping.java @@ -11,7 +11,7 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QObjectReferenceMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationalStateType; @@ -21,7 +21,8 @@ /** * Mapping between {@link QResource} and {@link ResourceType}. */ -public class QResourceMapping extends QObjectMapping { +public class QResourceMapping + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "res"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/QShadow.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/QShadow.java index 4531c09ed7b..072f2c4dcf1 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/QShadow.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/shadow/QShadow.java @@ -28,7 +28,6 @@ public class QShadow extends QObject { public static final String TABLE_NAME = "m_shadow"; - // TODO public static final ColumnMetadata OBJECT_CLASS_ID = ColumnMetadata.named("objectClassId").ofType(Types.INTEGER); public static final ColumnMetadata RESOURCE_REF_TARGET_OID = diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSecurityPolicy.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSecurityPolicy.java index d1c04cb8b8b..1629898a721 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSecurityPolicy.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSecurityPolicy.java @@ -7,13 +7,13 @@ package com.evolveum.midpoint.repo.sqale.qmodel.system; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QSecurityPolicy extends QObject { +public class QSecurityPolicy extends QAssignmentHolder { private static final long serialVersionUID = 289603300613404007L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSecurityPolicyMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSecurityPolicyMapping.java index af1e42c0853..0f2806ae855 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSecurityPolicyMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSecurityPolicyMapping.java @@ -10,14 +10,14 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.xml.ns._public.common.common_3.SecurityPolicyType; /** * Mapping between {@link QSecurityPolicy} and {@link SecurityPolicyType}. */ public class QSecurityPolicyMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "sp"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSystemConfiguration.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSystemConfiguration.java index 8d59518f318..763b227e19d 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSystemConfiguration.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSystemConfiguration.java @@ -7,13 +7,13 @@ package com.evolveum.midpoint.repo.sqale.qmodel.system; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QSystemConfiguration extends QObject { +public class QSystemConfiguration extends QAssignmentHolder { private static final long serialVersionUID = -2157392986065893792L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSystemConfigurationMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSystemConfigurationMapping.java index e54d99c287e..17aab166e07 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSystemConfigurationMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QSystemConfigurationMapping.java @@ -10,14 +10,14 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; /** * Mapping between {@link QSystemConfiguration} and {@link SystemConfigurationType}. */ public class QSystemConfigurationMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "sc"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QValuePolicy.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QValuePolicy.java index bdb931b40e0..02320efbe52 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QValuePolicy.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QValuePolicy.java @@ -7,13 +7,13 @@ package com.evolveum.midpoint.repo.sqale.qmodel.system; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; /** * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QValuePolicy extends QObject { +public class QValuePolicy extends QAssignmentHolder { private static final long serialVersionUID = 5623917383769868004L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QValuePolicyMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QValuePolicyMapping.java index 94d75b731ae..e0f26481b81 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QValuePolicyMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/system/QValuePolicyMapping.java @@ -10,14 +10,14 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.xml.ns._public.common.common_3.ValuePolicyType; /** * Mapping between {@link QValuePolicy} and {@link ValuePolicyType}. */ public class QValuePolicyMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "vp"; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTask.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTask.java index ad9ae14b128..96cb740a21b 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTask.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTask.java @@ -13,7 +13,7 @@ import com.querydsl.sql.ColumnMetadata; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; @@ -21,7 +21,7 @@ * Querydsl query type for {@value #TABLE_NAME} table. */ @SuppressWarnings("unused") -public class QTask extends QObject { +public class QTask extends QAssignmentHolder { private static final long serialVersionUID = 6249403929032616177L; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTaskMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTaskMapping.java index acd439e3ff8..5250d26665a 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTaskMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/task/QTaskMapping.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; -import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolderMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; @@ -18,7 +18,7 @@ * Mapping between {@link QTask} and {@link TaskType}. */ public class QTaskMapping - extends QObjectMapping { + extends QAssignmentHolderMapping { public static final String DEFAULT_ALIAS_NAME = "t"; diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java index e3fd231a902..6e343367d35 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java @@ -13,16 +13,20 @@ import java.util.UUID; import javax.xml.namespace.QName; -import com.evolveum.midpoint.prism.path.ItemPath; - import org.jetbrains.annotations.NotNull; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; +import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.repo.sqale.SqaleRepoBaseTest; +import com.evolveum.midpoint.repo.sqale.qmodel.focus.QFocus; +import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; +import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; +import com.evolveum.midpoint.repo.sqale.qmodel.object.QAssignmentHolder; import com.evolveum.midpoint.repo.sqale.qmodel.object.QObject; import com.evolveum.midpoint.repo.sqlbase.perfmon.SqlPerformanceMonitorImpl; import com.evolveum.midpoint.schema.GetOperationOptions; @@ -490,6 +494,7 @@ public void test301QueryForFocusWithInOid() throws SchemaException { } /* + // TODO TYPE tests - unclear how to implement TYPE filter at the moment @Test public void test310QueryWithTypeFilter() throws SchemaException { when("query includes type filter"); @@ -499,8 +504,8 @@ public void test310QueryWithTypeFilter() throws SchemaException { .type(FocusType.class) .block() .id(user1Oid, task1Oid, org2Oid) // task will not match, it's not a focus -// .and() TODO I want this -// .item(FocusType.F_COST_CENTER).eq("5") + .and() + .item(FocusType.F_COST_CENTER).eq("5") .endBlock() .build(), operationResult); @@ -513,8 +518,73 @@ public void test310QueryWithTypeFilter() throws SchemaException { } */ - // TODO TYPE tests - // TODO EXISTS tests +/* TODO EXISTS tests +1. @Count property => pendingOperationCount > 0; see: ClassDefinitionParser#parseMethod() + getJaxbName() +EXISTS(pendingOperation, null) + +2. multi-value container stored in table: +EXISTS(operationExecution, AND(REF: taskRef, PRV(oid=task-oid-2, targetType=null); EQUAL: status, PPV(OperationResultStatusType:SUCCESS))) + +3. cointainer query (AccessCertificationWorkItemType) with EXISTS to the parent container (AccessCertificationCaseType) + matching on parent's ownerID (OID of AccessCertificationCampaignType) + its own CID (AccessCertificationCaseType) +EXISTS({http://prism.evolveum.com/xml/ns/public/types-3}parent, AND(IN OID (for owner): e8c07a7a-1b11-11e8-9b32-1715a2e8273b, IN OID: 1)) + +parent vs ..? + +4. part of AND in container query, used on sub-container (workItem) with nested AND condition +CertificationTest.test730CurrentUnansweredCases >>> Q{ +AND( + EQUAL: stageNumber, {http://prism.evolveum.com/xml/ns/public/types-3}parent/stageNumber; + EQUAL: ../state, PPV(AccessCertificationCampaignStateType:IN_REVIEW_STAGE); + EXISTS(workItem, + AND( + EQUAL: closeTimestamp, ; + EQUAL: output/outcome, ))) +, null paging} + +5. EXISTS with .. (another way how to say parent) +EXISTS(.., + OR( + IN OID: c0c010c0-d34d-b33f-f00d-111111111111; , + REF: ownerRef,PRV(oid=c0c010c0-d34d-b33f-f00d-111111111111, targetType={.../common/common-3}UserType))) + +6. nothing new, nested in OR, EXISTS(workItem) with AND + +OR( + IN OID: af69e388-88bd-43f9-9259-73676124c196; , + EXISTS(workItem, + AND( + EQUAL: closeTimestamp,, + REF: assigneeRef, PRV(oid=af69e388-88bd-43f9-9259-73676124c196, targetType=null), PRV(oid=c0c010c0-d34d-b33f-f00d-111111111111, targetType=null)))) + +OR( + NONE, + LESS-OR-EQUAL: activation/validFrom,PPV(XMLGregorianCalendarImpl:2021-05-21T15:44:41.955+02:00), + LESS-OR-EQUAL: activation/validTo,PPV(XMLGregorianCalendarImpl:2021-05-21T15:44:41.955+02:00), + EXISTS(assignment, + OR( + LESS-OR-EQUAL: activation/validFrom,PPV(XMLGregorianCalendarImpl:2021-05-21T15:44:41.955+02:00), + LESS-OR-EQUAL: activation/validTo,PPV(XMLGregorianCalendarImpl:2021-05-21T15:44:41.955+02:00)))) + +7. typical case EXISTS(assignment, ...) + +8. AND(2x EXISTS with multiple ref values) +main >>> Q{ +AND( + EXISTS(assignment, + REF: targetRef, + PRV(oid=4076afcc-4075-4f18-b596-edb71dbf72a9, targetType={.../common/common-3}OrgType, targetName=A-newOrg, relation={.../common/org-3}default), + PRV(oid=4076afcc-4075-4f18-b596-edb71dbf72a9, targetType={.../common/common-3}OrgType, targetName=A-newOrg, relation={.../common/org-3}manager), + PRV(oid=4076afcc-4075-4f18-b596-edb71dbf72a9, targetType={.../common/common-3}OrgType, targetName=A-newOrg, relation={.../common/org-3}approver), + PRV(oid=4076afcc-4075-4f18-b596-edb71dbf72a9, targetType={.../common/common-3}OrgType, targetName=A-newOrg, relation={.../common/org-3}owner)); + EXISTS(assignment, + REF: targetRef, + PRV(oid=4076afcc-4075-4f18-b596-edb71dbf72a9, targetType={.../common/common-3}OrgType, targetName=A-newOrg, relation={.../common/org-3}default), + PRV(oid=4076afcc-4075-4f18-b596-edb71dbf72a9, targetType={.../common/common-3}OrgType, targetName=A-newOrg, relation={.../common/org-3}manager), + PRV(oid=4076afcc-4075-4f18-b596-edb71dbf72a9, targetType={.../common/common-3}OrgType, targetName=A-newOrg, relation={.../common/org-3}approver), + PRV(oid=4076afcc-4075-4f18-b596-edb71dbf72a9, targetType={.../common/common-3}OrgType, targetName=A-newOrg, relation={.../common/org-3}owner))) +, null paging} +*/ // endregion // region special cases @@ -531,6 +601,59 @@ public void test900SearchByWholeContainerIsNotPossible() { // even if query was possible this would fail in the actual repo search, which is expected } + @Test + public void test920SearchObjectTypeFindsAllObjects() throws SchemaException { + OperationResult operationResult = createOperationResult(); + + given("query without any filter"); + ObjectQuery query = prismContext.queryFor(ObjectType.class).build(); + + when("search is called with ObjectType"); + SearchResultList result = + searchObjects(ObjectType.class, query, operationResult); + + then("all repository objects are returned"); + assertThat(result).hasSize((int) count(QObject.CLASS)); + } + + @Test + public void test921SearchAssignmentHolderTypeFindsAllObjectsExceptShadows() + throws SchemaException { + OperationResult operationResult = createOperationResult(); + + given("query without any filter"); + ObjectQuery query = prismContext.queryFor(ObjectType.class).build(); + + when("search is called with AssignmentHolderType"); + SearchResultList result = + searchObjects(AssignmentHolderType.class, query, operationResult); + + then("all repository objects except shadows are returned"); + QObject o = aliasFor(QObject.CLASS); + assertThat(result).hasSize((int) count(o, o.objectType.ne(MObjectType.SHADOW))); + assertThat(result).hasSize((int) count(QAssignmentHolder.CLASS)); + // without additional objects the test would be meaningless + assertThat(result).hasSizeLessThan((int) count(o)); + } + + @Test + public void test921SearchFocusTypeFindsOnlyFocusObjects() + throws SchemaException { + OperationResult operationResult = createOperationResult(); + + given("query without any filter"); + ObjectQuery query = prismContext.queryFor(ObjectType.class).build(); + + when("search is called with FocusType"); + SearchResultList result = + searchObjects(FocusType.class, query, operationResult); + + then("only focus objects from repository are returned"); + assertThat(result).hasSize((int) count(QFocus.CLASS)); + // without additional objects the test would be meaningless + assertThat(result).hasSizeLessThan((int) count(QObject.CLASS)); + } + @Test public void test950SearchOperationUpdatesPerformanceMonitor() throws SchemaException { OperationResult operationResult = createOperationResult(); @@ -549,9 +672,35 @@ public void test950SearchOperationUpdatesPerformanceMonitor() throws SchemaExcep assertThatOperationResult(operationResult).isSuccess(); assertSingleOperationRecorded(pm, RepositoryService.OP_SEARCH_OBJECTS); } + + @Test(enabled = false) + public void test960SearchByAxiomQueryLanguage() throws SchemaException { + OperationResult operationResult = createOperationResult(); + SearchResultList focusTypes = searchObjects(FocusType.class, + ". type UserType and employeeNumber startsWith \"5\"", + operationResult); + System.out.println("focusTypes = " + focusTypes); + // even if query was possible this would fail in the actual repo search, which is expected + } // endregion // support methods + + /** Search objects using Axiom query language. */ + @SafeVarargs + @NotNull + private SearchResultList searchObjects( + @NotNull Class type, + String query, + OperationResult operationResult, + SelectorOptions... selectorOptions) + throws SchemaException { + ObjectFilter objectFilter = prismContext.createQueryParser().parseQuery(type, query); + ObjectQuery objectQuery = prismContext.queryFactory().createQuery(objectFilter); + return searchObjects(type, objectQuery, operationResult, selectorOptions); + } + + /** Search objects using {@link ObjectQuery}. */ @SafeVarargs @NotNull private SearchResultList searchObjects( From 5ee17418fee5bc47c29de2375b12f3dcc910cdae Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 26 May 2021 21:55:44 +0200 Subject: [PATCH 37/43] ValueFilterValues: rename of to S is used as "schema type" elsewhere, it could have been confusing. --- .../sqlbase/filtering/ValueFilterValues.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/ValueFilterValues.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/ValueFilterValues.java index c3afe7f2e4c..755dc161d1c 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/ValueFilterValues.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/ValueFilterValues.java @@ -31,30 +31,30 @@ * from {@link PrismPropertyValue} to "real value" and then to convert it. * Both {@link #singleValue()} and {@link #allValues()} are handled the same way. * - * If {@link #conversionFunction} is used any {@link IllegalArgumentException} will be rewrapped + * If {@link #conversionFunction} is used any {@link IllegalArgumentException} will be re-wrapped * as {@link QueryException}, other runtime exceptions are not intercepted. * * @param type of filter value - * @param type of value after conversion (can by the same like T) + * @param type of value after conversion (can by the same like T) */ -public class ValueFilterValues { +public class ValueFilterValues { @NotNull private final PropertyValueFilter filter; - @Nullable private final Function conversionFunction; + @Nullable private final Function conversionFunction; public static ValueFilterValues from(@NotNull PropertyValueFilter filter) { return new ValueFilterValues<>(filter, null); } - public static ValueFilterValues from( + public static ValueFilterValues from( @NotNull PropertyValueFilter filter, - @Nullable Function conversionFunction) { + @Nullable Function conversionFunction) { return new ValueFilterValues<>(filter, conversionFunction); } private ValueFilterValues( @NotNull PropertyValueFilter filter, - @Nullable Function conversionFunction) { + @Nullable Function conversionFunction) { this.filter = Objects.requireNonNull(filter); this.conversionFunction = conversionFunction; } @@ -79,14 +79,15 @@ private ValueFilterValues( /** * Returns multiple values, all converted, or empty list - never null. */ - public @NotNull List allValues() { + public @NotNull List allValues() { if (filter.getValues() == null) { return Collections.emptyList(); } Stream realValueStream = filter.getValues().stream() .map(ppv -> ppv.getRealValue()); if (conversionFunction == null) { - return realValueStream.collect(Collectors.toList()); + //noinspection unchecked + return (List) realValueStream.collect(Collectors.toList()); } return realValueStream .map(conversionFunction) From 2ea93d2976232068198380a9359cdbbe021b7dcb Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 26 May 2021 23:29:13 +0200 Subject: [PATCH 38/43] Enum+UriItemFilterProcessor: both throw for other than EQ operation --- .../sqale/filtering/UriItemFilterProcessor.java | 13 +++++++++++++ .../filtering/item/EnumItemFilterProcessor.java | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/UriItemFilterProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/UriItemFilterProcessor.java index 2125d540458..9912c1d794d 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/UriItemFilterProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/UriItemFilterProcessor.java @@ -8,10 +8,13 @@ import java.util.function.Function; +import com.querydsl.core.types.Ops; import com.querydsl.core.types.Predicate; import com.querydsl.core.types.dsl.NumberPath; +import com.evolveum.midpoint.prism.query.EqualFilter; import com.evolveum.midpoint.prism.query.PropertyValueFilter; +import com.evolveum.midpoint.prism.query.ValueFilter; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqlbase.QueryException; import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; @@ -38,4 +41,14 @@ public Predicate process(PropertyValueFilter filter) throws QueryExcepti ValueFilterValues.from(filter, u -> ((SqaleRepoContext) context.repositoryContext()).searchCachedUriId(u))); } + + @Override + protected Ops operation(ValueFilter filter) throws QueryException { + if (filter instanceof EqualFilter && filter.getMatchingRule() == null) { + return Ops.EQ; + } else { + throw new QueryException("Can't translate filter '" + filter + "' to operation." + + " URI/QName value supports only equals with no matching rule."); + } + } } diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/item/EnumItemFilterProcessor.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/item/EnumItemFilterProcessor.java index 498c6e66382..f49af7926a1 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/item/EnumItemFilterProcessor.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/item/EnumItemFilterProcessor.java @@ -8,10 +8,13 @@ import java.util.function.Function; +import com.querydsl.core.types.Ops; import com.querydsl.core.types.Predicate; import com.querydsl.core.types.dsl.EnumPath; +import com.evolveum.midpoint.prism.query.EqualFilter; import com.evolveum.midpoint.prism.query.PropertyValueFilter; +import com.evolveum.midpoint.prism.query.ValueFilter; import com.evolveum.midpoint.repo.sqlbase.QueryException; import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; import com.evolveum.midpoint.repo.sqlbase.filtering.ValueFilterValues; @@ -39,4 +42,14 @@ public , R> EnumItemFilterProcessor( public Predicate process(PropertyValueFilter filter) throws QueryException { return createBinaryCondition(filter, path, ValueFilterValues.from(filter)); } + + @Override + protected Ops operation(ValueFilter filter) throws QueryException { + if (filter instanceof EqualFilter && filter.getMatchingRule() == null) { + return Ops.EQ; + } else { + throw new QueryException("Can't translate filter '" + filter + "' to operation." + + " Enumeration value supports only equals with no matching rule."); + } + } } From 84fbfd6c7b93bf62e28368c5059d734e5f8e3e53 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 26 May 2021 23:32:41 +0200 Subject: [PATCH 39/43] added ArrayPathItemFilterProcessor for paths like subtypes + tests Support for subtypes (String[]) and policySituations (Integer[]). Added tests for enum value equality. --- .../ArrayPathItemFilterProcessor.java | 69 +++++ .../repo/sqale/qmodel/SqaleTableMapping.java | 30 +- .../qmodel/connector/QConnectorMapping.java | 2 +- .../sqale/qmodel/object/QObjectMapping.java | 6 +- .../sqale/func/SqaleRepoSearchObjectTest.java | 259 +++++++++++++++++- .../filtering/item/ItemFilterProcessor.java | 2 - .../sqlbase/mapping/QueryTableMapping.java | 2 +- 7 files changed, 353 insertions(+), 17 deletions(-) create mode 100644 repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/ArrayPathItemFilterProcessor.java diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/ArrayPathItemFilterProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/ArrayPathItemFilterProcessor.java new file mode 100644 index 00000000000..f24026e0c02 --- /dev/null +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/ArrayPathItemFilterProcessor.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2010-2021 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.repo.sqale.filtering; + +import java.lang.reflect.Array; +import java.util.function.Function; + +import com.querydsl.core.types.Predicate; +import com.querydsl.core.types.dsl.ArrayPath; +import com.querydsl.core.types.dsl.Expressions; +import org.jetbrains.annotations.Nullable; + +import com.evolveum.midpoint.prism.query.EqualFilter; +import com.evolveum.midpoint.prism.query.PropertyValueFilter; +import com.evolveum.midpoint.repo.sqlbase.QueryException; +import com.evolveum.midpoint.repo.sqlbase.RepositoryException; +import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext; +import com.evolveum.midpoint.repo.sqlbase.filtering.ValueFilterValues; +import com.evolveum.midpoint.repo.sqlbase.filtering.item.SinglePathItemFilterProcessor; +import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; + +/** + * Filter processor for multi-value property represented by single array column. + * These paths support only value equality (of any value), which is "contains" in DB terminology. + * Our filter "contains" (meaning substring) is *not* supported. + */ +public class ArrayPathItemFilterProcessor + extends SinglePathItemFilterProcessor> { + + private final String dbType; + private final Class elementType; + @Nullable private final Function conversionFunction; + + /** + * Creates filter processor for array column. + * + * @param dbType name of the type for element in DB (without []) for the cast part of the condition + */ + public , R> ArrayPathItemFilterProcessor( + SqlQueryContext context, + Function> rootToPath, + String dbType, + Class elementType, + @Nullable Function conversionFunction) { + super(context, rootToPath); + this.dbType = dbType; + this.elementType = elementType; + this.conversionFunction = conversionFunction; + } + + @Override + public Predicate process(PropertyValueFilter filter) throws RepositoryException { + if (!(filter instanceof EqualFilter) || filter.getMatchingRule() != null) { + throw new QueryException("Can't translate filter '" + filter + "' to operation." + + " Array stored value supports only equals with no matching rule."); + } + + ValueFilterValues values = ValueFilterValues.from(filter, conversionFunction); + // valueArray can't be just Object[], it must be concrete type, e.g. String[], + // otherwise PG JDBC driver will complain. + //noinspection unchecked + E[] valueArray = values.allValues().toArray(i -> (E[]) Array.newInstance(elementType, i)); + return Expressions.booleanTemplate("{0} && {1}::" + dbType + "[]", path, valueArray); + } +} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java index d5d836e7fb0..8100e20a876 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java @@ -21,6 +21,7 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.delta.item.*; +import com.evolveum.midpoint.repo.sqale.filtering.ArrayPathItemFilterProcessor; import com.evolveum.midpoint.repo.sqale.filtering.RefItemFilterProcessor; import com.evolveum.midpoint.repo.sqale.filtering.UriItemFilterProcessor; import com.evolveum.midpoint.repo.sqale.mapping.SqaleItemSqlMapper; @@ -210,6 +211,33 @@ public > SqaleItemSqlMapper enumMapper( rootToQueryItem); } + /** + * Returns the mapper creating string multi-value filter/delta processors from context. + * + * @param mapped schema type, see javadoc in {@link QueryTableMapping} + */ + protected SqaleItemSqlMapper multiStringMapper( + Function> rootToQueryItem) { + return new SqaleItemSqlMapper<>( + ctx -> new ArrayPathItemFilterProcessor( + ctx, rootToQueryItem, "TEXT", String.class, null), + ctx -> null); // TODO + } + + /** + * Returns the mapper creating integer multi-value filter/delta processors from context. + * + * @param mapped schema type, see javadoc in {@link QueryTableMapping} + */ + protected SqaleItemSqlMapper multiUriMapper( + Function> rootToQueryItem) { + return new SqaleItemSqlMapper<>( + ctx -> new ArrayPathItemFilterProcessor<>( + ctx, rootToQueryItem, "INTEGER", Integer.class, + ((SqaleRepoContext) ctx.repositoryContext())::searchCachedUriId), + ctx -> null); // TODO + } + @Override public S toSchemaObject(R row) { throw new UnsupportedOperationException("Use toSchemaObject(Tuple,...)"); @@ -339,7 +367,7 @@ protected , OR } } - protected String[] arrayFor(List strings) { + protected String[] listToArray(List strings) { if (strings == null || strings.isEmpty()) { return null; } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorMapping.java index 99c76974d9a..107ca0780cb 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/connector/QConnectorMapping.java @@ -68,7 +68,7 @@ public MConnector newRowObject() { t -> row.connectorHostRefTargetType = t, r -> row.connectorHostRefRelationId = r); - row.targetSystemTypes = arrayFor(schemaObject.getTargetSystemType()); + row.targetSystemTypes = listToArray(schemaObject.getTargetSystemType()); return row; } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java index 970a7ee9234..ae84724a91e 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/object/QObjectMapping.java @@ -76,7 +76,9 @@ protected QObjectMapping( addItemMapping(F_LIFECYCLE_STATE, stringMapper(q -> q.lifecycleState)); // version/cidSeq is not mapped for queries or deltas, it's managed by repo explicitly - // TODO mapper for policySituations and subtypes + addItemMapping(F_POLICY_SITUATION, multiUriMapper(q -> q.policySituations)); + addItemMapping(F_SUBTYPE, multiStringMapper(q -> q.subtypes)); + // full-text is not item mapping, but filter on the whole object // TODO ext mapping can't be done statically addNestedMapping(F_METADATA, MetadataType.class) @@ -191,7 +193,7 @@ public R toRowObjectWithoutFullObject(S schemaObject, JdbcSession jdbcSession) { // complex DB fields row.policySituations = processCacheableUris(schemaObject.getPolicySituation()); - row.subtypes = arrayFor(schemaObject.getSubtype()); + row.subtypes = listToArray(schemaObject.getSubtype()); // TODO textInfo (fulltext support) // repo.getTextInfoItems().addAll(RObjectTextInfo.createItemsSet(jaxb, repo, repositoryContext)); // TODO extensions stored inline (JSON) - that is ext column diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java index 6e343367d35..1d624d962fb 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSearchObjectTest.java @@ -17,7 +17,6 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.prism.query.ObjectQuery; @@ -37,6 +36,7 @@ import com.evolveum.midpoint.schema.result.OperationResultStatus; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.prism.xml.ns._public.query_3.QueryType; @@ -57,6 +57,7 @@ public class SqaleRepoSearchObjectTest extends SqaleRepoBaseTest { private String user3Oid; // another user in org private String user4Oid; // another user in org private String task1Oid; // task has more attribute type variability + private String task2Oid; // task has more attribute type variability private String shadow1Oid; // ditto private String service1Oid; // object with integer attribute private String case1Oid; // Closed case, two work items @@ -84,11 +85,13 @@ public void initObjects() throws Exception { org111Oid = repositoryService.addObject( new OrgType(prismContext).name("org-1-1-1") .parentOrgRef(org11Oid, OrgType.COMPLEX_TYPE, relation2) + .subtype("newWorkers") .asPrismObject(), null, result); org112Oid = repositoryService.addObject( new OrgType(prismContext).name("org-1-1-2") .parentOrgRef(org11Oid, OrgType.COMPLEX_TYPE, relation1) + .subtype("secret") .asPrismObject(), null, result); org12Oid = repositoryService.addObject( @@ -103,6 +106,7 @@ public void initObjects() throws Exception { new OrgType(prismContext).name("org-2-1") .costCenter("5") .parentOrgRef(org2Oid, OrgType.COMPLEX_TYPE) + .policySituation("situationC") .asPrismObject(), null, result); orgXOid = repositoryService.addObject( @@ -122,12 +126,17 @@ public void initObjects() throws Exception { .modifierRef(modifierOid, UserType.COMPLEX_TYPE, relation2) .modifyChannel("modify-channel") .modifyTimestamp(MiscUtil.asXMLGregorianCalendar(2L))) + .subtype("workerA") + .subtype("workerC") + .policySituation("situationA") + .policySituation("situationC") .asPrismObject(), null, result); user2Oid = repositoryService.addObject( new UserType(prismContext).name("user-2") .parentOrgRef(orgXOid, OrgType.COMPLEX_TYPE) .parentOrgRef(org11Oid, OrgType.COMPLEX_TYPE, relation1) + .subtype("workerA") .asPrismObject(), null, result); user3Oid = repositoryService.addObject( @@ -135,16 +144,26 @@ public void initObjects() throws Exception { .costCenter("50") .parentOrgRef(orgXOid, OrgType.COMPLEX_TYPE) .parentOrgRef(org21Oid, OrgType.COMPLEX_TYPE, relation1) + .policySituation("situationA") .asPrismObject(), null, result); user4Oid = repositoryService.addObject( new UserType(prismContext).name("user-4") .costCenter("51") .parentOrgRef(org111Oid, OrgType.COMPLEX_TYPE) + .subtype("workerB") + .policySituation("situationB") .asPrismObject(), null, result); task1Oid = repositoryService.addObject( - new TaskType(prismContext).name("task-1").asPrismObject(), + new TaskType(prismContext).name("task-1") + .executionStatus(TaskExecutionStateType.RUNNABLE) + .asPrismObject(), + null, result); + task2Oid = repositoryService.addObject( + new TaskType(prismContext).name("task-2") + .executionStatus(TaskExecutionStateType.CLOSED) + .asPrismObject(), null, result); shadow1Oid = repositoryService.addObject( new ShadowType(prismContext).name("shadow-1").asPrismObject(), @@ -218,18 +237,238 @@ public void test110SearchUserByName() throws Exception { } @Test - public void test180SearchCaseWorkitemByOutcome() throws Exception { - searchCaseWorkitemByOutcome("OUTCOME one", case1Oid); - searchCaseWorkitemByOutcome("OUTCOME two", case1Oid); - searchCaseWorkitemByOutcome("OUTCOME nonexist", null); + public void test120SearchObjectsBySubtype() throws Exception { + when("searching objects with subtype equal to value"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(ObjectType.class, + prismContext.queryFor(ObjectType.class) + .item(ObjectType.F_SUBTYPE).eq("workerA") + .build(), + operationResult); + + then("only objects having the specified subtype are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result) + .hasSize(2) + .extracting(row -> row.getOid()) + .containsExactlyInAnyOrder(user1Oid, user2Oid); + } + + @Test + public void test121SearchObjectsBySubtypeWithMultipleValues() throws Exception { + when("searching objects with any subtype equal to any of the provided values"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(ObjectType.class, + prismContext.queryFor(ObjectType.class) + .item(ObjectType.F_SUBTYPE).eq("workerA", "workerB") + .build(), + operationResult); + + then("objects with any of the subtypes are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result) + .hasSize(3) + .extracting(row -> row.getOid()) + .containsExactlyInAnyOrder(user1Oid, user2Oid, user4Oid); + } + + @Test + public void test122SearchObjectsHavingTwoSubtypeValuesUsingAnd() throws Exception { + when("searching objects with multiple subtype values equal to provided values"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(ObjectType.class, + prismContext.queryFor(ObjectType.class) + .item(ObjectType.F_SUBTYPE).eq("workerA") + .and() + .item(ObjectType.F_SUBTYPE).eq("workerC") + .build(), + operationResult); + + then("only objects with all specified subtypes are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result) + .hasSize(1) + .extracting(row -> row.getOid()) + .containsExactlyInAnyOrder(user1Oid); + } + + @Test + public void test123SearchOrgsHavingTwoSubtypeValuesUsingAndButNoneMatches() throws Exception { + when("searching objects with multiple subtype values equal to provided values"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(OrgType.class, + prismContext.queryFor(OrgType.class) + .item(ObjectType.F_SUBTYPE).eq("workerA") + .and() + .item(ObjectType.F_SUBTYPE).eq("workerB") + .build(), + operationResult); + + then("nothing is returned because no org matches the condition"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result).isEmpty(); + } + + @Test + public void test125SearchObjectsBySubtypeContainsIsNotSupported() { + given("query for subtype containing (=substring) value"); + OperationResult operationResult = createOperationResult(); + ObjectQuery query = prismContext.queryFor(UserType.class) + .item(ObjectType.F_SUBTYPE).contains("worker") + .build(); + + expect("repository throws exception because it is not supported"); + assertThatThrownBy(() -> searchObjects(ObjectType.class, query, operationResult)) + .isInstanceOf(SystemException.class) + .hasMessageStartingWith("Can't translate filter"); + } + + @Test + public void test130SearchObjectsByPolicySituation() throws Exception { + when("searching objects with policy situation equal to value"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(ObjectType.class, + prismContext.queryFor(UserType.class) + .item(ObjectType.F_POLICY_SITUATION).eq("situationC") + .build(), + operationResult); + + then("only objects having the specified policy situation are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result) + .hasSize(2) + .extracting(row -> row.getOid()) + .containsExactlyInAnyOrder(user1Oid, org21Oid); + } + + @Test + public void test131SearchOrgsByPolicySituation() throws Exception { + when("searching orgs with policy situation equal to value"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(OrgType.class, + prismContext.queryFor(OrgType.class) + .item(ObjectType.F_POLICY_SITUATION).eq("situationC") + .build(), + operationResult); + + then("only orgs having the specified policy situation are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result) + .hasSize(1) + .extracting(row -> row.getOid()) + .containsExactlyInAnyOrder(org21Oid); + } + + @Test + public void test132SearchObjectsByPolicySituationWithMultipleValues() throws Exception { + when("searching objects with any policy situation equal to any of the provided values"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(ObjectType.class, + prismContext.queryFor(UserType.class) + .item(ObjectType.F_POLICY_SITUATION).eq("situationA", "situationB") + .build(), + operationResult); + + then("objects with any of the policy situations are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result) + .hasSize(3) + .extracting(row -> row.getOid()) + .containsExactlyInAnyOrder(user1Oid, user3Oid, user4Oid); + } + + @Test + public void test135SearchObjectsByPolicySituationContainsIsNotSupported() { + given("query for policy situation containing (=substring) value"); + OperationResult operationResult = createOperationResult(); + ObjectQuery query = prismContext.queryFor(UserType.class) + .item(ObjectType.F_POLICY_SITUATION).contains("worker") + .build(); + + expect("repository throws exception because it is not supported"); + assertThatThrownBy(() -> searchObjects(ObjectType.class, query, operationResult)) + .isInstanceOf(SystemException.class) + .hasMessageStartingWith("Can't translate filter"); + } + + @Test + public void test140SearchTaskByEnumValue() throws Exception { + when("searching task with execution status equal to one value"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(TaskType.class, + prismContext.queryFor(TaskType.class) + .item(TaskType.F_EXECUTION_STATUS).eq(TaskExecutionStateType.RUNNABLE) + .build(), + operationResult); + + then("tasks with the execution status with the provided value are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result) + .hasSize(1) + .extracting(row -> row.getOid()) + .containsExactlyInAnyOrder(task1Oid); + } + + @Test + public void test141SearchTaskByEnumWithMultipleValues() throws Exception { + when("searching task with execution status equal to any of provided value"); + OperationResult operationResult = createOperationResult(); + SearchResultList result = searchObjects(TaskType.class, + prismContext.queryFor(TaskType.class) + .item(TaskType.F_EXECUTION_STATUS).eq( + TaskExecutionStateType.RUNNABLE, TaskExecutionStateType.CLOSED) + .build(), + operationResult); + + then("tasks with execution status equal to any of the provided values are returned"); + assertThatOperationResult(operationResult).isSuccess(); + assertThat(result) + .hasSize(2) + .extracting(row -> row.getOid()) + .containsExactlyInAnyOrder(task1Oid, task2Oid); + } + + @Test + public void test145SearchObjectsByEnumValueContainsIsNotSupported() { + given("query for task's execution status containing (=substring) value"); + OperationResult operationResult = createOperationResult(); + ObjectQuery query = prismContext.queryFor(TaskType.class) + .item(TaskType.F_EXECUTION_STATUS).contains(TaskExecutionStateType.RUNNABLE) + .build(); + + expect("repository throws exception because it is not supported"); + assertThatThrownBy(() -> searchObjects(TaskType.class, query, operationResult)) + .isInstanceOf(SystemException.class) + .hasMessageStartingWith("Can't translate filter"); + } + + @Test + public void test146SearchTaskByEnumValueProvidedAsStringIsNotSupported() { + given("query for enum equality using string value"); + OperationResult operationResult = createOperationResult(); + ObjectQuery query = prismContext.queryFor(TaskType.class) + .item(TaskType.F_EXECUTION_STATUS).eq("RUNNABLE") + .build(); + + expect("repository throws exception because it is not supported, enum must be used"); + assertThatThrownBy(() -> searchObjects(TaskType.class, query, operationResult)) + .isInstanceOf(SystemException.class); + } + + @Test + public void test180SearchCaseWorkItemByOutcome() throws Exception { + searchCaseWorkItemByOutcome("OUTCOME one", case1Oid); + searchCaseWorkItemByOutcome("OUTCOME two", case1Oid); + searchCaseWorkItemByOutcome("OUTCOME nonexist", null); } - private void searchCaseWorkitemByOutcome(String wiOutcome, String expectedOid) throws Exception { + private void searchCaseWorkItemByOutcome(String wiOutcome, String expectedOid) throws Exception { when("searching case with query for workitem/output/outcome " + wiOutcome); OperationResult operationResult = createOperationResult(); SearchResultList result = searchObjects(CaseType.class, prismContext.queryFor(CaseType.class) - .item(ItemPath.create(CaseType.F_WORK_ITEM, CaseWorkItemType.F_OUTPUT, AbstractWorkItemOutputType.F_OUTCOME)).eq(wiOutcome) + .item(CaseType.F_WORK_ITEM, CaseWorkItemType.F_OUTPUT, + AbstractWorkItemOutputType.F_OUTCOME).eq(wiOutcome) .build(), operationResult); @@ -637,7 +876,7 @@ public void test921SearchAssignmentHolderTypeFindsAllObjectsExceptShadows() } @Test - public void test921SearchFocusTypeFindsOnlyFocusObjects() + public void test922SearchFocusTypeFindsOnlyFocusObjects() throws SchemaException { OperationResult operationResult = createOperationResult(); @@ -651,7 +890,7 @@ public void test921SearchFocusTypeFindsOnlyFocusObjects() then("only focus objects from repository are returned"); assertThat(result).hasSize((int) count(QFocus.CLASS)); // without additional objects the test would be meaningless - assertThat(result).hasSizeLessThan((int) count(QObject.CLASS)); + assertThat(result).hasSizeLessThan((int) count(QAssignmentHolder.CLASS)); } @Test diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/item/ItemFilterProcessor.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/item/ItemFilterProcessor.java index 4aa949778b0..01499af3635 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/item/ItemFilterProcessor.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/filtering/item/ItemFilterProcessor.java @@ -89,8 +89,6 @@ protected Predicate createBinaryCondition( } } if (values.isMultiValue()) { - // TODO do we want Ops.EQ_IGNORE_CASE too? For one value, Querydsl takes care of - // ignore-case. For IN, we would have to do it ourselves. if (operator == Ops.EQ) { return ExpressionUtils.predicate(Ops.IN, path, ConstantImpl.create(values.allValues())); diff --git a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/QueryTableMapping.java b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/QueryTableMapping.java index a8d69609b3f..4760f1a64b1 100644 --- a/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/QueryTableMapping.java +++ b/repo/repo-sqlbase/src/main/java/com/evolveum/midpoint/repo/sqlbase/mapping/QueryTableMapping.java @@ -57,7 +57,7 @@ * to the same schema type because of the nested mappings. * E.g. attribute `name` is part of the `S` object, but `metadata/createChannel` is based on nested * mapping for `metadata` for which `S` is `MetadataType`. - * That's why these methods have flexible schema type parameter. + * That's why these methods have flexible schema type parameter, typically designated `MS`. * Because such nested mapping still uses the same table, types `Q` and `R` remain the same. * * Mapping for tables is initialized once and requires {@link SqlRepoContext}. From 3b92c9fc0c758bedf750bcb6fefbbcc2848ca74f Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 26 May 2021 23:33:23 +0200 Subject: [PATCH 40/43] repo-sqale: SQL schema, added indexes using gin(subtypes) --- repo/repo-sqale/sql/pgnew-experiments.sql | 9 ++- repo/repo-sqale/sql/pgnew-repo.sql | 76 +++++++++++++++++++---- 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/repo/repo-sqale/sql/pgnew-experiments.sql b/repo/repo-sqale/sql/pgnew-experiments.sql index 35a2de16a03..f61e44a0258 100644 --- a/repo/repo-sqale/sql/pgnew-experiments.sql +++ b/repo/repo-sqale/sql/pgnew-experiments.sql @@ -86,7 +86,7 @@ SELECT 'resource-' || LPAD(r::text, 10, '0'), 1 from generate_series(1, 10) as r; -INSERT INTO m_user (nameNorm, nameOrig, fullobject, ext, policySituations, version) +INSERT INTO m_user (nameNorm, nameOrig, fullobject, ext, policySituations, subtypes, version) SELECT 'user-' || LPAD(r::text, 10, '0'), 'user-' || LPAD(r::text, 10, '0'), random_bytea(100, 2000), @@ -114,8 +114,13 @@ SELECT 'user-' || LPAD(r::text, 10, '0'), random_pick(ARRAY(SELECT a.n FROM generate_series(1, 100) AS a(n)), r % 10 / 100::decimal) -- ELSE NULL is default and redundant END, + CASE + WHEN r % 10 < random() * 2 THEN + random_pick(ARRAY['eating', 'books', 'music', 'dancing', 'walking', 'jokes', 'video', 'photo', 'writing', 'gaming'], 0.3) + -- ELSE NULL is default and redundant + END, 1 -from generate_series(100001,1000000) as r; +from generate_series(1, 50000) as r; EXPLAIN (ANALYZE, VERBOSE, BUFFERS) select oid, policysituations from m_user diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index b218c6c8faf..74ff58d2b6f 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -21,6 +21,7 @@ -- drop schema public cascade; CREATE SCHEMA IF NOT EXISTS public; CREATE EXTENSION IF NOT EXISTS intarray; -- support for indexing INTEGER[] columns +--CREATE EXTENSION IF NOT EXISTS pg_trgm; -- support for trigram indexes TODO for ext with LIKE and fulltext -- region custom enum types -- Some enums are from schema, some are only defined in repo-sqale. @@ -241,9 +242,9 @@ CREATE TABLE m_object ( version INTEGER NOT NULL DEFAULT 1, -- complex DB columns, add indexes as needed per concrete table, e.g. see m_user -- TODO compare with [] in JSONB, check performance, indexing, etc. first - policySituations INTEGER[], -- soft-references m_uri, add index per table as/if needed - subtypes TEXT[], - textInfo TEXT[], -- TODO not mapped yet, see RObjectTextInfo#createItemsSet + policySituations INTEGER[], -- soft-references m_uri, only EQ filter + subtypes TEXT[], -- only EQ filter + textInfo TEXT[], -- TODO not mapped yet, see RObjectTextInfo#createItemsSet, this may not be [] ext JSONB, -- metadata creatorRefTargetOid UUID, @@ -460,6 +461,7 @@ CREATE TRIGGER m_generic_object_oid_delete_tr AFTER DELETE ON m_generic_object -- No indexes for GenericObjectType#objectType were in old repo, what queries are expected? CREATE INDEX m_generic_object_nameOrig_idx ON m_generic_object (nameOrig); ALTER TABLE m_generic_object ADD CONSTRAINT m_generic_object_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_generic_object_subtypes_idx ON m_generic_object USING gin(subtypes); -- endregion -- region USER related tables @@ -503,6 +505,7 @@ CREATE INDEX m_user_fullNameOrig_idx ON m_user (fullNameOrig); CREATE INDEX m_user_familyNameOrig_idx ON m_user (familyNameOrig); CREATE INDEX m_user_givenNameOrig_idx ON m_user (givenNameOrig); CREATE INDEX m_user_employeeNumber_idx ON m_user (employeeNumber); +CREATE INDEX m_user_subtypes_idx ON m_user USING gin(subtypes); /* TODO JSON of polystrings? CREATE TABLE m_user_organization ( @@ -558,6 +561,7 @@ CREATE TRIGGER m_role_oid_delete_tr AFTER DELETE ON m_role CREATE INDEX m_role_nameOrig_idx ON m_role (nameOrig); ALTER TABLE m_role ADD CONSTRAINT m_role_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_role_subtypes_idx ON m_role USING gin(subtypes); -- Represents ServiceType, see https://wiki.evolveum.com/display/midPoint/Service+Account+Management CREATE TABLE m_service ( @@ -595,6 +599,7 @@ CREATE TRIGGER m_archetype_oid_delete_tr AFTER DELETE ON m_archetype CREATE INDEX m_archetype_nameOrig_idx ON m_archetype (nameOrig); ALTER TABLE m_archetype ADD CONSTRAINT m_archetype_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_archetype_subtypes_idx ON m_archetype USING gin(subtypes); -- endregion -- region Organization hierarchy support @@ -618,6 +623,7 @@ CREATE TRIGGER m_org_oid_delete_tr AFTER DELETE ON m_org CREATE INDEX m_org_nameOrig_idx ON m_org (nameOrig); ALTER TABLE m_org ADD CONSTRAINT m_org_nameNorm_key UNIQUE (nameNorm); CREATE INDEX m_org_displayOrder_idx ON m_org (displayOrder); +CREATE INDEX m_org_subtypes_idx ON m_org USING gin(subtypes); -- stores ObjectType/parentOrgRef CREATE TABLE m_ref_object_parent_org ( @@ -749,6 +755,7 @@ CREATE TRIGGER m_resource_oid_delete_tr AFTER DELETE ON m_resource CREATE INDEX m_resource_nameOrig_idx ON m_resource (nameOrig); ALTER TABLE m_resource ADD CONSTRAINT m_resource_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_resource_subtypes_idx ON m_resource USING gin(subtypes); -- stores ResourceType/business/approverRef CREATE TABLE m_ref_resource_business_configuration_approver ( @@ -798,6 +805,7 @@ CREATE TRIGGER m_shadow_oid_delete_tr AFTER DELETE ON m_shadow CREATE INDEX m_shadow_nameOrig_idx ON m_shadow (nameOrig); ALTER TABLE m_shadow ADD CONSTRAINT m_shadow_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_shadow_subtypes_idx ON m_shadow USING gin(subtypes); CREATE INDEX m_shadow_policySituation_idx ON m_shadow USING GIN(policysituations gin__int_ops); CREATE INDEX m_shadow_ext_idx ON m_shadow USING gin (ext); /* @@ -833,7 +841,7 @@ CREATE TRIGGER m_node_oid_delete_tr AFTER DELETE ON m_node CREATE INDEX m_node_nameOrig_idx ON m_node (nameOrig); ALTER TABLE m_node ADD CONSTRAINT m_node_nameNorm_key UNIQUE (nameNorm); --- not interested in ext index for this one, this table will be small +-- not interested in other indexes for this one, this table will be small -- Represents SystemConfigurationType, see https://wiki.evolveum.com/display/midPoint/System+Configuration+Object CREATE TABLE m_system_configuration ( @@ -850,6 +858,8 @@ CREATE TRIGGER m_system_configuration_update_tr BEFORE UPDATE ON m_system_config CREATE TRIGGER m_system_configuration_oid_delete_tr AFTER DELETE ON m_system_configuration FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); +ALTER TABLE m_system_configuration + ADD CONSTRAINT m_system_configuration_nameNorm_key UNIQUE (nameNorm); -- no need for the name index, m_system_configuration table is very small -- Represents SecurityPolicyType, see https://wiki.evolveum.com/display/midPoint/Security+Policy+Configuration @@ -867,7 +877,11 @@ CREATE TRIGGER m_security_policy_update_tr BEFORE UPDATE ON m_security_policy CREATE TRIGGER m_security_policy_oid_delete_tr AFTER DELETE ON m_security_policy FOR EACH ROW EXECUTE PROCEDURE delete_object_oid(); --- no need for the name index, m_security_policy table is very small +CREATE INDEX m_security_policy_nameOrig_idx ON m_security_policy (nameOrig); +ALTER TABLE m_security_policy ADD CONSTRAINT m_security_policy_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_security_policy_subtypes_idx ON m_security_policy USING gin(subtypes); +CREATE INDEX m_security_policy_policySituation_idx + ON m_security_policy USING GIN(policysituations gin__int_ops); -- Represents ObjectCollectionType, see https://wiki.evolveum.com/display/midPoint/Object+Collections+and+Views+Configuration CREATE TABLE m_object_collection ( @@ -886,6 +900,9 @@ CREATE TRIGGER m_object_collection_oid_delete_tr AFTER DELETE ON m_object_collec CREATE INDEX m_object_collection_nameOrig_idx ON m_object_collection (nameOrig); ALTER TABLE m_object_collection ADD CONSTRAINT m_object_collection_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_object_collection_subtypes_idx ON m_object_collection USING gin(subtypes); +CREATE INDEX m_object_collection_policySituation_idx + ON m_object_collection USING GIN(policysituations gin__int_ops); -- Represents DashboardType, see https://wiki.evolveum.com/display/midPoint/Dashboard+configuration CREATE TABLE m_dashboard ( @@ -904,6 +921,9 @@ CREATE TRIGGER m_dashboard_oid_delete_tr AFTER DELETE ON m_dashboard CREATE INDEX m_dashboard_nameOrig_idx ON m_dashboard (nameOrig); ALTER TABLE m_dashboard ADD CONSTRAINT m_dashboard_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_dashboard_subtypes_idx ON m_dashboard USING gin(subtypes); +CREATE INDEX m_dashboard_policySituation_idx + ON m_dashboard USING GIN(policysituations gin__int_ops); -- Represents ValuePolicyType CREATE TABLE m_value_policy ( @@ -922,6 +942,9 @@ CREATE TRIGGER m_value_policy_oid_delete_tr AFTER DELETE ON m_value_policy CREATE INDEX m_value_policy_nameOrig_idx ON m_value_policy (nameOrig); ALTER TABLE m_value_policy ADD CONSTRAINT m_value_policy_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_value_policy_subtypes_idx ON m_value_policy USING gin(subtypes); +CREATE INDEX m_value_policy_policySituation_idx + ON m_value_policy USING GIN(policysituations gin__int_ops); -- Represents ReportType, see https://wiki.evolveum.com/display/midPoint/Report+Configuration CREATE TABLE m_report ( @@ -942,6 +965,8 @@ CREATE TRIGGER m_report_oid_delete_tr AFTER DELETE ON m_report CREATE INDEX m_report_nameOrig_idx ON m_report (nameOrig); ALTER TABLE m_report ADD CONSTRAINT m_report_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_report_subtypes_idx ON m_report USING gin(subtypes); +CREATE INDEX m_report_policySituation_idx ON m_report USING GIN(policysituations gin__int_ops); -- TODO old repo had index on parent (boolean), does it make sense? if so, which value is sparse? -- Represents ReportDataType, see also m_report above @@ -964,6 +989,9 @@ CREATE TRIGGER m_report_data_oid_delete_tr AFTER DELETE ON m_report_data CREATE INDEX m_report_data_nameOrig_idx ON m_report_data (nameOrig); ALTER TABLE m_report_data ADD CONSTRAINT m_report_data_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_report_data_subtypes_idx ON m_report_data USING gin(subtypes); +CREATE INDEX m_report_data_policySituation_idx + ON m_report_data USING GIN(policysituations gin__int_ops); -- Represents LookupTableType, see https://wiki.evolveum.com/display/midPoint/Lookup+Tables CREATE TABLE m_lookup_table ( @@ -982,6 +1010,9 @@ CREATE TRIGGER m_lookup_table_oid_delete_tr AFTER DELETE ON m_lookup_table CREATE INDEX m_lookup_table_nameOrig_idx ON m_lookup_table (nameOrig); ALTER TABLE m_lookup_table ADD CONSTRAINT m_lookup_table_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_lookup_table_subtypes_idx ON m_lookup_table USING gin(subtypes); +CREATE INDEX m_lookup_table_policySituation_idx + ON m_lookup_table USING GIN(policysituations gin__int_ops); -- Represents LookupTableRowType, see also m_lookup_table above CREATE TABLE m_lookup_table_row ( @@ -1026,14 +1057,9 @@ CREATE TRIGGER m_connector_oid_delete_tr AFTER DELETE ON m_connector CREATE INDEX m_connector_nameOrig_idx ON m_connector (nameOrig); ALTER TABLE m_connector ADD CONSTRAINT m_connector_nameNorm_key UNIQUE (nameNorm); - --- TODO array/json in m_connector table --- CREATE TABLE m_connector_target_system ( --- connector_oid UUID NOT NULL, --- targetSystemType TEXT --- ); --- ALTER TABLE m_connector_target_system --- ADD CONSTRAINT fk_connector_target_system FOREIGN KEY (connector_oid) REFERENCES m_connector; +CREATE INDEX m_connector_subtypes_idx ON m_connector USING gin(subtypes); +CREATE INDEX m_connector_policySituation_idx + ON m_connector USING GIN(policysituations gin__int_ops); -- Represents ConnectorHostType, see https://wiki.evolveum.com/display/midPoint/Connector+Server CREATE TABLE m_connector_host ( @@ -1054,6 +1080,9 @@ CREATE TRIGGER m_connector_host_oid_delete_tr AFTER DELETE ON m_connector_host CREATE INDEX m_connector_host_nameOrig_idx ON m_connector_host (nameOrig); ALTER TABLE m_connector_host ADD CONSTRAINT m_connector_host_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_connector_host_subtypes_idx ON m_connector_host USING gin(subtypes); +CREATE INDEX m_connector_host_policySituation_idx + ON m_connector_host USING GIN(policysituations gin__int_ops); -- Represents persistent TaskType, see https://wiki.evolveum.com/display/midPoint/Task+Manager CREATE TABLE m_task ( @@ -1098,6 +1127,8 @@ CREATE INDEX m_task_parent_idx ON m_task (parent); CREATE INDEX m_task_objectRefTargetOid_idx ON m_task(objectRefTargetOid); ALTER TABLE m_task ADD CONSTRAINT m_task_taskIdentifier_key UNIQUE (taskIdentifier); CREATE INDEX m_task_dependentTaskIdentifiers_idx ON m_task USING GIN(dependentTaskIdentifiers); +CREATE INDEX m_task_subtypes_idx ON m_task USING gin(subtypes); +CREATE INDEX m_task_policySituation_idx ON m_task USING GIN(policysituations gin__int_ops); -- endregion -- region cases @@ -1132,6 +1163,8 @@ CREATE TRIGGER m_case_oid_delete_tr AFTER DELETE ON m_case CREATE INDEX m_case_nameOrig_idx ON m_case (nameOrig); ALTER TABLE m_case ADD CONSTRAINT m_case_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_case_subtypes_idx ON m_case USING gin(subtypes); +CREATE INDEX m_case_policySituation_idx ON m_case USING GIN(policysituations gin__int_ops); CREATE INDEX m_case_objectRefTargetOid_idx ON m_case(objectRefTargetOid); CREATE INDEX m_case_targetRefTargetOid_idx ON m_case(targetRefTargetOid); @@ -1154,10 +1187,12 @@ CREATE TABLE m_case_wi ( performerRefTargetType ObjectType, performerRefRelationId INTEGER REFERENCES m_uri(id), stageNumber INTEGER, + PRIMARY KEY (ownerOid, cid) ) INHERITS(m_container); +-- TODO INDEXES, old repo had no indexes either -- endregion -- region Access Certification object tables @@ -1185,6 +1220,9 @@ CREATE TRIGGER m_access_cert_definition_oid_delete_tr AFTER DELETE ON m_access_c CREATE INDEX m_access_cert_definition_nameOrig_idx ON m_access_cert_definition (nameOrig); ALTER TABLE m_access_cert_definition ADD CONSTRAINT m_access_cert_definition_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_access_cert_definition_subtypes_idx ON m_access_cert_definition USING gin(subtypes); +CREATE INDEX m_access_cert_definition_policySituation_idx + ON m_access_cert_definition USING GIN(policysituations gin__int_ops); CREATE INDEX m_access_cert_definition_ext_idx ON m_access_cert_definition USING gin (ext); -- TODO not mapped yet @@ -1217,6 +1255,9 @@ CREATE TRIGGER m_access_cert_campaign_oid_delete_tr AFTER DELETE ON m_access_cer CREATE INDEX m_access_cert_campaign_nameOrig_idx ON m_access_cert_campaign (nameOrig); ALTER TABLE m_access_cert_campaign ADD CONSTRAINT m_access_cert_campaign_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_access_cert_campaign_subtypes_idx ON m_access_cert_campaign USING gin(subtypes); +CREATE INDEX m_access_cert_campaign_policySituation_idx + ON m_access_cert_campaign USING GIN(policysituations gin__int_ops); CREATE INDEX m_access_cert_campaign_ext_idx ON m_access_cert_campaign USING gin (ext); CREATE TABLE m_access_cert_case ( @@ -1329,6 +1370,8 @@ CREATE TRIGGER m_object_template_oid_delete_tr AFTER DELETE ON m_object_template CREATE INDEX m_object_template_nameOrig_idx ON m_object_template (nameOrig); ALTER TABLE m_object_template ADD CONSTRAINT m_object_template_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_object_template_subtypes_idx ON m_object_template USING gin(subtypes); +CREATE INDEX m_object_template_policySituation_idx ON m_object_template USING GIN(policysituations gin__int_ops); -- stores ObjectTemplateType/includeRef CREATE TABLE m_ref_include ( @@ -1362,6 +1405,9 @@ CREATE TRIGGER m_function_library_oid_delete_tr AFTER DELETE ON m_function_libra CREATE INDEX m_function_library_nameOrig_idx ON m_function_library (nameOrig); ALTER TABLE m_function_library ADD CONSTRAINT m_function_library_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_function_library_subtypes_idx ON m_function_library USING gin(subtypes); +CREATE INDEX m_function_library_policySituation_idx + ON m_function_library USING GIN(policysituations gin__int_ops); -- Represents SequenceType, see https://wiki.evolveum.com/display/midPoint/Sequences CREATE TABLE m_sequence ( @@ -1380,6 +1426,8 @@ CREATE TRIGGER m_sequence_oid_delete_tr AFTER DELETE ON m_sequence CREATE INDEX m_sequence_nameOrig_idx ON m_sequence (nameOrig); ALTER TABLE m_sequence ADD CONSTRAINT m_sequence_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_sequence_subtypes_idx ON m_sequence USING gin(subtypes); +CREATE INDEX m_sequence_policySituation_idx ON m_sequence USING GIN(policysituations gin__int_ops); -- Represents FormType, see https://wiki.evolveum.com/display/midPoint/Custom+forms CREATE TABLE m_form ( @@ -1398,6 +1446,8 @@ CREATE TRIGGER m_form_oid_delete_tr AFTER DELETE ON m_form CREATE INDEX m_form_nameOrig_idx ON m_form (nameOrig); ALTER TABLE m_form ADD CONSTRAINT m_form_nameNorm_key UNIQUE (nameNorm); +CREATE INDEX m_form_subtypes_idx ON m_form USING gin(subtypes); +CREATE INDEX m_form_policySituation_idx ON m_form USING GIN(policysituations gin__int_ops); -- endregion -- region Assignment/Inducement table From 4bd20fe2a7ac245891ef00d230ea6fd87805a13b Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 27 May 2021 09:38:01 +0200 Subject: [PATCH 41/43] repo-sqale: fixes of messages in UnsupportedOperationException --- .../midpoint/repo/sqale/delta/ItemDeltaValueProcessor.java | 4 ++-- .../midpoint/repo/sqale/qmodel/common/QContainerMapping.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/ItemDeltaValueProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/ItemDeltaValueProcessor.java index cea2ba90bbf..e699e12f722 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/ItemDeltaValueProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/ItemDeltaValueProcessor.java @@ -80,7 +80,7 @@ public void addRealValues(Collection values) { } public void addValues(Collection values) { - throw new UnsupportedOperationException("deleteRealValues not implemented"); + throw new UnsupportedOperationException("addValues not implemented"); } /** Adds the provided real values to the database, implements ADD modification. */ @@ -89,7 +89,7 @@ public void deleteRealValues(Collection values) { } public void deleteValues(Collection values) { - throw new UnsupportedOperationException("deleteRealValues not implemented"); + throw new UnsupportedOperationException("deleteValues not implemented"); } /** Resets the database columns or deletes sub-entities like refs, containers, etc. */ diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/QContainerMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/QContainerMapping.java index b78cd02cdc5..289993c5738 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/QContainerMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/common/QContainerMapping.java @@ -76,6 +76,6 @@ public R initRowObject(S schemaObject, OR ownerRow) { @Override public R insert(S schemaObject, OR ownerRow, JdbcSession jdbcSession) { - throw new UnsupportedOperationException("insert not implemented in the subclass"); + throw new UnsupportedOperationException("insert must be implemented in the subclass"); } } From 00747ea635d328e57604d1da3daa2adcf2b42fee Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 27 May 2021 16:56:01 +0200 Subject: [PATCH 42/43] repo-sqale: added modify support for array columns Added ArrayItemDeltaProcessor to set array column. SqaleUpdateContext#findItem(path) returns item from root prism object; used to obtain new value (modification is applied before processor). Added missing mapping for assignment/policySituations. --- .../delta/DelegatingItemDeltaProcessor.java | 3 +- .../repo/sqale/delta/ItemDeltaProcessor.java | 3 +- .../sqale/delta/ItemDeltaValueProcessor.java | 10 +- .../delta/item/ArrayItemDeltaProcessor.java | 88 +++++++ .../item/ItemDeltaSingleValueProcessor.java | 2 + .../item/SinglePathItemDeltaProcessor.java | 2 + .../ArrayPathItemFilterProcessor.java | 5 + .../repo/sqale/qmodel/SqaleTableMapping.java | 8 +- .../qmodel/assignment/QAssignmentMapping.java | 2 + .../repo/sqale/update/RootUpdateContext.java | 11 +- .../repo/sqale/update/SqaleUpdateContext.java | 11 + .../sqale/func/SqaleRepoModifyObjectTest.java | 230 +++++++++++++++++- 12 files changed, 360 insertions(+), 15 deletions(-) create mode 100644 repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/ArrayItemDeltaProcessor.java diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/DelegatingItemDeltaProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/DelegatingItemDeltaProcessor.java index 73d0c59d724..43cc9d8429e 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/DelegatingItemDeltaProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/DelegatingItemDeltaProcessor.java @@ -20,6 +20,7 @@ import com.evolveum.midpoint.repo.sqlbase.mapping.ItemRelationResolver; import com.evolveum.midpoint.repo.sqlbase.mapping.ItemSqlMapper; import com.evolveum.midpoint.repo.sqlbase.mapping.QueryModelMapping; +import com.evolveum.midpoint.util.exception.SchemaException; /** * This is default item delta processor that decides what to do with the modification. @@ -40,7 +41,7 @@ public DelegatingItemDeltaProcessor(SqaleUpdateContext context) { } @Override - public void process(ItemDelta modification) throws RepositoryException { + public void process(ItemDelta modification) throws RepositoryException, SchemaException { QName itemName = resolvePath(modification.getPath()); if (itemName == null) { // This may indicate forgotten mapping, but normally it means that the item is simply diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/ItemDeltaProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/ItemDeltaProcessor.java index 51d96842baa..8b14f0beeb8 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/ItemDeltaProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/ItemDeltaProcessor.java @@ -8,6 +8,7 @@ import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.repo.sqlbase.RepositoryException; +import com.evolveum.midpoint.util.exception.SchemaException; /** * Essential contract for processing item delta modifications. @@ -16,5 +17,5 @@ */ public interface ItemDeltaProcessor { - void process(ItemDelta modification) throws RepositoryException; + void process(ItemDelta modification) throws RepositoryException, SchemaException; } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/ItemDeltaValueProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/ItemDeltaValueProcessor.java index e699e12f722..c2a9b71f3e2 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/ItemDeltaValueProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/ItemDeltaValueProcessor.java @@ -15,6 +15,7 @@ import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.repo.sqale.update.SqaleUpdateContext; import com.evolveum.midpoint.repo.sqlbase.RepositoryException; +import com.evolveum.midpoint.util.exception.SchemaException; /** * Applies item delta values to an item and arranges necessary SQL changes using update context. @@ -39,9 +40,14 @@ protected ItemDeltaValueProcessor(SqaleUpdateContext context) { this.context = context; } - /** Default process implementation, most generic case covering especially multi-values. */ + /** + * Default process implementation, most generic case covering especially multi-values + * stored in separate rows. + * This works when implementations of {@link #deleteRealValues} and {@link #addRealValues} + * are independent, it's not usable for array update where a single `SET` clause is allowed. + */ @Override - public void process(ItemDelta modification) throws RepositoryException { + public void process(ItemDelta modification) throws RepositoryException, SchemaException { if (modification.isReplace()) { setRealValues(modification.getRealValuesToReplace()); return; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/ArrayItemDeltaProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/ArrayItemDeltaProcessor.java new file mode 100644 index 00000000000..fded02e3d11 --- /dev/null +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/ArrayItemDeltaProcessor.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010-2021 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.repo.sqale.delta.item; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.function.Function; +import java.util.function.IntFunction; + +import com.querydsl.core.types.dsl.ArrayPath; +import org.jetbrains.annotations.Nullable; + +import com.evolveum.midpoint.prism.Item; +import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.prism.delta.ItemDelta; +import com.evolveum.midpoint.repo.sqale.delta.ItemDeltaValueProcessor; +import com.evolveum.midpoint.repo.sqale.update.SqaleUpdateContext; +import com.evolveum.midpoint.repo.sqlbase.RepositoryException; +import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; +import com.evolveum.midpoint.util.exception.SchemaException; + +/** + * Filter processor for multi-value property represented by single array column. + * These paths support only value equality (of any value), which is "contains" in DB terminology. + * Our filter "contains" (meaning substring) is *not* supported. + * + * @param type of value in schema + * @param type of element in DB (can be the same like `V`) + */ +public class ArrayItemDeltaProcessor extends ItemDeltaValueProcessor { + + private final ArrayPath path; + private final Class elementType; + @Nullable private final Function conversionFunction; + + /** + * @param entity query type from which the attribute is resolved + * @param row type related to {@link Q} + * @param elementType class of {@link E} necessary for array creation + * @param conversionFunction optional conversion function, can be null if no conversion is necessary + */ + public , R> ArrayItemDeltaProcessor( + SqaleUpdateContext context, + Function> rootToQueryItem, + Class elementType, + @Nullable Function conversionFunction) { + super(context); + this.path = rootToQueryItem.apply(context.path()); + this.elementType = elementType; + this.conversionFunction = conversionFunction; + } + + @Override + public void process(ItemDelta modification) throws RepositoryException, SchemaException { + Item item = context.findItem(modification.getPath()); + Collection realValues = item != null ? item.getRealValues() : null; + + if (realValues == null || realValues.isEmpty()) { + delete(); + } else { + // Whatever the operation is, we just set the new value here. + setRealValues(realValues); + } + } + + @Override + public void setRealValues(Collection values) { + //noinspection unchecked + IntFunction arrayConstructor = i -> (E[]) Array.newInstance(elementType, i); + + // valueArray can't be just Object[], it must be concrete type, e.g. String[], + // otherwise PG JDBC driver will complain. + //noinspection SuspiciousToArrayCall,unchecked + E[] valueArray = conversionFunction != null + ? ((Collection) values).stream().map(conversionFunction).toArray(arrayConstructor) + : values.toArray(arrayConstructor); + context.set(path, valueArray); + } + + @Override + public void delete() { + context.set(path, null); + } +} diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/ItemDeltaSingleValueProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/ItemDeltaSingleValueProcessor.java index 520f9d22923..3d0096382f1 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/ItemDeltaSingleValueProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/ItemDeltaSingleValueProcessor.java @@ -21,6 +21,8 @@ * * This hierarchy branch should not need {@link #addRealValues} and {@link #deleteRealValues}, * so it's not overridden and throws {@link UnsupportedOperationException}. + * + * @param expected type of the real value for the modification (after optional conversion) */ public abstract class ItemDeltaSingleValueProcessor extends ItemDeltaValueProcessor { diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/SinglePathItemDeltaProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/SinglePathItemDeltaProcessor.java index c5fbe97c07a..a4a4e0a4061 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/SinglePathItemDeltaProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/delta/item/SinglePathItemDeltaProcessor.java @@ -14,6 +14,8 @@ import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase; /** + * Processor for items represented by a single column (query path). + * * @param type of real value after optional conversion ({@link #convertRealValue(Object)} * to match the column (attribute) type in the row bean (M-type) * @param

type of the corresponding path in the Q-type diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/ArrayPathItemFilterProcessor.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/ArrayPathItemFilterProcessor.java index f24026e0c02..1ab65fa9629 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/ArrayPathItemFilterProcessor.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/filtering/ArrayPathItemFilterProcessor.java @@ -27,6 +27,9 @@ * Filter processor for multi-value property represented by single array column. * These paths support only value equality (of any value), which is "contains" in DB terminology. * Our filter "contains" (meaning substring) is *not* supported. + * + * @param type of value in schema + * @param type of element in DB (can be the same like `T`) */ public class ArrayPathItemFilterProcessor extends SinglePathItemFilterProcessor> { @@ -39,6 +42,8 @@ public class ArrayPathItemFilterProcessor * Creates filter processor for array column. * * @param dbType name of the type for element in DB (without []) for the cast part of the condition + * @param elementType class of {@link E} necessary for array creation + * @param conversionFunction optional conversion function, can be null if no conversion is necessary */ public , R> ArrayPathItemFilterProcessor( SqlQueryContext context, diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java index 8100e20a876..e1b2a775ba3 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/SqaleTableMapping.java @@ -221,11 +221,12 @@ protected SqaleItemSqlMapper multiStringMapper( return new SqaleItemSqlMapper<>( ctx -> new ArrayPathItemFilterProcessor( ctx, rootToQueryItem, "TEXT", String.class, null), - ctx -> null); // TODO + ctx -> new ArrayItemDeltaProcessor( + ctx, rootToQueryItem, String.class, null)); } /** - * Returns the mapper creating integer multi-value filter/delta processors from context. + * Returns the mapper creating cached URI multi-value filter/delta processors from context. * * @param mapped schema type, see javadoc in {@link QueryTableMapping} */ @@ -235,7 +236,8 @@ protected SqaleItemSqlMapper multiUriMapper( ctx -> new ArrayPathItemFilterProcessor<>( ctx, rootToQueryItem, "INTEGER", Integer.class, ((SqaleRepoContext) ctx.repositoryContext())::searchCachedUriId), - ctx -> null); // TODO + ctx -> new ArrayItemDeltaProcessor<>(ctx, rootToQueryItem, Integer.class, + ((SqaleRepoContext) ctx.repositoryContext())::processCacheableUri)); } @Override diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentMapping.java index b66cda08b24..da92b96f84a 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentMapping.java @@ -98,6 +98,8 @@ private QAssignmentMapping( q -> q.tenantRefTargetOid, q -> q.tenantRefTargetType, q -> q.tenantRefRelationId)); + addItemMapping(F_POLICY_SITUATION, multiUriMapper(q -> q.policySituations)); + // TODO no idea how extId/Oid works, see RAssignment.getExtension // TODO ext mapping can't be done statically addNestedMapping(F_CONSTRUCTION, ConstructionType.class) diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/RootUpdateContext.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/RootUpdateContext.java index 2d137a9bccd..8f3b7a3dbf4 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/RootUpdateContext.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/RootUpdateContext.java @@ -13,10 +13,12 @@ import com.querydsl.core.types.Path; import com.querydsl.sql.dml.SQLUpdateClause; +import org.jetbrains.annotations.NotNull; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy; +import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.repo.sqale.ContainerValueIdGenerator; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; import com.evolveum.midpoint.repo.sqale.SqaleUtils; @@ -26,7 +28,7 @@ import com.evolveum.midpoint.repo.sqale.qmodel.object.QObjectMapping; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.repo.sqlbase.RepositoryException; -import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping; +import com.evolveum.midpoint.repo.sqlbase.mapping.QueryModelMapping; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; @@ -68,7 +70,7 @@ public Q path() { } @Override - public QueryTableMapping mapping() { + public QueryModelMapping mapping() { return mapping; } @@ -156,6 +158,11 @@ protected void finishExecutionOwn() throws SchemaException, RepositoryException } } + @Override + public Item findItem(@NotNull ItemPath path) { + return object.asPrismObject().findItem(path); + } + public SQLUpdateClause update() { return update; } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/SqaleUpdateContext.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/SqaleUpdateContext.java index 7cc1b9823f4..715b276a50d 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/SqaleUpdateContext.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/SqaleUpdateContext.java @@ -10,7 +10,10 @@ import java.util.Map; import com.querydsl.core.types.Path; +import org.jetbrains.annotations.NotNull; +import com.evolveum.midpoint.prism.Item; +import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.path.ItemName; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.repo.sqale.SqaleRepoContext; @@ -149,4 +152,12 @@ protected final void finishExecution() throws SchemaException, RepositoryExcepti } protected abstract void finishExecutionOwn() throws SchemaException, RepositoryException; + + public Item findItem(@NotNull ItemPath path) { + if (parentContext == null) { + throw new UnsupportedOperationException( + "findItem() is unsupported on non-root update context"); + } + return parentContext.findItem(path); + } } diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java index f9bb90c6d54..3802f29916f 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java @@ -1150,6 +1150,227 @@ public void test171DeletingMetadataContainerRemovesContainedRefs() r = QObjectReferenceMapping.getForModifyApprover().defaultAlias(); assertThat(count(r, r.ownerOid.eq(UUID.fromString(user1Oid)))).isZero(); } + + @Test + public void test180ReplacingSubtypeValuesSetsArrayColumn() throws Exception { + OperationResult result = createOperationResult(); + MUser originalRow = selectObjectByOid(QUser.class, user1Oid); + + given("delta to replace subtypes with a couple of values"); + ObjectDelta delta = prismContext.deltaFor(UserType.class) + .item(UserType.F_SUBTYPE).replace("subtype-1", "subtype-2") + .asObjectDelta(user1Oid); + + when("modifyObject is called"); + repositoryService.modifyObject(UserType.class, user1Oid, delta.getModifications(), result); + + then("operation is successful"); + assertThatOperationResult(result).isSuccess(); + + and("serialized form (fullObject) is updated and has provided subtypes"); + UserType userObject = repositoryService + .getObject(UserType.class, user1Oid, null, result) + .asObjectable(); + assertThat(userObject.getVersion()).isEqualTo(String.valueOf(originalRow.version + 1)); + assertThat(userObject.getSubtype()) + .containsExactlyInAnyOrder("subtype-1", "subtype-2"); + + and("column with subtypes is updated"); + MUser row = selectObjectByOid(QUser.class, user1Oid); + assertThat(row.version).isEqualTo(originalRow.version + 1); + assertThat(row.subtypes).containsExactlyInAnyOrder("subtype-1", "subtype-2"); + } + + @Test + public void test181AddingAndDeletingSubtypeValuesSetsArrayColumn() throws Exception { + OperationResult result = createOperationResult(); + + given("delta for subtypes with both delete and add values"); + ObjectDelta delta = prismContext.deltaFor(UserType.class) + .item(UserType.F_SUBTYPE).delete("subtype-2", "wrong").add("subtype-3", "subtype-4") + .asObjectDelta(user1Oid); + + when("modifyObject is called"); + repositoryService.modifyObject(UserType.class, user1Oid, delta.getModifications(), result); + + then("operation is successful"); + assertThatOperationResult(result).isSuccess(); + + and("serialized form (fullObject) is updated and has expected subtypes"); + UserType userObject = repositoryService + .getObject(UserType.class, user1Oid, null, result) + .asObjectable(); + assertThat(userObject.getSubtype()) + .containsExactlyInAnyOrder("subtype-1", "subtype-3", "subtype-4"); + + and("column with subtypes is updated"); + MUser row = selectObjectByOid(QUser.class, user1Oid); + assertThat(row.subtypes).containsExactlyInAnyOrder("subtype-1", "subtype-3", "subtype-4"); + } + + @Test + public void test182DeletingAllSubtypesByValuesSetsColumnToNull() throws Exception { + OperationResult result = createOperationResult(); + + given("delta deleting all subtype values"); + ObjectDelta delta = prismContext.deltaFor(UserType.class) + .item(UserType.F_SUBTYPE).delete("subtype-1", "subtype-3", "subtype-4") + .asObjectDelta(user1Oid); + + when("modifyObject is called"); + repositoryService.modifyObject(UserType.class, user1Oid, delta.getModifications(), result); + + then("operation is successful"); + assertThatOperationResult(result).isSuccess(); + + and("serialized form (fullObject) is updated and has no subtypes now"); + UserType userObject = repositoryService + .getObject(UserType.class, user1Oid, null, result) + .asObjectable(); + assertThat(userObject.getSubtype()).isNullOrEmpty(); + + and("column with subtypes is set to null"); + MUser row = selectObjectByOid(QUser.class, user1Oid); + assertThat(row.subtypes).isNull(); + } + + // this section tests two things: array in container, and integer[] requiring conversion (URIs) + @Test + public void test185AddingAssignmentWithPolicySituations() throws Exception { + OperationResult result = createOperationResult(); + MUser originalRow = selectObjectByOid(QUser.class, user1Oid); + + // Container tests are in 3xx category, but let's focus on policy situations. + given("delta adding assignment with policy situations"); + ObjectDelta delta = prismContext.deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT).add(new AssignmentType(prismContext) + .policySituation("policy-situation-1") + .policySituation("policy-situation-2")) + .asObjectDelta(user1Oid); + + when("modifyObject is called"); + repositoryService.modifyObject(UserType.class, user1Oid, delta.getModifications(), result); + + then("operation is successful"); + assertThatOperationResult(result).isSuccess(); + + and("serialized form (fullObject) is updated"); + UserType userObject = repositoryService + .getObject(UserType.class, user1Oid, null, result) + .asObjectable(); + assertThat(userObject.getVersion()).isEqualTo(String.valueOf(originalRow.version + 1)); + assertThat(userObject.getAssignment().get(0).getPolicySituation()) + .containsExactlyInAnyOrder("policy-situation-1", "policy-situation-2"); + + and("policySituation column is set in the assignment row"); + MUser row = selectObjectByOid(QUser.class, user1Oid); + assertThat(row.version).isEqualTo(originalRow.version + 1); + + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); + MAssignment aRow = selectOne(a, a.ownerOid.eq(UUID.fromString(user1Oid))); + assertThat(aRow.policySituations) + .extracting(uriId -> cachedUriById(uriId)) + .containsExactlyInAnyOrder("policy-situation-1", "policy-situation-2"); + } + + @Test + public void test186AddingAndDeletingAssignmentPolicySituations() throws Exception { + OperationResult result = createOperationResult(); + MUser originalRow = selectObjectByOid(QUser.class, user1Oid); + long assignmentId = originalRow.containerIdSeq - 1; + + given("delta both adding and deleting assignment's policy situations"); + ObjectDelta delta = prismContext.deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT, assignmentId, AssignmentType.F_POLICY_SITUATION) + .delete("policy-situation-2", "wrong").add("policy-situation-3") + .asObjectDelta(user1Oid); + + when("modifyObject is called"); + repositoryService.modifyObject(UserType.class, user1Oid, delta.getModifications(), result); + + then("operation is successful"); + assertThatOperationResult(result).isSuccess(); + + and("serialized form (fullObject) is updated"); + UserType userObject = repositoryService + .getObject(UserType.class, user1Oid, null, result) + .asObjectable(); + assertThat(userObject.getAssignment().get(0).getPolicySituation()) + .containsExactlyInAnyOrder("policy-situation-1", "policy-situation-3"); + + and("column with subtypes is updated accordingly"); + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); + MAssignment aRow = selectOne(a, a.ownerOid.eq(UUID.fromString(user1Oid))); + assertThat(aRow.policySituations) + .extracting(uriId -> cachedUriById(uriId)) + .containsExactlyInAnyOrder("policy-situation-1", "policy-situation-3"); + } + + @Test + public void test187ReplacingAssignmentPolicySituationsValues() throws Exception { + OperationResult result = createOperationResult(); + MUser originalRow = selectObjectByOid(QUser.class, user1Oid); + long assignmentId = originalRow.containerIdSeq - 1; + + given("delta replacing assignment's policy situations"); + ObjectDelta delta = prismContext.deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT, assignmentId, AssignmentType.F_POLICY_SITUATION) + .replace("policy-situation-a", "policy-situation-z") + .asObjectDelta(user1Oid); + + when("modifyObject is called"); + repositoryService.modifyObject(UserType.class, user1Oid, delta.getModifications(), result); + + then("operation is successful"); + assertThatOperationResult(result).isSuccess(); + + and("serialized form (fullObject) is updated"); + UserType userObject = repositoryService + .getObject(UserType.class, user1Oid, null, result) + .asObjectable(); + assertThat(userObject.getAssignment().get(0).getPolicySituation()) + .containsExactlyInAnyOrder("policy-situation-a", "policy-situation-z"); + + and("column with subtypes is updated accordingly"); + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); + MAssignment aRow = selectOne(a, a.ownerOid.eq(UUID.fromString(user1Oid))); + assertThat(aRow.policySituations) + .extracting(uriId -> cachedUriById(uriId)) + .containsExactlyInAnyOrder("policy-situation-a", "policy-situation-z"); + } + + @Test + public void test188ReplacingAssignmentPolicySituationsWithNoValue() throws Exception { + OperationResult result = createOperationResult(); + MUser originalRow = selectObjectByOid(QUser.class, user1Oid); + long assignmentId = originalRow.containerIdSeq - 1; + + given("delta replacing assignment's policy situations with no value"); + ObjectDelta delta = prismContext.deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT, assignmentId, AssignmentType.F_POLICY_SITUATION) + .replace() + .asObjectDelta(user1Oid); + + when("modifyObject is called"); + repositoryService.modifyObject(UserType.class, user1Oid, delta.getModifications(), result); + + then("operation is successful"); + assertThatOperationResult(result).isSuccess(); + + and("serialized form (fullObject) is updated"); + UserType userObject = repositoryService + .getObject(UserType.class, user1Oid, null, result) + .asObjectable(); + assertThat(userObject.getAssignment().get(0).getPolicySituation()).isNullOrEmpty(); + + and("column with policy situations is set to null"); + QAssignment a = QAssignmentMapping.getAssignment().defaultAlias(); + MAssignment aRow = selectOne(a, a.ownerOid.eq(UUID.fromString(user1Oid))); + assertThat(aRow.policySituations).isNull(); + } + + // TODO test for multi-value (e.g. subtypes) with item delta with both add and delete lists + // But with current implementation this can go to the first hundred section...? // endregion // region nested (embedded) single-value containers (e.g. metadata) @@ -1631,16 +1852,16 @@ public void test213OverwritingParentOfDeeplyNestedEmbeddedContainer() // region multi-value containers (e.g. assignments) @Test - public void test300AddAssignmentStoresItAndGeneratesMissingId() + public void test300AddedAssignmentStoresItAndGeneratesMissingId() throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { OperationResult result = createOperationResult(); MUser originalRow = selectObjectByOid(QUser.class, user1Oid); - given("delta adding assignment for user 1"); + given("delta replacing assignments for user 1 with a single one"); UUID roleOid = UUID.randomUUID(); ObjectDelta delta = prismContext.deltaFor(UserType.class) .item(UserType.F_ASSIGNMENT) - .add(new AssignmentType(prismContext) + .replace(new AssignmentType(prismContext) .targetRef(roleOid.toString(), RoleType.COMPLEX_TYPE)) // default relation .asObjectDelta(user1Oid); @@ -2112,9 +2333,6 @@ public void test399DeleteAllAssignments() } // endregion - // TODO test for multi-value (e.g. subtypes) with item delta with both add and delete lists - // But with current implementation this can go to the first hundred section...? - // TODO: photo test, should work fine, but it is kinda special, not part of full object // region other tests From a2e9412dc2c755ceefece5a5da600334d3a01fab Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 27 May 2021 17:17:37 +0200 Subject: [PATCH 43/43] repo-sqale: TODO refresh --- .../repo/sqale/func/SqaleRepoModifyObjectTest.java | 6 ++---- .../midpoint/repo/sqale/func/SqaleRepoSmokeTest.java | 8 +++++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java index 3802f29916f..955e6e79895 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoModifyObjectTest.java @@ -1368,9 +1368,6 @@ public void test188ReplacingAssignmentPolicySituationsWithNoValue() throws Excep MAssignment aRow = selectOne(a, a.ownerOid.eq(UUID.fromString(user1Oid))); assertThat(aRow.policySituations).isNull(); } - - // TODO test for multi-value (e.g. subtypes) with item delta with both add and delete lists - // But with current implementation this can go to the first hundred section...? // endregion // region nested (embedded) single-value containers (e.g. metadata) @@ -2333,7 +2330,8 @@ public void test399DeleteAllAssignments() } // endregion - // TODO: photo test, should work fine, but it is kinda special, not part of full object + // TODO: photo test, currently it puts it into fullObject and not into column. + // It should be other way around. // region other tests @Test diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSmokeTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSmokeTest.java index 309382e380a..2381e4b354a 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSmokeTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoSmokeTest.java @@ -88,10 +88,15 @@ public void test200GetObject() throws SchemaException, ObjectNotFoundException { then("object is obtained and performance monitor is updated"); assertThatOperationResult(result).isSuccess(); + assertThat(object).isNotNull(); assertSingleOperationRecorded(pm, RepositoryService.OP_GET_OBJECT); } - @Test(enabled = false) // TODO deleteObject not implemented yet + // TODO test for getObject() with typical options (here or separate class?) + // - ObjectOperationOptions(jpegPhoto:retrieve=INCLUDE) + // - ObjectOperationOptions(/:resolveNames) + + @Test public void test800DeleteObject() throws ObjectNotFoundException { OperationResult result = createOperationResult(); @@ -100,6 +105,7 @@ public void test800DeleteObject() throws ObjectNotFoundException { assertThat(deleteResult).isNotNull(); assertThatOperationResult(result).isSuccess(); + assertThat(selectNullableObjectByOid(QUser.class, sanityUserOid)).isNull(); } // region low-level tests