2020
2121import io .delta .kernel .CommitRange ;
2222import io .delta .kernel .Snapshot ;
23+ import io .delta .kernel .defaults .engine .DefaultEngine ;
2324import io .delta .kernel .engine .Engine ;
25+ import io .delta .kernel .internal .DeltaHistoryManager ;
26+ import io .delta .kernel .internal .SnapshotImpl ;
27+ import io .delta .kernel .internal .files .ParsedCatalogCommitData ;
28+ import io .delta .kernel .spark .exception .VersionNotFoundException ;
29+ import java .util .List ;
2430import java .util .Optional ;
2531
2632import io .delta .kernel .internal .DeltaHistoryManager ;
3036import org .slf4j .LoggerFactory ;
3137
3238/**
33- * Wireframe implementation of DeltaSnapshotManager for catalog-managed tables (e.g., UC).
39+ * Implementation of DeltaSnapshotManager for catalog-managed tables (e.g., UC).
3440 *
35- * <p>All operations are intentionally stubbed in this PR. Functionality will be implemented in a
36- * follow-up PR .
41+ * <p>This snapshot manager is agnostic to the underlying catalog implementation. It delegates to a
42+ * {@link ManagedCatalogAdapter}, keeping catalog-specific wiring out of the manager itself .
3743 */
3844@ Experimental
3945public class CatalogManagedSnapshotManager implements DeltaSnapshotManager , AutoCloseable {
@@ -43,6 +49,7 @@ public class CatalogManagedSnapshotManager implements DeltaSnapshotManager, Auto
4349 private final ManagedCatalogAdapter catalogAdapter ;
4450 private final String tableId ;
4551 private final String tablePath ;
52+ private final Engine kernelEngine ;
4653
4754 public CatalogManagedSnapshotManager (
4855 ManagedCatalogAdapter catalogAdapter ,
@@ -54,44 +61,133 @@ public CatalogManagedSnapshotManager(
5461 this .tablePath = requireNonNull (tablePath , "tablePath is null" );
5562 requireNonNull (hadoopConf , "hadoopConf is null" );
5663
64+ this .kernelEngine = DefaultEngine .create (hadoopConf );
5765 logger .info (
5866 "Created CatalogManagedSnapshotManager for table {} at path {}" , tableId , tablePath );
5967 }
6068
69+ /** Loads the latest snapshot of the catalog-managed Delta table. */
6170 @ Override
6271 public Snapshot loadLatestSnapshot () {
63- throw new UnsupportedOperationException ("loadLatestSnapshot not implemented yet" );
72+ return catalogAdapter .loadSnapshot (
73+ kernelEngine , /* versionOpt = */ Optional .empty (), /* timestampOpt = */ Optional .empty ());
6474 }
6575
76+ /**
77+ * Loads a specific version of the Unity Catalog managed Delta table.
78+ *
79+ * @param version the version to load (must be >= 0)
80+ * @return the snapshot at the specified version
81+ */
6682 @ Override
6783 public Snapshot loadSnapshotAt (long version ) {
6884 checkArgument (version >= 0 , "version must be non-negative" );
69- throw new UnsupportedOperationException ("loadSnapshotAt not implemented yet" );
85+ return catalogAdapter .loadSnapshot (
86+ kernelEngine , Optional .of (version ), /* timestampOpt = */ Optional .empty ());
7087 }
7188
89+ /**
90+ * Finds the active commit at a specific timestamp.
91+ *
92+ * <p>For catalog-managed tables, this method retrieves ratified commits from the catalog and uses
93+ * {@link DeltaHistoryManager#getActiveCommitAtTimestamp} to find the commit that was active at
94+ * the specified timestamp.
95+ *
96+ * @param timestampMillis the timestamp in milliseconds since epoch (UTC)
97+ * @param canReturnLastCommit if true, returns the last commit if the timestamp is after all
98+ * commits; if false, throws an exception
99+ * @param mustBeRecreatable if true, only considers commits that can be fully recreated from
100+ * available log files; if false, considers all commits
101+ * @param canReturnEarliestCommit if true, returns the earliest commit if the timestamp is before
102+ * all commits; if false, throws an exception
103+ * @return the commit that was active at the specified timestamp
104+ */
72105 @ Override
73106 public DeltaHistoryManager .Commit getActiveCommitAtTime (
74107 long timestampMillis ,
75108 boolean canReturnLastCommit ,
76109 boolean mustBeRecreatable ,
77110 boolean canReturnEarliestCommit ) {
78- throw new UnsupportedOperationException ("getActiveCommitAtTime not implemented yet" );
111+ // Load the latest snapshot for timestamp resolution
112+ SnapshotImpl latestSnapshot = (SnapshotImpl ) loadLatestSnapshot ();
113+
114+ // Extract catalog commits from the snapshot's log segment (avoids redundant UC call)
115+ List <ParsedCatalogCommitData > catalogCommits =
116+ latestSnapshot .getLogSegment ().getAllCatalogCommits ();
117+
118+ return DeltaHistoryManager .getActiveCommitAtTimestamp (
119+ kernelEngine ,
120+ latestSnapshot ,
121+ latestSnapshot .getLogPath (),
122+ timestampMillis ,
123+ mustBeRecreatable ,
124+ canReturnLastCommit ,
125+ canReturnEarliestCommit ,
126+ catalogCommits );
79127 }
80128
129+ /**
130+ * Checks if a specific version exists and is accessible.
131+ *
132+ * <p>For catalog-managed tables, versions are assumed to be contiguous (enforced by the catalog
133+ * coordinator). This method performs a lightweight check by verifying the version is within the
134+ * valid range [0, latestRatifiedVersion].
135+ *
136+ * <p>This approach is consistent with the existing Spark Delta behavior in {@code
137+ * DeltaHistoryManager.checkVersionExists} which also assumes contiguous commits.
138+ *
139+ * @param version the version to check
140+ * @param mustBeRecreatable if true, requires that the version can be fully recreated from
141+ * available log files. For catalog-managed tables, all versions are recreatable since the
142+ * catalog maintains the complete commit history.
143+ * @param allowOutOfRange if true, allows versions greater than the latest version without
144+ * throwing an exception; if false, throws exception for out-of-range versions
145+ * @throws VersionNotFoundException if the version is not available
146+ */
81147 @ Override
82- public void checkVersionExists (long version , boolean mustBeRecreatable , boolean allowOutOfRange ) {
148+ public void checkVersionExists (long version , boolean mustBeRecreatable , boolean allowOutOfRange )
149+ throws VersionNotFoundException {
83150 checkArgument (version >= 0 , "version must be non-negative" );
84- throw new UnsupportedOperationException ("checkVersionExists not implemented yet" );
151+
152+ // For catalog-managed tables, the earliest recreatable version is 0 since the catalog
153+ // maintains the complete commit history
154+ long earliestVersion = 0 ;
155+ long latestVersion = catalogAdapter .getLatestRatifiedVersion ();
156+
157+ if (version < earliestVersion || ((version > latestVersion ) && !allowOutOfRange )) {
158+ throw new VersionNotFoundException (version , earliestVersion , latestVersion );
159+ }
85160 }
86161
162+ /**
163+ * Gets a range of table changes between versions.
164+ *
165+ * <p><strong>Note:</strong> This operation delegates to the managed commit client.
166+ *
167+ * @throws UnsupportedOperationException if not yet implemented for catalog-managed tables
168+ */
87169 @ Override
88170 public CommitRange getTableChanges (Engine engine , long startVersion , Optional <Long > endVersion ) {
89- throw new UnsupportedOperationException ("getTableChanges not implemented yet" );
171+ requireNonNull (engine , "engine is null" );
172+ checkArgument (startVersion >= 0 , "startVersion must be non-negative" );
173+ endVersion .ifPresent (v -> checkArgument (v >= 0 , "endVersion must be non-negative" ));
174+
175+ return catalogAdapter .loadCommitRange (
176+ engine ,
177+ Optional .of (startVersion ),
178+ /* startTimestampOpt = */ Optional .empty (),
179+ endVersion ,
180+ /* endTimestampOpt = */ Optional .empty ());
90181 }
91182
183+ /**
184+ * Closes the UC client and releases resources.
185+ *
186+ * <p>This method should be called when the snapshot manager is no longer needed. Prefer using
187+ * try-with-resources to ensure proper cleanup.
188+ */
92189 @ Override
93190 public void close () {
94- // no-op in wireframe; adapter may implement close in the future
95191 try {
96192 catalogAdapter .close ();
97193 logger .info ("Closed CatalogManagedSnapshotManager for table {}" , tableId );
0 commit comments