flask-postgresql-audit 1.1.2__tar.gz → 1.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/CHANGES.md +4 -0
  2. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/PKG-INFO +1 -1
  3. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/__init__.py +1 -1
  4. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/alembic/entities.py +10 -0
  5. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/alembic/setup.py +1 -0
  6. flask_postgresql_audit-1.1.3/flask_postgresql_audit/templates/jsonb_diff.sql +42 -0
  7. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/tests/test_operators.py +48 -0
  8. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/.gitignore +0 -0
  9. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/LICENSE +0 -0
  10. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/README.md +0 -0
  11. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/alembic/__init__.py +0 -0
  12. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/alembic/schema.py +0 -0
  13. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/base.py +0 -0
  14. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/core.py +0 -0
  15. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/extensions/__init__.py +0 -0
  16. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/extensions/alembic_utils/__init__.py +0 -0
  17. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/extensions/alembic_utils/pg_aggregate.py +0 -0
  18. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/extensions/document_staging/__init__.py +0 -0
  19. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/extensions/document_staging/enum.py +0 -0
  20. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/extensions/document_staging/listener.py +0 -0
  21. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/extensions/document_staging/model.py +0 -0
  22. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/models.py +0 -0
  23. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/templates/audit_trigger_delete.sql +0 -0
  24. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/templates/audit_trigger_insert.sql +0 -0
  25. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/templates/audit_trigger_update.sql +0 -0
  26. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/templates/create_activity.sql +0 -0
  27. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/templates/get_pk_values.sql +0 -0
  28. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/templates/get_setting.sql +0 -0
  29. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/templates/get_table_relid.sql +0 -0
  30. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/templates/jsonb_array_subtract.sql +0 -0
  31. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/templates/jsonb_subtract.sql +0 -0
  32. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/typing.py +0 -0
  33. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/flask_postgresql_audit/utils.py +0 -0
  34. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/pyproject.toml +0 -0
  35. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/tests/__init__.py +0 -0
  36. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/tests/app.py +0 -0
  37. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/tests/conftest.py +0 -0
  38. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/tests/migrations/README +0 -0
  39. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/tests/migrations/alembic.ini +0 -0
  40. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/tests/migrations/env.py +0 -0
  41. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/tests/migrations/script.py.mako +0 -0
  42. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/tests/test_defaults.py +0 -0
  43. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/tests/test_document_staging.py +0 -0
  44. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/tests/test_pg_aggregate.py +0 -0
  45. {flask_postgresql_audit-1.1.2 → flask_postgresql_audit-1.1.3}/tests/utils.py +0 -0
@@ -3,6 +3,10 @@
3
3
  Here you can see the full list of changes between each Flask-PostgreSQL-Audit
4
4
  release.
5
5
 
6
+ ## 1.1.3 (2026-08-21)
7
+
8
+ - Add `jsonb_diff()` function to recursively compute differences across JSONB objects and arrays.
9
+
6
10
  ## 1.1.2 (2026-08-21)
7
11
 
8
12
  - Add dedicated `jsonb_array_subtract()` function to support set difference operations on JSONB arrays.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: flask-postgresql-audit
3
- Version: 1.1.2
3
+ Version: 1.1.3
4
4
  Summary: Versioning and auditing extension for PostgreSQL and SQLAlchemy.
5
5
  Project-URL: Homepage, https://github.com/higamigu/flask-postgresql-audit
6
6
  Author-email: Higamigu <higamigu@gmail.com>, Konsta Vesterinen <konsta@fastmonkeys.com>
@@ -1,4 +1,4 @@
1
1
  # ruff: noqa: F401
2
2
  from .core import Audit, PostgreSQLAudit
3
3
 
4
- __version__ = "1.1.2"
4
+ __version__ = "1.1.3"
@@ -90,3 +90,13 @@ def jsonb_array_subtract_factory(**context):
90
90
  signature="jsonb_array_subtract(arg1 jsonb, arg2 jsonb)",
91
91
  definition=sql["definition"],
92
92
  )
93
+
94
+
95
+ def jsonb_diff_factory(**context):
96
+ context.setdefault("jsonb_subtract_join_type", "LEFT")
97
+ sql = parse_template("jsonb_diff.sql", **context)
98
+ return PGFunction(
99
+ schema=context["schema_name"],
100
+ signature="jsonb_diff(arg1 jsonb, arg2 jsonb)",
101
+ definition=sql["definition"],
102
+ )
@@ -129,6 +129,7 @@ def register_core_entities(audit: "PostgreSQLAudit"):
129
129
  audit.pg_audit_entities.add(entities.get_setting_factory(**audit.context))
130
130
  audit.pg_audit_entities.add(entities.jsonb_subtract_factory(**audit.context))
131
131
  audit.pg_audit_entities.add(entities.jsonb_array_subtract_factory(**audit.context))
132
+ audit.pg_audit_entities.add(entities.jsonb_diff_factory(**audit.context))
132
133
  audit.pg_audit_entities.add(entities.create_activity_factory(**audit.context))
133
134
 
134
135
 
@@ -0,0 +1,42 @@
1
+ CREATE FUNCTION ${schema_prefix}jsonb_diff(arg1 jsonb, arg2 jsonb) RETURNS jsonb AS $$
2
+ SELECT
3
+ CASE
4
+ WHEN jsonb_typeof(arg1) = 'array' AND jsonb_typeof(arg2) = 'array' THEN
5
+ ${schema_prefix}jsonb_array_subtract(arg1, arg2)
6
+ WHEN jsonb_typeof(arg1) = 'object' AND jsonb_typeof(arg2) = 'object' THEN
7
+ COALESCE(
8
+ (
9
+ SELECT json_object_agg(
10
+ KEY,
11
+ CASE
12
+ WHEN new_type = 'object' AND old_type = 'object'
13
+ THEN ${schema_prefix}jsonb_diff(new_value, old_value)
14
+ WHEN new_type = 'array' AND old_type = 'array'
15
+ THEN ${schema_prefix}jsonb_diff(new_value, old_value)
16
+ ELSE new_value
17
+ END
18
+ )
19
+ FROM (
20
+ SELECT KEY, VALUE AS new_value, jsonb_typeof(VALUE) AS new_type
21
+ FROM jsonb_each(arg1)
22
+ ) AS NEW
23
+ ${jsonb_subtract_join_type} OUTER JOIN (
24
+ SELECT KEY, VALUE AS old_value, jsonb_typeof(VALUE) AS old_type
25
+ FROM jsonb_each(arg2)
26
+ ) AS OLD
27
+ USING (KEY)
28
+ WHERE new_value IS DISTINCT FROM old_value
29
+ )::jsonb,
30
+ '{}'::jsonb
31
+ )
32
+ WHEN jsonb_typeof(arg1) = 'array' THEN
33
+ ${schema_prefix}jsonb_array_subtract(arg1, arg2)
34
+ WHEN jsonb_typeof(arg1) = 'object' AND (arg2 IS NULL OR jsonb_typeof(arg2) != 'object') THEN
35
+ arg1
36
+ WHEN arg1 IS DISTINCT FROM arg2 THEN
37
+ arg1
38
+ ELSE
39
+ NULL
40
+ END
41
+ $$ LANGUAGE SQL;
42
+
@@ -63,3 +63,51 @@ def test_jsonb_array_subtract(test_client, arg1, arg2, result):
63
63
  )
64
64
  == result
65
65
  )
66
+
67
+
68
+ JSONB_DIFF = "SELECT {schema}jsonb_diff((:new)::jsonb, (:old)::jsonb)"
69
+
70
+
71
+ @pytest.mark.parametrize(
72
+ ("old", "new", "result"),
73
+ (
74
+ ({}, {}, {}),
75
+ ({"age": 13}, {"age": 14}, {"age": 14}),
76
+ ({"name": "Someone"}, {"name": "Someone"}, {}),
77
+ (
78
+ {"a": {"b": 1, "c": 2}},
79
+ {"a": {"b": 1, "c": 3}},
80
+ {"a": {"c": 3}},
81
+ ),
82
+ ([2], [1, 2, 3], [1, 3]),
83
+ ([1, 2, 3], [1, 2, 3], []),
84
+ (
85
+ {"tags": [2], "name": "a"},
86
+ {"tags": [1, 2, 3], "name": "a"},
87
+ {"tags": [1, 3]},
88
+ ),
89
+ (
90
+ {"tags": [1, 2], "name": "a"},
91
+ {"tags": [1, 2], "name": "a"},
92
+ {},
93
+ ),
94
+ (
95
+ {"data": {"items": ["b"], "meta": {"v": 1}}},
96
+ {"data": {"items": ["a", "b", "c"], "meta": {"v": 2}}},
97
+ {"data": {"items": ["a", "c"], "meta": {"v": 2}}},
98
+ ),
99
+ (
100
+ [{"id": 1}],
101
+ [{"id": 1}, {"id": 2}],
102
+ [{"id": 2}],
103
+ ),
104
+ ),
105
+ )
106
+ def test_jsonb_diff(test_client, old, new, result):
107
+ assert (
108
+ db.session.scalar(
109
+ text(JSONB_DIFF.format(schema=audit.context["schema_prefix"])),
110
+ {"old": json.dumps(old), "new": json.dumps(new)},
111
+ )
112
+ == result
113
+ )