sdmxlib 0.24.0__tar.gz → 0.26.0__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.
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/PKG-INFO +1 -1
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/pyproject.toml +1 -1
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/__init__.py +4 -1
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/data_store.py +5 -23
- sdmxlib-0.26.0/src/sdmxlib/sql.py +146 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/README.md +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/api/__init__.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/api/client.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/api/federated.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/api/filters.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/api/policy.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/api/providers.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/api/query.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/api/registry.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/api/session.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/catalog.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/__init__.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_csv/__init__.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_csv/reader.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_csv/writer.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_json/__init__.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_json/metadata.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_json/reader.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_json/writer.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_ml21/__init__.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_ml21/namespaces.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_ml21/reader.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_ml21/writer.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_ml30/__init__.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_ml30/metadata.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_ml30/namespaces.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_ml30/reader.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/formats/sdmx_ml30/writer.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/local/__init__.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/local/_data_store.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/local/_registry.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/__init__.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/annotations.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/base.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/binding.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/category.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/code_query.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/codelist.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/collections.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/concept.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/constraint.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/convert.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/dataflow.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/dataset.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/datastructure.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/expr.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/hierarchy.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/hierarchy_query.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/in_memory_registry.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/istring.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/mapping.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/message.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/metadataflow.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/metadataset.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/metadatastructure.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/organisation.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/provision.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/ref.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/registry.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/representation.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/urn.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/model/validation.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/polars.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/py.typed +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/rest.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/storage/__init__.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/storage/lazy.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/storage/readers.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/storage/schema.py +0 -0
- {sdmxlib-0.24.0 → sdmxlib-0.26.0}/src/sdmxlib/storage/writers.py +0 -0
|
@@ -7,7 +7,8 @@ from sdmxlib.api.providers import Provider
|
|
|
7
7
|
from sdmxlib.api.query import DataQuery
|
|
8
8
|
from sdmxlib.api.registry import BearerToken, FmrRegistry, RestRegistry
|
|
9
9
|
from sdmxlib.catalog import DataflowEntry, FederatedCatalog
|
|
10
|
-
from sdmxlib.data_store import DataStore
|
|
10
|
+
from sdmxlib.data_store import DataStore
|
|
11
|
+
from sdmxlib.sql import CompileContext, Predicate, SqlRelation
|
|
11
12
|
from sdmxlib.formats import parse, read, serialize, to_json, to_xml
|
|
12
13
|
from sdmxlib.local import LazyCodelist, LazyHierarchy, Registry
|
|
13
14
|
from sdmxlib.local._data_store import DuckDBDataStore
|
|
@@ -113,6 +114,7 @@ __all__ = [
|
|
|
113
114
|
"CodeQuery",
|
|
114
115
|
"Codelist",
|
|
115
116
|
"CodelistMap",
|
|
117
|
+
"CompileContext",
|
|
116
118
|
"ComponentFilter",
|
|
117
119
|
"ComponentMap",
|
|
118
120
|
"Concept",
|
|
@@ -166,6 +168,7 @@ __all__ = [
|
|
|
166
168
|
"MetadataSet",
|
|
167
169
|
"MetadataStructure",
|
|
168
170
|
"Metadataflow",
|
|
171
|
+
"Predicate",
|
|
169
172
|
"Provider",
|
|
170
173
|
"ProvisionAgreement",
|
|
171
174
|
"Ref",
|
|
@@ -25,35 +25,17 @@ import polars as pl
|
|
|
25
25
|
|
|
26
26
|
from sdmxlib.model.binding import DataflowBinding
|
|
27
27
|
|
|
28
|
+
# SqlRelation lives in sdmxlib.sql now (suite-level public contract);
|
|
29
|
+
# re-exported here for backwards compatibility with code that imports
|
|
30
|
+
# ``from sdmxlib.data_store import SqlRelation``.
|
|
31
|
+
from sdmxlib.sql import SqlRelation as SqlRelation # noqa: PLC0414 — public re-export
|
|
32
|
+
|
|
28
33
|
if TYPE_CHECKING:
|
|
29
34
|
import pyarrow as pa
|
|
30
35
|
|
|
31
36
|
from sdmxlib.rest import ComponentFilter
|
|
32
37
|
|
|
33
38
|
|
|
34
|
-
@runtime_checkable
|
|
35
|
-
class SqlRelation(Protocol):
|
|
36
|
-
"""A lazy single-column relation usable inside ``WHERE col IN (...)``.
|
|
37
|
-
|
|
38
|
-
Producers (typically :class:`~sdmxlib.model.code_query.CodeQuery`)
|
|
39
|
-
expose their compiled SELECT as a ``(sql, params)`` pair. The
|
|
40
|
-
streaming-query SQL builder splices the SQL inline as a subquery
|
|
41
|
-
and concatenates the params with the outer query's binds, so a
|
|
42
|
-
50k-row hierarchy traversal becomes one ``IN (SELECT ...)`` clause
|
|
43
|
-
with no parameter explosion.
|
|
44
|
-
|
|
45
|
-
The subquery's tables must be visible on the data store's
|
|
46
|
-
connection. For :class:`~sdmxlib.local.DuckDBDataStore` (which
|
|
47
|
-
shares its connection with :class:`~sdmxlib.local.Registry`),
|
|
48
|
-
codelist relations compose automatically because both sides see
|
|
49
|
-
the same ``sdmx.code`` table.
|
|
50
|
-
"""
|
|
51
|
-
|
|
52
|
-
def to_sql(self) -> tuple[str, list[object]]:
|
|
53
|
-
"""Return ``(sql, params)`` — a SELECT producing a single column."""
|
|
54
|
-
...
|
|
55
|
-
|
|
56
|
-
|
|
57
39
|
@runtime_checkable
|
|
58
40
|
class DataStore(Protocol):
|
|
59
41
|
"""Read-side contract for the data plane.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Shared SQL-composition primitives — used across pinax-suite.
|
|
2
|
+
|
|
3
|
+
Lifted from internal helpers in ``sdmxlib.data_store`` and
|
|
4
|
+
``sdmxlib.model.code_query`` so other libraries (pinaxlib, future
|
|
5
|
+
catalog-style tools) can compose with sdmxlib's lazy SQL machinery
|
|
6
|
+
without re-implementing the contracts.
|
|
7
|
+
|
|
8
|
+
Three primitives:
|
|
9
|
+
|
|
10
|
+
- :class:`SqlRelation` — a producer of ``(sql, params)`` for use as a
|
|
11
|
+
single-column subquery. Lets a 50k-row hierarchy traversal compile
|
|
12
|
+
to one ``IN (SELECT …)`` clause, with no parameter explosion. This
|
|
13
|
+
is the load-bearing pattern for cross-library lazy composition.
|
|
14
|
+
- :class:`Predicate` — a typed query predicate that compiles to a SQL
|
|
15
|
+
fragment. Composes into a tree on a builder; the builder's
|
|
16
|
+
compilation walks the tree once and emits one SQL.
|
|
17
|
+
- :class:`CompileContext` — state threaded through one compilation
|
|
18
|
+
pass: the row alias for the main table, accumulators for joins,
|
|
19
|
+
and a join-alias dedup map (so multiple predicates touching the
|
|
20
|
+
same bridge table or annotation type share one join).
|
|
21
|
+
|
|
22
|
+
Topology B from ``bundle-architecture.md`` (single DuckDB file
|
|
23
|
+
co-locating ``sdmx.*``, ``pinax.*``, and the DuckLake catalog tables)
|
|
24
|
+
is what makes :class:`SqlRelation` composition routinely useful across
|
|
25
|
+
sdmxlib + pinaxlib: subquery tables are always visible on the same
|
|
26
|
+
connection.
|
|
27
|
+
|
|
28
|
+
See ``predicate-and-sqlrelation.md`` in the statcan-catalog-builder
|
|
29
|
+
design docs for the full design rationale and decisions D1-D10.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
from dataclasses import dataclass, field
|
|
35
|
+
from typing import Protocol, runtime_checkable
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"CompileContext",
|
|
39
|
+
"Predicate",
|
|
40
|
+
"SqlRelation",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@runtime_checkable
|
|
45
|
+
class SqlRelation(Protocol):
|
|
46
|
+
"""A lazy single-column relation usable inside ``WHERE col IN (...)``.
|
|
47
|
+
|
|
48
|
+
Producers (typically :class:`~sdmxlib.model.code_query.CodeQuery`,
|
|
49
|
+
:class:`~sdmxlib.model.hierarchy_query.HierarchyNodeQuery`, and the
|
|
50
|
+
pinaxlib query builders) expose their compiled SELECT as a
|
|
51
|
+
``(sql, params)`` pair. The streaming-query SQL builder splices the
|
|
52
|
+
SQL inline as a subquery and concatenates the params with the outer
|
|
53
|
+
query's binds, so an N-row traversal becomes one
|
|
54
|
+
``IN (SELECT ...)`` clause with no parameter explosion.
|
|
55
|
+
|
|
56
|
+
The subquery's tables must be visible on the consumer's connection.
|
|
57
|
+
For :class:`~sdmxlib.local.DuckDBDataStore` (which shares its
|
|
58
|
+
connection with :class:`~sdmxlib.local.Registry`), codelist
|
|
59
|
+
relations compose automatically because both sides see the same
|
|
60
|
+
``sdmx.code`` table. For pinaxlib's ``Catalog``, the same applies
|
|
61
|
+
when the bundle uses Topology B (one DuckDB file holding sdmx.*,
|
|
62
|
+
pinax.*, and the DuckLake catalog).
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def to_sql(self) -> tuple[str, list[object]]:
|
|
66
|
+
"""Return ``(sql, params)`` — a SELECT producing a single column."""
|
|
67
|
+
...
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class CompileContext:
|
|
72
|
+
"""State threaded through one compilation pass.
|
|
73
|
+
|
|
74
|
+
Predicate compilers receive a context object so multiple predicates
|
|
75
|
+
in the same query can share state — most importantly, joins. The
|
|
76
|
+
``join_aliases`` map records "did we already join the bridge table
|
|
77
|
+
for this query?"; a second predicate touching the same join reuses
|
|
78
|
+
the alias instead of adding a duplicate.
|
|
79
|
+
|
|
80
|
+
Attributes:
|
|
81
|
+
row_alias: The main table's alias in the outer query (e.g.
|
|
82
|
+
``"d"`` for ``pinax.dataset d``, ``"c"`` for ``sdmx.code c``,
|
|
83
|
+
``"o"`` for an observation-table scan).
|
|
84
|
+
join_aliases: Dedup map for repeated joins. Keyed by an
|
|
85
|
+
implementation-defined join id (e.g. annotation type,
|
|
86
|
+
bridge table name + dimension); value is the SQL alias
|
|
87
|
+
assigned the first time the join was added.
|
|
88
|
+
join_sqls: Accumulated JOIN fragments to splice between
|
|
89
|
+
``FROM`` and ``WHERE`` in the outer query.
|
|
90
|
+
join_params: Parameters for the accumulated joins, in
|
|
91
|
+
insertion order.
|
|
92
|
+
schemas: Logical-to-physical schema name map. Default identity
|
|
93
|
+
(``"sdmx"`` → ``"sdmx"``, ``"pinax"`` → ``"pinax"``);
|
|
94
|
+
override for federation or alternate naming.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
row_alias: str
|
|
98
|
+
join_aliases: dict[str, str] = field(default_factory=dict)
|
|
99
|
+
join_sqls: list[str] = field(default_factory=list)
|
|
100
|
+
join_params: list[object] = field(default_factory=list)
|
|
101
|
+
schemas: dict[str, str] = field(default_factory=dict)
|
|
102
|
+
_alias_counter: int = 0
|
|
103
|
+
|
|
104
|
+
def schema(self, logical: str) -> str:
|
|
105
|
+
"""Resolve a logical schema name to its physical SQL identifier."""
|
|
106
|
+
return self.schemas.get(logical, logical)
|
|
107
|
+
|
|
108
|
+
def next_alias(self, prefix: str = "f") -> str:
|
|
109
|
+
"""Generate a unique SQL alias for use in a subquery or join.
|
|
110
|
+
|
|
111
|
+
Each call returns a fresh ``{prefix}{n}`` identifier. Predicate
|
|
112
|
+
compilers use this to give correlated subqueries unique table
|
|
113
|
+
aliases when multiple predicates would otherwise collide.
|
|
114
|
+
|
|
115
|
+
The counter is per-context (per-compilation-pass), so aliases are
|
|
116
|
+
deterministic within a query and naturally re-numbered for the
|
|
117
|
+
next compilation. Use a prefix like ``"ef"`` (exists filter) or
|
|
118
|
+
``"j"`` (join) to make the generated SQL self-documenting.
|
|
119
|
+
"""
|
|
120
|
+
self._alias_counter += 1
|
|
121
|
+
return f"{prefix}{self._alias_counter}"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@runtime_checkable
|
|
125
|
+
class Predicate(Protocol):
|
|
126
|
+
"""A query predicate that compiles to a SQL WHERE-clause fragment.
|
|
127
|
+
|
|
128
|
+
Implementations live in pinaxlib (e.g. ``has_code``, ``MapFilter``,
|
|
129
|
+
``ExistsFilter``) and sdmxlib (e.g. ``code.id == "X"``,
|
|
130
|
+
``dim("FREQ") == "M"``). Both library's compilers reuse the same
|
|
131
|
+
:class:`CompileContext`, so a single query containing predicates
|
|
132
|
+
from both libraries compiles into one SQL with shared joins.
|
|
133
|
+
|
|
134
|
+
The implementation may add to ``ctx.join_sqls`` / ``ctx.join_params``
|
|
135
|
+
via :func:`CompileContext.join_aliases` dedup as a side effect of
|
|
136
|
+
compilation.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
``(fragment, params)`` — a SQL fragment safe to AND into a
|
|
140
|
+
WHERE clause, and the parameters for it. The fragment must
|
|
141
|
+
reference the main table via ``ctx.row_alias``.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def to_sql(self, ctx: CompileContext) -> tuple[str, list[object]]:
|
|
145
|
+
"""Emit a SQL fragment + params for this predicate."""
|
|
146
|
+
...
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|