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
1 change: 1 addition & 0 deletions docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
** xref:how-to/bulk-loads.adoc[Bulk loads and commit size]
** xref:how-to/import-export.adoc[Importing and exporting data]
** xref:how-to/dataframes-and-columns.adoc[DataFrames, Arrow and columns]
** xref:how-to/functions-and-events.adoc[Python functions and record events]
** xref:how-to/vectors.adoc[Vectors]
* Development
** xref:development/ci.adoc[Continuous integration]
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/how-to/databases-and-queries.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// Sources: bindings docs guide/core/database.md and guide/core/queries.md.

This guide covers the life of a database directory and the two calls that talk to it. Prefer SQL or openCypher through `db.command(...)` and `db.query(...)` for schema, inserts and graph work. The bindings also expose record objects and a `Schema` API; they are on xref:how-to/values-and-records.adoc[Values and records]. The wrappers for the rest of the Java API are not covered here.
This guide covers the life of a database directory and the two calls that talk to it. Prefer SQL or openCypher through `db.command(...)` and `db.query(...)` for schema, inserts and graph work. The bindings also expose record objects and a `Schema` API; they are on xref:how-to/values-and-records.adoc[Values and records]. A Python function that a statement calls, and a callback on record events, are on xref:how-to/functions-and-events.adoc[Python functions and record events]. The wrappers for the rest of the Java API are not covered here.

Every script on this page ran against the published `{jvm-pkg-name}` wheel. Run each one in an empty directory.

Expand Down
358 changes: 358 additions & 0 deletions docs/modules/ROOT/pages/how-to/functions-and-events.adoc

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion docs/modules/ROOT/pages/how-to/transactions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ after the commit, other thread: 4

Share one `Database` between threads, as on xref:how-to/concurrency.adoc#threads[Concurrency, processes and crashes]. That page also covers what happens when two transactions change the same record.

[[failed-statement]]
== A failed statement inside a transaction

A statement that fails inside your transaction is undone on its own, and the transaction goes on. This is the behavior of ArcadeDB 26.11.1 and later. Before it, a statement that had written part of its rows left them in the transaction, and the transaction could no longer be committed.
Expand All @@ -184,7 +185,7 @@ still active: True

The second statement writes `frank` once, and then the unique index refuses the second `frank`. The first `frank` is gone with it, while `erin` from the statement before and `grace` from the statement after are committed. The engine keeps a journal of what each statement changes in order to undo it. When a statement changes more entries than `arcadedb.txStatementSavepointMaxEntries` (100,000 by default), the engine does not undo it, and the transaction can no longer be committed: roll it back. The setting is listed in the main manual: {arcadedb-docs-url}/reference/settings[Settings].

A failed statement is one thing, and an exception that you let leave the `with` block is another: that one rolls the whole transaction back, as above.
A failed statement is one thing, and an exception that you let leave the `with` block is another: that one rolls the whole transaction back, as above. A Python function or a record event callback that raises fails its statement the same way, see xref:how-to/functions-and-events.adoc#raises[Python functions and record events].

== A second begin

Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/installation.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Each native wheel is about 38 MB. These platforms are not built, and the install

Engine modules the bindings do not use are left out to keep the wheel small. These include the gRPC and MongoDB wire protocols, Raft HA, and Gremlin.

NOTE: *JavaScript is not available.* The GraalJS jars are left out, so `DEFINE FUNCTION ... LANGUAGE js` fails. Define functions in SQL or openCypher, or register a Python function with `Database.register_function` (it works on both packages).
NOTE: *JavaScript is not available.* The GraalJS jars are left out, so `DEFINE FUNCTION ... LANGUAGE js` fails. Define functions in SQL or openCypher, or register a Python function with `Database.register_function` (it works on both packages, see xref:how-to/functions-and-events.adoc[Python functions and record events]).

// REMOVE WHEN: the old arcadedb-embedded* shims are retired and two releases have shipped with the two-package layout allow-old-name
NOTE: *The Studio web UI is in the JVM wheels from 26.11.1.* Studio used to bundle ApexCharts, which is no longer open source (https://github.com/ArcadeData/arcadedb/issues/10579[ArcadeData/arcadedb#10579]). https://github.com/ArcadeData/arcadedb/pull/10627[ArcadeData/arcadedb#10627] replaced it with Apache ECharts. The earlier 26.10.x wheels on PyPI do not contain Studio. The HTTP API of the in-process server works in all of them.
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/quickstart.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,4 @@ In the `{jvm-pkg-name}` wheels from 26.11.1 the server also serves the Studio we
* xref:how-to/values-and-records.adoc[Values and records]: which Python types go in and what comes back, datetimes, record objects and the `Schema` API.
* xref:how-to/import-export.adoc[Importing and exporting data]: load a file, export a database or a query.
* xref:how-to/dataframes-and-columns.adoc[DataFrames, Arrow and columns]: load and read pandas, Polars, Arrow and numpy data.
* xref:how-to/functions-and-events.adoc[Python functions and record events]: call a Python function from SQL and openCypher, and run Python when a record is written.
62 changes: 62 additions & 0 deletions tests/test_docs_functions_page.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Every Python block of the Antora page ``docs/modules/ROOT/pages/how-to/functions-and-events.adoc`` runs, and prints what the page says.

Same pattern as ``test_docs_vectors_page.py``: the blocks are chained on purpose, they run in the order of the page, in one
namespace, against one fresh temporary database that the namespace holds as ``db``. A block that is followed by a
``[source,text]`` block must print exactly that text. What each statement of the page claims is pinned on its own in
``test_functions_and_events_behavior.py``. The test uses no Java object, so it runs on the JVM build and the native build alike.
"""

import contextlib
import io
import re
from pathlib import Path

PAGE = (
Path(__file__).resolve().parents[1]
/ "docs"
/ "modules"
/ "ROOT"
/ "pages"
/ "how-to"
/ "functions-and-events.adoc"
)

# a Python listing, then (optionally, directly after it) the text listing with its output
BLOCK_RE = re.compile(
r"\[source,python[^\]\n]*\]\n----\n(?P<code>.*?)\n----\n"
r"(?:\n\[source,text\]\n----\n(?P<out>.*?)\n----\n)?",
re.DOTALL,
)


def _blocks():
text = PAGE.read_text(encoding="utf-8")
found = [(m.group("code"), m.group("out")) for m in BLOCK_RE.finditer(text)]
# no Python listing may escape the extraction (a different spelling of the block header would run nowhere)
assert text.count("[source,python") == len(found)
return found


def test_the_page_has_its_blocks():
blocks = _blocks()
assert len(blocks) >= 9
assert sum(1 for _, out in blocks if out is not None) >= 8


def test_every_block_runs_in_order_and_prints_what_the_page_says(temp_db):
namespace = {"db": temp_db, "__name__": "functions_page"}
for number, (code, expected) in enumerate(_blocks(), start=1):
captured = io.StringIO()
compiled = compile(
code, f"functions-and-events.adoc python block {number}", "exec"
)
with contextlib.redirect_stdout(captured):
exec(compiled, namespace) # nosec B102 - the page of this repository
if expected is not None:
assert (
captured.getvalue().strip() == expected.strip()
), f"block {number} printed other text than the page shows:\n{code}"
else:
assert (
captured.getvalue() == ""
), f"block {number} prints, the page shows no output"
Loading
Loading