|
26 | 26 | import org.lance.spark.function.LanceFragmentIdWithDefaultFunction; |
27 | 27 | import org.lance.spark.utils.Optional; |
28 | 28 | import org.lance.spark.utils.SchemaConverter; |
| 29 | +import org.lance.spark.utils.Utils; |
29 | 30 |
|
30 | 31 | import org.apache.spark.sql.catalyst.analysis.NamespaceAlreadyExistsException; |
31 | 32 | import org.apache.spark.sql.catalyst.analysis.NoSuchFunctionException; |
|
57 | 58 | import java.util.stream.Collectors; |
58 | 59 | import java.util.stream.Stream; |
59 | 60 |
|
| 61 | +import static org.lance.spark.utils.Utils.createReadOptions; |
| 62 | +import static org.lance.spark.utils.Utils.getSchema; |
| 63 | + |
60 | 64 | public abstract class BaseLanceNamespaceSparkCatalog |
61 | 65 | implements TableCatalog, SupportsNamespaces, FunctionCatalog { |
62 | 66 |
|
@@ -411,60 +415,17 @@ public boolean tableExists(Identifier ident) { |
411 | 415 |
|
412 | 416 | @Override |
413 | 417 | public Table loadTable(Identifier ident) throws NoSuchTableException { |
414 | | - // Transform identifier for API call |
415 | | - Identifier actualIdent = transformIdentifierForApi(ident); |
416 | | - |
417 | | - // Build the table ID for credential vending |
418 | | - List<String> tableId = buildTableId(actualIdent); |
419 | | - |
420 | | - // Call describeTable to get location and initial storage options |
421 | | - DescribeTableRequest describeRequest = new DescribeTableRequest(); |
422 | | - tableId.forEach(describeRequest::addIdItem); |
423 | | - DescribeTableResponse describeResponse; |
424 | | - try { |
425 | | - describeResponse = namespace.describeTable(describeRequest); |
426 | | - } catch (TableNotFoundException e) { |
427 | | - throw new NoSuchTableException(ident); |
428 | | - } catch (RuntimeException e) { |
429 | | - throw new RuntimeException("Failed to describe table: " + ident, e); |
430 | | - } |
431 | | - |
432 | | - String location = describeResponse.getLocation(); |
433 | | - Map<String, String> initialStorageOptions = describeResponse.getStorageOptions(); |
434 | | - |
435 | | - // Open dataset to get schema |
436 | | - StructType schema; |
437 | | - try (Dataset dataset = |
438 | | - Dataset.open() |
439 | | - .allocator(LanceRuntime.allocator()) |
440 | | - .namespace(namespace) |
441 | | - .tableId(tableId) |
442 | | - .build()) { |
443 | | - schema = LanceArrowUtils.fromArrowSchema(dataset.getSchema()); |
444 | | - } catch (TableNotFoundException e) { |
445 | | - throw new NoSuchTableException(ident); |
446 | | - } |
| 418 | + return loadTableInternal(ident, Optional.empty(), Optional.empty()); |
| 419 | + } |
447 | 420 |
|
448 | | - // Create read options with namespace support |
449 | | - LanceSparkReadOptions readOptions = createReadOptions(location, tableId); |
450 | | - return createDataset( |
451 | | - readOptions, schema, initialStorageOptions, namespaceImpl, namespaceProperties); |
| 421 | + @Override |
| 422 | + public Table loadTable(Identifier ident, String version) throws NoSuchTableException { |
| 423 | + return loadTableInternal(ident, Optional.empty(), Optional.of(version)); |
452 | 424 | } |
453 | 425 |
|
454 | | - /** |
455 | | - * Creates LanceSparkReadOptions with namespace settings for this catalog. |
456 | | - * |
457 | | - * @param location the dataset location URI |
458 | | - * @param tableId the table identifier within the namespace |
459 | | - * @return a new LanceSparkReadOptions with all catalog settings |
460 | | - */ |
461 | | - private LanceSparkReadOptions createReadOptions(String location, List<String> tableId) { |
462 | | - return LanceSparkReadOptions.builder() |
463 | | - .datasetUri(location) |
464 | | - .withCatalogDefaults(catalogConfig) |
465 | | - .namespace(namespace) |
466 | | - .tableId(tableId) |
467 | | - .build(); |
| 426 | + @Override |
| 427 | + public Table loadTable(Identifier ident, long timestamp) throws NoSuchTableException { |
| 428 | + return loadTableInternal(ident, Optional.of(timestamp), Optional.empty()); |
468 | 429 | } |
469 | 430 |
|
470 | 431 | @Override |
@@ -500,7 +461,13 @@ public Table createTable( |
500 | 461 | Map<String, String> initialStorageOptions = describeResponse.getStorageOptions(); |
501 | 462 |
|
502 | 463 | // Create read options with namespace settings |
503 | | - LanceSparkReadOptions readOptions = createReadOptions(location, tableIdList); |
| 464 | + LanceSparkReadOptions readOptions = |
| 465 | + createReadOptions( |
| 466 | + location, |
| 467 | + catalogConfig, |
| 468 | + Optional.empty(), |
| 469 | + Optional.of(namespace), |
| 470 | + Optional.of(tableIdList)); |
504 | 471 | return createDataset( |
505 | 472 | readOptions, processedSchema, initialStorageOptions, namespaceImpl, namespaceProperties); |
506 | 473 | } |
@@ -651,6 +618,63 @@ private List<String> buildTableId(Identifier ident) { |
651 | 618 | .collect(Collectors.toList()); |
652 | 619 | } |
653 | 620 |
|
| 621 | + private Table loadTableInternal( |
| 622 | + Identifier ident, Optional<Long> timestamp, Optional<String> version) |
| 623 | + throws NoSuchTableException { |
| 624 | + |
| 625 | + // Transform identifier for API call |
| 626 | + Identifier actualIdent = transformIdentifierForApi(ident); |
| 627 | + |
| 628 | + // Build the table ID for credential vending |
| 629 | + List<String> tableId = buildTableId(actualIdent); |
| 630 | + |
| 631 | + // Call describeTable to get location and initial storage options |
| 632 | + DescribeTableRequest describeRequest = new DescribeTableRequest(); |
| 633 | + tableId.forEach(describeRequest::addIdItem); |
| 634 | + DescribeTableResponse describeResponse; |
| 635 | + try { |
| 636 | + describeResponse = namespace.describeTable(describeRequest); |
| 637 | + } catch (TableNotFoundException e) { |
| 638 | + throw new NoSuchTableException(ident); |
| 639 | + } catch (RuntimeException e) { |
| 640 | + throw new RuntimeException("Failed to describe table: " + ident, e); |
| 641 | + } |
| 642 | + String location = describeResponse.getLocation(); |
| 643 | + Map<String, String> initialStorageOptions = describeResponse.getStorageOptions(); |
| 644 | + |
| 645 | + Optional<Long> versionId = Optional.empty(); |
| 646 | + if (timestamp.isPresent()) { |
| 647 | + try (Dataset dataset = |
| 648 | + Dataset.open() |
| 649 | + .allocator(LanceRuntime.allocator()) |
| 650 | + .uri(location) |
| 651 | + .readOptions( |
| 652 | + createReadOptions( |
| 653 | + location, |
| 654 | + catalogConfig, |
| 655 | + Optional.empty(), |
| 656 | + Optional.of(namespace), |
| 657 | + Optional.of(tableId)) |
| 658 | + .toReadOptions()) |
| 659 | + .build()) { |
| 660 | + versionId = Optional.of(Utils.findVersion(dataset.listVersions(), timestamp.get())); |
| 661 | + } catch (TableNotFoundException e) { |
| 662 | + throw new NoSuchTableException(ident); |
| 663 | + } |
| 664 | + } else if (version.isPresent()) { |
| 665 | + versionId = Optional.of(Utils.parseVersion(version.get())); |
| 666 | + } |
| 667 | + |
| 668 | + LanceSparkReadOptions readOptions = |
| 669 | + createReadOptions( |
| 670 | + location, catalogConfig, versionId, Optional.of(namespace), Optional.of(tableId)); |
| 671 | + StructType schema = getSchema(ident, location, readOptions, namespace); |
| 672 | + |
| 673 | + // Create read options with namespace support |
| 674 | + return createDataset( |
| 675 | + readOptions, schema, initialStorageOptions, namespaceImpl, namespaceProperties); |
| 676 | + } |
| 677 | + |
654 | 678 | public abstract LanceDataset createDataset( |
655 | 679 | LanceSparkReadOptions readOptions, |
656 | 680 | StructType sparkSchema, |
|
0 commit comments