GEN -19341 : Improvement - Avoid usage of CONCAT in WHERE clause (#19510)

* Improvement : Avoid usage of CONCAT in WHERE clause

* fix tests

* remove usage of @BindFqnConcat

* remove usage of @BindFqnConcat

* fix test

* fix test
This commit is contained in:
sonika-shah 2025-01-27 11:20:25 +05:30 committed by GitHub
parent d386e9ca35
commit 82198f8126
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 261 additions and 75 deletions

View File

@ -153,6 +153,7 @@ import org.openmetadata.service.resources.tags.TagLabelUtil;
import org.openmetadata.service.util.EntityUtil; import org.openmetadata.service.util.EntityUtil;
import org.openmetadata.service.util.FullyQualifiedName; import org.openmetadata.service.util.FullyQualifiedName;
import org.openmetadata.service.util.JsonUtils; import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.jdbi.BindConcat;
import org.openmetadata.service.util.jdbi.BindFQN; import org.openmetadata.service.util.jdbi.BindFQN;
import org.openmetadata.service.util.jdbi.BindListFQN; import org.openmetadata.service.util.jdbi.BindListFQN;
import org.openmetadata.service.util.jdbi.BindUUID; import org.openmetadata.service.util.jdbi.BindUUID;
@ -747,20 +748,27 @@ public interface CollectionDAO {
@SqlQuery( @SqlQuery(
"SELECT id, extension, json " "SELECT id, extension, json "
+ "FROM entity_extension " + "FROM entity_extension "
+ "WHERE id IN (<ids>) AND extension LIKE CONCAT(:extensionPrefix, '.%') " + "WHERE id IN (<ids>) AND extension LIKE :extension "
+ "ORDER BY id, extension") + "ORDER BY id, extension")
@RegisterRowMapper(ExtensionRecordWithIdMapper.class) @RegisterRowMapper(ExtensionRecordWithIdMapper.class)
List<ExtensionRecordWithId> getExtensionsBatch( List<ExtensionRecordWithId> getExtensionsBatch(
@BindList("ids") List<String> ids, @Bind("extensionPrefix") String extensionPrefix); @BindList("ids") List<String> ids,
@BindConcat(
value = "extension",
parts = {":extensionPrefix", ".%"})
String extensionPrefix);
@SqlQuery( @SqlQuery(
"SELECT id, extension, json, jsonschema " "SELECT id, extension, json, jsonschema "
+ "FROM entity_extension " + "FROM entity_extension "
+ "WHERE extension LIKE CONCAT(:extensionPrefix, '%') " + "WHERE extension LIKE :extension "
+ "ORDER BY id, extension") + "ORDER BY id, extension")
@RegisterRowMapper(ExtensionWithIdAndSchemaRowMapper.class) @RegisterRowMapper(ExtensionWithIdAndSchemaRowMapper.class)
List<ExtensionWithIdAndSchemaObject> getExtensionsByPrefixBatch( List<ExtensionWithIdAndSchemaObject> getExtensionsByPrefixBatch(
@Bind("extensionPrefix") String extensionPrefix); @BindConcat(
value = "extension",
parts = {":extensionPrefix", "%"})
String extensionPrefix);
@Transaction @Transaction
@ConnectionAwareSqlBatch( @ConnectionAwareSqlBatch(
@ -1492,16 +1500,25 @@ public interface CollectionDAO {
@SqlQuery( @SqlQuery(
"SELECT json FROM thread_entity <condition> " "SELECT json FROM thread_entity <condition> "
+ "AND hash_id in (SELECT fromFQNHash FROM field_relationship WHERE " + "AND hash_id in (SELECT fromFQNHash FROM field_relationship WHERE "
+ "(:fqnPrefixHash IS NULL OR toFQNHash LIKE CONCAT(:fqnPrefixHash, '.%') OR toFQNHash=:fqnPrefixHash) AND fromType='THREAD' AND " + "(:fqnPrefixHash IS NULL OR toFQNHash LIKE :concatFqnPrefixHash OR toFQNHash=:fqnPrefixHash) AND fromType='THREAD' AND "
+ "(:toType IS NULL OR toType LIKE CONCAT(:toType, '.%') OR toType=:toType) AND relation= :relation) " + "(:toType IS NULL OR toType LIKE :concatToType OR toType=:toType) AND relation= :relation) "
+ "AND (:userName IS NULL OR MD5(id) in (SELECT toFQNHash FROM field_relationship WHERE " + "AND (:userName IS NULL OR MD5(id) in (SELECT toFQNHash FROM field_relationship WHERE "
+ " ((fromType='user' AND fromFQNHash= :userName) OR" + " ((fromType='user' AND fromFQNHash= :userName) OR"
+ " (fromType='team' AND fromFQNHash IN (<teamNames>))) AND toType='THREAD' AND relation= :filterRelation) )" + " (fromType='team' AND fromFQNHash IN (<teamNames>))) AND toType='THREAD' AND relation= :filterRelation) )"
+ "<sortingOrder> " + "<sortingOrder> "
+ "LIMIT :limit") + "LIMIT :limit")
List<String> listThreadsByEntityLink( List<String> listThreadsByEntityLink(
@BindFQN("fqnPrefixHash") String fqnPrefixHash, @BindConcat(
@Bind("toType") String toType, value = "concatFqnPrefixHash",
original = "fqnPrefixHash",
parts = {":fqnPrefixHash", ".%"},
hash = true)
String fqnPrefixHash,
@BindConcat(
value = "concatToType",
original = "toType",
parts = {":toType", ".%"})
String toType,
@Bind("limit") int limit, @Bind("limit") int limit,
@Bind("relation") int relation, @Bind("relation") int relation,
@BindFQN("userName") String userName, @BindFQN("userName") String userName,
@ -1533,14 +1550,23 @@ public interface CollectionDAO {
@SqlQuery( @SqlQuery(
"SELECT count(id) FROM thread_entity <condition> " "SELECT count(id) FROM thread_entity <condition> "
+ "AND hash_id in (SELECT fromFQNHash FROM field_relationship WHERE " + "AND hash_id in (SELECT fromFQNHash FROM field_relationship WHERE "
+ "(:fqnPrefixHash IS NULL OR toFQNHash LIKE CONCAT(:fqnPrefixHash, '.%') OR toFQNHash=:fqnPrefixHash) AND fromType='THREAD' AND " + "(:fqnPrefixHash IS NULL OR toFQNHash LIKE :concatFqnPrefixHash OR toFQNHash=:fqnPrefixHash) AND fromType='THREAD' AND "
+ "(:toType IS NULL OR toType LIKE CONCAT(:toType, '.%') OR toType=:toType) AND relation= :relation) " + "(:toType IS NULL OR toType LIKE :concatToType OR toType=:toType) AND relation= :relation) "
+ "AND (:userName IS NULL OR id in (SELECT toFQNHash FROM field_relationship WHERE " + "AND (:userName IS NULL OR id in (SELECT toFQNHash FROM field_relationship WHERE "
+ " ((fromType='user' AND fromFQNHash= :userName) OR" + " ((fromType='user' AND fromFQNHash= :userName) OR"
+ " (fromType='team' AND fromFQNHash IN (<teamNames>))) AND toType='THREAD' AND relation= :filterRelation) )") + " (fromType='team' AND fromFQNHash IN (<teamNames>))) AND toType='THREAD' AND relation= :filterRelation) )")
int listCountThreadsByEntityLink( int listCountThreadsByEntityLink(
@BindFQN("fqnPrefixHash") String fqnPrefixHash, @BindConcat(
@Bind("toType") String toType, value = "concatFqnPrefixHash",
original = "fqnPrefixHash",
parts = {":fqnPrefixHash", ".%"},
hash = true)
String fqnPrefixHash,
@BindConcat(
value = "concatToType",
original = "toType",
parts = {":toType", ".%"})
String toType,
@Bind("relation") int relation, @Bind("relation") int relation,
@Bind("userName") String userName, @Bind("userName") String userName,
@BindList("teamNames") List<String> teamNames, @BindList("teamNames") List<String> teamNames,
@ -1562,9 +1588,9 @@ public interface CollectionDAO {
+ " WHERE hash_id IN ( " + " WHERE hash_id IN ( "
+ " SELECT fromFQNHash FROM field_relationship " + " SELECT fromFQNHash FROM field_relationship "
+ " WHERE " + " WHERE "
+ " (:fqnPrefixHash IS NULL OR toFQNHash LIKE CONCAT(:fqnPrefixHash, '.%') OR toFQNHash = :fqnPrefixHash) " + " (:fqnPrefixHash IS NULL OR toFQNHash LIKE :concatFqnPrefixHash OR toFQNHash = :fqnPrefixHash) "
+ " AND fromType = 'THREAD' " + " AND fromType = 'THREAD' "
+ " AND (:toType IS NULL OR toType LIKE CONCAT(:toType, '.%') OR toType = :toType) " + " AND (:toType IS NULL OR toType LIKE :concatToType OR toType = :toType) "
+ " AND relation = 3 " + " AND relation = 3 "
+ " ) " + " ) "
+ " UNION " + " UNION "
@ -1576,8 +1602,17 @@ public interface CollectionDAO {
@RegisterRowMapper(ThreadCountFieldMapper.class) @RegisterRowMapper(ThreadCountFieldMapper.class)
List<List<String>> listCountByEntityLink( List<List<String>> listCountByEntityLink(
@BindUUID("entityId") UUID entityId, @BindUUID("entityId") UUID entityId,
@BindFQN("fqnPrefixHash") String fqnPrefixHash, @BindConcat(
@Bind("toType") String toType); value = "concatFqnPrefixHash",
original = "fqnPrefixHash",
parts = {":fqnPrefixHash", ".%"},
hash = true)
String fqnPrefixHash,
@BindConcat(
value = "concatToType",
original = "toType",
parts = {":toType", ".%"})
String toType);
@ConnectionAwareSqlQuery( @ConnectionAwareSqlQuery(
value = value =
@ -1790,18 +1825,31 @@ public interface CollectionDAO {
@SqlQuery( @SqlQuery(
"SELECT json FROM thread_entity <condition> " "SELECT json FROM thread_entity <condition> "
+ "AND MD5(id) in (SELECT fromFQNHash FROM field_relationship WHERE " + "AND MD5(id) in (SELECT fromFQNHash FROM field_relationship WHERE "
+ "(:fqnPrefixHash IS NULL OR toFQNHash LIKE CONCAT(:fqnPrefixHash, '.%') OR toFQNHash=:fqnPrefixHash) AND fromType='THREAD' AND " + "(:fqnPrefixHash IS NULL OR toFQNHash LIKE :concatFqnPrefixHash OR toFQNHash=:fqnPrefixHash) AND fromType='THREAD' AND "
+ "((:toType1 IS NULL OR toType LIKE CONCAT(:toType1, '.%') OR toType=:toType1) OR " + "((:toType1 IS NULL OR toType LIKE :concatToType1 OR toType=:toType1) OR "
+ "(:toType2 IS NULL OR toType LIKE CONCAT(:toType2, '.%') OR toType=:toType2)) AND relation= :relation)" + "(:toType2 IS NULL OR toType LIKE :concatToType2 OR toType=:toType2)) AND relation= :relation)"
+ "AND (:userName IS NULL OR MD5(id) in (SELECT toFQNHash FROM field_relationship WHERE " + "AND (:userName IS NULL OR MD5(id) in (SELECT toFQNHash FROM field_relationship WHERE "
+ " ((fromType='user' AND fromFQNHash= :userName) OR" + " ((fromType='user' AND fromFQNHash= :userName) OR"
+ " (fromType='team' AND fromFQNHash IN (<teamNames>))) AND toType='THREAD' AND relation= :filterRelation) )" + " (fromType='team' AND fromFQNHash IN (<teamNames>))) AND toType='THREAD' AND relation= :filterRelation) )"
+ "<sortingOrder> " + "<sortingOrder> "
+ "LIMIT :limit") + "LIMIT :limit")
List<String> listThreadsByGlossaryAndTerms( List<String> listThreadsByGlossaryAndTerms(
@BindFQN("fqnPrefixHash") String fqnPrefixHash, @BindConcat(
@Bind("toType1") String toType1, value = "concatFqnPrefixHash",
@Bind("toType2") String toType2, original = "fqnPrefixHash",
parts = {":fqnPrefixHash", ".%"},
hash = true)
String fqnPrefixHash,
@BindConcat(
value = "concatToType1",
original = "toType1",
parts = {":toType1", ".%"})
String toType1,
@BindConcat(
value = "concatToType2",
original = "toType2",
parts = {":toType2", ".%"})
String toType2,
@Bind("limit") int limit, @Bind("limit") int limit,
@Bind("relation") int relation, @Bind("relation") int relation,
@BindFQN("userName") String userName, @BindFQN("userName") String userName,
@ -1833,9 +1881,9 @@ public interface CollectionDAO {
+ " WHERE te.hash_id IN ( " + " WHERE te.hash_id IN ( "
+ " SELECT fr.fromFQNHash " + " SELECT fr.fromFQNHash "
+ " FROM field_relationship fr " + " FROM field_relationship fr "
+ " WHERE (:fqnPrefixHash IS NULL OR fr.toFQNHash LIKE CONCAT(:fqnPrefixHash, '.%') OR fr.toFQNHash = :fqnPrefixHash) " + " WHERE (:fqnPrefixHash IS NULL OR fr.toFQNHash LIKE :concatFqnPrefixHash OR fr.toFQNHash = :fqnPrefixHash) "
+ " AND fr.fromType = 'THREAD' " + " AND fr.fromType = 'THREAD' "
+ " AND (:toType1 IS NULL OR fr.toType LIKE CONCAT(:toType1, '.%') OR fr.toType = :toType1) " + " AND (:toType1 IS NULL OR fr.toType LIKE :concatToType1 OR fr.toType = :toType1) "
+ " AND fr.relation = 3 " + " AND fr.relation = 3 "
+ " ) " + " ) "
+ " UNION " + " UNION "
@ -1846,9 +1894,9 @@ public interface CollectionDAO {
+ " SELECT fr.fromFQNHash " + " SELECT fr.fromFQNHash "
+ " FROM field_relationship fr " + " FROM field_relationship fr "
+ " JOIN thread_entity te2 ON te2.hash_id = fr.fromFQNHash WHERE fr.fromFQNHash = te.hash_id AND te2.type = 'Task' " + " JOIN thread_entity te2 ON te2.hash_id = fr.fromFQNHash WHERE fr.fromFQNHash = te.hash_id AND te2.type = 'Task' "
+ " AND (:fqnPrefixHash IS NULL OR fr.toFQNHash LIKE CONCAT(:fqnPrefixHash, '.%') OR fr.toFQNHash = :fqnPrefixHash) " + " AND (:fqnPrefixHash IS NULL OR fr.toFQNHash LIKE :concatFqnPrefixHash OR fr.toFQNHash = :fqnPrefixHash) "
+ " AND fr.fromType = 'THREAD' " + " AND fr.fromType = 'THREAD' "
+ " AND (:toType2 IS NULL OR fr.toType LIKE CONCAT(:toType2, '.%') OR fr.toType = :toType2) " + " AND (:toType2 IS NULL OR fr.toType LIKE :concatToType2 OR fr.toType = :toType2) "
+ " AND fr.relation = 3 " + " AND fr.relation = 3 "
+ " ) " + " ) "
+ ") AS combined_results WHERE combined_results.type is not NULL " + ") AS combined_results WHERE combined_results.type is not NULL "
@ -1856,9 +1904,22 @@ public interface CollectionDAO {
@RegisterRowMapper(ThreadCountFieldMapper.class) @RegisterRowMapper(ThreadCountFieldMapper.class)
List<List<String>> listCountThreadsByGlossaryAndTerms( List<List<String>> listCountThreadsByGlossaryAndTerms(
@BindUUID("entityId") UUID entityId, @BindUUID("entityId") UUID entityId,
@BindFQN("fqnPrefixHash") String fqnPrefixHash, @BindConcat(
@Bind("toType1") String toType1, value = "concatFqnPrefixHash",
@Bind("toType2") String toType2); original = "fqnPrefixHash",
parts = {":fqnPrefixHash", ".%"},
hash = true)
String fqnPrefixHash,
@BindConcat(
value = "concatToType1",
original = "toType1",
parts = {":toType1", ".%"})
String toType1,
@BindConcat(
value = "concatToType2",
original = "toType2",
parts = {":toType2", ".%"})
String toType2);
@SqlQuery("select id from thread_entity where entityId = :entityId") @SqlQuery("select id from thread_entity where entityId = :entityId")
List<String> findByEntityId(@Bind("entityId") String entityId); List<String> findByEntityId(@Bind("entityId") String entityId);
@ -1963,11 +2024,15 @@ public interface CollectionDAO {
@SqlQuery( @SqlQuery(
"SELECT fromFQN, toFQN, json FROM field_relationship WHERE " "SELECT fromFQN, toFQN, json FROM field_relationship WHERE "
+ "fromFQNHash LIKE CONCAT(:fqnPrefixHash, '%') AND fromType = :fromType AND toType = :toType " + "fromFQNHash LIKE :concatFqnPrefixHash AND fromType = :fromType AND toType = :toType "
+ "AND relation = :relation") + "AND relation = :relation")
@RegisterRowMapper(ToFieldMapper.class) @RegisterRowMapper(ToFieldMapper.class)
List<Triple<String, String, String>> listToByPrefix( List<Triple<String, String, String>> listToByPrefix(
@BindFQN("fqnPrefixHash") String fqnPrefixHash, @BindConcat(
value = "concatFqnPrefixHash",
parts = {":fqnPrefixHash", "%"},
hash = true)
String fqnPrefixHash,
@Bind("fromType") String fromType, @Bind("fromType") String fromType,
@Bind("toType") String toType, @Bind("toType") String toType,
@Bind("relation") int relation); @Bind("relation") int relation);
@ -1993,13 +2058,17 @@ public interface CollectionDAO {
@SqlQuery( @SqlQuery(
"SELECT fromFQN, toFQN, json FROM field_relationship WHERE " "SELECT fromFQN, toFQN, json FROM field_relationship WHERE "
+ "fromFQNHash LIKE CONCAT(:fqnPrefixHash, '%') AND fromType = :type AND toType = :otherType AND relation = :relation " + "fromFQNHash LIKE :concatFqnPrefixHash AND fromType = :type AND toType = :otherType AND relation = :relation "
+ "UNION " + "UNION "
+ "SELECT toFQN, fromFQN, json FROM field_relationship WHERE " + "SELECT toFQN, fromFQN, json FROM field_relationship WHERE "
+ "toFQNHash LIKE CONCAT(:fqnPrefixHash, '%') AND toType = :type AND fromType = :otherType AND relation = :relation") + "toFQNHash LIKE :concatFqnPrefixHash AND toType = :type AND fromType = :otherType AND relation = :relation")
@RegisterRowMapper(ToFieldMapper.class) @RegisterRowMapper(ToFieldMapper.class)
List<Triple<String, String, String>> listBidirectionalByPrefix( List<Triple<String, String, String>> listBidirectionalByPrefix(
@BindFQN("fqnPrefixHash") String fqnPrefixHash, @BindConcat(
value = "concatFqnPrefixHash",
parts = {":fqnPrefixHash", "%"},
hash = true)
String fqnPrefixHash,
@Bind("type") String type, @Bind("type") String type,
@Bind("otherType") String otherType, @Bind("otherType") String otherType,
@Bind("relation") int relation); @Bind("relation") int relation);
@ -2491,12 +2560,12 @@ public interface CollectionDAO {
String directChildrenOf = filter.getQueryParam("directChildrenOf"); String directChildrenOf = filter.getQueryParam("directChildrenOf");
if (!nullOrEmpty(directChildrenOf)) { if (!nullOrEmpty(directChildrenOf)) {
filter.queryParams.put( String parentFqnHash = FullyQualifiedName.buildHash(directChildrenOf);
"directChildrenOfHash", FullyQualifiedName.buildHash(directChildrenOf)); filter.queryParams.put("fqnHashSingleLevel", parentFqnHash + ".%");
condition = filter.queryParams.put("fqnHashNestedLevel", parentFqnHash + ".%.%");
String.format(
" %s AND fqnHash = CONCAT(:directChildrenOfHash, '.', MD5(CASE WHEN name LIKE '%%.%%' THEN CONCAT('\"', name, '\"') ELSE name END)) ", condition +=
condition); " AND fqnHash LIKE :fqnHashSingleLevel AND fqnHash NOT LIKE :fqnHashNestedLevel";
} }
return listCount(getTableName(), getNameHashColumn(), filter.getQueryParams(), condition); return listCount(getTableName(), getNameHashColumn(), filter.getQueryParams(), condition);
@ -2509,12 +2578,12 @@ public interface CollectionDAO {
String directChildrenOf = filter.getQueryParam("directChildrenOf"); String directChildrenOf = filter.getQueryParam("directChildrenOf");
if (!nullOrEmpty(directChildrenOf)) { if (!nullOrEmpty(directChildrenOf)) {
filter.queryParams.put( String parentFqnHash = FullyQualifiedName.buildHash(directChildrenOf);
"directChildrenOfHash", FullyQualifiedName.buildHash(directChildrenOf)); filter.queryParams.put("fqnHashSingleLevel", parentFqnHash + ".%");
condition = filter.queryParams.put("fqnHashNestedLevel", parentFqnHash + ".%.%");
String.format(
" %s AND fqnHash = CONCAT(:directChildrenOfHash, '.', MD5(CASE WHEN name LIKE '%%.%%' THEN CONCAT('\"', name, '\"') ELSE name END)) ", condition +=
condition); " AND fqnHash LIKE :fqnHashSingleLevel AND fqnHash NOT LIKE :fqnHashNestedLevel";
} }
return listBefore( return listBefore(
@ -2527,19 +2596,24 @@ public interface CollectionDAO {
String directChildrenOf = filter.getQueryParam("directChildrenOf"); String directChildrenOf = filter.getQueryParam("directChildrenOf");
if (!nullOrEmpty(directChildrenOf)) { if (!nullOrEmpty(directChildrenOf)) {
filter.queryParams.put( String parentFqnHash = FullyQualifiedName.buildHash(directChildrenOf);
"directChildrenOfHash", FullyQualifiedName.buildHash(directChildrenOf)); filter.queryParams.put("fqnHashSingleLevel", parentFqnHash + ".%");
condition = filter.queryParams.put("fqnHashNestedLevel", parentFqnHash + ".%.%");
String.format(
" %s AND fqnHash = CONCAT(:directChildrenOfHash, '.', MD5(CASE WHEN name LIKE '%%.%%' THEN CONCAT('\"', name, '\"') ELSE name END)) ", condition +=
condition); " AND fqnHash LIKE :fqnHashSingleLevel AND fqnHash NOT LIKE :fqnHashNestedLevel";
} }
return listAfter( return listAfter(
getTableName(), filter.getQueryParams(), condition, limit, afterName, afterId); getTableName(), filter.getQueryParams(), condition, limit, afterName, afterId);
} }
@SqlQuery("select json FROM glossary_term_entity where fqnhash LIKE CONCAT(:fqnhash, '.%')") @SqlQuery("select json FROM glossary_term_entity where fqnhash LIKE :concatFqnhash ")
List<String> getNestedTerms(@BindFQN("fqnhash") String fqnhash); List<String> getNestedTerms(
@BindConcat(
value = "concatFqnhash",
parts = {":fqnhash", ".%"},
hash = true)
String fqnhash);
} }
interface IngestionPipelineDAO extends EntityDAO<IngestionPipeline> { interface IngestionPipelineDAO extends EntityDAO<IngestionPipeline> {
@ -3170,8 +3244,13 @@ public interface CollectionDAO {
afterId); afterId);
} }
@SqlQuery("select json FROM tag where fqnhash LIKE CONCAT(:fqnhash, '.%')") @SqlQuery("select json FROM tag where fqnhash LIKE :concatFqnhash")
List<String> getTagsStartingWithPrefix(@BindFQN("fqnhash") String fqnhash); List<String> getTagsStartingWithPrefix(
@BindConcat(
value = "concatFqnhash",
parts = {":fqnhash", ".%"},
hash = true)
String fqnhash);
} }
@RegisterRowMapper(TagLabelMapper.class) @RegisterRowMapper(TagLabelMapper.class)
@ -3200,10 +3279,11 @@ public interface CollectionDAO {
default Map<String, List<TagLabel>> getTagsByPrefix( default Map<String, List<TagLabel>> getTagsByPrefix(
String targetFQNPrefix, String postfix, boolean requiresFqnHash) { String targetFQNPrefix, String postfix, boolean requiresFqnHash) {
String fqnHash = String targetFQNPrefixHash =
requiresFqnHash ? FullyQualifiedName.buildHash(targetFQNPrefix) : targetFQNPrefix; requiresFqnHash ? FullyQualifiedName.buildHash(targetFQNPrefix) : targetFQNPrefix;
Map<String, List<TagLabel>> resultSet = new LinkedHashMap<>(); Map<String, List<TagLabel>> resultSet = new LinkedHashMap<>();
List<Pair<String, TagLabel>> tags = getTagsInternalByPrefix(fqnHash, postfix); List<Pair<String, TagLabel>> tags =
getTagsInternalByPrefix(new String[] {targetFQNPrefixHash, postfix});
tags.forEach( tags.forEach(
pair -> { pair -> {
String targetHash = pair.getLeft(); String targetHash = pair.getLeft();
@ -3249,7 +3329,7 @@ public interface CollectionDAO {
+ " ON ta.fqnHash = tu.tagFQNHash " + " ON ta.fqnHash = tu.tagFQNHash "
+ " WHERE tu.source = 0 " + " WHERE tu.source = 0 "
+ ") AS combined_data " + ") AS combined_data "
+ "WHERE combined_data.targetFQNHash LIKE CONCAT(:targetFQNHashPrefix, :postfix)", + "WHERE combined_data.targetFQNHash LIKE :targetFQNHash",
connectionType = MYSQL) connectionType = MYSQL)
@ConnectionAwareSqlQuery( @ConnectionAwareSqlQuery(
value = value =
@ -3265,11 +3345,14 @@ public interface CollectionDAO {
+ " JOIN tag_usage AS tu ON ta.fqnHash = tu.tagFQNHash " + " JOIN tag_usage AS tu ON ta.fqnHash = tu.tagFQNHash "
+ " WHERE tu.source = 0 " + " WHERE tu.source = 0 "
+ ") AS combined_data " + ") AS combined_data "
+ "WHERE combined_data.targetFQNHash LIKE CONCAT(:targetFQNHashPrefix, :postfix)", + "WHERE combined_data.targetFQNHash LIKE :targetFQNHash",
connectionType = POSTGRES) connectionType = POSTGRES)
@RegisterRowMapper(TagLabelRowMapperWithTargetFqnHash.class) @RegisterRowMapper(TagLabelRowMapperWithTargetFqnHash.class)
List<Pair<String, TagLabel>> getTagsInternalByPrefix( List<Pair<String, TagLabel>> getTagsInternalByPrefix(
@Bind("targetFQNHashPrefix") String targetFQNHashPrefix, @Bind("postfix") String postfix); @BindConcat(
value = "targetFQNHash",
parts = {":targetFQNHashPrefix", ":postfix"})
String... targetFQNHash);
@SqlQuery("SELECT * FROM tag_usage") @SqlQuery("SELECT * FROM tag_usage")
@Deprecated(since = "Release 1.1") @Deprecated(since = "Release 1.1")
@ -3278,17 +3361,29 @@ public interface CollectionDAO {
@SqlQuery( @SqlQuery(
"SELECT COUNT(*) FROM tag_usage " "SELECT COUNT(*) FROM tag_usage "
+ "WHERE (tagFQNHash LIKE CONCAT(:tagFqnHash, '.%') OR tagFQNHash = :tagFqnHash) " + "WHERE (tagFQNHash LIKE :concatTagFQNHash OR tagFQNHash = :tagFqnHash) "
+ "AND source = :source") + "AND source = :source")
int getTagCount(@Bind("source") int source, @BindFQN("tagFqnHash") String tagFqnHash); int getTagCount(
@Bind("source") int source,
@BindConcat(
value = "concatTagFQNHash",
original = "tagFqnHash",
parts = {":tagFqnHash", ".%"},
hash = true)
String tagFqnHash);
@SqlUpdate("DELETE FROM tag_usage where targetFQNHash = :targetFQNHash") @SqlUpdate("DELETE FROM tag_usage where targetFQNHash = :targetFQNHash")
void deleteTagsByTarget(@BindFQN("targetFQNHash") String targetFQNHash); void deleteTagsByTarget(@BindFQN("targetFQNHash") String targetFQNHash);
@SqlUpdate( @SqlUpdate(
"DELETE FROM tag_usage where tagFQNHash = :tagFqnHash AND targetFQNHash LIKE CONCAT(:targetFQNHash, '%')") "DELETE FROM tag_usage where tagFQNHash = :tagFqnHash AND targetFQNHash LIKE :targetFQNHash")
void deleteTagsByTagAndTargetEntity( void deleteTagsByTagAndTargetEntity(
@BindFQN("tagFqnHash") String tagFqnHash, @BindFQN("targetFQNHash") String targetFQNHash); @BindFQN("tagFqnHash") String tagFqnHash,
@BindConcat(
value = "targetFQNHash",
parts = {":targetFQNHashPrefix", "%"},
hash = true)
String targetFQNHashPrefix);
@SqlUpdate("DELETE FROM tag_usage where tagFQNHash = :tagFQNHash AND source = :source") @SqlUpdate("DELETE FROM tag_usage where tagFQNHash = :tagFQNHash AND source = :source")
void deleteTagLabels(@Bind("source") int source, @BindFQN("tagFQNHash") String tagFQNHash); void deleteTagLabels(@Bind("source") int source, @BindFQN("tagFQNHash") String tagFQNHash);
@ -3297,8 +3392,14 @@ public interface CollectionDAO {
void deleteTagLabelsByFqn(@BindFQN("tagFQNHash") String tagFQNHash); void deleteTagLabelsByFqn(@BindFQN("tagFQNHash") String tagFQNHash);
@SqlUpdate( @SqlUpdate(
"DELETE FROM tag_usage where targetFQNHash = :targetFQNHash OR targetFQNHash LIKE CONCAT(:targetFQNHash, '.%')") "DELETE FROM tag_usage where targetFQNHash = :targetFQNHash OR targetFQNHash LIKE :concatTargetFQNHash")
void deleteTagLabelsByTargetPrefix(@BindFQN("targetFQNHash") String targetFQNHash); void deleteTagLabelsByTargetPrefix(
@BindConcat(
value = "concatTargetFQNHash",
original = "targetFQNHash",
parts = {":targetFQNHashPrefix", ".%"},
hash = true)
String targetFQNHashPrefix);
@Deprecated(since = "Release 1.1") @Deprecated(since = "Release 1.1")
@ConnectionAwareSqlUpdate( @ConnectionAwareSqlUpdate(
@ -3380,9 +3481,14 @@ public interface CollectionDAO {
@RegisterRowMapper(TagLabelMapper.class) @RegisterRowMapper(TagLabelMapper.class)
List<String> getTargetFQNHashForTag(@BindFQN("tagFQNHash") String tagFQNHash); List<String> getTargetFQNHashForTag(@BindFQN("tagFQNHash") String tagFQNHash);
@SqlQuery("select targetFQNHash FROM tag_usage where tagFQNHash LIKE CONCAT(:tagFQNHash, '.%')") @SqlQuery("select targetFQNHash FROM tag_usage where tagFQNHash LIKE :tagFQNHash")
@RegisterRowMapper(TagLabelMapper.class) @RegisterRowMapper(TagLabelMapper.class)
List<String> getTargetFQNHashForTagPrefix(@BindFQN("tagFQNHash") String tagFQNHash); List<String> getTargetFQNHashForTagPrefix(
@BindConcat(
value = "tagFQNHash",
parts = {":tagFQNHashPrefix", ".%"},
hash = true)
String tagFQNHashPrefix);
class TagLabelMapper implements RowMapper<TagLabel> { class TagLabelMapper implements RowMapper<TagLabel> {
@Override @Override
@ -5586,8 +5692,8 @@ public interface CollectionDAO {
if (fqnPrefix != null) { if (fqnPrefix != null) {
String fqnPrefixHash = FullyQualifiedName.buildHash(fqnPrefix); String fqnPrefixHash = FullyQualifiedName.buildHash(fqnPrefix);
filter.queryParams.put("fqnPrefixHash", fqnPrefixHash); filter.queryParams.put("fqnPrefixHash", fqnPrefixHash);
String fqnCond = filter.queryParams.put("concatFqnPrefixHash", fqnPrefixHash + ".%");
" AND (fqnHash LIKE CONCAT(:fqnPrefixHash, '.%') OR fqnHash=:fqnPrefixHash)"; String fqnCond = " AND (fqnHash LIKE :concatFqnPrefixHash OR fqnHash=:fqnPrefixHash)";
mysqlCondition.append(fqnCond); mysqlCondition.append(fqnCond);
psqlCondition.append(fqnCond); psqlCondition.append(fqnCond);
} }
@ -5625,8 +5731,8 @@ public interface CollectionDAO {
if (fqnPrefix != null) { if (fqnPrefix != null) {
String fqnPrefixHash = FullyQualifiedName.buildHash(fqnPrefix); String fqnPrefixHash = FullyQualifiedName.buildHash(fqnPrefix);
filter.queryParams.put("fqnPrefixHash", fqnPrefixHash); filter.queryParams.put("fqnPrefixHash", fqnPrefixHash);
String fqnCond = filter.queryParams.put("concatFqnPrefixHash", fqnPrefixHash + ".%");
" AND (fqnHash LIKE CONCAT(:fqnPrefixHash, '.%') OR fqnHash=:fqnPrefixHash)"; String fqnCond = " AND (fqnHash LIKE :concatFqnPrefixHash OR fqnHash=:fqnPrefixHash)";
mysqlCondition.append(fqnCond); mysqlCondition.append(fqnCond);
psqlCondition.append(fqnCond); psqlCondition.append(fqnCond);
} }
@ -5663,8 +5769,8 @@ public interface CollectionDAO {
if (fqnPrefix != null) { if (fqnPrefix != null) {
String fqnPrefixHash = FullyQualifiedName.buildHash(fqnPrefix); String fqnPrefixHash = FullyQualifiedName.buildHash(fqnPrefix);
filter.queryParams.put("fqnPrefixHash", fqnPrefixHash); filter.queryParams.put("fqnPrefixHash", fqnPrefixHash);
String fqnCond = filter.queryParams.put("concatFqnPrefixHash", fqnPrefixHash + ".%");
" AND (fqnHash LIKE CONCAT(:fqnPrefixHash, '.%') OR fqnHash=:fqnPrefixHash)"; String fqnCond = " AND (fqnHash LIKE :concatFqnPrefixHash OR fqnHash=:fqnPrefixHash)";
mysqlCondition.append(fqnCond); mysqlCondition.append(fqnCond);
psqlCondition.append(fqnCond); psqlCondition.append(fqnCond);
} }

View File

@ -0,0 +1,80 @@
package org.openmetadata.service.util.jdbi;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import org.jdbi.v3.sqlobject.customizer.SqlStatementCustomizerFactory;
import org.jdbi.v3.sqlobject.customizer.SqlStatementCustomizingAnnotation;
import org.jdbi.v3.sqlobject.customizer.SqlStatementParameterCustomizer;
import org.openmetadata.service.util.FullyQualifiedName;
/** Concatenate parts of a string to bind as a parameter, and avoid usage of CONCAT() in where clause */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@SqlStatementCustomizingAnnotation(BindConcat.Factory.class)
public @interface BindConcat {
String value(); // Name of the concatenated parameter to bind
String original() default
""; // Optional: Use when both the original and concatenated values are needed
String[] parts() default {}; // Parts to concatenate (placeholders or static values)
boolean hash() default false; // Optional: Apply FullyQualifiedName.buildHash if true
class Factory implements SqlStatementCustomizerFactory {
@Override
public SqlStatementParameterCustomizer createForParameter(
Annotation annotation,
Class<?> sqlObjectType,
Method method,
Parameter param,
int index,
Type type) {
BindConcat bind = (BindConcat) annotation;
return (stmt, arg) -> {
String[] partValues =
(arg instanceof String[]) ? (String[]) arg : new String[] {String.valueOf(arg)};
StringBuilder concatenatedResult = new StringBuilder();
boolean containsNull = false;
for (int i = 0; i < bind.parts().length; i++) {
String part = bind.parts()[i];
if (part.startsWith(":")) { // Dynamic value in argument list to replace placeholder
if (i >= partValues.length)
throw new IllegalArgumentException(
"Not enough values for placeholders in @BindConcat. Expected at least "
+ (i + 1)
+ " but got "
+ partValues.length);
String dynamicValue = partValues[i];
if (dynamicValue == null) {
containsNull = true;
break;
}
concatenatedResult.append(
bind.hash() ? FullyQualifiedName.buildHash(dynamicValue) : dynamicValue);
} else { // Static part of the string, defined directly in the annotation
concatenatedResult.append(part);
}
}
String finalValue = containsNull ? null : concatenatedResult.toString();
stmt.bind(bind.value(), finalValue);
if (!bind.original().isEmpty() && partValues.length > 0) {
String originalValue = partValues[0];
stmt.bind(
bind.original(),
bind.hash() ? FullyQualifiedName.buildHash(originalValue) : originalValue);
}
};
}
}
}