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


##########
catalog/sql/sql.go:
##########
@@ -1285,6 +1333,12 @@ func (c *Catalog) CreateView(ctx context.Context, 
identifier table.Identifier, s
 
                return nil
        })
+       if err != nil {
+               loadFS := io.LoadFSFunc(props, metadataLocation)

Review Comment:
   This cleanup won't run on any backend that keeps credentials in the catalog 
config.
   
   `props` here is the caller's view-level props, which is nil in the common 
case — the S3/GCS/ADLS credentials live in `c.props`, which is what `LoadView` 
and `CheckTableExists` use. So `io.LoadFSFunc(props, ...)` fails to 
authenticate, `removeUncommittedMetadata` returns an error that just gets 
joined and returned, and the orphaned metadata file is exactly what stays 
behind. The table path already gets this right via `staged.FS` (built from 
`c.props`).
   
   I'd switch this to `io.LoadFSFunc(c.props, metadataLocation)`.



##########
catalog/sql/sql.go:
##########
@@ -627,6 +640,14 @@ func (c *Catalog) CreateTable(ctx context.Context, ident 
table.Identifier, sc *i
                return nil, fmt.Errorf("%w: %s", catalog.ErrNoSuchNamespace, ns)
        }
 
+       viewExists, err := c.CheckViewExists(ctx, ident)

Review Comment:
   The symmetry with `CreateView` isn't quite complete. `CreateView` now 
returns typed sentinels for both collisions, but `CreateTable` only prechecks 
the view case — a duplicate *table* still falls through to the raw DB 
unique-constraint error rather than `ErrTableAlreadyExists`, so callers 
switching on the error for idempotent-create or "already exists" handling will 
misclassify it.
   
   I'd add a `CheckTableExists` precheck right after this block. Worth a test 
for the real duplicate-table case too — the trigger test forces every insert to 
fail, which is a different path. wdyt?



##########
catalog/sql/sql_test.go:
##########
@@ -1972,6 +1972,95 @@ func (s *SqliteCatalogTestSuite) TestCreateView() {
        s.True(exists)
 }
 
+func (s *SqliteCatalogTestSuite) TestCreateTableConflictsWithView() {
+       ctx := context.Background()
+       db := s.getCatalogSqlite()
+       identifier := s.randomTableIdentifier()
+       s.Require().NoError(db.CreateNamespace(ctx, 
catalog.NamespaceFromIdent(identifier), nil))
+       s.Require().NoError(db.CreateView(ctx, identifier, tableSchemaNested, 
"SELECT 1", nil))
+
+       metadataDir := filepath.Join(s.warehouse, identifier[0]+".db", 
identifier[1], "metadata")
+       before, err := os.ReadDir(metadataDir)
+       s.Require().NoError(err)
+
+       _, err = db.CreateTable(ctx, identifier, tableSchemaNested)
+       s.ErrorIs(err, catalog.ErrViewAlreadyExists)
+
+       after, err := os.ReadDir(metadataDir)
+       s.Require().NoError(err)
+       s.Len(after, len(before))
+}
+
+func (s *SqliteCatalogTestSuite) TestCreateViewConflictsWithTable() {
+       ctx := context.Background()
+       db := s.getCatalogSqlite()
+       identifier := s.randomTableIdentifier()
+       s.Require().NoError(db.CreateNamespace(ctx, 
catalog.NamespaceFromIdent(identifier), nil))
+       tbl, err := db.CreateTable(ctx, identifier, tableSchemaNested)
+       s.Require().NoError(err)
+       
s.Require().NoError(os.Remove(strings.TrimPrefix(tbl.MetadataLocation(), 
"file://")))

Review Comment:
   This `os.Remove` makes the file-count assertion prove nothing.
   
   After deleting the metadata file, `before` is empty, `CreateView` bails at 
the `CheckTableExists` guard before it ever writes metadata, so `after` is also 
empty and `s.Len(after, len(before))` is just `0 == 0`. It never touches the 
cleanup path, and it leaves the catalog in a state that can't occur in practice 
(row present, file gone).
   
   I'd drop the `os.Remove`, take `before` right after `CreateTable` (one file 
present), then assert `after` still equals `before` after the failed 
`CreateView`.



##########
catalog/sql/sql.go:
##########
@@ -919,16 +944,31 @@ func (c *Catalog) RenameTable(ctx context.Context, from, 
to table.Identifier) (*
 }
 
 func (c *Catalog) CheckTableExists(ctx context.Context, identifier 
table.Identifier) (bool, error) {
-       _, err := c.LoadTable(ctx, identifier)
+       ns, err := c.namespaceKey(ctx, catalog.NamespaceFromIdent(identifier))

Review Comment:
   This quietly changes the contract of a public interface method. Before, it 
went through `LoadTable`, so a table with a missing or unreadable metadata file 
returned false; now it's a catalog-row `EXISTS` check that returns true 
whenever the row is present, regardless of metadata reachability.
   
   That's the right behavior for collision detection and matches Java's 
`JdbcUtil.tableExists`, but it's a behavioral break for anyone relying on the 
old reachability semantics. I'd add a one-line doc comment here (and on the 
interface in `catalog/catalog.go`) noting it checks catalog-registry existence, 
not metadata-file validity.



-- 
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