Hibernate-reactive: Erro de substituição de parâmetro ao limitar os resultados no MS SQL Server e Hibernate Reactive 1.0.0.CR9

Criado em 5 ago. 2021  ·  13Comentários  ·  Fonte: hibernate/hibernate-reactive

Olá, estou encontrando um problema com os parâmetros de consulta do MS SQL em 1.0.0.CR9 ao usar Paging ou Ranges (Exceção SQL, mensagem = 'Sintaxe incorreta próxima a'? '.'), Possivelmente porque o parâmetro está na cláusula Select?

Pilha: Quarkus 2.1.0.Final / Panache Reactive / Mutiny 4.1.2 / Hibernate Reactive 1.0.0.CR9 / Hibernate Core 5.5.5.Final

Do meu recurso de descanso, executando este:
[PanacheRepository].find("firstName LIKE ?1").page(Page.of(25)).list();

Com o SQL logando no Hibernate, este é o SQL produzido:

Hibernate:
    select
        top(?) request0_.id as id1_1_,
        request0_.caseId as caseid2_1_,
        request0_.caseNumber as casenumb3_1_,
        request0_.expires as expires4_1_,
        request0_.addressLine1 as addressl5_1_,
        request0_.addressLine2 as addressl6_1_,
        request0_.agency as agency7_1_,
        request0_.city as city8_1_,
        request0_.email as email9_1_,
        request0_.firstName as firstna10_1_,
        request0_.lastName as lastnam11_1_,
        request0_.phone as phone12_1_,
        request0_.relationship as relatio13_1_,
        request0_.state as state14_1_,
        request0_.zipcode as zipcode15_1_,
        request0_.submitted as submitt16_1_
    from
        Request request0_
    where
        request0_.firstName like <strong i="11">@P1</strong>

Observe a substituição correta do parâmetro na cláusula where, mas a substituição não ocorreu para o "top (?)" Na cláusula select.
Se eu remover a paginação e executar novamente, a consulta será executada com êxito.

Log de rastreamento mostrado abaixo, mostrando HQL e SQL. Eu acho que o Panache está usando setMaxResults() na consulta. Fico feliz em abrir um problema com Quarkus ou Mutiny, se esse é o problema, e agradeço desde já!

2021-08-05 12:55:57,342 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-1) HQL: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1
2021-08-05 12:55:57,342 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-1) SQL: select request0_.id as id1_1_, request0_.caseId as caseid2_1_, request0_.caseNumber as casenumb3_1_, request0_.expires as expires4_1_, request0_.addressLine1 as addressl5_1_, request0_.addressLine2 as addressl6_1_, request0_.agency as agency7_1_, request0_.city as city8_1_, request0_.email as email9_1_, request0_.firstName as firstna10_1_, request0_.lastName as lastnam11_1_, request0_.phone as phone12_1_, request0_.relationship as relatio13_1_, request0_.state as state14_1_, request0_.zipcode as zipcode15_1_, request0_.submitted as submitt16_1_ from Request request0_ where request0_.firstName like ?
2021-08-05 12:55:57,342 DEBUG [org.hib.hql.int.ast.ErrorTracker] (vert.x-eventloop-thread-1) throwQueryException() : no errors
2021-08-05 12:55:57,342 TRACE [org.hib.rea.ses.imp.ReactiveHQLQueryPlan] (vert.x-eventloop-thread-1) Find: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1
2021-08-05 12:55:57,344 TRACE [org.hib.eng.spi.QueryParameters] (vert.x-eventloop-thread-1) Named parameters: {1=%j%}
2021-08-05 12:55:57,345 TRACE [org.hib.typ.des.sql.BasicBinder] (vert.x-eventloop-thread-1) binding parameter [2] as [VARCHAR] - [%j%]
2021-08-05 12:55:57,345 TRACE [org.hib.loa.Loader] (vert.x-eventloop-thread-1) Bound [3] parameters total
2021-08-05 12:55:57,355 DEBUG [org.hib.SQL] (vert.x-eventloop-thread-1)
    select
        top(?) request0_.id as id1_1_,
        request0_.caseId as caseid2_1_,
        request0_.caseNumber as casenumb3_1_,
        request0_.expires as expires4_1_,
        request0_.addressLine1 as addressl5_1_,
        request0_.addressLine2 as addressl6_1_,
        request0_.agency as agency7_1_,
        request0_.city as city8_1_,
        request0_.email as email9_1_,
        request0_.firstName as firstna10_1_,
        request0_.lastName as lastnam11_1_,
        request0_.phone as phone12_1_,
        request0_.relationship as relatio13_1_,
        request0_.state as state14_1_,
        request0_.zipcode as zipcode15_1_,
        request0_.submitted as submitt16_1_
    from
        Request request0_
    where
        request0_.firstName like <strong i="18">@P1</strong>
2021-08-05 12:55:57,358 ERROR [org.hib.rea.errors] (vert.x-eventloop-thread-1) HR000057: Failed to execute statement [$1select request0_.id as id1_1_, request0_.caseId as caseid2_1_, request0_.caseNumber as casenumb3_1_, request0_.expires as expires4_1_, request0_.addressLine1 as addressl5_1_, request0_.addressLine2 as addressl6_1_, request0_.agency as agency7_1_, request0_.city as city8_1_, request0_.email as email9_1_, request0_.firstName as firstna10_1_, request0_.lastName as lastnam11_1_, request0_.phone as phone12_1_, request0_.relationship as relatio13_1_, request0_.state as state14_1_, request0_.zipcode as zipcode15_1_, request0_.submitted as submitt16_1_ from Request request0_ where request0_.firstName like @P1]: $2could not execute query: java.util.concurrent.CompletionException: io.vertx.mssqlclient.MSSQLException: {number=102, state=1, severity=15, message='Incorrect syntax near '?'.', serverName='aa3ae4a0fd3c', lineNumber=1, additional=[io.vertx.mssqlclient.MSSQLException: {number=8180, state=1, severity=16, message='Statement(s) could not be prepared.', serverName='aa3ae4a0fd3c', lineNumber=1}]}
bug

Todos 13 comentários

Há algo estranho nesse SQL.

Por que estamos usando top n vez de offset ... fetch ... . E por que os parênteses redundantes?

Eu acho que o Panache está usando setMaxResults() na consulta.

Bem, certamente deveria ser. A Panache não tem negócios fazendo sua própria geração de SQL, certo @FroMage?

Bem, eu tentei isso em RH simples e recebo:

select hqlquerypa0_.id as id1_0_, hqlquerypa0_.description as descript2_0_, hqlquerypa0_.name as name3_0_, hqlquerypa0_.type as type4_0_ from Flour hqlquerypa0_ order by hqlquerypa0_.id offset <strong i="6">@P1</strong> rows fetch next <strong i="7">@P2</strong> rows only

Portanto, parece-me que este deve ser um problema relacionado ao Panache.

Muito obrigado por revisar e entrarei em contato com alguns dos projetos da pilha. Como o único HQL mostrado nos logs de rastreamento era FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 , presumi que a adição de top(?) estava vindo da implementação do MS SQL para setMaxResults() , porém o SQL em sua postagem mostra que está usando uma sintaxe completamente diferente.

Ainda encontrando o problema, tentei adicionar um pedido por, para ter este HQL: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 ORDER BY submitted

2021-08-05 16:20:37,992 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-19) HQL: FROM myproject.entities.Request WHERE requestor.firstName LIKE ?1 ORDER BY submitted
2021-08-05 16:20:37,992 DEBUG [org.hib.hql.int.ast.QueryTranslatorImpl] (vert.x-eventloop-thread-19) SQL: select request0_.id as id1_1_, request0_.caseId as caseid2_1_, request0_.caseNumber as casenumb3_1_, request0_.expires as expires4_1_, request0_.addressLine1 as addressl5_1_, request0_.addressLine2 as addressl6_1_, request0_.agency as agency7_1_, request0_.city as city8_1_, request0_.email as email9_1_, request0_.firstName as firstna10_1_, request0_.lastName as lastnam11_1_, request0_.phone as phone12_1_, request0_.relationship as relatio13_1_, request0_.state as state14_1_, request0_.zipcode as zipcode15_1_, request0_.submitted as submitt16_1_ from Request request0_ where request0_.firstName like ? order by request0_.submitted

Isso tentou executar o seguinte SQL:

2021-08-05 16:20:38,022 DEBUG [org.hib.SQL] (vert.x-eventloop-thread-19)
    select
        request0_.id as id1_1_,
        request0_.caseId as caseid2_1_,
        request0_.caseNumber as casenumb3_1_,
        request0_.expires as expires4_1_,
        request0_.addressLine1 as addressl5_1_,
        request0_.addressLine2 as addressl6_1_,
        request0_.agency as agency7_1_,
        request0_.city as city8_1_,
        request0_.email as email9_1_,
        request0_.firstName as firstna10_1_,
        request0_.lastName as lastnam11_1_,
        request0_.phone as phone12_1_,
        request0_.relationship as relatio13_1_,
        request0_.state as state14_1_,
        request0_.zipcode as zipcode15_1_,
        request0_.submitted as submitt16_1_
    from
        Request request0_
    where
        request0_.firstName like <strong i="10">@P1</strong>
    order by
        request0_.submitted offset 0 rows fetch next ? rows only

Então ... o mesmo problema ainda existe em que ele não fez a substituição de parâmetro corretamente (agora para fetch next ? rows only ), mas mudou a sintaxe SQL gerada para corresponder ao seu exemplo.

Como o Quarkus ainda não inclui o hibernate-reactive-core 1.0.0.CR9, eu modifiquei especificamente a versão em meu pom para este e o hibernate-core 5.5.5.Final. Posso estar faltando uma dependência?

Eu olhei onde acredito que a consulta do Hibernate é criada no Panache io.quarkus.hibernate.reactive.panache.common.runtime.CommonPanache QueryImpl: L254 , parece usar apenas setFirstResult e setMaxResults na consulta. Eu olhei para o objeto de consulta no depurador e no final desse método createQuery , o ReactiveQueryImpl subjacente tem queryOptions contendo firstRow = 0 e maxRows = 25.

Agradeço a ajuda! (Entenda que estou usando uma visualização)

Acho que encontrei um bug, provavelmente em SQLServerParameters.processLimit ()

Panache está criando uma consulta de hibernação com firstRow = 0.

Dentro de org.hibernate.reactive.loader.ReactiveLoader.executeReactiveQueryStatement , SQLServer2012LimitHandler.getOffsetFetch() é chamado:

if ( !LimitHelper.hasFirstRow( selection ) ) {
    return " offset 0 rows fetch next ? rows only";
}
return " offset ? rows fetch next ? rows only";

Portanto, o SQL neste ponto termina com offset 0 rows fetch next ? rows only . Observe o 0 em vez do?.
De SQLServerParameters.processLimit() :

        if (isProcessingNotRequired(sql)) {
            return sql;
        }

        // Replace 'offset ? fetch next ? rows only' with the <strong i="17">@P</strong> style parameters for Sql Server
        int index = hasOffset ? parameterArray.length - 1 : parameterArray.length;
        int pos = sql.indexOf( " offset ?" );
        if ( pos > -1 ) {
            String sqlProcessed = sql.substring( 0, pos ) + " offset @P" + index++ + " rows";
            if ( sql.indexOf( " fetch next ?" ) > -1 ) {
                sqlProcessed += " fetch next @P" + index + " rows only ";
            }
            return sqlProcessed;
        }

        return sql;

O sql.indexOf( " offset ?") é o problema: como o LimitHandler já codificou o deslocamento para 0, a substring offset ? não foi encontrada, portanto, ele não tenta processar nenhum parâmetro adicional.
A string aninhada deve corresponder à procura de fetch next ? ser movida para fora da correspondência de offset ?

@bharward Se você pudesse criar um caso de teste que usa apenas Hibernate Reactive, seria muito apreciado

Acho que também daremos uma olhada em um que usa Panache, mas este repositório não é o lugar certo para esse tipo de problema

Suspeito que esteja faltando um teste que define apenas os resultados máximos e não o primeiro resultado.

@gavinking e @DavideD - Obrigado. Parece que outro teste, um que faria _both_ setFirstResult(0) e setMaxResults(n) (n sendo qualquer valor> 0), iria reproduzir o erro que estou vendo.

SQLServer2012LimitHandler.getOffsetFetch() cria SQL diferente quando firstResult == 0 ("deslocamento 0 linhas ...") em comparação com quando firstResult> 0 ("deslocamento? Linhas");

Se ainda fosse útil, em alguns dias eu poderia gastar algum tempo para criar um pequeno exemplo apenas de RH para reproduzir ou modificar HQLQueryParameterNamedLimitTest para adicionar um teste como descrevi.

Ah, você está certo, este teste falhará em HQLQueryParameterNamedLimitTest :

    <strong i="7">@Test</strong>
    public void testFirstResultZeroAndMaxResults(TestContext context) {
        test(
                context,
                openSession()
                        .thenCompose( s ->
                                s.createQuery( "from Flour order by id" )
                                        .setFirstResult( 0 )
                                        .setMaxResults( 10 )
                                        .getResultList()
                                        .thenAccept( result -> {
                                            context.assertEquals( 3, result.size() );
                                        } )
                        )
        );
    }

vou dar uma olhada

@DavideD _além_ ao teste que você propôs, também posso sugerir um teste adicional como este:

       <strong i="7">@Test</strong>
    public void testFirstResultZeroAndMaxResultsWithoutOrderBy(TestContext context) {
        test(
                context,
                openSession()
                        .thenCompose( s ->
                                s.createQuery( "from Flour" )
                                        .setFirstResult( 0 )
                                        .setMaxResults( 10 )
                                        .getResultList()
                                        .thenAccept( result -> {
                                            context.assertEquals( 3, result.size() );
                                        } )
                        )
        );
    }

A diferença está em remover o "ordenar por". O motivo é que SQLServer2005LimitHandler grava a consulta como select top(?) ... sempre que a consulta não inclui um "ordenar por", mas quando inclui , usa a sintaxe offset ? fetch next ? . Então eu acho que 2 casos de teste são necessários.

Muito apreciado!!

Esta página foi útil?
0 / 5 - 0 avaliações