├── .cargo └── config.toml ├── .github └── workflows │ ├── audit.yaml │ ├── docs.yaml │ ├── python_query_tests.yml │ ├── python_release.yml │ └── rust_tests.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Cargo.lock ├── Cargo.toml ├── IMPORTANT TODOS ├── LICENSE ├── README.md ├── doc ├── .gitignore └── chrontext_arch.png ├── lib ├── bigquery-polars │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ └── src │ │ ├── errors.rs │ │ ├── lib.rs │ │ └── querying.rs ├── chrontext │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ ├── change_types.rs │ │ ├── combiner.rs │ │ ├── combiner │ │ ├── lazy_aggregate.rs │ │ ├── lazy_expressions.rs │ │ ├── lazy_graph_patterns.rs │ │ ├── lazy_graph_patterns │ │ │ ├── distinct.rs │ │ │ ├── extend.rs │ │ │ ├── filter.rs │ │ │ ├── group.rs │ │ │ ├── join.rs │ │ │ ├── left_join.rs │ │ │ ├── minus.rs │ │ │ ├── order_by.rs │ │ │ ├── project.rs │ │ │ ├── slice.rs │ │ │ ├── union.rs │ │ │ └── values.rs │ │ ├── lazy_order.rs │ │ ├── static_subqueries.rs │ │ └── virtualized_queries.rs │ │ ├── constants.rs │ │ ├── constraints.rs │ │ ├── engine.rs │ │ ├── errors.rs │ │ ├── lib.rs │ │ ├── preparing.rs │ │ ├── preparing │ │ ├── expressions.rs │ │ ├── expressions │ │ │ ├── and_expression.rs │ │ │ ├── binary_ordinary_expression.rs │ │ │ ├── coalesce_expression.rs │ │ │ ├── exists_expression.rs │ │ │ ├── function_call_expression.rs │ │ │ ├── if_expression.rs │ │ │ ├── in_expression.rs │ │ │ ├── not_expression.rs │ │ │ ├── or_expression.rs │ │ │ └── unary_ordinary_expression.rs │ │ ├── graph_patterns.rs │ │ ├── graph_patterns │ │ │ ├── bgp_pattern.rs │ │ │ ├── distinct_pattern.rs │ │ │ ├── expression_rewrites.rs │ │ │ ├── extend_pattern.rs │ │ │ ├── filter_pattern.rs │ │ │ ├── graph_pattern.rs │ │ │ ├── group_pattern.rs │ │ │ ├── join_pattern.rs │ │ │ ├── left_join_pattern.rs │ │ │ ├── minus_pattern.rs │ │ │ ├── order_by_pattern.rs │ │ │ ├── path_pattern.rs │ │ │ ├── project_pattern.rs │ │ │ ├── reduced_pattern.rs │ │ │ ├── service_pattern.rs │ │ │ ├── slice_pattern.rs │ │ │ ├── union_pattern.rs │ │ │ └── values_pattern.rs │ │ └── synchronization.rs │ │ ├── preprocessing.rs │ │ ├── rename_vars.rs │ │ ├── rewriting.rs │ │ ├── rewriting │ │ ├── aggregate_expression.rs │ │ ├── expressions.rs │ │ ├── expressions │ │ │ ├── and_expression.rs │ │ │ ├── binary_ordinary_expression.rs │ │ │ ├── coalesce_expression.rs │ │ │ ├── exists_expression.rs │ │ │ ├── function_call_expression.rs │ │ │ ├── if_expression.rs │ │ │ ├── in_expression.rs │ │ │ ├── not_expression.rs │ │ │ ├── or_expression.rs │ │ │ └── unary_ordinary_expression.rs │ │ ├── graph_patterns.rs │ │ ├── graph_patterns │ │ │ ├── bg_pattern.rs │ │ │ ├── distinct_pattern.rs │ │ │ ├── extend_pattern.rs │ │ │ ├── filter_pattern.rs │ │ │ ├── graph_pattern.rs │ │ │ ├── group_pattern.rs │ │ │ ├── join_pattern.rs │ │ │ ├── left_join_pattern.rs │ │ │ ├── minus_pattern.rs │ │ │ ├── order_by_pattern.rs │ │ │ ├── path_pattern.rs │ │ │ ├── project_pattern.rs │ │ │ ├── reduced_pattern.rs │ │ │ ├── service_pattern.rs │ │ │ ├── sliced_pattern.rs │ │ │ ├── union_pattern.rs │ │ │ └── values_pattern.rs │ │ ├── order_expression.rs │ │ ├── project_static.rs │ │ └── subqueries.rs │ │ ├── sparql_result_to_polars.rs │ │ └── splitter.rs ├── flight │ ├── Cargo.toml │ └── src │ │ ├── client.rs │ │ ├── lib.rs │ │ └── server.rs ├── postgres │ ├── Cargo.toml │ └── src │ │ ├── catalog.rs │ │ ├── config.rs │ │ ├── errors.rs │ │ ├── lib.rs │ │ └── server.rs ├── sparql_database │ ├── Cargo.toml │ └── src │ │ ├── embedded_oxigraph.rs │ │ ├── endpoint.rs │ │ └── lib.rs ├── virtualization │ ├── Cargo.toml │ └── src │ │ ├── bigquery.rs │ │ ├── errors.rs │ │ ├── lib.rs │ │ ├── python.rs │ │ └── python │ │ └── sql_translation.rs └── virtualized_query │ ├── Cargo.toml │ └── src │ ├── lib.rs │ ├── pushdown_setting.rs │ └── python.rs ├── licensing └── POLARS_LICENSE └── py_chrontext ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── chrontext ├── .gitignore ├── __init__.py ├── chrontext.pyi ├── py.typed └── vq │ ├── __init__.py │ └── __init__.pyi ├── pyproject.toml ├── src ├── errors.rs └── lib.rs └── tests ├── .gitignore ├── FIX_OPENSSL_CONDA ├── conftest.py ├── disable_test_opcua.py ├── my_graph.ttl ├── requirements.txt ├── test_bigquery.py ├── test_databricks.py ├── test_python_database.py ├── test_readme_tutorial.py ├── testdata ├── expected_simple_query.csv ├── expected_simplified_opcua_case.csv ├── python_based │ ├── expected_coalesce_query.csv │ ├── expected_complex_hybrid.csv │ ├── expected_distinct_query.csv │ ├── expected_if_query.csv │ ├── expected_in_expression.csv │ ├── expected_minus_query.csv │ ├── expected_optional_clause_query.csv │ ├── expected_path_group_by_query.csv │ ├── expected_pushdown_exists_aggregated_timeseries_value_hybrid.csv │ ├── expected_pushdown_exists_timeseries_value_hybrid.csv │ ├── expected_pushdown_group_by_concat_agg_hybrid.csv │ ├── expected_pushdown_group_by_exists_something_hybrid.csv │ ├── expected_pushdown_group_by_hybrid.csv │ ├── expected_pushdown_group_by_second_having_hybrid.csv │ ├── expected_pushdown_group_by_second_hybrid.csv │ ├── expected_pushdown_not_exists_aggregated_timeseries_value_hybrid.csv │ ├── expected_simple_hybrid.csv │ ├── expected_simple_hybrid_sugar.csv │ ├── expected_simple_hybrid_sugar_agg_avg.csv │ ├── expected_union_of_two_groupby.csv │ ├── expected_union_query.csv │ ├── expected_values_query.csv │ ├── testdata.ttl │ ├── ts1.csv │ └── ts2.csv └── testdata_opcua_history_read.sparql ├── ts1.csv └── ts2.csv /.cargo/config.toml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataTreehouse/chrontext/372f343e9a16d3cef152cd332a71f3d3f8e8a305/.cargo/config.toml -------------------------------------------------------------------------------- /.github/workflows/audit.yaml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | push: 4 | paths: 5 | - '**/Cargo.toml' 6 | - '**/Cargo.lock' 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | RUST_TOOLCHAIN: nightly-2025-03-07 11 | 12 | jobs: 13 | security_audit: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout chrontext 17 | uses: actions/checkout@v4 18 | 19 | - name: Audit 20 | uses: rustsec/audit-check@v1.4.1 21 | with: 22 | token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/docs.yaml: -------------------------------------------------------------------------------- 1 | #Based on https://github.com/mitmproxy/pdoc/blob/main/.github/workflows/docs.yml 2 | name: website 3 | 4 | # build the documentation whenever there are new changes on main 5 | on: 6 | push: 7 | branches: 8 | - main 9 | 10 | # security: restrict permissions for CI jobs. 11 | permissions: 12 | contents: read 13 | 14 | env: 15 | CARGO_TERM_COLOR: always 16 | RUST_LOG: debug 17 | MATURIN_VERSION: '1.7.4' 18 | RUST_TOOLCHAIN: nightly-2025-03-07 19 | 20 | jobs: 21 | # Build the documentation and upload the static HTML files as an artifact. 22 | build: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v3 26 | - name: Install dependencies 27 | run: | 28 | python -m venv .venv 29 | source .venv/bin/activate 30 | pip install --upgrade pip 31 | pip install "maturin[patchelf]"==${{ env.MATURIN_VERSION }} 32 | pip install -r tests/requirements.txt 33 | pip install pdoc 34 | working-directory: ./py_chrontext 35 | 36 | - name: Set up nightly rust 37 | run: | 38 | rustup toolchain install ${{ env.RUST_TOOLCHAIN }} 39 | rustup default ${{ env.RUST_TOOLCHAIN }} 40 | 41 | - name: Build install package and build docs 42 | run: | 43 | source .venv/bin/activate 44 | maturin develop 45 | pdoc -o docs/ chrontext 46 | working-directory: ./py_chrontext 47 | 48 | - uses: actions/upload-pages-artifact@v3 49 | with: 50 | path: py_chrontext/docs/ 51 | 52 | # Deploy the artifact to GitHub pages. 53 | # This is a separate job so that only actions/deploy-pages has the necessary permissions. 54 | deploy: 55 | needs: build 56 | runs-on: ubuntu-latest 57 | permissions: 58 | pages: write 59 | id-token: write 60 | environment: 61 | name: github-pages 62 | url: ${{ steps.deployment.outputs.page_url }} 63 | steps: 64 | - id: deployment 65 | uses: actions/deploy-pages@v4 -------------------------------------------------------------------------------- /.github/workflows/python_query_tests.yml: -------------------------------------------------------------------------------- 1 | # Adapted from https://github.com/pola-rs/polars/blob/main/.github/workflows/release-python.yml 2 | # With license found in chrontext/licensing/POLARS_LICENSE 3 | name: Python query tests 4 | 5 | on: 6 | push: 7 | branches: [ main, feature/*, bugfix/* ] 8 | pull_request: 9 | branches: [ main, feature/*, bugfix/* ] 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | RUST_LOG: debug 14 | MATURIN_VERSION: '1.7.4' 15 | RUST_TOOLCHAIN: nightly-2025-03-07 16 | 17 | jobs: 18 | build_and_test: 19 | runs-on: 20 | group: ubuntu_runners 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | 25 | - name: Fix README symlink 26 | run: | 27 | rm py_chrontext/README.md 28 | cp README.md py_chrontext/README.md 29 | 30 | - name: Install dependencies 31 | run: | 32 | python -m venv .venv 33 | source .venv/bin/activate 34 | pip install --upgrade pip 35 | pip install -r tests/requirements.txt 36 | pip install "maturin[patchelf]"==${{ env.MATURIN_VERSION }} 37 | working-directory: ./py_chrontext 38 | 39 | - name: Set up nightly rust 40 | run: | 41 | rustup toolchain install ${{ env.RUST_TOOLCHAIN }} 42 | rustup default ${{ env.RUST_TOOLCHAIN }} 43 | 44 | - name: Build install package 45 | run: | 46 | source .venv/bin/activate 47 | maturin develop 48 | working-directory: ./py_chrontext 49 | 50 | - name: pytest 51 | run: | 52 | source ../.venv/bin/activate 53 | pytest 54 | working-directory: ./py_chrontext/tests 55 | -------------------------------------------------------------------------------- /.github/workflows/python_release.yml: -------------------------------------------------------------------------------- 1 | # Adapted from https://github.com/pola-rs/polars/blob/main/.github/workflows/release-python.yml 2 | # With license found in chrontext/licensing/POLARS_LICENSE 3 | 4 | name: Release Python 5 | 6 | on: 7 | push: 8 | tags: 9 | - 'py-v[0-9]+.[0-9]+.[0-9]+' 10 | 11 | permissions: 12 | contents: write 13 | 14 | env: 15 | CARGO_TERM_COLOR: always 16 | RUST_TOOLCHAIN: nightly-2025-03-07 17 | MATURIN_VERSION: '1.7.4' 18 | MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }} 19 | PYTHON_VERSION: '3.12' 20 | 21 | jobs: 22 | manylinux-x64_64: 23 | runs-on: ubuntu-latest 24 | timeout-minutes: 60 25 | environment: release 26 | steps: 27 | - uses: actions/checkout@v3 28 | - uses: actions/setup-python@v4 29 | with: 30 | python-version: ${{ env.PYTHON_VERSION }} 31 | 32 | - name: Fix README symlink 33 | run: | 34 | rm py_chrontext/README.md 35 | cp README.md py_chrontext/README.md 36 | cp -r doc py_chrontext/doc 37 | 38 | - name: Publish wheel 39 | uses: PyO3/maturin-action@v1 40 | env: 41 | RUSTFLAGS: -C target-feature=+fxsr,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt,+avx,+fma 42 | with: 43 | command: publish 44 | args: -m py_chrontext/Cargo.toml --skip-existing -o wheels -u magbak 45 | container: quay.io/pypa/manylinux_2_28_x86_64:latest 46 | before-script-linux: yum -y install perl-IPC-Cmd clang 47 | maturin-version: ${{ env.MATURIN_VERSION }} 48 | rust-toolchain: ${{ env.RUST_TOOLCHAIN }} 49 | 50 | win: 51 | runs-on: windows-latest 52 | timeout-minutes: 60 53 | environment: release 54 | steps: 55 | - uses: actions/checkout@v3 56 | - uses: actions/setup-python@v4 57 | with: 58 | python-version: ${{ env.PYTHON_VERSION }} 59 | 60 | - name: Fix README symlink 61 | run: | 62 | rm py_chrontext/README.md 63 | cp README.md py_chrontext/README.md 64 | cp -r doc py_chrontext/doc 65 | 66 | - name: Publish wheel 67 | uses: PyO3/maturin-action@v1 68 | env: 69 | RUSTFLAGS: -C target-feature=+fxsr,+sse,+sse2,+sse3,+sse4.1,+sse4.2 70 | with: 71 | command: publish 72 | args: -m py_chrontext/Cargo.toml --features=opcua --no-sdist --skip-existing -o wheels -u magbak 73 | maturin-version: ${{ env.MATURIN_VERSION }} 74 | rust-toolchain: ${{ env.RUST_TOOLCHAIN }} 75 | 76 | macos: 77 | runs-on: macos-latest 78 | timeout-minutes: 60 79 | environment: release 80 | steps: 81 | - uses: actions/checkout@v3 82 | - uses: actions/setup-python@v4 83 | with: 84 | python-version: ${{ env.PYTHON_VERSION }} 85 | 86 | - name: Fix README symlink 87 | run: | 88 | rm py_chrontext/README.md 89 | cp README.md py_chrontext/README.md 90 | cp -r doc py_chrontext/doc 91 | 92 | - name: Publish wheel 93 | uses: PyO3/maturin-action@v1 94 | env: 95 | MACOSX_DEPLOYMENT_TARGET: 12.6 96 | with: 97 | command: publish 98 | args: -m py_chrontext/Cargo.toml --features=opcua --no-sdist --skip-existing -o wheels -u magbak 99 | maturin-version: ${{ env.MATURIN_VERSION }} 100 | rust-toolchain: ${{ env.RUST_TOOLCHAIN }} 101 | 102 | macos-aarch64: 103 | runs-on: macos-latest 104 | timeout-minutes: 60 105 | environment: release 106 | steps: 107 | - uses: actions/checkout@v3 108 | - uses: actions/setup-python@v4 109 | with: 110 | python-version: ${{ matrix.python-version }} 111 | 112 | - name: Fix README symlink 113 | run: | 114 | rm py_chrontext/README.md 115 | cp README.md py_chrontext/README.md 116 | cp -r doc py_chrontext/doc 117 | 118 | - name: Set up Rust targets 119 | run: rustup target add aarch64-apple-darwin 120 | 121 | - name: Publish wheel 122 | uses: PyO3/maturin-action@v1 123 | with: 124 | command: publish 125 | args: -m py_chrontext/Cargo.toml --features=opcua --target aarch64-apple-darwin --no-sdist -o wheels -u magbak 126 | maturin-version: ${{ env.MATURIN_VERSION }} 127 | rust-toolchain: ${{ env.RUST_TOOLCHAIN }} 128 | -------------------------------------------------------------------------------- /.github/workflows/rust_tests.yml: -------------------------------------------------------------------------------- 1 | name: Rust tests 2 | 3 | on: 4 | push: 5 | branches: [ main, feature/*, bugfix/* ] 6 | pull_request: 7 | branches: [ main, feature/*, bugfix/* ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | RUST_LOG: debug 12 | RUST_TOOLCHAIN: nightly-2025-03-07 13 | 14 | jobs: 15 | build_and_test: 16 | runs-on: 17 | group: ubuntu_runners 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | 22 | - name: Upgrade rust with nightly, install build-essential, pull oxigraph and dremio image 23 | run: | 24 | rustup update 25 | rustup toolchain install ${{ env.RUST_TOOLCHAIN }} 26 | rustup default ${{ env.RUST_TOOLCHAIN }} 27 | 28 | docker pull oxigraph/oxigraph:v0.3.8 29 | 30 | - name: Build 31 | run: cargo build --verbose --all 32 | - name: Run rust tests 33 | run: cargo test --verbose --all 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | chrontext/pki 2 | chrontext/pki-server 3 | .idea 4 | 5 | # Generated by Cargo 6 | # will have compiled files and executables 7 | /target/ 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | skratch.py -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "1" 3 | members = [ 4 | "lib/chrontext", 5 | "lib/virtualization", 6 | "lib/postgres", 7 | "lib/virtualized_query", 8 | "py_chrontext", 9 | "lib/flight", 10 | "lib/sparql_database" 11 | ] 12 | 13 | [workspace.dependencies] 14 | #spargebra = { path = "../maplib/lib/spargebra", features = ["rdf-star"]} 15 | #query_processing = { path = "../maplib/lib/query_processing"} 16 | #pydf_io = { path = "../maplib/lib/pydf_io"} 17 | #representation = { path = "../maplib/lib/representation", features = ["rdf-star"]} 18 | #templates = { path = "../maplib/lib/templates"} 19 | spargebra = { git = "https://github.com/DataTreehouse/maplib", rev="c3f59a6210de3f003e455c63bace8b03579d2657" } 20 | query_processing = { git = "https://github.com/DataTreehouse/maplib", rev="c3f59a6210de3f003e455c63bace8b03579d2657" } 21 | pydf_io = { git = "https://github.com/DataTreehouse/maplib", rev="c3f59a6210de3f003e455c63bace8b03579d2657" } 22 | representation = { git = "https://github.com/DataTreehouse/maplib", rev="c3f59a6210de3f003e455c63bace8b03579d2657" } 23 | templates = { git = "https://github.com/DataTreehouse/maplib", rev="c3f59a6210de3f003e455c63bace8b03579d2657" } 24 | 25 | 26 | sparesults = { version = "0.2.3" } 27 | oxrdf = { version = "0.2.2" } 28 | polars = "0.46.0" 29 | tonic = "0.12.3" 30 | tokio = "1.44.2" 31 | arrow-flight = "54.3.1" 32 | futures = "0.3.31" 33 | bincode = { version="2.0.1", features = ["serde"] } 34 | log = "0.4.21" 35 | pyo3 = { version= "0.24.1", features = ["abi3-py39"] } 36 | reqwest = "0.12.5" 37 | env_logger = "0.11.3" 38 | thiserror = "2.0.12" 39 | async-recursion = "1.1.0" 40 | async-trait = "0.1.81" 41 | serde = "1.0.203" 42 | serde_json = "1.0.117" 43 | secrecy = "=0.10.3" 44 | gcp-bigquery-client = "0.25.1" 45 | rayon = "1.10.0" 46 | uuid = {version = "1.10.0", features = ["fast-rng", "v4"]} 47 | rustls = {version = "0.23.18"} 48 | url = {version = "2.5.4"} 49 | 50 | [patch.crates-io] 51 | polars = { git = 'https://github.com/pola-rs/polars', rev="319a9a84ab573886b2a13548a8e462fee353acef" } 52 | polars-core = { git = 'https://github.com/pola-rs/polars', rev="319a9a84ab573886b2a13548a8e462fee353acef" } -------------------------------------------------------------------------------- /IMPORTANT TODOS: -------------------------------------------------------------------------------- 1 | Fix left join expression pushdowns 2 | Fix condition that both datapoints and data values are declared within the same BGP. 3 | Engine should complain otherwise. 4 | Synchronized queries now require partitioning support even though it might not be present - fix this. 5 | Also do not project partitioning cols further if they were introduced here 6 | Exists introductions of vq in aggregations and in ordering expressions are not kept.. fix in preparing 7 | 8 | New todos: 9 | - Fix create subquery is set by create subquery || is_subquery for arity>1 expression rewrite. 10 | - Fix creation of all appropriate data types in combiner 11 | - Fix insert static data in static queries and in vqs.. 12 | - Fix support for subquery in other expression types, e.g. extend.. -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | windpower.* -------------------------------------------------------------------------------- /doc/chrontext_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataTreehouse/chrontext/372f343e9a16d3cef152cd332a71f3d3f8e8a305/doc/chrontext_arch.png -------------------------------------------------------------------------------- /lib/bigquery-polars/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bigquery-polars" 3 | version = "0.2.3" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | gcp-bigquery-client = {workspace = true, features = ["rust-tls"]} 8 | polars = {workspace = true, features = ["dtype-full", "cse", "nightly", "performant", "timezones", "lazy"]} 9 | thiserror.workspace = true 10 | tokio = {workspace = true, features = ["time"]} 11 | rayon.workspace = true 12 | -------------------------------------------------------------------------------- /lib/bigquery-polars/README.md: -------------------------------------------------------------------------------- 1 | # bigquery-polars 2 | -------------------------------------------------------------------------------- /lib/bigquery-polars/src/errors.rs: -------------------------------------------------------------------------------- 1 | use gcp_bigquery_client::error::BQError; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum BigQueryExecutorError { 6 | #[error(transparent)] 7 | ClientError(BQError), 8 | #[error("Job reference missing")] 9 | JobReferenceMissingError, 10 | #[error("Job id is none")] 11 | JobIdNoneError, 12 | #[error("Schema is missing")] 13 | SchemaMissing, 14 | } 15 | -------------------------------------------------------------------------------- /lib/bigquery-polars/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod errors; 2 | mod querying; 3 | 4 | pub use gcp_bigquery_client::env_vars; 5 | pub use gcp_bigquery_client::Client; 6 | pub use querying::*; 7 | -------------------------------------------------------------------------------- /lib/chrontext/.gitignore: -------------------------------------------------------------------------------- 1 | pki 2 | pki-server -------------------------------------------------------------------------------- /lib/chrontext/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chrontext" 3 | version = "0.4.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | virtualization = {path = "../virtualization"} 8 | virtualized_query = { path = "../virtualized_query" } 9 | sparql_database = { path = "../sparql_database" } 10 | 11 | polars = { workspace = true, features = [ 12 | "lazy", 13 | "concat_str", 14 | "unique_counts", 15 | "list_eval", 16 | "abs", 17 | "round_series", 18 | "is_in", 19 | "cum_agg", 20 | "dtype-full", 21 | "cse", 22 | "nightly", 23 | "performant"] } 24 | log.workspace = true 25 | spargebra.workspace = true 26 | representation.workspace=true 27 | query_processing.workspace = true 28 | sparesults.workspace = true 29 | oxrdf.workspace = true 30 | thiserror.workspace = true 31 | async-recursion.workspace = true 32 | uuid.workspace = true 33 | rayon.workspace = true 34 | 35 | -------------------------------------------------------------------------------- /lib/chrontext/src/change_types.rs: -------------------------------------------------------------------------------- 1 | #[derive(PartialEq, Debug, Clone)] 2 | pub enum ChangeType { 3 | Relaxed, 4 | Constrained, 5 | NoChange, 6 | } 7 | 8 | impl ChangeType { 9 | pub fn opposite(&self) -> ChangeType { 10 | match self { 11 | ChangeType::Relaxed => ChangeType::Constrained, 12 | ChangeType::Constrained => ChangeType::Relaxed, 13 | ChangeType::NoChange => ChangeType::NoChange, 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_graph_patterns/distinct.rs: -------------------------------------------------------------------------------- 1 | use super::Combiner; 2 | use crate::combiner::CombinerError; 3 | use async_recursion::async_recursion; 4 | use log::debug; 5 | use query_processing::graph_patterns::distinct; 6 | use representation::query_context::{Context, PathEntry}; 7 | use representation::solution_mapping::SolutionMappings; 8 | use spargebra::algebra::GraphPattern; 9 | use spargebra::Query; 10 | use std::collections::HashMap; 11 | use virtualized_query::VirtualizedQuery; 12 | 13 | impl Combiner { 14 | #[async_recursion] 15 | pub(crate) async fn lazy_distinct( 16 | &mut self, 17 | inner: &GraphPattern, 18 | solution_mappings: Option, 19 | static_query_map: HashMap, 20 | prepared_virtualized_queries: Option>>, 21 | context: &Context, 22 | ) -> Result { 23 | debug!("Processing distinct graph pattern"); 24 | let solution_mappings = self 25 | .lazy_graph_pattern( 26 | inner, 27 | solution_mappings, 28 | static_query_map, 29 | prepared_virtualized_queries, 30 | &context.extension_with(PathEntry::DistinctInner), 31 | ) 32 | .await?; 33 | Ok(distinct(solution_mappings)?) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_graph_patterns/extend.rs: -------------------------------------------------------------------------------- 1 | use super::Combiner; 2 | use crate::combiner::static_subqueries::split_static_queries; 3 | use crate::combiner::virtualized_queries::split_virtualized_queries; 4 | use crate::combiner::CombinerError; 5 | use async_recursion::async_recursion; 6 | use log::debug; 7 | use oxrdf::Variable; 8 | use polars::prelude::{lit, LiteralValue}; 9 | use query_processing::find_query_variables::solution_mappings_has_all_expression_variables; 10 | use query_processing::graph_patterns::extend; 11 | use representation::query_context::{Context, PathEntry}; 12 | use representation::solution_mapping::SolutionMappings; 13 | use representation::{BaseRDFNodeType, RDFNodeType}; 14 | use spargebra::algebra::{Expression, GraphPattern}; 15 | use spargebra::Query; 16 | use std::collections::HashMap; 17 | use virtualized_query::VirtualizedQuery; 18 | 19 | impl Combiner { 20 | #[async_recursion] 21 | #[allow(clippy::too_many_arguments)] 22 | pub(crate) async fn lazy_extend( 23 | &mut self, 24 | inner: &GraphPattern, 25 | variable: &Variable, 26 | expression: &Expression, 27 | input_solution_mappings: Option, 28 | mut static_query_map: HashMap, 29 | mut prepared_virtualized_queries: Option>>, 30 | context: &Context, 31 | ) -> Result { 32 | debug!("Processing extend graph pattern"); 33 | let inner_context = context.extension_with(PathEntry::ExtendInner); 34 | let expression_context = context.extension_with(PathEntry::ExtendExpression); 35 | let inner_prepared_virtualized_queries = 36 | split_virtualized_queries(&mut prepared_virtualized_queries, &inner_context); 37 | let expression_prepared_virtualized_queries = 38 | split_virtualized_queries(&mut prepared_virtualized_queries, &expression_context); 39 | let inner_static_query_map = split_static_queries(&mut static_query_map, &inner_context); 40 | let expression_static_query_map = 41 | split_static_queries(&mut static_query_map, &expression_context); 42 | assert!(static_query_map.is_empty()); 43 | assert!(if let Some(vqs) = &prepared_virtualized_queries { 44 | vqs.is_empty() 45 | } else { 46 | true 47 | }); 48 | let mut output_solution_mappings = self 49 | .lazy_graph_pattern( 50 | inner, 51 | input_solution_mappings, 52 | inner_static_query_map, 53 | inner_prepared_virtualized_queries, 54 | &inner_context, 55 | ) 56 | .await?; 57 | 58 | let has_all = 59 | solution_mappings_has_all_expression_variables(&output_solution_mappings, expression); 60 | if has_all { 61 | output_solution_mappings = self 62 | .lazy_expression( 63 | expression, 64 | output_solution_mappings, 65 | Some(expression_static_query_map), 66 | expression_prepared_virtualized_queries, 67 | &expression_context, 68 | ) 69 | .await?; 70 | Ok(extend( 71 | output_solution_mappings, 72 | &expression_context, 73 | variable, 74 | )?) 75 | } else { 76 | output_solution_mappings.mappings = output_solution_mappings.mappings.with_column( 77 | lit(LiteralValue::untyped_null()) 78 | .cast(BaseRDFNodeType::None.polars_data_type()) 79 | .alias(variable.as_str()), 80 | ); 81 | output_solution_mappings 82 | .rdf_node_types 83 | .insert(variable.as_str().to_string(), RDFNodeType::None); 84 | Ok(output_solution_mappings) 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_graph_patterns/filter.rs: -------------------------------------------------------------------------------- 1 | use super::Combiner; 2 | use crate::combiner::static_subqueries::split_static_queries; 3 | use crate::combiner::virtualized_queries::split_virtualized_queries; 4 | use crate::combiner::CombinerError; 5 | use async_recursion::async_recursion; 6 | use log::debug; 7 | use query_processing::find_query_variables::solution_mappings_has_all_expression_variables; 8 | use query_processing::graph_patterns::filter; 9 | use representation::query_context::{Context, PathEntry}; 10 | use representation::solution_mapping::SolutionMappings; 11 | use spargebra::algebra::{Expression, GraphPattern}; 12 | use spargebra::Query; 13 | use std::collections::HashMap; 14 | use virtualized_query::VirtualizedQuery; 15 | 16 | impl Combiner { 17 | #[async_recursion] 18 | pub(crate) async fn lazy_filter( 19 | &mut self, 20 | inner: &GraphPattern, 21 | expression: &Expression, 22 | input_solution_mappings: Option, 23 | mut static_query_map: HashMap, 24 | mut prepared_virtualized_queries: Option>>, 25 | context: &Context, 26 | ) -> Result { 27 | debug!("Processing filter graph pattern"); 28 | let inner_context = context.extension_with(PathEntry::FilterInner); 29 | let expression_context = context.extension_with(PathEntry::FilterExpression); 30 | let inner_prepared_virtualized_queries = 31 | split_virtualized_queries(&mut prepared_virtualized_queries, &inner_context); 32 | let expression_prepared_virtualized_queries = 33 | split_virtualized_queries(&mut prepared_virtualized_queries, &expression_context); 34 | let inner_static_query_map = split_static_queries(&mut static_query_map, &inner_context); 35 | let expression_static_query_map = 36 | split_static_queries(&mut static_query_map, &expression_context); 37 | assert!(static_query_map.is_empty()); 38 | assert!(if let Some(vqs) = prepared_virtualized_queries { 39 | vqs.is_empty() 40 | } else { 41 | true 42 | }); 43 | 44 | let mut output_solution_mappings = self 45 | .lazy_graph_pattern( 46 | inner, 47 | input_solution_mappings, 48 | inner_static_query_map, 49 | inner_prepared_virtualized_queries, 50 | &inner_context, 51 | ) 52 | .await?; 53 | let has_all = 54 | solution_mappings_has_all_expression_variables(&output_solution_mappings, expression); 55 | if has_all { 56 | output_solution_mappings = self 57 | .lazy_expression( 58 | expression, 59 | output_solution_mappings, 60 | Some(expression_static_query_map), 61 | expression_prepared_virtualized_queries, 62 | &expression_context, 63 | ) 64 | .await?; 65 | Ok(filter(output_solution_mappings, &expression_context)?) 66 | } else { 67 | Ok(output_solution_mappings) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_graph_patterns/group.rs: -------------------------------------------------------------------------------- 1 | use super::Combiner; 2 | use crate::combiner::static_subqueries::split_static_queries; 3 | use crate::combiner::virtualized_queries::split_virtualized_queries; 4 | use crate::combiner::CombinerError; 5 | use log::debug; 6 | use oxrdf::Variable; 7 | use query_processing::aggregates::AggregateReturn; 8 | use query_processing::find_query_variables::solution_mappings_has_all_aggregate_expression_variables; 9 | use query_processing::graph_patterns::{group_by, prepare_group_by}; 10 | use representation::query_context::{Context, PathEntry}; 11 | use representation::solution_mapping::SolutionMappings; 12 | use spargebra::algebra::{AggregateExpression, GraphPattern}; 13 | use spargebra::Query; 14 | use std::collections::HashMap; 15 | use virtualized_query::VirtualizedQuery; 16 | 17 | impl Combiner { 18 | #[allow(clippy::too_many_arguments)] 19 | pub(crate) async fn lazy_group( 20 | &mut self, 21 | inner: &GraphPattern, 22 | variables: &[Variable], 23 | aggregates: &[(Variable, AggregateExpression)], 24 | solution_mapping: Option, 25 | mut static_query_map: HashMap, 26 | mut prepared_virtualized_queries: Option>>, 27 | context: &Context, 28 | ) -> Result { 29 | debug!("Processing group graph pattern"); 30 | let inner_context = context.extension_with(PathEntry::GroupInner); 31 | let inner_prepared_virtualized_queries = 32 | split_virtualized_queries(&mut prepared_virtualized_queries, &inner_context); 33 | let inner_static_query_map = split_static_queries(&mut static_query_map, &inner_context); 34 | 35 | let output_solution_mappings = self 36 | .lazy_graph_pattern( 37 | inner, 38 | solution_mapping, 39 | inner_static_query_map, 40 | inner_prepared_virtualized_queries, 41 | &inner_context, 42 | ) 43 | .await?; 44 | let (mut output_solution_mappings, by, dummy_varname) = 45 | prepare_group_by(output_solution_mappings, variables); 46 | 47 | let mut aggregate_expressions = vec![]; 48 | let mut new_rdf_node_types = HashMap::new(); 49 | for i in 0..aggregates.len() { 50 | let aggregate_context = context.extension_with(PathEntry::GroupAggregation(i as u16)); 51 | let (v, a) = aggregates.get(i).unwrap(); 52 | if solution_mappings_has_all_aggregate_expression_variables( 53 | &output_solution_mappings, 54 | a, 55 | ) { 56 | let AggregateReturn { 57 | solution_mappings: aggregate_solution_mappings, 58 | expr, 59 | context: _, 60 | rdf_node_type, 61 | } = self 62 | .sparql_aggregate_expression_as_lazy_column_and_expression( 63 | v, 64 | a, 65 | output_solution_mappings, 66 | &aggregate_context, 67 | ) 68 | .await?; 69 | output_solution_mappings = aggregate_solution_mappings; 70 | new_rdf_node_types.insert(v.as_str().to_string(), rdf_node_type); 71 | aggregate_expressions.push(expr); 72 | } 73 | } 74 | Ok(group_by( 75 | output_solution_mappings, 76 | aggregate_expressions, 77 | by, 78 | dummy_varname, 79 | new_rdf_node_types, 80 | )?) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_graph_patterns/join.rs: -------------------------------------------------------------------------------- 1 | use super::Combiner; 2 | use crate::combiner::static_subqueries::split_static_queries; 3 | use crate::combiner::virtualized_queries::split_virtualized_queries; 4 | use crate::combiner::CombinerError; 5 | use async_recursion::async_recursion; 6 | use log::debug; 7 | use representation::query_context::{Context, PathEntry}; 8 | use representation::solution_mapping::SolutionMappings; 9 | use spargebra::algebra::GraphPattern; 10 | use spargebra::Query; 11 | use std::collections::HashMap; 12 | use virtualized_query::VirtualizedQuery; 13 | 14 | impl Combiner { 15 | #[async_recursion] 16 | pub(crate) async fn lazy_join( 17 | &mut self, 18 | left: &GraphPattern, 19 | right: &GraphPattern, 20 | solution_mappings: Option, 21 | mut static_query_map: HashMap, 22 | mut prepared_virtualized_queries: Option>>, 23 | context: &Context, 24 | ) -> Result { 25 | debug!("Processing join graph pattern"); 26 | let left_context = context.extension_with(PathEntry::JoinLeftSide); 27 | let right_context = context.extension_with(PathEntry::JoinRightSide); 28 | let left_prepared_virtualized_queries = 29 | split_virtualized_queries(&mut prepared_virtualized_queries, &left_context); 30 | let right_prepared_virtualized_queries = 31 | split_virtualized_queries(&mut prepared_virtualized_queries, &right_context); 32 | let left_static_query_map = split_static_queries(&mut static_query_map, &left_context); 33 | let right_static_query_map = split_static_queries(&mut static_query_map, &right_context); 34 | assert!(static_query_map.is_empty()); 35 | assert!(if let Some(vqs) = &prepared_virtualized_queries { 36 | vqs.is_empty() 37 | } else { 38 | true 39 | }); 40 | let mut output_solution_mappings = self 41 | .lazy_graph_pattern( 42 | left, 43 | solution_mappings, 44 | left_static_query_map, 45 | left_prepared_virtualized_queries, 46 | &left_context, 47 | ) 48 | .await?; 49 | output_solution_mappings = self 50 | .lazy_graph_pattern( 51 | right, 52 | Some(output_solution_mappings), 53 | right_static_query_map, 54 | right_prepared_virtualized_queries, 55 | &right_context, 56 | ) 57 | .await?; 58 | Ok(output_solution_mappings) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_graph_patterns/left_join.rs: -------------------------------------------------------------------------------- 1 | //Uses code from https://github.com/magbak/maplib/blob/main/triplestore/src/sparql/lazy_graph_patterns/left_join.rs 2 | 3 | use super::Combiner; 4 | use crate::combiner::static_subqueries::split_static_queries; 5 | use crate::combiner::virtualized_queries::split_virtualized_queries; 6 | use crate::combiner::CombinerError; 7 | use async_recursion::async_recursion; 8 | use log::debug; 9 | use polars::prelude::JoinType; 10 | use query_processing::graph_patterns::{filter, join}; 11 | use representation::query_context::{Context, PathEntry}; 12 | use representation::solution_mapping::SolutionMappings; 13 | use spargebra::algebra::{Expression, GraphPattern}; 14 | use spargebra::Query; 15 | use std::collections::HashMap; 16 | use virtualized_query::VirtualizedQuery; 17 | 18 | impl Combiner { 19 | #[async_recursion] 20 | #[allow(clippy::too_many_arguments)] 21 | pub(crate) async fn lazy_left_join( 22 | &mut self, 23 | left: &GraphPattern, 24 | right: &GraphPattern, 25 | expression: &Option, 26 | solution_mapping: Option, 27 | mut static_query_map: HashMap, 28 | mut prepared_virtualized_queries: Option>>, 29 | context: &Context, 30 | ) -> Result { 31 | debug!("Processing left join graph pattern"); 32 | let left_context = context.extension_with(PathEntry::LeftJoinLeftSide); 33 | let right_context = context.extension_with(PathEntry::LeftJoinRightSide); 34 | let expression_context = context.extension_with(PathEntry::LeftJoinExpression); 35 | let left_prepared_virtualized_queries = 36 | split_virtualized_queries(&mut prepared_virtualized_queries, &left_context); 37 | let right_prepared_virtualized_queries = 38 | split_virtualized_queries(&mut prepared_virtualized_queries, &right_context); 39 | let expression_prepared_virtualized_queries = 40 | split_virtualized_queries(&mut prepared_virtualized_queries, &right_context); 41 | let left_static_query_map = split_static_queries(&mut static_query_map, &left_context); 42 | let right_static_query_map = split_static_queries(&mut static_query_map, &right_context); 43 | let expression_static_query_map = 44 | split_static_queries(&mut static_query_map, &expression_context); 45 | assert!(static_query_map.is_empty()); 46 | assert!(if let Some(vqs) = &prepared_virtualized_queries { 47 | vqs.is_empty() 48 | } else { 49 | true 50 | }); 51 | let left_solution_mappings = self 52 | .lazy_graph_pattern( 53 | left, 54 | solution_mapping, 55 | left_static_query_map, 56 | left_prepared_virtualized_queries, 57 | &left_context, 58 | ) 59 | .await?; 60 | 61 | let mut right_solution_mappings = self 62 | .lazy_graph_pattern( 63 | right, 64 | Some(left_solution_mappings.clone()), 65 | right_static_query_map, 66 | right_prepared_virtualized_queries, 67 | &right_context, 68 | ) 69 | .await?; 70 | 71 | if let Some(expr) = expression { 72 | right_solution_mappings = self 73 | .lazy_expression( 74 | expr, 75 | right_solution_mappings, 76 | Some(expression_static_query_map), 77 | expression_prepared_virtualized_queries, 78 | &expression_context, 79 | ) 80 | .await?; 81 | right_solution_mappings = filter(right_solution_mappings, &expression_context)?; 82 | right_solution_mappings 83 | .rdf_node_types 84 | .remove(expression_context.as_str()); 85 | } 86 | Ok(join( 87 | left_solution_mappings, 88 | right_solution_mappings, 89 | JoinType::Left, 90 | )?) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_graph_patterns/minus.rs: -------------------------------------------------------------------------------- 1 | use super::Combiner; 2 | use crate::combiner::static_subqueries::split_static_queries; 3 | use crate::combiner::virtualized_queries::split_virtualized_queries; 4 | use crate::combiner::CombinerError; 5 | use async_recursion::async_recursion; 6 | use log::debug; 7 | use query_processing::graph_patterns::minus; 8 | use representation::query_context::{Context, PathEntry}; 9 | use representation::solution_mapping::SolutionMappings; 10 | use spargebra::algebra::GraphPattern; 11 | use spargebra::Query; 12 | use std::collections::HashMap; 13 | use virtualized_query::VirtualizedQuery; 14 | 15 | impl Combiner { 16 | #[async_recursion] 17 | pub(crate) async fn lazy_minus( 18 | &mut self, 19 | left: &GraphPattern, 20 | right: &GraphPattern, 21 | solution_mappings: Option, 22 | mut static_query_map: HashMap, 23 | mut prepared_virtualized_queries: Option>>, 24 | context: &Context, 25 | ) -> Result { 26 | debug!("Processing minus graph pattern"); 27 | let left_context = context.extension_with(PathEntry::MinusLeftSide); 28 | let right_context = context.extension_with(PathEntry::MinusRightSide); 29 | let left_prepared_virtualized_queries = 30 | split_virtualized_queries(&mut prepared_virtualized_queries, &left_context); 31 | let right_prepared_virtualized_queries = 32 | split_virtualized_queries(&mut prepared_virtualized_queries, &right_context); 33 | let left_static_query_map = split_static_queries(&mut static_query_map, &left_context); 34 | let right_static_query_map = split_static_queries(&mut static_query_map, &right_context); 35 | assert!(static_query_map.is_empty()); 36 | assert!(if let Some(vqs) = &prepared_virtualized_queries { 37 | vqs.is_empty() 38 | } else { 39 | true 40 | }); 41 | self.counter += 1; 42 | let left_solution_mappings = self 43 | .lazy_graph_pattern( 44 | left, 45 | solution_mappings, 46 | left_static_query_map, 47 | left_prepared_virtualized_queries, 48 | &left_context, 49 | ) 50 | .await?; 51 | let right_solution_mappings = self 52 | .lazy_graph_pattern( 53 | right, 54 | Some(left_solution_mappings.clone()), 55 | right_static_query_map, 56 | right_prepared_virtualized_queries, 57 | &right_context, 58 | ) 59 | .await?; 60 | Ok(minus(left_solution_mappings, right_solution_mappings)?) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_graph_patterns/order_by.rs: -------------------------------------------------------------------------------- 1 | use super::Combiner; 2 | use crate::combiner::CombinerError; 3 | use async_recursion::async_recursion; 4 | use log::debug; 5 | use query_processing::find_query_variables::solution_mappings_has_all_order_expression_variables; 6 | use query_processing::graph_patterns::order_by; 7 | use representation::query_context::{Context, PathEntry}; 8 | use representation::solution_mapping::SolutionMappings; 9 | use spargebra::algebra::{GraphPattern, OrderExpression}; 10 | use spargebra::Query; 11 | use std::collections::HashMap; 12 | use virtualized_query::VirtualizedQuery; 13 | 14 | impl Combiner { 15 | #[async_recursion] 16 | pub(crate) async fn lazy_order_by( 17 | &mut self, 18 | inner: &GraphPattern, 19 | expression: &Vec, 20 | solution_mappings: Option, 21 | static_query_map: HashMap, 22 | prepared_virtualized_queries: Option>>, 23 | context: &Context, 24 | ) -> Result { 25 | debug!("Processing order by graph pattern"); 26 | let mut output_solution_mappings = self 27 | .lazy_graph_pattern( 28 | inner, 29 | solution_mappings, 30 | static_query_map, 31 | prepared_virtualized_queries, 32 | &context.extension_with(PathEntry::OrderByInner), 33 | ) 34 | .await?; 35 | let mut order_expressions = vec![]; 36 | for oe in expression { 37 | if solution_mappings_has_all_order_expression_variables(&output_solution_mappings, oe) { 38 | //Todo: Avoid clone 39 | order_expressions.push(oe.clone()); 40 | } 41 | } 42 | let order_expression_contexts: Vec = (0..order_expressions.len()) 43 | .map(|i| context.extension_with(PathEntry::OrderByExpression(i as u16))) 44 | .collect(); 45 | let mut asc_ordering = vec![]; 46 | let mut inner_contexts = vec![]; 47 | for i in 0..order_expressions.len() { 48 | let (ordering_solution_mappings, reverse, inner_context) = self 49 | .lazy_order_expression( 50 | order_expressions.get(i).unwrap(), 51 | output_solution_mappings, 52 | order_expression_contexts.get(i).unwrap(), 53 | ) 54 | .await?; 55 | output_solution_mappings = ordering_solution_mappings; 56 | inner_contexts.push(inner_context); 57 | asc_ordering.push(reverse); 58 | } 59 | let context_strings: Vec<_> = inner_contexts 60 | .iter() 61 | .map(|x| x.as_str().to_string()) 62 | .collect(); 63 | output_solution_mappings = 64 | order_by(output_solution_mappings, &context_strings, asc_ordering)?; 65 | output_solution_mappings.mappings = output_solution_mappings.mappings.drop(context_strings); 66 | Ok(output_solution_mappings) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_graph_patterns/project.rs: -------------------------------------------------------------------------------- 1 | use super::Combiner; 2 | use crate::combiner::lazy_graph_patterns::SolutionMappings; 3 | use crate::combiner::CombinerError; 4 | use async_recursion::async_recursion; 5 | use log::debug; 6 | use oxrdf::Variable; 7 | use query_processing::graph_patterns::project; 8 | use representation::query_context::{Context, PathEntry}; 9 | use spargebra::algebra::GraphPattern; 10 | use spargebra::Query; 11 | use std::collections::HashMap; 12 | use virtualized_query::VirtualizedQuery; 13 | 14 | impl Combiner { 15 | #[async_recursion] 16 | pub(crate) async fn lazy_project( 17 | &mut self, 18 | inner: &GraphPattern, 19 | variables: &Vec, 20 | solution_mappings: Option, 21 | static_query_map: HashMap, 22 | prepared_virtualized_queries: Option>>, 23 | context: &Context, 24 | ) -> Result { 25 | debug!("Processing project graph pattern"); 26 | let solution_mappings = self 27 | .lazy_graph_pattern( 28 | inner, 29 | solution_mappings, 30 | static_query_map, 31 | prepared_virtualized_queries, 32 | &context.extension_with(PathEntry::ProjectInner), 33 | ) 34 | .await?; 35 | Ok(project(solution_mappings, variables)?) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_graph_patterns/slice.rs: -------------------------------------------------------------------------------- 1 | use super::Combiner; 2 | use crate::combiner::CombinerError; 3 | use async_recursion::async_recursion; 4 | use log::debug; 5 | use polars::prelude::col; 6 | use query_processing::graph_patterns::distinct; 7 | use representation::query_context::{Context, PathEntry}; 8 | use representation::solution_mapping::SolutionMappings; 9 | use spargebra::algebra::GraphPattern; 10 | use spargebra::Query; 11 | use std::collections::HashMap; 12 | use virtualized_query::VirtualizedQuery; 13 | 14 | impl Combiner { 15 | #[async_recursion] 16 | #[allow(clippy::too_many_arguments)] 17 | pub(crate) async fn lazy_slice( 18 | &mut self, 19 | inner: &GraphPattern, 20 | start: &usize, 21 | length: &Option, 22 | solution_mappings: Option, 23 | static_query_map: HashMap, 24 | prepared_virtualized_queries: Option>>, 25 | context: &Context, 26 | ) -> Result { 27 | debug!("Processing slice graph pattern"); 28 | if *start > 0 { 29 | todo!() 30 | } else { 31 | let mut solution_mappings = self 32 | .lazy_graph_pattern( 33 | inner, 34 | solution_mappings, 35 | static_query_map, 36 | prepared_virtualized_queries, 37 | &context.extension_with(PathEntry::SliceInner), 38 | ) 39 | .await?; 40 | if let Some(length) = length { 41 | solution_mappings.mappings = solution_mappings 42 | .mappings 43 | .select(vec![col("*").head(Some(*length))]); 44 | } 45 | Ok(distinct(solution_mappings)?) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_graph_patterns/union.rs: -------------------------------------------------------------------------------- 1 | use super::Combiner; 2 | use crate::combiner::lazy_graph_patterns::SolutionMappings; 3 | use crate::combiner::static_subqueries::split_static_queries; 4 | use crate::combiner::virtualized_queries::split_virtualized_queries; 5 | use crate::combiner::CombinerError; 6 | use async_recursion::async_recursion; 7 | use log::debug; 8 | use query_processing::graph_patterns::union; 9 | use representation::query_context::{Context, PathEntry}; 10 | use spargebra::algebra::GraphPattern; 11 | use spargebra::Query; 12 | use std::collections::HashMap; 13 | use virtualized_query::VirtualizedQuery; 14 | 15 | impl Combiner { 16 | #[async_recursion] 17 | pub(crate) async fn lazy_union( 18 | &mut self, 19 | left: &GraphPattern, 20 | right: &GraphPattern, 21 | solution_mappings: Option, 22 | mut static_query_map: HashMap, 23 | mut prepared_virtualized_queries: Option>>, 24 | context: &Context, 25 | ) -> Result { 26 | debug!("Processing union graph pattern"); 27 | let left_context = context.extension_with(PathEntry::UnionLeftSide); 28 | let right_context = context.extension_with(PathEntry::UnionRightSide); 29 | let left_prepared_virtualized_queries = 30 | split_virtualized_queries(&mut prepared_virtualized_queries, &left_context); 31 | let right_prepared_virtualized_queries = 32 | split_virtualized_queries(&mut prepared_virtualized_queries, &right_context); 33 | let left_static_query_map = split_static_queries(&mut static_query_map, &left_context); 34 | let right_static_query_map = split_static_queries(&mut static_query_map, &right_context); 35 | assert!(static_query_map.is_empty()); 36 | assert!(if let Some(vqs) = &prepared_virtualized_queries { 37 | vqs.is_empty() 38 | } else { 39 | true 40 | }); 41 | let left_solution_mappings = self 42 | .lazy_graph_pattern( 43 | left, 44 | solution_mappings.clone(), 45 | left_static_query_map, 46 | left_prepared_virtualized_queries, 47 | &left_context, 48 | ) 49 | .await?; 50 | 51 | let right_solution_mappings = self 52 | .lazy_graph_pattern( 53 | right, 54 | solution_mappings, 55 | right_static_query_map, 56 | right_prepared_virtualized_queries, 57 | &right_context, 58 | ) 59 | .await?; 60 | Ok(union( 61 | vec![left_solution_mappings, right_solution_mappings], 62 | false, 63 | )?) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_graph_patterns/values.rs: -------------------------------------------------------------------------------- 1 | use super::Combiner; 2 | use crate::combiner::CombinerError; 3 | use oxrdf::Variable; 4 | use polars::prelude::JoinType; 5 | use query_processing::graph_patterns::{join, values_pattern}; 6 | use representation::solution_mapping::SolutionMappings; 7 | use spargebra::term::GroundTerm; 8 | 9 | impl Combiner { 10 | pub(crate) fn lazy_values( 11 | &mut self, 12 | solution_mappings: Option, 13 | variables: &[Variable], 14 | bindings: &[Vec>], 15 | ) -> Result { 16 | let sm = values_pattern(variables, bindings); 17 | if let Some(mut mappings) = solution_mappings { 18 | mappings = join(mappings, sm, JoinType::Inner)?; 19 | Ok(mappings) 20 | } else { 21 | Ok(sm) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/chrontext/src/combiner/lazy_order.rs: -------------------------------------------------------------------------------- 1 | use super::Combiner; 2 | use crate::combiner::CombinerError; 3 | use representation::query_context::{Context, PathEntry}; 4 | use representation::solution_mapping::SolutionMappings; 5 | use spargebra::algebra::OrderExpression; 6 | 7 | impl Combiner { 8 | pub async fn lazy_order_expression( 9 | &mut self, 10 | oexpr: &OrderExpression, 11 | solution_mappings: SolutionMappings, 12 | context: &Context, 13 | ) -> Result<(SolutionMappings, bool, Context), CombinerError> { 14 | match oexpr { 15 | OrderExpression::Asc(expr) => { 16 | let inner_context = context.extension_with(PathEntry::OrderingOperation); 17 | Ok(( 18 | self.lazy_expression(expr, solution_mappings, None, None, &inner_context) 19 | .await?, 20 | true, 21 | inner_context, 22 | )) 23 | } 24 | OrderExpression::Desc(expr) => { 25 | let inner_context = context.extension_with(PathEntry::OrderingOperation); 26 | Ok(( 27 | self.lazy_expression(expr, solution_mappings, None, None, &inner_context) 28 | .await?, 29 | false, 30 | inner_context, 31 | )) 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/chrontext/src/constants.rs: -------------------------------------------------------------------------------- 1 | pub const HAS_TIMESTAMP: &str = "https://github.com/DataTreehouse/chrontext#hasTimestamp"; 2 | pub const HAS_TIMESERIES: &str = "https://github.com/DataTreehouse/chrontext#hasTimeseries"; 3 | pub const HAS_DATA_POINT: &str = "https://github.com/DataTreehouse/chrontext#hasDataPoint"; 4 | pub const HAS_VALUE: &str = "https://github.com/DataTreehouse/chrontext#hasValue"; 5 | pub const HAS_RESOURCE: &str = "https://github.com/DataTreehouse/chrontext#hasResource"; 6 | pub const HAS_EXTERNAL_ID: &str = "https://github.com/DataTreehouse/chrontext#hasExternalId"; 7 | 8 | pub const DATE_BIN: &str = "https://github.com/DataTreehouse/chrontext#dateBin"; 9 | pub const NEST: &str = "https://github.com/DataTreehouse/chrontext#nestAggregation"; 10 | pub const GROUPING_COL: &str = "grouping_col"; 11 | -------------------------------------------------------------------------------- /lib/chrontext/src/constraints.rs: -------------------------------------------------------------------------------- 1 | use oxrdf::Variable; 2 | use representation::query_context::{Context, VariableInContext}; 3 | 4 | #[derive(PartialEq, Debug, Clone)] 5 | pub enum Constraint { 6 | External, 7 | ExternallyDerived, 8 | } 9 | 10 | #[derive(Clone, Debug)] 11 | pub struct VariableConstraints { 12 | variable_constraints: Vec<(VariableInContext, Constraint)>, 13 | } 14 | 15 | impl Default for VariableConstraints { 16 | fn default() -> Self { 17 | Self::new() 18 | } 19 | } 20 | 21 | impl VariableConstraints { 22 | pub fn get_constraint(&self, variable: &Variable, context: &Context) -> Option<&Constraint> { 23 | let mut constraint = None; 24 | for (v, c) in &self.variable_constraints { 25 | if v.same_name(variable) && v.in_scope(context, true) { 26 | if constraint.is_none() { 27 | constraint = Some(c); 28 | } else if constraint.unwrap() != c { 29 | panic!( 30 | "There should be only one type of constraint per variable {:?}, {:?}, {:?}", 31 | v, constraint, c 32 | ); 33 | } 34 | } 35 | } 36 | constraint 37 | } 38 | 39 | pub fn contains(&self, variable: &Variable, context: &Context) -> bool { 40 | self.get_constraint(variable, context).is_some() 41 | } 42 | 43 | pub fn insert(&mut self, variable: Variable, context: Context, constraint: Constraint) { 44 | self.variable_constraints 45 | .push((VariableInContext::new(variable, context), constraint)); 46 | } 47 | 48 | pub fn new() -> VariableConstraints { 49 | VariableConstraints { 50 | variable_constraints: vec![], 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/chrontext/src/errors.rs: -------------------------------------------------------------------------------- 1 | use crate::combiner::CombinerError; 2 | use crate::splitter::QueryParseError; 3 | use thiserror::Error; 4 | 5 | #[derive(Debug, Error)] 6 | pub enum ChrontextError { 7 | #[error("Missing SPARQL database")] 8 | NoSPARQLDatabaseDefined, 9 | #[error("Error creating SPARQL database `{0}`")] 10 | CreateSPARQLDatabaseError(String), 11 | #[error("No timeseries database defined")] 12 | NoTimeseriesDatabaseDefined, 13 | #[error(transparent)] 14 | QueryParseError(#[from] QueryParseError), 15 | #[error(transparent)] 16 | CombinerError(#[from] CombinerError), 17 | } 18 | -------------------------------------------------------------------------------- /lib/chrontext/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod change_types; 2 | pub mod combiner; 3 | pub mod constants; 4 | pub mod constraints; 5 | pub mod engine; 6 | pub mod errors; 7 | mod preparing; 8 | pub mod preprocessing; 9 | mod rename_vars; 10 | pub mod rewriting; 11 | mod sparql_result_to_polars; 12 | pub mod splitter; 13 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing.rs: -------------------------------------------------------------------------------- 1 | mod expressions; 2 | pub(crate) mod graph_patterns; 3 | mod synchronization; 4 | 5 | use crate::combiner::CombinerError; 6 | use polars::prelude::DataType; 7 | use representation::query_context::Context; 8 | use representation::solution_mapping::SolutionMappings; 9 | use spargebra::algebra::Expression; 10 | use spargebra::Query; 11 | use std::collections::{HashMap, HashSet}; 12 | use std::sync::Arc; 13 | use virtualization::Virtualization; 14 | use virtualized_query::pushdown_setting::PushdownSetting; 15 | use virtualized_query::{BasicVirtualizedQuery, VirtualizedQuery}; 16 | 17 | #[derive(Debug)] 18 | pub struct TimeseriesQueryPrepper { 19 | pub(crate) pushdown_settings: HashSet, 20 | pub(crate) basic_virtualized_queries: Vec, 21 | grouping_counter: u16, 22 | rewritten_filters: HashMap, 23 | virtualization: Arc, 24 | } 25 | 26 | impl TimeseriesQueryPrepper { 27 | pub fn new( 28 | pushdown_settings: HashSet, 29 | basic_virtualized_queries: Vec, 30 | rewritten_filters: HashMap, 31 | virtualization: Arc, 32 | ) -> TimeseriesQueryPrepper { 33 | TimeseriesQueryPrepper { 34 | pushdown_settings, 35 | basic_virtualized_queries, 36 | grouping_counter: 0, 37 | rewritten_filters, 38 | virtualization, 39 | } 40 | } 41 | 42 | pub fn prepare( 43 | &mut self, 44 | query: &Query, 45 | solution_mappings: &mut SolutionMappings, 46 | ) -> Result>, CombinerError> { 47 | if let Query::Select { pattern, .. } = query { 48 | let pattern_prepared = 49 | self.prepare_graph_pattern(pattern, false, solution_mappings, &Context::new())?; 50 | Ok(pattern_prepared.virtualized_queries) 51 | } else { 52 | panic!("Only support for Select"); 53 | } 54 | } 55 | } 56 | 57 | pub fn grouping_col_type() -> DataType { 58 | DataType::Int64 59 | } 60 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/expressions/and_expression.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::expressions::EXPrepReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use representation::solution_mapping::SolutionMappings; 6 | use spargebra::algebra::Expression; 7 | 8 | impl TimeseriesQueryPrepper { 9 | pub fn prepare_and_expression( 10 | &mut self, 11 | left: &Expression, 12 | right: &Expression, 13 | try_groupby_complex_query: bool, 14 | solution_mappings: &mut SolutionMappings, 15 | context: &Context, 16 | ) -> Result { 17 | // We allow translations of left- or right hand sides of And-expressions to be None. 18 | // This allows us to enforce the remaining conditions that were not removed due to a prepare 19 | let mut left_prepare = self.prepare_expression( 20 | left, 21 | try_groupby_complex_query, 22 | solution_mappings, 23 | &context.extension_with(PathEntry::AndLeft), 24 | )?; 25 | let right_prepare = self.prepare_expression( 26 | right, 27 | try_groupby_complex_query, 28 | solution_mappings, 29 | &context.extension_with(PathEntry::AndRight), 30 | )?; 31 | if left_prepare.fail_groupby_complex_query || right_prepare.fail_groupby_complex_query { 32 | return Ok(EXPrepReturn::fail_groupby_complex_query()); 33 | } 34 | left_prepare.with_virtualized_queries_from(right_prepare); 35 | Ok(left_prepare) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/expressions/binary_ordinary_expression.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::expressions::EXPrepReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use representation::solution_mapping::SolutionMappings; 6 | use spargebra::algebra::Expression; 7 | 8 | pub enum BinaryOrdinaryOperator { 9 | Add, 10 | Subtract, 11 | Multiply, 12 | Divide, 13 | LessOrEqual, 14 | Less, 15 | Greater, 16 | GreaterOrEqual, 17 | SameTerm, 18 | Equal, 19 | } 20 | 21 | impl TimeseriesQueryPrepper { 22 | pub fn prepare_binary_ordinary_expression( 23 | &mut self, 24 | left: &Expression, 25 | right: &Expression, 26 | operation: &BinaryOrdinaryOperator, 27 | try_groupby_complex_query: bool, 28 | solution_mappings: &mut SolutionMappings, 29 | context: &Context, 30 | ) -> Result { 31 | let (left_path_entry, right_path_entry) = match operation { 32 | BinaryOrdinaryOperator::Add => (PathEntry::AddLeft, PathEntry::AddRight), 33 | BinaryOrdinaryOperator::Subtract => (PathEntry::SubtractLeft, PathEntry::SubtractRight), 34 | BinaryOrdinaryOperator::Multiply => (PathEntry::MultiplyLeft, PathEntry::MultiplyRight), 35 | BinaryOrdinaryOperator::Divide => (PathEntry::DivideLeft, PathEntry::DivideRight), 36 | BinaryOrdinaryOperator::LessOrEqual => { 37 | (PathEntry::LessOrEqualLeft, PathEntry::LessOrEqualRight) 38 | } 39 | BinaryOrdinaryOperator::Less => (PathEntry::LessLeft, PathEntry::LessRight), 40 | BinaryOrdinaryOperator::Greater => (PathEntry::GreaterLeft, PathEntry::GreaterRight), 41 | BinaryOrdinaryOperator::GreaterOrEqual => ( 42 | PathEntry::GreaterOrEqualLeft, 43 | PathEntry::GreaterOrEqualRight, 44 | ), 45 | BinaryOrdinaryOperator::SameTerm => (PathEntry::SameTermLeft, PathEntry::SameTermRight), 46 | BinaryOrdinaryOperator::Equal => (PathEntry::EqualLeft, PathEntry::EqualRight), 47 | }; 48 | 49 | let mut left_prepare = self.prepare_expression( 50 | left, 51 | try_groupby_complex_query, 52 | solution_mappings, 53 | &context.extension_with(left_path_entry), 54 | )?; 55 | let right_prepare = self.prepare_expression( 56 | right, 57 | try_groupby_complex_query, 58 | solution_mappings, 59 | &context.extension_with(right_path_entry), 60 | )?; 61 | if left_prepare.fail_groupby_complex_query || right_prepare.fail_groupby_complex_query { 62 | return Ok(EXPrepReturn::fail_groupby_complex_query()); 63 | } 64 | left_prepare.with_virtualized_queries_from(right_prepare); 65 | Ok(left_prepare) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/expressions/coalesce_expression.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::expressions::EXPrepReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use representation::solution_mapping::SolutionMappings; 6 | use spargebra::algebra::Expression; 7 | use std::collections::HashMap; 8 | 9 | impl TimeseriesQueryPrepper { 10 | pub fn prepare_coalesce_expression( 11 | &mut self, 12 | wrapped: &[Expression], 13 | try_groupby_complex_query: bool, 14 | solution_mappings: &mut SolutionMappings, 15 | context: &Context, 16 | ) -> Result { 17 | let prepared = wrapped 18 | .iter() 19 | .enumerate() 20 | .map(|(i, e)| { 21 | self.prepare_expression( 22 | e, 23 | try_groupby_complex_query, 24 | solution_mappings, 25 | &context.extension_with(PathEntry::Coalesce(i as u16)), 26 | ) 27 | }) 28 | .collect::, CombinerError>>(); 29 | let mut prepared = prepared?; 30 | if prepared.iter().any(|x| x.fail_groupby_complex_query) { 31 | return Ok(EXPrepReturn::fail_groupby_complex_query()); 32 | } 33 | if prepared.is_empty() { 34 | Ok(EXPrepReturn::new(HashMap::new())) 35 | } else { 36 | let mut first_prepared = prepared.remove(0); 37 | for p in prepared { 38 | first_prepared.with_virtualized_queries_from(p); 39 | } 40 | Ok(first_prepared) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/expressions/exists_expression.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::expressions::EXPrepReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use representation::solution_mapping::SolutionMappings; 6 | use spargebra::algebra::GraphPattern; 7 | 8 | impl TimeseriesQueryPrepper { 9 | pub fn prepare_exists_expression( 10 | &mut self, 11 | wrapped: &GraphPattern, 12 | try_groupby_complex_query: bool, 13 | solution_mappings: &mut SolutionMappings, 14 | context: &Context, 15 | ) -> Result { 16 | if try_groupby_complex_query { 17 | Ok(EXPrepReturn::fail_groupby_complex_query()) 18 | } else { 19 | let wrapped_prepare = self.prepare_graph_pattern( 20 | wrapped, 21 | try_groupby_complex_query, 22 | solution_mappings, 23 | &context.extension_with(PathEntry::Exists), 24 | )?; 25 | if wrapped_prepare.fail_groupby_complex_query { 26 | Ok(EXPrepReturn::fail_groupby_complex_query()) 27 | } else { 28 | Ok(EXPrepReturn::new(wrapped_prepare.virtualized_queries)) 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/expressions/function_call_expression.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::expressions::EXPrepReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use representation::solution_mapping::SolutionMappings; 6 | use spargebra::algebra::{Expression, Function}; 7 | use std::collections::HashMap; 8 | 9 | impl TimeseriesQueryPrepper { 10 | pub fn prepare_function_call_expression( 11 | &mut self, 12 | _fun: &Function, 13 | args: &[Expression], 14 | try_groupby_complex_query: bool, 15 | solution_mappings: &mut SolutionMappings, 16 | context: &Context, 17 | ) -> Result { 18 | let args_prepared = args 19 | .iter() 20 | .enumerate() 21 | .map(|(i, e)| { 22 | self.prepare_expression( 23 | e, 24 | try_groupby_complex_query, 25 | solution_mappings, 26 | &context.extension_with(PathEntry::FunctionCall(i as u16)), 27 | ) 28 | }) 29 | .collect::, CombinerError>>(); 30 | let mut args_prepared = args_prepared?; 31 | if args_prepared.iter().any(|x| x.fail_groupby_complex_query) { 32 | return Ok(EXPrepReturn::fail_groupby_complex_query()); 33 | } 34 | if !args_prepared.is_empty() { 35 | let mut first_prepared = args_prepared.remove(0); 36 | for p in args_prepared { 37 | first_prepared.with_virtualized_queries_from(p) 38 | } 39 | Ok(first_prepared) 40 | } else { 41 | Ok(EXPrepReturn::new(HashMap::new())) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/expressions/if_expression.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::expressions::EXPrepReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use representation::solution_mapping::SolutionMappings; 6 | use spargebra::algebra::Expression; 7 | 8 | impl TimeseriesQueryPrepper { 9 | pub fn prepare_if_expression( 10 | &mut self, 11 | left: &Expression, 12 | mid: &Expression, 13 | right: &Expression, 14 | try_groupby_complex_query: bool, 15 | solution_mappings: &mut SolutionMappings, 16 | context: &Context, 17 | ) -> Result { 18 | let mut left_prepare = self.prepare_expression( 19 | left, 20 | try_groupby_complex_query, 21 | solution_mappings, 22 | &context.extension_with(PathEntry::IfLeft), 23 | )?; 24 | let mid_prepare = self.prepare_expression( 25 | mid, 26 | try_groupby_complex_query, 27 | solution_mappings, 28 | &context.extension_with(PathEntry::IfMiddle), 29 | )?; 30 | let right_prepare = self.prepare_expression( 31 | right, 32 | try_groupby_complex_query, 33 | solution_mappings, 34 | &context.extension_with(PathEntry::IfRight), 35 | )?; 36 | left_prepare.with_virtualized_queries_from(mid_prepare); 37 | left_prepare.with_virtualized_queries_from(right_prepare); 38 | Ok(left_prepare) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/expressions/in_expression.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::expressions::EXPrepReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use representation::solution_mapping::SolutionMappings; 6 | use spargebra::algebra::Expression; 7 | 8 | impl TimeseriesQueryPrepper { 9 | pub fn prepare_in_expression( 10 | &mut self, 11 | left: &Expression, 12 | expressions: &[Expression], 13 | try_groupby_complex_query: bool, 14 | solution_mappings: &mut SolutionMappings, 15 | context: &Context, 16 | ) -> Result { 17 | let mut left_prepare = self.prepare_expression( 18 | left, 19 | try_groupby_complex_query, 20 | solution_mappings, 21 | &context.extension_with(PathEntry::InLeft), 22 | )?; 23 | let prepared: Result, CombinerError> = expressions 24 | .iter() 25 | .map(|x| { 26 | self.prepare_expression(x, try_groupby_complex_query, solution_mappings, context) 27 | }) 28 | .collect(); 29 | let prepared = prepared?; 30 | if left_prepare.fail_groupby_complex_query 31 | || prepared.iter().any(|x| x.fail_groupby_complex_query) 32 | { 33 | return Ok(EXPrepReturn::fail_groupby_complex_query()); 34 | } 35 | for p in prepared { 36 | left_prepare.with_virtualized_queries_from(p) 37 | } 38 | Ok(left_prepare) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/expressions/not_expression.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::expressions::EXPrepReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use representation::solution_mapping::SolutionMappings; 6 | use spargebra::algebra::Expression; 7 | 8 | impl TimeseriesQueryPrepper { 9 | pub fn prepare_not_expression( 10 | &mut self, 11 | wrapped: &Expression, 12 | try_groupby_complex_query: bool, 13 | solution_mappings: &mut SolutionMappings, 14 | context: &Context, 15 | ) -> Result { 16 | self.prepare_expression( 17 | wrapped, 18 | try_groupby_complex_query, 19 | solution_mappings, 20 | &context.extension_with(PathEntry::Not), 21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/expressions/or_expression.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::expressions::EXPrepReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use representation::solution_mapping::SolutionMappings; 6 | use spargebra::algebra::Expression; 7 | 8 | impl TimeseriesQueryPrepper { 9 | pub fn prepare_or_expression( 10 | &mut self, 11 | left: &Expression, 12 | right: &Expression, 13 | try_groupby_complex_query: bool, 14 | solution_mappings: &mut SolutionMappings, 15 | context: &Context, 16 | ) -> Result { 17 | let mut left_prepare = self.prepare_expression( 18 | left, 19 | try_groupby_complex_query, 20 | solution_mappings, 21 | &context.extension_with(PathEntry::OrLeft), 22 | )?; 23 | let right_prepare = self.prepare_expression( 24 | right, 25 | try_groupby_complex_query, 26 | solution_mappings, 27 | &context.extension_with(PathEntry::OrRight), 28 | )?; 29 | if left_prepare.fail_groupby_complex_query || right_prepare.fail_groupby_complex_query { 30 | return Ok(EXPrepReturn::fail_groupby_complex_query()); 31 | } 32 | left_prepare.with_virtualized_queries_from(right_prepare); 33 | Ok(left_prepare) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/expressions/unary_ordinary_expression.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::expressions::EXPrepReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use representation::solution_mapping::SolutionMappings; 6 | use spargebra::algebra::Expression; 7 | 8 | pub enum UnaryOrdinaryOperator { 9 | UnaryPlus, 10 | UnaryMinus, 11 | } 12 | 13 | impl TimeseriesQueryPrepper { 14 | pub fn prepare_unary_ordinary_expression( 15 | &mut self, 16 | wrapped: &Expression, 17 | operation: &UnaryOrdinaryOperator, 18 | try_groupby_complex_query: bool, 19 | solution_mappings: &mut SolutionMappings, 20 | context: &Context, 21 | ) -> Result { 22 | let path_entry = match operation { 23 | UnaryOrdinaryOperator::UnaryPlus => PathEntry::UnaryPlus, 24 | UnaryOrdinaryOperator::UnaryMinus => PathEntry::UnaryMinus, 25 | }; 26 | 27 | self.prepare_expression( 28 | wrapped, 29 | try_groupby_complex_query, 30 | solution_mappings, 31 | &context.extension_with(path_entry), 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/bgp_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::graph_patterns::GPPrepReturn; 4 | use crate::preparing::synchronization::create_identity_synchronized_queries; 5 | use representation::query_context::{Context, PathEntry}; 6 | use spargebra::term::TriplePattern; 7 | use std::collections::HashMap; 8 | use virtualized_query::VirtualizedQuery; 9 | 10 | impl TimeseriesQueryPrepper { 11 | pub(crate) fn prepare_bgp( 12 | &mut self, 13 | try_groupby_complex_query: bool, 14 | patterns: &Vec, 15 | context: &Context, 16 | ) -> Result { 17 | let mut local_vqs = vec![]; 18 | let bgp_context = context.extension_with(PathEntry::BGP); 19 | for vq in &mut self.basic_virtualized_queries { 20 | if vq.query_source_context == bgp_context { 21 | if let Some(resource) = &vq.resource { 22 | if let Some(template) = self.virtualization.resources.get(resource) { 23 | vq.finish_column_mapping(patterns, template); 24 | } else { 25 | return Err(CombinerError::ResourceTemplateNotFound(resource.clone())); 26 | } 27 | } 28 | //We create a degenerate VQ to be able to remove the columns later. 29 | local_vqs.push(VirtualizedQuery::Basic(vq.clone())); 30 | } 31 | } 32 | if try_groupby_complex_query { 33 | local_vqs = create_identity_synchronized_queries(local_vqs); 34 | } 35 | let mut vqs_map = HashMap::new(); 36 | if !local_vqs.is_empty() { 37 | vqs_map.insert(context.clone(), local_vqs); 38 | } 39 | Ok(GPPrepReturn::new(vqs_map)) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/distinct_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use log::debug; 3 | 4 | use crate::combiner::CombinerError; 5 | use crate::preparing::graph_patterns::GPPrepReturn; 6 | use representation::query_context::{Context, PathEntry}; 7 | use representation::solution_mapping::SolutionMappings; 8 | use spargebra::algebra::GraphPattern; 9 | 10 | impl TimeseriesQueryPrepper { 11 | pub fn prepare_distinct( 12 | &mut self, 13 | inner: &GraphPattern, 14 | try_groupby_complex_query: bool, 15 | solution_mappings: &mut SolutionMappings, 16 | context: &Context, 17 | ) -> Result { 18 | if try_groupby_complex_query { 19 | debug!( 20 | "Encountered distinct inside groupby, not supported for complex groupby pushdown" 21 | ); 22 | return Ok(GPPrepReturn::fail_groupby_complex_query()); 23 | } 24 | 25 | self.prepare_graph_pattern( 26 | inner, 27 | try_groupby_complex_query, 28 | solution_mappings, 29 | &context.extension_with(PathEntry::DistinctInner), 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/extend_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::graph_patterns::GPPrepReturn; 4 | use oxrdf::Variable; 5 | use query_processing::find_query_variables::find_all_used_variables_in_expression; 6 | use representation::query_context::{Context, PathEntry}; 7 | use representation::solution_mapping::SolutionMappings; 8 | use spargebra::algebra::{Expression, GraphPattern}; 9 | use std::collections::HashSet; 10 | use virtualized_query::VirtualizedQuery; 11 | 12 | impl TimeseriesQueryPrepper { 13 | pub(crate) fn prepare_extend( 14 | &mut self, 15 | inner: &GraphPattern, 16 | var: &Variable, 17 | expr: &Expression, 18 | try_groupby_complex_query: bool, 19 | solution_mappings: &mut SolutionMappings, 20 | context: &Context, 21 | ) -> Result { 22 | let inner_context = context.extension_with(PathEntry::ExtendInner); 23 | let mut inner_prepare = self.prepare_graph_pattern( 24 | inner, 25 | try_groupby_complex_query, 26 | solution_mappings, 27 | &inner_context, 28 | )?; 29 | if try_groupby_complex_query { 30 | let mut expression_vars = HashSet::new(); 31 | find_all_used_variables_in_expression(expr, &mut expression_vars, true, true); 32 | let mut found_i = None; 33 | let mut found_context = None; 34 | 35 | for (c, vqs) in &inner_prepare.virtualized_queries { 36 | for (i, vq) in vqs.iter().enumerate() { 37 | let mut found_all = true; 38 | let mut found_some = false; 39 | for expression_var in &expression_vars { 40 | if vq.has_equivalent_variable(expression_var, context) { 41 | found_some = true; 42 | } else { 43 | found_all = false; 44 | break; 45 | } 46 | } 47 | if found_all && found_some { 48 | found_i = Some(i); 49 | found_context = Some(c.clone()); 50 | } 51 | } 52 | } 53 | if let (Some(i), Some(c)) = (found_i, found_context) { 54 | let inner_vq = inner_prepare 55 | .virtualized_queries 56 | .get_mut(&c) 57 | .unwrap() 58 | .remove(i); 59 | if inner_prepare 60 | .virtualized_queries 61 | .get(&c) 62 | .unwrap() 63 | .is_empty() 64 | { 65 | inner_prepare.virtualized_queries.remove(&c); 66 | } 67 | let new_vq = 68 | VirtualizedQuery::ExpressionAs(Box::new(inner_vq), var.clone(), expr.clone()); 69 | if !inner_prepare.virtualized_queries.contains_key(context) { 70 | inner_prepare 71 | .virtualized_queries 72 | .insert(context.clone(), vec![]); 73 | } 74 | inner_prepare 75 | .virtualized_queries 76 | .get_mut(context) 77 | .unwrap() 78 | .push(new_vq); 79 | 80 | Ok(inner_prepare) 81 | } else { 82 | Ok(GPPrepReturn::fail_groupby_complex_query()) 83 | } 84 | } else { 85 | Ok(inner_prepare) 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/filter_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::change_types::ChangeType; 3 | use crate::combiner::CombinerError; 4 | use crate::preparing::graph_patterns::expression_rewrites::rewrite_filter_expression; 5 | use crate::preparing::graph_patterns::GPPrepReturn; 6 | use log::debug; 7 | use representation::query_context::{Context, PathEntry}; 8 | use representation::solution_mapping::SolutionMappings; 9 | use spargebra::algebra::{Expression, GraphPattern}; 10 | use std::collections::HashMap; 11 | use virtualized_query::VirtualizedQuery; 12 | 13 | impl TimeseriesQueryPrepper { 14 | pub fn prepare_filter( 15 | &mut self, 16 | expression: &Expression, 17 | inner: &GraphPattern, 18 | try_groupby_complex_query: bool, 19 | solution_mappings: &mut SolutionMappings, 20 | context: &Context, 21 | ) -> Result { 22 | let expression_prepare = self.prepare_expression( 23 | expression, 24 | try_groupby_complex_query, 25 | solution_mappings, 26 | &context.extension_with(PathEntry::FilterExpression), 27 | )?; 28 | debug!("Expression prepare: {:?}", expression_prepare); 29 | let inner_filter_context = context.extension_with(PathEntry::FilterInner); 30 | let inner_prepare = self.prepare_graph_pattern( 31 | inner, 32 | try_groupby_complex_query, 33 | solution_mappings, 34 | &inner_filter_context, 35 | )?; 36 | if expression_prepare.fail_groupby_complex_query || inner_prepare.fail_groupby_complex_query 37 | { 38 | return Ok(GPPrepReturn::fail_groupby_complex_query()); 39 | } 40 | 41 | let mut out_vqs = HashMap::new(); 42 | out_vqs.extend(expression_prepare.virtualized_queries); 43 | let mut lost_any = false; 44 | for (inner_context, vqs) in inner_prepare.virtualized_queries { 45 | let mut out_vq_vec = vec![]; 46 | for t in vqs { 47 | let use_change_type = if try_groupby_complex_query { 48 | ChangeType::NoChange 49 | } else { 50 | ChangeType::Relaxed 51 | }; 52 | let conj_vec = conjunction_to_vec(self.rewritten_filters.get(context)); 53 | let (virtualized_condition, lost_value) = rewrite_filter_expression( 54 | &t, 55 | expression, 56 | &use_change_type, 57 | context, 58 | &conj_vec, 59 | &self.pushdown_settings, 60 | ); 61 | lost_any = lost_value || lost_any; 62 | if try_groupby_complex_query && (lost_value || virtualized_condition.is_none()) { 63 | return Ok(GPPrepReturn::fail_groupby_complex_query()); 64 | } 65 | if let Some(expr) = virtualized_condition { 66 | out_vq_vec.push(VirtualizedQuery::Filtered(Box::new(t), expr)); 67 | } else { 68 | out_vq_vec.push(t); 69 | } 70 | } 71 | if !out_vq_vec.is_empty() { 72 | if !lost_any && inner_context.path.len() == context.path.len() + 1 { 73 | out_vqs.insert(context.clone(), out_vq_vec); 74 | } else { 75 | out_vqs.insert(inner_context, out_vq_vec); 76 | } 77 | } 78 | } 79 | Ok(GPPrepReturn::new(out_vqs)) 80 | } 81 | } 82 | 83 | fn conjunction_to_vec(expr_opt: Option<&Expression>) -> Option> { 84 | let mut out = vec![]; 85 | if let Some(expr) = expr_opt { 86 | match expr { 87 | Expression::And(left, right) => { 88 | let left_conj = conjunction_to_vec(Some(left)); 89 | if let Some(left_vec) = left_conj { 90 | out.extend(left_vec); 91 | } 92 | let right_conj = conjunction_to_vec(Some(right)); 93 | if let Some(right_vec) = right_conj { 94 | out.extend(right_vec); 95 | } 96 | } 97 | _ => { 98 | out.push(expr); 99 | } 100 | } 101 | } 102 | if !out.is_empty() { 103 | Some(out) 104 | } else { 105 | None 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/graph_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use log::debug; 3 | 4 | use crate::combiner::CombinerError; 5 | use crate::preparing::graph_patterns::GPPrepReturn; 6 | use representation::query_context::{Context, PathEntry}; 7 | use representation::solution_mapping::SolutionMappings; 8 | use spargebra::algebra::GraphPattern; 9 | 10 | impl TimeseriesQueryPrepper { 11 | pub fn prepare_graph( 12 | &mut self, 13 | inner: &GraphPattern, 14 | try_groupby_complex_query: bool, 15 | solution_mappings: &mut SolutionMappings, 16 | context: &Context, 17 | ) -> Result { 18 | if try_groupby_complex_query { 19 | debug!("Encountered graph inside groupby, not supported for complex groupby pushdown"); 20 | Ok(GPPrepReturn::fail_groupby_complex_query()) 21 | } else { 22 | self.prepare_graph_pattern( 23 | inner, 24 | try_groupby_complex_query, 25 | solution_mappings, 26 | &context.extension_with(PathEntry::GraphInner), 27 | ) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/join_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::graph_patterns::GPPrepReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use representation::solution_mapping::SolutionMappings; 6 | use spargebra::algebra::GraphPattern; 7 | 8 | impl TimeseriesQueryPrepper { 9 | pub fn prepare_join( 10 | &mut self, 11 | left: &GraphPattern, 12 | right: &GraphPattern, 13 | try_groupby_complex_query: bool, 14 | solution_mappings: &mut SolutionMappings, 15 | context: &Context, 16 | ) -> Result { 17 | let mut left_prepare = self.prepare_graph_pattern( 18 | left, 19 | try_groupby_complex_query, 20 | solution_mappings, 21 | &context.extension_with(PathEntry::JoinLeftSide), 22 | )?; 23 | if left_prepare.fail_groupby_complex_query { 24 | return Ok(left_prepare); 25 | } 26 | 27 | let right_prepare = self.prepare_graph_pattern( 28 | right, 29 | try_groupby_complex_query, 30 | solution_mappings, 31 | &context.extension_with(PathEntry::JoinRightSide), 32 | )?; 33 | if right_prepare.fail_groupby_complex_query { 34 | return Ok(right_prepare); 35 | } 36 | 37 | left_prepare.with_virtualized_queries_from(right_prepare); 38 | if try_groupby_complex_query && left_prepare.virtualized_queries.len() > 1 { 39 | return Ok(GPPrepReturn::fail_groupby_complex_query()); 40 | //TODO: Fix synchronized queries 41 | } 42 | Ok(left_prepare) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/left_join_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::graph_patterns::GPPrepReturn; 4 | use log::debug; 5 | use representation::query_context::{Context, PathEntry}; 6 | use representation::solution_mapping::SolutionMappings; 7 | use spargebra::algebra::{Expression, GraphPattern}; 8 | 9 | impl TimeseriesQueryPrepper { 10 | pub fn prepare_left_join( 11 | &mut self, 12 | left: &GraphPattern, 13 | right: &GraphPattern, 14 | _expression_opt: &Option, 15 | try_groupby_complex_query: bool, 16 | solution_mappings: &mut SolutionMappings, 17 | context: &Context, 18 | ) -> Result { 19 | if try_groupby_complex_query { 20 | debug!( 21 | "Encountered graph inside left join, not supported for complex groupby pushdown" 22 | ); 23 | Ok(GPPrepReturn::fail_groupby_complex_query()) 24 | } else { 25 | let mut left_prepare = self.prepare_graph_pattern( 26 | left, 27 | try_groupby_complex_query, 28 | solution_mappings, 29 | &context.extension_with(PathEntry::LeftJoinLeftSide), 30 | )?; 31 | let right_prepare = self.prepare_graph_pattern( 32 | right, 33 | try_groupby_complex_query, 34 | solution_mappings, 35 | &context.extension_with(PathEntry::LeftJoinRightSide), 36 | )?; 37 | left_prepare.with_virtualized_queries_from(right_prepare); 38 | Ok(left_prepare) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/minus_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::graph_patterns::GPPrepReturn; 4 | use log::debug; 5 | use representation::query_context::{Context, PathEntry}; 6 | use representation::solution_mapping::SolutionMappings; 7 | use spargebra::algebra::GraphPattern; 8 | 9 | impl TimeseriesQueryPrepper { 10 | pub fn prepare_minus( 11 | &mut self, 12 | left: &GraphPattern, 13 | right: &GraphPattern, 14 | try_groupby_complex_query: bool, 15 | solution_mappings: &mut SolutionMappings, 16 | context: &Context, 17 | ) -> Result { 18 | if try_groupby_complex_query { 19 | debug!("Encountered minus inside groupby, not supported for complex groupby pushdown"); 20 | Ok(GPPrepReturn::fail_groupby_complex_query()) 21 | } else { 22 | let mut left_prepare = self.prepare_graph_pattern( 23 | left, 24 | try_groupby_complex_query, 25 | solution_mappings, 26 | &context.extension_with(PathEntry::MinusLeftSide), 27 | )?; 28 | let right_prepare = self.prepare_graph_pattern( 29 | right, 30 | try_groupby_complex_query, 31 | solution_mappings, 32 | &context.extension_with(PathEntry::MinusRightSide), 33 | )?; 34 | left_prepare.with_virtualized_queries_from(right_prepare); 35 | Ok(left_prepare) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/order_by_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::preparing::graph_patterns::GPPrepReturn; 3 | use std::collections::HashMap; 4 | 5 | use crate::combiner::CombinerError; 6 | use crate::preparing::graph_patterns::expression_rewrites::rewrite_order_expressions; 7 | use representation::query_context::{Context, PathEntry}; 8 | use representation::solution_mapping::SolutionMappings; 9 | use spargebra::algebra::{GraphPattern, OrderExpression}; 10 | use virtualized_query::VirtualizedQuery; 11 | 12 | impl TimeseriesQueryPrepper { 13 | pub fn prepare_order_by( 14 | &mut self, 15 | inner: &GraphPattern, 16 | order_expressions: &Vec, 17 | try_groupby_complex_query: bool, 18 | solution_mappings: &mut SolutionMappings, 19 | context: &Context, 20 | ) -> Result { 21 | let expression_prepare = self.prepare_order_expressions( 22 | order_expressions, 23 | try_groupby_complex_query, 24 | solution_mappings, 25 | &context.extension_with(PathEntry::FilterExpression), 26 | )?; 27 | 28 | let inner_prepare = self.prepare_graph_pattern( 29 | inner, 30 | try_groupby_complex_query, 31 | solution_mappings, 32 | &context.extension_with(PathEntry::OrderByInner), 33 | )?; 34 | if expression_prepare.fail_groupby_complex_query || inner_prepare.fail_groupby_complex_query 35 | { 36 | return Ok(GPPrepReturn::fail_groupby_complex_query()); 37 | } 38 | let mut out_vqs = HashMap::new(); 39 | for (inner_context, vqs) in inner_prepare.virtualized_queries { 40 | let mut out_vq_vec = vec![]; 41 | for vq in vqs { 42 | let (rewritten, lost_value) = rewrite_order_expressions( 43 | &vq, 44 | order_expressions, 45 | context, 46 | &self.pushdown_settings, 47 | ); 48 | if try_groupby_complex_query && lost_value { 49 | return Ok(GPPrepReturn::fail_groupby_complex_query()); 50 | } else if let Some(ordering) = rewritten { 51 | out_vq_vec.push(VirtualizedQuery::Ordered(Box::new(vq), ordering)); 52 | } else { 53 | out_vq_vec.push(vq); 54 | } 55 | } 56 | out_vqs.insert(inner_context, out_vq_vec); 57 | } 58 | Ok(GPPrepReturn::new(out_vqs)) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/path_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::preparing::graph_patterns::GPPrepReturn; 3 | use spargebra::algebra::PropertyPathExpression; 4 | use spargebra::term::TermPattern; 5 | use std::collections::HashMap; 6 | 7 | impl TimeseriesQueryPrepper { 8 | //We assume that all paths have been prepared so as to not contain any datapoint, timestamp, or data value. 9 | //These should have been split into ordinary triples. 10 | pub fn prepare_path( 11 | &mut self, 12 | _subject: &TermPattern, 13 | _path: &PropertyPathExpression, 14 | _object: &TermPattern, 15 | ) -> GPPrepReturn { 16 | GPPrepReturn::new(HashMap::new()) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/project_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::graph_patterns::GPPrepReturn; 4 | use oxrdf::Variable; 5 | use representation::query_context::{Context, PathEntry}; 6 | use representation::solution_mapping::SolutionMappings; 7 | use spargebra::algebra::GraphPattern; 8 | 9 | impl TimeseriesQueryPrepper { 10 | pub fn prepare_project( 11 | &mut self, 12 | inner: &GraphPattern, 13 | _variables: &[Variable], 14 | try_groupby_complex_query: bool, 15 | solution_mappings: &mut SolutionMappings, 16 | context: &Context, 17 | ) -> Result { 18 | let inner_context = context.extension_with(PathEntry::ProjectInner); 19 | 20 | self.prepare_graph_pattern( 21 | inner, 22 | try_groupby_complex_query, 23 | solution_mappings, 24 | &inner_context, 25 | ) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/reduced_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::graph_patterns::GPPrepReturn; 4 | use log::debug; 5 | use representation::query_context::{Context, PathEntry}; 6 | use representation::solution_mapping::SolutionMappings; 7 | use spargebra::algebra::GraphPattern; 8 | 9 | impl TimeseriesQueryPrepper { 10 | pub fn prepare_reduced( 11 | &mut self, 12 | inner: &GraphPattern, 13 | try_groupby_complex_query: bool, 14 | solution_mappings: &mut SolutionMappings, 15 | context: &Context, 16 | ) -> Result { 17 | if try_groupby_complex_query { 18 | debug!("Encountered graph inside reduced, not supported for complex groupby pushdown"); 19 | Ok(GPPrepReturn::fail_groupby_complex_query()) 20 | } else { 21 | self.prepare_graph_pattern( 22 | inner, 23 | try_groupby_complex_query, 24 | solution_mappings, 25 | &context.extension_with(PathEntry::ReducedInner), 26 | ) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/service_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use std::collections::HashMap; 3 | 4 | use crate::preparing::graph_patterns::GPPrepReturn; 5 | 6 | impl TimeseriesQueryPrepper { 7 | pub fn prepare_service(&mut self) -> GPPrepReturn { 8 | //Service pattern should not contain anything dynamic 9 | GPPrepReturn::new(HashMap::new()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/slice_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::graph_patterns::GPPrepReturn; 4 | use log::debug; 5 | use representation::query_context::{Context, PathEntry}; 6 | use representation::solution_mapping::SolutionMappings; 7 | use spargebra::algebra::GraphPattern; 8 | use virtualized_query::VirtualizedQuery; 9 | 10 | impl TimeseriesQueryPrepper { 11 | pub fn prepare_slice( 12 | &mut self, 13 | start: usize, 14 | length: Option, 15 | inner: &GraphPattern, 16 | try_groupby_complex_query: bool, 17 | solution_mappings: &mut SolutionMappings, 18 | context: &Context, 19 | ) -> Result { 20 | let inner_context = context.extension_with(PathEntry::SliceInner); 21 | if try_groupby_complex_query { 22 | debug!("Encountered graph inside slice, not supported for complex groupby pushdown"); 23 | Ok(GPPrepReturn::fail_groupby_complex_query()) 24 | } else { 25 | let mut inner_prepare = self.prepare_graph_pattern( 26 | inner, 27 | try_groupby_complex_query, 28 | solution_mappings, 29 | &inner_context, 30 | )?; 31 | if !inner_prepare.fail_groupby_complex_query { 32 | for (c, vqs) in &mut inner_prepare.virtualized_queries { 33 | let mut found_noncompatible = false; 34 | for i in inner_context.path.len()..c.path.len() { 35 | if c.path[i] != PathEntry::ProjectInner { 36 | found_noncompatible = true; 37 | break; 38 | } 39 | } 40 | if !found_noncompatible && vqs.len() == 1 { 41 | let vq = VirtualizedQuery::Sliced(Box::new(vqs.remove(0)), start, length); 42 | *vqs = vec![vq]; 43 | } 44 | } 45 | } 46 | Ok(inner_prepare) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/union_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use crate::combiner::CombinerError; 3 | use crate::preparing::graph_patterns::GPPrepReturn; 4 | use log::debug; 5 | use representation::query_context::{Context, PathEntry}; 6 | use representation::solution_mapping::SolutionMappings; 7 | use spargebra::algebra::GraphPattern; 8 | 9 | impl TimeseriesQueryPrepper { 10 | pub fn prepare_union( 11 | &mut self, 12 | left: &GraphPattern, 13 | right: &GraphPattern, 14 | try_groupby_complex_query: bool, 15 | solution_mappings: &mut SolutionMappings, 16 | context: &Context, 17 | ) -> Result { 18 | if try_groupby_complex_query { 19 | debug!( 20 | "Encountered union inside left join, not supported for complex groupby pushdown" 21 | ); 22 | Ok(GPPrepReturn::fail_groupby_complex_query()) 23 | } else { 24 | let mut left_prepare = self.prepare_graph_pattern( 25 | left, 26 | try_groupby_complex_query, 27 | solution_mappings, 28 | &context.extension_with(PathEntry::UnionLeftSide), 29 | )?; 30 | let right_prepare = self.prepare_graph_pattern( 31 | right, 32 | try_groupby_complex_query, 33 | solution_mappings, 34 | &context.extension_with(PathEntry::UnionRightSide), 35 | )?; 36 | left_prepare.with_virtualized_queries_from(right_prepare); 37 | Ok(left_prepare) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/graph_patterns/values_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::TimeseriesQueryPrepper; 2 | use std::collections::HashMap; 3 | 4 | use crate::preparing::graph_patterns::GPPrepReturn; 5 | use oxrdf::Variable; 6 | use spargebra::term::GroundTerm; 7 | 8 | impl TimeseriesQueryPrepper { 9 | pub fn prepare_values( 10 | &mut self, 11 | _variables: &[Variable], 12 | _bindings: &[Vec>], 13 | ) -> GPPrepReturn { 14 | GPPrepReturn::new(HashMap::new()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/chrontext/src/preparing/synchronization.rs: -------------------------------------------------------------------------------- 1 | use oxrdf::Variable; 2 | use std::collections::HashSet; 3 | use virtualized_query::{Synchronizer, VirtualizedQuery}; 4 | 5 | pub fn create_identity_synchronized_queries( 6 | mut vqs: Vec, 7 | ) -> Vec { 8 | let mut out_queries = vec![]; 9 | while vqs.len() > 1 { 10 | let mut queries_to_synchronize = vec![]; 11 | let first_query = vqs.remove(0); 12 | let first_query_virtualized_variables_set: HashSet = HashSet::from_iter( 13 | first_query 14 | .get_virtualized_variables() 15 | .into_iter() 16 | .map(|x| x.variable.clone()), 17 | ); 18 | let mut keep_vqs = vec![]; 19 | for other in vqs.into_iter() { 20 | let other_query_virtualized_variables_set = HashSet::from_iter( 21 | other 22 | .get_virtualized_variables() 23 | .into_iter() 24 | .map(|x| x.variable.clone()), 25 | ); 26 | if !first_query_virtualized_variables_set 27 | .is_disjoint(&other_query_virtualized_variables_set) 28 | { 29 | queries_to_synchronize.push(other); 30 | } else { 31 | keep_vqs.push(other); 32 | } 33 | } 34 | vqs = keep_vqs; 35 | if !queries_to_synchronize.is_empty() { 36 | queries_to_synchronize.push(first_query); 37 | out_queries.push(VirtualizedQuery::InnerJoin( 38 | queries_to_synchronize, 39 | vec![Synchronizer::Identity( 40 | first_query_virtualized_variables_set 41 | .iter() 42 | .next() 43 | .unwrap() 44 | .as_str() 45 | .to_string(), 46 | )], 47 | )); 48 | } else { 49 | out_queries.push(first_query); 50 | } 51 | } 52 | out_queries.extend(vqs); 53 | out_queries 54 | } 55 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting.rs: -------------------------------------------------------------------------------- 1 | mod aggregate_expression; 2 | mod expressions; 3 | mod graph_patterns; 4 | mod order_expression; 5 | mod project_static; 6 | mod subqueries; 7 | 8 | use crate::constraints::{Constraint, VariableConstraints}; 9 | use crate::rewriting::expressions::ExReturn; 10 | use oxrdf::NamedNode; 11 | use representation::query_context::Context; 12 | use spargebra::algebra::Expression; 13 | use spargebra::term::Variable; 14 | use spargebra::Query; 15 | use std::collections::{HashMap, HashSet}; 16 | use virtualized_query::BasicVirtualizedQuery; 17 | 18 | #[derive(Debug)] 19 | pub struct StaticQueryRewriter { 20 | variable_counter: u16, 21 | variable_constraints: VariableConstraints, 22 | additional_projections: HashSet, 23 | basic_virtualized_queries: Vec, 24 | first_level_virtualized_predicates: HashSet, 25 | static_subqueries: HashMap, 26 | rewritten_filters: HashMap, 27 | is_hybrid: bool, 28 | } 29 | 30 | impl StaticQueryRewriter { 31 | pub fn new( 32 | variable_constraints: VariableConstraints, 33 | first_level_virtualized_predicates: HashSet, 34 | ) -> StaticQueryRewriter { 35 | StaticQueryRewriter { 36 | variable_counter: 0, 37 | additional_projections: Default::default(), 38 | variable_constraints, 39 | first_level_virtualized_predicates, 40 | basic_virtualized_queries: vec![], 41 | static_subqueries: HashMap::new(), 42 | rewritten_filters: HashMap::new(), 43 | is_hybrid: true, //TODO! 44 | } 45 | } 46 | 47 | pub fn rewrite_query( 48 | mut self, 49 | query: Query, 50 | ) -> ( 51 | HashMap, 52 | Vec, 53 | HashMap, 54 | ) { 55 | if !self.is_hybrid { 56 | let mut map = HashMap::new(); 57 | map.insert(Context::new(), query); 58 | return (map, vec![], HashMap::new()); 59 | } 60 | if let Query::Select { 61 | dataset, 62 | pattern, 63 | base_iri, 64 | } = query 65 | { 66 | let pattern_rewrite = self.rewrite_graph_pattern(&pattern, &Context::new()); 67 | if let Some(p) = pattern_rewrite.graph_pattern { 68 | self.static_subqueries.insert( 69 | Context::new(), 70 | Query::Select { 71 | dataset, 72 | pattern: p, 73 | base_iri, 74 | }, 75 | ); 76 | } 77 | ( 78 | self.static_subqueries, 79 | self.basic_virtualized_queries, 80 | self.rewritten_filters, 81 | ) 82 | } else { 83 | panic!("Only support for select query") 84 | } 85 | } 86 | 87 | fn project_all_static_variables(&mut self, rewrites: Vec<&ExReturn>, context: &Context) { 88 | for r in rewrites { 89 | if let Some(expr) = &r.expression { 90 | self.project_all_static_variables_in_expression(expr, context); 91 | } 92 | } 93 | } 94 | 95 | fn rewrite_variable(&self, v: &Variable, context: &Context) -> Option { 96 | if let Some(ctr) = self.variable_constraints.get_constraint(v, context) { 97 | if !(ctr == &Constraint::External || ctr == &Constraint::ExternallyDerived) { 98 | Some(v.clone()) 99 | } else { 100 | None 101 | } 102 | } else { 103 | Some(v.clone()) 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/aggregate_expression.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::change_types::ChangeType; 3 | use oxrdf::Variable; 4 | use representation::query_context::{Context, PathEntry}; 5 | use spargebra::algebra::AggregateExpression; 6 | use std::collections::HashSet; 7 | 8 | pub struct AEReturn { 9 | pub aggregate_expression: Option, 10 | } 11 | 12 | impl AEReturn { 13 | fn new() -> AEReturn { 14 | AEReturn { 15 | aggregate_expression: None, 16 | } 17 | } 18 | 19 | fn with_aggregate_expression( 20 | &mut self, 21 | aggregate_expression: AggregateExpression, 22 | ) -> &mut AEReturn { 23 | self.aggregate_expression = Some(aggregate_expression); 24 | self 25 | } 26 | } 27 | 28 | impl StaticQueryRewriter { 29 | pub fn rewrite_aggregate_expression( 30 | &mut self, 31 | aggregate_expression: &AggregateExpression, 32 | variables_in_scope: &HashSet, 33 | create_subquery: bool, 34 | context: &Context, 35 | ) -> AEReturn { 36 | let mut aer = AEReturn::new(); 37 | match aggregate_expression { 38 | AggregateExpression::CountSolutions { distinct } => { 39 | aer.with_aggregate_expression(AggregateExpression::CountSolutions { 40 | distinct: *distinct, 41 | }); 42 | } 43 | AggregateExpression::FunctionCall { 44 | name, 45 | expr, 46 | distinct, 47 | } => { 48 | let mut expr_rewritten = self.rewrite_expression( 49 | expr, 50 | &ChangeType::NoChange, 51 | variables_in_scope, 52 | create_subquery, 53 | &context.extension_with(PathEntry::AggregationOperation), 54 | ); 55 | if expr_rewritten.is_subquery { 56 | unimplemented!("Exists patterns containing time series values within aggregation is not supported") 57 | } 58 | if expr_rewritten.expression.is_some() 59 | && expr_rewritten.change_type.as_ref().unwrap() == &ChangeType::NoChange 60 | { 61 | aer.with_aggregate_expression(AggregateExpression::FunctionCall { 62 | name: name.clone(), 63 | expr: expr_rewritten.expression.take().unwrap(), 64 | distinct: *distinct, 65 | }); 66 | } 67 | } 68 | } 69 | aer 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/expressions/coalesce_expression.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::change_types::ChangeType; 3 | use crate::rewriting::expressions::ExReturn; 4 | use oxrdf::Variable; 5 | use representation::query_context::{Context, PathEntry}; 6 | use spargebra::algebra::Expression; 7 | use std::collections::HashSet; 8 | 9 | impl StaticQueryRewriter { 10 | pub fn rewrite_coalesce_expression( 11 | &mut self, 12 | wrapped: &[Expression], 13 | variables_in_scope: &HashSet, 14 | create_subquery: bool, 15 | context: &Context, 16 | ) -> ExReturn { 17 | let mut rewritten = wrapped 18 | .iter() 19 | .enumerate() 20 | .map(|(i, e)| { 21 | self.rewrite_expression( 22 | e, 23 | &ChangeType::NoChange, 24 | variables_in_scope, 25 | create_subquery, 26 | &context.extension_with(PathEntry::Coalesce(i as u16)), 27 | ) 28 | }) 29 | .collect::>(); 30 | let mut exr = ExReturn::new(); 31 | for e in rewritten.iter_mut() { 32 | exr.with_is_subquery(e); 33 | } 34 | if rewritten.iter().all(|x| { 35 | x.expression.is_some() && x.change_type.as_ref().unwrap() == &ChangeType::NoChange 36 | }) { 37 | { 38 | exr.with_expression(Expression::Coalesce( 39 | rewritten 40 | .iter_mut() 41 | .map(|x| x.expression.take().unwrap()) 42 | .collect(), 43 | )) 44 | .with_change_type(ChangeType::NoChange); 45 | return exr; 46 | } 47 | } 48 | self.project_all_static_variables(rewritten.iter().collect(), context); 49 | exr 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/expressions/exists_expression.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::change_types::ChangeType; 3 | use crate::rewriting::expressions::ExReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use spargebra::algebra::{Expression, GraphPattern}; 6 | 7 | impl StaticQueryRewriter { 8 | pub fn rewrite_exists_expression( 9 | &mut self, 10 | wrapped: &GraphPattern, 11 | create_subquery: bool, 12 | context: &Context, 13 | ) -> ExReturn { 14 | let exists_context = context.extension_with(PathEntry::Exists); 15 | let mut wrapped_rewrite = self.rewrite_graph_pattern(wrapped, &exists_context); 16 | 17 | if !wrapped_rewrite.is_subquery { 18 | if !wrapped_rewrite.rewritten && !create_subquery { 19 | let mut exr = ExReturn::new(); 20 | exr.with_expression(Expression::Exists(Box::new( 21 | wrapped_rewrite.graph_pattern.take().unwrap(), 22 | ))) 23 | .with_change_type(ChangeType::NoChange); 24 | exr 25 | } else { 26 | self.create_add_subquery(wrapped_rewrite, &exists_context); 27 | ExReturn::subquery() 28 | } 29 | } else { 30 | ExReturn::subquery() 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/expressions/function_call_expression.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::change_types::ChangeType; 3 | use crate::rewriting::expressions::ExReturn; 4 | use oxrdf::Variable; 5 | use representation::query_context::{Context, PathEntry}; 6 | use spargebra::algebra::{Expression, Function}; 7 | use std::collections::HashSet; 8 | 9 | impl StaticQueryRewriter { 10 | pub fn rewrite_function_call_expression( 11 | &mut self, 12 | fun: &Function, 13 | args: &[Expression], 14 | variables_in_scope: &HashSet, 15 | create_subquery: bool, 16 | context: &Context, 17 | ) -> ExReturn { 18 | let mut args_rewritten = args 19 | .iter() 20 | .enumerate() 21 | .map(|(i, e)| { 22 | self.rewrite_expression( 23 | e, 24 | &ChangeType::NoChange, 25 | variables_in_scope, 26 | create_subquery, 27 | &context.extension_with(PathEntry::FunctionCall(i as u16)), 28 | ) 29 | }) 30 | .collect::>(); 31 | let mut exr = ExReturn::new(); 32 | for arg in args_rewritten.iter_mut() { 33 | exr.with_is_subquery(arg); 34 | } 35 | if args_rewritten.iter().all(|x| { 36 | x.expression.is_some() && x.change_type.as_ref().unwrap() == &ChangeType::NoChange 37 | }) { 38 | exr.with_expression(Expression::FunctionCall( 39 | fun.clone(), 40 | args_rewritten 41 | .iter_mut() 42 | .map(|x| x.expression.take().unwrap()) 43 | .collect(), 44 | )) 45 | .with_change_type(ChangeType::NoChange); 46 | return exr; 47 | } 48 | self.project_all_static_variables(args_rewritten.iter().collect(), context); 49 | exr 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/expressions/if_expression.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::change_types::ChangeType; 3 | use crate::rewriting::expressions::ExReturn; 4 | use oxrdf::Variable; 5 | use representation::query_context::{Context, PathEntry}; 6 | use spargebra::algebra::Expression; 7 | use std::collections::HashSet; 8 | 9 | impl StaticQueryRewriter { 10 | pub fn rewrite_if_expression( 11 | &mut self, 12 | left: &Expression, 13 | mid: &Expression, 14 | right: &Expression, 15 | variables_in_scope: &HashSet, 16 | create_subquery: bool, 17 | context: &Context, 18 | ) -> ExReturn { 19 | let mut left_rewrite = self.rewrite_expression( 20 | left, 21 | &ChangeType::NoChange, 22 | variables_in_scope, 23 | create_subquery, 24 | &context.extension_with(PathEntry::IfLeft), 25 | ); 26 | let mid_rewrite = self.rewrite_expression( 27 | mid, 28 | &ChangeType::NoChange, 29 | variables_in_scope, 30 | create_subquery || left_rewrite.is_subquery, 31 | &context.extension_with(PathEntry::IfMiddle), 32 | ); 33 | let mut right_rewrite = self.rewrite_expression( 34 | right, 35 | &ChangeType::NoChange, 36 | variables_in_scope, 37 | create_subquery || left_rewrite.is_subquery || mid_rewrite.is_subquery, 38 | &context.extension_with(PathEntry::IfRight), 39 | ); 40 | let mut exr = ExReturn::new(); 41 | exr.with_is_subquery(&left_rewrite) 42 | .with_is_subquery(&mid_rewrite) 43 | .with_is_subquery(&right_rewrite); 44 | if left_rewrite.expression.is_some() 45 | && left_rewrite.change_type.as_ref().unwrap() == &ChangeType::NoChange 46 | && mid_rewrite.expression.is_some() 47 | && mid_rewrite.change_type.as_ref().unwrap() == &ChangeType::NoChange 48 | && right_rewrite.expression.is_some() 49 | && right_rewrite.change_type.as_ref().unwrap() == &ChangeType::NoChange 50 | { 51 | let left_expression_rewrite = left_rewrite.expression.take().unwrap(); 52 | let mid_expression_rewrite = left_rewrite.expression.take().unwrap(); 53 | let right_expression_rewrite = right_rewrite.expression.take().unwrap(); 54 | exr.with_expression(Expression::If( 55 | Box::new(left_expression_rewrite), 56 | Box::new(mid_expression_rewrite), 57 | Box::new(right_expression_rewrite), 58 | )) 59 | .with_change_type(ChangeType::NoChange); 60 | return exr; 61 | } 62 | self.project_all_static_variables( 63 | vec![&left_rewrite, &mid_rewrite, &right_rewrite], 64 | context, 65 | ); 66 | exr 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/expressions/not_expression.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::change_types::ChangeType; 3 | use crate::rewriting::expressions::ExReturn; 4 | use oxrdf::Variable; 5 | use representation::query_context::{Context, PathEntry}; 6 | use spargebra::algebra::Expression; 7 | use std::collections::HashSet; 8 | 9 | impl StaticQueryRewriter { 10 | pub fn rewrite_not_expression( 11 | &mut self, 12 | wrapped: &Expression, 13 | required_change_direction: &ChangeType, 14 | variables_in_scope: &HashSet, 15 | create_subquery: bool, 16 | context: &Context, 17 | ) -> ExReturn { 18 | let mut wrapped_rewrite = self.rewrite_expression( 19 | wrapped, 20 | &required_change_direction.opposite(), 21 | variables_in_scope, 22 | create_subquery, 23 | &context.extension_with(PathEntry::Not), 24 | ); 25 | let mut exr = ExReturn::new(); 26 | exr.with_is_subquery(&wrapped_rewrite); 27 | if wrapped_rewrite.expression.is_some() { 28 | let wrapped_change = wrapped_rewrite.change_type.take().unwrap(); 29 | let use_change_type = match wrapped_change { 30 | ChangeType::NoChange => ChangeType::NoChange, 31 | ChangeType::Relaxed => ChangeType::Constrained, 32 | ChangeType::Constrained => ChangeType::Relaxed, 33 | }; 34 | if use_change_type == ChangeType::NoChange 35 | || &use_change_type == required_change_direction 36 | { 37 | let wrapped_expression_rewrite = wrapped_rewrite.expression.take().unwrap(); 38 | exr.with_expression(Expression::Not(Box::new(wrapped_expression_rewrite))) 39 | .with_change_type(use_change_type); 40 | return exr; 41 | } 42 | } 43 | self.project_all_static_variables(vec![&wrapped_rewrite], context); 44 | exr 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/expressions/unary_ordinary_expression.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::change_types::ChangeType; 3 | use crate::rewriting::expressions::ExReturn; 4 | use oxrdf::Variable; 5 | use representation::query_context::{Context, PathEntry}; 6 | use spargebra::algebra::Expression; 7 | use std::collections::HashSet; 8 | 9 | pub enum UnaryOrdinaryOperator { 10 | UnaryPlus, 11 | UnaryMinus, 12 | } 13 | 14 | impl StaticQueryRewriter { 15 | pub fn rewrite_unary_ordinary_expression( 16 | &mut self, 17 | wrapped: &Expression, 18 | operation: &UnaryOrdinaryOperator, 19 | variables_in_scope: &HashSet, 20 | create_subquery: bool, 21 | context: &Context, 22 | ) -> ExReturn { 23 | let (path_entry, expression): (_, fn(Box) -> Expression) = match operation { 24 | UnaryOrdinaryOperator::UnaryPlus => (PathEntry::UnaryPlus, Expression::UnaryPlus), 25 | UnaryOrdinaryOperator::UnaryMinus => (PathEntry::UnaryMinus, Expression::UnaryMinus), 26 | }; 27 | let mut wrapped_rewrite = self.rewrite_expression( 28 | wrapped, 29 | &ChangeType::NoChange, 30 | variables_in_scope, 31 | create_subquery, 32 | &context.extension_with(path_entry), 33 | ); 34 | let mut exr = ExReturn::new(); 35 | exr.with_is_subquery(&wrapped_rewrite); 36 | if wrapped_rewrite.expression.is_some() 37 | && wrapped_rewrite.change_type.as_ref().unwrap() == &ChangeType::NoChange 38 | { 39 | let wrapped_expression_rewrite = wrapped_rewrite.expression.take().unwrap(); 40 | exr.with_expression(expression(Box::new(wrapped_expression_rewrite))) 41 | .with_change_type(ChangeType::NoChange); 42 | return exr; 43 | } 44 | self.project_all_static_variables(vec![&wrapped_rewrite], context); 45 | exr 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/distinct_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::graph_patterns::GPReturn; 3 | use representation::query_context::{Context, PathEntry}; 4 | use spargebra::algebra::GraphPattern; 5 | 6 | impl StaticQueryRewriter { 7 | pub fn rewrite_distinct(&mut self, inner: &GraphPattern, context: &Context) -> GPReturn { 8 | let mut gpr_inner = 9 | self.rewrite_graph_pattern(inner, &context.extension_with(PathEntry::DistinctInner)); 10 | if !gpr_inner.is_subquery { 11 | let inner_graph_pattern = gpr_inner.graph_pattern.take().unwrap(); 12 | gpr_inner.with_graph_pattern(GraphPattern::Distinct { 13 | inner: Box::new(inner_graph_pattern), 14 | }); 15 | } 16 | gpr_inner 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/extend_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::change_types::ChangeType; 3 | use crate::rewriting::graph_patterns::GPReturn; 4 | use oxrdf::Variable; 5 | use representation::query_context::{Context, PathEntry}; 6 | use spargebra::algebra::{Expression, GraphPattern}; 7 | 8 | impl StaticQueryRewriter { 9 | pub(crate) fn rewrite_extend( 10 | &mut self, 11 | inner: &GraphPattern, 12 | var: &Variable, 13 | expr: &Expression, 14 | context: &Context, 15 | ) -> GPReturn { 16 | let mut inner_rewrite = 17 | self.rewrite_graph_pattern(inner, &context.extension_with(PathEntry::ExtendInner)); 18 | if inner_rewrite.is_subquery { 19 | return inner_rewrite; 20 | } 21 | 22 | let mut expr_rewrite = self.rewrite_expression( 23 | expr, 24 | &ChangeType::NoChange, 25 | &inner_rewrite.variables_in_scope, 26 | inner_rewrite.is_subquery, 27 | &context.extension_with(PathEntry::ExtendExpression), 28 | ); 29 | 30 | if expr_rewrite.is_subquery { 31 | unimplemented!("No support for exists with time series values in extend yet") 32 | } 33 | 34 | if expr_rewrite.expression.is_some() { 35 | inner_rewrite.variables_in_scope.insert(var.clone()); 36 | let inner_graph_pattern = inner_rewrite.graph_pattern.take().unwrap(); 37 | inner_rewrite.with_graph_pattern(GraphPattern::Extend { 38 | inner: Box::new(inner_graph_pattern), //No need for push up since there should be no change 39 | variable: var.clone(), 40 | expression: expr_rewrite.expression.take().unwrap(), 41 | }); 42 | inner_rewrite.with_rewritten( 43 | inner_rewrite.rewritten || expr_rewrite.change_type != Some(ChangeType::NoChange), 44 | ); 45 | inner_rewrite 46 | } else { 47 | inner_rewrite.with_rewritten(true); 48 | inner_rewrite 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/filter_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::change_types::ChangeType; 3 | use crate::rewriting::graph_patterns::GPReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use spargebra::algebra::{Expression, GraphPattern}; 6 | 7 | impl StaticQueryRewriter { 8 | pub fn rewrite_filter( 9 | &mut self, 10 | expression: &Expression, 11 | inner: &GraphPattern, 12 | context: &Context, 13 | ) -> GPReturn { 14 | let inner_context = context.extension_with(PathEntry::FilterInner); 15 | let expression_context = context.extension_with(PathEntry::FilterExpression); 16 | let mut inner_rewrite = self.rewrite_graph_pattern(inner, &inner_context); 17 | let mut expression_rewrite = self.rewrite_expression( 18 | expression, 19 | &ChangeType::Relaxed, 20 | &inner_rewrite.variables_in_scope, 21 | inner_rewrite.is_subquery, 22 | &expression_context, 23 | ); 24 | if expression_rewrite.is_subquery || inner_rewrite.is_subquery { 25 | if !inner_rewrite.is_subquery { 26 | self.create_add_subquery(inner_rewrite, &inner_context); 27 | } 28 | 29 | let ret = GPReturn::subquery(); 30 | return ret; 31 | } 32 | 33 | if expression_rewrite.expression.is_some() { 34 | let rewritten = inner_rewrite.rewritten 35 | || expression_rewrite.change_type != Some(ChangeType::NoChange); 36 | self.rewritten_filters.insert( 37 | context.clone(), 38 | expression_rewrite.expression.as_ref().unwrap().clone(), 39 | ); 40 | let inner_graph_pattern = inner_rewrite.graph_pattern.take().unwrap(); 41 | inner_rewrite 42 | .with_graph_pattern(GraphPattern::Filter { 43 | expr: expression_rewrite.expression.take().unwrap(), 44 | inner: Box::new(inner_graph_pattern), 45 | }) 46 | .with_rewritten(rewritten); 47 | inner_rewrite 48 | } else { 49 | let inner_graph_pattern = inner_rewrite.graph_pattern.take().unwrap(); 50 | inner_rewrite 51 | .with_graph_pattern(inner_graph_pattern) 52 | .with_rewritten(true); 53 | inner_rewrite 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/graph_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::graph_patterns::GPReturn; 3 | use representation::query_context::Context; 4 | use spargebra::algebra::GraphPattern; 5 | use spargebra::term::NamedNodePattern; 6 | 7 | impl StaticQueryRewriter { 8 | pub fn rewrite_graph( 9 | &mut self, 10 | name: &NamedNodePattern, 11 | inner: &GraphPattern, 12 | context: &Context, 13 | ) -> GPReturn { 14 | let mut inner_gpr = self.rewrite_graph_pattern(inner, context); 15 | if !inner_gpr.is_subquery { 16 | let inner_rewrite = inner_gpr.graph_pattern.take().unwrap(); 17 | inner_gpr.with_graph_pattern(GraphPattern::Graph { 18 | name: name.clone(), 19 | inner: Box::new(inner_rewrite), 20 | }); 21 | return inner_gpr; 22 | } 23 | unimplemented!("No support for rewritten graph graph pattern") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/group_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::aggregate_expression::AEReturn; 3 | use crate::rewriting::graph_patterns::GPReturn; 4 | use oxrdf::Variable; 5 | use representation::query_context::{Context, PathEntry}; 6 | use spargebra::algebra::{AggregateExpression, GraphPattern}; 7 | 8 | impl StaticQueryRewriter { 9 | pub fn rewrite_group( 10 | &mut self, 11 | graph_pattern: &GraphPattern, 12 | variables: &[Variable], 13 | aggregates: &[(Variable, AggregateExpression)], 14 | context: &Context, 15 | ) -> GPReturn { 16 | let inner_context = context.extension_with(PathEntry::GroupInner); 17 | 18 | let mut graph_pattern_rewrite = self.rewrite_graph_pattern(graph_pattern, &inner_context); 19 | if !graph_pattern_rewrite.is_subquery { 20 | if !graph_pattern_rewrite.rewritten { 21 | let variables_rewritten: Vec> = variables 22 | .iter() 23 | .map(|v| self.rewrite_variable(v, context)) 24 | .collect(); 25 | 26 | let aes_rewritten: Vec<(Option, AEReturn)> = aggregates 27 | .iter() 28 | .enumerate() 29 | .map(|(i, (v, a))| { 30 | ( 31 | self.rewrite_variable(v, context), 32 | self.rewrite_aggregate_expression( 33 | a, 34 | &graph_pattern_rewrite.variables_in_scope, 35 | graph_pattern_rewrite.is_subquery, 36 | &context.extension_with(PathEntry::GroupAggregation(i as u16)), 37 | ), 38 | ) 39 | }) 40 | .collect(); 41 | 42 | if variables_rewritten.iter().all(|v| v.is_some()) 43 | && aes_rewritten 44 | .iter() 45 | .all(|(v, a)| v.is_some() && a.aggregate_expression.is_some()) 46 | { 47 | for v in &variables_rewritten { 48 | graph_pattern_rewrite 49 | .variables_in_scope 50 | .insert(v.as_ref().unwrap().clone()); 51 | } 52 | let inner_graph_pattern = graph_pattern_rewrite.graph_pattern.take().unwrap(); 53 | 54 | graph_pattern_rewrite.with_graph_pattern(GraphPattern::Group { 55 | inner: Box::new(inner_graph_pattern), 56 | variables: variables_rewritten 57 | .into_iter() 58 | .map(|v| v.unwrap()) 59 | .collect(), 60 | aggregates: vec![], 61 | }); 62 | return graph_pattern_rewrite; 63 | } 64 | } else { 65 | self.create_add_subquery(graph_pattern_rewrite.clone(), &inner_context); 66 | return GPReturn::subquery(); 67 | } 68 | } 69 | graph_pattern_rewrite 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/join_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::graph_patterns::GPReturn; 3 | use representation::query_context::{Context, PathEntry}; 4 | 5 | use spargebra::algebra::GraphPattern; 6 | 7 | impl StaticQueryRewriter { 8 | pub fn rewrite_join( 9 | &mut self, 10 | left: &GraphPattern, 11 | right: &GraphPattern, 12 | context: &Context, 13 | ) -> GPReturn { 14 | let left_context = context.extension_with(PathEntry::JoinLeftSide); 15 | let mut left_rewrite = self.rewrite_graph_pattern(left, &left_context); 16 | let right_context = context.extension_with(PathEntry::JoinRightSide); 17 | let mut right_rewrite = self.rewrite_graph_pattern(right, &right_context); 18 | 19 | if left_rewrite.is_subquery || right_rewrite.is_subquery { 20 | if !left_rewrite.is_subquery { 21 | self.create_add_subquery(left_rewrite, &left_context); 22 | } 23 | if !right_rewrite.is_subquery { 24 | self.create_add_subquery(right_rewrite, &right_context); 25 | } 26 | let ret = GPReturn::subquery(); 27 | return ret; 28 | } 29 | 30 | let use_rewritten = left_rewrite.rewritten || right_rewrite.rewritten; 31 | let left_graph_pattern = left_rewrite.graph_pattern.take().unwrap(); 32 | let right_graph_pattern = right_rewrite.graph_pattern.take().unwrap(); 33 | 34 | left_rewrite 35 | .with_scope(&mut right_rewrite) 36 | .with_graph_pattern(GraphPattern::Join { 37 | left: Box::new(left_graph_pattern), 38 | right: Box::new(right_graph_pattern), 39 | }) 40 | .with_rewritten(use_rewritten); 41 | left_rewrite 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/left_join_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::change_types::ChangeType; 3 | use crate::rewriting::graph_patterns::GPReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use spargebra::algebra::{Expression, GraphPattern}; 6 | 7 | impl StaticQueryRewriter { 8 | pub fn rewrite_left_join( 9 | &mut self, 10 | left: &GraphPattern, 11 | right: &GraphPattern, 12 | expression_opt: &Option, 13 | context: &Context, 14 | ) -> GPReturn { 15 | let left_context = context.extension_with(PathEntry::LeftJoinLeftSide); 16 | let mut left_rewrite = self.rewrite_graph_pattern(left, &left_context); 17 | let right_context = context.extension_with(PathEntry::LeftJoinRightSide); 18 | let mut right_rewrite = self.rewrite_graph_pattern(right, &right_context); 19 | 20 | let expression_context = context.extension_with(PathEntry::LeftJoinExpression); 21 | let mut expression_rewrite = None; 22 | 23 | if let Some(expression) = expression_opt { 24 | let mut use_variables_in_scope = left_rewrite.variables_in_scope.clone(); 25 | use_variables_in_scope.extend(right_rewrite.variables_in_scope.clone()); 26 | 27 | expression_rewrite = Some(self.rewrite_expression( 28 | expression, 29 | &ChangeType::NoChange, 30 | &use_variables_in_scope, 31 | left_rewrite.is_subquery || right_rewrite.is_subquery, 32 | &expression_context, 33 | )); 34 | } 35 | 36 | if left_rewrite.rewritten 37 | || right_rewrite.rewritten 38 | || (expression_rewrite.is_some() 39 | && expression_rewrite.as_ref().unwrap().expression.is_none()) 40 | { 41 | if !left_rewrite.is_subquery { 42 | self.create_add_subquery(left_rewrite, &left_context); 43 | } 44 | if !right_rewrite.is_subquery { 45 | self.create_add_subquery(right_rewrite, &right_context); 46 | } 47 | return GPReturn::subquery(); 48 | } 49 | 50 | let left_graph_pattern = left_rewrite.graph_pattern.take().unwrap(); 51 | let right_graph_pattern = right_rewrite.graph_pattern.take().unwrap(); 52 | let expression = if let Some(mut expression) = expression_rewrite { 53 | expression.expression.take() 54 | } else { 55 | None 56 | }; 57 | 58 | left_rewrite 59 | .with_scope(&mut right_rewrite) 60 | .with_graph_pattern(GraphPattern::LeftJoin { 61 | left: Box::new(left_graph_pattern), 62 | right: Box::new(right_graph_pattern), 63 | expression, 64 | }); 65 | left_rewrite 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/minus_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::graph_patterns::GPReturn; 3 | use representation::query_context::{Context, PathEntry}; 4 | use spargebra::algebra::GraphPattern; 5 | 6 | impl StaticQueryRewriter { 7 | pub fn rewrite_minus( 8 | &mut self, 9 | left: &GraphPattern, 10 | right: &GraphPattern, 11 | 12 | context: &Context, 13 | ) -> GPReturn { 14 | let left_context = context.extension_with(PathEntry::MinusLeftSide); 15 | let mut left_rewrite = self.rewrite_graph_pattern(left, &left_context); 16 | let right_context = context.extension_with(PathEntry::MinusRightSide); 17 | let mut right_rewrite = self.rewrite_graph_pattern(right, &right_context); 18 | if left_rewrite.is_subquery || right_rewrite.is_subquery { 19 | if !left_rewrite.is_subquery { 20 | self.create_add_subquery(left_rewrite, &left_context); 21 | } 22 | if !right_rewrite.is_subquery { 23 | self.create_add_subquery(right_rewrite, &right_context); 24 | } 25 | let ret = GPReturn::subquery(); 26 | return ret; 27 | } 28 | 29 | if !left_rewrite.rewritten && !right_rewrite.rewritten { 30 | let left_graph_pattern = left_rewrite.graph_pattern.take().unwrap(); 31 | let right_graph_pattern = right_rewrite.graph_pattern.take().unwrap(); 32 | left_rewrite.with_graph_pattern(GraphPattern::Minus { 33 | left: Box::new(left_graph_pattern), 34 | right: Box::new(right_graph_pattern), 35 | }); 36 | left_rewrite 37 | } else if left_rewrite.rewritten && !right_rewrite.rewritten { 38 | let left_graph_pattern = left_rewrite.graph_pattern.take().unwrap(); 39 | let right_graph_pattern = right_rewrite.graph_pattern.take().unwrap(); 40 | left_rewrite 41 | .with_graph_pattern(GraphPattern::Minus { 42 | left: Box::new(left_graph_pattern), 43 | right: Box::new(right_graph_pattern), 44 | }) 45 | .with_rewritten(true); 46 | return left_rewrite; 47 | } else { 48 | // right rewritten 49 | self.create_add_subquery(left_rewrite, &left_context); 50 | self.create_add_subquery(right_rewrite, &right_context); 51 | 52 | let ret = GPReturn::subquery(); 53 | return ret; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/order_by_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::graph_patterns::GPReturn; 3 | use crate::rewriting::order_expression::OEReturn; 4 | use representation::query_context::{Context, PathEntry}; 5 | use spargebra::algebra::{GraphPattern, OrderExpression}; 6 | 7 | impl StaticQueryRewriter { 8 | pub fn rewrite_order_by( 9 | &mut self, 10 | inner: &GraphPattern, 11 | order_expressions: &[OrderExpression], 12 | 13 | context: &Context, 14 | ) -> GPReturn { 15 | let mut inner_rewrite = 16 | self.rewrite_graph_pattern(inner, &context.extension_with(PathEntry::OrderByInner)); 17 | 18 | if inner_rewrite.is_subquery { 19 | return inner_rewrite; 20 | } 21 | 22 | let mut order_expressions_rewrite = order_expressions 23 | .iter() 24 | .enumerate() 25 | .map(|(i, e)| { 26 | self.rewrite_order_expression( 27 | e, 28 | &inner_rewrite.variables_in_scope, 29 | inner_rewrite.is_subquery, 30 | &context.extension_with(PathEntry::OrderByExpression(i as u16)), 31 | ) 32 | }) 33 | .collect::>(); 34 | 35 | let inner_graph_pattern = inner_rewrite.graph_pattern.take().unwrap(); 36 | if order_expressions_rewrite 37 | .iter() 38 | .any(|oer| oer.order_expression.is_some()) 39 | { 40 | inner_rewrite.with_graph_pattern(GraphPattern::OrderBy { 41 | inner: Box::new(inner_graph_pattern), 42 | expression: order_expressions_rewrite 43 | .iter_mut() 44 | .filter(|oer| oer.order_expression.is_some()) 45 | .map(|oer| oer.order_expression.take().unwrap()) 46 | .collect(), 47 | }); 48 | } else { 49 | inner_rewrite.with_graph_pattern(inner_graph_pattern); 50 | } 51 | inner_rewrite 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/path_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::graph_patterns::GPReturn; 3 | use spargebra::algebra::{GraphPattern, PropertyPathExpression}; 4 | use spargebra::term::TermPattern; 5 | use std::collections::HashSet; 6 | 7 | impl StaticQueryRewriter { 8 | //We assume that all paths have been rewritten so as to not contain any datapoint, timestamp, or data value. 9 | //These should have been split into ordinary triples. 10 | pub fn rewrite_path( 11 | &mut self, 12 | subject: &TermPattern, 13 | path: &PropertyPathExpression, 14 | object: &TermPattern, 15 | ) -> GPReturn { 16 | let mut variables_in_scope = HashSet::new(); 17 | if let TermPattern::Variable(s) = subject { 18 | variables_in_scope.insert(s.clone()); 19 | } 20 | if let TermPattern::Variable(o) = object { 21 | variables_in_scope.insert(o.clone()); 22 | } 23 | 24 | GPReturn::new( 25 | GraphPattern::Path { 26 | subject: subject.clone(), 27 | path: path.clone(), 28 | object: object.clone(), 29 | }, 30 | false, 31 | variables_in_scope, 32 | Default::default(), 33 | Default::default(), 34 | false, 35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/project_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::graph_patterns::GPReturn; 3 | use oxrdf::Variable; 4 | use representation::query_context::{Context, PathEntry}; 5 | use spargebra::algebra::GraphPattern; 6 | 7 | impl StaticQueryRewriter { 8 | pub fn rewrite_project( 9 | &mut self, 10 | inner: &GraphPattern, 11 | variables: &[Variable], 12 | context: &Context, 13 | ) -> GPReturn { 14 | let mut inner_rewrite = 15 | self.rewrite_graph_pattern(inner, &context.extension_with(PathEntry::ProjectInner)); 16 | if !inner_rewrite.is_subquery { 17 | let project_pattern = 18 | self.create_projection_graph_pattern(&inner_rewrite, context, variables); 19 | inner_rewrite.with_graph_pattern(project_pattern); 20 | return inner_rewrite; 21 | } 22 | inner_rewrite 23 | } 24 | 25 | pub(crate) fn create_projection_graph_pattern( 26 | &self, 27 | gpreturn: &GPReturn, 28 | context: &Context, 29 | variables: &[Variable], 30 | ) -> GraphPattern { 31 | let mut variables_rewrite = variables 32 | .iter() 33 | .filter_map(|v| self.rewrite_variable(v, context)) 34 | .collect::>(); 35 | 36 | let mut resource_keys_sorted = gpreturn 37 | .resources_in_scope 38 | .keys() 39 | .collect::>(); 40 | resource_keys_sorted.sort_by_key(|v| v.to_string()); 41 | 42 | for k in resource_keys_sorted { 43 | let vs = gpreturn.resources_in_scope.get(k).unwrap(); 44 | let mut vars = vs.iter().collect::>(); 45 | //Sort to make rewrites deterministic 46 | vars.sort_by_key(|v| v.to_string()); 47 | for v in vars { 48 | if !variables_rewrite.contains(v) { 49 | variables_rewrite.push(v.clone()); 50 | } 51 | } 52 | } 53 | 54 | let mut id_keys_sorted = gpreturn 55 | .external_ids_in_scope 56 | .keys() 57 | .collect::>(); 58 | id_keys_sorted.sort_by_key(|v| v.to_string()); 59 | for k in id_keys_sorted { 60 | let vs = gpreturn.external_ids_in_scope.get(k).unwrap(); 61 | let mut vars = vs.iter().collect::>(); 62 | //Sort to make rewrites deterministic 63 | vars.sort_by_key(|v| v.to_string()); 64 | for v in vars { 65 | if !variables_rewrite.contains(v) { 66 | variables_rewrite.push(v.clone()); 67 | } 68 | } 69 | } 70 | let mut additional_projections_sorted = self 71 | .additional_projections 72 | .iter() 73 | .collect::>(); 74 | additional_projections_sorted.sort_by_key(|x| x.to_string()); 75 | for v in additional_projections_sorted { 76 | if !variables_rewrite.contains(v) { 77 | variables_rewrite.push(v.clone()); 78 | } 79 | } 80 | GraphPattern::Project { 81 | inner: Box::new(gpreturn.graph_pattern.as_ref().unwrap().clone()), 82 | variables: variables_rewrite, 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/reduced_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::graph_patterns::GPReturn; 3 | use representation::query_context::{Context, PathEntry}; 4 | use spargebra::algebra::GraphPattern; 5 | 6 | impl StaticQueryRewriter { 7 | pub fn rewrite_reduced(&mut self, inner: &GraphPattern, context: &Context) -> GPReturn { 8 | let mut inner_rewrite = 9 | self.rewrite_graph_pattern(inner, &context.extension_with(PathEntry::ReducedInner)); 10 | if !inner_rewrite.is_subquery { 11 | let inner_graph_pattern = inner_rewrite.graph_pattern.take().unwrap(); 12 | inner_rewrite.with_graph_pattern(GraphPattern::Reduced { 13 | inner: Box::new(inner_graph_pattern), 14 | }); 15 | return inner_rewrite; 16 | } 17 | inner_rewrite 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/service_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::graph_patterns::GPReturn; 3 | use representation::query_context::{Context, PathEntry}; 4 | use spargebra::algebra::GraphPattern; 5 | use spargebra::term::NamedNodePattern; 6 | 7 | impl StaticQueryRewriter { 8 | pub fn rewrite_service( 9 | &mut self, 10 | name: &NamedNodePattern, 11 | inner: &GraphPattern, 12 | silent: &bool, 13 | context: &Context, 14 | ) -> GPReturn { 15 | let mut inner_rewrite = 16 | self.rewrite_graph_pattern(inner, &context.extension_with(PathEntry::ServiceInner)); 17 | if !inner_rewrite.rewritten { 18 | let inner_graph_pattern = inner_rewrite.graph_pattern.take().unwrap(); 19 | inner_rewrite.with_graph_pattern(GraphPattern::Service { 20 | name: name.clone(), 21 | inner: Box::new(inner_graph_pattern), 22 | silent: *silent, 23 | }); 24 | return inner_rewrite; 25 | } 26 | panic!("Should never happen") 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/sliced_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::graph_patterns::GPReturn; 3 | use representation::query_context::{Context, PathEntry}; 4 | use spargebra::algebra::GraphPattern; 5 | 6 | impl StaticQueryRewriter { 7 | pub fn rewrite_slice( 8 | &mut self, 9 | inner: &GraphPattern, 10 | start: &usize, 11 | length: &Option, 12 | 13 | context: &Context, 14 | ) -> GPReturn { 15 | let mut inner_rewrite = 16 | self.rewrite_graph_pattern(inner, &context.extension_with(PathEntry::SliceInner)); 17 | if !inner_rewrite.is_subquery { 18 | let inner_graph_pattern = inner_rewrite.graph_pattern.take().unwrap(); 19 | inner_rewrite.with_graph_pattern(GraphPattern::Slice { 20 | inner: Box::new(inner_graph_pattern), 21 | start: *start, 22 | length: *length, 23 | }); 24 | return inner_rewrite; 25 | } 26 | inner_rewrite 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/union_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::graph_patterns::GPReturn; 3 | use representation::query_context::{Context, PathEntry}; 4 | use spargebra::algebra::GraphPattern; 5 | 6 | impl StaticQueryRewriter { 7 | pub fn rewrite_union( 8 | &mut self, 9 | left: &GraphPattern, 10 | right: &GraphPattern, 11 | 12 | context: &Context, 13 | ) -> GPReturn { 14 | let left_context = context.extension_with(PathEntry::UnionLeftSide); 15 | let mut left_rewrite = self.rewrite_graph_pattern(left, &left_context); 16 | let right_context = context.extension_with(PathEntry::UnionRightSide); 17 | let mut right_rewrite = self.rewrite_graph_pattern(right, &right_context); 18 | 19 | if left_rewrite.rewritten || right_rewrite.rewritten { 20 | if !left_rewrite.is_subquery { 21 | self.create_add_subquery(left_rewrite, &left_context); 22 | } 23 | if !right_rewrite.is_subquery { 24 | self.create_add_subquery(right_rewrite, &right_context); 25 | } 26 | return GPReturn::subquery(); 27 | } 28 | 29 | let left_graph_pattern = left_rewrite.graph_pattern.take().unwrap(); 30 | let right_graph_pattern = right_rewrite.graph_pattern.take().unwrap(); 31 | left_rewrite 32 | .with_scope(&mut right_rewrite) 33 | .with_graph_pattern(GraphPattern::Union { 34 | left: Box::new(left_graph_pattern), 35 | right: Box::new(right_graph_pattern), 36 | }); 37 | left_rewrite 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/graph_patterns/values_pattern.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::rewriting::graph_patterns::GPReturn; 3 | use oxrdf::Variable; 4 | use spargebra::algebra::GraphPattern; 5 | use spargebra::term::GroundTerm; 6 | use std::collections::HashMap; 7 | 8 | impl StaticQueryRewriter { 9 | pub fn rewrite_values( 10 | &mut self, 11 | variables: &[Variable], 12 | bindings: &[Vec>], 13 | ) -> GPReturn { 14 | GPReturn::new( 15 | GraphPattern::Values { 16 | variables: variables.to_vec(), 17 | bindings: bindings.to_vec(), 18 | }, 19 | false, 20 | variables.iter().cloned().collect(), 21 | HashMap::new(), 22 | HashMap::new(), 23 | false, 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/order_expression.rs: -------------------------------------------------------------------------------- 1 | use super::StaticQueryRewriter; 2 | use crate::change_types::ChangeType; 3 | use oxrdf::Variable; 4 | use representation::query_context::{Context, PathEntry}; 5 | use spargebra::algebra::OrderExpression; 6 | use std::collections::HashSet; 7 | 8 | pub struct OEReturn { 9 | pub order_expression: Option, 10 | } 11 | 12 | impl OEReturn { 13 | fn new() -> OEReturn { 14 | OEReturn { 15 | order_expression: None, 16 | } 17 | } 18 | 19 | fn with_order_expression(&mut self, order_expression: OrderExpression) -> &mut OEReturn { 20 | self.order_expression = Some(order_expression); 21 | self 22 | } 23 | } 24 | 25 | impl StaticQueryRewriter { 26 | pub fn rewrite_order_expression( 27 | &mut self, 28 | order_expression: &OrderExpression, 29 | variables_in_scope: &HashSet, 30 | create_subquery: bool, 31 | context: &Context, 32 | ) -> OEReturn { 33 | let mut oer = OEReturn::new(); 34 | match order_expression { 35 | OrderExpression::Asc(e) => { 36 | let mut e_rewrite = self.rewrite_expression( 37 | e, 38 | &ChangeType::NoChange, 39 | variables_in_scope, 40 | create_subquery, 41 | &context.extension_with(PathEntry::OrderingOperation), 42 | ); 43 | if e_rewrite.is_subquery { 44 | unimplemented!("Exists patterns containing time series values within aggregation is not supported") 45 | } 46 | if e_rewrite.expression.is_some() { 47 | oer.with_order_expression(OrderExpression::Asc( 48 | e_rewrite.expression.take().unwrap(), 49 | )); 50 | } 51 | } 52 | OrderExpression::Desc(e) => { 53 | let mut e_rewrite = self.rewrite_expression( 54 | e, 55 | &ChangeType::NoChange, 56 | variables_in_scope, 57 | create_subquery, 58 | &context.extension_with(PathEntry::OrderingOperation), 59 | ); 60 | if e_rewrite.is_subquery { 61 | unimplemented!("Exists patterns containing time series values within aggregation is not supported") 62 | } 63 | if e_rewrite.expression.is_some() { 64 | oer.with_order_expression(OrderExpression::Desc( 65 | e_rewrite.expression.take().unwrap(), 66 | )); 67 | } 68 | } 69 | } 70 | oer 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/chrontext/src/rewriting/subqueries.rs: -------------------------------------------------------------------------------- 1 | use crate::rewriting::graph_patterns::GPReturn; 2 | use crate::rewriting::StaticQueryRewriter; 3 | use oxrdf::Variable; 4 | use representation::query_context::Context; 5 | use spargebra::algebra::GraphPattern; 6 | use spargebra::Query; 7 | 8 | impl StaticQueryRewriter { 9 | pub(crate) fn create_add_subquery(&mut self, gpreturn: GPReturn, context: &Context) { 10 | if gpreturn.graph_pattern.is_some() { 11 | let is_gp = matches!(&gpreturn.graph_pattern, Some(GraphPattern::Project { .. })); 12 | if is_gp { 13 | self.add_subquery(context, gpreturn.graph_pattern.unwrap()); 14 | } else { 15 | let mut variables: Vec = 16 | gpreturn.variables_in_scope.iter().cloned().collect(); 17 | variables.sort_by_key(|x| x.as_str().to_string()); 18 | let projection = 19 | self.create_projection_graph_pattern(&gpreturn, context, &variables); 20 | self.add_subquery(context, projection) 21 | } 22 | } 23 | } 24 | 25 | fn add_subquery(&mut self, context: &Context, gp: GraphPattern) { 26 | self.static_subqueries.insert( 27 | context.clone(), 28 | Query::Select { 29 | dataset: None, 30 | pattern: gp, 31 | base_iri: None, 32 | }, 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/chrontext/src/splitter.rs: -------------------------------------------------------------------------------- 1 | use spargebra::{Query, SparqlSyntaxError}; 2 | use thiserror::Error; 3 | 4 | #[derive(Debug, Error)] 5 | pub enum QueryParseError { 6 | #[error(transparent)] 7 | Parse(SparqlSyntaxError), 8 | #[error("Not a select query")] 9 | NotSelectQuery, 10 | #[error("Unsupported construct: `{0}`")] 11 | Unsupported(String), 12 | } 13 | 14 | pub fn parse_sparql_select_query(query_str: &str) -> Result { 15 | let q_res = Query::parse(query_str, None); 16 | match q_res { 17 | Ok(q) => match q { 18 | Query::Select { 19 | dataset, 20 | pattern, 21 | base_iri, 22 | } => { 23 | let mut unsupported_constructs = vec![]; 24 | if dataset.is_some() { 25 | unsupported_constructs.push("Dataset") 26 | } 27 | if base_iri.is_some() { 28 | unsupported_constructs.push("BaseIri") 29 | } 30 | if !unsupported_constructs.is_empty() { 31 | Err(QueryParseError::Unsupported( 32 | unsupported_constructs.join(","), 33 | )) 34 | } else { 35 | Ok(Query::Select { 36 | dataset, 37 | pattern, 38 | base_iri, 39 | }) 40 | } 41 | } 42 | _ => Err(QueryParseError::NotSelectQuery), 43 | }, 44 | Err(e) => Err(QueryParseError::Parse(e)), 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/flight/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flight" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | chrontext = {path="../chrontext"} 8 | 9 | representation.workspace = true 10 | query_processing.workspace = true 11 | 12 | tonic.workspace = true 13 | tokio.workspace = true 14 | arrow-flight.workspace = true 15 | futures.workspace = true 16 | bincode.workspace = true 17 | polars = {workspace = true, features = ["polars-io", "ipc_streaming"]} 18 | thiserror.workspace = true 19 | secrecy.workspace = true 20 | log.workspace = true 21 | -------------------------------------------------------------------------------- /lib/flight/src/client.rs: -------------------------------------------------------------------------------- 1 | use polars::prelude::PolarsError; 2 | use query_processing::errors::QueryProcessingError; 3 | use representation::solution_mapping::SolutionMappings; 4 | use secrecy::SecretString; 5 | use std::collections::HashMap; 6 | use thiserror::*; 7 | use tonic::Status; 8 | 9 | #[derive(Error, Debug)] 10 | pub enum ChrontextFlightClientError { 11 | #[error("Cannot create endpoint `{0}`")] 12 | IpcError(String), 13 | #[error(transparent)] 14 | QueryExecutionError(Status), 15 | #[error(transparent)] 16 | PolarsDeserializationError(PolarsError), 17 | #[error(transparent)] 18 | UnionError(QueryProcessingError), 19 | #[error(transparent)] 20 | ConnectError(tonic::transport::Error), 21 | } 22 | 23 | #[derive(Clone)] 24 | #[allow(dead_code)] 25 | pub struct ChrontextFlightClient { 26 | uri: String, 27 | } 28 | 29 | impl ChrontextFlightClient { 30 | pub fn new(uri: &str) -> ChrontextFlightClient { 31 | Self { 32 | uri: uri.to_string(), 33 | } 34 | } 35 | 36 | pub async fn query( 37 | &mut self, 38 | _query: &str, 39 | _metadata: &HashMap, 40 | ) -> Result { 41 | unimplemented!("Contact Data Treehouse to try") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/flight/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod client; 2 | pub mod server; 3 | -------------------------------------------------------------------------------- /lib/flight/src/server.rs: -------------------------------------------------------------------------------- 1 | use chrontext::engine::Engine; 2 | use std::net::AddrParseError; 3 | use std::sync::Arc; 4 | use thiserror::*; 5 | 6 | #[derive(Error, Debug)] 7 | pub enum ChrontextFlightServerError { 8 | #[error(transparent)] 9 | TonicTransportError(tonic::transport::Error), 10 | #[error(transparent)] 11 | AddrParseError(AddrParseError), 12 | } 13 | 14 | #[derive(Clone)] 15 | #[allow(dead_code)] 16 | pub struct ChrontextFlightService { 17 | engine: Option>, 18 | } 19 | 20 | #[allow(dead_code)] 21 | pub struct ChrontextFlightServer { 22 | chrontext_flight_service: ChrontextFlightService, 23 | } 24 | 25 | impl ChrontextFlightServer { 26 | pub fn new(engine: Option>) -> Self { 27 | Self { 28 | chrontext_flight_service: ChrontextFlightService { engine }, 29 | } 30 | } 31 | 32 | pub async fn serve(self, _addr: &str) -> Result<(), ChrontextFlightServerError> { 33 | unimplemented!("Contact Data Treehouse to try") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/postgres/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "postgres" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | chrontext = { path = "../chrontext" } 8 | representation.workspace = true 9 | spargebra.workspace = true 10 | thiserror.workspace = true 11 | -------------------------------------------------------------------------------- /lib/postgres/src/catalog.rs: -------------------------------------------------------------------------------- 1 | use representation::BaseRDFNodeType; 2 | use spargebra::{Query, SparqlSyntaxError}; 3 | use std::collections::HashMap; 4 | 5 | pub struct Catalog { 6 | pub data_products: HashMap, 7 | } 8 | pub struct DataProduct { 9 | pub query_string: String, 10 | pub parsed_query: Option, 11 | pub rdf_node_types: HashMap, 12 | } 13 | 14 | impl DataProduct { 15 | pub fn init(&mut self) -> Result<(), SparqlSyntaxError> { 16 | self.parsed_query = Some(Query::parse(&self.query_string, None)?); 17 | Ok(()) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/postgres/src/config.rs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/GreptimeTeam/greptimedb/blob/05751084e7bbfc5e646df7f51bb7c3e5cbf16d58/src/session/src/session_config.rs 2 | // and https://github.com/GreptimeTeam/greptimedb/blob/05751084e7bbfc5e646df7f51bb7c3e5cbf16d58/src/servers/src/postgres/types/datetime.rs 3 | // 4 | // Copyright 2023 Greptime Team 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | use std::fmt::Display; 19 | 20 | // Refers to: https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-DATESTYLE 21 | #[derive(Default, PartialEq, Eq, Clone, Copy, Debug)] 22 | pub enum PGDateOrder { 23 | #[default] 24 | MDY, 25 | DMY, 26 | YMD, 27 | } 28 | 29 | impl Display for PGDateOrder { 30 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 31 | match self { 32 | PGDateOrder::MDY => write!(f, "MDY"), 33 | PGDateOrder::DMY => write!(f, "DMY"), 34 | PGDateOrder::YMD => write!(f, "YMD"), 35 | } 36 | } 37 | } 38 | 39 | #[derive(Default, PartialEq, Eq, Clone, Copy, Debug)] 40 | pub enum PGDateTimeStyle { 41 | #[default] 42 | ISO, 43 | SQL, 44 | Postgres, 45 | German, 46 | } 47 | 48 | impl Display for PGDateTimeStyle { 49 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 50 | match self { 51 | PGDateTimeStyle::ISO => write!(f, "ISO"), 52 | PGDateTimeStyle::SQL => write!(f, "SQL"), 53 | PGDateTimeStyle::Postgres => write!(f, "Postgres"), 54 | PGDateTimeStyle::German => write!(f, "German"), 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/postgres/src/errors.rs: -------------------------------------------------------------------------------- 1 | use chrontext::errors::ChrontextError; 2 | use thiserror::Error; 3 | #[derive(Debug, Error)] 4 | pub enum ChrontextPGWireError { 5 | #[error(transparent)] 6 | ChrontextError(#[from] ChrontextError), 7 | } 8 | -------------------------------------------------------------------------------- /lib/postgres/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod catalog; 2 | pub mod config; 3 | pub mod errors; 4 | pub mod server; 5 | -------------------------------------------------------------------------------- /lib/postgres/src/server.rs: -------------------------------------------------------------------------------- 1 | //Based on https://github.com/sunng87/pgwire/blob/master/examples/duckdb.rs 2 | //License found in licenses PGWIRE-LICENSE-* 3 | 4 | use crate::catalog::Catalog; 5 | use crate::config::{PGDateOrder, PGDateTimeStyle}; 6 | use crate::errors::ChrontextPGWireError; 7 | use chrontext::engine::Engine; 8 | 9 | pub async fn start_server( 10 | _engine: Engine, 11 | _config: Config, 12 | _catalog: Catalog, 13 | ) -> Result<(), ChrontextPGWireError> { 14 | unimplemented!("Contact Data Treehouse to try") 15 | } 16 | 17 | #[derive(Clone, Default)] 18 | #[allow(dead_code)] 19 | pub struct Config { 20 | pub(crate) pg_date_time_style: PGDateTimeStyle, 21 | pub(crate) pg_date_order: PGDateOrder, 22 | } 23 | -------------------------------------------------------------------------------- /lib/sparql_database/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sparql_database" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | representation.workspace = true 8 | spargebra.workspace = true 9 | thiserror.workspace = true 10 | 11 | reqwest.workspace = true 12 | sparesults.workspace = true 13 | async-trait.workspace = true 14 | pyo3.workspace = true -------------------------------------------------------------------------------- /lib/sparql_database/src/embedded_oxigraph.rs: -------------------------------------------------------------------------------- 1 | use super::{parse_json_text, SparqlQueryError, SparqlQueryable}; 2 | use async_trait::async_trait; 3 | use pyo3::types::{PyAnyMethods, PyNone}; 4 | use pyo3::{Py, PyAny, Python}; 5 | use sparesults::QuerySolution; 6 | use spargebra::Query; 7 | use thiserror::Error; 8 | #[derive(Debug, Error)] 9 | pub enum EmbeddedOxigraphError { 10 | #[error("Oxigraph evaluation error")] 11 | EvaluationError(String), 12 | } 13 | 14 | pub struct EmbeddedOxigraph { 15 | pub store: Py, 16 | } 17 | 18 | #[async_trait] 19 | impl SparqlQueryable for EmbeddedOxigraph { 20 | async fn execute(&self, query: &Query) -> Result, SparqlQueryError> { 21 | Python::with_gil(|py| { 22 | let json_format = py 23 | .import("pyoxigraph") 24 | .unwrap() 25 | .getattr("QueryResultsFormat") 26 | .unwrap() 27 | .getattr("JSON") 28 | .unwrap(); 29 | let json = self 30 | .store 31 | .call_method1(py, "query", (query.to_string(),)) 32 | .unwrap() 33 | .call_method1(py, "serialize", (PyNone::get(py), json_format)) 34 | .unwrap(); 35 | let json_bytes: Vec = json.extract(py).unwrap(); 36 | let json_string = String::from_utf8(json_bytes).unwrap(); 37 | parse_json_text(&json_string) 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/sparql_database/src/endpoint.rs: -------------------------------------------------------------------------------- 1 | use super::{parse_json_text, SparqlQueryError, SparqlQueryable}; 2 | use async_trait::async_trait; 3 | use reqwest::header::{ACCEPT, USER_AGENT}; 4 | use sparesults::{QueryResultsSyntaxError, QuerySolution}; 5 | use spargebra::Query; 6 | use thiserror::Error; 7 | 8 | #[derive(Debug, Error)] 9 | pub enum SparqlEndpointQueryExecutionError { 10 | #[error(transparent)] 11 | RequestError(reqwest::Error), 12 | #[error("Bad status code `{0}`")] 13 | BadStatusCode(String), 14 | #[error("Results parse error `{0}`")] 15 | ResultsParseError(QueryResultsSyntaxError), 16 | #[error("Solution parse error `{0}`")] 17 | SolutionParseError(QueryResultsSyntaxError), 18 | #[error("Wrong result type, expected solutions")] 19 | WrongResultType, 20 | } 21 | 22 | pub struct SparqlEndpoint { 23 | pub endpoint: String, 24 | } 25 | #[async_trait] 26 | impl SparqlQueryable for SparqlEndpoint { 27 | async fn execute(&self, query: &Query) -> Result, SparqlQueryError> { 28 | let client = reqwest::Client::new(); 29 | let response = client 30 | .get(&self.endpoint) 31 | .header(ACCEPT, "application/sparql-results+json,application/json,text/javascript,application/javascript") 32 | .header(USER_AGENT, "chrontext") 33 | .query(&[("query",query.to_string())]) 34 | .query(&[("format", "json"), ("output", "json"), ("results", "json")]) 35 | .send() 36 | .await; 37 | match response { 38 | Ok(proper_response) => { 39 | if proper_response.status().as_u16() != 200 { 40 | Err(SparqlEndpointQueryExecutionError::BadStatusCode( 41 | proper_response.status().to_string(), 42 | ) 43 | .into()) 44 | } else { 45 | parse_json_text(&proper_response.text().await.expect("Read text error")) 46 | } 47 | } 48 | Err(error) => Err(SparqlEndpointQueryExecutionError::RequestError(error).into()), 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/sparql_database/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod embedded_oxigraph; 2 | pub mod endpoint; 3 | 4 | use async_trait::async_trait; 5 | use embedded_oxigraph::EmbeddedOxigraphError; 6 | use endpoint::SparqlEndpointQueryExecutionError; 7 | use sparesults::{ 8 | QueryResultsFormat, QueryResultsParser, QuerySolution, SliceQueryResultsParserOutput, 9 | }; 10 | use spargebra::Query; 11 | use thiserror::Error; 12 | #[derive(Debug, Error)] 13 | pub enum SparqlQueryError { 14 | #[error(transparent)] 15 | EmbeddedOxigraphError(#[from] EmbeddedOxigraphError), 16 | #[error(transparent)] 17 | SparqlEndpointQueryExecutionError(#[from] SparqlEndpointQueryExecutionError), 18 | } 19 | 20 | #[async_trait] 21 | pub trait SparqlQueryable: Send + Sync { 22 | async fn execute(&self, query: &Query) -> Result, SparqlQueryError>; 23 | } 24 | 25 | fn parse_json_text(text: &str) -> Result, SparqlQueryError> { 26 | let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Json); 27 | let parsed_results = json_parser.for_slice(text.as_bytes()); 28 | match parsed_results { 29 | Ok(reader) => { 30 | let mut solns = vec![]; 31 | if let SliceQueryResultsParserOutput::Solutions(solutions) = reader { 32 | for s in solutions { 33 | match s { 34 | Ok(query_solution) => solns.push(query_solution), 35 | Err(syntax_error) => { 36 | return Err(SparqlEndpointQueryExecutionError::SolutionParseError( 37 | syntax_error, 38 | ) 39 | .into()) 40 | } 41 | } 42 | } 43 | Ok(solns) 44 | } else { 45 | Err(SparqlEndpointQueryExecutionError::WrongResultType.into()) 46 | } 47 | } 48 | Err(parse_error) => { 49 | Err(SparqlEndpointQueryExecutionError::ResultsParseError(parse_error).into()) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/virtualization/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virtualization" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | virtualized_query = { path="../virtualized_query" } 8 | bigquery-polars = {path = "../bigquery-polars"} 9 | 10 | templates.workspace = true 11 | pydf_io.workspace = true 12 | representation.workspace=true 13 | gcp-bigquery-client.workspace=true 14 | polars = {workspace=true, features=[ 15 | "lazy", 16 | "dtype-full", 17 | "nightly", 18 | "performant"] } 19 | oxrdf.workspace = true 20 | spargebra.workspace=true 21 | reqwest = { workspace = true, features = ["stream"] } 22 | thiserror.workspace = true 23 | serde_json.workspace = true 24 | pyo3.workspace = true 25 | rustls.workspace = true 26 | url.workspace = true -------------------------------------------------------------------------------- /lib/virtualization/src/errors.rs: -------------------------------------------------------------------------------- 1 | use bigquery_polars::errors::BigQueryExecutorError; 2 | use gcp_bigquery_client::error::BQError; 3 | use polars::prelude::PolarsError; 4 | use pyo3::PyErr; 5 | use thiserror::*; 6 | use url::ParseError; 7 | 8 | #[derive(Error, Debug)] 9 | pub enum ChrontextError { 10 | #[error(transparent)] 11 | PyVirtualizedDatabaseError(#[from] PyErr), 12 | #[error(transparent)] 13 | PolarsError(#[from] PolarsError), 14 | #[error("Problem creating dataframe from arrow: `{0}`")] 15 | TranslationError(String), 16 | #[error("Invalid node id `{0}`")] 17 | InvalidNodeIdError(String), 18 | #[error("Only grouped and basic query types are supported")] 19 | VirtualizedQueryTypeNotSupported, 20 | #[error(transparent)] 21 | ReadFileError(#[from] std::io::Error), 22 | #[error(transparent)] 23 | ReadJSONError(#[from] serde_json::Error), 24 | #[error(transparent)] 25 | BigQueryExecutorError(#[from] BigQueryExecutorError), 26 | #[error(transparent)] 27 | BigQueryKeyPathParseError(#[from] ParseError), 28 | #[error(transparent)] 29 | BigQueryError(#[from] BQError), 30 | } 31 | -------------------------------------------------------------------------------- /lib/virtualization/src/python.rs: -------------------------------------------------------------------------------- 1 | mod sql_translation; 2 | 3 | use polars::prelude::DataFrame; 4 | use pydf_io::to_rust::polars_df_to_rust_df; 5 | use pyo3::ffi::c_str; 6 | use pyo3::prelude::*; 7 | use pyo3::types::PyDict; 8 | use sql_translation::PYTHON_CODE; 9 | use std::collections::HashSet; 10 | use std::ffi::CString; 11 | use virtualized_query::pushdown_setting::{all_pushdowns, PushdownSetting}; 12 | use virtualized_query::python::PyVirtualizedQuery; 13 | use virtualized_query::VirtualizedQuery; 14 | 15 | #[derive(Clone, Debug)] 16 | #[pyclass] 17 | pub struct VirtualizedPythonDatabase { 18 | pub database: Py, 19 | pub resource_sql_map: Option>, 20 | pub sql_dialect: Option, 21 | } 22 | 23 | #[pymethods] 24 | impl VirtualizedPythonDatabase { 25 | #[new] 26 | #[pyo3(signature = (database, resource_sql_map=None, sql_dialect=None))] 27 | pub fn new( 28 | database: Py, 29 | resource_sql_map: Option>, 30 | sql_dialect: Option, 31 | ) -> VirtualizedPythonDatabase { 32 | VirtualizedPythonDatabase { 33 | database, 34 | resource_sql_map, 35 | sql_dialect, 36 | } 37 | } 38 | } 39 | 40 | impl VirtualizedPythonDatabase { 41 | pub fn pushdown_settings(&self) -> HashSet { 42 | all_pushdowns() 43 | } 44 | 45 | pub fn query(&self, vq: &VirtualizedQuery) -> PyResult { 46 | Python::with_gil(|py| { 47 | let py_df = if let Some(resource_sql_map) = &self.resource_sql_map { 48 | let s = translate_sql( 49 | vq, 50 | resource_sql_map, 51 | self.sql_dialect.as_ref().unwrap().as_str(), 52 | )?; 53 | let query_func = self.database.getattr(py, "query")?; 54 | query_func.call1(py, (s,))? 55 | } else { 56 | let pyvq = PyVirtualizedQuery::new(vq.clone(), py)?; 57 | let query_func = self.database.getattr(py, "query")?; 58 | query_func.call1(py, (pyvq,))? 59 | }; 60 | polars_df_to_rust_df(&py_df.into_bound(py)) 61 | }) 62 | } 63 | } 64 | 65 | pub fn translate_sql( 66 | vq: &VirtualizedQuery, 67 | resource_sql_map: &Py, 68 | dialect: &str, 69 | ) -> PyResult { 70 | Python::with_gil(|py| { 71 | let pyvq = PyVirtualizedQuery::new(vq.clone(), py)?; 72 | let db_mod = PyModule::from_code( 73 | py, 74 | CString::new(PYTHON_CODE).unwrap().as_c_str(), 75 | c_str!("my_translator"), 76 | c_str!("my_translator"), 77 | )?; 78 | let translate_sql_func = db_mod.getattr("translate_sql")?; 79 | let query_string = translate_sql_func.call((pyvq, dialect, resource_sql_map), None)?; 80 | query_string.extract::() 81 | }) 82 | } 83 | -------------------------------------------------------------------------------- /lib/virtualized_query/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virtualized_query" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | representation.workspace = true 8 | query_processing.workspace = true 9 | templates.workspace = true 10 | 11 | polars = {workspace=true, features=[ 12 | "lazy", 13 | "dtype-full", 14 | "nightly", 15 | "performant"] } 16 | spargebra.workspace = true 17 | serde.workspace = true 18 | pyo3 = {workspace = true } -------------------------------------------------------------------------------- /lib/virtualized_query/src/pushdown_setting.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::collections::HashSet; 3 | 4 | pub fn all_pushdowns() -> HashSet { 5 | [ 6 | PushdownSetting::GroupBy, 7 | PushdownSetting::ValueConditions, 8 | PushdownSetting::Ordering, 9 | ] 10 | .into() 11 | } 12 | 13 | #[derive(Hash, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] 14 | pub enum PushdownSetting { 15 | ValueConditions, 16 | GroupBy, 17 | Ordering, 18 | } 19 | -------------------------------------------------------------------------------- /licensing/POLARS_LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Ritchie Vink 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /py_chrontext/.gitignore: -------------------------------------------------------------------------------- 1 | tests/solar.zip 2 | pki 3 | # Generated by Cargo 4 | # will have compiled files and executables 5 | /target/ 6 | 7 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 8 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 9 | Cargo.lock 10 | 11 | # These are backup files generated by rustfmt 12 | **/*.rs.bk 13 | -------------------------------------------------------------------------------- /py_chrontext/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "py_chrontext" 3 | version = "0.9.16" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | postgres = { path = "../lib/postgres" } 8 | virtualized_query = {path="../lib/virtualized_query"} 9 | chrontext = { path = "../lib/chrontext" } 10 | virtualization = {path="../lib/virtualization"} 11 | flight = {path="../lib/flight"} 12 | sparql_database = {path="../lib/sparql_database"} 13 | 14 | representation.workspace = true 15 | templates.workspace = true 16 | pydf_io.workspace = true 17 | 18 | pyo3 = { workspace=true, features = ["extension-module"] } 19 | thiserror.workspace = true 20 | oxrdf.workspace = true 21 | tokio.workspace = true 22 | log.workspace = true 23 | env_logger.workspace = true 24 | spargebra.workspace = true 25 | secrecy.workspace = true 26 | 27 | [lib] 28 | name = "chrontext" 29 | crate-type = ["cdylib"] 30 | 31 | # Conditional dependency specification of mimalloc and jemallocator copied from: https://github.com/pola-rs/polars/blob/main/py-polars/Cargo.toml 32 | # It has the following license 33 | # Copyright (c) 2020 Ritchie Vink 34 | # 35 | #Permission is hereby granted, free of charge, to any person obtaining a copy 36 | #of this software and associated documentation files (the "Software"), to deal 37 | #in the Software without restriction, including without limitation the rights 38 | #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 39 | #copies of the Software, and to permit persons to whom the Software is 40 | #furnished to do so, subject to the following conditions: 41 | # 42 | #The above copyright notice and this permission notice shall be included in all 43 | #copies or substantial portions of the Software. 44 | # 45 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 46 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 47 | #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 48 | #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 49 | #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 50 | #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 51 | #SOFTWARE. 52 | [target.'cfg(not(target_os = "linux"))'.dependencies] 53 | mimalloc = { version = "0.1.37", default-features = false } 54 | 55 | [target.'cfg(target_os = "linux")'.dependencies] 56 | jemallocator = { version = "0.5.4", features = ["disable_initial_exec_tls"] } 57 | 58 | [lints.rust] 59 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(feature, values("gil-refs", "rdf-star"))'] } -------------------------------------------------------------------------------- /py_chrontext/README.md: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /py_chrontext/chrontext/.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | __pycache__ -------------------------------------------------------------------------------- /py_chrontext/chrontext/__init__.py: -------------------------------------------------------------------------------- 1 | from .chrontext import * -------------------------------------------------------------------------------- /py_chrontext/chrontext/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataTreehouse/chrontext/372f343e9a16d3cef152cd332a71f3d3f8e8a305/py_chrontext/chrontext/py.typed -------------------------------------------------------------------------------- /py_chrontext/chrontext/vq/__init__.py: -------------------------------------------------------------------------------- 1 | from .vq import * -------------------------------------------------------------------------------- /py_chrontext/chrontext/vq/__init__.pyi: -------------------------------------------------------------------------------- 1 | from typing import Optional, List, Type, Tuple, Dict, Literal as LiteralType 2 | 3 | from chrontext import Variable, Literal, IRI 4 | 5 | 6 | class Expression: 7 | And:Type["PyExpression__And"] 8 | Greater:Type["PyExpression__Greater"] 9 | Less:Type["PyExpression__Less"] 10 | left:Optional[Expression] 11 | right:Optional[Expression] 12 | Variable:Type["PyExpression__Variable"] 13 | variable:Optional[Variable] 14 | IRI:Type["PyExpression__IRI"] 15 | Literal:Type["PyExpression__Literal"] 16 | literal:Optional[Literal] 17 | FunctionCall:["PyExpression__FunctionCall"] 18 | function: Optional[str] 19 | arguments: Optional[List[Expression]] 20 | 21 | def expression_type(self) -> str: 22 | """ 23 | Workaround for pyo3 issue with constructing enums. 24 | :return: 25 | """ 26 | 27 | class XSDDuration: 28 | years: int 29 | months: int 30 | days: int 31 | hours: int 32 | minutes: int 33 | seconds: (int,int) 34 | """ 35 | 36 | """ 37 | 38 | class PyExpression__And(Expression): 39 | left:Expression 40 | right:Expression 41 | 42 | class PyExpression__Variable(Expression): 43 | variable:Variable 44 | 45 | class PyExpression__IRI(Expression): 46 | iri:IRI 47 | 48 | class PyExpression__Literal(Expression): 49 | literal:Literal 50 | 51 | class PyExpression__Greater(Expression): 52 | left:Expression 53 | right:Expression 54 | 55 | class PyExpression__Less(Expression): 56 | left:Expression 57 | right:Expression 58 | 59 | class PyExpression__FunctionCall(Expression): 60 | function: str 61 | arguments: List[Expression] 62 | 63 | 64 | class AggregateExpression: 65 | name: str 66 | expression: Expression 67 | separator: Optional[str] 68 | """ 69 | 70 | """ 71 | 72 | 73 | class VirtualizedQuery: 74 | Filtered:Type["PyVirtualizedQuery__Filtered"] 75 | filter: Optional[Expression] 76 | query: Optional[VirtualizedQuery] 77 | Basic:Type["PyVirtualizedQuery__Basic"] 78 | identifier_name: Optional[str] 79 | column_mapping: Optional[Dict[str, str]] 80 | resource: Optional[str] 81 | ids: Optional[List[str]] 82 | grouping_column_name: Optional[str] 83 | id_grouping_tuples: Optional[List[Tuple[str, int]]] 84 | Grouped:Type["PyVirtualizedQuery__Grouped"] 85 | by: List[Variable] 86 | aggregations: Optional[List[Tuple[Variable, AggregateExpression]]] 87 | ExpressionAs:Type["PyVirtualizedQuery__ExpressionAs"] 88 | variable: Optional[Variable] 89 | expression: Optional[Expression] 90 | 91 | def type_name(self) -> LiteralType["Filtered", "Basic"]: 92 | """ 93 | 94 | :return: 95 | """ 96 | 97 | 98 | class PyVirtualizedQuery__Basic: 99 | identifier_name: str 100 | column_mapping: Dict[str, str] 101 | resource: str 102 | ids: List[str] 103 | grouping_column_name: Optional[str] 104 | id_grouping_tuples: Optional[List[Tuple[str, int]]] 105 | 106 | """ 107 | Basic Virtualized Query 108 | """ 109 | 110 | class PyVirtualizedQuery__Filtered: 111 | query: VirtualizedQuery 112 | filter: Expression 113 | 114 | """ 115 | Filtered Virtualized Query 116 | """ 117 | 118 | 119 | class PyVirtualizedQuery__Grouped: 120 | by: List[Variable] 121 | aggregations: List[Tuple[Variable, AggregateExpression]] 122 | """ 123 | Grouped Virtualized Query 124 | """ 125 | 126 | 127 | class PyVirtualizedQuery__ExpressionAs: 128 | query: VirtualizedQuery 129 | variable: Variable 130 | expression: Expression 131 | """ 132 | ExpressionAs Virtualized Query 133 | """ 134 | -------------------------------------------------------------------------------- /py_chrontext/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "chrontext" 3 | description = "Hybrid SPARQL query engine for timeseries data" 4 | dependencies = ["polars>=0.20.2", 5 | "pyarrow>=7.0.0", 6 | "pandas", 7 | "sqlalchemy>=2.0.31", 8 | "sqlalchemy-bigquery>=1.11.0", 9 | "databricks-sqlalchemy>=2.0.4", 10 | "databricks-sql-connector>=3.3.0", 11 | "pyoxigraph>=0.4.2"] 12 | readme = "README.md" 13 | authors = [{ name = "Magnus Bakken", email = "magnus@data-treehouse.com" }] 14 | license = { file = "LICENSE" } 15 | requires-python = ">=3.9" 16 | keywords = ["rdf", "graph", "arrow", "sparql", "timeseries"] 17 | classifiers = [ 18 | "Development Status :: 4 - Beta", 19 | "License :: OSI Approved :: Apache Software License", 20 | "Programming Language :: Python :: 3 :: Only", 21 | "Programming Language :: Python :: 3.9", 22 | "Programming Language :: Python :: 3.10", 23 | "Programming Language :: Python :: 3.11", 24 | "Programming Language :: Rust", 25 | "Topic :: Database :: Database Engines/Servers", 26 | "Topic :: Scientific/Engineering", 27 | ] 28 | 29 | [project.urls] 30 | Homepage = "https://github.com/DataTreehouse/chrontext" 31 | Documentation = "https://datatreehouse.github.io/chrontext/chrontext/chrontext.html" 32 | Repository = "https://github.com/DataTreehouse/chrontext" 33 | Changelog = "https://github.com/DataTreehouse/chrontext/releases" 34 | 35 | [build-system] 36 | requires = ["maturin==1.7.4"] 37 | build-backend = "maturin" -------------------------------------------------------------------------------- /py_chrontext/src/errors.rs: -------------------------------------------------------------------------------- 1 | use chrontext::errors::ChrontextError as RustChrontextError; 2 | use flight::client::ChrontextFlightClientError; 3 | use flight::server::ChrontextFlightServerError; 4 | use oxrdf::IriParseError; 5 | use pyo3::{create_exception, exceptions::PyException, prelude::*}; 6 | use spargebra::SparqlSyntaxError; 7 | use thiserror::Error; 8 | 9 | #[derive(Error, Debug)] 10 | pub enum PyChrontextError { 11 | #[error(transparent)] 12 | DatatypeIRIParseError(#[from] IriParseError), 13 | #[error(transparent)] 14 | DataProductQueryParseError(#[from] SparqlSyntaxError), 15 | #[error("Missing SPARQL database")] 16 | MissingSPARQLDatabaseError, 17 | #[error("SPARQL database defined multiple times")] 18 | MultipleSPARQLDatabasesError, 19 | #[error(transparent)] 20 | ChrontextError(RustChrontextError), 21 | #[error("Missing virtualized database")] 22 | MissingVirtualizedDatabaseError, 23 | #[error("Virtualized database defined multiple times")] 24 | MultipleVirtualizedDatabasesError, 25 | #[error(transparent)] 26 | FlightClientError(ChrontextFlightClientError), 27 | #[error(transparent)] 28 | FlightServerError(ChrontextFlightServerError), 29 | } 30 | 31 | impl std::convert::From for PyErr { 32 | fn from(pqe: PyChrontextError) -> Self { 33 | match pqe { 34 | PyChrontextError::ChrontextError(e) => ChrontextError::new_err(format!("{}", e)), 35 | PyChrontextError::DatatypeIRIParseError(err) => { 36 | DatatypeIRIParseError::new_err(format!("{}", err)) 37 | } 38 | PyChrontextError::MissingSPARQLDatabaseError => MissingSPARQLDatabaseError::new_err(""), 39 | PyChrontextError::MultipleSPARQLDatabasesError => { 40 | MultipleSPARQLDatabasesError::new_err("") 41 | } 42 | PyChrontextError::DataProductQueryParseError(e) => { 43 | DataProductQueryParseError::new_err(format!("{}", e)) 44 | } 45 | PyChrontextError::MissingVirtualizedDatabaseError => { 46 | MissingVirtualizedDatabaseError::new_err("") 47 | } 48 | PyChrontextError::MultipleVirtualizedDatabasesError => { 49 | MultipleVirtualizedDatabasesError::new_err("") 50 | } 51 | PyChrontextError::FlightClientError(x) => FlightClientError::new_err(x.to_string()), 52 | PyChrontextError::FlightServerError(x) => FlightServerError::new_err(x.to_string()), 53 | } 54 | } 55 | } 56 | 57 | create_exception!(exceptions, DatatypeIRIParseError, PyException); 58 | create_exception!(exceptions, DataProductQueryParseError, PyException); 59 | create_exception!(exceptions, MissingSPARQLDatabaseError, PyException); 60 | create_exception!(exceptions, MultipleSPARQLDatabasesError, PyException); 61 | create_exception!(exceptions, MissingVirtualizedDatabaseError, PyException); 62 | create_exception!(exceptions, MultipleVirtualizedDatabasesError, PyException); 63 | create_exception!(exceptions, FlightClientError, PyException); 64 | create_exception!(exceptions, FlightServerError, PyException); 65 | create_exception!(exceptions, ChrontextError, PyException); 66 | -------------------------------------------------------------------------------- /py_chrontext/tests/.gitignore: -------------------------------------------------------------------------------- 1 | history.db 2 | __pycache__ 3 | pki 4 | bq.env 5 | solar.nt 6 | oxigraph_db 7 | oxigraph_db_bq 8 | oxigraph_db_tutorial -------------------------------------------------------------------------------- /py_chrontext/tests/FIX_OPENSSL_CONDA: -------------------------------------------------------------------------------- 1 | conda install -c conda-forge libstdcxx-ng -------------------------------------------------------------------------------- /py_chrontext/tests/conftest.py: -------------------------------------------------------------------------------- 1 | # Adapted from https://github.com/yehoshuadimarsky/bcpandas/blob/master/bcpandas/tests/conftest.py and utils.py 2 | # License: 3 | # 4 | # MIT License 5 | # 6 | # Copyright (c) 2019-2020 yehoshuadimarsky 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in all 16 | # copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | import time 26 | 27 | import docker 28 | import pytest 29 | import pathlib 30 | 31 | PATH_HERE = pathlib.Path(__file__).parent 32 | print(PATH_HERE) 33 | OXIGRAPH_SERVER_IMAGE = "oxigraph/oxigraph:v0.3.8" 34 | OXIGRAPH_CONTAINER_NAME ="my-oxigraph-server" 35 | 36 | 37 | @pytest.fixture(scope="module") 38 | def oxigraph_db(): 39 | client = docker.from_env() 40 | try: 41 | existing = client.containers.get(OXIGRAPH_CONTAINER_NAME) 42 | existing.stop() 43 | existing.remove() 44 | except: 45 | pass 46 | 47 | container = client.containers.run( 48 | image=OXIGRAPH_SERVER_IMAGE, 49 | name=OXIGRAPH_CONTAINER_NAME, 50 | detach=True, 51 | ports={"7878/tcp": "7878/tcp"}, 52 | command=[ 53 | "--location", 54 | "/data", 55 | "serve", 56 | "--bind", 57 | "0.0.0.0:7878", 58 | ] 59 | ) 60 | time.sleep(20) 61 | yield 62 | print("Stopping container") 63 | container.stop() 64 | print("Deleting container") 65 | container.remove() 66 | print("all done!") -------------------------------------------------------------------------------- /py_chrontext/tests/my_graph.ttl: -------------------------------------------------------------------------------- 1 | PREFIX case: 2 | PREFIX types: 3 | PREFIX chrontext: 4 | PREFIX xsd: 5 | 6 | case:myWidget1 types:hasSensor case:mySensor1 . 7 | case:myWidget1 types:hasSomething case:mySomething1 . 8 | case:myWidget2 types:hasSensor case:mySensor2 . 9 | case:myWidget1 a types:BigWidget . 10 | case:myWidget2 a types:SmallWidget . 11 | case:mySensor1 chrontext:hasTimeseries case:myTimeseries1 . 12 | case:mySensor2 chrontext:hasTimeseries case:myTimeseries2 . 13 | case:mySensor1 a types:ThingCounter . 14 | case:mySensor2 a types:ThingCounter . 15 | case:myTimeseries1 chrontext:hasResource "my_resource" . 16 | case:myTimeseries2 chrontext:hasResource "my_resource" . 17 | case:myTimeseries1 chrontext:hasExternalId "ts1" . 18 | case:myTimeseries2 chrontext:hasExternalId "ts2" . -------------------------------------------------------------------------------- /py_chrontext/tests/requirements.txt: -------------------------------------------------------------------------------- 1 | python-dotenv>=1.0.0 2 | pytest>=7.4.2 3 | pytest-order>=1.2.0 4 | docker>=7.1.0 5 | requests>=2.32.0 6 | sparqlwrapper>=2.0.0 7 | asyncua>=1.0.4 8 | duckdb>=1.0.0 -------------------------------------------------------------------------------- /py_chrontext/tests/test_databricks.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import polars as pl 3 | import pathlib 4 | 5 | from pyoxigraph import Store 6 | 7 | from sqlalchemy import Column, Table, MetaData 8 | 9 | from chrontext import VirtualizedPythonDatabase, Engine, Template, Prefix, Variable, Parameter, \ 10 | RDFType, XSD, Triple 11 | 12 | PATH_HERE = pathlib.Path(__file__).parent 13 | TESTDATA_PATH = PATH_HERE / "testdata" / "python_based" 14 | TS1_CSV = str(TESTDATA_PATH / "ts1.csv") 15 | TS2_CSV = str(TESTDATA_PATH / "ts2.csv") 16 | 17 | 18 | class DatabricksDB(): 19 | def __init__(self): 20 | pass 21 | 22 | def query(self, sql: str): 23 | self.last_query = sql 24 | #print(sql) 25 | ts_1 = pl.read_csv(TS1_CSV, try_parse_dates=True).with_columns( 26 | pl.lit("ts1").alias("id"), 27 | pl.col("timestamp").dt.replace_time_zone("UTC") 28 | ) 29 | ts_2 = pl.read_csv(TS2_CSV, try_parse_dates=True).with_columns( 30 | pl.lit("ts2").alias("id"), 31 | pl.col("timestamp").dt.replace_time_zone("UTC") 32 | ) 33 | df = pl.concat([ts_1, ts_2]).rename( 34 | {"timestamp":"t", 35 | "value":"v", 36 | "id":"ts_external_id_0"} 37 | ) 38 | return df 39 | 40 | db = DatabricksDB() 41 | 42 | @pytest.fixture(scope="module") 43 | def engine() -> Engine: 44 | metadata = MetaData() 45 | table = Table( 46 | "ts", 47 | metadata, 48 | Column("id"), 49 | Column("timestamp"), 50 | Column("value") 51 | ) 52 | vdb = VirtualizedPythonDatabase( 53 | database=db, 54 | resource_sql_map={"my_resource": table}, 55 | sql_dialect="databricks" 56 | ) 57 | 58 | ct = Prefix("ct", "https://github.com/DataTreehouse/chrontext#") 59 | xsd = XSD() 60 | id = Variable("id") 61 | timestamp = Variable("timestamp") 62 | value = Variable("value") 63 | dp = Variable("dp") 64 | resources = { 65 | "my_resource": Template( 66 | iri=ct.suf("my_resource"), 67 | parameters=[ 68 | Parameter(id, rdf_type=RDFType.Literal(xsd.string)), 69 | Parameter(timestamp, rdf_type=RDFType.Literal(xsd.dateTime)), 70 | Parameter(value, rdf_type=RDFType.Literal(xsd.double)), 71 | ], 72 | instances=[ 73 | Triple(id, ct.suf("hasDataPoint"), dp), 74 | Triple(dp, ct.suf("hasValue"), value), 75 | Triple(dp, ct.suf("hasTimestamp"), timestamp) 76 | ] 77 | ) 78 | } 79 | oxigraph_store = Store() 80 | oxigraph_store.bulk_load(path=TESTDATA_PATH / "testdata.ttl") 81 | engine = Engine( 82 | resources, 83 | virtualized_python_database=vdb, 84 | sparql_embedded_oxigraph=oxigraph_store) 85 | engine.init() 86 | return engine 87 | 88 | 89 | @pytest.mark.order(1) 90 | def test_simple_hybrid(engine): 91 | q = """ 92 | PREFIX xsd: 93 | PREFIX chrontext: 94 | PREFIX types: 95 | SELECT ?w ?s ?t ?v WHERE { 96 | ?w a types:BigWidget . 97 | ?w types:hasSensor ?s . 98 | ?s chrontext:hasTimeseries ?ts . 99 | ?ts chrontext:hasDataPoint ?dp . 100 | ?dp chrontext:hasTimestamp ?t . 101 | ?dp chrontext:hasValue ?v . 102 | FILTER(?t > "2022-06-01T08:46:53Z"^^xsd:dateTime && ?v < 200) . 103 | } 104 | """ 105 | engine.query(q, include_datatypes=True) 106 | sql = db.last_query 107 | assert sql.startswith("""SELECT inner_0.id AS ts_external_id_0""") -------------------------------------------------------------------------------- /py_chrontext/tests/test_readme_tutorial.py: -------------------------------------------------------------------------------- 1 | def test_tutorial(): 2 | #First part: 3 | import duckdb 4 | import polars as pl 5 | from pyoxigraph import Store 6 | 7 | class MyDuckDB(): 8 | def __init__(self): 9 | con = duckdb.connect() 10 | con.execute("SET TIME ZONE 'UTC';") 11 | con.execute("""CREATE TABLE ts1 ("timestamp" TIMESTAMPTZ, "value" INTEGER)""") 12 | ts_1 = pl.read_csv("ts1.csv", try_parse_dates=True).with_columns(pl.col("timestamp").dt.replace_time_zone("UTC")) 13 | con.append("ts1", df=ts_1.to_pandas()) 14 | con.execute("""CREATE TABLE ts2 ("timestamp" TIMESTAMPTZ, "value" INTEGER)""") 15 | ts_2 = pl.read_csv("ts2.csv", try_parse_dates=True).with_columns(pl.col("timestamp").dt.replace_time_zone("UTC")) 16 | con.append("ts2", df=ts_2.to_pandas()) 17 | self.con = con 18 | 19 | def query(self, sql:str) -> pl.DataFrame: 20 | # We execute the query and return it as a Polars DataFrame. 21 | # Chrontext expects this method to exist in the provided class. 22 | df = self.con.execute(sql).pl() 23 | return df 24 | 25 | my_db = MyDuckDB() 26 | 27 | #Second part: 28 | from sqlalchemy import MetaData, Table, Column, bindparam 29 | metadata = MetaData() 30 | ts1_table = Table( 31 | "ts1", 32 | metadata, 33 | Column("timestamp"), 34 | Column("value") 35 | ) 36 | ts2_table = Table( 37 | "ts2", 38 | metadata, 39 | Column("timestamp"), 40 | Column("value") 41 | ) 42 | ts1 = ts1_table.select().add_columns( 43 | bindparam("id1", "ts1").label("id"), 44 | ) 45 | ts2 = ts2_table.select().add_columns( 46 | bindparam("id2", "ts2").label("id"), 47 | ) 48 | sql = ts1.union(ts2) 49 | 50 | #Third part 51 | from chrontext import VirtualizedPythonDatabase 52 | 53 | vdb = VirtualizedPythonDatabase( 54 | database=my_db, 55 | resource_sql_map={"my_resource": sql}, 56 | sql_dialect="postgres" 57 | ) 58 | 59 | #Fourth part 60 | from chrontext import Prefix, Variable, Template, Parameter, RDFType, Triple, XSD 61 | ct = Prefix("ct", "https://github.com/DataTreehouse/chrontext#") 62 | xsd = XSD() 63 | id = Variable("id") 64 | timestamp = Variable("timestamp") 65 | value = Variable("value") 66 | dp = Variable("dp") 67 | resources = { 68 | "my_resource": Template( 69 | iri=ct.suf("my_resource"), 70 | parameters=[ 71 | Parameter(id, rdf_type=RDFType.Literal(xsd.string)), 72 | Parameter(timestamp, rdf_type=RDFType.Literal(xsd.dateTime)), 73 | Parameter(value, rdf_type=RDFType.Literal(xsd.double)), 74 | ], 75 | instances=[ 76 | Triple(id, ct.suf("hasDataPoint"), dp), 77 | Triple(dp, ct.suf("hasValue"), value), 78 | Triple(dp, ct.suf("hasTimestamp"), timestamp) 79 | ] 80 | )} 81 | 82 | #Fifth part 83 | from chrontext import Engine 84 | oxigraph_store = Store() 85 | oxigraph_store.bulk_load(path="my_graph.ttl") 86 | engine = Engine( 87 | resources, 88 | virtualized_python_database=vdb, 89 | sparql_embedded_oxigraph=oxigraph_store) 90 | engine.init() 91 | 92 | #Sixth part 93 | q = """ 94 | PREFIX xsd: 95 | PREFIX chrontext: 96 | PREFIX types: 97 | SELECT ?w (SUM(?v) as ?sum_v) WHERE { 98 | ?w types:hasSensor ?s . 99 | ?s a types:ThingCounter . 100 | ?s chrontext:hasTimeseries ?ts . 101 | ?ts chrontext:hasDataPoint ?dp . 102 | ?dp chrontext:hasTimestamp ?t . 103 | ?dp chrontext:hasValue ?v . 104 | FILTER(?t > "2022-06-01T08:46:53Z"^^xsd:dateTime) . 105 | } GROUP BY ?w 106 | """ 107 | df = engine.query(q) 108 | assert df.shape == (2,2) 109 | #print(df) 110 | 111 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/expected_simple_query.csv: -------------------------------------------------------------------------------- 1 | w,s,t,v 2 | ,,2022-06-01T08:46:54.000000000,100 3 | ,,2022-06-01T08:46:56.000000000,102 4 | ,,2022-06-01T08:46:59.000000000,105 5 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_coalesce_query.csv: -------------------------------------------------------------------------------- 1 | s1,t1,v1,v2,c 2 | ,2022-06-01T08:46:52.000000000Z,1,10,10 3 | ,2022-06-01T08:46:53.000000000Z,10,100,100 4 | ,2022-06-01T08:46:54.000000000Z,100,301,301 5 | ,2022-06-01T08:46:55.000000000Z,301,,301 6 | ,2022-06-01T08:46:56.000000000Z,102,301,301 7 | ,2022-06-01T08:46:56.000000000Z,102,303,303 8 | ,2022-06-01T08:46:57.000000000Z,303,304,304 9 | ,2022-06-01T08:46:58.000000000Z,304,,304 10 | ,2022-06-01T08:46:59.000000000Z,105,304,304 11 | ,2022-06-01T08:46:52.000000000Z,2,20,20 12 | ,2022-06-01T08:46:53.000000000Z,20,200,200 13 | ,2022-06-01T08:46:54.000000000Z,200,201,201 14 | ,2022-06-01T08:46:55.000000000Z,201,202,202 15 | ,2022-06-01T08:46:56.000000000Z,202,203,203 16 | ,2022-06-01T08:46:57.000000000Z,203,204,204 17 | ,2022-06-01T08:46:58.000000000Z,204,206,206 18 | ,2022-06-01T08:46:59.000000000Z,206,,206 19 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_complex_hybrid.csv: -------------------------------------------------------------------------------- 1 | w1,w2,t,v1,v2 2 | ,,2022-06-01T08:46:56.000000000Z,102,202 3 | ,,2022-06-01T08:46:59.000000000Z,105,206 4 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_distinct_query.csv: -------------------------------------------------------------------------------- 1 | w,v_with_min 2 | ,300 3 | ,301 4 | ,303 5 | ,304 6 | ,300 7 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_if_query.csv: -------------------------------------------------------------------------------- 1 | w,v_with_min 2 | ,300 3 | ,300 4 | ,300 5 | ,301 6 | ,300 7 | ,303 8 | ,304 9 | ,300 10 | ,300 11 | ,300 12 | ,300 13 | ,300 14 | ,300 15 | ,300 16 | ,300 17 | ,300 18 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_in_expression.csv: -------------------------------------------------------------------------------- 1 | w,v 2 | ,301 3 | ,304 4 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_minus_query.csv: -------------------------------------------------------------------------------- 1 | w,v 2 | ,2 3 | ,20 4 | ,200 5 | ,201 6 | ,202 7 | ,203 8 | ,204 9 | ,206 10 | ,1 11 | ,10 12 | ,100 13 | ,102 14 | ,105 15 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_optional_clause_query.csv: -------------------------------------------------------------------------------- 1 | w,v,greater 2 | ,1, 3 | ,10, 4 | ,100, 5 | ,102, 6 | ,105, 7 | ,301, 8 | ,303, 9 | ,304, 10 | ,2, 11 | ,20, 12 | ,200, 13 | ,201, 14 | ,202, 15 | ,203, 16 | ,204, 17 | ,206, 18 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_path_group_by_query.csv: -------------------------------------------------------------------------------- 1 | w,max_v 2 | ,206 3 | ,304 -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_pushdown_exists_aggregated_timeseries_value_hybrid.csv: -------------------------------------------------------------------------------- 1 | w,s 2 | , 3 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_pushdown_exists_timeseries_value_hybrid.csv: -------------------------------------------------------------------------------- 1 | w,s 2 | , 3 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_pushdown_group_by_concat_agg_hybrid.csv: -------------------------------------------------------------------------------- 1 | w,seconds_5,cc 2 | ,11,301-102-303-304-105 3 | ,10,100 4 | ,11,201-202-203-204-206 5 | ,10,200 6 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_pushdown_group_by_exists_something_hybrid.csv: -------------------------------------------------------------------------------- 1 | w,seconds_3,mean 2 | ,18,167.66666666666666 3 | ,19,237.33333333333334 4 | ,17,5.5 5 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_pushdown_group_by_hybrid.csv: -------------------------------------------------------------------------------- 1 | w,sum_v 2 | ,1215 3 | ,1216 4 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_pushdown_group_by_second_having_hybrid.csv: -------------------------------------------------------------------------------- 1 | w,period,sum_v 2 | ,2022-6-1-8-46-55,1115 3 | ,2022-6-1-8-46-50,200 4 | ,2022-6-1-8-46-55,1016 5 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_pushdown_group_by_second_hybrid.csv: -------------------------------------------------------------------------------- 1 | w,sum_v 2 | ,100 3 | ,303 4 | ,301 5 | ,102 6 | ,105 7 | ,304 8 | ,204 9 | ,206 10 | ,202 11 | ,201 12 | ,203 13 | ,200 14 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_pushdown_not_exists_aggregated_timeseries_value_hybrid.csv: -------------------------------------------------------------------------------- 1 | w,s 2 | , 3 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_simple_hybrid.csv: -------------------------------------------------------------------------------- 1 | w,s,t,v 2 | ,,2022-06-01T08:46:54.000000000Z,100 3 | ,,2022-06-01T08:46:56.000000000Z,102 4 | ,,2022-06-01T08:46:59.000000000Z,105 5 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_simple_hybrid_sugar.csv: -------------------------------------------------------------------------------- 1 | w,s,timestamp,ts_value 2 | ,,2022-06-01T08:46:53.000000Z,10 3 | ,,2022-06-01T08:46:54.000000Z,100 4 | ,,2022-06-01T08:46:55.000000Z,301 5 | ,,2022-06-01T08:46:56.000000Z,102 6 | ,,2022-06-01T08:46:57.000000Z,303 7 | ,,2022-06-01T08:46:58.000000Z,304 8 | ,,2022-06-01T08:46:59.000000Z,105 9 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_simple_hybrid_sugar_agg_avg.csv: -------------------------------------------------------------------------------- 1 | w,s,timestamp,ts_value_avg 2 | ,,2022-06-01T08:46:50.000,55.0 3 | ,,2022-06-01T08:46:55.000,223.0 -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_union_of_two_groupby.csv: -------------------------------------------------------------------------------- 1 | w,second_5,kind,sum_v 2 | ,11,over_1000,1115 3 | ,10,under_500,100 4 | ,11,over_1000,1016 5 | ,10,under_500,200 6 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_union_query.csv: -------------------------------------------------------------------------------- 1 | w,v 2 | ,102 3 | ,105 4 | ,303 5 | ,2 6 | ,20 7 | ,301 8 | ,304 9 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/expected_values_query.csv: -------------------------------------------------------------------------------- 1 | w,v 2 | ,301 3 | ,304 4 | -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/testdata.ttl: -------------------------------------------------------------------------------- 1 | PREFIX case: 2 | PREFIX types: 3 | PREFIX chrontext: 4 | PREFIX xsd: 5 | 6 | case:myWidget1 types:hasSensor case:mySensor1 . 7 | case:myWidget1 types:hasSomething case:mySomething1 . 8 | case:myWidget2 types:hasSensor case:mySensor2 . 9 | case:myWidget1 a types:BigWidget . 10 | case:myWidget2 a types:SmallWidget . 11 | case:mySensor1 chrontext:hasTimeseries case:myTimeseries1 . 12 | case:mySensor2 chrontext:hasTimeseries case:myTimeseries2 . 13 | case:mySensor1 a types:ThingCounter . 14 | case:mySensor2 a types:EventCounter . 15 | case:myTimeseries1 chrontext:hasResource "my_resource" . 16 | case:myTimeseries2 chrontext:hasResource "my_resource" . 17 | case:myTimeseries1 chrontext:hasExternalId "ts1" . 18 | case:myTimeseries2 chrontext:hasExternalId "ts2" . -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/ts1.csv: -------------------------------------------------------------------------------- 1 | timestamp,value 2 | 2022-06-01T08:46:52,1 3 | 2022-06-01T08:46:53,10 4 | 2022-06-01T08:46:54,100 5 | 2022-06-01T08:46:55,301 6 | 2022-06-01T08:46:56,102 7 | 2022-06-01T08:46:57,303 8 | 2022-06-01T08:46:58,304 9 | 2022-06-01T08:46:59,105 -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/python_based/ts2.csv: -------------------------------------------------------------------------------- 1 | timestamp,value 2 | 2022-06-01T08:46:52,2 3 | 2022-06-01T08:46:53,20 4 | 2022-06-01T08:46:54,200 5 | 2022-06-01T08:46:55,201 6 | 2022-06-01T08:46:56,202 7 | 2022-06-01T08:46:57,203 8 | 2022-06-01T08:46:58,204 9 | 2022-06-01T08:46:59,206 -------------------------------------------------------------------------------- /py_chrontext/tests/testdata/testdata_opcua_history_read.sparql: -------------------------------------------------------------------------------- 1 | PREFIX case: 2 | PREFIX types: 3 | PREFIX chrontext: 4 | PREFIX xsd: 5 | PREFIX rdfs: 6 | INSERT DATA { 7 | case:myWidget1 types:hasSensor case:mySensor1 . 8 | case:myWidget1 rdfs:label "myWidget1" . 9 | case:myWidget1 types:hasComponent case:mySensor1 . 10 | case:myWidget1 types:hasSomething case:mySomething1 . 11 | case:myWidget1 types:hasComponent case:mySomething1 . 12 | case:myWidget2 types:hasSensor case:mySensor2 . 13 | case:myWidget2 rdfs:label "myWidget2" . 14 | case:myWidget2 types:hasComponent case:mySensor2 . 15 | case:myWidget1 a types:BigWidget . 16 | types:BigWidget rdfs:label "BigWidget" . 17 | case:myWidget2 a types:SmallWidget . 18 | types:SmallWidget rdfs:label "SmallWidget" . 19 | case:mySensor1 rdfs:label "ImportantSensor" . 20 | case:mySensor2 rdfs:label "OtherImportantSensor" . 21 | case:mySensor1 chrontext:hasTimeseries case:myTimeseries1 . 22 | case:myTimeseries1 chrontext:hasResource "my_resource" . 23 | case:mySensor2 chrontext:hasTimeseries case:myTimeseries2 . 24 | case:myTimeseries2 chrontext:hasResource "my_resource" . 25 | case:myTimeseries1 chrontext:hasExternalId "ns=2;s=ts1" . 26 | case:myTimeseries2 chrontext:hasExternalId "ns=2;s=ts2" . 27 | } -------------------------------------------------------------------------------- /py_chrontext/tests/ts1.csv: -------------------------------------------------------------------------------- 1 | timestamp,value 2 | 2022-06-01T08:46:52,1 3 | 2022-06-01T08:46:53,10 4 | 2022-06-01T08:46:54,100 5 | 2022-06-01T08:46:55,301 6 | 2022-06-01T08:46:56,102 7 | 2022-06-01T08:46:57,303 8 | 2022-06-01T08:46:58,304 9 | 2022-06-01T08:46:59,105 -------------------------------------------------------------------------------- /py_chrontext/tests/ts2.csv: -------------------------------------------------------------------------------- 1 | timestamp,value 2 | 2022-06-01T08:46:52,2 3 | 2022-06-01T08:46:53,20 4 | 2022-06-01T08:46:54,200 5 | 2022-06-01T08:46:55,201 6 | 2022-06-01T08:46:56,202 7 | 2022-06-01T08:46:57,203 8 | 2022-06-01T08:46:58,204 9 | 2022-06-01T08:46:59,206 --------------------------------------------------------------------------------