> ## Documentation Index
> Fetch the complete documentation index at: https://cockroachlabs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# What's New in v26.3

export const InternalLink = ({version, path = "", children, ...props}) => {
  let detectedVersion = version || "stable";
  if (typeof window !== 'undefined' && !version) {
    const match = window.location.pathname.match(/\/docs\/([^/]+)/);
    if (match) {
      detectedVersion = match[1];
    }
  }
  const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
  return <a href={`/docs/${detectedVersion}/${normalizedPath}`} {...props}>
      {children}
    </a>;
};

export const MarketoEmailForm = ({successMessage = "Thanks!"}) => {
  useEffect(() => {
    function initializeReleaseNotesSignup() {
      if (window.__cockroachReleaseNotesSignupInitialized) {
        if (window.__cockroachReleaseNotesRefresh) {
          window.__cockroachReleaseNotesRefresh();
        }
        return;
      }
      window.__cockroachReleaseNotesSignupInitialized = true;
      const MARKETO_BASE_URL = "https://go.cockroachlabs.com";
      const MARKETO_MUNCHKIN_ID = "350-QIN-827";
      const MARKETO_FORMS_SCRIPT = "https://go.cockroachlabs.com/js/forms2/js/forms2.min.js";
      const RELEASE_NOTES_FORM_ID = 1083;
      const EMAIL_ERROR = "Enter a valid email address.";
      const FORM_LOAD_ERROR = "Unable to load the release notes form. Disable content blockers and try again.";
      const LOCALHOST_ERROR = "Marketo rejects submissions from localhost. Test this form on a deployed preview URL.";
      const SUCCESS_MESSAGE = "Thanks!";
      const WIDGET_SELECTOR = "[data-release-notes-signup]";
      const FORM_MOUNT_ID = "cockroachReleaseNotesFormMount";
      const SUBMIT_FRAME_NAME = "cockroachReleaseNotesSubmitFrame";
      const widgetState = new WeakMap();
      let marketoScriptPromise;
      let marketoFormPromise;
      let activeWidget = null;
      function toSecureMarketoUrl(url) {
        if (typeof url !== "string") {
          return url;
        }
        return url.replace("http://go.cockroachlabs.com", "https://go.cockroachlabs.com");
      }
      function patchMarketoFrameTransport() {
        if (window.__cockroachReleaseNotesFrameTransportPatched || typeof HTMLIFrameElement === "undefined") {
          return;
        }
        const iframeProto = HTMLIFrameElement.prototype;
        const originalSetAttribute = iframeProto.setAttribute;
        iframeProto.setAttribute = function (name, value) {
          if (name === "src") {
            value = toSecureMarketoUrl(value);
          }
          return originalSetAttribute.call(this, name, value);
        };
        const srcDescriptor = Object.getOwnPropertyDescriptor(iframeProto, "src");
        if (srcDescriptor && typeof srcDescriptor.get === "function" && typeof srcDescriptor.set === "function") {
          try {
            Object.defineProperty(iframeProto, "src", {
              configurable: true,
              enumerable: srcDescriptor.enumerable,
              get: function () {
                return srcDescriptor.get.call(this);
              },
              set: function (value) {
                srcDescriptor.set.call(this, toSecureMarketoUrl(value));
              }
            });
          } catch (error) {}
        }
        window.__cockroachReleaseNotesFrameTransportPatched = true;
      }
      function getState(widget) {
        let state = widgetState.get(widget);
        if (!state) {
          state = {
            error: "",
            isSubmitted: false,
            isSubmitting: false,
            successMessage: widget.getAttribute("data-success-message") || SUCCESS_MESSAGE,
            submitTimeoutId: null
          };
          widgetState.set(widget, state);
        }
        return state;
      }
      function getElements(widget) {
        return {
          emailInput: widget.querySelector("[data-release-notes-email]"),
          error: widget.querySelector("[data-release-notes-error]"),
          errorText: widget.querySelector("[data-release-notes-error-text]"),
          submitButton: widget.querySelector("[data-release-notes-submit]"),
          success: widget.querySelector("[data-release-notes-success]"),
          successText: widget.querySelector("[data-release-notes-success-text]")
        };
      }
      function ensureFormMount() {
        let mount = document.getElementById(FORM_MOUNT_ID);
        if (mount) {
          return mount;
        }
        mount = document.createElement("div");
        mount.id = FORM_MOUNT_ID;
        mount.setAttribute("aria-hidden", "true");
        mount.style.display = "none";
        document.body.appendChild(mount);
        return mount;
      }
      function getDomForm(form) {
        const formElement = typeof form.getFormElem === "function" ? form.getFormElem() : null;
        return formElement && formElement[0] ? formElement[0] : formElement;
      }
      function mountMarketoForm(form) {
        const domForm = getDomForm(form);
        if (!domForm || domForm.isConnected) {
          return domForm;
        }
        ensureFormMount().appendChild(domForm);
        return domForm;
      }
      function applyFormValues(form, values) {
        if (typeof form.setValues === "function") {
          form.setValues(values);
          return;
        }
        if (typeof form.vals === "function") {
          form.vals(values);
        }
      }
      function ensureSubmitFrame() {
        let frame = document.querySelector('iframe[name="' + SUBMIT_FRAME_NAME + '"]');
        if (frame) {
          return frame;
        }
        frame = document.createElement("iframe");
        frame.name = SUBMIT_FRAME_NAME;
        frame.setAttribute("aria-hidden", "true");
        frame.tabIndex = -1;
        frame.style.display = "none";
        document.body.appendChild(frame);
        return frame;
      }
      function clearSubmitTimeout(widget) {
        const state = getState(widget);
        if (state.submitTimeoutId) {
          window.clearTimeout(state.submitTimeoutId);
          state.submitTimeoutId = null;
        }
      }
      function forceSecureMarketoFrame() {
        const frame = document.querySelector("#MktoForms2XDIframe");
        if (!frame) {
          return;
        }
        const currentSrc = frame.getAttribute("src") || frame.src;
        if (!currentSrc) {
          return;
        }
        const secureSrc = toSecureMarketoUrl(currentSrc);
        if (secureSrc !== currentSrc) {
          frame.setAttribute("src", secureSrc);
        }
      }
      function ensureSecureMarketoFrame() {
        patchMarketoFrameTransport();
        forceSecureMarketoFrame();
        if (window.__cockroachReleaseNotesFrameObserverInitialized || typeof MutationObserver === "undefined") {
          return;
        }
        const observer = new MutationObserver(function () {
          forceSecureMarketoFrame();
        });
        observer.observe(document.documentElement, {
          childList: true,
          subtree: true,
          attributes: true,
          attributeFilter: ["src"]
        });
        window.__cockroachReleaseNotesFrameObserverInitialized = true;
        window.__cockroachReleaseNotesFrameObserver = observer;
      }
      function render(widget) {
        if (!widget || !document.contains(widget)) {
          return;
        }
        const state = getState(widget);
        const elements = getElements(widget);
        if (elements.submitButton) {
          elements.submitButton.disabled = state.isSubmitting;
        }
        if (elements.emailInput) {
          elements.emailInput.setAttribute("aria-invalid", state.error ? "true" : "false");
        }
        if (elements.error) {
          elements.error.hidden = !state.error;
          elements.error.style.display = state.error ? "block" : "none";
        }
        if (elements.errorText) {
          elements.errorText.textContent = state.error;
        }
        if (elements.success) {
          elements.success.hidden = !state.isSubmitted;
          elements.success.style.display = state.isSubmitted ? "block" : "none";
        }
        if (elements.successText) {
          elements.successText.textContent = state.successMessage || SUCCESS_MESSAGE;
        }
      }
      function hideMarketoForm(form) {
        const formElement = form && typeof form.getFormElem === "function" ? form.getFormElem() : null;
        if (!formElement) {
          return;
        }
        if (typeof formElement.hide === "function") {
          formElement.hide();
          return;
        }
        if (formElement[0] && formElement[0].style) {
          formElement[0].style.display = "none";
          return;
        }
        if (formElement.style) {
          formElement.style.display = "none";
        }
      }
      function isValidEmail(value) {
        return (/^[^\s@]+@[^\s@]+\.[^\s@]+$/).test(value);
      }
      function isFormLoadError(error) {
        return Boolean(error && typeof error === "object" && (error.code === "marketo_load_failed" || error.code === "marketo_not_available" || error.code === "marketo_form_init_failed"));
      }
      function isLocalhost() {
        return window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1";
      }
      function loadMarketoForms() {
        ensureSecureMarketoFrame();
        if (window.MktoForms2) {
          return Promise.resolve(window.MktoForms2);
        }
        if (!marketoScriptPromise) {
          marketoScriptPromise = new Promise(function (resolve, reject) {
            function finalizeLoad() {
              if (window.MktoForms2) {
                forceSecureMarketoFrame();
                resolve(window.MktoForms2);
                return;
              }
              marketoScriptPromise = null;
              const error = new Error("MktoForms2 did not load.");
              error.code = "marketo_not_available";
              reject(error);
            }
            function handleError() {
              marketoScriptPromise = null;
              const error = new Error("Failed to load Marketo forms.");
              error.code = "marketo_load_failed";
              reject(error);
            }
            const existingScript = document.querySelector('script[src="' + MARKETO_FORMS_SCRIPT + '"]');
            if (existingScript) {
              existingScript.addEventListener("load", finalizeLoad, {
                once: true
              });
              existingScript.addEventListener("error", handleError, {
                once: true
              });
              return;
            }
            const script = document.createElement("script");
            script.src = MARKETO_FORMS_SCRIPT;
            script.async = true;
            script.addEventListener("load", finalizeLoad, {
              once: true
            });
            script.addEventListener("error", handleError, {
              once: true
            });
            document.body.appendChild(script);
          });
        }
        return marketoScriptPromise;
      }
      function attachFormCallbacks(form) {
        if (form.__releaseNotesCallbacksAttached) {
          return;
        }
        form.__releaseNotesCallbacksAttached = true;
        form.onSuccess(function () {
          if (!activeWidget || !document.contains(activeWidget)) {
            return false;
          }
          const state = getState(activeWidget);
          clearSubmitTimeout(activeWidget);
          state.isSubmitting = false;
          state.isSubmitted = true;
          state.error = "";
          state.successMessage = activeWidget.getAttribute("data-success-message") || SUCCESS_MESSAGE;
          render(activeWidget);
          return false;
        });
        form.onValidate(function (isValid) {
          if (isValid || !activeWidget || !document.contains(activeWidget)) {
            return;
          }
          const state = getState(activeWidget);
          clearSubmitTimeout(activeWidget);
          state.isSubmitting = false;
          state.error = EMAIL_ERROR;
          render(activeWidget);
        });
      }
      function ensureForm() {
        if (!marketoFormPromise) {
          marketoFormPromise = loadMarketoForms().then(function (MktoForms2) {
            const existingForm = typeof MktoForms2.getForm === "function" ? MktoForms2.getForm(RELEASE_NOTES_FORM_ID) : null;
            if (existingForm) {
              forceSecureMarketoFrame();
              mountMarketoForm(existingForm);
              hideMarketoForm(existingForm);
              attachFormCallbacks(existingForm);
              ensureSubmitFrame();
              return existingForm;
            }
            return new Promise(function (resolve, reject) {
              MktoForms2.loadForm(MARKETO_BASE_URL, MARKETO_MUNCHKIN_ID, RELEASE_NOTES_FORM_ID, function (form) {
                if (!form) {
                  const error = new Error("Marketo form failed to initialize.");
                  error.code = "marketo_form_init_failed";
                  reject(error);
                  return;
                }
                forceSecureMarketoFrame();
                mountMarketoForm(form);
                hideMarketoForm(form);
                attachFormCallbacks(form);
                ensureSubmitFrame();
                resolve(form);
              });
            });
          }).catch(function (error) {
            marketoFormPromise = null;
            throw error;
          });
        }
        return marketoFormPromise;
      }
      function handleEmailInput(target) {
        const widget = target.closest(WIDGET_SELECTOR);
        if (!widget) {
          return;
        }
        const state = getState(widget);
        if (!state.error) {
          return;
        }
        state.error = "";
        render(widget);
      }
      function handleSubmit(button) {
        const widget = button.closest(WIDGET_SELECTOR);
        if (!widget) {
          return;
        }
        const state = getState(widget);
        const elements = getElements(widget);
        if (!elements.emailInput) {
          return;
        }
        const email = elements.emailInput.value.trim();
        if (!email) {
          state.error = EMAIL_ERROR;
          render(widget);
          return;
        }
        state.error = "";
        state.isSubmitted = false;
        state.isSubmitting = true;
        state.successMessage = widget.getAttribute("data-success-message") || SUCCESS_MESSAGE;
        render(widget);
        if (!isValidEmail(email)) {
          state.isSubmitting = false;
          state.error = EMAIL_ERROR;
          render(widget);
          return;
        }
        if (isLocalhost()) {
          state.isSubmitting = false;
          state.error = LOCALHOST_ERROR;
          render(widget);
          return;
        }
        activeWidget = widget;
        ensureForm().then(function (form) {
          const formValues = {
            Email: email,
            Send_me_product_and_feature_updates__c: "TRUE",
            subscriptionProductUpdates: "TRUE",
            optin: "TRUE"
          };
          applyFormValues(form, formValues);
          if (typeof form.validate === "function" && !form.validate()) {
            state.isSubmitting = false;
            state.error = EMAIL_ERROR;
            render(widget);
            return;
          }
          clearSubmitTimeout(widget);
          state.submitTimeoutId = window.setTimeout(function () {
            state.isSubmitting = false;
            state.error = EMAIL_ERROR;
            render(widget);
          }, 10000);
          if (typeof form.submit !== "function") {
            throw new Error("marketo_submit_missing");
          }
          form.submit();
        }).catch(function (error) {
          clearSubmitTimeout(widget);
          state.isSubmitting = false;
          state.error = isFormLoadError(error) ? FORM_LOAD_ERROR : EMAIL_ERROR;
          render(widget);
        });
      }
      document.addEventListener("click", function (event) {
        const submitButton = event.target.closest("[data-release-notes-submit]");
        if (!submitButton) {
          return;
        }
        event.preventDefault();
        handleSubmit(submitButton);
      });
      document.addEventListener("input", function (event) {
        if (!(event.target instanceof HTMLInputElement) || !event.target.matches("[data-release-notes-email]")) {
          return;
        }
        handleEmailInput(event.target);
      });
      window.__cockroachReleaseNotesRefresh = function () {
        ensureSecureMarketoFrame();
        const widgets = document.querySelectorAll(WIDGET_SELECTOR);
        widgets.forEach(render);
        if (widgets.length > 0) {
          ensureForm().catch(function () {});
        }
      };
      window.__cockroachReleaseNotesRefresh();
    }
    initializeReleaseNotesSignup();
  }, []);
  return <div data-release-notes-signup data-success-message={successMessage} className="not-prose my-4 max-w-xl">
      <div className="flex flex-col gap-3 sm:flex-row sm:items-start">
        <input data-release-notes-email type="email" inputMode="email" autoComplete="email" placeholder="Email*" aria-label="Email" className="min-w-0 flex-1 rounded-2xl border border-gray-300 bg-white px-4 py-3 text-sm text-gray-900 shadow-sm outline-none transition focus:border-primary dark:border-gray-700 dark:bg-gray-950 dark:text-white" />
        <button data-release-notes-submit type="button" className="inline-flex items-center justify-center rounded-2xl bg-primary px-5 py-3 text-sm font-semibold text-white transition hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-70">
          Submit
        </button>
      </div>

      <div data-release-notes-error hidden className="mt-3 rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/50 dark:bg-red-950/40 dark:text-red-300">
        <span data-release-notes-error-text />
      </div>

      <div data-release-notes-success hidden className="mt-3 rounded-2xl border border-green-200 bg-green-50 px-4 py-3 text-sm font-medium text-green-800 dark:border-green-900/50 dark:bg-green-950/40 dark:text-green-200">
        <span data-release-notes-success-text>{successMessage}</span>
      </div>
    </div>;
};

<Note>
  The releases on this page are testing releases, not supported or intended for production environments. The new features and bug fixes noted on this page may not yet be documented across CockroachDB's documentation.

  * **CockroachDB self-hosted**: All v26.3 testing binaries and Docker images are available for download.
  * **CockroachDB Advanced**: v26.3 testing releases are not yet available.
  * **CockroachDB Standard** and **Basic**: v26.3 testing releases are not available.

  When v26.3 becomes Generally Available (GA), a new v26.3.0 section on this page will describe key features and additional upgrade considerations.
</Note>

CockroachDB v26.3 is in active development, and the following <InternalLink path="index#release-schedule">testing releases</InternalLink> are intended for testing and experimentation only, and are not qualified for production environments or eligible for support or uptime SLA commitments. When CockroachDB v26.3 is Generally Available (GA), production releases will also be announced on this page.

* For details about the support window for this release type, review the <InternalLink path="release-support-policy">Release Support Policy</InternalLink>.
* For details about all supported releases, the release schedule, and licenses, refer to <InternalLink path="index">CockroachDB Releases Overview</InternalLink>.
* After downloading a supported CockroachDB binary, learn how to <InternalLink version="stable" path="install-cockroachdb">install CockroachDB</InternalLink> or <InternalLink version="stable" path="upgrade-cockroach-version">upgrade your cluster</InternalLink>.

Get future release notes emailed to you:

<MarketoEmailForm />

## v26.3.0-alpha.1

Release Date: June 10, 2026

### Downloads

<Danger>
  CockroachDB v26.3.0-alpha.1 is a testing release. Testing releases are intended for testing and experimentation only, and are not qualified for production environments and not eligible for support or uptime SLA commitments.
</Danger>

<Note>
  Experimental downloads are not qualified for production use and not eligible for support or uptime SLA commitments, whether they are for testing releases or production releases.
</Note>

<table><thead><tr><th>Operating System</th><th>Architecture</th><th>Full executable</th><th>SQL-only executable</th></tr></thead><tbody><tr><td rowspan="2">Linux</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.1.linux-amd64.tgz">cockroach-v26.3.0-alpha.1.linux-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.1.linux-amd64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.1.linux-amd64.tgz">cockroach-sql-v26.3.0-alpha.1.linux-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.1.linux-amd64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>ARM</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.1.linux-arm64.tgz">cockroach-v26.3.0-alpha.1.linux-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.1.linux-arm64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.1.linux-arm64.tgz">cockroach-sql-v26.3.0-alpha.1.linux-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.1.linux-arm64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td rowspan="2">Mac<br />(Experimental)</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.1.darwin-10.9-amd64.tgz">cockroach-v26.3.0-alpha.1.darwin-10.9-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.1.darwin-10.9-amd64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.1.darwin-10.9-amd64.tgz">cockroach-sql-v26.3.0-alpha.1.darwin-10.9-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.1.darwin-10.9-amd64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>ARM</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.1.darwin-11.0-arm64.tgz">cockroach-v26.3.0-alpha.1.darwin-11.0-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.1.darwin-11.0-arm64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.1.darwin-11.0-arm64.tgz">cockroach-sql-v26.3.0-alpha.1.darwin-11.0-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.1.darwin-11.0-arm64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>Windows<br />(Experimental)</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.1.windows-6.2-amd64.zip">cockroach-v26.3.0-alpha.1.windows-6.2-amd64.zip</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.1.windows-6.2-amd64.zip.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.1.windows-6.2-amd64.zip">cockroach-sql-v26.3.0-alpha.1.windows-6.2-amd64.zip</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.1.windows-6.2-amd64.zip.sha256sum">SHA256</a>)</td></tr></tbody></table>

### Docker image

[Multi-platform images](https://docs.docker.com/build/building/multi-platform) include support for both Intel and ARM. Multi-platform images do not take up additional space on your Docker host.

Within the multi-platform image, both Intel and ARM images are **generally available** for production use.

To download the Docker image:

```shell theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
docker pull cockroachdb/cockroach-unstable:v26.3.0-alpha.1
```

### Breaking changes

* User-defined views that reference `crdb_internal` virtual tables now enforce unsafe access checks. To restore the previous behavior, set the session variable `allow_unsafe_internals` or the cluster setting `sql.override.allow_unsafe_internals.enabled` to `true`.
* Changed privilege requirements for `RESTORE` statements for non-admin users. `RESTORE DATABASE` no longer accepts the deprecated `CREATEDB` role option, and requires the `RESTORE` system privilege `RESTORE TABLE` no longer accepts the deprecated `CREATE` privilege, and requires the `RESTORE` privilege on the parent database.
* The `system.statement_statistics` metadata `JSONB` column no longer carries a copy of the statement query text, query summary, or database for rows newly flushed once the cluster has finalized 26.3. Consumers should source these values from `system.statements` (or via the `query`, `query_summary`, and `database` columns on the `crdb_internal.statement_statistics`, `crdb_internal.statement_persisted`, and `crdb_internal.statement_activity` views which already join to it).
* The `alter_type` event has been replaced by the operation-specific events `alter_type_add_value`, `alter_type_rename_value`, and `alter_type_drop_value`.
* DDL operations such as `CREATE TABLE` and `ALTER TABLE ... ADD COLUMN` now reject computed columns that use unsupported UDF patterns, including:
  * PL/pgSQL UDFs.
  * Multi-statement SQL UDFs.
  * UDFs with `OUT`/`INOUT` parameters.
  * Nested UDF calls.
  * UDF bodies with subqueries, CTEs, `FROM` clauses, or set-returning functions.
    Previously these were accepted but would fail during schema change backfill, leaving the table in a state where schema changes could not be performed.
* Fixed enforcement of`CREATE SCHEMA ... AUTHORIZATION` to require that the executing user is a member of the specified role, matching PostgreSQL behavior. Previously, any user with `CREATE` privilege on the database could create schemas owned by arbitrary roles.
* Added enforcement to return an error when casting an array containing `NaN`, `Infinity`, or zero elements to the `vector` type. This change matches PostgreSQL behavior.
* Index definitions now use only the schema-qualified table name (for example, `public.t`), instead of the previous behavior where `pg_get_indexdef()` and `pg_indexes.indexdef` included a database name prefix. This fix improves PostgreSQL tool compatibility.
* Changed the cluster setting `server.gc_assist.enabled` to default to `false`, disabling GC assist to help reduce tail latencies. This change should not affect CockroachDB workloads, but if needed you can re-enable GC assist setting `server.gc_assist.enabled` to `true`.
* Statement fingerprinting no longer distinguishes between implicit and explicit transactions. After you upgrade, you might temporarily see duplicate statement entries in **SQL Activity** until older, pre-upgrade statistics age out.
* Webhook CSV output now separates rows with newlines. Previously, multiple rows in a batch could be concatenated onto one line.
* Follow-the-workload rebalancing is now disabled by default as part of its deprecation. The `kv.allocator.load_based_lease_rebalancing.enabled` cluster setting is retired and hidden from `SHOW CLUSTER SETTINGS`, but can still be set to re-enable the feature if needed.
* Renamed the `exportrequest.delay.total` metric to `kv.bulk_low_pri_read.delay.total`. This metric now tracks throttling delay for all low-priority bulk read requests.

### Security updates

* Added the `auth.min_password_length` metric that reports the configured minimum password length for the cluster. This metric can be used to monitor and alert on password length policy, ensuring compliance with organizational security requirements.
* Added the `auth.password_encryption.is_scram` metricto report whether password encryption is configured to use SCRAM-SHA-256 (1) or crdb-bcrypt (0). Use this metric to verify and alert on password hashing policy across the cluster, verifying security compliance requirements.
* Removed an overly restrictive TLS curve preference that limited FIPS mode to P-256. CockroachDB now uses Go's native FIPS curve selection, improving interoperability with clients that prefer other FIPS curves.
* Changed auditing so zone configuration changes (for example, `ALTER ... CONFIGURE ZONE`) are now logged to the `SENSITIVE_ACCESS` audit log channel for all users, not just admins. A new `zone_config_audit` event is emitted whenever a user sets or discards a zone configuration.
* Updated frontend dependencies to address known security vulnerabilities:
* `lodash` updated to 4.18.1.
* `immer` updated to 9.0.21.
* `elliptic` updated to 6.6.1.
* `bn.js` updated to 4.12.3/5.2.3.

### General changes

* Added a `csv_header` option to `CREATE CHANGEFEED` that adds a header row to `csv` format changefeeds to webhook and cloud storage sinks. When `csv_header` is enabled, CockroachDB includes a header row of column names at the start of each webhook request body and at the beginning of each generated CSV file.
* Added the following files to statement bundles produced by `EXPLAIN ANALYZE (DEBUG)`:
  * `descriptors.json`, which contains pretty-printed descriptor JSON for each object referenced by the statement.
  * `schema_changes.txt`, which contains recent schema-change history for those objects.
* Changed transactional logical data replication to require the `CURSOR` parameter.
* Improved rangefeed performance by prioritizing catch-up scans for high-priority rangefeeds (such as system table watchers) over bulk consumers, reducing delays in delivering updates under heavy scan load.
* Updated spatial libraries to use GEOS 3.14 instead of GEOS 3.13.

### SQL language changes

* Updated CockroachDB to align with PostgreSQL version 18.0.0, including the following changes:
  * PostgreSQL version 18.0.0 is now reported in the `server_version` and `server_version_num` read-only configuration parameters.
  * Updated schemas for `pg_attribute`, `pg_auth_members`, `pg_class`, `pg_collation`, `pg_constraint`, `pg_database`, `pg_prepared_statements`, `pg_statistic_ext`, and `pg_type` with new and corrected columns
  * Fixed 14 pre-existing column type mismatches across `pg_am`, `pg_conversion`, `pg_enum`, `pg_extension`, `pg_operator`, `pg_range`, `pg_seclabel`, `pg_settings`, and `pg_stat_activity`.
  * Updated 35 unimplemented and stub table schemas to match PostgreSQL 18: `pg_group`, `pg_hba_file_rules`, `pg_indexes`, `pg_inherits`, `pg_language`, `pg_locks`, `pg_proc`, `pg_publication`, `pg_publication_rel`, `pg_publication_tables`, `pg_range`, `pg_replication_slots`, `pg_stat_activity`, `pg_stat_all_indexes`, `pg_stat_all_tables`, `pg_stat_database`, `pg_stat_database_conflicts`, `pg_stat_gssapi`, `pg_stat_progress_analyze`, `pg_stat_progress_vacuum`, `pg_stat_subscription`, `pg_stat_sys_indexes`, `pg_stat_sys_tables`, `pg_stat_user_indexes`, `pg_stat_user_tables`, `pg_stat_xact_all_tables`, `pg_stat_xact_sys_tables`, `pg_stat_xact_user_tables`, `pg_statio_user_sequences`, `pg_statistic_ext_data`, `pg_stats`, `pg_stats_ext`, `pg_subscription`, `pg_ts_parser`, and `pg_user_mapping`.
  * Added 15 new stub tables: `pg_aios`, `pg_backend_memory_contexts`, `pg_ident_file_mappings`, `pg_parameter_acl`, `pg_publication_namespace`, `pg_shmem_allocations_numa`, `pg_stat_checkpointer`, `pg_stat_io`, `pg_stat_progress_copy`, `pg_stat_recovery_prefetch`, `pg_stat_replication_slots`, `pg_stat_subscription_stats`, `pg_stat_wal`, `pg_stats_ext_exprs`, and `pg_wait_events`.
* Added the `sql.udf.count` metric, which tracks the number of SQL statements that invoke a user-defined function (UDF).
* Added the `information_schema.crdb_statement_statistics` and `information_schema.crdb_transaction_statistics` views to expose persisted SQL statement and transaction statistics with a stable schema. Data can lag by up to one SQL stats flush interval (10 minutes by default). For real-time statistics, continue using `crdb_internal.cluster_statement_statistics`.
* Added `information_schema.crdb_jobs_with_progress`, a stable view exposing per-job metadata together with the current progress fraction, resolved HLC, status message, and last-updated timestamp. The `resolved` column is the raw HLC; apply `hlc_to_timestamp` at the call site if a wall-clock value is needed.
* Added `information_schema.crdb_job_messages(job_id)`, a set-returning function that returns the message history for one job (`recorded`, `kind`, `message`). The argument is mandatory; an unknown or invisible `job_id` returns zero rows.
* Added `information_schema.crdb_jobs`, a stable view exposing per-job metadata from the jobs system intended for programmatic use. Unlike `SHOW JOBS`, the `description` column is returned in full and the `error` column distinguishes `NULL` (no error) from the empty string. The lifecycle column is named `state`.
* Added `information_schema.crdb_job_progress_history(job_id)`, a set-returning function that returns the full progress trajectory of a job (`recorded`, `progress_fraction`, `resolved`). `resolved` is the raw HLC decimal; apply `hlc_to_timestamp` at the call site for a wall-clock value. The argument is mandatory; an unknown or invisible `job_id` returns zero rows.
* Added a new `VIEWEVENTLOG` system privilege that grants read-only access to the event log. Non-admin users can be granted event log visibility via `GRANT SYSTEM VIEWEVENTLOG TO <user>` without needing `VIEWCLUSTERMETADATA` or the `admin` role.
* Added support for `ALTER DOMAIN ... SET DEFAULT` and `ALTER DOMAIN ... DROP DEFAULT`.
* Added support for the `bit_xor` aggregate function, which computes the bitwise XOR of all non-null input values (or `NULL` if there are none) for integer and bit-string inputs, matching PostgreSQL.
* Added the cluster setting `sql.status.active_query_text.max_bytes`, which controls the maximum length of SQL text shown in `SHOW CLUSTER QUERIES`, `SHOW CLUSTER SESSIONS`, and the corresponding `crdb_internal` virtual tables. Statements longer than this limit are truncated with a trailing ellipsis.
* Added support for `CREATE DOMAIN` and `DROP DOMAIN` syntax, to define named types based on existing types with optional constraints.
* Added `ALTER DATABASE ... ALTER SUPER REGION ... SURVIVE {ZONE|REGION} FAILURE` syntax, allowing individual super regions to have their own survival goal independent of the database default.
* Added support for configuring zone settings at the super region level in multi-region databases using `ALTER DATABASE ... ALTER LOCALITY SUPER REGION ... CONFIGURE ZONE`. This allows setting zone configuration properties (for example, `num_voters` and `num_replicas`) that apply to all tables and partitions whose affinity region belongs to the specified super region.
* Added support for the `TRUNCATE` table privilege via `GRANT` and `REVOKE`, for parity with PostgreSQL. For backwards compatibility, `TRUNCATE` operations are allowed for users that have either `TRUNCATE` or `DROP` privileges.
* Added a new cluster setting, `sql.schema.auto_unlock.enabled`, that controls whether DDL operations automatically unlock `schema_locked` tables. When set to `false`, DDL on schema-locked tables is blocked unless the user manually unlocks the table first. This allows customers using LDR to enforce `schema_locked` as a hard lock that prevents user-initiated DDL. The default is `true`, preserving existing behavior.
* Added support for the `SHOW RANGES .. WITH ZONE` option which adds a `zone_config` column of type `JSONB` containing the fully resolved zone configuration for each range (with inheritance applied).
* Added a `SKIP FORIEGN KEYS` option for `CREATE LOGICALLY REPLICATED TABLE`, which creates the destination table without foreign key constraints.
* Added support for `ALTER DOMAIN ... RENAME TO` statements.
* Added a new cluster setting `sql.prepared_transactions.unsafe.enabled` (default: `false`) that controls whether `PREPARE TRANSACTION` statements are accepted. This setting is marked unsafe and requires the unsafe setting interlock to change. When disabled, attempting to prepare a transaction returns an error. `COMMIT PREPARED` and `ROLLBACK PREPARED` remain available regardless of this setting to allow cleanup of existing prepared transactions.
* Added support for an upper bound on execution latency when you request statement diagnostics bundles. Set `max_execution_latency` (optionally with `min_execution_latency`) to capture bundles only for statements whose execution latency falls within the specified range.
* Added support `DROP PROVISIONED ROLES` statements to bulk-drop provisioned roles with optional filter criteria, skipping users that own objects or have other dependencies.
* Added support for subscript access on values of type `name` using 0-based indexing (for example, `('hello'::name)[0]` returns `h`). Out-of-bounds subscripts return `NULL`, matching PostgreSQL behavior.
* Added the built-in function `pg_get_statisticsobjdef()`, which returns the `CREATE STATISTICS` statement for an extended statistics object.
* Added pretty-print style to statement bundles with `CREATE FUNCTION` and `CREATE PROCEDURE`.
* Added support to `SHOW TRACE FOR SESSION` for span tags such as `_verbose`, `_dropped_logs`, and `_dropped_children`. Tags use the `=== operation:<name> <tags>` format for span start messages to identify when trace output is truncated.
* Added a `pg_dump_compatibility` session variable that improves compatibility with the `pg_dump` PostgreSQL tool. Set it to `postgres` to make `pg_catalog` report OIDs that match the hardcoded ones expected by `pg_dump`, hide CockroachDB-internal objects, and make other fixups that allow `pg_dump` output to run on non-CockroachDB servers. Set it to `cockroachdb` for the same `pg_catalog` fixes while keeping CockroachDB-specific syntax. Like other session variables, `pg_dump_compatibility` can be set in the connection string.
* Added additional execution statistics to `EXPLAIN ANALYZE` for vector index operations including contention and wait times, MVCC scan statistics, and tenant RU consumption.
* Added KV statistics to `EXPLAIN ANALYZE` for vector search and vector mutation search operations, including gRPC calls, bytes read, pairs read, KV time, and KV CPU time.
* Added support for PostgreSQL-compatible transaction-level advisory lock functions (`pg_advisory_xact_lock`, `pg_advisory_xact_lock_shared`, `pg_try_advisory_xact_lock`, and `pg_try_advisory_xact_lock_shared`, including `int4` two-argument overloads). Locks are tied to the SQL transaction and released on commit or rollback.
* Added `query`, `query_summary`, and `database` columns to `crdb_internal.cluster_statement_statistics`, `crdb_internal.statement_statistics`, `crdb_internal.statement_statistics_persisted`, and `crdb_internal.statement_activity`. These columns expose the statement text, summary, and originating database directly, without requiring callers to parse the `metadata` `JSONB` column.
* Added support for the `ST_3DDistance` and `ST_3DDWithin` geospatial functions, which compute minimum distance and within-distance checks using 3D Euclidean distance. These functions consider the Z coordinate of geometries, unlike their 2D counterparts `ST_Distance` and `ST_DWithin`.
* `EXPLAIN` now recommends vector indexes for queries that use vector distance operators (`<->`, `<=>`, `<#>`) with `ORDER BY ... LIMIT`, including support for equality prefix columns and all three distance metrics (L2, cosine, inner product).
* Added a new cluster setting `sql.stats.table_statistics_cache.capacity` that controls the maximum number of tables whose statistics are retained in the in-memory LRU cache (default: `256`).
* Added support for the `SEQUENCE NAME <name>` clause on `ALTER TABLE ... ADD GENERATED ... AS IDENTITY` and `ALTER TABLE ... ADD COLUMN ... GENERATED AS IDENTITY`. The clause names the backing sequence explicitly instead of using the auto-generated `<table>_<column>_seq` pattern.
* Added the SQL CPU timeline to `EXPLAIN ANALYZE` for all queries, including mutations and queries run through the row-by-row execution engine. Previously, this line was only shown for non-mutation queries that ran through the vectorized execution engine.
* `CREATE SCHEMA`, `DROP SCHEMA`, `CREATE ROLE`, and `DROP ROLE` can now be used in PL/pgSQL stored procedure bodies.
* Added support for `ALTER DOMAIN ... OWNER TO`.
* Added an `options` argument to `crdb_internal.tsdb_query()` to control TSDB-side downsampling, derivatives, source filtering, and cross-source aggregation. The supported JSONB keys are documented in the function description (use `\\df+ crdb_internal.tsdb_query`).
* Added the cluster settings `sql.crdb_internal.tsdb_query.max_time_range` and `sql.crdb_internal.tsdb_query.max_rows` to limit the amount of work a single `crdb_internal.tsdb_query()` call can perform. Queries that exceed either limit are rejected with an error that identifies which setting to adjust.
* Added the `crdb_internal.tsdb_query(name, start_time, end_time)` table function, which returns datapoints from the in-cluster time series database for a named metric over a specified time window. Calls require the `VIEWCLUSTERMETADATA` system privilege.
* Added the `pg_database_size` PostgreSQL-compatible built-in function. It returns an approximate on-disk size sourced from a periodically refreshed cache.
* Added the `pg_relation_size`, `pg_table_size`, and `pg_total_relation_size` PostgreSQL-compatible built-in functions for table relations. Sizes come from a periodically refreshed cache and may lag the true value by minutes. Index OIDs and `pg_indexes_size` are not yet supported; they will be added in a follow-up.
* Added the `pg_size_pretty` and `pg_size_bytes` PostgreSQL-compatible built-in functions.
* Added support for `COMMENT ON VIEW` and `COMMENT ON SEQUENCE` statements. Comments set on views and sequences are visible via `pg_catalog.pg_description` and the `obj_description()` built-in function.
* Added support for `COMMENT ON FUNCTION`, `COMMENT ON PROCEDURE`, and `COMMENT ON ROUTINE` statements on user-defined functions and stored procedures. Comments are visible via `pg_catalog.pg_description` and the `obj_description(oid, 'pg_proc')` built-in function. The argument list is required when the routine name is overloaded and may be omitted otherwise. The `COMMENT ON ROUTINE` statement accepts either a function or a procedure, matching PostgreSQL. Comments are not supported on built-in functions.
* Added support for `LIST` columns to `IMPORT INTO ... PARQUET`. A `LIST` is decoded into a target `ARRAY` column (with an element type matching the Parquet element's physical or logical type) or a target `JSONB` column (serialized as a JSON array). When a `LIST` element is an unannotated `BYTE_ARRAY`, the bytes are passed through to the target as a string with no UTF-8 validation. For binary data, target an `ARRAY<BYTES>` column to preserve the original bytes losslessly. Nested `LIST`s and `MAP` columns are unsupported and rejected at file-open time.
* Added support for SQL-standard inline body syntax for SQL routines. `CREATE FUNCTION` and `CREATE PROCEDURE` with `LANGUAGE SQL` may now use either `BEGIN ATOMIC ... END` (with one or more body statements) or a bare `RETURN expr` in place of the dollar-quoted `AS $$ ... $$` form. Routines created with the inline form display as the dollar-quoted form in `SHOW CREATE` output.
* `CREATE TABLE` and `ALTER TABLE ... ADD COLUMN` now support `GENERATED AS IDENTITY (SEQUENCE NAME {name})`. This clause allows you to explicitly name the backing sequence instead of using the auto-generated `<table>_<column>_seq` sequence name.
* Added PostgreSQL-compatible built-in functions `factorial`, `gcd`, `lcm`, `scale`, `min_scale`, `trim_scale`, `log10`, `erf`, `erfc`, and `random_normal`.
* Added a 3-argument overload of `ST_DistanceSpheroid` that accepts a textual `SPHEROID` definition (e.g., `'SPHEROID["GRS_1980",6378137,298.257222101]'`) as the third argument, matching the PostGIS signature. The supplied spheroid is used for the geodesic distance computation in place of the one derived from the geographies' SRID.
* Added a `REFERENCES` privilege for tables, matching PostgreSQL behavior. Foreign key creation now requires the `REFERENCES` privilege on the referenced (parent) table instead of `CREATE`. The child (origin) table still requires `CREATE`. Existing users with `CREATE` on tables are automatically granted `REFERENCES` during upgrade. The `REFERENCES` privilege can be granted and revoked independently via `GRANT`/`REVOKE REFERENCES ON <table>`. Note that restoring a pre-26.3 backup onto a 26.3+ cluster does not automatically grant `REFERENCES` to users who had `CREATE`; an explicit `GRANT REFERENCES` is needed to create foreign keys on restored tables.
* Added support for creating foreign keys when the referenced table has a unique constraint on a subset of the referenced columns. Creation of new subset-unique foreign keys can be disabled by setting the `sql.subset_unique_fks.enabled` cluster setting to `false`.
* Added partial support for `ALTER DOMAIN ... SET NOT NULL` and `ALTER DOMAIN ... DROP NOT NULL`. The constraint is currently treated as NOT VALID which limits checks to newly inserted or updated rows.
* Stored procedures now support `GRANT`, `REVOKE`, and `ALTER DEFAULT PRIVILEGES`. These statements remain unsupported inside user-defined functions and `DO` blocks.
* Improved `CREATE PROCEDURE` validation for `PL/pgSQL` procedure bodies that contain DDL.
  * When late binding is disabled, procedure bodies containing DDL are now rejected with an error message referencing the `sql.procedures.plpgsql.late_binding.enabled` cluster setting.
  * When late binding is enabled, `CREATE SCHEMA`, `DROP SCHEMA`, `CREATE ROLE`, and `DROP ROLE` are now allowed in `PL/pgSQL` procedure bodies.
* Improved out-of-the-box support for dump/restore clients. Connecting a tool to CockroachDB with an `application_name` of `pg_dump`, `pg_restore`, or `pg_dumpall` now automatically sets the `pg_dump_compatibility` session setting to "cockroachdb" (emitting a `NOTICE`), unless the setting is provided explicitly in the connection string.
* Added the session setting `optimizer_inline_placeholder_equalities`, defaults to `true`. When set to `false`, the optimizer does not propagate placeholder equality constraints into correlated subqueries.
* Added `sql.routine.<type>.started.count` and `sql.routine.<type>.count` SQL statement metrics for DDL and DCL executed inside stored procedure bodies.
* Added the cluster setting `sql.procedures.plpgsql.late_binding.enabled` (defaults to `false`). When enabled, `PL/pgSQL` procedure bodies are not resolved at `CREATE PROCEDURE` time. References are instead resolved at `CALL` time, matching PostgreSQL `PL/pgSQL` semantics. `LANGUAGE` SQL procedures and functions are unaffected.
* Added support for the `pg_locks` table, which only supports the monitoring of advisory locks.
* A new cluster setting `sql.log.failed_query.enabled` causes every SQL statement that ends in an error to be logged on the `SQL_EXEC` log channel as a `failed_query` event, including the statement text, SQLSTATE, and error text. A companion setting `sql.log.failed_query.internal_queries.enabled` does the same for internally executed statements. Both default to off. Operators can use this to derive custom metrics for specific error classes without enabling `sql.log.all_statements.enabled`.
* Added support for the `ST_3DMaxDistance`, `ST_3DDFullyWithin`, `ST_3DIntersects`, `ST_3DShortestLine`, `ST_3DLongestLine`, `ST_3DClosestPoint`, and `ST_3DPerimeter` geospatial functions.
* Added support for the `ST_3DShortestLine`, `ST_3DLongestLine`, and `ST_3DClosestPoint` geospatial functions.
* CockroachDB now recognizes the fixed-offset timezone abbreviations defined by PostgreSQL's `pg_timezone_abbrevs` view (such as `EST`, `PST`, `EAT`, `CET`) when parsing `timestamptz` literals. Previously these abbreviations returned an "unimplemented" error. The abbreviations are also now exposed via the `pg_catalog.pg_timezone_abbrevs` virtual table.
* PL/pgSQL routines can now access fields of composite-typed variables without surrounding the variable name in parentheses (for example `v.x` in addition to the previously required `(v).x`), matching PostgreSQL behavior. When a PL/pgSQL variable shadows a column with the same name in a SQL statement inside the routine, the reference is now rejected as ambiguous instead of being silently resolved.
* Two new session variables, `distsql_plan_locality_filter` and `distsql_plan_locality_filter_strict`, allow restricting DistSQL physical planning of a session's queries to SQL instances whose locality matches a user-supplied filter. These variables mirror the `EXECUTION LOCALITY` option already available for `BACKUP`, `RESTORE`, and `CHANGEFEED` jobs.
* Added the `pg_get_function_sqlbody` built-in function for compatibility with PostgreSQL 14+. The function returns `NULL` for all functions today, matching PostgreSQL's behavior for functions defined with the `AS $$..$$` syntax.
* `ST_3DDistance` and `ST_3DDWithin` now fall back to 2D distance when either input lacks a Z dimension, matching PostGIS. Previously the missing Z was treated as 0.
* Added support for the `ST_3DPerimeter()` geospatial function.
* CockroachDB now supports the `WITH NO DATA` option for `CREATE TABLE ... AS`. When specified, the `AS` query is planned to derive the new table's column names and types, but is not executed, so the table is created empty. This matches PostgreSQL: the resulting table is an ordinary, immediately-usable table (it can be selected from and inserted into right away), unlike a materialized view created with `WITH NO DATA`, which must be refreshed before it can be queried. `WITH DATA` remains the default and populates the table from the query.
* `EXPLAIN` and `EXPLAIN ANALYZE` now display a `table stats mode` field (`canary` or `stable`) when the `sql.stats.canary_fraction` cluster setting is greater than 0, indicating which table statistics were used for query planning. Scan nodes for tables with active canary stats also show the configured canary window duration.
* Exposed the following settings for canary table statistics:
  * Cluster setting `sql.stats.canary_fraction`: probability that table statistics will use canary mode (i.e., always use the freshest stats) instead of stable mode (i.e., use the second-freshest stats) for query planning \[0.0-1.0].
  * Session variable `canary_stats_mode`: When `sql.stats.canary_fraction` is greater than `0`, controls which table statistics are used for query planning on the current session: `on` always uses the newest (canary) stats immediately when they are collected, `off` delays using new stats until they outlive the canary window, and `auto` selects probabilistically based on the canary fraction. Has no effect when `sql.stats.canary_fraction` is `0`.
* `EXPLAIN (VERBOSE)` and `EXPLAIN ANALYZE (VERBOSE)` now include a detailed tree of statement hints showing each hint's type, configuration (donor SQL or variable name/value), and skip reason if applicable.
* `EXPLAIN` and `EXPLAIN ANALYZE` now report statement hints as applied or skipped. Hints that fail at runtime (for example, referencing a missing index or an invalid session variable) appear as skipped. `EXPLAIN (VERBOSE)` and `EXPLAIN ANALYZE (VERBOSE)` also include per-hint details and the skip reason when applicable.
* Aggregation function `ST_AsMVT` can now also be used as a window function.
* `CREATE CHANGEFEED FOR DATABASE` now returns an error stating that the feature is not implemented.
* The `information_schema.crdb_enable_statement_hints` built-in function now accepts an optional `database` argument to enable or disable only hints scoped to a specific database.
* Changed the default value of the session variable `optimizer_span_limit` to `131072`. This bounds the number of spans the optimizer will allow in constrained index scans generated during query optimization. Queries that would exceed this limit will use fewer looser spans with remaining filters instead. Set to `0` to disable the limit.
* `REPLACE FUNCTION`/`PROCEDURE` in the legacy schema changer now strictly requires function ownership, matching the declarative schema changer and PostgreSQL behavior.
* Reduced the necessary user privileges for `SHOW SCHEDULES`, only requiring the `VIEWJOB` system privilege. Non-admin users can be granted schedule visibility via `GRANT SYSTEM VIEWJOB TO <user>` without needing direct `SELECT` on system tables.
* Changed statement bundles to pretty-print `DO` statements.
* Setting `skip_unique_checks = true` on an index now emits a notice warning that unique constraint enforcement is bypassed, with a pointer to the `INSPECT` documentation.
* Changed the `create_statement` column returned by `SHOW CREATE FUNCTION` and `SHOW CREATE PROCEDURE` to be pretty-printed.
* `pg_catalog` no longer shows the synthetic primary key constraint or its backing index for materialized views. This matches PostgreSQL, which never attaches a constraint or index to a materialized view implicitly.
* Changed statement hints so creating a hint that conflicts with an existing hint now emits a `NOTICE` indicating that older hints will be skipped. Use `SHOW STATEMENT HINTS` to identify stale hints.
* Renamed the `canary_stats_mode` session variable values from `"off"`/`"on"` to `"force_stable"`/`"force_canary"`. These modes now work independently of the `sql.stats.canary_fraction` cluster setting, allowing per-session opt-in without cluster-wide enrollment.
* Conflicting names in `ALTER TYPE ... RENAME VALUE` now error consistently with PostgreSQL.
* `ALTER TYPE ... SET (property = value)` now returns a clear unimplemented error instead of a syntax error.
* Removed the `implicit_txn` field from statement statistics. The `implicit_txn` column is no longer present in `crdb_internal.node_statement_statistics`, and statement statistics stored in `system.statement_statistics` no longer include an `implicitTxn` metadata key.
* The `custom_plans` and `generic_plans` columns in `pg_prepared_statements` now report the number of times a custom or generic plan was used for each prepared statement, matching PostgreSQL behavior.
* CockroachDB now sends `default_transaction_read_only`, `in_hot_standby`, `search_path`, and `scram_iterations` as `ParameterStatus` messages during connection startup, matching PostgreSQL 18 behavior.
* The `pg_catalog.pg_proc` columns `procost`, `prorows`, `prosupport`, and `proparallel` now return PostgreSQL-compatible default values instead of `NULL`. Built-ins report `procost=1`, user-defined functions report `procost=100`, `prorows` is `1000` for set-returning routines and `0` otherwise, `prosupport` is `-`, and `proparallel` is `u` (unsafe) for all routines because CockroachDB does not track parallel safety.
* `pg_catalog.pg_constraint` now exposes `NOT NULL` constraints as named entries, matching PostgreSQL behavior for non-nullable columns. These entries use `contype='n'`, set `conkey` to an array containing the column's `attnum`, and set `conname` to a name like `{table}_{column}_not_null`.
* `GENERATED ALWAYS AS (<expr>)` and `AS (<expr>)` column definitions no longer require a trailing `STORED` or `VIRTUAL` keyword. When neither is specified, the column defaults to `VIRTUAL`, matching PostgreSQL.
* Updated `pg_relation_size` and `pg_table_size` to return the size of the primary index only, matching PostgreSQL's "heap only" semantics. The previous (over-counting) behavior is preserved by `pg_total_relation_size`. `pg_relation_size` also accepts an index OID, returning that index's cached size. The new `pg_indexes_size()` built-in function returns the sum of the relation's secondary index sizes.
* The `ALTER DOMAIN ... SET SCHEMA` statement is now supported.
* DDL executed inside a stored procedure now honors the `SERIALIZABLE` isolation requirement that top-level DDL has always had: when a procedure containing DDL is called under `READ COMMITTED` or `REPEATABLE READ` in an implicit transaction, the transaction is automatically upgraded to `SERIALIZABLE` for the call; otherwise the call is rejected with a clear error.
* Unimplemented PL/pgSQL syntax errors now include a link to the GitHub issue tracking the missing feature.
* Fixed the error messages for `GRANT ROLE` and `REVOKE ROLE` inside functions and procedures to correctly identify the statement as `GRANT ROLE` / `REVOKE ROLE` instead of the generic `GRANT` / `REVOKE`.

### Operational changes

* Changed the default value of the `obs.ash.enabled` cluster setting to `true`, enabling Active Session History sampling by default.
* Added the `obs.ash.enabled`, `obs.ash.sample_interval`, `obs.ash.buffer_size`, `obs.ash.log_interval`, `obs.ash.log_top_n`, and `obs.ash.response_limit` cluster settings. These settings control Active Session History (ASH) sampling frequency, buffer size, logging intervals, and query limits.
* Statement diagnostics requests with `sampling_probability` and `expires_at` now collect up to 10 bundles (configurable via `sql.stmt_diagnostics.max_bundles_per_request`) instead of a single bundle. Set the cluster setting to `1` to restore single-bundle behavior.
* Added two new metrics, `auth.cert.san.conn.total` and `auth.cert.san.conn.success`, to track SAN-based certificate authentication attempts and successes.
* Added the metrics `storage.wal.failover.secondary.disk.capacity` and `storage.wal.failover.secondary.disk.available` to report disk space utilization of the secondary WAL volume when WAL failover is configured.
* A new cluster setting, `server.gc_assist.enabled`, allows operators to dynamically disable GC assist in CockroachDB's forked Go runtime. By default, it follows the `GODEBUG=gcnoassist` flag. A new metric, `sys.gc.assist.enabled`, reports the current state (`1` = enabled, `0` = disabled).
* Added support for a visibility level filter on the Prometheus scrape endpoints `/_status/vars` and `/metrics`. Use the `?visibility=` query parameter (`all`, `support`, or `essential`) or the cluster setting `obs.metrics_scrape.default_visibility` to control which metrics are exported. Default value is `all` to support existing behavior.
* Added the `liveness.uncached_scans` metric, which counts direct KV scans of the node liveness range.
* Added throttling for low-priority bulk read operations, such as TTL `SCAN` and backup `EXPORT` operations. Configure throttling with the `kv.bulk_low_pri_read.max_rate` and `kv.bulk_low_pri_read.max_concurrent` cluster settings.
* Added certificate lifecycle metrics to improve observability and alerting for certificate health:
  * `security.certificate.last_rotation` (exported as `security_certificate_last_rotation` in Prometheus) reports the Unix timestamp, in seconds, of the most recent certificate rotation. Reports `0` if the process has not rotated the certificate since startup.
  * `security.certificate.expiry_days` (exported as `security_certificate_expiry_days` in Prometheus) reports the number of days remaining until each certificate expires. Reports `0` if the certificate is expired, missing, or cannot be loaded.
* Physical cluster replication now returns clearer errors if you attempt to cut over before the initial scan replicates any data. Cutting over to `LATEST` during the initial scan now returns an explicit error instead of using the replication start time. Cutting over to an explicit timestamp now validates that the timestamp is not earlier than the replication start time.
* The cluster settings `storage.sstable.compression_algorithm_backup_storage` and `storage.sstable.compression_algorithm_backup_transport` no longer appear in `SHOW ALL CLUSTER SETTINGS`. These settings can still be viewed and set explicitly by name.
* Added additional statement statistics to the `sql.stats.discarded.current` to report statement drops due to memory pressure and fingerprint-tracking limits on the SQL stats ingester.
* Added three new admission control metrics for monitoring disk bandwidth token usage: `admission.granter.disk_write_byte_tokens_used.regular.kv`, `admission.granter.disk_write_byte_tokens_used.elastic.kv`, and `admission.granter.disk_write_byte_tokens_used.snapshot.kv`. The existing `admission.granter.disk_write_byte_tokens_exhausted_duration.kv` metric is now marked as essential and will appear on the **Overload** dashboard.
* Four new gauges `mma.overloaded_store.{lease_grace,short_dur,medium_dur,long_dur}.blocked` report overloaded stores that the multi-metric allocator (MMA) deferred because they already had too much pending work. Per duration bucket, success + failure + blocked equals the count of overloaded stores observed. A persistently non-zero value on the `long_dur.blocked` gauge indicates an overloaded store that is repeatedly being deferred and may not be receiving relief.
* The `changefeed.checkpoint_lag` metric is now reported per job and fixed to report correctly when changefeed jobs are paused.
* Active Session History (ASH) now includes a `COMMIT` workload type that attributes commit-deferred work for an explicit transaction with the transaction fingerprint as the `workload_id`.

### Command-line changes

* Added the `--background` flag to `cockroach demo` to start the demo cluster without opening an interactive SQL shell. The process prints connection information and runs until it receives `SIGINT` or `SIGTERM`.
* Added a "restricted" mode for `cockroach sql`, which can be used with the `\restrict` and `\unrestrict` metacommands. In restricted mode, the shell blocks all backslash metacommands except `\unrestrict`, helping prevent metacommands embedded in plain-text SQL dumps from running when you pipe the dump into the shell. SQL statements still run normally, and restricted mode is preserved across `\i` and `\ir` includes.
* Added the `cockroach debug upload` command to upload a `debug.zip` file to Cockroach Labs Support.
* The `\\l+` and `\\dt+` metacommands in the built-in SQL shell now include a `Size` column showing on-disk sizes, matching PostgreSQL `psql`. These values are stored in a periodically refreshed cache that pulls from `pg_database_size` and `pg_table_size`, which may lag the live byte count by a few minutes.

### DB Console changes

* Added a "Changed only" checkbox to the Cluster Settings report page in DB Console. When checked, only settings that have been explicitly overridden are shown.

### Bug fixes

* Fixed `pg_catalog.pg_proc.prosecdef` and `information_schema.routines.security_type` to correctly report `DEFINER` for `SECURITY DEFINER` functions and procedures.
* Fixed a bug that could cause an internal error when running `SELECT ... FOR UPDATE` or `SELECT ... FOR SHARE` with `ORDER BY` that uses a non-default `NULL` ordering (for example, `null_ordered_last`) under `READ COMMITTED`.
* Fixed a bug where `BACKUP TENANT` could silently omit the data of tables located after a table that is excluded from backup and is the last table in the tenant's keyspace.
* `width_bucket(operand, b1, b2, count)` now returns an error when `count` is zero or negative, matching PostgreSQL.
* `width_bucket(operand, b1, b2, count)` now returns an error when the two bounds are equal, matching PostgreSQL.
* Fixed a bug where division between `FLOAT` and `INT` values (for example, `8.0::FLOAT / 2::INT`) could fail with an "unsupported binary operator" error. Mixed `FLOAT`/`INT` division now returns a `FLOAT` result, matching PostgreSQL behavior.
* Fixed a bug in encryption at rest where 256-bit keys generated with `cockroach gen encryption-key -version=2 --size=256` were misclassified as AES-192-CTR-V2 on load. This bug resulted in data keys being generated at 24 bytes (AES-192) instead of the intended 32 bytes (AES-256). Existing key files with the wrong algorithm string are now automatically corrected on load, and data keys are rotated to the correct size on node restart.
* Fixed a bug where `IMPORT INTO` and some schema change backfills could fail when the destination table had computed columns that call user-defined functions (UDFs). Computed columns that reference `IMMUTABLE`, single-expression SQL UDFs now work with `IMPORT INTO` and `ALTER TABLE ... ADD COLUMN`.
* Fixed a bug where `ALTER DATABASE ... PRIMARY REGION` could leave stale table zone configurations, causing voter constraints and lease preferences to reference the previous primary region.
* Fixed a bug where a CockroachDB node could hang during startup if a secondary tenant (shared-process) server failed to initialize, blocking the system tenant from accepting connections.
* Fixed a bug where `pg_index.indisready` was incorrectly set to `false` for all valid indexes. In PostgreSQL, `indisready` is `true` for all fully created indexes.
* Fixed a bug where restoring a database backup containing default privileges that referenced non-existent users would leave dangling user references in the restored database descriptor.
* Fixed a bug where rolling back a `CREATE TABLE` that referenced user-defined types or sequences would leave orphaned back-references on the type and sequence descriptors, causing them to appear in `crdb_internal.invalid_objects` after the table was GC'd.
* Fixed a bug where running `EXPLAIN ANALYZE (DEBUG)` on a query that invokes a UDF with many blocks could cause out-of-memory errors (OOMs).
* Fixed a bug where concurrent updates to a table using multiple column families during a partial index creation could result in data loss, incorrect `NULL` values, or validation failures in the resulting index.
* Fixed a bug where setting `AWS_SKIP_CHECKSUM=true` did not fully suppress checksums on multipart uploads to S3-compatible storage, which could cause errors with services that do not support `CRC` checksums.
* Added a new cluster setting `changefeed.kafka.max_request_size` and a per-changefeed `Flush.MaxBytes` option in the Kafka sink config to control the maximum size of record batches sent to Kafka by the v2 sink. Lowering this from the default of 256 MiB can prevent spurious message-too-large errors when multiple batches are coalesced into a single broker request.
* Fixed a bug where the DB Console Overview page could crash for users with the `MODIFYSQLCLUSTERSETTING` and `VIEWACTIVITY` privileges but without `VIEWCLUSTERSETTING`. The Settings API now always includes required non-sensitive console keys such as `version`.
* The PCR job now switches into the cutover phase more promptly after a failover is requested, terminating the replication phase more quickly and more reliably when components of the ingestion process are hung due to network errors.
* Fixed a bug where `ALTER FUNCTION ... RENAME TO` and `ALTER PROCEDURE ... RENAME TO` could create duplicate functions in non-public schemas.
* Fixed a data race that could cause certificate expiration metrics (`security.certificate.expiration.node-client`, `security.certificate.expiration.client-tenant`, `security.certificate.expiration.ca-client-tenant` and their TTL counterparts) to not update after certificate rotation via `SIGHUP`.
* Fixed a crash (`traceRegion: alloc too large`) that could occur when Go's execution tracer was enabled and a range cache lookup used a key longer than about 64 KB.
* Fixed a bug where descriptor version fetching could be incorrectly throttled by the elastic CPU limiter, potentially leading to increased query latency or timeouts under high CPU load.
* Context cancellation is now surfaced if a `statement_timeout` occurs while waiting for a schema change.
* Fixed a bug where owner columns for built-in objects in some `pg_catalog` tables (`pg_proc.proowner`, `pg_type.typowner`, `pg_collation.collowner`, `pg_operator.oprowner`, `pg_tablespace.spcowner`, and `pg_statistic_ext.stxowner`) could be `NULL`.
* Fixed a bug where transient I/O errors (such as cloud storage network timeouts) during split or merge trigger evaluation were misidentified as replica corruption, causing the node to crash. These errors now correctly fail the operation, which is retried automatically.
* Fixed a bug where transient I/O errors reading from the `AbortSpan` were misidentified as replica corruption, causing the node to crash. These errors are now returned to the caller as regular errors.
* Fixed a bug where comparing an `OID[]` column against a string constant could fail with the error `unsupported comparison operator: <oid[]> = <string>`, improving PostgreSQL compatibility (for example, for `pg_dump`).
* Fixed a bug where statement hints that set session variables with integer, float, or timeout types (for example, `reorder_joins_limit`, `testing_optimizer_cost_perturbation`, or `statement_timeout`) could be accepted but then silently ignored at execution time.
* Fixed a bug where converting a table from `REGIONAL BY ROW` to `GLOBAL` would not clear the `skip_unique_checks` storage parameter on the primary key, even though implicit partitioning was removed.
* Fixed a bug where executing a mutation in a subquery (e.g., as a CTE) could cause the "rows written" metrics like `sql.statements.index_rows_written.count` and `sql.statements.index_bytes_written.count` to not be incremented correctly.
* Fixed a bug that caused an "Unsupported system page size" startup crash with `ppc64le` (POWER) CockroachDB binaries on systems with `64 KB` pages.
* `REFRESH MATERIALIZED VIEW` now evaluates row-level security (RLS) policies using the view owner's identity instead of the invoker's, matching PostgreSQL's definer semantics.
* Fixed a bug where DB Console **Databases** page privilege checks did not resolve role membership chains for `CONNECT` grants. Users who inherited `CONNECT` through role hierarchies now correctly see their authorized databases and tables.
* Fixed a bug where the `lock_timeout` and `deadlock_timeout` session settings were not honored by FK existence checks performed during insert fast path execution. This could cause inserts to block indefinitely on conflicting locks instead of returning a timeout error.
* Fixed a bug where CockroachDB might not have respected the table-level parameters `sql_stats_automatic_full_collection_enabled` and `sql_stats_automatic_partial_collection_enabled` and defaulted to using the corresponding cluster settings when deciding whether to perform automatic statistics collection on a table.
* Fixed a bug where creating a PL/pgSQL or SQL user-defined function that referenced a schema-qualified sequence or table through a `REGCLASS` cast (for example, `nextval('sc.myseq'::REGCLASS)`) could fail with the error `relation does not exist` at `CREATE FUNCTION` time if the object's schema was not in the search path.
* Fixed a bug where logical replication job status messages showed a fully redacted error (`"permanent error: ‹×›"`). The actual error text is now preserved.
* Fixed a nil-pointer panic during tenant garbage collection that could occur when the zone configuration or GC policy for the tenants range was missing.
* Fixed a rare panic that could occur when a virtual cluster entry was removed before it was fully populated by the rangefeed.
* Fixed an assertion failure during `DROP SCHEMA CASCADE` or concurrent table drops when triggers had cross-table dependencies.
* Fixed a bug where `ALTER TABLE ... ADD CONSTRAINT ... UNIQUE` could ignore the `STORING` clause and create a unique index without the specified stored columns.
* Fixed a bug causing an internal error when creating a trigger on a `REGIONAL BY TABLE` or `GLOBAL` table, if the trigger function referenced the multi-region enum type `crdb_internal_region`.
* `ALTER FUNCTION RENAME` and `ALTER FUNCTION SET SCHEMA` now correctly detect dependencies from triggers and row-level security policies, preventing renames that would break those objects.
* Fixed a bug where `EXPORT INTO PARQUET` could panic when `chunk_size` or `chunk_rows` was set to a large value. This fix avoids an integer overflow in the Parquet library's buffer sizing logic.
* Trigonometric functions now return errors consistent with PostgreSQL when passed out-of-range input.
* Fixed a bug that could cause an infinite loop in the optimizer when a query used a `LIMIT` value larger than `4294967295` with a join.
* The `pg_catalog` and `information_schema` views no longer report a column default for identity columns, matching expected behavior in PostgreSQL.
* Fixed a bug where `experimental_strftime` could truncate years before 1000 when you used the `%Y` format directive. `experimental_strftime` now zero-pads `%Y` to at least four digits (for example, year 1 formats as `0001`).
* Fixed a bug under the declarative schema changer where `ALTER TABLE ... DROP CONSTRAINT {pk}, ADD PRIMARY KEY (...)` would leave behind an unwanted unique secondary index on the old primary key columns.
* Fixed a bug where the DB Console login page did not show the **OIDC login** button when navigating with `?cluster=<tenant>` to a tenant with OIDC enabled. OIDC login on non-default virtual clusters now works correctly.
* Fixed a bug where a malformed binary numeric value sent over the pgwire protocol could cause the server to panic with a slice bounds error, crashing the connection. These inputs are now rejected with a proper error.
* Fixed a bug that caused `PGCOPY` imports to reject valid octal and hexadecimal byte escapes for values greater than 127. `PGCOPY` imports now also treat the standard `\.` marker as the end of input.
* Fixed a bug where setting `--advertise-sql-addr` to the same value across multiple SQL instances could cause changefeeds with `execution_locality` filters to fail with "no instances found matching locality filter".
* A physical cluster replication reader tenant no longer fails authentication and other queries with errors of the form `resolved <name> to <id> but found no descriptor with id <id>` after the reader tenant ingests a system table at an ID different from the one it was bootstrapped with. Previously, a per-node namespace cache could pin the bootstrap-time ID and require a tenant restart to recover.
* Fixed a panic during `CREATE VECTOR INDEX` backfill when the table contained a public column ordered before the vector column that was not stored in the source primary index and was not referenced by the new index. In practice this was triggered by virtual computed columns. The schema change crashed the SQL node processing the backfill instead of completing.
* Stopped logging a spurious "declarative schema changer does not support DISCARD" message every time a `DISCARD` statement was executed. The message had no functional impact but could produce very high log volume on busy clusters that issue `DISCARD` on every connection checkout.
* Fixed a hang in `IMPORT INTO` rollback where the revert could wedge the job indefinitely until the node was restarted.
* Fixed a bug where unqualified function calls could fail with incorrect privilege errors when two databases on the same cluster had identically-named functions in custom schemas. The query cache could serve a memo from one database context to another, causing `USAGE` privilege errors referencing schemas from the wrong database.
* Fixed an issue where `ALTER TYPE ... DROP VALUE` could fail on enums referenced by very large tables when the validation scan ran long enough for GC to invalidate its read timestamp. The schema changer now installs a protected timestamp over referencing tables for the duration of the scan.
* Fixed a bug where `RESTORE TABLE` of a multi-region table backed up mid-`ALTER TABLE ... SET LOCALITY` would fail with a descriptor rewrite error.
* Fixed a bug where formatting a `UNIQUE` constraint or `PRIMARY KEY` that used both storage parameters (`WITH (...)`) and a `WHERE` clause or visibility clause could produce invalid SQL that CockroachDB could not parse.
* Fixed incorrect results in `ST_3DDistance`, `ST_3DDWithin`, `ST_3DShortestLine`, and `ST_3DClosestPoint` for polygons lying in a vertical plane, and improved detection of when a line passes through a 3D polygon's interior.
* Fixed a bug where Physical Cluster Replication (PCR) reader virtual clusters could permanently fail authentication, causing all SQL connections to fail with "descriptor not found".
* Fixed a bug where some scalar expressions could incorrectly fail type checking with "unsupported binary operator" errors. These expressions now proceed further through type checking, which can allow them to succeed or return a more accurate error message.
* Fixed a bug in `cockroach debug tsdump` where the dump output could omit labeled histogram child time series (for example, `changefeed.sink_backpressure_nanos{scope="default"}-count` and `changefeed.sink_backpressure_nanos{scope="default"}-p99`), even when the `timeseries.persist_child_metrics.enabled` cluster setting was enabled.
* Fixed a data race in the multi-metric allocator between gossip-driven store load updates and concurrent lease/replica rebalancing decisions.
* Fixed a bug that could cause `DROP COLUMN ... CASCADE` to return an error referencing an internal placeholder column name when concurrent writes occurred during a schema change.
* Fixed a bug where `make_date()`, `make_timestamp()`, and `make_timestamptz()` could return an incorrect year for negative (BC) year inputs. CockroachDB now interprets negative years as "N BC", matching PostgreSQL semantics.
* A long-running `BACKUP` to S3 using `AUTH=implicit` no longer fails with an `ExpiredToken` error when it races the rotation of the underlying short-lived credentials. The S3 client now retries `ExpiredToken`, `ExpiredTokenException`, and `RequestExpired` errors the same way the legacy `aws-sdk-go` v1 client did.
* The AWS S3 and KMS clients now refresh short-lived credentials a few seconds before they expire, rather than only after expiry. This avoids `ExpiredToken` errors that could occasionally fail long-running `BACKUP`, `RESTORE`, or other operations when running with `AUTH=implicit` against credentials providers that issue short-lived tokens.
* Columns produced by set-returning functions in FROM clauses can now be referenced using the function name as a qualifier (e.g. `SELECT jsonb_each_text.key FROM jsonb_each_text(j)`), matching PostgreSQL behavior.
* Fixed a job profiler assertion failure that could occur when requesting execution details for a job that was currently running
* Fixed a rare nil pointer panic in the internal SQL executor.
* The `^` operator and the `power()` and `pow()` built-in functions now return an error (instead of `NaN` or `Inf`) for invalid float exponentiation, such as a negative base with a non-integer exponent or a zero base with a negative exponent. This matches PostgreSQL behavior and prevents invalid values from propagating to downstream functions, such as geospatial functions.
* Fixed a bug where dropping a table with `exclude_data_from_backup` enabled could cause a concurrent `BACKUP` holding a protected timestamp on the table's data to fail with the error `batch timestamp must be after replica GC threshold`.
* The `storage.compression.cr` metric now includes blob files.
* The `pg_get_function_arguments` and `pg_get_function_identity_arguments` built-in functions now include parameter names, parameter modes (`OUT` and `INOUT`), `VARIADIC` parameters, and `DEFAULT` clauses for user-defined routines, matching PostgreSQL behavior. Previously, these functions returned only comma-separated input argument types.
* Fixed a bug where using the pgwire extended query protocol to bind parameters (including enum types) to a prepared statement after rolling back to a savepoint could cause an internal error (`read sequence number is ignored after savepoint rollback`).
* Fixed a bug where using the pgwire extended query protocol to prepare a statement after rolling back to a savepoint could cause an internal error (`read sequence number is ignored after savepoint rollback`). This bug affected client drivers that use the `Parse` message (extended protocol) instead of simple query execution.
* Fixed a panic of the form `runtime error: index out of range [N] with length N` that could occur when running a `COPY FROM` concurrently with an `ALTER TABLE ADD COLUMN`.
* Fixed a bug where `IMPORT INTO` from Parquet files could cause cascading node crashes across a cluster when the specified column list excluded a non-last table column.
* Fixed a compatibility bug where casting an OID to `regclass`, `regproc`, `regprocedure`, or a user-defined `regtype` and then to text emitted only the bare entity name. CockroachDB now follows PostgreSQL and emits the schema-qualified name (`schema.name`, with each component quoted only when necessary) whenever the bare name would not resolve back to the same OID through the current `search_path`.
* `pg_function_is_visible` now matches PostgreSQL's signature-aware semantics: a function in a non-search-path schema whose signature is disjoint from same-named functions in earlier search-path schemas is correctly reported as visible (`true`) rather than shadowed.
* Fixed a `no stores for meansForStoreSet` panic in the multi-metric allocator (MMA) store rebalancer that could occur when the rebalancer's periodic tick raced gossip propagation of store descriptors during process startup. The rebalancer now skips its work for that tick and retries on the next interval.
* Fixed a limitation/warning for Active Session History (ASH) when enabled on high-core nodes.
* Fixed a crash that could occur when `st_linemerge` was called on invalid geometries that contain `NaN` coordinates.
* Fixed a bug where queries calling a UDF that transitively performs mutations could incorrectly use a LeafTxn, potentially leading to incorrect behavior.
* Fixed some node-to-node connection error paths during `DistSQL` execution to correctly return error code `58C01` (InternalConnectionFailure) instead of `XXUUU` (Uncategorized).
* Fixed an internal optimizer assertion that could cause query planning to fail, most notably during `INSPECT` index consistency checks, with an "estimated distinct count must be non-zero" error when the optimizer encountered inconsistent column statistics.
* Fixed a bug in the legacy replica allocator where ranges with `voter_constraints` discriminating sibling stores on the same node could get stuck on the wrong sibling indefinitely.
* Fixed a bug where logging large-row events could cause excessive memory usage and potentially lead to out-of-memory (OOM) crashes when a single statement wrote many rows larger than the `sql.guardrails.max_row_size_log` threshold (particularly with wide primary keys). The logged primary key is now size-bounded, and large-row event logging is rate-limited with `SkippedLargeRows` reporting suppressed events.
* Fixed a bug that could cause an internal error when executing a prepared statement with an untyped `NULL` argument.
* Fixed a regression where the Jobs page could display duplicate titles when embedded in the CockroachDB Cloud Console with the new navigation enabled.
* Fixed a bug where the DB Console on virtual clusters showed zero values for histogram-based charts (such as Service Latency and SQL Execution Latency) even though the metrics were being recorded correctly. Prior to this fix, the metrics were visible in Prometheus scrapes (`/_status/vars`) and Datadog but not in the DB Console.
* Fixed a bug where `ALTER DATABASE system DROP REGION` could fail with the error `unsupported comparison: bytes to crdb_internal_region` when the `system` database was configured as multi-region.
* DDL and `current_user` inside a `SECURITY DEFINER` stored procedure now resolve against the procedure owner, matching PostgreSQL. Previously they evaluated against the invoker.
* Fixed a bug where `pg_class.relhastriggers` always reported `false`, even for tables that had triggers defined.
* Fixed a bug that could cause an internal error during `INSERT`, `UPDATE`, or `UPSERT` on a `REGIONAL BY ROW` table configured with `infer_rbr_region_col_using_constraint` when the referenced parent table did not have a unique constraint covering the foreign key's non-region columns. The region is now inferred by selecting an arbitrary matching parent row.
* Fixed a bug where creating a user-defined function or view could fail with an internal error if its body contained `NULL::REGCLASS` or a `REGCLASS` parameter that evaluated to `NULL`.
* Fixed a planning issue where queries that use `FOR UPDATE SKIP LOCKED` with `ORDER BY` on an unindexed column and `LIMIT` could lock more rows than necessary. This could cause concurrent sessions to skip those rows and return fewer results than expected. These queries now lock only the rows needed to satisfy the `LIMIT`.

### Performance improvements

* Fixed a performance bug that could prevent query plans containing user-defined functions (UDFs) from being cached. Repeated executions of prepared statements that reference UDFs now have less planning overhead.
* Improved performance in some prepared statements by storing inline placeholder equalities into other references of the same column, such as correlated subqueries. This change enables constrained index scans in prepared statements where previously a full scan was required.
* Statement executions using canary stats will no longer use cached plans, which prevents cache thrashing but causes a slight increase in planning time over statement executions using stable stats.
* Improved performance of queries against `information_schema.routines` when looking up role information for built-in functions.
* Fixed a bug that caused pretty-printing of large SQL statements to fail due to exceeding max recursion depth.
* Improved throughput for the Kafka v2 changefeed sink by increasing the per-broker limit to 5 concurrent produce requests, matching the v1 behavior.
* Fixed a bug where the optimizer could fail to generate efficient generic plans for queries whose filters reference placeholders and also include subqueries (such as `EXISTS`, scalar subqueries, or `ANY`) that reference the same placeholder.
* Improved performance for concurrent protected `timestamp` protect and release calls, such as those used by `BACKUP` operations and changefeeds.
* Enabled parallelization of multi-key lookup joins for all lookup joins by default, improving performance of general mutation statements. Previously, this behavior applied only to mutations on multi-region tables. To restore the previous behavior, set `parallelize_multi_key_lookup_joins_only_on_mr_mutations` to `true`.
* Improved performance on queries that use `ORDER BY` with `LIMIT` on hash-sharded secondary indexes by using an efficient `UNION ALL` plan instead of falling back to a full table scan. Previously, either an index hint or `SET unconstrained_non_covering_index_scan_enabled = on;` were required to use an efficient `UNION ALL` plan when the index was non-covering.

### Miscellaneous

* Added a new session variable `optimizer_span_limit` that bounds the number of spans the optimizer will allow in a single constrained index scan. If a single `IN` set has more items than this limit, that `IN` set will not be used to build a constrained index scan. If the cross product of two or more `IN` sets would produce more spans than this limit for a composite index, then only a prefix of the `IN` sets will be used to produce spans.

  For example, for the following table and query, only the predicates on columns `a` and `b` will be used to construct the constrained scan of `abc_idx`, because including the predicate on column `c` would produce more spans than `optimizer_span_limit`:

  ```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
  CREATE TABLE abc (a INT, b INT, c INT, INDEX abc_idx (a, b, c));
  SET optimizer_span_limit = 10;
  SELECT * FROM abc WHERE a IN (1, 3, 5) AND b IN (2, 4, 6) AND c IN (7, 9, 11);
  ```
* Upgraded the CockroachDB Go toolchain to version 1.26.2.

## v26.3.0-alpha.2

Release Date: June 17, 2026

### Downloads

<Danger>
  CockroachDB v26.3.0-alpha.2 is a testing release. Testing releases are intended for testing and experimentation only, and are not qualified for production environments and not eligible for support or uptime SLA commitments.
</Danger>

<Note>
  Experimental downloads are not qualified for production use and not eligible for support or uptime SLA commitments, whether they are for testing releases or production releases.
</Note>

<table><thead><tr><th>Operating System</th><th>Architecture</th><th>Full executable</th><th>SQL-only executable</th></tr></thead><tbody><tr><td rowspan="2">Linux</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.2.linux-amd64.tgz">cockroach-v26.3.0-alpha.2.linux-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.2.linux-amd64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.2.linux-amd64.tgz">cockroach-sql-v26.3.0-alpha.2.linux-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.2.linux-amd64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>ARM</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.2.linux-arm64.tgz">cockroach-v26.3.0-alpha.2.linux-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.2.linux-arm64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.2.linux-arm64.tgz">cockroach-sql-v26.3.0-alpha.2.linux-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.2.linux-arm64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td rowspan="2">Mac<br />(Experimental)</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.2.darwin-10.9-amd64.tgz">cockroach-v26.3.0-alpha.2.darwin-10.9-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.2.darwin-10.9-amd64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.2.darwin-10.9-amd64.tgz">cockroach-sql-v26.3.0-alpha.2.darwin-10.9-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.2.darwin-10.9-amd64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>ARM</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.2.darwin-11.0-arm64.tgz">cockroach-v26.3.0-alpha.2.darwin-11.0-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.2.darwin-11.0-arm64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.2.darwin-11.0-arm64.tgz">cockroach-sql-v26.3.0-alpha.2.darwin-11.0-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.2.darwin-11.0-arm64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>Windows<br />(Experimental)</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.2.windows-6.2-amd64.zip">cockroach-v26.3.0-alpha.2.windows-6.2-amd64.zip</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-alpha.2.windows-6.2-amd64.zip.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.2.windows-6.2-amd64.zip">cockroach-sql-v26.3.0-alpha.2.windows-6.2-amd64.zip</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-alpha.2.windows-6.2-amd64.zip.sha256sum">SHA256</a>)</td></tr></tbody></table>

### Docker image

[Multi-platform images](https://docs.docker.com/build/building/multi-platform) include support for both Intel and ARM. Multi-platform images do not take up additional space on your Docker host.

Within the multi-platform image, both Intel and ARM images are **generally available** for production use.

To download the Docker image:

```shell theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
docker pull cockroachdb/cockroach-unstable:v26.3.0-alpha.2
```

### Breaking changes

* Changed `ALTER TABLE ... RENAME TO`, `ALTER VIEW ... RENAME TO`, and `ALTER SEQUENCE ... RENAME TO` to require the `CREATE` privilege on the schema that contains the object (instead of `CREATE` on the database), matching PostgreSQL behavior. In the default configuration, renaming objects in the `public` schema is unaffected.

### General changes

* Updated the `--store` flag options to support supplying values for Basalt stores.

### SQL language changes

* Added support for `ALTER TRIGGER ... ON ... RENAME TO` to rename a trigger on a table. Renaming a trigger requires `OWNERSHIP` on the table. CockroachDB also supports `ALTER TRIGGER IF EXISTS` (an extension to PostgreSQL), which performs no action if the table or trigger does not exist.
* Added support for `ALTER DOMAIN ... ADD CONSTRAINT`. Constraints added this way are created as `NOT VALID` (they are not checked against existing rows).
* Changed execution insights to record only statements that have an insight (for example, slow or failed statements) within a flagged transaction, rather than recording every statement in the transaction. This results in the following changes:
  * `crdb_internal.{cluster,node}_stmt_execution_insights` no longer lists problem-free statements from flagged transactions.
  * The `query` column of `crdb_internal.{cluster,node}_txn_execution_insights` now includes only statements that generated an insight, and can be empty if the transaction was flagged due to an issue like a failure at `COMMIT` or high contention.
  * In the DB Console, the Transaction Insight Details page's Statement Executions tab now lists only statements that generated an insight.
    To view all statements for a transaction, including those hidden by this change, refer to the transaction in SQL activity.
* Changed `CREATE DOMAIN ... DEFAULT` and `ALTER DOMAIN ... SET DEFAULT` to sanitize and validate default expressions. These statements now reject defaults that reference other database objects.
* Improved `ALTER TABLE ... ALTER COLUMN TYPE` so it can succeed even when the column is referenced by dependent user-defined functions or stored procedures, as long as the type change is compatible with the routine bodies. Incompatible changes now return an error that identifies the routine that would become invalid.
* Improved PostgreSQL compatibility for sequences:
  * Added the built-in function `pg_get_sequence_data()` for compatibility with `pg_dump`, which uses it to read sequence data during dumps.
  * Fixed `SELECT last_value, is_called FROM <sequence>` on a sequence that has never been used to return the sequence start value with `is_called = false` (matching PostgreSQL behavior).
* `COPY FROM` now accepts values for `GENERATED ALWAYS AS IDENTITY` columns and writes them as supplied, matching PostgreSQL behavior. Previously, such columns could not appear in a `COPY` column list.
* Changed `DROP TRIGGER` to allow users with the `DROP` privilege on a table (in addition to the table owner and admins) to drop triggers on that table.
* The `CREATE DOMAIN ... CHECK` statements now sanitize and validate check expressions. References to descriptors are rejected.
* The `information_schema.crdb_node_active_session_history` and `information_schema.crdb_cluster_active_session_history` views now include the `query`, `query_summary`, and `database` columns for statement samples, which makes Active Session History easier to interpret without manually joining to statement statistics.
* The `information_schema.crdb_delete_statement_hints` built-in function now accepts an optional second `database` argument to delete only hints scoped to a specific database.
* Changed `SHOW RANGES WITH ZONE` by renaming the `zone_config_conformant` column to `zone_config_needs_split`. The boolean meaning is now inverted: `true` indicates the range crosses a zone configuration boundary and should be split.

### Bug fixes

* Fixed a bug where a `DELETE` or `UPDATE` that triggered a foreign key cascade could hit an internal assertion error ("execution requires all update columns have a fetch column") if a concurrent `ALTER TABLE ... ADD COLUMN` or `ALTER TABLE ... DROP COLUMN` modified the child table while the cascade was running.
* Fixed a bug where `DROP TABLE ... CASCADE` could fail when another `REGIONAL BY ROW` table referenced it via a foreign key used to infer its region column with the `infer_rbr_region_col_using_constraint` storage parameter.
* Fixed a bug where incremental backups could fail when the collection URI had no path component (for example, `gs://my-bucket?AUTH=...`).
* Fixed a bug where `INSERT`, `UPSERT`, and `UPDATE` statements could fail with the error "Access to crdb\_internal and system is restricted" while adding an expression index, adding a computed column, or changing a column's type, even when the statement did not reference `crdb_internal`. This error occurred only while the schema change was in progress.
* Fixed a bug where a node could crash with a "transaction unexpectedly finalized" error when a transaction disabled write buffering mid-transaction and then issued `ROLLBACK` while buffered writes were still pending.
* Fixed a bug where a node whose `store descriptor` stopped being propagated via gossip was not kept in the `suspect` state for the configured interval, which could cause a flapping node to be reported as `live` and rejoin service prematurely.
* Fixed a bug where `FETCH FIRST` on an empty `WITH HOLD` cursor could return an internal error.
* Fixed a bug where concurrent role DDL (for example, `CREATE USER`, `ALTER ROLE`, `DROP USER`, `GRANT`, and `REVOKE`) could leave orphaned lease records and block subsequent role schema changes until the affected node was restarted.
* Fixed bugs in the DB Console `All sources` view that caused incorrect time-series metric totals in deployments using virtual clusters:
  * Application/SQL metrics now show the sum across all virtual clusters.
  * Several store metrics are no longer double-counted or incorrectly scoped to a tenant.
* Fixed issues in multi-tenant deployments (virtual clusters) where the DB Console `All sources` view displayed incorrect metric values, including:
  * Missing totals for application- and SQL-level metrics across tenants.
  * Double-counting some store metrics, such as per-replica CPU and intent age.

### Performance improvements

* Improved performance for SQL routines, including user-defined functions (UDFs) and procedures, by deferring routine body construction to execution time instead of planning time.
* Improved planning performance for queries against tables with user-defined `ENUM` columns.

### Miscellaneous

* Changed `ALTER VIRTUAL CLUSTER ... RENAME TO` to disallow renaming a tenant while it is participating in a Physical Cluster Replication (PCR) stream.

## v26.3.0-beta.1

Release Date: June 24, 2026

### Downloads

<Danger>
  CockroachDB v26.3.0-beta.1 is a testing release. Testing releases are intended for testing and experimentation only, and are not qualified for production environments and not eligible for support or uptime SLA commitments.
</Danger>

<Note>
  Experimental downloads are not qualified for production use and not eligible for support or uptime SLA commitments, whether they are for testing releases or production releases.
</Note>

<table><thead><tr><th>Operating System</th><th>Architecture</th><th>Full executable</th><th>SQL-only executable</th></tr></thead><tbody><tr><td rowspan="2">Linux</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.1.linux-amd64.tgz">cockroach-v26.3.0-beta.1.linux-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.1.linux-amd64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.1.linux-amd64.tgz">cockroach-sql-v26.3.0-beta.1.linux-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.1.linux-amd64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>ARM</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.1.linux-arm64.tgz">cockroach-v26.3.0-beta.1.linux-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.1.linux-arm64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.1.linux-arm64.tgz">cockroach-sql-v26.3.0-beta.1.linux-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.1.linux-arm64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td rowspan="2">Mac<br />(Experimental)</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.1.darwin-10.9-amd64.tgz">cockroach-v26.3.0-beta.1.darwin-10.9-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.1.darwin-10.9-amd64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.1.darwin-10.9-amd64.tgz">cockroach-sql-v26.3.0-beta.1.darwin-10.9-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.1.darwin-10.9-amd64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>ARM</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.1.darwin-11.0-arm64.tgz">cockroach-v26.3.0-beta.1.darwin-11.0-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.1.darwin-11.0-arm64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.1.darwin-11.0-arm64.tgz">cockroach-sql-v26.3.0-beta.1.darwin-11.0-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.1.darwin-11.0-arm64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>Windows<br />(Experimental)</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.1.windows-6.2-amd64.zip">cockroach-v26.3.0-beta.1.windows-6.2-amd64.zip</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.1.windows-6.2-amd64.zip.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.1.windows-6.2-amd64.zip">cockroach-sql-v26.3.0-beta.1.windows-6.2-amd64.zip</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.1.windows-6.2-amd64.zip.sha256sum">SHA256</a>)</td></tr></tbody></table>

### Docker image

[Multi-platform images](https://docs.docker.com/build/building/multi-platform) include support for both Intel and ARM. Multi-platform images do not take up additional space on your Docker host.

Within the multi-platform image, both Intel and ARM images are **generally available** for production use.

To download the Docker image:

```shell theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
docker pull cockroachdb/cockroach-unstable:v26.3.0-beta.1
```

### Breaking changes

* Changed `ALTER TABLE ... RENAME TO`, `ALTER VIEW ... RENAME TO`, and `ALTER SEQUENCE ... RENAME TO` to require the `CREATE` privilege on the schema that contains the object (instead of `CREATE` on the database), matching PostgreSQL behavior. In the default configuration, renaming objects in the `public` schema is unaffected.
* `COPY ... FROM` in text format now correctly decodes `BYTES`/`BYTEA` input (including the standard `\x...` hex format), matching PostgreSQL. Previously, these values could be stored as literal text instead of being decoded. To restore the previous behavior for workloads that depend on it, set `copy_from_decode_bytes_enabled` to `off`.

### Security updates

* Fixed the declarative schema-changer component to further address the privilege-escalation vulnerability in constraint validation. Constraint, column `NOT NULL`, and `enum-value-removal` validation now execute under the schema-change issuer's privileges instead of with internal node-level privileges, so a UDF reached via a `CHECK` predicate, `RLS USING` clause, or virtual computed column is bounded by the issuer's own grants.
* Fixed a privilege-escalation vulnerability in Logical Data Replication. Writer-side internal-executor sessions previously ran with internal node-level privileges, so any user-supplied expression evaluated on the destination table (including the optional UDF configured by `CREATE LOGICAL REPLICATION STREAM ... WITH FUNCTION`, `CHECK` constraints, computed columns, RLS policy expressions, and trigger bodies) inherited that authority and could escalate any user with `REPLICATIONDEST` to admin via `crdb_internal.execute_internally`. LDR writer sessions now run with the stream creator's privileges, scoped to the destination tables; row-level-security policies on the destination are bypassed for the writer so replicated rows are applied rather than rejected by a policy the stream creator cannot satisfy.
* Fixed a dead-letter-queue component to further address the privilege-escalation vulnerability in constraint validation. An attacker with `CREATE` privileges on the destination database who pre-created the predictably-named DLQ table with extra columns, `CHECK` constraints, or triggers could otherwise have those evaluated under the LDR job owner's identity. The DLQ writer now refuses to use a DLQ table whose owner is not the node user, or a table the LDR job did not create.
* Fixed a privilege-escalation vulnerability in constraint validation. Previously, a SQL user with `CREATE TABLE` and `CREATE FUNCTION` privileges could escalate to admin by adding a `CHECK` constraint, an FK, an RLS policy, or a virtual computed column whose expression invoked a UDF that called `crdb_internal.execute_internally` with a privileged statement; the constraint and foreign-key validation queries ran with internal node-level privileges, which the UDF body inherited under `SECURITY INVOKER`. Validation now runs with the schema-change issuer's privileges.

### General changes

* Improved the default batching mechanism for changefeeds to Kafka, Google Cloud Pub/Sub, and webhooks, including for multi-topic feeds (such as `EACH_FAMILY` changefeeds).

### SQL language changes

* Added support for `ALTER TRIGGER ... ON ... RENAME TO` to rename a trigger on a table. Renaming a trigger requires `OWNERSHIP` on the table. CockroachDB also supports `ALTER TRIGGER IF EXISTS` (an extension to PostgreSQL), which performs no action if the table or trigger does not exist.
* Added per-execution enrichment columns to the virtual tables `crdb_internal.node_active_session_history` and `crdb_internal.cluster_active_session_history`, and to the table `system.active_session_history`, when ASH enrichment is enabled with the cluster setting `obs.ash.enrichment.enabled`. The new columns are `user`, `plan_gist`, `canary_stats`, `txn_id`, `session_id`.
* Added support for `ALTER DOMAIN ... ADD CONSTRAINT`. Constraints added this way are created as `NOT VALID` (they are not checked against existing rows).
* `ALTER DOMAIN ... ADD CONSTRAINT` and `ALTER DOMAIN ... NOT NULL` now validate new constraints against existing data before applying them.
* Changed execution insights to record only statements that have an insight (for example, slow or failed statements) within a flagged transaction, rather than recording every statement in the transaction. This results in the following changes:
  * `crdb_internal.{cluster,node}_stmt_execution_insights` no longer lists problem-free statements from flagged transactions.
  * The `query` column of `crdb_internal.{cluster,node}_txn_execution_insights` now includes only statements that generated an insight, and can be empty if the transaction was flagged due to an issue like a failure at `COMMIT` or high contention.
  * In the DB Console, the Transaction Insight Details page's Statement Executions tab now lists only statements that generated an insight.
    To view all statements for a transaction, including those hidden by this change, refer to the transaction in SQL activity.
* Changed `CREATE DOMAIN ... DEFAULT` and `ALTER DOMAIN ... SET DEFAULT` to sanitize and validate default expressions. These statements now reject defaults that reference other database objects.
* Improved `ALTER TABLE ... ALTER COLUMN TYPE` so it can succeed even when the column is referenced by dependent user-defined functions or stored procedures, as long as the type change is compatible with the routine bodies. Incompatible changes now return an error that identifies the routine that would become invalid.
* Improved PostgreSQL compatibility for sequences:
  * Added the built-in function `pg_get_sequence_data()` for compatibility with `pg_dump`, which uses it to read sequence data during dumps.
  * Fixed `SELECT last_value, is_called FROM <sequence>` on a sequence that has never been used to return the sequence start value with `is_called = false` (matching PostgreSQL behavior).
* `COPY FROM` now accepts values for `GENERATED ALWAYS AS IDENTITY` columns and writes them as supplied, matching PostgreSQL behavior. Previously, such columns could not appear in a `COPY` column list.
* Added support for the PostgreSQL-compatible `OVERRIDING SYSTEM VALUE` and `OVERRIDING USER VALUE` clauses to `INSERT` for identity columns:
  * `OVERRIDING SYSTEM VALUE` allows inserting an explicit value into a `GENERATED ALWAYS AS IDENTITY` column.
  * `OVERRIDING USER VALUE` ignores explicit identity-column values and uses the sequence-generated value instead.
* Changed `DROP TRIGGER` to allow users with the `DROP` privilege on a table (in addition to the table owner and admins) to drop triggers on that table.
* Added the per-table storage parameter `sql_stats_forecasts_min_goodness_of_fit`, which lets you override the cluster setting `sql.stats.forecasts.min_goodness_of_fit` for individual tables to better support statistics forecasting on tables with irregular growth patterns.
* The `CREATE DOMAIN ... CHECK` statements now sanitize and validate check expressions. References to descriptors are rejected.
* The `information_schema.crdb_node_active_session_history` and `information_schema.crdb_cluster_active_session_history` views now include the `query`, `query_summary`, and `database` columns for statement samples, which makes Active Session History easier to interpret without manually joining to statement statistics.
* Added support for `ALTER DOMAIN ... RENAME CONSTRAINT`.
* Added the view `information_schema.crdb_persisted_active_session_history`, which exposes durable, cluster-wide Active Session History (ASH) samples stored in `system.active_session_history`. The view returns the same columns as `crdb_node_active_session_history` and `crdb_cluster_active_session_history`, including the sampled statement's `query`, `query_summary`, and `database`.
* Added Active Session History (ASH) per-execution enrichment columns to the `information_schema` views `crdb_node_active_session_history`, `crdb_cluster_active_session_history`, and `crdb_persisted_active_session_history`: `user`, `plan_gist`, `canary_stats`, `txn_id`, and `session_id`.
* Added support for `ALTER DOMAIN ... DROP CONSTRAINT`.

### Operational changes

* Added the cluster setting `sql.insights.cooldown.duration` (default `5m`), which suppresses repeated execution insights for the same transaction fingerprint and problem profile within a configurable window. This helps prevent noisy transactions from crowding out the bounded insights store; set the value to `0` to record every occurrence. Added the metrics `sql.insights.cooldown.suppressed` and `sql.insights.cooldown.evictions` to monitor this behavior.
* Added an experimental Ubuntu-based Docker image alongside the existing Red Hat UBI-based Docker image. The Ubuntu-based image is available from the same registries with a `-noble` tag suffix.

### Bug fixes

* Fixed a bug where a `DELETE` or `UPDATE` that triggered a foreign key cascade could hit an internal assertion error ("execution requires all update columns have a fetch column") if a concurrent `ALTER TABLE ... ADD COLUMN` or `ALTER TABLE ... DROP COLUMN` modified the child table while the cascade was running.
* Fixed a bug where `DROP TABLE ... CASCADE` could fail when another `REGIONAL BY ROW` table referenced it via a foreign key used to infer its region column with the `infer_rbr_region_col_using_constraint` storage parameter.
* Fixed a bug where incremental backups could fail when the collection URI had no path component (for example, `gs://my-bucket?AUTH=...`).
* Fixed a bug where `INSERT`, `UPSERT`, and `UPDATE` statements could fail with the error "Access to crdb\_internal and system is restricted" while adding an expression index, adding a computed column, or changing a column's type, even when the statement did not reference `crdb_internal`. This error occurred only while the schema change was in progress.
* Fixed a bug that could cause a node to crash with a `transaction unexpectedly finalized` error when a transaction disabled write buffering mid-transaction and then issued `ROLLBACK` while buffered writes were still pending.
* Fixed a bug where a node whose `store descriptor` stopped being propagated via gossip was not kept in the `suspect` state for the configured interval, which could cause a flapping node to be reported as `live` and rejoin service prematurely.
* Fixed a bug where `FETCH FIRST` on an empty `WITH HOLD` cursor could return an internal error.
* Fixed a bug where concurrent role DDL (for example, `CREATE USER`, `ALTER ROLE`, `DROP USER`, `GRANT`, and `REVOKE`) could leave orphaned lease records and block subsequent role schema changes until the affected node was restarted.
* Fixed issues in multi-tenant deployments (virtual clusters) where the DB Console `All sources` view displayed incorrect metric values, including:
  * Missing totals for application- and SQL-level metrics across tenants.
  * Double-counting some store metrics, such as per-replica CPU and intent age.
* Fixed bugs in the DB Console `All sources` view that caused incorrect time-series metric totals in deployments using virtual clusters:
  * Application/SQL metrics now show the sum across all virtual clusters.
  * Several store metrics are no longer double-counted or incorrectly scoped to a tenant.
* Fixed a bug where running `ALTER SEQUENCE IF EXISTS` with the `SEQUENCE NAME` option on a non-existent sequence could cause a panic.
* Fixed a bug where the `STANDBY READ TS POLLER` job on a Physical Cluster Replication (PCR) standby cluster could get stuck waiting for the system `public` schema descriptor (ID 29) lease to expire.
* Fixed a bug where the row-count verification run after `IMPORT` could double-count rows that straddle span boundaries.
* Fixed a bug where a schema change could hang indefinitely if a node's clock lagged behind the commit timestamp of a new descriptor version. The lease manager now retries reading the new version instead of abandoning lease cleanup.
* Fixed a bug where a changefeed using a CDC expression (for example, `CREATE CHANGEFEED ... AS SELECT ...`) could fail during certain schema changes (adding a computed column, creating an expression index, or changing a column type) with an error like `function "crdb_internal.assignment_cast" unsupported by CDC`, even when the changefeed expression did not reference the new column.
* Fixed a bug where `pg_catalog.pg_enum` could expose enum values that were still being added by a schema change and were not yet usable. This could cause confusing errors for tools that read enum values from `pg_enum` and then attempted to use them in queries.
* Fixed a race condition in the SQL lease manager where a schema change could leave a stale lease record in `system.lease`, causing later lease acquisitions for that descriptor to fail with a "failed to insert lease" error until the stale record was cleaned up.

### Performance improvements

* Improved performance for SQL routines, including user-defined functions (UDFs) and procedures, by deferring routine body construction to execution time instead of planning time.
* Improved planning performance for queries against tables with user-defined `ENUM` columns.

### Miscellaneous

* Added the Physical Cluster Replication (PCR) metric `physical_replication.cluster.replication_lag`, which reports replication lag directly (without requiring manual calculation).
* Changed `ALTER VIRTUAL CLUSTER ... RENAME TO` to disallow renaming a tenant while it is participating in a Physical Cluster Replication (PCR) stream.

## v26.3.0-beta.2

Release Date: July 1, 2026

### Downloads

<Danger>
  CockroachDB v26.3.0-beta.2 is a testing release. Testing releases are intended for testing and experimentation only, and are not qualified for production environments and not eligible for support or uptime SLA commitments.
</Danger>

<Note>
  Experimental downloads are not qualified for production use and not eligible for support or uptime SLA commitments, whether they are for testing releases or production releases.
</Note>

<table><thead><tr><th>Operating System</th><th>Architecture</th><th>Full executable</th><th>SQL-only executable</th></tr></thead><tbody><tr><td rowspan="2">Linux</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.2.linux-amd64.tgz">cockroach-v26.3.0-beta.2.linux-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.2.linux-amd64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.2.linux-amd64.tgz">cockroach-sql-v26.3.0-beta.2.linux-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.2.linux-amd64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>ARM</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.2.linux-arm64.tgz">cockroach-v26.3.0-beta.2.linux-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.2.linux-arm64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.2.linux-arm64.tgz">cockroach-sql-v26.3.0-beta.2.linux-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.2.linux-arm64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td rowspan="2">Mac<br />(Experimental)</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.2.darwin-10.9-amd64.tgz">cockroach-v26.3.0-beta.2.darwin-10.9-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.2.darwin-10.9-amd64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.2.darwin-10.9-amd64.tgz">cockroach-sql-v26.3.0-beta.2.darwin-10.9-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.2.darwin-10.9-amd64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>ARM</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.2.darwin-11.0-arm64.tgz">cockroach-v26.3.0-beta.2.darwin-11.0-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.2.darwin-11.0-arm64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.2.darwin-11.0-arm64.tgz">cockroach-sql-v26.3.0-beta.2.darwin-11.0-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.2.darwin-11.0-arm64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>Windows<br />(Experimental)</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.2.windows-6.2-amd64.zip">cockroach-v26.3.0-beta.2.windows-6.2-amd64.zip</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.2.windows-6.2-amd64.zip.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.2.windows-6.2-amd64.zip">cockroach-sql-v26.3.0-beta.2.windows-6.2-amd64.zip</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.2.windows-6.2-amd64.zip.sha256sum">SHA256</a>)</td></tr></tbody></table>

### Docker image

[Multi-platform images](https://docs.docker.com/build/building/multi-platform) include support for both Intel and ARM. Multi-platform images do not take up additional space on your Docker host.

Within the multi-platform image, both Intel and ARM images are **generally available** for production use.

To download the Docker image:

```shell theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
docker pull cockroachdb/cockroach-unstable:v26.3.0-beta.2
```

### Breaking changes

* Changed `ALTER TABLE ... RENAME TO`, `ALTER VIEW ... RENAME TO`, and `ALTER SEQUENCE ... RENAME TO` to require the `CREATE` privilege on the schema that contains the object (instead of `CREATE` on the database), matching PostgreSQL behavior. In the default configuration, renaming objects in the `public` schema is unaffected.
* `COPY ... FROM` in text format now correctly decodes `BYTES`/`BYTEA` input (including the standard `\x...` hex format), matching PostgreSQL. Previously, these values could be stored as literal text instead of being decoded. To restore the previous behavior for workloads that depend on it, set `copy_from_decode_bytes_enabled` to `off`.

### Security updates

* Fixed the declarative schema-changer component to further address the privilege-escalation vulnerability in constraint validation. Constraint, column `NOT NULL`, and `enum-value-removal` validation now execute under the schema-change issuer's privileges instead of with internal node-level privileges, so a UDF reached via a `CHECK` predicate, `RLS USING` clause, or virtual computed column is bounded by the issuer's own grants.
* Fixed a privilege-escalation vulnerability in Logical Data Replication. Writer-side internal-executor sessions previously ran with internal node-level privileges, so any user-supplied expression evaluated on the destination table (including the optional UDF configured by `CREATE LOGICAL REPLICATION STREAM ... WITH FUNCTION`, `CHECK` constraints, computed columns, RLS policy expressions, and trigger bodies) inherited that authority and could escalate any user with `REPLICATIONDEST` to admin via `crdb_internal.execute_internally`. LDR writer sessions now run with the stream creator's privileges, scoped to the destination tables; row-level-security policies on the destination are bypassed for the writer so replicated rows are applied rather than rejected by a policy the stream creator cannot satisfy.
* Fixed a dead-letter-queue component to further address the privilege-escalation vulnerability in constraint validation. An attacker with `CREATE` privileges on the destination database who pre-created the predictably-named DLQ table with extra columns, `CHECK` constraints, or triggers could otherwise have those evaluated under the LDR job owner's identity. The DLQ writer now refuses to use a DLQ table whose owner is not the node user, or a table the LDR job did not create.
* Fixed a privilege-escalation vulnerability in constraint validation. Previously, a SQL user with `CREATE TABLE` and `CREATE FUNCTION` privileges could escalate to admin by adding a `CHECK` constraint, an FK, an RLS policy, or a virtual computed column whose expression invoked a UDF that called `crdb_internal.execute_internally` with a privileged statement; the constraint and foreign-key validation queries ran with internal node-level privileges, which the UDF body inherited under `SECURITY INVOKER`. Validation now runs with the schema-change issuer's privileges.

### General changes

* Improved the default batching mechanism for changefeeds to Kafka, Google Cloud Pub/Sub, and webhooks, including for multi-topic feeds (such as `EACH_FAMILY` changefeeds).

### SQL language changes

* Added support for `ALTER TRIGGER ... ON ... RENAME TO` to rename a trigger on a table. Renaming a trigger requires `OWNERSHIP` on the table. CockroachDB also supports `ALTER TRIGGER IF EXISTS` (an extension to PostgreSQL), which performs no action if the table or trigger does not exist.
* Added per-execution enrichment columns to the virtual tables `crdb_internal.node_active_session_history` and `crdb_internal.cluster_active_session_history`, and to the table `system.active_session_history`, when ASH enrichment is enabled with the cluster setting `obs.ash.enrichment.enabled`. The new columns are `user`, `plan_gist`, `canary_stats`, `txn_id`, `session_id`.
* Added support for `ALTER DOMAIN ... ADD CONSTRAINT`. Constraints added this way are created as `NOT VALID` (they are not checked against existing rows).
* `ALTER DOMAIN ... ADD CONSTRAINT` and `ALTER DOMAIN ... NOT NULL` now validate new constraints against existing data before applying them.
* Changed execution insights to record only statements that have an insight (for example, slow or failed statements) within a flagged transaction, rather than recording every statement in the transaction. This results in the following changes:
  * `crdb_internal.{cluster,node}_stmt_execution_insights` no longer lists problem-free statements from flagged transactions.
  * The `query` column of `crdb_internal.{cluster,node}_txn_execution_insights` now includes only statements that generated an insight, and can be empty if the transaction was flagged due to an issue like a failure at `COMMIT` or high contention.
  * In the DB Console, the Transaction Insight Details page's Statement Executions tab now lists only statements that generated an insight.
    To view all statements for a transaction, including those hidden by this change, refer to the transaction in SQL activity.
* Changed `CREATE DOMAIN ... DEFAULT` and `ALTER DOMAIN ... SET DEFAULT` to sanitize and validate default expressions. These statements now reject defaults that reference other database objects.
* Improved `ALTER TABLE ... ALTER COLUMN TYPE` so it can succeed even when the column is referenced by dependent user-defined functions or stored procedures, as long as the type change is compatible with the routine bodies. Incompatible changes now return an error that identifies the routine that would become invalid.
* Improved PostgreSQL compatibility for sequences:
  * Added the built-in function `pg_get_sequence_data()` for compatibility with `pg_dump`, which uses it to read sequence data during dumps.
  * Fixed `SELECT last_value, is_called FROM <sequence>` on a sequence that has never been used to return the sequence start value with `is_called = false` (matching PostgreSQL behavior).
* `COPY FROM` now accepts values for `GENERATED ALWAYS AS IDENTITY` columns and writes them as supplied, matching PostgreSQL behavior. Previously, such columns could not appear in a `COPY` column list.
* Added support for the PostgreSQL-compatible `OVERRIDING SYSTEM VALUE` and `OVERRIDING USER VALUE` clauses to `INSERT` for identity columns:
  * `OVERRIDING SYSTEM VALUE` allows inserting an explicit value into a `GENERATED ALWAYS AS IDENTITY` column.
  * `OVERRIDING USER VALUE` ignores explicit identity-column values and uses the sequence-generated value instead.
* Changed `DROP TRIGGER` to allow users with the `DROP` privilege on a table (in addition to the table owner and admins) to drop triggers on that table.
* Added the per-table storage parameter `sql_stats_forecasts_min_goodness_of_fit`, which lets you override the cluster setting `sql.stats.forecasts.min_goodness_of_fit` for individual tables to better support statistics forecasting on tables with irregular growth patterns.
* The `CREATE DOMAIN ... CHECK` statements now sanitize and validate check expressions. References to descriptors are rejected.
* The `information_schema.crdb_node_active_session_history` and `information_schema.crdb_cluster_active_session_history` views now include the `query`, `query_summary`, and `database` columns for statement samples, which makes Active Session History easier to interpret without manually joining to statement statistics.
* Added support for `ALTER DOMAIN ... RENAME CONSTRAINT`.
* Added the view `information_schema.crdb_persisted_active_session_history`, which exposes durable, cluster-wide Active Session History (ASH) samples stored in `system.active_session_history`. The view returns the same columns as `crdb_node_active_session_history` and `crdb_cluster_active_session_history`, including the sampled statement's `query`, `query_summary`, and `database`.
* Added Active Session History (ASH) per-execution enrichment columns to the `information_schema` views `crdb_node_active_session_history`, `crdb_cluster_active_session_history`, and `crdb_persisted_active_session_history`: `user`, `plan_gist`, `canary_stats`, `txn_id`, and `session_id`.
* Added support for `ALTER DOMAIN ... DROP Constraint`.

### Operational changes

* Added the cluster setting `sql.insights.cooldown.duration` (default `5m`), which suppresses repeated execution insights for the same transaction fingerprint and problem profile within a configurable window. This helps prevent noisy transactions from crowding out the bounded insights store; set the value to `0` to record every occurrence. Added the metrics `sql.insights.cooldown.suppressed` and `sql.insights.cooldown.evictions` to monitor this behavior.
* Added an experimental Ubuntu-based Docker image alongside the existing Red Hat UBI-based Docker image. The Ubuntu-based image is available from the same registries with a `-noble` tag suffix.
* Reduced the default value of the `kv.range_merge.queue_interval` cluster setting (which controls how long the merge queue waits between processing replicas) from 5s to 200ms. This helps the merge queue keep up with range creation and reduces range accumulation.

### Bug fixes

* Fixed a bug where a `DELETE` or `UPDATE` that triggered a foreign key cascade could hit an internal assertion error ("execution requires all update columns have a fetch column") if a concurrent `ALTER TABLE ... ADD COLUMN` or `ALTER TABLE ... DROP COLUMN` modified the child table while the cascade was running.
* Fixed a bug where `DROP TABLE ... CASCADE` could fail when another `REGIONAL BY ROW` table referenced it via a foreign key used to infer its region column with the `infer_rbr_region_col_using_constraint` storage parameter.
* Fixed a bug where incremental backups could fail when the collection URI had no path component (for example, `gs://my-bucket?AUTH=...`).
* Fixed a bug where `INSERT`, `UPSERT`, and `UPDATE` statements could fail with the error "Access to crdb\_internal and system is restricted" while adding an expression index, adding a computed column, or changing a column's type, even when the statement did not reference `crdb_internal`. This error occurred only while the schema change was in progress.
* Fixed a bug that could cause a node to crash with a `transaction unexpectedly finalized` error when a transaction disabled write buffering mid-transaction and then issued `ROLLBACK` while buffered writes were still pending.
* Fixed a bug where a node whose `store descriptor` stopped being propagated via gossip was not kept in the `suspect` state for the configured interval, which could cause a flapping node to be reported as `live` and rejoin service prematurely.
* Fixed a bug where `FETCH FIRST` on an empty `WITH HOLD` cursor could return an internal error.
* Fixed a bug where concurrent role DDL (for example, `CREATE USER`, `ALTER ROLE`, `DROP USER`, `GRANT`, and `REVOKE`) could leave orphaned lease records and block subsequent role schema changes until the affected node was restarted.
* Fixed issues in multi-tenant deployments (virtual clusters) where the DB Console `All sources` view displayed incorrect metric values, including:
  * Missing totals for application- and SQL-level metrics across tenants.
  * Double-counting some store metrics, such as per-replica CPU and intent age.
* Fixed bugs in the DB Console `All sources` view that caused incorrect time-series metric totals in deployments using virtual clusters:
  * Application/SQL metrics now show the sum across all virtual clusters.
  * Several store metrics are no longer double-counted or incorrectly scoped to a tenant.
* Fixed a bug where running `ALTER SEQUENCE IF EXISTS` with the `SEQUENCE NAME` option on a non-existent sequence could cause a panic.
* Fixed a bug where the `STANDBY READ TS POLLER` job on a Physical Cluster Replication (PCR) standby cluster could get stuck waiting for the system `public` schema descriptor (ID 29) lease to expire.
* Fixed a bug where the row-count verification run after `IMPORT` could double-count rows that straddle span boundaries.
* Fixed a bug where a schema change could hang indefinitely if a node's clock lagged behind the commit timestamp of a new descriptor version. The lease manager now retries reading the new version instead of abandoning lease cleanup.
* Fixed a bug where a changefeed using a CDC expression (for example, `CREATE CHANGEFEED ... AS SELECT ...`) could fail during certain schema changes (adding a computed column, creating an expression index, or changing a column type) with an error like `function "crdb_internal.assignment_cast" unsupported by CDC`, even when the changefeed expression did not reference the new column.
* Fixed a bug where `pg_catalog.pg_enum` could expose enum values that were still being added by a schema change and were not yet usable. This could cause confusing errors for tools that read enum values from `pg_enum` and then attempted to use them in queries.
* Fixed a race condition in the SQL lease manager where a schema change could leave a stale lease record in `system.lease`, causing later lease acquisitions for that descriptor to fail with a "failed to insert lease" error until the stale record was cleaned up.
* Fixed an issue where a partitioned index scan on a JSONB column could generate an empty key span and fail with `end key must be greater than start`.
* Fixed a bug where Avro-enriched changefeed messages could emit stale values for `source.ts_ns`, `source.ts_hlc`, and `source.mvcc_timestamp`, repeating the timestamp from an earlier event instead of the current event.
* Fixed a bug that could cause an initial-scan changefeed with `diff` enabled, or a CDC query using `cdc_prev`, to fail with a GC threshold error if garbage collection ran during the scan.
* Fixed a bug where importing Avro data with large records could cause unbounded memory growth, slowing or stalling the import. Such records now fail with a clear error suggesting that the `max_row_size` option be increased.
* Fixed a bug where `CREATE TEMPORARY TABLE` could fail with an internal error in the same session after the session's first temporary table creation was rolled back.

### Performance improvements

* Improved performance for SQL routines, including user-defined functions (UDFs) and procedures, by deferring routine body construction to execution time instead of planning time.
* Improved planning performance for queries against tables with user-defined `ENUM` columns.

### Miscellaneous

* Added the Physical Cluster Replication (PCR) metric `physical_replication.cluster.replication_lag`, which reports replication lag directly (without requiring manual calculation).
* Changed `ALTER VIRTUAL CLUSTER ... RENAME TO` to disallow renaming a tenant while it is participating in a Physical Cluster Replication (PCR) stream.

## v26.3.0-beta.3

Release Date: July 8, 2026

### Downloads

<Danger>
  CockroachDB v26.3.0-beta.3 is a testing release. Testing releases are intended for testing and experimentation only, and are not qualified for production environments and not eligible for support or uptime SLA commitments.
</Danger>

<Note>
  Experimental downloads are not qualified for production use and not eligible for support or uptime SLA commitments, whether they are for testing releases or production releases.
</Note>

<table><thead><tr><th>Operating System</th><th>Architecture</th><th>Full executable</th><th>SQL-only executable</th></tr></thead><tbody><tr><td rowspan="2">Linux</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.3.linux-amd64.tgz">cockroach-v26.3.0-beta.3.linux-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.3.linux-amd64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.3.linux-amd64.tgz">cockroach-sql-v26.3.0-beta.3.linux-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.3.linux-amd64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>ARM</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.3.linux-arm64.tgz">cockroach-v26.3.0-beta.3.linux-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.3.linux-arm64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.3.linux-arm64.tgz">cockroach-sql-v26.3.0-beta.3.linux-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.3.linux-arm64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td rowspan="2">Mac<br />(Experimental)</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.3.darwin-10.9-amd64.tgz">cockroach-v26.3.0-beta.3.darwin-10.9-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.3.darwin-10.9-amd64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.3.darwin-10.9-amd64.tgz">cockroach-sql-v26.3.0-beta.3.darwin-10.9-amd64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.3.darwin-10.9-amd64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>ARM</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.3.darwin-11.0-arm64.tgz">cockroach-v26.3.0-beta.3.darwin-11.0-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.3.darwin-11.0-arm64.tgz.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.3.darwin-11.0-arm64.tgz">cockroach-sql-v26.3.0-beta.3.darwin-11.0-arm64.tgz</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.3.darwin-11.0-arm64.tgz.sha256sum">SHA256</a>)</td></tr><tr><td>Windows<br />(Experimental)</td><td>Intel</td><td><a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.3.windows-6.2-amd64.zip">cockroach-v26.3.0-beta.3.windows-6.2-amd64.zip</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-v26.3.0-beta.3.windows-6.2-amd64.zip.sha256sum">SHA256</a>)</td><td><a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.3.windows-6.2-amd64.zip">cockroach-sql-v26.3.0-beta.3.windows-6.2-amd64.zip</a><br />(<a href="https://binaries.cockroachdb.com/cockroach-sql-v26.3.0-beta.3.windows-6.2-amd64.zip.sha256sum">SHA256</a>)</td></tr></tbody></table>

### Docker image

[Multi-platform images](https://docs.docker.com/build/building/multi-platform) include support for both Intel and ARM. Multi-platform images do not take up additional space on your Docker host.

Within the multi-platform image, both Intel and ARM images are **generally available** for production use.

To download the Docker image:

```shell theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
docker pull cockroachdb/cockroach-unstable:v26.3.0-beta.3
```

### Breaking changes

* Changed `ALTER TABLE ... RENAME TO`, `ALTER VIEW ... RENAME TO`, and `ALTER SEQUENCE ... RENAME TO` to require the `CREATE` privilege on the schema that contains the object (instead of `CREATE` on the database), matching PostgreSQL behavior. In the default configuration, renaming objects in the `public` schema is unaffected.
* `COPY ... FROM` in text format now correctly decodes `BYTES`/`BYTEA` input (including the standard `\x...` hex format), matching PostgreSQL. Previously, these values could be stored as literal text instead of being decoded. To restore the previous behavior for workloads that depend on it, set `copy_from_decode_bytes_enabled` to `off`.

### Security updates

* Fixed the declarative schema-changer component to further address the privilege-escalation vulnerability in constraint validation. Constraint, column `NOT NULL`, and `enum-value-removal` validation now execute under the schema-change issuer's privileges instead of with internal node-level privileges, so a UDF reached via a `CHECK` predicate, `RLS USING` clause, or virtual computed column is bounded by the issuer's own grants.
* Fixed a privilege-escalation vulnerability in Logical Data Replication. Writer-side internal-executor sessions previously ran with internal node-level privileges, so any user-supplied expression evaluated on the destination table (including the optional UDF configured by `CREATE LOGICAL REPLICATION STREAM ... WITH FUNCTION`, `CHECK` constraints, computed columns, RLS policy expressions, and trigger bodies) inherited that authority and could escalate any user with `REPLICATIONDEST` to admin via `crdb_internal.execute_internally`. LDR writer sessions now run with the stream creator's privileges, scoped to the destination tables; row-level-security policies on the destination are bypassed for the writer so replicated rows are applied rather than rejected by a policy the stream creator cannot satisfy.
* Fixed a dead-letter-queue component to further address the privilege-escalation vulnerability in constraint validation. An attacker with `CREATE` privileges on the destination database who pre-created the predictably-named DLQ table with extra columns, `CHECK` constraints, or triggers could otherwise have those evaluated under the LDR job owner's identity. The DLQ writer now refuses to use a DLQ table whose owner is not the node user, or a table the LDR job did not create.
* Fixed a privilege-escalation vulnerability in constraint validation. Previously, a SQL user with `CREATE TABLE` and `CREATE FUNCTION` privileges could escalate to admin by adding a `CHECK` constraint, an FK, an RLS policy, or a virtual computed column whose expression invoked a UDF that called `crdb_internal.execute_internally` with a privileged statement; the constraint and foreign-key validation queries ran with internal node-level privileges, which the UDF body inherited under `SECURITY INVOKER`. Validation now runs with the schema-change issuer's privileges.

### General changes

* Improved the default batching mechanism for changefeeds to Kafka, Google Cloud Pub/Sub, and webhooks, including for multi-topic feeds (such as `EACH_FAMILY` changefeeds).
* `CREATE CHANGEFEED` now supports the `create_kafka_topics` option to control Kafka topic creation: `broker_auto` (default) relies on the Kafka cluster to auto-create topics, `explicit` creates topics using the Kafka Admin API, and `off` disables topic creation.
* The `no-linger` sink for changefeeds (the default for the kafka, webhook, and pubsub v2 sinks) no longer requires `Frequency` when `Messages` or `Bytes` is set. The no-linger sink interprets the `Flush.Frequency` sink config option as a minimum linger; a batch is held until its oldest event is at least `Frequency` old, coalescing events into fewer, larger requests at the cost of latency, for example `kafka_sink_config='{"Flush": {"Frequency": "1s"}}'`. Leave it unset for the lowest latency.

### SQL language changes

* Added support for `ALTER TRIGGER ... ON ... RENAME TO` to rename a trigger on a table. Renaming a trigger requires `OWNERSHIP` on the table. CockroachDB also supports `ALTER TRIGGER IF EXISTS` (an extension to PostgreSQL), which performs no action if the table or trigger does not exist.
* Added per-execution enrichment columns to the virtual tables `crdb_internal.node_active_session_history` and `crdb_internal.cluster_active_session_history`, and to the table `system.active_session_history`, when ASH enrichment is enabled with the cluster setting `obs.ash.enrichment.enabled`. The new columns are `user`, `plan_gist`, `canary_stats`, `txn_id`, `session_id`.
* Added support for `ALTER DOMAIN ... ADD CONSTRAINT`. Constraints added this way are created as `NOT VALID` (they are not checked against existing rows).
* `ALTER DOMAIN ... ADD CONSTRAINT` and `ALTER DOMAIN ... NOT NULL` now validate new constraints against existing data before applying them.
* Changed execution insights to record only statements that have an insight (for example, slow or failed statements) within a flagged transaction, rather than recording every statement in the transaction. This results in the following changes:
  * `crdb_internal.{cluster,node}_stmt_execution_insights` no longer lists problem-free statements from flagged transactions.
  * The `query` column of `crdb_internal.{cluster,node}_txn_execution_insights` now includes only statements that generated an insight, and can be empty if the transaction was flagged due to an issue like a failure at `COMMIT` or high contention.
  * In the DB Console, the Transaction Insight Details page's Statement Executions tab now lists only statements that generated an insight.
    To view all statements for a transaction, including those hidden by this change, refer to the transaction in SQL activity.
* Changed `CREATE DOMAIN ... DEFAULT` and `ALTER DOMAIN ... SET DEFAULT` to sanitize and validate default expressions. These statements now reject defaults that reference other database objects.
* Improved `ALTER TABLE ... ALTER COLUMN TYPE` so it can succeed even when the column is referenced by dependent user-defined functions or stored procedures, as long as the type change is compatible with the routine bodies. Incompatible changes now return an error that identifies the routine that would become invalid.
* Improved PostgreSQL compatibility for sequences:
  * Added the built-in function `pg_get_sequence_data()` for compatibility with `pg_dump`, which uses it to read sequence data during dumps.
  * Fixed `SELECT last_value, is_called FROM <sequence>` on a sequence that has never been used to return the sequence start value with `is_called = false` (matching PostgreSQL behavior).
* `COPY FROM` now accepts values for `GENERATED ALWAYS AS IDENTITY` columns and writes them as supplied, matching PostgreSQL behavior. Previously, such columns could not appear in a `COPY` column list.
* Added support for the PostgreSQL-compatible `OVERRIDING SYSTEM VALUE` and `OVERRIDING USER VALUE` clauses to `INSERT` for identity columns:
  * `OVERRIDING SYSTEM VALUE` allows inserting an explicit value into a `GENERATED ALWAYS AS IDENTITY` column.
  * `OVERRIDING USER VALUE` ignores explicit identity-column values and uses the sequence-generated value instead.
* Changed `DROP TRIGGER` to allow users with the `DROP` privilege on a table (in addition to the table owner and admins) to drop triggers on that table.
* Added the per-table storage parameter `sql_stats_forecasts_min_goodness_of_fit`, which lets you override the cluster setting `sql.stats.forecasts.min_goodness_of_fit` for individual tables to better support statistics forecasting on tables with irregular growth patterns.
* The `CREATE DOMAIN ... CHECK` statements now sanitize and validate check expressions. References to descriptors are rejected.
* The `information_schema.crdb_node_active_session_history` and `information_schema.crdb_cluster_active_session_history` views now include the `query`, `query_summary`, and `database` columns for statement samples, which makes Active Session History easier to interpret without manually joining to statement statistics.
* Added support for `ALTER DOMAIN ... RENAME CONSTRAINT`.
* Added the view `information_schema.crdb_persisted_active_session_history`, which exposes durable, cluster-wide Active Session History (ASH) samples stored in `system.active_session_history`. The view returns the same columns as `crdb_node_active_session_history` and `crdb_cluster_active_session_history`, including the sampled statement's `query`, `query_summary`, and `database`.
* Added Active Session History (ASH) per-execution enrichment columns to the `information_schema` views `crdb_node_active_session_history`, `crdb_cluster_active_session_history`, and `crdb_persisted_active_session_history`: `user`, `plan_gist`, `canary_stats`, `txn_id`, and `session_id`.
* Added support for `ALTER DOMAIN ... DROP CONSTRAINT`.

### Operational changes

* Added the cluster setting `sql.insights.cooldown.duration` (default `5m`), which suppresses repeated execution insights for the same transaction fingerprint and problem profile within a configurable window. This helps prevent noisy transactions from crowding out the bounded insights store; set the value to `0` to record every occurrence. Added the metrics `sql.insights.cooldown.suppressed` and `sql.insights.cooldown.evictions` to monitor this behavior.
* Added an experimental Ubuntu-based Docker image alongside the existing Red Hat UBI-based Docker image. The Ubuntu-based image is available from the same registries with a `-noble` tag suffix.
* Reduced the default value of the `kv.range_merge.queue_interval` cluster setting (which controls how long the merge queue waits between processing replicas) from 5s to 200ms. This helps the merge queue keep up with range creation and reduces range accumulation.
* Users of the `STRICT` backup option may want to enable this option for `RESTORE` as well, to obey domiciling requirements.
* Added sixteen new SQL metrics for tracking `EXPLAIN` and `EXPLAIN ANALYZE` usage: `sql.explain.started.count` / `sql.explain.count`, `sql.explain_analyze.started.count` / `sql.explain_analyze.count`, plus four sub-counters (`sql.explain.procedure.count`, `sql.explain_analyze.procedure.count`, `sql.explain.udf.count`, `sql.explain_analyze.udf.count` and their `.internal` counterparts for internal queries) that fire when the `EXPLAIN` target is a `CALL` of a stored procedure or a statement that invokes a user-defined function. These statements were previously bucketed into the `sql.misc.*.count` series and were not visible individually.

### Bug fixes

* Fixed a bug where a `DELETE` or `UPDATE` that triggered a foreign key cascade could hit an internal assertion error ("execution requires all update columns have a fetch column") if a concurrent `ALTER TABLE ... ADD COLUMN` or `ALTER TABLE ... DROP COLUMN` modified the child table while the cascade was running.
* Fixed a bug where `DROP TABLE ... CASCADE` could fail when another `REGIONAL BY ROW` table referenced it via a foreign key used to infer its region column with the `infer_rbr_region_col_using_constraint` storage parameter.
* Fixed a bug where incremental backups could fail when the collection URI had no path component (for example, `gs://my-bucket?AUTH=...`).
* Fixed a bug where `INSERT`, `UPSERT`, and `UPDATE` statements could fail with the error "Access to crdb\_internal and system is restricted" while adding an expression index, adding a computed column, or changing a column's type, even when the statement did not reference `crdb_internal`. This error occurred only while the schema change was in progress.
* Fixed a bug that could cause a node to crash with a `transaction unexpectedly finalized` error when a transaction disabled write buffering mid-transaction and then issued `ROLLBACK` while buffered writes were still pending.
* Fixed a bug where a node whose `store descriptor` stopped being propagated via gossip was not kept in the `suspect` state for the configured interval, which could cause a flapping node to be reported as `live` and rejoin service prematurely.
* Fixed a bug where `FETCH FIRST` on an empty `WITH HOLD` cursor could return an internal error.
* Fixed a bug where concurrent role DDL (for example, `CREATE USER`, `ALTER ROLE`, `DROP USER`, `GRANT`, and `REVOKE`) could leave orphaned lease records and block subsequent role schema changes until the affected node was restarted.
* Fixed issues in multi-tenant deployments (virtual clusters) where the DB Console `All sources` view displayed incorrect metric values, including:
  * Missing totals for application- and SQL-level metrics across tenants.
  * Double-counting some store metrics, such as per-replica CPU and intent age.
* Fixed bugs in the DB Console `All sources` view that caused incorrect time-series metric totals in deployments using virtual clusters:
  * Application/SQL metrics now show the sum across all virtual clusters.
  * Several store metrics are no longer double-counted or incorrectly scoped to a tenant.
* Fixed a bug where running `ALTER SEQUENCE IF EXISTS` with the `SEQUENCE NAME` option on a non-existent sequence could cause a panic.
* Fixed a bug where the `STANDBY READ TS POLLER` job on a Physical Cluster Replication (PCR) standby cluster could get stuck waiting for the system `public` schema descriptor (ID 29) lease to expire.
* Fixed a bug where the row-count verification run after `IMPORT` could double-count rows that straddle span boundaries.
* Fixed a bug where a schema change could hang indefinitely if a node's clock lagged behind the commit timestamp of a new descriptor version. The lease manager now retries reading the new version instead of abandoning lease cleanup.
* Fixed a bug where a changefeed using a CDC expression (for example, `CREATE CHANGEFEED ... AS SELECT ...`) could fail during certain schema changes (adding a computed column, creating an expression index, or changing a column type) with an error like `function "crdb_internal.assignment_cast" unsupported by CDC`, even when the changefeed expression did not reference the new column.
* Fixed a bug where `pg_catalog.pg_enum` could expose enum values that were still being added by a schema change and were not yet usable. This could cause confusing errors for tools that read enum values from `pg_enum` and then attempted to use them in queries.
* Fixed a race condition in the SQL lease manager where a schema change could leave a stale lease record in `system.lease`, causing later lease acquisitions for that descriptor to fail with a "failed to insert lease" error until the stale record was cleaned up.
* Fixed an issue where a partitioned index scan on a JSONB column could generate an empty key span and fail with `end key must be greater than start`.
* Fixed a bug where Avro-enriched changefeed messages could emit stale values for `source.ts_ns`, `source.ts_hlc`, and `source.mvcc_timestamp`, repeating the timestamp from an earlier event instead of the current event.
* Fixed a bug that could cause an initial-scan changefeed with `diff` enabled, or a CDC query using `cdc_prev`, to fail with a GC threshold error if garbage collection ran during the scan.
* Fixed a bug where importing Avro data with large records could cause unbounded memory growth, slowing or stalling the import. Such records now fail with a clear error suggesting that the `max_row_size` option be increased.
* Fixed a bug where `CREATE TEMPORARY TABLE` could fail with an internal error in the same session after the session's first temporary table creation was rolled back.
* Fixed a bug where `DISTINCT ON` queries with `ORDER BY` could return incorrect results during distributed execution when the operation spilled to disk (for example, due to memory pressure).
* Fixed a bug where `CREATE SCHEMA` with an empty quoted schema name (for example, `""`) could trigger an internal error. It now returns a user-facing syntax error.
* Fixed a bug where binding a prepared `EXPLAIN` or `EXPLAIN ANALYZE` statement could fail with the error "EXPLAIN ANALYZE can only be used as a top-level statement".

### Performance improvements

* Improved performance for SQL routines, including user-defined functions (UDFs) and procedures, by deferring routine body construction to execution time instead of planning time.
* Improved planning performance for queries against tables with user-defined `ENUM` columns.

### Miscellaneous

* Added the Physical Cluster Replication (PCR) metric `physical_replication.cluster.replication_lag`, which reports replication lag directly (without requiring manual calculation).
* Changed `ALTER VIRTUAL CLUSTER ... RENAME TO` to disallow renaming a tenant while it is participating in a Physical Cluster Replication (PCR) stream.
