1414package io .trino .gateway .ha .router ;
1515
1616import com .fasterxml .jackson .annotation .JsonCreator ;
17+ import com .fasterxml .jackson .annotation .JsonIgnore ;
1718import com .fasterxml .jackson .annotation .JsonProperty ;
1819import com .fasterxml .jackson .core .JsonGenerator ;
1920import com .fasterxml .jackson .databind .SerializerProvider ;
2021import com .fasterxml .jackson .databind .annotation .JsonSerialize ;
2122import com .fasterxml .jackson .databind .ser .std .StdSerializer ;
23+ import com .google .common .collect .ImmutableList ;
2224import com .google .common .collect .ImmutableMap ;
2325import com .google .common .collect .ImmutableSet ;
2426import io .airlift .compress .zstd .ZstdDecompressor ;
2931import io .trino .sql .parser .SqlParser ;
3032import io .trino .sql .tree .AddColumn ;
3133import io .trino .sql .tree .Analyze ;
34+ import io .trino .sql .tree .Call ;
3235import io .trino .sql .tree .CreateCatalog ;
3336import io .trino .sql .tree .CreateMaterializedView ;
3437import io .trino .sql .tree .CreateSchema ;
4043import io .trino .sql .tree .DropTable ;
4144import io .trino .sql .tree .Execute ;
4245import io .trino .sql .tree .ExecuteImmediate ;
46+ import io .trino .sql .tree .Expression ;
4347import io .trino .sql .tree .Identifier ;
4448import io .trino .sql .tree .Node ;
4549import io .trino .sql .tree .NodeLocation ;
5761import io .trino .sql .tree .ShowSchemas ;
5862import io .trino .sql .tree .ShowTables ;
5963import io .trino .sql .tree .Statement ;
64+ import io .trino .sql .tree .StringLiteral ;
6065import io .trino .sql .tree .Table ;
6166import io .trino .sql .tree .TableFunctionInvocation ;
6267import jakarta .servlet .http .HttpServletRequest ;
7378import java .util .Set ;
7479import java .util .stream .Collectors ;
7580
81+ import static com .google .common .base .Preconditions .checkArgument ;
7682import static com .google .common .io .BaseEncoding .base64Url ;
7783import static io .airlift .json .JsonCodec .jsonCodec ;
7884import static java .lang .Math .toIntExact ;
@@ -85,6 +91,7 @@ public class TrinoQueryProperties
8591{
8692 private final Logger log = Logger .get (TrinoQueryProperties .class );
8793 private final boolean isClientsUseV2Format ;
94+ private final int maxBodySize ;
8895 private String body = "" ;
8996 private String queryType = "" ;
9097 private String resourceGroupQueryType = "" ;
@@ -96,6 +103,7 @@ public class TrinoQueryProperties
96103 private Set <String > catalogSchemas = ImmutableSet .of ();
97104 private boolean isNewQuerySubmission ;
98105 private Optional <String > errorMessage = Optional .empty ();
106+ private Optional <String > queryId = Optional .empty ();
99107
100108 public static final String TRINO_CATALOG_HEADER_NAME = "X-Trino-Catalog" ;
101109 public static final String TRINO_SCHEMA_HEADER_NAME = "X-Trino-Schema" ;
@@ -128,21 +136,24 @@ public TrinoQueryProperties(
128136 this .isNewQuerySubmission = isNewQuerySubmission ;
129137 this .errorMessage = requireNonNullElse (errorMessage , Optional .empty ());
130138 isClientsUseV2Format = false ;
139+ maxBodySize = -1 ;
131140 }
132141
133- public TrinoQueryProperties (HttpServletRequest request , RequestAnalyzerConfig config )
142+ public TrinoQueryProperties (HttpServletRequest request , boolean isClientsUseV2Format , int maxBodySize )
134143 {
135- isClientsUseV2Format = config .isClientsUseV2Format ();
144+ requireNonNull (request , "request is null" );
145+ this .isClientsUseV2Format = isClientsUseV2Format ;
146+ this .maxBodySize = maxBodySize ;
136147
137148 defaultCatalog = Optional .ofNullable (request .getHeader (TRINO_CATALOG_HEADER_NAME ));
138149 defaultSchema = Optional .ofNullable (request .getHeader (TRINO_SCHEMA_HEADER_NAME ));
139150 if (request .getMethod ().equals (HttpMethod .POST )) {
140151 isNewQuerySubmission = true ;
141- processRequestBody (request , config );
152+ processRequestBody (request );
142153 }
143154 }
144155
145- private void processRequestBody (HttpServletRequest request , RequestAnalyzerConfig config )
156+ private void processRequestBody (HttpServletRequest request )
146157 {
147158 try (BufferedReader reader = request .getReader ()) {
148159 if (reader == null ) {
@@ -153,11 +164,11 @@ private void processRequestBody(HttpServletRequest request, RequestAnalyzerConfi
153164
154165 Map <String , String > preparedStatements = getPreparedStatements (request );
155166 SqlParser parser = new SqlParser ();
156- reader .mark (config . getMaxBodySize () );
157- char [] buffer = new char [config . getMaxBodySize () ];
158- int nChars = reader .read (buffer , 0 , config . getMaxBodySize () );
167+ reader .mark (maxBodySize );
168+ char [] buffer = new char [maxBodySize ];
169+ int nChars = reader .read (buffer , 0 , maxBodySize );
159170 reader .reset ();
160- if (nChars == config . getMaxBodySize () ) {
171+ if (nChars == maxBodySize ) {
161172 log .warn ("Query length greater or equal to requestAnalyzerConfig.maxBodySize detected" );
162173 return ;
163174 //The body is truncated - there is a chance that it could still be syntactically valid SQL, for example if truncated on
@@ -199,7 +210,7 @@ else if (statement instanceof ExecuteImmediate executeImmediate) {
199210 ImmutableSet .Builder <String > schemaBuilder = ImmutableSet .builder ();
200211 ImmutableSet .Builder <String > catalogSchemaBuilder = ImmutableSet .builder ();
201212
202- getNames (statement , tableBuilder , catalogBuilder , schemaBuilder , catalogSchemaBuilder );
213+ visitNode (statement , tableBuilder , catalogBuilder , schemaBuilder , catalogSchemaBuilder );
203214 tables = tableBuilder .build ();
204215 catalogBuilder .addAll (tables .stream ().map (q -> q .getParts ().getFirst ()).iterator ());
205216 catalogs = catalogBuilder .build ();
@@ -260,7 +271,7 @@ private String decodePreparedStatementFromHeader(String headerValue)
260271 return new String (preparedStatement , UTF_8 );
261272 }
262273
263- private void getNames (Node node , ImmutableSet .Builder <QualifiedName > tableBuilder ,
274+ private void visitNode (Node node , ImmutableSet .Builder <QualifiedName > tableBuilder ,
264275 ImmutableSet .Builder <String > catalogBuilder ,
265276 ImmutableSet .Builder <String > schemaBuilder ,
266277 ImmutableSet .Builder <String > catalogSchemaBuilder )
@@ -269,6 +280,7 @@ private void getNames(Node node, ImmutableSet.Builder<QualifiedName> tableBuilde
269280 switch (node ) {
270281 case AddColumn s -> tableBuilder .add (qualifyName (s .getName ()));
271282 case Analyze s -> tableBuilder .add (qualifyName (s .getTableName ()));
283+ case Call call -> queryId = extractQueryIdFromCall (call );
272284 case CreateCatalog s -> catalogBuilder .add (s .getCatalogName ().getValue ());
273285 case CreateMaterializedView s -> tableBuilder .add (qualifyName (s .getName ()));
274286 case CreateSchema s -> setCatalogAndSchemaNameFromSchemaQualifiedName (Optional .of (s .getSchemaName ()), catalogBuilder , schemaBuilder , catalogSchemaBuilder );
@@ -342,10 +354,22 @@ private void getNames(Node node, ImmutableSet.Builder<QualifiedName> tableBuilde
342354 }
343355
344356 for (Node child : node .getChildren ()) {
345- getNames (child , tableBuilder , catalogBuilder , schemaBuilder , catalogSchemaBuilder );
357+ visitNode (child , tableBuilder , catalogBuilder , schemaBuilder , catalogSchemaBuilder );
346358 }
347359 }
348360
361+ private Optional <String > extractQueryIdFromCall (Call call )
362+ throws RequestParsingException
363+ {
364+ QualifiedName callName = qualifyName (call .getName ());
365+ if (callName .equals (QualifiedName .of ("system" , "runtime" , "kill_query" ))) {
366+ Expression argument = call .getArguments ().getFirst ().getValue ();
367+ checkArgument (argument instanceof StringLiteral , "Unable to route kill_query procedures where the first argument is not a String Literal" );
368+ return Optional .of (((StringLiteral ) argument ).getValue ());
369+ }
370+ return Optional .empty ();
371+ }
372+
349373 private void setCatalogAndSchemaNameFromSchemaQualifiedName (
350374 Optional <QualifiedName > schemaOptional ,
351375 ImmutableSet .Builder <String > catalogBuilder ,
@@ -381,15 +405,16 @@ private RequestParsingException unsetDefaultExceptionSupplier()
381405 return new RequestParsingException ("Name not fully qualified" );
382406 }
383407
384- private QualifiedName qualifyName (QualifiedName table )
408+ private QualifiedName qualifyName (QualifiedName name )
385409 throws RequestParsingException
386410 {
387- List <String > tableParts = table .getParts ();
388- return switch (tableParts .size ()) {
389- case 1 -> QualifiedName .of (defaultCatalog .orElseThrow (this ::unsetDefaultExceptionSupplier ), defaultSchema .orElseThrow (this ::unsetDefaultExceptionSupplier ), tableParts .getFirst ());
390- case 2 -> QualifiedName .of (defaultCatalog .orElseThrow (this ::unsetDefaultExceptionSupplier ), tableParts .getFirst (), tableParts .get (1 ));
391- case 3 -> QualifiedName .of (tableParts .getFirst (), tableParts .get (1 ), tableParts .get (2 ));
392- default -> throw new RequestParsingException ("Unexpected table name: " + table .getParts ());
411+ List <String > nameParts = name .getParts ();
412+ return switch (nameParts .size ()) {
413+ case 1 ->
414+ QualifiedName .of (defaultCatalog .orElseThrow (this ::unsetDefaultExceptionSupplier ), defaultSchema .orElseThrow (this ::unsetDefaultExceptionSupplier ), nameParts .getFirst ());
415+ case 2 -> QualifiedName .of (defaultCatalog .orElseThrow (this ::unsetDefaultExceptionSupplier ), nameParts .getFirst (), nameParts .get (1 ));
416+ case 3 -> QualifiedName .of (nameParts .getFirst (), nameParts .get (1 ), nameParts .get (2 ));
417+ default -> throw new RequestParsingException ("Unexpected qualified name: " + name .getParts ());
393418 };
394419 }
395420
@@ -520,6 +545,12 @@ public Optional<String> getErrorMessage()
520545 return errorMessage ;
521546 }
522547
548+ @ JsonIgnore
549+ public Optional <String > getQueryId ()
550+ {
551+ return queryId ;
552+ }
553+
523554 public static class AlternateStatementRequestBodyFormat
524555 {
525556 // Based on https://github.com/trinodb/trino/wiki/trino-v2-client-protocol, without session
0 commit comments