diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9548bb95b3dc8e1c98c6736afe25174bd3591f26..12e9b2e7837da3cae36be30e5f54ce343f75f5f3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,7 +22,7 @@ services: - mariadb:10.4 variables: - DEPLOY_REF: f-feature-branch-pipeline + DEPLOY_REF: dev CI_REGISTRY_IMAGE: $CI_REGISTRY/caosdb/caosdb-mysqlbackend/testenv:latest MYSQL_ROOT_PASSWORD: caosdb1234 diff --git a/CHANGELOG.md b/CHANGELOG.md index e1b9e5aeef66c8a9fba2fa2a3bbd0e273492b996..87753bce3c0c3b9d9f85f1fcdb5634d1b2de656b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,4 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed ### +* POV with the 'name' property, e.g. `FIND ENTITY WITH name = something` + [caosdb-server#51](https://gitlab.com/caosdb/caosdb-server/-/issues/51) - Fixed several bugs when an Entity inherits from itself (#18, caosdb-server #85). + +### Security ### diff --git a/patches/patch20200615-3.0.0rc1/move_names.sql b/patches/patch20200615-3.0.0rc1/move_names.sql new file mode 100644 index 0000000000000000000000000000000000000000..872256f3892fc999e271556b62df2c1fedaddafb --- /dev/null +++ b/patches/patch20200615-3.0.0rc1/move_names.sql @@ -0,0 +1,38 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +/* + * This patch moves all names of existing entities from the entities table into + * the name_data table. + */ + + +-- unique key adds savety in writing transactions and performance in reading +-- transactions. +ALTER TABLE name_data ADD UNIQUE KEY (domain_id, entity_id, property_id); + +-- copy names of old entities. 20 is the (hard-coded) id of the name property +-- itself. status is "FIX" because names are not to be inherited (by default). +-- pidx 0 is irrelevant here because the primary names have no "order" in which +-- they appear. +INSERT INTO name_data (domain_id, entity_id, property_id, value, status, pidx) SELECT 0, id, 20, name, "FIX", 0 FROM entities WHERE name IS NOT NULL; + +-- delete the name column and effectively all the names from entities table. +ALTER TABLE entities DROP COLUMN name; diff --git a/patches/patch20200615-3.0.0rc1/patch.sh b/patches/patch20200615-3.0.0rc1/patch.sh new file mode 100755 index 0000000000000000000000000000000000000000..315ede2d8d2b0cabf9a26d365db8cc0e2e583b90 --- /dev/null +++ b/patches/patch20200615-3.0.0rc1/patch.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# +# ** header v3.0 +# This file is a part of the CaosDB Project. +# +# Copyright (C) 2018 Research Group Biomedical Physics, +# Max-Planck-Institute for Dynamics and Self-Organization Göttingen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +# ** end header +# + +# Refactors the entities table and move all names to name_data +# Update mysql schema to version v3.0.0-rc1 +NEW_VERSION="v3.0.0-rc1" +OLD_VERSION="v2.1.2" + +if [ -z "$UTILSPATH" ]; then + UTILSPATH="../utils" +fi + +. $UTILSPATH/patch_header.sh $* + + +check_version $OLD_VERSION + +mysql_execute_file $PATCH_DIR/move_names.sql + +update_version $NEW_VERSION + +success + diff --git a/procedures/insertEntity.sql b/procedures/insertEntity.sql index be4f77d8bb013424192582015b7710f9b7cdae2e..4816eddd848b68a736d1c483a09be035a727b358 100644 --- a/procedures/insertEntity.sql +++ b/procedures/insertEntity.sql @@ -48,9 +48,15 @@ BEGIN DECLARE Version VARBINARY(255) DEFAULT NULL; DECLARE Transaction VARBINARY(255) DEFAULT NULL; + -- insert the acl. the new acl id is being written (c-style) into the + -- variable NewACLID. call entityACL(NewACLID, ACL); - INSERT INTO entities (name, description, role, acl) VALUES (EntityName, EntityDesc, EntityRole, NewACLID); + -- insert the description, role, and acl into entities table... + INSERT INTO entities (description, role, acl) + VALUES (EntityDesc, EntityRole, NewACLID); + + -- ... and return the generated id SET NewEntityID = LAST_INSERT_ID(); IF is_feature_config("ENTITY_VERSIONING", "ENABLED") THEN @@ -60,6 +66,14 @@ BEGIN CALL insert_single_child_version(NewEntityID, Hash, Version, Null, Transaction); END IF; + -- insert the name of the entity into name_data table + -- 20 is the (hard-coded) id of the 'name' property. + IF EntityName IS NOT NULL THEN + INSERT INTO name_data + (domain_id, entity_id, property_id, value, status, pidx) + VALUES (0, NewEntityID, 20, EntityName, "FIX", 0); + END IF; + SELECT NewEntityID as EntityID, Version as Version; END; diff --git a/procedures/query/applyPOV.sql b/procedures/query/applyPOV.sql index eefee066d8b72b7d13cc24ec6c56fa5b6150f061..bf49d930a7daab9d1cd81de482b5dc6d8dd4f462 100644 --- a/procedures/query/applyPOV.sql +++ b/procedures/query/applyPOV.sql @@ -55,6 +55,7 @@ CREATE PROCEDURE db_2_0.applyPOV(in sourceSet VARCHAR(255), /* (?) Name of the t POV_LABEL: BEGIN DECLARE data TEXT DEFAULT NULL; /*data subselect statement string*/ DECLARE sTextData VARCHAR(20000) DEFAULT NULL; /*SELECT PREFIX for data subselect plus WHERE CLAUSE for text_data*/ + DECLARE sNameData VARCHAR(20000) DEFAULT NULL; /*WHERE CLAUSE for name_data*/ DECLARE sEnumData VARCHAR(20000) DEFAULT NULL; /*WHERE CLAUSE for enum_data*/ DECLARE sIntData VARCHAR(20000) DEFAULT NULL; /*WHERE CLAUSE for integer_data*/ DECLARE sDoubleData VARCHAR(20000) DEFAULT NULL; /*WHERE CLAUSE for double_data*/ @@ -86,6 +87,7 @@ POV_LABEL: BEGIN SET vText = NULL; /* Union of the following tables: text_data + name_data enum_data integer_data date_data @@ -96,7 +98,7 @@ POV_LABEL: BEGIN SELECT DISTINCT -> No duplicate values UNION ALL -> Allow also duplicate values */ - SET sTextData = 'SELECT DISTINCT domain_id, entity_id, property_id FROM `text_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `enum_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `integer_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `double_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `date_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `datetime_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `reference_data` AS subdata WHERE subdata.value IS NOT NULL'; + SET sTextData = 'SELECT DISTINCT domain_id, entity_id, property_id FROM `text_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `name_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `enum_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `integer_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `double_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `date_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `datetime_data` AS subdata WHERE subdata.value IS NOT NULL UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `reference_data` AS subdata WHERE subdata.value IS NOT NULL'; ELSEIF o = "(" or o = "!(" THEN SET sTextData = IF(vText IS NULL, ' SELECT DISTINCT domain_id, entity_id, property_id FROM `date_data`', @@ -144,6 +146,7 @@ POV_LABEL: BEGIN ELSE #-- generate statement parts SET sTextData = IF(vText IS NULL, 'SELECT DISTINCT domain_id, entity_id, property_id FROM `text_data`', CONCAT('SELECT DISTINCT domain_id, entity_id, property_id FROM `text_data` AS subdata WHERE subdata.value ',o,' ?')); + SET sNameData = IF(vText IS NULL, ' UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `name_data`', CONCAT(' UNION SELECT DISTINCT domain_id, entity_id, property_id FROM `name_data` AS subdata WHERE subdata.value ', o, ' ?')); SET sEnumData = IF(vText IS NULL, ' UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `enum_data`', CONCAT(' UNION SELECT DISTINCT domain_id, entity_id, property_id FROM `enum_data` AS subdata WHERE subdata.value ', o, ' ?')); SET sIntData = IF(vText IS NULL, ' UNION ALL SELECT DISTINCT subdata.domain_id, subdata.entity_id, subdata.property_id FROM `integer_data` AS subdata', IF(vInt IS NULL AND vDoubleStdUnit IS NULL, NULL, CONCAT(' UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `integer_data` AS subdata WHERE ', getDoubleWhereClause(vInt, unit_sig, vDoubleStdUnit, stdUnit_sig, o)))); SET sDoubleData = IF(vText IS NULL, ' UNION ALL SELECT DISTINCT subdata.domain_id, subdata.entity_id, subdata.property_id FROM `double_data` AS subdata', IF(vDouble IS NULL, NULL, CONCAT(' UNION ALL SELECT DISTINCT domain_id, entity_id, property_id FROM `double_data` AS subdata WHERE ', getDoubleWhereClause(vDouble,unit_sig,vDoubleStdUnit,stdUnit_sig,o)))); @@ -155,6 +158,7 @@ POV_LABEL: BEGIN END IF; SET data = CONCAT('(',sTextData, + IF(sNameData IS NULL, '', sNameData), IF(sEnumData IS NULL, '', sEnumData), IF(sDoubleData IS NULL, '', sDoubleData), IF(sIntData IS NULL, '', sIntData), @@ -187,7 +191,7 @@ POV_LABEL: BEGIN EXECUTE stmtPOVkeepTbl; ELSE SET @vText = vText; - EXECUTE stmtPOVkeepTbl USING @vText, @vText; + EXECUTE stmtPOVkeepTbl USING @vText, @vText, @vText; END IF; EXECUTE stmt3; DEALLOCATE PREPARE stmt3; diff --git a/procedures/query/initEntity.sql b/procedures/query/initEntity.sql index b1f1a9d690ef5b87bbf8357a5fcbd174a3e59377..f8877303a6d47a3cbf620b45c2fd8484caa1804b 100644 --- a/procedures/query/initEntity.sql +++ b/procedures/query/initEntity.sql @@ -26,36 +26,48 @@ DELIMITER // CREATE PROCEDURE db_2_0.initEntity(in eid INT UNSIGNED, in ename VARCHAR(255), in enameLike VARCHAR(255), in enameRegexp VARCHAR(255), in resultset VARCHAR(255)) initEntityLabel: BEGIN - SET @initEntityStmtStr = NULL; + SET @initEntityStmtStr = NULL; - IF ename IS NOT NULL THEN - SET @initEntityStmtStr = CONCAT('INSERT IGNORE INTO `',resultset,'` (id) SELECT id FROM entities WHERE name=? and id>=100 UNION ALL SELECT entity_id FROM name_data WHERE value=?;'); - SET @query_param = ename; - ELSEIF enameLike IS NOT NULL THEN - SET @initEntityStmtStr = CONCAT('INSERT IGNORE INTO `',resultset,'` (id) SELECT id FROM entities WHERE name LIKE ? and id>=100 UNION ALL SELECT entity_id FROM name_data WHERE value LIKE ?;'); - SET @query_param = enameLike; - ELSEIF enameRegexp IS NOT NULL THEN - SET @initEntityStmtStr = CONCAT('INSERT IGNORE INTO `',resultset,'` (id) SELECT id FROM entities WHERE name REGEXP ? and id>=100 UNION ALL SELECT entity_id FROM name_data WHERE value REGEXP ?;'); - SET @query_param = enameRegexp; + -- Prepare a statement which resolves the name or pattern to ids. The ids + -- are collected in a temporary table (resultset). + IF ename IS NOT NULL THEN + SET @initEntityStmtStr = CONCAT( + 'INSERT IGNORE INTO `', + resultset, + '` (id) SELECT entity_id FROM name_data WHERE value=?; '); + SET @query_param = ename; + ELSEIF enameLike IS NOT NULL THEN + SET @initEntityStmtStr = CONCAT( + 'INSERT IGNORE INTO `', + resultset, + '` (id) SELECT entity_id FROM name_data WHERE value LIKE ?;'); + SET @query_param = enameLike; + ELSEIF enameRegexp IS NOT NULL THEN + SET @initEntityStmtStr = CONCAT( + 'INSERT IGNORE INTO `', + resultset, + '` (id) SELECT entity_id FROM name_data WHERE value REGEXP ?;'); + SET @query_param = enameRegexp; END IF; - IF @initEntityStmtStr IS NOT NULL THEN - PREPARE initEntityStmt FROM @initEntityStmtStr; - EXECUTE initEntityStmt USING @query_param, @query_param; - DEALLOCATE PREPARE initEntityStmt; + -- execute the statement + IF @initEntityStmtStr IS NOT NULL THEN + PREPARE initEntityStmt FROM @initEntityStmtStr; + EXECUTE initEntityStmt USING @query_param; + DEALLOCATE PREPARE initEntityStmt; END IF; - + IF eid IS NOT NULL THEN - SET @initEntityStmtStr = CONCAT('INSERT IGNORE INTO `',resultset,'` (id) SELECT id FROM entities WHERE id=',eid,';'); - PREPARE initEntityStmt FROM @initEntityStmtStr; - EXECUTE initEntityStmt; - DEALLOCATE PREPARE initEntityStmt; + SET @initEntityStmtStr = CONCAT('INSERT IGNORE INTO `',resultset,'` (id) SELECT id FROM entities WHERE id=',eid,';'); + PREPARE initEntityStmt FROM @initEntityStmtStr; + EXECUTE initEntityStmt; + DEALLOCATE PREPARE initEntityStmt; END IF; - - IF @initEntityStmtStr IS NOT NULL THEN - call getChildren(resultset); - END IF; + + IF @initEntityStmtStr IS NOT NULL THEN + call getChildren(resultset); + END IF; END; // diff --git a/procedures/query/initPOV.sql b/procedures/query/initPOV.sql index 1d7c75a7d53190511212409b68dbfd9cdbe3a192..d1fa16300e4ef36902fd702739e389d336f1b3f5 100644 --- a/procedures/query/initPOV.sql +++ b/procedures/query/initPOV.sql @@ -41,12 +41,12 @@ BEGIN IF pname is NOT NULL THEN SELECT conv( concat( substring(uid,16,3), substring(uid,10,4), substring(uid,1,8)),16,10) div 10000 - (141427 * 24 * 60 * 60 * 1000) as current_mills INTO t1 from (select uuid() uid) as alias; call createTmpTable2(propertiesTable); - - -- fill in all properties named "pname" - SET @initPOVPropertiesTableStmt1 = CONCAT('INSERT IGNORE INTO `', propertiesTable, '` (id, id2, domain) SELECT id, 0, 0 FROM entities WHERE name = ? UNION ALL SELECT property_id, entity_id, domain_id from name_overrides WHERE name = ? UNION ALL SELECT entity_id, domain_id, 0 FROM name_data WHERE value = ?;'); + + -- fill in all properties named "pname" + SET @initPOVPropertiesTableStmt1 = CONCAT('INSERT IGNORE INTO `', propertiesTable, '` (id, id2, domain) SELECT property_id, entity_id, domain_id from name_overrides WHERE name = ? UNION ALL SELECT entity_id, domain_id, 0 FROM name_data WHERE value = ?;'); PREPARE stmt FROM @initPOVPropertiesTableStmt1; SET @pname = pname; - EXECUTE stmt USING @pname, @pname, @pname; + EXECUTE stmt USING @pname, @pname; SET ecount = ROW_COUNT(); -- fill in all properties with id="pid" diff --git a/procedures/query/initSubEntity.sql b/procedures/query/initSubEntity.sql index 2c3aefb3f7061efed43a5c4d36d4e413dca97786..543a0ce0f4745561ad0fa489da580d5e30fc3267 100644 --- a/procedures/query/initSubEntity.sql +++ b/procedures/query/initSubEntity.sql @@ -37,15 +37,13 @@ BEGIN SET @stmtStr = CONCAT('INSERT IGNORE INTO `', tableName, - '` (id) SELECT id FROM entities WHERE name ', - op, - ' ? UNION ALL SELECT entity_id FROM name_data WHERE value ', + '` (id) SELECT entity_id FROM name_data WHERE value ', op, ' ? AND domain_id=0;'); PREPARE stmt FROM @stmtStr; SET @ename = ename; - EXECUTE stmt USING @ename, @ename; + EXECUTE stmt USING @ename; SET ecount = ROW_COUNT(); DEALLOCATE PREPARE stmt; diff --git a/procedures/updateEntity.sql b/procedures/updateEntity.sql index e3c21af4f3a2d95ff04b9f1092398bf6a1681e69..b067c251678017b3bba17fa5e4bf028e08d3f465 100644 --- a/procedures/updateEntity.sql +++ b/procedures/updateEntity.sql @@ -56,9 +56,9 @@ BEGIN -- move old data to archive - INSERT INTO archive_entities (id, name, description, role, + INSERT INTO archive_entities (id, description, role, acl, _iversion) - SELECT e.id, e.name, e.description, e.role, e.acl, OldIVersion + SELECT e.id, e.description, e.role, e.acl, OldIVersion FROM entities AS e WHERE e.id = EntityID; @@ -94,12 +94,15 @@ BEGIN END IF; UPDATE entities e - SET e.name = EntityName, - e.description = EntityDescription, + SET e.description = EntityDescription, e.role=EntityRole, e.acl = ACLID WHERE e.id = EntityID; + IF EntityName IS NOT NULL THEN + INSERT INTO name_data (domain_id, entity_id, property_id, value, status, pidx) VALUES (0, EntityID, 20, EntityName, "FIX", 0); + END IF; + DELETE FROM data_type WHERE domain_id=0 AND entity_id=0 AND property_id=EntityID; @@ -108,8 +111,10 @@ BEGIN IF Datatype IS NOT NULL THEN INSERT INTO data_type (domain_id, entity_id, property_id, datatype) - SELECT 0, 0, EntityID, - ( SELECT id from entities where name = Datatype LIMIT 1); + INSERT INTO data_type (domain_id, entity_id, property_id, datatype) + SELECT 0, 0, EntityID, + ( SELECT entity_id FROM name_data WHERE domain_id = 0 + AND property_id = 20 AND value = Datatype LIMIT 1 ); IF Collection IS NOT NULL THEN INSERT INTO collection_type (domain_id, entity_id, property_id, diff --git a/tests/test_autotap.sql b/tests/test_autotap.sql index e23ef3bfd40f8f27b0f8ed920e7d7c0da5a1ac52..9c09fbbccc120dda022701c0edb2ebd3ab9d4b94 100644 --- a/tests/test_autotap.sql +++ b/tests/test_autotap.sql @@ -623,7 +623,7 @@ SELECT tap.table_collation_is('_caosdb_schema_unit_tests','entities','utf8_unico SELECT tap.table_engine_is('_caosdb_schema_unit_tests','entities','InnoDB',''); -- COLUMNS -SELECT tap.columns_are('_caosdb_schema_unit_tests','entities','`id`,`name`,`description`,`role`,`acl`',''); +SELECT tap.columns_are('_caosdb_schema_unit_tests','entities','`id`,`description`,`role`,`acl`',''); -- COLUMN entities.id @@ -634,15 +634,6 @@ SELECT tap.col_default_is('_caosdb_schema_unit_tests','entities','id',NULL,''); SELECT tap.col_charset_is('_caosdb_schema_unit_tests','entities','id',NULL,''); SELECT tap.col_collation_is('_caosdb_schema_unit_tests','entities','id',NULL,''); --- COLUMN entities.name - -SELECT tap.has_column('_caosdb_schema_unit_tests','entities','name',''); -SELECT tap.col_column_type_is('_caosdb_schema_unit_tests','entities','name','varchar(255)',''); -SELECT tap.col_extra_is('_caosdb_schema_unit_tests','entities','name','',''); -SELECT tap.col_default_is('_caosdb_schema_unit_tests','entities','name','NULL',''); -SELECT tap.col_charset_is('_caosdb_schema_unit_tests','entities','name','utf8',''); -SELECT tap.col_collation_is('_caosdb_schema_unit_tests','entities','name','utf8_unicode_ci',''); - -- COLUMN entities.description SELECT tap.has_column('_caosdb_schema_unit_tests','entities','description',''); @@ -1220,7 +1211,7 @@ SELECT tap.index_is_type('_caosdb_schema_unit_tests','name_data','value','BTREE' SELECT tap.is_indexed('_caosdb_schema_unit_tests','name_data','`value`',''); -- CONSTRAINTS -SELECT tap.constraints_are('_caosdb_schema_unit_tests','name_data','`name_data_domain_id_entity`,`name_data_entity_id_entity`,`name_data_property_id_entity`',''); +SELECT tap.constraints_are('_caosdb_schema_unit_tests','name_data','`name_data_domain_id_entity`,`domain_id_2`,`name_data_entity_id_entity`,`name_data_property_id_entity`',''); -- CONSTRAINT name_data.name_data_domain_id_entity @@ -1229,6 +1220,11 @@ SELECT tap.constraint_type_is('_caosdb_schema_unit_tests','name_data','name_data SELECT tap.fk_on_delete('_caosdb_schema_unit_tests','name_data','name_data_domain_id_entity','RESTRICT',''); SELECT tap.fk_on_update('_caosdb_schema_unit_tests','name_data','name_data_domain_id_entity','RESTRICT',''); +-- CONSTRAINT name_data.domain_id_2 + +SELECT tap.has_constraint('_caosdb_schema_unit_tests','name_data','domain_id_2',''); +SELECT tap.constraint_type_is('_caosdb_schema_unit_tests','name_data','domain_id_2','UNIQUE',''); + -- CONSTRAINT name_data.name_data_entity_id_entity SELECT tap.has_constraint('_caosdb_schema_unit_tests','name_data','name_data_entity_id_entity','');