- Enclosing class:
Relix
Relix session.
Deliberately short. A knob here is one every embedder has to read past, so the bar is that it cannot be expressed as a declaration or a relation.
- Since:
- 1.0
-
Method Summary
Modifier and TypeMethodDescriptionAccepts relations whose names the analyser could not resolve, instead of rejecting them.baseDirectory(Path directory) Sets the directory a source's relative path resolves against.build()Builds the session.catalog(CatalogProvider catalog) Supplies table schemas and statistics, replacing live introspection.Fixes the clock the session's temporal functions read, soNOW()is reproducible.connector(RelixConnector connector) Installs a connector of the caller's own, asRelix.connector(com.darkcollective.relix.processor.connector.RelixConnector)does, for a session declared in one expression.functions(FunctionLibrary library) Installs a library of the caller's own functions before the session opens.jdbc(String name, DataSource dataSource) Binds a connection name to a live handle, so a script may reference its tables by dotted name without declaring a URL.jdbc(String name, DataSource dataSource, String dialect) Binds a connection name to a live handle with an explicit dialect, skipping the metadata probe.maxFixpointRounds(int rounds) Caps how many rounds a recursiveFIXmay iterate before the engine stops it.maxMaterializedRows(int rows) Caps how many rows a single blocking operator may buffer before the engine stops it.maxProcessedRows(long rows) Caps the work one execution may do, counted as rows passed from one operator to the next.placeholders(Function<String, Optional<String>> resolver) Supplies the values of the${NAME}placeholders in source and connection declarations: a bearer token, an API key, a database password or URL.provisioners(DriverProvisioner drivers, ConnectorProvisioner connectors) Supplies the provisioners that fetch a missing JDBC driver or connector plugin.relationships(SchemaGraph graph) Supplies relationships beyond the ones the session declares.remoteFiles(boolean allow) Says whether a file connection may name its file by anhttpsURL.Restricts what this session's users may reach, and how much one query may do.scriptLoader(ScriptLoader loader) Supplies where animportstatement resolves from.sessionEvents(List<QueryEvent> events) Supplies the extent of therelix.eventscatalog relation.Limits how long one execution may run.
-
Method Details
-
placeholders
Supplies the values of the${NAME}placeholders in source and connection declarations: a bearer token, an API key, a database password or URL.Relix relix = Relix.builder() .placeholders(name -> Optional.ofNullable(System.getenv(name))) .build(); relix.define(""" source Orders from http { url: "https://api.example.com/orders", headers: { "Authorization": "Bearer ${ORDERS_TOKEN}" } }; """);The placeholder stays in the declaration. It is resolved each time a query runs, for the declarations that query reaches, and the value goes to the connector for that run alone. So the value is never written into the text a script is parsed from, and nothing that prints the session —
Relix.definitions(),Relix.ir(), an analysis error — shows it. Because it is asked for on each run, a rotated credential is picked up without redefining anything.Only string values are resolved: URLs, paths, table names, header values, request bodies, credentials, column defaults and connection properties. Each name is asked for at most once per run, and only for the declarations the query reaches. Analysis asks too, when it introspects a connection's tables. When the resolver returns empty for a name a query needs, the query fails with an error naming the placeholder and the source or connection holding it, rather than sending the text
${NAME}. A session built without a resolver leaves placeholders as written.- Parameters:
resolver- the value of each placeholder name, or empty when it has none; must not be null- Returns:
- this builder, for chaining
- Since:
- 1.0
-
remoteFiles
Says whether a file connection may name its file by anhttpsURL.Allowed by default. A
url:is something the script asked for, like an HTTP source's request, so reaching for it is not a surprise. It does put bytes on disk inside an embedded process: a fetched file is cached under~/.relix/cache/filesand reused by later sessions when the server says it has not changed. A session that must not do that says so here, and a connection naming a URL is then an error rather than a download.Within one session a URL is fetched at most once, and every scan reads that copy, so a file that changes on the server does not change under a running query.
- Parameters:
allow- whether anhttpsfile may be fetched- Returns:
- this builder, for chaining
- Since:
- 1.0
-
jdbc
Binds a connection name to a live handle, so a script may reference its tables by dotted name without declaring a URL.- Parameters:
name- the connection name; must not be blankdataSource- the live handle; must not be null- Returns:
- this builder
- Since:
- 1.0
-
jdbc
Binds a connection name to a live handle with an explicit dialect, skipping the metadata probe.- Parameters:
name- the connection name; must not be blankdataSource- the live handle; must not be nulldialect- the dialect token (e.g."postgres"); must not be null- Returns:
- this builder
- Since:
- 1.0
-
catalog
Supplies table schemas and statistics, replacing live introspection.- Parameters:
catalog- the catalog provider; must not be null- Returns:
- this builder
- Since:
- 1.0
-
baseDirectory
Sets the directory a source's relative path resolves against.source Orders from csv("./orders.csv")names a file relative to something, and in a script that something is the script's own directory. An embedded session has no script, so it is this — the working directory unless said otherwise, which is what a program run from its own root expects.- Parameters:
directory- the base directory; must not be null- Returns:
- this builder
- Since:
- 1.0
-
clock
Fixes the clock the session's temporal functions read, soNOW()is reproducible.- Parameters:
clock- the clock; must not be null- Returns:
- this builder
- Since:
- 1.0
-
functions
Installs a library of the caller's own functions before the session opens.- Parameters:
library- the library; must not be null- Returns:
- this builder
- Since:
- 1.0
-
connector
Installs a connector of the caller's own, asRelix.connector(com.darkcollective.relix.processor.connector.RelixConnector)does, for a session declared in one expression.- Parameters:
connector- the connector; must not be null- Returns:
- this builder, for chaining
- Since:
- 1.0
-
maxFixpointRounds
Caps how many rounds a recursiveFIXmay iterate before the engine stops it.The safety valve for a recursion that does not converge — a counting semiring over a cyclic graph is the standard case, where the fixpoint is genuinely infinite rather than merely slow. Unbounded by default, because a cap that fires is an answer that is silently incomplete and that should be a thing a caller asked for.
- Parameters:
rounds- the maximum iterations; must be at least 1- Returns:
- this builder, for chaining
- Since:
- 1.0
-
maxMaterializedRows
Caps how many rows a single blocking operator may buffer before the engine stops it.A blocking operator —
γ,τ, a deduplicating set operation,÷, a full outer or hash join's build side — cannot emit a row until it has read its whole input, so it holds that input in the JVM heap. Nothing in the plan says how large that is.toList()refuses an unbounded relation, and so does the planner for a blocking operator over one, but a bounded table can be far larger than the heap: the query then dies with anOutOfMemoryErrorattributable to no operator in particular. Under a cap the query stops instead with an error naming the operator that was buffering and the limit it crossed — which is a thing a caller can act on.The cap is per operator, not per session total — the shape
maxFixpointRounds(int)has, which likewise bounds one fixpoint rather than a run's total rounds. A running total would refuse a recursion that buffers the same thousand rows each round while its peak memory never moves. So a plan with many blocking operators can exceed it in aggregate while no single operator does; what it converts is the failure where one operator swallows a table.Unbounded by default, because a cap that fires turns a slow answer into no answer, and that should be a thing a caller asked for.
- Parameters:
rows- the maximum rows one blocking operator may buffer; must be at least 1- Returns:
- this builder, for chaining
- Since:
- 1.0
-
maxProcessedRows
Caps the work one execution may do, counted as rows passed from one operator to the next.Every row an operator hands to the operator above it is charged, so the count is the sum of every operator's output. It stops the queries a cap on buffering or on output cannot: a selection over an endless generator that matches nothing, which never yields a row, and an aggregate over a large product, which holds nothing and yields one. It is a total for the whole execution, recursion included, because what it bounds is work rather than memory. The same query over the same data charges the same count on any machine.
Unbounded by default. A query that crosses it fails with a
QueryExecutionExceptionnaming the limit.- Parameters:
rows- the most rows one execution may process; must be at least 1- Returns:
- this builder, for chaining
- Since:
- 1.0
-
timeout
Limits how long one execution may run.The clock starts when a query starts executing, and is checked as rows pass between operators. That reaches every loop in the engine that moves rows, but not a call that does not return to the engine: a JDBC driver waiting on its database, or a solver's search. Unlike
maxProcessedRows(long)the outcome depends on the machine, so prefer that limit where the answer should be reproducible, and use this one as a backstop.Unlimited by default. A query that crosses it fails with a
QueryExecutionExceptionnaming the limit.- Parameters:
timeout- the longest one execution may run; must be positive- Returns:
- this builder, for chaining
- Since:
- 1.0
-
provisioners
Supplies the provisioners that fetch a missing JDBC driver or connector plugin.A session provisions nothing by default, and that is a decision rather than an oversight: a library call that reached out to the network and wrote a jar into the user's home directory would be a surprise an embedder cannot see coming, and a driver an embedded program needs is a dependency of that program.
An application is a different matter — it can ask, report progress and be told no — so one that wants the behaviour states it here, which is where the decision is visible.
- Parameters:
drivers- the JDBC driver provisioner; must not be nullconnectors- the connector-plugin provisioner; must not be null- Returns:
- this builder, for chaining
- Since:
- 1.0
-
scriptLoader
Supplies where animportstatement resolves from.A session serves imports from nothing by default: it is assembled in memory and has no file of its own, so there is no directory for a relative path to be relative to. An application that reads scripts from disk supplies
FileSystemScriptLoader; one that holds them already can serve them from a map, which is what the seam is for.- Parameters:
loader- the loader; must not be null- Returns:
- this builder, for chaining
- Since:
- 1.0
-
relationships
Supplies relationships beyond the ones the session declares.Where an edge comes from that a
relatestatement did not: a host that has observed one — two relations repeatedly joined on the same columns — can offer it here, and the analyser resolves a later join the same way it would resolve a declared one.They are supplemental rather than authoritative: a declared edge wins, and nothing here changes what a query means, only what the engine can infer when a query does not say.
- Parameters:
graph- the additional edges; must not be null- Returns:
- this builder, for chaining
- Since:
- 1.0
-
sessionEvents
Supplies the extent of therelix.eventscatalog relation.A statement cannot observe itself — its events postdate the analysis that would have to register them — so what a query selecting from
relix.eventssees is a feed some earlier run produced, and this is where a host that kept one hands it back. A session given none correctly reports no events rather than pretending to none.- Parameters:
events- the feed; must not be null- Returns:
- this builder, for chaining
- Since:
- 1.0
-
sandbox
Restricts what this session's users may reach, and how much one query may do.Sessions are open by default. A closed sandbox accepts internal declarations (inline tables, views, functions) from
Relix.define(String)andRelix.script(String), but an external one (a file, database or HTTP source, a connection, an import) only when the sandbox's own declarations contain the same one. It also caps input length and result size. SeeSandbox.Relix relix = Relix.builder() .sandbox(Sandbox.load(Path.of("sandbox.json"))) .build();- Parameters:
sandbox- the sandbox; must not be null- Returns:
- this builder, for chaining
- Since:
- 1.0
-
allowUnresolved
Accepts relations whose names the analyser could not resolve, instead of rejecting them.What makes offline work full-strength for a reference that needs a catalog:
warehouse.orderscomposes, renders and optimises with the database unreachable, at the cost of the weaker plan an unknown schema implies.Off by default, because the same signal is what a typo produces. Turning it on trades an error at composition time for one at execution time, which is the right trade only when composing is the whole intent — tuning a query in CI, on a machine with no access to production.
- Returns:
- this builder
- Since:
- 1.0
-
build
Builds the session.- Returns:
- the session
- Since:
- 1.0
-