laskoviymishka commented on code in PR #2075:
URL: https://github.com/apache/iceberg-go/pull/2075#discussion_r4153186787


##########
catalog/rest/rest.go:
##########
@@ -1329,6 +1331,33 @@ func (r *Catalog) fetchTableCreds(ctx context.Context, 
ident []string, location
        return resolveStorageCredentials(ret.StorageCredentials, location), nil
 }
 
+// RefreshTableCredentials updates a *table.Table with newly-vended 
credentials from the catalog
+// without updating any other table-internal state that a full Refresh() would.
+// Allows for a quick table credential refresh if the table was created 
without any pre-seeded
+// credentials.
+//
+// Requires that the passed-in table instance be created with the 
table.WithSavedConfig() option to
+// save any table-specific configs. All tables created by this catalog pass in 
that option.
+func (r *Catalog) RefreshTableCredentials(ctx context.Context, tbl 
*table.Table) (*table.Table, error) {
+       metadataLoc := tbl.MetadataLocation()
+       resp, err := r.fetchTableCreds(ctx, tbl.Identifier(), metadataLoc)
+       if err != nil {
+               return nil, err
+       }
+       if len(resp) == 0 {
+               // No new credentials vended. Return as-is.
+               return tbl, nil

Review Comment:
   This returns the caller's own pointer rather than a new instance, and for an 
externally-built table that pointer has no refresher wiring, so a server that 
correctly vends nothing (IAM-role storage, say) hands back exactly the un-wired 
table we set out to fix, with no signal that nothing was established. The 
interface promises "a new/modified instance" that "can refresh its own 
credentials internally," and this path delivers neither. I'd rebuild through 
`tableFromResponse` even when `resp` is empty so the identity is always a 
fresh, wired object. If the aliasing is deliberate, let's document it and 
assert it in `TestRefreshTableCredentialsNoCredentialsVended`, since the 
equality checks there pass trivially today (same object vs itself).



##########
table/table.go:
##########
@@ -133,6 +134,8 @@ func (t Table) Schema() *iceberg.Schema                     
 { return t.metadata
 func (t Table) Spec() iceberg.PartitionSpec                  { return 
t.metadata.PartitionSpec() }
 func (t Table) SortOrder() SortOrder                         { return 
t.metadata.SortOrder() }
 func (t Table) Properties() iceberg.Properties               { return 
t.metadata.Properties() }
+func (t Table) ScanPlanningConfig() iceberg.Properties       { return 
t.scanPlanningIOProps }
+func (t Table) SavedConfig() iceberg.Properties              { return 
t.savedConfig }

Review Comment:
   I'd clone on read here. Both accessors hand back the internal map directly, 
so `tbl.SavedConfig()["k"] = v` mutates the table's stored field in place. 
That's sharp now that `savedConfig` carries the vended creds: a caller poking 
at the returned map corrupts the credential set `RefreshTableCredentials` 
rebuilds FileIO from. `Identifier()` already clones on read, and every write 
path here clones (`maps.Clone` in the `With*` options, `Refresh`, `doCommit`), 
so clone-on-read is the consistent move. `maps.Clone(nil)` is nil, so 
non-mutating callers don't change behavior.
   
   ```go
   func (t Table) ScanPlanningConfig() iceberg.Properties { return 
maps.Clone(t.scanPlanningIOProps) }
   func (t Table) SavedConfig() iceberg.Properties        { return 
maps.Clone(t.savedConfig) }
   ```



##########
table/table.go:
##########
@@ -1400,6 +1405,20 @@ func WithScanPlanningIOProperties(props 
iceberg.Properties) Option {
        }
 }
 
+// WithSavedConfig supplies a set of properties used to create a *Table
+// instance. This saved config is exposed through table.SavedConfig() and
+// can be used by callers to save properties useful for loading files of a
+// table, such as client factories and resolved storage credentials, so that
+// new clones of this table can contain the same settings.
+func WithSavedConfig(config iceberg.Properties) Option {
+       if config == nil {
+               return noopTableOption
+       }
+       return func(t *Table) {

Review Comment:
   `nlreturn` wants a blank line before this `return`. Both sibling options 
(`WithMetricsReporter`, `WithScanPlanningIOProperties`) have it and the 
linter's active in `.golangci.yml`, so this trips CI. Blank line after the 
nil-guard's closing brace.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to