@@ -171,14 +171,15 @@ Status TableScanContext::Validate() const {
171171 return {};
172172}
173173
174- bool TableScanContext:: IsScanCurrentLineage () const {
175- return !from_snapshot_id.has_value () && !to_snapshot_id.has_value ();
174+ bool IsScanCurrentLineage (const TableScanContext& context) {
175+ return !context. from_snapshot_id .has_value () && !context. to_snapshot_id .has_value ();
176176}
177177
178- Result<int64_t > TableScanContext:: ToSnapshotIdInclusive (
179- const TableMetadata& metadata) const {
178+ Result<int64_t > ToSnapshotIdInclusive (const TableScanContext& context,
179+ const TableMetadata& metadata) {
180180 // Get the branch's current snapshot ID if branch is set
181181 std::shared_ptr<Snapshot> branch_snapshot;
182+ const std::string& branch = context.branch ;
182183 if (!branch.empty ()) {
183184 auto iter = metadata.refs .find (branch);
184185 ICEBERG_CHECK (iter != metadata.refs .end () && iter->second != nullptr ,
@@ -187,8 +188,8 @@ Result<int64_t> TableScanContext::ToSnapshotIdInclusive(
187188 metadata.SnapshotById (iter->second ->snapshot_id ));
188189 }
189190
190- if (to_snapshot_id.has_value ()) {
191- int64_t to_snapshot_id_value = to_snapshot_id.value ();
191+ if (context. to_snapshot_id .has_value ()) {
192+ int64_t to_snapshot_id_value = context. to_snapshot_id .value ();
192193
193194 if (branch_snapshot != nullptr ) {
194195 // Validate `to_snapshot_id` is on the current branch
@@ -217,41 +218,41 @@ Result<int64_t> TableScanContext::ToSnapshotIdInclusive(
217218 return current_snapshot->snapshot_id ;
218219}
219220
220- Result<std::optional<int64_t >> TableScanContext::FromSnapshotIdExclusive (
221- const TableMetadata& metadata, int64_t to_snapshot_id_inclusive) const {
222- if (!from_snapshot_id.has_value ()) {
221+ Result<std::optional<int64_t >> FromSnapshotIdExclusive (const TableScanContext& context,
222+ const TableMetadata& metadata,
223+ int64_t to_snapshot_id_inclusive) {
224+ if (!context.from_snapshot_id .has_value ()) {
223225 return std::nullopt ;
224226 }
225227
226- int64_t from_snapshot_id_value = from_snapshot_id.value ();
228+ int64_t from_snapshot_id = context. from_snapshot_id .value ();
227229
228230 // Validate `from_snapshot_id` is an ancestor of `to_snapshot_id_inclusive`
229- if (from_snapshot_id_inclusive) {
230- ICEBERG_ASSIGN_OR_RAISE (bool is_ancestor,
231- SnapshotUtil::IsAncestorOf (metadata, to_snapshot_id_inclusive ,
232- from_snapshot_id_value ));
231+ if (context. from_snapshot_id_inclusive ) {
232+ ICEBERG_ASSIGN_OR_RAISE (
233+ bool is_ancestor ,
234+ SnapshotUtil::IsAncestorOf (metadata, to_snapshot_id_inclusive, from_snapshot_id ));
233235 ICEBERG_CHECK (
234236 is_ancestor,
235237 " Starting snapshot (inclusive) {} is not an ancestor of end snapshot {}" ,
236- from_snapshot_id_value , to_snapshot_id_inclusive);
238+ from_snapshot_id , to_snapshot_id_inclusive);
237239
238240 // For inclusive behavior, return the parent snapshot ID (can be nullopt)
239- ICEBERG_ASSIGN_OR_RAISE (auto from_snapshot,
240- metadata.SnapshotById (from_snapshot_id_value));
241+ ICEBERG_ASSIGN_OR_RAISE (auto from_snapshot, metadata.SnapshotById (from_snapshot_id));
241242 return from_snapshot->parent_snapshot_id ;
242243 }
243244
244245 // Validate there is an ancestor of `to_snapshot_id_inclusive` where parent is
245246 // `from_snapshot_id`
246- ICEBERG_ASSIGN_OR_RAISE (bool is_parent_ancestor, SnapshotUtil::IsParentAncestorOf (
247- metadata, to_snapshot_id_inclusive,
248- from_snapshot_id_value ));
247+ ICEBERG_ASSIGN_OR_RAISE (bool is_parent_ancestor,
248+ SnapshotUtil::IsParentAncestorOf (
249+ metadata, to_snapshot_id_inclusive, from_snapshot_id ));
249250 ICEBERG_CHECK (
250251 is_parent_ancestor,
251252 " Starting snapshot (exclusive) {} is not a parent ancestor of end snapshot {}" ,
252- from_snapshot_id_value , to_snapshot_id_inclusive);
253+ from_snapshot_id , to_snapshot_id_inclusive);
253254
254- return from_snapshot_id_value ;
255+ return from_snapshot_id ;
255256}
256257
257258} // namespace internal
@@ -730,4 +731,29 @@ IncrementalChangelogScan::PlanFiles(std::optional<int64_t> from_snapshot_id_excl
730731 return NotImplemented (" IncrementalChangelogScan::PlanFiles is not implemented" );
731732}
732733
734+ // Explicit template implementations for IncrementalScan
735+ // This moves the template implementation from header to source file
736+ template <typename ScanTaskType>
737+ Result<std::vector<std::shared_ptr<ScanTaskType>>>
738+ IncrementalScan<ScanTaskType>::PlanFiles() const {
739+ if (IsScanCurrentLineage (context_)) {
740+ ICEBERG_ASSIGN_OR_RAISE (auto current_snapshot, metadata_->Snapshot ());
741+ if (current_snapshot == nullptr ) {
742+ return std::vector<std::shared_ptr<ScanTaskType>>{};
743+ }
744+ }
745+
746+ ICEBERG_ASSIGN_OR_RAISE (int64_t to_snapshot_id_inclusive,
747+ internal::ToSnapshotIdInclusive (context_, *metadata_));
748+ ICEBERG_ASSIGN_OR_RAISE (
749+ std::optional<int64_t > from_snapshot_id_exclusive,
750+ internal::FromSnapshotIdExclusive (context_, *metadata_, to_snapshot_id_inclusive));
751+
752+ return PlanFiles (from_snapshot_id_exclusive, to_snapshot_id_inclusive);
753+ }
754+
755+ // Explicitly instantiate the templates
756+ template class IncrementalScan <FileScanTask>;
757+ template class IncrementalScan <ChangelogScanTask>;
758+
733759} // namespace iceberg
0 commit comments