13
13
*/
14
14
package io .trino .plugin .hive .metastore .polaris ;
15
15
16
- import com .fasterxml .jackson .databind .JsonNode ;
16
+ import com .fasterxml .jackson .annotation .JsonCreator ;
17
+ import com .fasterxml .jackson .annotation .JsonProperty ;
17
18
import com .fasterxml .jackson .databind .ObjectMapper ;
18
19
import com .google .common .collect .ImmutableList ;
19
20
import com .google .common .collect .ImmutableMap ;
20
21
import com .google .common .collect .ImmutableMultimap ;
22
+ import com .google .common .collect .ImmutableMultimap ;
21
23
import com .google .common .collect .Multimap ;
22
24
import com .google .inject .Inject ;
23
25
import io .airlift .http .client .BodyGenerator ;
26
28
import io .airlift .http .client .Response ;
27
29
import io .airlift .http .client .ResponseHandler ;
28
30
import io .airlift .http .client .StaticBodyGenerator ;
29
- import io .airlift .json .JsonCodec ;
30
- import io .airlift .log .Logger ;
31
- import io .trino .hive .thrift .metastore .Table ;
32
- import io .trino .metastore .TableAlreadyExistsException ;
33
- import io .trino .spi .connector .SchemaNotFoundException ;
34
31
import io .trino .spi .connector .SchemaTableName ;
35
32
import io .trino .spi .connector .TableNotFoundException ;
33
+ import org .apache .iceberg .HasTableOperations ;
34
+ import org .apache .iceberg .PartitionSpec ;
35
+ import org .apache .iceberg .Schema ;
36
+ import org .apache .iceberg .TableOperations ;
36
37
import org .apache .iceberg .catalog .Namespace ;
37
38
import org .apache .iceberg .catalog .SessionCatalog ;
38
39
import org .apache .iceberg .catalog .TableIdentifier ;
39
40
import org .apache .iceberg .exceptions .NoSuchTableException ;
40
41
import org .apache .iceberg .exceptions .RESTException ;
41
42
import org .apache .iceberg .rest .RESTSessionCatalog ;
42
43
import org .apache .iceberg .rest .auth .OAuth2Properties ;
43
- import org .apache .iceberg .HasTableOperations ;
44
- import org .apache .iceberg .TableOperations ;
45
- import org .apache .iceberg .Schema ;
46
- import org .apache .iceberg .PartitionSpec ;
47
44
45
+ import java .io .IOException ;
48
46
import java .net .URI ;
47
+ import java .util .ArrayList ;
48
+ import java .util .HashMap ;
49
49
import java .util .List ;
50
50
import java .util .Map ;
51
51
import java .util .Optional ;
52
52
import java .util .UUID ;
53
- import java .util .ArrayList ;
54
- import java .util .HashMap ;
55
- import java .io .IOException ;
56
53
57
54
import static com .google .common .collect .ImmutableList .toImmutableList ;
58
- import static io .airlift .http .client .Request .Builder .prepareDelete ;
59
55
import static io .airlift .http .client .Request .Builder .prepareGet ;
60
- import static io .airlift .http .client .Request .Builder .prepareHead ;
61
- import static io .airlift .http .client .Request .Builder .preparePost ;
62
56
import static java .nio .charset .StandardCharsets .UTF_8 ;
63
57
import static java .util .Objects .requireNonNull ;
64
58
@@ -189,30 +183,9 @@ public List<PolarisTableIdentifier> handle(Request request, Response response)
189
183
if (response .getStatusCode () != 200 ) {
190
184
throw new PolarisException ("Failed to list generic tables: " + response .getStatusCode ());
191
185
}
192
-
193
186
try {
194
- JsonNode root = objectMapper .readTree (response .getInputStream ());
195
- JsonNode identifiers = root .get ("identifiers" );
196
-
197
- if (identifiers == null || !identifiers .isArray ()) {
198
- return ImmutableList .of ();
199
- }
200
-
201
- ImmutableList .Builder <PolarisTableIdentifier > tables = ImmutableList .builder ();
202
- for (JsonNode identifier : identifiers ) {
203
- JsonNode namespaceNode = identifier .get ("namespace" );
204
- JsonNode nameNode = identifier .get ("name" );
205
-
206
- if (namespaceNode != null && nameNode != null && namespaceNode .isArray ()) {
207
- List <String > namespaceParts = new ArrayList <>();
208
- for (JsonNode part : namespaceNode ) {
209
- namespaceParts .add (part .asText ());
210
- }
211
- String namespace = String .join ("." , namespaceParts );
212
- tables .add (new PolarisTableIdentifier (namespace , nameNode .asText ()));
213
- }
214
- }
215
- return tables .build ();
187
+ ListGenericTablesResponse listResponse = objectMapper .readValue (response .getInputStream (), ListGenericTablesResponse .class );
188
+ return listResponse .toPolarisTableIdentifiers ();
216
189
}
217
190
catch (IOException e ) {
218
191
throw new PolarisException ("Failed to parse generic tables response" , e );
@@ -250,19 +223,12 @@ public PolarisGenericTable handle(Request request, Response response)
250
223
if (response .getStatusCode () != 200 ) {
251
224
throw new PolarisException ("Failed to load generic table: " + response .getStatusCode ());
252
225
}
253
-
254
226
try {
255
- JsonNode root = objectMapper .readTree (response .getInputStream ());
256
- JsonNode tableNode = root .get ("table" );
257
-
258
- if (tableNode == null ) {
259
- throw new PolarisException ("Missing 'table' field in response" );
260
- }
261
-
262
- return parseGenericTable (tableNode );
227
+ LoadGenericTableResponse loadResponse = objectMapper .readValue (response .getInputStream (), LoadGenericTableResponse .class );
228
+ return loadResponse .getTable ();
263
229
}
264
230
catch (IOException e ) {
265
- throw new PolarisException ("Failed to parse generic table response" , e );
231
+ throw new PolarisException ("Failed to parse load generic table response" , e );
266
232
}
267
233
}
268
234
});
@@ -284,26 +250,6 @@ public void dropGenericTable(String databaseName, String tableName)
284
250
// TODO: Implement REST API call
285
251
}
286
252
287
- /**
288
- * Parses a Generic table from JSON response
289
- */
290
- private PolarisGenericTable parseGenericTable (JsonNode tableNode )
291
- {
292
- String name = getRequiredString (tableNode , "name" );
293
- String format = getRequiredString (tableNode , "format" );
294
- String baseLocation = getOptionalString (tableNode , "base-location" );
295
- String doc = getOptionalString (tableNode , "doc" );
296
-
297
- Map <String , String > properties = new HashMap <>();
298
- JsonNode propertiesNode = tableNode .get ("properties" );
299
- if (propertiesNode != null && propertiesNode .isObject ()) {
300
- propertiesNode .fields ().forEachRemaining (entry ->
301
- properties .put (entry .getKey (), entry .getValue ().asText ()));
302
- }
303
-
304
- return new PolarisGenericTable (name , format , baseLocation , doc , properties );
305
- }
306
-
307
253
// HELPER METHODS
308
254
309
255
/**
@@ -329,7 +275,7 @@ private PolarisTableMetadata convertIcebergTableToPolaris(org.apache.iceberg.Tab
329
275
TableOperations ops = ((HasTableOperations ) table ).operations ();
330
276
String location = ops .current ().location ();
331
277
Map <String , String > properties = ops .current ().properties ();
332
-
278
+
333
279
// Convert Iceberg schema to map representation
334
280
Schema icebergSchema = ops .current ().schema ();
335
281
Map <String , Object > schemaMap = ImmutableMap .of (
@@ -342,11 +288,11 @@ private PolarisTableMetadata convertIcebergTableToPolaris(org.apache.iceberg.Tab
342
288
"required" , field .isRequired (),
343
289
"type" , field .type ().toString ()))
344
290
.collect (toImmutableList ()));
345
-
291
+
346
292
return new PolarisTableMetadata (
347
- location ,
348
- schemaMap ,
349
- properties );
293
+ location ,
294
+ schemaMap ,
295
+ properties );
350
296
}
351
297
352
298
// AUTHENTICATION & HTTP UTILITIES
@@ -423,86 +369,61 @@ private URI buildUri(String path)
423
369
*/
424
370
private String encodeNamespace (String namespace )
425
371
{
426
- return namespace .replace ("." , "%1F" );
372
+ // TODO: Add proper URL encoding
373
+ return namespace ;
427
374
}
428
375
429
- // TODO: CONVERSION METHODS
430
-
431
- private org .apache .iceberg .Schema extractSchemaFromRequest (Map <String , Object > tableRequest )
376
+ private static class ListGenericTablesResponse
432
377
{
433
- // Extract schema from table creation request
434
- Object schemaObj = tableRequest .get ("schema" );
435
- if (schemaObj == null ) {
436
- throw new PolarisException ("Schema not found in table request" );
437
- }
438
-
439
- try {
440
- // For now, return a dummy schema - actual implementation would parse the schema
441
- // from the JSON representation in the table request
442
- return new Schema ();
443
- }
444
- catch (Exception e ) {
445
- throw new PolarisException ("Failed to parse schema from table request" , e );
446
- }
447
- }
378
+ private final List <TableIdentifierDto > identifiers ;
448
379
449
- private org .apache .iceberg .PartitionSpec extractPartitionSpecFromRequest (Map <String , Object > tableRequest )
450
- {
451
- // Extract partition spec from table creation request
452
- Object partitionSpecObj = tableRequest .get ("partition-spec" );
453
- if (partitionSpecObj == null ) {
454
- // No partitioning specified
455
- return PartitionSpec .unpartitioned ();
380
+ @ JsonCreator
381
+ public ListGenericTablesResponse (@ JsonProperty ("identifiers" ) List <TableIdentifierDto > identifiers )
382
+ {
383
+ this .identifiers = identifiers != null ? ImmutableList .copyOf (identifiers ) : ImmutableList .of ();
456
384
}
457
385
458
- try {
459
- // For now, return unpartitioned - actual implementation would parse the spec
460
- return PartitionSpec .unpartitioned ();
461
- }
462
- catch (Exception e ) {
463
- throw new PolarisException ("Failed to parse partition spec from table request" , e );
386
+ public List <PolarisTableIdentifier > toPolarisTableIdentifiers ()
387
+ {
388
+ return identifiers .stream ()
389
+ .map (TableIdentifierDto ::toPolarisTableIdentifier )
390
+ .collect (toImmutableList ());
464
391
}
465
392
}
466
393
467
- private Map < String , String > extractPropertiesFromRequest ( Map < String , Object > tableRequest )
394
+ private static class TableIdentifierDto
468
395
{
469
- // Extract properties from table creation request
470
- Object propertiesObj = tableRequest .get ("properties" );
471
- if (propertiesObj == null ) {
472
- return ImmutableMap .of ();
473
- }
396
+ private final List <String > namespace ;
397
+ private final String name ;
474
398
475
- if (propertiesObj instanceof Map ) {
476
- ImmutableMap .Builder <String , String > properties = ImmutableMap .builder ();
477
- ((Map <?, ?>) propertiesObj ).forEach ((key , value ) -> {
478
- if (key != null && value != null ) {
479
- properties .put (key .toString (), value .toString ());
480
- }
481
- });
482
- return properties .build ();
399
+ @ JsonCreator
400
+ public TableIdentifierDto (
401
+ @ JsonProperty ("namespace" ) List <String > namespace ,
402
+ @ JsonProperty ("name" ) String name )
403
+ {
404
+ this .namespace = requireNonNull (namespace , "namespace is null" );
405
+ this .name = requireNonNull (name , "name is null" );
483
406
}
484
407
485
- throw new PolarisException ("Properties must be a map" );
486
- }
487
-
488
- private String getRequiredString (JsonNode node , String fieldName )
489
- {
490
- JsonNode fieldNode = node .get (fieldName );
491
- if (fieldNode == null || !fieldNode .isTextual ()) {
492
- throw new PolarisException ("Missing or invalid " + fieldName + " field" );
408
+ public PolarisTableIdentifier toPolarisTableIdentifier ()
409
+ {
410
+ return new PolarisTableIdentifier (String .join ("." , namespace ), name );
493
411
}
494
- return fieldNode .asText ();
495
412
}
496
413
497
- private String getOptionalString ( JsonNode node , String fieldName )
414
+ private static class LoadGenericTableResponse
498
415
{
499
- JsonNode fieldNode = node .get (fieldName );
500
- if (fieldNode == null || fieldNode .isNull ()) {
501
- return null ;
416
+ private final PolarisGenericTable table ;
417
+
418
+ @ JsonCreator
419
+ public LoadGenericTableResponse (@ JsonProperty ("table" ) PolarisGenericTable table )
420
+ {
421
+ this .table = requireNonNull (table , "table is null" );
502
422
}
503
- if (!fieldNode .isTextual ()) {
504
- throw new PolarisException ("Invalid " + fieldName + " field format" );
423
+
424
+ public PolarisGenericTable getTable ()
425
+ {
426
+ return table ;
505
427
}
506
- return fieldNode .asText ();
507
428
}
508
429
}
0 commit comments