1
0
mirror of https://github.com/google/nomulus synced 2026-06-09 16:33:02 +00:00

Add SQL queries to RdapNameserverSearchAction (#987)

This has the same issue as the domain-search action where the database
ordering is not consistent between Objectify and SQL -- as a result,
there is one test that we have to duplicate in order to account for the
two sort orders.

In addition, there isn't a way to query @Convert-ed fields in Postgres
via the standard Hibernate / JPA query language, meaning we have to use
a raw Postgres query for that.
This commit is contained in:
gbrodman
2021-03-22 12:33:11 -04:00
committed by GitHub
parent 955f1b1ff8
commit 28fd425ccb
5 changed files with 198 additions and 114 deletions

View File

@@ -26,6 +26,7 @@ import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
@@ -489,40 +490,33 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
.map(VKey::from)
.collect(toImmutableSet());
} else {
// Hibernate does not allow us to query @Converted array fields directly, either
// in the CriteriaQuery or the raw text format. However, Postgres does -- so we
// use native queries to find hosts where any of the inetAddresses match.
StringBuilder queryBuilder =
new StringBuilder(
"SELECT h.repo_id FROM \"Host\" h WHERE :address = ANY(h.inet_addresses) AND "
+ "h.deletion_time = CAST(:endOfTime AS timestamptz)");
ImmutableMap.Builder<String, String> parameters =
new ImmutableMap.Builder<String, String>()
.put("address", InetAddresses.toAddrString(inetAddress))
.put("endOfTime", END_OF_TIME.toString());
if (desiredRegistrar.isPresent()) {
queryBuilder.append(" AND h.current_sponsor_registrar_id = :desiredRegistrar");
parameters.put("desiredRegistrar", desiredRegistrar.get());
}
hostKeys =
jpaTm()
.transact(
() -> {
// Hibernate does not allow us to query @Converted array fields directly, either
// in the CriteriaQuery or the raw text format. However, Postgres does -- so we
// use native queries to find hosts where any of the inetAddresses match.
javax.persistence.Query query;
if (desiredRegistrar.isPresent()) {
query =
jpaTm()
.getEntityManager()
.createNativeQuery(
"SELECT h.repo_id FROM \"Host\" h WHERE :address = "
+ "ANY(h.inet_addresses) AND "
+ "h.current_sponsor_registrar_id = :desiredRegistrar AND "
+ "h.deletion_time = CAST(:endOfTime AS timestamptz)")
.setParameter("desiredRegistrar", desiredRegistrar.get());
} else {
query =
jpaTm()
.getEntityManager()
.createNativeQuery(
"SELECT h.repo_id FROM \"Host\" h WHERE :address = "
+ "ANY(h.inet_addresses) AND "
+ "h.deletion_time = CAST(:endOfTime AS timestamptz)");
}
javax.persistence.Query query =
jpaTm()
.getEntityManager()
.createNativeQuery(queryBuilder.toString())
.setMaxResults(maxNameserversInFirstStage);
parameters.build().forEach(query::setParameter);
@SuppressWarnings("unchecked")
Stream<String> resultStream =
query
.setParameter("address", InetAddresses.toAddrString(inetAddress))
.setParameter("endOfTime", END_OF_TIME.toString())
.setMaxResults(maxNameserversInFirstStage)
.getResultStream();
Stream<String> resultStream = query.getResultStream();
return resultStream
.map(repoId -> VKey.create(HostResource.class, repoId))
.collect(toImmutableSet());

View File

@@ -15,9 +15,12 @@
package google.registry.rdap;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.HEAD;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.net.InetAddresses;
@@ -25,6 +28,7 @@ import com.google.common.primitives.Booleans;
import com.googlecode.objectify.cmd.Query;
import google.registry.model.domain.DomainBase;
import google.registry.model.host.HostResource;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapMetrics.SearchType;
@@ -216,33 +220,90 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
private NameserverSearchResponse searchByNameUsingPrefix(RdapSearchPattern partialStringQuery) {
// Add 1 so we can detect truncation.
int querySizeLimit = getStandardQuerySizeLimit();
Query<HostResource> query =
queryItems(
HostResource.class,
"fullyQualifiedHostName",
partialStringQuery,
cursorString,
getDeletedItemHandling(),
querySizeLimit);
return makeSearchResults(
getMatchingResources(query, shouldIncludeDeleted(), querySizeLimit), CursorType.NAME);
if (isDatastore()) {
Query<HostResource> query =
queryItems(
HostResource.class,
"fullyQualifiedHostName",
partialStringQuery,
cursorString,
getDeletedItemHandling(),
querySizeLimit);
return makeSearchResults(
getMatchingResources(query, shouldIncludeDeleted(), querySizeLimit), CursorType.NAME);
} else {
return jpaTm()
.transact(
() -> {
CriteriaQueryBuilder<HostResource> queryBuilder =
queryItemsSql(
HostResource.class,
"fullyQualifiedHostName",
partialStringQuery,
cursorString,
getDeletedItemHandling());
return makeSearchResults(
getMatchingResourcesSql(queryBuilder, shouldIncludeDeleted(), querySizeLimit),
CursorType.NAME);
});
}
}
/** Searches for nameservers by IP address, returning a JSON array of nameserver info maps. */
private NameserverSearchResponse searchByIp(InetAddress inetAddress) {
// Add 1 so we can detect truncation.
int querySizeLimit = getStandardQuerySizeLimit();
Query<HostResource> query =
queryItems(
HostResource.class,
"inetAddresses",
inetAddress.getHostAddress(),
Optional.empty(),
cursorString,
getDeletedItemHandling(),
querySizeLimit);
return makeSearchResults(
getMatchingResources(query, shouldIncludeDeleted(), querySizeLimit), CursorType.ADDRESS);
RdapResultSet<HostResource> rdapResultSet;
if (isDatastore()) {
Query<HostResource> query =
queryItems(
HostResource.class,
"inetAddresses",
inetAddress.getHostAddress(),
Optional.empty(),
cursorString,
getDeletedItemHandling(),
querySizeLimit);
rdapResultSet = getMatchingResources(query, shouldIncludeDeleted(), querySizeLimit);
} else {
// Hibernate does not allow us to query @Converted array fields directly, either in the
// CriteriaQuery or the raw text format. However, Postgres does -- so we use native queries to
// find hosts where any of the inetAddresses match.
StringBuilder queryBuilder =
new StringBuilder("SELECT * FROM \"Host\" WHERE :address = ANY(inet_addresses)");
ImmutableMap.Builder<String, String> parameters =
new ImmutableMap.Builder<String, String>()
.put("address", InetAddresses.toAddrString(inetAddress));
if (getDeletedItemHandling().equals(DeletedItemHandling.EXCLUDE)) {
queryBuilder.append(" AND deletion_time = CAST(:endOfTime AS timestamptz)");
parameters.put("endOfTime", END_OF_TIME.toString());
}
if (cursorString.isPresent()) {
// cursorString here must be the repo ID
queryBuilder.append(" AND repo_id > :repoId");
parameters.put("repoId", cursorString.get());
}
if (getDesiredRegistrar().isPresent()) {
queryBuilder.append(" AND current_sponsor_registrar_id = :desiredRegistrar");
parameters.put("desiredRegistrar", getDesiredRegistrar().get());
}
queryBuilder.append(" ORDER BY repo_id ASC");
rdapResultSet =
jpaTm()
.transact(
() -> {
javax.persistence.Query query =
jpaTm()
.getEntityManager()
.createNativeQuery(queryBuilder.toString(), HostResource.class)
.setMaxResults(querySizeLimit);
parameters.build().forEach(query::setParameter);
@SuppressWarnings("unchecked")
List<HostResource> resultList = query.getResultList();
return filterResourcesByVisibility(resultList, querySizeLimit);
});
}
return makeSearchResults(rdapResultSet, CursorType.ADDRESS);
}
/** Output JSON for a lists of hosts contained in an {@link RdapResultSet}. */

View File

@@ -214,7 +214,7 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
}
}
private <T extends EppResource> RdapResultSet<T> filterResourcesByVisibility(
protected <T extends EppResource> RdapResultSet<T> filterResourcesByVisibility(
List<T> queryResult, int querySizeLimit) {
// If we are including deleted resources, we need to check that we're authorized for each one.
List<T> resources = new ArrayList<>();