surreal-orm-lite 0.31.0__tar.gz → 0.31.2__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.
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/CHANGELOG.md +102 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/PKG-INFO +55 -24
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/README.md +54 -23
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/pyproject.toml +1 -1
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/__init__.py +1 -1
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/_sdk.py +6 -1
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/concurrency.py +7 -2
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/connection_manager.py +11 -5
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/decoding.py +112 -16
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/model_base.py +2 -2
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/transaction.py +206 -43
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/.gitignore +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/LICENSE +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/Makefile +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/aggregations.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/auth.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/cache.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/changefeed.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/connection_config.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/constants.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/define_parser.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/edge.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/embedded.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/encoding.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/enum.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/exceptions.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/functions.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/geo.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/introspection.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/live.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/model_auth.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/model_generator.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/py.typed +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/q.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/query_set.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/routing.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/schema.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/schema_state.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/signals.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/subquery.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/surql_literal.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/testing/__init__.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/testing/database.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/testing/fixtures.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/time_travel.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/types.py +0 -0
- {surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/utils.py +0 -0
|
@@ -5,6 +5,108 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.31.2] - 2026-10-19
|
|
9
|
+
|
|
10
|
+
Transaction errors give the server's reason and the failed statement's position, without the
|
|
11
|
+
server's excerpt of the statement, and a buffered commit raises `SurrealDbError` on the embedded
|
|
12
|
+
engine too (#278). No new public API.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **Transaction errors give the server's reason and the statement's position, without the
|
|
17
|
+
server's excerpt of the statement** (#278). When the server refused the query as a whole (a
|
|
18
|
+
statement it cannot parse), the error read "Transaction failed: unrecognized query_raw()
|
|
19
|
+
response shape: {…}" and quoted the server's raw response, its excerpt of the statement
|
|
20
|
+
included. It now gives the server's reason and the position:
|
|
21
|
+
|
|
22
|
+
- at a buffered commit (SurrealDB 2.x, HTTP, the embedded engine), "Transaction failed: the
|
|
23
|
+
server refused statement 2 of 3: … (at line 1, column 7 of the statement)", the line and column
|
|
24
|
+
of the statement as written, before the ORM renames its `$name` parameters for the batch; a
|
|
25
|
+
delimiter left unclosed names the statement it opens in, even when the server first reports it
|
|
26
|
+
in a later statement or on the batch's `COMMIT` line;
|
|
27
|
+
- for a write or a definition in an interactive transaction (SurrealDB 3.x over WebSocket),
|
|
28
|
+
"Transaction failed: the server refused the statement: … (at line 1, column 7 of the
|
|
29
|
+
statement)";
|
|
30
|
+
- a failed statement's error, "Transaction failed and rolled back: …", keeps only the first line
|
|
31
|
+
of a reason that quotes the query;
|
|
32
|
+
- a response of any other shape raises "Transaction failed: unrecognized query_raw() response
|
|
33
|
+
shape (<type>)", without the response.
|
|
34
|
+
|
|
35
|
+
The server's full text stays reachable as the error's `__context__`, which a formatted traceback
|
|
36
|
+
does not print. A read in a transaction still raises the SDK's own error, as it does outside a
|
|
37
|
+
transaction.
|
|
38
|
+
|
|
39
|
+
- **A buffered commit on the embedded engine raises `SurrealDbError`** (#278). The engine reports a
|
|
40
|
+
query it cannot parse as a bare `RuntimeError`, which propagated out of the `transaction()` block;
|
|
41
|
+
the commit now raises `SurrealDbError`, or `SurrealDbConflictError` for a retryable conflict, as
|
|
42
|
+
on a server.
|
|
43
|
+
- **A statement holding conflict words is not taken for a conflict** (#278). When the server could
|
|
44
|
+
not parse a statement holding words such as "write conflict", `is_conflict_error()` recognised
|
|
45
|
+
the transaction's error as a retryable conflict, so `retry_on_conflict` retried it, and an
|
|
46
|
+
interactive transaction's cancellation error was a `SurrealDbConflictError`. Conflicts are now
|
|
47
|
+
recognised from the server's reason, not from its excerpt of the statement.
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- **The cancellation error names the failed statement's error type** (#278). Since v0.30.3,
|
|
52
|
+
leaving an interactive `transaction()` block after a failed statement, or an explicit
|
|
53
|
+
`tx.commit()`, raises "The transaction was cancelled: a statement in it failed; nothing was
|
|
54
|
+
committed (…)", whose parenthesis repeated the failed statement's error text. The parenthesis now
|
|
55
|
+
holds that error's type name, for example `(SurrealDbError)`. The error itself is still chained
|
|
56
|
+
as `__cause__`, and a retryable conflict still raises `SurrealDbConflictError`, so
|
|
57
|
+
`retry_on_conflict` behaves as before.
|
|
58
|
+
- **`is_conflict_error()` follows the chain a traceback prints** (#278): a context suppressed with
|
|
59
|
+
`raise … from None` is no longer read, since the ORM keeps the server's full text there, which
|
|
60
|
+
may quote the statement; `__cause__` and an implicit `__context__` are still followed. A conflict
|
|
61
|
+
your code re-raises as another exception with `raise … from None` is therefore no longer
|
|
62
|
+
recognised, nor retried by `retry_on_conflict`: chain it (`raise … from exc`, or `raise …`
|
|
63
|
+
without `from` inside the `except` block), or raise a `SurrealDbConflictError`, to keep it
|
|
64
|
+
retryable.
|
|
65
|
+
|
|
66
|
+
### Notes
|
|
67
|
+
|
|
68
|
+
- The v0.31.0 caution on the DDL appliers with `tx=` (#278), that an error raised when the
|
|
69
|
+
transaction ends can carry the server's excerpt of the statement, no longer applies: that error
|
|
70
|
+
now gives the server's reason and the statement's position instead. README sections 11 and 32
|
|
71
|
+
and the embedded-engine table say so.
|
|
72
|
+
- Same behaviour on SurrealDB 2.x, 3.x and the embedded engine. Server pins unchanged: tested
|
|
73
|
+
against SurrealDB 3.3.4 and 2.7.0 and on the embedded engine, plus 3.1.5 for backward
|
|
74
|
+
compatibility.
|
|
75
|
+
|
|
76
|
+
## [0.31.1] - 2026-10-19
|
|
77
|
+
|
|
78
|
+
A datetime outside years 1-9999 no longer fails the response it is in (#279). SurrealDB stores
|
|
79
|
+
datetimes far beyond the years a Python `datetime` holds, and the ORM now reads them back as the
|
|
80
|
+
server's own text. No new public API.
|
|
81
|
+
|
|
82
|
+
### Fixed
|
|
83
|
+
|
|
84
|
+
- **Reading a datetime before year 1 or after year 9999** (#279). SurrealDB stores datetimes from
|
|
85
|
+
year -262143 to +262142, on 2.x and 3.x, but the surrealdb SDK 2.0.0 decodes a datetime into a
|
|
86
|
+
Python `datetime`, which holds years 1-9999 only: one such value failed the whole response it
|
|
87
|
+
was in, whether it came from `get()`, `filter()`, `raw_query()` or a live notification. The
|
|
88
|
+
ORM's clients now read it as its RFC 3339 text, the one SurrealDB gives it with `<string>`:
|
|
89
|
+
`"0000-06-01T00:00:00Z"`, `"-0001-01-01T00:00:00Z"`, `"+10000-01-01T00:00:00.500Z"` (a sign and
|
|
90
|
+
at least four digits outside years 0000-9999, every nanosecond kept). An untyped field holds
|
|
91
|
+
that string; a `datetime` field refuses it with a `ValidationError` naming the field, so `get()`
|
|
92
|
+
raises it and `filter().exec()` and `raw_query()` return such rows as dicts, as for any row
|
|
93
|
+
that does not validate. Written back through the ORM, the value is stored as that string. A
|
|
94
|
+
datetime within years 1-9999, before 1970 included, reads as the SDK's `datetime`, as before.
|
|
95
|
+
|
|
96
|
+
### Notes
|
|
97
|
+
|
|
98
|
+
- As for zero durations, native sets and degenerate geometries, only the clients the ORM opens
|
|
99
|
+
(`SurrealDBConnectionManager.get_client()`, `ephemeral_client()`, the embedded engine's
|
|
100
|
+
included) read these values; an `AsyncSurreal` client the application builds itself keeps the
|
|
101
|
+
SDK's behaviour. The ORM leaves them to the SDK as soon as an installed SDK decodes them itself.
|
|
102
|
+
README: "Values the SDK cannot read" and a new row of the 2.x/3.x behaviour table.
|
|
103
|
+
- The function catalogue test (`TestFunctionEnumsE2E`) calls `rand::time()` between 2000 and 2030
|
|
104
|
+
and checks the result. Unbounded, it drew a year-0 datetime in about one call in 7,400 (27 in
|
|
105
|
+
200,000 on each line), which made the test fail at random.
|
|
106
|
+
- Same behaviour on SurrealDB 2.x, 3.x and the embedded engine. Server pins unchanged: tested
|
|
107
|
+
against SurrealDB 3.3.4 and 2.7.0 and on the embedded engine, plus 3.1.5 for backward
|
|
108
|
+
compatibility.
|
|
109
|
+
|
|
8
110
|
## [0.31.0] - 2026-10-19
|
|
9
111
|
|
|
10
112
|
Server-side events and DDL helpers: the ORM writes and applies `DEFINE EVENT` and
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: surreal-orm-lite
|
|
3
|
-
Version: 0.31.
|
|
3
|
+
Version: 0.31.2
|
|
4
4
|
Summary: Lightweight Django-style ORM for SurrealDB using the official Python SDK. Async support with Pydantic validation.
|
|
5
5
|
Project-URL: Homepage, https://github.com/EulogySnowfall/SurrealDB-ORM-lite
|
|
6
6
|
Project-URL: Documentation, https://github.com/EulogySnowfall/SurrealDB-ORM-lite
|
|
@@ -732,10 +732,11 @@ cancelled when the block ends, nothing of the block is committed, and leaving th
|
|
|
732
732
|
`SurrealDbError` (`SurrealDbConflictError` for a retryable conflict, so `retry_on_conflict` still
|
|
733
733
|
retries the block). A buffered transaction fails as a whole at commit; an interactive one raises
|
|
734
734
|
the failed statement's error at once, then, when the block ends, "The transaction was cancelled: a
|
|
735
|
-
statement in it failed; nothing was committed (
|
|
736
|
-
`tx.commit()` after a failure raises the same error. When
|
|
737
|
-
that exception wins, as before. Reads the ORM answers itself
|
|
738
|
-
that does not exist reads as no rows, and `get()` raises
|
|
735
|
+
statement in it failed; nothing was committed (SurrealDbError)", which names the type of that
|
|
736
|
+
error and is chained to it. An explicit `tx.commit()` after a failure raises the same error. When
|
|
737
|
+
the block's own exception propagates, that exception wins, as before. Reads the ORM answers itself
|
|
738
|
+
are not failed statements: a table that does not exist reads as no rows, and `get()` raises
|
|
739
|
+
`SurrealDbNotFoundError`.
|
|
739
740
|
|
|
740
741
|
```python
|
|
741
742
|
async with SurrealDBConnectionManager.transaction() as tx:
|
|
@@ -747,6 +748,18 @@ async with SurrealDBConnectionManager.transaction() as tx:
|
|
|
747
748
|
# leaving the block raised SurrealDbError: alice was not saved either
|
|
748
749
|
```
|
|
749
750
|
|
|
751
|
+
**Transaction errors leave out the server's excerpt of the statement** (v0.31.2). When the server
|
|
752
|
+
refuses the query as a whole (a statement it cannot parse), the error gives the server's reason and
|
|
753
|
+
the position, without the server's excerpt of the statement: a buffered commit raises "Transaction
|
|
754
|
+
failed: the server refused statement 2 of 3: … (at line 1, column 7 of the statement)", the
|
|
755
|
+
embedded engine included, and a write or a definition in an interactive transaction raises
|
|
756
|
+
"Transaction failed: the server refused the statement: … (at line 1, column 7 of the statement)".
|
|
757
|
+
The line and column are those of the statement as written, before the ORM renames its `$name`
|
|
758
|
+
parameters for the batch, and a delimiter left unclosed names the statement it opens in, even when
|
|
759
|
+
the server stops in a later statement. The server's full text stays reachable as the error's
|
|
760
|
+
`__context__`, which a traceback does not print. A read in a transaction is not affected: it raises
|
|
761
|
+
the SDK's own error, as it does outside a transaction.
|
|
762
|
+
|
|
750
763
|
### 12. Upsert & get_or_create / update_or_create
|
|
751
764
|
|
|
752
765
|
```python
|
|
@@ -891,7 +904,11 @@ except SurrealDbConflictError:
|
|
|
891
904
|
- Total attempts = `max_retries + 1`; after they are exhausted the conflict is re-raised as
|
|
892
905
|
`SurrealDbConflictError`. The numeric parameters are validated at decoration time.
|
|
893
906
|
- Detection anchors on SurrealDB's own retryable marker ("This transaction can be retried"), so
|
|
894
|
-
a non-retryable failure (e.g. a duplicate-key error) is **not** retried.
|
|
907
|
+
a non-retryable failure (e.g. a duplicate-key error) is **not** retried. `is_conflict_error()`
|
|
908
|
+
reads the exception and its chain as a traceback prints it: a context suppressed with
|
|
909
|
+
`raise … from None` is not read (v0.31.2), so a conflict you re-raise as another exception with
|
|
910
|
+
`raise … from None` is no longer recognised: chain it (`raise … from exc`, or `raise …` without
|
|
911
|
+
`from` inside the `except` block), or raise a `SurrealDbConflictError`.
|
|
895
912
|
- The exception type is identical on 2.6.x and 3.x; conflicts simply arise more often on 3.x
|
|
896
913
|
(optimistic MVCC) than on 2.6.x (see the behaviour table), except on change-feed tables, where
|
|
897
914
|
2.x refuses concurrent writes that 3.x accepts (see [section 22](#22-change-feeds-changes)).
|
|
@@ -2499,31 +2516,42 @@ an `IntEnum` gives the value), so use `.value` to build a key, a URL or a log li
|
|
|
2499
2516
|
#### Values the SDK cannot read
|
|
2500
2517
|
|
|
2501
2518
|
The surrealdb SDK 2.0.0 cannot decode a zero duration, a SurrealDB 3.x native set (a
|
|
2502
|
-
`TYPE set<…>` column on SurrealDB 3.3, see [Sets](#sets))
|
|
2503
|
-
[Geometries the SDK cannot read](#geometries-the-sdk-cannot-read))
|
|
2504
|
-
|
|
2505
|
-
|
|
2519
|
+
`TYPE set<…>` column on SurrealDB 3.3, see [Sets](#sets)), a degenerate geometry (see
|
|
2520
|
+
[Geometries the SDK cannot read](#geometries-the-sdk-cannot-read)) or a datetime outside years
|
|
2521
|
+
1-9999, the years a Python `datetime` holds (SurrealDB stores years -262143 to +262142, on both
|
|
2522
|
+
lines): one such value fails the whole response it is in. The clients the ORM opens read them
|
|
2523
|
+
anyway, through a hook around the SDK's own CBOR decoder that handles only these values; everything
|
|
2524
|
+
else reads exactly as the SDK reads it:
|
|
2506
2525
|
|
|
2507
2526
|
- a zero duration reads as zero: `timedelta(0)` or `Duration(0)` in a typed field, the SDK's
|
|
2508
2527
|
`Duration(0)` in an untyped one;
|
|
2509
2528
|
- a native set reads as a Python `set`, which `set[X]` and `frozenset[X]` fields validate, or as a
|
|
2510
2529
|
`list` when its items are unhashable (record ids, objects, nested sets) or two of them are equal
|
|
2511
|
-
in Python, like `1` and `true`: every item the server holds comes back
|
|
2530
|
+
in Python, like `1` and `true`: every item the server holds comes back;
|
|
2531
|
+
- a datetime before `0001-01-01T00:00:00Z` or after `9999-12-31T23:59:59.999999999Z` reads as its
|
|
2532
|
+
RFC 3339 text, the one SurrealDB gives it (`<string>`): `"0000-06-01T00:00:00Z"`,
|
|
2533
|
+
`"-0001-01-01T00:00:00Z"`, `"+10000-01-01T00:00:00.500Z"` (a sign and at least four digits outside
|
|
2534
|
+
years 0000-9999, every nanosecond kept). An untyped field, `raw_query()` and live notifications
|
|
2535
|
+
give that string; a `datetime` field refuses it with a `ValidationError` naming the field: `get()`
|
|
2536
|
+
raises it, and `filter().exec()` and `raw_query()` return such rows as dicts, as for any row that
|
|
2537
|
+
does not validate. A datetime within years 1-9999, before 1970 included, reads as the SDK's
|
|
2538
|
+
`datetime`, as before.
|
|
2512
2539
|
|
|
2513
2540
|
Written back through the ORM (`merge(v=row.v)`, or `update()`, which rewrites the whole row, the
|
|
2514
2541
|
untouched fields included), a zero duration stays a duration, but a native set is stored as an
|
|
2515
|
-
array, like every set the ORM writes, unless its column is `TYPE set<…>` (see [Sets](#sets)),
|
|
2516
|
-
|
|
2517
|
-
[Geometries the SDK cannot read](#geometries-the-sdk-cannot-read))
|
|
2518
|
-
flattens the SDK's `Duration(0)` into
|
|
2542
|
+
array, like every set the ORM writes, unless its column is `TYPE set<…>` (see [Sets](#sets)), a
|
|
2543
|
+
degenerate geometry as a plain object (see
|
|
2544
|
+
[Geometries the SDK cannot read](#geometries-the-sdk-cannot-read)), and a datetime outside years
|
|
2545
|
+
1-9999 as its text, a string. In an untyped field, `update()` flattens the SDK's `Duration(0)` into
|
|
2546
|
+
an object, like any SDK value there
|
|
2519
2547
|
([#222](https://github.com/EulogySnowfall/SurrealDB-ORM-lite/issues/222)).
|
|
2520
2548
|
|
|
2521
2549
|
The hook is installed only while the installed SDK cannot decode these values, and only on the
|
|
2522
2550
|
ORM's own clients (`SurrealDBConnectionManager.get_client()`, `ephemeral_client()`), the embedded
|
|
2523
2551
|
engine's included (see [Embedded engine and test fixtures](#26-embedded-engine-and-test-fixtures)):
|
|
2524
|
-
there a zero duration
|
|
2525
|
-
array, as on 2.x. An `AsyncSurreal` client the application
|
|
2526
|
-
and fails on them.
|
|
2552
|
+
there a zero duration, a degenerate geometry and a datetime outside years 1-9999 read as on a
|
|
2553
|
+
server, and `<set>[…]` is stored as an array, as on 2.x. An `AsyncSurreal` client the application
|
|
2554
|
+
builds itself keeps the SDK's behaviour and fails on them.
|
|
2527
2555
|
A later SDK reads zero durations and native sets itself
|
|
2528
2556
|
([#205](https://github.com/EulogySnowfall/SurrealDB-ORM-lite/issues/205)), and the hook then
|
|
2529
2557
|
leaves them to it.
|
|
@@ -4674,7 +4702,8 @@ await SurrealDBConnectionManager.remove_function("fn::greet")
|
|
|
4674
4702
|
(SurrealDB 2.x, HTTP, the embedded engine) only queues the statement, so a refusal comes back when the block
|
|
4675
4703
|
commits, as the transaction's own error, never as `SurrealDbAuthenticationError` or `SurrealDbNotFoundError`,
|
|
4676
4704
|
`missing_ok=False` included. When a definition is applied in a transaction (`tx=`), an error raised when the
|
|
4677
|
-
transaction ends
|
|
4705
|
+
transaction ends gives the server's reason and the statement's position, without the server's excerpt of the
|
|
4706
|
+
statement (v0.31.2, [#278](https://github.com/EulogySnowfall/SurrealDB-ORM-lite/issues/278)).
|
|
4678
4707
|
- Live queries see the rows an event writes (`watch()` on their table). The query cache
|
|
4679
4708
|
([section 29](#29-query-cache-cache-querycache)) does not: it forgets the tables the ORM itself writes, so a cached
|
|
4680
4709
|
read of a table that events write stays as it was until its time to live runs out. Do not cache such reads, or call
|
|
@@ -4708,9 +4737,10 @@ swallow the clauses the ORM appends, a second statement where one expression is
|
|
|
4708
4737
|
defence: expressions are trusted application code, never user input. To use a value, read it from the record
|
|
4709
4738
|
(`$after.email`) or from a `DEFINE PARAM`, never format it into the text. Kinds are parsed and written in canonical
|
|
4710
4739
|
form. An `Event`'s `repr()` leaves out its expressions, and a refusal raised without `tx=` names the event or the
|
|
4711
|
-
function, never its statement; with `tx=`, an error raised when the transaction ends
|
|
4712
|
-
|
|
4713
|
-
|
|
4740
|
+
function, never its statement; with `tx=`, an error raised when the transaction ends gives the server's reason and
|
|
4741
|
+
the statement's position, without the server's excerpt of the statement (v0.31.2,
|
|
4742
|
+
[#278](https://github.com/EulogySnowfall/SurrealDB-ORM-lite/issues/278)). Applying a definition needs a privileged
|
|
4743
|
+
system user: run `define_events()` and `define_function()` at start-up or deployment, not on a record user's session.
|
|
4714
4744
|
|
|
4715
4745
|
#### Coming from the full SurrealDB-ORM
|
|
4716
4746
|
|
|
@@ -4849,7 +4879,7 @@ features introduced in SurrealDB 3.x. On 2.x the ORM degrades gracefully. Capabi
|
|
|
4849
4879
|
listed behave the same on both lines. The 2.x column was measured on 2.6.5 and holds unchanged
|
|
4850
4880
|
on 2.7.0. The 3.x column was measured on 3.2.4 and holds on 3.3.0, except where a row says
|
|
4851
4881
|
otherwise. The full suite and every divergence below were re-verified on both new versions.
|
|
4852
|
-
Rows added in v0.20.0 were measured directly on 2.7.0 and 3.3.0; rows added in v0.21.0 on 2.7.0 and 3.3.2, re-verified on 3.3.3, with 3.1.5 where it differs; rows added in v0.22.0 on 2.7.0 and 3.3.3, with 3.1.5 where it differs; rows added in v0.23.0 on 2.7.0 and 3.3.4, with 3.1.5 where it differs; rows added in v0.24.0 on 2.7.0 and 3.3.4, identical on 3.1.5; rows added in v0.26.0 on 2.7.0 and 3.3.4, in memory and on versioned storage, identical on 3.1.5; rows added in v0.27.0 on 2.7.0 and 3.3.4, with 3.1.5 where it differs; rows added in v0.28.0 on 2.7.0 and 3.3.4, identical on 3.1.5; the rows added in v0.28.1 on 2.7.0 and 3.3.4, identical on 3.1.5; rows added in v0.29.0 on 2.7.0 and 3.3.4 in one process, identical on 3.1.5. The row added in v0.29.2 was measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5. Rows added in v0.30.0 were measured on 2.7.0, 3.3.4 and the embedded engine, with 3.1.5 where it differs. The row added in v0.30.3 was measured on 2.7.0 and 3.3.4, over WebSocket and HTTP, and on the embedded engine, identical on 3.1.5. The row added in v0.30.7 was measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5: its parser refuses a statement that nests six objects, and `update()` nests at most four. Rows added in v0.31.0 were measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5.
|
|
4882
|
+
Rows added in v0.20.0 were measured directly on 2.7.0 and 3.3.0; rows added in v0.21.0 on 2.7.0 and 3.3.2, re-verified on 3.3.3, with 3.1.5 where it differs; rows added in v0.22.0 on 2.7.0 and 3.3.3, with 3.1.5 where it differs; rows added in v0.23.0 on 2.7.0 and 3.3.4, with 3.1.5 where it differs; rows added in v0.24.0 on 2.7.0 and 3.3.4, identical on 3.1.5; rows added in v0.26.0 on 2.7.0 and 3.3.4, in memory and on versioned storage, identical on 3.1.5; rows added in v0.27.0 on 2.7.0 and 3.3.4, with 3.1.5 where it differs; rows added in v0.28.0 on 2.7.0 and 3.3.4, identical on 3.1.5; the rows added in v0.28.1 on 2.7.0 and 3.3.4, identical on 3.1.5; rows added in v0.29.0 on 2.7.0 and 3.3.4 in one process, identical on 3.1.5. The row added in v0.29.2 was measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5. Rows added in v0.30.0 were measured on 2.7.0, 3.3.4 and the embedded engine, with 3.1.5 where it differs. The row added in v0.30.3 was measured on 2.7.0 and 3.3.4, over WebSocket and HTTP, and on the embedded engine, identical on 3.1.5. The row added in v0.30.7 was measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5: its parser refuses a statement that nests six objects, and `update()` nests at most four. Rows added in v0.31.0 were measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5. The row added in v0.31.1 was measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5.
|
|
4853
4883
|
The SDK's in-process engine has a table of its own: [ORM behaviour: the embedded engine](#orm-behaviour-the-embedded-engine).
|
|
4854
4884
|
|
|
4855
4885
|
| ORM capability | SurrealDB 2.6.x / 2.7.x | SurrealDB 3.2.x / 3.3.x | Since |
|
|
@@ -4976,6 +5006,7 @@ The SDK's in-process engine has a table of its own: [ORM behaviour: the embedded
|
|
|
4976
5006
|
| An event whose `THEN` fails (a `THROW`, an error) | the write is refused; the SDK's `InternalError` (`An error occurred: …` for a `THROW`, the bare cause otherwise) | the write is refused; the SDK's `InternalError` (`Error while processing event <name>: …`) | v0.31.0 |
|
|
4977
5007
|
| Event and function definitions read back (`EventState`, `FunctionState`) | a block on several lines, each statement ending with `;`; `option<T>` parameters as written | a block on one line, no `;` after a single statement; optional parameters rendered with `none` first | v0.31.0 |
|
|
4978
5008
|
| `define_function()`, `remove_function()` and `call_function()` | same on both lines: the next `call_function()` sees a redefinition on every connection of this process to the same namespace and database, whatever its URL | the same | v0.31.0 |
|
|
5009
|
+
| Reading a datetime before year 1 or after year 9999 (SurrealDB stores years -262143 to +262142) | its RFC 3339 text, as `<string>` gives it (`"+10000-01-01T00:00:00Z"`): an untyped field holds it, a `datetime` field refuses it (`ValidationError`) | same on both lines | v0.31.1 |
|
|
4979
5010
|
| Full suite and every divergence in this table, re-run on SurrealDB 2.7.0 and 3.3.0 | 2.7.0 identical to 2.6.5 (same passes, same 3.x-only skips) | 3.3.0 identical to 3.2.4, except the system-user signin row | v0.19.1 |
|
|
4980
5011
|
|
|
4981
5012
|
> **Note on record IDs**: A record loaded from the database has its `id` field set to a native `surrealdb.RecordID` object, not a plain string. Use `model.get_raw_id()` to obtain the bare identifier string (e.g. `"alice"`), or compare directly with `model.id == RecordID("User", "alice")`. In-memory instances you construct yourself retain whatever value you assign.
|
|
@@ -5004,7 +5035,7 @@ Capabilities not listed behave as on a 2.x server.
|
|
|
5004
5035
|
| Anonymous session | the in-process owner (full access); `info()` returns `None` | not used by the ORM (the configured user signs in); an anonymous `info()` raises `SurrealDbAuthenticationError` | v0.25.0 |
|
|
5005
5036
|
| `invalidate()` | back to the in-process owner | signs in again as the configured user | v0.25.0 |
|
|
5006
5037
|
| Reading a table that does not exist | `[]`, raw queries included (as on 2.x) | the ORM reads `[]`; a raw query on 3.x raises `NotFoundError` | v0.25.0 |
|
|
5007
|
-
| Parse errors | bare `RuntimeError` (the DDL helpers raise `SurrealDbError`)
|
|
5038
|
+
| Parse errors | bare `RuntimeError` (the DDL helpers and the commit of `transaction()` raise `SurrealDbError`) | the SDK's error classes | v0.25.0 |
|
|
5008
5039
|
| Time travel (`as_of()`) | `surrealkv+versioned://` only (`?versioned=true` is ignored by SDK 2.0.0); any other URL → `SurrealDbError` before any request; `fetch()` refused (2.x core) | versioned storage required (see the 2.x/3.x table) | v0.26.0 |
|
|
5009
5040
|
| Several connections (`add_connection()`) | `mem://`: each name is a database of its own; an on-disk store (`surrealkv://`, `file://`) is open under one name at a time — a second name raises `SurrealDbConnectionError` | each name opens its own clients; two databases on one server are isolated | v0.29.0 |
|
|
5010
5041
|
| Schema introspection (`DatabaseIntrospector`) | reads like a 2.x server (core 2.3.10): same state and same generated model types as on the servers; the anonymous owner session may run `INFO`; `COUNT` indexes cannot be defined | need a system user (root, namespace or database user) | v0.30.0 |
|
|
@@ -681,10 +681,11 @@ cancelled when the block ends, nothing of the block is committed, and leaving th
|
|
|
681
681
|
`SurrealDbError` (`SurrealDbConflictError` for a retryable conflict, so `retry_on_conflict` still
|
|
682
682
|
retries the block). A buffered transaction fails as a whole at commit; an interactive one raises
|
|
683
683
|
the failed statement's error at once, then, when the block ends, "The transaction was cancelled: a
|
|
684
|
-
statement in it failed; nothing was committed (
|
|
685
|
-
`tx.commit()` after a failure raises the same error. When
|
|
686
|
-
that exception wins, as before. Reads the ORM answers itself
|
|
687
|
-
that does not exist reads as no rows, and `get()` raises
|
|
684
|
+
statement in it failed; nothing was committed (SurrealDbError)", which names the type of that
|
|
685
|
+
error and is chained to it. An explicit `tx.commit()` after a failure raises the same error. When
|
|
686
|
+
the block's own exception propagates, that exception wins, as before. Reads the ORM answers itself
|
|
687
|
+
are not failed statements: a table that does not exist reads as no rows, and `get()` raises
|
|
688
|
+
`SurrealDbNotFoundError`.
|
|
688
689
|
|
|
689
690
|
```python
|
|
690
691
|
async with SurrealDBConnectionManager.transaction() as tx:
|
|
@@ -696,6 +697,18 @@ async with SurrealDBConnectionManager.transaction() as tx:
|
|
|
696
697
|
# leaving the block raised SurrealDbError: alice was not saved either
|
|
697
698
|
```
|
|
698
699
|
|
|
700
|
+
**Transaction errors leave out the server's excerpt of the statement** (v0.31.2). When the server
|
|
701
|
+
refuses the query as a whole (a statement it cannot parse), the error gives the server's reason and
|
|
702
|
+
the position, without the server's excerpt of the statement: a buffered commit raises "Transaction
|
|
703
|
+
failed: the server refused statement 2 of 3: … (at line 1, column 7 of the statement)", the
|
|
704
|
+
embedded engine included, and a write or a definition in an interactive transaction raises
|
|
705
|
+
"Transaction failed: the server refused the statement: … (at line 1, column 7 of the statement)".
|
|
706
|
+
The line and column are those of the statement as written, before the ORM renames its `$name`
|
|
707
|
+
parameters for the batch, and a delimiter left unclosed names the statement it opens in, even when
|
|
708
|
+
the server stops in a later statement. The server's full text stays reachable as the error's
|
|
709
|
+
`__context__`, which a traceback does not print. A read in a transaction is not affected: it raises
|
|
710
|
+
the SDK's own error, as it does outside a transaction.
|
|
711
|
+
|
|
699
712
|
### 12. Upsert & get_or_create / update_or_create
|
|
700
713
|
|
|
701
714
|
```python
|
|
@@ -840,7 +853,11 @@ except SurrealDbConflictError:
|
|
|
840
853
|
- Total attempts = `max_retries + 1`; after they are exhausted the conflict is re-raised as
|
|
841
854
|
`SurrealDbConflictError`. The numeric parameters are validated at decoration time.
|
|
842
855
|
- Detection anchors on SurrealDB's own retryable marker ("This transaction can be retried"), so
|
|
843
|
-
a non-retryable failure (e.g. a duplicate-key error) is **not** retried.
|
|
856
|
+
a non-retryable failure (e.g. a duplicate-key error) is **not** retried. `is_conflict_error()`
|
|
857
|
+
reads the exception and its chain as a traceback prints it: a context suppressed with
|
|
858
|
+
`raise … from None` is not read (v0.31.2), so a conflict you re-raise as another exception with
|
|
859
|
+
`raise … from None` is no longer recognised: chain it (`raise … from exc`, or `raise …` without
|
|
860
|
+
`from` inside the `except` block), or raise a `SurrealDbConflictError`.
|
|
844
861
|
- The exception type is identical on 2.6.x and 3.x; conflicts simply arise more often on 3.x
|
|
845
862
|
(optimistic MVCC) than on 2.6.x (see the behaviour table), except on change-feed tables, where
|
|
846
863
|
2.x refuses concurrent writes that 3.x accepts (see [section 22](#22-change-feeds-changes)).
|
|
@@ -2448,31 +2465,42 @@ an `IntEnum` gives the value), so use `.value` to build a key, a URL or a log li
|
|
|
2448
2465
|
#### Values the SDK cannot read
|
|
2449
2466
|
|
|
2450
2467
|
The surrealdb SDK 2.0.0 cannot decode a zero duration, a SurrealDB 3.x native set (a
|
|
2451
|
-
`TYPE set<…>` column on SurrealDB 3.3, see [Sets](#sets))
|
|
2452
|
-
[Geometries the SDK cannot read](#geometries-the-sdk-cannot-read))
|
|
2453
|
-
|
|
2454
|
-
|
|
2468
|
+
`TYPE set<…>` column on SurrealDB 3.3, see [Sets](#sets)), a degenerate geometry (see
|
|
2469
|
+
[Geometries the SDK cannot read](#geometries-the-sdk-cannot-read)) or a datetime outside years
|
|
2470
|
+
1-9999, the years a Python `datetime` holds (SurrealDB stores years -262143 to +262142, on both
|
|
2471
|
+
lines): one such value fails the whole response it is in. The clients the ORM opens read them
|
|
2472
|
+
anyway, through a hook around the SDK's own CBOR decoder that handles only these values; everything
|
|
2473
|
+
else reads exactly as the SDK reads it:
|
|
2455
2474
|
|
|
2456
2475
|
- a zero duration reads as zero: `timedelta(0)` or `Duration(0)` in a typed field, the SDK's
|
|
2457
2476
|
`Duration(0)` in an untyped one;
|
|
2458
2477
|
- a native set reads as a Python `set`, which `set[X]` and `frozenset[X]` fields validate, or as a
|
|
2459
2478
|
`list` when its items are unhashable (record ids, objects, nested sets) or two of them are equal
|
|
2460
|
-
in Python, like `1` and `true`: every item the server holds comes back
|
|
2479
|
+
in Python, like `1` and `true`: every item the server holds comes back;
|
|
2480
|
+
- a datetime before `0001-01-01T00:00:00Z` or after `9999-12-31T23:59:59.999999999Z` reads as its
|
|
2481
|
+
RFC 3339 text, the one SurrealDB gives it (`<string>`): `"0000-06-01T00:00:00Z"`,
|
|
2482
|
+
`"-0001-01-01T00:00:00Z"`, `"+10000-01-01T00:00:00.500Z"` (a sign and at least four digits outside
|
|
2483
|
+
years 0000-9999, every nanosecond kept). An untyped field, `raw_query()` and live notifications
|
|
2484
|
+
give that string; a `datetime` field refuses it with a `ValidationError` naming the field: `get()`
|
|
2485
|
+
raises it, and `filter().exec()` and `raw_query()` return such rows as dicts, as for any row that
|
|
2486
|
+
does not validate. A datetime within years 1-9999, before 1970 included, reads as the SDK's
|
|
2487
|
+
`datetime`, as before.
|
|
2461
2488
|
|
|
2462
2489
|
Written back through the ORM (`merge(v=row.v)`, or `update()`, which rewrites the whole row, the
|
|
2463
2490
|
untouched fields included), a zero duration stays a duration, but a native set is stored as an
|
|
2464
|
-
array, like every set the ORM writes, unless its column is `TYPE set<…>` (see [Sets](#sets)),
|
|
2465
|
-
|
|
2466
|
-
[Geometries the SDK cannot read](#geometries-the-sdk-cannot-read))
|
|
2467
|
-
flattens the SDK's `Duration(0)` into
|
|
2491
|
+
array, like every set the ORM writes, unless its column is `TYPE set<…>` (see [Sets](#sets)), a
|
|
2492
|
+
degenerate geometry as a plain object (see
|
|
2493
|
+
[Geometries the SDK cannot read](#geometries-the-sdk-cannot-read)), and a datetime outside years
|
|
2494
|
+
1-9999 as its text, a string. In an untyped field, `update()` flattens the SDK's `Duration(0)` into
|
|
2495
|
+
an object, like any SDK value there
|
|
2468
2496
|
([#222](https://github.com/EulogySnowfall/SurrealDB-ORM-lite/issues/222)).
|
|
2469
2497
|
|
|
2470
2498
|
The hook is installed only while the installed SDK cannot decode these values, and only on the
|
|
2471
2499
|
ORM's own clients (`SurrealDBConnectionManager.get_client()`, `ephemeral_client()`), the embedded
|
|
2472
2500
|
engine's included (see [Embedded engine and test fixtures](#26-embedded-engine-and-test-fixtures)):
|
|
2473
|
-
there a zero duration
|
|
2474
|
-
array, as on 2.x. An `AsyncSurreal` client the application
|
|
2475
|
-
and fails on them.
|
|
2501
|
+
there a zero duration, a degenerate geometry and a datetime outside years 1-9999 read as on a
|
|
2502
|
+
server, and `<set>[…]` is stored as an array, as on 2.x. An `AsyncSurreal` client the application
|
|
2503
|
+
builds itself keeps the SDK's behaviour and fails on them.
|
|
2476
2504
|
A later SDK reads zero durations and native sets itself
|
|
2477
2505
|
([#205](https://github.com/EulogySnowfall/SurrealDB-ORM-lite/issues/205)), and the hook then
|
|
2478
2506
|
leaves them to it.
|
|
@@ -4623,7 +4651,8 @@ await SurrealDBConnectionManager.remove_function("fn::greet")
|
|
|
4623
4651
|
(SurrealDB 2.x, HTTP, the embedded engine) only queues the statement, so a refusal comes back when the block
|
|
4624
4652
|
commits, as the transaction's own error, never as `SurrealDbAuthenticationError` or `SurrealDbNotFoundError`,
|
|
4625
4653
|
`missing_ok=False` included. When a definition is applied in a transaction (`tx=`), an error raised when the
|
|
4626
|
-
transaction ends
|
|
4654
|
+
transaction ends gives the server's reason and the statement's position, without the server's excerpt of the
|
|
4655
|
+
statement (v0.31.2, [#278](https://github.com/EulogySnowfall/SurrealDB-ORM-lite/issues/278)).
|
|
4627
4656
|
- Live queries see the rows an event writes (`watch()` on their table). The query cache
|
|
4628
4657
|
([section 29](#29-query-cache-cache-querycache)) does not: it forgets the tables the ORM itself writes, so a cached
|
|
4629
4658
|
read of a table that events write stays as it was until its time to live runs out. Do not cache such reads, or call
|
|
@@ -4657,9 +4686,10 @@ swallow the clauses the ORM appends, a second statement where one expression is
|
|
|
4657
4686
|
defence: expressions are trusted application code, never user input. To use a value, read it from the record
|
|
4658
4687
|
(`$after.email`) or from a `DEFINE PARAM`, never format it into the text. Kinds are parsed and written in canonical
|
|
4659
4688
|
form. An `Event`'s `repr()` leaves out its expressions, and a refusal raised without `tx=` names the event or the
|
|
4660
|
-
function, never its statement; with `tx=`, an error raised when the transaction ends
|
|
4661
|
-
|
|
4662
|
-
|
|
4689
|
+
function, never its statement; with `tx=`, an error raised when the transaction ends gives the server's reason and
|
|
4690
|
+
the statement's position, without the server's excerpt of the statement (v0.31.2,
|
|
4691
|
+
[#278](https://github.com/EulogySnowfall/SurrealDB-ORM-lite/issues/278)). Applying a definition needs a privileged
|
|
4692
|
+
system user: run `define_events()` and `define_function()` at start-up or deployment, not on a record user's session.
|
|
4663
4693
|
|
|
4664
4694
|
#### Coming from the full SurrealDB-ORM
|
|
4665
4695
|
|
|
@@ -4798,7 +4828,7 @@ features introduced in SurrealDB 3.x. On 2.x the ORM degrades gracefully. Capabi
|
|
|
4798
4828
|
listed behave the same on both lines. The 2.x column was measured on 2.6.5 and holds unchanged
|
|
4799
4829
|
on 2.7.0. The 3.x column was measured on 3.2.4 and holds on 3.3.0, except where a row says
|
|
4800
4830
|
otherwise. The full suite and every divergence below were re-verified on both new versions.
|
|
4801
|
-
Rows added in v0.20.0 were measured directly on 2.7.0 and 3.3.0; rows added in v0.21.0 on 2.7.0 and 3.3.2, re-verified on 3.3.3, with 3.1.5 where it differs; rows added in v0.22.0 on 2.7.0 and 3.3.3, with 3.1.5 where it differs; rows added in v0.23.0 on 2.7.0 and 3.3.4, with 3.1.5 where it differs; rows added in v0.24.0 on 2.7.0 and 3.3.4, identical on 3.1.5; rows added in v0.26.0 on 2.7.0 and 3.3.4, in memory and on versioned storage, identical on 3.1.5; rows added in v0.27.0 on 2.7.0 and 3.3.4, with 3.1.5 where it differs; rows added in v0.28.0 on 2.7.0 and 3.3.4, identical on 3.1.5; the rows added in v0.28.1 on 2.7.0 and 3.3.4, identical on 3.1.5; rows added in v0.29.0 on 2.7.0 and 3.3.4 in one process, identical on 3.1.5. The row added in v0.29.2 was measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5. Rows added in v0.30.0 were measured on 2.7.0, 3.3.4 and the embedded engine, with 3.1.5 where it differs. The row added in v0.30.3 was measured on 2.7.0 and 3.3.4, over WebSocket and HTTP, and on the embedded engine, identical on 3.1.5. The row added in v0.30.7 was measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5: its parser refuses a statement that nests six objects, and `update()` nests at most four. Rows added in v0.31.0 were measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5.
|
|
4831
|
+
Rows added in v0.20.0 were measured directly on 2.7.0 and 3.3.0; rows added in v0.21.0 on 2.7.0 and 3.3.2, re-verified on 3.3.3, with 3.1.5 where it differs; rows added in v0.22.0 on 2.7.0 and 3.3.3, with 3.1.5 where it differs; rows added in v0.23.0 on 2.7.0 and 3.3.4, with 3.1.5 where it differs; rows added in v0.24.0 on 2.7.0 and 3.3.4, identical on 3.1.5; rows added in v0.26.0 on 2.7.0 and 3.3.4, in memory and on versioned storage, identical on 3.1.5; rows added in v0.27.0 on 2.7.0 and 3.3.4, with 3.1.5 where it differs; rows added in v0.28.0 on 2.7.0 and 3.3.4, identical on 3.1.5; the rows added in v0.28.1 on 2.7.0 and 3.3.4, identical on 3.1.5; rows added in v0.29.0 on 2.7.0 and 3.3.4 in one process, identical on 3.1.5. The row added in v0.29.2 was measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5. Rows added in v0.30.0 were measured on 2.7.0, 3.3.4 and the embedded engine, with 3.1.5 where it differs. The row added in v0.30.3 was measured on 2.7.0 and 3.3.4, over WebSocket and HTTP, and on the embedded engine, identical on 3.1.5. The row added in v0.30.7 was measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5: its parser refuses a statement that nests six objects, and `update()` nests at most four. Rows added in v0.31.0 were measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5. The row added in v0.31.1 was measured on 2.7.0, 3.3.4 and the embedded engine, identical on 3.1.5.
|
|
4802
4832
|
The SDK's in-process engine has a table of its own: [ORM behaviour: the embedded engine](#orm-behaviour-the-embedded-engine).
|
|
4803
4833
|
|
|
4804
4834
|
| ORM capability | SurrealDB 2.6.x / 2.7.x | SurrealDB 3.2.x / 3.3.x | Since |
|
|
@@ -4925,6 +4955,7 @@ The SDK's in-process engine has a table of its own: [ORM behaviour: the embedded
|
|
|
4925
4955
|
| An event whose `THEN` fails (a `THROW`, an error) | the write is refused; the SDK's `InternalError` (`An error occurred: …` for a `THROW`, the bare cause otherwise) | the write is refused; the SDK's `InternalError` (`Error while processing event <name>: …`) | v0.31.0 |
|
|
4926
4956
|
| Event and function definitions read back (`EventState`, `FunctionState`) | a block on several lines, each statement ending with `;`; `option<T>` parameters as written | a block on one line, no `;` after a single statement; optional parameters rendered with `none` first | v0.31.0 |
|
|
4927
4957
|
| `define_function()`, `remove_function()` and `call_function()` | same on both lines: the next `call_function()` sees a redefinition on every connection of this process to the same namespace and database, whatever its URL | the same | v0.31.0 |
|
|
4958
|
+
| Reading a datetime before year 1 or after year 9999 (SurrealDB stores years -262143 to +262142) | its RFC 3339 text, as `<string>` gives it (`"+10000-01-01T00:00:00Z"`): an untyped field holds it, a `datetime` field refuses it (`ValidationError`) | same on both lines | v0.31.1 |
|
|
4928
4959
|
| Full suite and every divergence in this table, re-run on SurrealDB 2.7.0 and 3.3.0 | 2.7.0 identical to 2.6.5 (same passes, same 3.x-only skips) | 3.3.0 identical to 3.2.4, except the system-user signin row | v0.19.1 |
|
|
4929
4960
|
|
|
4930
4961
|
> **Note on record IDs**: A record loaded from the database has its `id` field set to a native `surrealdb.RecordID` object, not a plain string. Use `model.get_raw_id()` to obtain the bare identifier string (e.g. `"alice"`), or compare directly with `model.id == RecordID("User", "alice")`. In-memory instances you construct yourself retain whatever value you assign.
|
|
@@ -4953,7 +4984,7 @@ Capabilities not listed behave as on a 2.x server.
|
|
|
4953
4984
|
| Anonymous session | the in-process owner (full access); `info()` returns `None` | not used by the ORM (the configured user signs in); an anonymous `info()` raises `SurrealDbAuthenticationError` | v0.25.0 |
|
|
4954
4985
|
| `invalidate()` | back to the in-process owner | signs in again as the configured user | v0.25.0 |
|
|
4955
4986
|
| Reading a table that does not exist | `[]`, raw queries included (as on 2.x) | the ORM reads `[]`; a raw query on 3.x raises `NotFoundError` | v0.25.0 |
|
|
4956
|
-
| Parse errors | bare `RuntimeError` (the DDL helpers raise `SurrealDbError`)
|
|
4987
|
+
| Parse errors | bare `RuntimeError` (the DDL helpers and the commit of `transaction()` raise `SurrealDbError`) | the SDK's error classes | v0.25.0 |
|
|
4957
4988
|
| Time travel (`as_of()`) | `surrealkv+versioned://` only (`?versioned=true` is ignored by SDK 2.0.0); any other URL → `SurrealDbError` before any request; `fetch()` refused (2.x core) | versioned storage required (see the 2.x/3.x table) | v0.26.0 |
|
|
4958
4989
|
| Several connections (`add_connection()`) | `mem://`: each name is a database of its own; an on-disk store (`surrealkv://`, `file://`) is open under one name at a time — a second name raises `SurrealDbConnectionError` | each name opens its own clients; two databases on one server are isolated | v0.29.0 |
|
|
4959
4990
|
| Schema introspection (`DatabaseIntrospector`) | reads like a 2.x server (core 2.3.10): same state and same generated model types as on the servers; the anonymous owner session may run `INFO`; `COUNT` indexes cannot be defined | need a system user (root, namespace or database user) | v0.30.0 |
|
|
@@ -30,12 +30,16 @@ then that module.
|
|
|
30
30
|
|
|
31
31
|
v0.27.0 adds ``ThrownError`` (a top-level export of SDK 2.0.0): SurrealDB 3.1 reports a table that does not
|
|
32
32
|
exist, read by a ``LET`` inside a block, as a ``ThrownError`` instead of a ``NotFoundError``.
|
|
33
|
+
|
|
34
|
+
#279 adds ``TAG_DATETIME_COMPACT`` (a top-level export of SDK 2.0.0, like ``TAG_DURATION_COMPACT``): the read
|
|
35
|
+
hook reads a datetime outside years 1-9999, which the SDK's ``tag_decoder`` refuses.
|
|
33
36
|
"""
|
|
34
37
|
|
|
35
38
|
import importlib
|
|
36
39
|
from typing import TYPE_CHECKING, Any, Final
|
|
37
40
|
|
|
38
41
|
from surrealdb import ( # noqa: F401
|
|
42
|
+
TAG_DATETIME_COMPACT,
|
|
39
43
|
TAG_DECIMAL_STRING,
|
|
40
44
|
TAG_DURATION_COMPACT,
|
|
41
45
|
AlreadyExistsError,
|
|
@@ -157,8 +161,9 @@ __all__ = [
|
|
|
157
161
|
"cbor_encode",
|
|
158
162
|
"cbor_decode",
|
|
159
163
|
"SdkInvalidDurationError",
|
|
160
|
-
# Read hook (#235)
|
|
164
|
+
# Read hook (#235); a datetime outside years 1-9999 (#279)
|
|
161
165
|
"cbor_tag_decoder",
|
|
166
|
+
"TAG_DATETIME_COMPACT",
|
|
162
167
|
"SdkAsyncWsConnection",
|
|
163
168
|
"SdkAsyncHttpConnection",
|
|
164
169
|
"SdkAsyncEmbeddedConnection",
|
|
@@ -42,7 +42,9 @@ def is_conflict_error(error: BaseException) -> bool:
|
|
|
42
42
|
A :class:`SurrealDbConflictError` always qualifies. Otherwise the exception
|
|
43
43
|
and its ``__cause__`` / ``__context__`` chain are scanned for one of the
|
|
44
44
|
conflict signal phrases (so a conflict wrapped in another exception is still
|
|
45
|
-
recognised).
|
|
45
|
+
recognised). The chain is the one a traceback prints: a context suppressed with
|
|
46
|
+
``raise … from None`` is not part of it (#278) — the ORM keeps a server's full
|
|
47
|
+
text there, and a statement it quotes may hold conflict words.
|
|
46
48
|
"""
|
|
47
49
|
if isinstance(error, SurrealDbConflictError):
|
|
48
50
|
return True
|
|
@@ -53,7 +55,10 @@ def is_conflict_error(error: BaseException) -> bool:
|
|
|
53
55
|
text = str(current).lower()
|
|
54
56
|
if any(signal in text for signal in _CONFLICT_SIGNALS):
|
|
55
57
|
return True
|
|
56
|
-
|
|
58
|
+
if current.__cause__ is not None:
|
|
59
|
+
current = current.__cause__
|
|
60
|
+
else:
|
|
61
|
+
current = None if current.__suppress_context__ else current.__context__
|
|
57
62
|
return False
|
|
58
63
|
|
|
59
64
|
|
{surreal_orm_lite-0.31.0 → surreal_orm_lite-0.31.2}/src/surreal_orm_lite/connection_manager.py
RENAMED
|
@@ -1682,9 +1682,15 @@ class SurrealDBConnectionManager:
|
|
|
1682
1682
|
raises ``SurrealDbError`` (``SurrealDbConflictError`` for a retryable conflict, so
|
|
1683
1683
|
``retry_on_conflict`` still retries). A buffered batch fails as a whole at commit; an
|
|
1684
1684
|
interactive transaction raises "The transaction was cancelled: a statement in it failed;
|
|
1685
|
-
nothing was committed (
|
|
1686
|
-
|
|
1687
|
-
read as no rows, ``get()`` raising
|
|
1685
|
+
nothing was committed (SurrealDbError)", which names the type of the failed statement's
|
|
1686
|
+
error and is chained to it. When the body's own exception propagates, that exception wins.
|
|
1687
|
+
A read the ORM answers itself (a missing table read as no rows, ``get()`` raising
|
|
1688
|
+
``SurrealDbNotFoundError``) is not a failed statement.
|
|
1689
|
+
|
|
1690
|
+
A query the server refuses as a whole (a statement it cannot parse) raises ``SurrealDbError``
|
|
1691
|
+
with the server's reason and the position, without the server's excerpt of the statement
|
|
1692
|
+
(issue #278): "Transaction failed: the server refused statement 2 of 3: … (at line 1, column 7
|
|
1693
|
+
of the statement)" at a buffered commit, the embedded engine included.
|
|
1688
1694
|
|
|
1689
1695
|
SurrealDB has no savepoints/nested transactions; nested ``transaction()`` calls
|
|
1690
1696
|
open INDEPENDENT transactions, not savepoints.
|
|
@@ -2488,7 +2494,7 @@ class SurrealDBConnectionManager:
|
|
|
2488
2494
|
without *tx*, a refusal names the event and its table, never its statement, whose ``when`` and
|
|
2489
2495
|
``then`` may hold a secret). In a buffered transaction (*tx* over HTTP, on SurrealDB 2.x or the
|
|
2490
2496
|
embedded engine) the statement is only queued: a refusal comes back when the block commits, as the
|
|
2491
|
-
transaction's own error, never as ``SurrealDbAuthenticationError``. With *tx*, an error raised when the transaction ends
|
|
2497
|
+
transaction's own error, never as ``SurrealDbAuthenticationError``. With *tx*, an error raised when the transaction ends gives the server's reason and the statement's position, without the server's excerpt of the statement (#278).
|
|
2492
2498
|
:raises SurrealDbAuthenticationError: the session may not define events.
|
|
2493
2499
|
"""
|
|
2494
2500
|
check_call_transaction(cls._connection_name or "default", tx, operation="define_event()")
|
|
@@ -2610,7 +2616,7 @@ class SurrealDBConnectionManager:
|
|
|
2610
2616
|
statement (``Can't apply function definition: 'fn::greet' -> …``: without *tx*, a refusal names the
|
|
2611
2617
|
function, never its statement, whose body may hold a secret). In a buffered transaction (*tx* over HTTP,
|
|
2612
2618
|
on SurrealDB 2.x or the embedded engine) the statement is only queued: a refusal comes back when the
|
|
2613
|
-
block commits, as the transaction's own error, never as ``SurrealDbAuthenticationError``. With *tx*, an error raised when the transaction ends
|
|
2619
|
+
block commits, as the transaction's own error, never as ``SurrealDbAuthenticationError``. With *tx*, an error raised when the transaction ends gives the server's reason and the statement's position, without the server's excerpt of the statement (#278).
|
|
2614
2620
|
"""
|
|
2615
2621
|
check_call_transaction(cls._connection_name or "default", tx, operation="define_function()")
|
|
2616
2622
|
statement = function_ddl(
|