)]}'
{
  "log": [
    {
      "commit": "e6f3a47f440d7b4bb9482344891ee7b00966c865",
      "tree": "8d5b55c130429fc4d952d178c65ba82d884b151a",
      "parents": [
        "29d40aa6404606b0caf99dcbf9366854f2cacfea"
      ],
      "author": {
        "name": "Mark Hoerth",
        "email": "47870294+markhoerth@users.noreply.github.com",
        "time": "Wed Jul 29 18:40:59 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 30 09:40:59 2026 +0800"
      },
      "message": "[MINOR] docs: update the access control page (#12247)\n\n### What changes were proposed in this pull request?\n\nA full rewrite of `docs/security/access-control.md`. Every claim on the\npage was checked against the source rather than carried over from the\nprevious version. The page goes from 1405 lines to 591.\n\n**Corrections**\n\n- Removed a note in the Overview stating that Gravitino \"won\u0027t check the\nprivileges when Gravitino receives the requests\". The server layer\ncarries 117 `@AuthorizationExpression` annotations, so metadata\nauthorization is enforced. The note appears to predate built-in\nauthorization and contradicted the rest of the page.\n- Removed a diagram that showed `CREATE_TABLE` bound to a table.\n`CreateTable.canBindTo` uses `SCHEMA_SUPPORTED_TYPES`, so the server\nrejects that binding.\n- Added `gravitino.authorization.impl` and\n`gravitino.authorization.threadPoolSize`, which were missing from the\nconfiguration table.\n- Corrected `jcasbin.roleCacheSize`, which is applied separately to\nthree caches, so real memory use is roughly triple the configured value.\n- Corrected the `MODIFY_TABLE` description, which claimed it grants\nreads.\n- Fixed `COMSUME_TOPIC`, four unclosed backticks around `USE_MODEL`, a\ngarbled row in the conditions table, and two duplicate rows, one of\nwhich was a mislabelled copy of a different operation.\n- Added the multi-node consequence of `jcasbin.changePollIntervalSecs`:\na revocation can take up to that interval to take effect on other nodes.\n- Added the behaviour of a failed check, `403 Forbidden`, with `404 Not\nFound` on some read paths so callers cannot probe for objects they\ncannot see.\n- Added the fact that role object names are not validated, so a typo\nproduces a role that grants nothing.\n\n**Removals**\n\n- Five diagrams. Each was stale, wrong, or a rendering of adjacent text.\nThe securable-object tree omitted View, Model, and Function while the\ntext listed them; the relationship diagram had its arrows reversed and\nnamed individuals; the role diagram encoded the invalid grant above; the\nALLOW/DENY diagram used a full screen to repeat one word; the workflow\ndiagram duplicated the walkthrough beneath it as pixels.\n- The per-operation REST and Java reference, about 400 lines, which\nduplicated `docs/open-api/` and the published Javadoc and had drifted\nfrom both. Four of the Java examples did not compile. Replaced with a\ncompact endpoint table, three client examples covering the calls whose\narguments are not obvious from the signature, and links to the generated\nreferences.\n- 20 `:::` admonition blocks and 24 tab widgets, per the conventions\nsettled on the credential vending page.\n\n**Restructuring**\n\n- 12 top-level sections reduced to 8, nested rather than flat.\n- The 105-row API conditions table, in which 49 rows opened with the\nsame sentence, is replaced by two grids plus four short paragraphs for\nthe operations that do not follow the pattern.\n- 12 per-object privilege tables consolidated into 2, so columns align.\n- Quick Start reduced to the two properties that enable authorization.\nThe full sequence lives in the Walkthrough, which now runs from an empty\nserver to a user reading one schema.\n- Authenticator configuration moved to Server Configuration, including\nthe point that `principalFields` defaults to `sub` and must produce the\nnames used in metalake membership.\n\n### Why are the changes needed?\n\nThe page told readers that Gravitino does not check privileges on\nincoming requests, which is false and appeared under the Overview\nheading. It documented a grant the server rejects, and shipped Java\nexamples that do not compile. Anyone reading it to understand the\nsecurity model was being misinformed, and anyone copying from it was\nbeing sent into errors.\n\nThe page was also unusable in shape: 1405 lines, 12 flat top-level\nsections, and a reference table in which the same sentence was repeated\n49 times.\n\nFix: #(issue)\n\n### Does this PR introduce _any_ user-facing change?\n\nDocumentation only. No API, configuration, or behaviour changes.\n\nSection anchors change. Two inbound links break and are fixed in a\nfollow-up PR to keep this one scoped to a single file:\n\n- `docs/webui-v2.md:347` links to `#securable-objects` and\n`#privilege-types`\n- `docs/security/how-to-use-built-in-idp.md:390` links to `#add-a-user`\n\n### How was this patch tested?\n\nDocumentation only, so no automated tests.\n\nEvery factual claim was checked against `main`:\n\n- `api/.../authorization/Privilege.java` and `Privileges.java` for the\nprivilege names and the object types each can bind to\n- `core/.../Configs.java` for configuration keys, defaults, and\nsince-versions\n- `server/.../web/rest/*.java` and\n`AuthorizationExpressionConstants.java` for the per-operation conditions\nand for confirming authorization is enforced\n- `core/.../hook/MetalakeHookDispatcher.java` for the creator becoming\nuser and owner\n- `core/.../authorization/RoleManager.java` for role creation performing\nno existence check\n- `server-common/.../authentication/SimpleAuthenticator.java` and\n`OAuthConfig.java` for the identity settings\n- `clients/client-java/.../GravitinoClient.java` for the client method\nsignatures\n\nRendering was checked for heading nesting, table widths without\nhorizontal scroll, and the four internal anchors resolving.\n\n---------\n\nCo-authored-by: Mark Hoerth \u003cmark@datastrato.com\u003e"
    },
    {
      "commit": "29d40aa6404606b0caf99dcbf9366854f2cacfea",
      "tree": "8d598789a4ac5943d174524fe1a759b12806e61e",
      "parents": [
        "304ea72afdbc4bd13944fd49e169bc123b513c14"
      ],
      "author": {
        "name": "Phước",
        "email": "37379686+vanphuoc3012@users.noreply.github.com",
        "time": "Wed Jul 29 17:29:33 2026 +0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 18:29:33 2026 +0800"
      },
      "message": "[#12200] fix(web-v2): Gate metalake fetch until OAuth token is ready (#12203)\n\n### What changes were proposed in this pull request?\n\nThis PR updates the V2 Web UI metalakes page to avoid fetching metalakes\nbefore authentication bootstrap is ready.\n\nThe metalake store reset still runs when the page mounts, but the\nmetalake fetch now waits until the auth type is known. For OAuth/OIDC\nauthentication, it also waits until the OAuth token exists in Redux\nbefore calling the metalake API.\n\n### Why are the changes needed?\n\nBefore this change, the metalakes page called `fetchMetalakes()`\nimmediately on mount. In OAuth/OIDC mode, this could race with the\nasynchronous auth bootstrap after login. The page could request metalake\ndata before the access token was available, causing unauthorized API\ncalls, redirects back to login, or an empty metalakes page.\n\nFix: #12200\n\n### Does this PR introduce _any_ user-facing change?\n\nNo user-facing API or configuration changes.\n\nFor OAuth/OIDC users, the V2 Web UI avoids making the metalake API\nrequest until the OAuth token is ready.\n\n### How was this patch tested?\n\nRan formatter for the V2 Web UI:\n\n```bash\ncd web-v2/web \u0026\u0026 pnpm format\n```\n\nVerified the PR diff is minimal and only changes:\n\n```text\nweb-v2/web/src/app/metalakes/page.js\n```\n\nCo-authored-by: phuocho \u003cphuoc.ho@mservice.com.vn\u003e"
    },
    {
      "commit": "304ea72afdbc4bd13944fd49e169bc123b513c14",
      "tree": "e09d86550ee3dcbaaeb75002e675cdcb96991404",
      "parents": [
        "4d60cc71c2f87c4814559b9ebcb511174be4d309"
      ],
      "author": {
        "name": "roryqi",
        "email": "roryqi@datastrato.com",
        "time": "Wed Jul 29 18:05:07 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 18:05:07 2026 +0800"
      },
      "message": "[#12125] feat(design-docs): Add tag assignment values design (#12126)\n\n### What changes were proposed in this pull request?\n\nAdd a design document for tag assignment values.\n\nThe design covers:\n- multiple values per tag assignment\n- allowedValues on tag creation\n- one row per assignment value in tag_relation_meta\n- pair-array assignment updates through tagsToAdd and tagsToRemove\n- exact value lookup through GET /tags/{tag}/objects?value\u003d...\n\n### Why are the changes needed?\n\nTag assignments are currently boolean and cannot carry object-specific\nvalues. This design defines the API, storage, client, cache,\nauthorization, event, and test plan for assignment-level tag values.\n\nFix: #12125\n\n### Does this PR introduce _any_ user-facing change?\n\nNo code or API behavior changes. This PR only adds a design document.\n\n### How was this patch tested?\n\n- git diff --check apache/main...HEAD\n- Checked the design document for stale standalone search API references\nand placeholders."
    },
    {
      "commit": "4d60cc71c2f87c4814559b9ebcb511174be4d309",
      "tree": "0870ad78699036f81731e399b522e0d829cc9ab8",
      "parents": [
        "74de33af4bacc423162802b93f4cc3396fd624df"
      ],
      "author": {
        "name": "Mark Hoerth",
        "email": "47870294+markhoerth@users.noreply.github.com",
        "time": "Wed Jul 29 02:36:31 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 17:36:31 2026 +0800"
      },
      "message": "[MINOR] docs: update the Trino Iceberg REST engine page (#12242)\n\n### What changes were proposed in this pull request?\n\nRewrites `docs/iceberg-rest-engine/trino.md` so it covers the whole\nTrino catalog file rather than the connection properties alone, and\nlinks to `credential-vending.md` for catalog-side setup instead of\nrestating it.\n\nCorrections:\n\n- `fs.native-s3.enabled` is renamed to `fs.s3.enabled` as of Trino 481,\nwhen legacy object storage support was removed. The page documented only\nthe old name.\n- The native S3 file system floor was listed as Trino 430. It is 419.\n- The page stated that Trino vends credentials for S3 only, citing\ntrinodb/trino#23238 as an open feature request. Trino 481 closed it and\nadded Azure.\n- The credential refresh limitation was presented as current. Trino 481\nadded refreshable vended credentials, so it now applies only to earlier\nreleases.\n\nAdditions:\n\n- A capability-to-release table, since the page spans behavior that\nrequires Trino 419, 458, 481, and 482.\n- A section on which Gravitino catalogs the IRC serves, covering the\ndynamic and static config providers, the fact that `gravitino-metalake`\nnames exactly one metalake fixed at server startup while catalogs within\nit are picked up dynamically, and the three conditions Gravitino access\ncontrol needs over the IRC.\n- The credential vending request sequence, and an explicit statement\nthat vended credentials are scoped to the table path rather than to the\ncalling user.\n- A symptom-to-cause troubleshooting table.\n- Two known issues: the query JSON credential exposure advisory\n(GHSA-x27p-5f68-m644) and a version-specific consumption bug\n(trinodb/trino#27416).\n- Explanations of how each authentication option works, including where\nTrino obtains the token and how it presents it, and what\n`iceberg.rest-catalog.http-headers` actually is.\n\n### Why are the changes needed?\n\nA user configuring Trino against the Gravitino IRC could not answer\nseveral questions from this page and required a separate document to\ncomplete the setup. The page had two structural problems.\n\nIt documented the Trino side of credential vending while delegating the\nentire Gravitino side to a link, but most failures in this path\noriginate on the Gravitino side. A reader could write a correct Trino\ncatalog file and still get no vending, with nothing on the page to help\nthem locate the cause.\n\nIt also listed upstream bug reports under Known Issues but had no\ntroubleshooting section. Someone debugging arrives with a symptom, not\nan issue number, so the existing content did not match how the page was\nbeing used.\n\nSeparately, the property rename in Trino 481 meant the documented Quick\nStart would not start on a current Trino release.\n\n### Does this PR introduce any user-facing change?\n\nDocumentation only. No API or property changes in Gravitino. The page\nnow documents Trino property names that changed upstream:\n\n- `fs.s3.enabled` replaces `fs.native-s3.enabled` for Trino 481 and\nlater. Both names are documented, with the release each applies to.\n- Newly referenced Trino properties:\n`iceberg.rest-catalog.http-headers`,\n`iceberg.rest-catalog.case-insensitive-name-matching`,\n`iceberg.rest-catalog.oauth2.server-uri`.\n- Newly referenced Gravitino properties:\n`gravitino.iceberg-rest.catalog-config-provider`,\n`gravitino.iceberg-rest.gravitino-metalake`,\n`gravitino.iceberg-rest.default-catalog-name`.\n\n### How was this patch tested?\n\nThe configurations were exercised against Gravitino 1.3.0 and Trino 478\nwith AWS S3, twice: once with authorization disabled to isolate vending\nfrom identity, and once with OAuth2 against Keycloak and authorization\nenabled. In both runs Spark wrote the table and Trino read it back using\nvended STS credentials.\n\nBehavior at Trino 481 and later, including the property rename and\nrefreshable vended credentials, comes from Trino\u0027s documentation and\nrelease notes rather than from that run. The page marks which is which.\n\nCo-authored-by: Mark Hoerth \u003cmark@datastrato.com\u003e"
    },
    {
      "commit": "74de33af4bacc423162802b93f4cc3396fd624df",
      "tree": "2758edc5917af71e2be182eb2de45317fb4eb391",
      "parents": [
        "7dadbf76d6a65e9dbc40b02a1980ce132dc34b09"
      ],
      "author": {
        "name": "Qi Yu",
        "email": "yuqi@datastrato.com",
        "time": "Wed Jul 29 14:22:33 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 14:22:33 2026 +0800"
      },
      "message": "[#12237] fix(iceberg): de-flake IcebergClassLoaderPoolIT by tolerating concurrent JdbcCatalog view-migration on a shared iceberg_tables (#12240)\n\n### What changes were proposed in this pull request?\n\nDe-flake `IcebergClassLoaderPoolIT` and harden Iceberg JDBC catalog\ninitialization when several logical catalogs share one JDBC metadata\ndatabase:\n\n- Detect the narrow V1 migration conflict where `JdbcCatalog.initialize`\nfails because the `iceberg_type` column already exists.\n- Close the failed catalog\u0027s partially initialized JDBC connection pool\nand `FileIO` before discarding it. Iceberg creates these resources\nbefore the schema migration but does not register them with its\n`CloseableGroup` until the migration succeeds.\n- Initialize a fresh catalog once after the conflict. The shared schema\nhas already been migrated, so the retry can complete normally.\n- Recognize MySQL, PostgreSQL, and SQLite duplicate-column error\nwordings in `isConcurrentViewMigrationConflict`.\n- Keep the existing access-denied conversion null-safe and simplify the\nauthentication branch without changing its behavior.\n\n### Why are the changes needed?\n\nIceberg JDBC catalogs that use the same backend `uri` share the physical\n`iceberg_tables` and `iceberg_namespace_properties` control tables.\nDifferent `catalog-backend-name` values isolate their logical namespaces\nand tables through the `catalog_name` column, so this is a valid,\nalthough relatively narrow, deployment topology.\n\nGravitino defaults `jdbc.schema-version` to `V1`. Iceberg creates\n`iceberg_tables` without `iceberg_type`, checks for that column with\nJDBC metadata, and then runs a non-idempotent migration:\n\n```sql\nALTER TABLE iceberg_tables ADD COLUMN iceberg_type ...\n```\n\nThe conflict can occur when:\n\n1. Two JDBC catalog instances point to the same backend `uri`.\n2. They initialize concurrently while the shared control table is being\nmigrated to V1, or a JDBC metadata probe temporarily reports the\nalready-added column as missing.\n3. Both instances attempt the same `ALTER TABLE`; the losing instance\nfails with a duplicate-column error such as:\n\n```text\nCaused by: java.sql.SQLSyntaxErrorException: Duplicate column name \u0027iceberg_type\u0027\n  at org.apache.iceberg.jdbc.JdbcCatalog.executeV1CatalogUpdate(JdbcCatalog.java:276)\n  at org.apache.iceberg.jdbc.JdbcCatalog.updateSchemaIfRequired(JdbcCatalog.java:235)\n```\n\n`IcebergClassLoaderPoolIT` deliberately creates multiple catalogs on the\nsame MySQL JDBC URL because it verifies that catalogs with the same\nisolation key share a ClassLoader and that dropping one catalog does not\nbreak its sibling. This supported topology exposed the migration race in\n`JDK17-deploy-mysql`.\n\nThe duplicate-column error means another initializer has completed the\nshared migration. This patch therefore closes the losing catalog\u0027s\npartial resources and retries initialization once with a fresh catalog.\nOther SQL failures are still propagated unchanged.\n\nFix: #12237\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\n- Added classifier tests for MySQL, PostgreSQL, and SQLite\nduplicate-column messages, plus unrelated and null-message cases.\n- Added a deterministic failed-migration test that simulates the\nduplicate `iceberg_type` error and verifies that the JDBC pool,\nunderlying connection, and `FileIO` are closed.\n- `./gradlew :iceberg:iceberg-common:test\n:iceberg:iceberg-common:spotlessCheck` — all 64 tests pass.\n- `IcebergClassLoaderPoolIT` continues to cover the end-to-end\nshared-URI scenario in the deploy/MySQL CI."
    },
    {
      "commit": "7dadbf76d6a65e9dbc40b02a1980ce132dc34b09",
      "tree": "c65d9259330f0b92b44fd866f6abbe740a8803de",
      "parents": [
        "7270c750a4e1cd32756c1ce86dff1e7fbce50c75"
      ],
      "author": {
        "name": "Yuhui",
        "email": "hui@datastrato.com",
        "time": "Wed Jul 29 14:20:59 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 14:20:59 2026 +0800"
      },
      "message": "[MINOR] refactor(flink-connector): Add overridable toGravitinoType hook to BaseCatalog (#12244)\n\n### What changes were proposed in this pull request?\nAdded an overridable `toGravitinoType(LogicalType)` hook in\n`BaseCatalog` (Flink connector), replacing the hardcoded static\n`TypeUtils.toGravitinoType` calls with calls to this instance method.\n\n### Why are the changes needed?\nThe Flink connector\u0027s type conversion (`TypeUtils.toGravitinoType`) had\nno per-catalog override point, unlike the Spark and Trino connectors,\nwhich already special-case certain JDBC catalogs for type-mapping quirks\n(e.g. `SparkTypeConverter`). This adds the same extension point to the\nFlink connector so catalog-specific subclasses can override type mapping\nwhere needed.\n\n### Does this PR introduce any user-facing change?\nNo public API change. `BaseCatalog` subclasses can now override\n`toGravitinoType` to customize column type mapping.\n\n### How was this patch tested?\nExisting `TestBaseCatalog` suite updated to construct catalogs via the\n`TestableBaseCatalog` test helper instead of calling static methods, and\npasses."
    },
    {
      "commit": "7270c750a4e1cd32756c1ce86dff1e7fbce50c75",
      "tree": "4dd9bb258672773124f958e41704b26a0cc78a4e",
      "parents": [
        "f3a2551a61c42101e59b013cc354be50d8db4bca"
      ],
      "author": {
        "name": "Qi Yu",
        "email": "yuqi@datastrato.com",
        "time": "Wed Jul 29 12:01:34 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 12:01:34 2026 +0800"
      },
      "message": "[#12238] fix(core): Make PostgreSQL table version soft delete idempotent (#12243)\n\n### What changes were proposed in this pull request?\n\nAdd an active-record condition to the PostgreSQL table version\nsoft-delete SQL and add a regression test for the generated SQL.\n\n### Why are the changes needed?\n\nRepeated soft deletes could update an already deleted table version.\nWhen both updates generated the same millisecond timestamp, PostgreSQL\nreported a duplicate key violation.\n\nFix: #12238\n\n### Does this PR introduce _any_ user-facing change?\n\nNo API or configuration changes. Repeated PostgreSQL table version soft\ndeletes no longer fail with a duplicate key violation.\n\n### How was this patch tested?\n\n- `./gradlew spotlessApply`\n- `./gradlew :core:test --tests\norg.apache.gravitino.storage.relational.mapper.provider.postgresql.TestTableVersionPostgreSQLProvider\n-PskipITs -PskipDockerTests\u003dfalse`\n- `./gradlew :core:check -PskipITs -PskipDockerTests\u003dfalse`"
    },
    {
      "commit": "f3a2551a61c42101e59b013cc354be50d8db4bca",
      "tree": "aa2a4b2d516355d647277d760b3aa81aac4a68dd",
      "parents": [
        "02cafdb0da00b3949193d68476d7414aa8648c85"
      ],
      "author": {
        "name": "Qi Yu",
        "email": "yuqi@datastrato.com",
        "time": "Wed Jul 29 11:06:24 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 11:06:24 2026 +0800"
      },
      "message": "[#11736] fix(core): retry failed entity change log listeners and do not remove logs when polling. (#11739)\n\n### What changes were proposed in this pull request?\n\nThis PR makes `EntityChangeLogPoller` retry a batch when a listener\nfails, with a bounded number of retries:\n\n- Keep the fetched batch until every registered listener applies it.\n- Retry only listeners that have not succeeded, and stop waiting for one\nthat is unregistered.\n- Advance the high-water cursor after all pending listeners succeed.\n- Bound the retries with `gravitino.entityChangeLog.listenerMaxRetries`\n(default 10). Once a listener exhausts them,\n`gravitino.entityChangeLog.listenerFailureAction` decides what happens:\n`EXIT` (default) stops this server, because its local caches are known\nto be stale; `SKIP` drops the batch for that listener and keeps serving.\n`EXIT` goes through `System.exit`, so the server shutdown hook still\nstops the server gracefully.\n- Keep `CatalogChangeLogListener` swallowing its own failures instead of\npropagating them. `CatalogManager.consumeLocalMutation` is single-shot,\nso re-delivering an already-applied batch would invalidate a catalog\nthis process mutated itself and close its in-use `IsolatedClassLoader`.\nA dropped invalidation only costs staleness bounded by the catalog cache\nTTL.\n- Increase the fetch batch size from 500 to 2000.\n- Log poller startup, consumed ID ranges, cursor advancement, retries,\nand listener failures.\n- Move cleanup out of the polling path into a dedicated single-threaded\ncleaner, and run its first pass after a short randomized delay so that a\nfrequently restarted server still prunes and HA nodes do not delete the\nsame rows at once.\n- Retain change logs for 30 days by default and run cleanup daily.\n- Use database time for both insertion and expiration, and commit each\n1000-row cleanup batch.\n- Close every entity store component even if an earlier one fails or was\nnever created.\n\nThis PR intentionally does not address the commit-ordering gap or\ncache-expiration behavior tracked by #11736.\n\n### Why are the changes needed?\n\nPreviously, the poller advanced its cursor even when a listener threw,\npermanently dropping that batch for the failed listener. Retrying\nwithout a bound has the opposite problem: a permanently failing listener\nwould freeze cache invalidation for the whole process, so the retries\nare now bounded and the outcome is explicit. Cleanup was coupled to\npolling, used a non-committing session (so deletions were never\ncommitted), and calculated expiration from each server\u0027s JVM clock.\n\nFixed: #11736.\n\n### Does this PR introduce _any_ user-facing change?\n\nYes.\n\n- New config `gravitino.entityChangeLog.listenerMaxRetries` (default\n`10`).\n- New config `gravitino.entityChangeLog.listenerFailureAction` (default\n`EXIT`). With the default, a server whose change log listener keeps\nfailing after its retries stops itself instead of serving from caches it\nknows to be stale. Set it to `SKIP` to prefer availability.\n- The default change-log retention increases from 1 day to 30 days, and\nthe default cleanup interval changes from 1 hour to 1 day.\n\nBoth new configs are documented in `docs/gravitino-server-config.md`.\n\n### How was this patch tested?\n\n- `./gradlew :core:test -PskipITs -PskipDockerTests\u003dfalse`\n- Added coverage for failed-listener retries, the retry bound under both\n`EXIT` and `SKIP`, a 2001-record backlog, a paused batch blocking new\nfetches, catalog-listener failure containment, the cleaner lifecycle and\nits initial delay, independent batched cleanup, database-time SQL, and\ncommitted cleanup visibility from a new SQL session.\n- The pruning statements are exercised against H2. The PostgreSQL\nvariant is only asserted at the SQL-string level, matching the existing\nPostgreSQL provider tests in `core`; verifying it against a real\nPostgreSQL would need a new docker-tagged integration test."
    },
    {
      "commit": "02cafdb0da00b3949193d68476d7414aa8648c85",
      "tree": "279a7f41dbadcb41aba2c564784dd3e3fbfed609",
      "parents": [
        "b368aaf2b037200b5e3a65560cbbd2c808d88a98"
      ],
      "author": {
        "name": "roryqi",
        "email": "roryqi@datastrato.com",
        "time": "Tue Jul 28 16:40:03 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 28 16:40:03 2026 +0800"
      },
      "message": "[#12163] feat(api): Add tag assignment value APIs (#12162)\n\n### What changes were proposed in this pull request?\n\nAdd the public tag assignment value APIs with `TagValue`, tag allowed\nvalues, and valued tag association overloads.\n\n### Why are the changes needed?\n\nThis is the API layer for supporting multiple assignment values for a\ntag.\n\nFix: #12163\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. It adds `TagValue`, exposes tag allowed values, and adds valued tag\nassociation APIs while keeping existing valueless tag association APIs.\n\n### How was this patch tested?\n\n`./gradlew :api:spotlessApply`\n`./gradlew :api:test --tests org.apache.gravitino.tag.TestTagValue\n-PskipITs -PskipDockerTests\u003dfalse`"
    },
    {
      "commit": "b368aaf2b037200b5e3a65560cbbd2c808d88a98",
      "tree": "afb6623f16bd7176cc51ced81f0383af3d1836a2",
      "parents": [
        "ab2645a799eff40dbaeb36b1381695fc8fd7a802"
      ],
      "author": {
        "name": "jarred0214",
        "email": "jarred0214@gmail.com",
        "time": "Tue Jul 28 09:51:55 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 28 09:51:55 2026 +0800"
      },
      "message": "[#12051] fix(flink-connector): Move Paimon Hadoop options to Hadoop conf (#12121)\n\n### What changes were proposed in this pull request?\n\nMove Paimon Flink connector Hadoop filesystem options (`hadoop.*`,\n`fs.*`, and `dfs.*`) from Paimon catalog options into Hadoop\n`Configuration` before creating the inner Paimon catalog.\n\nThis creates the inner Paimon catalog with a `CatalogContext` that\ncarries the sanitized Paimon options plus Hadoop configuration, and\nkeeps vended Paimon S3/OSS/JDBC credentials available as Paimon options.\n\n### Why are the changes needed?\n\nPaimon can log dynamic/catalog options and expose sensitive filesystem\ncredentials such as OSS/BOS access keys when they remain in Paimon\noptions. Moving Hadoop filesystem credentials to Hadoop configuration\nfollows the Flink Hive connector pattern and avoids passing these\nsensitive Hadoop properties through Paimon options.\n\nFix: #12051\n\n### Does this PR introduce _any_ user-facing change?\n\nNo API changes. Existing Hadoop filesystem catalog properties continue\nto be accepted, but the Flink Paimon connector forwards them via Hadoop\n`Configuration` instead of Paimon catalog options.\n\n### How was this patch tested?\n\nAdded unit coverage for:\n- moving `hadoop.*`/`fs.*` filesystem options into Hadoop configuration\n- keeping vended S3/OSS credentials in Paimon options for native Paimon\nFileIOs\n- keeping JDBC credentials in Paimon options\n\nLocal checks:\n- `git diff --check`\n\nCould not run Gradle tests locally because only JDK 8 is installed and\nthe build requires JDK 17."
    },
    {
      "commit": "ab2645a799eff40dbaeb36b1381695fc8fd7a802",
      "tree": "853432eab2719a7fda2d416a4313d7efcec11fa8",
      "parents": [
        "e1afb700e959035c3062b39b0286f2ddc1b3103a"
      ],
      "author": {
        "name": "roryqi",
        "email": "roryqi@datastrato.com",
        "time": "Mon Jul 27 15:49:50 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 15:49:50 2026 +0800"
      },
      "message": "[#12112] fix(authz): Align group load authorization (#12113)\n\n### What changes were proposed in this pull request?\n\n- Add an explicit authorization expression for loading group details.\n- Include metalake and group path parameters in the authorization\nmetadata context.\n- Support GROUP::SELF in JCasbin authorization checks.\n- Add regression tests for group load authorization metadata and\nGROUP::SELF evaluation.\n\n### Why are the changes needed?\n\nThe group load API should follow the same authorization interception\npath as related user and group access-control APIs.\n\nFix: #12112\n\n### Does this PR introduce _any_ user-facing change?\n\nNo public API change. Group detail loading now follows the expected\nauthorization behavior.\n\n### How was this patch tested?\n\n- `./gradlew :server:test --tests\norg.apache.gravitino.server.web.rest.authorization.TestGroupAuthorizationExpression\n--tests org.apache.gravitino.server.web.rest.TestGroupOperations`\n- `./gradlew :server-common:test --tests\norg.apache.gravitino.server.authorization.jcasbin.TestJcasbinAuthorizer`\n- `git diff --check`"
    },
    {
      "commit": "e1afb700e959035c3062b39b0286f2ddc1b3103a",
      "tree": "fe9126db7f41778fcc94b02afe89f3def7a47cd0",
      "parents": [
        "e4e66e446a07e993aa4df81c248c3d19fc8a8d4e"
      ],
      "author": {
        "name": "StormSpirit",
        "email": "jiangxt2@vip.qq.com",
        "time": "Mon Jul 27 15:28:03 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 15:28:03 2026 +0800"
      },
      "message": "[#11881] fix(clickhouse): distinguish MATERIALIZED/ALIAS from DEFAULT via default_kind (#11886)\n\n### What changes were proposed in this pull request?\n\nFix column default value handling for MATERIALIZED and ALIAS columns in\nthe ClickHouse catalog.\n\nClickHouse distinguishes column default value kinds via\n`system.columns.default_kind` (DEFAULT,\nMATERIALIZED, ALIAS). The JDBC driver`getColumns()` hardcodes `\u0027NO\u0027` as\n`IS_GENERATEDCOLUMN`\n(clickhouse-java#1625 pattern), so the catalog cannot distinguish these\nkinds through standard\nJDBC metadata.\n\nChanges:\n- Added `getDefaultKinds()` method that queries `system.columns` for\n`default_kind`, following\nthe same pattern as `getIndexes()` which already bypasses the JDBC\ndriver for similar limitations\n- `load()` now calls `getDefaultKinds()` and uses the result to correct\ncolumn metadata inline\n  during the column loading loop, without exposing instance state\n- Fixed `ClickHouseContainer.getJdbcUrl()` to use `localhost` +\n`getMappedPort()` instead of\n`getContainerIpAddress()`, consistent with how `DorisContainer` handles\nmacOS Docker Desktop\n  compatibility\n\nThread safety: `ClickHouseTableOperations` is a shared singleton (cached\nby CatalogManager\u0027s\nCaffeine cache, accessed concurrently via JettyServer\u0027s\nQueuedThreadPool). The `default_kinds`\nmap is a local variable in `load()`, not an instance field, making the\nmethod reentrant.\n\n### Why are the changes needed?\n\nFix: #11881\n\nMATERIALIZED and ALIAS columns were indistinguishable from DEFAULT\ncolumns on round-trip,\nbecause the catalog never fetched `default_kind` from ClickHouse system\ntables.\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. MATERIALIZED and ALIAS columns now have correct default value kinds\nin Gravitino metadata,\nenabling consumers to distinguish them from DEFAULT columns.\n\n### How was this patch tested?\n\n**Unit tests** (`TestClickHouseTableOperations`):\n- `testLoadTableWithMaterializedAndAliasColumns`: creates table with all\nthree default kinds\nvia raw SQL, verifies all three columns return\n`UnparsedExpression(\"today()\")` via their\n  respective code paths\n\n**Integration tests** (`CatalogClickHouseIT`):\n- `testMATERIALIZEDAndALIASColumnDefaultKinds`: end-to-end test through\nGravitino API covering\nDEFAULT/MATERIALIZED/ALIAS × multiple types (Date, Int64, String) ×\nNullable columns\n\nBoth test classes require `-PskipDockerTests\u003dfalse` to run.\n\n---------\n\nSigned-off-by: jiangxt2 \u003cjiangxt2@vip.qq.com\u003e"
    },
    {
      "commit": "e4e66e446a07e993aa4df81c248c3d19fc8a8d4e",
      "tree": "3c363e5cafc1f55330d48c1d1d83aafcdb0eb45e",
      "parents": [
        "5dfa38803e577a3c666ff23374e6158bb5988da7"
      ],
      "author": {
        "name": "Henry Chen",
        "email": "henrychen@apache.org",
        "time": "Mon Jul 27 15:04:26 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 15:04:26 2026 +0800"
      },
      "message": "[#3767] refactor(client-python): Share authorization integration test setup (#11994)\n\n### What changes were proposed in this pull request?\n\n- Add shared helpers in `IntegrationTestEnv` for authorization\nconfiguration, external Gravitino detection, server start/restart\nbehavior, and metalake setup/cleanup.\n- Add `AuthorizationIntegrationTestEnv` to share common authorization\nintegration test lifecycle.\n- Update user, owner, group, and role integration tests to reuse the\nshared setup helpers.\n- Move group and role management tests onto the shared authorization\nintegration test base class.\n\n### Why are the changes needed?\n\nSeveral Python client authorization integration tests duplicated the\nsame authorization config setup, Gravitino server lifecycle handling,\nand metalake setup/cleanup logic.\n\nCentralizing this logic reduces duplicate test boilerplate and keeps the\nauthorization integration tests consistent while preserving their\nexisting behavior.\n\nRelated to #3767.\n\n### Does this PR introduce *any* user-facing change?\n\nNo.\n\n### How was this patch tested?\n\n- `/private/tmp/gravitino-client-python-test-venv/bin/black\nclients/client-python/tests/integration/integration_test_env.py\nclients/client-python/tests/integration/test_user.py\nclients/client-python/tests/integration/test_owner.py\nclients/client-python/tests/integration/test_group_management.py\nclients/client-python/tests/integration/test_role_management.py`\n- `PYTHONPATH\u003d. PYLINTHOME\u003d/private/tmp/gravitino-pylint-cache\n/private/tmp/gravitino-client-python-test-venv/bin/pylint\n--rcfile\u003dpylintrc --persistent\u003dn\ntests/integration/integration_test_env.py tests/integration/test_user.py\ntests/integration/test_owner.py\ntests/integration/test_group_management.py\ntests/integration/test_role_management.py`\n  - Result: `10.00/10`\n- `PYTHONPATH\u003d.\n/private/tmp/gravitino-client-python-test-venv/bin/python -m py_compile\ntests/integration/integration_test_env.py tests/integration/test_user.py\ntests/integration/test_owner.py\ntests/integration/test_group_management.py\ntests/integration/test_role_management.py`\n- `git diff --check`\n\n---------\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nCo-authored-by: Jerry Shao \u003cjerryshao@datastrato.com\u003e"
    },
    {
      "commit": "5dfa38803e577a3c666ff23374e6158bb5988da7",
      "tree": "23c02df9b3440b24abeee3fb96825afb94d31af9",
      "parents": [
        "a7d221cd0a33fbce27931bb7889bee7ba32385c2"
      ],
      "author": {
        "name": "Qi Yu",
        "email": "yuqi@datastrato.com",
        "time": "Mon Jul 27 14:41:21 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 14:41:21 2026 +0800"
      },
      "message": "[#12145] fix(test): prevent overlapping embedded server lifecycle (#12146)\n\n### What changes were proposed in this pull request?\n\nWait for the embedded Gravitino server task to terminate completely in\n`MiniGravitino.stop()` instead of treating an unavailable HTTP port as\ncompletion.\n\nAlways close the REST client and delete the temporary configuration\ndirectory,\neven when server termination times out or is interrupted. Preserve the\noriginal\nshutdown failure and attach REST client cleanup failures as suppressed\nexceptions.\n\n### Why are the changes needed?\n\nEmbedded integration tests repeatedly start and stop Gravitino servers\nin the\nsame JVM. The HTTP port can close before `GravitinoEnv.shutdown()`\nfinishes,\nallowing the next server to initialize the singleton environment while\nthe old\nserver is still shutting down.\n\nThe old shutdown can then close the new server catalog manager, leaving\nits\n`ClassLoaderPool` permanently closed and causing catalog creation to\nfail with:\n\n`IllegalStateException: ClassLoaderPool is already closed`\n\nFix: #12145\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\n- `./gradlew :integration-test-common:check\n:integration-test-common:spotlessCheck -PskipITs`\n- `./gradlew :core:test --tests\norg.apache.gravitino.catalog.TestClassLoaderPoolIntegration\n:integration-test-common:compileTestJava -PskipITs`\n- `./gradlew :clients:client-java:test --tests\norg.apache.gravitino.client.integration.test.MetalakeIT\n-PskipDockerTests\u003dtrue`\n- `git diff --check`\n\n---------\n\nSigned-off-by: yuqi \u003cyuqi@datastrato.com\u003e"
    },
    {
      "commit": "a7d221cd0a33fbce27931bb7889bee7ba32385c2",
      "tree": "0ec0e6e9114046a1d0fc2c1bad19101d7871e839",
      "parents": [
        "4d72543e0cd0beccc36acbd8adabfd7cd6d32475"
      ],
      "author": {
        "name": "Zhiguo Wu",
        "email": "wzg547228197@gmail.com",
        "time": "Mon Jul 27 14:36:40 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 14:36:40 2026 +0800"
      },
      "message": "[#12181] feat(client-python): Support tags for views and functions (#12182)\n\n### What changes were proposed in this pull request?\n\nThis PR adds Python client support for tag operations on views and\nfunctions.\n\nChanges include:\n\n- Add default `supports_tags()` methods to the `View` and `Function` API\nclasses.\n- Extend `GenericView` and `GenericFunction` with tag operations backed\nby `MetadataObjectTagOperations`.\n- Return tag-capable wrappers from Python client view and function\noperations.\n- Add Python client unit and integration tests for view/function tag\nsupport.\n\n### Why are the changes needed?\n\n#11844 enables server-side tag association for `VIEW` and `FUNCTION`\nmetadata objects. The Python client should expose the same capability so\nusers can manage tags for views and functions through the typed client\nAPI, consistent with other tag-capable metadata objects.\n\nFix: #12181\nRelated: #11844\n\n### Does this PR introduce _any_ user-facing change?\n\nYes.\n\nPython client users can now call `supports_tags()` on `View` and\n`Function` objects returned by the client, for example:\n\n```python\nview.supports_tags().associate_tags(...)\nfunction.supports_tags().list_tags()\n```\n\n### How was this patch tested?\n\n- `./gradlew :clients:client-python:black :clients:client-python:pylint\n:clients:client-python:unitTests`\n- `./gradlew :clients:client-python:integrationTest`"
    },
    {
      "commit": "4d72543e0cd0beccc36acbd8adabfd7cd6d32475",
      "tree": "47f0adf4fb9fb922ec197fc034e53f0789ed53b2",
      "parents": [
        "074a288d85806748c4e30fb40d2e0356da49de12"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Sun Jul 26 19:33:57 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 10:33:57 2026 +0800"
      },
      "message": "[#12131] feat(kms): Configure and resolve named sources (#12133)\n\n### What changes were proposed in this pull request?\n\nAdd server-side configuration, resolution, and lifecycle management for\nnamed KMS sources:\n\n- parse and validate `gravitino.kms.*` source configuration;\n- discover `KmsClientFactory` implementations with `ServiceLoader`;\n- create and own one reusable client per configured source during\nenvironment initialization;\n- resolve each `KmsReference` to its configured source and API;\n- close clients in reverse order, including after partial startup\nfailure; and\n- define provider-client concurrency and ownership expectations.\n\n`KmsClientFactory.create(...)` validates local configuration and\nconstructs a reusable client without contacting the configured KMS.\nNetwork, authentication, and availability failures are reported by\nclient operations and do not invalidate the reusable client.\n\nThis builds on the provider-neutral KMS contracts merged in #12132.\nConcrete provider implementations remain outside this PR.\n\n### Why are the changes needed?\n\nA deployment can use multiple named KMS instances, including multiple\ninstances of the same provider. Gravitino needs one server-side resolver\nand lifecycle owner so callers do not construct provider SDK or HTTP\nclients per request or depend on provider-specific configuration.\n\nCreating clients during environment initialization validates the\ncomplete local configuration deterministically without making startup\ndepend on KMS availability.\n\nPart of #12131.\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. It introduces these server configuration properties:\n\n- `gravitino.kms.sources`\n- `gravitino.kms.source.\u003cname\u003e.api`\n- `gravitino.kms.source.\u003cname\u003e.\u003cprovider-property\u003e`\n\nProvider properties remain server-private. The registry is initialized\nwith both base and full Gravitino environments. Client construction does\nnot connect to the configured KMS.\n\n### How was this patch tested?\n\n- `./gradlew :core:test --tests\norg.apache.gravitino.encryption.kms.TestKmsConfig --tests\norg.apache.gravitino.encryption.kms.TestKmsClientRegistry --tests\norg.apache.gravitino.TestGravitinoEnvKmsClientRegistry -PskipITs\n-PskipDockerTests\u003dtrue -PskipWeb\u003dtrue`\n- `./gradlew :common:spotlessCheck :core:spotlessCheck :common:javadoc\n:core:javadoc -PskipITs -PskipDockerTests\u003dtrue -PskipWeb\u003dtrue`\n\n### Review size\n\nThis PR contains 1,129 additions: 434 production lines and 695 test\nlines. Configuration parsing, client resolution, eager lifecycle\nmanagement, and server wiring form one usable unit. Splitting them would\nleave unused internal code or separate lifecycle tests from the behavior\nthey verify.\n\n### Series\n\n- Epic: #12131\n- Previous: #12132 (merged)\n- Position: 2 of 2"
    },
    {
      "commit": "074a288d85806748c4e30fb40d2e0356da49de12",
      "tree": "45f61ae47f41be73b35f22f98eb04fff8519c900",
      "parents": [
        "fd8afd6e33edaaa717330bbf8603601d136a9215"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Sun Jul 26 19:06:42 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 10:06:42 2026 +0800"
      },
      "message": "[#12201] refactor(core): inject table hook dependencies (#12202)\n\n### What changes were proposed in this pull request?\n\nThis pull request:\n\n- Injects `Supplier\u003cOwnerDispatcher\u003e` and `CatalogManager` into\n`TableHookDispatcher`.\n- Wires both dependencies from `GravitinoEnv`.\n- Replaces singleton- and reflection-based test setup with focused\ndispatcher unit tests.\n- Adds focused `AuthorizationUtils` tests for table rename and removal\npayloads.\n- Covers normalized ownership, disabled authorization, privilege\nremoval, privilege rename, result propagation, and owner-assignment\nfailure.\n\nGravitinoEnv becomes composition-root-only in the touched areas; not\ndeleted.\n\n### Why are the changes needed?\n\n`TableHookDispatcher` currently resolves collaborators through\n`GravitinoEnv.getInstance()`. This makes its dependencies implicit and\nforces\ntests to mutate shared singleton state.\n\nConstructor injection makes the dependency relationship explicit while\npreserving lazy owner resolution and authorization-disabled behavior.\n\n#### Test design rationale\n\nThe previous `TestTableHookDispatcher` was an over-scoped hybrid rather\nthan a\nfocused unit test or a true end-to-end test. It combined hook\norchestration with\nreal table and schema operations, shared singleton mutation, lock setup,\nand\nmocked authorization collaborators.\n\nReviewing its assertions exposed two fidelity problems:\n\n- The drop test mocked `AuthorizationUtils` and did not verify that\nprivilege\n  removal was requested.\n- The rename test only verified `onMetadataUpdated(any())`; it did not\nverify\n  the metadata change type, old and new table identities, or locations.\n\nThe replacement separates those responsibilities:\n\n- `TestTableHookDispatcher` verifies delegation, returned-result\npropagation,\nowner handling, and the exact authorization helper calls and arguments.\n- `TestAuthorizationUtils` executes the real rename and removal helpers\nand\n  verifies that the authorization plugin receives exactly one\n`RenameMetadataObject` or `RemoveMetadataObject` with the expected table\n  identity and locations.\n- Existing operation-level tests cover real table behavior:\n  `TestTableOperationDispatcher#testCreateAndDropTable` and\n  `TestManagedTableOperations#testAlterTable`.\n- Existing Docker-backed Ranger tests provide broader integration\ncoverage:\n`RangerHiveHdfsE2EIT#testRenameTable` exercises the client, REST\nresource,\n`TableHookDispatcher`, `AuthorizationUtils`, and Ranger plugin, then\nverifies\n  permissions at the renamed HDFS path.\n`RangerBaseE2EIT#testRenameMetadataObjectPrivilege` renames through the\nsame\n  server path and verifies operations on the renamed table.\n- `RangerHiveIT#testMetadataObjectChangeRenameTable` and\n`#testMetadataObjectChangeRemoveTable` directly verify the resulting\nRanger\n  policy changes.\n\n##### Coverage map\n\n| Previous test or behavior | Coverage after this change |\n| --- | --- |\n| `testDropAuthorizationPrivilege` | The rewritten test verifies the\ndelegated result and exact remove-helper arguments.\n`TestAuthorizationUtils` now verifies the real `RemoveMetadataObject`\npayload. Real create/drop behavior remains in\n`TestTableOperationDispatcher#testCreateAndDropTable`. |\n| `testCreateTableSetsOwnerWithNormalizedIdentifier` | The same owner\ncall and normalized table/schema assertions are retained using injected\nmocks, and delegate-result propagation is now asserted. |\n| `testCreateTableThrowsWhenSetOwnerFails` | The same exception type and\nmessage are retained. The unrelated real table-create path remains\ncovered by table-operation tests. |\n| `testRenameAuthorizationPrivilege` | The rewritten hook test verifies\nnon-rename behavior, exact rename-helper arguments, and result\npropagation. `TestAuthorizationUtils` verifies the real rename payload,\nwhile the Ranger tests retain broader integration coverage. |\n| Inherited `testGetCatalogIdentifier` | It still runs through\n`TestTableOperationDispatcher` and other `TestOperationDispatcher`\nsubclasses; removing inheritance here only removes a duplicate\nexecution. |\n| Authorization disabled | A new test verifies that owner/catalog work\nis skipped while the delegated table result is preserved. |\n\nThis gives each layer a focused contract while retaining broader\nend-to-end\ncoverage.\n\nFixes #12201\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. This changes internal server wiring and tests only. It does not\nchange\npublic APIs, REST contracts, or configuration.\n\n### How was this patch tested?\n\n- `./gradlew spotlessApply`\n- `./gradlew :core:test :server:test -PskipITs`\n\nThe Docker-backed Ranger tests listed above are pre-existing coverage\nand were\nnot executed by the `-PskipITs` verification command."
    },
    {
      "commit": "fd8afd6e33edaaa717330bbf8603601d136a9215",
      "tree": "7657973e8e9f1584c1f3cfac02f1582bc58c25f6",
      "parents": [
        "8f3211278a9ff9d81ae1924cac5d1e8261093590"
      ],
      "author": {
        "name": "Niraj Mahajan",
        "email": "nirajmahajan61452@gmail.com",
        "time": "Sun Jul 26 10:35:30 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 26 13:05:30 2026 +0800"
      },
      "message": "[MINOR] docs: Fix minor typos and inaccurate build instructions in documentation (#12192)\n\n### What changes were proposed in this pull request?\n\nFix minor typos and inaccurate build instructions in documentation\n\n### Why are the changes needed?\n\nJust a minor.\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\nPass CI."
    },
    {
      "commit": "8f3211278a9ff9d81ae1924cac5d1e8261093590",
      "tree": "2ed94857677b563ac36a4aa95803fd4276bad0db",
      "parents": [
        "bc2b09b7a17cf229debff1e70e58b1ab7434c8a5"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Sat Jul 25 22:04:36 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 26 13:04:36 2026 +0800"
      },
      "message": "[#12131] feat(kms): Define key inspection API (#12132)\n\n### What changes were proposed in this pull request?\n\nAdd provider-neutral Java contracts for server-private KMS key\ninspection:\n\n- `api`: `KmsReference` (api / source / keyId) and `KmsApiIdentifiers`\nvalidation helper\n- `common`: `KmsClient` / `KmsClientFactory` SPI, `KmsKeyProperties`,\nconfig/auth exceptions, `KmsReferenceDTO`, reusable fake + contract\ntests\n\nKey identity rules:\n- `api` is a `String` matched exactly (no trim/lowercase normalization)\n- identifiers must be lowercase kebab-case with no padding (e.g.\n`aws-kms`), enforced by `KmsApiIdentifiers`\n- `source` is trimmed; provider-native `keyId` is preserved as-is\n- unknown/custom APIs are allowed at the contract layer; dispatch\nremains SPI-based\n\nInspection semantics:\n- `KmsClient.getKeyProperties` returns `Optional\u003cKmsKeyProperties\u003e`\n- `Optional.empty()` means authoritative key-not-found\n- configuration vs authentication failures remain distinct exception\ntypes\n\n### Why are the changes needed?\n\nKMS integrations need a common contract that does not expose credentials\nor key material. Using string API identifiers (instead of a closed enum)\nkeeps the Developer API easier to extend through SPI, consistent with\ncatalog/credential provider patterns.\n\nThis PR defines only the provider-neutral API/SPI. Named-source routing\nfollows in #12133. Concrete providers, provider ITs, server\nconfiguration, and cryptographic operations are outside this PR.\n\nPart of #12131.\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. It adds new `@DeveloperApi` Java contracts. It does not add REST\nAPIs, server configuration, concrete providers, or cryptographic\noperations.\n\n### How was this patch tested?\n\n- `./gradlew :api:test :common:test`\n- `./gradlew :api:spotlessCheck :common:spotlessCheck`\n- `./gradlew :common:testFixturesJar`\n- `./gradlew :api:javadoc :common:javadoc`\n\n### Series\n\n- Epic: #12131\n- Position: 1 of 2\n- Previous: none\n- Next: #12133\n- Full stack: #12132 → #12133\n\n---------\n\nCo-authored-by: Cursor \u003ccursoragent@cursor.com\u003e"
    },
    {
      "commit": "bc2b09b7a17cf229debff1e70e58b1ab7434c8a5",
      "tree": "17f5a62dda37c85283bfd29ac8e7db67b7563499",
      "parents": [
        "a33fa0d1d7fd5ee4e1010c14008662e901eef401"
      ],
      "author": {
        "name": "YangJie",
        "email": "yangjie01@baidu.com",
        "time": "Fri Jul 24 19:21:13 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 24 19:21:13 2026 +0800"
      },
      "message": "[#12024] improvement(common): harden JDBC unsafe-parameter detection against connectionProperties smuggling (#12025)\n\n### What changes were proposed in this pull request?\n\n- Replace the config-**value** scan in `JdbcUrlUtils` with a\nconfig-**name** scan: every config key, plus each parameter name\nembedded in DBCP2\u0027s `connectionProperties` value.\n- Parse `connectionProperties` exactly as DBCP2 does (`Properties.load`\nafter replacing `;`→`\\n`); reject a malformed value fail-closed.\n- Use `Locale.ROOT` for all case folding.\n\n### Why are the changes needed?\n\n`JdbcUrlUtils.validateJdbcConfig` guards against dangerous JDBC\nparameters (e.g. MySQL `autoDeserialize`, an RCE enabler) but only\nmatched config *values* and never inspected DBCP2\u0027s\n`connectionProperties`. An unsafe parameter could be smuggled to the\ndriver as a config key, or inside `connectionProperties` (reachable via\n`gravitino.bypass.connectionProperties\u003dautoDeserialize\u003dtrue`, whose\nprefix is stripped before the map reaches the datasource), fully\nbypassing the check.\n\nFix: #12024\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. A configuration that was already unsafe is now rejected earlier with\na clear error. The exception message wording changes from \"...detected\nin JDBC URL\" to \"...detected in JDBC configuration\" to reflect that\nconfig keys/`connectionProperties` are inspected too, not only the URL.\n\n### How was this patch tested?\n\nNew unit tests in `TestJdbcUrlUtils` (connectionProperties for\nMySQL/MariaDB/PostgreSQL, config-key detection, newline / `\\uXXXX` /\ndouble-URL-encoding smuggling, whole-name-vs-substring and value-vs-name\nnegative controls, Turkish-locale folding, malformed-value rejection)\nand an end-to-end `TestDataSourceUrlValidation` case through\n`DataSourceUtils.createDataSource`. `./gradlew :common:test\n:catalogs:catalog-jdbc-common:test -PskipITs` and spotless pass."
    },
    {
      "commit": "a33fa0d1d7fd5ee4e1010c14008662e901eef401",
      "tree": "6889a3db394d83f7bb0327dec3a949b61f0e09a5",
      "parents": [
        "2214a29dcc3d6091b52f0ee4ad8eedee92615393"
      ],
      "author": {
        "name": "jarred0214",
        "email": "jarred0214@gmail.com",
        "time": "Fri Jul 24 16:44:50 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 24 16:44:50 2026 +0800"
      },
      "message": "[#12169] fix(auth): clear only role policies on cache eviction (#12172)\n\n### What changes were proposed in this pull request?\n\nThis PR updates `JcasbinLoadedRolesCache` to clear only the loaded role\npolicies when a role cache entry is evicted.\n\nInstead of calling `deleteRole(roleId)`, the cache eviction now\ndelegates to `JcasbinAuthorizer#clearRolePolicies`, which removes only\n`p(roleId, ...)` policies from the allow and deny enforcers.\n\nThe test is also updated to verify that cache invalidation removes the\nrole policies but preserves the user-role bindings.\n\n### Why are the changes needed?\n\n`loadedRoles` tracks whether a role\u0027s permission policies have been\nloaded into jCasbin. It should only own role policy cleanup.\n\nCalling `deleteRole(roleId)` also removes `g(user/group, roleId)` role\nbindings. This can cause a transient authorization failure when a\nrequest has already bound user roles, then the loaded role cache entry\nexpires and removes the binding before enforcement. In that case,\njCasbin may return `Hit Policy: []`, while a later retry succeeds after\nthe binding is added again.\n\nUsing `clearRolePolicies` keeps role policy refresh behavior while\navoiding accidental removal of user/group role bindings.\n\nFix: #12169\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\n```bash\n./gradlew spotlessApply :server-common:test --tests org.apache.gravitino.server.authorization.jcasbin.TestJcasbinAuthorizer -PskipITs -PskipDockerTests\u003dfalse"
    },
    {
      "commit": "2214a29dcc3d6091b52f0ee4ad8eedee92615393",
      "tree": "207bd92086b1a177f7e174de83d5caa3c1c6fcac",
      "parents": [
        "c54b342ac8bb3f147ea9bbdb5aed811e58648cc0"
      ],
      "author": {
        "name": "Zhiguo Wu",
        "email": "wzg547228197@gmail.com",
        "time": "Fri Jul 24 11:13:54 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 24 11:13:54 2026 +0800"
      },
      "message": "[#12158] improvement(client-python): Add view query/alter operations (#12159)\n\n### What changes were proposed in this pull request?\n\n- Add view list, load, existence check, and alter operations to\n`RelationalCatalog`.\n- Add request DTOs for view updates.\n- Support rename, set/remove property, and replace view changes.\n- Add corresponding unit and integration tests.\n\n### Why are the changes needed?\n\nThe Python client currently supports view create/drop operations but\nlacks view query/alter operations. These changes complete the basic view\nmanagement workflow through the Python client.\n\nFix: #12158\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. Python client users can now list, load, check, and alter views\nthrough `RelationalCatalog.as_view_catalog()`.\n\n### How was this patch tested?\n\nAdded unit tests for view query operations, update request conversion,\nserialization, validation, and error handling.\n\nAdded integration tests covering view listing, loading, existence\nchecks, property updates, and related error cases."
    },
    {
      "commit": "c54b342ac8bb3f147ea9bbdb5aed811e58648cc0",
      "tree": "9a2e5b78f40d02953172968e55f4778076d7a594",
      "parents": [
        "9e790c649ddc421bf389f2105641c99f27fdc8e9"
      ],
      "author": {
        "name": "Qi Yu",
        "email": "yuqi@datastrato.com",
        "time": "Fri Jul 24 09:22:56 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 24 09:22:56 2026 +0800"
      },
      "message": "[#12154] fix(core): Clean up orphaned metadata object relations (#12160)\n\n### What changes were proposed in this pull request?\n\nAdd a garbage collection pass to soft-delete metadata object relation\nrows whose referenced entity no longer has a live record.\n\nThe cleanup:\n\n- covers owner, tag, policy, statistic, and role securable-object\nrelations;\n- iterates over supported metadata object types;\n- processes orphaned object IDs in bounded batches;\n- runs on the existing relational garbage collector schedule;\n- remains idempotent.\n\n### Why are the changes needed?\n\nMetadata object relation tables reference entities using plain object ID\nand type columns without foreign keys. If an entity row is lost outside\nthe normal transactional deletion path, its relation rows remain\norphaned indefinitely.\n\nThis change provides background cleanup for those orphaned rows while\nleaving relations belonging to live entities untouched.\n\nFix: #12154\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. This change only adds background cleanup for orphaned\nrelational-store data.\n\n### How was this patch tested?\n\nAdded tests covering:\n\n- cleanup of orphaned owner, tag, policy, statistic, and\nsecurable-object relations;\n- preservation of relations referencing live entities;\n- idempotent repeated cleanup;\n- repeated batch execution by the relational garbage collector.\n\nRan:\n\n`./gradlew :core:test -PskipITs -PskipDockerTests\u003dtrue`"
    },
    {
      "commit": "9e790c649ddc421bf389f2105641c99f27fdc8e9",
      "tree": "99c5730785a7d2d41a8f77a9db16828070e4d005",
      "parents": [
        "cf1b173ca8bf99b719573a407c2923a95d7127fa"
      ],
      "author": {
        "name": "Bharath Krishna",
        "email": "bmurali@roku.com",
        "time": "Thu Jul 23 10:26:05 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 23 10:26:05 2026 -0700"
      },
      "message": "[#12099] feat(mcp-server): add read tools for table partitions (#12128)\n\n### What changes were proposed in this pull request?\n\nAdd two read-only MCP tools for table partitions:\n\n- `list_of_partitions(catalog, schema, table, details\u003dfalse)` — returns\npartition\n  names by default, or full partition metadata when `details\u003dtrue`.\n- `get_partition(catalog, schema, table, partition)` — returns the\nmetadata of a\n  single partition.\n\nThey follow the existing metadata-type pattern (interface → plain REST\nclient →\nFastMCP tool → factory wiring → mock + unit tests). All user-supplied\npath\nsegments are URL-encoded, and authorization is enforced by Gravitino\n(`LOAD_TABLE`), so no filtering logic is added in the MCP layer.\n\n### Why are the changes needed?\n\nThe server already exposes `list_statistics_for_partition`, which\nrequires\npartition names as input, but there was no tool to discover those names.\nThese\ntools close that gap and round out read coverage for partitioned tables.\n\nFix: #12099\n\n### Does this PR introduce _any_ user-facing change?\n\nYes — two new MCP tools (`list_of_partitions`, `get_partition`), tagged\n`partition`.\n\n### How was this patch tested?\n\nAdded unit tests for the tools (`test_partition.py`) and for\nURL-encoding /\nquery-param handling (`test_url_encoding.py`). Full unit suite passes\n(163 tests); `black` and `pylint` (10/10) are clean."
    },
    {
      "commit": "cf1b173ca8bf99b719573a407c2923a95d7127fa",
      "tree": "22d2dfb5a5cb2b701568e3f157f0641580e09537",
      "parents": [
        "1249341afdd47b5748a144eafd0f16621145df71"
      ],
      "author": {
        "name": "Mark Hoerth",
        "email": "47870294+markhoerth@users.noreply.github.com",
        "time": "Thu Jul 23 03:15:53 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 23 18:15:53 2026 +0800"
      },
      "message": "[MINOR] docs: update credential vending, add vended credentials to Trino (#12165)\n\n### What changes were proposed in this pull request?\n\nRewrites `docs/security/credential-vending.md` and fixes\n`docs/iceberg-rest-engine/trino.md`.\n\n**docs/security/credential-vending.md**\n\n- Restructure around where credential vending properties are set. The\ntwo property columns held the same setting twice, differing only by a\n`gravitino.iceberg-rest.` prefix, so they collapse to one column plus a\nsingle stated rule.\n- Add a quick start showing a working catalog and engine configuration.\n- Add the IAM trust and permission policies for `s3-token`, and the\nequivalent role requirements for `oss-token`, `adls-token`, and\n`gcs-token`.\n- Correct provider auto-detection. `s3-role-arn` does not select\n`s3-token`. `BaseCatalog.addStorageCredentialProviders` never inspects\nthe role ARN, so a catalog with the key pair and a role ARN but no\nexplicit `credential-providers` falls back to `s3-secret-key`.\n- Remove `azure-storage-account-key` from `adls-token`.\n`ADLSTokenGenerator.initialize` reads only `storageAccountName`,\n`tenantId`, `clientId`, and `clientSecret`.\n- Correct the Iceberg GCP and Aliyun bundle links, which pointed at each\nother\u0027s artifacts.\n- Correct `credential-cache-max-size`. The right-hand column gave\n`gravitino.iceberg-rest.cache-max-size`; `CredentialConstants` defines\n`credential-cache-max-size`, so the prefix rule holds with no exception.\n- Cover the Iceberg, Hive, Glue, and Paimon catalog classpaths in\nDeployment, which previously covered only the Iceberg REST server and\nFileset.\n- State that `s3-secret-key`, `oss-secret-key`, and `azure-account-key`\nreturn the configured long-lived key to the client.\n\n**docs/iceberg-rest-engine/trino.md**\n\n- Add `iceberg.rest-catalog.vended-credentials-enabled` and\n`iceberg.rest-catalog.prefix`. Neither appeared anywhere in the docs,\nwhile the page\u0027s comparison table advertised credential vending.\n- Separate storage credentials from authentication so vended credentials\ncan be configured without static keys. Every previous example set static\nS3 keys.\n- Warn that `s3.aws-access-key` and `s3.aws-secret-key` silently\noverride vended credentials, so queries succeed while vending is not in\nuse.\n- Scope the access control and credential vending claims in the\ncomparison table to what applies.\n\n### Why are the changes needed?\n\nThe credential vending page carried four factual errors. The most\nconsequential is provider auto-detection: the page states that\n`s3-role-arn` enables the `s3-token` provider. It does not. A catalog\nconfigured with the access key pair and a role ARN, but without an\nexplicit `credential-providers`, falls back to `s3-secret-key` and vends\nthe long-lived access key. Users following the current page believe they\nhave scoped STS credentials and are shipping permanent ones to every\nengine that loads a table.\n\nThe Trino page advertised credential vending in its comparison table\nwhile never documenting `vended-credentials-enabled`, and set static S3\nkeys in all four of its configuration examples. Those keys cause Trino\nto ignore vended credentials while queries continue to succeed, so the\nsetup appears correct and no vending occurs.\n\n### Does this PR introduce _any_ user-facing change?\n\nDocumentation only. No API or property changes.\n\n### How was this patch tested?\n\nDocs build.\n\nBehavioral claims were checked against `BaseCatalog`, `CredentialUtils`,\n`CredentialConfig`, `CredentialConstants`, `ADLSTokenGenerator`, and the\ncatalog `build.gradle.kts` copy tasks.\n\nThe documented Trino configuration was verified end to end against\nGravitino 1.3.0 with AWS S3: Spark wrote an Iceberg table and Trino 478\nread it back through the Iceberg REST endpoint using vended STS\ncredentials, with OAuth2 client-credentials authentication and\nauthorization enabled.\n\nFlagged for review: the Azure role names for `adls-token`, the GCS role\nfor `gcs-token`, the Alibaba RAM policy syntax for `oss-token`, and\nwhether the Hive and Glue catalogs take `gravitino-{cloud}` or the\n`-bundle` variant. These follow each vendor\u0027s documented model and the\nPaimon precedent, but were not confirmed against a running deployment.\n\nCo-authored-by: Mark Hoerth \u003cmark@datastrato.com\u003e"
    },
    {
      "commit": "1249341afdd47b5748a144eafd0f16621145df71",
      "tree": "c1fc727769d6318fe9247f22d656d81ab2f832c2",
      "parents": [
        "f8d7523b0d9a7fabdef6ee65c683b3484f997eda"
      ],
      "author": {
        "name": "StormSpirit",
        "email": "jiangxt2@vip.qq.com",
        "time": "Thu Jul 23 14:35:50 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 23 14:35:50 2026 +0800"
      },
      "message": "[#12102] feat(jdbc-doris): Support frequently-used table properties (#12103)\n\n### What changes were proposed in this pull request?\n\nRegister 5 frequently-used table properties in\n`DorisTablePropertiesMetadata`:\n\n- 3 writable: `compression`, `bloom_filter_columns`, `storage_policy`\n- 2 reserved (read-only): `light_schema_change`,\n  `enable_unique_key_merge_on_write`\n\nThe existing read and write paths are already generic, so the\nnew properties pass through without additional logic changes. A\ndefense-in-depth filter in `appendNecessaryProperties` strips reserved\nproperties from the DDL before SQL generation.\n\n### Why are the changes needed?\n\nThe jdbc-doris catalog currently only exposes `replication_num`. Users\ncannot configure bloom filter columns, compression, or storage policy\nthrough Gravitino, requiring manual SQL intervention after table\ncreation.\n\nFix: #12102\n\n### Does this PR introduce _any_ user-facing change?\n\nYes — 3 new writable table properties and 2 new read-only properties\nare exposed in `table.properties()`. Writable properties can be set\nwhen creating or altering a table. Reserved properties are rejected\nwith a clear error if passed to `createTable`.\n\n### How was this patch tested?\n\n- `./gradlew :catalogs:catalog-jdbc-doris:test -PskipITs`\n  (unit tests: metadata validation + reserved property rejection)\n- `./gradlew :catalogs:catalog-jdbc-doris:test -PskipDockerTests\u003dfalse\n  -PdorisMultiVersionTest` (Docker IT: 1.2.x / 3.0.x / 4.0.x, all\n  passing)\n- `./gradlew :catalogs:catalog-jdbc-doris:spotlessCheck`\n- `./gradlew rat`\n\n---------\n\nSigned-off-by: jiangxt2 \u003cjiangxt2@vip.qq.com\u003e"
    },
    {
      "commit": "f8d7523b0d9a7fabdef6ee65c683b3484f997eda",
      "tree": "7544d4bec5950f70e57252691ce186cb0fde70c9",
      "parents": [
        "941f030f3e0f624351fda392f39fd969c081c893"
      ],
      "author": {
        "name": "Bharath Krishna",
        "email": "bmurali@roku.com",
        "time": "Wed Jul 22 22:23:23 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 22 22:23:23 2026 -0700"
      },
      "message": "[#12095] feat(auth): parse X-Gravitino-Active-Roles into the request path (#12096)\n\n### What changes were proposed in this pull request?\n\nParse the `X-Gravitino-Active-Roles` header and carry the declared\nactive roles on the `UserPrincipal`, so the narrowing from #11967 takes\neffect.\n\n- `UserPrincipal`: carry an immutable `ActiveRoles` (defaults to `ALL`)\nalongside the existing per-request `accessToken`; `withActiveRoles()`\nattaches the parsed value.\n- `AuthenticationFilter`: parse the header after authentication and\nattach it to the principal. A malformed value maps to `400` (the Lance\nfilter maps the same).\n- `AuthorizationRequestContext`: read `activeRoles` from the current\n`UserPrincipal`, so every authorization decision uses the request\u0027s\ndeclaration.\n\nMembership validation (`403` for an unheld role) is metalake-scoped and\nfollows in a later PR. Part of #11965.\n\n### Why are the changes needed?\n\n#11967 added the narrowing enforcement, but nothing populated the active\nroles, so it never activated. This wires the header through the request\npath.\n\n### Does this PR introduce any user-facing change?\n\nYes, additive and backward compatible. A request may send\n`X-Gravitino-Active-Roles: \u003crole\u003e[,\u003crole\u003e] | ALL | NONE` to narrow the\nroles used for authorization; a malformed value returns `400`. An absent\nheader behaves as before (`ALL`).\n\n### How was this patch tested?\n\nNew unit tests for `UserPrincipal.withActiveRoles`, the filter\u0027s header\nparsing (valid / absent / malformed → `400`), and\n`AuthorizationRequestContext` reading the active roles from the\nprincipal. Verified with the module `test` and `spotlessCheck` tasks for\n`core`, `server-common`, and `lance-rest-server`."
    },
    {
      "commit": "941f030f3e0f624351fda392f39fd969c081c893",
      "tree": "8e41ad51326e4c59cd0ebb09540f2fd6e3c31554",
      "parents": [
        "b0aaac4b9ac243f4ed5fe8c6d58b6803f4540b2c"
      ],
      "author": {
        "name": "Zhiguo Wu",
        "email": "wzg547228197@gmail.com",
        "time": "Wed Jul 22 12:53:39 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 22 12:53:39 2026 +0800"
      },
      "message": "[#12116] improvement(client-python): Add view create/drop operations (#12117)\n\n### What changes were proposed in this pull request?\n\n- Extend `RelationalCatalog` with `ViewCatalog`.\n- Add `as_view_catalog`, `create_view`, and `drop_view`.\n- Add request and response DTOs for view creation.\n- Add view-specific REST error handling.\n- Generalize the entity namespace helper for table and view operations.\n- Add corresponding unit and integration tests.\n\n### Why are the changes needed?\n\nThe Python client currently lacks support for view create/drop\noperations. These changes allow users to create and drop views through\nthe Python client.\n\nFix: #12116\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. Python client users can now create and drop views through\n`RelationalCatalog.as_view_catalog()`.\n\n### How was this patch tested?\n\nAdded unit tests for request and response validation, REST error\nhandling, and view create/drop operations.\n\nAdded integration tests covering view creation, duplicate creation, view\ndeletion, and deletion of nonexistent views."
    },
    {
      "commit": "b0aaac4b9ac243f4ed5fe8c6d58b6803f4540b2c",
      "tree": "b45f9a1a959f0ae3dc2e80c665836a32f79a406f",
      "parents": [
        "5fe2959078dd8bd845da6eb4f483df98f0aade11"
      ],
      "author": {
        "name": "YangJie",
        "email": "yangjie01@baidu.com",
        "time": "Tue Jul 21 20:55:21 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 21 20:55:21 2026 +0800"
      },
      "message": "[#12106] improvement(common): gate Kerberos login-module debug on sun.security.krb5.debug (#12107)\n\n### What changes were proposed in this pull request?\n\nIn `KerberosUtils.KerberosConfiguration`, replace the hardcoded\n`options.put(\"debug\", \"true\")` with `options.put(\"debug\",\nString.valueOf(Boolean.getBoolean(\"sun.security.krb5.debug\")))`, so the\n`Krb5LoginModule` debug flag follows the standard JDK Kerberos debug\nswitch instead of being always on.\n\n### Why are the changes needed?\n\nThe login module printed Kerberos detail (principal, keytab path, etc.)\nto stdout on every login. The one production path using this class is\n`clients/client-java`\u0027s `KerberosTokenProvider`, so a\nKerberos-authenticating Java client emitted this output unconditionally\n— log noise and a minor information leak. Gating on\n`-Dsun.security.krb5.debug` keeps it off by default and turns it on only\nwhen Kerberos is already being debugged.\n\nFix: #12106\n\n### Does this PR introduce _any_ user-facing change?\n\nNo API change. Behavior change: Kerberos login-module debug output is no\nlonger printed unless `-Dsun.security.krb5.debug\u003dtrue` is set. Existing\nKerberos integration tests already set that property, so their debug\noutput is unaffected.\n\n### How was this patch tested?\n\nCompile + spotless. No unit test is added: the change is a one-line\ndefault flip whose behavior (`Boolean.getBoolean`, off unless the\nstandard krb5 debug property is set) is self-evident, and observing the\ninternal JAAS option would require widening production visibility for no\nreal regression coverage."
    },
    {
      "commit": "5fe2959078dd8bd845da6eb4f483df98f0aade11",
      "tree": "ec3f82fab0388a515e64e329239f6d6d772640b1",
      "parents": [
        "afbd958c45c3007d7324a69e402a9d126ad3d361"
      ],
      "author": {
        "name": "Qi Yu",
        "email": "yuqi@datastrato.com",
        "time": "Tue Jul 21 17:45:45 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 21 17:45:45 2026 +0800"
      },
      "message": "[#11923] docs(core): add multi-node entity store cache invalidation design (#11738)\n\n### What changes were proposed in this pull request?\n\nAdds a design doc,\n`design-docs/entity-cache-multinode-changelog-design.md`, proposing how\nto make the entity store cache (`gravitino.cache.enabled`) usable in\nmulti-node deployments by invalidating it across nodes via the existing\n`entity_change_log` and `EntityChangeLogPoller`. It presents two options\n(coarse by-metalake vs. precise per-endpoint) with a recommendation,\nplus a consistency model and a coverage audit.\n\n### Why are the changes needed?\n\nToday the entity store cache only invalidates locally, so multi-node\ndeployments must disable it, which hurts performance (Iceberg\nespecially). This is design-only; no code behavior changes yet.\n\nFix: #11923\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. Documentation and design only.\n\n### How was this patch tested?\n\nNo code changes; not applicable. The doc passes the repo docs-style\naudit scripts."
    },
    {
      "commit": "afbd958c45c3007d7324a69e402a9d126ad3d361",
      "tree": "901f90632e151af4e067a8349f766eb5aa442998",
      "parents": [
        "d9c9237410259ea3f396ef4ef207a3ab2312666f"
      ],
      "author": {
        "name": "Qi Yu",
        "email": "yuqi@datastrato.com",
        "time": "Tue Jul 21 16:38:31 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 21 16:38:31 2026 +0800"
      },
      "message": "[MINOR] fix(jdbc): Quote comments in generated DDL (#12007)\n\n### What changes were proposed in this pull request?\n\n- Add a shared utility for escaping SQL string literal content.\n- Escape table, column, and schema comments in generated JDBC DDL.\n- Apply the escaping to MySQL, PostgreSQL, Doris, and StarRocks\noperations.\n- Add regression tests covering quotes, backslashes, semicolons, and SQL\ncomment tokens.\n\n### Why are the changes needed?\n\nJDBC catalog comments were inserted directly into generated DDL\nstatements. Comments containing quotes could produce malformed SQL or\nchange the structure of the generated statement.\n\nThe change ensures comment content remains inside its SQL string\nliteral.\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. JDBC table, column, and PostgreSQL schema comments containing\nquotes or backslashes are now handled correctly.\n\nThere is no API or configuration change.\n\n### How was this patch tested?\n\n- `./gradlew spotlessApply`\n- `env SKIP_DOCKER_TESTS\u003dtrue ./gradlew\n:catalogs:catalog-jdbc-common:test :catalogs:catalog-jdbc-mysql:test\n:catalogs:catalog-jdbc-postgresql:test :catalogs:catalog-jdbc-doris:test\n:catalogs:catalog-jdbc-starrocks:test -PskipITs -PskipDockerTests\u003dtrue`"
    },
    {
      "commit": "d9c9237410259ea3f396ef4ef207a3ab2312666f",
      "tree": "d619a74561b5ecd37b67fb28e541456b8eed93d6",
      "parents": [
        "65d305fdaa380b6d629963ab807124cfda1d54da"
      ],
      "author": {
        "name": "YangJie",
        "email": "yangjie01@baidu.com",
        "time": "Tue Jul 21 16:00:36 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 21 16:00:36 2026 +0800"
      },
      "message": "[#10093] feat(core): Introduce ClassLoaderPool to share ClassLoaders across same-type catalogs (#10480)\n\n### What changes were proposed in this pull request?\nIntroduce a `ClassLoaderPool` with reference counting to share\n`IsolatedClassLoader` instances across catalogs of the same type, and\ncentralize ClassLoader resource cleanup into the pool\u0027s lifecycle. Add\n`gravitino.catalog.classloader.sharing.enabled` as a safety-net toggle\nto disable sharing if needed.\n\n**Core mechanism:** Catalogs with identical isolation-relevant\nproperties share a single `IsolatedClassLoader`. The isolation key is\nbuilt from the provider plus the package, authorization provider,\nauthentication type, Kerberos principal/keytab, and backend URIs\n(`metastore.uris`, `jdbc-url`, `uri`, `fs.defaultFS`). The pool uses\n`ConcurrentHashMap.compute()` for atomic acquire/release, and runs\ncleanup (JDBC driver deregistration, then the shared\n`ClassLoaderResourceCleanerUtils` pass over Hadoop FileSystem,\nThreadLocals, commons-logging, cloud SDKs, and shutdown hooks) only when\nthe last catalog releases the shared ClassLoader.\n\nWhen sharing is disabled via\n`gravitino.catalog.classloader.sharing.enabled\u003dfalse`, each catalog gets\nits own dedicated ClassLoader without going through the pool — restoring\nthe pre-PR behavior as a fallback.\n\n**New classes:**\n- `ClassLoaderKey` — `Map\u003cString, String\u003e`-based key for ClassLoader\nsharing, decoupled from specific property names\n- `ClassLoaderPool` — thread-safe pool with reference counting and\nlifecycle management\n- `PooledClassLoaderEntry` — holds a shared ClassLoader and its\nreference count\n\n**Changes to existing classes:**\n- `CatalogManager` — integrates the pool into catalog creation, test\nconnection, and close paths; adds a non-pooled path gated by\n`classLoaderSharingEnabled`; extracts `initCatalogWrapper()` to remove\nduplication between the pooled and non-pooled paths; fixes ClassLoader\nleaks in `testConnection()` and `getResolvedProperties()`\n- `Configs` — adds `gravitino.catalog.classloader.sharing.enabled`\n- `IsolatedClassLoader` — exposes `getInternalClassLoader()` so the pool\ncan run cleanup on the underlying `URLClassLoader`\n- Removes per-catalog cleanup calls from `JdbcCatalogOperations`,\n`IcebergCatalogOperations`, `IcebergCatalogWrapper`,\n`PaimonCatalogOperations`, `FilesetCatalogOperations`,\n`HiveCatalogOperations`, and `CatalogWrapperForREST`, since cleanup now\nhappens once in the pool on final release\n\n### Why are the changes needed?\nConcurrent catalog creation with different names but the same provider\ntype causes `OutOfMemoryError: Metaspace`. Each catalog creates an\nindependent `IsolatedClassLoader` that loads all provider JARs into\nMetaspace. With `MaxMetaspaceSize\u003d512m` (default) and Iceberg catalogs\nconsuming ~30-80 MB each, ~10 catalogs exhaust the limit.\n\nThis patch addresses three causes:\n1. **No ClassLoader sharing** — same-type catalogs loaded identical\nclasses into separate Metaspace regions\n2. **ClassLoader leak in `testConnection()`** — the throwaway wrapper\nwas never closed after a connection test\n3. **Inconsistent cleanup** — only 2 of 9+ catalog types called\n`ClassLoaderResourceCleanerUtils`; centralizing it in the pool ensures\nevery type is cleaned on final release\n\nFix: #10093\n\n### Does this PR introduce _any_ user-facing change?\nYes. A new optional server configuration is added (since 2.0.0):\n\n- `gravitino.catalog.classloader.sharing.enabled` — whether to share\nClassLoaders across catalogs with identical isolation-relevant\nproperties. Default is `true`. Set to `false` to give each catalog its\nown dedicated ClassLoader as in previous releases.\n\n### How was this patch tested?\n**Unit tests** (`TestClassLoaderPool` — 19 tests): acquire/release\nsemantics, reference counting, concurrent access with 20 threads,\nclose-during-acquire race, double-release resilience, Kerberos key\nisolation, backend URI isolation (metastore URIs, JDBC URLs, `uri`,\nfs.defaultFS), authorization provider isolation, package property\nisolation.\n\n**Integration tests** (`TestClassLoaderPoolIntegration` — 6 tests):\nsame-type catalogs share one ClassLoader; dropping one doesn\u0027t affect\nothers; `testConnection` on the same key doesn\u0027t break a live catalog;\nmanager close cleans up the pool; with sharing disabled each catalog\ngets a distinct ClassLoader; with sharing disabled dropping one doesn\u0027t\naffect another.\n\n**Docker integration tests** (`@Tag(\"gravitino-docker-test\")`, real\nMySQL): `IcebergClassLoaderPoolIT` (3 tests) verifies over a live JDBC\nbackend that catalogs on the same `uri` share a ClassLoader while\ndifferent `uri`s isolate, and that dropping a shared catalog / running\n`testConnection` keeps a sibling usable. `TestMultipleJDBCLoad` covers\nmultiple Iceberg JDBC catalogs on one server. Both scope Iceberg\u0027s\nmetadata lookups with `nullCatalogMeansCurrent\u003dtrue` (test-only) so\ncatalogs on separate databases of a shared MySQL container don\u0027t see\neach other\u0027s `iceberg_tables`.\n\n**Existing tests**: `TestCatalogManager` passes without modification;\n`TestJdbcCatalogOperations` was trimmed (the driver-deregistration path\nit covered is now handled by the pool).\n\n**Benchmark** (JDK 17, `-XX:MaxMetaspaceSize\u003d512m`, `fileset` provider,\n10 concurrent threads):\n\nMetaspace growth (committed KB):\n\n| Catalogs | Baseline (`main`) | ClassLoaderPool | Reduction |\n|---|---|---|---|\n| 100 | +890 | +261 | 3.4x |\n| 500 | +3,280 | +82 | 40x |\n| 1,000 | +6,416 | +9 | 713x |\n| 5,000 | +13,969 | +67 | 209x |\n| 10,000 | +40,394 | +11 | **3,672x** |\n\nClasses loaded:\n\n| Catalogs | Baseline | ClassLoaderPool | Reduction |\n|---|---|---|---|\n| 1,000 | 21,772 | 11,387 | 48% |\n| 10,000 | 60,373 | 11,961 | **80%** |\n\nBaseline Metaspace grows O(N) with catalog count. The pool stays flat at\n~8.7 MB — O(number of distinct keys). No OOM or performance regression\non either version. For Iceberg catalogs (~50 MB/ClassLoader), baseline\nOOMs at ~10 catalogs; with the pool, catalogs sharing the same key reuse\na single ClassLoader, so Metaspace scales with the number of distinct\nconfigurations rather than the number of catalog instances."
    },
    {
      "commit": "65d305fdaa380b6d629963ab807124cfda1d54da",
      "tree": "b4cf6066d04d74e9ddaec1478d95e6855c8b2700",
      "parents": [
        "394e0c6a99dbb31b0f66572a515c7d2b166d146e"
      ],
      "author": {
        "name": "Yuhui",
        "email": "hui@datastrato.com",
        "time": "Tue Jul 21 14:24:13 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 21 14:24:13 2026 +0800"
      },
      "message": "[#12097] improvement(core): Validate names before normalizing in CapabilityHelpers (#12098)\n\n### What changes were proposed in this pull request?\n\nReorder validation and normalization in `CapabilityHelpers` so\n`applyNameSpecification`\nruns on the original name before\n`applyCaseSensitiveOn*`/`normalizeName`. Remove the\nredundant re-normalization of identifiers already returned by\n`listSchemas`/`listTables`\nin `SchemaNormalizeDispatcher`/`TableNormalizeDispatcher`.\n\n### Why are the changes needed?\n\nFor catalogs whose name folding depends on the name\u0027s original form\n(e.g. a quoted\nidentifier keeps case/spaces, an unquoted one gets folded), validating\nafter\nnormalization checks the wrong form of the name. Similarly, re-applying\n`normalizeName`\nto identifiers a catalog already returned in canonical form isn\u0027t\nguaranteed to be\nidempotent for such catalogs and can corrupt an already-correct name.\n\nFix: #12097\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\nAdded a unit test; existing core module tests pass."
    },
    {
      "commit": "394e0c6a99dbb31b0f66572a515c7d2b166d146e",
      "tree": "d063b472a4be5fd21465967a29733e275c27dc67",
      "parents": [
        "648fdd7c1a869f798d05e471420de6cf56e74957"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Tue Jul 21 08:51:58 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 21 08:51:58 2026 +0800"
      },
      "message": "build(deps-dev): bump coverage from 7.13.4 to 7.14.3 in /clients/client-python (#11872)\n\nBumps [coverage](https://github.com/coveragepy/coveragepy) from 7.13.4\nto 7.14.3.\n\u003cdetails\u003e\n\u003csummary\u003eChangelog\u003c/summary\u003e\n\u003cp\u003e\u003cem\u003eSourced from \u003ca\nhref\u003d\"https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst\"\u003ecoverage\u0027s\nchangelog\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003ch2\u003eVersion 7.14.3 — 2026-06-22\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eFix: the default \u003ccode\u003e...\u003c/code\u003e exclusion rule now also matches\nfunction bodies\nwhose closing return-type bracket is on its own line (for example, after\na\nlong \u003ccode\u003e-\u0026gt; dict[ ... ]\u003c/code\u003e annotation that a formatter has\nsplit over multiple\nlines). Closes \u003ccode\u003eissue 2185\u003c/code\u003e\u003cem\u003e, thanks \u003ccode\u003eMengjia Shang\n\u0026lt;pull 2196_\u0026gt;\u003c/code\u003e\u003c/em\u003e.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eFix: On 3.13t, we incorrectly issued \u003ccode\u003eCouldn\u0027t import C\ntracer\u003c/code\u003e errors.\nWe can\u0027t import the C tracer because in 7.14.2 we stopped shipping\ncompiled\nwheels for 3.13t. Thanks, \u003ccode\u003eHugo van Kemenade \u0026lt;pull\n2203_\u0026gt;\u003c/code\u003e_.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e.. _issue 2185: \u003ca\nhref\u003d\"https://redirect.github.com/coveragepy/coveragepy/issues/2185\"\u003ecoveragepy/coveragepy#2185\u003c/a\u003e\n.. _pull 2196: \u003ca\nhref\u003d\"https://redirect.github.com/coveragepy/coveragepy/pull/2196\"\u003ecoveragepy/coveragepy#2196\u003c/a\u003e\n.. _pull 2203: \u003ca\nhref\u003d\"https://redirect.github.com/coveragepy/coveragepy/pull/2203\"\u003ecoveragepy/coveragepy#2203\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e.. _changes_7-14-2:\u003c/p\u003e\n\u003ch2\u003eVersion 7.14.2 — 2026-06-20\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eFix: some messages were being written to stdout, making\n\u003ccode\u003ecoverage json -o -\u003c/code\u003e useless for capturing JSON output. Now\nmessages are written to stderr,\nfixing \u003ccode\u003eissue 2197\u003c/code\u003e_.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eFix: \u003ccode\u003eCoverageData\u003c/code\u003e kept one SQLite connection per thread\nthat recorded\ncoverage, but never closed them when those threads terminated. On long\nruns\nwith many short-lived threads this leaked one file descriptor per dead\nthread, eventually failing with \u003ccode\u003eOSError: [Errno 24] Too many open\nfiles\u003c/code\u003e.\nConnections belonging to terminated threads are now closed and dropped.\nFixes \u003ccode\u003eissue 2192\u003c/code\u003e\u003cem\u003e. Thanks, \u003ccode\u003eMatthew Lloyd \u0026lt;pull\n2193_\u0026gt;\u003c/code\u003e\u003c/em\u003e.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eFix: when using sys.monitoring, we were assuming we could use the\n\u003ccode\u003eCOVERAGE_ID\u003c/code\u003e tool id. But other tools might also assume they\ncould use\nthat id. Pre-allocated ids don\u0027t really make sense, so now we search for\na\nusable one instead. Fixes \u003ccode\u003eissue 2187\u003c/code\u003e_.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eFollowing \u003ccode\u003ethe advice of cibuildwheel \u0026lt;no-13t_\u0026gt;\u003c/code\u003e_,\nwe no longer distribute\nwheels for Python 3.13 free-threaded.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e.. _issue 2187: \u003ca\nhref\u003d\"https://redirect.github.com/coveragepy/coveragepy/issues/2187\"\u003ecoveragepy/coveragepy#2187\u003c/a\u003e\n.. _issue 2192: \u003ca\nhref\u003d\"https://redirect.github.com/coveragepy/coveragepy/issues/2192\"\u003ecoveragepy/coveragepy#2192\u003c/a\u003e\n.. _pull 2193: \u003ca\nhref\u003d\"https://redirect.github.com/coveragepy/coveragepy/pull/2193\"\u003ecoveragepy/coveragepy#2193\u003c/a\u003e\n.. _issue 2197: \u003ca\nhref\u003d\"https://redirect.github.com/coveragepy/coveragepy/issues/2197\"\u003ecoveragepy/coveragepy#2197\u003c/a\u003e\n.. _no-13t: \u003ca\nhref\u003d\"https://py-free-threading.github.io/ci/#building-free-threaded-wheels-with-cibuildwheel\"\u003ehttps://py-free-threading.github.io/ci/#building-free-threaded-wheels-with-cibuildwheel\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e.. _changes_7-14-1:\u003c/p\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e... (truncated)\u003c/p\u003e\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003eCommits\u003c/summary\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/coveragepy/coveragepy/commit/22f13eacc2258aee53198b6176db1ca89c32a12e\"\u003e\u003ccode\u003e22f13ea\u003c/code\u003e\u003c/a\u003e\ndocs: sample HTML for 7.14.3\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/coveragepy/coveragepy/commit/2ca4e5fe61a551fc55e48428c8140cb7d325566a\"\u003e\u003ccode\u003e2ca4e5f\u003c/code\u003e\u003c/a\u003e\ndocs: prep for 7.14.3\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/coveragepy/coveragepy/commit/01d714e5cb457177e999e78fde0c438283f7e4db\"\u003e\u003ccode\u003e01d714e\u003c/code\u003e\u003c/a\u003e\ndocs: add changelog entry for \u003ca\nhref\u003d\"https://redirect.github.com/coveragepy/coveragepy/issues/2203\"\u003e#2203\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/coveragepy/coveragepy/commit/f36248d7ba313734d4fcf3d6eab713b5eba2c259\"\u003e\u003ccode\u003ef36248d\u003c/code\u003e\u003c/a\u003e\nfix: don\u0027t emit \u0027Couldn\u0027t import C tracer\u0027 warning for 3.13t (\u003ca\nhref\u003d\"https://redirect.github.com/coveragepy/coveragepy/issues/2203\"\u003e#2203\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/coveragepy/coveragepy/commit/86d73d1c4c60bdb2ea03f982cd4e33c77879ba77\"\u003e\u003ccode\u003e86d73d1\u003c/code\u003e\u003c/a\u003e\ndocs: thanks, Mengjia Shang\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/coveragepy/coveragepy/commit/3d4ae3cb7c77f7521d91200a1e5de13425bbdff4\"\u003e\u003ccode\u003e3d4ae3c\u003c/code\u003e\u003c/a\u003e\ndocs: add the \u003ca\nhref\u003d\"https://redirect.github.com/coveragepy/coveragepy/issues/2196\"\u003e#2196\u003c/a\u003e\npr link to CHANGES\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/coveragepy/coveragepy/commit/f4b2b4d35e1b7cd8f33f1ee88d354b0debeeed08\"\u003e\u003ccode\u003ef4b2b4d\u003c/code\u003e\u003c/a\u003e\nfix: exclude \u003ccode\u003e...\u003c/code\u003e bodies after multi-line return-type\nannotations (\u003ca\nhref\u003d\"https://redirect.github.com/coveragepy/coveragepy/issues/2185\"\u003e#2185\u003c/a\u003e)\n(#...\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/coveragepy/coveragepy/commit/1980ed0730956f0cc179e8568e1c8ee848f2d42b\"\u003e\u003ccode\u003e1980ed0\u003c/code\u003e\u003c/a\u003e\nchore: bump sigstore/gh-action-sigstore-python (\u003ca\nhref\u003d\"https://redirect.github.com/coveragepy/coveragepy/issues/2201\"\u003e#2201\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/coveragepy/coveragepy/commit/bca3217a1dadf35fa1d444722ec9f05dd5819089\"\u003e\u003ccode\u003ebca3217\u003c/code\u003e\u003c/a\u003e\nbuild: since we don\u0027t ship 3.13t, don\u0027t test it\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/coveragepy/coveragepy/commit/77550d83b2f35818a1ca8931289b81d161222031\"\u003e\u003ccode\u003e77550d8\u003c/code\u003e\u003c/a\u003e\ndocs: oops, mismatched pull requests\u003c/li\u003e\n\u003cli\u003eAdditional commits viewable in \u003ca\nhref\u003d\"https://github.com/coveragepy/coveragepy/compare/7.13.4...7.14.3\"\u003ecompare\nview\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/details\u003e\n\u003cbr /\u003e\n\n\n[![Dependabot compatibility\nscore](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name\u003dcoverage\u0026package-manager\u003dpip\u0026previous-version\u003d7.13.4\u0026new-version\u003d7.14.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)\n\nDependabot will resolve any conflicts with this PR as long as you don\u0027t\nalter it yourself. You can also trigger a rebase manually by commenting\n`@dependabot rebase`.\n\n[//]: # (dependabot-automerge-start)\n[//]: # (dependabot-automerge-end)\n\n---\n\n\u003cdetails\u003e\n\u003csummary\u003eDependabot commands and options\u003c/summary\u003e\n\u003cbr /\u003e\n\nYou can trigger Dependabot actions by commenting on this PR:\n- `@dependabot rebase` will rebase this PR\n- `@dependabot recreate` will recreate this PR, overwriting any edits\nthat have been made to it\n- `@dependabot show \u003cdependency name\u003e ignore conditions` will show all\nof the ignore conditions of the specified dependency\n- `@dependabot ignore this major version` will close this PR and stop\nDependabot creating any more for this major version (unless you reopen\nthe PR or upgrade to it yourself)\n- `@dependabot ignore this minor version` will close this PR and stop\nDependabot creating any more for this minor version (unless you reopen\nthe PR or upgrade to it yourself)\n- `@dependabot ignore this dependency` will close this PR and stop\nDependabot creating any more for this dependency (unless you reopen the\nPR or upgrade to it yourself)\n\n\n\u003c/details\u003e\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e\nCo-authored-by: Jerry Shao \u003cjerryshao@datastrato.com\u003e"
    },
    {
      "commit": "648fdd7c1a869f798d05e471420de6cf56e74957",
      "tree": "6b905b0c5d9125b872f3c8a5e5aa519588e43534",
      "parents": [
        "23f871f8055ac8e93f186a7c7a98e0d5e3c4e0b1"
      ],
      "author": {
        "name": "Zhiguo Wu",
        "email": "wzg547228197@gmail.com",
        "time": "Mon Jul 20 21:22:50 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 20 21:22:50 2026 +0800"
      },
      "message": "[#12089] improvement(client-python): Implement client-side view models (#12090)\n\n### What changes were proposed in this pull request?\n\n- Add `GenericView` as the client-side implementation of `View`.\n- Add DTOs for views and SQL representations.\n- Add polymorphic JSON serialization for view representations.\n- Extend `DTOConverters` with representation conversions.\n- Add unit tests for the new models, serialization, and conversions.\n\n### Why are the changes needed?\n\nThe Python client needs concrete view models and serialization support\nbefore view catalog operations can consume and return view metadata.\n\nFix: #12089\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. This PR adds the supporting client-side models. View catalog\noperations will be added separately.\n\n### How was this patch tested?\n\nAdded unit tests covering `GenericView`, view and representation DTOs,\nrepresentation serialization, and DTO conversions. Python client\nformatting, lint, and unit tests pass."
    },
    {
      "commit": "23f871f8055ac8e93f186a7c7a98e0d5e3c4e0b1",
      "tree": "519a42068e7e39a50757b2768f881abeb4599001",
      "parents": [
        "b49d895eaf358bdbeafef1ca9e702652514a3055"
      ],
      "author": {
        "name": "StormSpirit",
        "email": "jiangxt2@vip.qq.com",
        "time": "Mon Jul 20 20:15:07 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 20 20:15:07 2026 +0800"
      },
      "message": "[#11802] fix(clickhouse): support user-defined GRANULARITY and validate shard key type (#11806)\n\n### What changes were proposed in this pull request?\n\n#### GRANULARITY round-trip (`ClickHouseTableOperations.java`)\n\n- **Read path**: `getSecondaryIndexes()` queries the `granularity`\ncolumn from `system.data_skipping_indices` and stores it in\n`Index.properties()`. Default values (1) are omitted so that indexes\ncreated without explicit GRANULARITY have empty properties and match the\noriginal creation state (avoids false index-change diffs).\n- **Write path**: `appendIndexesSql()` passes\n`resolveGranularity(index.properties(), 1)` to\n`buildDataSkippingIndexDdl`, replacing the old hardcoded defaults.\n\n#### Shard key validation (`ClickHouseTableOperations.java`)\n\n- Extracted `validateShardKeyColumns()` shared method covering column\nexistence, nullable check, and integer type check. Called from both\n`create()` (empty columns path) and `handleDistributeTable()` (explicit\ncolumns path).\n- Added `isIntegerType()`: accepts Gravitino `IntegralType` (Int8-Int64,\nUInt8-UInt64) and ClickHouse wide integers (Int128/256, UInt128/256)\nmapped to `ExternalType`, using regex for future-proof coverage.\n- Function-wrapped shard keys (e.g. `cityHash64(string_col)`) skip\ntype/nullability checks — only column existence is verified.\n- **Empty columns path**: New `create()` override fetches remote table\ncolumns via `fetchRemoteColumns()` and validates shard keys before\n`super.create()`, covering the case where `handleDistributeTable` is\nskipped (distributed table using `AS remote_table`).\n\n#### `getColumns()` override (`ClickHouseTableOperations.java`)\n\n- Parent `JdbcTableOperations.getColumns()` uses\n`connection.getSchema()` as the schema pattern, which is incorrect for\nClickHouse when the target database differs from the connection\u0027s\ndefault database. The override passes `databaseName` directly to\n`metaData.getColumns()`.\n\n### Why are the changes needed?\n\nClickHouse data skipping indexes support a `GRANULARITY` parameter, but\nthe existing code hardcoded default values. Users who create indexes\nwith custom GRANULARITY would lose that information on round-trip.\n\nDistributed table shard keys in ClickHouse must be non-nullable integer\ncolumns. Previously this was documented in a TODO comment — invalid\nshard keys reached the ClickHouse server and produced opaque error\nmessages instead of clear client-side validation.\n\n### Does this PR introduce any user-facing change?\n\n1. Custom GRANULARITY for data skipping indexes via `Index.properties()`\n— round-trip (create → load → recreate) preserves non-default values.\n2. Nullable or non-integer shard keys are rejected at the Gravitino\nclient level with clear `IllegalArgumentException` messages.\n3. Int128/UInt128/Int256/UInt256 columns are now accepted as shard keys.\n\n### How was this patch tested?\n\n**Unit tests** (`-PskipITs`):\n\n`TestClickHouseTableOperations`:\n- `testGranularityNormalizedToCanonicalForm` — leading zero / whitespace\ntrimming\n\n`TestClickHouseTableOperationsCluster`:\n- `testIndexNonNumericGranularityRejected`,\n`testIndexZeroGranularityRejected`,\n`testIndexZeroAfterParsingGranularityRejected`,\n`testIndexNegativeGranularityRejected` — invalid GRANULARITY\n- `testShardingKeyNullableColumnRejected`,\n`testShardingKeyNonIntegerColumnRejected` — shard key rejected\n- `testShardingKeyFunctionWithNonIntegerColumnAccepted` —\nfunction-wrapped key\n- `testShardingKeyInt128ColumnAccepted`,\n`testShardingKeyUInt128ColumnAccepted` — wide integer keys\n- `testAlterTableAddIndexDefaultGranularity`,\n`testAlterTableAddIndexWithClusterProperties`,\n`testAlterTableAddIndexWithNullProperties` — ALTER TABLE ADD INDEX\n\n**Docker integration tests** (`-PskipDockerTests\u003dfalse`):\n\n`CatalogClickHouseIT` (35 tests, 0 failures):\n- `testCreateAndLoadWithCustomGranularity`,\n`testCreateAndLoadWithDefaultGranularity`,\n`testLoadSqlCreatedTableWithCustomGranularity` — GRANULARITY round-trip\n- `testAlterTableAddIndexUsesDefaultGranularity` — ALTER TABLE ADD INDEX\nend-to-end\n\n`CatalogClickHouseClusterIT` (17 tests, 0 failures):\n- `testDistributedTableNullableShardKeyRejected`,\n`testDistributedTableNonIntegerShardKeyRejected` — shard key rejected\n- `testDistributedTableIntegerShardKeyAccepted`,\n`testDistributedTableWideIntegerShardKeyAccepted`,\n`testDistributedTableFunctionShardKeyAccepted` — shard key accepted\n\nRun commands:\n```bash\n./gradlew :catalogs-contrib:catalog-jdbc-clickhouse:test -PskipITs\n./gradlew :catalogs-contrib:catalog-jdbc-clickhouse:test --tests \"CatalogClickHouseIT\" -PskipDockerTests\u003dfalse\n./gradlew :catalogs-contrib:catalog-jdbc-clickhouse:test --tests \"CatalogClickHouseClusterIT\" -PskipDockerTests\u003dfalse\n```\n\nCloses #11802.\n\n---------\n\nSigned-off-by: jiangxt2 \u003cjiangxt2@vip.qq.com\u003e"
    },
    {
      "commit": "b49d895eaf358bdbeafef1ca9e702652514a3055",
      "tree": "6b7b629079e3910776c7cefb133c21de85667875",
      "parents": [
        "e675c0465c2be89b5738f44d090dc5eb29aa3f48"
      ],
      "author": {
        "name": "Qi Yu",
        "email": "yuqi@datastrato.com",
        "time": "Mon Jul 20 19:27:43 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 20 19:27:43 2026 +0800"
      },
      "message": "[MINOR] improvement(authz): Add debug logging for list-authorization short-circuit (#12101)\n\n### What changes were proposed in this pull request?\n\nAdds `DEBUG`-level logging around the list-authorization parent-scope\nshort-circuit (`MetadataAuthzHelper.filterByExpression` /\n`allVisibleViaParentScope`, introduced in #11778) so it is possible to\ntell, per request and per principal, whether the short-circuit fired and\n— when it did not — exactly which condition disqualified it:\n\n- **HIT**: every listed object is returned via a parent-scope grant and\nthe per-object authorization loop is skipped.\n- **MISS / skipped**, with the reason: no short-circuit spec registered\n/ filter-expression mismatch, listed objects span multiple parent\nnamespaces, the principal has no parent-scope grant, or a deny policy\nexists on the relevant privileges.\n\nEvery log line now includes the current principal, the entity type, and\nthe metalake.\n\nThis is purely diagnostic — no behavior change.\n\n### Why are the changes needed?\n\nWhen a `list` call (e.g. `list catalogs`) is slow, there is currently no\nway to observe whether the #11778 short-circuit applied. The `verbose`\n(`details\u003dtrue`) path and finer-grained or denied grants fall back to\nper-object authorization; these logs make that visible and greatly speed\nup diagnosing slow-list reports.\n\nFix: N/A (minor diagnostic improvement)\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. Debug logging only.\n\n### How was this patch tested?\n\nNo behavior change (only added `LOG.debug` calls and an extracted\n`hasDeny` local; control flow is unchanged). Existing tests cover the\nshort-circuit logic. Verified `:server-common:compileJava` succeeds."
    },
    {
      "commit": "e675c0465c2be89b5738f44d090dc5eb29aa3f48",
      "tree": "7cabe870ee57b3fdee4c7754dfe451366ab48fb8",
      "parents": [
        "fc88ddc8d4e8c35bb0d06cebd125a4559e771084"
      ],
      "author": {
        "name": "roryqi",
        "email": "roryqi@datastrato.com",
        "time": "Mon Jul 20 19:07:07 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 20 19:07:07 2026 +0800"
      },
      "message": "[#12091] improvement(ci): Route contrib catalog tests to contrib CI (#12092)\n\n### What changes were proposed in this pull request?\n\nThis PR moves heavyweight contrib catalog JDBC tests out of the general\nBuild and Backend Integration Test workflows.\n\n- Exclude `:catalogs-contrib:catalog-jdbc-clickhouse:test`,\n`:catalogs-contrib:catalog-jdbc-hologres:test`, and\n`:catalogs-contrib:catalog-jdbc-oceanbase:test` from Build.\n- Add `.github/workflows/contrib-catalog-test.yml` to run contrib\ncatalog tests in a dedicated pipeline.\n- Detect changed `catalogs-contrib` modules and run only their Gradle\ntest tasks. Shared CI, Gradle, `catalogs-contrib/build.gradle.kts`,\n`catalog-common`, or `catalog-jdbc-common` changes run all contrib\ncatalog tests.\n- Remove the `catalogs-contrib` module detection and routing from\nBackend Integration Test. The reusable backend action now excludes all\n`catalogs-contrib` test tasks, and contrib ITs run in `Contrib Catalog\nTest` without `-PskipITs`.\n\n### Why are the changes needed?\n\nThe OceanBase CI in the Build pipeline can block unrelated PRs when its\nDocker service startup is flaky. ClickHouse and Hologres are similar\ncontrib catalog Docker-backed tests, and contrib integration tests were\nalso routed through Backend Integration Test, so these tests should be\nisolated in the contrib catalog workflow.\n\nFix: #12091\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\n- `git diff --check`\n- YAML parse for `.github/workflows/build.yml`,\n`.github/workflows/backend-integration-test.yml`,\n`.github/workflows/backend-integration-test-action.yml`, and\n`.github/workflows/contrib-catalog-test.yml`\n- `./gradlew :catalogs-contrib:catalog-jdbc-clickhouse:testClasses\n:catalogs-contrib:catalog-jdbc-hologres:testClasses\n:catalogs-contrib:catalog-jdbc-oceanbase:testClasses -PskipWeb\u003dtrue`"
    },
    {
      "commit": "fc88ddc8d4e8c35bb0d06cebd125a4559e771084",
      "tree": "6436d8a926d7403cb0ef04ad15aec1cc3422dae3",
      "parents": [
        "a03467c5e552b4093f88c093068a5f4106ca7bb3"
      ],
      "author": {
        "name": "YangJie",
        "email": "yangjie01@baidu.com",
        "time": "Mon Jul 20 16:55:46 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 20 16:55:46 2026 +0800"
      },
      "message": "[#12035] improvement(catalog-jdbc): block DBCP2 class-loading properties in datasource config (#12036)\n\n### What changes were proposed in this pull request?\n\nAdd `rejectUnsafePoolProperties` in `DataSourceUtils`, rejecting the\nDBCP2 pool properties `connectionFactoryClassName`,\n`evictionPolicyClassName`, `driverClassName`, and `initialSize`\n(case-insensitive whole-key match) before the config map is handed to\n`BasicDataSourceFactory`.\n\n### Why are the changes needed?\n\nDBCP2\u0027s factory loads and instantiates the class named by the first\nthree via reflection (`Class.forName` + `newInstance`), and\n`initialSize` \u003e 0 forces an eager connection — triggering that driver\nload — during `createDataSource`, before Gravitino\u0027s explicit\n`setDriverClassName` override. Reachable via\n`gravitino.bypass.\u003ckey\u003e\u003d...`, this lets a catalog creator run arbitrary\nclasses on the server (RCE).\n\nFix: #12035\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. These properties are never used by a legitimate Gravitino JDBC\ncatalog (the driver comes from `jdbc-driver`; pool sizing from\n`jdbc.pool.*`). Supplying one now fails fast with a clear message.\n\n### How was this patch tested?\n\nNew unit tests in `TestDataSourceUrlValidation` covering each rejected\nproperty (including case-insensitive variants) and a whole-key negative\ncontrol proving a similarly-named legitimate property still builds.\n`./gradlew :catalogs:catalog-jdbc-common:test -PskipITs` and spotless\npass."
    },
    {
      "commit": "a03467c5e552b4093f88c093068a5f4106ca7bb3",
      "tree": "49ddb3232b7554da6cd13dcf81d6551f62606a7b",
      "parents": [
        "c60b82cc63348336536008f14ed01dc5e70b0c3a"
      ],
      "author": {
        "name": "Bharath Krishna",
        "email": "bmurali@roku.com",
        "time": "Sun Jul 19 21:28:00 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 19 21:28:00 2026 -0700"
      },
      "message": "[#11967] feat(auth): narrow allow enforcement to active roles (deny stays global) (#12044)\n\n### What changes were proposed in this pull request?\n\nPhase 1 role-assumption **enforcement** in `JcasbinAuthorizer`. When a\nrequest declares a subset of the caller\u0027s roles as active, allow\npolicies are evaluated against only that subset; deny stays global.\n\n- `AuthorizationRequestContext`: carry an immutable `ActiveRoles`\n(defaults to `ALL`, so an unset value is byte-for-byte today\u0027s\nbehavior).\n- `JcasbinAuthorizer`: only the allow enforcer narrows. When a subset is\ndeclared, `enforceNarrowed`:\n- applies deny **globally** first (over the caller\u0027s full role union) —\na deny on any held role always applies, keeping narrowing subtractive;\n- then ORs allow across the active set \u003d `(declared names) ∩ (caller\u0027s\neffective roles)`.\n  - `NONE` activates no role; `ALL` / absent is unchanged.\n- Ownership is deliberately not narrowed: access a caller derives from\n*owning* an object comes from the ownership grant, not a role, so\n`isOwner` is untouched and owners keep that access regardless of which\nroles are active. This is the design\u0027s default (Option A in\n`design-docs/gravitino-role-assumption.md` §5).\n\nThis is intentionally scoped to enforcement so it can be reviewed on its\nown. Carrying the value from the `X-Gravitino-Active-Roles` header onto\nthe request and the `400`/`403` mapping follow in later PRs.\n\n### Why are the changes needed?\n\nImplements the enforcement step of the approved design\n[`design-docs/gravitino-role-assumption.md`](https://github.com/apache/gravitino/blob/main/design-docs/gravitino-role-assumption.md)\n(discussion\n[#10894](https://github.com/apache/gravitino/discussions/10894)). A\ncaller needs authorization to be narrowed to a declared subset of its\nroles; this PR is the server-side narrowing the rest of the feature\nbuilds on.\n\nPart of #11965. Closes #11967.\n\n### Does this PR introduce any user-facing change?\n\nNo. The narrowing path only activates when an `ActiveRoles` subset is\nset on the request; nothing populates it yet, so behavior is unchanged.\n\n### How was this patch tested?\n\nNew unit tests in `TestJcasbinAuthorizer`: allow-narrowing to a named\nrole, `NONE`, deny-stays-global when the allow is narrowed,\ngroup-inherited active role, and unheld-role-activates-nothing\n(subtractive). Verified locally:\n\n- `./gradlew :server-common:test --tests\n\"org.apache.gravitino.server.authorization.jcasbin.TestJcasbinAuthorizer\"`\n- `./gradlew :core:test --tests\n\"org.apache.gravitino.authorization.TestAuthorizationRequestContext\"`\n- `./gradlew :server-common:spotlessCheck :core:spotlessCheck`"
    },
    {
      "commit": "c60b82cc63348336536008f14ed01dc5e70b0c3a",
      "tree": "41214c7a2d81df278291c7fda4abb074aefbc88a",
      "parents": [
        "6842f00b74dc69434059aec2e4f4e34f08f6f6c2"
      ],
      "author": {
        "name": "jarred0214",
        "email": "jarred0214@gmail.com",
        "time": "Mon Jul 20 11:10:23 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 20 11:10:23 2026 +0800"
      },
      "message": "[#12038] improvement(scripts): increase relational store column comment length limit to 4096 (#12047)\n\n### What changes were proposed in this pull request?\n\nThis PR increases the `table_column_version_info.column_comment` length\nfrom `VARCHAR(256)` to `VARCHAR(4096)` for all relational store\nbackends:\n\n- MySQL\n- H2\n- PostgreSQL\n\nIt updates both schema initialization scripts and upgrade scripts so\nnewly initialized databases and upgraded existing databases use the same\ncolumn comment length.\n\n### Why are the changes needed?\n\nImporting an external table can fail when a column comment is longer\nthan 256 characters because Gravitino stores column metadata in\n`table_column_version_info.column_comment`.\n\nIncreasing the column length to 4096 follows the discussion in #12038\nand avoids import failures for most practical column comments. For\ncomments larger than 4096, users can still customize their database\nschema definition.\n\nFix: #12038\n\n### Does this PR introduce _any_ user-facing change?\n\nNo API or configuration changes.\n\nThe relational store schema now supports column comments up to 4096\ncharacters in `table_column_version_info`.\n\n### How was this patch tested?\n\n- Verified all `table_column_version_info.column_comment` definitions in\nMySQL, H2, and PostgreSQL schema scripts are updated to `VARCHAR(4096)`.\n- Verified related upgrade scripts include column type migration\nstatements.\n- Ran `git diff --check`.\n\n---------\n\nCo-authored-by: jarred0214 \u003cjarredhj@163.com\u003e"
    },
    {
      "commit": "6842f00b74dc69434059aec2e4f4e34f08f6f6c2",
      "tree": "6768ba2e15dd5db4355efe35a33988a5b036277e",
      "parents": [
        "da99abcc36207f2c15c563dde657ab05d67ace82"
      ],
      "author": {
        "name": "Qi Yu",
        "email": "yuqi@datastrato.com",
        "time": "Mon Jul 20 09:47:43 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 20 09:47:43 2026 +0800"
      },
      "message": "[MINOR] improvement(cache): Add logs for Caffeine cache removals (#12043)\n\n### What changes were proposed in this pull request?\n\nAdd DEBUG-level removal logs to all production Caffeine removal\nlisteners. The logs include cache-specific keys or identifiers and the\nremoval cause, while avoiding cached values that may be sensitive or\nlarge.\n\n### Why are the changes needed?\n\nCache expiration, eviction, replacement, and explicit invalidation were\nnot consistently observable across modules. Consistent DEBUG-level logs\nmake cache lifecycle and resource cleanup easier to diagnose.\n\nFix: N/A\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. Cache removal logs now consistently use DEBUG level and include\ncache keys or identifiers and removal causes. There are no API or\nconfiguration changes.\n\n### How was this patch tested?\n\n- Ran `./gradlew spotlessApply`.\n- Ran targeted unit tests for the affected core, server-common,\ncatalog-fileset, hive-metastore-common, filesystem-hadoop3,\niceberg-common, and iceberg-rest-server modules.\n- After standardizing the log level, reran targeted tests for core,\nserver-common, catalog-fileset, and filesystem-hadoop3.\n- Attempted `./gradlew test -PskipITs`; an unrelated\n`TestJDBCBackendBatchGet` PostgreSQL case failed because the container\nreported `No space left on device`."
    },
    {
      "commit": "da99abcc36207f2c15c563dde657ab05d67ace82",
      "tree": "9d8336009db5613a2dc00339b24c8c98fa544356",
      "parents": [
        "b30e45731883cfe37311ec9b6c418ffb2245e3d7"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Sun Jul 19 01:50:53 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 19 16:50:53 2026 +0800"
      },
      "message": "[#11943] improvement(iceberg,core): validate warehouse at catalog creation and fail fast with an actionable error (#11959)\n\n### What changes were proposed in this pull request?\n\nThis PR fixes the visible configuration error reported in #11943 for a\n`lakehouse-iceberg` catalog using `catalog-backend\u003drest`.\n\nFor the Iceberg REST backend, `warehouse` is forwarded to `GET\n/v1/config?warehouse\u003d...` as a server-side catalog selector. It is not a\nstorage location. Previously, a catalog could be created successfully\nwith an unresolvable value such as `s3://warehouse/`, then fail\ncryptically on first use.\n\nThe changes are:\n\n- **Validate the REST `warehouse` selector at creation time.**\n`IcebergCatalog` opts into create-time initialization only when the\nbackend is REST and `warehouse` is nonblank. Hive, JDBC, and REST\ncatalogs without `warehouse` keep their existing creation behavior.\n- **Return an actionable configuration error.** If the REST server\nrejects the selector with `NoSuchWarehouseException`, creation fails\nwith `IllegalArgumentException` / HTTP 400 and explains that `warehouse`\nshould be removed to use the default catalog or set to a catalog name\nthe REST server recognizes.\n- **Keep dependency failures distinct from invalid configuration.**\nBecause the new validation contacts the remote REST service, a transport\nfailure or HTTP 503 during REST initialization is returned as\n`ConnectionFailedException` / HTTP 502 instead of being presented as an\ninvalid warehouse or a generic internal error. With a nonblank\n`warehouse`, the failed creation is rolled back and no catalog is\npersisted.\n- **Preserve the exception across Gravitino boundaries.** The catalog\nclassloader, server handler, and Java client preserve\n`IllegalArgumentException` and `ConnectionFailedException` so callers\nreceive the intended 400 or 502 result.\n- **Clarify the property in OpenAPI examples.** The examples contrast\nstorage backends, where `warehouse` is a location, with the REST\nbackend, where it is a catalog selector and may be omitted.\n\n#### Scope and future work\n\nThe primary scope of this PR is the `NoSuchWarehouseException` case from\n#11943. The adjacent transport/503 mapping is included only so the new\ncreate-time validation does not confuse a temporarily unavailable REST\ndependency with an invalid warehouse value.\n\nThis PR does **not** attempt to normalize every response or exception\nproduced by the Iceberg REST client. Iceberg also has other response\ntypes and endpoint-specific errors, including bad-request,\nauthorization, forbidden, service-failure, and operation-time failures\nafter initialization. Mapping those consistently into Gravitino\u0027s error\ntaxonomy should be handled as future work with dedicated scenarios and\ntests.\n\n### Why are the changes needed?\n\nOn a lakehouse-iceberg catalog with `catalog-backend\u003drest`, the\n`warehouse` property is a catalog selector sent to the Iceberg REST\nserver, not a storage path. A user copying a Hive or JDBC example may\nset it to a storage URI such as `s3://warehouse/`. Before this change,\ncatalog creation returned success and every later attempt to use the\ncatalog failed with a cryptic `NoSuchWarehouseException`.\n\nThe catalog should reject that specific misconfiguration at creation\ntime with a useful message. At the same time, a temporarily unavailable\nremote server must remain distinguishable from an invalid selector.\n\nFix: #11943\n\n### Does this PR introduce _any_ user-facing change?\n\n- Creating a REST-backed lakehouse-iceberg catalog with a nonblank\n`warehouse` that the remote server cannot resolve now fails at creation\nwith HTTP 400 and an actionable message.\n- If the remote service is unreachable or returns HTTP 503 while REST\ninitialization is running, the failure is returned as HTTP 502\n(`ConnectionFailedException`, error code `1007`). This can occur during\ncreation when `warehouse` is configured, or during lazy initialization\non first use when it is omitted.\n- Creation behavior for REST catalogs without `warehouse` is unchanged:\ninitialization remains deferred until first use.\n- Hive and JDBC behavior is unchanged.\n- No property keys are added or removed.\n\n### How was this patch tested?\n\n**Unit tests** (`./gradlew :core:test\n:catalogs:catalog-lakehouse-iceberg:test -PskipITs`):\n\n- `TestCatalogManager#testCreateCatalogValidatesBackendConnection`\nverifies opt-in create-time validation, rollback after validation\nfailure, and that catalogs which do not opt in are not eagerly\ninitialized.\n- `TestIcebergCatalog#testShouldValidateWarehouseProperty` covers REST\nwith a value, missing/blank warehouse, JDBC, and a null backend.\n-\n`TestIcebergCatalogOperations#testHandleRestExceptionTranslatesUnavailableToConnectionFailed`\nverifies the scoped initialization mapping for a transport failure and\nHTTP 503.\n-\n`TestIcebergCatalogOperations#testTestConnectionNonRestWarehouseHasNoHint`\nverifies that the REST-specific warehouse guidance is not shown for\nother backends.\n\n**Integration tests** (`CatalogIcebergRestIT`, against the embedded\nIceberg REST service):\n\n- `testCreateRestCatalogWithUnresolvableWarehouseFailsAtCreate` verifies\nthat an invalid selector returns the actionable error and leaves no\ncatalog behind.\n- `testCreateRestCatalogWithValidWarehouseSucceeds` verifies that a\nnamed selector recognized by the REST server creates successfully and\ncan list schemas.\n- `testCreateRestCatalogWithUnreachableServerAtCreate` verifies that a\ndependency outage during create-time validation returns\n`ConnectionFailedException` and persists nothing.\n- `testOperationOnUnreachableRestCatalogFailsWithConnectionError`\nverifies the lazy-initialization path: creation is allowed without\n`warehouse`, and the first operation reports the unavailable REST\ndependency.\n\nFull `CatalogIcebergRestIT` run: 43 tests, 1 skipped, 0 failures.\n\nManual verification covered the three warehouse-selector cases:\n\n- Recognized named selector → HTTP 200 and usable catalog.\n- Unrecognized storage-URI-shaped selector → HTTP 400 with the\nselector-versus-location explanation.\n- Omitted selector → HTTP 200 using the server\u0027s default catalog.\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "b30e45731883cfe37311ec9b6c418ffb2245e3d7",
      "tree": "04c99c0b5e5c756cc54bed35244710fe90349c61",
      "parents": [
        "988bbe043881d0d33bcffb5d0bf1091c4fb08039"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Sat Jul 18 22:40:36 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 19 13:40:36 2026 +0800"
      },
      "message": "[#11985] fix(openapi): correct the OpenLineage translation in the lineage schemas (#11989)\n\n### What changes were proposed in this pull request?\n\nA minimal correction to the lineage event schemas in\n`docs/open-api/lineage.yaml` — two line edits and two deletions, no\nstructural change:\n\n- remove the two `not: { required: [...] }` blocks on\n`DatasetEvent`/`JobEvent`;\n- fix the `eventType` example — the pipe-joined\n`START|RUNNING|COMPLETE|ABORT|FAIL|OTHER` is a single string that is not\na member of the enum; replace it with `START`;\n- change the `/lineage` request-body union from `oneOf` to `anyOf`.\n\n`BaseEvent`, the `allOf` composition, and the open schemas all stay\nexactly as they are.\n\n### Why are the changes needed?\n\nThese schemas are translated from the [OpenLineage\nspec](https://github.com/OpenLineage/OpenLineage/blob/main/spec/OpenLineage.json),\nwhose source of truth is **JSON Schema draft 2020-12**. Two constructs\ndid not survive the translation into this **OpenAPI 3.0.3** document,\nand this PR is the faithful re-translation.\n\n**1. The `not` blocks are invalid here, and ineffective everywhere.**\nThey express OpenLineage\u0027s intent that static events carry no run state.\nBut they reference properties (`job`, `run`) the enclosing schemas never\ndeclare — which is what Redocly\u0027s\n`no-required-schema-properties-undefined` flags — and `not: {required:\n[job, run]}` only rejects a payload carrying *both* fields, so a\n`DatasetEvent` carrying just `run` validated anyway. Code generators\nignore `not` entirely.\n\n**2. `oneOf` cannot survive their removal.** The event schemas are\ndeliberately **open** (OpenLineage\u0027s extensibility model), so a valid\n`RunEvent` — which carries `job` — also matches `JobEvent`. Under a\nstrict `oneOf` that is two matches, and the most common event type is\nrejected. The `not` blocks were the only thing masking this. Verified\nwith an ajv payload matrix against the bundled spec:\n\n| payload | old `oneOf` | `not` removed, `oneOf` kept | this PR\n(`anyOf`) |\n|---|---|---|---|\n| valid `RunEvent` | ACCEPT | **REJECT** (also matches `JobEvent`) |\nACCEPT |\n| valid `JobEvent` | ACCEPT | ACCEPT | ACCEPT |\n| valid `DatasetEvent` | ACCEPT | ACCEPT | ACCEPT |\n| garbage (no required fields) | REJECT | REJECT | REJECT |\n\n**Why `anyOf` is the right encoding.** With open, overlapping variants,\n\"matches exactly one\" is unsatisfiable by construction; \"matches at\nleast one well-formed event\" is the contract the endpoint actually\noffers. The per-event `required` sets keep malformed payloads out, and\nexact event discrimination stays where it already lives — the server\u0027s\ntyped deserialization. This mirrors how OpenLineage itself handles the\ngap (its OpenAPI file offloads validation to the JSON Schema layer) and\nhow Marquez, the reference implementation, accepts a single loose\n`LineageEvent`.\n\nClears 4 Redocly `recommended-strict` findings (3 ×\n`no-required-schema-properties-undefined`, 1 ×\n`no-invalid-schema-examples`).\n\nPart of #11985\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. Spec/docs-only. Every payload the old spec accepted is still\naccepted; the schemas remain open, so OpenLineage producer extensibility\nis preserved. No server behavior change.\n\n### How was this patch tested?\n\n- `redocly lint --extends\u003drecommended-strict` — the 4 lineage findings\nare cleared (10 → 6 against `main`), no new findings.\n- `redocly bundle` — bundles to valid `openapi.json`.\n- ajv payload matrix (table above) against three bundles — the original,\na `not`-removed/`oneOf`-kept variant, and this PR — confirming all valid\nevents accept and payloads missing every event\u0027s required fields reject."
    },
    {
      "commit": "988bbe043881d0d33bcffb5d0bf1091c4fb08039",
      "tree": "71a215fc1fa1a86a2fff68bc25cea41a26fe6c56",
      "parents": [
        "849aa37c71673e81bc08efd001d80dab47020577"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Sat Jul 18 20:59:30 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 19 11:59:30 2026 +0800"
      },
      "message": "[#11985] fix(openapi): clear the remaining non-lineage recommended-strict findings (#11990)\n\n### What changes were proposed in this pull request?\n\nClears the five remaining **non-lineage** Redocly `recommended-strict`\nfindings. Each is a spec-correctness fix where the described contract\ndidn\u0027t match its own schema — **documentation/spec only, no wire or\nbehavior change.**\n\n| # | Schema (file) | Lint error | Fix | Validation |\n|---|---|---|---|---|\n| 1 | `PrimitiveType` (`datatype.yaml`) | schema is `type: string` but\n`example` was a YAML **array** of type names → example doesn\u0027t match\ntype | Replaced the array with a single valid `example: \"integer\"`;\nmoved the type enumeration into `description` | finding at\n`datatype.yaml:34` cleared |\n| 2 | `UnparsedType` (`datatype.yaml`) | `unparsedType` is `type:\nstring` but the example gave an **array** `[\"unknown-type\"]` |\n`\"unparsedType\": \"unknown-type\"` | `datatype.yaml:205` cleared |\n| 3 | `UpdatePolicyContentRequest` (`policies.yaml`) | `newContent`\nexample missing the required `policyType` discriminator → example fails\nits schema | Added `\"policyType\": \"custom\"` | `policies.yaml:637`\ncleared |\n| 4 | `AddTableColumnRequest` (`tables.yaml`) |\n`position`/`defaultValue` placed `nullable`/`default` as **siblings of a\n`$ref`** (OAS 3.0 ignores keywords beside a `$ref`; the stray `nullable`\nwith no `type` breaks example validation) | Wrapped each `$ref` in\n`allOf` so `description` attaches; dropped the ignored keywords |\n`tables.yaml:506` cleared |\n| 5 | `KerberosAuth` (`openapi.yaml`) | security scheme defined but\nnever referenced (`no-unused-components`) | Added it to the global\n`security` requirement (Kerberos is a supported authenticator) rather\nthan deleting it | `openapi.yaml:636` cleared |\n\n### Why are the changes needed?\n\nThe OpenAPI description is the contract downstream, non-Java consumers\nrely on (SDK generators, connectors, mock servers). These five findings\nare invalid examples / schema mistakes that make examples fail to render\nor validate and can break typed codegen. Clearing them leaves the\n`recommended-strict` count at **4 — all of which are the lineage\nfindings**, handled separately in #11989. That puts the linter one step\nfrom enforcing (the pipeline in #11986).\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. No REST API, schema shape, or property-key changes. Rendered API-doc\nexamples become valid, and `KerberosAuth` becomes a documented auth\noption (it was already a supported authenticator).\n\n### How was this patch tested?\n\nRan Redocly strict lint (`@redocly/cli` 2.x) against the bundled spec,\nbefore and after, on this branch rebased on `main`:\n\n```\nredocly lint docs/open-api/openapi.yaml --extends\u003drecommended-strict\n```\n\n- **Before (current `main`): 9 errors** — the 5 addressed here + 4\nlineage.\n- **After (this branch): 4 errors** — all in `lineage.yaml` (`RunEvent`\n/ `DatasetEvent` / `JobEvent`), out of scope here and tracked in #11989.\n\nAll 5 targeted findings are confirmed cleared, with no new findings\nintroduced. The branch is rebased on `main` and merges cleanly.\n\nPart of #11985"
    },
    {
      "commit": "849aa37c71673e81bc08efd001d80dab47020577",
      "tree": "6255a75bb6b2c0187e8138f260459e482c788a51",
      "parents": [
        "58636323726cc519e6a7127ec0ae324025b9ae05"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Sat Jul 18 20:27:11 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 19 11:27:11 2026 +0800"
      },
      "message": "[#11985] fix(openapi): restore the ExternalType variant to DataType (#11991)\n\n### What changes were proposed in this pull request?\n\nRestores `ExternalType` to the OpenAPI `DataType` union:\n\n- adds `ExternalType` to `DataType.oneOf`;\n- moves `ExternalType` under `components.schemas`.\n\n### Why are the changes needed?\n\nGravitino can return `{\"type\":\"external\",\"catalogString\":\"...\"}` for\ncolumn types originating from external catalogs. The schema was\nmisnested and missing from `DataType`, so generated clients could not\nmodel or deserialize this valid response.\n\nPart of #11985.\n\n### Does this PR introduce any user-facing change?\n\nThe OpenAPI contract now describes a response the server could already\nreturn. There is no server behavior change.\n\n### How was this patch tested?\n\n- `./gradlew :docs:build` passes.\n- The complete specification bundles successfully with Redocly;\nassertions confirm `ExternalType` is a schema and a `DataType` variant.\n- OpenAPI Generator 7.23.0 successfully generates the full\n`typescript-fetch` client; the generated `DataType` union and decoder\ninclude `ExternalType`.\n- Progenitor A/B test using the bundled `DataType` graph:\n- `main` rejects an external-type response because its generated union\nlacks the variant;\n- this PR deserializes the same response and preserves `catalogString`.\n- The generated Rust crate passes `cargo check`; the consumer test\npasses.\n\nFull-spec Progenitor generation remains blocked earlier by an unrelated\nvendor media-type limitation tracked by #11985."
    },
    {
      "commit": "58636323726cc519e6a7127ec0ae324025b9ae05",
      "tree": "236fa31e58041effe410c8c1a07aea2de0e35e5b",
      "parents": [
        "40f9cb432369c1274ca6d1605f33a5fba2de4946"
      ],
      "author": {
        "name": "Zhiguo Wu",
        "email": "wzg547228197@gmail.com",
        "time": "Fri Jul 17 18:48:18 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 18:48:18 2026 +0800"
      },
      "message": "[MINOR] test(client-python): Fix API unit test discovery (#12041)\n\n### What changes were proposed in this pull request?\n\nAdd the missing Python package markers under the API unit test\ndirectories so that `unittest` can discover and execute those tests.\n\nAlso fix a pylint issue exposed after the API tests are included in the\ndefault test run.\n\n### Why are the changes needed?\n\nWithout the package markers, Python\u0027s unittest discovery does not\nrecurse into the API test directories, causing existing API unit tests\nto be skipped.\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\nThe Python client formatting, lint, and unit test checks passed. The\ndefault unit test task now discovers and runs the API unit tests."
    },
    {
      "commit": "40f9cb432369c1274ca6d1605f33a5fba2de4946",
      "tree": "dd4dcc961922826296ada0874c544060df78884c",
      "parents": [
        "840fe67f552ab93dcd64f4f0c4105ffa1e8c873d"
      ],
      "author": {
        "name": "Zhiguo Wu",
        "email": "wzg547228197@gmail.com",
        "time": "Fri Jul 17 18:21:34 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 18:21:34 2026 +0800"
      },
      "message": "[MINOR] fix(web-v2): Remove stale twc prefix from toast (#11950)\n\n### What changes were proposed in this pull request?\n\nThis PR removes the stale `twc-` Tailwind prefix from the web-v2\n`StyledToast` component.\n\n`web-v2` does not configure the `twc-` Tailwind prefix, while web-v1\ndoes. The remaining `twc-*` classes in `StyledToast` are migrated to the\nunprefixed Tailwind classes used by web-v2.\n\n### Why are the changes needed?\n\nThe `twc-*` classes in web-v2 do not match the current web-v2 Tailwind\nconfiguration, so those utility classes are not generated. This leaves\nthe toast component with stale styling class names.\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\nThe toast component keeps the same intended styling, but now uses valid\nweb-v2 Tailwind class names.\n\n### How was this patch tested?\n\nRan web-v2 lint, format check, and unit tests.\nVerified that no `twc-` references remain in the web-v2 source or\nTailwind config."
    },
    {
      "commit": "840fe67f552ab93dcd64f4f0c4105ffa1e8c873d",
      "tree": "17adfb969265588e30fcc82b9c56a951a0d21b3c",
      "parents": [
        "c10925adb789d5f9c1ed9519faad4bcfc46bc15a"
      ],
      "author": {
        "name": "Octavio Herrera Contreras",
        "email": "156601957+Octavi00@users.noreply.github.com",
        "time": "Fri Jul 17 03:20:35 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 18:20:35 2026 +0800"
      },
      "message": "Web UI support for built-in IdP Basic login (#11812)\n\nWeb UI support for built-in IdP Basic login [#11682] EPIC\n\n### What changes were proposed in this pull request?\n- Fix: #11681: Added Basic login functionality following front-end\nsession logic as requested + supporting UI modifications.\n\n### Files Changed + why are the changes needed?\n\n1.\nserver-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticationFilter.java\n(Feature)\n- Adds web login header check to skip the unwanted browser basic auth\npopup\n\n2. web/web/src/app/login/components/BasicLogin.js (New Component)\n- Basic Login Page setup\n- Handles user login input + error catching and message upon invalid\ncredentials\n- (Possibly add a more descriptive error message for invalid login?)\n- Prompts UI error popup upon failed login\n\n3. web/web/src/app/login/page.js (Feature)\n- When visited + logged in (confirmed with an access token) immediately\nredirects to metalakes\n- Replaced if-else logic with switch case to handle different login\ncomponents (oidc, basic, and default)\n\n4. web/web/src/app/page.js (Feature)\n- Replaced empty web page with auto redirect to real main page\n/metalakes as described to be the home page in the issue thread.\n\n5. web/web/src/lib/api/auth/index.js (API Token)\n- Added new \u0027basicToken\u0027\n- Allows simple token-accept functionalities, mainly\n\u0027X-Gravitino-Web-Login\u0027 which is useful later on for stopping unwanted\npage refresh upon invalid logins\n\n6. web/web/src/lib/auth/providers/factory.js (Feature)\n- Added new variable \u0027authenticators\u0027 to read from the current config\nfile to allow for setting \u0027providerType\u0027 and returning early before\nunsuitable OAuth behaviour, which isn\u0027t wanted for Basic Auth\n- Added missing \u0027this.providerType \u003d providerType\u0027 line\n- Added null catch case for provider now that basic doesn\u0027t carry one\n- Fixed getProvider type to prevent possible null issues\n\n7. web/web/src/lib/provider/session.js (Feature)\n- Added else-if to redirect user to the metalake (home) page if already\nlogged in otherwise forced into the login page.\n\n8. web/web/src/lib/store/auth/index.js (Feature)\n- Added basicLoginAction to replicate loginAction with custom Basic Auth\nlogic\n- Added specific variable retention during refresh in authSlice.\nPrevents logout button from disappearing during refresh\n- Added authType to extraReducers for extra security with \u0027basic\u0027\nfunctionality (previously only for oauth)\n\n9. web/web/src/lib/utils/axios/index.js (Feature)\n- Added isFullAuthHeader to set tokens relative to Basic Login auth\nwhile also maintaining jwt flow in the subsequent line\n- Added \u0027isFailedWebUILoginRequest\u0027 to prevent page refresh upon failed\nlogins (this is a feature meant for OAuth which should be disabled for\nbasic auth)\n- Added catch for 401 API pop-up caused by failed logins\n\n### Does this PR introduce _any_ user-facing change?\n\n(Please list the user-facing changes introduced by your change,\nincluding\n  1. Change in user-facing APIs\n- Added new basicLoginApi with basicToken.\n- Contains X-Gravitino-Web-Login\n\n       \n### How was this patch tested?\n\n(Please test your changes, and provide instructions on how to test it:\n1. Enabled basic auth mode using given instructions, and manually tested\n  3. Ensured additions didn\u0027t cause any new unit test errors\n4. Manually reverted to the default conf file to ensure default\nbehaviours unaffected\n\n---------\n\nCo-authored-by: Copilot Autofix powered by AI \u003c175728472+Copilot@users.noreply.github.com\u003e\nCo-authored-by: Qian Xia \u003cqian@datastrato.com\u003e"
    },
    {
      "commit": "c10925adb789d5f9c1ed9519faad4bcfc46bc15a",
      "tree": "b1d3aae66142770c40103efbd28e371ff39b0cd6",
      "parents": [
        "4074abf9147c9ce4b40bc3a6f06f864ded8745bf"
      ],
      "author": {
        "name": "Zhiguo Wu",
        "email": "wzg547228197@gmail.com",
        "time": "Fri Jul 17 16:53:52 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 16:53:52 2026 +0800"
      },
      "message": "[#12039] feat(client-python): Add view API definitions (#12040)\n\n### What changes were proposed in this pull request?\n\nThis PR adds the public Python API definitions required for view\nsupport.\n\nThe changes include:\n\n- Add `View`, `ViewCatalog`, and `ViewChange`.\n- Add `Representation`, `SQLRepresentation`, and predefined SQL\ndialects.\n- Add `Catalog.as_view_catalog()`.\n- Add view-specific exception types.\n- Add unit tests for view representations and changes.\n\n### Why are the changes needed?\n\nThe Python client currently lacks the public API contracts required to\nrepresent and manage views.\n\nFix: #12039\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. It adds public Python API definitions for views.\n\n### How was this patch tested?\n\nAdded unit tests for `View`, `ViewChange`, and `SQLRepresentation`. The\nformatting, lint, and unit test checks passed."
    },
    {
      "commit": "4074abf9147c9ce4b40bc3a6f06f864ded8745bf",
      "tree": "b18c3a40e34a304aacc0f187dbc6d7537e6eed22",
      "parents": [
        "d4190675a01f402171ffdb7db5e3313f8da6c86b"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Fri Jul 17 00:52:04 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 15:52:04 2026 +0800"
      },
      "message": "[#11985] fix(openapi): repair three codegen-breaking defects invisible to lint (#11988)\n\n### What changes were proposed in this pull request?\n\nFixes three defects in the OpenAPI spec that a linter cannot see but a\ntyped code generator chokes on. Each is a different OpenAPI 3.0 pitfall:\n\n1. **`default: null` on optional string query params** — `models.yaml`\n`uriName` (×2) and `filesets.yaml` `location_name`. `null` is not a\nvalid default for a `type: string` schema, and the params are already\n`required: false`, so the default is meaningless. → removed.\n2. **`$ref` sibling keywords silently ignored** — `tables.yaml`\n`ColumnDefaultValue` declared `nullable`, `description`, `required`, and\n`default: null` *next to* a `$ref`. In OpenAPI 3.0 everything beside a\n`$ref` is dropped, so none of it ever applied. → wrapped the `$ref` in\n`allOf` so the `description` actually attaches; dropped the\nignored/invalid keywords.\n3. **Bare `format` without `type`** — `models.yaml` model `versions`\nused `items: { format: int32 }` with no `type`, leaving a generator\nnothing to infer. → added `type: integer`.\n\n### Why are the changes needed?\n\nThe value of shipping an OpenAPI spec is that a generator can turn it\ninto a typed client with zero friction. These three defects break that:\n`progenitor`/`typify` (Rust) — the strictest common consumer — fail on\nan invalid `default`, an untyped `format`, and (for\n`ColumnDefaultValue`) a nullability annotation that never took effect.\n\nCritically, **all three pass `redocly lint\n--extends\u003drecommended-strict`** — the error count is unchanged by this\nPR (10 → 10 against `main`). They are exactly the class of problem a\nlinter cannot catch and only a codegen pass surfaces, which is why the\nspec-validation pipeline adds a `progenitor` codegen smoke test.\n\nPart of #11985\n\n### Does this PR introduce _any_ user-facing change?\n\nNo behavioral change. Spec/docs-only:\n- The three query params remain optional; only a meaningless `null`\ndefault is removed.\n- `ColumnDefaultValue` is unchanged on the wire (its `$ref` siblings\nwere already ignored); it now correctly carries its description.\n- Model `versions` items are now typed as `integer` (they already were\nintegers).\n\nNo REST API, schema, or property-key changes.\n\n### How was this patch tested?\n\n- `redocly lint --extends\u003drecommended-strict` — error count unchanged\n(10 → 10), confirming no regression and that these defects are invisible\nto the linter.\n- `redocly bundle` — the fixed spec bundles to valid `openapi.json`.\n- Confirmed zero `default: null` remain across `docs/open-api/`."
    },
    {
      "commit": "d4190675a01f402171ffdb7db5e3313f8da6c86b",
      "tree": "c2c39d796d607a1ab519c745b06c6cd7defa79df",
      "parents": [
        "11c39c2d2acfa232ebba47f8991381247ab9feee"
      ],
      "author": {
        "name": "roryqi",
        "email": "roryqi@datastrato.com",
        "time": "Fri Jul 17 15:15:43 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 15:15:43 2026 +0800"
      },
      "message": "[#12037] improvement(authz): Improve the JCasbin checks (#12001)\n\n### What changes were proposed in this pull request?\n\nImprove the JCasbin checks\n\n### Why are the changes needed?\n\nFix: #12037\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\nUT."
    },
    {
      "commit": "11c39c2d2acfa232ebba47f8991381247ab9feee",
      "tree": "18b4b61b29a66f9bb07c3ea66afd520024ece16a",
      "parents": [
        "64009ee93c770d0b99cea5053ce5840924afff8d"
      ],
      "author": {
        "name": "Henry Chen",
        "email": "henryhenry0512@gmail.com",
        "time": "Fri Jul 17 14:27:19 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 14:27:19 2026 +0800"
      },
      "message": "[#3767] refactor(client-python): Share metalake integration test setup (#12005)\n\n### What changes were proposed in this pull request?\n\n- Added shared metalake/client helpers and `MetalakeTestMixin` in the\nPython integration test environment.\n- Updated user, owner, catalog, and schema integration tests to reuse\nthe shared metalake setup and cleanup flow.\n- Kept test-specific cleanup logic for catalog and schema resources,\nwhile preserving the existing `TestCatalog` metalake cleanup logging.\n\n### Why are the changes needed?\n\nSeveral Python client integration tests repeated the same metalake\ncreation, `GravitinoClient` initialization, and metalake cleanup code.\nSharing the common setup and cleanup logic reduces duplicated test\ninfrastructure code while keeping each test\u0027s resource-specific cleanup\nbehavior unchanged.\n\nFix: #3767\n\n### Does this PR introduce *any* user-facing change?\n\nNo.\n\n### How was this patch tested?\n\n- `git diff --check`\n- `./gradlew :clients:client-python:pylint`"
    },
    {
      "commit": "64009ee93c770d0b99cea5053ce5840924afff8d",
      "tree": "b23fce1f7688d04dceb7d86b7e41574b091fedc3",
      "parents": [
        "1f5cee0770664390f1e30ad4c23db512dde2c651"
      ],
      "author": {
        "name": "gaohong",
        "email": "1348041449@qq.com",
        "time": "Fri Jul 17 13:57:48 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 13:57:48 2026 +0800"
      },
      "message": "[MINOR] fix(web-v2): Normalize service admins configuration (#12046)\n\n### What changes were proposed in this pull request?\n\nThis PR adds a shared `normalizeServiceAdmins` utility and uses it in\nthe Metalake list page and user settings component.\n\nThe utility normalizes `serviceAdmins` into an array and supports both\nformats returned by the configuration API:\n\n- An array of user names\n- A comma-separated string of user names\n\nUnsupported values are normalized to an empty array.\n\n### Why are the changes needed?\n\n`serviceAdmins` can be returned as either an array or a comma-separated\nstring. The previous duplicated page-level logic could call\n`.split(\u0027,\u0027)` on a non-string value, causing a frontend runtime error.\n\nThis change centralizes the normalization logic and prevents the UI from\nfailing when the configuration value has an unexpected type.\n\nFix: N/A\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. The UI now handles both supported `serviceAdmins` formats\nconsistently and no longer fails for unsupported values.\n\nNo user-facing API or configuration property is added or changed.\n\n### How was this patch tested?\n\n- Ran `git diff --check upstream/main...HEAD`.\n- Verified that both existing `serviceAdmins` consumers use the shared\nutility:\n  - `web-v2/web/src/app/rootLayout/UserSetting.js`\n  - `web-v2/web/src/app/metalakes/page.js`\n\nNo automated frontend tests were run.\n\nCo-authored-by: gaohong \u003cgaohong@chehejia.com\u003e"
    },
    {
      "commit": "1f5cee0770664390f1e30ad4c23db512dde2c651",
      "tree": "62f1f0569929038fb4736d2032609b06e16de141",
      "parents": [
        "ece6c4168be0eca0c0c0c2b836ec6dbc0b93826f"
      ],
      "author": {
        "name": "Henry Chen",
        "email": "henryhenry0512@gmail.com",
        "time": "Thu Jul 16 20:06:11 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 16 20:06:11 2026 +0800"
      },
      "message": "[#3767] refactor(client-python): Share table test fixtures (#11992)\n\n### What changes were proposed in this pull request?\n\nThis PR refactors duplicated Python client unit test fixtures for\nrelational table metadata.\n\nThe duplicated table and identity partition JSON fixtures are moved into\nshared test fixture helpers under\n`tests/unittests/fixtures/table_fixtures.py`. Related unit tests now\nreuse these shared fixtures instead of embedding large repeated JSON\nstrings directly in each test file.\n\n### Why are the changes needed?\n\nSeveral Python client unit tests duplicated the same table and partition\nJSON payloads, which contributes to the remaining `duplicate-code`\nPylint violations tracked by #3767.\n\nCentralizing these fixtures makes the tests easier to maintain and\nreduces duplicated test data without changing the tested behavior.\n\nRelated to #3767.\n\n### Does this PR introduce *any* user-facing change?\n\nNo. This change only refactors Python client unit test fixtures.\n\n### How was this patch tested?\n\n```bash\ngit diff --cached --check"
    },
    {
      "commit": "ece6c4168be0eca0c0c0c2b836ec6dbc0b93826f",
      "tree": "54029b74789844a8618f69caf5202a8396d1cc3d",
      "parents": [
        "f229b4c4272e15e0f0c21ea30ec64b4593082f9c"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu Jul 16 19:57:28 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 16 19:57:28 2026 +0800"
      },
      "message": "build(deps): update fakeredis requirement from \u003c2.35.0 to \u003c2.37.0 in /mcp-server (#11866)\n\nUpdates the requirements on\n[fakeredis](https://github.com/cunla/fakeredis-py) to permit the latest\nversion.\n\u003cdetails\u003e\n\u003csummary\u003eRelease notes\u003c/summary\u003e\n\u003cp\u003e\u003cem\u003eSourced from \u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/releases\"\u003efakeredis\u0027s\nreleases\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003ch2\u003ev2.36.2\u003c/h2\u003e\n\u003ch3\u003e🐛 Bug Fixes\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003efix: replace async \u003ccode\u003ecan_read\u003c/code\u003e busy-poll with event-based\nwakeup (\u003ca\nhref\u003d\"https://redirect.github.com/cunla/fakeredis-py/issues/506\"\u003e#506\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003efix: enforce \u003ccode\u003eGT\u003c/code\u003e/\u003ccode\u003eLT\u003c/code\u003e conditions in\n\u003ccode\u003eZADD\u003c/code\u003e with \u003ccode\u003eINCR\u003c/code\u003e (\u003ca\nhref\u003d\"https://redirect.github.com/cunla/fakeredis-py/issues/507\"\u003e#507\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003efix: reject negative \u003ccode\u003eLIMIT\u003c/code\u003e in \u003ccode\u003eSINTERCARD\u003c/code\u003e\nand \u003ccode\u003eZINTERCARD\u003c/code\u003e (\u003ca\nhref\u003d\"https://redirect.github.com/cunla/fakeredis-py/issues/508\"\u003e#508\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003efix: EXPIRE GT/LT mishandle keys with no existing TTL (\u003ca\nhref\u003d\"https://redirect.github.com/cunla/fakeredis-py/issues/509\"\u003e#509\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003efix: restore missing\n\u003ccode\u003eaddr\u003c/code\u003e/\u003ccode\u003eladdr\u003c/code\u003e/\u003ccode\u003efd\u003c/code\u003e fields in\n\u003ccode\u003eCLIENT LIST\u003c/code\u003e/\u003ccode\u003eCLIENT INFO\u003c/code\u003e (\u003ca\nhref\u003d\"https://redirect.github.com/cunla/fakeredis-py/issues/512\"\u003e#512\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003efix: SORT/SORT_RO BY \u003c!-- raw HTML omitted --\u003e must keep natural\norder (\u003ca\nhref\u003d\"https://redirect.github.com/cunla/fakeredis-py/issues/510\"\u003e#510\u003c/a\u003e)\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2\u003eNew Contributors\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href\u003d\"https://github.com/xav-ie\"\u003e\u003ccode\u003e@​xav-ie\u003c/code\u003e\u003c/a\u003e made\ntheir first contribution in \u003ca\nhref\u003d\"https://redirect.github.com/cunla/fakeredis-py/pull/506\"\u003ecunla/fakeredis-py#506\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href\u003d\"https://github.com/gaoflow\"\u003e\u003ccode\u003e@​gaoflow\u003c/code\u003e\u003c/a\u003e made\ntheir first contribution in \u003ca\nhref\u003d\"https://redirect.github.com/cunla/fakeredis-py/pull/507\"\u003ecunla/fakeredis-py#507\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eFull Changelog\u003c/strong\u003e: \u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/compare/v2.36.1...v2.36.2\"\u003ehttps://github.com/cunla/fakeredis-py/compare/v2.36.1...v2.36.2\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003eCommits\u003c/summary\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/commit/7d830d698a16b6397c3111e788501f3dc77ed8e8\"\u003e\u003ccode\u003e7d830d6\u003c/code\u003e\u003c/a\u003e\ndoc:update\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/commit/f47bbe7613d5c8c0560ddae33e796640f3376b2d\"\u003e\u003ccode\u003ef47bbe7\u003c/code\u003e\u003c/a\u003e\nfix: correct key formatting in ClientInfo initialization and items\nmethod\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/commit/b23d5fdaa29a6171535a5c99b2b51a714f994295\"\u003e\u003ccode\u003eb23d5fd\u003c/code\u003e\u003c/a\u003e\nfix: SORT/SORT_RO BY \u0026lt;constant\u0026gt; must keep natural order (\u003ca\nhref\u003d\"https://redirect.github.com/cunla/fakeredis-py/issues/510\"\u003e#510\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/commit/178105295fdca5d983ae69a93edd6b25adf58e85\"\u003e\u003ccode\u003e1781052\u003c/code\u003e\u003c/a\u003e\nfix: correct key formatting in client_info items method\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/commit/a24c832302a166d6137fc88cd99e6ecc534492c9\"\u003e\u003ccode\u003ea24c832\u003c/code\u003e\u003c/a\u003e\ntest: validate presence of \u0027age\u0027 and \u0027addr\u0027 in client_info response\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/commit/4b06dff4e7ee5ec3e9160c979dbab599307865c6\"\u003e\u003ccode\u003e4b06dff\u003c/code\u003e\u003c/a\u003e\nadd default addr\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/commit/adbce34bb098ec5723e63595662f4f17941a4fc6\"\u003e\u003ccode\u003eadbce34\u003c/code\u003e\u003c/a\u003e\nfix: GETEX without options must not clear the key TTL (\u003ca\nhref\u003d\"https://redirect.github.com/cunla/fakeredis-py/issues/511\"\u003e#511\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/commit/edd3b3cd108389fcada857f5105d798a4375abaa\"\u003e\u003ccode\u003eedd3b3c\u003c/code\u003e\u003c/a\u003e\nupdate fake_only marker and doc\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/commit/37966fb03b6ae5a2a9828132497a6ae31eb5d4aa\"\u003e\u003ccode\u003e37966fb\u003c/code\u003e\u003c/a\u003e\ndoc:update\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/commit/72ef07dba4e3d03a0d78e77dc93a38a158fa5bb1\"\u003e\u003ccode\u003e72ef07d\u003c/code\u003e\u003c/a\u003e\nchore:update deps\u003c/li\u003e\n\u003cli\u003eAdditional commits viewable in \u003ca\nhref\u003d\"https://github.com/cunla/fakeredis-py/compare/v0.3.0...v2.36.2\"\u003ecompare\nview\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/details\u003e\n\u003cbr /\u003e\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e\nCo-authored-by: Jerry Shao \u003cjerryshao@datastrato.com\u003e"
    },
    {
      "commit": "f229b4c4272e15e0f0c21ea30ec64b4593082f9c",
      "tree": "15d2b7262dfcef27658819ea722c8aa04e80c280",
      "parents": [
        "41cb729b6ee1e00037ee7b52ae7379a437e727e8"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu Jul 16 19:56:38 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 16 19:56:38 2026 +0800"
      },
      "message": "build(deps): bump cachetools from 7.0.1 to 7.1.4 in /clients/client-python (#11868)\n\nBumps [cachetools](https://github.com/tkem/cachetools) from 7.0.1 to\n7.1.4.\n\u003cdetails\u003e\n\u003csummary\u003eChangelog\u003c/summary\u003e\n\u003cp\u003e\u003cem\u003eSourced from \u003ca\nhref\u003d\"https://github.com/tkem/cachetools/blob/master/CHANGELOG.rst\"\u003ecachetools\u0027s\nchangelog\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003ch1\u003ev7.1.4 (2026-05-22)\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eMinor unit test improvements.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eUpdate build environment.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1\u003ev7.1.3 (2026-05-18)\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eMinor type stub improvements.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eUpdate build environment.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1\u003ev7.1.2 (2026-05-16)\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eMinor type stub improvements.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eMinor documentation improvements.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eModernize build environment.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1\u003ev7.1.1 (2026-05-03)\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003eVarious type stub improvements.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1\u003ev7.1.0 (2026-05-01)\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eAdd type stubs based on the work of the good people at \u003ccode\u003etypeshed\n\u0026lt;https://github.com/python/typeshed/tree/main/stubs/cachetools/\u0026gt;\u003c/code\u003e__.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eUpdate unit tests.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1\u003ev7.0.6 (2026-04-20)\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eMinor code improvements.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eUpdate project URLs.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eUpdate CI environment.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e... (truncated)\u003c/p\u003e\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003eCommits\u003c/summary\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/tkem/cachetools/commit/48284d73d0a8834c9c50f8d41bb99e6f93b2dfed\"\u003e\u003ccode\u003e48284d7\u003c/code\u003e\u003c/a\u003e\nRelease v7.1.4.\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/tkem/cachetools/commit/55ea96b88a485fca9effae0f838186274f00897c\"\u003e\u003ccode\u003e55ea96b\u003c/code\u003e\u003c/a\u003e\nUpdate build environment.\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/tkem/cachetools/commit/c5439fe5dc883220b59469e450dbcbf9f4c2e52d\"\u003e\u003ccode\u003ec5439fe\u003c/code\u003e\u003c/a\u003e\nAdd threading tests for lock-only decorators.\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/tkem/cachetools/commit/91828fccd629d426157a165d38563614ba06a875\"\u003e\u003ccode\u003e91828fc\u003c/code\u003e\u003c/a\u003e\nRun threading tests unconditionally with timeout.\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/tkem/cachetools/commit/16952edb1eb2d2ced7601e12db722008e5156912\"\u003e\u003ccode\u003e16952ed\u003c/code\u003e\u003c/a\u003e\nRelease v7.1.3.\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/tkem/cachetools/commit/92dd756b93813d1ddfe70893e9c219342a52e19a\"\u003e\u003ccode\u003e92dd756\u003c/code\u003e\u003c/a\u003e\nPrepare v7.1.3.\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/tkem/cachetools/commit/ced08f52ef792a010b8171715c7842da4e11b9ac\"\u003e\u003ccode\u003eced08f5\u003c/code\u003e\u003c/a\u003e\nImprove cachetools.func type stubs.\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/tkem/cachetools/commit/d809d7be5a222effd3663c33baaaee3802972daa\"\u003e\u003ccode\u003ed809d7b\u003c/code\u003e\u003c/a\u003e\nUpdate build environment.\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/tkem/cachetools/commit/c84b5e5be3d33a32d33f0988b524fb86de1e44f2\"\u003e\u003ccode\u003ec84b5e5\u003c/code\u003e\u003c/a\u003e\nRelease v7.1.2.\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/tkem/cachetools/commit/39ad61c1db56600fe903f3c4216996c491e775bf\"\u003e\u003ccode\u003e39ad61c\u003c/code\u003e\u003c/a\u003e\nPrepare v7.1.2.\u003c/li\u003e\n\u003cli\u003eAdditional commits viewable in \u003ca\nhref\u003d\"https://github.com/tkem/cachetools/compare/v7.0.1...v7.1.4\"\u003ecompare\nview\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/details\u003e\n\u003cbr /\u003e\n\n\n[![Dependabot compatibility\nscore](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name\u003dcachetools\u0026package-manager\u003dpip\u0026previous-version\u003d7.0.1\u0026new-version\u003d7.1.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)\n\nDependabot will resolve any conflicts with this PR as long as you don\u0027t\nalter it yourself. You can also trigger a rebase manually by commenting\n`@dependabot rebase`.\n\n[//]: # (dependabot-automerge-start)\n[//]: # (dependabot-automerge-end)\n\n---\n\n\u003cdetails\u003e\n\u003csummary\u003eDependabot commands and options\u003c/summary\u003e\n\u003cbr /\u003e\n\nYou can trigger Dependabot actions by commenting on this PR:\n- `@dependabot rebase` will rebase this PR\n- `@dependabot recreate` will recreate this PR, overwriting any edits\nthat have been made to it\n- `@dependabot show \u003cdependency name\u003e ignore conditions` will show all\nof the ignore conditions of the specified dependency\n- `@dependabot ignore this major version` will close this PR and stop\nDependabot creating any more for this major version (unless you reopen\nthe PR or upgrade to it yourself)\n- `@dependabot ignore this minor version` will close this PR and stop\nDependabot creating any more for this minor version (unless you reopen\nthe PR or upgrade to it yourself)\n- `@dependabot ignore this dependency` will close this PR and stop\nDependabot creating any more for this dependency (unless you reopen the\nPR or upgrade to it yourself)\n\n\n\u003c/details\u003e\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e\nCo-authored-by: Jerry Shao \u003cjerryshao@datastrato.com\u003e"
    },
    {
      "commit": "41cb729b6ee1e00037ee7b52ae7379a437e727e8",
      "tree": "b9fd35d1ed4d29990d30b2d9f571991a42a9ed5f",
      "parents": [
        "b473bfd98fde3cb4a1e93b73f5279badaf8ec015"
      ],
      "author": {
        "name": "Henry Chen",
        "email": "henryhenry0512@gmail.com",
        "time": "Thu Jul 16 19:30:49 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 16 19:30:49 2026 +0800"
      },
      "message": "[#3767] refactor(client-python): Share metalake operation test fixtures (#11993)\n\n### What changes were proposed in this pull request?\n\n- Add shared Python client unit test fixtures for metalake operation\ntests.\n- Move repeated user, group, and role DTO builders into\n`operation_test_fixtures.py`.\n- Share the `GravitinoClient` test instance creation and `get_metalake`\nmock setup across user, group, and role delegate tests.\n- Update metalake user/group/role unit tests to reuse the shared helpers\ninstead of duplicating setup code.\n\n### Why are the changes needed?\n\nThe metalake user, group, and role operation unit tests duplicated\nsimilar DTO construction and client mock setup code. This contributes to\nthe Python client\u0027s duplicate-code pylint cleanup work and makes the\ntests harder to keep consistent.\n\nCentralizing these fixtures reduces repeated test boilerplate while\nkeeping the tested behavior unchanged.\n\nRelated to #3767.\n\n### Does this PR introduce *any* user-facing change?\n\nNo.\n\n### How was this patch tested?\n\n- `PYTHONPATH\u003d.\n/private/tmp/gravitino-client-python-test-venv/bin/python -m unittest\ntests.unittests.client.test_metalake_user_operations\ntests.unittests.client.test_metalake_group_operations\ntests.unittests.client.test_metalake_role_operations`\n  - Result: `Ran 54 tests`, `OK`\n- `PYTHONPATH\u003d. PYLINTHOME\u003d/private/tmp/gravitino-pylint-cache\n/private/tmp/gravitino-client-python-test-venv/bin/pylint\n--rcfile\u003dpylintrc --persistent\u003dn\ntests/unittests/client/operation_test_fixtures.py\ntests/unittests/client/test_metalake_user_operations.py\ntests/unittests/client/test_metalake_group_operations.py\ntests/unittests/client/test_metalake_role_operations.py`\n  - Result: `10.00/10`\n- `git diff --check`"
    },
    {
      "commit": "b473bfd98fde3cb4a1e93b73f5279badaf8ec015",
      "tree": "7fd29667f36441744d7e426ae940c60e71f3e9f0",
      "parents": [
        "4059726b7f6435941d710b4f223da9767cc9698f"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Thu Jul 16 04:18:42 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 16 19:18:42 2026 +0800"
      },
      "message": "[MINOR] chore: Add nevzheng as GitHub triage collaborator (#12028)\n\n### What changes were proposed in this pull request?\n\nAdd `nevzheng` to the `github.collaborators` section of `.asf.yaml`.\n\nFor Apache repositories, external collaborators listed in this section\nreceive the GitHub Triage role. This role permits issue and pull-request\nmanagement without granting write access to the code.\n\nThis PR is intentionally being opened as a draft while the community\ndiscusses and records the required approval.\n\n### Why are the changes needed?\n\nThis change would allow `nevzheng` to help organize the Gravitino\ncontribution workflow by:\n\n- Triaging and labeling issues and pull requests.\n- Assigning issues and pull requests.\n- Applying milestones.\n- Requesting pull-request reviews.\n- Helping route contributions to the appropriate reviewers.\n\nThese permissions would make PR responsibilities clearer and allow\nreview work to be coordinated through GitHub.\n\nThe Apache GitHub Triage role is documented here:\n\nhttps://infra.apache.org/github-roles.html\n\nApproval discussion: #12027\n\nThis PR should remain a draft and should not be merged until the\nrequired project approval is recorded.\n\nFix: #12027\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. This only updates repository collaboration permissions and does not\nchange Gravitino code, APIs, or configuration exposed to users.\n\n### How was this patch tested?\n\nValidated that `.asf.yaml` remains valid YAML and that the patch passes\n`git diff --check`."
    },
    {
      "commit": "4059726b7f6435941d710b4f223da9767cc9698f",
      "tree": "d337f1395c114cf1d69fe2d3812de5e9e2ac2fcf",
      "parents": [
        "4ff7aac2590b2285df4ac19ccb47e75ed6098089"
      ],
      "author": {
        "name": "Henry Chen",
        "email": "henryhenry0512@gmail.com",
        "time": "Thu Jul 16 12:00:34 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 16 12:00:34 2026 +0800"
      },
      "message": "[#5820] test(client-python): Improve IT server lifecycle (#11977)\n\n### What changes were proposed in this pull request?\n\nThis PR improves the Gravitino server lifecycle used by Python client\nintegration tests.\n\nThe Gradle `integrationTest` task now uses dedicated\n`startGravitinoServer` and `stopGravitinoServer` tasks, with\n`stopGravitinoServer` registered through `finalizedBy` so Gradle\nattempts to stop the server even when the integration test task fails.\n\nWhen `START_EXTERNAL_GRAVITINO\u003dtrue` and the expected external Gravitino\nserver is unavailable, fail the integration test immediately with a\nclear exception instead of only logging an error and continuing.\n\nThis PR also centralizes the `START_EXTERNAL_GRAVITINO` check in\n`IntegrationTestEnv.use_external_gravitino()` and updates several\nauthorization-related integration tests to reuse that helper.\n\n### Why are the changes needed?\n\nThe Python client integration test setup already supports using an\nexternally started Gravitino server in CI. However, the server lifecycle\nlogic was still embedded directly inside the `integrationTest` task, and\nthe external-server environment check was duplicated across multiple\ntest classes.\n\nSeparating start and stop into dedicated Gradle tasks makes the\nlifecycle easier to understand and safer on failure. Centralizing the\nenvironment check also keeps related integration tests consistent.\n\nFailing fast makes a missing or unreachable externally managed server\nimmediately visible to CI and developers, avoiding follow-up failures\nwith less actionable error messages.\n\nRelated to #5820.\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. This change only affects Python client integration test\ninfrastructure.\n\n### How was this patch tested?\n\n- `git diff --check`"
    },
    {
      "commit": "4ff7aac2590b2285df4ac19ccb47e75ed6098089",
      "tree": "246fa237f7ac20106ef88d196fcaf874486a881f",
      "parents": [
        "e5fe677dabe0e533d52ea2be870967297fef33fe"
      ],
      "author": {
        "name": "jarred0214",
        "email": "jarred0214@gmail.com",
        "time": "Thu Jul 16 11:52:04 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 16 11:52:04 2026 +0800"
      },
      "message": "[#12014] fix(core): Don\u0027t wrap plain ClassNotFoundException in IsolatedClassLoader (#12015)\n\n### What changes were proposed in this pull request?\n\nThis PR updates `IsolatedClassLoader.CustomURLClassLoader#loadClass` to\nrethrow a\n`ClassNotFoundException` from `doLoadClass` as-is, instead of wrapping\nit in a new\n`ClassNotFoundException` with a cause. Other exceptions are still\nwrapped as before.\n\nIt also adds a unit test verifying that a class-miss surfaces as a plain\n`ClassNotFoundException` with a `null` cause.\n\n### Why are the changes needed?\n\nProbe-driven callers such as Janino (used by codegen paths, e.g. Paimon)\ntry a series of\ncandidate class names (`Override`, then `java.lang.Override`, ...) and\nrely on the standard\nclassloader contract: a `ClassNotFoundException` with a `null` cause\nmeans \"class does not\nexist, try the next candidate\", while a non-null cause means a fatal\nloading failure.\n\n`IsolatedClassLoader` previously wrapped every miss in a\n`ClassNotFoundException` with a\ncause, which turned an ordinary probe miss into a fatal failure and\nbroke such codegen\npaths under the isolated catalog classloader.\n\nNote: the classloader does not (and should not) resolve simple names\nlike `Override` to\n`java.lang.Override` itself; with this fix, probing callers can handle\nthat themselves.\n\nFix: #12014\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\nAdded `testClassNotFoundExceptionDoesNotWrapProbeMiss` in\n`TestIsolatedClassLoader`,\nwhich fails against the old wrapping behavior and passes with this\nchange.\n\n---------\n\nCo-authored-by: jarred0214 \u003cjarredhj@163.com\u003e"
    },
    {
      "commit": "e5fe677dabe0e533d52ea2be870967297fef33fe",
      "tree": "d676a85afe9a244d3ad51308d809c8de5aaa07aa",
      "parents": [
        "8d6591d9f0917841ad1a2b87a35c761da1d3da67"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Wed Jul 15 20:47:41 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 16 11:47:41 2026 +0800"
      },
      "message": "[#11963] fix(cli): support parsing the variant column type (#11964)\n\n### What changes were proposed in this pull request?\n\nAdds a `variant` case to the CLI\u0027s `TypeConverter.convert(String)` so a\nuser can specify a `variant` column from the terminal via the type\nstring (e.g. `--datatype variant`). Previously this threw\n`IllegalArgumentException: Unknown or unsupported type: variant`, even\nthough `Types.VariantType` exists in the type system.\n\n- `TypeConverter.java`: map `\"variant\"` → `Types.VariantType.get()`.\n- Added unit tests in `TestTypeConverter` and `TestParseType` covering\nboth the direct converter and the full `ParseType.toType(\"variant\")`\nentry point.\n\n### Why are the changes needed?\n\nThe native `variant` type (#11932) was wired into the type system but\nnot into the CLI\u0027s string→type parsing. As a result the CLI could not\ncreate or alter a column of type `variant`, leaving a gap in the\n\"specify a column by typing its type string\" flow. `variant` is a\nparameterless primitive, so parsing is lossless.\n\nFix: #11963\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. The CLI now accepts `variant` as a column datatype\n(case-insensitive), e.g.:\n\n```\ngravitino table create ... --columns col --datatype variant\n```\n\n### How was this patch tested?\n\nAdded and ran unit tests:\n\n```\n./gradlew :clients:cli:test --tests \u0027org.apache.gravitino.cli.TestTypeConverter\u0027 \\\n                            --tests \u0027org.apache.gravitino.cli.TestParseType\u0027 -PskipITs\n```\n\nBoth pass. Tests assert `TypeConverter.convert(\"variant\")` and\n`ParseType.toType(\"variant\")` return `Types.VariantType`."
    },
    {
      "commit": "8d6591d9f0917841ad1a2b87a35c761da1d3da67",
      "tree": "715ece56bd00bc6d90ba305af4edce42a79a32f8",
      "parents": [
        "771c5e22cd69bb68097a96b454fb764c938e99a8"
      ],
      "author": {
        "name": "StormSpirit",
        "email": "jiangxt2@vip.qq.com",
        "time": "Wed Jul 15 19:29:40 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 15 19:29:40 2026 +0800"
      },
      "message": "[#11946] improvement(api): Add properties field to TableChange.AddIndex for custom index parameters (#12013)\n\n### What changes were proposed in this pull request?\n\nAdd a `Map\u003cString, String\u003e properties` field to `TableChange.AddIndex`,\nenabling catalog implementations to accept custom index parameters\n(e.g., GRANULARITY for ClickHouse data-skipping indexes) through the\nALTER TABLE ADD INDEX path.\n\nThe change touches four layers:\n\n- **API** (`TableChange.java`): New `addIndex(type, name, fieldNames,\nproperties)` factory method; existing no-properties method delegates\nwith `Collections.emptyMap()`. Defensive copy + null safety in\nconstructor. `equals`/`hashCode` updated.\n- **REST DTO** (`TableUpdateRequest.java`): Pass `index.properties()`\nthrough deserialization.\n- **Core** (`ManagedTableOperations.java`): Pass\n`addIndex.getProperties()` when building the new `Index` object.\n- **ClickHouse catalog**: Both CREATE TABLE and ALTER TABLE paths\nresolve GRANULARITY and `set(N)` max values from `index.properties()`,\nfalling back to sensible defaults when absent. Constants defined for\nproperty keys (`granularity`, `set_max_values`).\n\n### Why are the changes needed?\n\n`TableChange.AddIndex` currently carries only `type`, `name`, and\n`fieldNames` — no `properties`. This means the ALTER TABLE ADD INDEX\npath cannot pass index-specific parameters to catalog implementations,\nforcing them to use hardcoded defaults.\n\nConcrete example: the ClickHouse data-skipping index support (see\n#11806) allows custom GRANULARITY via `Index.properties()`. CREATE TABLE\nrespects user-specified GRANULARITY, but ALTER TABLE ADD INDEX was stuck\nat `GRANULARITY 1` because `AddIndex` had no way to carry the value.\nThis PR closes that gap for both CREATE TABLE and ALTER TABLE paths.\n\nFix: #11946\n\n### Does this PR introduce _any_ user-facing change?\n\nYes.\n\n- New public API: `TableChange.addIndex(IndexType, String, String[][],\nMap\u003cString, String\u003e)`. The existing 3-parameter overload is unchanged\nand fully backward compatible.\n- **Behavior change (ClickHouse catalog)**: bloom_filter default\nGRANULARITY is unified to `1` (previously `3` in CREATE TABLE, `1` in\nALTER TABLE). This aligns with the ClickHouse server default.\n\n### How was this patch tested?\n\n- **Unit tests**: `TestTableChange` — 6 new tests covering properties,\nnull safety, equals/hashCode. `TestClickHouseTableOperations` — SQL\ngeneration for custom properties in both CREATE TABLE and ALTER TABLE\npaths, plus negative tests for invalid input.\n- **Docker integration test**:\n`CatalogClickHouseIT.testAlterTableAddIndexWithCustomGranularity` —\nend-to-end ALTER TABLE ADD INDEX with GRANULARITY\u003d5, verified via `SHOW\nCREATE TABLE`.\n\nRun commands:\n- `./gradlew :catalogs-contrib:catalog-jdbc-clickhouse:test -PskipITs`\n(unit tests)\n- `./gradlew :catalogs-contrib:catalog-jdbc-clickhouse:test --tests\n\"CatalogClickHouseIT\" -PskipDockerTests\u003dfalse` (Docker integration test)\n\n---------\n\nSigned-off-by: jiangxt2 \u003cjiangxt2@vip.qq.com\u003e"
    },
    {
      "commit": "771c5e22cd69bb68097a96b454fb764c938e99a8",
      "tree": "5cc07e16ebb6a67e39e190b07e764b1682545e86",
      "parents": [
        "438da702f194cf441a421b354073ac8eb843d688"
      ],
      "author": {
        "name": "Bharath Krishna",
        "email": "bmurali@roku.com",
        "time": "Tue Jul 14 23:35:50 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 15 14:35:50 2026 +0800"
      },
      "message": "[#11966] feat(auth): add X-Gravitino-Active-Roles header grammar and parser (#11970)\n\n### What changes were proposed in this pull request?\n\nFirst step of role assumption (SET ROLE): the header grammar and parser\nonly, with no wiring and no behavior change yet.\n\n- Add the `X-Gravitino-Active-Roles` header constant to `AuthConstants`.\n- Add `ActiveRoles`, an immutable value type modeling the declared\nactive roles as `ALL` / `NONE` / `NAMED` (ordered, de-duplicated role\nnames).\n- Add `ActiveRolesParser`, which parses a raw header value into\n`ActiveRoles`:\n  - absent / blank value maps to `ALL` (today\u0027s behavior);\n- entries are trimmed and de-duplicated; role names are case-sensitive;\n- `ALL` and `NONE` are reserved keywords (exact upper-case) and must\neach appear alone;\n- a malformed value (empty entry, or a reserved keyword combined with\nanything) raises `IllegalActiveRolesException` (extends\n`IllegalArgumentException`).\n- Unit tests covering the full grammar, malformed inputs, and the value\ntype.\n\nThis is intentionally scoped to the model plus parser so it can be\nreviewed on its own. Carrying the value on the request, the `400`/`403`\nmapping, and enforcement follow in later PRs.\n\nPart of #11966.\n\n### Why are the changes needed?\n\nImplements Phase 1 of the approved design\n[design-docs/gravitino-role-assumption.md](https://github.com/apache/gravitino/blob/main/design-docs/gravitino-role-assumption.md)\n(discussion #10894). A caller needs a way to declare which of its roles\nshould be active for a request so authorization can be narrowed; this PR\nestablishes the header grammar that the rest of the feature builds on.\n\n### Does this PR introduce _any_ user-facing change?\n\nNot yet. It adds the `X-Gravitino-Active-Roles` header name and its\ngrammar as internal building blocks, but nothing reads the header or\nchanges authorization behavior in this PR.\n\n### How was this patch tested?\n\nNew unit tests (JUnit 5): `TestActiveRolesParser` covers single role,\ncomma-separated lists, whitespace trimming, duplicate collapsing,\ncase-sensitivity, `ALL`/`NONE`, absent/blank values, and malformed\ninputs; `TestActiveRoles` covers the value type contract. Verified\nlocally:\n\n- `./gradlew :common:test --tests\n\"org.apache.gravitino.auth.TestActiveRolesParser\" --tests\n\"org.apache.gravitino.auth.TestActiveRoles\"`\n- `./gradlew :common:spotlessCheck :common:javadoc`"
    },
    {
      "commit": "438da702f194cf441a421b354073ac8eb843d688",
      "tree": "64791fc306a4d164bccc576d673d3f2f0238398e",
      "parents": [
        "69ef06b81ba947a275922086a720a92335d644ad"
      ],
      "author": {
        "name": "Yuhui",
        "email": "hui@datastrato.com",
        "time": "Wed Jul 15 11:35:40 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 15 11:35:40 2026 +0800"
      },
      "message": "[#12009] improvement(core): Support custom case sensitivity normalization in Capability (#12010)\n\n### What type of PR is it?\nImprovement\n\n### What does this PR do?\nAdds a `default String normalizeName(Scope scope, String name)` method\nto `Capability`, and updates `CapabilityHelpers` to call it instead of\nhardcoding a lowercase fold. This lets catalogs override normalization\nfor case sensitivity behavior that isn\u0027t a simple lowercase fold (e.g.\nuppercase-by-default catalogs, or ones supporting both quoted\ncase-sensitive and unquoted case-insensitive forms).\n\n### Related issues\n#12009\n\n### Does this PR introduce any user-facing change?\nNo\n\n### How was this patch tested?\n`./gradlew :core:test --tests\n\"org.apache.gravitino.connector.capability.TestCapability\"`"
    },
    {
      "commit": "69ef06b81ba947a275922086a720a92335d644ad",
      "tree": "32e8f43c722024af79cf1be71f229bb19fb30bba",
      "parents": [
        "425112e554b91fb20370288bed006f03faa57fc2"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Tue Jul 14 20:24:37 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 15 11:24:37 2026 +0800"
      },
      "message": "[#11951] feat(iceberg): map V3 unknown type to Gravitino NullType (#11969)\n\n### What changes were proposed in this pull request?\n\nMap Iceberg V3\u0027s `unknown` type to Gravitino\u0027s existing\n`Types.NullType`, so an `unknown` column loads through the native\nmetadata API as a first-class `null` type instead of the\n`ExternalType(\"UNKNOWN\")` stopgap it resolves to today.\n\n- `catalog-lakehouse-iceberg`: `FromIcebergType` maps `unknown →\nNullType`; `ToIcebergType` / `ToIcebergTypeVisitor` map `NullType →\nunknown` (via a `nullType()` dispatch hook, since `NullType` isn\u0027t a\n`PrimitiveType`), and reject a required (non-nullable) `unknown` column\nper the Iceberg spec.\n- docs: unified type reference (`Null type` section), Iceberg\ntype-mapping table, OpenAPI examples.\n\nNo new `api` type and no `common`/Python serde change — `NullType`\nalready exists, round-trips as the JSON token `\"null\"`, and is already\nmapped for Spark, Flink, Lance, and CLI (the Python client decodes\n`\"null\"` to `NullType` with no change).\n\nBuilds on the merged native `variant` support (#11932) and\nformat-version-3 support (#11954).\n\nFixes #11951\n\n### Why are the changes needed?\n\nLoading an Iceberg V3 table with an `unknown` column through the native\nAPI resolves it to `ExternalType(\"UNKNOWN\")` — opaque (nothing can\nbranch on it), not writable back, and external types have caused\ndownstream problems (unqueryable via Trino #10957; `catalogString()`\nwritten verbatim into DDL #11805). `unknown` is the universal null/void\ncolumn type — Iceberg\u0027s own converters map it to each engine\u0027s null type\n(Spark `NullType`, Flink `NULL`, Arrow `null`) — and Gravitino already\nmodels that as `NullType`, so this is just the missing wiring.\n\n### Does this PR introduce _any_ user-facing change?\n\nYes — Iceberg V3 tables with an `unknown` column now load through the\nnative API as `null` (previously `external(UNKNOWN)`), and a\n`null`-typed column can be written to a format-version-3 Iceberg table.\nOther connectors are unchanged. Connector propagation (reject-with-test\nfor engines without a null-type equivalent) is a planned follow-up.\n\n### How was this patch tested?\n\n- Unit (`TestConvertUtil`): `testUnknownType` (converter both\ndirections) and `testUnknownColumnToIcebergSchema` (write path →\noptional `unknown` field; required column rejected).\n- Docker IT (`CatalogIcebergRestIT`, passing) — both cross-surface\ndirections between the Iceberg REST (IRC) API and the native metadata\nAPI:\n- `testV3TypeConversionViaIcebergClient`: IRC writes an `unknown` column\n→ the native API reads it back as `NullType`.\n- `testCreateUnknownColumnWriteRoundTrip`: the native API writes a\n`NullType` column at format-version 3 → native reload returns\n`NullType`, and the IRC reads the same table back as Iceberg `unknown`."
    },
    {
      "commit": "425112e554b91fb20370288bed006f03faa57fc2",
      "tree": "6ba59f09c51a97eb0096cddb5b00c0b8388eab6a",
      "parents": [
        "40ab9a58f2909f9c8f8c3c27f6ca510fc4ffbf8a"
      ],
      "author": {
        "name": "StormSpirit",
        "email": "jiangxt2@vip.qq.com",
        "time": "Tue Jul 14 22:08:51 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 14 22:08:51 2026 +0800"
      },
      "message": "[MINOR] docs: update Doris catalog documentation for 3.0.x/4.0.x compatibility (#11995)\n\n### What changes were proposed in this pull request?\n\nUpdate the Doris catalog documentation to reflect the type system,\nindex, and auto-increment improvements from PRs #11763 and #11731.\n\n- **Type mapping table**: Add `Binary`, `JSON`, `Variant`,\n`IPv4`/`IPv6`, `LargeInt`, `Bitmap`, `HLL`, `DateV2`\n- **Index section**: Expand from PRIMARY_KEY-only to 5 index types\n(`PRIMARY_KEY`, `UNIQUE_KEY`, `INVERTED`, `BITMAP`, `VECTOR`) with DDL\ndescriptions, version requirements, and examples\n- **Auto-Increment**: Replace \"Unsupported for now\" with full\ndocumentation including Doris server constraints and JSON/Java examples\n- **Version compatibility**: Add `:::tip` block listing minimum Doris\nversions for each feature\n- **BITMAP legacy behavior**: Document write-only round-trip — creating\na BITMAP index reads back as `INVERTED` because Doris 4.0.6 removed\nBITMAP from the grammar\n- **DUPLICATE KEY clarification**: Note that DUPLICATE KEY tables are\ncreated by omitting key indexes\n\n### Why are the changes needed?\n\nThe existing documentation only covers Doris 1.2.x with minimal index\nsupport (PRIMARY_KEY only). PRs #11763 and #11731 added support for new\ntypes, indexes, and auto-increment, but the documentation was not\nupdated accordingly.\n\nRelated: #11590\n\n### Does this PR introduce any user-facing change?\n\nNo. Documentation only.\n\n### How was this patch tested?\n\nDocumentation only. All DDL descriptions verified against unit tests in\n`TestDorisTableOperationsSqlGeneration` and source code in\n`DorisTableOperations.java`.\n\n---------\n\nSigned-off-by: jiangxt2 \u003cjiangxt2@vip.qq.com\u003e"
    },
    {
      "commit": "40ab9a58f2909f9c8f8c3c27f6ca510fc4ffbf8a",
      "tree": "8651ec61724918ab4daf841b995b1f29dfd697a6",
      "parents": [
        "e71af9ca988f70b0f11c76c4bbc5416343ddcbb5"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Tue Jul 14 03:25:44 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 14 18:25:44 2026 +0800"
      },
      "message": "[#11983] test(iceberg): add end-to-end coverage for round-trip of remaining V2 column types (#11984)\n\n### What changes were proposed in this pull request?\n\nAdds `testCreateAndLoadIcebergTableV2Types` to `CatalogIcebergBaseIT`.\nIt creates a table containing the V2 column types that were not\npreviously exercised end-to-end — `boolean`, `float`, `double`, `uuid`,\n`fixed`, `binary`, `decimal`, `list`, `map` — through the native\nmetadata API, loads it back, and asserts that (a) the Gravitino types\nround-trip unchanged and (b) the underlying Iceberg schema stores the\nexpected Iceberg types. Because these are all valid V2 types, the test\nruns on both backends via the existing subclasses\n(`CatalogIcebergRestIT`, `CatalogIcebergHiveIT`) with no assumptions.\n\n### Why are the changes needed?\n\n`CatalogIcebergBaseIT` only round-tripped a subset of V2 types through a\nlive backend (`int`, `long`, `date`, `string`, `struct`, `time`,\n`timestamp`). The converter mappings for the remaining types are\nunit-tested in `TestConvertUtil`, but were never verified through the\nreal REST/IRC and Hive backends — so backend/serialization regressions\nfor `decimal` (precision/scale), `fixed` (length), `list`, and `map` in\nparticular could slip through. This closes that gap.\n\nFix: #11983\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. Test-only.\n\n### How was this patch tested?\n\nThis PR adds the test (`testCreateAndLoadIcebergTableV2Types`), and it\npasses. Run locally on both backends via `-PskipTests\n-PskipDockerTests\u003dfalse`:\n\n- `CatalogIcebergRestIT.testCreateAndLoadIcebergTableV2Types` — passed\n(1 test, 0 failures).\n- `CatalogIcebergHiveIT.testCreateAndLoadIcebergTableV2Types` — passed\n(1 test, 0 failures)."
    },
    {
      "commit": "e71af9ca988f70b0f11c76c4bbc5416343ddcbb5",
      "tree": "e55a0fa778b7e05c36bd2b793e07ee1a118a6a71",
      "parents": [
        "7ba1da716619cef26b31bb323aaa62f3e5a061c7"
      ],
      "author": {
        "name": "Zhiguo Wu",
        "email": "wzg547228197@gmail.com",
        "time": "Tue Jul 14 14:24:28 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 14 14:24:28 2026 +0800"
      },
      "message": "[#11902] feat(client-java): Support tags for views and functions (#11903)\n\n### What changes were proposed in this pull request?\n\nThis PR adds Java client support for tag operations on views and\nfunctions.\n\nChanges include:\n\n- Add default `supportsTags()` methods to the `View` and `Function` API\ninterfaces.\n- Add `GenericView` and `GenericFunction` client wrappers backed by\n`MetadataObjectTagOperations`.\n- Return tag-capable wrappers from Java client view and function\noperations.\n- Add Java client tests for view/function tag support.\n\n### Why are the changes needed?\n\n#11844 enables server-side tag association for `VIEW` and `FUNCTION`\nmetadata objects. The Java client should expose the same capability so\nusers can manage tags for views and functions through the typed client\nAPI, consistent with other tag-capable metadata objects.\n\nFix: #11902\nRelated: #11844\n\n### Does this PR introduce _any_ user-facing change?\n\nYes.\n\nJava client users can now call `supportsTags()` on `View` and `Function`\nobjects returned by the client, for example:\n\n```java\nview.supportsTags().associateTags(...);\nfunction.supportsTags().listTags();\n```\n\n### How was this patch tested?\n\n- `./gradlew :clients:client-java:test -PskipITs`\n\n---------\n\nCo-authored-by: Jerry Shao \u003cjerryshao@datastrato.com\u003e"
    },
    {
      "commit": "7ba1da716619cef26b31bb323aaa62f3e5a061c7",
      "tree": "3060f836b23952c7bdc6c0a6677f32dbbb283488",
      "parents": [
        "9fbed05f36d18670a4b51efd20ca0fbbd22f1c7e"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Mon Jul 13 23:15:22 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 14 14:15:22 2026 +0800"
      },
      "message": "[#11962] feat(iceberg): support V3 nanosecond timestamp types (#11938)\n\nFixes #11962. Design discussed in #11936.\n\n### What changes were proposed in this pull request?\n\nSupport Iceberg V3\u0027s nanosecond timestamp types, `timestamp_ns` and\n`timestamptz_ns`, in the Iceberg\ncatalog. They currently load as an opaque\n`ExternalType(\"TIMESTAMP_NANO\")` and cannot be created\nthrough the native API.\n\nGravitino\u0027s `TimestampType` already carries a precision, so these map\nonto the existing\n`timestamp(9)` / `timestamp_tz(9)` — no new type is added. The change is\nconfined to the Iceberg\nconverter:\n\n- `FromIcebergType`: `TIMESTAMP_NANO` → `TimestampType.withTimeZone(9)`\n/ `withoutTimeZone(9)`,\n  honoring `shouldAdjustToUTC()`.\n- `ToIcebergType`: a `timestamp[_tz]` at precision `9` →\n`TimestampNanoType.withZone()` /\n`withoutZone()`. Precision `6` (or unset) still maps to the microsecond\n`TimestampType`; any other\n  precision is rejected, since Iceberg V3 has only these two.\n\n| Gravitino timestamp precision | Iceberg type |\n|---|---|\n| unset or `6` | `timestamp` / `timestamptz` (µs) — unchanged |\n| `9` | `timestamp_ns` / `timestamptz_ns` (ns) — new |\n| any other | rejected (Iceberg cannot represent it) — unchanged |\n\nThe mapping is bijective, so the round-trip is lossless.\n\nIt also updates the `lakehouse-iceberg-catalog` type-mapping table —\nadding the `timestamp(9)` /\n`timestamp_tz(9)` rows and correcting a pre-existing inversion in the\n`timestamp` / `timestamptz`\nzone mapping.\n\n### Why are the changes needed?\n\nLoading an Iceberg V3 table with a `timestamp_ns` / `timestamptz_ns`\ncolumn throws away its identity\nas an `ExternalType`, and such a column cannot be created natively.\nMapping to `timestamp[_tz](9)`\ngives it a precise, native type.\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. Iceberg V3 tables with a `timestamp_ns` / `timestamptz_ns` column\nnow load as `timestamp(9)` /\n`timestamp_tz(9)`, and such columns can be created through the native\nAPI on a V3-capable Iceberg\nbackend. Microsecond timestamps are unaffected.\n\n### How was this patch tested?\n\n- Unit (`TestConvertUtil`): both directions, both zones, a precision-6\nregression, and the\n  unsupported-precision reject case.\n- Integration (`CatalogIcebergBaseIT`), run locally against Docker:\n- REST backend: a round-trip creating a format-version-3 table with\n`timestamp_ns` /\n`timestamptz_ns` columns and asserting the native round-trip plus the\nunderlying Iceberg schema.\n    Passes.\n- REST backend, IRC→Gravitino read path: a raw-Iceberg V3 `timestamp_ns`\n/ `timestamptz_ns`\ncreate (`testV3TypeConversionViaIcebergClient`) loads back through the\nnative API as\n    `timestamp(9)` / `timestamp_tz(9)`, not `ExternalType`. Passes.\n- Hive backend: the round-trip is skipped — Iceberg\u0027s `HiveSchemaUtil`\nhas no `TIMESTAMP_NANO` arm\n(apache/iceberg#11937), so a Hive-backed create throws from within\nIceberg.\n- Updated `testTimeTypePrecisionValidation`: precision 9 is no longer\nrejected for\ntimestamp/timestamptz, so its `_col_9` cases were dropped and the\nmessage assertions updated.\n- Python client (`test_type_serdes`): `timestamp(9)` / `timestamp_tz(9)`\nround-trip through the type\nserde (serialize + deserialize). No client source change needed — the\ngeneric precision parser\n  already handles it."
    },
    {
      "commit": "9fbed05f36d18670a4b51efd20ca0fbbd22f1c7e",
      "tree": "3edd160fe37a73ab3c49561bd4faeb2f7c9754dd",
      "parents": [
        "cf289fb759c26cb8cde984cb98e35b9770f08ff4"
      ],
      "author": {
        "name": "Mark Hoerth",
        "email": "47870294+markhoerth@users.noreply.github.com",
        "time": "Mon Jul 13 18:07:45 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 14 09:07:45 2026 +0800"
      },
      "message": "[MINOR] fix: polish log and error message quality across Java modules (#10387)\n\nImproves the English quality of user-facing log messages, exception\nmessages, and configuration descriptions across `api`, `authorizations`,\n`bundles`, `catalogs` (paimon, hadoop-common, hive-metastore-common),\n`clients`, `core`, `iceberg`, `server`, `server-common`, and\n`trino-connector` modules.\n\nAll changes are native English speaker corrections with no behavioral\nimpact, except for two genuine message-content fixes called out below.\n\n### Message-content fixes (not pure polish)\n\n1. **`GCSTokenGenerator`** (`bundles/gcp/.../GCSTokenGenerator.java`):\nthe `IOException` message concatenated a path directly after a period:\n`\"GCS credential file does not exist.\" + gcsCredentialFilePath`. At\nruntime that produces `...does not exist./path/to/key.json`. Changed the\nperiod to a colon-space so the message reads correctly.\n\n2. **`RangerHelper`** (`authorizations/.../RangerHelper.java`): a\n`LOG.warn` had a malformed format string: `\"The role({}) does not exist\nin the Ranger!, e: {}\"`. The spurious `!,` and the `e: {}` pattern\nproduced confusing output. Replaced with `\"Role {} does not exist in\nRanger\"` and let SLF4J auto-format the trailing exception.\n\n### Polish patterns applied consistently\n\n- `\"Can not\"` / `\"can not\"` → `\"Cannot\"` / `\"cannot\"`\n- `\"Fail to\"` → `\"Failed to\"`\n- `\"id\"` → `\"ID\"`, `\"uri\"` → `\"URI\"` when used as identifiers\n- `\"SIP\"` → `\"SPI\"` (typo in `GravitinoConnectorPluginManager`)\n- `\"Grivitino\"` → `\"Gravitino\"` (typo in `GravitinoConfig`)\n- Removed trailing periods from log and exception messages being touched\n- Fixed missing spaces after colons in log messages\n- Fixed non-idiomatic phrasing (\"would better use\", \"throw a exception\",\n\"occurs an unexpected\")\n- Improved `GravitinoConfig` property descriptions for clarity\n- Capitalized project names within touched messages (Ranger)\n\n### Notes\n\n- Rebased onto current `main`; resolved conflicts in four files where\nupstream had refactored around messages originally touched\n(`CatalogHookDispatcher`, `JcasbinAuthorizer`, `SchemaOperations`,\n`GravitinoConfig`).\n- `./gradlew spotlessApply` run and folded in; `spotlessCheck` passes\nclean.\n- Scope is broader than the originally-stated \"core, server,\ntrino-connector\". Now covers the catalog modules that the original PR\ndescription had called out as a follow-on.\n- A separate follow-on PR for broader readability and meaning\nimprovements in user-facing messages will come once this lands.\n\nCo-authored-by: Mark Hoerth \u003cmark@datastrato.com\u003e"
    },
    {
      "commit": "cf289fb759c26cb8cde984cb98e35b9770f08ff4",
      "tree": "4549f57845f52d6ff22eecd5c2b1de64bb497066",
      "parents": [
        "c2c1b8a6727bcd54742e248fb6e5f16e7ca497bb"
      ],
      "author": {
        "name": "StormSpirit",
        "email": "jiangxt2@vip.qq.com",
        "time": "Mon Jul 13 21:31:37 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 13 21:31:37 2026 +0800"
      },
      "message": "[#11590] fix(doris): fix partition parsing for Doris 3.0+ format (#11732)\n\n## What changes were proposed in this pull request?\n\n### Partition Regex Fix (`DorisUtils.java`)\n\n- Added `\\\\s*` to `PARTITION_INFO_PATTERN` to tolerate whitespace\nbetween `LIST`/`RANGE` and `(` in Doris 3.0+ `SHOW CREATE TABLE` output\n(`PARTITION BY LIST (` vs `PARTITION BY LIST(`)\n\n### Integration Tests\n\n- `CatalogDorisIT` (1.2.x): `testListPartitionRoundTrip` +\n`testMultiColumnListPartitionRoundTrip`\n- `CatalogDoris3xIT` (3.0.x): `testListPartitionRoundTrip` +\n`testRangePartitionRoundTrip`\n- `CatalogDoris4xIT` (4.0.x): `testListPartitionRoundTrip` +\n`testRangePartitionRoundTrip`\n\nAll tests verify partition metadata survives the create → Doris → load\nround-trip.\n\n\nFixed: #11590\n\n## Does this PR introduce any user-facing change?\n\nNo. Partition parsing fix is internal to Gravitino metadata loading.\n\n## How was this patch tested?\n\nUnit tests: `TestDorisUtils` (existing, covers both LIST and RANGE\nparsing)\n\nIntegration tests (Docker, all three Doris versions):\n- `CatalogDorisIT` (1.2.x): 19/19 passed\n- `CatalogDoris3xIT` (3.0.x): 8/8 passed\n- `CatalogDoris4xIT` (4.0.x): 8/8 passed\n\n---------\n\nSigned-off-by: jiangxt2 \u003cjiangxt2@vip.qq.com\u003e"
    },
    {
      "commit": "c2c1b8a6727bcd54742e248fb6e5f16e7ca497bb",
      "tree": "d05f9e1e1730c92ebbb4684d8d2ada096fcc2d53",
      "parents": [
        "fe44f02ecf63fd7f36c3b53b4c180d659ac75605"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Mon Jul 13 01:57:05 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 13 16:57:05 2026 +0800"
      },
      "message": "[#11955] feat(iceberg): native geometry and geography type support for Iceberg V3 (#11958)\n\n### What changes were proposed in this pull request?\n\nAdds native `Types.GeometryType(crs)` and `Types.GeographyType(crs,\nalgorithm)` to Gravitino\u0027s unified type system, so Iceberg V3 geospatial\ncolumns load through the native metadata API as first-class types\ninstead of the read-only `ExternalType(\"GEOMETRY\")` /\n`ExternalType(\"GEOGRAPHY\")` stopgap, and become creatable through\nGravitino.\n\n- **api**: `Type.Name.GEOMETRY`/`GEOGRAPHY` + two parameterized\n`PrimitiveType`s (modeled on `FixedType`/`DecimalType`). CRS is an\nopaque string preserved verbatim and compared case-insensitively; the\ngeography edge algorithm is validated against the five spec values\n(`spherical`, `vincenty`, `thomas`, `andoyer`, `karney`) and normalized\nto lowercase. Defaults (`OGC:CRS84`, `spherical`) elide to the bare\n`geometry`/`geography` token.\n- **common (`JsonUtils`)**: registers the default tokens and parses the\nparameterized form; the matcher runs against the original\n(non-lowercased) type string so a case-sensitive CRS survives the\nround-trip.\n- **catalog-lakehouse-iceberg**: `FromIcebergType`/`ToIcebergType`\nround-trip CRS (and algorithm) via the existing `atomic()` path.\n- **clients/client-python, docs, OpenAPI**: mirror both types + serde.\n\n### Why are the changes needed?\n\nIceberg V3 adds `geometry` and `geography` (planar vs. spheroidal\ngeospatial shapes, WKB-encoded, parameterized by CRS ± edge algorithm).\nToday they load as `external(GEOMETRY/GEOGRAPHY)`, which drops the\nCRS/algorithm to a bare name and is opaque to consumers (external types\nhave caused downstream issues — #10957, #11805), and the write path\nthrows. Native types preserve the parameters and make the columns\nwritable. As a catalog, Gravitino only describes the column and\npreserves metadata; it does not interpret the WKB.\n\nFix: #11955\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. New unified types `Types.GeometryType` / `Types.GeographyType`\n(Java + Python), with JSON tokens `geometry` / `geometry(\u003ccrs\u003e)` and\n`geography` / `geography(\u003ccrs\u003e,\u003calgorithm\u003e)`. Iceberg\n`geometry`/`geography` columns now load natively and can be created via\nGravitino at `format-version \u003d 3`. No property-key changes.\n\n### How was this patch tested?\n\n- Unit: `TestTypes` (contract + validation + case-insensitivity),\n`TestJsonUtils` (JSON serde incl. mixed-case CRS), `TestConvertUtil`\n(converter both directions, non-default CRS/algorithm); Python\n`test_types.py` + `test_type_serdes.py` (serialize/deserialize\nround-trip for `geometry(srid:3857)` and `geography(EPSG:4326,karney)`).\n- Integration (REST/IRC backend): `CatalogIcebergRestIT` —\n`testV3TypeConversionViaIcebergClient` (IRC→Gravitino native load),\n`testCreateGeometryColumnWriteRoundTrip` and\n`testCreateGeographyColumnWriteRoundTrip` (Gravitino→IRC write\nround-trip). Ran green: 3 tests, 0 skipped, 0 failures.\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "fe44f02ecf63fd7f36c3b53b4c180d659ac75605",
      "tree": "fee2ed0da39d299c301884e8050ef113643899b5",
      "parents": [
        "61ee16ae6367576200e9b2ff13c30fd1a627602e"
      ],
      "author": {
        "name": "Jerry Shao",
        "email": "jerryshao@datastrato.com",
        "time": "Mon Jul 13 16:46:51 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 13 16:46:51 2026 +0800"
      },
      "message": "[#11976] fix(core): refresh built-in job template when content drifts even if version is unchanged (#11978)\n\n### What changes were proposed in this pull request?\n\n`BuiltInJobTemplateEventListener.reconcileBuiltInJobTemplates()` decided\nwhether to refresh a persisted built-in job template by comparing only\nthe `version` custom field. This PR makes it also compare the full\ntemplate content (via `JobTemplate.equals()`) and refresh the persisted\nrecord whenever it drifts, regardless of the version field. Also added a\nregression test,\n`testReconcileBuiltInJobTemplatesUpdateWhenExecutableChangedButVersionSame`.\n\n### Why are the changes needed?\n\nThe `executable` path of a built-in job template is resolved from the\non-disk jar location at server startup\n(`BuiltInJob.resolveExecutable()`), so it changes across a Gravitino\nrelease upgrade even though the job logic version (`\"v1\"`) stays the\nsame. Since reconciliation only checked `version`, the stale jar path\nfrom the old release was never refreshed in the database, causing\nbuilt-in job runs to fail after upgrade with `Source file does not\nexist: /opt/gravitino/auxlib/gravitino-jobs-\u003cold-version\u003e.jar`.\n\nFix: #11976\n\n### Does this PR introduce _any_ user-facing change?\n\nNo user-facing API changes. Built-in job templates will now self-heal\ntheir `executable` path (and other fields) on server startup after an\nupgrade, instead of requiring manual intervention.\n\n### How was this patch tested?\n\nAdded a unit test verifying that when `executable` differs but `version`\nis unchanged, the persisted template is updated. Ran\n`TestBuiltInJobTemplateEventListener` (14 tests, all passing)."
    },
    {
      "commit": "61ee16ae6367576200e9b2ff13c30fd1a627602e",
      "tree": "bb1a1605a51f72ac4d590b960afc1ca9b6bac6c5",
      "parents": [
        "f5948f872b593632031f8ed8a71cf9733f07f0ae"
      ],
      "author": {
        "name": "MehulBatra",
        "email": "66407733+MehulBatra@users.noreply.github.com",
        "time": "Mon Jul 13 14:14:37 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 13 16:44:37 2026 +0800"
      },
      "message": "[#11980] fix(core): sort column ordinal positions while fetching from entity store (#11979)\n\nCloses: #11980 \n\n\u003c!--\n1. Title: [#\u003cissue\u003e] \u003ctype\u003e(\u003cscope\u003e): \u003csubject\u003e\n   Examples:\n     - \"[#123] feat(operator): Support xxx\"\n     - \"[#233] fix: Check null before access result in xxx\"\n     - \"[MINOR] refactor: Fix typo in variable name\"\n     - \"[MINOR] docs: Fix typo in README\"\n     - \"[#255] test: Fix flaky test NameOfTheTest\"\n   Reference: https://www.conventionalcommits.org/en/v1.0.0/\n2. If the PR is unfinished, please mark this PR as draft.\n--\u003e\n\n### What changes were proposed in this pull request?\n\nProposed fix:\n\nSort by position in toGenericTable():\n\n```\n.withColumns(                                                                                                                                                   \n    tableEntity.columns().stream()                                                                                                                              \n        .sorted(Comparator.comparingInt(ColumnEntity::position))                                                                                                \n        .map(this::toGenericColumn)                                                                                                                             \n        .toArray(Column[]::new))    \n```  \n        \n        \ncolumn_position is already persisted correctly, so this fixes existing\ntables on read with no migration or re-registration. Comparator and\nColumnEntity are already imported in the file.\n\n\n### Why are the changes needed?\n\nManagedTableOperations.toGenericTable() builds the column list from\ntableEntity.columns() without sorting by position, so loaded tables can\nreturn columns in an arbitrary, unstable order even though\ncolumn_position is stored correctly. This breaks position-based\nconsumers (e.g. the Lance REST catalog), causing wrong column mapping or\ncrashes. The fix sorts columns by position on load, matching what the\nalter path already does.\nFix: #11980\n\n### Does this PR introduce _any_ user-facing change?\n\nNo API or property changes.\nBehavior fix only: loadTable/describe now always returns columns in\ntheir declared position order (previously they could come back\nunordered). No user-facing API changes, and no property keys added or\nremoved.\n\n### How was this patch tested?\n\nBefore Fix: \n\u003cimg width\u003d\"1803\" height\u003d\"606\" alt\u003d\"image\"\nsrc\u003d\"https://github.com/user-attachments/assets/34b7c62f-91e2-44d7-9a54-52a270ba8c9e\"\n/\u003e\n\nAfter Fix:\n\u003cimg width\u003d\"1395\" height\u003d\"430\" alt\u003d\"image\"\nsrc\u003d\"https://github.com/user-attachments/assets/26f4cfe6-7ac4-4719-bde5-d0fc868bdee8\"\n/\u003e\n\n\nWithout sorting:\n\n\u003cimg width\u003d\"1395\" height\u003d\"523\" alt\u003d\"image\"\nsrc\u003d\"https://github.com/user-attachments/assets/0146aa9f-6918-497a-a83e-88f2a65e7f3d\"\n/\u003e\n\n\nWith sorting:\n\n\u003cimg width\u003d\"1395\" height\u003d\"523\" alt\u003d\"image\"\nsrc\u003d\"https://github.com/user-attachments/assets/5e8f9c34-3d28-48fe-b7ff-6d272776afa9\"\n/\u003e"
    },
    {
      "commit": "f5948f872b593632031f8ed8a71cf9733f07f0ae",
      "tree": "75f00d42f92c1bebac8f89f0833a6b2ef797c3d8",
      "parents": [
        "b464820eb0ca1d51ec954a368c7e6e256a8fceb5"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Mon Jul 13 01:23:19 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 13 16:23:19 2026 +0800"
      },
      "message": "[#11985] fix(openapi): drop stray OpenAPI 3.1 null token from statistics value (#11987)\n\n### What changes were proposed in this pull request?\n\nRemoves the `type: \"null\"` branch from `Statistic.value`\u0027s `oneOf` in\n`docs/open-api/statistics.yaml` and gives the field a more useful\ndescription.\n\n### Why are the changes needed?\n\n`type: \"null\"` is JSON Schema / OpenAPI **3.1** syntax. The document\ndeclares `openapi: 3.0.3`, where it is invalid — Redocly\n`recommended-strict` reports it as a `struct` error, the single hard\nstructural error in the spec.\n\n`value` is already optional: `StatisticDTO` holds it as an `@Nullable`\n`Optional\u003cStatisticValue\u003c?\u003e\u003e` and it is not in the schema\u0027s `required`\nlist, so \"no value\" is expressed by the field\u0027s absence. The null branch\nis therefore unnecessary, and dropping it keeps a clean typed `oneOf`\nfor codegen.\n\nThis is genuinely a 3.0.3 document with one stray 3.1 token, so it is\nfixed in place. Expressing a union-of-types-plus-null natively would\nrequire upgrading to 3.1, which removes the `nullable` keyword and would\nmean migrating ~130 `nullable` usages plus verifying tooling — a\nseparate discussion, out of scope here.\n\nPart of #11985\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. Spec/docs-only. `Statistic.value` remains optional and typed; no\nREST API, schema, or property-key changes.\n\n### How was this patch tested?\n\n`cd dev/openapi \u0026\u0026 npx redocly lint --extends\u003drecommended-strict` — the\n`statistics.yaml` finding is cleared (total errors 10 → 9) with no new\nfindings introduced."
    },
    {
      "commit": "b464820eb0ca1d51ec954a368c7e6e256a8fceb5",
      "tree": "87425f3c75f502cb202b325174f4e90b44c89fe6",
      "parents": [
        "4894435f7a396c9f96b86bfbc6368bd8a6b2f56a"
      ],
      "author": {
        "name": "Zhiguo Wu",
        "email": "wuzhiguo@apache.org",
        "time": "Mon Jul 13 15:19:43 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 13 15:19:43 2026 +0800"
      },
      "message": "[#11844] feat(tag): Support tags for views and functions (#11897)\n\n### What changes were proposed in this pull request?\n\nThis PR adds tag support for VIEW and FUNCTION metadata objects.\n\nThe changes include:\n- Allowing VIEW and FUNCTION in `TagManager` supported metadata object\ntypes.\n- Updating OpenAPI definitions for metadata object tag operations.\n- Cleaning up function tag relations when functions are dropped.\n- Cleaning up view/function tag relations during catalog and schema\ncascade deletion, including PostgreSQL-specific SQL providers.\n- Adding unit tests for tag association and cascade cleanup behavior.\n\n### Why are the changes needed?\n\nViews and functions are first-class metadata objects in Gravitino, but\nthey were excluded from tag association support. Users could not\norganize, classify, or discover views and functions through tags like\nother metadata objects.\n\nFix: #11844\n\n### Does this PR introduce _any_ user-facing change?\n\nYes.\n\nUsers can now associate, list, get, and remove tags for VIEW and\nFUNCTION metadata objects through the existing metadata object tag REST\nAPIs.\n\n### How was this patch tested?\n\nAdded unit tests covering:\n- TagManager support for VIEW and FUNCTION.\n- REST metadata object tag operations for VIEW and FUNCTION.\n- Tag relation cleanup when views/functions are dropped.\n- Catalog/schema cascade cleanup for view/function tag relations.\n- PostgreSQL tag relation cascade SQL provider coverage.\n\nRan:\n\n```bash\n./gradlew :core:test --tests org.apache.gravitino.storage.relational.mapper.provider.postgresql.TestTagMetadataObjectRelPostgreSQLProvider --tests org.apache.gravitino.storage.relational.service.TestSchemaMetaService --tests org.apache.gravitino.storage.relational.service.TestCatalogMetaService\n```"
    },
    {
      "commit": "4894435f7a396c9f96b86bfbc6368bd8a6b2f56a",
      "tree": "22ea833befe7bf9b85fd2b202732f94ff0b352dc",
      "parents": [
        "e211d267a0ef31d4844dfe2165a8c33bc8ed90c6"
      ],
      "author": {
        "name": "StormSpirit",
        "email": "jiangxt2@vip.qq.com",
        "time": "Mon Jul 13 14:27:49 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 13 14:27:49 2026 +0800"
      },
      "message": "[#11880] fix(clickhouse): preserve SETTINGS clause on table round-trip (#11885)\n\n### What changes were proposed in this pull request?\n\nFix SETTINGS clause lost on table round-trip in the ClickHouse catalog.\n\nThe write path (`appendTableProperties`) correctly generates `SETTINGS\nkey \u003d value` from\ntable properties with `settings.` prefix, but the read path never parsed\nSETTINGS back into\ntable properties. This caused SETTINGS to be silently dropped on\nround-trip.\n\nChanges:\n- Added `SETTINGS_PATTERN` regex to extract the SETTINGS clause from\n`SHOW CREATE TABLE` output\n- Extended `ShowCreateTableMetadata` with a `settings` field\n- `parseCreateStatement()` now parses SETTINGS content (in addition to\nexisting ORDER BY / PARTITION BY parsing)\n- `load()` merges parsed SETTINGS into table properties with `settings.`\nprefix\n\n### Why are the changes needed?\n\nFix: #11880\n\nTables with custom SETTINGS (e.g. `index_granularity \u003d 4096`) lost those\nsettings when loaded\nthrough Gravitino, because the read path only extracted COMMENT, ENGINE,\nand cluster metadata\nfrom `system.tables`.\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. Table properties now include `settings.*` keys for tables with\ncustom SETTINGS, enabling\ncorrect round-trip of ClickHouse table settings.\n\n### How was this patch tested?\n\n- New test `testParseSettingsFromCreateSql` in\n`TestClickHouseTableOperations` (4 test cases: empty,\nsingle, multiple settings, settings with COMMENT). Note: this class has\n`@Tag(\"gravitino-docker-test\")`\nat class level, so the test runs under `-PskipDockerTests\u003dfalse` (not\n`-PskipITs`), though the\n  test logic itself is pure string parsing with no container dependency.\n- Docker integration tests: `./gradlew\n:catalogs-contrib:catalog-jdbc-clickhouse:test -PskipDockerTests\u003dfalse\n--tests \"TestClickHouseTableOperations\"`\n\n---------\n\nSigned-off-by: jiangxt2 \u003cjiangxt2@vip.qq.com\u003e"
    },
    {
      "commit": "e211d267a0ef31d4844dfe2165a8c33bc8ed90c6",
      "tree": "f33cf1f69bd307462b3cee21252c7770488969fe",
      "parents": [
        "effa6f2d6f5b4122cc1e477bac56004b6e5c1a17"
      ],
      "author": {
        "name": "Sachin Ranjalkar",
        "email": "52783123+sachinnn99@users.noreply.github.com",
        "time": "Mon Jul 13 07:01:01 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 13 09:31:01 2026 +0800"
      },
      "message": "[#10685] feat(iceberg-rest): Support vended credentials on planTableScan endpoint (#10800)\n\n### What changes were proposed in this pull request?\n\nAdd `X-Iceberg-Access-Delegation` header support to the `planTableScan`\nendpoint and return storage credentials in the response when\n`vended-credentials` is requested. Mirrors the existing\ncredential-vending flow from `createTable`/`loadTable`/`registerTable`\n(#10684).\n\nChanges:\n- `IcebergTableOperations.planTableScan`: read\n`@HeaderParam(X_ICEBERG_ACCESS_DELEGATION)`, compute\n`isCredentialVending`, build `IcebergRequestContext` with credential\nflag\n- `IcebergTableOperationExecutor.planTableScan`: resolve\n`CredentialPrivilege` and pass credential-vending flag through the\ndispatcher to `CatalogWrapperForREST`\n- `CatalogWrapperForREST.planTableScan`: add 4-arg overload that accepts\n`requestCredentialVending` and `CredentialPrivilege`; inject credentials\nvia `PlanTableScanResponse.Builder.withCredentials()` *after* cache\nlookup so cached plans remain credential-free\n- `CatalogWrapperForREST.injectScanCredentials`: reuses the\nalready-loaded `Table` object from scan planning (no redundant\n`loadTable`), catches only `ServiceUnavailableException`\n- `CatalogWrapperForREST.getCredentialFromTable`: extracts credentials\nfrom a `Table` object directly, following the same location/property\nlogic as `getCredential(TableMetadata, ...)`\n- `FederatedCatalogWrapper.planTableScan`: overrides to fetch\ncredentials from the remote REST catalog instead of the local credential\nmanager\n-\n`TestCatalogWrapperForREST.testPlanTableScanCacheDoesNotLeakCredentials`:\nverifies cached plans don\u0027t leak credentials to non-vended requests and\nthat vended requests on cache hits get fresh credentials\n- `TestIcebergTableOperations`: three new tests — credential vending (no\nheader / local / S3 with prefix verification), remote-signing rejection\n(406), invalid header (400)\n\n### Why are the changes needed?\n\nThe Iceberg REST spec defines `X-Iceberg-Access-Delegation` as a valid\nheader on `planTableScan` and `CompletedPlanningResult` includes a\n`storage-credentials` field. Currently, clients performing server-side\nscan planning must make a separate `GET .../credentials` call to obtain\nstorage access credentials before reading the data files returned in the\nscan plan.\n\nFixes #10685\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. The `planTableScan` REST endpoint now accepts the\n`X-Iceberg-Access-Delegation` header and returns vended credentials in\nthe response `storage-credentials` field when requested. Backward\ncompatible -- clients that do not send the header get existing behavior.\n\n### How was this patch tested?\n\nAdded unit tests:\n-\n`TestCatalogWrapperForREST.testPlanTableScanCacheDoesNotLeakCredentials`\n-- vended request returns credentials, non-vended cache hit returns\nnone, vended cache hit returns fresh credentials\n- `TestIcebergTableOperations.testPlanTableScanWithCredentialVending` --\nno vending without header, no vending for `file://` location, vending\npresent for `s3://` location with correct credential type and prefix\n-\n`TestIcebergTableOperations.testPlanTableScanRemoteSigningNotSupported`\n-- 406 response\n- `TestIcebergTableOperations.testPlanTableScanInvalidAccessDelegation`\n-- 400 response\n\nAll existing tests pass (no regressions)."
    },
    {
      "commit": "effa6f2d6f5b4122cc1e477bac56004b6e5c1a17",
      "tree": "665f5bbf349457ac1376580ae94602ced6cf6a85",
      "parents": [
        "8e2d4ddafbb5c1d2973320c34316ddeac10b494a"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Jul 10 18:19:22 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 10 18:19:22 2026 +0800"
      },
      "message": "build(deps): bump fastmcp from 3.2.0 to 3.4.2 in /mcp-server (#11869)\n\nBumps [fastmcp](https://github.com/PrefectHQ/fastmcp) from 3.2.0 to\n3.4.2.\n\u003cdetails\u003e\n\u003csummary\u003eRelease notes\u003c/summary\u003e\n\u003cp\u003e\u003cem\u003eSourced from \u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/releases\"\u003efastmcp\u0027s\nreleases\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003ch2\u003ev3.4.2: Heads Up\u003c/h2\u003e\n\u003cp\u003eFastMCP 3.4.2 restores JWT compatibility for providers that include\nprivate, non-critical JWS header parameters. Tokens from providers like\nClerk can carry header metadata such as \u003ccode\u003ecat\u003c/code\u003e without being\nrejected before signature and claim validation, while unsupported\ncritical headers are still rejected.\u003c/p\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003ch2\u003eWhat\u0027s Changed\u003c/h2\u003e\n\u003ch3\u003eFixes 🐞\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAllow private JWT headers by \u003ca\nhref\u003d\"https://github.com/jlowin\"\u003e\u003ccode\u003e@​jlowin\u003c/code\u003e\u003c/a\u003e in \u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/pull/4290\"\u003ePrefectHQ/fastmcp#4290\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3\u003eDocs 📚\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eDocs: add v3.4.1 changelog entries by \u003ca\nhref\u003d\"https://github.com/jlowin\"\u003e\u003ccode\u003e@​jlowin\u003c/code\u003e\u003c/a\u003e in \u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/pull/4289\"\u003ePrefectHQ/fastmcp#4289\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eFull Changelog\u003c/strong\u003e: \u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/compare/v3.4.1...v3.4.2\"\u003ehttps://github.com/PrefectHQ/fastmcp/compare/v3.4.1...v3.4.2\u003c/a\u003e\u003c/p\u003e\n\u003ch2\u003ev3.4.1: Floor It\u003c/h2\u003e\n\u003cp\u003eFastMCP 3.4.1 floors Starlette at \u003ccode\u003e\u0026gt;\u003d1.0.1\u003c/code\u003e so installs\ncan no longer resolve to a version affected by CVE-2026-48710 —\npreviously the dependency was only constrained transitively through\n\u003ccode\u003emcp\u003c/code\u003e, which allowed vulnerable versions. It also makes\nOAuthProxy log refresh-token cache misses instead of failing\nsilently.\u003c/p\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003ch2\u003eWhat\u0027s Changed\u003c/h2\u003e\n\u003ch3\u003eEnhancements ✨\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eLog refresh-token misses in OAuthProxy instead of failing silently\nby \u003ca href\u003d\"https://github.com/jlowin\"\u003e\u003ccode\u003e@​jlowin\u003c/code\u003e\u003c/a\u003e in \u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/pull/4276\"\u003ePrefectHQ/fastmcp#4276\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3\u003eSecurity 🔒\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAdd explicit starlette\u0026gt;\u003d1.0.1 floor (CVE-2026-48710) by \u003ca\nhref\u003d\"https://github.com/jlowin\"\u003e\u003ccode\u003e@​jlowin\u003c/code\u003e\u003c/a\u003e in \u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/pull/4286\"\u003ePrefectHQ/fastmcp#4286\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3\u003eDocs 📚\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eDocument --notes-start-tag in release instructions by \u003ca\nhref\u003d\"https://github.com/jlowin\"\u003e\u003ccode\u003e@​jlowin\u003c/code\u003e\u003c/a\u003e in \u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/pull/4275\"\u003ePrefectHQ/fastmcp#4275\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eFull Changelog\u003c/strong\u003e: \u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/compare/v3.4.0...v3.4.1\"\u003ehttps://github.com/PrefectHQ/fastmcp/compare/v3.4.0...v3.4.1\u003c/a\u003e\u003c/p\u003e\n\u003ch2\u003ev3.4.0: Remote Control\u003c/h2\u003e\n\u003cp\u003eFastMCP 3.4 is about reaching servers that live somewhere else. The\nheadline is \u003ccode\u003efastmcp-remote\u003c/code\u003e, a standalone bridge that\nconnects stdio-only MCP hosts to servers hosted over HTTP. Around it,\nthis release hardens the proxy layer those remote connections depend on\n— making bridges fail loudly instead of silently, and keeping\nauthenticated sessions alive across the long idle periods that remote\nclients are prone to.\u003c/p\u003e\n\u003ch2\u003efastmcp-remote\u003c/h2\u003e\n\u003cp\u003eSome MCP hosts still insist on launching a local stdio command, even\nwhen the server you want is already running over HTTP. FastMCP could\nalready proxy a remote URL through \u003ccode\u003efastmcp run\u003c/code\u003e, but that\npulls in the full server-runner surface. \u003ccode\u003efastmcp-remote\u003c/code\u003e is\nthe small, single-purpose version: one URL in, one local stdio proxy\nout.\u003c/p\u003e\n\u003cpre lang\u003d\"json\"\u003e\u003ccode\u003e{\n  \u0026quot;mcpServers\u0026quot;: {\n    \u0026quot;linear\u0026quot;: {\n      \u0026quot;command\u0026quot;: \u0026quot;uvx\u0026quot;,\n\u0026quot;args\u0026quot;: [\u0026quot;fastmcp-remote\u0026quot;,\n\u0026quot;https://mcp.linear.app/mcp\u0026quot;]\n    }\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eOAuth is enabled automatically for HTTPS servers, with support for\nexplicit bearer tokens and custom headers when you need them. The\nimplementation stays on FastMCP primitives — \u003ccode\u003eClient\u003c/code\u003e, OAuth,\n\u003ccode\u003ecreate_proxy\u003c/code\u003e, and stdio — and credits the original npm\n\u003ccode\u003emcp-remote\u003c/code\u003e project for the command shape.\u003c/p\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e... (truncated)\u003c/p\u003e\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003eCommits\u003c/summary\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/commit/3b8538e2422a1c43fdb69661c610de7985b785f2\"\u003e\u003ccode\u003e3b8538e\u003c/code\u003e\u003c/a\u003e\nAllow private JWT headers (\u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/issues/4290\"\u003e#4290\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/commit/0445c31feffa5581b1e47cbe3ea75eaabed18f9d\"\u003e\u003ccode\u003e0445c31\u003c/code\u003e\u003c/a\u003e\nchore: Update SDK documentation (\u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/issues/4223\"\u003e#4223\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/commit/9261793ae0858f009b6550159287a4965da9d51f\"\u003e\u003ccode\u003e9261793\u003c/code\u003e\u003c/a\u003e\nDocs: add v3.4.1 changelog entries (\u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/issues/4289\"\u003e#4289\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/commit/e1b52d00fe579faf3b76a3364a5639aec9ad8014\"\u003e\u003ccode\u003ee1b52d0\u003c/code\u003e\u003c/a\u003e\nAdd explicit starlette\u0026gt;\u003d1.0.1 floor (CVE-2026-48710) (\u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/issues/4286\"\u003e#4286\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/commit/e58f386bacefe4283a8782b821ae0305c36bb073\"\u003e\u003ccode\u003ee58f386\u003c/code\u003e\u003c/a\u003e\nLog refresh-token misses in OAuthProxy instead of failing silently (\u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/issues/4276\"\u003e#4276\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/commit/3f09c683cc440b308c17db5a8f9c89ce4449df6c\"\u003e\u003ccode\u003e3f09c68\u003c/code\u003e\u003c/a\u003e\nDocument --notes-start-tag requirement in release instructions (\u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/issues/4275\"\u003e#4275\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/commit/e124bde34eeea66f5472e239c70cda247508da34\"\u003e\u003ccode\u003ee124bde\u003c/code\u003e\u003c/a\u003e\nFix MDX syntax error in changelog (\u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/issues/4270\"\u003e#4270\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/commit/dae11bbc40fd4ef3cf0a61e1f2363a1630186ba8\"\u003e\u003ccode\u003edae11bb\u003c/code\u003e\u003c/a\u003e\nBackfill changelog and updates through v3.4.0 (\u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/issues/4269\"\u003e#4269\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/commit/0f4f78c46491a001ba0b409ef643a6649428a951\"\u003e\u003ccode\u003e0f4f78c\u003c/code\u003e\u003c/a\u003e\nFix resource templates with query params on proxied servers (\u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/issues/4251\"\u003e#4251\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/commit/1a06130fcfaece1d494bf444c1561e752d94c61a\"\u003e\u003ccode\u003e1a06130\u003c/code\u003e\u003c/a\u003e\nFix GitHub MCP resource integration test (\u003ca\nhref\u003d\"https://redirect.github.com/PrefectHQ/fastmcp/issues/4253\"\u003e#4253\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003eAdditional commits viewable in \u003ca\nhref\u003d\"https://github.com/PrefectHQ/fastmcp/compare/v3.2.0...v3.4.2\"\u003ecompare\nview\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/details\u003e\n\u003cbr /\u003e\n\n\n[![Dependabot compatibility\nscore](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name\u003dfastmcp\u0026package-manager\u003dpip\u0026previous-version\u003d3.2.0\u0026new-version\u003d3.4.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)\n\nDependabot will resolve any conflicts with this PR as long as you don\u0027t\nalter it yourself. You can also trigger a rebase manually by commenting\n`@dependabot rebase`.\n\n[//]: # (dependabot-automerge-start)\n[//]: # (dependabot-automerge-end)\n\n---\n\n\u003cdetails\u003e\n\u003csummary\u003eDependabot commands and options\u003c/summary\u003e\n\u003cbr /\u003e\n\nYou can trigger Dependabot actions by commenting on this PR:\n- `@dependabot rebase` will rebase this PR\n- `@dependabot recreate` will recreate this PR, overwriting any edits\nthat have been made to it\n- `@dependabot show \u003cdependency name\u003e ignore conditions` will show all\nof the ignore conditions of the specified dependency\n- `@dependabot ignore this major version` will close this PR and stop\nDependabot creating any more for this major version (unless you reopen\nthe PR or upgrade to it yourself)\n- `@dependabot ignore this minor version` will close this PR and stop\nDependabot creating any more for this minor version (unless you reopen\nthe PR or upgrade to it yourself)\n- `@dependabot ignore this dependency` will close this PR and stop\nDependabot creating any more for this dependency (unless you reopen the\nPR or upgrade to it yourself)\n\n\n\u003c/details\u003e\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e\nCo-authored-by: Jerry Shao \u003cjerryshao@datastrato.com\u003e"
    },
    {
      "commit": "8e2d4ddafbb5c1d2973320c34316ddeac10b494a",
      "tree": "374ace6010be0ac6976652a791aa3a54b592063d",
      "parents": [
        "799c50c737b2f2367fa7481fedb5e20084e6d26a"
      ],
      "author": {
        "name": "StormSpirit",
        "email": "jiangxt2@vip.qq.com",
        "time": "Fri Jul 10 08:55:38 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 10 08:55:38 2026 +0800"
      },
      "message": "[#11912] feat(clickhouse): support set data skipping index type (#11916)\n\n### What changes were proposed in this pull request?\n\n- Add set data skipping index type to the Index API\n- Support set index in CREATE TABLE, ALTER TABLE, and table loading\n- Refactor index DDL generation into a shared helper\n\n### Why are the changes needed?\n\nFix: #11912\n\nThe ClickHouse catalog only supports minmax and bloom_filter skip\nindexes. Tables with set indexes load without error but index metadata\nis silently dropped, and explicit creation attempts fail. The set index\ntype has no required parameters (unlike ngrambf_v1/tokenbf_v1), making\nit safe to add without a parameter-passing mechanism.\n\n### Does this PR introduce any user-facing change?\n\nYes. ClickHouse tables with set data skipping indexes can now be created\nand loaded through Gravitino with index metadata preserved.\n\n### How was this patch tested?\n\n**Unit tests** (`TestClickHouseTableOperations`):\n- `testGetClickHouseIndexType`: verifies index type string-to-enum\nmapping (minmax, bloom_filter, set)\n\n**Integration tests** (`CatalogClickHouseIT`):\n- `testCreateAndLoadWithPartitionSortAndIndexes`: extended with SET\nindex create/load round-trip\n- `testAlterTableAddIndexWithSetIndex`: ALTER TABLE ADD INDEX with SET\ntype\n\nBoth test classes require `-PskipDockerTests\u003dfalse` to run.\n\n---------\n\nSigned-off-by: jiangxt2 \u003cjiangxt2@vip.qq.com\u003e"
    },
    {
      "commit": "799c50c737b2f2367fa7481fedb5e20084e6d26a",
      "tree": "02f4faef321027340666a4f617351c86885a6bf6",
      "parents": [
        "fc1c4a2edee83fd7d9228ea390b951e4652595ee"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Thu Jul 09 01:18:38 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 09 16:18:38 2026 +0800"
      },
      "message": "[#11954] feat(iceberg): enable creating format-version 3 (V3) tables via the Gravitino API (#11957)\n\n### What changes were proposed in this pull request?\nEnable creating Iceberg **format-version 3 (V3)** tables through the\nGravitino relational API — the format version required for Iceberg\u0027s V3\ntypes such as `variant` (added in #11932). This makes `format-version` a\nfirst-class, validated table property; everything else follows from\nthat:\n\n- **Select the version** — Gravitino accepts creating tables at versions\n**1–4** (the range the bundled Iceberg 1.11.0 can write); other values\nare rejected at the Gravitino layer with a clear message instead of a\nlow-level Iceberg error.\n- **Sensible default** — unset or empty resolves to `2` (`empty → 2`, `2\n→ 2`, `3 → 3`), stamped by Gravitino at create time, so existing\nbehavior is unchanged and v3 is opt-in.\n- **Tests** — unit (validation over `1–4` + create-property resolution)\nand REST/IRC integration: create a `variant` column via the Gravitino\nAPI at v3 and read it back.\n- **Docs** — catalog property table + two OpenAPI create examples\n(`IcebergTableCreate` v2, `IcebergVariantTableCreate` v3).\n\nPer-version support: **v1/v2** are fully supported (v2 is the community\ndefault since Iceberg 1.4.0); **v3** is required for V3 types and its\ntype coverage is being added incrementally (`variant` now); **v4** is\ncreatable but not yet a finalized Iceberg spec.\n\n### Why are the changes needed?\nFormat version 3 is a prerequisite for Iceberg\u0027s V3 types (e.g.\n`variant`; see the [Iceberg table\nspec](https://iceberg.apache.org/spec/) — \"Version 3\"). Before this,\n`format-version` was an unvalidated free-form string with no default or\ndocumentation, so creating a v3 table was not a supported, discoverable\noperation — attempts that needed v3 failed with confusing downstream\nIceberg errors, and nothing covered the write path to a REST/IRC\nbackend.\n\nFixes #11954. Related to #11949 (the umbrella tracking issue for the\nwhole variant feature).\n\n### Does this PR introduce _any_ user-facing change?\n- You can now create Iceberg tables at **format versions 1–4** through\nthe Gravitino API (default `2`) — e.g. `\"format-version\": \"3\"` for V3\ntypes like `variant`.\n- `format-version` is now validated: allowed values `1`/`2`/`3`/`4`,\nunset/empty defaults to `2`, other values rejected with a clear error.\n- New OpenAPI examples for creating Iceberg tables.\n\n### How was this patch tested?\n- Unit tests: property validation (default; valid `\"\"`/`1`/`2`/`3`/`4`;\ninvalid `0`/`5`/`100`/`-1`/`INT_MAX`/`INT_MIN`/non-numeric) and\ncreate-property mapping (`absent/\"\"/2/3`).\n- Integration tests (REST/IRC): variant column created via the Gravitino\nAPI at v3 and read back; variant-without-v3 rejected; unset/`2`/empty\nresolve to `2`.\n- Verified end-to-end in the Gravitino playground against an Iceberg\nREST catalog (read back over the `/iceberg` endpoint); confirmed Iceberg\n1.11 creates v1–v4 and rejects v5.\n- `./gradlew :docs:build` validates the OpenAPI spec.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "fc1c4a2edee83fd7d9228ea390b951e4652595ee",
      "tree": "7f60dc796bf7df9409148ad05609b280478d2c44",
      "parents": [
        "99941e2d38f8978be66efb09015b5f025cfcb5ce"
      ],
      "author": {
        "name": "Qi Yu",
        "email": "yuqi@datastrato.com",
        "time": "Thu Jul 09 13:02:24 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 09 13:02:24 2026 +0800"
      },
      "message": "[#11567][#11574] feat(mcp-server): write tools for tag, catalog, schema, table, model, topic, fileset (#11621)\n\n### What changes were proposed in this pull request?\n\nThis is the **write/modify** PR (second in the stack), adding governed\nwrite tools to the MCP server.\n\n- **#11567**: enable tag write tools (create/alter/delete/associate),\nprotected by Gravitino authorization.\n- **#11574**: implement write operations for catalog, schema, table,\nmodel, topic, and fileset (create/alter/drop, plus register/link/delete\nfor models).\n\nWrite tools are exposed by default; authorization is enforced by\nGravitino core.\n\n\u003e **Stacked on #11622.** This PR depends on the\nauthentication/authorization/audit\n\u003e foundation in #11622. Until #11622 merges, this PR\u0027s diff will also\nshow those\n\u003e commits; review after #11622 or focus on the two write commits here.\nMerge order:\n\u003e #11622 first, then this PR.\n\n### Why are the changes needed?\n\nThe MCP server previously exposed only read-only tools. These write\ntools let agents manage Gravitino metadata through MCP, with per-request\nauthorization (from #11622) ensuring each principal\u0027s permissions are\nenforced.\n\nFix: #11567\nFix: #11574\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. New MCP write tools for tag, catalog, schema, table, model, topic,\nand fileset.\n\n### How was this patch tested?\n\n`./gradlew :mcp-server:formatCheckPython :mcp-server:pylint` clean\n(pylint 10.00/10) and 123 unit tests pass."
    },
    {
      "commit": "99941e2d38f8978be66efb09015b5f025cfcb5ce",
      "tree": "3d161c507c27508a7c5ef6e85af72302273d37fa",
      "parents": [
        "70a81aedd35eac1aa572a41e2d80dfc2245afd6a"
      ],
      "author": {
        "name": "Nevin Zheng",
        "email": "6531363+nevzheng@users.noreply.github.com",
        "time": "Wed Jul 08 21:56:26 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 09 12:56:26 2026 +0800"
      },
      "message": "[#11949] feat(iceberg): native variant type support (#11932)\n\n### What changes were proposed in this pull request?\n\nAdd a first-class `variant` type to Gravitino\u0027s unified type model and\nwire it through the Iceberg catalog and the Python client. At the schema\nlevel Iceberg V3\u0027s `variant` is an opaque, parameterless leaf, so\n`VariantType` is a singleton `PrimitiveType` like `UUIDType`.\n\n- `api`: `Type.Name.VARIANT` + `Types.VariantType`.\n- `common`: `JsonUtils` serializes/parses it as the token `\"variant\"`.\n- `catalog-lakehouse-iceberg`: map Iceberg `VariantType` ⇄ Gravitino\n`VariantType`.\n- Python client: mirror `Name.VARIANT`, `Types.VariantType`, and the\nserde registry, so a Python client loads a variant column as\n`VariantType` rather than `UnparsedType`.\n- Docs: unified type reference, Iceberg type-mapping table, OpenAPI\nexamples.\n\nFixes #11949\n\n**Follow-ups:** this is the first of a short series, split for\nreviewability. Later PRs add native `variant` support for the other\nengines that have Open Variant (Paimon, Doris), then test coverage\nlocking in reject behavior for the engines that don\u0027t. They\u0027ll go up one\nat a time as each merges.\n\n### Why are the changes needed?\n\nLoading an Iceberg V3 table with a `variant` column through the native\nmetadata API failed with `UnsupportedOperationException: Unsupported\ntype: variant` (#11927). Modeling variant natively rather than as an\nopaque `ExternalType` string gives it a stable identity so each\nconnector maps it deliberately. Discussed in #11929; supersedes the\n`ExternalType` stopgap (#11928).\n\n### Does this PR introduce any user-facing change?\n\nYes — Iceberg V3 tables with a `variant` column now load through the\nnative API as a `variant` type. Other connectors are unchanged in this\nPR.\n\n### How was this patch tested?\n\n- Unit: `TestTypes` (type contract), `TestJsonUtils` (JSON round-trip),\n`TestConvertUtil` (Iceberg converter, both directions).\n- Python unit: variant type contract + serde round-trip.\n- Docker IT: `CatalogIcebergBaseIT` — create a format-version-3 table\nvia the raw Iceberg client, load it through the native API, and assert\nvariant resolves to the native type."
    },
    {
      "commit": "70a81aedd35eac1aa572a41e2d80dfc2245afd6a",
      "tree": "f45e387629fd08669737ab926785ef8a81cad956",
      "parents": [
        "a1ba08b2e7bd6896e4508607275be40729b7fda0"
      ],
      "author": {
        "name": "StormSpirit",
        "email": "jiangxt2@vip.qq.com",
        "time": "Thu Jul 09 11:31:20 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 09 11:31:20 2026 +0800"
      },
      "message": "[#11590] improvement(doris): upgrade type system for Doris 3.0+/4.0.x compatibility (#11763)\n\n## What changes were proposed in this pull request?\n\n### Type Converter Enhancement (`DorisTypeConverter.java`)\n\n**`toGravitino()` direction (Doris → Gravitino):**\n\n- **Parameterized type parsing**: Extract base type name from\nparameterized types\n(`\"int(11)\"` → `\"int\"`, `\"decimal(10,2)\"` → `\"decimal\"`) before switch\nmatching,\n  preventing them from falling into `ExternalType` fallback\n- **`datetime(N)` precision**: Parse precision directly from type string\nwhen JDBC\n`datetimePrecision` is null, bypassing inaccurate `COLUMN_SIZE` from\nolder MySQL drivers\n- **New type mappings**: `binary`/`varbinary` → `BinaryType`,\n`json`/`variant`/`ipv4`/`ipv6`\n/`largeint`/`bitmap`/`hll`/`bigint unsigned` → `ExternalType`, `datev2`\n→ `DateType`\n- **Error handling**: `parseTypeParamsOrExternal()` catches\n`NumberFormatException` on\n  malformed type strings and returns `ExternalType` instead of crashing\n- **Malformed input guard**: `endsWith(\")\")` check prevents\n`StringIndexOutOfBoundsException`\n  for inputs like `\"datetime(\"` or `\"varchar(\"`\n\n**`fromGravitino()` direction (Gravitino → Doris):**\n\n- **`datev2`**: `DateType` → `\"datev2\"` (Doris 1.2+ accepts `datev2`;\n4.0.x requires it\n  due to `disable_datev1\u003dtrue`)\n- **`ExternalType` round-trip**: `ExternalType` → `catalogString()`\n(e.g.\n  `ExternalType(\"json\")` → `\"json\"`)\n- **Complex types**: `array`/`map`/`struct` are mapped to `ExternalType`\nin `toGravitino`\ndirection. `fromGravitino` does not handle\n`ListType`/`MapType`/`StructType` (throws\n`IllegalArgumentException`). Full complex type support requires `SHOW\nCREATE TABLE` parsing,\n  which will be added in a follow-up PR.\n\n### DATETIME Precision Fix (`DorisTableOperations.java`)\n\n- **`DATETIME(N)` first**: Parse precision directly from type string\n(e.g. `\"DATETIME(3)\"`\n  → 3), no dependency on JDBC `COLUMN_SIZE` or driver version\n- **Plain `DATETIME` fallback**: Uses `COLUMN_SIZE` with MySQL driver\nversion guard\n  (requires \u003e\u003d 8.0.16 for accurate precision)\n\n## Does this PR introduce any user-facing change?\n\nNo. Type mapping improvements are transparent to users. Complex types\n(`array`/`map`/`struct`)\nare recognized as `ExternalType` rather than being completely unknown.\n\n## How was this patch tested?\n\n**Unit tests** (`TestDorisTypeConverter`):\n- All scalar types, `datev2`, `datetime(N)` precision (0/3/6)\n- Parameterized types: `int(11)`, `varchar(100)`, `decimal(10,2)`,\n`datetime(3)`\n- External type round-trip: `json`, `variant`, `ipv4`, `ipv6`,\n`largeint`, `bitmap`, `hll`,\n  `bigint unsigned`\n- Malformed type string fallback: `varchar(abc)`, `char(xyz)`,\n`decimal(a,b)`\n\n**DATETIME precision tests** (`TestDorisTableOperations`):\n- `DATETIME(0/3/6)` precision parsing, `DATETIME(x)` invalid input\n- `DATETIME(3)` with unsupported driver version (returns 3, not null)\n\n**Cross-version E2E verification** (local Doris clusters):\n- Scalar type create/load round-trip on Doris 1.2.2, 3.0.6.2, 4.0.6\n- `datev2` column creation verified on all three versions\n- `DATETIME(N)` precision verified on all three versions\n\nRelated to #11590, Fixes #11762\n\n\u003e **Note to maintainer**: Could you please add `- [ ] #11762` to the\ntask list\n\u003e in #11590 to link this sub-issue? I don\u0027t have write access to do it\ndirectly.\n\u003e Thank you!\n\n---------\n\nSigned-off-by: jiangxt2 \u003cjiangxt2@vip.qq.com\u003e\nCo-authored-by: Chang-Tong \u003czdcheerful@hotmail.com\u003e\nCo-authored-by: ArtificialIdoit \u003cbill.sea@hotmail.com\u003e\nCo-authored-by: cwq222 \u003c15503804976@163.com\u003e"
    },
    {
      "commit": "a1ba08b2e7bd6896e4508607275be40729b7fda0",
      "tree": "3e5867e35f4b4c1dd65512af388a3f056ec1f0a1",
      "parents": [
        "82fc98bddc70748d9166caff224ed52da3c61436"
      ],
      "author": {
        "name": "MaSai",
        "email": "30949009+lasdf1234@users.noreply.github.com",
        "time": "Thu Jul 09 08:54:14 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 09 08:54:14 2026 +0800"
      },
      "message": "[#11900] feat(iceberg): Support HDFS Kerberos auth for IRC JDBC catalog (#11901)\n\n### What changes were proposed in this pull request?\n\nThis PR adds Kerberos authentication support for the Iceberg REST\nCatalog (IRC) when using a **JDBC** catalog backend with an HDFS\nwarehouse.\n\n- Add `ClosableJdbcCatalog` wrapping Iceberg `JdbcCatalog`, implementing\n`SupportsKerberos` and `Closeable` (aligned with `ClosableHiveCatalog`).\n- Update `IcebergCatalogUtil.loadJdbcCatalog` to use\n`ClosableJdbcCatalog`, configure HDFS security settings, and initialize\nKerberos login.\n- Make `JdbcCatalogWithMetadataLocationSupport` extend\n`ClosableJdbcCatalog`.\n- Add unit tests (`TestClosableJdbcCatalog`) and integration test\n(`IcebergRestKerberosJdbcCatalogIT`).\n- Update IRC documentation and\n`gravitino-iceberg-rest-server.conf.template` with Kerberos\nconfiguration examples.\n\nFix: #11900\n\n### Why are the changes needed?\n\nThe Hive catalog backend already supports HDFS Kerberos via\n`ClosableHiveCatalog`. JDBC-backed IRC deployments on Kerberized HDFS\nclusters currently lack equivalent support, causing warehouse access\nfailures.\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. IRC with JDBC backend now supports Kerberos authentication for HDFS\nwarehouse access when configured:\n\n```\ngravitino.iceberg-rest.authentication.type\u003dkerberos\ngravitino.iceberg-rest.authentication.kerberos.principal\u003dxxx@REALM\ngravitino.iceberg-rest.authentication.kerberos.keytab-uri\u003dfile:///path/to/xxx.keytab\ngravitino.iceberg-rest.hadoop.security.authentication\u003dkerberos\n```\n\nJDBC metadata store authentication still uses `jdbc-user` and\n`jdbc-password`.\n\n### How was this patch tested?\n\n- UT: `./gradlew :iceberg:iceberg-common:test --tests\n\"org.apache.gravitino.iceberg.common.TestClosableJdbcCatalog\" --tests\n\"org.apache.gravitino.iceberg.common.utils.TestIcebergCatalogUtil\"\n-PskipITs`\n- IT: `./gradlew :iceberg:iceberg-rest-server:test --tests\n\"org.apache.gravitino.iceberg.integration.test.IcebergRestKerberosJdbcCatalogIT\"\n-PskipDockerTests\u003dfalse`\n- `./gradlew :iceberg:iceberg-common:spotlessApply\n:iceberg:iceberg-rest-server:spotlessApply`\n\n---------\n\nCo-authored-by: Cursor \u003ccursoragent@cursor.com\u003e"
    },
    {
      "commit": "82fc98bddc70748d9166caff224ed52da3c61436",
      "tree": "ec46191368abfd617e5ed16081370408c99a345f",
      "parents": [
        "27ad2cac7a3b74cc689b7850a4524d17d102e1cf"
      ],
      "author": {
        "name": "Qi Yu",
        "email": "yuqi@datastrato.com",
        "time": "Wed Jul 08 20:33:36 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 08 20:33:36 2026 +0800"
      },
      "message": "[#11939] fix(core,auth): tolerate concurrent directory creation instead of failing spuriously (#11940)\n\n### What changes were proposed in this pull request?\n\nAdd `DirectoryUtils.ensureDirectory(File)` in `common` (backed by\n`Files.createDirectories`, which is atomic and idempotent) and replace\nthe racy `!dir.exists() \u0026\u0026 !dir.mkdirs()` / bare `!dir.mkdirs()`\npatterns with it in:\n\n- `KerberosAuthUtils.fetchKeytabFromUri` (`catalogs/hadoop-auth`)\n- `JobManager` constructor and `JobManager.runJob` (`core`), keeping the\noriginal exception types at both call sites\n\n### Why are the changes needed?\n\n`File.mkdirs()` returns `false` when the directory already exists, so\ntwo threads racing to create the same directory (TOCTOU between\n`exists()` and `mkdirs()`) make the loser fail spuriously even though\nthe directory was created. For `KerberosAuthUtils` this surfaces as\n`IOException(\"Failed to create keytab directory .../keytabs\")` → HTTP\n500 when concurrent Kerberos-enabled Hive clients initialize, seen as\nflaky `HudiCatalogKerberosHiveIT.testHudiCatalogWithKerberos`.\n`JobManager.runJob` additionally fails misleadingly whenever the job\nstaging directory already exists.\n\nFix: #11939\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\nNew unit tests, each written first and verified to fail before the fix:\n\n-\n`TestKerberosAuthUtils.testConcurrentFetchKeytabCreatesParentDirectoryOnce`:\n4 threads × 200 iterations racing `fetchKeytabFromUri` on a fresh parent\ndirectory via `CyclicBarrier`. Reproduces the exact CI failure on the\nfirst iteration before the fix; passes after.\n- `TestJobManager.testRunJobSucceedsWhenStagingDirectoryAlreadyExists`:\nwith a fixed job ID, running the same job twice hits an existing staging\ndirectory; threw `RuntimeException(\"Failed to create staging directory\n...\")` before the fix.\n- `TestDirectoryUtils` (4 tests): nested creation, idempotency,\nrejecting a regular file at the path, and barrier-synchronized\nconcurrent creation.\n\n`./gradlew :common:test :catalogs:hadoop-auth:test -PskipITs` and\n`TestJobManager` all pass."
    },
    {
      "commit": "27ad2cac7a3b74cc689b7850a4524d17d102e1cf",
      "tree": "e0ab8e4c18884336f00495fb90bfbc92f2e8e3b7",
      "parents": [
        "ddbf6fc6e61c1df42423e1a7d38b73d9054ad4c4"
      ],
      "author": {
        "name": "Zhiguo Wu",
        "email": "wuzhiguo@apache.org",
        "time": "Wed Jul 08 12:07:49 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 08 12:07:49 2026 +0800"
      },
      "message": "[MINOR] chore(web-v2): Allow newer Node.js versions (#11904)\n\n### What changes were proposed in this pull request?\n\nThis PR relaxes the Node.js engine constraint in web-v2 from an exact\nversion to a minimum version:\n\n```json\n\"node\": \"\u003e\u003d20.19.0\"\n```\n\nThis makes web-v2 consistent with web v1, which already uses\n`\u003e\u003d20.19.0`.\n\n### Why are the changes needed?\n\nweb v1 and web-v2 are both built by the Gradle Node plugin with Node.js\n`20.19.0`. However, their `package.json` engine constraints are\ninconsistent:\n\n- web v1 uses `\u003e\u003d20.19.0`\n- web-v2 uses exactly `20.19.0`\n\nBecause `web-v2/web/.npmrc` enables `engine-strict\u003dtrue`, local `pnpm`\ncommands fail when using newer Node.js versions, even if they satisfy\nthe intended minimum version requirement.\n\nGradle still builds web-v2 with Node.js `20.19.0`, so this change does\nnot affect the Gradle-managed build environment.\n\nFix: N/A\n\n### Does this PR introduce _any_ user-facing change?\n\nNo end-user-facing change.\n\nFor developers, local web-v2 commands can now run with Node.js versions\nnewer than `20.19.0`, consistent with web v1.\n\n### How was this patch tested?\n\nNot run. The change only relaxes the `engines.node` semver range in\n`web-v2/web/package.json`."
    },
    {
      "commit": "ddbf6fc6e61c1df42423e1a7d38b73d9054ad4c4",
      "tree": "e6bc0165c7a93b281bd9bb59c9dc651c948fdd98",
      "parents": [
        "84119161706bd9dab346503a8d5ddf07604fd377"
      ],
      "author": {
        "name": "StormSpirit",
        "email": "jiangxt2@vip.qq.com",
        "time": "Wed Jul 08 11:35:45 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 08 11:35:45 2026 +0800"
      },
      "message": "[#11590] fix(doris): fix INDEX syntax and support AUTO_INCREMENT for Doris 3.0+ (#11731)\n\n### What changes were proposed in this pull request?\n\nFix INDEX DDL generation and enable AUTO_INCREMENT support for Doris\n3.0+.\n\n**INDEX syntax fix (`DorisTableOperations.java`)**\n- Filter `PRIMARY_KEY` / `UNIQUE_KEY` from `appendIndexesSql()` — these\nare table-model keys in Doris, not index-level concepts. Emit them as\n`UNIQUE KEY(col)` via a new `appendTableModelKeySql()` method.\n- Non-key indexes generate `` INDEX `name` (`col`) USING \u003ctype\u003e ``\nsyntax (INVERTED / BITMAP / ANN).\n- Omit the `USING` clause for BITMAP indexes: on Doris 1.2.x the bare\n`INDEX name (col)` syntax defaults to BITMAP, and on 3.0+/4.0+ it\ndefaults to INVERTED — which is the correct replacement. This avoids\nhard-coding `USING INVERTED` (rejected by Doris 4.0.6) or `USING BITMAP`\n(ignored by 3.0+).\n- Read path maps Doris ANN index type to Gravitino VECTOR; write path\ngenerates `USING ANN`.\n- Backtick-quote index names in `addIndexDefinition()` and\n`deleteIndexDefinition()`.\n\n**Auto Increment support (`DorisTableOperations.java`)**\n- Remove the local `validateIncrementCol(JdbcColumn[])` override that\nhard-rejected all auto-increment columns.\n- Delegate to base class\n`JdbcTableOperations.validateIncrementCol(columns, indexes)` which\nvalidates via index metadata.\n- Doris-side constraints (UNIQUE_KEYS / DUP_KEYS only, BIGINT type, NOT\nNULL) are enforced by the Doris server.\n\n**Doris version detection (`DorisTableOperations.java`)**\n- Replace `SELECT VERSION()` with `SHOW FRONTENDS` to retrieve the\nactual Doris version. `SELECT VERSION()` always returns `5.7.99` (the\nMySQL protocol compatibility version), making it unreliable for Doris\nversion detection.\n- Extract version using regex `(\\\\d+\\\\.\\\\d+\\\\.\\\\d+\\\\.?\\\\d*)` to handle\nboth 3-part (`1.2.2`) and 4-part (`3.0.6.2`) formats.\n\n### Why are the changes needed?\n\nThe Doris catalog generates invalid INDEX DDL: `PRIMARY KEY` / `UNIQUE\nKEY` were incorrectly emitted as index clauses, `USING BITMAP` syntax\nwas rejected by Doris 4.0.6, and `USING INVERTED` broke Doris 1.2.x.\nAuto-increment columns were unconditionally rejected despite Doris 3.0+\nsupporting them. `SELECT VERSION()` cannot detect the real Doris\nversion, making version-dependent DDL logic unreliable.\n\nFixes #11590\n\n### Does this PR introduce _any_ user-facing change?\n\nUsers can now create tables with auto-increment columns and secondary\nindexes (INVERTED / ANN) through Gravitino on Doris 3.0+. Index DDL is\nnow backward-compatible across Doris 1.2.x, 3.0.x, and 4.0.x.\n\n### How was this patch tested?\n\n**Unit tests** (`TestDorisTableOperationsSqlGeneration`):\n- `testCreateTableWithPrimaryKeyIndex`: PRIMARY_KEY filtered from INDEX\nclause\n- `testCreateTableWithInvertedIndex`: INVERTED index generates `USING\nINVERTED`\n- `testCreateTableWithBitmapIndex`: BITMAP index omits `USING` clause\n(backward-compatible default)\n- `testMapDorisIndexType`: ANN→VECTOR,\nBLOOMFILTER→DATA_SKIPPING_BLOOM_FILTER\n- `testCreateTableWithAutoIncrement`: AUTO_INCREMENT + UNIQUE KEY model\n- `testAddIndexDefinition`: ALTER TABLE ADD INDEX with USING clause\n- `testDeleteIndexDefinition`: DROP INDEX with backtick quoting\n\n**Integration tests**:\n- Doris 1.2.2: CatalogDorisIT ✅\n- Doris 3.0.6.2: CatalogDoris3xIT ✅\n- Doris 4.0.6: CatalogDoris4xIT ✅\n\nRelated to #3272\n\n---------\n\nSigned-off-by: jiangxt2 \u003cjiangxt2@vip.qq.com\u003e\nCo-authored-by: Chang-Tong \u003czdcheerful@hotmail.com\u003e\nCo-authored-by: ArtificialIdoit \u003cbill.sea@hotmail.com\u003e\nCo-authored-by: cwq222 \u003c15503804976@163.com\u003e"
    },
    {
      "commit": "84119161706bd9dab346503a8d5ddf07604fd377",
      "tree": "0ed7f5439a4abf46fc77f3640c691ba3b322c8d2",
      "parents": [
        "63447660092e1cd871f4626d4186f2f044e9f8e9"
      ],
      "author": {
        "name": "MaSai",
        "email": "30949009+lasdf1234@users.noreply.github.com",
        "time": "Wed Jul 08 10:37:33 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 08 10:37:33 2026 +0800"
      },
      "message": "[#11919] feat(core): AccessControlEventDispatcher events for external-id APIs (#11920)\n\n### What changes were proposed in this pull request?\n\nWire listener events for external-id user and group dispatcher APIs.\n\n**AccessControlEventDispatcher**\n- Add Pre/Event/Failure dispatch for external-id user APIs:\n`addUser(metalake, user, externalId, enabled)`, `getUserByExternalId`,\n`removeUserByExternalId`, `enableUser`, `disableUser`\n- Add Pre/Event/Failure dispatch for external-id group APIs:\n`addGroup(metalake, group, externalId)`, `getGroupByExternalId`,\n`removeGroupByExternalId`\n- Follow the same patterns as legacy `addUser` / `addGroup` / `getUser`\n/ `removeUser` events\n\n**Listener types**\n- Add external-id event classes under `listener/api/event`\n- Extend `OperationType` with `GET_USER_BY_EXTERNAL_ID`,\n`REMOVE_USER_BY_EXTERNAL_ID`, `ENABLE_USER`, `DISABLE_USER`, and group\nequivalents\n- Extend `UserInfo` with `externalId()` and `enabled()`; extend\n`GroupInfo` with `externalId()`\n\n**Tests**\n- Add external-id event coverage to `TestUserEvent` and `TestGroupEvent`\n\nFix: #11919\n\n\n### Why are the changes needed?\n\nAccess-control listener plugins and audit integrations need consistent\nPre/Event/Failure notifications for the new external-id dispatcher APIs,\nmatching the behavior of existing user/group operations.\n\n### Does this PR introduce _any_ user-facing change?\n\nNo. This PR only adds internal listener event types and dispatcher event\nwiring. No REST API or public client API changes.\n\n### How was this patch tested?\n\n```bash\nexport JAVA_HOME\u003d/Library/Java/JavaVirtualMachines/microsoft-17.jdk/Contents/Home\n./gradlew spotlessApply\n./gradlew :core:test \\\n  --tests \"org.apache.gravitino.listener.api.event.TestUserEvent\" \\\n  --tests \"org.apache.gravitino.listener.api.event.TestGroupEvent\" \\\n  -PskipITs\n```\n\n---------\n\nCo-authored-by: Cursor \u003ccursoragent@cursor.com\u003e"
    },
    {
      "commit": "63447660092e1cd871f4626d4186f2f044e9f8e9",
      "tree": "81397cf3fa8f48d0e71bb4ab347ca17c0f4ea42b",
      "parents": [
        "407f0724fde0e9ab1cd37baa9cf980062de31b02"
      ],
      "author": {
        "name": "Yuhui",
        "email": "hui@datastrato.com",
        "time": "Wed Jul 08 09:56:49 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 08 09:56:49 2026 +0800"
      },
      "message": "[#11907] feat(trino-connector): Support selecting Trino IT container subset via TEST_CONTAINERS (#11909)\n\n### What changes were proposed in this pull request?\n\nAdd a `TEST_CONTAINERS` env var / `--test_containers` CLI option so\nTrino IT can start only the containers a test needs (e.g.\n`trino`+`mysql`) instead of the full stack. `TrinoITContainers` now\nexposes a generic `getServiceUrls()` map instead of fixed getters;\n`launch.sh`/`shutdown.sh`/`inspect_ip.sh` no longer assume the full\ncontainer set; adds `docker-compose-trino-mysql.yaml` as the first\nsubset; `trino_integration_test.sh` auto-maps `--test_set\u003djdbc-mysql` to\n`--test_containers\u003dmysql`.\n\n### Why are the changes needed?\n\nTrino IT always starts the full container stack even when a testset only\nneeds one backend.\n\nFix: #11907\n\n### Does this PR introduce _any_ user-facing change?\n\nAdds an optional `--test_containers` CLI flag / `TEST_CONTAINERS` env\nvar; default (full stack) unchanged.\n\n### How was this patch tested?\n\nRan `trino_integration_test.sh` with default full-stack and mysql-only\nsubset against `jdbc-mysql`. Manually verified launch/inspect/shutdown\nscripts clean up containers and networks correctly."
    },
    {
      "commit": "407f0724fde0e9ab1cd37baa9cf980062de31b02",
      "tree": "9729723a62aff685aa1f2e35244fc398234bbf15",
      "parents": [
        "28a4ac73c8f73188ae022f1fa65d9f124b0cac80"
      ],
      "author": {
        "name": "Akshay Thorat",
        "email": "ak007t@gmail.com",
        "time": "Tue Jul 07 17:18:33 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 08 08:18:33 2026 +0800"
      },
      "message": "[#10548] feat(iceberg): support registerView in Iceberg REST catalog (#11828)\n\n### What changes were proposed in this pull request?\n\nImplement the `registerView` endpoint (`POST\n/v1/{prefix}/namespaces/{namespace}/register-view`) for the Gravitino\nIceberg REST catalog service, using the `V1_REGISTER_VIEW` support added\nin Iceberg 1.11.0.\n\n- Add `registerView()` to `IcebergCatalogWrapper`, delegating to\n`CatalogHandlers.registerView`.\n- Add `REGISTER_VIEW` to the `OperationType` and `AuditLog.Operation`\nenums, with the corresponding audit compatibility mapping.\n- Add the `IcebergRegisterViewPreEvent`, `IcebergRegisterViewEvent`, and\n`IcebergRegisterViewFailureEvent` event classes.\n- Extend the `IcebergViewOperationDispatcher` interface and all\nimplementations (`IcebergViewOperationExecutor`,\n`IcebergViewEventDispatcher`, `IcebergViewHookDispatcher`).\n- Add the `IcebergViewRegisterOperations` REST endpoint class.\n- Advertise `V1_REGISTER_VIEW` in the config endpoint view endpoints\nlist and register the new class in `IcebergRESTAuthInterceptionService`.\n- In the hook dispatcher, skip import and owner assignment when the view\nentity already exists in the Gravitino `EntityStore`; otherwise import\nit and fail hard if the import fails (mirroring the `registerTable` hook\nbehavior).\n\n### Why are the changes needed?\n\nThe Gravitino Iceberg REST catalog service did not implement the\n`registerView` endpoint defined by the Apache Iceberg REST Catalog API\nspec, so clients could not register existing views into the catalog.\nWith the Iceberg dependency upgraded to 1.11.0,\n`CatalogHandlers.registerView()`, `RegisterViewRequest`, and\n`Endpoint.V1_REGISTER_VIEW` are now available to support this.\n\nFix: #10548\n\n### Does this PR introduce _any_ user-facing change?\n\nYes. It adds a new REST endpoint `POST\n/v1/{prefix}/namespaces/{namespace}/register-view` to the Iceberg REST\ncatalog service, and advertises the `V1_REGISTER_VIEW` capability via\nthe `/v1/config` endpoint for catalogs that support views.\n\n### How was this patch tested?\n\n- Added `TestIcebergViewOperations.testRegisterView` covering success,\nnamespace-not-found (404), and already-exists (409) cases plus event\ndispatching.\n- Added `TestIcebergViewOperationExecutor.testRegisterView`.\n- Added `registerView` cases to `TestIcebergViewHookDispatcher` (import\n+ set owner when absent, skip when the entity exists, and fail hard when\nimport fails).\n- Added a `CatalogWrapperForTest.registerView` mock with\nnamespace-existence and failure simulation.\n- All existing unit tests pass (`core`, `iceberg-common`,\n`iceberg-rest-server`)."
    },
    {
      "commit": "28a4ac73c8f73188ae022f1fa65d9f124b0cac80",
      "tree": "8adb53efae95ca7fe36cd710cb43b0d42e0d7092",
      "parents": [
        "ab0d52bae5b056c6b2e4816006dd58ee0bc2e333"
      ],
      "author": {
        "name": "Qi Yu",
        "email": "yuqi@datastrato.com",
        "time": "Tue Jul 07 20:41:07 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 07 20:41:07 2026 +0800"
      },
      "message": "[#11891] fix(lance): retry repair-on-load metadata update on optimistic-lock conflict (#11892)\n\n### What changes were proposed in this pull request?\n\n`LanceTableOperations.repairTableMetadata` and\n`recordCheckedEmptyVersion` run an optimistic-locked\n`EntityStore.update` on every `loadTable`. When two loads repair the\nsame table concurrently, the slower CAS matches zero rows and surfaces\nas `IOException(\"Failed to update the entity\")`, which was rethrown as a\nfatal `RuntimeException(\"Failed to repair table\")` (HTTP 500).\n\nThis wraps that update in a bounded CAS retry\n(`updateTableWithCasRetry`, 5 attempts): on conflict it re-reads the\nlatest (already-repaired) entity and re-applies the idempotent updater,\nreturning a usable table instead of failing.\n\n### Why are the changes needed?\n\nConcurrent repair-on-load races (e.g. Spark parallel `LOAD` during\nplanning + execution) intermittently fail table loads with HTTP 500.\nSeen as flaky\n`LanceSparkRESTServiceIT.testSelectFromEmptyTableViaSpark`.\n\nFix: #11891\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\nAdded\n`TestLanceTableOperations.testLoadTableSurvivesConcurrentRepairVersionRace`:\nit models a lost CAS (first `store.update` throws the conflict\n`IOException`, the retry re-reads the winner\u0027s already-repaired entity)\nand asserts `loadTable` returns the repaired table. It fails before the\nfix and passes after. The full `TestLanceTableOperations` suite (23\ntests) passes locally.\n\n---------\n\nSigned-off-by: yuqi \u003cyuqi@datastrato.com\u003e\nCo-authored-by: Copilot Autofix powered by AI \u003c175728472+Copilot@users.noreply.github.com\u003e"
    },
    {
      "commit": "ab0d52bae5b056c6b2e4816006dd58ee0bc2e333",
      "tree": "3d82cb945b5d1a28cf795aeae48334b8a8154207",
      "parents": [
        "409f83447fde452eef58db4a86405826d71651ad"
      ],
      "author": {
        "name": "MaSai",
        "email": "30949009+lasdf1234@users.noreply.github.com",
        "time": "Tue Jul 07 19:27:38 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 07 19:27:38 2026 +0800"
      },
      "message": "[#11835] feat: external-id user/group REST create APIs and AccessControlIT (#11921)\n\n### What changes were proposed in this pull request?\n\nExtend 8090 user/group create REST APIs and Java client to support\noptional external-id create, and add integration coverage for the\nREST-exposed dispatcher paths.\n\n**REST (`server`)**\n- `POST /metalakes/{metalake}/users`: route to `addUser(metalake, name,\nexternalId, enabled)` when `externalId` is present; otherwise keep\nexisting `addUser(metalake, name)` path\n- `POST /metalakes/{metalake}/groups`: route to `addGroup(metalake,\nname, externalId)` when `externalId` is present; otherwise keep existing\n`addGroup(metalake, name)` path\n\n**DTOs (`common`)**\n- Extend `UserAddRequest` with optional `externalId` and optional\n`enabled` (defaults to `true` when unset)\n- Extend `GroupAddRequest` with optional `externalId`\n\n**Java client (`clients/client-java`)**\n- Add `GravitinoMetalake.addUser(name, externalId, enabled)` and\n`addGroup(name, externalId)`\n- Delegate the new overloads from `GravitinoClient`\n\n**Docs**\n- Update `docs/security/access-control.md` with external-id create\nexamples for REST and Java client\n- Update OpenAPI schemas in `docs/open-api/users.yaml` and\n`docs/open-api/groups.yaml`\n\n**Tests**\n- Add REST unit tests in `TestUserOperations` and `TestGroupOperations`\n- Add client unit tests in `TestUserGroup`\n- Add `AccessControlIT.testManageUsersAndGroupsWithExternalId` for\nend-to-end create/read/duplicate-externalId flows\n\nFix: #11835\n\nPart of epic #11830. Split from #11918.\n\n### Why are the changes needed?\n\nSCIM and external identity providers need to create users and groups\nthrough the 8090 REST layer with stable external identifiers, without\nbreaking existing name-only create clients. Integration tests ensure the\nclient → REST → dispatcher create path works against a real Gravitino\nserver.\n\n### Does this PR introduce _any_ user-facing change?\n\n1. `POST /metalakes/{metalake}/users` accepts optional request fields:\n   - `externalId` (string)\n- `enabled` (boolean, default `true` when `externalId` is provided and\n`enabled` is omitted)\n2. `POST /metalakes/{metalake}/groups` accepts optional request field:\n   - `externalId` (string)\n3. Create responses include `externalId` (and `enabled` for users) when\nprovided\n4. Java client adds `addUser(name, externalId, enabled)` and\n`addGroup(name, externalId)`\n\nNote: lookup/enable/disable/delete-by-external-id dispatcher APIs from\n#11832/#11834 are not exposed on 8090 REST yet and are therefore not\ncovered by `AccessControlIT`.\n\n### How was this patch tested?\n\n```bash\nexport JAVA_HOME\u003d/Library/Java/JavaVirtualMachines/microsoft-17.jdk/Contents/Home\n./gradlew spotlessApply\n./gradlew :server:test \\\n  --tests \"org.apache.gravitino.server.web.rest.TestUserOperations\" \\\n  --tests \"org.apache.gravitino.server.web.rest.TestGroupOperations\" \\\n  -PskipITs\n./gradlew :clients:client-java:test \\\n  --tests \"org.apache.gravitino.client.TestUserGroup\" \\\n  -PskipITs\n./gradlew :clients:client-java:test \\\n  --tests \"org.apache.gravitino.client.integration.test.authorization.AccessControlIT.testManageUsersAndGroupsWithExternalId\"\n./gradlew :docs:build\n```\n\n---------\n\nCo-authored-by: Cursor \u003ccursoragent@cursor.com\u003e"
    },
    {
      "commit": "409f83447fde452eef58db4a86405826d71651ad",
      "tree": "a1aad5e70909fa816b844fe81bc430062503aec3",
      "parents": [
        "8fb593a47da29e7da5287affc9c9d64b520247ae"
      ],
      "author": {
        "name": "YangJie",
        "email": "yangjie01@baidu.com",
        "time": "Tue Jul 07 17:24:30 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 07 17:24:30 2026 +0800"
      },
      "message": "[#11784] improvement(common): harden remote file fetch against SSRF DNS-rebinding (#11785)\n\n### What changes were proposed in this pull request?\n\n`FileFetcher` already checked a remote URI\u0027s host against an SSRF\ndenylist, but the download step then let the JDK re-resolve the hostname\nwhen it connected. That leaves a DNS-rebinding window: the host can look\nsafe during validation and resolve to an internal address when the\ndownload actually connects.\n\nThe fix resolves the host once in `RemoteUriValidator`, validates every\naddress it resolves to, and returns one for the caller to pin.\n`RemoteFileDownloader` is a small HTTP/1.0 client that connects straight\nto that pinned `InetAddress` and never looks the hostname up again,\nwhile still using the original hostname for TLS/SNI verification. It\nfollows no redirects and accepts only Content-Length or chunked bodies,\nso a truncated download can\u0027t be mistaken for a complete one.\n\nAlong the way it also:\n\n- widens the denylist to 0.0.0.0/8, CGNAT 100.64/10 (covers the Alibaba\nmetadata IP), the Oracle metadata IP, broadcast, IPv6 unique-local, and\nthe IPv4-compatible/mapped/NAT64/6to4/ISATAP forms that hide an IPv4 in\nan IPv6 literal;\n- rejects `ftp://` when blocking is on, because its PASV data channel\nconnects to an address the server picks and can\u0027t be pinned;\n- adds `SafeUri.redact` to strip userinfo and query strings from URIs\nbefore they reach logs or error messages.\n\n### Why are the changes needed?\n\nThe server downloads keytabs and jars from operator-supplied URIs.\nWithout pinning, someone who controls DNS for one of those hosts can\npass validation and still steer the connection to a cloud metadata\nendpoint or an internal service. The extra denylist entries and the FTP\nrestriction remove the other ways to reach those addresses.\n\nFix: #11784\n\n### Does this PR introduce _any_ user-facing change?\n\nNo new config or API. One behavior change: with\n`gravitino.fetchFile.blockUnsafeRemoteUri` enabled (the default),\n`ftp://` URIs are now rejected. Set it to false to allow them from a\ntrusted source.\n\n### How was this patch tested?\n\nAdded 48 unit tests across four classes covering address classification\n(including the IPv6-embedded forms), address pinning,\nredirect/unframed/oversized/truncated responses, chunked decoding, FTP\nrejection, and URI redaction. `./gradlew :common:test` and\n`:common:spotlessCheck` pass."
    },
    {
      "commit": "8fb593a47da29e7da5287affc9c9d64b520247ae",
      "tree": "a37b2ad33d72bb218a6573590a44d39dd4d9bf69",
      "parents": [
        "90c43acca36f0dc1d6cde214622515facfde2bb3"
      ],
      "author": {
        "name": "MaSai",
        "email": "30949009+lasdf1234@users.noreply.github.com",
        "time": "Tue Jul 07 15:09:40 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 07 15:09:40 2026 +0800"
      },
      "message": "[#11834] feat(core): Add group external ID and delete-by-external-id APIs (#11914)\n\n### What changes were proposed in this pull request?\n\n- Add `externalId` support to Group entity, DTO, PO, and relational\nstorage (mapper/SQL).\n- Add group external-id APIs: `addGroup(metalake, group, externalId)`,\n`getGroupByExternalId`, and `removeGroupByExternalId`.\n- Wire external-id operations through `UserGroupExternalManager`,\n`SupportsExternalIdOperations`, and `GroupMetaService`.\n- Align group external-id semantics with merged user SCIM APIs in #11848\n(NameIdentifier-based lookup, `removeGroupByExternalId` returns `false`\nwhen missing).\n\nFix: #11834\n\nPart of epic #11830. User external-id work is already in #11848.\n\n### Why are the changes needed?\n\nSCIM and external identity providers need stable group identifiers\nindependent of Gravitino group names. This completes the group side of\nthe core platform prerequisites for user/group access control.\n\n### Does this PR introduce _any_ user-facing change?\n\n1. New `Group.externalId()` API field.\n2. New `AccessControlDispatcher` methods:\n   - `addGroup(String metalake, String group, String externalId)`\n   - `getGroupByExternalId(String metalake, String externalId)`\n   - `removeGroupByExternalId(String metalake, String externalId)`\n\n### How was this patch tested?\n\n```bash\nexport JAVA_HOME\u003d/Library/Java/JavaVirtualMachines/microsoft-17.jdk/Contents/Home\n./gradlew :core:test \\\n  --tests \"org.apache.gravitino.authorization.TestAccessControlManager\" \\\n  --tests \"org.apache.gravitino.storage.relational.service.TestGroupMetaService\" \\\n  --tests \"org.apache.gravitino.authorization.TestAuthorizationUtils\" \\\n  --tests \"org.apache.gravitino.storage.memory.TestMemoryEntityStore\" \\\n  -PskipITs\n```\n\n\nMade with [Cursor](https://cursor.com)\n\n---------\n\nCo-authored-by: Cursor \u003ccursoragent@cursor.com\u003e"
    },
    {
      "commit": "90c43acca36f0dc1d6cde214622515facfde2bb3",
      "tree": "d5a1679155836c888ad2fe8d380f3b7ac4dab9bb",
      "parents": [
        "bcb9d2da8725a9a4f353de48a5b297c25c686aa3"
      ],
      "author": {
        "name": "MaSai",
        "email": "30949009+lasdf1234@users.noreply.github.com",
        "time": "Tue Jul 07 10:38:09 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 07 10:38:09 2026 +0800"
      },
      "message": "[#11832] feat(core): Add user external ID, enable/disable, and delete-by-external-id APIs (#11848)\n\n### What changes were proposed in this pull request?\n\nUser-scoped external ID correlation, enable/disable, and\ndelete-by-external-id. Group external-id work is split to #11834;\npagination remains #11833.\n\n**Persistence**\n- Persist `external_id` / `enabled` in user PO, entity, SQL, and\nconverters\n\n**New dispatcher APIs**\n- `getUserByExternalId`\n- `addUser(metalake, user, externalId, enabled)`\n- `enableUser` / `disableUser` (by `externalId`; updates\n`user_meta.enabled` only, keeps `user_role_rel`)\n- `removeUserByExternalId`\n\n**Store path**\n- External-id lookup/update/delete via `EntityStore` →\n`RelationalBackend` → `UserMetaService`\n- `SupportsExternalIdOperations` with `getByExternalId`,\n`updateByExternalId`, and `deleteByExternalId` (USER)\n- `RelationalEntityStore` invalidates cache after enable/disable/delete\n\n**Locking**\n- Get/enable/disable/delete-by-externalId use\n`AuthorizationUtils.ofUserExternalId` for per-externalId TreeLock paths\n\n**Validation \u0026 errors**\n- `checkExternalId` in `MetaService` and lock-path helpers; duplicate\nexternalId rejected by DB unique constraint\n- Dedicated error messages for missing user by external id\n\n**Wiring**\n- `AccessControlManager`, `UserGroupManager`, hook/event dispatchers,\nand `UserDTO`\n\nFix: #11832\n\n### Why are the changes needed?\n\nDownstream integrations need stable external ID lookup on users, the\nability to disable a user without removing role bindings, and\ndelete-by-external-id for identity synchronization workflows.\n\n### Does this PR introduce _any_ user-facing change?\n\n1. New `User.externalId()` and `User.enabled()` API fields\n2. New user dispatcher methods listed above (no new 8090 REST endpoints)\n\n### How was this patch tested?\n\n**UT** (`./gradlew :core:test -PskipITs`):\n- `TestAccessControlManager` — user external ID CRUD, enable/disable,\nduplicate/missing externalId, cache invalidation\n- `TestUserMetaService` — external ID lookup, enable/disable,\ndelete-by-externalId, duplicate externalId\n- `TestAuthorizationUtils` — `ofUserExternalId` lock paths\n- `TestMemoryEntityStore`, `TestOwnerEvent`\n\n**IT** (`./gradlew :core:test -PskipTests -PskipDockerTests\u003dfalse`):\n- `TestUserMetaService` (H2 + MySQL + PostgreSQL via Testcontainers)\n\n---------\n\nCo-authored-by: Cursor \u003ccursoragent@cursor.com\u003e"
    },
    {
      "commit": "bcb9d2da8725a9a4f353de48a5b297c25c686aa3",
      "tree": "332a14bdb0e76af27e14bd76a7d7fbfdde2e4eb3",
      "parents": [
        "5bcfc167a4e5bb0f9e095e98c7d17276fe35453a"
      ],
      "author": {
        "name": "mchades",
        "email": "liminghuang@datastrato.com",
        "time": "Mon Jul 06 23:21:56 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 06 23:21:56 2026 +0800"
      },
      "message": "[MINOR] fix(workflow): refresh docker login action SHA for ASF allowlist (#11908)\n\n### What changes were proposed in this pull request?\n\nUpdate `.github/workflows/docker-image.yml` to replace the expired\n`docker/login-action` v4.0.0 SHA with the current ASF-allowlisted\nv4.2.0 SHA.\n\n### Why are the changes needed?\n\nThe existing\n`docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2`\nreference is no longer on the ASF allowlist, which causes\n`asf-allowlist-check` to fail when scanning repository workflows.\n\nFix: N/A\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\nRan the ASF allowlist check locally against `.github/**/*.y*ml`;\nall 19 unique action refs passed."
    },
    {
      "commit": "5bcfc167a4e5bb0f9e095e98c7d17276fe35453a",
      "tree": "d738790fae34ec818ca72d95d6f110fc7573cf48",
      "parents": [
        "15778b188f10bbd8cd5698eb5788884611b09e0f"
      ],
      "author": {
        "name": "Bharath Krishna",
        "email": "bmurali@roku.com",
        "time": "Mon Jul 06 01:54:56 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 06 16:54:56 2026 +0800"
      },
      "message": "[#11899] fix(flink): exclude vulnerable log4j 2.x from hive-common compile classpath (#11898)\n\n### What changes were proposed in this pull request?\n\nAdd `exclude(\"org.apache.logging.log4j\")` to the\n`compileOnly(libs.hive2.common)` block in:\n- `flink-connector/v1.18/flink/build.gradle.kts`\n- `flink-connector/v1.19/flink/build.gradle.kts`\n- `flink-connector/v1.20/flink/build.gradle.kts`\n\n### Why are the changes needed?\n\nThese modules declare `org.apache.hive:hive-common:2.3.9` as a\n`compileOnly` dependency, which transitively pulls\n`org.apache.logging.log4j:log4j-1.2-api` / `log4j-web` /\n`log4j-core:2.6.2` (affected by Log4Shell, CVE-2021-44228) onto the\ncompile classpath.\n\n`flink-common` already excludes `org.apache.logging.log4j` from its\n`hive2.exec` dependency; the versioned Flink modules omit the same\nexclude on `hive2.common`. This change makes them consistent, removes\nthe vulnerable log4j from the compile classpath, and avoids SCA false\npositives. The `flink-runtime` shadow jar already forbids\n`org.apache.logging.log4j`, so the connector runtime artifact is\nunaffected.\n\nFixes #11899\n\n### Does this PR introduce _any_ user-facing change?\n\nNo.\n\n### How was this patch tested?\n\nLocal compilation of all three modules:\n\n```\n./gradlew :flink-connector:flink-1.18:compileJava \\\n          :flink-connector:flink-1.19:compileJava \\\n          :flink-connector:flink-1.20:compileJava\n```\n\nAll succeed; the connector code does not reference log4j classes."
    },
    {
      "commit": "15778b188f10bbd8cd5698eb5788884611b09e0f",
      "tree": "8c254f895cb4ee7b48b57560d6984d9425f496b1",
      "parents": [
        "c2fc58bf13b618d6169f47f6cbc24c873b4c2144"
      ],
      "author": {
        "name": "StormSpirit",
        "email": "jiangxt2@vip.qq.com",
        "time": "Mon Jul 06 09:13:22 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 06 09:13:22 2026 +0800"
      },
      "message": "[#11893] fix(lance): Add external table guard to deregisterTable (#11896)\n\n### What changes were proposed in this pull request?\n\nAdd a guard in `GravitinoLanceTableOperations.deregisterTable()` that\nrejects\nnon-external (managed) tables with `UnsupportedOperationException`\nbefore calling\n`dropTable`.\n\nThe guard reads `PROPERTY_EXTERNAL` from the loaded table properties\nusing the same\n`Optional.ofNullable` + `Boolean.parseBoolean` pattern as the downstream\n`LanceTableOperations.dropTable()`, ensuring consistent behavior across\nboth layers.\n\n### Why are the changes needed?\n\n`deregisterTable()` delegates to `dropTable()`, which for managed tables\ndeletes the\nunderlying Lance dataset — violating the interface contract (\"It will\nnot delete the\nunderlying lance data\").\n\nThe current code is safe only because all REST-created tables are forced\nto\n`PROPERTY_EXTERNAL\u003dtrue` (lines 190, 221, 248). This is an\nimplementation coincidence,\nnot a semantic guarantee. If a managed table path is ever introduced,\n`deregisterTable`\nwould silently delete physical data.\n\nThe guard converts this implicit assumption into an explicit invariant:\nrefuse\nnon-external tables rather than risk silent data loss. This mirrors\n`HiveCatalogOperations.purgeTable()`, which throws\n`UnsupportedOperationException` for\nexternal tables — same exception type, opposite direction (purge rejects\nexternal\ntables; deregister rejects managed tables).\n\nFix: #11893\n\n### Does this PR introduce _any_ user-facing change?\n\n`deregisterTable` now throws `UnsupportedOperationException` for managed\ntables, which\n`LanceExceptionMapper` maps to HTTP 406 (Not Acceptable). Clients would\nreceive an\nexplicit error instead of silent data loss. This path is currently\nunreachable via the\nREST API (all REST tables are external), so no existing clients are\naffected.\n\n### How was this patch tested?\n\n- Unit test `testDeregisterTableRejectsManagedTable` mocks a managed\ntable (empty\n  properties) and verifies:\n  - `UnsupportedOperationException` is thrown\n  - `dropTable` is never called on the catalog\n- Existing integration test `LanceRESTServiceIT` covers the external\ntable happy path\n  (deregister preserves physical data).\n\nSigned-off-by: jiangxt2 \u003cjiangxt2@vip.qq.com\u003e"
    }
  ],
  "next": "c2fc58bf13b618d6169f47f6cbc24c873b4c2144"
}
