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