diff --git a/pages/advanced-algorithms/available-algorithms.mdx b/pages/advanced-algorithms/available-algorithms.mdx
index fad07b562..c4741566f 100644
--- a/pages/advanced-algorithms/available-algorithms.mdx
+++ b/pages/advanced-algorithms/available-algorithms.mdx
@@ -168,6 +168,7 @@ Running `SHOW QUERY CALLABLE MAPPINGS` requires the `CONFIG` privilege.
| apoc.coll.unionAll | Returns the union of two input lists, including duplicates | [collections.union_all()](/advanced-algorithms/available-algorithms/collections#union_all) |
| apoc.coll.removeAll | Removes defined elements from the input list | [collections.remove_all()](/advanced-algorithms/available-algorithms/collections#remove_all) |
| apoc.coll.contains | Verifies the existence of an input value in an input list | [collections.contains()](/advanced-algorithms/available-algorithms/collections#contains) |
+| apoc.coll.indexOf | Returns the position of the first element equal to a value | [collections.index_of()](/advanced-algorithms/available-algorithms/collections#index_of) |
| apoc.coll.flatten | Returns flattened list of inputs provided | [collections.flatten()](/advanced-algorithms/available-algorithms/collections#flatten) |
| apoc.coll.frequenciesAsMap | Returns a map of frequencies of the items in the collection | [collections.frequencies_as_map()](/advanced-algorithms/available-algorithms/collections#frequencies_as_map) |
| apoc.coll.pairs | Creates pairs of neighbor elements within an input list | [collections.pairs()](/advanced-algorithms/available-algorithms/collections#pairs) |
diff --git a/pages/advanced-algorithms/available-algorithms/collections.mdx b/pages/advanced-algorithms/available-algorithms/collections.mdx
index ef45860c5..acca75b15 100644
--- a/pages/advanced-algorithms/available-algorithms/collections.mdx
+++ b/pages/advanced-algorithms/available-algorithms/collections.mdx
@@ -46,7 +46,7 @@ argument-validation error. Each function returns a defined result:
| Function | Result when a list argument is `NULL` |
| --- | --- |
-| `sum()`, `sum_longs()`, `avg()`, `min()`, `max()`, `to_set()`, `pairs()` | `null` |
+| `sum()`, `sum_longs()`, `avg()`, `min()`, `max()`, `to_set()`, `pairs()`, `index_of()` | `null` |
| `union()`, `union_all()`, `disjunction()` | the other list; `null` when both are `NULL` |
| `remove_all()`, `subtract()` | `null` when the first list is `NULL`; the first list when the second is `NULL` |
| `intersection()`, `sort()`, `flatten()`, `duplicates()` | empty list `[]` |
@@ -58,6 +58,7 @@ argument-validation error. Each function returns a defined result:
- `min()` and `max()` skip `NULL` elements; a list containing only `NULL` values returns `null`.
- `contains()` and `contains_all()` never match a `NULL` search value (`NULL = NULL` is not true), so searching for `NULL` returns `false`.
+- `index_of()` returns `null` for a `NULL` search value, and `-1` for a search value that holds a `NULL` at any depth (`NULL = x` is never true). A `NULL` list element matches nothing.
- `split()` treats a `NULL` delimiter as matching nothing and returns the whole list as a single part; `NULL` list elements are kept.
- `frequencies_as_map()` counts `NULL` elements under the `"NO_VALUE"` key.
- `flatten()`, `pairs()`, `to_set()`, `union()`, `union_all()`, `remove_all()`, `intersection()`, `disjunction()`, `subtract()` and `duplicates()` keep `NULL` elements as ordinary values.
@@ -281,6 +282,41 @@ RETURN collections.contains([1,2,3], "e") AS output;
+---------------------------------------------------------+
```
+### `index_of()`
+
+Returns the position of the first element in a list that is equal to an input
+value, compared with `=`.
+
+
+This function is equivalent to Cypher's **coll.indexOf**.
+
+
+{
Input:
}
+
+- `coll: List[Any]` ➡ The input list searched for the value.
+- `value: Any` ➡ The input value searched in the list.
+
+{ Output:
}
+
+- `integer` ➡ The zero-based position of the first element equal to `value`,
+ or `-1` if no element is.
+
+{ Usage:
}
+
+The following query will return the position of the first `5` in the list:
+
+```cypher
+RETURN collections.index_of([1, 5, 3, 5], 5) AS index;
+```
+
+```plaintext
++---------------------------------------------------------+
+| index |
++---------------------------------------------------------+
+| 1 |
++---------------------------------------------------------+
+```
+
### `contains_all()`
Checks if a list contains all the values from another list.
diff --git a/pages/client-libraries/java.mdx b/pages/client-libraries/java.mdx
index 26dda806a..000ca3ea3 100644
--- a/pages/client-libraries/java.mdx
+++ b/pages/client-libraries/java.mdx
@@ -667,7 +667,7 @@ public void createNode() {
```
The above code runs a simple query without any transaction management. It simplifies the experience, but you lose flexibility when running queries.
-Since explicit transactions are multi-statement transactions, creating an index, constraints or auth config queries is not possible in multi-line transactions. Therefore, you need to use implicit transactions for those queries.
+Since explicit transactions are multi-statement transactions, creating an index or constraints is not possible in multi-line transactions. Therefore, you need to use implicit transactions for those queries.
#### Asynchronous transaction management
diff --git a/pages/clustering/high-availability/coordinator-authentication.mdx b/pages/clustering/high-availability/coordinator-authentication.mdx
index 1d6685aa3..e90b4a185 100644
--- a/pages/clustering/high-availability/coordinator-authentication.mdx
+++ b/pages/clustering/high-availability/coordinator-authentication.mdx
@@ -42,7 +42,8 @@ carrying a coordinator privilege mask.
| Users | Stored in the auth store | Not supported |
| Roles | Stored in the auth store | Stored in the **Raft-replicated cluster state** |
| Privileges | Full privilege set + fine-grained access control | Exactly two: `COORDINATOR_READ`, `COORDINATOR_WRITE` |
-| Basic auth | Username and password are validated | Passthrough — credentials are ignored (see [below](#basic-authentication-passthrough)) |
+| Basic auth | Username and password are validated | Passthrough — credentials are ignored (see [below](#authentication-passthrough)) |
+| Other Bolt schemes | Rejected unless mapped to an auth module | Passthrough, same as basic auth (see [below](#authentication-passthrough)) |
| SSO | Supported (OIDC, SAML, Kerberos) | Supported (OIDC, SAML, Kerberos) |
Because roles live in the Raft log, they survive coordinator restarts, are
@@ -110,57 +111,88 @@ Anything not recognized fails closed and requires `COORDINATOR_WRITE`.
## Authentication modes
-A coordinator accepts exactly two kinds of Bolt connection: **basic/none** and
-an **SSO scheme listed in `--auth-module-mappings`**. Any other scheme is
-rejected with:
+A coordinator sorts every Bolt connection into one of two paths based on the
+authentication scheme the client sends:
-```
-The "" authentication scheme isn't supported on this coordinator;
-connect with basic auth or an SSO scheme listed in the auth-module-mappings flag.
-```
+- A scheme **listed in `--auth-module-mappings`** takes the [SSO
+ path](#sso-authentication): the coordinator runs the mapped auth module and
+ genuinely authenticates the connection.
+- **Every other scheme** — `basic`, `none`, or a scheme that is **not** listed
+ in `--auth-module-mappings` — takes the [passthrough
+ path](#authentication-passthrough) described next.
+
+There is no separate "unsupported scheme" rule. A client that keeps its
+data-instance SSO configuration (for example, `oidc`) when talking to a
+coordinator that has no SSO configured is treated exactly like a basic-auth
+client.
-### Basic authentication passthrough
+### Authentication passthrough
-When SSO is not in effect, connecting with a username and password (or with no
-auth at all) **succeeds and the credentials are ignored**. The session gets full
+When SSO is not in effect, connecting with a username and password, with no
+auth at all, or with any scheme not listed in `--auth-module-mappings`
+**succeeds and the credentials are ignored**. The session gets full
`COORDINATOR_WRITE` access. This is the pre-3.13 behavior, it requires no
license, and it keeps existing admin tooling working unchanged.
-Basic/none authentication is **denied** only when **all three** of the following
-hold:
+
+**Memgraph 3.13 rejected unlisted schemes.** In 3.13, a coordinator only let
+`basic`/`none` through and refused any other scheme outright, so drivers that
+sent their data-instance SSO scheme to a coordinator without SSO stopped
+connecting after an upgrade from 3.12. From Memgraph 3.14, an unlisted scheme
+follows the same passthrough rule as `basic`/`none`, restoring the 3.12
+behavior.
+
+
+Passthrough authentication is **denied** only when **all three** of the
+following hold:
1. SSO is configured — `--auth-module-mappings` is non-empty.
2. The enterprise license is valid.
3. The committed role set contains at least one role holding
`COORDINATOR_WRITE`.
-In that case the connection is rejected with:
+In that case the connection is rejected. The message tells `basic`/`none` apart
+from an unlisted scheme, because the remedy differs: the former needs an SSO
+login, while the latter is usually a misspelled or unconfigured scheme.
+
+For `basic`/`none`:
```
Basic authentication is disabled on this coordinator because SSO is configured;
connect with an SSO scheme listed in the auth-module-mappings flag.
```
+For any other scheme not listed in `--auth-module-mappings`:
+
+```
+The "" authentication scheme isn't supported on this coordinator;
+connect with an SSO scheme listed in the auth-module-mappings flag.
+```
+
+The deny rule applies to arbitrary scheme names as well as to `basic`, so a
+client cannot bypass the SSO privilege model by sending a made-up scheme.
+
**Anybody can log in until a `COORDINATOR_WRITE` role exists.** Condition 3 is
what makes coordinator SSO self-bootstrapping. On a coordinator that starts with
`--auth-module-mappings` set but an empty role set, SSO cannot yet grant a
-privileged session to anybody — so basic auth stays open, and you use it to
-create the first role and grant it `COORDINATOR_WRITE`. The moment that grant
-commits to Raft, basic auth closes on the next login attempt and SSO takes over.
-**No coordinator restart is required.**
+privileged session to anybody — so the passthrough stays open, and you use a
+basic-auth connection to create the first role and grant it `COORDINATOR_WRITE`.
+The moment that grant commits to Raft, the passthrough closes on the next login
+attempt and SSO takes over. **No coordinator restart is required.**
The same rule applies in reverse: if you drop or revoke the last
-`COORDINATOR_WRITE` role on a live cluster, basic auth reopens rather than
-leaving the cluster unadministrable.
+`COORDINATOR_WRITE` role on a live cluster, the passthrough reopens rather than
+leaving the cluster unadministrable. Unlisted schemes are admitted during
+break-glass in the same way as `basic`/`none`.
**Break-glass on license loss.** If the enterprise license is missing, expired
-or invalid, SSO rejects every login. Condition 2 above means basic auth falls
-back to the passthrough in exactly that case, so a license transition can never
-lock every Bolt session out of a coordinator. Use that session to re-install the
-license over Bolt:
+or invalid, SSO rejects every login. Condition 2 above means `basic`/`none` and
+unlisted schemes fall back to the passthrough in exactly that case, so a license
+transition can never lock every Bolt session out of a coordinator. Use that
+session to re-install the license over Bolt:
```cypher
SET DATABASE SETTING 'enterprise.license' TO 'License';
@@ -174,7 +206,7 @@ refresh.
### A follower without quorum cannot be logged into
-Both the basic-auth decision and the SSO role check need the **leader's**
+Both the passthrough decision and the SSO role check need the **leader's**
committed role set, and both are **fail-closed**: when the leader cannot be
reached, the login is rejected rather than validated against possibly-stale
local replicated state, which could still list a dropped role or an
@@ -191,14 +223,14 @@ license:
can't be validated. Retry once a leader is elected.
```
-- **Basic/none logins are also rejected.** An unknown role set is not treated as
- "no writable role", so the break-glass path does not open during a transient
- leader outage.
+- **Passthrough logins (`basic`/`none` and unlisted schemes) are also
+ rejected.** An unknown role set is not treated as "no writable role", so the
+ break-glass path does not open during a transient leader outage.
This is intentional and temporary: SSO is unavailable in that window anyway, and
access returns as soon as a leader is elected. If SSO is **not** configured, the
-basic-auth passthrough never contacts the leader, so it keeps working on a
-partitioned follower.
+passthrough never contacts the leader, so it keeps working on a partitioned
+follower.
### SSO authentication
@@ -298,11 +330,10 @@ a session whose roles were revoked mid-flight can still inspect who it is.
- `SHOW CURRENT USER` returns the principal the identity provider authenticated.
It is purely session-local and works even when the leader is unreachable. A
- basic-auth passthrough session authenticated no principal and returns `null`.
+ passthrough session authenticated no principal and returns `null`.
- `SHOW CURRENT ROLE` returns the session's roles **filtered against the
leader's committed role set**, so it stops naming a role that `DROP ROLE`
- already removed. A basic-auth passthrough session has no roles and returns
- `null`.
+ already removed. A passthrough session has no roles and returns `null`.
### Auth queries rejected on coordinators
diff --git a/pages/clustering/high-availability/ha-commands-reference.mdx b/pages/clustering/high-availability/ha-commands-reference.mdx
index 246c0a01a..fbaf192da 100644
--- a/pages/clustering/high-availability/ha-commands-reference.mdx
+++ b/pages/clustering/high-availability/ha-commands-reference.mdx
@@ -910,11 +910,11 @@ SHOW CURRENT ROLE;
- **No privilege and no license are required** — these are self-service queries
that reveal only the session's own identity.
- `SHOW CURRENT USER` returns the principal the SSO module reported. It is
- session-local and works even when the leader is unreachable. A basic-auth
- passthrough session returns `null`.
+ session-local and works even when the leader is unreachable. A passthrough
+ session (basic auth or any scheme not mapped to an SSO module) returns `null`.
- `SHOW CURRENT ROLE` returns the session's roles filtered against the leader's
- committed role set, so a dropped role stops being reported. A basic-auth
- passthrough session has no roles and returns `null`.
+ committed role set, so a dropped role stops being reported. A passthrough
+ session has no roles and returns `null`.
## Error handling
diff --git a/pages/data-streams.mdx b/pages/data-streams.mdx
index 79f60a721..c4ac0b24e 100644
--- a/pages/data-streams.mdx
+++ b/pages/data-streams.mdx
@@ -297,6 +297,11 @@ the following Memgraph flag:
The `INTERVAL_TIME` is measured in `milliseconds` and the default value is
`500ms`.
+The queries a transformation returns for one batch run in one transaction. If
+they include [auth
+queries](/database-management/authentication-and-authorization#run-auth-queries-in-a-transaction),
+they must all be auth queries.
+
## Setting a stream offset
When using a Kafka stream, you can manually set the offset of the next consumed
diff --git a/pages/database-management/authentication-and-authorization.mdx b/pages/database-management/authentication-and-authorization.mdx
index cf799884c..58fb0c021 100644
--- a/pages/database-management/authentication-and-authorization.mdx
+++ b/pages/database-management/authentication-and-authorization.mdx
@@ -120,6 +120,114 @@ and
[Replication](/clustering/replication)
documentation.
+## Run auth queries in a transaction
+
+You can run auth queries, such as `CREATE USER`, `CREATE ROLE`, `GRANT`, and
+`SET ROLE`, in an [explicit
+transaction](/fundamentals/transactions#explicit-transactions). Other sessions
+and REPLICAs see none of the changes until `COMMIT`, and then all of them, so a
+user never exists without the privileges you meant to give it. Available since
+Memgraph 3.14.
+
+```cypher
+BEGIN;
+CREATE USER alice IDENTIFIED BY 'alice_password';
+CREATE ROLE analyst;
+GRANT MATCH TO analyst;
+SET ROLE FOR alice TO analyst;
+COMMIT;
+```
+
+This example uses roles, which need [Memgraph
+Enterprise](/database-management/enabling-memgraph-enterprise). The
+transaction itself works in Memgraph Community too.
+
+Auth transactions are serializable. Each query sees the changes made by the
+queries before it. Memgraph snapshots each user or role the first time the
+transaction reads the user or role, and later reads return that snapshot. If
+anything the transaction has read changes before `COMMIT`, the commit fails.
+`ROLLBACK` discards all of the transaction's changes. After this transaction,
+neither `bob` nor `dave` exists:
+
+```cypher
+BEGIN;
+CREATE USER bob;
+CREATE USER dave;
+ROLLBACK;
+```
+
+A single auth query outside a transaction works as before.
+
+### What an auth transaction can contain
+
+The first query inside a transaction decides the kind of transaction, either
+data or auth. After an auth query, the transaction accepts only auth queries,
+including the `SHOW` queries that read users, roles, and privileges. After any
+other query type, it refuses auth queries: auth and data queries can't be mixed
+within a single transaction.
+
+```cypher invalid
+BEGIN;
+CREATE USER carol;
+MATCH (n) RETURN count(n);
+```
+
+Either way, the refused query fails with this error:
+
+```plaintext copy=false
+A transaction that runs an auth query can contain only auth queries. Run other queries in a separate transaction.
+```
+
+The other queries refused in an auth transaction include `SHOW TRANSACTIONS`,
+replication queries, and [user profile](/database-management/authentication-and-authorization/user-profiles)
+queries.
+
+User and [tenant profile](/database-management/tenant-profiles) changes are
+refused in every explicit transaction. Run them as single queries.
+
+### Conflicts between transactions
+
+Memgraph doesn't lock users or roles while a transaction is open. At `COMMIT`,
+it checks whether another session has changed anything the transaction read or
+wrote. If it has, nothing is applied and the commit fails with this error:
+
+```plaintext copy=false
+Cannot resolve conflicting transactions. Retry this transaction when the conflicting transaction is finished.
+```
+
+The error is transient, so a driver's retry logic runs the transaction again
+(see [conflicting transactions](/help-center/errors/transactions#conflicting-transactions)).
+Two transactions that change different users or roles both commit.
+
+Some queries read more than their names suggest, so they conflict more often:
+
+- **Listing queries,** such as `SHOW USERS`: the transaction depends on the
+ whole list, so the commit fails if another session created or dropped a user
+ in the meantime.
+- **`DROP ROLE`:** checks every user's roles, so it conflicts with any role
+ assignment in another session, even of a different role.
+- **The first user:** on an instance with no users, two transactions that each
+ create a user conflict. With an Enterprise license, creating the first user
+ also creates the built-in roles, so it conflicts with a concurrent
+ `CREATE ROLE`.
+
+The commit also fails with this error if another session holds the system
+lock for more than 100 ms, for example while it runs a replication or
+multi-tenancy query.
+
+A transaction that grants, denies, or revokes a database, or sets a user's main
+database, fails at `COMMIT` if another session dropped that database in the
+meantime:
+
+```plaintext copy=false
+Tried to retrieve an unknown database "sales".
+```
+
+### Auth transactions and replication
+
+A REPLICA applies a committed auth transaction whole, so it never holds some of
+the transaction's changes without the rest.
+
## [Users](/database-management/authentication-and-authorization/users)
Learn how to manage users in Memgraph.
diff --git a/pages/database-management/authentication-and-authorization/query-privileges.mdx b/pages/database-management/authentication-and-authorization/query-privileges.mdx
index 053000261..6331efc61 100644
--- a/pages/database-management/authentication-and-authorization/query-privileges.mdx
+++ b/pages/database-management/authentication-and-authorization/query-privileges.mdx
@@ -147,6 +147,8 @@ Memgraph's privilege system controls access to various database operations throu
| `SHOW TRANSACTIONS` | `TRANSACTION_MANAGEMENT` | `SHOW TRANSACTIONS` |
| `TERMINATE TRANSACTIONS` | `TRANSACTION_MANAGEMENT` | `TERMINATE TRANSACTIONS 'transaction_id'` |
| `TERMINATE TRANSACTIONS "*"` | `TRANSACTION_MANAGEMENT` | `TERMINATE TRANSACTIONS "*"` terminates every transaction the user may terminate. Without the privilege, only the user's own transactions are terminated. |
+| `SHOW SESSIONS` | **None** | `SHOW SESSIONS` lists the user's own sessions. With the `TRANSACTION_MANAGEMENT` privilege, it also lists other users' sessions on the databases where the user has the privilege. |
+| `TERMINATE SESSIONS` | **None** | `TERMINATE SESSIONS 'session_id'` terminates the user's own other sessions. Terminating another user's session needs the `TRANSACTION_MANAGEMENT` privilege on the database that session is using. |
| `RELOAD BOLT_SERVER TLS` | `RELOAD_TLS` | `RELOAD BOLT_SERVER TLS` |
| `RELOAD INTRA_CLUSTER TLS` | `RELOAD_TLS` | `RELOAD INTRA_CLUSTER TLS` |
diff --git a/pages/database-management/authentication-and-authorization/user-profiles.mdx b/pages/database-management/authentication-and-authorization/user-profiles.mdx
index 665809853..a4eca3829 100644
--- a/pages/database-management/authentication-and-authorization/user-profiles.mdx
+++ b/pages/database-management/authentication-and-authorization/user-profiles.mdx
@@ -136,6 +136,7 @@ The system provides clear error messages for invalid operations:
- **Non-existent profile operations**: Error when trying to show, update, or drop non-existent profiles
- **Invalid limit values**: Error for negative numbers or invalid memory units
- **Invalid limit names**: Error for unsupported limit types
+- **Profile changes in an explicit transaction**: `CREATE PROFILE`, `UPDATE PROFILE`, `DROP PROFILE`, `SET PROFILE`, and `CLEAR PROFILE` fail inside an [explicit transaction](/fundamentals/transactions#explicit-transactions) with an error starting `Managing user profiles is not allowed in multicommand transactions.` Run them as single queries. Before Memgraph 3.14, they were applied immediately, even if the transaction was rolled back.
**Note**: Assigning a profile to a non-existent user will **not** cause an error. The assignment will be stored and applied when the user connects to the database.
diff --git a/pages/database-management/logs.mdx b/pages/database-management/logs.mdx
index b5b266075..07ea428a0 100644
--- a/pages/database-management/logs.mdx
+++ b/pages/database-management/logs.mdx
@@ -129,6 +129,12 @@ RESET SESSION SETTING "log.min_duration_ms";
`SET SESSION SETTING` accepts only the three keys above; any other key raises
`Setting "" cannot be set per-session`.
+A session value lasts for the client connection. A failed query or a rollback
+keeps it, including the Bolt `RESET` a driver sends after a failure. Switching
+to another user on the same connection (the driver re-authenticates, sending
+`LOGOFF` and `LOGON`) clears it, and the global value applies again. The new
+user can set their own.
+
## Access logs
If you installed Memgraph with Linux, logs can be found in the
diff --git a/pages/database-management/monitoring.mdx b/pages/database-management/monitoring.mdx
index 1ea5dfe9c..f513722c9 100644
--- a/pages/database-management/monitoring.mdx
+++ b/pages/database-management/monitoring.mdx
@@ -772,6 +772,10 @@ The command returns the unique UUID of the session being traced:
Use this UUID to find the session's events in the log. From this point on, every query executed in the session emits
tagged trace events until tracing is turned off.
+Tracing lasts for the client connection. A failed query or a rollback keeps it on, including the Bolt `RESET` a driver
+sends after a failure. Switching to another user on the same connection (the driver re-authenticates, sending `LOGOFF`
+and `LOGON`) turns it off. The new user can turn it on again and gets the same session UUID.
+
#### Reading the trace
Each trace event is a line in the main log prefixed with the session tag, the user (when authenticated) and the current
diff --git a/pages/database-management/multi-tenancy.mdx b/pages/database-management/multi-tenancy.mdx
index 3099a932f..26f3fd839 100644
--- a/pages/database-management/multi-tenancy.mdx
+++ b/pages/database-management/multi-tenancy.mdx
@@ -152,10 +152,16 @@ Users interact with multi-tenant features through specialized Cypher queries:
database is currently in use. You can also use `SHOW CURRENT DATABASE` for
the same functionality. This command does not require any special privileges.
5. `SHOW DATABASES`: Shows only the existing set of multitenant databases. The
- result includes a `Name` column, a `State` column (`HOT`/`COLD`) and a
- `Health` column (`ready`/`broken`). `HOT` databases are resident in memory while `COLD` are suspended, but still listed. A database is reported as `broken` when it
+ result includes a `Name` column, a `State` column (`HOT`/`COLD`/`DROPPING`) and a
+ `Health` column (`ready`/`broken`/`draining`). `HOT` databases are resident in memory while `COLD` are suspended, but still listed. A database is reported as `broken` when it
failed durability recovery and came up empty — see
[recovery failure handling](/fundamentals/data-durability#recovery-failure-handling).
+
+ A `HOT` database dropped with `FORCE` stays listed as `DROPPING`/`draining`
+ until its teardown finishes. The row is named ` ()` if a live
+ database or another draining copy has the same name. Users without access
+ to all databases don't see it; see
+ [DROP DATABASE with FORCE](#drop-database-with-force).
6. `USE DATABASE name`: Switches focus to a specific database (disabled during
transactions).
7. `GRANT DATABASE name TO user_or_role`: Grants a user or role access to a
@@ -210,6 +216,35 @@ DROP DATABASE database_name [FORCE];
but actual deletion is deferred until it's safe to proceed. All active
transactions using the database will be terminated.
+{ Sessions on a dropped database
}
+
+An idle connection that still has the database selected, such as a pooled
+driver connection, would keep the database from being deleted. So Memgraph
+closes such sessions the next time the drop worker runs, within about 10
+seconds. On a REPLICA, this happens when the drop replicates from MAIN, with or
+without `FORCE`. What a session sees depends on what it's doing:
+
+- **Idle:** Memgraph closes the connection. The client's next request fails
+ with a connection error, unless the driver's retry helpers rerun it on a new
+ connection. A client that names the dropped database when it connects then
+ gets `Tried to retrieve an unknown database "".` A session that had
+ switched with `USE DATABASE` reconnects on its default database.
+ request. A client that names the dropped database when it connects then
+ gets an error that the database doesn't exist.
+- **Sends a query before it's closed:** if the client named the database when
+ it connected, the query fails with `Tried to retrieve an unknown database "".`
+ and the connection is closed on the next pass. A session that selected the
+ database with `USE DATABASE` keeps its connection but has no database. Its
+ queries fail with a `Database required for ...` error until it runs
+ `USE DATABASE` with another database.
+- **In an open transaction:** on MAIN, the drop terminates the transaction.
+ Its commit fails with a transient error, or with a connection error once the
+ session is closed. On a REPLICA, the transaction runs to completion, and the
+ session is closed on a later pass.
+
+Before Memgraph 3.14, an idle connection delayed the database's deletion until
+its client sent another query.
+
{ Use cases for FORCE
}
- **Emergency cleanup**: Remove a database stuck in an inconsistent or
@@ -223,16 +258,71 @@ DROP DATABASE database_name [FORCE];
Using the `FORCE` option requires:
- `MULTI_DATABASE_EDIT` privilege
-- Access to the `memgraph` database
+- Access to the database being dropped
- `TRANSACTION_MANAGEMENT` privilege (to terminate active transactions)
{ Important considerations
}
- All active transactions on the target database will be forcibly terminated.
+ A query module procedure that doesn't check for abort runs until it returns;
+ its commit then fails.
- The database becomes immediately unavailable to new connections.
-- Actual deletion may be deferred until existing connections are properly closed.
+- Actual deletion waits until nothing holds the database. Memgraph closes
+ idle sessions on it; see [sessions on a dropped
+ database](#sessions-on-a-dropped-database).
- **This operation cannot be undone.**
+{ After a forced drop
}
+
+A forced drop returns immediately. A `COLD` database is removed at once. A `HOT`
+database is torn down in the background. Until that finishes,
+`SHOW DATABASES` lists it with State `DROPPING` and Health `draining`.
+
+A worker that runs every 10 seconds removes it once nothing holds it. So even a
+`HOT` database nothing uses stays listed for up to about 10 seconds.
+
+While the database is listed as `DROPPING`:
+
+- `DROP DATABASE x`, with or without `FORCE`, fails with
+ `Database x is currently being dropped.` It's a client error, so drivers
+ don't retry it. Once the row is gone, it fails with a `does not exist` error.
+- `USE DATABASE x` fails with an `unknown database` error.
+- `CREATE DATABASE x` creates a new, empty database. A later
+ `DROP DATABASE x` drops this new database.
+- A procedure that doesn't check for abort keeps the database listed, but
+ `SHOW TRANSACTIONS` no longer shows it. When it returns, its commit fails with
+ `Aborting transaction commit because the transaction was requested to stop from other session.`
+- A running `AFTER COMMIT` trigger on the database is asked to abort too.
+ The database stays listed until the trigger stops.
+
+A database still held after 5 minutes gets a server-log warning that names it,
+starting `Deferred teardown has been pending for over 5 minutes`.
+The usual cause is a procedure that doesn't check for abort.
+
+The draining row is named ` ()`, with the dropped database's
+UUID, if a live database or another draining copy has the same name:
+
+```cypher
+DROP DATABASE x FORCE;
+CREATE DATABASE x;
+SHOW DATABASES;
+```
+
+```plaintext copy=false
++--------------------------------------------+------------+------------+
+| Name | State | Health |
++--------------------------------------------+------------+------------+
+| "memgraph" | "HOT" | "ready" |
+| "x" | "HOT" | "ready" |
+| "x (3beec4b5-1d20-4a9b-99c3-5af905d871c2)" | "DROPPING" | "draining" |
++--------------------------------------------+------------+------------+
+```
+
+Only users with access to all databases see a draining row; with auth
+disabled, every session does. The drop removes the database from every user's
+and role's grants and denies. Users and roles that had it by name must be
+granted the recreated database again.
+
### RENAME DATABASE
The `RENAME DATABASE` command allows you to rename an existing database to a new
@@ -427,7 +517,9 @@ granted.
-User-role mappings are simple maps located in the user. Deleting or renaming the database will not update this information. The admin needs to make sure the correct access is maintained at all times.
+User-role mappings are simple maps located in the user. Renaming the database will not update this information. The admin needs to make sure the correct access is maintained at all times.
+
+`DROP DATABASE` removes the database from every user's and role's grants and denies. A database recreated under the same name must be granted or denied again to users and roles that had it by name.
@@ -585,6 +677,8 @@ with driver.session(database="db2") as session:
session.run("USE DATABASE db1") # Error: database switching disabled
```
+The database selected with `USE DATABASE` stays selected for the client connection. A failed query or a rollback keeps it, including the Bolt `RESET` a driver sends after a failure. Switching to another user on the same connection (the driver re-authenticates, sending `LOGOFF` and `LOGON`) clears it. Queries then run on the database the new session requests, or on the new user's [main database](#users-main-database).
+
### Audit Logs
Audit logs now encompass the active database name, positioned immediately after
diff --git a/pages/database-management/server-stats.mdx b/pages/database-management/server-stats.mdx
index c3ffc364f..6907ff456 100644
--- a/pages/database-management/server-stats.mdx
+++ b/pages/database-management/server-stats.mdx
@@ -81,6 +81,7 @@ process (across all databases):
| memory_res | The non-swapped physical RAM memory (RSS) used by the whole Memgraph process, as reported by the OS (in B, KiB, MiB, GiB or TiB). |
| peak_memory_res | Peak RAM memory (RSS) usage of the Memgraph process during the whole run. |
| disk_usage | The amount of disk space used by the data directory ([`--data-directory`](/database-management/configuration), in B, KiB, MiB, GiB or TiB). |
+| disk_available | The amount of disk space still available on the filesystem that holds the data directory ([`--data-directory`](/database-management/configuration), in B, KiB, MiB, GiB or TiB). Reads `"unknown"` if the filesystem cannot be queried. |
| memory_tracked | The total amount of RAM allocated across the instance and tracked by Memgraph (in B, KiB, MiB, GiB or TiB).
For more info, check out [memory control](/fundamentals/storage-memory-usage). |
| memory_limit | The active instance-level runtime memory limit, taken from the [`--memory-limit`](/database-management/configuration) flag (in B, KiB, MiB, GiB or TiB). |
| license_memory_limit | The memory limit imposed by the active license, or `"unlimited"` if no license is active. With an `enterprise` license this gates total tracked memory; with an `ai_platform` license it gates only graph and query memory. See [License types](/database-management/enabling-memgraph-enterprise#license-types). |
@@ -118,7 +119,7 @@ only exist for a running database — `query_memory_tracked`,
`tenant_memory_limit` reads `unlimited`.
Process-wide fields such as `vm_max_map_count`, `memory_res`, `peak_memory_res`,
-`memory_tracked` and the license/runtime memory limits are **not** included —
+`disk_available`, `memory_tracked` and the license/runtime memory limits are **not** included —
those are only available from the unscoped `SHOW STORAGE INFO`.
| Field | Description |
@@ -366,7 +367,7 @@ SHOW BUILD INFO;
## Active users information
-Running this query will provide information about currently logged-in Memgraph users and their session details.
+Running this query lists one row per open session, including the session that runs it:
```cypher
SHOW ACTIVE USERS INFO;
@@ -378,8 +379,23 @@ Identical information can be obtained by executing the following command:
SHOW ACTIVE USERS;
```
+| Column | Description |
+|------------------------------|-----------------------------------------------------------------------------------------------------|
+| username | The session's logged-in user, also when the session impersonates another user; an empty string for a session that connected while authentication was disabled. |
+| session uuid | The session's unique ID. |
+| login timestamp | The time of the session's most recent login, as a string with microseconds. When another user logs in on the same connection, this time and the `username` change and the `session uuid` stays the same. |
+| database | The session's current database: the one selected with `USE DATABASE`, the one named when connecting, or otherwise the user's main database (`memgraph` by default). |
+| database marked for deletion | `true` if the session's database was dropped with [`FORCE`](/database-management/multi-tenancy#drop-database-with-force), otherwise `false`. Such sessions, including ones with an open transaction, are closed within about 10 seconds. |
+
+For example, after a second session runs `USE DATABASE analytics` and `analytics` is then dropped with `DROP DATABASE analytics FORCE`:
+
+```cypher
+SHOW ACTIVE USERS INFO;
+```
+
```copy=false
-| username | session uuid | login timestamp |
-|----------|----------------------------------------|-----------------------|
-| test | "550e8400-e29b-41d4-a716-446655440000" | "2024-08-03 18:53:00" |
+| username | session uuid | login timestamp | database | database marked for deletion |
+|-----------|----------------------------------------|------------------------------|-------------|------------------------------|
+| "admin_u" | "f1082c78-82ff-4b87-a5c4-340060a04a02" | "2026-10-19 12:02:10.248993" | "analytics" | true |
+| "admin_u" | "bf7a6fbd-64cc-475c-a82c-5c293dd8108d" | "2026-10-19 12:02:10.065680" | "memgraph" | false |
```
diff --git a/pages/database-management/system-configuration.mdx b/pages/database-management/system-configuration.mdx
index b2fd2d7fe..f6865ab14 100644
--- a/pages/database-management/system-configuration.mdx
+++ b/pages/database-management/system-configuration.mdx
@@ -17,32 +17,118 @@ The steps may vary slightly depending on the Linux distribution you are using.
## Handle numerous open files simultaneously
-The `fs.file-max` parameter in Linux sets the maximum number of file handles
-that the Linux kernel will allocate. This adjustment is important when the
-system needs to handle numerous open files simultaneously. To adjust this
-parameter, do the following:
+Every client connection, every database, every text index and every replica
+connection consumes file descriptors. When a process runs out of them, it fails
+with one of two errors:
+
+- `Too many open files` (`EMFILE`): the **per-process** limit
+ (`RLIMIT_NOFILE`) has been reached. This is the one you will normally hit.
+- `Too many open files in system` (`ENFILE`): the **system-wide** limit
+ (`fs.file-max`) has been reached.
+
+When Memgraph reaches the per-process limit, new client connections fail at the
+Bolt handshake, and the Memgraph log shows:
+
+```plaintext
+Bolt accept failed (Too many open files); retrying every 500ms, further occurrences logged at most every 10s
+```
+
+Connections succeed again once other connections close.
+
+Starting with v3.14, Memgraph raises its soft limit to the hard limit
+at startup. It never changes the hard limit, so only the hard limit matters.
+With a hard limit of `1024`, Memgraph starts failing at roughly 950 client
+connections. The Memgraph package sets `LimitNOFILE=1048576` in its systemd
+unit, so both limits are `1048576`.
+
+If the limit after the raise is below `65536`, Memgraph logs a warning at
+startup. The warning begins with `Open files limit is too low (soft N, hard N)`.
+Both numbers are the hard limit, so raise the hard limit. Memgraph still starts
+and serves queries.
+
+A custom unit file or container setup with a high hard limit needs no action. A
+systemd unit without `LimitNOFILE` gets a soft limit of `1024` and a hard limit
+that depends on the systemd version. If the hard limit is low, or on releases
+before v3.14, follow the steps below.
+
+Memgraph also logs the resulting limits as
+`Open files limit (RLIMIT_NOFILE): soft N, hard N.` at `INFO` level. The default
+log level is `WARNING`, so this line isn't written unless you start Memgraph
+with `--log-level=INFO`. To check the limits Memgraph runs with, read
+`/proc//limits` as shown in the steps below.
+
+### Sizing the limit
+
+Memgraph needs roughly one descriptor per client connection plus files per
+database (WAL, snapshots, indexes); `65536` or more is recommended.
+
+
+`LimitNOFILE` (and `--ulimit nofile`) cannot exceed the kernel's `fs.nr_open`.
+The kernel default is `1048576`, but systemd 240 and later raises it at boot.
+Higher values are rejected (the service or container fails to start). Check the
+current ceiling with `cat /proc/sys/fs/nr_open`.
+
+
+### Linux with systemd
+
+`/etc/security/limits.conf` does **not** apply to systemd services. Set the limit
+in the service unit instead:
-{Open `/etc/sysctl.conf` in a text editor
}
-You need root privileges to
-edit this file, which is used for configuring kernel parameters at runtime.
+{Edit the service
}
+Run `sudo systemctl edit memgraph` and add:
+```ini
+[Service]
+LimitNOFILE=1048576
+```
-{Set the file handle limit
}
-Add the line `fs.file-max=100000` to
-`sysctl.conf`.
+{Reload and restart
}
+Run `sudo systemctl daemon-reload` and `sudo systemctl restart memgraph`.
-{Apply the changes
}
-Save your changes and exit the text editor. Run
-`sudo sysctl -p` to apply the changes immediately without rebooting.
+{Verify the limit
}
+Run the following command. Both the soft and the hard limit should show `1048576`:
-{Verify the changes
}
-Run `sysctl fs.file-max` to check that the new
-value has been applied. It should return `100000`.
+```bash
+grep 'open files' /proc/$(pidof memgraph)/limits
+```
+### Docker
+
+Pass the limit to the container with `--ulimit`:
+
+```bash
+docker run --ulimit nofile=1048576:1048576 -p 7687:7687 memgraph/memgraph
+```
+
+With Docker Compose:
+
+```yaml
+services:
+ memgraph:
+ image: memgraph/memgraph
+ ulimits:
+ nofile:
+ soft: 1048576
+ hard: 1048576
+```
+
+### Kubernetes
+
+Starting with v3.14, Memgraph raises its soft limit to the container's hard limit
+at startup, so only the hard limit counts. If you see the warning in the Memgraph
+log, raise the hard open files limit that containers get on the node.
+
+### System-wide limit
+
+Modern kernels and systemd already set `fs.file-max` very high, so there is
+usually nothing to do. Do not lower it. Only if `cat /proc/sys/fs/file-max`
+returns a low value, raise it by adding `fs.file-max=` to
+`/etc/sysctl.conf` and running `sudo sysctl -p`.
+
## Increasing memory map areas
If users are working on a larger scale, there is a possibility that a
diff --git a/pages/database-management/tenant-profiles.mdx b/pages/database-management/tenant-profiles.mdx
index f49925d6e..9c006aad1 100644
--- a/pages/database-management/tenant-profiles.mdx
+++ b/pages/database-management/tenant-profiles.mdx
@@ -239,6 +239,12 @@ SHOW MEMORY INFO;
## Limitations
- A database can have at most one tenant profile attached at a time.
+- Queries that change a tenant profile or its attachment (`CREATE`, `ALTER`,
+ `SET`, `REMOVE`, and `DROP TENANT PROFILE`) fail inside an [explicit
+ transaction](/fundamentals/transactions#explicit-transactions) with an error starting
+ `Managing tenant profiles is not allowed in multicommand transactions.` Run
+ them as single queries. Before Memgraph 3.14, they were applied immediately,
+ even if the transaction was rolled back.
- Tenant profiles cap memory only — there is no CPU or disk I/O throttling.
Each tenant database has its own durable storage directory.
- The memory tracked by tenant profiles (`tenant_memory_tracked`) is the sum of
diff --git a/pages/database-management/upgrades/specific-versions.mdx b/pages/database-management/upgrades/specific-versions.mdx
index 0bab025d7..7b4f15852 100644
--- a/pages/database-management/upgrades/specific-versions.mdx
+++ b/pages/database-management/upgrades/specific-versions.mdx
@@ -9,6 +9,18 @@ Some Memgraph releases introduce breaking changes or modify internal behavior
in ways that require extra steps during an upgrade. Before upgrading, check
whether your target version is listed below and follow the corresponding guide.
+## v3.14 - Profile changes in transactions
+
+Memgraph 3.14 refuses [user
+profile](/database-management/authentication-and-authorization/user-profiles)
+and [tenant profile](/database-management/tenant-profiles) changes inside an
+explicit transaction. Before Memgraph 3.14, they were applied immediately,
+even if the transaction was rolled back. Move them out of the transaction and
+run them as single queries.
+
+See [Run auth queries in a
+transaction](/database-management/authentication-and-authorization#run-auth-queries-in-a-transaction).
+
## v3.9 - High-availability coordinator migration
Memgraph 3.9 includes a fix for a bug in how durability is handled on
diff --git a/pages/deployment/environments/docker.mdx b/pages/deployment/environments/docker.mdx
index 7f42357a1..fb06f2fd3 100644
--- a/pages/deployment/environments/docker.mdx
+++ b/pages/deployment/environments/docker.mdx
@@ -49,7 +49,9 @@ Lab](/memgraph-lab).
## System configuration
Before running Memgraph, please check the [system configuration guidelines](/database-management/system-configuration), especially the
-`vm.max_map_count` parameter setting.
+`vm.max_map_count` parameter setting. Also make sure the container's [open files
+limit](/database-management/system-configuration#handle-numerous-open-files-simultaneously)
+is high enough, e.g. by running the container with `--ulimit nofile=1048576:1048576`.
## Configure Memgraph
diff --git a/pages/fundamentals/constraints.mdx b/pages/fundamentals/constraints.mdx
index 10e302314..966359a85 100644
--- a/pages/fundamentals/constraints.mdx
+++ b/pages/fundamentals/constraints.mdx
@@ -109,6 +109,11 @@ Trying to modify the database in a way that violates the constraint will yield
an error `Unable to commit due to unique constraint violation on
:Label(property)`.
+A node that doesn't have one of the constrained properties, or whose value
+is a list or map holding `null` (such as `[1, null]`), is never a duplicate. Before
+Memgraph 3.14, the constraint refused a second node whose list or map held
+`null`.
+
Constraints are dropped using the `DROP` clause:
```cypher
diff --git a/pages/fundamentals/data-durability.mdx b/pages/fundamentals/data-durability.mdx
index 899434294..848592a86 100644
--- a/pages/fundamentals/data-durability.mdx
+++ b/pages/fundamentals/data-durability.mdx
@@ -420,6 +420,11 @@ Two operator-facing queries report each database's health as `ready` or
+------------+-------+---------+
```
+ `Health` is `draining` while a database is still being
+ [dropped with `FORCE`](/database-management/multi-tenancy#drop-database-with-force).
+ On a replica, this also happens for a database dropped without `FORCE` on
+ the main instance.
+
- [`SHOW STORAGE INFO ON CURRENT DATABASE`](/database-management/server-stats#per-database-storage-information)
(and `ON DATABASE `) includes a `health` row with the value `ready` or
`broken`.
diff --git a/pages/fundamentals/data-types.mdx b/pages/fundamentals/data-types.mdx
index 91e4658d4..3d4520caf 100644
--- a/pages/fundamentals/data-types.mdx
+++ b/pages/fundamentals/data-types.mdx
@@ -773,6 +773,9 @@ CREATE (:Machine {status: Status::Okay});
MATCH (n:Machine) WHERE n.status = Status::Bad RETURN n;
```
+Enum values compare with `=` and `<>`, but they have no order, so `<`,
+`<=`, `>` and `>=` return `null`.
+
**Strings**
The `ToEnum()` function takes string(s) to lookup and return enum value.
diff --git a/pages/fundamentals/storage-memory-usage.mdx b/pages/fundamentals/storage-memory-usage.mdx
index 4210281e8..c9eff4ad7 100644
--- a/pages/fundamentals/storage-memory-usage.mdx
+++ b/pages/fundamentals/storage-memory-usage.mdx
@@ -806,6 +806,7 @@ SHOW STORAGE INFO;
| "memory_res" | "43.16MiB" |
| "peak_memory_res" | "43.16MiB" |
| "disk_usage" | "104.46KiB" |
+| "disk_available" | "212.37GiB" |
| "memory_tracked" | "8.52MiB" |
| "memory_limit" | "58.55GiB" |
| "license_memory_limit" | "unlimited" |
diff --git a/pages/fundamentals/transactions.mdx b/pages/fundamentals/transactions.mdx
index 98c93a0dd..9686abeaf 100644
--- a/pages/fundamentals/transactions.mdx
+++ b/pages/fundamentals/transactions.mdx
@@ -44,6 +44,11 @@ of the final query.
Commit successful transactions by executing the `COMMIT;` query.
Roll back unsuccessful transactions by executing the `ROLLBACK;` query.
+Auth queries, such as `CREATE USER` and `GRANT`, can also run in an explicit
+transaction, but not in the same transaction as other queries. See [Run auth
+queries in a
+transaction](/database-management/authentication-and-authorization#run-auth-queries-in-a-transaction).
+
The following query will find to nodes and adjust a certain relationship
property in order to check the results of a
[max_flow](/advanced-algorithms/available-algorithms/max_flow) algorithm, then
@@ -92,7 +97,7 @@ SHOW TRANSACTIONS;
```
Each row in the result represents one transaction (or an in-progress snapshot
-creation or garbage collection) and contains seven columns:
+creation or garbage collection) and contains eight columns:
| Column | Type | Description |
|---|---|---|
@@ -103,15 +108,18 @@ creation or garbage collection) and contains seven columns:
| `metadata` | `Map` | Metadata supplied by the client when the transaction was opened. For in-progress snapshots and garbage collection it contains progress details (see below). |
| `start_time` | `ZonedDateTime` | UTC time at which the transaction started. |
| `elapsed_ms` | `Integer` | How long the transaction has been running, in milliseconds. |
-
-```copy=false
-memgraph> SHOW TRANSACTIONS;
-+----------+-----------------------+-----------------------------------------------+--------------+----------+-------------------------------+------------+
-| username | transaction_id | query | status | metadata | start_time | elapsed_ms |
-+----------+-----------------------+-----------------------------------------------+--------------+----------+-------------------------------+------------+
-| "" | "9223372036854794885" | ["UNWIND range(1,100) AS i CREATE(:L{p:i});"] | "committing" | {} | 2026-05-12T14:32:18.412Z[UTC] | 47 |
-| "" | "9223372036854794896" | ["SHOW TRANSACTIONS"] | "running" | {} | 2026-05-12T14:32:18.451Z[UTC] | 8 |
-+----------+-----------------------+-----------------------------------------------+--------------+----------+-------------------------------+------------+
+| `database` | `String` | The database the listed transaction is running on, not the database of the session that runs `SHOW TRANSACTIONS`. In Memgraph Community it's always `memgraph`. |
+
+In the `query` column, a query that sets a password, such as `CREATE USER`
+with `IDENTIFIED BY` or `SET PASSWORD`, shows each password as `'****'`.
+
+```plaintext copy=false
++----------+-----------------------+------------------------------------------------------+-----------+----------+----------------------------------------+------------+------------+
+| username | transaction_id | query | status | metadata | start_time | elapsed_ms | database |
++----------+-----------------------+------------------------------------------------------+-----------+----------+----------------------------------------+------------+------------+
+| "" | "9223372036854775816" | ["SHOW TRANSACTIONS"] | "running" | {} | 2026-10-12 12:13:47.209533000[Etc/UTC] | 0 | "memgraph" |
+| "" | "9223372036854775815" | ["UNWIND range(1, 100) AS i CREATE (:Item {id: i})"] | "running" | {} | 2026-10-12 12:13:47.116640000[Etc/UTC] | 93 | "memgraph" |
++----------+-----------------------+------------------------------------------------------+-----------+----------+----------------------------------------+------------+------------+
```
#### Filter by status
@@ -139,21 +147,22 @@ rows contains:
| Key | Description |
|---|---|
-| `phase` | Current phase of snapshot creation: `EDGES`, `VERTICES`, `INDICES`, `CONSTRAINTS`, or `FINALIZING`. |
+| `phase` | Current phase of snapshot creation: `edges`, `vertices`, `indices`, `constraints`, or `finalizing`; `idle` until the first phase starts. |
| `items_done` | Number of objects serialized in the current phase so far. |
| `items_total` | Total number of objects expected in the current phase. |
-| `db_name` | Name of the database whose snapshot is being created. |
+| `db_name` | Name of the database whose snapshot is being created. The `database` column shows the same value. |
The top-level `start_time` and `elapsed_ms` columns are populated for snapshot
rows as well, reflecting when the snapshot started.
-```copy=false
-memgraph> SHOW TRANSACTIONS;
-+----------+----------------+---------------------+-----------+------------------------------------------------------------------+-------------------------------+------------+
-| username | transaction_id | query | status | metadata | start_time | elapsed_ms |
-+----------+----------------+---------------------+-----------+------------------------------------------------------------------+-------------------------------+------------+
-| "" | "snapshot" | ["CREATE SNAPSHOT"] | "running" | {phase: "VERTICES", items_done: 142000, items_total: 500000, ... | 2026-05-12T14:32:17.205Z[UTC] | 1247 |
-+----------+----------------+---------------------+-----------+------------------------------------------------------------------+-------------------------------+------------+
+```plaintext copy=false
++----------+-----------------------+-----------------------+-----------+-----------------------------------------------------------------------------------+----------------------------------------+------------+------------+
+| username | transaction_id | query | status | metadata | start_time | elapsed_ms | database |
++----------+-----------------------+-----------------------+-----------+-----------------------------------------------------------------------------------+----------------------------------------+------------+------------+
+| "" | "9223372036854775818" | ["SHOW TRANSACTIONS"] | "running" | {} | 2026-10-12 12:13:47.275859000[Etc/UTC] | 0 | "memgraph" |
+| "" | "9223372036854775817" | ["CREATE SNAPSHOT"] | "running" | {} | 2026-10-12 12:13:47.221765000[Etc/UTC] | 54 | "memgraph" |
+| "" | "snapshot" | ["CREATE SNAPSHOT"] | "running" | {db_name: "memgraph", items_done: 53824, items_total: 3000100, phase: "vertices"} | 2026-10-12 12:13:47.221848000[Etc/UTC] | 55 | "memgraph" |
++----------+-----------------------+-----------------------+-----------+-----------------------------------------------------------------------------------+----------------------------------------+------------+------------+
```
@@ -181,19 +190,31 @@ map for these rows contains:
| `phase` | Current phase of the cycle: `unlink` (detach obsolete versions no active transaction can still see), `index_cleanup` (remove stale index and constraint entries — usually the most expensive phase), or `delete` (free the unlinked versions). |
| `trigger` | `periodic` for the background cycle, or `forced` for a `FREE MEMORY` query or a storage-mode switch. |
| `exclusive_lock` | `true` when the collection holds the storage lock exclusively and is therefore blocking all transactions. |
-| `db_name` | Name of the database whose garbage is being collected. |
+| `db_name` | Name of the database whose garbage is being collected. The `database` column shows the same value. |
The top-level `start_time` and `elapsed_ms` columns reflect when the collection
started; a large `elapsed_ms` is the clearest signal of a stuck collection. One
`gc` row appears per database that currently has a collection running.
-```copy=false
-memgraph> SHOW TRANSACTIONS;
-+----------+----------------+------------------------+-----------+-------------------------------------------------------------------------------------------+-------------------------------+------------+
-| username | transaction_id | query | status | metadata | start_time | elapsed_ms |
-+----------+----------------+------------------------+-----------+-------------------------------------------------------------------------------------------+-------------------------------+------------+
-| "" | "gc" | ["GARBAGE COLLECTION"] | "running" | {db_name: "memgraph", exclusive_lock: true, phase: "index_cleanup", trigger: "periodic"} | 2026-05-12T14:32:17.881Z[UTC] | 395 |
-+----------+----------------+------------------------+-----------+-------------------------------------------------------------------------------------------+-------------------------------+------------+
+For example, a `FREE MEMORY` query on the `tenant1` database starts a forced
+collection. While that collection runs, run the following query from another
+session:
+
+```cypher
+SHOW TRANSACTIONS;
+```
+
+The result lists the `FREE MEMORY` transaction and its `gc` row, both on
+`tenant1`:
+
+```plaintext copy=false
++----------+-----------------------+------------------------+-----------+---------------------------------------------------------------------------------+----------------------------------------+------------+------------+
+| username | transaction_id | query | status | metadata | start_time | elapsed_ms | database |
++----------+-----------------------+------------------------+-----------+---------------------------------------------------------------------------------+----------------------------------------+------------+------------+
+| "" | "9223372036854775814" | ["FREE MEMORY"] | "running" | {} | 2026-10-19 12:01:18.288170000[Etc/UTC] | 4 | "tenant1" |
+| "" | "9223372036854775815" | ["SHOW TRANSACTIONS"] | "running" | {} | 2026-10-19 12:01:18.292917000[Etc/UTC] | 0 | "memgraph" |
+| "" | "gc" | ["GARBAGE COLLECTION"] | "running" | {db_name: "tenant1", exclusive_lock: false, phase: "unlink", trigger: "forced"} | 2026-10-19 12:01:18.288188000[Etc/UTC] | 5 | "tenant1" |
++----------+-----------------------+------------------------+-----------+---------------------------------------------------------------------------------+----------------------------------------+------------+------------+
```
@@ -233,6 +254,12 @@ using the following query:
REVOKE TRANSACTION_MANAGEMENT FROM user;
```
+An [auth
+transaction](/database-management/authentication-and-authorization#run-auth-queries-in-a-transaction)
+belongs to its session's current database. A user with the
+`TRANSACTION_MANAGEMENT` privilege on that database can see its queries and
+terminate it without the `AUTH` privilege.
+
When Memgraph is first started there is only one explicit
super-admin user that has all the privileges, including the
@@ -337,8 +364,8 @@ following the same rules as the id list form:
The wildcard must be the only argument. Mixing it with ids, such as
`TERMINATE TRANSACTIONS "*", "9223372036854794885"`, raises an error.
-A parameterized id is treated exactly like a literal one, so running
-`TERMINATE TRANSACTIONS $id` with `$id = "*"` also terminates everything.
+Transaction ids, including `"*"`, must be string literals. Query parameters
+such as `$id` aren't supported and cause a syntax error.
System transactions (for example an in-flight `CREATE DATABASE` or `GRANT`) are
@@ -401,44 +428,184 @@ Managing transactions is done by establishing a new connection to the database.
**Show and terminate transactions**
The output of the `SHOW TRANSACTIONS` command shows that a query is
-currently being run as part of the transaction ID "9223372036854794885".
+being run on the `memgraph` database as part of the transaction ID
+"9223372036854775809".
```copy=false
memgraph> SHOW TRANSACTIONS;
-+----------+-----------------------+-------------------------------------------+-----------+----------+-------------------------------+------------+
-| username | transaction_id | query | status | metadata | start_time | elapsed_ms |
-+----------+-----------------------+-------------------------------------------+-----------+----------+-------------------------------+------------+
-| "" | "9223372036854794885" | ["CALL infinite.get() YIELD * RETURN *;"] | "running" | {} | 2026-05-12T14:32:00.000Z[UTC] | 18230 |
-| "" | "9223372036854794896" | ["SHOW TRANSACTIONS"] | "running" | {} | 2026-05-12T14:32:18.230Z[UTC] | 0 |
-+----------+-----------------------+-------------------------------------------+-----------+----------+-------------------------------+------------+
++----------+-----------------------+-------------------------------------------------------------------------------------------+-----------+----------+----------------------------------------+------------+------------+
+| username | transaction_id | query | status | metadata | start_time | elapsed_ms | database |
++----------+-----------------------+-------------------------------------------------------------------------------------------+-----------+----------+----------------------------------------+------------+------------+
+| "" | "9223372036854775810" | ["SHOW TRANSACTIONS"] | "running" | {} | 2026-10-19 12:11:16.345268000[Etc/UTC] | 0 | "memgraph" |
+| "" | "9223372036854775809" | ["UNWIND range(1, 100000) AS a UNWIND range(1, 100000) AS b WITH a WHERE a < 0 RETURN a"] | "running" | {} | 2026-10-19 12:11:14.342553000[Etc/UTC] | 2002 | "memgraph" |
++----------+-----------------------+-------------------------------------------------------------------------------------------+-----------+----------+----------------------------------------+------------+------------+
```
To terminate the transaction, run the following query:
```cypher
-TERMINATE TRANSACTIONS "9223372036854794885";
+TERMINATE TRANSACTIONS "9223372036854775809";
```
Upon the transaction termination, the following confirmation will appear:
```copy=false
-memgraph> TERMINATE TRANSACTIONS "9223372036854794885";
+memgraph> TERMINATE TRANSACTIONS "9223372036854775809";
+-----------------------+-----------------------+
| transaction_id | killed |
+-----------------------+-----------------------+
-| "9223372036854794885" | true |
+| "9223372036854775809" | true |
+-----------------------+-----------------------+
1 row in set (round trip in 0.000 sec)
```
-The following message will appear in the session in which the infinite query was being run:
+The following message will appear in the session in which the long-running query was being run:
```copy=false
-memgraph> CALL infinite.get() YIELD * RETURN *;
-Client received exception: Transactions was asked to abort either because it was executing longer than time
-specified or another user asked to abort it.
+memgraph> UNWIND range(1, 100000) AS a UNWIND range(1, 100000) AS b WITH a WHERE a < 0 RETURN a;
+Client received query exception: Transaction was asked to abort by another user.
+```
+
+## Manage sessions
+
+A session is one client connection to Memgraph over Bolt. You can list the
+open sessions and close any of them from another connection, for example an
+idle (often pooled) connection that still has a database selected and makes a
+[`DROP DATABASE`](/database-management/multi-tenancy#drop-database-with-force)
+query without `FORCE` fail with the `Cannot delete , it is currently
+being used.` error. With `FORCE`, Memgraph closes such sessions itself.
+Available since Memgraph 3.14.
+
+Session queries run on data instances (MAIN and REPLICA), and each instance
+lists and closes only the sessions connected to it. On a high availability
+coordinator they fail with the `Coordinator can run only coordinator queries!`
+error. Run them on their own, not inside an [explicit
+transaction](#explicit-transactions) (one you start with `BEGIN`), where they
+raise an error.
+
+### Show sessions
+
+To list the open sessions, run:
+
+```cypher
+SHOW SESSIONS;
```
+Each row is one session that is currently logged in. A connection that hasn't
+logged in yet, or that has logged off, isn't listed. After it logs on again it
+reappears with the same `session_id`. If another user logs on, the row shows
+that user and the database of the new login:
+
+| Column | Type | Description |
+|---|---|---|
+| `session_id` | `String` | Unique id of the session. Use this value with the `TERMINATE SESSIONS` query to close the session. |
+| `username` | `String` | The user the session logged in as, or `""` for a session that logged in while authentication was disabled. With impersonation, this is still the login user. |
+| `database` | `String` | The database the session is using: the one selected with `USE DATABASE`, the one named when connecting, or otherwise the user's main database (`memgraph` by default). `""` if the session has no database, for example after its database was dropped. |
+| `login_timestamp` | `String` | UTC time of the session's most recent login, formatted as `YYYY-MM-DD HH:MM:SS.ffffff` (no time zone suffix). |
+
+The `SHOW SESSIONS` query shows your own sessions, and the sessions on every
+database where you have the `TRANSACTION_MANAGEMENT` privilege. A session that isn't using a
+database counts as using the default database, `memgraph`. When
+authentication is disabled, every connection counts as the same user, so you
+see all sessions. With [user
+impersonation](/database-management/authentication-and-authorization/impersonate-user),
+a connection counts as the impersonated user, and its queries are checked
+against that user's privileges.
+
+In the following example, `admin` has the `TRANSACTION_MANAGEMENT` privilege
+on every database, and `alice` has two idle connections open, one on the
+`analytics` database and one on the `memgraph` database. The session running
+the query is listed too:
+
+```plaintext copy=false
++----------------------------------------+----------+-------------+------------------------------+
+| session_id | username | database | login_timestamp |
++----------------------------------------+----------+-------------+------------------------------+
+| "eba078f8-2c56-4e22-bf47-4545ac9d4b1a" | "admin" | "memgraph" | "2026-10-07 10:30:33.022574" |
+| "e9e40294-db31-4122-9875-75affefc1130" | "alice" | "memgraph" | "2026-10-07 10:30:32.391885" |
+| "f9892d9b-ca91-4e16-b3df-1ead198cc846" | "alice" | "analytics" | "2026-10-07 10:30:32.228485" |
++----------------------------------------+----------+-------------+------------------------------+
+```
+
+### Terminate sessions
+
+To close one or more sessions, run the following query from another
+connection:
+
+```plaintext
+TERMINATE SESSIONS 'SESSION_ID' [, 'SESSION_ID' ...];
+```
+
+`SESSION_ID` is a `session_id` from the `SHOW SESSIONS` output, written as a
+string literal. Query parameters such as `$id` aren't supported and cause a
+syntax error. Any other literal value raises the `Session id must be a
+string.` error.
+
+You can terminate your own other sessions without any privilege. To terminate
+another user's session, you need the `TRANSACTION_MANAGEMENT` privilege on
+the database that session is using (the default database, `memgraph`, if it
+isn't using one). The session that runs the `TERMINATE SESSIONS` query can't
+terminate itself.
+
+
+These privilege checks apply only in Memgraph Enterprise. In Memgraph
+Community, every user has all privileges, so any user can see and terminate
+any other user's session.
+
+
+The result has two columns, `session_id` and `killed`, with one row per id in
+the query, in the same order. A row reports `killed: true` if the session was
+found and you were allowed to close it. This means the close was requested: a
+session that is busy with a query can still appear in `SHOW SESSIONS` until
+that query returns. An id that matches no session, an id you're not allowed to
+terminate, the id of the session running the query, and every repeat of an id
+already in the list all report `killed: false`.
+
+For example, to close alice's `analytics` session (the second id doesn't
+match any session):
+
+```cypher
+TERMINATE SESSIONS 'f9892d9b-ca91-4e16-b3df-1ead198cc846', '00000000-0000-0000-0000-000000000000';
+```
+
+```plaintext copy=false
++----------------------------------------+--------+
+| session_id | killed |
++----------------------------------------+--------+
+| "f9892d9b-ca91-4e16-b3df-1ead198cc846" | true |
+| "00000000-0000-0000-0000-000000000000" | false |
++----------------------------------------+--------+
+```
+
+What happens to a terminated session depends on what it's doing:
+
+- **Idle:** Memgraph closes the session immediately.
+- **Running a query:** Memgraph aborts the query's transaction and closes the
+ session as soon as the query returns. Most queries stop right away. A query
+ that doesn't check for termination, such as a long-running function call,
+ runs until it finishes, and its transaction isn't committed.
+- **In an open explicit transaction** (it ran `BEGIN` and hasn't committed):
+ Memgraph rolls the transaction back. Its uncommitted changes are discarded
+ and the writes it holds are released, so conflicting transactions can
+ continue.
+- **Committing:** the commit isn't interrupted; it completes.
+
+The client of a terminated session loses its connection:
+
+- **A query it's running** fails with the transient `Transaction was asked to
+ abort by another user.` error. A query that doesn't check for termination
+ fails when it finishes, with the transient `Aborting transaction commit
+ because the transaction was requested to stop from other session.` error.
+- **An idle client** gets a connection error on its next request, and the
+ driver opens a new connection for the request after that. Driver retry
+ helpers (for example, the Python driver's `execute_read`) retry on that
+ error, so their request succeeds.
+
+Because the error is transient, driver retry helpers (for example, the Python
+driver's `execute_write`) retry the transaction on a new connection, and the
+retry can commit. Terminating a session doesn't stop an application that
+uses retry helpers from running the same work again.
+
## Isolation levels
In database systems, isolation determines how transaction integrity is visible
@@ -532,6 +699,10 @@ SET TRANSACTION ISOLATION LEVEL
- SESSION - apply the new isolation level only for the current session
- NEXT - apply the new isolation level only for the next transaction in the current session
+A SESSION setting lasts for the client connection. A failed query or a rollback keeps it, including the Bolt `RESET` a driver sends after a failure.
+Switching to another user on the same connection (the driver re-authenticates, sending `LOGOFF` and `LOGON`) clears both a SESSION setting and a pending NEXT one.
+The new user's transactions then run at the database's current isolation level.
+
`` defines the isolation level:
- SNAPSHOT ISOLATION
- READ COMMITTED
diff --git a/pages/getting-started/install-memgraph/direct-download-links.mdx b/pages/getting-started/install-memgraph/direct-download-links.mdx
index f67bbaa88..c96248107 100644
--- a/pages/getting-started/install-memgraph/direct-download-links.mdx
+++ b/pages/getting-started/install-memgraph/direct-download-links.mdx
@@ -26,17 +26,17 @@ you only need when debugging with `gdb` or analyzing a core dump. See
## Docker
-- [https://download.memgraph.com/memgraph/v3.13.2/docker-x86_64/memgraph-3.13.2-docker.tar.gz](https://download.memgraph.com/memgraph/v3.13.2/docker-x86_64/memgraph-3.13.2-docker.tar.gz)
-- [https://download.memgraph.com/memgraph/v3.13.2/docker-aarch64/memgraph-3.13.2-docker.tar.gz](https://download.memgraph.com/memgraph/v3.13.2/docker-aarch64/memgraph-3.13.2-docker.tar.gz)
-- [https://download.memgraph.com/memgraph/v3.13.2/docker-x86_64-relwithdebinfo/memgraph-3.13.2-relwithdebinfo-docker.tar.gz](https://download.memgraph.com/memgraph/v3.13.2/docker-x86_64-relwithdebinfo/memgraph-3.13.2-relwithdebinfo-docker.tar.gz)
-- [https://download.memgraph.com/memgraph/v3.13.2/docker-aarch64-relwithdebinfo/memgraph-3.13.2-relwithdebinfo-docker.tar.gz](https://download.memgraph.com/memgraph/v3.13.2/docker-aarch64-relwithdebinfo/memgraph-3.13.2-relwithdebinfo-docker.tar.gz)
-- [https://download.memgraph.com/memgraph/v3.13.2/docker-x86_64-malloc-relwithdebinfo/memgraph-3.13.2-relwithdebinfo-malloc-docker.tar.gz](https://download.memgraph.com/memgraph/v3.13.2/docker-x86_64-malloc-relwithdebinfo/memgraph-3.13.2-relwithdebinfo-malloc-docker.tar.gz)
-- [https://download.memgraph.com/memgraph/v3.13.2/docker-aarch64-malloc-relwithdebinfo/memgraph-3.13.2-relwithdebinfo-malloc-docker.tar.gz](https://download.memgraph.com/memgraph/v3.13.2/docker-aarch64-malloc-relwithdebinfo/memgraph-3.13.2-relwithdebinfo-malloc-docker.tar.gz)
-- [https://download.memgraph.com/memgraph/v3.13.2/docker-x86_64-fips/memgraph-3.13.2-fips-docker.tar.gz](https://download.memgraph.com/memgraph/v3.13.2/docker-x86_64-fips/memgraph-3.13.2-fips-docker.tar.gz) (FIPS 140-3)
-- [https://download.memgraph.com/memgraph/v3.13.2/docker-aarch64-fips/memgraph-3.13.2-fips-docker.tar.gz](https://download.memgraph.com/memgraph/v3.13.2/docker-aarch64-fips/memgraph-3.13.2-fips-docker.tar.gz) (FIPS 140-3)
+- [https://download.memgraph.com/memgraph/v3.14.0/docker-x86_64/memgraph-3.14.0-docker.tar.gz](https://download.memgraph.com/memgraph/v3.14.0/docker-x86_64/memgraph-3.14.0-docker.tar.gz)
+- [https://download.memgraph.com/memgraph/v3.14.0/docker-aarch64/memgraph-3.14.0-docker.tar.gz](https://download.memgraph.com/memgraph/v3.14.0/docker-aarch64/memgraph-3.14.0-docker.tar.gz)
+- [https://download.memgraph.com/memgraph/v3.14.0/docker-x86_64-relwithdebinfo/memgraph-3.14.0-relwithdebinfo-docker.tar.gz](https://download.memgraph.com/memgraph/v3.14.0/docker-x86_64-relwithdebinfo/memgraph-3.14.0-relwithdebinfo-docker.tar.gz)
+- [https://download.memgraph.com/memgraph/v3.14.0/docker-aarch64-relwithdebinfo/memgraph-3.14.0-relwithdebinfo-docker.tar.gz](https://download.memgraph.com/memgraph/v3.14.0/docker-aarch64-relwithdebinfo/memgraph-3.14.0-relwithdebinfo-docker.tar.gz)
+- [https://download.memgraph.com/memgraph/v3.14.0/docker-x86_64-malloc-relwithdebinfo/memgraph-3.14.0-relwithdebinfo-malloc-docker.tar.gz](https://download.memgraph.com/memgraph/v3.14.0/docker-x86_64-malloc-relwithdebinfo/memgraph-3.14.0-relwithdebinfo-malloc-docker.tar.gz)
+- [https://download.memgraph.com/memgraph/v3.14.0/docker-aarch64-malloc-relwithdebinfo/memgraph-3.14.0-relwithdebinfo-malloc-docker.tar.gz](https://download.memgraph.com/memgraph/v3.14.0/docker-aarch64-malloc-relwithdebinfo/memgraph-3.14.0-relwithdebinfo-malloc-docker.tar.gz)
+- [https://download.memgraph.com/memgraph/v3.14.0/docker-x86_64-fips/memgraph-3.14.0-fips-docker.tar.gz](https://download.memgraph.com/memgraph/v3.14.0/docker-x86_64-fips/memgraph-3.14.0-fips-docker.tar.gz) (FIPS 140-3)
+- [https://download.memgraph.com/memgraph/v3.14.0/docker-aarch64-fips/memgraph-3.14.0-fips-docker.tar.gz](https://download.memgraph.com/memgraph/v3.14.0/docker-aarch64-fips/memgraph-3.14.0-fips-docker.tar.gz) (FIPS 140-3)
The FIPS images are also published to Docker Hub as
-`memgraph/memgraph:3.13.2-fips`. See [FIPS 140-3 compliant
+`memgraph/memgraph:3.14.0-fips`. See [FIPS 140-3 compliant
image](/getting-started/install-memgraph/docker#fips-140-3-compliant-image) for
what they contain and how to run them.
@@ -50,54 +50,52 @@ has a matching `memgraph-debuginfo` package containing its debug symbols.
The `-malloc` variants are builds without jemalloc, using the system allocator
(x86_64 only).
-The `-no-python` variants are built without embedded Python support, so they
-cannot run Python query modules or Python auth modules. These are the packages
-that the FIPS 140-3 Docker image is built from — Python is excluded because the
-auth-module wheels bundle their own statically linked copy of OpenSSL, which
-would be a second, unvalidated cryptographic module. You can install them
-directly if you need a Python-free Memgraph, but note that the packages do not
-ship the validated OpenSSL FIPS provider: that comes from the host's OpenSSL
-configuration, and the [FIPS 140-3 Docker
+The `-fips` variants are the packages that the FIPS 140-3 Docker image is built
+from. They include embedded Python but omit the Kerberos auth module, whose
+cryptography does not come from OpenSSL. They can be installed directly, but on
+their own they are not a FIPS 140-3 deployment: the packages do not ship the
+validated OpenSSL FIPS provider, which has to come from the host's OpenSSL
+configuration. The [FIPS 140-3 Docker
image](/getting-started/install-memgraph/docker#fips-140-3-compliant-image)
provides it ready-configured.
### Debian / Ubuntu (`.deb`)
-- [https://download.memgraph.com/memgraph/v3.13.2/x86_64-deb/memgraph_3.13.2-1_amd64.deb](https://download.memgraph.com/memgraph/v3.13.2/x86_64-deb/memgraph_3.13.2-1_amd64.deb)
-- [https://download.memgraph.com/memgraph/v3.13.2/x86_64-deb/memgraph-debuginfo_3.13.2-1_amd64.deb](https://download.memgraph.com/memgraph/v3.13.2/x86_64-deb/memgraph-debuginfo_3.13.2-1_amd64.deb)
-- [https://download.memgraph.com/memgraph/v3.13.2/aarch64-deb/memgraph_3.13.2-1_arm64.deb](https://download.memgraph.com/memgraph/v3.13.2/aarch64-deb/memgraph_3.13.2-1_arm64.deb)
-- [https://download.memgraph.com/memgraph/v3.13.2/aarch64-deb/memgraph-debuginfo_3.13.2-1_arm64.deb](https://download.memgraph.com/memgraph/v3.13.2/aarch64-deb/memgraph-debuginfo_3.13.2-1_arm64.deb)
-- [https://download.memgraph.com/memgraph/v3.13.2/x86_64-deb-malloc/memgraph_3.13.2-1_amd64.deb](https://download.memgraph.com/memgraph/v3.13.2/x86_64-deb-malloc/memgraph_3.13.2-1_amd64.deb) (system allocator)
-- [https://download.memgraph.com/memgraph/v3.13.2/x86_64-deb-malloc/memgraph-debuginfo_3.13.2-1_amd64.deb](https://download.memgraph.com/memgraph/v3.13.2/x86_64-deb-malloc/memgraph-debuginfo_3.13.2-1_amd64.deb) (system allocator)
-- [https://download.memgraph.com/memgraph/v3.13.2/x86_64-deb-no-python/memgraph_3.13.2-1_amd64.deb](https://download.memgraph.com/memgraph/v3.13.2/x86_64-deb-no-python/memgraph_3.13.2-1_amd64.deb) (no Python support)
-- [https://download.memgraph.com/memgraph/v3.13.2/x86_64-deb-no-python/memgraph-debuginfo_3.13.2-1_amd64.deb](https://download.memgraph.com/memgraph/v3.13.2/x86_64-deb-no-python/memgraph-debuginfo_3.13.2-1_amd64.deb) (no Python support)
-- [https://download.memgraph.com/memgraph/v3.13.2/aarch64-deb-no-python/memgraph_3.13.2-1_arm64.deb](https://download.memgraph.com/memgraph/v3.13.2/aarch64-deb-no-python/memgraph_3.13.2-1_arm64.deb) (no Python support)
-- [https://download.memgraph.com/memgraph/v3.13.2/aarch64-deb-no-python/memgraph-debuginfo_3.13.2-1_arm64.deb](https://download.memgraph.com/memgraph/v3.13.2/aarch64-deb-no-python/memgraph-debuginfo_3.13.2-1_arm64.deb) (no Python support)
+- [https://download.memgraph.com/memgraph/v3.14.0/x86_64-deb/memgraph_3.14.0-1_amd64.deb](https://download.memgraph.com/memgraph/v3.14.0/x86_64-deb/memgraph_3.14.0-1_amd64.deb)
+- [https://download.memgraph.com/memgraph/v3.14.0/x86_64-deb/memgraph-debuginfo_3.14.0-1_amd64.deb](https://download.memgraph.com/memgraph/v3.14.0/x86_64-deb/memgraph-debuginfo_3.14.0-1_amd64.deb)
+- [https://download.memgraph.com/memgraph/v3.14.0/aarch64-deb/memgraph_3.14.0-1_arm64.deb](https://download.memgraph.com/memgraph/v3.14.0/aarch64-deb/memgraph_3.14.0-1_arm64.deb)
+- [https://download.memgraph.com/memgraph/v3.14.0/aarch64-deb/memgraph-debuginfo_3.14.0-1_arm64.deb](https://download.memgraph.com/memgraph/v3.14.0/aarch64-deb/memgraph-debuginfo_3.14.0-1_arm64.deb)
+- [https://download.memgraph.com/memgraph/v3.14.0/x86_64-deb-malloc/memgraph_3.14.0-1_amd64.deb](https://download.memgraph.com/memgraph/v3.14.0/x86_64-deb-malloc/memgraph_3.14.0-1_amd64.deb) (system allocator)
+- [https://download.memgraph.com/memgraph/v3.14.0/x86_64-deb-malloc/memgraph-debuginfo_3.14.0-1_amd64.deb](https://download.memgraph.com/memgraph/v3.14.0/x86_64-deb-malloc/memgraph-debuginfo_3.14.0-1_amd64.deb) (system allocator)
+- [https://download.memgraph.com/memgraph/v3.14.0/x86_64-deb-fips/memgraph_3.14.0-1_amd64.deb](https://download.memgraph.com/memgraph/v3.14.0/x86_64-deb-fips/memgraph_3.14.0-1_amd64.deb) (FIPS 140-3)
+- [https://download.memgraph.com/memgraph/v3.14.0/x86_64-deb-fips/memgraph-debuginfo_3.14.0-1_amd64.deb](https://download.memgraph.com/memgraph/v3.14.0/x86_64-deb-fips/memgraph-debuginfo_3.14.0-1_amd64.deb) (FIPS 140-3)
+- [https://download.memgraph.com/memgraph/v3.14.0/aarch64-deb-fips/memgraph_3.14.0-1_arm64.deb](https://download.memgraph.com/memgraph/v3.14.0/aarch64-deb-fips/memgraph_3.14.0-1_arm64.deb) (FIPS 140-3)
+- [https://download.memgraph.com/memgraph/v3.14.0/aarch64-deb-fips/memgraph-debuginfo_3.14.0-1_arm64.deb](https://download.memgraph.com/memgraph/v3.14.0/aarch64-deb-fips/memgraph-debuginfo_3.14.0-1_arm64.deb) (FIPS 140-3)
### RHEL / CentOS / Rocky / Fedora (`.rpm`)
-- [https://download.memgraph.com/memgraph/v3.13.2/x86_64-rpm/memgraph-3.13.2_1-1.x86_64.rpm](https://download.memgraph.com/memgraph/v3.13.2/x86_64-rpm/memgraph-3.13.2_1-1.x86_64.rpm)
-- [https://download.memgraph.com/memgraph/v3.13.2/x86_64-rpm/memgraph-debuginfo-3.13.2_1-1.x86_64.rpm](https://download.memgraph.com/memgraph/v3.13.2/x86_64-rpm/memgraph-debuginfo-3.13.2_1-1.x86_64.rpm)
-- [https://download.memgraph.com/memgraph/v3.13.2/aarch64-rpm/memgraph-3.13.2_1-1.aarch64.rpm](https://download.memgraph.com/memgraph/v3.13.2/aarch64-rpm/memgraph-3.13.2_1-1.aarch64.rpm)
-- [https://download.memgraph.com/memgraph/v3.13.2/aarch64-rpm/memgraph-debuginfo-3.13.2_1-1.aarch64.rpm](https://download.memgraph.com/memgraph/v3.13.2/aarch64-rpm/memgraph-debuginfo-3.13.2_1-1.aarch64.rpm)
-- [https://download.memgraph.com/memgraph/v3.13.2/x86_64-rpm-malloc/memgraph-3.13.2_1-1.x86_64.rpm](https://download.memgraph.com/memgraph/v3.13.2/x86_64-rpm-malloc/memgraph-3.13.2_1-1.x86_64.rpm) (system allocator)
-- [https://download.memgraph.com/memgraph/v3.13.2/x86_64-rpm-malloc/memgraph-debuginfo-3.13.2_1-1.x86_64.rpm](https://download.memgraph.com/memgraph/v3.13.2/x86_64-rpm-malloc/memgraph-debuginfo-3.13.2_1-1.x86_64.rpm) (system allocator)
-- [https://download.memgraph.com/memgraph/v3.13.2/x86_64-rpm-no-python/memgraph-3.13.2_1-1.x86_64.rpm](https://download.memgraph.com/memgraph/v3.13.2/x86_64-rpm-no-python/memgraph-3.13.2_1-1.x86_64.rpm) (no Python support)
-- [https://download.memgraph.com/memgraph/v3.13.2/x86_64-rpm-no-python/memgraph-debuginfo-3.13.2_1-1.x86_64.rpm](https://download.memgraph.com/memgraph/v3.13.2/x86_64-rpm-no-python/memgraph-debuginfo-3.13.2_1-1.x86_64.rpm) (no Python support)
-- [https://download.memgraph.com/memgraph/v3.13.2/aarch64-rpm-no-python/memgraph-3.13.2_1-1.aarch64.rpm](https://download.memgraph.com/memgraph/v3.13.2/aarch64-rpm-no-python/memgraph-3.13.2_1-1.aarch64.rpm) (no Python support)
-- [https://download.memgraph.com/memgraph/v3.13.2/aarch64-rpm-no-python/memgraph-debuginfo-3.13.2_1-1.aarch64.rpm](https://download.memgraph.com/memgraph/v3.13.2/aarch64-rpm-no-python/memgraph-debuginfo-3.13.2_1-1.aarch64.rpm) (no Python support)
+- [https://download.memgraph.com/memgraph/v3.14.0/x86_64-rpm/memgraph-3.14.0_1-1.x86_64.rpm](https://download.memgraph.com/memgraph/v3.14.0/x86_64-rpm/memgraph-3.14.0_1-1.x86_64.rpm)
+- [https://download.memgraph.com/memgraph/v3.14.0/x86_64-rpm/memgraph-debuginfo-3.14.0_1-1.x86_64.rpm](https://download.memgraph.com/memgraph/v3.14.0/x86_64-rpm/memgraph-debuginfo-3.14.0_1-1.x86_64.rpm)
+- [https://download.memgraph.com/memgraph/v3.14.0/aarch64-rpm/memgraph-3.14.0_1-1.aarch64.rpm](https://download.memgraph.com/memgraph/v3.14.0/aarch64-rpm/memgraph-3.14.0_1-1.aarch64.rpm)
+- [https://download.memgraph.com/memgraph/v3.14.0/aarch64-rpm/memgraph-debuginfo-3.14.0_1-1.aarch64.rpm](https://download.memgraph.com/memgraph/v3.14.0/aarch64-rpm/memgraph-debuginfo-3.14.0_1-1.aarch64.rpm)
+- [https://download.memgraph.com/memgraph/v3.14.0/x86_64-rpm-malloc/memgraph-3.14.0_1-1.x86_64.rpm](https://download.memgraph.com/memgraph/v3.14.0/x86_64-rpm-malloc/memgraph-3.14.0_1-1.x86_64.rpm) (system allocator)
+- [https://download.memgraph.com/memgraph/v3.14.0/x86_64-rpm-malloc/memgraph-debuginfo-3.14.0_1-1.x86_64.rpm](https://download.memgraph.com/memgraph/v3.14.0/x86_64-rpm-malloc/memgraph-debuginfo-3.14.0_1-1.x86_64.rpm) (system allocator)
+- [https://download.memgraph.com/memgraph/v3.14.0/x86_64-rpm-fips/memgraph-3.14.0_1-1.x86_64.rpm](https://download.memgraph.com/memgraph/v3.14.0/x86_64-rpm-fips/memgraph-3.14.0_1-1.x86_64.rpm) (FIPS 140-3)
+- [https://download.memgraph.com/memgraph/v3.14.0/x86_64-rpm-fips/memgraph-debuginfo-3.14.0_1-1.x86_64.rpm](https://download.memgraph.com/memgraph/v3.14.0/x86_64-rpm-fips/memgraph-debuginfo-3.14.0_1-1.x86_64.rpm) (FIPS 140-3)
+- [https://download.memgraph.com/memgraph/v3.14.0/aarch64-rpm-fips/memgraph-3.14.0_1-1.aarch64.rpm](https://download.memgraph.com/memgraph/v3.14.0/aarch64-rpm-fips/memgraph-3.14.0_1-1.aarch64.rpm) (FIPS 140-3)
+- [https://download.memgraph.com/memgraph/v3.14.0/aarch64-rpm-fips/memgraph-debuginfo-3.14.0_1-1.aarch64.rpm](https://download.memgraph.com/memgraph/v3.14.0/aarch64-rpm-fips/memgraph-debuginfo-3.14.0_1-1.aarch64.rpm) (FIPS 140-3)
# MAGE direct download links
## Docker
-- [MAGE Docker amd64 (release)](https://download.memgraph.com/memgraph-mage/v3.13.2/docker-x86_64/mage-3.13.2.tar.gz)
-- [MAGE Docker arm64 (release)](https://download.memgraph.com/memgraph-mage/v3.13.2/docker-aarch64/mage-3.13.2-arm64.tar.gz)
-- [MAGE Docker amd64 (relwithdebinfo)](https://download.memgraph.com/memgraph-mage/v3.13.2/docker-x86_64-relwithdebinfo/mage-3.13.2-relwithdebinfo.tar.gz)
-- [MAGE Docker arm64 (relwithdebinfo)](https://download.memgraph.com/memgraph-mage/v3.13.2/docker-aarch64-relwithdebinfo/mage-3.13.2-arm64-relwithdebinfo.tar.gz)
-- [MAGE Docker amd64 (malloc)](https://download.memgraph.com/memgraph-mage/v3.13.2/docker-x86_64-malloc/mage-3.13.2-malloc.tar.gz)
-- [MAGE Docker amd64 (relwithdebinfo-malloc)](https://download.memgraph.com/memgraph-mage/v3.13.2/docker-x86_64-malloc-relwithdebinfo/mage-3.13.2-relwithdebinfo-malloc.tar.gz)
-- [MAGE Docker arm64 (relwithdebinfo-malloc)](https://download.memgraph.com/memgraph-mage/v3.13.2/docker-aarch64-malloc-relwithdebinfo/mage-3.13.2-arm64-relwithdebinfo-malloc.tar.gz)
-- [MAGE Docker amd64 (relwithdebinfo-cuda)](https://download.memgraph.com/memgraph-mage/v3.13.2/docker-x86_64-relwithdebinfo-cuda/mage-3.13.2-relwithdebinfo-cuda.tar.gz)
-- [MAGE Docker amd64 (relwithdebinfo-cugraph)](https://download.memgraph.com/memgraph-mage/v3.13.2/docker-x86_64-relwithdebinfo-cugraph/mage-3.13.2-relwithdebinfo-cugraph.tar.gz)
+- [MAGE Docker amd64 (release)](https://download.memgraph.com/memgraph-mage/v3.14.0/docker-x86_64/mage-3.14.0.tar.gz)
+- [MAGE Docker arm64 (release)](https://download.memgraph.com/memgraph-mage/v3.14.0/docker-aarch64/mage-3.14.0-arm64.tar.gz)
+- [MAGE Docker amd64 (relwithdebinfo)](https://download.memgraph.com/memgraph-mage/v3.14.0/docker-x86_64-relwithdebinfo/mage-3.14.0-relwithdebinfo.tar.gz)
+- [MAGE Docker arm64 (relwithdebinfo)](https://download.memgraph.com/memgraph-mage/v3.14.0/docker-aarch64-relwithdebinfo/mage-3.14.0-arm64-relwithdebinfo.tar.gz)
+- [MAGE Docker amd64 (malloc)](https://download.memgraph.com/memgraph-mage/v3.14.0/docker-x86_64-malloc/mage-3.14.0-malloc.tar.gz)
+- [MAGE Docker amd64 (relwithdebinfo-malloc)](https://download.memgraph.com/memgraph-mage/v3.14.0/docker-x86_64-malloc-relwithdebinfo/mage-3.14.0-relwithdebinfo-malloc.tar.gz)
+- [MAGE Docker arm64 (relwithdebinfo-malloc)](https://download.memgraph.com/memgraph-mage/v3.14.0/docker-aarch64-malloc-relwithdebinfo/mage-3.14.0-arm64-relwithdebinfo-malloc.tar.gz)
+- [MAGE Docker amd64 (relwithdebinfo-cuda)](https://download.memgraph.com/memgraph-mage/v3.14.0/docker-x86_64-relwithdebinfo-cuda/mage-3.14.0-relwithdebinfo-cuda.tar.gz)
+- [MAGE Docker amd64 (relwithdebinfo-cugraph)](https://download.memgraph.com/memgraph-mage/v3.14.0/docker-x86_64-relwithdebinfo-cugraph/mage-3.14.0-relwithdebinfo-cugraph.tar.gz)
## Linux
@@ -122,21 +120,21 @@ work but are untested.
### Debian / Ubuntu (`.deb`)
-- [MAGE amd64](https://download.memgraph.com/memgraph-mage/v3.13.2/x86_64-deb/memgraph-mage_3.13.2-1_amd64.deb)
-- [MAGE arm64](https://download.memgraph.com/memgraph-mage/v3.13.2/aarch64-deb/memgraph-mage_3.13.2-1_arm64.deb)
-- [MAGE amd64 (debuginfo)](https://download.memgraph.com/memgraph-mage/v3.13.2/x86_64-deb/memgraph-mage-debuginfo_3.13.2-1_amd64.deb)
-- [MAGE arm64 (debuginfo)](https://download.memgraph.com/memgraph-mage/v3.13.2/aarch64-deb/memgraph-mage-debuginfo_3.13.2-1_arm64.deb)
-- [MAGE amd64 (cuda)](https://download.memgraph.com/memgraph-mage/v3.13.2/x86_64-deb-cuda/memgraph-mage_3.13.2-1_amd64-cuda.deb)
-- [MAGE amd64 (cuda debuginfo)](https://download.memgraph.com/memgraph-mage/v3.13.2/x86_64-deb-cuda/memgraph-mage-debuginfo_3.13.2-1_amd64-cuda.deb)
-- [MAGE amd64 (cugraph)](https://download.memgraph.com/memgraph-mage/v3.13.2/x86_64-deb-cugraph/memgraph-mage_3.13.2-1_amd64-cugraph.deb)
-- [MAGE amd64 (cugraph debuginfo)](https://download.memgraph.com/memgraph-mage/v3.13.2/x86_64-deb-cugraph/memgraph-mage-debuginfo_3.13.2-1_amd64-cugraph.deb)
+- [MAGE amd64](https://download.memgraph.com/memgraph-mage/v3.14.0/x86_64-deb/memgraph-mage_3.14.0-1_amd64.deb)
+- [MAGE arm64](https://download.memgraph.com/memgraph-mage/v3.14.0/aarch64-deb/memgraph-mage_3.14.0-1_arm64.deb)
+- [MAGE amd64 (debuginfo)](https://download.memgraph.com/memgraph-mage/v3.14.0/x86_64-deb/memgraph-mage-debuginfo_3.14.0-1_amd64.deb)
+- [MAGE arm64 (debuginfo)](https://download.memgraph.com/memgraph-mage/v3.14.0/aarch64-deb/memgraph-mage-debuginfo_3.14.0-1_arm64.deb)
+- [MAGE amd64 (cuda)](https://download.memgraph.com/memgraph-mage/v3.14.0/x86_64-deb-cuda/memgraph-mage_3.14.0-1_amd64-cuda.deb)
+- [MAGE amd64 (cuda debuginfo)](https://download.memgraph.com/memgraph-mage/v3.14.0/x86_64-deb-cuda/memgraph-mage-debuginfo_3.14.0-1_amd64-cuda.deb)
+- [MAGE amd64 (cugraph)](https://download.memgraph.com/memgraph-mage/v3.14.0/x86_64-deb-cugraph/memgraph-mage_3.14.0-1_amd64-cugraph.deb)
+- [MAGE amd64 (cugraph debuginfo)](https://download.memgraph.com/memgraph-mage/v3.14.0/x86_64-deb-cugraph/memgraph-mage-debuginfo_3.14.0-1_amd64-cugraph.deb)
### RHEL / CentOS / Rocky / Fedora (`.rpm`)
-- [MAGE x86_64](https://download.memgraph.com/memgraph-mage/v3.13.2/x86_64-rpm/memgraph-mage-3.13.2_1-1.x86_64.rpm)
-- [MAGE aarch64](https://download.memgraph.com/memgraph-mage/v3.13.2/aarch64-rpm/memgraph-mage-3.13.2_1-1.aarch64.rpm)
-- [MAGE x86_64 (debuginfo)](https://download.memgraph.com/memgraph-mage/v3.13.2/x86_64-rpm/memgraph-mage-debuginfo-3.13.2_1-1.x86_64.rpm)
-- [MAGE aarch64 (debuginfo)](https://download.memgraph.com/memgraph-mage/v3.13.2/aarch64-rpm/memgraph-mage-debuginfo-3.13.2_1-1.aarch64.rpm)
-- [MAGE x86_64 (cuda)](https://download.memgraph.com/memgraph-mage/v3.13.2/x86_64-rpm-cuda/memgraph-mage-3.13.2_1-1.x86_64-cuda.rpm)
-- [MAGE x86_64 (cuda debuginfo)](https://download.memgraph.com/memgraph-mage/v3.13.2/x86_64-rpm-cuda/memgraph-mage-debuginfo-3.13.2_1-1.x86_64-cuda.rpm)
-- [MAGE x86_64 (cugraph)](https://download.memgraph.com/memgraph-mage/v3.13.2/x86_64-rpm-cugraph/memgraph-mage-3.13.2_1-1.x86_64-cugraph.rpm)
-- [MAGE x86_64 (cugraph debuginfo)](https://download.memgraph.com/memgraph-mage/v3.13.2/x86_64-rpm-cugraph/memgraph-mage-debuginfo-3.13.2_1-1.x86_64-cugraph.rpm)
+- [MAGE x86_64](https://download.memgraph.com/memgraph-mage/v3.14.0/x86_64-rpm/memgraph-mage-3.14.0_1-1.x86_64.rpm)
+- [MAGE aarch64](https://download.memgraph.com/memgraph-mage/v3.14.0/aarch64-rpm/memgraph-mage-3.14.0_1-1.aarch64.rpm)
+- [MAGE x86_64 (debuginfo)](https://download.memgraph.com/memgraph-mage/v3.14.0/x86_64-rpm/memgraph-mage-debuginfo-3.14.0_1-1.x86_64.rpm)
+- [MAGE aarch64 (debuginfo)](https://download.memgraph.com/memgraph-mage/v3.14.0/aarch64-rpm/memgraph-mage-debuginfo-3.14.0_1-1.aarch64.rpm)
+- [MAGE x86_64 (cuda)](https://download.memgraph.com/memgraph-mage/v3.14.0/x86_64-rpm-cuda/memgraph-mage-3.14.0_1-1.x86_64-cuda.rpm)
+- [MAGE x86_64 (cuda debuginfo)](https://download.memgraph.com/memgraph-mage/v3.14.0/x86_64-rpm-cuda/memgraph-mage-debuginfo-3.14.0_1-1.x86_64-cuda.rpm)
+- [MAGE x86_64 (cugraph)](https://download.memgraph.com/memgraph-mage/v3.14.0/x86_64-rpm-cugraph/memgraph-mage-3.14.0_1-1.x86_64-cugraph.rpm)
+- [MAGE x86_64 (cugraph debuginfo)](https://download.memgraph.com/memgraph-mage/v3.14.0/x86_64-rpm-cugraph/memgraph-mage-debuginfo-3.14.0_1-1.x86_64-cugraph.rpm)
diff --git a/pages/getting-started/install-memgraph/docker.mdx b/pages/getting-started/install-memgraph/docker.mdx
index 2eea6d21e..dd29c5aed 100644
--- a/pages/getting-started/install-memgraph/docker.mdx
+++ b/pages/getting-started/install-memgraph/docker.mdx
@@ -144,7 +144,7 @@ Memgraph publishes a FIPS 140-3 variant of the `memgraph/memgraph` image,
tagged with a `-fips` suffix:
```bash
-docker pull memgraph/memgraph:3.13.1-fips
+docker pull memgraph/memgraph:3.14.0-fips
```
It is a separate image rather than a flag on the standard one, because the
@@ -152,18 +152,14 @@ difference is in the image contents, not just the configuration.
{What differs from the standard image
}
-| | `memgraph/memgraph:3.13.1` | `memgraph/memgraph:3.13.1-fips` |
+| | `memgraph/memgraph:3.14.0` | `memgraph/memgraph:3.14.0-fips` |
|---|---|---|
| OpenSSL | Stock Ubuntu OpenSSL | OpenSSL + the validated **FIPS provider** (`fips.so`) |
| `OPENSSL_CONF` | Distribution default | `/etc/ssl/openssl-fips.cnf` — activates the FIPS and base providers only |
-| Python support | Included | **Not included** — no Python query modules, no Python auth modules |
+| Python support | Included | Included — `cryptography`, `lxml` and `xmlsec` are built against the validated OpenSSL |
+| Kerberos SSO | Available | **Not available** — the auth module is omitted |
| Approved mode | Not available | Opt-in with `--fips-mode=true` |
-Python is excluded deliberately. The Python auth-module wheels (`cryptography`,
-`xmlsec`) bundle their own statically linked OpenSSL, which would place a
-second, unvalidated cryptographic module inside the image on the SAML/JWT
-authentication path.
-
The image is verified at build time: the build fails unless the FIPS provider
is present in OpenSSL's module directory, reports `status: active` (a module
that fails its power-on self-test still appears in the provider list), and
@@ -177,6 +173,30 @@ approved mode cannot constrain.
+{Python on the FIPS image
}
+
+Python query modules and the SAML and OIDC auth modules run on this image. The
+`cryptography`, `lxml` and `xmlsec` wheels are built from source against the
+image's OpenSSL rather than installed from PyPI, because the published wheels
+bundle their own statically linked copy of OpenSSL — a second, unvalidated
+cryptographic module on the SAML and JWT authentication path.
+
+
+
+Python support is new in 3.14.0. The `3.13.x` FIPS images shipped without a
+Python interpreter, so Python query modules and Python auth modules cannot run
+on them.
+
+
+
+The **Kerberos auth module is not included**. The system Kerberos libraries
+implement their own cryptography instead of calling OpenSSL, so approved mode
+cannot constrain them. SAML and OIDC are unaffected.
+
+SAML signature verification rejects deprecated signature algorithms, such as
+RSA-SHA1, whenever the image is running — with or without `--fips-mode=true`,
+because it follows OpenSSL's own approved mode rather than the Memgraph flag.
+
{Running the image
}
Pulling the image is not enough — approved mode is opt-in per process. Pass
@@ -186,7 +206,7 @@ Pulling the image is not enough — approved mode is opt-in per process. Pass
docker run -p 7687:7687 -p 7444:7444 --name memgraph \
-e MEMGRAPH_ENTERPRISE_LICENSE="" \
-e MEMGRAPH_ORGANIZATION_NAME="" \
- memgraph/memgraph:3.13.1-fips --fips-mode=true
+ memgraph/memgraph:3.14.0-fips --fips-mode=true
```
Without `--fips-mode=true` the container starts normally and uses the FIPS
@@ -288,7 +308,7 @@ docker run -p 7687:7687 --name memgraph \
-e MEMGRAPH_ENTERPRISE_LICENSE="" \
-e MEMGRAPH_ORGANIZATION_NAME="" \
-v /path/to/reset.cypherl:/etc/memgraph/reset.cypherl:ro \
- memgraph/memgraph:3.13.1-fips \
+ memgraph/memgraph:3.14.0-fips \
--fips-mode=true --init-file=/etc/memgraph/reset.cypherl
```
diff --git a/pages/getting-started/install-memgraph/kubernetes.mdx b/pages/getting-started/install-memgraph/kubernetes.mdx
index 8213b2148..668f2a9da 100644
--- a/pages/getting-started/install-memgraph/kubernetes.mdx
+++ b/pages/getting-started/install-memgraph/kubernetes.mdx
@@ -126,6 +126,11 @@ The vm.max_map_count parameter is a kernel parameter that specifies the maximum
This change will be applied to all nodes in the cluster. If you want to disable this feature, you can set `sysctlInitContainer.enabled` to `false` in the `values.yaml` file.
+Starting with v3.14, Memgraph raises its soft open files limit to the hard limit
+at startup, so only the container's hard limit counts. If Memgraph logs a warning that the
+limit is too low, see [open files
+limit](/database-management/system-configuration#handle-numerous-open-files-simultaneously).
+
### Deploying on Red Hat OpenShift
diff --git a/pages/help-center/errors/replication.mdx b/pages/help-center/errors/replication.mdx
index b13d95fcb..4e3aad758 100644
--- a/pages/help-center/errors/replication.mdx
+++ b/pages/help-center/errors/replication.mdx
@@ -43,7 +43,11 @@ MAIN is the only source of truth.
## Query not allowed on multicommand transaction [#error-3]
-System queries cannot be executed inside a multicommand/explicit transaction.
+System queries, such as replication queries, cannot be executed inside a
+multicommand/explicit transaction. Auth queries are the exception: they can run
+in an explicit transaction that contains only auth queries. See [Run auth
+queries in a
+transaction](/database-management/authentication-and-authorization#run-auth-queries-in-a-transaction).
## Replication failure [#error-4]
diff --git a/pages/help-center/errors/transactions.mdx b/pages/help-center/errors/transactions.mdx
index 76460d26f..a75dd07d2 100644
--- a/pages/help-center/errors/transactions.mdx
+++ b/pages/help-center/errors/transactions.mdx
@@ -11,6 +11,7 @@ import {CommunityLinks} from '/components/social-card/CommunityLinks'
While working with Memgraph, you can encounter various transaction errors. Here are some of them, along with the instructions on how to handle them:
- [Conflicting transactions](#conflicting-transactions)
+- [Auth queries mixed with other queries](#auth-queries-mixed-with-other-queries)
- [Transaction timeout](#transaction-timeout)
- [Storage access timeout](#storage-access-timeout)
@@ -99,6 +100,19 @@ While some client drivers may handle serialization errors by retrying transactio
developers should not rely solely on this mechanism. Always include comprehensive error handling
in your application to address cases where the error persists beyond the retry logic.
+## Auth queries mixed with other queries
+
+### Error message
+
+1. **A transaction that runs an auth query can contain only auth queries. Run other queries in a separate transaction.**
+
+### Handling mixed auth and other queries
+
+An explicit transaction can contain auth queries, such as `CREATE USER` and `GRANT`, or other queries, but not both.
+The first query decides which kind the transaction is, and a later query of the other kind fails.
+Roll the transaction back and run the auth queries in a transaction of their own.
+See [Run auth queries in a transaction](/database-management/authentication-and-authorization#run-auth-queries-in-a-transaction).
+
## Transaction accessor misalignment
### Error message
diff --git a/pages/querying/clauses/call.mdx b/pages/querying/clauses/call.mdx
index 0d4774bfc..a69c5f748 100644
--- a/pages/querying/clauses/call.mdx
+++ b/pages/querying/clauses/call.mdx
@@ -22,13 +22,15 @@ Switch to MAGE documentation if you want to CALL a graph algorithm or some other
1.3. [Post-union processing](#13-post-union-processing)
1.4. [Observing changes from previous executions](#14-observing-changes-from-previous-executions)
1.5. [Unit subqueries](#15-unit-subqueries)
- 1.6. [Scoped variables with `CALL (...)`](#16-scoped-variables-with-call-)
+ 1.6. [Scoped variables with `CALL (...)`](#16-scoped-variables-with-call-)
+ 1.7. [Optional subqueries with `OPTIONAL CALL`](#17-optional-subqueries-with-optional-call)
+ 1.8. [Conditional subqueries with `WHEN`](#18-conditional-subqueries-with-when)
2. [Invalid uses of CALL subquery](#2-invalid-uses-of-call-subquery)
2.1. [Returning variables with the same name as those in the outer scope](#21-returning-variables-with-the-same-name-as-those-in-the-outer-scope)
2.2. [Returning non-aliased expressions](#22-returning-non-aliased-expressions)
2.3. [Referencing outer scope variables that don't exist](#23-referencing-outer-scope-variables-that-dont-exist)
- 2.4. [Using `OPTIONAL CALL` with scoped variables](#24-using-optional-call-with-scoped-variables)
+ 2.4. [Using `OPTIONAL` with a procedure call](#24-using-optional-with-a-procedure-call)
## 1. Uses of CALL subquery
@@ -224,6 +226,143 @@ The scoped form is equivalent to using `WITH` to import variables, but
narrows the subquery's scope explicitly at the boundary, which makes the
import list visible at a glance.
+### 1.7. Optional subqueries with `OPTIONAL CALL`
+
+`CALL` drops an input row when the subquery returns no rows for it. `OPTIONAL
+CALL` keeps that row and sets the columns the subquery returns to `null`, which
+is the same contract as [`OPTIONAL MATCH`](/querying/clauses/optional-match).
+
+Imagine the data from [1.2](#12-cartesian-products-with-bounded-symbols), plus a
+third `:Person` named `Carol` who has no `:HAS_PARENT` relationship:
+
+```cypher
+MATCH (person:Person)
+OPTIONAL CALL (person) {
+ MATCH (person)-[:HAS_PARENT]->(parent:Parent)
+ RETURN parent.name AS parent_name
+}
+RETURN person.name AS person_name, parent_name
+```
+
+Output:
+```nocopy
++-------------+-------------+
+| person_name | parent_name |
++---------------------------+
+| 'John' | 'John Sr.' |
+| 'John' | 'Anna' |
+| 'Alice' | 'Roxanne' |
+| 'Alice' | 'Bill' |
+| 'Carol' | null |
++---------------------------+
+```
+
+Without `OPTIONAL`, `Carol` does not appear in the result at all.
+
+`OPTIONAL` can be combined with every scope form (`CALL (v1, v2)`, `CALL (*)`,
+`CALL ()`, or no scope clause), with a `UNION` subquery, with nested subqueries,
+and with [`IN TRANSACTIONS`](/querying/read-and-modify-data#call-subqueries-in-transactions).
+
+Variables imported from the outer scope keep their values; only the columns the
+subquery itself introduces are set to `null`. A
+[unit subquery](#15-unit-subqueries) returns no columns, so `OPTIONAL` has
+nothing to set to `null` and the number of rows is the same either way.
+
+### 1.8. Conditional subqueries with `WHEN`
+
+A conditional subquery runs, for each input row, the first branch whose
+predicate is `true`:
+
+```cypher
+CALL (...) {
+ WHEN predicate THEN body
+ [WHEN predicate THEN body ...]
+ [ELSE body]
+}
+```
+
+```cypher
+UNWIND [{name: 'Ana', age: 65}, {name: 'Ivan', age: 25}, {name: 'Marko', age: 39}] AS person
+CALL (person) {
+ WHEN person.age > 60 THEN RETURN 'senior' AS bracket
+ WHEN person.age > 30 THEN RETURN 'adult' AS bracket
+ ELSE RETURN 'young' AS bracket
+}
+RETURN person.name AS name, bracket;
+```
+
+Output:
+```nocopy
++---------+----------+
+| name | bracket |
++---------+----------+
+| "Ana" | "senior" |
+| "Ivan" | "young" |
+| "Marko" | "adult" |
++---------+----------+
+```
+
+A body is a single query, or a query in braces that may contain `UNION` or
+another `WHEN`:
+
+```cypher
+UNWIND [1, 2] AS i
+CALL (i) {
+ WHEN i = 1 THEN {
+ RETURN 'a' AS x
+ UNION
+ RETURN 'b' AS x
+ }
+ ELSE {
+ WHEN i > 5 THEN RETURN 'big' AS x
+ ELSE RETURN 'small' AS x
+ }
+}
+RETURN i, x;
+```
+
+Output:
+```nocopy
++---+---------+
+| i | x |
++---+---------+
+| 1 | "a" |
+| 1 | "b" |
+| 2 | "small" |
++---+---------+
+```
+
+When no branch matches a row, the result depends on the body:
+
+- A body that returns rows drops the input row, as a `CALL` subquery that
+ returns no rows does. [`OPTIONAL CALL`](#17-optional-subqueries-with-optional-call)
+ keeps the row and sets the returned columns to `null`.
+- A body without `RETURN`, such as one that only updates the graph, keeps every
+ input row, as a [unit subquery](#15-unit-subqueries) does.
+
+```cypher
+UNWIND [1, 2, 3] AS i
+CALL (i) {
+ WHEN i = 1 THEN CREATE (:Tier {i: i, level: 'gold'})
+ WHEN i = 2 THEN CREATE (:Tier {i: i, level: 'silver'})
+}
+RETURN i;
+```
+
+The query returns the rows `1`, `2` and `3`, and creates two `:Tier` nodes.
+
+The branches of one conditional subquery must agree:
+
+- All branches return rows, all branches update the graph without `RETURN`, or
+ all branches are a standalone procedure call.
+- Branches that return rows return the same column names. The order of the
+ columns can differ.
+
+A conditional subquery needs a scope clause: `CALL { WHEN ... }` is an error, and
+`CALL () { WHEN ... }` is valid. A branch cannot set a query memory limit or
+start with `USING`. The whole conditional subquery can run
+[`IN TRANSACTIONS`](/querying/read-and-modify-data#call-subqueries-in-transactions).
+
## 2. Invalid uses of CALL subquery
### 2.1. Returning variables with the same name as those in the outer scope
@@ -308,19 +447,25 @@ CALL {
RETURN DISTINCT n;
```
-### 2.4. Using `OPTIONAL CALL` with scoped variables
+### 2.4. Using `OPTIONAL` with a procedure call
-The scoped `CALL (...)` form cannot be combined with `OPTIONAL`:
+`OPTIONAL` applies to a `CALL` subquery, not to a procedure call:
```cypher invalid
-MATCH (p:Player)
+MATCH (p:Person)
+OPTIONAL CALL algo.procedure(p)
+YIELD result
+RETURN p.name, result;
+```
+
+The above query results in an error. Wrap the procedure call in a subquery
+instead:
+
+```cypher template
+MATCH (p:Person)
OPTIONAL CALL (p) {
- MATCH (p)-[:PLAYS_FOR]->(team:Team)
- RETURN team.name AS team
+ CALL algo.procedure(p) YIELD result
+ RETURN result
}
-RETURN p.name, team;
+RETURN p.name, result;
```
-
-The above query results in an error. To make a scoped subquery optional,
-emit a sentinel value from the inner block instead, or fall back to the
-plain `OPTIONAL MATCH` outside the `CALL`.
diff --git a/pages/querying/clauses/match.mdx b/pages/querying/clauses/match.mdx
index 7658cb735..998c5a8e5 100644
--- a/pages/querying/clauses/match.mdx
+++ b/pages/querying/clauses/match.mdx
@@ -14,6 +14,10 @@ The `MATCH` clause is used to obtain data from the database by matching it to a
1.2. [Get all nodes with a label](#12-get-all-nodes-with-a-label)
1.3. [Get nodes with with all of multiple labels](#13-get-nodes-with-all-labels)
1.4. [Get nodes with any of multiple labels](#14-get-nodes-with-any-of-labels)
+ 1.5. [Get nodes without a label](#15-get-nodes-without-a-label)
+ 1.6. [Get nodes with any label](#16-get-nodes-with-any-label)
+ 1.7. [Combine label expressions](#17-combine-label-expressions)
+ 1.8. [Label expressions in expressions](#18-label-expressions-in-expressions)
2. [Matching relationships](#2-matching-relationships)
2.1. [Get all related nodes](#21-get-all-related-nodes)
2.2. [Get related nodes with a label](#22-get-related-nodes-with-a-label)
@@ -30,6 +34,7 @@ The `MATCH` clause is used to obtain data from the database by matching it to a
4. [Using multiple MATCH clauses](#4-using-multiple-match-clauses)
4.1. [Cartesian product of nodes](#41-cartesian-product-of-nodes)
4.2. [Creating a list](#42-creating-a-list)
+ 4.3. [Matching a variable that is already bound](#43-matching-a-variable-that-is-already-bound)
@@ -105,6 +110,13 @@ MATCH (n:Person:Employee)
RETURN n;
```
+The `&` (AND) operator gives the same result:
+
+```cypher
+MATCH (n:Person&Employee)
+RETURN n;
+```
+
Output:
```nocopy
+-----------------------------------+
@@ -142,7 +154,155 @@ This allows you to retrieve nodes of different types in a single query, making l
-Label OR expressions (`|`) can't be used in `MERGE` or `CREATE` clauses.
+`CREATE` and `MERGE` accept only a conjunction of labels, such as `(n:Person:Employee)` or
+`(n:Person&Employee)`. A label expression with `|`, `!` or `%` raises an error there, because it does
+not name the labels a new node gets.
+
+
+
+### 1.5. Get nodes without a label
+
+Use the `!` (NOT) operator to match nodes that do **not** have a label:
+
+```cypher
+MATCH (n:!Person)
+RETURN n;
+```
+
+Output:
+```nocopy
++-----------------------------------------------------------------------------------------------------+
+| n |
++-----------------------------------------------------------------------------------------------------+
+| (:Country {continent: "Europe", language: "German", name: "Germany", population: 83000000}) |
+| (:Country {continent: "Europe", language: "French", name: "France", population: 67000000}) |
+| (:Country {continent: "Europe", language: "English", name: "United Kingdom", population: 66000000}) |
+| (:City {name: "Berlin", population: 3600000}) |
+| (:City {name: "Paris", population: 2140000}) |
++-----------------------------------------------------------------------------------------------------+
+```
+
+### 1.6. Get nodes with any label
+
+The `%` (wildcard) matches a node that has at least one label. `!%` matches a node that has no labels.
+
+```cypher
+MATCH (n:%)
+RETURN count(n) AS labelled;
+```
+
+Output:
+```nocopy
++----------+
+| labelled |
++----------+
+| 8 |
++----------+
+```
+
+### 1.7. Combine label expressions
+
+The operators `&`, `|`, `!` and `%` combine into one label expression. `!` binds tightest, then `&`, then
+`|`. Use parentheses to change the order.
+
+```cypher
+MATCH (n:Person&!Employee)
+RETURN n;
+```
+
+Output:
+```nocopy
++---------------------------+
+| n |
++---------------------------+
+| (:Person {name: "Harry"}) |
+| (:Person {name: "Anna"}) |
++---------------------------+
+```
+
+`City|Person&Employee` reads as `City|(Person&Employee)`:
+
+```cypher
+MATCH (n:City|Person&Employee)
+RETURN n.name AS name ORDER BY name;
+```
+
+Output:
+```nocopy
++----------+
+| name |
++----------+
+| "Berlin" |
+| "John" |
+| "Paris" |
++----------+
+```
+
+With parentheses, the same labels give a different result:
+
+```cypher
+MATCH (n:(City|Person)&Employee)
+RETURN n.name AS name ORDER BY name;
+```
+
+Output:
+```nocopy
++--------+
+| name |
++--------+
+| "John" |
++--------+
+```
+
+The colon form `:A:B` can't be mixed with the operators in one expression: `(n:Person:Employee&!City)`
+raises a syntax error. Write `(n:Person&Employee&!City)` instead.
+
+### 1.8. Label expressions in expressions
+
+A label expression can test a node anywhere an expression is allowed, such as `WHERE`, `RETURN`, `WITH`
+and `CASE`. It returns `true` or `false`, and `null` for a `null` value.
+
+```cypher
+MATCH (n)
+WHERE n:City|Country
+RETURN n.name AS name ORDER BY name;
+```
+
+Output:
+```nocopy
++------------------+
+| name |
++------------------+
+| "Berlin" |
+| "France" |
+| "Germany" |
+| "Paris" |
+| "United Kingdom" |
++------------------+
+```
+
+```cypher
+MATCH (p:Person)
+RETURN p.name AS name, p:Employee AS employee, p:!Employee AS not_employee ORDER BY name;
+```
+
+Output:
+```nocopy
++--------------+--------------+--------------+
+| name | employee | not_employee |
++--------------+--------------+--------------+
+| "Anna" | false | true |
+| "Harry" | false | true |
+| "John" | true | false |
++--------------+--------------+--------------+
+```
+
+A label expression binds tighter than every other operator, so `n:A&B = true` tests `(n:A&B) = true`.
+
+
+
+In a list comprehension, the last `|` is the projection. `[x IN list WHERE x:A|B]` filters on `x:A` and
+projects `B`. Write `[x IN list WHERE x:(A|B)]` to filter on the disjunction.
@@ -476,6 +636,79 @@ Output:
The `OPTIONAL MATCH` clause bypasses the empty set and the query returns only non-empty sets. Therefore, the output of the query is a list containing only the results of the first `MATCH` clause.
+### 4.3. Matching a variable that is already bound
+
+A pattern may hold nothing but a variable the query has already bound. Such a
+pattern does not scan the graph — it matches on the value bound to that name:
+
+```cypher
+MATCH (p:Person)
+MATCH (p)
+RETURN p.name AS name
+ORDER BY name;
+```
+
+Output:
+
+```nocopy
++---------+
+| name |
++---------+
+| "Anna" |
+| "Harry" |
+| "John" |
++---------+
+```
+
+A node matches and the row is kept. A `null`, which an `OPTIONAL MATCH` leaves
+behind when nothing matched, matches nothing and the row is dropped:
+
+```cypher
+MATCH (p:Person)
+OPTIONAL MATCH (p)-[:LIVING_IN]->(c:Country {continent: 'Asia'})
+WITH p, c
+MATCH (c)
+RETURN p.name AS name, c.name AS country
+ORDER BY name;
+```
+
+Output:
+
+`Empty set`
+
+No person lives in an Asian country, so `c` is `null` on every row and `MATCH (c)`
+keeps none of them. Without the `MATCH (c)`, the query returns all three people
+with a `null` country.
+
+A value that is neither a node nor `null` raises an error:
+
+```cypher
+MATCH (p:Person)
+WITH p.name AS p
+MATCH (p)
+RETURN p;
+```
+
+Output:
+
+```nocopy
+Expected a node for 'p', but got string.
+```
+
+
+
+This applies wherever a pattern appears — a top-level `MATCH`, a `CALL {}`
+subquery, and the body of a [subquery
+expression](/querying/subquery-expressions). Before Memgraph 3.14 a pattern
+holding only a bound variable was ignored, so the row passed through whatever the
+variable held. Queries that carried a `null` or a non-node value through such a
+pattern now return fewer rows or raise an error.
+
+A pattern that also states a label or a property, such as `MATCH (p:Person)`, is
+unaffected — it already raised on a non-node value.
+
+
+
## Dataset queries
We encourage you to try out the examples by yourself.
diff --git a/pages/querying/clauses/return.md b/pages/querying/clauses/return.md
index bf9b4fde9..d1df61188 100644
--- a/pages/querying/clauses/return.md
+++ b/pages/querying/clauses/return.md
@@ -318,6 +318,32 @@ WITH n ORDER BY n.name DESC
RETURN collect(n.name) AS names;
```
+`ORDER BY` can sort a column that holds values of different types, for
+example a property that is a number on some nodes and a string on others.
+`null` comes last, or first with `DESC`:
+
+```cypher
+UNWIND [1, 'text', true, null, [1]] AS value
+RETURN value ORDER BY value;
+```
+
+```plaintext copy=false
++--------+
+| value |
++--------+
+| [1] |
+| "text" |
+| true |
+| 1 |
+| Null |
++--------+
+```
+
+A column that holds two maps, two nodes, two relationships or two paths
+raises an error, such as `Comparison is not defined for values of type map.`
+Before Memgraph 3.14, a column that held values of different types raised an
+error too.
+
## Dataset queries
We encourage you to try out the examples by yourself.
diff --git a/pages/querying/differences-in-cypher-implementations.mdx b/pages/querying/differences-in-cypher-implementations.mdx
index f29bcf9f5..1bbb18e2e 100644
--- a/pages/querying/differences-in-cypher-implementations.mdx
+++ b/pages/querying/differences-in-cypher-implementations.mdx
@@ -134,13 +134,16 @@ RETURN p;
Note that Memgraph requires both source and target nodes to be matched first using a `WITH` clause before applying the K-shortest paths algorithm.
-### NOT label expression
-In Neo4j, you can use the `NOT` label expression (`!`):
+### Relationship type expressions
+Memgraph supports [label expressions](/querying/clauses/match#17-combine-label-expressions) on nodes
+(`&`, `|`, `!`, `%` and parentheses). On relationship types, it supports only `|`. Memgraph doesn't support the use of `!` on a relationship type:
+
```cypher neo4j
MATCH (:Person {name:'Tom Hanks'})-[r:!ACTED_IN]->(m:Movie)
Return type(r) AS type, m.title AS movies
```
In Memgraph, such a construct is not supported, but there is still a workaround:
+
```cypher
MATCH (p:Person {name:'Tom Hanks'})-[r]->(m:Movie)
WHERE type(r) != "ACTED_IN"
@@ -252,10 +255,6 @@ RETURN val, valueType(val) = "INTEGER"
- `exists(n.property)` - can be expressed using `n.property IS NOT NULL`
- `isEmpty()`
-**Scalar functions**:
-
-- `nullIf()`
-
**Aggregating functions**:
- `percentileCont()`, `percentileDisc()`
diff --git a/pages/querying/expressions.mdx b/pages/querying/expressions.mdx
index f03ba99a9..584f52e29 100644
--- a/pages/querying/expressions.mdx
+++ b/pages/querying/expressions.mdx
@@ -29,6 +29,36 @@ WHERE p.name STARTS WITH 'Joh'
RETURN p.name;
```
+## Ordering comparisons
+
+Comparing two values that have no order between them with `<`, `<=`, `>` or
+`>=`, such as a number and a string, returns `null` instead of raising an
+error. In a `WHERE` clause, `null` counts as false, so the filter leaves those
+rows out. Here, the sensor that reports a string is left out instead of
+failing the query:
+
+```cypher
+CREATE (:Sensor {name: 'boiler', reading: 72}), (:Sensor {name: 'pump', reading: 'offline'});
+```
+
+```cypher
+MATCH (s:Sensor) WHERE s.reading > 50 RETURN s.name;
+```
+
+```plaintext copy=false
++----------+
+| s.name |
++----------+
+| "boiler" |
++----------+
+```
+
+To sort values of different types, use `ORDER BY`; see [order
+results](/querying/clauses/return#13-order-results).
+
+Before Memgraph 3.14, comparing two values that can't be ordered, or two
+booleans, raised an error.
+
## Parameters
When automating the queries for Memgraph, it comes in handy to change only some
@@ -202,12 +232,27 @@ MATCH (n)
RETURN CASE WHEN n.height < 30 THEN "short" WHEN n.height > 300 THEN "tall" END;
```
-Most expressions that take `null` as input will produce `null`. This includes boolean expressions that are used as
-predicates. In this case, anything that is not true is interpreted as being false. This also concludes that logically `null!=null`.
+A `WHEN` predicate that evaluates to `null` counts as false, so its branch isn't
+taken. See [comparisons with null](#comparisons-with-null) for which
+comparisons evaluate to `null`.
The [`exists()`](/querying/functions#pattern-functions) function and the `EXISTS { … }`, `COUNT { … }` and
`COLLECT { … }` [subquery expressions](/querying/subquery-expressions) can all be used inside `CASE`.
+## Comparisons with null
+
+`null` stands for a value that isn't known, so most expressions that take
+`null` as input return `null`. A comparison with `null` returns `null` too:
+`null = null` and `1 <> null` are both `null`. In a `WHERE` clause or a `CASE`
+predicate, `null` counts as false, so the row is filtered out or the branch
+isn't taken. To test for a missing value, use `IS NULL` or `IS NOT NULL`.
+
+The same applies to lists and maps that hold `null`: `[null] = [null]` is
+`null`, so a filter that compares a property with such a value matches
+nothing. `IN` compares with `=` too, so `WHERE NOT (n.code IN ['A', 'B'])`
+keeps no row where `n.code` is `null`. Before Memgraph 3.14, `[null] = [null]`
+returned `true`.
+
## Pattern existence (exists(pattern))
`exists(pattern)` is a short form for a simple pattern existence check. It is accepted in the same
diff --git a/pages/querying/functions.mdx b/pages/querying/functions.mdx
index ca5db7f84..b230767ca 100644
--- a/pages/querying/functions.mdx
+++ b/pages/querying/functions.mdx
@@ -50,6 +50,7 @@ This section contains the list of supported functions.
| `elementId` | `elementId(value: Node\|Relationship) -> (string)` | Returns the identifier for a given node or relationship as a string. The identifier is the same as the one returned by `id()`. |
| `last` | `last(list: List[any]) -> (any)` | Returns the last element of a list. |
| `length` | `length(value: List\|string\|Map\|Path) -> (integer)` | Returns the number of elements in the value. When given a **list** it returns the size of the list. When given a string it returns the number of characters. When given a path it returns the number of expansions (relationships) in that path. |
+ | `nullIf` | `nullIf(value1: any, value2: any) -> (any)` | Returns `null` if the two arguments are equal, and the first argument otherwise. The arguments are compared as the `=` operator compares them, so a comparison that involves a `null` is undecided and the first argument is returned. |
| `properties` | `properties(value: Node\|Relationship) -> (Map[string, any])` | Returns the property map of a node or a relationship. |
| `propertySize` | `propertySize(entity: Node\|Relationship, property-name: string) -> (integer)` | Returns the total amount of bytes stored in RAM for the property of a given entity node or relationship. For more information, check [storage of properties inside Memgraph](/fundamentals/storage-memory-usage#calculate-storage-memory-usage). |
| `randomUUID` | `randomUUID() -> (string)` | Returns randomly-generated Universally Unique Identifier (UUID) |
@@ -157,8 +158,8 @@ This section contains the list of supported functions.
| `collect` | `collect(values: any) -> (List[any])` | Returns a single aggregated list containing returned values. |
| `collect` | `collect(key: string, value: any) -> (Map[string, any])` | Returns a single aggregated map containing key-value pairs. |
| `count` | `count(values: any) -> (integer)` | Counts the number of non-null values returned by the expression. |
-| `max` | `max(row: integer\|float\|temporal) -> (integer\|float\|temporal)` | Returns the maximum value in a set of values. Supported temporal types: `Date`, `LocalTime`, `LocalDateTime` and `ZonedDateTime`. |
-| `min` | `min(row: integer\|float\|temporal) -> (integer\|float\|temporal)` | Returns the minimum value in a set of values. Supported temporal types: `Date`, `LocalTime`, `LocalDateTime` and `ZonedDateTime`. |
+| `max` | `max(row: any) -> (any)` | Returns the value that [`ORDER BY`](/querying/clauses/return#13-order-results) puts last, skipping `null`. Maps, nodes, relationships and paths raise an error. |
+| `min` | `min(row: any) -> (any)` | Returns the value that [`ORDER BY`](/querying/clauses/return#13-order-results) puts first, skipping `null`. Maps, nodes, relationships and paths raise an error. |
| `sum` | `sum(row: integer\|float) -> (integer\|float)` | Returns a sum value of rows with numerical values generated with the `MATCH` or `UNWIND` clause. |
diff --git a/pages/querying/subquery-expressions.mdx b/pages/querying/subquery-expressions.mdx
index 229b931b8..06a15eb41 100644
--- a/pages/querying/subquery-expressions.mdx
+++ b/pages/querying/subquery-expressions.mdx
@@ -353,8 +353,9 @@ Output:
### Supported clauses
A body is a read-only query — it never writes to the graph. It matches with
-`MATCH`, filters with `WHERE`, aggregates through `WITH`, and may end in a
-`RETURN`; it may also be a [`UNION`](#union) of such branches.
+`MATCH`, iterates a list with `UNWIND`, filters with `WHERE`, aggregates
+through `WITH`, and may end in a `RETURN`; it may also be a [`UNION`](#union)
+of such branches.
`WITH` lets the body aggregate and then filter on the aggregate — here, people
who acted in at least two movies:
@@ -399,12 +400,38 @@ Output:
+---------+
```
+`UNWIND` turns a list into rows inside the body — here, how many of the given
+years each person has a movie in:
+
+```cypher
+MATCH (p:Person)
+RETURN p.name AS name,
+ COUNT {
+ UNWIND [1995, 1999, 2003] AS year
+ MATCH (p)-[:ACTED_IN]->(:Movie {released: year})
+ RETURN DISTINCT year
+ } AS years
+ORDER BY name;
+```
+
+Output:
+
+```nocopy
++---------+-------+
+| name | years |
++---------+-------+
+| "Alice" | 2 |
+| "Bob" | 1 |
+| "Carol" | 0 |
+| "Dave" | 0 |
++---------+-------+
+```
+
### The bare pattern shorthand
`EXISTS` and `COUNT` accept a bare pattern in place of a full body, which is a
-shorter way to write a check or a count over one pattern. The pattern cannot
-introduce names of its own — a variable already bound outside may appear in it,
-and acts as a join:
+shorter way to write a check or a count over one pattern. A variable already
+bound outside may appear in it, and acts as a join:
```cypher
MATCH (p:Person)
@@ -427,10 +454,32 @@ Output:
+---------+-------+---------+
```
-The shorthand holds a pattern and nothing else, so a filter or any other clause
-needs the `MATCH` form — `COUNT { MATCH (p)-[r:KNOWS]->() WHERE r.since < 2015 }`.
-`COLLECT` has no shorthand, since a bare pattern returns no column for it to
-gather.
+The shorthand takes everything a `MATCH` pattern takes: a variable it declares
+itself, a trailing `WHERE`, several patterns separated by commas, a named path,
+and a pattern holding only a node.
+
+```cypher
+MATCH (p:Person)
+RETURN p.name AS name,
+ COUNT { (p)-[r:KNOWS]->(f:Person) WHERE r.since < 2015 } AS early
+ORDER BY name;
+```
+
+Output:
+
+```nocopy
++---------+-------+
+| name | early |
++---------+-------+
+| "Alice" | 1 |
+| "Bob" | 0 |
+| "Carol" | 0 |
+| "Dave" | 0 |
++---------+-------+
+```
+
+Any other clause still needs the `MATCH` form. `COLLECT` has no shorthand, since
+a bare pattern returns no column for it to gather.
### Correlation with the enclosing query
@@ -479,6 +528,74 @@ Output:
Variables introduced inside the body are scoped to it and are not available after
the subquery expression.
+A pattern in the body may hold nothing but a correlated variable. Such a pattern
+matches on the value bound to that name rather than scanning the graph: a node
+matches, a `null` matches nothing, and a value that is neither raises an error.
+
+```cypher
+MATCH (p:Person)
+OPTIONAL MATCH (p)-[:KNOWS]->(f:Person)
+WITH p, f
+RETURN p.name AS name, EXISTS { (f) } AS bound
+ORDER BY name, bound;
+```
+
+Output:
+
+```nocopy
++---------+-------+
+| name | bound |
++---------+-------+
+| "Alice" | true |
+| "Alice" | true |
+| "Bob" | true |
+| "Carol" | false |
+| "Dave" | false |
++---------+-------+
+```
+
+Carol and Dave know nobody, so `f` is `null` on their rows and `EXISTS { (f) }`
+is `false`. This is the same rule a top-level [`MATCH`](/querying/clauses/match)
+follows.
+
+A named path inside the body may not take its name from a variable declared
+outside the body. The body writes into the frame it shares with its caller, so
+one name cannot mean the caller's value in one place and the body's own path in
+another:
+
+```cypher
+MATCH (p:Person)
+WITH p, p.name AS path
+RETURN COUNT { MATCH path = (p)-[:KNOWS]->() } AS c;
+```
+
+Output:
+
+```nocopy
+Cannot name a pattern 'path' in COUNT, because that variable is already declared outside it.
+```
+
+Give the path a name of its own, and it works:
+
+```cypher
+MATCH (p:Person)
+RETURN COUNT { MATCH q = (p)-[:KNOWS]->() } AS c
+ORDER BY c;
+```
+
+Output:
+
+```nocopy
++---+
+| c |
++---+
+| 0 |
+| 0 |
+| 1 |
+| 2 |
++---+
+```
+
### The body's RETURN decides the result
The body is a query, and its final `RETURN` shapes the row set that the
diff --git a/pages/release-notes.mdx b/pages/release-notes.mdx
index 241f6fd31..3f8c38dc9 100644
--- a/pages/release-notes.mdx
+++ b/pages/release-notes.mdx
@@ -46,6 +46,422 @@ guide.
## 🚀 Latest release
+### Memgraph v3.14.0 - October 21st, 2026
+
+{⚠️ Breaking changes
}
+
+- A user or tenant profile change inside an explicit transaction is
+ refused. It previously applied immediately and was not undone by
+ `ROLLBACK`. Run profile changes as a single query.
+ [#4524](https://github.com/memgraph/memgraph/pull/4524)
+- `SHOW TRANSACTIONS` gains a `database` column with the database each
+ transaction runs on (for snapshot and garbage collection rows, the database
+ being processed). `SHOW ACTIVE USERS INFO` gains `database` and `database
+ marked for deletion`. Clients that parse these results by column position
+ need to account for the new fields.
+ [#4571](https://github.com/memgraph/memgraph/pull/4571)
+- Edge index operator metrics are merged into
+ `memgraph_scan_all_by_edge_type_property_operator_total` and
+ `memgraph_scan_all_by_edge_property_operator_total`. Dashboards that
+ scrape the older `_value` or `_range` series need those names updated.
+ [#4671](https://github.com/memgraph/memgraph/pull/4671)
+- Comparing an edge property with a type it cannot match, such as a
+ boolean or a point, now returns no rows instead of raising. Update
+ queries that relied on that error.
+ [#4671](https://github.com/memgraph/memgraph/pull/4671)
+- A pattern that names an already-bound variable is now a node test, including
+ in plain `MATCH`. After `OPTIONAL MATCH`, `MATCH (b)` no longer keeps a null
+ `b` row; it drops it (or errors if `b` is not a node). In `EXISTS` / `COUNT` /
+ `COLLECT`, a pattern may also no longer reuse a name from outside the body.
+ Rename those variables, or drop a no-op `MATCH` on a bound name.
+ [#4707](https://github.com/memgraph/memgraph/pull/4707)
+- Functions in the `collections` query module (`contains`, `to_set`, `union`
+ and `duplicates`) now treat maps with the same keys and values as equal,
+ matching Cypher's `=`. Queries that relied on every non-empty map being
+ distinct will see different results. Custom C++ query modules need to be
+ recompiled for this release.
+ [#4783](https://github.com/memgraph/memgraph/pull/4783)
+- Comparing lists or maps that hold `null` now returns `null`, the same as
+ `null = null`, instead of treating them as equal. Update filters and
+ conditions that assumed `[null] = [null]` or `{a: null} = {a: null}` was
+ true. `DISTINCT` and grouping still treat those values as the same key.
+ [#4794](https://github.com/memgraph/memgraph/pull/4794)
+- `NaN > x` and `NaN >= x` now return `false` instead of `true`. A filter
+ on those operators keeps no row where it previously kept every row.
+ [#4816](https://github.com/memgraph/memgraph/pull/4816)
+- Comparing values with `<`, `<=`, `>` or `>=` no longer fails the query
+ when the pair has no order. Two booleans are ordered as Cypher orders
+ them (`false` < `true`). A pair such as two points or two lists
+ returns `null`.
+ [#4816](https://github.com/memgraph/memgraph/pull/4816)
+- An indexed range over a date, local time, local date time or duration
+ returns only values of that same kind. It previously also returned the
+ other three. Recheck range queries on mixed temporal properties.
+ [#4816](https://github.com/memgraph/memgraph/pull/4816)
+- A uniqueness constraint now allows two nodes whose property holds
+ `null` at any depth, such as `[1, null]` or `{a: null}`. It
+ previously refused the second. Recheck constraints that relied on
+ that refusal.
+ [#4836](https://github.com/memgraph/memgraph/pull/4836)
+
+{✨ New features
}
+
+- Auth queries such as `CREATE USER`, `GRANT` and `SET PASSWORD` can run
+ in an explicit transaction and apply together or not at all. A
+ transaction cannot mix auth and data queries. `SHOW TRANSACTIONS`
+ shows passwords as `'****'`.
+ [#4524](https://github.com/memgraph/memgraph/pull/4524)
+- `DROP DATABASE ... FORCE` returns immediately and the database is torn
+ down in the background. Until that finishes, `SHOW DATABASES` lists it as
+ `DROPPING` with health `draining`, for up to about 10 seconds even when
+ nothing uses it. If the same name is created again meanwhile, the draining
+ row is shown as `name (uuid)`. Dropping a name that is still draining fails
+ with `Database is currently being dropped.`
+ [#4573](https://github.com/memgraph/memgraph/pull/4573)
+- `SHOW SESSIONS` lists active Bolt sessions (`session_id`, `username`,
+ `database`, `login_timestamp`). `TERMINATE SESSIONS` evicts sessions by
+ uuid from another connection. A user may manage their own sessions;
+ terminating another user's session requires `TRANSACTION_MANAGEMENT` on
+ that session's database.
+ [#4577](https://github.com/memgraph/memgraph/pull/4577)
+- The `collections` query module adds `index_of(coll, value)`: the position
+ of the first element equal to `value`, or `-1`. A null collection or
+ value returns null.
+ [#4783](https://github.com/memgraph/memgraph/pull/4783)
+- The built-in `nullIf(value1, value2)` returns null when the two arguments
+ are equal, and the first argument otherwise — the counterpart of
+ `coalesce`. The comparison is the same as `=`, so `nullIf(1, null)`
+ returns `1`.
+ [#4786](https://github.com/memgraph/memgraph/pull/4786)
+- `OPTIONAL CALL` keeps an input row when the subquery body returns
+ nothing for it, and sets the body's returned columns to null. Without
+ `OPTIONAL`, that row is dropped. Supported with a scope clause,
+ `(*)`, no scope, `UNION`, nesting, and `IN TRANSACTIONS`.
+ [#4833](https://github.com/memgraph/memgraph/pull/4833)
+- After `DROP DATABASE ... FORCE`, Memgraph closes the Bolt sessions still
+ using that database within about 10 seconds, so the drop can finish.
+ Clients see a disconnect and can reconnect.
+ [#4843](https://github.com/memgraph/memgraph/pull/4843)
+- Aggregation functions can be used inside a `CASE` expression in
+ `RETURN` and `WITH`. They were previously refused. Aggregations in
+ `ORDER BY` are still refused.
+ [#4859](https://github.com/memgraph/memgraph/pull/4859)
+- Node patterns and expressions accept label expressions built with `&`,
+ `|`, `!`, `%` and parentheses, for example `MATCH (n:Person&Employee)`
+ or `WHERE n:A|B`. `%` means the node has at least one label. `CREATE`
+ and `MERGE` accept only `&` and parentheses. `SET` and `REMOVE` still
+ take labels as `:Label` only.
+ [#4877](https://github.com/memgraph/memgraph/pull/4877)
+- The FIPS Docker image now includes a Python interpreter, so Python
+ query modules and auth modules run on that image.
+ [#4902](https://github.com/memgraph/memgraph/pull/4902)
+- An `EXISTS { ... }`, `COUNT { ... }` or `COLLECT { ... }` body can
+ contain `UNWIND`, for example `COUNT { UNWIND n.list AS x RETURN x }`.
+ Write clauses stay refused in these bodies.
+ [#4915](https://github.com/memgraph/memgraph/pull/4915)
+- `ORDER BY`, and a `WITH`'s `WHERE`, can repeat an expression that an
+ aggregating `RETURN` or `WITH` projects, for example
+ `RETURN n.type, count(n.value) ORDER BY count(n.value) DESC`.
+ [#4951](https://github.com/memgraph/memgraph/pull/4951)
+- `SHOW STORAGE INFO` now reports `disk_available`, the free space on
+ the filesystem that holds the data directory.
+ [#4952](https://github.com/memgraph/memgraph/pull/4952)
+- A `CALL` subquery can choose which body to run per input row with
+ `WHEN … THEN …` branches and an optional `ELSE`, for example
+ `CALL (n) { WHEN n.ok THEN CREATE (:A) ELSE CREATE (:B) }`.
+ [#4984](https://github.com/memgraph/memgraph/pull/4984)
+
+{🐞 Bug fixes
}
+
+- After a data instance joins a high-availability cluster, OpenMetrics
+ scrapes now use the cluster UUID for the default database on every
+ instance. Series for `database="memgraph"` can be aggregated across
+ the cluster.
+ [#4275](https://github.com/memgraph/memgraph/pull/4275)
+- `AFTER COMMIT` triggers no longer run for transactions that fail to commit
+ (for example a STRICT_SYNC write that aborts). They fire only when the
+ commit succeeds. On shutdown or `DROP TRIGGER`, any queued trigger work that
+ cannot run is logged instead of being discarded silently.
+ [#4565](https://github.com/memgraph/memgraph/pull/4565)
+- During a license refresh, a query could briefly skip Enterprise permission
+ checks. Those checks stay enforced while the license is revalidated.
+ [#4566](https://github.com/memgraph/memgraph/pull/4566)
+- Dropping a database while a write is still committing no longer crashes a
+ replica registered with STRICT_SYNC. The in-flight commit is aborted
+ before the database is removed.
+ [#4568](https://github.com/memgraph/memgraph/pull/4568)
+- Switching a database to on-disk storage mode is less likely to fail because of
+ brief concurrent activity (for example a metrics scrape or a Lab connection
+ between queries). Those no longer block the switch immediately. If another
+ session still holds the database after a short wait, the switch fails as
+ before, because exclusive access is required.
+ [#4569](https://github.com/memgraph/memgraph/pull/4569)
+- Dropping or renaming a multi-tenant database with a tenant profile now
+ persists correctly, including across crashes and replication.
+ [#4570](https://github.com/memgraph/memgraph/pull/4570)
+- `DROP DATABASE` no longer hangs when an index is being built or TTL is
+ running in the background.
+ [#4572](https://github.com/memgraph/memgraph/pull/4572)
+- Dropping an index is safe to roll back and to replay after recovery, including
+ when drops run alongside other writers or each other.
+ [#4741](https://github.com/memgraph/memgraph/pull/4741)
+- Bolt stays usable when a query fails after streaming a large result.
+ [#4758](https://github.com/memgraph/memgraph/pull/4758)
+- Dropping a database now also drops that database's server-side
+ parameters (`SET PARAMETER`), including across replication and
+ recovery. They no longer linger and reappear on replicas.
+ [#4774](https://github.com/memgraph/memgraph/pull/4774)
+- Creating or resuming a database at the memory limit no longer fails while
+ setting up stream support.
+ [#4785](https://github.com/memgraph/memgraph/pull/4785)
+- A procedure inside `CALL { ... }`, and clauses below it in that
+ subquery, now run for every input row instead of only the first.
+ Affected queries return different results and can write different
+ data, including `CALL { ... } IN TRANSACTIONS`.
+ [#4832](https://github.com/memgraph/memgraph/pull/4832)
+- A property holding `NaN` is kept in an index and found again. An
+ `IS NOT NULL` count on that property no longer shrinks or changes
+ from run to run once the index exists.
+ [#4836](https://github.com/memgraph/memgraph/pull/4836)
+- `DISTINCT` and grouping treat two NaNs as one value. A point with a
+ NaN coordinate is handled the same way. `=` is unchanged and still
+ returns false for a NaN.
+ [#4846](https://github.com/memgraph/memgraph/pull/4846)
+- `ORDER BY` places a NaN after every number, including positive
+ infinity, and two NaNs sort together. A column that held a NaN no
+ longer has an arbitrary order.
+ [#4849](https://github.com/memgraph/memgraph/pull/4849)
+- `ORDER BY` sorts a column that holds more than one type, placing each
+ row by its type (map, node, relationship, list, path, string, boolean,
+ number, then null). A pair of the same type with no order of its own,
+ such as two maps, is still refused.
+ [#4852](https://github.com/memgraph/memgraph/pull/4852)
+- An integer and a double are compared by the value each holds, so a
+ large integer is no longer treated as equal to the nearest double that
+ cannot represent it. `=`, ordering, `ORDER BY`, `DISTINCT`, indexes and
+ unique constraints agree on such pairs. `1 = 1.0` is unchanged.
+ [#4854](https://github.com/memgraph/memgraph/pull/4854)
+- List properties keep large integers as written (for example
+ `2147483648` no longer comes back as a different number), and range
+ filters on list properties return the matching rows.
+ [#4865](https://github.com/memgraph/memgraph/pull/4865)
+- An index holds property values in the same order as `ORDER BY`, so a
+ query over a column with more than one type returns the same rows in
+ the same order with or without an index. Two date-time values at the
+ same instant in different time zones are ordered by offset before zone
+ name.
+ [#4880](https://github.com/memgraph/memgraph/pull/4880)
+- `MIN` and `MAX` work on a column that holds more than one type, and
+ return the first and last values in `ORDER BY` order. A NaN counts as
+ the largest number.
+ [#4882](https://github.com/memgraph/memgraph/pull/4882)
+- `DISTINCT` and grouping tell apart doubles that fall between the same
+ pair of integers (for example `2.25` and `2.5`). A whole double still
+ matches the integer it equals.
+ [#4891](https://github.com/memgraph/memgraph/pull/4891)
+- Hash lookups find a list of numbers, a point that holds a NaN, and a
+ vector index identifier again when the value is stored in a different
+ form or the NaN bits differ.
+ [#4895](https://github.com/memgraph/memgraph/pull/4895)
+- A node with more than one unique constraint no longer leaks memory
+ when a write touches only one of them, including under
+ `USING PERIODIC COMMIT` and `CALL { ... } IN TRANSACTIONS`.
+ [#4848](https://github.com/memgraph/memgraph/pull/4848)
+- `EXISTS { ... }`, `COUNT { ... }` and `COLLECT { ... }` in a `WHERE`
+ no longer fail or keep the wrong rows when the body reads the outer
+ query only through a `WHERE`, a `WITH`, a later `UNION` branch, or a
+ nested subquery. `WHERE n.prop = COUNT { ... }` also returns the same
+ rows with a label-property index as without one.
+ [#4874](https://github.com/memgraph/memgraph/pull/4874)
+- Concurrent edge writes no longer crash during garbage collection.
+ [#4878](https://github.com/memgraph/memgraph/pull/4878)
+- `SHOW REPLICATION LAG` now reports the correct lag for `STRICT_SYNC`
+ replicas.
+ [#4884](https://github.com/memgraph/memgraph/pull/4884)
+- `STARTS WITH` on an indexed property no longer returns nothing when
+ the prefix is made entirely of `0xFF` bytes.
+ [#4886](https://github.com/memgraph/memgraph/pull/4886)
+- Creating or dropping a database no longer makes a healthy MAIN look
+ down to the coordinator. Cluster health checks are answered while
+ that operation holds its lock, so MAIN is not demoted.
+ [#4903](https://github.com/memgraph/memgraph/pull/4903)
+- `SHOW INSTANCE` now works on a coordinator that is still joining
+ the cluster.
+ [#4911](https://github.com/memgraph/memgraph/pull/4911)
+- Switching users on a pooled Bolt connection now clears the previous
+ user's session state: `SET SESSION` and pending `SET NEXT` isolation levels,
+ `SET SESSION SETTING` values, `SET SESSION TRACE` and the database selected
+ with `USE DATABASE`. The new session's requested impersonated user is
+ applied and checked against the new user's privileges.
+ [#4932](https://github.com/memgraph/memgraph/pull/4932)
+- Setting a property to the value it already holds no longer fires an
+ `ON UPDATE` trigger when that value is a NaN, or a list or map that
+ holds a null.
+ [#4936](https://github.com/memgraph/memgraph/pull/4936)
+- Label disjunctions now return the correct rows, including two
+ `:A|B` tests on the same node and `WHERE n:A OR m:B`.
+ [#4948](https://github.com/memgraph/memgraph/pull/4948)
+- Vertex vector indexes recover correctly after a crash or snapshot
+ load. An empty list `[]` on a vector-indexed property stays `[]`.
+ [#4953](https://github.com/memgraph/memgraph/pull/4953)
+- `valueType()` now returns `VIRTUAL_GRAPH` for the graph that
+ `derive()` returns.
+ [#4954](https://github.com/memgraph/memgraph/pull/4954)
+- Comparing two lists with `<`, `<=`, `>` or `>=` now uses dictionary
+ order instead of raising.
+ [#4956](https://github.com/memgraph/memgraph/pull/4956)
+- Edge vector indexes recover correctly after a crash or snapshot load.
+ [#4959](https://github.com/memgraph/memgraph/pull/4959)
+- On a coordinator without SSO, login now succeeds with a Bolt
+ scheme that is not listed in `--auth-module-mappings`.
+ [#4962](https://github.com/memgraph/memgraph/pull/4962)
+- `REMOVE COORDINATOR` now finishes if the same query is retried after
+ a timeout, instead of failing with "no such id".
+ [#4968](https://github.com/memgraph/memgraph/pull/4968)
+- After `DROP DATABASE ... FORCE`, a Bolt connection whose driver session
+ targeted that database is now closed, so the driver reconnects instead of
+ staying stuck on "Database required". A connection that switched with
+ `USE DATABASE` stays open and can switch to another database.
+ [#4969](https://github.com/memgraph/memgraph/pull/4969)
+- Periodic snapshots on a replica record the correct epoch even when a
+ new MAIN's first commit arrives while the snapshot is being written.
+ [#4982](https://github.com/memgraph/memgraph/pull/4982)
+- `CALL` subqueries with a `UNION` or `UNION ALL` body return the outer
+ rows, make writes in or before the `CALL` visible to later clauses, and
+ expose the body's columns to `RETURN *`, `WITH *` and `CALL (*)`.
+ [#4983](https://github.com/memgraph/memgraph/pull/4983)
+- Rolling back a map-style property write (`SET n = {...}`, `SET n = {}`,
+ `SET n += {...}`, and the same on edges) keeps a vector-indexed
+ embedding and its index entry.
+ [#4991](https://github.com/memgraph/memgraph/pull/4991)
+- A failed `CREATE VECTOR INDEX` or `CREATE VECTOR EDGE INDEX` (for
+ example a dimension mismatch) leaves every node and edge property
+ unchanged and readable.
+ [#4995](https://github.com/memgraph/memgraph/pull/4995)
+- `memgraph_show_instances_total` counts only `SHOW INSTANCES` queries.
+ OpenMetrics scrapes no longer increment it.
+ [#5005](https://github.com/memgraph/memgraph/pull/5005)
+- After `RENAME DATABASE`, metrics scrapes report the new database name
+ without a restart.
+ [#5006](https://github.com/memgraph/memgraph/pull/5006)
+- Memory from abandoned commits is reclaimed again, instead of growing
+ until restart.
+ [#5017](https://github.com/memgraph/memgraph/pull/5017)
+- Signed numbers inside map or list values of system queries, such as
+ `SET GLOBAL PARAMETER p = {a: -1}` or a vector index config, keep
+ their value. Unsupported expressions there, such as `{a: 1+1}`, fail
+ with a clear error.
+ [#5026](https://github.com/memgraph/memgraph/pull/5026)
+- A session whose user, or every one of whose roles, was dropped is denied
+ all privileges until it reconnects, even if a user with the same name is
+ created again. Losing only some roles still removes just those roles'
+ privileges.
+ [#5029](https://github.com/memgraph/memgraph/pull/5029)
+- After `RENAME DATABASE`, TTL keeps deleting expired data on the renamed
+ database, and index builds that run in the background finish.
+ [#5032](https://github.com/memgraph/memgraph/pull/5032)
+- When an edge delete fails part-way and rolls back, the graph no longer
+ keeps duplicate edges. Traversals do not over-count them, and replicas
+ can apply the WAL without a full snapshot catch-up.
+ [#5105](https://github.com/memgraph/memgraph/pull/5105)
+- An instance keeps running when the disk fills up while it writes a
+ snapshot or receives files from main. `CREATE SNAPSHOT` returns a "not
+ enough disk space" error.
+ [#5107](https://github.com/memgraph/memgraph/pull/5107)
+- `CREATE TRIGGER` refuses a statement that is not a Cypher query, such
+ as `CREATE USER` or `CREATE INDEX`. A trigger of that kind stored by an
+ earlier version is skipped with a warning at startup.
+ [#5108](https://github.com/memgraph/memgraph/pull/5108)
+- Vertices can be written to as usual after an edge creation on them
+ fails.
+ [#5111](https://github.com/memgraph/memgraph/pull/5111)
+- Snapshots created with multiple threads complete reliably.
+ [#5126](https://github.com/memgraph/memgraph/pull/5126)
+- When Memgraph runs out of file descriptors, the Bolt, HTTP metrics and
+ WebSocket listeners wait and retry, logging the error at most once every 10
+ seconds, and accept connections again once descriptors are available.
+ [#5129](https://github.com/memgraph/memgraph/pull/5129)
+- The Memgraph systemd service sets `LimitNOFILE=1048576`, and Memgraph raises
+ its soft open files limit to the hard limit at startup. A warning is logged
+ when the limit stays below `65536`. Deployments that kept the default soft
+ limit of 1024 no longer hit `Too many open files` at around 950 connections.
+ [#5132](https://github.com/memgraph/memgraph/pull/5132)
+- A pooled Bolt connection whose database was renamed or dropped gets an
+ "unknown database" client error instead of running its queries on another
+ database. A refused database or impersonation switch leaves the session
+ unchanged.
+ [#5135](https://github.com/memgraph/memgraph/pull/5135)
+- A query with an `IN [...]` list that hits a memory limit fails with
+ "Memory limit exceeded", and the instance keeps running.
+ [#5139](https://github.com/memgraph/memgraph/pull/5139)
+- During a rolling upgrade, a replica on an earlier version keeps users and
+ roles intact. Users, roles, databases and parameters replicate to it again
+ once it is upgraded, and until then `SHOW REPLICAS` shows it as `invalid`.
+ [#5156](https://github.com/memgraph/memgraph/pull/5156)
+
+{🛠️ Improvements
}
+
+- Queries that set a timeout do less bookkeeping while they run, because the
+ limit is checked against a shared clock instead of a per-query timer.
+ [#4662](https://github.com/memgraph/memgraph/pull/4662)
+- With `--experimental-enabled=commit-lock-narrowing`, a new transaction
+ can begin while a slow commit is still writing to the WAL or syncing to
+ replicas, and reads the last fully committed state instead of waiting.
+ The flag is off by default and can only be set at startup.
+ [#4685](https://github.com/memgraph/memgraph/pull/4685)
+- Edge property indexes now answer `STARTS WITH` from the index alone.
+ `CONTAINS`, `ENDS WITH` and regex read only the string run of the index,
+ not every value of the property.
+ [#4671](https://github.com/memgraph/memgraph/pull/4671)
+- Replicas apply incoming writes about twice as fast.
+ [#4714](https://github.com/memgraph/memgraph/pull/4714)
+- `KSHORTEST` path expansion is much faster and uses less memory, especially
+ for larger `k`, and equality and ordering comparisons between values are
+ faster.
+ [#4733](https://github.com/memgraph/memgraph/pull/4733)
+ [#4760](https://github.com/memgraph/memgraph/pull/4760)
+- Commits skip uniqueness and existence constraint checks for writes that
+ cannot affect any constraint, so concurrent writers wait less on validation.
+ [#4769](https://github.com/memgraph/memgraph/pull/4769)
+- Building Memgraph from source can reuse the compiler cache across different
+ checkout paths of the same repository.
+ [#4795](https://github.com/memgraph/memgraph/pull/4795)
+- A pattern comprehension that filters on a value from the outer query
+ can use an index, for example
+ `MATCH (m:Item) RETURN [ (n:Item {id: m.id})--(q) | q ]`.
+ [#4819](https://github.com/memgraph/memgraph/pull/4819)
+- `CONTAINS`, `ENDS WITH` and regex on an indexed string under
+ `USING PARALLEL EXECUTION` no longer scan the whole index column.
+ [#4827](https://github.com/memgraph/memgraph/pull/4827)
+- `path.expand_config` uses less time and memory on a breadth-first walk
+ with `uniqueness: NODE_PATH` or `RELATIONSHIP_PATH` when many paths
+ reach the same node. Walks that never revisit a node can be slower.
+ A memory-limit error now reports how many partial paths were held and
+ suggests a hop bound or `uniqueness: NODE_GLOBAL`.
+ [#4875](https://github.com/memgraph/memgraph/pull/4875)
+- `DISTINCT` and grouping over lists or maps that hold a null run about
+ twice as fast. Results are unchanged.
+ [#4892](https://github.com/memgraph/memgraph/pull/4892)
+- `CONTAINS`, `ENDS WITH` and regex on an indexed edge property are
+ tested while reading the index, so non-matching values are dropped
+ there rather than by a later filter.
+ [#4900](https://github.com/memgraph/memgraph/pull/4900)
+- Cypher queries now parse faster the first time they run, especially
+ relationships with a property map and `exists()` patterns.
+ [#4963](https://github.com/memgraph/memgraph/pull/4963)
+- A slow login (SSO, auth module, or password) no longer blocks queries
+ on other sessions. Permission checks during queries also wait less on
+ the auth lock.
+ [#4966](https://github.com/memgraph/memgraph/pull/4966)
+- Queries that evaluate expressions, including filters, run faster.
+ [#5014](https://github.com/memgraph/memgraph/pull/5014)
+
+### Lab v3.14.0 - October 21st, 2026
+
+
+
+## Previous releases
+
### Memgraph v3.13.2 - October 9th, 2026
{🐞 Bug fixes
}
@@ -661,8 +1077,6 @@ guide.
-## Previous releases
-
### Memgraph v3.12.0 - July 15th, 2026
{⚠️ Breaking changes
}
diff --git a/skills/README.md b/skills/README.md
new file mode 100644
index 000000000..55410a9a1
--- /dev/null
+++ b/skills/README.md
@@ -0,0 +1,13 @@
+# Documentation release skills
+
+Each skill lives in its own folder. Cursor requires the file to be named
+`SKILL.md`, so use the **folder name** to tell them apart.
+
+| Skill (folder) | Purpose |
+| --- | --- |
+| [check-release-milestone](./check-release-milestone/SKILL.md) | Audit milestone docs labels, changelog coverage, and docs PRs before/during a release |
+| [new-release-branch](./new-release-branch/SKILL.md) | Create a release branch, scaffold release-note titles, and open the tracking PR |
+| [pre-release](./pre-release/SKILL.md) | Final pre-publish steps (release dates, direct download links) |
+| [write-changelog-item](./write-changelog-item/SKILL.md) | Write one changelog / release-note bullet and sync the docs tracking PR |
+
+Open `.//SKILL.md` for the full instructions.
diff --git a/skills/pre-release/SKILL.md b/skills/pre-release/SKILL.md
index d86ac6c9e..64a2b7954 100644
--- a/skills/pre-release/SKILL.md
+++ b/skills/pre-release/SKILL.md
@@ -6,7 +6,8 @@ description: Run final pre-release tasks for the documentation site. Updates rel
# Pre-release
Run these steps right before publishing a new documentation release to ensure
-release dates are correct and download links point to the latest Memgraph version.
+release dates are correct and download links point to the latest Memgraph
+version.
## When to use
@@ -42,3 +43,6 @@ replace **every** occurrence of the previous version with the new one.
`3.9.0`) — this covers both patterns in a single pass.
- Verify the result: every URL in the file should reference only the new
version.
+
+Do **not** regenerate or commit `sitemap.xml` as part of a release. The sitemap
+is no longer updated per release.
diff --git a/skills/write-changelog-item/SKILL.md b/skills/write-changelog-item/SKILL.md
index 59c111438..9a143964a 100644
--- a/skills/write-changelog-item/SKILL.md
+++ b/skills/write-changelog-item/SKILL.md
@@ -43,6 +43,30 @@ Avoid:
- Second-person or first-person address ("you get a warning", "you can now",
"we fixed"). State the behavior without naming the reader.
+### Bug fixes
+
+For bug-fix bullets, lead with **what works now** (the correct, expected
+behavior). Do not narrate internal failure modes, races, or data structures
+when those details were never a documented or intentional user-facing
+surface.
+
+Prefer:
+
+- "Periodic snapshots on a replica record the correct epoch even when …"
+- "Edge vector indexes recover correctly after a crash or snapshot load."
+
+Avoid:
+
+- Long "no longer …" chains that explain the old crash path, dangling
+ pointers, or which lock was missing.
+- Contrasting before/after internals the operator could not observe or
+ configure.
+
+If the only user-visible effect was a crash, wrong result, or rejected
+replica, a short statement of the correct outcome is enough. Add a brief
+trigger or context only when it helps an operator recognize the situation
+(for example "while a new MAIN's first commit arrives").
+
The changelog item should be benefit focused (highlight the why). The changelog
item should be in the markdown format, don't remove ticks and markdown links,
don't use bold and italic, use - at the beginning of the item.