Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ existing tools and performs at any scale.
[![Deploy to Heroku](https://www.herokucdn.com/deploy/button.svg)](https://www.heroku.com/deploy/?template=https://github.com/baserow/baserow/tree/master)

```bash
docker run -v baserow_data:/baserow/data -p 80:80 -p 443:443 baserow/baserow:2.2.0
docker run -v baserow_data:/baserow/data -p 80:80 -p 443:443 baserow/baserow:2.2.1
```

![Baserow database screenshot](docs/assets/screenshot.png "Baserow database screenshot")
Expand Down Expand Up @@ -108,7 +108,7 @@ Created by Baserow B.V. - bram@baserow.io.

Distributes under the MIT license. See `LICENSE` for more information.

Version: 2.2.0
Version: 2.2.1

The official repository can be found at https://github.com/baserow/baserow.

Expand Down
2 changes: 1 addition & 1 deletion backend/docker/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -euo pipefail
# ENVIRONMENT VARIABLES USED DIRECTLY BY THIS ENTRYPOINT
# ======================================================

export BASEROW_VERSION="2.2.0"
export BASEROW_VERSION="2.2.1"

# Used by docker-entrypoint.sh to start the dev server
# If not configured you'll receive this: CommandError: "0.0.0.0:" is not a valid port number or address:port pair.
Expand Down
2 changes: 1 addition & 1 deletion backend/src/baserow/config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@
"name": "MIT",
"url": "https://github.com/baserow/baserow/blob/develop/LICENSE",
},
"VERSION": "2.2.0",
"VERSION": "2.2.1",
"SERVE_INCLUDE_SCHEMA": False,
"TAGS": [
{"name": "Settings"},
Expand Down
4 changes: 3 additions & 1 deletion backend/src/baserow/contrib/database/rows/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ def record_history_from_rows_action(
row_history_entries = row_history_provider.get_row_history(user, action)

if row_history_entries:
row_history_entries = RowHistory.objects.bulk_create(row_history_entries)
row_history_entries = RowHistory.objects.bulk_create(
row_history_entries, batch_size=1000
)
for table_id, per_table_row_history_entries in groupby(
row_history_entries, lambda e: e.table_id
):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ def _backup_everything_but_user_tables(
f"--exclude-table={MultipleSelectField.THROUGH_DATABASE_TABLE_PREFIX}*",
f"--exclude-table={USER_TABLE_DATABASE_NAME_PREFIX}*",
f"--exclude-table={LinkRowField.THROUGH_DATABASE_TABLE_PREFIX}*",
"--exclude-table=field_*_seq",
f"--file={temporary_directory_name}/everything_but_user_tables/",
]
self._run_command_in_sub_process(
Expand Down
37 changes: 37 additions & 0 deletions backend/src/baserow/test_utils/pytest_conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import sys
import threading
import uuid
from contextlib import ExitStack, contextmanager
from datetime import date, datetime
from decimal import Decimal
Expand Down Expand Up @@ -38,6 +39,7 @@
from baserow.core.exceptions import PermissionDenied
from baserow.core.jobs.registries import job_type_registry
from baserow.core.permission_manager import CorePermissionManagerType
from baserow.core.psycopg import is_psycopg3, psycopg
from baserow.core.services.dispatch_context import DispatchContext
from baserow.core.services.utils import ServiceAdhocRefinements
from baserow.core.trash.trash_types import WorkspaceTrashableItemType
Expand Down Expand Up @@ -219,6 +221,41 @@ def environ():
os.environ[key] = value


@pytest.fixture()
def temporary_database():
"""
Creates a temporary PostgreSQL database with a unique name,
yields its name, and drops it on teardown.
"""

settings = connection.settings_dict
db_name = f"test_tmp_{uuid.uuid4().hex[:10]}"

def _connect_autocommit():
conn = psycopg.connect(
host=settings["HOST"],
port=settings["PORT"],
dbname=settings["NAME"],
user=settings["USER"],
password=settings["PASSWORD"],
)
if is_psycopg3:
conn.autocommit = True
else:
conn.set_isolation_level(0) # ISOLATION_LEVEL_AUTOCOMMIT
return conn

admin_conn = _connect_autocommit()
admin_conn.cursor().execute(f"CREATE DATABASE {db_name}")
admin_conn.close()
try:
yield db_name
finally:
cleanup_conn = _connect_autocommit()
cleanup_conn.cursor().execute(f"DROP DATABASE IF EXISTS {db_name}")
cleanup_conn.close()


@pytest.fixture()
def print_sql():
with CaptureQueriesContext(connection) as ctx:
Expand Down
2 changes: 1 addition & 1 deletion backend/src/baserow/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = "2.2.0"
VERSION = "2.2.1"
93 changes: 48 additions & 45 deletions backend/tests/baserow/core/management/test_backup_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,72 @@
from pathlib import Path
from unittest.mock import call, patch

from django.db import connection, transaction
from django.db import connection

import pytest
from freezegun import freeze_time

from baserow.contrib.database.table.models import Table
from baserow.core.management.backup.backup_runner import BaserowBackupRunner
from baserow.core.management.backup.exceptions import InvalidBaserowBackupArchive
from baserow.core.psycopg import is_psycopg3
from baserow.core.trash.handler import TrashHandler
from baserow.core.psycopg import is_psycopg3, psycopg
from baserow.test_utils.helpers import setup_interesting_test_table


@pytest.mark.django_db(transaction=True)
@pytest.mark.once_per_day_in_ci
def test_can_backup_and_restore_baserow_reverting_changes(data_fixture, environ):
def test_can_backup_and_restore_baserow_reverting_changes(
data_fixture, environ, temporary_database
):
host = connection.settings_dict["HOST"]
dbname = connection.settings_dict["NAME"]
username = connection.settings_dict["USER"]
port = connection.settings_dict["PORT"]
password = connection.settings_dict["PASSWORD"]
environ["PGPASSWORD"] = password

runner = BaserowBackupRunner(
host=connection.settings_dict["HOST"],
database=connection.settings_dict["NAME"],
username=connection.settings_dict["USER"],
port=connection.settings_dict["PORT"],
host=host,
database=dbname,
username=username,
port=port,
jobs=1,
)
environ["PGPASSWORD"] = connection.settings_dict["PASSWORD"]

table, fields, rows = data_fixture.build_table(
columns=[
("Name", "text"),
],
rows=[["A"], ["B"], ["C"], ["D"]],
)
table_to_delete, _, _ = data_fixture.build_table(
columns=[
("Name", "text"),
],
rows=[["A"], ["B"], ["C"], ["D"]],
)
deleted_table_name = table_to_delete.get_database_table_name()
table, _, _, _, context = setup_interesting_test_table(data_fixture)

model = table.get_model()
original_row_count = model.objects.count()
user_table_name = table.get_database_table_name()

with tempfile.TemporaryDirectory() as temporary_directory_name:
backup_loc = temporary_directory_name + "/backup.tar.gz"
# With a batch size of 1 we expect 3 separate pg_dumps to be run.
runner.backup_baserow(backup_loc, 1)
runner.backup_baserow(backup_loc, batch_size=1)
assert Path(backup_loc).is_file()

model = table.get_model(attribute_names=True)

# Add a new row after we took the back-up that we want to reset by restoring.
model.objects.create(**{"name": "E"})
# Delete a table to check it is recreated.
with transaction.atomic():
TrashHandler.permanently_delete(table_to_delete)

assert model.objects.count() == 5
assert Table.objects.count() == 1
assert deleted_table_name not in connection.introspection.table_names()
restore_runner = BaserowBackupRunner(
host=host,
database=temporary_database,
username=username,
port=port,
jobs=1,
)
restore_runner.restore_baserow(backup_loc)

# --clean will make pg_restore overwrite existing db objects, not safe for
# general usage as it will not delete tables/relations created after the
# backup.
runner.restore_baserow(backup_loc, ["--clean", "--if-exists"])
with psycopg.connect(
host=host,
port=port,
dbname=temporary_database,
user=username,
password=password,
) as verify_conn:
with verify_conn.cursor() as cur:
cur.execute(f"SELECT COUNT(*) FROM {user_table_name}")
assert cur.fetchone()[0] == original_row_count

# The row we made after the backup has gone
assert model.objects.count() == 4
# The table we deleted has been restored
assert Table.objects.count() == 2
assert deleted_table_name in connection.introspection.table_names()
autonumber_field_id = context["name_to_field_id"]["autonumber"]
seq_name = f"field_{autonumber_field_id}_seq"
cur.execute(f"SELECT last_value FROM {seq_name}")
assert cur.fetchone()[0] > 0


@patch("tempfile.TemporaryDirectory")
Expand Down Expand Up @@ -119,6 +119,7 @@ def test_backup_baserow_dumps_database_in_batches(
"--exclude-table=database_multipleselect_*",
"--exclude-table=database_table_*",
"--exclude-table=database_relation_*",
"--exclude-table=field_*_seq",
"--file=/fake_tmp_dir/everything_but_user_tables/",
]
),
Expand Down Expand Up @@ -200,6 +201,7 @@ def test_can_change_num_jobs_and_insert_extra_args_for_baserow_backup(
"--exclude-table=database_multipleselect_*",
"--exclude-table=database_table_*",
"--exclude-table=database_relation_*",
"--exclude-table=field_*_seq",
"--file=/fake_tmp_dir/everything_but_user_tables/",
extra_arg,
]
Expand Down Expand Up @@ -597,6 +599,7 @@ def a_pg_dump_for_everything_else():
"--exclude-table=database_multipleselect_*",
"--exclude-table=database_table_*",
"--exclude-table=database_relation_*",
"--exclude-table=field_*_seq",
"--file=/fake_tmp_dir/everything_but_user_tables/",
]
)
Expand Down
4 changes: 2 additions & 2 deletions backend/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,47 @@
# Changelog

## Released 2.2.1

### New features
* [Builder] Add 'clear' button to the date input element. [#5202](https://github.com/baserow/baserow/issues/5202)
* [Database] Add file attachment support for Anthropic, Mistral and other AI field providers [#5154](https://github.com/baserow/baserow/issues/5154)

### Bug fixes
* [Database] AI file field is now validated during import [#3090](https://github.com/baserow/baserow/issues/3090)
* [Database] Exclude autonumber sequences from schema dump to prevent restore failure [#3855](https://github.com/baserow/baserow/issues/3855)
* [Database] Fix negative sign in formula output disappearing when a cell is selected [#4323](https://github.com/baserow/baserow/issues/4323)
* [Database] Fix AI field not correctly handling small text files [#5082](https://github.com/baserow/baserow/issues/5082)
* [Core] Fix import workspace crashes on retry after failed backend import [#5140](https://github.com/baserow/baserow/issues/5140)
* [Database] Fix import of unknown ai generative types [#5163](https://github.com/baserow/baserow/issues/5163)
* [Database] Fix notification panel crash when a periodic_data_sync_deactivated notification is rendered [#5171](https://github.com/baserow/baserow/issues/5171)
* [Database] Fix link-row group-by crash in grid view with more than 20 linked items [#5184](https://github.com/baserow/baserow/issues/5184)
* [Builder] prevent OIDC/SAML form crashes when editing a user source in the application builder [#5226](https://github.com/baserow/baserow/issues/5226)
* [Core] Fix RichTextEditor display in row comment notifications [#5230](https://github.com/baserow/baserow/issues/5230)
* [Core] Add database connection health check.
* [Database] Create row history entries in batches to avoid memory spikes
* [Integration] Ensure Local Baserow Upsert integration handles field constraint errors.
* [Database] Fix the database API docs page crashing when opening the database list from the header. [#5212](https://github.com/baserow/baserow/issues/5212)
* [Database] Fix database filters crashing when select option cells are temporarily null during row evaluation. [#5217](https://github.com/baserow/baserow/issues/5217)
* [Core] Custom enterprise logo can be set again
* [Database] Fix field drag-and-drop placeholder being offset by the group-by column width when a group by is active.
* [Database] Fix grid field quick edit crashing when the field update context is unavailable because the user lacks field update permission. [#5216](https://github.com/baserow/baserow/issues/5216)
* [Database] fix import preview
* [Core] Fix password reset tokens not being invalidated after use, allowing persistent account takeover. Tokens are now single-use, token expiry reduced to 1 hour, and a confirmation email is sent on every password change. [#5165](https://github.com/baserow/baserow/issues/5165)
* [Database] Fix rollup and count fields crashing when a stale in-memory relation points to a deleted field during formula recalculation. [#5214](https://github.com/baserow/baserow/issues/5214)
* [Core] Make concurrent index migrations idempotent so they can be re-run after a partial failure.
* [Database] Fix template sync failing when importing enterprise field rules without a license.
* [Automation] Fixed a bug that caused a crash due to a race condition that could happen if a node is deleted while it is being dispatched.
* [Builder] Fixes if() formulas in the Application Builder silently rendering nothing when the condition references an empty field
* [Integration] Improved error handling for the AI Form.

### Refactors
* [Automation] Improve rate limiting to support multiple time frames
* [Core] Optimize rate limiting: cache user and settings lookups and reorganize throttling code.

### Breaking API changes
* [Core] Workspace invitations no longer support custom messages, and the `BASEROW_MAX_PENDING_WORKSPACE_INVITES` env var has been removed.


## Released 2.2.0

### New features
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "bug",
"message": "Exclude autonumber sequences from schema dump to prevent restore failure",
"issue_origin": "github",
"issue_number": 3855,
"domain": "database",
"bullet_points": [],
"created_at": "2026-04-21"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "bug",
"message": "Create row history entries in batches to avoid memory spikes",
"issue_origin": "github",
"issue_number": null,
"domain": "database",
"bullet_points": [],
"created_at": "2026-04-22"
}
9 changes: 9 additions & 0 deletions changelog/entries/2.2.1/bug/fix_import_preview.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "bug",
"message": "fix import preview",
"issue_origin": "github",
"issue_number": null,
"domain": "database",
"bullet_points": [],
"created_at": "2026-04-22"
}
4 changes: 4 additions & 0 deletions changelog/releases.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"releases": [
{
"name": "2.2.1",
"created_at": "2026-04-22"
},
{
"name": "2.2.0",
"created_at": "2026-04-08"
Expand Down
Loading
Loading