aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcos Magueta <maguetamarcos@gmail.com>2025-11-11 00:00:43 -0300
committerMarcos Magueta <maguetamarcos@gmail.com>2025-11-11 00:00:43 -0300
commit3063608df6ff1668f683d9d22c57fea29d728d0d (patch)
tree36be7f6c6d901c5984c045d4ce823bd24c37e9af
parentb5dbdaa989a86477669aac0bf342029e1d2b6028 (diff)
downloadSakura-3063608df6ff1668f683d9d22c57fea29d728d0d.tar.gz
Sakura-3063608df6ff1668f683d9d22c57fea29d728d0d.zip
migrating to erlang to attach to mnesia
Diffstat (limited to '')
-rw-r--r--.build.yml15
-rw-r--r--.github/workflows/build.yml14
-rw-r--r--.gitignore5
-rw-r--r--LICENSE73
-rw-r--r--README.org38
-rw-r--r--bin/dune4
-rw-r--r--bin/main.ml7
-rw-r--r--deps/libressl.nix13
-rw-r--r--deps/merklecpp.nix19
-rw-r--r--deps/relational_engine_lib.nix24
-rw-r--r--docs/architecture.plantuml72
-rw-r--r--docs/features.org96
-rw-r--r--dune-project28
-rw-r--r--flake.lock60
-rw-r--r--flake.nix286
-rw-r--r--lib/core_functions.ml25
-rw-r--r--lib/core_types.ml3
-rw-r--r--lib/disk.ml202
-rw-r--r--lib/dune28
-rw-r--r--lib/extensions.ml7
-rw-r--r--lib/interop.ml22
-rw-r--r--rebar.config6
-rw-r--r--rebar.lock1
-rw-r--r--relational_engine.opam31
-rw-r--r--shared/librelational_engine/CMakeLists.txt12
-rw-r--r--shared/librelational_engine/library.cpp73
-rw-r--r--shared/librelational_engine/library.h10
-rw-r--r--src/main.app.src15
-rw-r--r--src/main.erl7
-rw-r--r--src/operations.erl15
-rw-r--r--src/relation.erl42
-rw-r--r--test/dune6
-rw-r--r--test/test_relational_engine.ml8
33 files changed, 304 insertions, 963 deletions
diff --git a/.build.yml b/.build.yml
deleted file mode 100644
index ffbf56e..0000000
--- a/.build.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-image: nixos/unstable
-oauth: pages.sr.ht/PAGES:RW
-sources:
- - git@git.sr.ht:~mmagueta/relational-engine
-environment:
- NIX_CONFIG: "experimental-features = nix-command flakes"
-tasks:
- - check_branch: |
- cd relational-engine
- if [ "$(git rev-parse master)" != "$(git rev-parse HEAD)" ]; then \
- complete-build; \
- fi
- - build: |
- cd relational-engine
- nix flake check \ No newline at end of file
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index d530a2b..0000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: "Build"
-on:
- pull_request:
- push:
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: cachix/install-nix-action@v25
- with:
- nix_path: nixpkgs=channel:nixos-unstable
- - uses: DeterminateSystems/magic-nix-cache-action@v2
- - run: nix flake check \ No newline at end of file
diff --git a/.gitignore b/.gitignore
index c0f3d8f..24320d0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,2 @@
-result
_build
-cmake-build-debug/
-*CMakeFiles
-*CMakeCache.txt \ No newline at end of file
+Mnesia.*
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7bfa10f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,73 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+ (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright 2025 mmagueta
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/README.org b/README.org
index dcd2b7c..3f533e7 100644
--- a/README.org
+++ b/README.org
@@ -1,24 +1,20 @@
-#+TITLE: Relational Engine
+#+TITLE: Domino
* About
-This is a relational engine.
-* Roadmap
-- [ ] Filesystem [0/6]
- - [ ] Durability
- - [ ] Immutable storage
- - [ ] ARIES
- - [ ] Merkle construction per virtual file
- - [ ] Branching
- - [ ] Branch merge strategies [0/3]
- - [ ] Selected merge
- - [ ] Union merge
- - [ ] Biased union merge
-- [ ] Language [0/4]
- - [ ] Lexer
- - [ ] Parser
- - [ ] AST
- - [ ] Unification and query terms
-- [ ] Server [0/2]
- - [ ] Authentication
- - [ ] Multi-client coordination
+Domino is a **Relational Algebra Engine** implements relational
+algebra in its strict, theoretical form, adhering rigorously to the
+principles of relational theory as articulated by E. F. Codd.
+
+A relation is modeled as a set of tuples, each tuple being a set of
+attributes. Each attribute is a pairing of a name with a value drawn
+from a domain. All operations are defined on entire relations as sets,
+with no recourse to the compromises typical of SQL or other practical
+implementations.
+
+The engine is intended as a study in correctness: each operation, from
+projection to join, is executed in a manner consistent with the
+classical definition of relational algebra. It is a platform for
+exploring the consequences of relational theory in a low-level
+programming context, providing both a tool for experimentation and a
+concrete realization of the abstract model.
diff --git a/bin/dune b/bin/dune
deleted file mode 100644
index 0888311..0000000
--- a/bin/dune
+++ /dev/null
@@ -1,4 +0,0 @@
-(executable
- (public_name relational_engine)
- (name main)
- (libraries relational_engine))
diff --git a/bin/main.ml b/bin/main.ml
deleted file mode 100644
index 3b718ee..0000000
--- a/bin/main.ml
+++ /dev/null
@@ -1,7 +0,0 @@
-open Relational_engine
-
-let () =
- begin
- (* print_endline @@ Interop.Merkle.merkle_generate_root [Disk.Executor._EMPTY_SHA_HASH_] *)
- Interop.Merkle.register_to_merkle Disk.Executor._EMPTY_SHA_HASH_;
- end
diff --git a/deps/libressl.nix b/deps/libressl.nix
deleted file mode 100644
index a517dab..0000000
--- a/deps/libressl.nix
+++ /dev/null
@@ -1,13 +0,0 @@
-{ pkgs, ... }:
-pkgs.stdenv.mkDerivation {
- name = "libressl-lib";
- src = ./.;
- buildInputs = with pkgs; [ libressl.dev pkg-config ];
- installPhase = ''
- mkdir -p $out/lib
- mkdir -p $out/include
- libressl_path=$(${pkgs.pkg-config}/bin/pkg-config --libs ${pkgs.libressl.dev}/lib/pkgconfig/libssl.pc | awk '{sub(/^-L/, ""); sub(/ -lssl$/, ""); print}')
- cp -r $libressl_path/* $out/lib
- cp -r ${pkgs.libressl.dev}/include/* $out/include
- '';
-}
diff --git a/deps/merklecpp.nix b/deps/merklecpp.nix
deleted file mode 100644
index d130be6..0000000
--- a/deps/merklecpp.nix
+++ /dev/null
@@ -1,19 +0,0 @@
-{ pkgs, ... }:
-pkgs.stdenv.mkDerivation rec {
- name = "merklecpp";
-
- src = pkgs.fetchFromGitHub {
- owner = "microsoft";
- repo = "merklecpp";
- rev = "master";
- sha256 = "jJJ6FEn+AkMknqun0GkXh5DSXsJnFbdE7U8BQ7Dd/Fw=";
- };
-
- prePatch = "";
- configurePhase = "";
- buildPhase = "";
- installPhase = ''
- mkdir -p $out/include
- mv merklecpp.h $out/include/merklecpp.h
- '';
-}
diff --git a/deps/relational_engine_lib.nix b/deps/relational_engine_lib.nix
deleted file mode 100644
index a3ea22a..0000000
--- a/deps/relational_engine_lib.nix
+++ /dev/null
@@ -1,24 +0,0 @@
-{ pkgs, libressl ? pkgs.callPackage ./libressl.nix { }
-, merklecpp ? pkgs.callPackage ./merklecpp.nix { }, ... }:
-let shared_lib_extension = if pkgs.stdenv.isDarwin then "dylib" else "so";
-in pkgs.stdenv.mkDerivation {
- name = "librelational_engine";
- src = ../shared/librelational_engine;
- nativeBuildInputs = [ pkgs.cmake pkgs.gcc ];
- buildInputs = [ libressl merklecpp ];
- env = {
- MERKLECPP_INCLUDE_PATH = "${merklecpp}/include";
- LIBRESSL_INCLUDE_PATH = "${libressl}/include";
- LIBRESSL_LIB_PATH = "${libressl}/lib";
- OS_LIB_EXTENSION = shared_lib_extension;
- };
- buildPhase = ''
- mkdir -p $out/lib
- mkdir -p $out/include
- cmake --build . --target relational_engine
- '';
- installPhase = ''
- mv librelational_engine.${shared_lib_extension} $out/lib/librelational_engine.${shared_lib_extension}
- cp $src/library.h $out/include/library.h
- '';
-}
diff --git a/docs/architecture.plantuml b/docs/architecture.plantuml
deleted file mode 100644
index 90985ba..0000000
--- a/docs/architecture.plantuml
+++ /dev/null
@@ -1,72 +0,0 @@
-@startuml
-allowmixing
-
-package Client {
- object "Query Interface" as query_interface
-}
-
-package Algebra {
- object "Unifier" as unifier
- object "Bytecode Builder" as bytecode_builder
- object "Parser" as parser
- object "Lexer" as lexer
-}
-
-query_interface --> lexer
-lexer --> parser
-parser --> unifier
-unifier --> bytecode_builder
-
-package Query {
- object "Schema Builder" as schema_builder
- object "Facts Builder" as facts_builder
- object "Optimizer" as optimizer
- object "Commands Generator" as commands_generator
- object "B+ Tree" as b_plus_tree
-}
-
-bytecode_builder --> optimizer
-optimizer --> commands_generator
-facts_builder --> commands_generator
-schema_builder --> commands_generator
-commands_generator --> b_plus_tree
-b_plus_tree --> query_interface
-
-package Coordinator {
- object "File Historical Hashes" as file_historical_hashes
- object "Location Hashes" as location_hashes
- object "Command Parser" as command_parser
- object "Curator" as curator
-}
-
-commands_generator <-- command_parser
-
-package Disk {
- object "Merkle Tree Root Generator" as merkle_tree
- object "WAL Reader" as wal_reader
- object "WAL Writer" as wal_writer
- object "ARIES" as aries
- object "SHA256 Hasher" as sha
- object "Executor" as executor
- map storage {
- write => +λ
- read => +λ
- }
- object "Courier" as courier
- database Wal
-}
-
-executor *--> merkle_tree
-executor *--> sha
-curator --> command_parser
-command_parser --> executor
-location_hashes *--> curator
-file_historical_hashes *--> curator
-executor --> aries
-aries --> wal_reader
-aries --> wal_writer
-wal_writer --> Wal
-wal_reader --> Wal
-courier <-- wal_reader
-courier --> storage
-@enduml \ No newline at end of file
diff --git a/docs/features.org b/docs/features.org
new file mode 100644
index 0000000..a6e592e
--- /dev/null
+++ b/docs/features.org
@@ -0,0 +1,96 @@
+* Relational Algebra Engine
+ :PROPERTIES:
+ :CATEGORY: relational-algebra
+ :END:
+ A pure Erlang implementation of E. F. Codd’s relational algebra.
+ Relations are treated as mathematical sets of tuples; operations are defined exactly
+ as in relational theory, with optional aleph₀ (infinite) relations.
+
+** Core Specification
+ The theoretical backbone of the system.
+
+*** TODO Define relation as a strict mathematical set
+ - [ ] Each tuple is a set of attributes (no duplicates, no ordering)
+ - [ ] Each attribute = (name, value) pair
+ - [ ] Attribute domains are strongly typed
+
+*** TODO Enforce "Codd's axioms" in implementation
+ - [ ] No duplicate tuples in any relation
+ - [ ] No implicit ordering of tuples or attributes
+ - [ ] All operations closed over the relation type
+ - [ ] Proper domain checking in all operations
+
+** Data Model Layer (`relation.erl`)
+ Fundamental constructs of the relational model.
+
+*** TODO Implement attributes, tuples, relations, and schemas
+*** TODO Add schema validation for domain and arity
+*** TODO Add conversion helpers:
+ - [ ] tuple <-> map
+ - [ ] schema validation checks
+
+** Algebraic Operations (`operations.erl`)
+ The complete set of classical relational algebra operators.
+
+*** TODO Selection (σ)
+*** TODO Projection (π)
+*** TODO Cartesian Product (×)
+*** TODO Natural Join (⋈)
+*** TODO Theta Join (⋈θ)
+*** TODO Union (∪)
+*** TODO Intersection (∩)
+*** TODO Difference (−)
+*** TODO Rename (ρ)
+*** TODO Set semantics enforcement (remove duplicates after each op)
+
+** Infinite Relations (`aleph0.erl`)
+ Representation of aleph₀-relations (countably infinite generators).
+
+*** TODO Implement integer() generator
+*** TODO Implement plus(), minus(), multiply(), neg() relations
+*** TODO Add constraints and modes for partially bound arguments
+*** TODO Implement lazy generation using streams or coroutines
+*** TODO Support user-defined infinite relations
+
+** Unified Relation Abstraction (`unified_relation.erl`)
+ A bridge between finite and infinite relations.
+
+*** TODO Implement unified type for finite and aleph₀
+*** TODO Implement unified select, project, join, union, etc.
+*** TODO Support type checking between finite and infinite relations
+*** TODO Add automatic materialization with configurable limits
+*** TODO Implement caching/memoization for aleph₀ evaluations
+
+** Backend Integration (Mnesia)
+ Persistent storage and retrieval of finite relations.
+
+*** TODO Map finite relations to Mnesia tables
+*** TODO Support schema registration and domain enforcement
+*** TODO Add persistence for partially materialized aleph₀ relations
+*** TODO Add atomic transactional updates
+*** TODO Add indexing support for joins
+
+** Correctness and Validation
+ Ensuring theoretical fidelity.
+
+*** TODO Implement relational algebra test suite
+*** TODO Verify closure and idempotence of all operations
+*** TODO Add property-based tests with PropEr or QuickCheck
+*** TODO Compare semantics with formal definitions (e.g., Date & Darwen)
+
+** Developer Tools
+ Optional but desirable tools for exploration and testing.
+
+*** TODO Add REPL-like console for interactive relational queries
+*** TODO Add pretty-printer for relations and schemas
+*** TODO Add visualization of join graphs and operation pipelines
+*** TODO Add Org Babel integration for relational queries
+
+** Documentation
+ Literate and theoretical exposition.
+
+*** TODO Write README with theoretical introduction
+*** TODO Add module-level comments summarizing relational definitions
+*** TODO Include examples of finite and aleph₀ joins
+*** TODO Provide link to E. F. Codd’s original papers
+
diff --git a/dune-project b/dune-project
deleted file mode 100644
index 661cecd..0000000
--- a/dune-project
+++ /dev/null
@@ -1,28 +0,0 @@
-(lang dune 3.16)
-
-(using ctypes 0.3)
-
-(name relational_engine)
-
-(generate_opam_files true)
-
-(source
- (github username/reponame))
-
-(authors "Author Name")
-
-(maintainers "Maintainer Name")
-
-(license LICENSE)
-
-(documentation https://url/to/documentation)
-
-(package
- (name relational_engine)
- (synopsis "A short synopsis")
- (description "A longer description")
- (depends ocaml dune)
- (tags
- (topics "to describe" your project)))
-
-; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html
diff --git a/flake.lock b/flake.lock
index e1f7e32..8dc25a3 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,49 +1,16 @@
{
"nodes": {
- "flake-utils": {
- "inputs": {
- "systems": "systems"
- },
- "locked": {
- "lastModified": 1726560853,
- "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
- "owner": "numtide",
- "repo": "flake-utils",
- "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
- "type": "github"
- },
- "original": {
- "owner": "numtide",
- "repo": "flake-utils",
- "type": "github"
- }
- },
- "nix-filter": {
- "locked": {
- "lastModified": 1730207686,
- "narHash": "sha256-SCHiL+1f7q9TAnxpasriP6fMarWE5H43t25F5/9e28I=",
- "owner": "numtide",
- "repo": "nix-filter",
- "rev": "776e68c1d014c3adde193a18db9d738458cd2ba4",
- "type": "github"
- },
- "original": {
- "owner": "numtide",
- "repo": "nix-filter",
- "type": "github"
- }
- },
"nixpkgs": {
"locked": {
- "lastModified": 1730200266,
- "narHash": "sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU=",
- "owner": "NixOS",
+ "lastModified": 1762363567,
+ "narHash": "sha256-YRqMDEtSMbitIMj+JLpheSz0pwEr0Rmy5mC7myl17xs=",
+ "owner": "nixos",
"repo": "nixpkgs",
- "rev": "807e9154dcb16384b1b765ebe9cd2bba2ac287fd",
+ "rev": "ae814fd3904b621d8ab97418f1d0f2eb0d3716f4",
"type": "github"
},
"original": {
- "owner": "NixOS",
+ "owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
@@ -51,25 +18,8 @@
},
"root": {
"inputs": {
- "flake-utils": "flake-utils",
- "nix-filter": "nix-filter",
"nixpkgs": "nixpkgs"
}
- },
- "systems": {
- "locked": {
- "lastModified": 1681028828,
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
- "owner": "nix-systems",
- "repo": "default",
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
- "type": "github"
- },
- "original": {
- "owner": "nix-systems",
- "repo": "default",
- "type": "github"
- }
}
},
"root": "root",
diff --git a/flake.nix b/flake.nix
index 7add9c5..249b892 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,270 +1,36 @@
{
- description = "Relational Engine";
+ description = "A very basic flake";
- # Flake dependency specification
- #
- # To update all flake inputs:
- #
- # $ nix flake update --commit-lockfile
- #
- # To update individual flake inputs:
- #
- # $ nix flake lock --update-input <input> ... --commit-lockfile
- #
inputs = {
- nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
- # Convenience functions for writing flakes
- flake-utils.url = "github:numtide/flake-utils";
- # Precisely filter files copied to the nix store
- nix-filter.url = "github:numtide/nix-filter";
+ nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
- outputs = { self, nixpkgs, flake-utils, nix-filter }:
- # Construct an output set that supports a number of default systems
- flake-utils.lib.eachDefaultSystem (system:
+ outputs = { self, nixpkgs, ... }:
+ let
+ systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
+ forAllSystems = nixpkgs.lib.genAttrs systems;
+ in {
+ packages = forAllSystems (system:
let
- # Legacy packages that have not been converted to flakes
- legacyPackages = nixpkgs.legacyPackages.${system};
- # OCaml packages available on nixpkgs
- ocamlPackages = legacyPackages.ocamlPackages;
- # Library functions from nixpkgs
- lib = legacyPackages.lib;
- merklecpp = legacyPackages.callPackage ./deps/merklecpp.nix { };
- libressl = legacyPackages.callPackage ./deps/libressl.nix { };
- librelational_engine =
- legacyPackages.callPackage ./deps/relational_engine_lib.nix {
- libressl = libressl;
- merklecpp = merklecpp;
- };
-
- # Filtered sources (prevents unecessary rebuilds)
- sources = {
- ocaml = nix-filter.lib {
- root = ./.;
- include = [
- ".ocamlformat"
- "dune-project"
- (nix-filter.lib.inDirectory "bin")
- (nix-filter.lib.inDirectory "lib")
- (nix-filter.lib.inDirectory "test")
- ];
- };
-
- nix = nix-filter.lib {
- root = ./.;
- include = [ (nix-filter.lib.matchExt "nix") ];
- };
- };
+ pkgs = nixpkgs.legacyPackages.${system};
in {
- # Exposed packages that can be built or run with `nix build` or
- # `nix run` respectively:
- #
- # $ nix build .#<name>
- # $ nix run .#<name> -- <args?>
- #
- packages = {
- # The package that will be built or run by default. For example:
- #
- # $ nix build
- # $ nix run -- <args?>
- #
- default = self.packages.${system}.relational_engine;
-
- relational_engine = ocamlPackages.buildDunePackage {
- pname = "relational_engine";
- version = "0.1.0";
- duneVersion = "3";
- src = sources.ocaml;
-
- env = {
- MERKLECPP_INCLUDE_PATH = "${merklecpp}/include";
- LIBRESSL_INCLUDE_PATH = "${libressl}/include";
- LIBRESSL_LIB_PATH = "${libressl}/lib";
- OS_LIB_EXTENSION =
- if legacyPackages.stdenv.isDarwin then "dylib" else "so";
- LIBRELATIONAL_ENGINE_LIB_PATH = "${librelational_engine}/lib";
- LIBRELATIONAL_ENGINE_INCLUDE_PATH =
- "${librelational_engine}/include";
- };
-
- nativeInputs = [ ];
-
- buildInputs = with ocamlPackages; [
- ctypes
- ctypes-foreign
- data-encoding
- ppx_inline_test
- ];
-
- strictDeps = true;
-
- preBuild = ''
- dune build relational_engine.opam
- '';
- };
- };
- # Flake checks
- #
- # $ nix flake check
- #
- checks = {
- # Run tests for the `relational_engine` package
- relational_engine = let
- # Patches calls to dune commands to produce log-friendly output
- # when using `nix ... --print-build-log`. Ideally there would be
- # support for one or more of the following:
- #
- # In Dune:
- #
- # - have workspace-specific dune configuration files
- #
- # In NixPkgs:
- #
- # - allow dune flags to be set in in `ocamlPackages.buildDunePackage`
- # - alter `ocamlPackages.buildDunePackage` to use `--display=short`
- # - alter `ocamlPackages.buildDunePackage` to allow `--config-file=FILE` to be set
- patchDuneCommand =
- let subcmds = [ "build" "test" "runtest" "install" ];
- in lib.replaceStrings
- (lib.lists.map (subcmd: "dune ${subcmd}") subcmds)
- (lib.lists.map (subcmd: "dune ${subcmd} --display=short")
- subcmds);
-
- in self.packages.${system}.relational_engine.overrideAttrs
- (oldAttrs: {
- name = "check-${oldAttrs.name}";
- doCheck = true;
- buildPhase = patchDuneCommand oldAttrs.buildPhase;
- checkPhase = patchDuneCommand oldAttrs.checkPhase;
- # skip installation (this will be tested in the `relational_engine-app` check)
- installPhase = "touch $out";
- });
-
- # Check Dune and OCaml formatting
- dune-fmt = legacyPackages.runCommand "check-dune-fmt" {
- nativeBuildInputs = [
- ocamlPackages.dune_3
- ocamlPackages.ocaml
- legacyPackages.ocamlformat
- ];
- } ''
- echo "checking dune and ocaml formatting"
- dune build \
- --display=short \
- --no-print-directory \
- --root="${sources.ocaml}" \
- --build-dir="$(pwd)/_build" \
- @fmt
- touch $out
- '';
+ hello = pkgs.hello;
+ default = pkgs.hello;
+ }
+ );
- dune-test = legacyPackages.runCommand "check-dune-test" {
- nativeBuildInputs = [
- ocamlPackages.dune_3
- ocamlPackages.ocaml
- legacyPackages.ocamlformat
- ocamlPackages.ppx_inline_test
- ];
- } ''
- echo "checking dune and ocaml formatting"
- dune build \
- --display=short \
- --no-print-directory \
- --root="${sources.ocaml}" \
- --build-dir="$(pwd)/_build" \
- @fmt
- touch $out
- '';
-
- # Check documentation generation
- dune-doc = legacyPackages.runCommand "check-dune-doc" {
- ODOC_WARN_ERROR = "true";
- nativeBuildInputs =
- [ ocamlPackages.dune_3 ocamlPackages.ocaml ocamlPackages.odoc ];
- } ''
- echo "checking ocaml documentation"
- dune build \
- --display=short \
- --no-print-directory \
- --root="${sources.ocaml}" \
- --build-dir="$(pwd)/_build" \
- @doc
- touch $out
- '';
-
- # Check Nix formatting
- # nixpkgs-fmt = legacyPackages.runCommand "check-nixpkgs-fmt" {
- # nativeBuildInputs = [ legacyPackages.nixpkgs-fmt ];
- # } ''
- # echo "checking nix formatting"
- # nixpkgs-fmt --check ${sources.nix}
- # touch $out
- # '';
- };
-
- # Development shells
- #
- # $ nix develop .#<name>
- # $ nix develop .#<name> --command dune build @test
- #
- # [Direnv](https://direnv.net/) is recommended for automatically loading
- # development environments in your shell. For example:
- #
- # $ echo "use flake" > .envrc && direnv allow
- # $ dune build @test
- #
- devShells = {
- default = legacyPackages.mkShell {
-
- env = {
- MERKLECPP_INCLUDE_PATH = "${merklecpp}/include";
- LIBRESSL_INCLUDE_PATH = "${libressl}/include";
- LIBRESSL_LIB_PATH = "${libressl}/lib";
- OS_LIB_EXTENSION =
- if legacyPackages.stdenv.isDarwin then "dylib" else "so";
- LIBRELATIONAL_ENGINE_LIB_PATH = "${librelational_engine}/lib";
- LIBRELATIONAL_ENGINE_INCLUDE_PATH =
- "${librelational_engine}/include";
- };
-
- # Development tools
- packages = [
- # Source file formatting
- legacyPackages.nixpkgs-fmt
- legacyPackages.ocamlformat
- # For `dune build --watch ...`
- legacyPackages.fswatch
- # For `dune build @doc`
- ocamlPackages.odoc
- # OCaml editor support
- ocamlPackages.ocaml-lsp
- # Nicely formatted types on hover
- ocamlPackages.ocamlformat-rpc-lib
- # Fancy REPL thing
- ocamlPackages.utop
- # Libraries
- ocamlPackages.menhir
- ocamlPackages.ctypes
- ocamlPackages.ctypes-foreign
- ocamlPackages.data-encoding
- ocamlPackages.ppx_inline_test
- legacyPackages.cmake
- legacyPackages.gcc
- legacyPackages.nixfmt-classic
- ];
-
- shellHook = ''
- echo MERKLECPP_INCLUDE_PATH=$MERKLECPP_INCLUDE_PATH
- echo LIBRESSL_INCLUDE_PATH=$LIBRESSL_INCLUDE_PATH
- echo LIBRESSL_LIB_PATH=$LIBRESSL_LIB_PATH
- echo OS_LIB_EXTENSION=$OS_LIB_EXTENSION
- echo LIBRELATIONAL_ENGINE_LIB_PATH=$LIBRELATIONAL_ENGINE_LIB_PATH
- echo LIBRELATIONAL_ENGINE_INCLUDE_PATH=$LIBRELATIONAL_ENGINE_INCLUDE_PATH
- '';
-
- # Tools from packages
- inputsFrom = [ self.packages.${system}.relational_engine ];
- };
+ devShells = forAllSystems (system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ in {
+ default = pkgs.mkShell {
+ packages = with pkgs; [
+ erlang_28
+ erlang-language-platform
+ rebar3
+ ];
};
- });
+ }
+ );
+ };
}
diff --git a/lib/core_functions.ml b/lib/core_functions.ml
deleted file mode 100644
index 3d1cd35..0000000
--- a/lib/core_functions.ml
+++ /dev/null
@@ -1,25 +0,0 @@
-(* open Ctypes *)
-
-module Types = Types_generated
-
-module Functions (F : Ctypes.FOREIGN) = struct
- (* open F *)
- open Ctypes_static
- let os_extension =
- if Sys.os_type = "Unix" then
- let ic = Unix.open_process_in "uname" in
- let uname = input_line ic in
- let () = close_in ic in
- if uname = "Darwin" then
- "dylib"
- else "so"
- else "dll"
- let libmerklecpp =
- Dl.dlopen
- ~flags:[RTLD_GLOBAL; RTLD_NOW]
- ~filename:((Sys.getenv "LIBRELATIONAL_ENGINE_LIB_PATH") ^ "/librelational_engine." ^ os_extension)
- let compute_hash = Foreign.foreign ~from:libmerklecpp "compute_hash" (ptr char @-> Ctypes.string @-> returning void)
- let merkle_generate_root = Foreign.foreign ~from:libmerklecpp "merkle_generate_root" (ptr Ctypes.string @-> int @-> ptr char @-> returning void)
- let register_to_merkle = Foreign.foreign ~from:libmerklecpp "register_to_merkle" (Ctypes.string @-> Ctypes.string @-> returning void)
-
-end
diff --git a/lib/core_types.ml b/lib/core_types.ml
deleted file mode 100644
index 45f5720..0000000
--- a/lib/core_types.ml
+++ /dev/null
@@ -1,3 +0,0 @@
-module Types (F : Ctypes.TYPE) = struct
- let hash_size = F.constant "HASH_SIZE" F.int
-end
diff --git a/lib/disk.ml b/lib/disk.ml
deleted file mode 100644
index aa971dd..0000000
--- a/lib/disk.ml
+++ /dev/null
@@ -1,202 +0,0 @@
-module type Configuration = sig
- val base_dir: string
- val storage: string
- val transactions: string
- val assure: unit -> unit
-end
-
-module DevelopmentConfiguration: Configuration = struct
- let base_dir = "/tmp/relational-engine/"
- let storage = base_dir ^ "storage/"
- let transactions = base_dir ^ "transactions/"
- let assure () =
- if Sys.file_exists base_dir
- then Sys.remove base_dir;
- Sys.mkdir base_dir 0o700;
- Sys.mkdir storage 0o700;
- Sys.mkdir transactions 0o700;
-end
-
-module type FileSystem = sig
- type target =
- | Storage
- | Transaction
- val append: target -> bytes -> (int*int64, string) result
-end
-
-module FileSystem (C: Configuration): FileSystem = struct
- type target =
- | Storage
- | Transaction
- let target_to_string = function
- | Storage -> "storage"
- | Transaction -> "transaction"
-
- (** [append target content] writes content to file in append only format, returning [content_length * offset_written] *)
- let append target content =
- let register channel =
- let offset_written = Out_channel.length channel in
- Out_channel.seek channel offset_written;
- Out_channel.output_bytes channel content;
- Out_channel.flush channel;
- Out_channel.close channel;
- Ok (Bytes.length content, offset_written)
- in
- try Out_channel.with_open_bin (target_to_string target) register
- with _ -> Error (Printf.sprintf "Error on appending to target '%s'" (target_to_string target))
-end
-
-module Executor = struct
- module StringMap = Map.Make(String)
- module FS = FileSystem(DevelopmentConfiguration)
- module Location = struct
- type t =
- { offset: int64;
- size: int;
- hash: string }
- end
- module Hashes = struct
- type t = { values: string list;
- hash: string }
- type history = t list
- end
- type commit = { state: string; files: Hashes.history StringMap.t }
- type history = commit list
- type locations = Location.t StringMap.t
- let _EMPTY_SHA_HASH_ = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
-
- (** Initializes the virtual file or opens it. *)
- let init_file (files: Hashes.history StringMap.t) filename: Hashes.history StringMap.t =
- match StringMap.find_opt filename files with
- | None ->
- let default: Hashes.t = { values = []; hash = _EMPTY_SHA_HASH_} in
- let new_files = StringMap.add filename [default] files
- in new_files
- | Some _ ->
- files
-
- let compose_new_state files =
- let history = StringMap.to_list files in
- let all_hashes = List.concat
- @@ List.map (fun (_, (history:Hashes.history)) ->
- List.map (fun ({hash; _}: Hashes.t) -> hash) history) history
- in Interop.Merkle.merkle_generate_root all_hashes
-
- let write ({files;_} as commit: commit) (locations: locations) ~filename ?hash_to_replace (content: Bytes.t) =
- let files = init_file files filename in
- let computed_hash: string = Interop.Sha256.compute_hash content in
- match StringMap.find_opt computed_hash locations with
- | None ->
- begin match hash_to_replace with
- | None ->
- begin
- (* This instead should store a list of list to keep the history *)
- let files =
- StringMap.update
- filename
- (function
- | Some (file_hashes: Hashes.history) ->
- let {values; _}: Hashes.t = List.hd file_hashes in
- let new_state = Interop.Merkle.merkle_generate_root (computed_hash::values) in
- Some ({values = computed_hash::values; hash = new_state}::file_hashes)
- | None -> Some [{values = [computed_hash]; hash = computed_hash}])
- files
- in
- let open Extensions.Result in
- let+ (size, offset) = FS.append Storage content in
- let locations = StringMap.add computed_hash ({size; offset; hash = computed_hash}: Location.t) locations in
- let commit = {state = compose_new_state files; files}
- in Ok (commit, locations)
- end
- | Some hash_to_replace ->
- let update_fun = function
- | Some (file_hashes: Hashes.history) ->
- let {values; _}: Hashes.t = List.hd file_hashes in
- let updated_entry = List.map (fun hash -> if hash = hash_to_replace then computed_hash else hash) values in
- let new_entry: Hashes.t = {values = updated_entry; hash = Interop.Merkle.merkle_generate_root updated_entry} in
- Some (new_entry::file_hashes)
- | None -> Some [{values = [computed_hash]; hash = computed_hash}]
- in
- let files = StringMap.update filename update_fun files in
- let open Extensions.Result in
- let+ (size, offset) = FS.append Storage content in
- let locations = StringMap.add computed_hash ({size; offset; hash = computed_hash}: Location.t) locations in
- let commit = {state = compose_new_state files; files}
- in Ok (commit, locations)
- end
- | Some _ ->
- begin match hash_to_replace with
- | Some hash_to_replace ->
- let update_fun = function
- | Some (file_hashes: Hashes.history) ->
- let {values; _}: Hashes.t = List.hd file_hashes in
- let updated_entry = List.map (fun hash -> if hash = hash_to_replace then computed_hash else hash) values in
- let new_entry: Hashes.t = {values = updated_entry; hash = Interop.Merkle.merkle_generate_root updated_entry} in
- Some (new_entry::file_hashes)
- | None -> Some [{values = [computed_hash]; hash = computed_hash}]
- in
- let files = StringMap.update filename update_fun files in
- let commit = {state = compose_new_state files; files}
- in Ok (commit, locations)
- | None ->
- Ok (commit, locations)
- end
-end
-
-module Startup = struct
- (* TODO: Weird behavior with a system error on the dir /tmp/relational-engine opening. Disabling for now *)
- (* let () = DevelopmentConfiguration.assure() *)
-end
-
-module Command = struct
- module FS = FileSystem(DevelopmentConfiguration)
- open Data_encoding
- type command_kind =
- | OPEN
- | WRITE
- | READ
- let command_kind_encoding =
- union [
- case ~title:"open" (Tag 0)
- Data_encoding.empty
- (function OPEN -> Some () | _ -> None)
- (function () -> OPEN);
- case ~title:"write" (Tag 1)
- Data_encoding.empty
- (function WRITE -> Some () | _ -> None)
- (function () -> WRITE);
- case ~title:"read" (Tag 2)
- Data_encoding.empty
- (function READ -> Some () | _ -> None)
- (function () -> READ);
- ]
- type t =
- { kind: command_kind;
- timestamp: float;
- hash: string;
- filename: string;
- (* branch must be added here later *)
- content: string }
- let command_encoding =
- conv
- (fun {kind; timestamp; hash; filename; content} -> (kind, timestamp, hash, filename, content))
- (fun (kind, timestamp, hash, filename, content) -> {kind; timestamp; hash; filename; content})
- Data_encoding.(tup5 command_kind_encoding float string string string)
-
- let parse_command ~data =
- let contract_encoding = Data_encoding.(tup4 command_kind_encoding string string string) in
- match Binary.of_bytes_opt contract_encoding data with
- | Some (kind, hash, filename, content) -> Ok {kind; timestamp = Unix.time(); hash; filename; content}
- | None -> Error "Failed to parse command"
-
- (** TODO: This does not write atomically. If the system crashes while it attempts to write, it will corrupt.
- Solve this with a temporary file and move later. *)
- let commit
- (* (_files: Filesystem.files) (_locations: Filesystem.locations) *)
- command =
- let open Extensions.Result in
- let+ serialized_command =
- Result.map_error (fun _ -> "Failed to serialize command to binary format.")
- @@ Binary.to_bytes command_encoding command in
- FS.append FS.Transaction serialized_command
-end
diff --git a/lib/dune b/lib/dune
deleted file mode 100644
index 0ef1c18..0000000
--- a/lib/dune
+++ /dev/null
@@ -1,28 +0,0 @@
-(library
- (name relational_engine)
- (libraries ctypes ctypes-foreign data-encoding)
- ;; (c_library_flags -lssl -lcrypto -L %{env:LIBRESSL_LIB_PATH=})
- ;; (foreign_stubs
- ;; (language cxx)
- ;; (names merklecpp_stubs)
- ;; (flags
- ;; (:standard -lssl -lcrypto)
- ;; (-L %{env:LIBRESSL_LIB_PATH=})
- ;; (-I %{env:LIBRESSL_INCLUDE_PATH=})
- ;; (-I %{env:MERKLECPP_INCLUDE_PATH=})))
- (ctypes
- (external_library_name relational_engine)
- (headers
- (include %{env:LIBRELATIONAL_ENGINE_INCLUDE_PATH=}/library.h))
- (build_flags_resolver
- (vendored
- (c_flags -L %{env:LIBRELATIONAL_ENGINE_LIB_PATH=})))
- (type_description
- (instance Types)
- (functor core_types))
- (function_description
- (concurrency unlocked)
- (instance Functions)
- (functor core_functions))
- (generated_types Types_generated)
- (generated_entry_point C)))
diff --git a/lib/extensions.ml b/lib/extensions.ml
deleted file mode 100644
index c128f2a..0000000
--- a/lib/extensions.ml
+++ /dev/null
@@ -1,7 +0,0 @@
-module Option = struct
- let ( let+ ) = Option.bind
-end
-
-module Result = struct
- let ( let+ ) = Result.bind
-end
diff --git a/lib/interop.ml b/lib/interop.ml
deleted file mode 100644
index 28b4fb9..0000000
--- a/lib/interop.ml
+++ /dev/null
@@ -1,22 +0,0 @@
-open Ctypes
-
-module Sha256 = struct
- let compute_hash content =
- let size_with_null_terminator = C.Types.hash_size + 1 in
- let buf = Ctypes.allocate_n char ~count:size_with_null_terminator in
- let () = C.Functions.compute_hash buf (Bytes.to_string content) in
- Ctypes.coerce (ptr char) Ctypes.string buf
-end
-
-module Merkle = struct
- let merkle_generate_root hashes =
- let size_with_null_terminator = C.Types.hash_size + 1 in
- let buf = Ctypes.allocate_n char ~count:size_with_null_terminator in
- let len = List.length hashes in
- let hashes = CArray.of_list string hashes in
- C.Functions.merkle_generate_root (CArray.start hashes) len buf;
- Ctypes.coerce (ptr char) Ctypes.string buf
-
- let register_to_merkle content =
- C.Functions.register_to_merkle "/tmp/relational-engine/tree" content
-end
diff --git a/rebar.config b/rebar.config
new file mode 100644
index 0000000..88ffb3b
--- /dev/null
+++ b/rebar.config
@@ -0,0 +1,6 @@
+{erl_opts, [debug_info]}.
+{deps, []}.
+
+{shell, [
+ {apps, [main]}
+]}. \ No newline at end of file
diff --git a/rebar.lock b/rebar.lock
new file mode 100644
index 0000000..57afcca
--- /dev/null
+++ b/rebar.lock
@@ -0,0 +1 @@
+[].
diff --git a/relational_engine.opam b/relational_engine.opam
deleted file mode 100644
index b296c11..0000000
--- a/relational_engine.opam
+++ /dev/null
@@ -1,31 +0,0 @@
-# This file is generated by dune, edit dune-project instead
-opam-version: "2.0"
-synopsis: "A short synopsis"
-description: "A longer description"
-maintainer: ["Maintainer Name"]
-authors: ["Author Name"]
-license: "LICENSE"
-tags: ["topics" "to describe" "your" "project"]
-homepage: "https://github.com/username/reponame"
-doc: "https://url/to/documentation"
-bug-reports: "https://github.com/username/reponame/issues"
-depends: [
- "ocaml"
- "dune" {>= "3.16"}
- "odoc" {with-doc}
-]
-build: [
- ["dune" "subst"] {dev}
- [
- "dune"
- "build"
- "-p"
- name
- "-j"
- jobs
- "@install"
- "@runtest" {with-test}
- "@doc" {with-doc}
- ]
-]
-dev-repo: "git+https://github.com/username/reponame.git"
diff --git a/shared/librelational_engine/CMakeLists.txt b/shared/librelational_engine/CMakeLists.txt
deleted file mode 100644
index d22d0d0..0000000
--- a/shared/librelational_engine/CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-cmake_minimum_required(VERSION 3.29)
-project(relational_engine)
-
-include_directories($ENV{LIBRESSL_INCLUDE_PATH})
-include_directories($ENV{MERKLECPP_INCLUDE_PATH})
-
-add_library(relational_engine SHARED library.cpp)
-
-target_link_libraries(relational_engine
- $ENV{LIBRESSL_LIB_PATH}/libssl.$ENV{OS_LIB_EXTENSION}
- $ENV{LIBRESSL_LIB_PATH}/libcrypto.$ENV{OS_LIB_EXTENSION}
-) \ No newline at end of file
diff --git a/shared/librelational_engine/library.cpp b/shared/librelational_engine/library.cpp
deleted file mode 100644
index cab8f93..0000000
--- a/shared/librelational_engine/library.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#include <iostream>
-#include <merklecpp.h>
-#include <openssl/sha.h>
-#include <iomanip>
-#include <fstream>
-#include <iterator>
-
-#include <sys/stat.h>
-
-std::vector<uint8_t> read_tree(const char* tree_path) {
- std::ifstream file(tree_path, std::ios::binary);
- file.unsetf(std::ios::skipws);
- file.seekg(0, std::ios::end);
- const std::streampos file_size = file.tellg();
- file.seekg(0, std::ios::beg);
- std::vector<uint8_t> v;
- v.reserve(file_size);
- v.insert(v.begin(), std::istream_iterator<uint8_t>(file), std::istream_iterator<uint8_t>());
- return v;
-}
-
-void write_tree(const char* tree_path, const std::vector<uint8_t>& serialized_tree) {
- std::ofstream out(tree_path, std::ios::out | std::ios::binary);
- out.write(reinterpret_cast<const char*>(serialized_tree.data()), serialized_tree.size());
- out.close();
-}
-
-extern "C" {
-#include "library.h"
- extern void register_to_merkle(const char* tree_path, const char* computed_hash) {
- struct stat st;
- if (stat(tree_path, &st) == 0) {
- const std::vector<uint8_t> tree_bytes = read_tree(tree_path);
- merkle::Tree tree;
- tree.deserialise(tree_bytes);
- const merkle::Tree::Hash hash(computed_hash);
- tree.insert(hash);
- std::vector<uint8_t> target;
- tree.serialise(target);
- write_tree(tree_path, target);
- } else {
- merkle::Tree::Hash hash(computed_hash);
- merkle::Tree tree;
- tree.insert(hash);
- std::vector<uint8_t> target;
- tree.serialise(target);
- write_tree(tree_path, target);
- }
- }
- extern void merkle_generate_root(char** hashes, const int n, char* root) {
- merkle::Tree tree;
- for (int i = 0; i < n; i++){
- tree.insert(merkle::Hash(hashes[i]));
- }
- std::memcpy(root, tree.root().to_string().c_str(), HASH_SIZE);
- }
- extern void compute_hash(char* computed_hash, const char *content) {
-
- unsigned char hash[SHA256_DIGEST_LENGTH];
- SHA256_CTX sha256;
- SHA256_Init(&sha256);
- SHA256_Update(&sha256, content, sizeof(content));
- SHA256_Final(hash, &sha256);
- std::stringstream ss;
- for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
- ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);
-
- std::memcpy(computed_hash, ss.str().c_str(), HASH_SIZE);
- // std::cout << "⚙️ COMPUTED HASH: " << computed_hash << std::endl;
- }
-
- extern int trash() { return 100; }
-}
diff --git a/shared/librelational_engine/library.h b/shared/librelational_engine/library.h
deleted file mode 100644
index 16734be..0000000
--- a/shared/librelational_engine/library.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef MERKLECPP_STUB_LIBRARY_H
-#define MERKLECPP_STUB_LIBRARY_H
-
-#define HASH_SIZE 64
-
-extern void merkle_generate_root(char**, const int, char*);
-extern void compute_hash(char*, const char*);
-extern void register_to_merkle(const char*, const char*);
-
-#endif //MERKLECPP_STUB_LIBRARY_H
diff --git a/src/main.app.src b/src/main.app.src
new file mode 100644
index 0000000..0b7700a
--- /dev/null
+++ b/src/main.app.src
@@ -0,0 +1,15 @@
+{application, main, [
+ {description, "A relational algebra engine"},
+ {vsn, "0.1.0"},
+ {registered, []},
+ {mod, {main_app, []}},
+ {applications, [
+ kernel,
+ stdlib,
+ mnesia
+ ]},
+ {env, []},
+ {modules, []},
+ {licenses, ["Apache-2.0"]},
+ {links, []}
+ ]}.
diff --git a/src/main.erl b/src/main.erl
new file mode 100644
index 0000000..3c34a31
--- /dev/null
+++ b/src/main.erl
@@ -0,0 +1,7 @@
+-module(main).
+-export([fibonacci/1]).
+
+fibonacci(0) -> 0;
+fibonacci(1) -> 1;
+fibonacci(N) ->
+ fibonacci(N-2) + fibonacci(N-1).
diff --git a/src/operations.erl b/src/operations.erl
new file mode 100644
index 0000000..7846caf
--- /dev/null
+++ b/src/operations.erl
@@ -0,0 +1,15 @@
+-module(operations).
+-export([select/2, project/2, cartesian_product/2]).
+
+%% @doc Selection - filter tuples based on a predicate
+%% `Predicate` is a function that takes a tuple and returns true/false
+select(Relation, Predicate) ->
+ sets:filter(Predicate, Relation).
+
+%% @doc Projection - select specific attributes from a relation
+%% Note that `AttributeNames` can have duplicate keys, which are ignored
+project(Relation, AttributeNames) when is_list(AttributeNames) ->
+ sets:map(fun (Tuple) -> maps:with(AttributeNames, Tuple) end, Relation).
+
+cartesian_product(Relation1, Relation2) ->
+ sets:from_list([maps:merge(Tuple1, Tuple2) || Tuple1 <- sets:to_list(Relation1), Tuple2 <- sets:to_list(Relation2)], [{version, 2}]).
diff --git a/src/relation.erl b/src/relation.erl
new file mode 100644
index 0000000..66930d2
--- /dev/null
+++ b/src/relation.erl
@@ -0,0 +1,42 @@
+-module(relation).
+-export([new_attribute/2, new_tuple/1, new_relation/1]).
+
+%% Attribute: A pair of (Name, Value), eg. ("Name", "Augusto")
+%% Tuple: A set of attributes, eg. (("Name", "Augusto"), ("Age", 20), ("Surname", "Rengel"))
+%% Relation: A set of tuples organized through some selector
+%% Example Relation: f(x: Nat) = x + 1
+%% |{(0, 1), (1, 2), (2, 3), ...}| = |N|
+%% RelationCustomer = [(Username, Text), (Password, Text)]
+%% RelationProduct = [(Name, Text), (Price, Real)]
+%% RelationOrder = [(OrderId, Natural), ((Username, Text),
+%% (Password, Text), RelationCustomer), ((Name, Text),
+%% (Price, Real), RelationProduct)]
+
+new_attribute(Name, Value) ->
+ #{name => Name, value => Value}.
+
+new_tuple(Attributes) when is_list(Attributes) ->
+ Fun = fun (#{name := Name, value := Value}, Acc) ->
+ maps:put(Name, Value, Acc)
+ end,
+ lists:foldl(Fun, #{}, Attributes).
+
+new_relation(Tuples) when is_list(Tuples) ->
+ sets:from_list(Tuples, [{version, 2}]).
+
+%% SoapName1 = relation:new_attribute(name, "ABC").
+%% SoapPrice1 = relation:new_attribute(price, 1.0).
+%% SoapName2 = relation:new_attribute(name, "XYZ").
+%% SoapPrice2 = relation:new_attribute(price, 1.25).
+%% BazookaName = relation:new_attribute(name, "Bazzum").
+%% BazookaPrice = relation:new_attribute(price, 413.0).
+%% SwordName = relation:new_attribute(name, "Sword").
+%% SwordPrice = relation:new_attribute(price, 100.0).
+%% relation:new_relation([relation:new_tuple([SoapName, SoapPrice]), relation:new_tuple([BazookaName, BazookaPrice]), relation:new_tuple([SwordName, SwordPrice])]).
+
+%% Jules = relation:new_attribute(customer_name, "Jules").
+%% Emily = relation:new_attribute(customer_name, "Emily").
+%% Customers = relation:new_relation([relation:new_tuple([Jules]),
+%% relation:new_tuple([Emily])]).
+
+%% operations:project(operations:select(operations:cartesian_product(relation:new_relation([relation:new_tuple([SoapName1, SoapPrice1]), relation:new_tuple([SoapName2, SoapPrice2]), relation:new_tuple([BazookaName, BazookaPrice]), relation:new_tuple([SwordName, SwordPrice])]), Customers), fun (Tuple) -> maps:get(price, Tuple) =< 1.0 end), [name, customer_name]).
diff --git a/test/dune b/test/dune
deleted file mode 100644
index 2f3e5eb..0000000
--- a/test/dune
+++ /dev/null
@@ -1,6 +0,0 @@
-(library
- (name test_relational_engine)
- (libraries relational_engine)
- (inline_tests)
- (preprocess
- (pps ppx_inline_test)))
diff --git a/test/test_relational_engine.ml b/test/test_relational_engine.ml
deleted file mode 100644
index 69a7df2..0000000
--- a/test/test_relational_engine.ml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-let%test _ =
- let open Relational_engine.Disk.Executor in
- let stream: history = [{state = _EMPTY_SHA_HASH_; files = StringMap.empty}] in
- let locations: locations = StringMap.empty in
- let (new_commit, _) = Result.get_ok @@ write (List.hd stream) locations ~filename:"trash" @@ Bytes.of_string "abc" in
- let expected_state_hash = "bca39cc55b19274133ce57d0bea1ff794589f025d4bcf670c44d5d2811837830" in
- new_commit.state = expected_state_hash