on master help sign in
merge upstream 0.8
commit315b3aeb3b1e17a7436fcbdd1ae6b162a1254491 authorhughjfchen <hugh.jf.chen@gmail.com> dateWed 27 May 2020, 11:40 (6 years ago) parenteddf4a34f76414
64 files changed+2974−1465
.travis/install-stack.sh+2−2 Changelog.markdown+4 README.markdown+2 cabal.project+3−3 docs/data_independence.markdown+103 docs/filesystem-persistence.markdown+1−1 docs/tutd_cheatsheet.markdown+97 examples/blog.hs+5−5 examples/hair.hs+5−3 project-m36.cabal+10−6 src/bin/ProjectM36/Server/RemoteCallTypes/Json.hs+6 src/bin/TutorialD/Interpreter/Base.hs+1−2 src/bin/TutorialD/Interpreter/DatabaseContextExpr.hs+9−7 src/bin/TutorialD/Interpreter/RelationalExpr.hs+10−7 src/bin/TutorialD/Interpreter/SchemaOperator.hs+3−3 src/bin/benchmark/bigrel.hs+16−7 src/lib/ProjectM36/AtomType.hs+41−2 src/lib/ProjectM36/AttributeNames.hs+3−2 src/lib/ProjectM36/Base.hs+129−47 src/lib/ProjectM36/Client.hs+95−67 src/lib/ProjectM36/Client/Simple.hs+2−1 src/lib/ProjectM36/DatabaseContext.hs+9−4 src/lib/ProjectM36/DatabaseContextFunctionUtils.hs+15−8 src/lib/ProjectM36/DateExamples.hs+3−3 src/lib/ProjectM36/DisconnectedTransaction.hs+7−1 src/lib/ProjectM36/Error.hs+5 src/lib/ProjectM36/GraphRefRelationalExpr.hs+45 src/lib/ProjectM36/IsomorphicSchema.hs+22−23 src/lib/ProjectM36/NormalizeExpr.hs+132 src/lib/ProjectM36/Notifications.hs+12−7 src/lib/ProjectM36/Relation.hs+2−18 src/lib/ProjectM36/RelationalExpression.hs+987−475 src/lib/ProjectM36/ScriptSession.hs+3−3 src/lib/ProjectM36/Server.hs+1−1 src/lib/ProjectM36/Server/EntryPoints.hs+4−3 src/lib/ProjectM36/Session.hs+3−2 src/lib/ProjectM36/Sessions.hs+1−1 src/lib/ProjectM36/StaticOptimizer.hs+369−249 src/lib/ProjectM36/TransGraphRelationalExpression.hs+137−122 src/lib/ProjectM36/Transaction.hs+19−16 src/lib/ProjectM36/Transaction/Persist.hs+11−24 src/lib/ProjectM36/Transaction/Type.hs+8 src/lib/ProjectM36/TransactionDiffs.hs+12 src/lib/ProjectM36/TransactionGraph.hs+168−141 src/lib/ProjectM36/TransactionGraph/Merge.hs+16−12 src/lib/ProjectM36/TransactionGraph/Persist.hs+6−5 src/lib/ProjectM36/TransactionGraph/Show.hs+3−4 src/lib/ProjectM36/TransactionGraph/Types.hs+12 src/lib/ProjectM36/TransactionInfo.hs+10 src/lib/ProjectM36/WithNameExpr.hs+84 stack.ghc8.8.yaml+54 test/IsomorphicSchema.hs+37−27 test/Relation/Atomable.hs+1−2 test/Relation/Basic.hs+10−4 test/Relation/StaticOptimizer.hs+76−51 test/Relation/Tupleable.hs+1−1 test/Server/Main.hs+2−1 test/Server/WebSocket.hs+3−2 test/TransactionGraph/Merge.hs+60−39 test/TransactionGraph/Persist.hs+8−8 test/TutorialD/Interpreter.hs+50−21 test/TutorialD/Interpreter/AtomFunctionScript.hs+7−7 test/TutorialD/Interpreter/Import/TutorialD.hs+2−2 test/TutorialD/Interpreter/TestBase.hs+10−13
.travis/install-stack.sh+2−2
@@ -10,11 +10,11 @@ travis_retry() {
1010}
1111
1212fetch_stack_osx() {
13− curl -skL https://www.stackage.org/stack/osx-x86_64 | tar xz --strip-components=1 --include '*/stack' -C ~/.local/bin;
13+ curl -skL https://get.haskellstack.org/stable/osx-x86_64.tar.gz | tar xz --strip-components=1 --include '*/stack' -C ~/.local/bin;
1414}
1515
1616fetch_stack_linux() {
17− curl -sL https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack';
17+ curl -sL https://get.haskellstack.org/stable/linux-x86_64.tar.gz | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack';
1818}
1919
2020# We need stack to generate cabal files with precise bounds, even for cabal
Changelog.markdown+4
@@ -1,3 +1,7 @@
1+# 2020-05-25 (v0.8)
2+
3+* refactor backend to maximize laziness: [An Architecture for Data Independence](docs/data_independence.markdown)
4+
15# 2019-09-07 (v0.7)
26
37* fix case sensitivity issue with relation variables in tutd
README.markdown+2
@@ -48,6 +48,7 @@ Project:M36 supports multiple frontends which target different audiences.
4848
49491. [Installation and Introduction to Project:M36](docs/introduction_to_projectm36.markdown)
50501. [Introduction to the Relational Algebra](docs/introduction_to_the_relational_algebra.markdown)
51+1. [TutorialD Cheatsheet](docs/tutd_cheatsheet.markdown)
51521. [TutorialD via Jupyter Notebook Walkthrough](jupyter/TutorialD%20Notebook%20Walkthrough.ipynb)
52531. [TutorialD Tutorial](docs/tutd_tutorial.markdown)
53541. [15 Minute Tutorial](docs/15_minute_tutorial.markdown)
@@ -60,6 +61,7 @@ Project:M36 supports multiple frontends which target different audiences.
60611. [ACID Database Properties](docs/acid_assessment.markdown)
61621. [On NULL (in SQL)](docs/on_null.markdown)
62631. [Reaching "Out of the Tarpit" with Project:M36](docs/reaching_out_of_the_tarpit.markdown)
64+1. [An Architecture for Data Independence](docs/data_independence.markdown)
6365
6466### Advanced Features
6567
cabal.project+3−3
@@ -1,9 +1,9 @@
11-- this is needed for GHC8 until distributed-process-extras is updated on Hackage with increased upper bounds
22packages: project-m36.cabal
3−allow-newer: distributed-process-extras:time,distributed-process-extras:binary,distributed-process-client-server:binary,distributed-process-client-server:time,distributed-process-async:time,distributed-process-async:binary,distributed-process-extras:stm,distributed-process-extras:containers,network-transport-tcp:containers,distributed-process-client-server:stm,distributed-process-client-server:containers,distributed-process-async:stm,distributed-process-async:containers
3+allow-newer: distributed-process-extras:time,distributed-process-extras:binary,distributed-process-client-server:binary,distributed-process-client-server:time,distributed-process-async:time,distributed-process-async:binary,distributed-process-extras:stm,distributed-process-extras:containers,network-transport-tcp:containers,distributed-process-client-server:stm,distributed-process-client-server:containers,distributed-process-async:stm,distributed-process-async:containers,distributed-process:hashable,distributed-process-extras:hashable,distributed-process-client-server:hashable,distributed-process-async:hashable
44
55--necessary for GHC 8.6 support :/
66source-repository-package
77 type: git
8− location: https://github.com/haskell-distributed/distributed-process.git
9− tag: 660d554f6acd2dba8b605c84e8fa69e45708bc14
8+ location: https://github.com/agentm/distributed-process.git
9+ tag: f352e097fbe494ad648b4e09249f865893ae90a3
docs/data_independence.markdownnew file+103
@@ -0,0 +1,103 @@
1+# Project:M36: Towards an Architecture for Data Independence
2+
3+## Introduction
4+
5+The Project:M36 relational algebra architecture sets itself apart from typical DBMSes by ensuring strict transactional isolation, a branchable/mergeable (rather than linear) transaction graph, and storage of relational expressions (rather than storage of tuples). Let's look at how these architectural features work together to make Project:M36 the first relational algebra engine to implement true data independence.
6+
7+## What is Data Independence?
8+
9+C.J. Date writes:
10+
11+`Certainly most SQL products have failed to live up to the [relational] model's full potential...in those products, what the user sees and what is physically stored are essentially the same thing... In particular, it accounts for the argument, so often heard, to the effect that we have to "denormalize for performance."`
12+
13+`In principle, logical design has absolutely nothing to do with performance at all.`
14+
15+(Date, C.J. "Database In Depth: Relational Theory for Practitioners". O'Reilly Media. 2005. pages 156-157.)
16+
17+Data independence is therefore defined as the separation of the database user's experience from the database's means of in-memory or on-disk representation. As Date points out, virtually all database products implement a one-to-one mapping of relations to files in storage.
18+
19+One advantage of high-level abstractions in programming languages is to leverage advanced algorithms which can choose optimization paths on behalf of the user which a human may never be able to discover. While the promise of ever higher-level abstractions is realized by progamming languages such as Haskell, DBMS implementors have been resistant to data independence. This is due to several legacy factors such as companies pushing one-trick-pony database technologies and middlemen such as DBAs required to play gatekeeper against "bad" schema changes. The result is customers forced to run multiple disparate databases such as one for realtime transaction processing and another for analytics and another for audit logging. This recalcitrance has also led to the rise of databases such as "document" JSON databases with weak mathematical underpinnings in the name of performance as users become frustrated when hitting performance walls and dealing with arbitrary database limitations.
20+
21+Implementations which closely tie the user interface to backend representation close the door on interesting alternative representations and optimizations. For example, SQL databases typically store a single relation as a list of tuples on disk with supplementary indexes stored in separate files. There is no reason that the same relation couldn't be on disk or in memory in any number of formats. Such a table might be useful in various formats ideal for servicing different types of queries. Data independence grants the DBMS the permission to create any number of representations- including none at all- as long as the correct answer is provided to the user. "Column-store" databases may try to slice the tuples in a different axis, but those databases simply shift the performance problems elsewhere. Only implementation details prevent a database from offering both column-oriented *and* tuple-oriented representation while allowing the database to choose.
22+
23+That is why it is critical for the user's interface to the database *not* to include features such as "query hints" which would force a specific implementation in the backend. The user's interface must be declarative (describing the state change or query rather than *how* to execute it) and sufficiently high level. Any implementation detail leakage to the user's interface is a lost potential algorithmic optimization. While the topic of providing high-level interfaces is not specific to database design, we will continue to explore the ramifications of data independence in databases.
24+
25+
26+## Transactional Isolation
27+
28+While other databases offer various transaction isolation levels with differing levels of implementation-specific surprises, Project:M36 solely suppports the maximum transaction isolation level. Instead of relying on details of [MVCC](https://www.postgresql.org/docs/current/mvcc-intro.html) which marks each tuple in a relation with a monotonically-increasing number identifying from which transaction the tuple was added or deleted, Project:M36 includes no singular backing store for tuples, instead storing a stream of state change requests. Because the transaction graph supports branching and merging (similar to version control such as git), transactions can be committed without contention at a singular head of the graph. Committing to disk can be implemented as writing a file to represent the transaction's state and
29+
30+## The Transaction Graph
31+
32+These state changes are, in fact, the idempotent relational update expressions that the user has submitted. For example, given a transaction which includes a single relation `s`:
33+
34+```
35+┌──────────┬────────┬───────────┬───────────┐
36+│city::Text│s#::Text│sname::Text│status::Int│
37+├──────────┼────────┼───────────┼───────────┤
38+│"Paris" │"S2" │"Jones" │10 │
39+└──────────┴────────┴───────────┴───────────┘
40+```
41+
42+if the user commits a transaction with an insert such as:
43+
44+```
45+insert x relation{tuple{city "Boston", s# "S3", sname "Smith", status 400}}
46+```
47+
48+then a typical database will find the file representing the relation, find some free space in it and save the new tuple along with the MVCC transaction identifier. Subsequent transactions will find the tuple and compare it to their own transaction id in order to determine its "visibility". Project:M36, on the other hand, after validating that all relational constraints (such as primary and foreign keys) still hold, will simply serialize and append the expression to the transaction graph log. By default, there is no file representing the relation.
49+
50+For those familiar with Haskell, note how serializing the update expression is similar to being able to serialize a thunk or deferred execution.
51+
52+The transaction graph also allows users to travel back to previously-committed transactions. This may be useful for audit logging or alternative schemas.
53+
54+## Storage Representations
55+
56+Given the ever-growing transaction graph based on state changes, let's examine how Project:M36 services subsequent queries.
57+
58+## Benefits
59+
60+The primary goal of a database should be to ensure data integrity. Project:M36 takes no shortcuts in achieving this goal, but choosing a backend architecture which minimizes the time which it takes to get the transaction to non-volatile storage (disk) by storing update expressions rather than attempting to shuffle tuples on disk. This architecture open the door to many optimization techniques, many of which are new to databases.
61+
62+### Multiple On-Disk Representations
63+
64+The promise of data independence allows database backends to store relations as they see fit. That can include any number of on-disk representations including none at all. For example, for a table accessed by multiple projections, the database may choose to materialize the relation in those projections. The representations are therefore created in service to the queries submitted or anticipated instead of being based on past update expressions. Because on-disk storage is obviously limited, the database must decide what is valuable to keep around for future queries. This could open a new area of research into the appropriate machine learning algorithms, but, fundamentally, this set of materialized query responses is effectively a cache which can always be refreshed.
65+
66+The alternative representations need not be a direct answer to queries- for example, a materialized relation from a transaction could be used to service a query from a later transaction if the updates since that transaction are applied.
67+
68+The database could also anticipate, for example, scheduled queries and actually materialize relations before the query is executed. On-disk cache could represent "denormalized" tuple sets while the user-facing interface shows a properly normalized schema, thereby supporting data independence.
69+
70+More research in determining just what should be in the cache is needed.
71+
72+### Transactional Commits in O(1) Time
73+
74+Because the Project:M36 backend need not save tuples into a specific file representing a relation, the backend can drastically reduce the amount of time needed to commit a transaction to disk. Specifically, following constraint validation (which can often be done in constant or logarithmic time relative to the tuple count), the fastest means of committing the transaction is to save the update expression itself. Since the previous state has already been presumably committed, any idempotent operation can be saved for deferred execution. This means that transactions can be committed to disk relative to the size of the update expression rather than the number of the tuples affected- committing can occur at a rate relative to that of update expression size (typically a few kilobytes) rather than relative to the number of tuples that the update expression happened to alter.
75+
76+This technique gets transactions to disk faster, thus drastically increasing the rate transactions can be committed overall. Note that the execution of the update expression may be deferred by this process, so the cost to evaluate it may occur later, but this processing may be performed in the background after the transaction is committed. When the data is never requested, for example, for a defunct transaction graph branch, the actual execution could be deferred indefinitely, incurring zero cost to calculate the consquence of the update.
77+
78+### Less Disk Usage
79+
80+Because Project:M36 stores a stream of update expressions rather than tuples themselves, the minimum storage requirements for a given database may be reduced. For example, if a relation with an attribute `x` of type `Integer` has 1,000,000 tuples, a subsequent update to increment all 1,000,000 values by one may be cheaper to evaluate on-demand rather than immediately generating 1,000,000 new tuples and saving them to disk. If the new tuples are requested, the database can determine that the tuples are not materialized, calculate them, and return them, all without ever having the tuples on disk. Furthermore, if only a subset of the tuples are ever requested, for example, due to a query restriction, then some of the tuples may never need to be calculated or materialized.
81+
82+### Audit Logging
83+
84+Typical databases implement various strategies for audit logging, including:
85+
86+* exposing MVCC-like values such as tuple "created at" and "created by" to the user and forcing all tables to be insert-only
87+* shuttling audit logs to a secondary database
88+
89+Project:M36, through its inherent ability to travel the append-only transaction graph, enables audit-logging by allowing users to checkout previous transaction states. Committed transaction states are immutable, so no relation pollution or secondary database is necessary.
90+
91+### Selective Caching
92+
93+Typical databases materialize entire tuple sets representing each named relation (relation variable), but there is nothing special about relations that happen to be named. Data independence allows us to cache answers to common or even anticipated queries- this allows the schema to be normalized and unmaterialized while the *denormalized* query results are materialized cached. This flexibility saves disk space and allows the cache to be much more dynamic than in a typical DBMS.
94+
95+### Never-Materialized Tuples Replace Partitions
96+
97+Queries on very large relations often very often restricted (filtered by tuple) to generate subsets which are often partitioned, for example, by an event's date. Instead of requiring DBMS users to manually partition and slice their data in one, fixed, particular way, Project:M36's cacheing system will only cache those tuples that serve the interests of the queries. With something like an LRU cache, a data independent architecture will naturally cache "partitions" of the full relation without manual intervention.
98+
99+## Conclusion
100+
101+Project:M36's backend architecture enables new optimizations and features that are sorely lacking in existing DBMSes. By leveraging its novel transaction graph and deferred evaluation, Project:M36 handles more DBMS use-cases.
102+
103+Project:M36's novel architecture opens the door for further database and algorithmic research.
docs/filesystem-persistence.markdown+1−1
@@ -1,4 +1,4 @@
1−# Project-M36 Filesystem Persistence
1+# Project-M36 Filesystem Persistence (Change Proposal)
22
33## Introduction
44
docs/tutd_cheatsheet.markdownnew file+97
@@ -0,0 +1,97 @@
1+# Tutorial D Cheatsheet
2+
3+The following tables list examples of expressions for `tutd`, Project:M36's "Tutorial D" implementation.
4+
5+## Relational Expressions
6+
7+Relational expressions query database state without being able to change it.
8+
9+|Relational Expression |Description |
10+|------------------------------------------------------|---------------------------------------|
11+|`:showexpr p`|Display the relation variable `p`|
12+|`:showexpr s rename {city as town}`|Display the result after renaming a relational attribute|
13+|`:showexpr p{color,city}`|Display the result of the projection on the `color` and `city` attributes|
14+|`:showexpr p{all but city}`|Display the result of the projection of all attribute of `p` except `city`|
15+|`:showexpr p join sp`|Display the result of the relational expression `p` joined against `sp` (equivalent to `NATURAL JOIN` in SQL)|
16+|`:showexpr (s join sp){all from s}`|Display the result of a join but only include attributes from `s`|
17+|`:showexpr s semijoin sp`|Equivalent to `(s join sp){all from s}`|
18+|`:showexpr s antijoin sp`|Display the result of all tuples in `s` which do not match appear in the result of `s semijoin sp`|
19+|`:showexpr s union s`| Display the result of `s` unioned with itself (which is equivalent to `s`)|
20+|`:showexpr s:{status2:=add(10,@status)}`| Display the result of extending the `s` relation variable with a new attribute which adds 10 to each `status` attribute|
21+|`:showexpr s group ({sname,status,s#} as subrel)`| Display the result of grouping the `sname`, `status`, and `s#` into a subrel for each tuple in the `s` relation where the `city` attribute (not mentioned) is the grouping criteria|
22+|`:showexpr (s group ({sname,status,s#} as subrel)) ungroup subrel`| Display the result of unwrapping a subrelation to create one new tuple for each subrelation tuple in the result|
23+|`:showexpr s minus s`|Display the result after removing all tuples that match the second argument. `x minus x` is equivalent to `x where false`|
24+|`:showexpr relation{tuple{name "Mike",age 6},tuple{name "Sam",age 10}}`|Display an unnamed relation using manually constructed tuples|
25+|`:showexpr relation{a Integer, b Text}`|Display an empty-tupled relation of a specified type|
26+|`:showexpr relation{a Integer, b Text}{tuple{a 4, b "four"}}`|Display a relation bound by the specified types and containing one or more tuples|
27+|`:showexpr s group ({s#,sname,status} as subrel):{citycount:=count(@subrel)}`|Display a result which groups `s` by `city` and extends the relation by a count for each city.|
28+|`:showrelvars`|Display a list of relation variables in the current context along with their types (attributes)|
29+
30+## Built-In Types
31+
32+|Example Value Expression |Type |Explanation|
33+|------------------------------------------------------|---------------------------------------|-----------|
34+|`"abc"`|Text|UTF-8 string|
35+|`123`|Integer|an integer of limitless size (using gmp)|
36+|`int(456)`|Int|machine-word-based integer|
37+|`12.56`|Double|64-bit IEEE 754 floating point number|
38+|`dateTimeFromEpochSeconds(1502304846)`|DateTime||
39+|`fromGregorian(2017,05,30)`|Date||
40+|`t`|Bool|`t` or `f`|
41+|`bytestring("dGVzdGRhdGE=")`|Bytestring|base-64-encoded string of bytes|
42+|`interval(3,5,f,f)`|Interval(Integer)|the constructor function includes two bounds and two boolean flags for inclusiveness|
43+
44+## Database Context Expressions
45+
46+Database context expressions take the current database context as input and alter it to create a new
47+
48+|Database Context Expression |Description |
49+|------------------------------------------------------|---------------------------------------|
50+|`:importexample date`|Imports a precooked schema from Chris Date's books resulting in relvars `s`, `sp`, and `p`|
51+|`newrelvar:=relation{tuple{age 3}}`|Assign a new relation variable named `newrelvar`|
52+|`undefine s`|Remove a relation variable|
53+|`insert s relation{tuple{city "Boston",s# "S10",sname "Gonzalez",status 10}}`| Append to an existing relation variable with another relational expression|
54+|`update s where status=20 (sname:="Mr. Twenty")`|Replace one or more values in matching tuples with a new value|
55+|`delete sp where s#="S4"`|Remove tuples matching the criteria|
56+|`key s_key_constraint {s#} s`|Add a constraint named `s_key_constraint` on relation variable `s` ensuring that `s#` is unique|
57+|`foreign key s#_in_sp sp{s#} in s{s#}`|Add a foreign key constraint that ensures that `sp`'s `s#` attribute always references a `s#` value in `s`|
58+|`constraint s_status_less_than_50 (s where ^lt(50,@status)){} in false`|Add a constraint that stipulates that the `status` values in `s` are less than 50|
59+|`createarbitraryrelation employee {name Text, empid Int, hired DateTime} 3-100`|Create a relation variable with random values with between 3-100 tuples- useful for testing|
60+|`notify steve_change person where name="Steve" true (person where name="Steve"){address}`|Notify the current connection with an asynchronous event if any committed tuples matching `person where name="Steve"` with `true` for the pre-change state and `person where name="Steve"){address}` for the post-change state- these states will be sent along with the notification|
61+|`data Hair = Bald | Brown | Blond | OtherColor Text`|Create a new algebraic data type for use with values inside tuples|
62+|`undata Hair`|Delete the `Hair` custom type|
63+
64+
65+## Transaction Graph Expressions
66+
67+Transaction graph expressions alter the state of the transaction graph by append new states.
68+
69+|Transaction Graph Expression |Description |
70+|------------------------------------------------------|---------------------------------------|
71+|`:commit`|Save the current database context to a new transaction and add it to the transaction graph|
72+|`:rollback`|Return the database context to the state before any alterations were made|
73+|`:branch experimental`|Create a new branch from the existing branch and switch the current context to be able to commit to the new branch|
74+|`:jumphead master`|Discard any changes in the current context and jump to the head of a different branch|
75+|`:jump 11b601c9-f46b-42b0-a2ce-cf18ced31b7f`|Jump to the specific transaction by UUID- useful for introspecting past states|
76+|`:showgraph`|Display a representation of the transaction graph|
77+|`:walkbacktotime "2017-08-10 16:44:00"`|Walk back in time from the current branch until a transaction less than or equal to the time is found and switch the current context to that past transaction- useful for auditing|
78+
79+## Data Frame Expressions
80+
81+In contrast to relational expressions, data frame expressions generate ordered tuples.
82+
83+|Data Frame Expression |Description |
84+|------------------------------------------------------|---------------------------------------|
85+|`:showdataframe s orderby {status}`|Display a data frame result for the relational expression `s` and sort the tuples by `status`|
86+|`:showdataframe s{status} orderby {status descending} offset 1 limit 3`|Display a data frame result for `s{status}` but ordered by `status` values descending, skip the first tuple, and return no more than 3 tuples|
87+
88+## Trans-Graph Relational Expressions
89+
90+Trans-graph relational expressions allow queries spanning multiple transactions.
91+
92+|Trans-Graph Relational Expressions |Description |
93+|------------------------------------------------------|---------------------------------------|
94+|`:showtransgraphexpr s@master join sp@master`|Display a result for joining `s` and `sp` on the branch `master`|
95+|`:showtransgraphexpr s@master~ join sp@master`|Display a result for joining `s` on the parent transaction of `master`'s current head and `sp` on master|
96+|`:showtransgraphexpr s@master~^2`|Display a result for `s` two parents previous to the parent of the current `master`'s head|
97+
examples/blog.hs+5−5
@@ -35,7 +35,7 @@ import Data.Time.Format
3535data Blog = Blog {
3636 title :: T.Text,
3737 entry :: T.Text,
38− stamp :: UTCTime,
38+ tstamp :: UTCTime,
3939 category :: Category --note that this type is an algebraic data type
4040 }
4141 deriving (Generic, Show) --derive Generic so that Tupleable can use default instances
@@ -105,11 +105,11 @@ insertSampleData :: SessionId -> Connection -> IO ()
105105insertSampleData sessionId conn = do
106106 let blogs = [Blog { title = "Haskell Lenses",
107107 entry = "I wear Haskell rose-colored lenses.",
108− stamp = UTCTime (fromGregorian 2017 5 8) (secondsToDiffTime 1000),
108+ tstamp = UTCTime (fromGregorian 2017 5 8) (secondsToDiffTime 1000),
109109 category = Food },
110110 Blog { title = "Haskell Monad Analogy",
111111 entry = "Monads are like burritos going through intestines.",
112− stamp = UTCTime (fromGregorian 2017 6 10) (secondsToDiffTime 2000),
112+ tstamp = UTCTime (fromGregorian 2017 6 10) (secondsToDiffTime 2000),
113113 category = Cats }
114114 ]
115115 comments = [Comment { blogTitle = "Haskell Lenses",
@@ -138,7 +138,7 @@ listBlogs sessionId conn = do
138138 Left err -> render500 (toHtml (show err))
139139 Right blogRel -> do
140140 blogs <- liftIO (toList blogRel) >>= mapM (handleWebError . fromTuple) :: ActionM [Blog]
141− let sortedBlogs = sortBy (\b1 b2 -> stamp b1 `compare` stamp b2) blogs
141+ let sortedBlogs = sortBy (\b1 b2 -> tstamp b1 `compare` tstamp b2) blogs
142142 html . renderHtml $ do
143143 h1 "Blog Posts"
144144 forM_ sortedBlogs $ \blog -> a ! href (toValue $ "/blog/" <> title blog) $ h2 (toHtml (title blog))
@@ -184,7 +184,7 @@ showBlogEntry sessionId conn = do
184184 render $ do
185185 --show blog details
186186 h1 (toHtml (title blog))
187− p (toHtml ("Posted at " <> formatStamp (stamp blog) <> " under " <> show (category blog)))
187+ p (toHtml ("Posted at " <> formatStamp (tstamp blog) <> " under " <> show (category blog)))
188188 p (toHtml (entry blog))
189189 hr
190190 h3 "Comments"
examples/hair.hs+5−3
@@ -31,11 +31,13 @@ main = do
3131
3232 --create a relation with the new Hair AtomType
3333 let blond = NakedAtomExpr (toAtom Blond)
34− eCheck $ executeDatabaseContextExpr sessionId conn (Assign "people" (MakeRelationFromExprs Nothing [
35− TupleExpr (M.fromList [("hair", blond), ("name", NakedAtomExpr (TextAtom "Colin"))])]))
34+ eCheck $ executeDatabaseContextExpr sessionId conn
35+ (Assign "people"
36+ (MakeRelationFromExprs Nothing $ TupleExprs () [
37+ TupleExpr (M.fromList [("hair", blond), ("name", NakedAtomExpr (TextAtom "Colin"))])]))
3638
3739 let restrictionPredicate = AttributeEqualityPredicate "hair" blond
3840 peopleRel <- eCheck $ executeRelationalExpr sessionId conn (Restrict restrictionPredicate (RelationVariable "people" ()))
3941
4042 TIO.putStrLn (showRelation peopleRel)
41−
43+
project-m36.cabal+10−6
@@ -1,5 +1,5 @@
11Name: project-m36
2−Version: 0.7
2+Version: 0.8
33License: PublicDomain
44Build-Type: Simple
55Homepage: https://github.com/agentm/project-m36
@@ -34,9 +34,9 @@ Flag haskell-scripting
3434 Default: True
3535
3636Library
37− Build-Depends: base>=4.8 && < 4.13, ghc-paths, mtl, containers, unordered-containers, hashable, haskeline, directory, MonadRandom, random-shuffle, uuid >= 1.3.12, cassava >= 0.4.5.1 && < 0.6, text, bytestring, deepseq, deepseq-generics, vector, parallel, monad-parallel, exceptions, transformers, gnuplot, binary, filepath, zlib, directory, vector-binary-instances, temporary, stm, time, hashable-time, old-locale, rset, attoparsec, either, base64-bytestring, data-interval, extended-reals, network-transport, aeson >= 1.1, path-pieces, conduit, resourcet, http-api-data, semigroups, QuickCheck, quickcheck-instances, distributed-process-client-server >= 0.2.3, distributed-process >= 0.7.4, distributed-process-extras >= 0.3.2, distributed-process-async >= 0.2.4.1, network-transport-tcp >= 0.6.0, network-transport, list-t, stm-containers >= 0.2.15, foldl, optparse-applicative, Glob, cryptohash-sha256
37+ Build-Depends: base>=4.8 && < 4.14, ghc-paths, mtl, containers, unordered-containers, hashable, haskeline, directory, MonadRandom, random-shuffle, uuid >= 1.3.12, cassava >= 0.4.5.1 && < 0.6, text, bytestring, deepseq, deepseq-generics, vector, parallel, monad-parallel, exceptions, transformers, gnuplot, binary, filepath, zlib, directory, vector-binary-instances, temporary, stm, time, hashable-time, old-locale, rset, attoparsec, either, base64-bytestring, data-interval, extended-reals, network-transport, aeson >= 1.1, path-pieces, conduit, resourcet, http-api-data, semigroups, QuickCheck, quickcheck-instances, distributed-process-client-server >= 0.2.3, distributed-process >= 0.7.4, distributed-process-extras >= 0.3.2, distributed-process-async >= 0.2.4.1, network-transport-tcp >= 0.6.0, network-transport, list-t, stm-containers >= 0.2.15, foldl, optparse-applicative, Glob, cryptohash-sha256
3838 if flag(haskell-scripting)
39− Build-Depends: ghc >= 8.2 && < 8.7
39+ Build-Depends: ghc >= 8.2 && < 8.9
4040 CPP-Options: -DPM36_HASKELL_SCRIPTING
4141 if impl(ghc>= 8) && flag(haskell-scripting)
4242 build-depends:
@@ -115,7 +115,11 @@ Library
115115 ProjectM36.TypeConstructorDef,
116116 ProjectM36.FileLock,
117117 ProjectM36.FSType,
118− ProjectM36.Arbitrary
118+ ProjectM36.Arbitrary,
119+ ProjectM36.GraphRefRelationalExpr,
120+ ProjectM36.NormalizeExpr,
121+ ProjectM36.TransactionInfo,
122+ ProjectM36.WithNameExpr
119123 GHC-Options: -Wall -rdynamic
120124 if os(windows)
121125 Build-Depends: Win32 >= 2.5.4.1
@@ -134,7 +138,7 @@ Library
134138
135139Executable tutd
136140 if flag(haskell-scripting)
137− Build-Depends: ghc >= 8.2 && < 8.7
141+ Build-Depends: ghc >= 8.2 && < 8.9
138142 Build-Depends: base >=4.8 && <5.0,
139143 ghc-paths,
140144 project-m36,
@@ -199,7 +203,7 @@ Executable tutd
199203
200204Executable project-m36-server
201205 if flag(haskell-scripting)
202− Build-Depends: ghc >= 8.2 && < 8.7
206+ Build-Depends: ghc >= 8.2 && < 8.9
203207 Build-Depends: base,
204208 ghc-paths,
205209 project-m36,
src/bin/ProjectM36/Server/RemoteCallTypes/Json.hs+6
@@ -22,6 +22,9 @@ instance FromJSON RelationalExpr
2222instance ToJSON TupleExpr
2323instance FromJSON TupleExpr
2424
25+instance ToJSON TupleExprs
26+instance FromJSON TupleExprs
27+
2528instance ToJSON RestrictionPredicateExpr
2629instance FromJSON RestrictionPredicateExpr
2730
@@ -149,3 +152,6 @@ instance FromJSON PersistenceError
149152
150153instance ToJSON AtomFunctionError
151154instance FromJSON AtomFunctionError
155+
156+instance ToJSON WithNameExpr
157+instance FromJSON WithNameExpr
src/bin/TutorialD/Interpreter/Base.hs+1−2
@@ -50,7 +50,6 @@ anySingle :: Parsec Void Text (Token Text)
5050anySingle = anyChar
5151#endif
5252
53−
5453displayOpResult :: TutorialDOperatorResult -> IO ()
5554displayOpResult QuitResult = return ()
5655displayOpResult (DisplayResult out) = TIO.putStrLn out
@@ -222,4 +221,4 @@ utcTimeP = do
222221 timeStr <- quotedString
223222 case parseTimeM True defaultTimeLocale "%Y-%m-%d %H:%M:%S" (T.unpack timeStr) of
224223 Nothing -> fail "invalid datetime input, use \"YYYY-MM-DD HH:MM:SS\""
225− Just stamp -> pure stamp
224+ Just stamp' -> pure stamp'
src/bin/TutorialD/Interpreter/DatabaseContextExpr.hs+9−7
@@ -6,7 +6,6 @@ import qualified Data.Text as T
66import TutorialD.Interpreter.RelationalExpr
77import TutorialD.Interpreter.Types
88import qualified Data.Map as M
9−import Control.Monad.State
109import ProjectM36.StaticOptimizer
1110import qualified ProjectM36.Error as PM36E
1211import ProjectM36.Error
@@ -192,19 +191,22 @@ executeDatabaseContextFunctionP = do
192191databaseExprOpP :: Parser DatabaseContextExpr
193192databaseExprOpP = multipleDatabaseContextExprP
194193
194+{-
195195evalDatabaseContextExpr :: Bool -> DatabaseContext -> DatabaseContextExpr -> Either RelationalError DatabaseContext
196196evalDatabaseContextExpr useOptimizer context expr = do
197197 optimizedExpr <- evalState (applyStaticDatabaseOptimization expr) (RE.freshDatabaseState context)
198198 case runState (RE.evalDatabaseContextExpr (if useOptimizer then optimizedExpr else expr)) (RE.freshDatabaseState context) of
199199 (Right (), (context',_, _)) -> Right context'
200200 (Left err, _) -> Left err
201+-}
201202
202−
203−interpretDatabaseContextExpr :: DatabaseContext -> T.Text -> Either RelationalError DatabaseContext
204−interpretDatabaseContextExpr context tutdstring = case parse databaseExprOpP "" tutdstring of
205− Left err -> Left $ PM36E.ParseError (T.pack (show err))
206− Right parsed ->
207− evalDatabaseContextExpr True context parsed
203+interpretDatabaseContextExpr :: DatabaseContext -> TransactionId -> TransactionGraph -> T.Text -> Either RelationalError DatabaseContext
204+interpretDatabaseContextExpr context transId graph tutdstring =
205+ case parse databaseExprOpP "" tutdstring of
206+ Left err -> Left $ PM36E.ParseError (T.pack (show err))
207+ Right parsed -> do
208+ let env = RE.mkDatabaseContextEvalEnv transId graph
209+ RE.dbc_context <$> RE.runDatabaseContextEvalMonad context env (optimizeAndEvalDatabaseContextExpr True parsed)
208210
209211{-
210212--no optimization
src/bin/TutorialD/Interpreter/RelationalExpr.hs+10−7
@@ -1,5 +1,4 @@
11{-# LANGUAGE CPP #-}
2−
32module TutorialD.Interpreter.RelationalExpr where
43import Text.Megaparsec
54#if MIN_VERSION_megaparsec(7,0,0)
@@ -30,7 +29,8 @@ makeRelationP = do
3029 reserved "relation"
3130 attrExprs <- try (fmap Just makeAttributeExprsP) <|> pure Nothing
3231 tupleExprs <- braces (sepBy tupleExprP comma) <|> pure []
33− pure $ MakeRelationFromExprs attrExprs tupleExprs
32+ marker <- parseMarkerP
33+ pure $ MakeRelationFromExprs attrExprs (TupleExprs marker tupleExprs)
3434
3535
3636--abstract data type parser- in this context, the type constructor must not include any type arguments
@@ -251,12 +251,15 @@ relationAtomExprP = RelationAtomExpr <$> makeRelationP
251251withMacroExprP :: RelationalMarkerExpr a => Parser (RelationalExprBase a)
252252withMacroExprP = do
253253 reservedOp "with"
254− views <- parens (sepBy1 createViewP comma)
254+ views <- parens (sepBy1 createMacroP comma)
255255 With views <$> relExprP
256256
257−createViewP :: RelationalMarkerExpr a => Parser (RelVarName, RelationalExprBase a)
258−createViewP = do
259− name <- relVarNameP
257+createMacroP :: RelationalMarkerExpr a => Parser (WithNameExprBase a, RelationalExprBase a)
258+createMacroP = do
259+ name <- identifier
260260 reservedOp "as"
261261 expr <- relExprP
262− pure (name, expr)
262+ marker <- parseMarkerP
263+ pure (WithNameExpr name marker, expr)
264+
265+
src/bin/TutorialD/Interpreter/SchemaOperator.hs+3−3
@@ -42,7 +42,7 @@ isoRestrictP = do
4242 reserved "isorestrict"
4343 relVarIn <- qrelVarP
4444 relvarsOut <- isoRestrictOutRelVarsP
45− IsoRestrict <$> pure relVarIn <*> restrictionPredicateP <*> pure relvarsOut
45+ IsoRestrict relVarIn <$> restrictionPredicateP <*> pure relvarsOut
4646
4747isoRestrictOutRelVarsP :: Parser (RelVarName, RelVarName)
4848isoRestrictOutRelVarsP = (,) <$> qrelVarP <*> qrelVarP
@@ -55,7 +55,7 @@ isoUnionP = do
5555 reserved "isounion"
5656 relVarsIn <- isoUnionInRelVarsP
5757 relVarsOut <- qrelVarP
58− IsoUnion <$> pure relVarsIn <*> restrictionPredicateP <*> pure relVarsOut
58+ IsoUnion relVarsIn <$> restrictionPredicateP <*> pure relVarsOut
5959
6060isoRenameP :: Parser SchemaIsomorph
6161isoRenameP = do
@@ -74,4 +74,4 @@ isoUnionInRelVarsP = (,) <$> qrelVarP <*> qrelVarP
7474evalSchemaOperator :: SessionId -> Connection -> SchemaOperator -> IO (Either RelationalError ())
7575evalSchemaOperator sessionId conn (ModifySchemaExpr expr) = executeSchemaExpr sessionId conn expr
7676evalSchemaOperator sessionId conn (SetCurrentSchema sname) = setCurrentSchemaName sessionId conn sname
77−
77+
src/bin/benchmark/bigrel.hs+16−7
@@ -4,28 +4,30 @@ import ProjectM36.Base
44import ProjectM36.Relation
55import ProjectM36.DateExamples
66import ProjectM36.Error
7+import ProjectM36.StaticOptimizer
78import qualified ProjectM36.Attribute as A
89import qualified Data.Text as T
910--import ProjectM36.Relation.Show.CSV
1011import ProjectM36.Relation.Show.HTML
11−import TutorialD.Interpreter.DatabaseContextExpr (interpretDatabaseContextExpr)
1212import ProjectM36.RelationalExpression
13+import ProjectM36.TransactionGraph
1314--import qualified Data.HashSet as HS
1415--import qualified Data.ByteString.Lazy.Char8 as BS
1516--import qualified Data.IntMap as IM
1617--import qualified Data.Hashable as Hash
1718import qualified Data.Vector as V
1819import Options.Applicative
19−import qualified Data.Map as M
2020import qualified Data.Text.IO as TIO
2121import System.IO
22−import Control.Monad.State
2322import Control.DeepSeq
2423import Data.Text hiding (map)
24+import Data.Time.Clock
25+import Data.UUID.V4
2526#if __GLASGOW_HASKELL__ < 804
2627import Data.Monoid
2728#endif
2829
30+
2931{-
3032dumpcsv :: Relation -> IO ()
3133dumpcsv rel = case relationAsCSV rel of
@@ -62,13 +64,20 @@ matrixRun (BigrelArgs attributeCount tupleCount tutd) =
6264 Right rel -> if tutd == "" then
6365 putStrLn "Done."
6466 else do
67+ now <- getCurrentTime
68+ tid <- nextRandom
6569 let setx = Assign "x" (ExistingRelation (force rel))
66− (context,_,_) = execState (evalDatabaseContextExpr setx) (freshDatabaseState dateExamples)
67− interpreted = interpretDatabaseContextExpr context tutd
70+ graph = bootstrapTransactionGraph now tid dateExamples
71+ env = mkDatabaseContextEvalEnv tid graph
72+ eNewState = runDatabaseContextEvalMonad dateExamples env (optimizeAndEvalDatabaseContextExpr True setx)
6873 --plan = interpretRODatabaseContextOp context $ ":showplan " ++ tutd
6974 --displayOpResult plan
70− case interpreted of
71− Right context' -> TIO.putStrLn $ relationAsHTML (relationVariables context' M.! "x")
75+ case eNewState of
76+ Right newState -> do
77+ let ctx = dbc_context newState
78+ Right x = optimizeAndEvalRelationalExpr env' (RelationVariable "x" ())
79+ env' = RelationalExprEnv ctx graph Nothing
80+ TIO.putStrLn $ relationAsHTML x
7281 Left err -> hPrint stderr err
7382
7483{-
src/lib/ProjectM36/AtomType.hs+41−2
@@ -221,7 +221,15 @@ findTypeConstructor name = foldr tConsFolder Nothing
221221 Just (tCons, dConsList)
222222 else
223223 accum
224−
224+
225+resolveAttributes :: Attribute -> Attribute -> Either RelationalError Attribute
226+resolveAttributes attrA attrB =
227+ if A.attributeName attrA /= A.attributeName attrB then
228+ Left $ AttributeNamesMismatchError (S.fromList (map A.attributeName [attrA, attrB]))
229+ else
230+ Attribute (A.attributeName attrA) <$> resolveAtomType (A.atomType attrA) (A.atomType attrB)
231+
232+--given two atom types, try to resolve type variables
225233resolveAtomType :: AtomType -> AtomType -> Either RelationalError AtomType
226234resolveAtomType (ConstructedAtomType tConsName resolvedTypeVarMap) (ConstructedAtomType _ unresolvedTypeVarMap) =
227235 ConstructedAtomType tConsName <$> resolveAtomTypesInTypeVarMap resolvedTypeVarMap unresolvedTypeVarMap
@@ -252,7 +260,7 @@ resolveAtomTypesInTypeVarMap resolvedTypeMap unresolvedTypeMap = do
252260 let resolveTypePair resKey resType =
253261 -- if the key is missing in the unresolved type map, then fill it in with the value from the resolved map
254262 case M.lookup resKey unresolvedTypeMap of
255− Just unresType -> case unresType of
263+ Just unresType -> case unresType of
256264 --do we need to recurse for RelationAtomType?
257265 subType@(ConstructedAtomType _ _) -> do
258266 resSubType <- resolveAtomType resType subType
@@ -310,6 +318,15 @@ validateAtomType (RelationAtomType attrs) tConss =
310318validateAtomType (TypeVariableType x) _ = Left (TypeConstructorTypeVarMissing x)
311319validateAtomType _ _ = pure ()
312320
321+validateAttributes :: TypeConstructorMapping -> Attributes -> Either RelationalError ()
322+validateAttributes tConss attrs =
323+ if not (null errs) then
324+ Left (someErrors errs)
325+ else
326+ pure ()
327+ where
328+ errs = lefts $ map ((`validateAtomType` tConss) . A.atomType) (V.toList attrs)
329+
313330--ensure that all type vars are fully resolved
314331validateTypeVarMap :: TypeVarMap -> TypeConstructorMapping -> Either RelationalError ()
315332validateTypeVarMap tvMap tConss = mapM_ (`validateAtomType` tConss) $ M.elems tvMap
@@ -414,3 +431,25 @@ resolvedAtomTypeForDataConstructorDefArg _ tvMap (DataConstructorDefTypeVarNameA
414431 Nothing -> Left (DataConstructorUsesUndeclaredTypeVariable tvName)
415432 Just typ -> Right typ
416433
434+isResolvedType :: AtomType -> Bool
435+isResolvedType typ =
436+ case typ of
437+ IntAtomType -> True
438+ IntegerAtomType -> True
439+ DoubleAtomType -> True
440+ TextAtomType -> True
441+ DayAtomType -> True
442+ DateTimeAtomType -> True
443+ ByteStringAtomType -> True
444+ BoolAtomType -> True
445+ RelationAtomType attrs -> isResolvedAttributes attrs
446+ ConstructedAtomType _ tvMap -> all isResolvedType (M.elems tvMap)
447+ TypeVariableType _ -> False
448+
449+isResolvedAttributes :: Attributes -> Bool
450+isResolvedAttributes attrs = all isResolvedAttribute (V.toList attrs)
451+
452+isResolvedAttribute :: Attribute -> Bool
453+isResolvedAttribute = isResolvedType . A.atomType
454+
455+--given two AtomTypes x,y
src/lib/ProjectM36/AttributeNames.hs+3−2
@@ -3,8 +3,9 @@ import ProjectM36.Base
33import qualified Data.Set as S
44--AttributeNames is a data structure which can represent inverted projection attributes and attribute names derived from relational expressions
55
6−empty :: AttributeNames
6+empty :: AttributeNamesBase a
77empty = AttributeNames S.empty
88
9−all :: AttributeNames
9+all :: AttributeNamesBase a
1010all = InvertedAttributeNames S.empty
11+
src/lib/ProjectM36/Base.hs+129−47
@@ -1,4 +1,4 @@
1−{-# LANGUAGE ExistentialQuantification,DeriveGeneric,DeriveAnyClass,FlexibleInstances,OverloadedStrings #-}
1+{-# LANGUAGE ExistentialQuantification,DeriveGeneric,DeriveAnyClass,FlexibleInstances,OverloadedStrings, DeriveTraversable #-}
22{-# OPTIONS_GHC -fno-warn-orphans #-}
33
44module ProjectM36.Base where
@@ -13,6 +13,7 @@ import Data.UUID (UUID)
1313import Control.DeepSeq (NFData, rnf)
1414import Control.DeepSeq.Generics (genericRnf)
1515import GHC.Generics (Generic)
16+import GHC.Stack
1617import qualified Data.Vector as V
1718import qualified Data.List as L
1819import Data.Text (Text,unpack)
@@ -24,6 +25,7 @@ import Data.Time.Calendar (Day,toGregorian,fromGregorian)
2425import Data.Hashable.Time ()
2526import Data.Typeable
2627import Data.ByteString (ByteString)
28+import qualified Data.List.NonEmpty as NE
2729type StringType = Text
2830
2931-- | Database atoms are the smallest, undecomposable units of a tuple. Common examples are integers, text, or unique identity keys.
@@ -79,7 +81,8 @@ data AtomType = IntAtomType |
7981 deriving (Eq, NFData, Generic, Binary, Show)
8082
8183instance Ord AtomType where
82− compare = undefined
84+ compare = undefined
85+
8386type TypeVarMap = M.Map TypeVarName AtomType
8487
8588instance Hashable TypeVarMap where
@@ -182,7 +185,7 @@ type RelationalExpr = RelationalExprBase ()
182185-- | A relational expression represents query (read) operations on a database.
183186data RelationalExprBase a =
184187 --- | Create a relation from tuple expressions.
185− MakeRelationFromExprs (Maybe [AttributeExprBase a]) [TupleExprBase a] |
188+ MakeRelationFromExprs (Maybe [AttributeExprBase a]) (TupleExprsBase a) |
186189 --- | Create and reference a relation from attributes and a tuple set.
187190 MakeStaticRelation Attributes RelationTupleSet |
188191 --- | Reference an existing relation in Haskell-space.
@@ -191,7 +194,7 @@ data RelationalExprBase a =
191194 --in Tutorial D, relational variables pick up the type of the first relation assigned to them
192195 --relational variables should also be able to be explicitly-typed like in Haskell
193196 --- | Reference a relation variable by its name.
194− RelationVariable RelVarName a |
197+ RelationVariable RelVarName a |
195198 --- | Create a projection over attribute names. (Note that the 'AttributeNames' structure allows for the names to be inverted.)
196199 Project (AttributeNamesBase a) (RelationalExprBase a) |
197200 --- | Create a union of two relational expressions. The expressions should have identical attributes.
@@ -214,11 +217,20 @@ data RelationalExprBase a =
214217 Extend (ExtendTupleExprBase a) (RelationalExprBase a) |
215218 --Summarize :: AtomExpr -> AttributeName -> RelationalExpr -> RelationalExpr -> RelationalExpr -- a special case of Extend
216219 --Evaluate relationalExpr with scoped views
217− With [(RelVarName,RelationalExprBase a)] (RelationalExprBase a)
218− deriving (Show, Eq, Generic, NFData)
220+ With [(WithNameExprBase a, RelationalExprBase a)] (RelationalExprBase a)
221+ deriving (Show, Eq, Generic, NFData, Foldable, Functor, Traversable)
219222
220223instance Binary RelationalExpr
221−
224+
225+data WithNameExprBase a = WithNameExpr RelVarName a
226+ deriving (Show, Eq, Generic, NFData, Foldable, Functor, Traversable)
227+
228+type WithNameExpr = WithNameExprBase ()
229+
230+instance Binary WithNameExpr
231+
232+type GraphRefWithNameExpr = WithNameExprBase GraphRefTransactionMarker
233+
222234type NotificationName = StringType
223235type Notifications = M.Map NotificationName Notification
224236
@@ -262,7 +274,14 @@ data DataConstructorDefArg = DataConstructorDefTypeConstructorArg TypeConstructo
262274 deriving (Show, Generic, Binary, Eq, NFData)
263275
264276type InclusionDependencies = M.Map IncDepName InclusionDependency
265−type RelationVariables = M.Map RelVarName Relation
277+type RelationVariables = M.Map RelVarName GraphRefRelationalExpr
278+
279+data GraphRefTransactionMarker = TransactionMarker TransactionId |
280+ UncommittedContextMarker
281+ deriving (Eq, Show, Binary, Generic, NFData, Ord)
282+
283+-- a fundamental relational expr to which other relational expressions compile
284+type GraphRefRelationalExpr = RelationalExprBase GraphRefTransactionMarker
266285
267286type SchemaName = StringType
268287
@@ -305,15 +324,21 @@ type AttributeNameAtomExprMap = M.Map AttributeName AtomExpr
305324--used for returning information about individual expressions
306325type DatabaseContextExprName = StringType
307326
327+type DatabaseContextExpr = DatabaseContextExprBase ()
328+
329+type GraphRefDatabaseContextExpr = DatabaseContextExprBase GraphRefTransactionMarker
330+
331+instance Binary GraphRefDatabaseContextExpr
332+
308333-- | Database context expressions modify the database context.
309−data DatabaseContextExpr =
334+data DatabaseContextExprBase a =
310335 NoOperation |
311− Define RelVarName [AttributeExpr] |
336+ Define RelVarName [AttributeExprBase a] |
312337 Undefine RelVarName | --forget existence of relvar X
313− Assign RelVarName RelationalExpr |
314− Insert RelVarName RelationalExpr |
315− Delete RelVarName RestrictionPredicateExpr |
316− Update RelVarName AttributeNameAtomExprMap RestrictionPredicateExpr |
338+ Assign RelVarName (RelationalExprBase a) |
339+ Insert RelVarName (RelationalExprBase a) |
340+ Delete RelVarName (RestrictionPredicateExprBase a) |
341+ Update RelVarName AttributeNameAtomExprMap (RestrictionPredicateExprBase a) |
317342
318343 AddInclusionDependency IncDepName InclusionDependency |
319344 RemoveInclusionDependency IncDepName |
@@ -329,24 +354,33 @@ data DatabaseContextExpr =
329354
330355 RemoveDatabaseContextFunction DatabaseContextFunctionName |
331356
332− ExecuteDatabaseContextFunction DatabaseContextFunctionName [AtomExpr] |
357+ ExecuteDatabaseContextFunction DatabaseContextFunctionName [AtomExprBase a] |
333358
334− MultipleExpr [DatabaseContextExpr]
335− deriving (Show, Eq, Binary, Generic)
359+ MultipleExpr [DatabaseContextExprBase a]
360+ deriving (Show, Eq, Generic)
361+
362+instance Binary DatabaseContextExpr
336363
337364type ObjModuleName = StringType
338365type ObjFunctionName = StringType
339366type Range = (Int,Int)
340367-- | Adding an atom function should be nominally a DatabaseExpr except for the fact that it cannot be performed purely. Thus, we create the DatabaseContextIOExpr.
341−data DatabaseContextIOExpr = AddAtomFunction AtomFunctionName [TypeConstructor] AtomFunctionBodyScript |
342− LoadAtomFunctions ObjModuleName ObjFunctionName FilePath |
343− AddDatabaseContextFunction DatabaseContextFunctionName [TypeConstructor] DatabaseContextFunctionBodyScript |
344− LoadDatabaseContextFunctions ObjModuleName ObjFunctionName FilePath |
345− CreateArbitraryRelation RelVarName [AttributeExpr] Range
368+data DatabaseContextIOExprBase a =
369+ AddAtomFunction AtomFunctionName [TypeConstructor] AtomFunctionBodyScript |
370+ LoadAtomFunctions ObjModuleName ObjFunctionName FilePath |
371+ AddDatabaseContextFunction DatabaseContextFunctionName [TypeConstructor] DatabaseContextFunctionBodyScript |
372+ LoadDatabaseContextFunctions ObjModuleName ObjFunctionName FilePath |
373+ CreateArbitraryRelation RelVarName [AttributeExprBase a] Range
346374 deriving (Show, Eq, Generic, Binary)
347375
376+type GraphRefDatabaseContextIOExpr = DatabaseContextIOExprBase GraphRefTransactionMarker
377+
378+type DatabaseContextIOExpr = DatabaseContextIOExprBase ()
379+
348380type RestrictionPredicateExpr = RestrictionPredicateExprBase ()
349381
382+type GraphRefRestrictionPredicateExpr = RestrictionPredicateExprBase GraphRefTransactionMarker
383+
350384-- | Restriction predicates are boolean algebra components which, when composed, indicate whether or not a tuple should be retained during a restriction (filtering) operation.
351385data RestrictionPredicateExprBase a =
352386 TruePredicate |
@@ -356,7 +390,7 @@ data RestrictionPredicateExprBase a =
356390 RelationalExprPredicate (RelationalExprBase a) | --type must be same as true and false relations (no attributes)
357391 AtomExprPredicate (AtomExprBase a) | --atom must evaluate to boolean
358392 AttributeEqualityPredicate AttributeName (AtomExprBase a) -- relationalexpr must result in relation with single tuple
359− deriving (Show, Eq, Generic, NFData)
393+ deriving (Show, Eq, Generic, NFData, Foldable, Functor, Traversable)
360394
361395instance Binary RestrictionPredicateExpr
362396
@@ -369,17 +403,26 @@ type TransactionHeads = M.Map HeadName Transaction
369403-- | The transaction graph is the global database's state which references every committed transaction.
370404data TransactionGraph = TransactionGraph TransactionHeads (S.Set Transaction)
371405
372−transactionsForGraph :: TransactionGraph -> S.Set Transaction
373−transactionsForGraph (TransactionGraph _ t) = t
374−
375406transactionHeadsForGraph :: TransactionGraph -> TransactionHeads
376−transactionHeadsForGraph (TransactionGraph heads _) = heads
407+transactionHeadsForGraph (TransactionGraph hs _) = hs
408+
409+transactionsForGraph :: TransactionGraph -> S.Set Transaction
410+transactionsForGraph (TransactionGraph _ ts) = ts
377411
378412-- | Every transaction has context-specific information attached to it.
379−data TransactionInfo = TransactionInfo TransactionId (S.Set TransactionId) UTCTime | -- 1 parent + n children
380− MergeTransactionInfo TransactionId TransactionId (S.Set TransactionId) UTCTime -- 2 parents, n children
381− deriving(Show, Generic)
382−
413+-- The `TransactionDiff`s represent child/edge relationships to previous transactions (branches or continuations of the same branch).
414+data TransactionInfo = TransactionInfo {
415+ parents :: TransactionParents,
416+ stamp :: UTCTime
417+ } deriving (Show, Generic)
418+
419+type TransactionParents = NE.NonEmpty TransactionId
420+{-
421+data TransactionInfo = TransactionInfo TransactionId TransactionDiffs UTCTime | -- 1 parent + n children
422+ MergeTransactionInfo TransactionId TransactionId TransactionDiffs UTCTime -- 2 parents, n children
423+ deriving (Show, Generic)
424+-}
425+
383426instance Binary TransactionInfo
384427
385428-- | Every set of modifications made to the database are atomically committed to the transaction graph as a transaction.
@@ -387,13 +430,13 @@ type TransactionId = UUID
387430
388431data Transaction = Transaction TransactionId TransactionInfo Schemas
389432
390−--instance Binary Transaction
391−
433+-- | The disconnected transaction represents an in-progress workspace used by sessions before changes are committed. This is similar to git's "index". After a transaction is committed, it is "connected" in the transaction graph and can no longer be modified.
434+data DisconnectedTransaction = DisconnectedTransaction TransactionId Schemas DirtyFlag
435+--the database context expression represents a difference between the disconnected transaction and its immutable parent transaction- is this diff expr used at all?
436+
392437type DirtyFlag = Bool
393438
394−-- | The disconnected transaction represents an in-progress workspace used by sessions before changes are committed. This is similar to git's "index". After a transaction is committed, it is "connected" in the transaction graph and can no longer be modified.
395−data DisconnectedTransaction = DisconnectedTransaction TransactionId Schemas DirtyFlag --parentID, context- the context here represents the singular concrete context from the schema
396−--the dirty flag indicates that the database context has diverged from its parent's context
439+type TransactionDiffExpr = DatabaseContextExpr
397440
398441transactionId :: Transaction -> TransactionId
399442transactionId (Transaction tid _ _) = tid
@@ -409,24 +452,27 @@ instance Ord Transaction where
409452
410453type AtomExpr = AtomExprBase ()
411454
455+type GraphRefAtomExpr = AtomExprBase GraphRefTransactionMarker
456+
412457-- | An atom expression represents an action to take when extending a relation or when statically defining a relation or a new tuple.
413458data AtomExprBase a = AttributeAtomExpr AttributeName |
414459 NakedAtomExpr Atom |
415460 FunctionAtomExpr AtomFunctionName [AtomExprBase a] a |
416461 RelationAtomExpr (RelationalExprBase a) |
417462 ConstructedAtomExpr DataConstructorName [AtomExprBase a] a
418− deriving (Eq,Show,Generic, NFData)
463+ deriving (Eq,Show,Generic, NFData, Foldable, Functor, Traversable)
419464
420465instance Binary AtomExpr
421466
422467-- | Used in tuple creation when creating a relation.
423468data ExtendTupleExprBase a = AttributeExtendTupleExpr AttributeName (AtomExprBase a)
424− deriving (Show, Eq, Generic, NFData)
469+ deriving (Show, Eq, Generic, NFData, Foldable, Functor, Traversable)
425470
426−type ExtendTupleExpr = ExtendTupleExprBase ()
471+type ExtendTupleExpr = ExtendTupleExprBase ()
472+type GraphRefExtendTupleExpr = ExtendTupleExprBase GraphRefTransactionMarker
427473
428474instance Binary ExtendTupleExpr
429−
475+
430476--enumerates the list of functions available to be run as part of tuple expressions
431477type AtomFunctions = HS.HashSet AtomFunction
432478
@@ -471,9 +517,12 @@ data AttributeNamesBase a = AttributeNames (S.Set AttributeName) |
471517 UnionAttributeNames (AttributeNamesBase a) (AttributeNamesBase a) |
472518 IntersectAttributeNames (AttributeNamesBase a) (AttributeNamesBase a) |
473519 RelationalExprAttributeNames (RelationalExprBase a) -- use attribute names from the relational expression's type
474− deriving (Eq, Show, Generic, NFData)
520+ deriving (Eq, Show, Generic, NFData, Foldable, Functor, Traversable)
475521
476−type AttributeNames = AttributeNamesBase ()
522+type AttributeNames = AttributeNamesBase ()
523+
524+type GraphRefAttributeNames = AttributeNamesBase GraphRefTransactionMarker
525+
477526instance Binary AttributeNames
478527
479528-- | The persistence strategy is a global database option which represents how to persist the database in the filesystem, if at all.
@@ -483,19 +532,31 @@ data PersistenceStrategy = NoPersistence | -- ^ no filesystem persistence/memory
483532 deriving (Show, Read)
484533
485534type AttributeExpr = AttributeExprBase ()
535+type GraphRefAttributeExpr = AttributeExprBase GraphRefTransactionMarker
486536
487537-- | Create attributes dynamically.
488538data AttributeExprBase a = AttributeAndTypeNameExpr AttributeName TypeConstructor a |
489539 NakedAttributeExpr Attribute
490− deriving (Eq, Show, Generic, Binary, NFData)
540+ deriving (Eq, Show, Generic, Binary, NFData, Foldable, Functor, Traversable)
491541
492542-- | Dynamically create a tuple from attribute names and 'AtomExpr's.
493543newtype TupleExprBase a = TupleExpr (M.Map AttributeName (AtomExprBase a))
494− deriving (Eq, Show, Generic, NFData)
544+ deriving (Eq, Show, Generic, NFData, Foldable, Functor, Traversable)
495545
496−instance Binary TupleExpr
497−
498−type TupleExpr = TupleExprBase ()
546+instance Binary TupleExpr
547+
548+type TupleExpr = TupleExprBase ()
549+
550+type GraphRefTupleExpr = TupleExprBase GraphRefTransactionMarker
551+
552+data TupleExprsBase a = TupleExprs a [TupleExprBase a]
553+ deriving (Eq, Show, Generic, NFData, Foldable, Functor, Traversable)
554+
555+type GraphRefTupleExprs = TupleExprsBase GraphRefTransactionMarker
556+
557+type TupleExprs = TupleExprsBase ()
558+
559+instance Binary TupleExprs
499560
500561data MergeStrategy =
501562 -- | After a union merge, the merge transaction is a result of union'ing relvars of the same name, introducing all uniquely-named relvars, union of constraints, union of atom functions, notifications, and types (unless the names and definitions collide, e.g. two types of the same name with different definitions)
@@ -568,3 +629,24 @@ atomTypeVars (RelationAtomType attrs) = S.unions (map attrTypeVars (V.toList att
568629atomTypeVars (ConstructedAtomType _ tvMap) = M.keysSet tvMap
569630atomTypeVars (TypeVariableType nam) = S.singleton nam
570631
632+unimplemented :: HasCallStack => a
633+unimplemented = undefined
634+
635+--for serializing GraphRefRelationalExpr as part of transaction serialization
636+instance Binary (TupleExprsBase GraphRefTransactionMarker)
637+
638+instance Binary (TupleExprBase GraphRefTransactionMarker)
639+
640+instance Binary GraphRefRelationalExpr
641+
642+instance Binary (AtomExprBase GraphRefTransactionMarker)
643+
644+instance Binary (AttributeNamesBase GraphRefTransactionMarker)
645+
646+instance Binary (RestrictionPredicateExprBase GraphRefTransactionMarker)
647+
648+instance Binary (ExtendTupleExprBase GraphRefTransactionMarker)
649+
650+instance Binary GraphRefWithNameExpr
651+
652+
src/lib/ProjectM36/Client.hs+95−67
@@ -1,4 +1,4 @@
1−{-# LANGUAGE DeriveAnyClass, DeriveGeneric, ScopedTypeVariables, BangPatterns, MonoLocalBinds #-}
1+{-# LANGUAGE DeriveAnyClass, DeriveGeneric, ScopedTypeVariables, MonoLocalBinds #-}
22{-|
33Module: ProjectM36.Client
44
@@ -46,8 +46,10 @@ module ProjectM36.Client
4646 PersistenceStrategy(..),
4747 RelationalExpr,
4848 RelationalExprBase(..),
49− DatabaseContextExpr(..),
50− DatabaseContextIOExpr(..),
49+ DatabaseContextExprBase(..),
50+ DatabaseContextExpr,
51+ DatabaseContextIOExprBase(..),
52+ DatabaseContextIOExpr,
5153 Attribute(..),
5254 MergeStrategy(..),
5355 attributesFromList,
@@ -97,6 +99,7 @@ module ProjectM36.Client
9799 AtomType(..),
98100 Atomable(..),
99101 TupleExprBase(..),
102+ TupleExprsBase(..),
100103 AtomExprBase(..),
101104 RestrictionPredicateExprBase(..),
102105 withTransaction
@@ -112,7 +115,6 @@ import qualified ProjectM36.DataFrame as DF
112115import ProjectM36.DatabaseContextFunction as DCF
113116import qualified ProjectM36.IsomorphicSchema as Schema
114117import Control.Monad.State
115−import Control.Monad.Trans.Reader
116118import qualified ProjectM36.RelationalExpression as RE
117119import ProjectM36.DatabaseContext (basicDatabaseContext)
118120import qualified ProjectM36.TransactionGraph as Graph
@@ -120,21 +122,20 @@ import ProjectM36.TransactionGraph
120122import qualified ProjectM36.Transaction as Trans
121123import ProjectM36.TransactionGraph.Persist
122124import ProjectM36.Attribute
123−import ProjectM36.TransGraphRelationalExpression (TransGraphRelationalExpr, evalTransGraphRelationalExpr)
125+import ProjectM36.TransGraphRelationalExpression as TGRE (TransGraphRelationalExpr)
124126import ProjectM36.Persist (DiskSync(..))
125127import ProjectM36.FileLock
128+import ProjectM36.NormalizeExpr
126129import ProjectM36.Notifications
127130import ProjectM36.Server.RemoteCallTypes
128131import qualified ProjectM36.DisconnectedTransaction as Discon
129132import ProjectM36.Relation (typesAsRelation)
130133import ProjectM36.ScriptSession (initScriptSession, ScriptSession)
131134import qualified ProjectM36.Relation as R
132−import qualified ProjectM36.DatabaseContext as DBC
133135import Control.Exception.Base
134136import GHC.Conc.Sync
135137
136138import Network.Transport (Transport(closeTransport))
137−
138139#if MIN_VERSION_network_transport_tcp(0,7,0)
139140import Network.Transport.TCP (createTransport, defaultTCPParameters, defaultTCPAddr)
140141import Network.Transport.TCP.Internal (encodeEndPointAddress)
@@ -155,7 +156,6 @@ import Control.Distributed.Process (ProcessId, Process, receiveWait, send, match
155156import Control.Exception (IOException, handle, AsyncException, throwIO, fromException, Exception)
156157import Control.Concurrent.MVar
157158import qualified Data.Map as M
158−import Control.Distributed.Process.Serializable (Serializable)
159159#if MIN_VERSION_stm_containers(1,0,0)
160160import qualified StmContainers.Map as StmMap
161161import qualified StmContainers.Set as StmSet
@@ -171,6 +171,7 @@ import GHC.Generics (Generic)
171171import Control.DeepSeq (force)
172172import System.IO
173173import Data.Time.Clock
174+import Data.Typeable
174175
175176type Hostname = String
176177
@@ -315,9 +316,9 @@ connectProjectM36 :: ConnectionInfo -> IO (Either ConnectionError Connection)
315316--create a new in-memory database/transaction graph
316317connectProjectM36 (InProcessConnectionInfo strat notificationCallback ghcPkgPaths) = do
317318 freshId <- nextRandom
318− stamp <- getCurrentTime
319+ tstamp <- getCurrentTime
319320 let bootstrapContext = basicDatabaseContext
320− freshGraph = bootstrapTransactionGraph stamp freshId bootstrapContext
321+ freshGraph = bootstrapTransactionGraph tstamp freshId bootstrapContext
321322 case strat of
322323 --create date examples graph for now- probably should be empty context in the future
323324 NoPersistence -> do
@@ -410,7 +411,7 @@ createSessionAtCommit_ commitId newSessionId (InProcessConnection conf) = do
410411 let sessions = ipSessions conf
411412 graphTvar = ipTransactionGraph conf
412413 graph <- readTVar graphTvar
413− case transactionForId commitId graph of
414+ case RE.transactionForId commitId graph of
414415 Left err -> pure (Left err)
415416 Right transaction -> do
416417 let freshDiscon = DisconnectedTransaction commitId (Trans.schemas transaction) False
@@ -498,7 +499,7 @@ safeLogin login procId = do
498499 Left (_ :: ServerError) -> pure False
499500 Right val -> pure val
500501
501−remoteCall :: (Serializable a, Serializable b) => Connection -> a -> IO b
502+remoteCall :: (Binary a, Binary b, Typeable a, Typeable b) => Connection -> a -> IO b
502503remoteCall (InProcessConnection _ ) _ = error "remoteCall called on local connection"
503504remoteCall (RemoteProcessConnection conf) arg = runProcessResult localNode $ do
504505 ret <- safeCall serverProcessId arg
@@ -573,13 +574,14 @@ executeRelationalExpr sessionId (InProcessConnection conf) expr = excEither $ at
573574 Right expr
574575 case expr' of
575576 Left err -> pure (Left err)
576− Right expr'' -> case optimizeRelationalExpr (Sess.concreteDatabaseContext session) expr'' of
577− Left err -> pure (Left err)
578− Right optExpr -> do
579− let evald = runReader (RE.evalRelationalExpr optExpr) (RE.mkRelationalExprState (Sess.concreteDatabaseContext session))
580− case evald of
581− Left err -> pure (Left err)
582− Right rel -> pure (force (Right rel)) -- this is necessary so that any undefined/error exceptions are spit out here
577+ Right expr'' -> do
578+ let graphTvar = ipTransactionGraph conf
579+ graph <- readTVar graphTvar
580+ let reEnv = RE.mkRelationalExprEnv (Sess.concreteDatabaseContext session) graph
581+ case optimizeAndEvalRelationalExpr reEnv expr'' of
582+ Right rel -> pure (force (Right rel)) -- this is necessary so that any undefined/error exceptions are spit out here
583+ Left err -> pure (Left err)
584+
583585executeRelationalExpr sessionId conn@(RemoteProcessConnection _) relExpr = remoteCall conn (ExecuteRelationalExpr sessionId relExpr)
584586
585587-- | Execute a database context expression in the context of the session and connection. Database expressions modify the current session's disconnected transaction but cannot modify the transaction graph.
@@ -596,21 +598,24 @@ executeDatabaseContextExpr sessionId (InProcessConnection conf) expr = excEither
596598 Schema.processDatabaseContextExprInSchema schema expr
597599 case expr' of
598600 Left err -> pure (Left err)
599− Right expr'' -> case runState (do
600− eOptExpr <- applyStaticDatabaseOptimization expr''
601− case eOptExpr of
602− Left err -> pure (Left err)
603− Right optExpr ->
604− RE.evalDatabaseContextExpr optExpr) (RE.freshDatabaseState (Sess.concreteDatabaseContext session)) of
605− (Left err,_) -> pure (Left err)
606− (Right (), (_,_,False)) -> pure (Right ()) --optimization- if nothing was dirtied, nothing to do
607− (Right (), (!context',_,True)) -> do
608− let newDiscon = DisconnectedTransaction (Sess.parentId session) newSchemas True
609− newSubschemas = Schema.processDatabaseContextExprSchemasUpdate (Sess.subschemas session) expr
610− newSchemas = Schemas context' newSubschemas
611− newSession = Session newDiscon (Sess.schemaName session)
612− StmMap.insert newSession sessionId sessions
613− pure (Right ())
601+ Right expr'' -> do
602+ graph <- readTVar (ipTransactionGraph conf)
603+ let ctx = Sess.concreteDatabaseContext session
604+ env = RE.mkDatabaseContextEvalEnv transId graph
605+ transId = Sess.parentId session
606+ case RE.runDatabaseContextEvalMonad ctx env (optimizeAndEvalDatabaseContextExpr True expr'') of
607+ Left err -> pure (Left err)
608+ Right newState ->
609+ if not (RE.dbc_dirty newState) then --nothing dirtied, nothing to do
610+ pure (Right ())
611+ else do
612+ let newDiscon = DisconnectedTransaction (Sess.parentId session) newSchemas True
613+ context' = RE.dbc_context newState
614+ newSubschemas = Schema.processDatabaseContextExprSchemasUpdate (Sess.subschemas session) expr
615+ newSchemas = Schemas context' newSubschemas
616+ newSession = Session newDiscon (Sess.schemaName session)
617+ StmMap.insert newSession sessionId sessions
618+ pure (Right ())
614619executeDatabaseContextExpr sessionId conn@(RemoteProcessConnection _) dbExpr = remoteCall conn (ExecuteDatabaseContextExpr sessionId dbExpr)
615620
616621-- | Similar to a git rebase, 'autoMergeToHead' atomically creates a temporary branch and merges it to the latest commit of the branch referred to by the 'HeadName' and commits the merge. This is useful to reduce incidents of 'TransactionIsNotAHeadError's but at the risk of merge errors (thus making it similar to rebasing). Alternatively, as an optimization, if a simple commit is possible (meaning that the head has not changed), then a fast-forward commit takes place instead.
@@ -620,7 +625,7 @@ autoMergeToHead sessionId (InProcessConnection conf) strat headName' = do
620625 id1 <- nextRandom
621626 id2 <- nextRandom
622627 id3 <- nextRandom
623− stamp <- getCurrentTime
628+ tstamp <- getCurrentTime
624629 commitLock_ sessionId conf $ \graph -> do
625630 eSession <- sessionForSessionId sessionId sessions
626631 case eSession of
@@ -631,10 +636,10 @@ autoMergeToHead sessionId (InProcessConnection conf) strat headName' = do
631636 Just headTrans -> do
632637 --attempt fast-forward commit, if possible
633638 let graphInfo = if Sess.parentId session == transactionId headTrans then do
634− ret <- Graph.evalGraphOp stamp id1 (Sess.disconnectedTransaction session) graph Commit
639+ ret <- Graph.evalGraphOp tstamp id1 (Sess.disconnectedTransaction session) graph Commit
635640 pure (ret, [id1])
636641 else do
637− ret <- Graph.autoMergeToHead stamp (id1, id2, id3) (Sess.disconnectedTransaction session) headName' strat graph
642+ ret <- Graph.autoMergeToHead tstamp (id1, id2, id3) (Sess.disconnectedTransaction session) headName' strat graph
638643 pure (ret, [id1,id2,id3])
639644 case graphInfo of
640645 Left err -> pure (Left err)
@@ -652,13 +657,18 @@ executeDatabaseContextIOExpr sessionId (InProcessConnection conf) expr = excEith
652657 case eSession of
653658 Left err -> pure (Left err)
654659 Right session -> do
655− res <- RE.evalDatabaseContextIOExpr scriptSession (Sess.concreteDatabaseContext session) expr
660+ graph <- readTVarIO (ipTransactionGraph conf)
661+ let env = RE.DatabaseContextIOEvalEnv transId graph scriptSession
662+ transId = Sess.parentId session
663+ context = Sess.concreteDatabaseContext session
664+ res <- RE.runDatabaseContextIOEvalMonad env context (optimizeAndEvalDatabaseContextIOExpr expr)
656665 case res of
657666 Left err -> pure (Left err)
658− Right context' -> do
659− let newDiscon = DisconnectedTransaction (Sess.parentId session) newSchemas True
667+ Right newState -> do
668+ let newDiscon = DisconnectedTransaction (Sess.parentId session) newSchemas False
660669 newSchemas = Schemas context' (Sess.subschemas session)
661670 newSession = Session newDiscon (Sess.schemaName session)
671+ context' = RE.dbc_context newState
662672 atomically $ StmMap.insert newSession sessionId sessions
663673 pure (Right ())
664674executeDatabaseContextIOExpr sessionId conn@(RemoteProcessConnection _) dbExpr = remoteCall conn (ExecuteDatabaseContextIOExpr sessionId dbExpr)
@@ -678,12 +688,18 @@ executeGraphExprSTM_ freshId sessionId session sessions graphExpr graph graphTVa
678688-}
679689
680690-- process notifications for commits
681−executeCommitExprSTM_ :: DatabaseContext -> DatabaseContext -> ClientNodes -> STM (EvaluatedNotifications, ClientNodes)
682−executeCommitExprSTM_ oldContext newContext nodes = do
691+executeCommitExprSTM_
692+ :: TransactionGraph
693+ -> DatabaseContext
694+ -> DatabaseContext
695+ -> ClientNodes
696+ -> STM (EvaluatedNotifications, ClientNodes)
697+executeCommitExprSTM_ graph oldContext newContext nodes = do
683698 let nots = notifications oldContext
684− fireNots = notificationChanges nots oldContext newContext
699+ fireNots = notificationChanges nots graph oldContext newContext
685700 evaldNots = M.map mkEvaldNot fireNots
686− evalInContext expr ctx = runReader (RE.evalRelationalExpr expr) (RE.mkRelationalExprState ctx)
701+ evalInContext expr ctx = optimizeAndEvalRelationalExpr (RE.mkRelationalExprEnv ctx graph) expr
702+
687703 mkEvaldNot notif = EvaluatedNotification { notification = notif,
688704 reportOldRelation = evalInContext (reportOldExpr notif) oldContext,
689705 reportNewRelation = evalInContext (reportNewExpr notif) newContext}
@@ -698,18 +714,18 @@ executeGraphExpr :: SessionId -> Connection -> TransactionGraphOperator -> IO (E
698714executeGraphExpr sessionId (InProcessConnection conf) graphExpr = excEither $ do
699715 let sessions = ipSessions conf
700716 freshId <- nextRandom
701− stamp <- getCurrentTime
717+ tstamp <- getCurrentTime
702718 commitLock_ sessionId conf $ \updatedGraph -> do
703719 eSession <- sessionForSessionId sessionId sessions
704720 case eSession of
705721 Left err -> pure (Left err)
706722 Right session -> do
707723 let discon = Sess.disconnectedTransaction session
708− case evalGraphOp stamp freshId discon updatedGraph graphExpr of
724+ case evalGraphOp tstamp freshId discon updatedGraph graphExpr of
709725 Left err -> pure (Left err)
710726 Right (discon', graph') -> do
711727 --if freshId appears in the graph, then we need to pass it on
712− let transIds = [freshId | isRight (transactionForId freshId graph')]
728+ let transIds = [freshId | isRight (RE.transactionForId freshId graph')]
713729 pure (Right (discon', graph', transIds))
714730{-
715731executeGraphExpr sessionId (InProcessConnection conf) graphExpr = excEither $ do
@@ -789,11 +805,10 @@ executeTransGraphRelationalExpr :: SessionId -> Connection -> TransGraphRelation
789805executeTransGraphRelationalExpr _ (InProcessConnection conf) tgraphExpr = excEither . atomically $ do
790806 let graphTvar = ipTransactionGraph conf
791807 graph <- readTVar graphTvar
792− case evalTransGraphRelationalExpr tgraphExpr graph of
793− Left err -> pure (Left err)
794− Right relExpr -> case runReader (RE.evalRelationalExpr relExpr) (RE.mkRelationalExprState DBC.empty) of
808+ pure $ force $ optimizeAndEvalTransGraphRelationalExpr graph tgraphExpr
809+{- case runReader (RE.evalRelationalExpr relExpr) (RE.mkRelationalExprState DBC.empty) of
795810 Left err -> pure (Left err)
796− Right rel -> pure (force (Right rel))
811+ Right rel -> pure (force (Right rel))-}
797812executeTransGraphRelationalExpr sessionId conn@(RemoteProcessConnection _) tgraphExpr = remoteCall conn (ExecuteTransGraphRelationalExpr sessionId tgraphExpr)
798813
799814-- | Schema expressions manipulate the isomorphic schemas for the current 'DatabaseContext'.
@@ -805,13 +820,16 @@ executeSchemaExpr sessionId (InProcessConnection conf) schemaExpr = atomically $
805820 Left err -> pure (Left err)
806821 Right (session, _) -> do
807822 let subschemas' = subschemas session
808− case Schema.evalSchemaExpr schemaExpr (Sess.concreteDatabaseContext session) subschemas' of
823+ transId = Sess.parentId session
824+ context = Sess.concreteDatabaseContext session
825+ graph <- readTVar (ipTransactionGraph conf)
826+ case Schema.evalSchemaExpr schemaExpr context transId graph subschemas' of
809827 Left err -> pure (Left err)
810828 Right (newSubschemas, newContext) -> do
811829 --hm- maybe we should start using lenses
812830 let discon = Sess.disconnectedTransaction session
813831 newSchemas = Schemas newContext newSubschemas
814− newSession = Session (DisconnectedTransaction (Discon.parentId discon) newSchemas True) (Sess.schemaName session)
832+ newSession = Session (DisconnectedTransaction (Discon.parentId discon) newSchemas False) (Sess.schemaName session)
815833 StmMap.insert newSession sessionId sessions
816834 pure (Right ())
817835executeSchemaExpr sessionId conn@(RemoteProcessConnection _) schemaExpr = remoteCall conn (ExecuteSchemaExpr sessionId schemaExpr)
@@ -861,7 +879,10 @@ typeForRelationalExprSTM sessionId (InProcessConnection conf) relExpr = do
861879 Schema.processRelationalExprInSchema schema relExpr
862880 case processed of
863881 Left err -> pure (Left err)
864− Right relExpr' -> pure $ runReader (RE.typeForRelationalExpr relExpr') (RE.mkRelationalExprState (Sess.concreteDatabaseContext session))
882+ Right relExpr' -> do
883+ graph <- readTVar (ipTransactionGraph conf)
884+ let reEnv = RE.mkRelationalExprEnv (Sess.concreteDatabaseContext session) graph
885+ pure $ RE.runRelationalExprM reEnv (RE.typeForRelationalExpr relExpr')
865886
866887typeForRelationalExprSTM _ _ _ = error "typeForRelationalExprSTM called on non-local connection"
867888
@@ -894,17 +915,22 @@ typeConstructorMapping sessionId (InProcessConnection conf) = do
894915typeConstructorMapping sessionId conn@(RemoteProcessConnection _) = remoteCall conn (RetrieveTypeConstructorMapping sessionId)
895916
896917-- | Return an optimized database expression which is logically equivalent to the input database expression. This function can be used to determine which expression will actually be evaluated.
897−planForDatabaseContextExpr :: SessionId -> Connection -> DatabaseContextExpr -> IO (Either RelationalError DatabaseContextExpr)
918+planForDatabaseContextExpr :: SessionId -> Connection -> DatabaseContextExpr -> IO (Either RelationalError GraphRefDatabaseContextExpr)
898919planForDatabaseContextExpr sessionId (InProcessConnection conf) dbExpr = do
899920 let sessions = ipSessions conf
900921 atomically $ do
922+ graph <- readTVar (ipTransactionGraph conf)
901923 eSession <- sessionAndSchema sessionId sessions
902924 case eSession of
903925 Left err -> pure $ Left err
904− Right (session, _) -> if schemaName session == defaultSchemaName then
905− pure $ evalState (applyStaticDatabaseOptimization dbExpr) (RE.freshDatabaseState (Sess.concreteDatabaseContext session))
906− else -- don't show any optimization because the current optimization infrastructure relies on access to the base context- this probably underscores the need for each schema to have its own DatabaseContext, even if it is generated on-the-fly
907− pure (Right dbExpr)
926+ Right (session, _) ->
927+ if schemaName session == defaultSchemaName then do
928+ let ctx = Sess.concreteDatabaseContext session
929+ transId = Sess.parentId session
930+ gfExpr = runProcessExprM UncommittedContextMarker (processDatabaseContextExpr dbExpr)
931+ pure $ runGraphRefSOptDatabaseContextExprM transId ctx graph (optimizeGraphRefDatabaseContextExpr gfExpr)
932+ else -- don't show any optimization because the current optimization infrastructure relies on access to the base context- this probably underscores the need for each schema to have its own DatabaseContext, even if it is generated on-the-fly-}
933+ pure (Left NonConcreteSchemaPlanError)
908934
909935planForDatabaseContextExpr sessionId conn@(RemoteProcessConnection _) dbExpr = remoteCall conn (RetrievePlanForDatabaseContextExpr sessionId dbExpr)
910936
@@ -931,17 +957,20 @@ relationVariablesAsRelation :: SessionId -> Connection -> IO (Either RelationalE
931957relationVariablesAsRelation sessionId (InProcessConnection conf) = do
932958 let sessions = ipSessions conf
933959 atomically $ do
960+ graph <- readTVar (ipTransactionGraph conf)
934961 eSession <- sessionAndSchema sessionId sessions
935962 case eSession of
936963 Left err -> pure (Left err)
937964 Right (session, schema) -> do
938965 let context = Sess.concreteDatabaseContext session
939966 if Sess.schemaName session == defaultSchemaName then
940− pure $ R.relationVariablesAsRelation (relationVariables context)
967+ pure $ RE.relationVariablesAsRelation context graph
941968 else
942− case Schema.relationVariablesInSchema schema context of
969+ case Schema.relationVariablesInSchema schema of
943970 Left err -> pure (Left err)
944− Right relvars -> pure $ R.relationVariablesAsRelation relvars
971+ Right relvars -> do
972+ let schemaContext = context {relationVariables = relvars }
973+ pure $ RE.relationVariablesAsRelation schemaContext graph
945974
946975relationVariablesAsRelation sessionId conn@(RemoteProcessConnection _) = remoteCall conn (RetrieveRelationVariableSummary sessionId)
947976
@@ -987,7 +1016,7 @@ headNameSTM_ sessionId sessions graphTvar = do
9871016 eSession <- sessionForSessionId sessionId sessions
9881017 case eSession of
9891018 Left err -> pure (Left err)
990− Right session -> case transactionForId (Sess.parentId session) graph of
1019+ Right session -> case RE.transactionForId (Sess.parentId session) graph of
9911020 Left err -> pure (Left err)
9921021 Right parentTrans -> case headNameForTransaction parentTrans graph of
9931022 Nothing -> pure (Left UnknownHeadError)
@@ -1099,11 +1128,11 @@ commitLock_ sessionId conf stmBlock = do
10991128 writeTVar graphTvar graph'
11001129 let newSession = Session discon' (Sess.schemaName session)
11011130 StmMap.insert newSession sessionId sessions
1102− case transactionForId (Sess.parentId session) oldGraph of
1131+ case RE.transactionForId (Sess.parentId session) oldGraph of
11031132 Left err -> pure $ Left err
11041133 Right previousTrans ->
11051134 if not (Prelude.null transactionIdsToPersist) then do
1106− (evaldNots, nodes) <- executeCommitExprSTM_ (Trans.concreteDatabaseContext previousTrans) (Sess.concreteDatabaseContext session) clientNodes
1135+ (evaldNots, nodes) <- executeCommitExprSTM_ graph' (Trans.concreteDatabaseContext previousTrans) (Sess.concreteDatabaseContext session) clientNodes
11071136 nodesToNotify <- stmSetToList nodes
11081137 pure $ Right (evaldNots, nodesToNotify, graph', transactionIdsToPersist)
11091138 else pure (Right (M.empty, [], graph', []))
@@ -1165,8 +1194,7 @@ executeDataFrameExpr sessionId conn@(InProcessConnection _) dfExpr = do
11651194 Left err -> pure (Left err)
11661195 Right attrs -> do
11671196 let attrOrders = zipWith
1168− (\attr' order'
1169− -> DF.AttributeOrder (attributeName attr') order')
1197+ (DF.AttributeOrder . attributeName)
11701198 attrs
11711199 orders
11721200 case DF.sortDataFrameBy attrOrders . DF.toDataFrame $ rel of
src/lib/ProjectM36/Client/Simple.hs+2−1
@@ -25,7 +25,8 @@ module ProjectM36.Client.Simple (
2525 C.PersistenceStrategy(..),
2626 C.NotificationCallback,
2727 C.emptyNotificationCallback,
28− C.DatabaseContextExpr(..),
28+ C.DatabaseContextExprBase(..),
29+ C.DatabaseContextExpr,
2930 C.RelationalExprBase(..)
3031 ) where
3132
src/lib/ProjectM36/DatabaseContext.hs+9−4
@@ -1,5 +1,6 @@
11module ProjectM36.DatabaseContext where
22import ProjectM36.Base
3+import Control.Monad (void)
34import qualified Data.Map as M
45import qualified Data.HashSet as HS
56import ProjectM36.DataTypes.Basic
@@ -14,20 +15,24 @@ empty = DatabaseContext { inclusionDependencies = M.empty,
1415 atomFunctions = HS.empty,
1516 dbcFunctions = HS.empty,
1617 typeConstructorMapping = [] }
18+
19+-- | Remove TransactionId markers on GraphRefRelationalExpr
20+stripGraphRefRelationalExpr :: GraphRefRelationalExpr -> RelationalExpr
21+stripGraphRefRelationalExpr = void
1722
1823-- | convert an existing database context into its constituent expression.
1924databaseContextAsDatabaseContextExpr :: DatabaseContext -> DatabaseContextExpr
2025databaseContextAsDatabaseContextExpr context = MultipleExpr $ relVarsExprs ++ incDepsExprs ++ funcsExprs
2126 where
22− relVarsExprs = map (\(name, rel) -> Assign name (ExistingRelation rel)) (M.toList (relationVariables context))
27+ relVarsExprs = map (\(name, rel) -> Assign name (stripGraphRefRelationalExpr rel)) (M.toList (relationVariables context))
2328 incDepsExprs :: [DatabaseContextExpr]
2429 incDepsExprs = map (uncurry AddInclusionDependency) (M.toList (inclusionDependencies context))
25− funcsExprs = [] -- map (\func -> ) (HS.toList funcs) -- there are no databaseExprs to add atom functions yet
30+ funcsExprs = [] -- map (\func -> ) (HS.toList funcs) -- there are no databaseExprs to add atom functions yet-}
2631
2732basicDatabaseContext :: DatabaseContext
2833basicDatabaseContext = DatabaseContext { inclusionDependencies = M.empty,
29− relationVariables = M.fromList [("true", relationTrue),
30− ("false", relationFalse)],
34+ relationVariables = M.fromList [("true", ExistingRelation relationTrue),
35+ ("false", ExistingRelation relationFalse)],
3136 atomFunctions = basicAtomFunctions,
3237 dbcFunctions = basicDatabaseContextFunctions,
3338 notifications = M.empty,
src/lib/ProjectM36/DatabaseContextFunctionUtils.hs+15−8
@@ -3,13 +3,20 @@ import ProjectM36.RelationalExpression
33import ProjectM36.Base
44import ProjectM36.DatabaseContextFunctionError
55import ProjectM36.Error
6−import Control.Monad.State
7−import Control.Monad.Trans.Reader
6+import ProjectM36.StaticOptimizer
87
9−executeDatabaseContextExpr :: DatabaseContextExpr -> DatabaseContext -> Either DatabaseContextFunctionError DatabaseContext
10−executeDatabaseContextExpr expr context = case runState (evalDatabaseContextExpr expr) (freshDatabaseState context) of
11− (Right (), (context', _, _)) -> pure context'
12− (Left err, _) -> error (show err)
8+executeDatabaseContextExpr :: DatabaseContextExpr -> TransactionId -> TransactionGraph -> DatabaseContext -> Either DatabaseContextFunctionError DatabaseContext
9+executeDatabaseContextExpr expr transId graph context' =
10+ case run of
11+ Right st -> pure (dbc_context st)
12+ Left err -> error (show err)
13+ where
14+ env = mkDatabaseContextEvalEnv transId graph
15+ run = runDatabaseContextEvalMonad context' env (optimizeAndEvalDatabaseContextExpr True expr)
1316
14−executeRelationalExpr :: RelationalExpr -> DatabaseContext -> Either RelationalError Relation
15−executeRelationalExpr expr context = runReader (evalRelationalExpr expr) (mkRelationalExprState context)
17+executeRelationalExpr :: RelationalExpr -> DatabaseContext -> TransactionGraph -> Either RelationalError Relation
18+executeRelationalExpr expr context graph =
19+ run
20+ where
21+ env = mkRelationalExprEnv context graph
22+ run = optimizeAndEvalRelationalExpr env expr
src/lib/ProjectM36/DateExamples.hs+3−3
@@ -16,9 +16,9 @@ dateExamples = empty { inclusionDependencies = dateIncDeps,
1616 atomFunctions = basicAtomFunctions,
1717 typeConstructorMapping = basicTypeConstructorMapping }
1818 where -- these must be lower case now that data constructors are in play
19− dateRelVars = M.fromList [("s", suppliers),
20− ("p", products),
21− ("sp", supplierProducts)]
19+ dateRelVars = M.fromList [("s", ExistingRelation suppliers),
20+ ("p", ExistingRelation products),
21+ ("sp", ExistingRelation supplierProducts)]
2222 suppliers = suppliersRel
2323 products = productsRel
2424 supplierProducts = supplierProductsRel
src/lib/ProjectM36/DisconnectedTransaction.hs+7−1
@@ -8,4 +8,10 @@ schemas :: DisconnectedTransaction -> Schemas
88schemas (DisconnectedTransaction _ s _) = s
99
1010parentId :: DisconnectedTransaction -> TransactionId
11−parentId (DisconnectedTransaction pid _ _) = pid
11+parentId (DisconnectedTransaction pid _ _) = pid
12+
13+isDirty :: DisconnectedTransaction -> Bool
14+isDirty (DisconnectedTransaction _ _ flag) = flag
15+
16+freshTransaction :: TransactionId -> Schemas -> DisconnectedTransaction
17+freshTransaction tid schemas' = DisconnectedTransaction tid schemas' False
src/lib/ProjectM36/Error.hs+5
@@ -96,6 +96,11 @@ data RelationalError = NoSuchAttributeNamesError (S.Set AttributeName)
9696 | SchemaCreationError SchemaError
9797
9898 | ImproperDatabaseStateError
99+
100+ | NonConcreteSchemaPlanError
101+
102+ | NoUncommittedContextInEvalError
103+ | TupleExprsReferenceMultipleMarkersError
99104
100105 | MultipleErrors [RelationalError]
101106 deriving (Show,Eq,Generic,Binary,Typeable, NFData)
src/lib/ProjectM36/GraphRefRelationalExpr.hsnew file+45
@@ -0,0 +1,45 @@
1+module ProjectM36.GraphRefRelationalExpr where
2+--evaluate relational expressions across the entire transaction graph to support cross-transaction referencing
3+import ProjectM36.Base
4+import qualified Data.Set as S
5+
6+--import Debug.Trace
7+
8+data SingularTransactionRef = SingularTransactionRef GraphRefTransactionMarker |
9+ MultipleTransactionsRef |
10+ NoTransactionsRef
11+ deriving (Eq, Show)
12+
13+instance Semigroup SingularTransactionRef where
14+ NoTransactionsRef <> x = x
15+ MultipleTransactionsRef <> _ = MultipleTransactionsRef
16+ SingularTransactionRef tidA <> s@(SingularTransactionRef tidB) =
17+ if tidA == tidB then
18+ s
19+ else
20+ MultipleTransactionsRef
21+ s@SingularTransactionRef{} <> NoTransactionsRef = s
22+ _ <> MultipleTransactionsRef = MultipleTransactionsRef
23+
24+instance Monoid SingularTransactionRef where
25+ mempty = NoTransactionsRef
26+
27+-- | return `Just transid` if this GraphRefRelationalExpr refers to just one transaction in the graph. This is useful for determining if certain optimizations can apply.
28+singularTransaction :: Foldable t => t GraphRefTransactionMarker -> SingularTransactionRef
29+singularTransaction expr
30+ | S.null transSet = NoTransactionsRef
31+ | S.size transSet == 1 = SingularTransactionRef (head (S.toList transSet))
32+ | otherwise = MultipleTransactionsRef
33+ where
34+ transSet = foldr S.insert S.empty expr
35+
36+-- | Return True if two 'GraphRefRelationalExpr's both refer exclusively to the same transaction (or none at all).
37+inSameTransaction :: GraphRefRelationalExpr -> GraphRefRelationalExpr -> Maybe GraphRefTransactionMarker
38+inSameTransaction exprA exprB = case (stA, stB) of
39+ (SingularTransactionRef tA, SingularTransactionRef tB) | tA == tB -> Just tA
40+ _ -> Nothing
41+ where stA = singularTransaction exprA
42+ stB = singularTransaction exprB
43+
44+singularTransactions :: (Foldable f, Foldable t) => f (t GraphRefTransactionMarker) -> SingularTransactionRef
45+singularTransactions = foldMap singularTransaction
src/lib/ProjectM36/IsomorphicSchema.hs+22−23
@@ -3,12 +3,11 @@ module ProjectM36.IsomorphicSchema where
33import ProjectM36.Base
44import ProjectM36.Error
55import ProjectM36.MiscUtils
6−import ProjectM36.RelationalExpression
76import ProjectM36.Relation
7+import ProjectM36.NormalizeExpr
8+import ProjectM36.RelationalExpression
89import qualified ProjectM36.AttributeNames as AN
910import Control.Monad
10−import Control.Monad.State
11−import Control.Monad.Trans.Reader
1211import GHC.Generics
1312import Data.Binary
1413import qualified Data.Map as M
@@ -256,15 +255,15 @@ inclusionDependencyInSchema schema (InclusionDependency rexprA rexprB) = do
256255inclusionDependenciesInSchema :: Schema -> InclusionDependencies -> Either RelationalError InclusionDependencies
257256inclusionDependenciesInSchema schema incDeps = M.fromList <$> mapM (\(depName, dep) -> inclusionDependencyInSchema schema dep >>= \newDep -> pure (depName, newDep)) (M.toList incDeps)
258257
259−relationVariablesInSchema :: Schema -> DatabaseContext -> Either RelationalError RelationVariables
260−relationVariablesInSchema schema@(Schema morphs) context = foldM transform M.empty morphs
258+relationVariablesInSchema :: Schema -> Either RelationalError RelationVariables
259+relationVariablesInSchema schema@(Schema morphs) = foldM transform M.empty morphs
261260 where
262261 transform newRvMap morph = do
263262 let rvNames = isomorphInRelVarNames morph
264263 rvAssocs <- mapM (\rv -> do
265264 expr' <- processRelationalExprInSchema schema (RelationVariable rv ())
266− rel <- runReader (evalRelationalExpr expr') (RelationalExprStateElems context)
267− pure (rv, rel)) rvNames
265+ let gfExpr = runProcessExprM UncommittedContextMarker (processRelationalExpr expr')
266+ pure (rv, gfExpr)) rvNames
268267 pure (M.union newRvMap (M.fromList rvAssocs))
269268
270269
@@ -312,24 +311,24 @@ createIncDepsForIsomorph sname (IsoRestrict origRv predi (rvTrue, rvFalse)) = le
312311createIncDepsForIsomorph _ _ = M.empty
313312
314313-- in the case of IsoRestrict, the database context should be updated with the restriction so that if the restriction does not hold, then the schema cannot be created
315−evalSchemaExpr :: SchemaExpr -> DatabaseContext -> Subschemas -> Either RelationalError (Subschemas, DatabaseContext)
316−evalSchemaExpr (AddSubschema sname morphs) context sschemas =
314+evalSchemaExpr :: SchemaExpr -> DatabaseContext -> TransactionId -> TransactionGraph -> Subschemas -> Either RelationalError (Subschemas, DatabaseContext)
315+evalSchemaExpr (AddSubschema sname morphs) context transId graph sschemas =
317316 if M.member sname sschemas then
318317 Left (SubschemaNameInUseError sname)
319− else case valid of
320− Just err -> Left (SchemaCreationError err)
321− Nothing ->
322− let newSchemas = M.insert sname newSchema sschemas
323− moreIncDeps = foldr (\morph acc -> M.union acc (createIncDepsForIsomorph sname morph)) M.empty morphs
324− incDepExprs = MultipleExpr (map (uncurry AddInclusionDependency) (M.toList moreIncDeps))
325− in
326− case runState (evalDatabaseContextExpr incDepExprs) (context, M.empty, False) of
327− (Left err, _) -> Left err
328− (Right (), (newContext,_,_)) -> pure (newSchemas, newContext) --need to propagate dirty flag here
329− where
330− newSchema = Schema morphs
331− valid = validateSchema newSchema context
332−evalSchemaExpr (RemoveSubschema sname) context sschemas = if M.member sname sschemas then
318+ else
319+ case validateSchema (Schema morphs) context of
320+ Just err -> Left (SchemaCreationError err)
321+ Nothing -> do
322+ let newSchemas = M.insert sname newSchema sschemas
323+ newSchema = Schema morphs
324+ moreIncDeps = foldr (\morph acc -> M.union acc (createIncDepsForIsomorph sname morph)) M.empty morphs
325+ incDepExprs = MultipleExpr (map (uncurry AddInclusionDependency) (M.toList moreIncDeps))
326+ dbenv = mkDatabaseContextEvalEnv transId graph
327+ dbstate <- runDatabaseContextEvalMonad context dbenv (evalGraphRefDatabaseContextExpr incDepExprs)
328+ pure (newSchemas, dbc_context dbstate)
329+--need to propagate dirty flag here
330+
331+evalSchemaExpr (RemoveSubschema sname) context _ _ sschemas = if M.member sname sschemas then
333332 pure (M.delete sname sschemas, context)
334333 else
335334 Left (SubschemaNameNotInUseError sname)
src/lib/ProjectM36/NormalizeExpr.hsnew file+132
@@ -0,0 +1,132 @@
1+-- | Functions to convert all types of expresions into their GraphRef- equivalents.
2+module ProjectM36.NormalizeExpr where
3+import ProjectM36.Base
4+import Control.Monad.Trans.Reader as R
5+import qualified Data.Map as M
6+
7+--used to process/normalize exprs to their respective graph ref forms
8+type ProcessExprM a = Reader GraphRefTransactionMarker a
9+
10+type CurrentTransactionId = TransactionId
11+
12+runProcessExprM :: GraphRefTransactionMarker -> ProcessExprM a -> a
13+runProcessExprM mtid m = runReader m mtid
14+
15+askMarker :: ProcessExprM GraphRefTransactionMarker
16+askMarker = R.ask
17+
18+-- convert a RelationalExpr into a GraphRefRelationalExpr using the current trans Id
19+processRelationalExpr :: RelationalExpr -> ProcessExprM GraphRefRelationalExpr
20+processRelationalExpr (MakeRelationFromExprs mAttrs tupleExprs) = do
21+ mAttrs' <- case mAttrs of
22+ Nothing -> pure Nothing
23+ Just mAttrs'' -> Just <$> mapM processAttributeExpr mAttrs''
24+ MakeRelationFromExprs mAttrs' <$> processTupleExprs tupleExprs
25+processRelationalExpr (MakeStaticRelation attrs tupSet) = pure (MakeStaticRelation attrs tupSet)
26+processRelationalExpr (ExistingRelation rel) = pure (ExistingRelation rel)
27+--requires current trans id and graph
28+processRelationalExpr (RelationVariable rv ()) = RelationVariable rv <$> askMarker
29+processRelationalExpr (Project attrNames expr) = Project <$> processAttributeNames attrNames <*> processRelationalExpr expr
30+processRelationalExpr (Union exprA exprB) = Union <$> processRelationalExpr exprA <*> processRelationalExpr exprB
31+processRelationalExpr (Join exprA exprB) = Join <$> processRelationalExpr exprA <*> processRelationalExpr exprB
32+processRelationalExpr (Rename attrA attrB expr) =
33+ Rename attrA attrB <$> processRelationalExpr expr
34+processRelationalExpr (Difference exprA exprB) = Difference <$> processRelationalExpr exprA <*> processRelationalExpr exprB
35+processRelationalExpr (Group attrNames attrName expr) = Group <$> processAttributeNames attrNames <*> pure attrName <*> processRelationalExpr expr
36+processRelationalExpr (Ungroup attrName expr) = Ungroup attrName <$> processRelationalExpr expr
37+processRelationalExpr (Restrict pred' expr) = Restrict <$> processRestrictionPredicateExpr pred' <*> processRelationalExpr expr
38+processRelationalExpr (Equals exprA exprB) =
39+ Equals <$> processRelationalExpr exprA <*> processRelationalExpr exprB
40+processRelationalExpr (NotEquals exprA exprB) =
41+ NotEquals <$> processRelationalExpr exprA <*> processRelationalExpr exprB
42+processRelationalExpr (Extend extendExpr expr) =
43+ Extend <$> processExtendTupleExpr extendExpr <*> processRelationalExpr expr
44+processRelationalExpr (With macros expr) =
45+ With <$> mapM (\(wnexpr, macroExpr) -> (,) <$> processWithNameExpr wnexpr <*> processRelationalExpr macroExpr) macros <*> processRelationalExpr expr
46+
47+processWithNameExpr :: WithNameExpr -> ProcessExprM GraphRefWithNameExpr
48+processWithNameExpr (WithNameExpr rvname ()) =
49+ WithNameExpr rvname <$> askMarker
50+
51+processAttributeNames :: AttributeNames -> ProcessExprM GraphRefAttributeNames
52+processAttributeNames (AttributeNames nameSet) = pure $ AttributeNames nameSet
53+processAttributeNames (InvertedAttributeNames attrNameSet) =
54+ pure $ InvertedAttributeNames attrNameSet
55+processAttributeNames (UnionAttributeNames attrNamesA attrNamesB) = UnionAttributeNames <$> processAttributeNames attrNamesA <*> processAttributeNames attrNamesB
56+processAttributeNames (IntersectAttributeNames attrNamesA attrNamesB) = IntersectAttributeNames <$> processAttributeNames attrNamesA <*> processAttributeNames attrNamesB
57+processAttributeNames (RelationalExprAttributeNames expr) = RelationalExprAttributeNames <$> processRelationalExpr expr
58+
59+processDatabaseContextExpr :: DatabaseContextExpr -> ProcessExprM GraphRefDatabaseContextExpr
60+processDatabaseContextExpr expr =
61+ case expr of
62+ NoOperation -> pure NoOperation
63+ Define nam attrExprs -> Define nam <$> mapM processAttributeExpr attrExprs
64+ Undefine nam -> pure (Undefine nam)
65+ Assign nam rexpr -> Assign nam <$> processRelationalExpr rexpr
66+ Insert nam rexpr -> Insert nam <$> processRelationalExpr rexpr
67+ Delete nam pred' -> Delete nam <$> processRestrictionPredicateExpr pred'
68+ Update nam attrMap pred' -> Update nam attrMap <$> processRestrictionPredicateExpr pred'
69+
70+ AddInclusionDependency nam dep -> pure (AddInclusionDependency nam dep)
71+ RemoveInclusionDependency nam -> pure (RemoveInclusionDependency nam)
72+ AddNotification nam exprA exprB exprC -> pure (AddNotification nam exprA exprB exprC)
73+ RemoveNotification nam -> pure (RemoveNotification nam)
74+ AddTypeConstructor tyDef consDefs -> pure (AddTypeConstructor tyDef consDefs)
75+ RemoveTypeConstructor tyName -> pure (RemoveTypeConstructor tyName)
76+
77+ RemoveAtomFunction aFuncName -> pure (RemoveAtomFunction aFuncName)
78+ RemoveDatabaseContextFunction funcName -> pure (RemoveDatabaseContextFunction funcName)
79+ ExecuteDatabaseContextFunction funcName atomExprs -> ExecuteDatabaseContextFunction funcName <$> mapM processAtomExpr atomExprs
80+ MultipleExpr exprs -> MultipleExpr <$> mapM processDatabaseContextExpr exprs
81+
82+processDatabaseContextIOExpr :: DatabaseContextIOExpr -> ProcessExprM GraphRefDatabaseContextIOExpr
83+processDatabaseContextIOExpr (AddAtomFunction f tcs sc) =
84+ pure (AddAtomFunction f tcs sc)
85+processDatabaseContextIOExpr (LoadAtomFunctions mod' fun file) =
86+ pure (LoadAtomFunctions mod' fun file)
87+processDatabaseContextIOExpr (AddDatabaseContextFunction mod' fun path) =
88+ pure (AddDatabaseContextFunction mod' fun path)
89+processDatabaseContextIOExpr (LoadDatabaseContextFunctions mod' fun path) =
90+ pure (LoadDatabaseContextFunctions mod' fun path)
91+processDatabaseContextIOExpr (CreateArbitraryRelation rvName attrExprs range) =
92+ CreateArbitraryRelation rvName <$> mapM processAttributeExpr attrExprs <*> pure range
93+
94+processRestrictionPredicateExpr :: RestrictionPredicateExpr -> ProcessExprM GraphRefRestrictionPredicateExpr
95+processRestrictionPredicateExpr TruePredicate = pure TruePredicate
96+processRestrictionPredicateExpr (AndPredicate a b) = AndPredicate <$> processRestrictionPredicateExpr a <*> processRestrictionPredicateExpr b
97+processRestrictionPredicateExpr (OrPredicate a b) = OrPredicate <$> processRestrictionPredicateExpr a <*> processRestrictionPredicateExpr b
98+processRestrictionPredicateExpr (NotPredicate a) = NotPredicate <$> processRestrictionPredicateExpr a
99+processRestrictionPredicateExpr (RelationalExprPredicate expr) =
100+ RelationalExprPredicate <$> processRelationalExpr expr
101+processRestrictionPredicateExpr (AtomExprPredicate expr) =
102+ AtomExprPredicate <$> processAtomExpr expr
103+processRestrictionPredicateExpr (AttributeEqualityPredicate nam expr) =
104+ AttributeEqualityPredicate nam <$> processAtomExpr expr
105+
106+processExtendTupleExpr :: ExtendTupleExpr -> ProcessExprM GraphRefExtendTupleExpr
107+processExtendTupleExpr (AttributeExtendTupleExpr nam atomExpr) =
108+ AttributeExtendTupleExpr nam <$> processAtomExpr atomExpr
109+
110+processAtomExpr :: AtomExpr -> ProcessExprM GraphRefAtomExpr
111+processAtomExpr (AttributeAtomExpr nam) = pure $ AttributeAtomExpr nam
112+processAtomExpr (NakedAtomExpr atom) = pure $ NakedAtomExpr atom
113+processAtomExpr (FunctionAtomExpr fName atomExprs ()) =
114+ FunctionAtomExpr fName <$> mapM processAtomExpr atomExprs <*> askMarker
115+processAtomExpr (RelationAtomExpr expr) = RelationAtomExpr <$> processRelationalExpr expr
116+processAtomExpr (ConstructedAtomExpr dConsName atomExprs ()) = ConstructedAtomExpr dConsName <$> mapM processAtomExpr atomExprs <*> askMarker
117+
118+processTupleExprs :: TupleExprs -> ProcessExprM GraphRefTupleExprs
119+processTupleExprs (TupleExprs () tupleExprs) = do
120+ marker <- askMarker
121+ TupleExprs marker <$> mapM processTupleExpr tupleExprs
122+
123+processTupleExpr :: TupleExpr -> ProcessExprM GraphRefTupleExpr
124+processTupleExpr (TupleExpr tMap) =
125+ TupleExpr . M.fromList <$> mapM (\(k,v) -> (,) k <$> processAtomExpr v) (M.toList tMap)
126+
127+--convert AttributeExpr to GraphRefAttributeExpr
128+processAttributeExpr :: AttributeExpr -> ProcessExprM GraphRefAttributeExpr
129+processAttributeExpr (AttributeAndTypeNameExpr nam tCons ()) =
130+ AttributeAndTypeNameExpr nam tCons <$> askMarker
131+processAttributeExpr (NakedAttributeExpr attr) = pure $ NakedAttributeExpr attr
132+
src/lib/ProjectM36/Notifications.hs+12−7
@@ -1,17 +1,22 @@
11module ProjectM36.Notifications where
22import ProjectM36.Base
3+import ProjectM36.Error
34import ProjectM36.RelationalExpression
4−import Control.Monad.Trans.Reader
5+import ProjectM36.StaticOptimizer
56import qualified Data.Map as M
67import Data.Either (isRight)
78
89-- | Returns the notifications which should be triggered based on the transition from the first 'DatabaseContext' to the second 'DatabaseContext'.
9−notificationChanges :: Notifications -> DatabaseContext -> DatabaseContext -> Notifications
10−notificationChanges nots context1 context2 = M.filter notificationFilter nots
10+notificationChanges :: Notifications -> TransactionGraph -> DatabaseContext -> DatabaseContext -> Notifications
11+notificationChanges nots graph context1 context2 = M.filter notificationFilter nots
1112 where
12− notificationFilter (Notification chExpr _ _) = let oldChangeEval = evalChangeExpr chExpr (RelationalExprStateElems context1)
13− newChangeEval = evalChangeExpr chExpr (RelationalExprStateElems context2) in
14− oldChangeEval /= newChangeEval && isRight oldChangeEval
15− evalChangeExpr chExpr = runReader (evalRelationalExpr chExpr)
13+ notificationFilter (Notification chExpr _ _) = oldChangeEval /= newChangeEval && isRight oldChangeEval
14+ where
15+ oldChangeEval = evalChangeExpr chExpr (mkRelationalExprEnv context1 graph)
16+ newChangeEval = evalChangeExpr chExpr (mkRelationalExprEnv context2 graph)
17+
18+ evalChangeExpr :: RelationalExpr -> RelationalExprEnv -> Either RelationalError Relation
19+ evalChangeExpr chExpr env =
20+ optimizeAndEvalRelationalExpr env chExpr
1621
1722
src/lib/ProjectM36/Relation.hs+2−18
@@ -4,8 +4,6 @@ import qualified Data.Set as S
44import qualified Data.HashSet as HS
55import Control.Monad
66import qualified Data.Vector as V
7−import qualified Data.Map as M
8−import ProjectM36.AtomType
97import ProjectM36.Base
108import ProjectM36.Tuple
119import qualified ProjectM36.Attribute as A
@@ -97,7 +95,7 @@ union (Relation attrs1 tupSet1) (Relation attrs2 tupSet2) =
9795 where
9896 newtuples = RelationTupleSet $ HS.toList . HS.fromList $ asList tupSet1 ++ map (reorderTuple attrs1) (asList tupSet2)
9997
100−project :: S.Set AttributeName -> Relation -> Either RelationalError Relation
98+project :: S.Set AttributeName -> Relation -> Either RelationalError Relation
10199project attrNames rel@(Relation _ tupSet) = do
102100 newAttrs <- A.projectionAttributesForNames attrNames (attributes rel)
103101 let newAttrNameSet = A.attributeNameSet newAttrs
@@ -304,20 +302,6 @@ typesAsRelation types = mkRelationFromTuples attrs tuples
304302 mkDataConsRelation dConsList = case mkRelationFromTuples subAttrs $ map (\dCons -> RelationTuple subAttrs (V.singleton $ TextAtom $ T.intercalate " " (DCD.name dCons:map (T.pack . show) (DCD.fields dCons)))) dConsList of
305303 Left err -> error ("mkRelationFromTuples pooped " ++ show err)
306304 Right rel -> RelationAtom rel
307−
308−-- | Return a Relation describing the relation variables.
309−relationVariablesAsRelation :: M.Map RelVarName Relation -> Either RelationalError Relation
310−relationVariablesAsRelation relVarMap = mkRelationFromList attrs tups
311− where
312− subrelAttrs = A.attributesFromList [Attribute "attribute" TextAtomType, Attribute "type" TextAtomType]
313− attrs = A.attributesFromList [Attribute "name" TextAtomType,
314− Attribute "attributes" (RelationAtomType subrelAttrs)]
315− tups = map relVarToAtomList (M.toList relVarMap)
316− relVarToAtomList (rvName, rel) = [TextAtom rvName, attributesToRel (attributes rel)]
317− attributesToRel attrl = case mkRelationFromList subrelAttrs (map attrAtoms (V.toList attrl)) of
318− Left err -> error ("relationVariablesAsRelation pooped " ++ show err)
319− Right rel -> RelationAtom rel
320− attrAtoms a = [TextAtom (A.attributeName a), TextAtom (prettyAtomType (A.atomType a))]
321305
322306-- | Randomly resort the tuples. This is useful for emphasizing that two relations are equal even when they are printed to the console in different orders.
323307randomizeTupleOrder :: MonadRandom m => Relation -> m Relation
@@ -330,4 +314,4 @@ oneTuple (Relation _ (RelationTupleSet [])) = Nothing
330314oneTuple (Relation _ (RelationTupleSet (x:_))) = Just x
331315
332316tuplesList :: Relation -> [RelationTuple]
333−tuplesList (Relation _ tupleSet) = asList tupleSet
317+tuplesList (Relation _ tupleSet) = asList tupleSet
src/lib/ProjectM36/RelationalExpression.hs+987−475
@@ -1,39 +1,51 @@
11{-# LANGUAGE ScopedTypeVariables #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE FlexibleContexts #-}
24module ProjectM36.RelationalExpression where
35import ProjectM36.Relation
46import ProjectM36.Tuple
57import ProjectM36.TupleSet
68import ProjectM36.Base
9+import qualified Data.UUID as U
710import ProjectM36.Error
811import ProjectM36.AtomType
9−import ProjectM36.Attribute (emptyAttributes)
12+import ProjectM36.Attribute (emptyAttributes, attributesFromList)
1013import ProjectM36.ScriptSession
1114import ProjectM36.DataTypes.Primitive
1215import ProjectM36.AtomFunction
1316import ProjectM36.DatabaseContextFunction
1417import ProjectM36.Arbitrary
18+import ProjectM36.GraphRefRelationalExpr
19+import ProjectM36.Transaction
1520import qualified ProjectM36.Attribute as A
1621import qualified Data.Map as M
1722import qualified Data.HashSet as HS
1823import qualified Data.Set as S
1924import Control.Monad.State hiding (join)
25+import Data.Bifunctor (second)
2026import Data.Maybe
2127import Data.Either
2228import Data.Char (isUpper)
29+import Data.Time
30+import qualified Data.List.NonEmpty as NE
31+import Data.Functor.Identity
2332import qualified Data.Text as T
2433import qualified Data.Vector as V
2534import qualified ProjectM36.TypeConstructorDef as TCD
26−import Control.Monad.Trans.Except
27−import Control.Monad.Trans.Reader
35+import qualified Control.Monad.RWS.Strict as RWS
36+import Control.Monad.RWS.Strict (RWST, execRWST, runRWST)
37+import Control.Monad.Except hiding (join)
38+import Control.Monad.Trans.Except (except)
39+import Control.Monad.Reader as R hiding (join)
40+import ProjectM36.NormalizeExpr
41+import ProjectM36.WithNameExpr
2842import Test.QuickCheck
29−
3043#ifdef PM36_HASKELL_SCRIPTING
31−import GHC
44+import GHC hiding (getContext)
3245import Control.Exception
3346import GHC.Paths
3447#endif
3548
36−
3749data DatabaseContextExprDetails = CountUpdatedTuples
3850
3951databaseContextExprDetailsFunc :: DatabaseContextExprDetails -> ResultAccumFunc
@@ -49,32 +61,63 @@ databaseContextExprDetailsFunc CountUpdatedTuples _ relIn = Relation attrs newTu
4961 Right ts -> ts
5062
5163-- | Used to start a fresh database state for a new database context expression.
52−freshDatabaseState :: DatabaseContext -> DatabaseStateElems
53−freshDatabaseState ctx = (ctx, M.empty, False) --future work: propagate return accumulator
64+mkDatabaseContextEvalState :: DatabaseContext -> DatabaseContextEvalState
65+mkDatabaseContextEvalState context = DatabaseContextEvalState {
66+ dbc_context = context,
67+ dbc_accum = M.empty,
68+ dbc_dirty = False
69+ } --future work: propagate return accumulator
5470
5571-- we need to pass around a higher level RelationTuple and Attributes in order to solve #52
56−data RelationalExprStateElems = RelationalExprStateTupleElems DatabaseContext RelationTuple | -- used when fully evaluating a relexpr
57− RelationalExprStateAttrsElems DatabaseContext Attributes | --used when evaluating the type of a relexpr
58− RelationalExprStateElems DatabaseContext --used by default at the top level of evaluation
59−
60−instance Show RelationalExprStateElems where
61− show (RelationalExprStateTupleElems _ tup) = "RelationalExprStateTupleElems " ++ show tup
62− show (RelationalExprStateAttrsElems _ attrs) = "RelationalExprStateAttrsElems" ++ show attrs
63− show (RelationalExprStateElems _) = "RelationalExprStateElems"
64−
65−mkRelationalExprState :: DatabaseContext -> RelationalExprStateElems
66−mkRelationalExprState = RelationalExprStateElems
67−
68−mergeTuplesIntoRelationalExprState :: RelationTuple -> RelationalExprStateElems -> RelationalExprStateElems
69−mergeTuplesIntoRelationalExprState tupIn (RelationalExprStateElems ctx) = RelationalExprStateTupleElems ctx tupIn
70−mergeTuplesIntoRelationalExprState _ st@(RelationalExprStateAttrsElems _ _) = st
71−mergeTuplesIntoRelationalExprState tupIn (RelationalExprStateTupleElems ctx existingTuple) = let mergedTupMap = M.union (tupleToMap tupIn) (tupleToMap existingTuple) in
72− RelationalExprStateTupleElems ctx (mkRelationTupleFromMap mergedTupMap)
72+data RelationalExprEnv = RelationalExprEnv {
73+ re_context :: DatabaseContext,
74+ re_graph :: TransactionGraph,
75+ re_extra :: Maybe (Either RelationTuple Attributes)
76+ }
77+
78+envTuple :: GraphRefRelationalExprEnv -> RelationTuple
79+envTuple e = fromLeft emptyTuple (fromMaybe (Left emptyTuple) (gre_extra e))
80+
81+envAttributes :: GraphRefRelationalExprEnv -> Attributes
82+envAttributes e = fromRight emptyAttributes (fromMaybe (Right emptyAttributes) (gre_extra e))
7383
74−mergeAttributesIntoRelationalExprState :: Attributes -> RelationalExprStateElems -> RelationalExprStateElems
75−mergeAttributesIntoRelationalExprState attrs (RelationalExprStateElems ctx) = RelationalExprStateAttrsElems ctx attrs
76−mergeAttributesIntoRelationalExprState _ st@(RelationalExprStateTupleElems _ _) = st
77−mergeAttributesIntoRelationalExprState attrsIn (RelationalExprStateAttrsElems ctx attrs) = RelationalExprStateAttrsElems ctx (A.union attrsIn attrs)
84+instance Show RelationalExprEnv where
85+ show e@RelationalExprEnv{} = "RelationalExprEnv " ++ show (re_extra e)
86+
87+--used to eval relationalexpr
88+type RelationalExprM a = ReaderT RelationalExprEnv (ExceptT RelationalError Identity) a
89+
90+runRelationalExprM :: RelationalExprEnv -> RelationalExprM a -> Either RelationalError a
91+runRelationalExprM env m = runIdentity (runExceptT (runReaderT m env))
92+
93+reGraph :: RelationalExprM TransactionGraph
94+reGraph = asks re_graph
95+
96+reContext :: RelationalExprM DatabaseContext
97+reContext = asks re_context
98+
99+mkRelationalExprEnv :: DatabaseContext -> TransactionGraph -> RelationalExprEnv
100+mkRelationalExprEnv ctx graph =
101+ RelationalExprEnv
102+ { re_context = ctx,
103+ re_graph = graph,
104+ re_extra = Nothing }
105+
106+askEnv :: GraphRefRelationalExprM GraphRefRelationalExprEnv
107+askEnv = R.ask
108+
109+mergeTuplesIntoGraphRefRelationalExprEnv :: RelationTuple -> GraphRefRelationalExprEnv -> GraphRefRelationalExprEnv
110+mergeTuplesIntoGraphRefRelationalExprEnv tupIn e =
111+ e{ gre_extra = new_elems }
112+ where
113+ new_elems = Just (Left newTuple)
114+ mergedTupMap = M.union (tupleToMap tupIn) (tupleToMap (envTuple e))
115+ newTuple = mkRelationTupleFromMap mergedTupMap
116+
117+mergeAttributesIntoGraphRefRelationalExprEnv :: Attributes -> GraphRefRelationalExprEnv -> GraphRefRelationalExprEnv
118+mergeAttributesIntoGraphRefRelationalExprEnv attrsIn e = e { gre_extra = newattrs }
119+ where
120+ newattrs = Just (Right (A.union attrsIn (envAttributes e)))
78121
79122type ResultAccumName = StringType
80123
@@ -84,36 +127,103 @@ data ResultAccum = ResultAccum { resultAccumFunc :: ResultAccumFunc,
84127 resultAccumResult :: Relation
85128 }
86129
87−type DatabaseStateElems = (DatabaseContext, M.Map ResultAccumName ResultAccum, DirtyFlag)
130+data DatabaseContextEvalState = DatabaseContextEvalState {
131+ dbc_context :: DatabaseContext, --new, alterable context for a new transaction
132+ dbc_accum :: M.Map ResultAccumName ResultAccum,
133+ dbc_dirty :: DirtyFlag
134+ }
88135
89−type DatabaseState a = State DatabaseStateElems a
136+data DatabaseContextEvalEnv = DatabaseContextEvalEnv
137+ { dce_transId :: TransactionId,
138+ dce_graph :: TransactionGraph
139+ }
90140
91−getStateContext :: DatabaseState DatabaseContext
92−getStateContext = do
93− (ctx,_, _) <- get
94− pure ctx
95−
96−putStateContext :: DatabaseContext -> DatabaseState ()
97−putStateContext ctx = do
98− (_, accum, _) <- get
99− put (ctx, accum, True)
141+mkDatabaseContextEvalEnv :: TransactionId -> TransactionGraph -> DatabaseContextEvalEnv
142+mkDatabaseContextEvalEnv = DatabaseContextEvalEnv
143+
144+type DatabaseContextEvalMonad a = RWST DatabaseContextEvalEnv () DatabaseContextEvalState (ExceptT RelationalError Identity) a
145+
146+runDatabaseContextEvalMonad :: DatabaseContext -> DatabaseContextEvalEnv -> DatabaseContextEvalMonad () -> Either RelationalError DatabaseContextEvalState
147+runDatabaseContextEvalMonad ctx env m = runIdentity (runExceptT (fst <$> execRWST m env freshEnv))
148+ where
149+ freshEnv = mkDatabaseContextEvalState ctx
150+
151+
152+dbcTransId :: DatabaseContextEvalMonad TransactionId
153+dbcTransId = dce_transId <$> RWS.ask
154+
155+dbcGraph :: DatabaseContextEvalMonad TransactionGraph
156+dbcGraph = dce_graph <$> RWS.ask
157+
158+dbcRelationalExprEnv :: DatabaseContextEvalMonad RelationalExprEnv
159+dbcRelationalExprEnv =
160+ mkRelationalExprEnv <$> getStateContext <*> dbcGraph
161+
162+getStateContext :: DatabaseContextEvalMonad DatabaseContext
163+getStateContext = gets dbc_context
164+
165+putStateContext :: DatabaseContext -> DatabaseContextEvalMonad ()
166+putStateContext ctx' = do
167+ s <- get
168+ put (s {dbc_context = ctx', dbc_dirty = True})
169+
170+-- | The context is optionally passed down along in cases where the current context is uncommitted.
171+data GraphRefRelationalExprEnv =
172+ GraphRefRelationalExprEnv {
173+ gre_context :: Maybe DatabaseContext,
174+ gre_graph :: TransactionGraph,
175+ gre_extra :: Maybe (Either RelationTuple Attributes)
176+ }
100177
101−type RelationalExprState a = Reader RelationalExprStateElems a
178+type GraphRefRelationalExprM a = ReaderT GraphRefRelationalExprEnv (ExceptT RelationalError Identity) a
179+
180+gfTransForId :: TransactionId -> GraphRefRelationalExprM Transaction
181+gfTransForId tid = do
182+ graph <- gfGraph
183+ lift $ except $ transactionForId tid graph
184+
185+gfDatabaseContextForMarker :: GraphRefTransactionMarker -> GraphRefRelationalExprM DatabaseContext
186+gfDatabaseContextForMarker (TransactionMarker transId) = concreteDatabaseContext <$> gfTransForId transId
187+gfDatabaseContextForMarker UncommittedContextMarker = do
188+ mctx <- gre_context <$> askEnv
189+ case mctx of
190+ Nothing -> throwError NoUncommittedContextInEvalError
191+ Just ctx -> pure ctx
192+
193+runGraphRefRelationalExprM :: GraphRefRelationalExprEnv -> GraphRefRelationalExprM a -> Either RelationalError a
194+runGraphRefRelationalExprM env m = runIdentity (runExceptT (runReaderT m env))
195+
196+freshGraphRefRelationalExprEnv :: Maybe DatabaseContext -> TransactionGraph -> GraphRefRelationalExprEnv
197+freshGraphRefRelationalExprEnv mctx graph = GraphRefRelationalExprEnv {
198+ gre_context = mctx,
199+ gre_graph = graph,
200+ gre_extra = Nothing
201+ }
202+
203+gfGraph :: GraphRefRelationalExprM TransactionGraph
204+gfGraph = asks gre_graph
102205
103−stateElemsContext :: RelationalExprStateElems -> DatabaseContext
104−stateElemsContext (RelationalExprStateTupleElems ctx _) = ctx
105−stateElemsContext (RelationalExprStateElems ctx) = ctx
106−stateElemsContext (RelationalExprStateAttrsElems ctx _) = ctx
206+envContext :: RelationalExprEnv -> DatabaseContext
207+envContext = re_context
107208
108−setStateElemsContext :: RelationalExprStateElems -> DatabaseContext -> RelationalExprStateElems
109−setStateElemsContext (RelationalExprStateTupleElems _ tup) ctx = RelationalExprStateTupleElems ctx tup
110−setStateElemsContext (RelationalExprStateElems _) ctx = RelationalExprStateElems ctx
111−setStateElemsContext (RelationalExprStateAttrsElems _ attrs) ctx = RelationalExprStateAttrsElems ctx attrs
209+setEnvContext :: RelationalExprEnv -> DatabaseContext -> RelationalExprEnv
210+setEnvContext e ctx = e { re_context = ctx }
112211
212+{-
213+--full evaluation down the graph
214+eval :: RelationalExpr -> RelationalExprState (Either RelationalError Relation)
215+eval expr = do
216+ env <- askEnv
217+ eGfExpr <- processRelationalExpr expr
218+ case eGfExpr of
219+ Left err -> pure (Left err)
220+ Right gfExpr -> pure $ evalGraphRefRelationalExpr gfExpr (re_graph env)
221+-}
222+{-
113223--relvar state is needed in evaluation of relational expression but only as read-only in order to extract current relvar values
114−evalRelationalExpr :: RelationalExpr -> RelationalExprState (Either RelationalError Relation)
224+evalRelationalExpr :: RelationalExpr -> RelationalExprState (Either RelationalError GraphRefRelationalExpr)
115225evalRelationalExpr (RelationVariable name _) = do
116− relvarTable <- fmap (relationVariables . stateElemsContext) ask
226+ relvarTable <- fmap (relationVariables . envContext) askEnv
117227 return $ case M.lookup name relvarTable of
118228 Just res -> Right res
119229 Nothing -> Left $ RelVarNotDefinedError name
@@ -125,7 +235,7 @@ evalRelationalExpr (Project attrNames expr) = do
125235 Right attrNameSet -> do
126236 rel <- evalRelationalExpr expr
127237 case rel of
128− Right rel2 -> pure $ project attrNameSet rel2
238+ Right rel2 -> pure $ Right (Project (AttributeNames attrNameSet) rel2)
129239 Left err -> pure $ Left err
130240
131241evalRelationalExpr (Union exprA exprB) = do
@@ -135,7 +245,7 @@ evalRelationalExpr (Union exprA exprB) = do
135245 Left err -> return $ Left err
136246 Right relA2 -> case relB of
137247 Left err -> return $ Left err
138− Right relB2 -> return $ union relA2 relB2
248+ Right relB2 -> return $ Right (Union relA2 relB2)
139249
140250evalRelationalExpr (Join exprA exprB) = do
141251 relA <- evalRelationalExpr exprA
@@ -144,7 +254,7 @@ evalRelationalExpr (Join exprA exprB) = do
144254 Left err -> return $ Left err
145255 Right relA2 -> case relB of
146256 Left err -> return $ Left err
147− Right relB2 -> return $ join relA2 relB2
257+ Right relB2 -> return $ Right (Join relA2 relB2)
148258
149259evalRelationalExpr (Difference exprA exprB) = do
150260 relA <- evalRelationalExpr exprA
@@ -153,33 +263,34 @@ evalRelationalExpr (Difference exprA exprB) = do
153263 Left err -> return $ Left err
154264 Right relA2 -> case relB of
155265 Left err -> return $ Left err
156− Right relB2 -> return $ difference relA2 relB2
266+ Right relB2 -> return $ Right (Difference relA2 relB2)
157267
158268evalRelationalExpr (MakeStaticRelation attributeSet tupleSet) =
159269 case mkRelation attributeSet tupleSet of
160− Right rel -> return $ Right rel
270+ Right rel -> return $ Right (ExistingRelation rel)
161271 Left err -> return $ Left err
162272
163273evalRelationalExpr (MakeRelationFromExprs mAttrExprs tupleExprs) = do
164− currentContext <- fmap stateElemsContext ask
274+ currentContext <- fmap envContext askEnv
165275 let tConss = typeConstructorMapping currentContext
166276 -- if the mAttrExprs is Nothing, then we should attempt to infer the tuple attributes from the first tuple itself- note that this is not always possible
167277 runExceptT $ do
168278 mAttrs <- case mAttrExprs of
169279 Just _ ->
170− Just . A.attributesFromList <$> mapM (either throwE pure . evalAttrExpr tConss) (fromMaybe [] mAttrExprs)
280+ Just . A.attributesFromList <$> mapM evalGraphRefAttrExpr (fromMaybe [] mAttrExprs)
171281 Nothing -> pure Nothing
172282 tuples <- mapM (liftE . evalTupleExpr mAttrs) tupleExprs
173283 let attrs = fromMaybe firstTupleAttrs mAttrs
174284 firstTupleAttrs = if null tuples then A.emptyAttributes else tupleAttributes (head tuples)
175− either throwE pure (mkRelation attrs (RelationTupleSet tuples))
285+ expr <- either throwE pure (mkRelation attrs (RelationTupleSet tuples))
286+ pure (ExistingRelation expr)
176287
177−evalRelationalExpr (ExistingRelation rel) = pure (Right rel)
288+evalRelationalExpr (ExistingRelation rel) = pure (Right (ExistingRelation rel))
178289
179290evalRelationalExpr (Rename oldAttrName newAttrName relExpr) = do
180291 evald <- evalRelationalExpr relExpr
181292 case evald of
182− Right rel -> return $ rename oldAttrName newAttrName rel
293+ Right expr -> return $ Right (Rename oldAttrName newAttrName expr)
183294 Left err -> return $ Left err
184295
185296evalRelationalExpr (Group oldAttrNames newAttrName relExpr) = do
@@ -189,35 +300,32 @@ evalRelationalExpr (Group oldAttrNames newAttrName relExpr) = do
189300 Right oldAttrNameSet -> do
190301 evald <- evalRelationalExpr relExpr
191302 case evald of
192− Right rel -> return $ group oldAttrNameSet newAttrName rel
303+ Right expr -> return $ Right (Group (AttributeNames oldAttrNameSet) newAttrName expr)
193304 Left err -> return $ Left err
194305
195306evalRelationalExpr (Ungroup attrName relExpr) = do
196307 evald <- evalRelationalExpr relExpr
197308 case evald of
198− Right rel -> return $ ungroup attrName rel
309+ Right expr -> return $ Right (Ungroup attrName expr)
199310 Left err -> return $ Left err
200311
201312evalRelationalExpr (Restrict predicateExpr relExpr) = do
202313 evald <- evalRelationalExpr relExpr
314+ pred <- processRestrictionPredicateExpr predicateExpr
203315 case evald of
204316 Left err -> return $ Left err
205− Right rel -> do
206− eFilterFunc <- predicateRestrictionFilter (attributes rel) predicateExpr
207− case eFilterFunc of
208− Left err -> return $ Left err
209− Right filterfunc ->
210− pure (restrict filterfunc rel)
317+ Right expr ->
318+ pure $ Right (Restrict pred expr)
211319
212320evalRelationalExpr (Equals relExprA relExprB) = do
213321 evaldA <- evalRelationalExpr relExprA
214322 evaldB <- evalRelationalExpr relExprB
215323 case evaldA of
216324 Left err -> return $ Left err
217− Right relA -> case evaldB of
325+ Right exprA -> case evaldB of
218326 Left err -> return $ Left err
219− Right relB -> return $ Right $ if relA == relB then relationTrue else relationFalse
220−
327+ Right exprB -> return $ Right (Equals exprA exprB)
328+{-
221329evalRelationalExpr (With views mainExpr) = do
222330 rstate <- ask
223331
@@ -231,310 +339,325 @@ evalRelationalExpr (With views mainExpr) = do
231339 case foldM addScopedView (stateElemsContext rstate) views of
232340 Left err -> return $ Left err
233341 Right ctx'' -> do
234− let evalMainExpr expr = runReader (evalRelationalExpr expr) (RelationalExprStateElems ctx'')
342+ let evalMainExpr expr = runReader (evalRelationalExpr expr) (RelationalExprEnv ctx'')
235343 case evalMainExpr mainExpr of
236344 Left err -> return $ Left err
237345 Right rel -> return $ Right rel
238−
346+-}
239347--warning: copy-pasta from above- refactor
240348evalRelationalExpr (NotEquals relExprA relExprB) = do
241349 evaldA <- evalRelationalExpr relExprA
242350 evaldB <- evalRelationalExpr relExprB
243351 case evaldA of
244352 Left err -> return $ Left err
245− Right relA -> case evaldB of
353+ Right exprA -> case evaldB of
246354 Left err -> return $ Left err
247− Right relB -> return $ Right $ if relA /= relB then relationTrue else relationFalse
355+ Right exprB -> return $ Right (NotEquals exprA exprB)
248356
249357-- extending a relation adds a single attribute with the results of the per-tuple expression evaluated
250358evalRelationalExpr (Extend tupleExpression relExpr) = do
251− rstate <- ask
252− let evald = runReader (evalRelationalExpr relExpr) rstate
253− case evald of
359+ eExpr <- evalRelationalExpr relExpr
360+ case eExpr of
254361 Left err -> pure (Left err)
255− Right rel -> do
256− tupProc <- extendTupleExpressionProcessor rel tupleExpression
257− case tupProc of
258− Left err -> pure (Left err)
259− Right (newAttrs, tupProc') -> pure $ relMogrify tupProc' newAttrs rel
260−
362+ Right expr -> do
363+ tupProc <- processExtendTupleExpr tupleExpression
364+ pure (Right (Extend tupProc expr))
365+-}
261366--helper function to process relation variable creation/assignment
262−setRelVar :: RelVarName -> Relation -> DatabaseState (Either RelationalError ())
263−setRelVar relVarName rel = do
367+setRelVar :: RelVarName -> GraphRefRelationalExpr -> DatabaseContextEvalMonad ()
368+setRelVar relVarName relExpr = do
264369 currentContext <- getStateContext
265− let newRelVars = M.insert relVarName rel $ relationVariables currentContext
370+ --prevent recursive relvar definition
371+ let newRelVars = M.insert relVarName relExpr $ relationVariables currentContext
266372 potentialContext = currentContext { relationVariables = newRelVars }
267−
268− case checkConstraints potentialContext of
269− Left err -> pure (Left err)
270− Right _ -> do
271− putStateContext potentialContext
272− return (Right ())
373+ --optimization: if the relexpr is unchanged, skip the update
374+ if M.lookup relVarName (relationVariables currentContext) == Just relExpr then
375+ pure ()
376+ else do
377+ --determine when to check constraints
378+ graph <- dbcGraph
379+ tid <- dbcTransId
380+ case checkConstraints potentialContext tid graph of
381+ Left err -> dbErr err
382+ Right _ -> putStateContext potentialContext
273383
274384-- it is not an error to delete a relvar which does not exist, just like it is not an error to insert a pre-existing tuple into a relation
275−deleteRelVar :: RelVarName -> DatabaseState (Either RelationalError ())
385+deleteRelVar :: RelVarName -> DatabaseContextEvalMonad ()
276386deleteRelVar relVarName = do
277387 currContext <- getStateContext
278388 let relVars = relationVariables currContext
279389 if M.notMember relVarName relVars then
280− pure (Right ())
390+ pure ()
281391 else do
282392 let newRelVars = M.delete relVarName relVars
283393 newContext = currContext { relationVariables = newRelVars }
284394 putStateContext newContext
285− pure (Right ())
395+ pure ()
286396
287−evalDatabaseContextExpr :: DatabaseContextExpr -> DatabaseState (Either RelationalError ())
288−evalDatabaseContextExpr NoOperation = pure (Right ())
397+evalGraphRefDatabaseContextExpr :: GraphRefDatabaseContextExpr -> DatabaseContextEvalMonad ()
398+evalGraphRefDatabaseContextExpr NoOperation = pure ()
289399
290−evalDatabaseContextExpr (Define relVarName attrExprs) = do
400+evalGraphRefDatabaseContextExpr (Define relVarName attrExprs) = do
401+ context <- getStateContext
291402 relvars <- fmap relationVariables getStateContext
292403 tConss <- fmap typeConstructorMapping getStateContext
293− let eAttrs = map (evalAttrExpr tConss) attrExprs
294− attrErrs = lefts eAttrs
295− attrsList = rights eAttrs
296− if not (null attrErrs) then
297− pure (Left (someErrors attrErrs))
298− else do
299− let atomTypeErrs = lefts $ map ((`validateAtomType` tConss) . A.atomType) attrsList
300− if not (null atomTypeErrs) then
301− pure (Left (someErrors atomTypeErrs))
302− else
303− case M.member relVarName relvars of
304− True -> pure (Left (RelVarAlreadyDefinedError relVarName))
305− False -> setRelVar relVarName emptyRelation >> pure (Right ())
404+ graph <- dbcGraph
405+ let eAttrs = runGraphRefRelationalExprM gfEnv (mapM evalGraphRefAttrExpr attrExprs)
406+ gfEnv = freshGraphRefRelationalExprEnv (Just context) graph
407+ case eAttrs of
408+ Left err -> dbErr err
409+ Right attrsList -> do
410+ lift $ except $ validateAttributes tConss (A.attributesFromList attrsList)
411+ case M.member relVarName relvars of
412+ True -> dbErr (RelVarAlreadyDefinedError relVarName)
413+ False -> setRelVar relVarName (ExistingRelation emptyRelation)
306414 where
307415 attrs = A.attributesFromList attrsList
308416 emptyRelation = Relation attrs emptyTupleSet
309417
310−evalDatabaseContextExpr (Undefine relVarName) = deleteRelVar relVarName
418+evalGraphRefDatabaseContextExpr (Undefine relVarName) = deleteRelVar relVarName
311419
312−evalDatabaseContextExpr (Assign relVarName expr) = do
313− -- in the future, it would be nice to get types from the RelationalExpr instead of needing to evaluate it
420+evalGraphRefDatabaseContextExpr (Assign relVarName expr) = do
421+ graph <- re_graph <$> dbcRelationalExprEnv
314422 context <- getStateContext
315− let existingRelVar = M.lookup relVarName relVarTable
316− relVarTable = relationVariables context
317− value = runReader (evalRelationalExpr expr) (RelationalExprStateElems context)
318− case value of
319− Left err -> pure (Left err)
320− Right rel -> case existingRelVar of
321− Nothing -> setRelVar relVarName rel
322− Just existingRel -> let expectedAttributes = attributes existingRel
323− foundAttributes = attributes rel in
324− if A.attributesEqual expectedAttributes foundAttributes then
325− setRelVar relVarName rel
326− else
327− pure (Left (RelationTypeMismatchError expectedAttributes foundAttributes))
328−
329−evalDatabaseContextExpr (Insert relVarName relExpr) = do
330− context <- getStateContext
331− let unionexp = Union relExpr rv
332− rv = RelationVariable relVarName ()
333− unioned = runReader (evalRelationalExpr unionexp) (RelationalExprStateElems context)
334− origRel = runReader (evalRelationalExpr rv) (RelationalExprStateElems context)
335− case unioned of
336− Left err -> pure (Left err)
337− Right unioned' -> case origRel of
338− Left err -> pure (Left err)
339− Right origRel' -> if cardinality unioned' == cardinality origRel' then --no tuples actually inserted
340− pure (Right ())
341− else
342− evalDatabaseContextExpr $ Assign relVarName (ExistingRelation unioned')
423+ let existingRelVar = M.lookup relVarName (relationVariables context)
424+ reEnv = freshGraphRefRelationalExprEnv (Just context) graph
425+ eNewExprType = runGraphRefRelationalExprM reEnv (typeForGraphRefRelationalExpr expr)
426+ case existingRelVar of
427+ Nothing -> do
428+ case runGraphRefRelationalExprM reEnv (typeForGraphRefRelationalExpr expr) of
429+ Left err -> dbErr err
430+ Right reltype -> do
431+ lift $ except $ validateAttributes (typeConstructorMapping context) (attributes reltype)
432+ setRelVar relVarName expr
433+ Just existingRel -> do
434+ let eExpectedType = runGraphRefRelationalExprM reEnv (typeForGraphRefRelationalExpr existingRel)
435+ case eExpectedType of
436+ Left err -> dbErr err
437+ Right expectedType ->
438+ case eNewExprType of
439+ Left err -> dbErr err
440+ Right newExprType -> do
441+ if newExprType == expectedType then do
442+ lift $ except $ validateAttributes (typeConstructorMapping context) (attributes newExprType)
443+ setRelVar relVarName expr
444+ else
445+ dbErr (RelationTypeMismatchError (attributes expectedType) (attributes newExprType))
343446
344−evalDatabaseContextExpr (Delete relVarName predicate) = do
345− context <- getStateContext
346− let rv = RelationVariable relVarName ()
347− let updatedRel = runReader (evalRelationalExpr (Restrict (NotPredicate predicate) rv)) (RelationalExprStateElems context)
348− origRel = runReader (evalRelationalExpr rv) (RelationalExprStateElems context)
349− case updatedRel of
350− Left err -> pure (Left err)
351− Right updatedRel' -> case origRel of
352− Left err -> pure (Left err)
353− Right origRel' -> if cardinality origRel' == cardinality updatedRel' then
354− pure (Right ())
355− else
356− setRelVar relVarName updatedRel'
447+evalGraphRefDatabaseContextExpr (Insert relVarName relExpr) = do
448+ gfExpr <- relVarByName relVarName
449+ evalGraphRefDatabaseContextExpr (Assign relVarName
450+ (Union
451+ gfExpr
452+ relExpr))
357453
454+evalGraphRefDatabaseContextExpr (Delete relVarName predicate) = do
455+ gfExpr <- relVarByName relVarName
456+ setRelVar relVarName (Restrict (NotPredicate predicate) gfExpr)
457+
358458--union of restricted+updated portion and the unrestricted+unupdated portion
359−evalDatabaseContextExpr (Update relVarName atomExprMap restrictionPredicateExpr) = do
360− context <- getStateContext
361− let relVarTable = relationVariables context
362− case M.lookup relVarName relVarTable of
363− Nothing -> pure (Left (RelVarNotDefinedError relVarName))
364− Just rel ->
365− case runReader (predicateRestrictionFilter (attributes rel) restrictionPredicateExpr) (RelationalExprStateElems context) of
366− Left err -> pure (Left err)
367− Right predicateFunc -> do
368− let ret = do
369− restrictedPortion <- restrict predicateFunc rel
370− if cardinality restrictedPortion == Finite 0 then
371− pure Nothing
372− else do
373− unrestrictedPortion <- restrict (predicateFunc >=> (pure . not)) rel
374− updatedPortion <- relMap (updateTupleWithAtomExprs atomExprMap context) restrictedPortion
375− updatedRel <- updatedPortion `union` unrestrictedPortion
376− pure (Just updatedRel)
377− case ret of
378− Left err -> pure (Left err)
379− Right Nothing -> pure (Right ())
380− Right (Just updatedRel) -> setRelVar relVarName updatedRel
459+evalGraphRefDatabaseContextExpr (Update relVarName atomExprMap pred') = do
460+ rvExpr <- relVarByName relVarName
461+ let unrestrictedPortion = Restrict (NotPredicate pred') rvExpr
462+ tmpAttr attr = "_tmp_" <> attr --this could certainly be improved to verify that there is no attribute name conflict
463+ updateAttr nam atomExpr = Extend (AttributeExtendTupleExpr (tmpAttr nam) atomExpr)
464+ projectAndRename attr expr = Rename (tmpAttr attr) attr (Project (InvertedAttributeNames (S.singleton attr)) expr)
465+ restrictedPortion = Restrict pred' rvExpr
466+ updated = foldr (\(oldname, atomExpr) accum ->
467+ let procAtomExpr = runProcessExprM UncommittedContextMarker (processAtomExpr atomExpr) in
468+ updateAttr oldname procAtomExpr accum
469+ ) restrictedPortion (M.toList atomExprMap)
470+ -- the atomExprMap could reference other attributes, so we must perform multi-pass folds
471+ updatedPortion = foldr projectAndRename updated (M.keys atomExprMap)
472+ setRelVar relVarName (Union unrestrictedPortion updatedPortion)
381473
382−evalDatabaseContextExpr (AddInclusionDependency newDepName newDep) = do
474+evalGraphRefDatabaseContextExpr (AddInclusionDependency newDepName newDep) = do
383475 currContext <- getStateContext
476+ transId <- dbcTransId
477+ graph <- dbcGraph
384478 let currDeps = inclusionDependencies currContext
385479 newDeps = M.insert newDepName newDep currDeps
386480 if M.member newDepName currDeps then
387− pure (Left (InclusionDependencyNameInUseError newDepName))
481+ dbErr (InclusionDependencyNameInUseError newDepName)
388482 else do
389483 let potentialContext = currContext { inclusionDependencies = newDeps }
390− case checkConstraints potentialContext of
391− Left err -> pure (Left err)
392− Right _ -> do
484+ -- if the potential context passes all constraints, then save it
485+ -- potential optimization: validate only the new constraint- all old constraints must already hold
486+ case checkConstraints potentialContext transId graph of
487+ Left err -> dbErr err
488+ Right _ ->
393489 putStateContext potentialContext
394− return (Right ())
395490
396−evalDatabaseContextExpr (RemoveInclusionDependency depName) = do
491+evalGraphRefDatabaseContextExpr (RemoveInclusionDependency depName) = do
397492 currContext <- getStateContext
398493 let currDeps = inclusionDependencies currContext
399494 newDeps = M.delete depName currDeps
400495 if M.notMember depName currDeps then
401− pure (Left (InclusionDependencyNameNotInUseError depName))
402− else do
496+ dbErr (InclusionDependencyNameNotInUseError depName)
497+ else
403498 putStateContext $ currContext {inclusionDependencies = newDeps }
404− return (Right ())
405499
406500-- | Add a notification which will send the resultExpr when triggerExpr changes between commits.
407−evalDatabaseContextExpr (AddNotification notName triggerExpr resultOldExpr resultNewExpr) = do
501+evalGraphRefDatabaseContextExpr (AddNotification notName triggerExpr resultOldExpr resultNewExpr) = do
408502 currentContext <- getStateContext
409503 let nots = notifications currentContext
410504 if M.member notName nots then
411− pure (Left (NotificationNameInUseError notName))
505+ dbErr (NotificationNameInUseError notName)
412506 else do
413507 let newNotifications = M.insert notName newNotification nots
414508 newNotification = Notification { changeExpr = triggerExpr,
415509 reportOldExpr = resultOldExpr,
416510 reportNewExpr = resultNewExpr}
417511 putStateContext $ currentContext { notifications = newNotifications }
418− return (Right ())
419512
420−evalDatabaseContextExpr (RemoveNotification notName) = do
513+evalGraphRefDatabaseContextExpr (RemoveNotification notName) = do
421514 currentContext <- getStateContext
422515 let nots = notifications currentContext
423516 if M.notMember notName nots then
424− pure (Left (NotificationNameNotInUseError notName))
517+ dbErr (NotificationNameNotInUseError notName)
425518 else do
426519 let newNotifications = M.delete notName nots
427520 putStateContext $ currentContext { notifications = newNotifications }
428− pure (Right ())
521+
429522
430523-- | Adds type and data constructors to the database context.
431524-- validate that the type *and* constructor names are unique! not yet implemented!
432−evalDatabaseContextExpr (AddTypeConstructor tConsDef dConsDefList) = do
525+evalGraphRefDatabaseContextExpr (AddTypeConstructor tConsDef dConsDefList) = do
433526 currentContext <- getStateContext
434527 let oldTypes = typeConstructorMapping currentContext
435528 tConsName = TCD.name tConsDef
436529 -- validate that the constructor's types exist
437530 case validateTypeConstructorDef tConsDef dConsDefList oldTypes of
438− Left err -> pure (Left err)
439− Right _ | T.null tConsName || not (isUpper (T.head tConsName)) -> pure (Left (InvalidAtomTypeName tConsName))
440− | isJust (findTypeConstructor tConsName oldTypes) -> pure (Left (AtomTypeNameInUseError tConsName))
441− | otherwise -> do
442− let newTypes = oldTypes ++ [(tConsDef, dConsDefList)]
443− putStateContext $ currentContext { typeConstructorMapping = newTypes }
444− pure (Right ())
531+ Left err -> throwError err
532+ Right () | T.null tConsName || not (isUpper (T.head tConsName)) -> dbErr (InvalidAtomTypeName tConsName)
533+ | isJust (findTypeConstructor tConsName oldTypes) -> dbErr (AtomTypeNameInUseError tConsName)
534+ | otherwise -> do
535+ let newTypes = oldTypes ++ [(tConsDef, dConsDefList)]
536+ putStateContext $ currentContext { typeConstructorMapping = newTypes }
445537
446538-- | Removing the atom constructor prevents new atoms of the type from being created. Existing atoms of the type remain. Thus, the atomTypes list in the DatabaseContext need not be all-inclusive.
447−evalDatabaseContextExpr (RemoveTypeConstructor tConsName) = do
539+evalGraphRefDatabaseContextExpr (RemoveTypeConstructor tConsName) = do
448540 currentContext <- getStateContext
449541 let oldTypes = typeConstructorMapping currentContext
450542 if isNothing (findTypeConstructor tConsName oldTypes) then
451− pure (Left (AtomTypeNameNotInUseError tConsName))
543+ dbErr (AtomTypeNameNotInUseError tConsName)
452544 else do
453545 let newTypes = filter (\(tCons, _) -> TCD.name tCons /= tConsName) oldTypes
454546 putStateContext $ currentContext { typeConstructorMapping = newTypes }
455− pure (Right ())
456547
457−evalDatabaseContextExpr (MultipleExpr exprs) = do
548+evalGraphRefDatabaseContextExpr (MultipleExpr exprs) =
458549 --the multiple expressions must pass the same context around- not the old unmodified context
459− evald <- forM exprs evalDatabaseContextExpr
460− --some lifting magic needed here
461− case lefts evald of
462− [] -> pure (Right ())
463− errs -> pure (Left (someErrors errs))
464−
465−evalDatabaseContextExpr (RemoveAtomFunction funcName) = do
550+ mapM_ evalGraphRefDatabaseContextExpr exprs
551+
552+evalGraphRefDatabaseContextExpr (RemoveAtomFunction funcName) = do
466553 currentContext <- getStateContext
467554 let atomFuncs = atomFunctions currentContext
468555 case atomFunctionForName funcName atomFuncs of
469− Left err -> pure (Left err)
470− Right realFunc -> if isScriptedAtomFunction realFunc then do
471− let updatedFuncs = HS.delete realFunc atomFuncs
472− putStateContext (currentContext {atomFunctions = updatedFuncs })
473− pure (Right ())
474− else
475− pure (Left (PrecompiledFunctionRemoveError funcName))
556+ Left err -> dbErr err
557+ Right realFunc ->
558+ if isScriptedAtomFunction realFunc then do
559+ let updatedFuncs = HS.delete realFunc atomFuncs
560+ putStateContext (currentContext {atomFunctions = updatedFuncs })
561+ else
562+ dbErr (PrecompiledFunctionRemoveError funcName)
476563
477−evalDatabaseContextExpr (RemoveDatabaseContextFunction funcName) = do
564+evalGraphRefDatabaseContextExpr (RemoveDatabaseContextFunction funcName) = do
478565 context <- getStateContext
479566 let dbcFuncs = dbcFunctions context
480567 case databaseContextFunctionForName funcName dbcFuncs of
481− Left err -> pure (Left err)
482− Right realFunc -> if isScriptedDatabaseContextFunction realFunc then do
483− let updatedFuncs = HS.delete realFunc dbcFuncs
484− putStateContext (context { dbcFunctions = updatedFuncs })
485− pure (Right ())
486− else
487− pure (Left (PrecompiledFunctionRemoveError funcName))
568+ Left err -> dbErr err
569+ Right realFunc ->
570+ if isScriptedDatabaseContextFunction realFunc then do
571+ let updatedFuncs = HS.delete realFunc dbcFuncs
572+ putStateContext (context { dbcFunctions = updatedFuncs })
573+ else
574+ dbErr (PrecompiledFunctionRemoveError funcName)
488575
489−evalDatabaseContextExpr (ExecuteDatabaseContextFunction funcName atomArgExprs) = do
576+evalGraphRefDatabaseContextExpr (ExecuteDatabaseContextFunction funcName atomArgExprs) = do
490577 context <- getStateContext
578+ graph <- dbcGraph
491579 --resolve atom arguments
492− let relExprState = mkRelationalExprState context
493− eAtomTypes = map (\atomExpr -> runReader (typeFromAtomExpr emptyAttributes atomExpr) relExprState) atomArgExprs
580+ let eAtomTypes = mapM (runGraphRefRelationalExprM gfEnv . typeForGraphRefAtomExpr emptyAttributes) atomArgExprs
494581 eFunc = databaseContextFunctionForName funcName (dbcFunctions context)
582+ gfEnv = freshGraphRefRelationalExprEnv (Just context) graph
495583 case eFunc of
496− Left err -> pure (Left err)
584+ Left err -> dbErr err
497585 Right func -> do
498586 let expectedArgCount = length (dbcFuncType func)
499587 actualArgCount = length atomArgExprs
500588 if expectedArgCount /= actualArgCount then
501− pure (Left (FunctionArgumentCountMismatchError expectedArgCount actualArgCount))
589+ dbErr (FunctionArgumentCountMismatchError expectedArgCount actualArgCount)
502590 else
503591 --check that the atom types are valid
504− case lefts eAtomTypes of
505− _:_ -> pure (Left (someErrors (lefts eAtomTypes)))
506− [] -> do
507− let atomTypes = rights eAtomTypes
508− let mValidTypes = zipWith
509− (\expType actType
510− -> case atomTypeVerify expType actType of
511− Left err -> Just err
512− Right _ -> Nothing)
513− (dbcFuncType func)
514− atomTypes
515− typeErrors = catMaybes mValidTypes
516− eAtomArgs = map (\arg -> runReader (evalAtomExpr emptyTuple arg) relExprState) atomArgExprs
592+ case eAtomTypes of
593+ Left err -> dbErr err
594+ Right atomTypes -> do
595+ let mValidTypes = zipWith (\ expType actType
596+ -> case atomTypeVerify expType actType of
597+ Left err -> Just err
598+ Right _ -> Nothing)
599+ (dbcFuncType func) atomTypes
600+ typeErrors = catMaybes mValidTypes
601+ eAtomArgs = map (runGraphRefRelationalExprM gfEnv . evalGraphRefAtomExpr emptyTuple) atomArgExprs
517602 if length (lefts eAtomArgs) > 1 then
518− pure (Left (someErrors (lefts eAtomArgs)))
603+ dbErr (someErrors (lefts eAtomArgs))
519604 else if not (null typeErrors) then
520− pure (Left (someErrors typeErrors))
605+ dbErr (someErrors typeErrors)
521606 else
522607 case evalDatabaseContextFunction func (rights eAtomArgs) context of
523− Left err -> pure (Left err)
524− Right newContext -> putStateContext newContext >> pure (Right ())
525−
526−evalDatabaseContextIOExpr :: Maybe ScriptSession -> DatabaseContext -> DatabaseContextIOExpr -> IO (Either RelationalError DatabaseContext)
527−#if !defined(PM36_HASKELL_SCRIPTING)
528−evalDatabaseContextIOExpr _ _ AddAtomFunction{} = pure (Left (ScriptError ScriptCompilationDisabledError))
529−evalDatabaseContextIOExpr _ _ AddDatabaseContextFunction{} = pure (Left (ScriptError ScriptCompilationDisabledError))
530−evalDatabaseContextIOExpr _ _ LoadAtomFunctions{} = pure (Left (ScriptError ScriptCompilationDisabledError))
531−evalDatabaseContextIOExpr _ _ LoadDatabaseContextFunctions{} = pure (Left (ScriptError ScriptCompilationDisabledError))
608+ Left err -> dbErr err
609+ Right newContext -> putStateContext newContext
610+data DatabaseContextIOEvalEnv = DatabaseContextIOEvalEnv
611+ { dbcio_transId :: TransactionId,
612+ dbcio_graph :: TransactionGraph,
613+ dbcio_mScriptSession :: Maybe ScriptSession
614+ }
615+
616+type DatabaseContextIOEvalMonad a = RWST DatabaseContextIOEvalEnv () DatabaseContextEvalState IO a
617+
618+runDatabaseContextIOEvalMonad :: DatabaseContextIOEvalEnv -> DatabaseContext -> DatabaseContextIOEvalMonad (Either RelationalError ()) -> IO (Either RelationalError DatabaseContextEvalState)
619+runDatabaseContextIOEvalMonad env ctx m = do
620+ res <- runRWST m env freshState
621+ case res of
622+ (Left err,_,_) -> pure (Left err)
623+ (Right (),s,_) -> pure (Right s)
624+ where
625+ freshState = mkDatabaseContextEvalState ctx
626+
627+requireScriptSession :: DatabaseContextIOEvalMonad (Either RelationalError ScriptSession)
628+requireScriptSession = do
629+ env <- RWS.ask
630+ case dbcio_mScriptSession env of
631+ Nothing -> pure $ Left $ ScriptError ScriptCompilationDisabledError
632+ Just ss -> pure (Right ss)
633+
634+putDBCIOContext :: DatabaseContext -> DatabaseContextIOEvalMonad (Either RelationalError ())
635+putDBCIOContext ctx = do
636+ RWS.modify (\dbstate -> dbstate { dbc_context = ctx})
637+ pure (Right ())
638+
639+getDBCIOContext :: DatabaseContextIOEvalMonad DatabaseContext
640+getDBCIOContext = dbc_context <$> RWS.get
641+
642+getDBCIORelationalExprEnv :: DatabaseContextIOEvalMonad RelationalExprEnv
643+getDBCIORelationalExprEnv = do
644+ context <- getDBCIOContext
645+ mkRelationalExprEnv context . dbcio_graph <$> RWS.ask
646+
647+evalGraphRefDatabaseContextIOExpr :: GraphRefDatabaseContextIOExpr -> DatabaseContextIOEvalMonad (Either RelationalError ())
648+#if !defined(PM36_HASKELL_SCRIPTING)
649+evalGraphRefDatabaseContextIOExpr AddAtomFunction{} = pure (Left (ScriptError ScriptCompilationDisabledError))
650+evalGraphRefDatabaseContextIOExpr AddDatabaseContextFunction{} = pure (Left (ScriptError ScriptCompilationDisabledError))
651+evalGraphRefDatabaseContextIOExpr LoadAtomFunctions{} = pure (Left (ScriptError ScriptCompilationDisabledError))
652+evalGraphRefDatabaseContextIOExpr LoadDatabaseContextFunctions{} = pure (Left (ScriptError ScriptCompilationDisabledError))
532653#else
533−evalDatabaseContextIOExpr mScriptSession currentContext (AddAtomFunction funcName funcType script) =
534− case mScriptSession of
535− Nothing -> pure (Left (ScriptError ScriptCompilationDisabledError))
536− Just scriptSession -> do
537− res <- try $ runGhc (Just libdir) $ do
654+evalGraphRefDatabaseContextIOExpr (AddAtomFunction funcName funcType script) = do
655+ eScriptSession <- requireScriptSession
656+ currentContext <- getDBCIOContext
657+ case eScriptSession of
658+ Left err -> pure (Left err)
659+ Right scriptSession -> do
660+ res <- liftIO $ try $ runGhc (Just libdir) $ do
538661 setSession (hscEnv scriptSession)
539662 let atomFuncs = atomFunctions currentContext
540663 case extractAtomFunctionType funcType of
@@ -560,12 +683,13 @@ evalDatabaseContextIOExpr mScriptSession currentContext (AddAtomFunction funcNam
560683 Left (exc :: SomeException) -> pure $ Left (ScriptError (OtherScriptCompilationError (show exc)))
561684 Right eContext -> case eContext of
562685 Left err -> pure (Left err)
563− Right context' -> pure (Right context')
564−
565−evalDatabaseContextIOExpr mScriptSession currentContext (AddDatabaseContextFunction funcName funcType script) =
566− case mScriptSession of
567− Nothing -> pure (Left (ScriptError ScriptCompilationDisabledError))
568− Just scriptSession -> do
686+ Right context' -> putDBCIOContext context'
687+evalGraphRefDatabaseContextIOExpr (AddDatabaseContextFunction funcName funcType script) = do
688+ eScriptSession <- requireScriptSession
689+ currentContext <- getDBCIOContext
690+ case eScriptSession of
691+ Left err -> pure (Left err)
692+ Right scriptSession -> do
569693 --validate that the function signature is of the form x -> y -> ... -> DatabaseContext -> DatabaseContext
570694 let last2Args = reverse (take 2 (reverse funcType))
571695 atomArgs = take (length funcType - 2) funcType
@@ -575,7 +699,7 @@ evalDatabaseContextIOExpr mScriptSession currentContext (AddDatabaseContextFunct
575699 if last2Args /= [ADTypeConstructor "DatabaseContext" [], dbContextTypeCons] then
576700 pure (Left (ScriptError (TypeCheckCompilationError expectedType actualType)))
577701 else do
578− res <- try $ runGhc (Just libdir) $ do
702+ res <- liftIO $ try $ runGhc (Just libdir) $ do
579703 setSession (hscEnv scriptSession)
580704 eCompiledFunc <- compileScript (dbcFunctionBodyType scriptSession) script
581705 pure $ case eCompiledFunc of
@@ -600,68 +724,93 @@ evalDatabaseContextIOExpr mScriptSession currentContext (AddDatabaseContextFunct
600724 Left (exc :: SomeException) -> pure $ Left (ScriptError (OtherScriptCompilationError (show exc)))
601725 Right eContext -> case eContext of
602726 Left err -> pure (Left err)
603− Right context' -> pure (Right context')
604−evalDatabaseContextIOExpr _ currentContext (LoadAtomFunctions modName funcName modPath) = do
605− eLoadFunc <- loadAtomFunctions (T.unpack modName) (T.unpack funcName) modPath
727+ Right context' -> putDBCIOContext context'
728+evalGraphRefDatabaseContextIOExpr (LoadAtomFunctions modName funcName modPath) = do
729+ currentContext <- getDBCIOContext
730+ eLoadFunc <- liftIO $ loadAtomFunctions (T.unpack modName) (T.unpack funcName) modPath
606731 case eLoadFunc of
607732 Left LoadSymbolError -> pure (Left LoadFunctionError)
608733 Right atomFunctionListFunc -> let newContext = currentContext { atomFunctions = mergedFuncs }
609734 mergedFuncs = HS.union (atomFunctions currentContext) (HS.fromList atomFunctionListFunc)
610− in pure (Right newContext)
611−evalDatabaseContextIOExpr _ currentContext (LoadDatabaseContextFunctions modName funcName modPath) = do
612− eLoadFunc <- loadDatabaseContextFunctions (T.unpack modName) (T.unpack funcName) modPath
735+ in putDBCIOContext newContext
736+evalGraphRefDatabaseContextIOExpr (LoadDatabaseContextFunctions modName funcName modPath) = do
737+ currentContext <- getDBCIOContext
738+ eLoadFunc <- liftIO $ loadDatabaseContextFunctions (T.unpack modName) (T.unpack funcName) modPath
613739 case eLoadFunc of
614740 Left LoadSymbolError -> pure (Left LoadFunctionError)
615741 Right dbcListFunc -> let newContext = currentContext { dbcFunctions = mergedFuncs }
616742 mergedFuncs = HS.union (dbcFunctions currentContext) (HS.fromList dbcListFunc)
617− in pure (Right newContext)
618−#endif
619−
620−evalDatabaseContextIOExpr _ currentContext (CreateArbitraryRelation relVarName attrExprs range) =
743+ in putDBCIOContext newContext
744+evalGraphRefDatabaseContextIOExpr (CreateArbitraryRelation relVarName attrExprs range) = do
621745 --Define
622− case runState ( evalDatabaseContextExpr (Define relVarName attrExprs)) (freshDatabaseState currentContext) of
623− (Left err,_) -> pure (Left err)
624− (Right (), elems@(ctx,_,_)) -> do
746+ currentContext <- getDBCIOContext
747+ env <- RWS.ask
748+ --create graph ref expr
749+ let gfExpr = Define relVarName attrExprs
750+ evalEnv = mkDatabaseContextEvalEnv (dbcio_transId env) (dbcio_graph env)
751+ graph = dbcio_graph env
752+ case runDatabaseContextEvalMonad currentContext evalEnv (evalGraphRefDatabaseContextExpr gfExpr) of
753+ Left err -> pure (Left err)
754+ Right dbstate -> do
625755 --Assign
626756 let existingRelVar = M.lookup relVarName relVarTable
627− relVarTable = relationVariables ctx
757+ relVarTable = relationVariables (dbc_context dbstate)
628758 case existingRelVar of
629759 Nothing -> pure $ Left (RelVarNotDefinedError relVarName)
630760 Just existingRel -> do
631− let expectedAttributes = attributes existingRel
632− let tcMap = typeConstructorMapping ctx
633− eitherRel <- generate $ runReaderT (arbitraryRelation expectedAttributes range) tcMap
634− case eitherRel of
635− Left err -> pure $ Left err
636− Right rel ->
637− case runState (setRelVar relVarName rel) elems of
638− (Left err,_) -> pure (Left err)
639− (Right (), (ctx',_,_)) -> pure $ Right ctx'
640−
641−updateTupleWithAtomExprs :: M.Map AttributeName AtomExpr -> DatabaseContext -> RelationTuple -> Either RelationalError RelationTuple
642−updateTupleWithAtomExprs exprMap context tupIn = do
761+ let gfEnv = freshGraphRefRelationalExprEnv (Just currentContext) graph
762+ case runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr existingRel) of
763+ Left err -> pure (Left err)
764+ Right relType -> do
765+ let expectedAttributes = attributes relType
766+ tcMap = typeConstructorMapping (dbc_context dbstate)
767+ eitherRel <- liftIO $ generate $ runReaderT (arbitraryRelation expectedAttributes range) tcMap
768+ case eitherRel of
769+ Left err -> pure $ Left err
770+ Right rel ->
771+ case runDatabaseContextEvalMonad currentContext evalEnv (setRelVar relVarName (ExistingRelation rel)) of
772+ Left err -> pure (Left err)
773+ Right dbstate' -> putDBCIOContext (dbc_context dbstate')
774+#endif
775+{-
776+updateTupleWithAtomExprs :: M.Map AttributeName AtomExpr -> DatabaseContext -> TransactionGraph -> RelationTuple -> Either RelationalError RelationTuple
777+updateTupleWithAtomExprs exprMap context graph tupIn = do
643778 --resolve all atom exprs
779+ let
644780 atomsAssoc <- mapM (\(attrName, atomExpr) -> do
645− atom <- runReader (evalAtomExpr tupIn atomExpr) (RelationalExprStateElems context)
781+ let atom = unimplemented
782+ --atom <- runReader (evalAtomExpr tupIn atomExpr) (mkRelationalExprState context graph)
646783 pure (attrName, atom)
647784 ) (M.toList exprMap)
648785 pure (updateTupleWithAtoms (M.fromList atomsAssoc) tupIn)
649−
786+-}
650787--run verification on all constraints
651−checkConstraints :: DatabaseContext -> Either RelationalError ()
652−checkConstraints context =
788+checkConstraints :: DatabaseContext -> TransactionId -> TransactionGraph -> Either RelationalError ()
789+checkConstraints context transId graph@(TransactionGraph graphHeads transSet) =
653790 mapM_ (uncurry checkIncDep) (M.toList deps)
654791 where
792+ potentialGraph = TransactionGraph graphHeads (S.insert tempTrans transSet)
793+ tempStamp = UTCTime { utctDay = fromGregorian 2000 1 1,
794+ utctDayTime = secondsToDiffTime 0 }
795+ tempSchemas = Schemas context M.empty
796+ tempTrans = Transaction U.nil (TransactionInfo (transId NE.:| []) tempStamp) tempSchemas
797+
655798 deps = inclusionDependencies context
656− eval expr = evalReader (evalRelationalExpr expr)
657− evalReader f = runReader f (RelationalExprStateElems context)
799+ -- no optimization available here, really? perhaps the optimizer should be passed down to here or the eval function should be passed through the environment
658800 checkIncDep depName (InclusionDependency subsetExpr supersetExpr) = do
801+ let process = runProcessExprM UncommittedContextMarker
802+ gfSubsetExpr = process (processRelationalExpr subsetExpr)
803+ gfSupersetExpr = process (processRelationalExpr supersetExpr)
659804 --if both expressions are of a single-attribute (such as with a simple foreign key), the names of the attributes are irrelevant (they need not match) because the expression is unambiguous, but special-casing this to rename the attribute automatically would not be orthogonal behavior and probably cause confusion. Instead, special case the error to make it clear.
660− typeSub <- evalReader (typeForRelationalExpr subsetExpr)
661− typeSuper <- evalReader (typeForRelationalExpr supersetExpr)
805+ let gfEnv = freshGraphRefRelationalExprEnv (Just context) graph
806+ runGfRel = runGraphRefRelationalExprM gfEnv
807+ typeSub <- runGfRel (typeForGraphRefRelationalExpr gfSubsetExpr)
808+ typeSuper <- runGfRel (typeForGraphRefRelationalExpr gfSupersetExpr)
662809 when (typeSub /= typeSuper) (Left (RelationTypeMismatchError (attributes typeSub) (attributes typeSuper)))
663− let checkExpr = Equals supersetExpr (Union subsetExpr supersetExpr)
664− case eval checkExpr of
810+ let checkExpr = Equals gfSupersetExpr (Union gfSubsetExpr gfSupersetExpr)
811+ gfEvald = runGraphRefRelationalExprM gfEnv' (evalGraphRefRelationalExpr checkExpr)
812+ gfEnv' = freshGraphRefRelationalExprEnv (Just context) potentialGraph
813+ case gfEvald of
665814 Left err -> Left err
666815 Right resultRel -> if resultRel == relationTrue then
667816 pure ()
@@ -671,91 +820,90 @@ checkConstraints context =
671820-- the type of a relational expression is equal to the relation attribute set returned from executing the relational expression; therefore, the type can be cheaply derived by evaluating a relational expression and ignoring and tuple processing
672821-- furthermore, the type of a relational expression is the resultant header of the evaluated empty-tupled relation
673822
674−typeForRelationalExpr :: RelationalExpr -> RelationalExprState (Either RelationalError Relation)
823+typeForRelationalExpr :: RelationalExpr -> RelationalExprM Relation
675824typeForRelationalExpr expr = do
676− rstate <- ask
677− let context = stateElemsContext rstate
678825 --replace the relationVariables context element with a cloned set of relation devoid of tuples
679− let context' = contextWithEmptyTupleSets context
680− rstate' = setStateElemsContext rstate context'
681826 --evalRelationalExpr could still return an existing relation with tuples, so strip them
682− pure $ case runReader (evalRelationalExpr expr) rstate' of
683− Left err -> Left err
684− Right typeRel -> Right (relationWithEmptyTupleSet typeRel)
827+ graph <- reGraph
828+ context <- reContext
829+ let gfExpr = runProcessExprM UncommittedContextMarker (processRelationalExpr expr)
830+ gfEnv = freshGraphRefRelationalExprEnv (Just context) graph
831+ runGf = runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr gfExpr)
832+ lift $ except runGf
685833
686834--returns a database context with all tuples removed
687835--this is useful for type checking and optimization
836+{-
688837contextWithEmptyTupleSets :: DatabaseContext -> DatabaseContext
689838contextWithEmptyTupleSets contextIn = contextIn { relationVariables = relVars }
690839 where
691− relVars = M.map (\rel -> Relation (attributes rel) emptyTupleSet) (relationVariables contextIn)
840+ relVars = M.map (\rel -> ExistingRelation (Relation (attributes rel) emptyTupleSet)) (relationVariables contextIn)
841+-}
692842
693843liftE :: (Monad m) => m (Either a b) -> ExceptT a m b
694844liftE v = do
695845 y <- lift v
696846 case y of
697− Left err -> throwE err
847+ Left err -> throwError err
698848 Right val -> pure val
699849
700850{- used for restrictions- take the restrictionpredicate and return the corresponding filter function -}
701−predicateRestrictionFilter :: Attributes -> RestrictionPredicateExpr -> RelationalExprState (Either RelationalError RestrictionFilter)
702−predicateRestrictionFilter attrs (AndPredicate expr1 expr2) =
703− runExceptT $ do
704− expr1v <- liftE (predicateRestrictionFilter attrs expr1)
705− expr2v <- liftE (predicateRestrictionFilter attrs expr2)
706− pure (\x -> do
707− ev1 <- expr1v x
708− ev2 <- expr2v x
709− pure (ev1 && ev2))
851+predicateRestrictionFilter :: Attributes -> GraphRefRestrictionPredicateExpr -> GraphRefRelationalExprM RestrictionFilter
852+predicateRestrictionFilter attrs (AndPredicate expr1 expr2) = do
853+ expr1v <- predicateRestrictionFilter attrs expr1
854+ expr2v <- predicateRestrictionFilter attrs expr2
855+ pure (\x -> do
856+ ev1 <- expr1v x
857+ ev2 <- expr2v x
858+ pure (ev1 && ev2))
710859
711−predicateRestrictionFilter attrs (OrPredicate expr1 expr2) =
712− runExceptT $ do
713− expr1v <- liftE (predicateRestrictionFilter attrs expr1)
714− expr2v <- liftE (predicateRestrictionFilter attrs expr2)
860+predicateRestrictionFilter attrs (OrPredicate expr1 expr2) = do
861+ expr1v <- predicateRestrictionFilter attrs expr1
862+ expr2v <- predicateRestrictionFilter attrs expr2
715863 pure (\x -> do
716864 ev1 <- expr1v x
717865 ev2 <- expr2v x
718866 pure (ev1 || ev2))
719867
720−predicateRestrictionFilter _ TruePredicate = pure (Right (\_ -> pure True))
868+predicateRestrictionFilter _ TruePredicate = pure (\_ -> pure True)
721869
722−predicateRestrictionFilter attrs (NotPredicate expr) =
723− runExceptT $ do
724− exprv <- liftE (predicateRestrictionFilter attrs expr)
725− pure (fmap not . exprv)
870+predicateRestrictionFilter attrs (NotPredicate expr) = do
871+ exprv <- predicateRestrictionFilter attrs expr
872+ pure (fmap not . exprv)
726873
727874--optimization opportunity: if the subexpression does not reference attributes in the top-level expression, then it need only be evaluated once, statically, outside the tuple filter- see historical implementation here
728875predicateRestrictionFilter _ (RelationalExprPredicate relExpr) = do
729− rstate <- ask
730− pure (Right (\tup -> case runReader (evalRelationalExpr relExpr) (mergeTuplesIntoRelationalExprState tup rstate) of
876+ renv <- askEnv
877+ let eval :: RelationTuple -> Either RelationalError Relation
878+ eval tup =
879+ let gfEnv = mergeTuplesIntoGraphRefRelationalExprEnv tup renv in
880+ runGraphRefRelationalExprM gfEnv (evalGraphRefRelationalExpr relExpr)
881+ pure (\tup -> case eval tup of
731882 Left err -> Left err
732883 Right rel -> if arity rel /= 0 then
733884 Left (PredicateExpressionError "Relational restriction filter must evaluate to 'true' or 'false'")
734885 else
735− pure (rel == relationTrue)))
886+ pure (rel == relationTrue))
736887
737888predicateRestrictionFilter attrs (AttributeEqualityPredicate attrName atomExpr) = do
738− rstate <- ask
739− let (attrs', ctxtup') = case rstate of
740− RelationalExprStateElems _ -> (attrs, emptyTuple)
741− RelationalExprStateAttrsElems _ _ -> (attrs, emptyTuple)
742− RelationalExprStateTupleElems _ ctxtup -> (A.union attrs (tupleAttributes ctxtup), ctxtup)
743− runExceptT $ do
744− atomExprType <- liftE (typeFromAtomExpr attrs' atomExpr)
745− attr <- either throwE pure $ case A.attributeForName attrName attrs of
889+ env <- askEnv
890+ let attrs' = A.union attrs (envAttributes env)
891+ ctxtup' = envTuple env
892+ atomExprType <- typeForGraphRefAtomExpr attrs' atomExpr
893+ attr <- lift $ except $ case A.attributeForName attrName attrs of
746894 Right attr -> Right attr
747895 Left (NoSuchAttributeNamesError _) -> case A.attributeForName attrName (tupleAttributes ctxtup') of
748896 Right ctxattr -> Right ctxattr
749897 Left err2@(NoSuchAttributeNamesError _) -> Left err2
750898 Left err -> Left err
751899 Left err -> Left err
752− if atomExprType /= A.atomType attr then
753− throwE (TupleAttributeTypeMismatchError (A.attributesFromList [attr]))
754− else
900+ if atomExprType /= A.atomType attr then
901+ throwError (TupleAttributeTypeMismatchError (A.attributesFromList [attr]))
902+ else
755903 pure $ \tupleIn -> let evalAndCmp atomIn = case atomEvald of
756904 Right atomCmp -> atomCmp == atomIn
757905 Left _ -> False
758− atomEvald = runReader (evalAtomExpr tupleIn atomExpr) rstate
906+ atomEvald = runGraphRefRelationalExprM env (evalGraphRefAtomExpr tupleIn atomExpr)
759907 in
760908 pure $ case atomForAttributeName attrName tupleIn of
761909 Left (NoSuchAttributeNamesError _) -> case atomForAttributeName attrName ctxtup' of
@@ -766,16 +914,15 @@ predicateRestrictionFilter attrs (AttributeEqualityPredicate attrName atomExpr)
766914-- in the future, it would be useful to do typechecking on the attribute and atom expr filters in advance
767915predicateRestrictionFilter attrs (AtomExprPredicate atomExpr) = do
768916 --merge attrs into the state attributes
769− rstate <- ask
770− runExceptT $ do
771− aType <- liftE (typeFromAtomExpr attrs atomExpr)
772− if aType /= BoolAtomType then
773− throwE (AtomTypeMismatchError aType BoolAtomType)
774− else
917+ renv <- askEnv
918+ aType <- typeForGraphRefAtomExpr attrs atomExpr
919+ if aType /= BoolAtomType then
920+ throwError (AtomTypeMismatchError aType BoolAtomType)
921+ else
775922 pure (\tupleIn ->
776− case runReader (evalAtomExpr tupleIn atomExpr) rstate of
777− Left err -> Left err
778− Right boolAtomValue -> pure (boolAtomValue == BoolAtom True))
923+ case runGraphRefRelationalExprM renv (evalGraphRefAtomExpr tupleIn atomExpr) of
924+ Left err -> Left err
925+ Right boolAtomValue -> pure (boolAtomValue == BoolAtom True))
779926
780927tupleExprCheckNewAttrName :: AttributeName -> Relation -> Either RelationalError Relation
781928tupleExprCheckNewAttrName attrName rel = if isRight (attributeForName attrName rel) then
@@ -783,31 +930,83 @@ tupleExprCheckNewAttrName attrName rel = if isRight (attributeForName attrName r
783930 else
784931 Right rel
785932
786−extendTupleExpressionProcessor :: Relation -> ExtendTupleExpr -> RelationalExprState (Either RelationalError (Attributes, RelationTuple -> Either RelationalError RelationTuple))
787−extendTupleExpressionProcessor relIn (AttributeExtendTupleExpr newAttrName atomExpr) = do
788− rstate <- ask
933+extendGraphRefTupleExpressionProcessor :: Relation -> GraphRefExtendTupleExpr -> GraphRefRelationalExprM (Attributes, RelationTuple -> Either RelationalError RelationTuple)
934+extendGraphRefTupleExpressionProcessor relIn (AttributeExtendTupleExpr newAttrName atomExpr) =
935+-- renv <- askEnv
789936 -- check that the attribute name is not in use
790937 case tupleExprCheckNewAttrName newAttrName relIn of
791− Left err -> pure (Left err)
792− Right _ -> runExceptT $ do
793− atomExprType <- liftE (typeFromAtomExpr (attributes relIn) atomExpr)
794− atomExprType' <- liftE (verifyAtomExprTypes relIn atomExpr atomExprType)
938+ Left err -> throwError err
939+ Right _ -> do
940+ atomExprType <- typeForGraphRefAtomExpr (attributes relIn) atomExpr
941+ atomExprType' <- verifyGraphRefAtomExprTypes relIn atomExpr atomExprType
795942 let newAttrs = A.attributesFromList [Attribute newAttrName atomExprType']
796943 newAndOldAttrs = A.addAttributes (attributes relIn) newAttrs
797− pure (newAndOldAttrs, \tup -> let substate = mergeTuplesIntoRelationalExprState tup rstate in case runReader (evalAtomExpr tup atomExpr) substate of
798− Left err -> Left err
799− Right atom -> Right (tupleAtomExtend newAttrName atom tup)
944+ env <- ask
945+ pure (newAndOldAttrs, \tup -> do
946+ let gfEnv = mergeTuplesIntoGraphRefRelationalExprEnv tup env
947+ atom <- runGraphRefRelationalExprM gfEnv (evalGraphRefAtomExpr tup atomExpr)
948+ Right (tupleAtomExtend newAttrName atom tup)
800949 )
801950
802−evalAtomExpr :: RelationTuple -> AtomExpr -> RelationalExprState (Either RelationalError Atom)
951+evalGraphRefAtomExpr :: RelationTuple -> GraphRefAtomExpr -> GraphRefRelationalExprM Atom
952+evalGraphRefAtomExpr tupIn (AttributeAtomExpr attrName) =
953+ case atomForAttributeName attrName tupIn of
954+ Right atom -> pure atom
955+ Left err@(NoSuchAttributeNamesError _) -> do
956+ env <- askEnv
957+ case gre_extra env of
958+ Nothing -> throwError err
959+ Just (Left ctxtup) -> lift $ except $ atomForAttributeName attrName ctxtup
960+ Just (Right _) -> throwError err
961+ Left err -> throwError err
962+evalGraphRefAtomExpr _ (NakedAtomExpr atom) = pure atom
963+evalGraphRefAtomExpr tupIn (FunctionAtomExpr funcName arguments tid) = do
964+ argTypes <- mapM (typeForGraphRefAtomExpr (tupleAttributes tupIn)) arguments
965+ context <- gfDatabaseContextForMarker tid
966+ let functions = atomFunctions context
967+ func <- lift $ except (atomFunctionForName funcName functions)
968+ let expectedArgCount = length (atomFuncType func) - 1
969+ actualArgCount = length argTypes
970+ safeInit [] = [] -- different behavior from normal init
971+ safeInit xs = init xs
972+ if expectedArgCount /= actualArgCount then
973+ throwError (FunctionArgumentCountMismatchError expectedArgCount actualArgCount)
974+ else do
975+ let zippedArgs = zip (safeInit (atomFuncType func)) argTypes
976+ mapM_ (\(expType, actType) ->
977+ lift $ except (atomTypeVerify expType actType)) zippedArgs
978+ evaldArgs <- mapM (evalGraphRefAtomExpr tupIn) arguments
979+ case evalAtomFunction func evaldArgs of
980+ Left err -> throwError (AtomFunctionUserError err)
981+ Right result -> do
982+ --validate that the result matches the expected type
983+ _ <- lift $ except (atomTypeVerify (last (atomFuncType func)) (atomTypeForAtom result))
984+ pure result
985+evalGraphRefAtomExpr tupIn (RelationAtomExpr relExpr) = do
986+ --merge existing state tuple context into new state tuple context to support an arbitrary number of levels, but new attributes trounce old attributes
987+ env <- ask
988+ let gfEnv = mergeTuplesIntoGraphRefRelationalExprEnv tupIn env
989+ relAtom <- lift $ except $ runGraphRefRelationalExprM gfEnv (evalGraphRefRelationalExpr relExpr)
990+ pure (RelationAtom relAtom)
991+evalGraphRefAtomExpr tupIn cons@(ConstructedAtomExpr dConsName dConsArgs _) = do --why is the tid unused here? suspicious
992+ let mergeEnv = mergeTuplesIntoGraphRefRelationalExprEnv tupIn
993+ aType <- local mergeEnv (typeForGraphRefAtomExpr (tupleAttributes tupIn) cons)
994+ argAtoms <- local mergeEnv $
995+ mapM (evalGraphRefAtomExpr tupIn) dConsArgs
996+ pure (ConstructedAtom dConsName aType argAtoms)
997+
998+{-
999+evalAtomExpr :: RelationTuple -> AtomExpr -> RelationalExprM Atom
1000+evalAtomExpr tupIn expr =
1001+ -}
1002+{-
1003+
8031004evalAtomExpr tupIn (AttributeAtomExpr attrName) = case atomForAttributeName attrName tupIn of
8041005 Right atom -> pure (Right atom)
8051006 err@(Left (NoSuchAttributeNamesError _)) -> do
8061007 rstate <- ask
807− case rstate of
808− RelationalExprStateElems _ -> pure err
809− RelationalExprStateAttrsElems _ _ -> pure err
810− RelationalExprStateTupleElems _ ctxtup -> pure (atomForAttributeName attrName ctxtup)
1008+ let stateTup = stateTuple rstate
1009+ pure (atomForAttributeName attrName stateTup)
8111010 Left err -> pure (Left err)
8121011evalAtomExpr _ (NakedAtomExpr atom) = pure (Right atom)
8131012evalAtomExpr tupIn (FunctionAtomExpr funcName arguments ()) = do
@@ -847,131 +1046,170 @@ evalAtomExpr tupIn cons@(ConstructedAtomExpr dConsName dConsArgs ()) = runExcept
8471046 aType <- either throwE pure (runReader (typeFromAtomExpr (tupleAttributes tupIn) cons) newState)
8481047 argAtoms <- mapM (\arg -> either throwE pure (runReader (evalAtomExpr tupIn arg) newState)) dConsArgs
8491048 pure (ConstructedAtom dConsName aType argAtoms)
850−
851−typeFromAtomExpr :: Attributes -> AtomExpr -> RelationalExprState (Either RelationalError AtomType)
852−typeFromAtomExpr attrs (AttributeAtomExpr attrName) = do
853− rstate <- ask
1049+-}
1050+typeForGraphRefAtomExpr :: Attributes -> GraphRefAtomExpr -> GraphRefRelationalExprM AtomType
1051+typeForGraphRefAtomExpr attrs (AttributeAtomExpr attrName) = do
1052+ renv <- askEnv
8541053 case A.atomTypeForAttributeName attrName attrs of
855− Right aType -> pure (Right aType)
856− Left err@(NoSuchAttributeNamesError _) -> case rstate of
857− RelationalExprStateAttrsElems _ attrs' -> case A.attributeForName attrName attrs' of
858− Left err' -> pure (Left err')
859− Right attr -> pure (Right (A.atomType attr))
860− RelationalExprStateElems _ -> pure (Left err)
861− RelationalExprStateTupleElems _ tup -> case atomForAttributeName attrName tup of
862− Left err' -> pure (Left err')
863− Right atom -> pure (Right (atomTypeForAtom atom))
864− Left err -> pure (Left err)
865−typeFromAtomExpr _ (NakedAtomExpr atom) = pure (Right (atomTypeForAtom atom))
866−typeFromAtomExpr attrs (FunctionAtomExpr funcName atomArgs _) = do
867− context <- fmap stateElemsContext ask
868− let funcs = atomFunctions context
1054+ Right aType -> pure aType
1055+ Left err@(NoSuchAttributeNamesError _) ->
1056+ let envTup = envTuple renv
1057+ envAttrs = envAttributes renv in
1058+ case A.attributeForName attrName envAttrs of
1059+ Right attr -> pure (A.atomType attr)
1060+ Left _ -> case atomForAttributeName attrName envTup of
1061+ Right atom -> pure (atomTypeForAtom atom)
1062+ Left _ -> --throwError (traceStack (show ("typeForGRAtomExpr", attrs, envTup)) err)
1063+ throwError err
1064+ Left err -> throwError err
1065+
1066+typeForGraphRefAtomExpr _ (NakedAtomExpr atom) = pure (atomTypeForAtom atom)
1067+typeForGraphRefAtomExpr attrs (FunctionAtomExpr funcName atomArgs transId) = do
1068+ funcs <- atomFunctions <$> gfDatabaseContextForMarker transId
8691069 case atomFunctionForName funcName funcs of
870− Left err -> pure (Left err)
1070+ Left err -> throwError err
8711071 Right func -> do
8721072 let funcRetType = last (atomFuncType func)
8731073 funcArgTypes = init (atomFuncType func)
874− eArgTypes <- mapM (typeFromAtomExpr attrs) atomArgs
875− case lefts eArgTypes of
876− errs@(_:_) -> pure (Left (someErrors errs))
877− [] -> do
878− let eTvMap = resolveTypeVariables funcArgTypes argTypes
879− argTypes = rights eArgTypes
880− case eTvMap of
881− Left err -> pure (Left err)
882− Right tvMap -> pure (resolveFunctionReturnValue funcName tvMap funcRetType)
883−typeFromAtomExpr attrs (RelationAtomExpr relExpr) = runExceptT $ do
884− rstate <- lift ask
885− relType <- either throwE pure (runReader (typeForRelationalExpr relExpr) (mergeAttributesIntoRelationalExprState attrs rstate))
1074+ argTypes <- mapM (typeForGraphRefAtomExpr attrs) atomArgs
1075+ let eTvMap = resolveTypeVariables funcArgTypes argTypes
1076+ case eTvMap of
1077+ Left err -> throwError err
1078+ Right tvMap -> lift $ except $ resolveFunctionReturnValue funcName tvMap funcRetType
1079+typeForGraphRefAtomExpr attrs (RelationAtomExpr relExpr) = do
1080+ relType <- R.local (mergeAttributesIntoGraphRefRelationalExprEnv attrs) (typeForGraphRefRelationalExpr relExpr)
8861081 pure (RelationAtomType (attributes relType))
8871082-- grab the type of the data constructor, then validate that the args match the expected types
888−typeFromAtomExpr attrs (ConstructedAtomExpr dConsName dConsArgs _) =
889− runExceptT $ do
890− argsTypes <- mapM (liftE . typeFromAtomExpr attrs) dConsArgs
891− context <- fmap stateElemsContext (lift ask)
892− either throwE pure (atomTypeForDataConstructor (typeConstructorMapping context) dConsName argsTypes)
1083+typeForGraphRefAtomExpr attrs (ConstructedAtomExpr dConsName dConsArgs tid) =
1084+ do
1085+ argsTypes <- mapM (typeForGraphRefAtomExpr attrs) dConsArgs
1086+ tConsMap <- typeConstructorMapping <$> gfDatabaseContextForMarker tid
1087+ lift $ except $ atomTypeForDataConstructor tConsMap dConsName argsTypes
8931088
8941089-- | Validate that the type of the AtomExpr matches the expected type.
895−verifyAtomExprTypes :: Relation -> AtomExpr -> AtomType -> RelationalExprState (Either RelationalError AtomType)
896−verifyAtomExprTypes relIn (AttributeAtomExpr attrName) expectedType = runExceptT $ do
897− rstate <- lift ask
1090+verifyGraphRefAtomExprTypes :: Relation -> GraphRefAtomExpr -> AtomType -> GraphRefRelationalExprM AtomType
1091+verifyGraphRefAtomExprTypes relIn (AttributeAtomExpr attrName) expectedType = do
1092+ env <- askEnv
8981093 case A.atomTypeForAttributeName attrName (attributes relIn) of
899− Right aType -> either throwE pure (atomTypeVerify expectedType aType)
900− (Left err@(NoSuchAttributeNamesError _)) -> case rstate of
901− RelationalExprStateTupleElems _ _ -> throwE err
902− RelationalExprStateElems _ -> throwE err
903− RelationalExprStateAttrsElems _ attrs -> case A.attributeForName attrName attrs of
904− Left err' -> throwE err'
905− Right attrType -> either throwE pure (atomTypeVerify expectedType (A.atomType attrType))
906− Left err -> throwE err
907−verifyAtomExprTypes _ (NakedAtomExpr atom) expectedType = pure (atomTypeVerify expectedType (atomTypeForAtom atom))
908−verifyAtomExprTypes relIn (FunctionAtomExpr funcName funcArgExprs _) expectedType = do
909− rstate <- ask
1094+ Right aType -> lift $ except $ atomTypeVerify expectedType aType
1095+ (Left err@(NoSuchAttributeNamesError _)) ->
1096+ let attrs' = envAttributes env in
1097+ if attrs' == emptyAttributes then
1098+ throwError err
1099+ else
1100+ case A.attributeForName attrName attrs' of
1101+ Left err' -> throwError err'
1102+ Right attrType -> lift $ except $ atomTypeVerify expectedType (A.atomType attrType)
1103+ Left err -> throwError err
1104+
1105+verifyGraphRefAtomExprTypes _ (NakedAtomExpr atom) expectedType =
1106+ lift $ except $ atomTypeVerify expectedType (atomTypeForAtom atom)
1107+verifyGraphRefAtomExprTypes relIn (FunctionAtomExpr funcName funcArgExprs tid) expectedType = do
1108+ context <- gfDatabaseContextForMarker tid
9101109 let functions = atomFunctions context
911− context = stateElemsContext rstate
912− runExceptT $ do
913− func <- either throwE pure (atomFunctionForName funcName functions)
914− let expectedArgTypes = atomFuncType func
915− funcArgTypes <- mapM (\(atomExpr,expectedType2,argCount) -> case runReader (verifyAtomExprTypes relIn atomExpr expectedType2) rstate of
916− Left (AtomTypeMismatchError expSubType actSubType) -> throwE (AtomFunctionTypeError funcName argCount expSubType actSubType)
917− Left err -> throwE err
918− Right x -> pure x
919− ) $ zip3 funcArgExprs expectedArgTypes [1..]
920− if length funcArgTypes /= length expectedArgTypes - 1 then
921− throwE (AtomTypeCountError funcArgTypes expectedArgTypes)
1110+ func <- lift $ except $ atomFunctionForName funcName functions
1111+ let expectedArgTypes = atomFuncType func
1112+ funcArgVerifier (atomExpr, expectedType2, argCount) = do
1113+ let handler :: RelationalError -> GraphRefRelationalExprM AtomType
1114+ handler (AtomTypeMismatchError expSubType actSubType) = throwError (AtomFunctionTypeError funcName argCount expSubType actSubType)
1115+ handler err = throwError err
1116+ verifyGraphRefAtomExprTypes relIn atomExpr expectedType2 `catchError` handler
1117+ funcArgTypes <- mapM funcArgVerifier $ zip3 funcArgExprs expectedArgTypes [1..]
1118+ if length funcArgTypes /= length expectedArgTypes - 1 then
1119+ throwError (AtomTypeCountError funcArgTypes expectedArgTypes)
9221120 else
923− either throwE pure (atomTypeVerify expectedType (last expectedArgTypes))
924−verifyAtomExprTypes relIn (RelationAtomExpr relationExpr) expectedType = runExceptT $ do
925− rstate <- lift ask
926− relType <- either throwE pure (runReader (typeForRelationalExpr relationExpr) (mergeAttributesIntoRelationalExprState (attributes relIn) rstate))
927− either throwE pure (atomTypeVerify expectedType (RelationAtomType (attributes relType)))
928−verifyAtomExprTypes rel cons@ConstructedAtomExpr{} expectedType = runExceptT $ do
929− cType <- liftE (typeFromAtomExpr (attributes rel) cons)
930− either throwE pure (atomTypeVerify expectedType cType)
1121+ lift $ except $ atomTypeVerify expectedType (last expectedArgTypes)
1122+verifyGraphRefAtomExprTypes relIn (RelationAtomExpr relationExpr) expectedType =
1123+ do
1124+ let mergedAttrsEnv e = mergeAttributesIntoGraphRefRelationalExprEnv (attributes relIn) e
1125+ relType <- R.local mergedAttrsEnv (typeForGraphRefRelationalExpr relationExpr)
1126+ lift $ except $ atomTypeVerify expectedType (RelationAtomType (attributes relType))
1127+verifyGraphRefAtomExprTypes rel cons@ConstructedAtomExpr{} expectedType = do
1128+ cType <- typeForGraphRefAtomExpr (attributes rel) cons
1129+ lift $ except $ atomTypeVerify expectedType cType
9311130
9321131-- | Look up the type's name and create a new attribute.
933−evalAttrExpr :: TypeConstructorMapping -> AttributeExpr -> Either RelationalError Attribute
934−evalAttrExpr aTypes (AttributeAndTypeNameExpr attrName tCons ()) = do
935− aType <- atomTypeForTypeConstructorValidate True tCons aTypes M.empty
936− validateAtomType aType aTypes
937− Right (Attribute attrName aType)
1132+evalGraphRefAttrExpr :: GraphRefAttributeExpr -> GraphRefRelationalExprM Attribute
1133+evalGraphRefAttrExpr (AttributeAndTypeNameExpr attrName tCons transId) = do
1134+ tConsMap <- typeConstructorMapping <$> gfDatabaseContextForMarker transId
1135+ aType <- lift $ except $ atomTypeForTypeConstructorValidate True tCons tConsMap M.empty
1136+ lift $ except $ validateAtomType aType tConsMap
1137+ pure $ Attribute attrName aType
9381138
939−evalAttrExpr _ (NakedAttributeExpr attr) = Right attr
940−
941−evalTupleExpr :: Maybe Attributes -> TupleExpr -> RelationalExprState (Either RelationalError RelationTuple)
942−evalTupleExpr mAttrs (TupleExpr tupMap) = do
943− context <- fmap stateElemsContext ask
944− runExceptT $ do
1139+evalGraphRefAttrExpr (NakedAttributeExpr attr) = pure attr
1140+
1141+-- for tuple type concrete resolution (Nothing ==> Maybe Int) when the attributes hint is Nothing, we need to first process all the tuples, then extract the concrete types on a per-attribute basis, then reprocess the tuples to include the concrete types
1142+evalGraphRefTupleExprs :: Maybe Attributes -> GraphRefTupleExprs -> GraphRefRelationalExprM [RelationTuple]
1143+evalGraphRefTupleExprs _ (TupleExprs _ []) = pure []
1144+evalGraphRefTupleExprs mAttrs (TupleExprs fixedMarker tupleExprL) = do
1145+ tuples <- mapM (evalGraphRefTupleExpr mAttrs) tupleExprL
1146+ finalAttrs <- case mAttrs of
1147+ Just attrs -> pure attrs
1148+ Nothing ->
1149+ case tuples of
1150+ [] -> pure emptyAttributes
1151+ (headTuple:tailTuples) -> do
1152+ --gather up resolved atom types or throw an error if an attribute cannot be made concrete from the inferred types- this could still fail if the type cannot be inferred (e.g. from [Nothing, Nothing])
1153+ let mostResolvedTypes =
1154+ foldr (\tup acc ->
1155+ fmap (\(tupAttr,accAttr) -> --if the attribute is a constructedatomtype, we can recurse into it to potentially resolve type variables
1156+ if isResolvedAttribute accAttr then
1157+ accAttr
1158+ else
1159+ case resolveAttributes accAttr tupAttr of
1160+ Left _ -> accAttr
1161+ Right val -> val) (zip (V.toList $ tupleAttributes tup) acc)) (V.toList $ tupleAttributes headTuple) tailTuples
1162+ pure (A.attributesFromList mostResolvedTypes)
1163+ --strategy: if all the tuple expr transaction markers refer to one location, then we can pass the type constructor mapping from that location, otherwise, we cannot assume that the types are the same
1164+ tConsMap <- case singularTransactions tupleExprL of
1165+ SingularTransactionRef commonTransId ->
1166+ typeConstructorMapping <$> gfDatabaseContextForMarker commonTransId
1167+ NoTransactionsRef ->
1168+ typeConstructorMapping <$> gfDatabaseContextForMarker fixedMarker
1169+ -- if there are multiple transaction markers in the TupleExprs, then we can't assume a single type constructor mapping- this could be improved in the future, but if all the tuples are fully resolved, then we don't need further resolution
1170+ _ -> throwError TupleExprsReferenceMultipleMarkersError
1171+ lift $ except $ validateAttributes tConsMap finalAttrs
1172+ mapM (lift . except . resolveTypesInTuple finalAttrs tConsMap) tuples
1173+
1174+
1175+--resolveAttributes (Attribute "gonk" (ConstructedAtomType "Either" (fromList [("a",IntegerAtomType),("b",TypeVariableType "b")]))) (Attribute "gonk" (ConstructedAtomType "Either" (fromList [("a",TypeVariableType "a"),("b",TextAtomType)])))
1176+
1177+evalGraphRefTupleExpr :: Maybe Attributes -> GraphRefTupleExpr -> GraphRefRelationalExprM RelationTuple
1178+evalGraphRefTupleExpr mAttrs (TupleExpr tupMap) = do
9451179 -- it's not possible for AtomExprs in tuple constructors to reference other Attributes' atoms due to the necessary order-of-operations (need a tuple to pass to evalAtomExpr)- it may be possible with some refactoring of type usage or delayed evaluation- needs more thought, but not a priority
9461180 -- I could adjust this logic so that when the attributes are not specified (Nothing), then I can attempt to extract the attributes from the tuple- the type resolution will blow up if an ambiguous data constructor is used (Left 4) and this should allow simple cases to "relation{tuple{a 4}}" to be processed
947− let attrs = fromMaybe A.emptyAttributes mAttrs
948− attrAtoms <- mapM (\(attrName, aExpr) -> do
949− --provided when the relation header is available
950− let eExpectedAtomType = A.atomTypeForAttributeName attrName attrs
951− unresolvedType <- liftE (typeFromAtomExpr attrs aExpr)
952− resolvedType <- case eExpectedAtomType of
1181+ let attrs = fromMaybe A.emptyAttributes mAttrs
1182+ resolveOneAtom (attrName, aExpr) =
1183+ do
1184+ --provided when the relation header is available
1185+ let eExpectedAtomType = A.atomTypeForAttributeName attrName attrs
1186+ unresolvedType <- typeForGraphRefAtomExpr attrs aExpr
1187+ resolvedType <- case eExpectedAtomType of
9531188 Left _ -> pure unresolvedType
954− Right typeHint -> either throwE pure (resolveAtomType typeHint unresolvedType)
1189+ Right typeHint -> lift $ except $ resolveAtomType typeHint unresolvedType
9551190 --resolve atom typevars based on resolvedType?
956− newAtom <- liftE (evalAtomExpr emptyTuple aExpr)
957− pure (attrName, newAtom, resolvedType)
958− ) (M.toList tupMap)
959− let tupAttrs = A.attributesFromList $ map (\(attrName, _, aType) -> Attribute attrName aType) attrAtoms
960− atoms = V.fromList $ map (\(_, atom, _) -> atom) attrAtoms
961− tup = mkRelationTuple tupAttrs atoms
962− tConss = typeConstructorMapping context
963− finalAttrs = fromMaybe tupAttrs mAttrs
1191+ newAtom <- evalGraphRefAtomExpr emptyTuple aExpr
1192+ pure (attrName, newAtom, resolvedType)
1193+ attrAtoms <- mapM resolveOneAtom (M.toList tupMap)
1194+ let tupAttrs = A.attributesFromList $ map (\(attrName, _, aType) -> Attribute attrName aType) attrAtoms
1195+ atoms = V.fromList $ map (\(_, atom, _) -> atom) attrAtoms
1196+ tup = mkRelationTuple tupAttrs atoms
1197+ finalAttrs = fromMaybe tupAttrs mAttrs
9641198 --verify that the attributes match
965− when (A.attributeNameSet finalAttrs /= A.attributeNameSet tupAttrs) $ throwE (TupleAttributeTypeMismatchError tupAttrs)
966− tup' <- either throwE pure (resolveTypesInTuple finalAttrs tConss (reorderTuple finalAttrs tup))
967− _ <- either throwE pure (validateTuple tup' tConss)
968− pure tup'
1199+ when (A.attributeNameSet finalAttrs /= A.attributeNameSet tupAttrs) $ throwError (TupleAttributeTypeMismatchError tupAttrs)
1200+ --we can't resolve types here- they have to be resolved at the atom level where the graph ref is held
1201+ --tup' <- lift $ except (resolveTypesInTuple finalAttrs tConss (reorderTuple finalAttrs tup))
1202+ let tup' = reorderTuple finalAttrs tup
1203+ --TODO: restore type resolution
1204+-- _ <- lift $ except (validateTuple tup' tConss)
1205+ pure tup'
9691206
970−evalAttributeNames :: AttributeNames -> RelationalExpr -> RelationalExprState (Either RelationalError (S.Set AttributeName))
1207+{-
1208+evalAttributeNames :: AttributeNames -> RelationalExpr -> RelationalExprM (Either RelationalError (S.Set AttributeName))
9711209evalAttributeNames attrNames expr = do
9721210 eExprType <- typeForRelationalExpr expr
9731211 case eExprType of
974− Left err -> pure (Left err)
1212+ Left err -> throwError err
9751213 Right exprTyp -> do
9761214 let typeNameSet = S.fromList (V.toList (A.attributeNames (attributes exprTyp)))
9771215 case attrNames of
@@ -1014,4 +1252,278 @@ evalAttributeNames attrNames expr = do
10141252 case eAttrExprType of
10151253 Left err -> pure (Left err)
10161254 Right attrExprType -> pure (Right (A.attributeNameSet (attributes attrExprType)))
1017−
1255+-}
1256+
1257+--temporary implementation until we have a proper planner+executor
1258+evalGraphRefRelationalExpr :: GraphRefRelationalExpr -> GraphRefRelationalExprM Relation
1259+evalGraphRefRelationalExpr (MakeRelationFromExprs mAttrExprs tupleExprs) = do
1260+ mAttrs <- case mAttrExprs of
1261+ Just _ ->
1262+ Just . A.attributesFromList <$> mapM evalGraphRefAttrExpr (fromMaybe [] mAttrExprs)
1263+ Nothing -> pure Nothing
1264+ tuples <- evalGraphRefTupleExprs mAttrs tupleExprs
1265+ let attrs = fromMaybe firstTupleAttrs mAttrs
1266+ firstTupleAttrs = if null tuples then A.emptyAttributes else tupleAttributes (head tuples)
1267+ lift $ except $ mkRelation attrs (RelationTupleSet tuples)
1268+evalGraphRefRelationalExpr (MakeStaticRelation attributeSet tupleSet) =
1269+ lift $ except $ mkRelation attributeSet tupleSet
1270+evalGraphRefRelationalExpr (ExistingRelation rel) = pure rel
1271+evalGraphRefRelationalExpr (RelationVariable name tid) = do
1272+ ctx <- gfDatabaseContextForMarker tid
1273+ case M.lookup name (relationVariables ctx) of
1274+ Nothing -> throwError (RelVarNotDefinedError name)
1275+ Just rv -> evalGraphRefRelationalExpr rv
1276+evalGraphRefRelationalExpr (Project attrNames expr) = do
1277+ attrNameSet <- evalGraphRefAttributeNames attrNames expr
1278+ rel <- evalGraphRefRelationalExpr expr
1279+ lift $ except $ project attrNameSet rel
1280+evalGraphRefRelationalExpr (Union exprA exprB) = do
1281+ relA <- evalGraphRefRelationalExpr exprA
1282+ relB <- evalGraphRefRelationalExpr exprB
1283+ lift $ except $ union relA relB
1284+evalGraphRefRelationalExpr (Join exprA exprB) = do
1285+ relA <- evalGraphRefRelationalExpr exprA
1286+ relB <- evalGraphRefRelationalExpr exprB
1287+ lift $ except $ join relA relB
1288+evalGraphRefRelationalExpr (Rename oldName newName expr) = do
1289+ rel <- evalGraphRefRelationalExpr expr
1290+ lift $ except $ rename oldName newName rel
1291+evalGraphRefRelationalExpr (Difference exprA exprB) = do
1292+ relA <- evalGraphRefRelationalExpr exprA
1293+ relB <- evalGraphRefRelationalExpr exprB
1294+ lift $ except $ difference relA relB
1295+evalGraphRefRelationalExpr (Group groupAttrNames newAttrName expr) = do
1296+ groupNames <- evalGraphRefAttributeNames groupAttrNames expr
1297+ rel <- evalGraphRefRelationalExpr expr
1298+ lift $ except $ group groupNames newAttrName rel
1299+evalGraphRefRelationalExpr (Ungroup groupAttrName expr) = do
1300+ rel <- evalGraphRefRelationalExpr expr
1301+ lift $ except $ ungroup groupAttrName rel
1302+evalGraphRefRelationalExpr (Restrict predExpr expr) = do
1303+ rel <- evalGraphRefRelationalExpr expr
1304+ filt <- predicateRestrictionFilter (attributes rel) predExpr
1305+ lift $ except $ restrict filt rel
1306+evalGraphRefRelationalExpr (Equals exprA exprB) = do
1307+ relA <- evalGraphRefRelationalExpr exprA
1308+ relB <- evalGraphRefRelationalExpr exprB
1309+ pure $ if relA == relB then relationTrue else relationFalse
1310+evalGraphRefRelationalExpr (NotEquals exprA exprB) = do
1311+ relA <- evalGraphRefRelationalExpr exprA
1312+ relB <- evalGraphRefRelationalExpr exprB
1313+ pure $ if relA == relB then relationFalse else relationTrue
1314+evalGraphRefRelationalExpr (Extend extendTupleExpr expr) = do
1315+ rel <- evalGraphRefRelationalExpr expr
1316+ (newAttrs, tupProc) <- extendGraphRefTupleExpressionProcessor rel extendTupleExpr
1317+ lift $ except $ relMogrify tupProc newAttrs rel
1318+evalGraphRefRelationalExpr expr@With{} =
1319+ --strategy A: add relation variables to the contexts in the graph
1320+ --strategy B: drop in macros in place (easier programmatically)
1321+ --strategy B implementation
1322+ evalGraphRefRelationalExpr (substituteWithNameMacros [] expr)
1323+
1324+dbContextForTransId :: TransactionId -> TransactionGraph -> Either RelationalError DatabaseContext
1325+dbContextForTransId tid graph = do
1326+ trans <- transactionForId tid graph
1327+ pure (concreteDatabaseContext trans)
1328+
1329+transactionForId :: TransactionId -> TransactionGraph -> Either RelationalError Transaction
1330+transactionForId tid graph
1331+ | tid == U.nil =
1332+ Left RootTransactionTraversalError
1333+ | S.null matchingTrans =
1334+ Left $ NoSuchTransactionError tid
1335+ | otherwise =
1336+ Right $ head (S.toList matchingTrans)
1337+ where
1338+ matchingTrans = S.filter (\(Transaction idMatch _ _) -> idMatch == tid) (transactionsForGraph graph)
1339+
1340+typeForGraphRefRelationalExpr :: GraphRefRelationalExpr -> GraphRefRelationalExprM Relation
1341+typeForGraphRefRelationalExpr (MakeStaticRelation attrs _) = lift $ except $ mkRelation attrs emptyTupleSet
1342+typeForGraphRefRelationalExpr (ExistingRelation rel) = pure (emptyRelationWithAttrs (attributes rel))
1343+typeForGraphRefRelationalExpr (MakeRelationFromExprs mAttrExprs tupleExprs) = do
1344+ mAttrs <- case mAttrExprs of
1345+ Just attrExprs -> do
1346+ attrs <- mapM evalGraphRefAttributeExpr attrExprs
1347+ pure (Just (attributesFromList attrs))
1348+ Nothing -> pure Nothing
1349+ tuples <- evalGraphRefTupleExprs mAttrs tupleExprs
1350+ let retAttrs = case tuples of
1351+ (tup:_) -> tupleAttributes tup
1352+ [] -> fromMaybe A.emptyAttributes mAttrs
1353+ pure $ emptyRelationWithAttrs retAttrs
1354+
1355+typeForGraphRefRelationalExpr (RelationVariable rvName tid) = do
1356+ relVars <- relationVariables <$> gfDatabaseContextForMarker tid
1357+ case M.lookup rvName relVars of
1358+ Nothing -> throwError (RelVarNotDefinedError rvName)
1359+ Just rvExpr ->
1360+ typeForGraphRefRelationalExpr rvExpr
1361+typeForGraphRefRelationalExpr (Project attrNames expr) = do
1362+ exprType' <- typeForGraphRefRelationalExpr expr
1363+ projectionAttrs <- evalGraphRefAttributeNames attrNames expr
1364+ lift $ except $ project projectionAttrs exprType'
1365+typeForGraphRefRelationalExpr (Union exprA exprB) = do
1366+ exprA' <- typeForGraphRefRelationalExpr exprA
1367+ exprB' <- typeForGraphRefRelationalExpr exprB
1368+ lift $ except $ union exprA' exprB'
1369+typeForGraphRefRelationalExpr (Join exprA exprB) = do
1370+ exprA' <- typeForGraphRefRelationalExpr exprA
1371+ exprB' <- typeForGraphRefRelationalExpr exprB
1372+ lift $ except $ join exprA' exprB'
1373+typeForGraphRefRelationalExpr (Rename oldAttr newAttr expr) = do
1374+ expr' <- typeForGraphRefRelationalExpr expr
1375+ lift $ except $ rename oldAttr newAttr expr'
1376+typeForGraphRefRelationalExpr (Difference exprA exprB) = do
1377+ exprA' <- typeForGraphRefRelationalExpr exprA
1378+ exprB' <- typeForGraphRefRelationalExpr exprB
1379+ lift $ except $ difference exprA' exprB'
1380+typeForGraphRefRelationalExpr (Group groupNames attrName expr) = do
1381+ expr' <- typeForGraphRefRelationalExpr expr
1382+ groupNames' <- evalGraphRefAttributeNames groupNames expr
1383+ lift $ except $ group groupNames' attrName expr'
1384+typeForGraphRefRelationalExpr (Ungroup groupAttrName expr) = do
1385+ expr' <- typeForGraphRefRelationalExpr expr
1386+ lift $ except $ ungroup groupAttrName expr'
1387+typeForGraphRefRelationalExpr (Restrict pred' expr) = do
1388+ expr' <- typeForGraphRefRelationalExpr expr
1389+ filt <- predicateRestrictionFilter (attributes expr') pred'
1390+ lift $ except $ restrict filt expr'
1391+typeForGraphRefRelationalExpr Equals{} =
1392+ pure relationFalse
1393+typeForGraphRefRelationalExpr NotEquals{} =
1394+ pure relationFalse
1395+typeForGraphRefRelationalExpr (Extend extendTupleExpr expr) = do
1396+ rel <- typeForGraphRefRelationalExpr expr
1397+ evalGraphRefRelationalExpr (Extend extendTupleExpr (ExistingRelation rel))
1398+typeForGraphRefRelationalExpr expr@(With withs _) = do
1399+ let expr' = substituteWithNameMacros [] expr
1400+ checkMacroName (WithNameExpr macroName tid) = do
1401+ rvs <- relationVariables <$> gfDatabaseContextForMarker tid
1402+ case M.lookup macroName rvs of
1403+ Just _ -> lift $ except $ Left (RelVarAlreadyDefinedError macroName) --this error does not include the transaction marker, but should be good enough to identify the cause
1404+ Nothing -> pure ()
1405+ mapM_ (checkMacroName . fst) withs
1406+ typeForGraphRefRelationalExpr expr'
1407+
1408+evalGraphRefAttributeNames :: GraphRefAttributeNames -> GraphRefRelationalExpr -> GraphRefRelationalExprM (S.Set AttributeName)
1409+evalGraphRefAttributeNames attrNames expr = do
1410+ exprType' <- typeForGraphRefRelationalExpr expr
1411+ let typeNameSet = S.fromList (V.toList (A.attributeNames (attributes exprType')))
1412+ case attrNames of
1413+ AttributeNames names ->
1414+ case A.projectionAttributesForNames names (attributes exprType') of
1415+ Left err -> throwError err
1416+ Right attrs -> pure (S.fromList (V.toList (A.attributeNames attrs)))
1417+
1418+ InvertedAttributeNames names -> do
1419+ let nonExistentAttributeNames = A.attributeNamesNotContained names typeNameSet
1420+ if not (S.null nonExistentAttributeNames) then
1421+ throwError $ AttributeNamesMismatchError nonExistentAttributeNames
1422+ else
1423+ pure (A.nonMatchingAttributeNameSet names typeNameSet)
1424+
1425+ UnionAttributeNames namesA namesB -> do
1426+ nameSetA <- evalGraphRefAttributeNames namesA expr
1427+ nameSetB <- evalGraphRefAttributeNames namesB expr
1428+ pure (S.union nameSetA nameSetB)
1429+
1430+ IntersectAttributeNames namesA namesB -> do
1431+ nameSetA <- evalGraphRefAttributeNames namesA expr
1432+ nameSetB <- evalGraphRefAttributeNames namesB expr
1433+ pure (S.intersection nameSetA nameSetB)
1434+
1435+ RelationalExprAttributeNames attrExpr -> do
1436+ attrExprType <- typeForGraphRefRelationalExpr attrExpr
1437+ pure (A.attributeNameSet (attributes attrExprType))
1438+
1439+evalGraphRefAttributeExpr :: GraphRefAttributeExpr -> GraphRefRelationalExprM Attribute
1440+evalGraphRefAttributeExpr (AttributeAndTypeNameExpr attrName tCons tid) = do
1441+ tConsMap <- typeConstructorMapping <$> gfDatabaseContextForMarker tid
1442+ case atomTypeForTypeConstructorValidate True tCons tConsMap M.empty of
1443+ Left err -> throwError err
1444+ Right aType -> do
1445+ case validateAtomType aType tConsMap of
1446+ Left err -> throwError err
1447+ Right _ -> pure (Attribute attrName aType)
1448+evalGraphRefAttributeExpr (NakedAttributeExpr attr) = pure attr
1449+
1450+mkEmptyRelVars :: RelationVariables -> RelationVariables
1451+mkEmptyRelVars = M.map mkEmptyRelVar
1452+ where
1453+ mkEmptyRelVar expr@MakeRelationFromExprs{} = expr --do not truncate here because we might lose essential type information in emptying the tuples
1454+ mkEmptyRelVar (MakeStaticRelation attrs _) = MakeStaticRelation attrs emptyTupleSet
1455+ mkEmptyRelVar (ExistingRelation rel) = ExistingRelation (emptyRelationWithAttrs (attributes rel))
1456+ mkEmptyRelVar rv@RelationVariable{} = Restrict (NotPredicate TruePredicate) rv
1457+ mkEmptyRelVar (Project attrNames expr) = Project attrNames (mkEmptyRelVar expr)
1458+ mkEmptyRelVar (Union exprA exprB) = Union (mkEmptyRelVar exprA) (mkEmptyRelVar exprB)
1459+ mkEmptyRelVar (Join exprA exprB) = Join (mkEmptyRelVar exprA) (mkEmptyRelVar exprB)
1460+ mkEmptyRelVar (Rename nameA nameB expr) = Rename nameA nameB (mkEmptyRelVar expr)
1461+ mkEmptyRelVar (Difference exprA exprB) = Difference (mkEmptyRelVar exprA) (mkEmptyRelVar exprB)
1462+ mkEmptyRelVar (Group attrNames attrName expr) = Group attrNames attrName (mkEmptyRelVar expr)
1463+ mkEmptyRelVar (Ungroup attrName expr) = Ungroup attrName (mkEmptyRelVar expr)
1464+ mkEmptyRelVar (Restrict pred' expr) = Restrict pred' (mkEmptyRelVar expr)
1465+ mkEmptyRelVar (Equals exprA exprB) = Equals (mkEmptyRelVar exprA) (mkEmptyRelVar exprB)
1466+ mkEmptyRelVar (NotEquals exprA exprB) = NotEquals (mkEmptyRelVar exprA) (mkEmptyRelVar exprB)
1467+ mkEmptyRelVar (Extend extTuple expr) = Extend extTuple (mkEmptyRelVar expr)
1468+ mkEmptyRelVar (With macros expr) = With (map (second mkEmptyRelVar) macros) (mkEmptyRelVar expr)
1469+
1470+
1471+dbErr :: RelationalError -> DatabaseContextEvalMonad ()
1472+dbErr err = lift (except (Left err))
1473+
1474+-- | Return a Relation describing the relation variables.
1475+relationVariablesAsRelation :: DatabaseContext -> TransactionGraph -> Either RelationalError Relation
1476+relationVariablesAsRelation ctx graph = do
1477+ let subrelAttrs = A.attributesFromList [Attribute "attribute" TextAtomType, Attribute "type" TextAtomType]
1478+ attrs = A.attributesFromList [Attribute "name" TextAtomType,
1479+ Attribute "attributes" (RelationAtomType subrelAttrs)]
1480+ relVars = relationVariables ctx
1481+ mkRvDesc (rvName, gfExpr) = do
1482+ let gfEnv = freshGraphRefRelationalExprEnv (Just ctx) graph
1483+ gfType <- runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr gfExpr)
1484+ pure (rvName, gfType)
1485+ relVarToAtomList (rvName, rel) = [TextAtom rvName, attributesToRel (attributes rel)]
1486+ attrAtoms a = [TextAtom (A.attributeName a), TextAtom (prettyAtomType (A.atomType a))]
1487+ attributesToRel attrl = case mkRelationFromList subrelAttrs (map attrAtoms (V.toList attrl)) of
1488+ Left err -> error ("relationVariablesAsRelation pooped " ++ show err)
1489+ Right rel -> RelationAtom rel
1490+ rvs <- mapM mkRvDesc (M.toList relVars)
1491+ let tups = map relVarToAtomList rvs
1492+ mkRelationFromList attrs tups
1493+
1494+-- | An unoptimized variant of evalGraphRefRelationalExpr for testing.
1495+evalRelationalExpr :: RelationalExpr -> RelationalExprM Relation
1496+evalRelationalExpr expr = do
1497+ graph <- reGraph
1498+ context <- reContext
1499+ let expr' = runProcessExprM UncommittedContextMarker (processRelationalExpr expr)
1500+ gfEnv = freshGraphRefRelationalExprEnv (Just context) graph
1501+ case runGraphRefRelationalExprM gfEnv (evalGraphRefRelationalExpr expr') of
1502+ Left err -> throwError err
1503+ Right rel -> pure rel
1504+
1505+{-
1506+relVarByName :: RelVarName -> GraphRefRelationalExprM GraphRefRelationalExpr
1507+relVarByName = do
1508+ relvars <- relationVariables <$> getStateContext
1509+ case M.lookup relVarName relvars of
1510+ Nothing -> dbErr (RelVarNotDefinedError relVarName)
1511+ Just gfexpr -> pure gfExpr
1512+-}
1513+
1514+class (MonadError RelationalError m, Monad m) => DatabaseContextM m where
1515+ getContext :: m DatabaseContext
1516+
1517+instance DatabaseContextM (ReaderT GraphRefRelationalExprEnv (ExceptT RelationalError Identity)) where
1518+ getContext = gfDatabaseContextForMarker UncommittedContextMarker
1519+
1520+instance DatabaseContextM (RWST DatabaseContextEvalEnv () DatabaseContextEvalState (ExceptT RelationalError Identity)) where
1521+ getContext = getStateContext
1522+
1523+relVarByName :: DatabaseContextM m => RelVarName -> m GraphRefRelationalExpr
1524+relVarByName rvName = do
1525+ relvars <- relationVariables <$> getContext
1526+ case M.lookup rvName relvars of
1527+ Nothing -> throwError (RelVarNotDefinedError rvName)
1528+ Just gfexpr -> pure gfexpr
1529+
src/lib/ProjectM36/ScriptSession.hs+3−3
@@ -80,13 +80,13 @@ initScriptSession ghcPkgPaths = do
8080 let ghcVersion = projectVersion dflags
8181
8282 sandboxPkgPaths <- liftIO $ concat <$> mapM glob [
83− "./dist-newstyle/packagedb/ghc-" ++ ghcVersion,
83+ --"./dist-newstyle/packagedb/ghc-" ++ ghcVersion, --rely on cabal 3's ghc.environment install: "cabal install --lib project-m36"
8484 ".cabal-sandbox/*ghc-" ++ ghcVersion ++ "-packages.conf.d",
8585 ".stack-work/install/*/*/" ++ ghcVersion ++ "/pkgdb",
8686 ".stack-work/install/*/pkgdb/", --windows stack build
8787 "C:/sr/snapshots/b201cfe6/pkgdb", --windows stack build- ideally, we could run `stack path --snapshot-pkg-db, but this is sufficient to pass CI
88− homeDir </> ".stack/snapshots/*/*/" ++ ghcVersion ++ "/pkgdb",
89− homeDir </> ".cabal/store/ghc-" ++ ghcVersion ++ "/package.db"
88+ homeDir </> ".stack/snapshots/*/*/" ++ ghcVersion ++ "/pkgdb"
89+ --homeDir </> ".cabal/store/ghc-" ++ ghcVersion ++ "/package.db"
9090 ]
9191
9292 let localPkgPaths = map PkgConfFile (ghcPkgPaths ++ sandboxPkgPaths)
src/lib/ProjectM36/Server.hs+1−1
@@ -9,7 +9,7 @@ import ProjectM36.FSType
99
1010import Control.Monad.IO.Class (liftIO)
1111#if MIN_VERSION_network_transport_tcp(0,7,0)
12−import Network.Transport.TCP (createTransport, defaultTCPParameters, defaultTCPAddr)
12+import Network.Transport.TCP (createTransport, defaultTCPParameters, defaultTCPAddr)
1313#else
1414import Network.Transport.TCP (createTransport, defaultTCPParameters)
1515#endif
src/lib/ProjectM36/Server/EntryPoints.hs+4−3
@@ -8,12 +8,13 @@ import Control.Distributed.Process (Process, ProcessId)
88import Control.Distributed.Process.ManagedProcess (ProcessReply)
99import Control.Distributed.Process.ManagedProcess.Server (reply)
1010import Control.Distributed.Process.Async (async, task, waitCancelTimeout, AsyncResult(..))
11−import Control.Distributed.Process.Serializable (Serializable)
1211import Control.Monad.IO.Class (liftIO)
1312import Data.Map
1413import Control.Concurrent (threadDelay)
14+import Data.Typeable
15+import Data.Binary
1516
16−timeoutOrDie :: Serializable a => Timeout -> IO a -> Process (Either ServerError a)
17+timeoutOrDie :: (Binary a, Typeable a) => Timeout -> IO a -> Process (Either ServerError a)
1718timeoutOrDie micros act =
1819 if micros == 0 then
1920 liftIO act >>= \x -> pure (Right x)
@@ -83,7 +84,7 @@ handleRetrieveInclusionDependencies ti sessionId conn = do
8384 ret <- timeoutOrDie ti (inclusionDependencies sessionId conn)
8485 reply ret conn
8586
86−handleRetrievePlanForDatabaseContextExpr :: Timeout -> SessionId -> Connection -> DatabaseContextExpr -> Reply (Either RelationalError DatabaseContextExpr)
87+handleRetrievePlanForDatabaseContextExpr :: Timeout -> SessionId -> Connection -> DatabaseContextExpr -> Reply (Either RelationalError GraphRefDatabaseContextExpr)
8788handleRetrievePlanForDatabaseContextExpr ti sessionId conn dbExpr = do
8889 ret <- timeoutOrDie ti (planForDatabaseContextExpr sessionId conn dbExpr)
8990 reply ret conn
src/lib/ProjectM36/Session.hs+3−2
@@ -3,6 +3,7 @@ import ProjectM36.Base
33import Data.UUID
44import qualified Data.Map as M
55import ProjectM36.Error
6+import qualified ProjectM36.DisconnectedTransaction as Discon
67
78type SessionId = UUID
89
@@ -18,7 +19,7 @@ disconnectedTransaction :: Session -> DisconnectedTransaction
1819disconnectedTransaction (Session discon _) = discon
1920
2021isDirty :: Session -> DirtyFlag
21−isDirty (Session (DisconnectedTransaction _ _ dirtyFlag) _) = dirtyFlag
22+isDirty (Session discon _) = Discon.isDirty discon
2223
2324concreteDatabaseContext :: Session -> DatabaseContext
2425concreteDatabaseContext (Session (DisconnectedTransaction _ (Schemas context _) _) _) = context
@@ -40,4 +41,4 @@ setSchemaName sname session = if sname == defaultSchemaName || M.member sname (s
4041 pure (Session (disconnectedTransaction session) sname)
4142 else
4243 Left (SubschemaNameNotInUseError sname)
43−
44+
src/lib/ProjectM36/Sessions.hs+1−1
@@ -7,7 +7,7 @@ import qualified StmContainers.Set as StmSet
77#else
88import qualified STMContainers.Map as StmMap
99import qualified STMContainers.Set as StmSet
10−#endif
10+#endif
1111import ProjectM36.Attribute
1212import ProjectM36.Base
1313import ProjectM36.Session
src/lib/ProjectM36/StaticOptimizer.hs+369−249
@@ -1,289 +1,402 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE FlexibleContexts #-}
13module ProjectM36.StaticOptimizer where
24import ProjectM36.Base
3−import ProjectM36.RelationalExpression
5+import ProjectM36.GraphRefRelationalExpr
46import ProjectM36.Relation
7+import ProjectM36.RelationalExpression
8+import ProjectM36.TransGraphRelationalExpression as TGRE hiding (askGraph)
59import ProjectM36.Error
10+import ProjectM36.Transaction
11+import ProjectM36.NormalizeExpr
612import qualified ProjectM36.Attribute as A
713import qualified ProjectM36.AttributeNames as AS
814import ProjectM36.TupleSet
915import Control.Monad.State
10−import Data.Either (rights, lefts)
11−import Control.Monad.Trans.Reader
16+import Control.Monad.Reader
17+import Control.Monad.Except
18+import Control.Monad.Trans.Except
19+import Data.Functor.Identity
1220import qualified Data.Map as M
1321import qualified Data.Set as S
1422
1523-- the static optimizer performs optimizations which need not take any specific-relation statistics into account
16−optimizeRelationalExpr :: DatabaseContext -> RelationalExpr -> Either RelationalError RelationalExpr
17−optimizeRelationalExpr context expr = runReader (optimizeRelationalExprReader expr) (mkRelationalExprState context)
18−
19−optimizeRelationalExprReader :: RelationalExpr -> RelationalExprState (Either RelationalError RelationalExpr)
20−optimizeRelationalExprReader expr = do
21− eOptExpr <- applyStaticRelationalOptimization expr
24+
25+--import Debug.Trace
26+
27+data GraphRefSOptRelationalExprEnv =
28+ GraphRefSOptRelationalExprEnv
29+ {
30+ ore_graph :: TransactionGraph,
31+ ore_mcontext :: Maybe DatabaseContext
32+ }
33+
34+type GraphRefSOptRelationalExprM a = ReaderT GraphRefSOptRelationalExprEnv (ExceptT RelationalError Identity) a
35+
36+data GraphRefSOptDatabaseContextExprEnv =
37+ GraphRefSOptDatabaseContextExprEnv
38+ {
39+ odce_graph :: TransactionGraph,
40+ odce_context :: DatabaseContext, --not optional for DatabaseContextExpr evaluation
41+ odce_transId :: TransactionId -- parent if context is committed- needed because MultipleExpr optimization requires running the DatabaseContextExprs (with empty relvars)
42+ }
43+
44+type GraphRefSOptDatabaseContextExprM a = ReaderT GraphRefSOptDatabaseContextExprEnv (ExceptT RelationalError Identity) a
45+
46+-- | A temporary function to be replaced by IO-based implementation.
47+optimizeAndEvalRelationalExpr :: RelationalExprEnv -> RelationalExpr -> Either RelationalError Relation
48+optimizeAndEvalRelationalExpr env expr = do
49+ let gfExpr = runProcessExprM UncommittedContextMarker (processRelationalExpr expr) -- references parent tid instead of context! options- I could add the context to the graph with a new transid or implement an evalRelationalExpr in RE.hs to use the context (which is what I had previously)
50+ graph = re_graph env
51+ ctx = re_context env
52+ gfEnv = freshGraphRefRelationalExprEnv (Just ctx) graph
53+ optExpr <- runGraphRefSOptRelationalExprM (Just ctx) (re_graph env) (fullOptimizeGraphRefRelationalExpr gfExpr)
54+ runGraphRefRelationalExprM gfEnv (evalGraphRefRelationalExpr optExpr)
55+
56+class Monad m => AskGraphContext m where
57+ askGraph :: m TransactionGraph
58+ askContext :: m DatabaseContext
59+
60+instance AskGraphContext (ReaderT GraphRefSOptDatabaseContextExprEnv (ExceptT RelationalError Identity)) where
61+ askGraph = asks odce_graph
62+ askContext = asks odce_context
63+
64+instance AskGraphContext (ReaderT GraphRefSOptRelationalExprEnv (ExceptT RelationalError Identity)) where
65+ askGraph = asks ore_graph
66+ askContext = do
67+ mctx <- asks ore_mcontext
68+ case mctx of
69+ Nothing -> throwError NoUncommittedContextInEvalError
70+ Just ctx -> pure ctx
71+
72+askTransId :: GraphRefSOptDatabaseContextExprM TransactionId
73+askTransId = asks odce_transId
74+
75+askMaybeContext :: GraphRefSOptRelationalExprM (Maybe DatabaseContext)
76+askMaybeContext = asks ore_mcontext
77+
78+optimizeDatabaseContextExpr :: DatabaseContextExpr -> GraphRefSOptDatabaseContextExprM GraphRefDatabaseContextExpr
79+optimizeDatabaseContextExpr expr = do
80+ let gfExpr = runProcessExprM UncommittedContextMarker (processDatabaseContextExpr expr)
81+ optimizeGraphRefDatabaseContextExpr gfExpr
82+
83+optimizeAndEvalDatabaseContextExpr :: Bool -> DatabaseContextExpr -> DatabaseContextEvalMonad ()
84+optimizeAndEvalDatabaseContextExpr optimize expr = do
85+ graph <- asks dce_graph
86+ transId <- asks dce_transId
87+ context <- getStateContext
88+ let gfExpr = runProcessExprM UncommittedContextMarker (processDatabaseContextExpr expr)
89+ eOptExpr = if optimize then
90+ runGraphRefSOptDatabaseContextExprM transId context graph (optimizeGraphRefDatabaseContextExpr gfExpr)
91+ else
92+ pure gfExpr
93+ case eOptExpr of
94+ Left err -> throwError err
95+ Right optExpr -> evalGraphRefDatabaseContextExpr optExpr
96+
97+
98+optimizeAndEvalTransGraphRelationalExpr :: TransactionGraph -> TransGraphRelationalExpr -> Either RelationalError Relation
99+optimizeAndEvalTransGraphRelationalExpr graph tgExpr = do
100+ gfExpr <- TGRE.process (TransGraphEvalEnv graph) tgExpr
101+ optExpr <- runGraphRefSOptRelationalExprM Nothing graph (fullOptimizeGraphRefRelationalExpr gfExpr)
102+ let gfEnv = freshGraphRefRelationalExprEnv Nothing graph
103+ runGraphRefRelationalExprM gfEnv (evalGraphRefRelationalExpr optExpr)
104+
105+optimizeAndEvalDatabaseContextIOExpr :: DatabaseContextIOExpr -> DatabaseContextIOEvalMonad (Either RelationalError ())
106+optimizeAndEvalDatabaseContextIOExpr expr = do
107+ transId <- asks dbcio_transId
108+ ctx <- getDBCIOContext
109+ graph <- asks dbcio_graph
110+ let gfExpr = runProcessExprM UncommittedContextMarker (processDatabaseContextIOExpr expr)
111+ eOptExpr = runGraphRefSOptDatabaseContextExprM transId ctx graph (optimizeDatabaseContextIOExpr gfExpr)
22112 case eOptExpr of
23113 Left err -> pure (Left err)
24114 Right optExpr ->
25− applyStaticJoinElimination (applyStaticRestrictionPushdown (applyStaticRestrictionCollapse optExpr))
115+ evalGraphRefDatabaseContextIOExpr optExpr
116+
117+{-
118+runStaticOptimizerMonad :: RelationalExprEnv -> StaticOptimizerMonad a -> Either RelationalError a
119+runStaticOptimizerMonad env m = runIdentity (runExceptT (runReaderT m env))
120+-}
121+
122+runGraphRefSOptRelationalExprM ::
123+ Maybe DatabaseContext ->
124+ TransactionGraph ->
125+ GraphRefSOptRelationalExprM a ->
126+ Either RelationalError a
127+runGraphRefSOptRelationalExprM mctx graph m = runIdentity (runExceptT (runReaderT m env))
128+ where
129+ env = GraphRefSOptRelationalExprEnv {
130+ ore_graph = graph,
131+ ore_mcontext = mctx
132+ }
133+
134+
135+runGraphRefSOptDatabaseContextExprM ::
136+ TransactionId ->
137+ DatabaseContext ->
138+ TransactionGraph ->
139+ GraphRefSOptDatabaseContextExprM a ->
140+ Either RelationalError a
141+runGraphRefSOptDatabaseContextExprM tid ctx graph m =
142+ runIdentity (runExceptT (runReaderT m env))
143+ where
144+ env = GraphRefSOptDatabaseContextExprEnv {
145+ odce_graph = graph,
146+ odce_context = ctx,
147+ odce_transId = tid
148+ }
149+
150+optimizeGraphRefRelationalExpr' ::
151+ Maybe DatabaseContext ->
152+ TransactionGraph ->
153+ GraphRefRelationalExpr ->
154+ Either RelationalError GraphRefRelationalExpr
155+optimizeGraphRefRelationalExpr' mctx graph expr =
156+ runIdentity (runExceptT (runReaderT (optimizeGraphRefRelationalExpr expr) env))
157+ where
158+ env = GraphRefSOptRelationalExprEnv {
159+ ore_graph = graph,
160+ ore_mcontext = mctx
161+ }
162+
163+-- | optimize relational expression within database context expr monad
164+liftGraphRefRelExpr :: GraphRefSOptRelationalExprM a -> GraphRefSOptDatabaseContextExprM a
165+liftGraphRefRelExpr m = do
166+ context <- asks odce_context
167+ graph <- asks odce_graph
168+ lift $ except $ runGraphRefSOptRelationalExprM (Just context) graph m
169+
170+fullOptimizeGraphRefRelationalExpr :: GraphRefRelationalExpr -> GraphRefSOptRelationalExprM GraphRefRelationalExpr
171+fullOptimizeGraphRefRelationalExpr expr = do
172+ optExpr <- optimizeGraphRefRelationalExpr expr
173+ let optExpr' = applyStaticRestrictionPushdown (applyStaticRestrictionCollapse optExpr)
174+ applyStaticJoinElimination optExpr'
26175
27−optimizeDatabaseContextExpr :: DatabaseContext -> DatabaseContextExpr -> Either RelationalError DatabaseContextExpr
28−optimizeDatabaseContextExpr context dbExpr = evalState (applyStaticDatabaseOptimization dbExpr) (freshDatabaseState context)
29176-- apply optimizations which merely remove steps to become no-ops: example: projection of a relation across all of its attributes => original relation
30177
31178--should optimizations offer the possibility to pure errors? If they perform the up-front type-checking, maybe so
32−applyStaticRelationalOptimization :: RelationalExpr -> RelationalExprState (Either RelationalError RelationalExpr)
33−applyStaticRelationalOptimization e@(MakeStaticRelation _ _) = pure $ Right e
179+optimizeGraphRefRelationalExpr :: GraphRefRelationalExpr -> GraphRefSOptRelationalExprM GraphRefRelationalExpr
180+optimizeGraphRefRelationalExpr e@(MakeStaticRelation _ _) = pure e
34181
35−applyStaticRelationalOptimization e@(MakeRelationFromExprs _ _) = pure $ Right e
182+optimizeGraphRefRelationalExpr e@(MakeRelationFromExprs _ _) = pure e
36183
37−applyStaticRelationalOptimization e@(ExistingRelation _) = pure $ Right e
184+optimizeGraphRefRelationalExpr e@(ExistingRelation _) = pure e
38185
39−applyStaticRelationalOptimization e@(RelationVariable _ _) = pure $ Right e
186+optimizeGraphRefRelationalExpr e@(RelationVariable _ _) = pure e
40187
41188--remove project of attributes which removes no attributes
42−applyStaticRelationalOptimization (Project attrNameSet expr) = do
43− relType <- typeForRelationalExpr expr
189+optimizeGraphRefRelationalExpr (Project attrNameSet expr) = do
190+ graph <- askGraph
191+ mctx <- askMaybeContext
192+ let relType = runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr expr)
193+ gfEnv = freshGraphRefRelationalExprEnv mctx graph
44194 case relType of
45− Left err -> pure $ Left err
195+ Left err -> throwError err
46196 Right relType2
47− | AS.all == attrNameSet ->
48− applyStaticRelationalOptimization expr
197+ | AS.all == attrNameSet ->
198+ optimizeGraphRefRelationalExpr expr
49199 | AttributeNames (attributeNames relType2) == attrNameSet ->
50− applyStaticRelationalOptimization expr
200+ optimizeGraphRefRelationalExpr expr
51201 | otherwise -> do
52− optimizedSubExpression <- applyStaticRelationalOptimization expr
53− case optimizedSubExpression of
54− Left err -> pure $ Left err
55− Right optSubExpr -> pure (Right (Project attrNameSet optSubExpr))
202+ optSubExpr <- optimizeGraphRefRelationalExpr expr
203+ pure (Project attrNameSet optSubExpr)
56204
57−applyStaticRelationalOptimization (Union exprA exprB) = do
58− eOptExprA <- applyStaticRelationalOptimization exprA
59− eOptExprB <- applyStaticRelationalOptimization exprB
60− case eOptExprA of
61− Left err -> pure $ Left err
62− Right optExprA -> case eOptExprB of
63− Left err -> pure $ Left err
64− Right optExprB -> -- (x where pred1) union (x where pred2) -> (x where pred1 or pred2)
65− case (optExprA, optExprB) of
66− (Restrict predA (RelationVariable nameA ()),
67− Restrict predB (RelationVariable nameB ())) | nameA == nameB -> pure (Right (Restrict (AndPredicate predA predB) (RelationVariable nameA ())))
205+optimizeGraphRefRelationalExpr (Union exprA exprB) = do
206+ optExprA <- optimizeGraphRefRelationalExpr exprA
207+ optExprB <- optimizeGraphRefRelationalExpr exprB
208+ -- (x where pred1) union (x where pred2) -> (x where pred1 or pred2)
209+ case (optExprA, optExprB) of
210+ (Restrict predA (RelationVariable nameA sA),
211+ Restrict predB (RelationVariable nameB sB)) | nameA == nameB && sA == sB -> pure (Restrict (AndPredicate predA predB) (RelationVariable nameA sA))
68212 _ -> if optExprA == optExprB then
69− pure (Right optExprA)
213+ pure optExprA
70214 else
71− pure $ Right $ Union optExprA optExprB
215+ pure $ Union optExprA optExprB
72216
73−applyStaticRelationalOptimization (Join exprA exprB) = do
74− eOptExprA <- applyStaticRelationalOptimization exprA
75− eOptExprB <- applyStaticRelationalOptimization exprB
76− case eOptExprA of
77− Left err -> pure $ Left err
78− Right optExprA -> case eOptExprB of
79− Left err -> pure $ Left err
80− Right optExprB ->
81− -- if the relvars to join are the same but with predicates, then just AndPredicate the predicates
82− case (optExprA, optExprB) of
83− (Restrict predA (RelationVariable nameA ()),
84− Restrict predB (RelationVariable nameB ())) | nameA == nameB -> pure (Right (Restrict (AndPredicate predA predB) (RelationVariable nameA ())))
217+optimizeGraphRefRelationalExpr (Join exprA exprB) = do
218+ optExprA <- optimizeGraphRefRelationalExpr exprA
219+ optExprB <- optimizeGraphRefRelationalExpr exprB
220+ -- if the relvars to join are the same but with predicates, then just AndPredicate the predicates
221+ case (optExprA, optExprB) of
222+ (Restrict predA (RelationVariable nameA sA),
223+ Restrict predB (RelationVariable nameB sB)) | nameA == nameB && sA == sB -> pure (Restrict (AndPredicate predA predB) (RelationVariable nameA sA))
85224 _ -> if optExprA == optExprB then --A join A == A
86− pure (Right optExprA)
225+ pure optExprA
87226 else
88− pure (Right (Join optExprA optExprB))
227+ pure (Join optExprA optExprB)
89228
90−applyStaticRelationalOptimization (Difference exprA exprB) = do
91− optExprA <- applyStaticRelationalOptimization exprA
92− optExprB <- applyStaticRelationalOptimization exprB
93− case optExprA of
94− Left err -> pure $ Left err
95− Right optExprA2 -> case optExprB of
96− Left err -> pure $ Left err
97− Right optExprB2 -> if optExprA == optExprB then do --A difference A == A where false
98− eEmptyRel <- typeForRelationalExpr optExprA2
99− case eEmptyRel of
100− Left err -> pure (Left err)
101− Right emptyRel -> pure (Right (ExistingRelation emptyRel))
102− else
103− pure $ Right (Difference optExprA2 optExprB2)
229+optimizeGraphRefRelationalExpr (Difference exprA exprB) = do
230+ graph <- askGraph
231+ context <- askMaybeContext
232+ optExprA <- optimizeGraphRefRelationalExpr exprA
233+ optExprB <- optimizeGraphRefRelationalExpr exprB
234+ if optExprA == optExprB then do --A difference A == A where false
235+ let eEmptyRel = runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr optExprA)
236+ gfEnv = freshGraphRefRelationalExprEnv context graph
237+ case eEmptyRel of
238+ Left err -> throwError err
239+ Right emptyRel -> pure (ExistingRelation emptyRel)
240+ else
241+ pure (Difference optExprA optExprB)
104242
105−applyStaticRelationalOptimization e@Rename{} = pure $ Right e
243+optimizeGraphRefRelationalExpr e@Rename{} = pure e
106244
107−applyStaticRelationalOptimization (Group oldAttrNames newAttrName expr) =
108− pure $ Right $ Group oldAttrNames newAttrName expr
245+optimizeGraphRefRelationalExpr (Group oldAttrNames newAttrName expr) =
246+ pure $ Group oldAttrNames newAttrName expr
109247
110−applyStaticRelationalOptimization (Ungroup attrName expr) =
111− pure $ Right $ Ungroup attrName expr
248+optimizeGraphRefRelationalExpr (Ungroup attrName expr) =
249+ pure $ Ungroup attrName expr
112250
113251--remove restriction of nothing
114−applyStaticRelationalOptimization (Restrict predicate expr) = do
252+optimizeGraphRefRelationalExpr (Restrict predicate expr) = do
253+ graph <- askGraph
254+ mctx <- askMaybeContext
115255 optimizedPredicate <- applyStaticPredicateOptimization predicate
116256 case optimizedPredicate of
117− Left err -> pure $ Left err
118− Right optimizedPredicate2
119− | isTrueExpr optimizedPredicate2 -> applyStaticRelationalOptimization expr -- remove predicate entirely
120− | isFalseExpr optimizedPredicate2 -> do -- replace where false predicate with empty relation with attributes from relexpr
121− attributesRel <- typeForRelationalExpr expr
257+ optimizedPredicate' | isTrueExpr optimizedPredicate' -> optimizeGraphRefRelationalExpr expr -- remove predicate entirely
258+ optimizedPredicate' | isFalseExpr optimizedPredicate' -> do -- replace where false predicate with empty relation with attributes from relexpr
259+ let attributesRel = runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr expr)
260+ gfEnv = freshGraphRefRelationalExprEnv mctx graph
122261 case attributesRel of
123− Left err -> pure $ Left err
124− Right attributesRelA -> pure $ Right $ MakeStaticRelation (attributes attributesRelA) emptyTupleSet
262+ Left err -> throwError err
263+ Right attributesRelA -> pure $ MakeStaticRelation (attributes attributesRelA) emptyTupleSet
125264 | otherwise -> do
126− optimizedSubExpression <- applyStaticRelationalOptimization expr
127− case optimizedSubExpression of
128− Left err -> pure $ Left err
129− Right optSubExpr -> pure $ Right $ Restrict optimizedPredicate2 optSubExpr
265+ optSubExpr <- optimizeGraphRefRelationalExpr expr
266+ pure $ Restrict optimizedPredicate' optSubExpr
130267
131−applyStaticRelationalOptimization e@(Equals _ _) = pure $ Right e
268+optimizeGraphRefRelationalExpr e@(Equals _ _) = pure e
132269
133−applyStaticRelationalOptimization e@(NotEquals _ _) = pure $ Right e
270+optimizeGraphRefRelationalExpr e@(NotEquals _ _) = pure e
134271
135−applyStaticRelationalOptimization e@(Extend _ _) = pure $ Right e
272+optimizeGraphRefRelationalExpr e@(Extend _ _) = pure e
136273
137−applyStaticRelationalOptimization e@(With _ _) = pure $ Right e
274+optimizeGraphRefRelationalExpr e@(With _ _) = pure e
138275
139−applyStaticDatabaseOptimization :: DatabaseContextExpr -> DatabaseState (Either RelationalError DatabaseContextExpr)
140−applyStaticDatabaseOptimization x@NoOperation = pure $ Right x
141−applyStaticDatabaseOptimization x@(Define _ _) = pure $ Right x
276+-- database context expr
277+optimizeGraphRefDatabaseContextExpr :: GraphRefDatabaseContextExpr -> GraphRefSOptDatabaseContextExprM GraphRefDatabaseContextExpr
278+optimizeGraphRefDatabaseContextExpr x@NoOperation = pure x
279+optimizeGraphRefDatabaseContextExpr x@(Define _ _) = pure x
142280
143−applyStaticDatabaseOptimization x@(Undefine _) = pure $ Right x
281+optimizeGraphRefDatabaseContextExpr x@(Undefine _) = pure x
144282
145−applyStaticDatabaseOptimization (Assign name expr) = do
146− context <- getStateContext
147− let optimizedExpr = optimizeRelationalExpr context expr
148− case optimizedExpr of
149− Left err -> pure $ Left err
150− Right optimizedExpr2 -> pure $ Right (Assign name optimizedExpr2)
283+optimizeGraphRefDatabaseContextExpr (Assign name expr) = do
284+ optExpr <- liftGraphRefRelExpr (fullOptimizeGraphRefRelationalExpr expr)
285+ pure $ Assign name optExpr
151286
152−applyStaticDatabaseOptimization (Insert targetName expr) = do
153− context <- getStateContext
154− let optimizedExpr = optimizeRelationalExpr context expr
155− case optimizedExpr of
156− Left err -> pure $ Left err
157− Right optimizedExpr2 -> if isEmptyRelationExpr optimizedExpr2 then -- if we are trying to insert an empty relation, do nothing
158− pure (Right NoOperation)
159− else
160− case optimizedExpr2 of
161− -- if the target relvar and the insert relvar are the same, there is nothing to do
162− -- insert s s -> NoOperation
163− RelationVariable insName () | insName == targetName -> pure (Right NoOperation)
164− _ -> pure $ Right (Insert targetName optimizedExpr2)
287+optimizeGraphRefDatabaseContextExpr (Insert targetName expr) = do
288+ optimizedExpr <- liftGraphRefRelExpr (fullOptimizeGraphRefRelationalExpr expr)
289+ if isEmptyRelationExpr optimizedExpr then -- if we are trying to insert an empty relation, do nothing
290+ pure NoOperation
291+ else
292+ case optimizedExpr of
293+ -- if the target relvar and the insert relvar are the same, there is nothing to do
294+ -- insert s s -> NoOperation
295+ RelationVariable insName _ | insName == targetName -> pure NoOperation
296+ _ -> pure (Insert targetName optimizedExpr)
165297
166−applyStaticDatabaseOptimization (Delete name predicate) = do
167− context <- getStateContext
168− let optimizedPredicate = runReader (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
169− case optimizedPredicate of
170− Left err -> pure $ Left err
171− Right optimizedPredicate2 -> pure $ Right (Delete name optimizedPredicate2)
298+optimizeGraphRefDatabaseContextExpr (Delete name predicate) =
299+ Delete name <$> liftGraphRefRelExpr (applyStaticPredicateOptimization predicate)
172300
173−applyStaticDatabaseOptimization (Update name upmap predicate) = do
174− context <- getStateContext
175− let optimizedPredicate = runReader (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
176− case optimizedPredicate of
177− Left err -> pure $ Left err
178− Right optimizedPredicate2 -> pure $ Right (Update name upmap optimizedPredicate2)
301+optimizeGraphRefDatabaseContextExpr (Update name upmap predicate) =
302+ Update name upmap <$> liftGraphRefRelExpr (applyStaticPredicateOptimization predicate)
179303
180−applyStaticDatabaseOptimization dep@(AddInclusionDependency _ _) = pure $ Right dep
304+optimizeGraphRefDatabaseContextExpr dep@(AddInclusionDependency _ _) = pure dep
181305
182−applyStaticDatabaseOptimization (RemoveInclusionDependency name) = pure $ Right (RemoveInclusionDependency name)
306+optimizeGraphRefDatabaseContextExpr (RemoveInclusionDependency name) = pure (RemoveInclusionDependency name)
183307
184−applyStaticDatabaseOptimization (AddNotification name triggerExpr resultOldExpr resultNewExpr) = do
185− context <- getStateContext
186− let eTriggerExprOpt = optimizeRelationalExpr context triggerExpr
187− case eTriggerExprOpt of
188− Left err -> pure $ Left err
189− Right triggerExprOpt -> --it doesn't make sense to optimize queries when we don't have their proper contexts
190− pure (Right (AddNotification name triggerExprOpt resultOldExpr resultNewExpr))
308+optimizeGraphRefDatabaseContextExpr (AddNotification name triggerExpr resultOldExpr resultNewExpr) =
309+ --we can't optimize these expressions until they run
310+ pure (AddNotification name triggerExpr resultOldExpr resultNewExpr)
191311
192−applyStaticDatabaseOptimization notif@(RemoveNotification _) = pure (Right notif)
312+optimizeGraphRefDatabaseContextExpr notif@(RemoveNotification _) = pure notif
193313
194−applyStaticDatabaseOptimization c@(AddTypeConstructor _ _) = pure (Right c)
195−applyStaticDatabaseOptimization c@(RemoveTypeConstructor _) = pure (Right c)
196−applyStaticDatabaseOptimization c@(RemoveAtomFunction _) = pure (Right c)
197−applyStaticDatabaseOptimization c@(RemoveDatabaseContextFunction _) = pure (Right c)
198−applyStaticDatabaseOptimization c@(ExecuteDatabaseContextFunction _ _) = pure (Right c)
314+optimizeGraphRefDatabaseContextExpr c@(AddTypeConstructor _ _) = pure c
315+optimizeGraphRefDatabaseContextExpr c@(RemoveTypeConstructor _) = pure c
316+optimizeGraphRefDatabaseContextExpr c@(RemoveAtomFunction _) = pure c
317+optimizeGraphRefDatabaseContextExpr c@(RemoveDatabaseContextFunction _) = pure c
318+optimizeGraphRefDatabaseContextExpr c@(ExecuteDatabaseContextFunction _ _) = pure c
199319
200320--optimization: from pgsql lists- check for join condition referencing foreign key- if join projection project away the referenced table, then it does not need to be scanned
201321
202322--applyStaticDatabaseOptimization (MultipleExpr exprs) = pure $ Right $ MultipleExpr exprs
203323--for multiple expressions, we must evaluate
204−applyStaticDatabaseOptimization (MultipleExpr exprs) = do
205− context <- getStateContext
206− let optExprs = evalState substateRunner (contextWithEmptyTupleSets context, M.empty, False)
207− let errors = lefts optExprs
208− if not (null errors) then
209− pure $ Left (head errors)
210− else
211− pure $ Right $ MultipleExpr (rights optExprs)
212− where
213− substateRunner = forM exprs $ \expr -> do
214− --a previous expression could create a relvar, we don't want to miss it, so we clear the tuples and execute the expression to get an empty relation in the relvar
215− _ <- evalDatabaseContextExpr expr
216− applyStaticDatabaseOptimization expr
217− --this error handling could be improved with some lifting presumably
218− --restore original context
219−
220−applyStaticPredicateOptimization :: RestrictionPredicateExpr -> RelationalExprState (Either RelationalError RestrictionPredicateExpr)
324+optimizeGraphRefDatabaseContextExpr (MultipleExpr exprs) = do
325+ --a previous expression in the exprs list could create a relvar; we don't want to miss it, so we clear the tuples and execute the expression to get an empty relation in the relvar
326+ context <- askContext
327+ graph <- askGraph
328+ parentId <- askTransId
329+
330+ let emptyRvs ctx = ctx { relationVariables = mkEmptyRelVars (relationVariables ctx) }
331+ dbcEnv = mkDatabaseContextEvalEnv parentId graph
332+ folder (ctx, expracc) expr = do
333+ --optimize the expr and run it against empty relvars to add it to the context
334+ case runGraphRefSOptDatabaseContextExprM parentId ctx graph (optimizeGraphRefDatabaseContextExpr expr) of
335+ Left err -> throwError err
336+ Right optExpr ->
337+ case runDatabaseContextEvalMonad ctx dbcEnv (evalGraphRefDatabaseContextExpr optExpr) of
338+ Left err -> throwError err
339+ Right dbcState ->
340+ pure (emptyRvs $ dbc_context dbcState, expracc ++ [optExpr])
341+
342+ (_, exprs') <- foldM folder (context,[]) exprs
343+ pure (MultipleExpr exprs')
344+
345+applyStaticPredicateOptimization :: GraphRefRestrictionPredicateExpr -> GraphRefSOptRelationalExprM GraphRefRestrictionPredicateExpr
221346applyStaticPredicateOptimization predi = do
222− eOptPred <- case predi of
347+ optPred <- case predi of
223348-- where x and x => where x
224349 AndPredicate pred1 pred2 -> do
225− eOptPred1 <- applyStaticPredicateOptimization pred1
226− case eOptPred1 of
227− Left err -> pure (Left err)
228− Right optPred1 -> do
229− eOptPred2 <- applyStaticPredicateOptimization pred2
230− case eOptPred2 of
231− Left err -> pure (Left err)
232− Right optPred2 ->
233− if optPred1 == optPred2 then
234− pure (Right optPred1)
235− else
236− pure (Right (AndPredicate optPred1 optPred2))
350+ optPredA <- applyStaticPredicateOptimization pred1
351+ optPredB <- applyStaticPredicateOptimization pred2
352+ if optPredA == optPredB then
353+ pure optPredA
354+ else
355+ pure (AndPredicate optPredA optPredB)
237356-- where x or x => where x
238357 OrPredicate pred1 pred2 -> do
239− eOptPred1 <- applyStaticPredicateOptimization pred1
240− case eOptPred1 of
241− Left err -> pure (Left err)
242− Right optPred1 -> do
243− eOptPred2 <- applyStaticPredicateOptimization pred2
244− case eOptPred2 of
245− Left err -> pure (Left err)
246− Right optPred2 | optPred1 == optPred2 -> pure (Right optPred1)
247− | isTrueExpr optPred1 -> pure (Right optPred1) -- True or x -> True
248− | isTrueExpr optPred2 -> pure (Right optPred2)
249− | otherwise -> pure (Right (OrPredicate optPred1 optPred2))
358+ optPredA <- applyStaticPredicateOptimization pred1
359+ optPredB <- applyStaticPredicateOptimization pred2
360+ if (optPredA == optPredB) || isTrueExpr optPredA then
361+ pure optPredA
362+ else if isTrueExpr optPredB then
363+ pure optPredB
364+ else
365+ pure (OrPredicate optPredA optPredB)
250366 AttributeEqualityPredicate attrNameA (AttributeAtomExpr attrNameB) ->
251367 if attrNameA == attrNameB then
252− pure (Right TruePredicate)
368+ pure TruePredicate
253369 else
254− pure (Right predi)
255− AttributeEqualityPredicate{} -> pure (Right predi)
256− TruePredicate -> pure $ Right predi
257− NotPredicate{} -> pure $ Right predi
258− RelationalExprPredicate{} -> pure (Right predi)
259− AtomExprPredicate{} -> pure (Right predi)
260− case eOptPred of
261− Left err -> pure (Left err)
262− Right optPred ->
263− let attrMap = findStaticRestrictionPredicates optPred in
264− pure (Right (replaceStaticAtomExprs optPred attrMap))
370+ pure predi
371+ AttributeEqualityPredicate{} -> pure predi
372+ TruePredicate -> pure predi
373+ NotPredicate{} -> pure predi
374+ RelationalExprPredicate{} -> pure predi
375+ AtomExprPredicate{} -> pure predi
376+ let attrMap = findStaticRestrictionPredicates optPred
377+ pure (replaceStaticAtomExprs optPred attrMap)
265378
266379--determines if an atom expression is tautologically true
267−isTrueExpr :: RestrictionPredicateExpr -> Bool
380+isTrueExpr :: RestrictionPredicateExprBase a -> Bool
268381isTrueExpr TruePredicate = True
269382isTrueExpr (AtomExprPredicate (NakedAtomExpr (BoolAtom True))) = True
270383isTrueExpr _ = False
271384
272385--determines if an atom expression is tautologically false
273−isFalseExpr :: RestrictionPredicateExpr -> Bool
386+isFalseExpr :: RestrictionPredicateExprBase a -> Bool
274387isFalseExpr (NotPredicate expr) = isTrueExpr expr
275388isFalseExpr (AtomExprPredicate (NakedAtomExpr (BoolAtom False))) = True
276389isFalseExpr _ = False
277390
278391-- determine if the created relation can statically be determined to be empty
279−isEmptyRelationExpr :: RelationalExpr -> Bool
280−isEmptyRelationExpr (MakeRelationFromExprs _ []) = True
392+isEmptyRelationExpr :: RelationalExprBase a -> Bool
393+isEmptyRelationExpr (MakeRelationFromExprs _ (TupleExprs _ [])) = True
281394isEmptyRelationExpr (MakeStaticRelation _ tupSet) = null (asList tupSet)
282395isEmptyRelationExpr (ExistingRelation rel) = rel == emptyRelationWithAttrs (attributes rel)
283396isEmptyRelationExpr _ = False
284397
285398--transitive static variable optimization
286−replaceStaticAtomExprs :: RestrictionPredicateExpr -> M.Map AttributeName AtomExpr -> RestrictionPredicateExpr
399+replaceStaticAtomExprs :: GraphRefRestrictionPredicateExpr -> M.Map AttributeName GraphRefAtomExpr -> GraphRefRestrictionPredicateExpr
287400replaceStaticAtomExprs predIn replaceMap = case predIn of
288401 AttributeEqualityPredicate newAttrName (AttributeAtomExpr matchName) -> case M.lookup matchName replaceMap of
289402 Nothing -> predIn
@@ -296,7 +409,7 @@ replaceStaticAtomExprs predIn replaceMap = case predIn of
296409 RelationalExprPredicate{} -> predIn
297410 AtomExprPredicate{} -> predIn
298411-- used for transitive attribute optimization- only works on statically-determined atoms for now- in the future, this could work for all AtomExprs which don't reference attributes
299−findStaticRestrictionPredicates :: RestrictionPredicateExpr -> M.Map AttributeName AtomExpr
412+findStaticRestrictionPredicates :: GraphRefRestrictionPredicateExpr -> M.Map AttributeName GraphRefAtomExpr
300413findStaticRestrictionPredicates (AttributeEqualityPredicate attrName atomExpr) =
301414 case atomExpr of
302415 val@NakedAtomExpr{} -> M.singleton attrName val
@@ -320,60 +433,65 @@ isStaticAtomExpr FunctionAtomExpr{} = False
320433isStaticAtomExpr RelationAtomExpr{} = False
321434
322435--if the projection of a join only uses the attributes from one of the expressions and there is a foreign key relationship between the expressions, we know that the join is inconsequential and can be removed
323−applyStaticJoinElimination :: RelationalExpr -> RelationalExprState (Either RelationalError RelationalExpr)
436+applyStaticJoinElimination :: GraphRefRelationalExpr -> GraphRefSOptRelationalExprM GraphRefRelationalExpr
324437applyStaticJoinElimination expr@(Project attrNameSet (Join exprA exprB)) = do
325− relState <- ask
326− eProjType <- typeForRelationalExpr expr
327− eTypeA <- typeForRelationalExpr exprA
328− eTypeB <- typeForRelationalExpr exprB
329− case eProjType of
330− Left err -> pure (Left err)
331− Right projType ->
332− case eTypeA of
333− Left err -> pure (Left err)
334− Right typeA ->
335− case eTypeB of
336− Left err -> pure (Left err)
337− Right typeB -> do
338− let matchesProjectionAttributes
339− | attrNames projType `S.isSubsetOf` attrNames typeA =
340− Just ((exprA, typeA), (exprB, typeB))
341− | attrNames projType `S.isSubsetOf` attrNames typeB =
342− Just ((exprB, typeB), (exprA, typeA))
343− | otherwise =
344− Nothing
345− attrNames = A.attributeNameSet . attributes
346− case matchesProjectionAttributes of
347− Nothing -> -- this optimization does not apply
348− pure (Right expr)
349− Just ((joinedExpr, joinedType), (unjoinedExpr, _)) -> do
350− --scan inclusion dependencies for a foreign key relationship
351− incDeps <- inclusionDependencies . stateElemsContext <$> ask
352− let fkConstraint = foldM isFkConstraint False incDeps
353− --search for matching fk constraint
354− isFkConstraint acc (InclusionDependency (Project subattrNames subrv) (Project _ superrv)) =
355− case runReader (evalAttributeNames subattrNames expr) relState of
356− Left _ -> pure acc
357− Right subAttrNameSet ->
358− pure (acc || (joinedExpr == subrv &&
359− unjoinedExpr == superrv &&
360− -- the fk attribute is one of the projection attributes
361− A.attributeNamesContained subAttrNameSet (A.attributeNameSet (attributes joinedType))
438+ graph <- askGraph
439+ case inSameTransaction exprA exprB of
440+ -- the sub exprs are in different transactions or none at all, so we cannot extract inclusion dependencies across transaction boundaries
441+ Nothing -> pure expr
442+ Just marker -> do
443+ commonContext <- case marker of
444+ UncommittedContextMarker -> askContext
445+ TransactionMarker tid -> concreteDatabaseContext <$> lift (except (transactionForId tid graph))
446+ let typeForExpr e = lift $ except $ runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr e)
447+ gfEnv = freshGraphRefRelationalExprEnv (Just commonContext) graph
448+
449+ projType <- typeForExpr expr
450+ typeA <- typeForExpr exprA
451+ typeB <- typeForExpr exprB
452+ let matchesProjectionAttributes
453+ | attrNames projType `S.isSubsetOf` attrNames typeA =
454+ Just ((exprA, typeA), (exprB, typeB))
455+ | attrNames projType `S.isSubsetOf` attrNames typeB =
456+ Just ((exprB, typeB), (exprA, typeA))
457+ | otherwise =
458+ Nothing
459+ attrNames = A.attributeNameSet . attributes
460+ case matchesProjectionAttributes of
461+ Nothing -> -- this optimization does not apply
462+ pure expr
463+ Just ((joinedExpr, joinedType), (unjoinedExpr, _)) -> do
464+ --lookup transaction
465+ --scan inclusion dependencies for a foreign key relationship
466+ let incDeps = inclusionDependencies commonContext
467+ fkConstraint = foldM isFkConstraint False incDeps
468+ --search for matching fk constraint
469+ isFkConstraint acc (InclusionDependency (Project subAttrNames subrv) (Project _ superrv)) = do
470+ let gfSubAttrNames = processM (processAttributeNames subAttrNames)
471+ gfSubRv = processM (processRelationalExpr subrv)
472+ gfSuperRv = processM (processRelationalExpr superrv)
473+ processM = runProcessExprM marker
474+ case runGraphRefRelationalExprM gfEnv (evalGraphRefAttributeNames gfSubAttrNames expr) of
475+ Left _ -> pure acc
476+ Right subAttrNameSet ->
477+ pure (acc || (joinedExpr == gfSubRv &&
478+ unjoinedExpr == gfSuperRv &&
479+ -- the fk attribute is one of the projection attributes
480+ A.attributeNamesContained subAttrNameSet (A.attributeNameSet (attributes joinedType))
362481 ))
363− isFkConstraint acc _ = pure acc
364− case fkConstraint of
365− Right True -> --join elimination optimization applies
366− applyStaticRelationalOptimization (Project attrNameSet joinedExpr)
367− Right False -> --join elimination optimization does not apply
368− pure (Right expr)
369− Left err ->
370− pure (Left err)
482+ isFkConstraint acc _ = pure acc
483+ case fkConstraint of
484+ Right True -> --join elimination optimization applies
485+ optimizeGraphRefRelationalExpr (Project attrNameSet joinedExpr)
486+ Right False -> --join elimination optimization does not apply
487+ pure expr
488+ Left err -> throwError err
371489
372−applyStaticJoinElimination expr = pure (Right expr)
490+applyStaticJoinElimination expr = pure expr
373491
374492--restriction collapse converts chained restrictions into (Restrict (And pred1 pred2 pred3...))
375493 --this optimization should be fairly uncontroversial- performing a tuple scan once is cheaper than twice- parallelization can still take place
376−applyStaticRestrictionCollapse :: RelationalExpr -> RelationalExpr
494+applyStaticRestrictionCollapse :: GraphRefRelationalExpr -> GraphRefRelationalExpr
377495applyStaticRestrictionCollapse expr =
378496 case expr of
379497 MakeRelationFromExprs _ _ -> expr
@@ -410,14 +528,14 @@ applyStaticRestrictionCollapse expr =
410528 andPreds = foldr (\(Restrict subpred _) acc -> AndPredicate acc subpred) firstPred (tail restrictions) in
411529 Restrict andPreds optFinalExpr
412530
413−sequentialRestrictions :: RelationalExpr -> [RelationalExpr]
531+sequentialRestrictions :: RelationalExprBase a -> [RelationalExprBase a]
414532sequentialRestrictions expr@(Restrict _ subexpr) = expr:sequentialRestrictions subexpr
415533sequentialRestrictions _ = []
416534
417535--restriction pushdown only really makes sense for tuple-oriented storage schemes where performing a restriction before projection can cut down on the intermediate storage needed to store the data before the projection
418536-- x{proj} where c1 -> (x where c1){proj} #project on fewer tuples
419537-- (x union y) where c -> (x where c) union (y where c) #with a selective restriction, fewer tuples will need to be joined
420−applyStaticRestrictionPushdown :: RelationalExpr -> RelationalExpr
538+applyStaticRestrictionPushdown :: GraphRefRelationalExpr -> GraphRefRelationalExpr
421539applyStaticRestrictionPushdown expr = case expr of
422540 MakeRelationFromExprs _ _ -> expr
423541 MakeStaticRelation _ _ -> expr
@@ -454,4 +572,6 @@ applyStaticRestrictionPushdown expr = case expr of
454572 Extend n sub ->
455573 Extend n (applyStaticRestrictionPushdown sub)
456574
457−
575+-- no optimizations available
576+optimizeDatabaseContextIOExpr :: GraphRefDatabaseContextIOExpr -> GraphRefSOptDatabaseContextExprM GraphRefDatabaseContextIOExpr
577+optimizeDatabaseContextIOExpr = pure
src/lib/ProjectM36/TransGraphRelationalExpression.hs+137−122
@@ -4,13 +4,12 @@
44module ProjectM36.TransGraphRelationalExpression where
55import ProjectM36.Base
66import ProjectM36.TransactionGraph
7−import ProjectM36.Transaction
8−import ProjectM36.RelationalExpression
97import ProjectM36.Error
10−import ProjectM36.Tuple
11−import ProjectM36.AtomType
128import qualified Data.Map as M
139import Control.Monad.Trans.Reader
10+import Control.Monad.Trans.Class
11+import Control.Monad.Trans.Except
12+import Data.Functor.Identity
1413import Data.Binary
1514
1615-- | The TransGraphRelationalExpression is equivalent to a relational expression except that relation variables can reference points in the transaction graph (at previous points in time).
@@ -30,6 +29,10 @@ type TransGraphTupleExpr = TupleExprBase TransactionIdLookup
3029
3130instance Binary TransGraphTupleExpr
3231
32+type TransGraphTupleExprs = TupleExprsBase TransactionIdLookup
33+
34+instance Binary TransGraphTupleExprs
35+
3336type TransGraphRestrictionPredicateExpr = RestrictionPredicateExprBase TransactionIdLookup
3437
3538instance Binary TransGraphRestrictionPredicateExpr
@@ -40,143 +43,155 @@ instance Binary TransGraphAtomExpr
4043
4144type TransGraphAttributeExpr = AttributeExprBase TransactionIdLookup
4245
46+type TransGraphWithNameExpr = WithNameExprBase TransactionIdLookup
47+
48+instance Binary TransGraphWithNameExpr
49+
50+newtype TransGraphEvalEnv = TransGraphEvalEnv {
51+ tge_graph :: TransactionGraph
52+ }
53+
54+type TransGraphEvalMonad a = ReaderT TransGraphEvalEnv (ExceptT RelationalError Identity) a
55+
56+process :: TransGraphEvalEnv -> TransGraphRelationalExpr -> Either RelationalError GraphRefRelationalExpr
57+process env texpr = runIdentity (runExceptT (runReaderT (processTransGraphRelationalExpr texpr) env))
58+
59+liftE :: Either RelationalError a -> TransGraphEvalMonad a
60+liftE = lift . except
61+
62+askGraph :: TransGraphEvalMonad TransactionGraph
63+askGraph = tge_graph <$> ask
64+
65+findTransId :: TransactionIdLookup -> TransGraphEvalMonad GraphRefTransactionMarker
66+findTransId tlook = TransactionMarker . transactionId <$> findTrans tlook
67+
68+findTrans :: TransactionIdLookup -> TransGraphEvalMonad Transaction
69+findTrans tlook = do
70+ graph <- askGraph
71+ liftE $ lookupTransaction graph tlook
72+
4373-- OUTDATED a previous attempt at this function attempted to convert TransGraphRelationalExpr to RelationalExpr by resolving the transaction lookups. However, there is no way to resolve a FunctionAtomExpr to an Atom without fully evaluating the higher levels (TupleExpr, etc.). An anonymous function expression cannot be serialized, so that workaround is out. Still, I suspect we can reuse the existing static optimizer logic to work on both structures. The current conversion reduces the chance of whole-query optimization due to full-evaluation on top of full-evaluation, so this function would benefit from some re-design.
44−evalTransGraphRelationalExpr :: TransGraphRelationalExpr -> TransactionGraph -> Either RelationalError RelationalExpr
45−evalTransGraphRelationalExpr (MakeRelationFromExprs mAttrExprs tupleExprs) graph = do
46− tupleExprs' <- mapM (evalTransGraphTupleExpr graph) tupleExprs
74+processTransGraphRelationalExpr :: TransGraphRelationalExpr -> TransGraphEvalMonad GraphRefRelationalExpr
75+processTransGraphRelationalExpr (MakeRelationFromExprs mAttrExprs tupleExprs) = do
76+ tupleExprs' <- processTransGraphTupleExprs tupleExprs
4777 case mAttrExprs of
4878 Nothing -> pure (MakeRelationFromExprs Nothing tupleExprs')
4979 Just attrExprs -> do
50− attrExprs' <- mapM (evalTransGraphAttributeExpr graph) attrExprs
80+ attrExprs' <- mapM processTransGraphAttributeExpr attrExprs
5181 pure (MakeRelationFromExprs (Just attrExprs') tupleExprs')
52−evalTransGraphRelationalExpr (MakeStaticRelation attrs tupSet) _ = pure (MakeStaticRelation attrs tupSet)
53−evalTransGraphRelationalExpr (ExistingRelation rel) _ = pure (ExistingRelation rel)
54−evalTransGraphRelationalExpr (RelationVariable rvname transLookup) graph = do
55− trans <- lookupTransaction graph transLookup
56− rel <- runReader (evalRelationalExpr (RelationVariable rvname ())) (RelationalExprStateElems (concreteDatabaseContext trans))
57− pure (ExistingRelation rel)
58−evalTransGraphRelationalExpr (Project transAttrNames expr) graph = do
59− expr' <- evalTransGraphRelationalExpr expr graph
60− attrNames <- evalTransAttributeNames transAttrNames graph
61− pure (Project attrNames expr')
62−evalTransGraphRelationalExpr (Union exprA exprB) graph = do
63− exprA' <- evalTransGraphRelationalExpr exprA graph
64− exprB' <- evalTransGraphRelationalExpr exprB graph
65− pure (Union exprA' exprB')
66−evalTransGraphRelationalExpr (Join exprA exprB) graph = do
67− exprA' <- evalTransGraphRelationalExpr exprA graph
68− exprB' <- evalTransGraphRelationalExpr exprB graph
69− pure (Join exprA' exprB')
70−evalTransGraphRelationalExpr (Rename attrName1 attrName2 expr) graph = do
71− let expr' = evalTransGraphRelationalExpr expr graph
72− Rename attrName1 attrName2 <$> expr'
73−evalTransGraphRelationalExpr (Difference exprA exprB) graph = do
74− exprA' <- evalTransGraphRelationalExpr exprA graph
75− exprB' <- evalTransGraphRelationalExpr exprB graph
76− pure (Difference exprA' exprB')
77−evalTransGraphRelationalExpr (Group transAttrNames attrName expr) graph = do
78− expr' <- evalTransGraphRelationalExpr expr graph
79− attrNames <- evalTransAttributeNames transAttrNames graph
80− pure (Group attrNames attrName expr')
81−evalTransGraphRelationalExpr (Ungroup attrName expr) graph = do
82− let expr' = evalTransGraphRelationalExpr expr graph
83− Ungroup attrName <$> expr'
84−evalTransGraphRelationalExpr (Restrict predicateExpr expr) graph = do
85− expr' <- evalTransGraphRelationalExpr expr graph
86− predicateExpr' <- evalTransGraphRestrictionPredicateExpr predicateExpr graph
87− pure (Restrict predicateExpr' expr')
88−evalTransGraphRelationalExpr (Equals exprA exprB) graph = do
89− exprA' <- evalTransGraphRelationalExpr exprA graph
90− exprB' <- evalTransGraphRelationalExpr exprB graph
82+processTransGraphRelationalExpr (MakeStaticRelation attrs tupSet) = pure (MakeStaticRelation attrs tupSet)
83+processTransGraphRelationalExpr (ExistingRelation rel) = pure (ExistingRelation rel)
84+processTransGraphRelationalExpr (RelationVariable rvname transLookup) =
85+ RelationVariable rvname <$> findTransId transLookup
86+processTransGraphRelationalExpr (Project transAttrNames expr) =
87+ Project <$> processTransGraphAttributeNames transAttrNames <*> processTransGraphRelationalExpr expr
88+processTransGraphRelationalExpr (Union exprA exprB) =
89+ Union <$> processTransGraphRelationalExpr exprA <*> processTransGraphRelationalExpr exprB
90+processTransGraphRelationalExpr (Join exprA exprB) =
91+ Join <$> processTransGraphRelationalExpr exprA <*> processTransGraphRelationalExpr exprB
92+processTransGraphRelationalExpr (Rename attrName1 attrName2 expr) =
93+ Rename attrName1 attrName2 <$> processTransGraphRelationalExpr expr
94+processTransGraphRelationalExpr (Difference exprA exprB) =
95+ Difference <$> processTransGraphRelationalExpr exprA <*> processTransGraphRelationalExpr exprB
96+processTransGraphRelationalExpr (Group transAttrNames attrName expr) =
97+ Group <$>
98+ processTransGraphAttributeNames transAttrNames <*>
99+ pure attrName <*>
100+ processTransGraphRelationalExpr expr
101+processTransGraphRelationalExpr (Ungroup attrName expr) =
102+ Ungroup attrName <$> processTransGraphRelationalExpr expr
103+processTransGraphRelationalExpr (Restrict predicateExpr expr) =
104+ Restrict <$> evalTransGraphRestrictionPredicateExpr predicateExpr <*>
105+ processTransGraphRelationalExpr expr
106+processTransGraphRelationalExpr (Equals exprA exprB) = do
107+ exprA' <- processTransGraphRelationalExpr exprA
108+ exprB' <- processTransGraphRelationalExpr exprB
91109 pure (Equals exprA' exprB')
92−evalTransGraphRelationalExpr (NotEquals exprA exprB) graph = do
93− exprA' <- evalTransGraphRelationalExpr exprA graph
94− exprB' <- evalTransGraphRelationalExpr exprB graph
110+processTransGraphRelationalExpr (NotEquals exprA exprB) = do
111+ exprA' <- processTransGraphRelationalExpr exprA
112+ exprB' <- processTransGraphRelationalExpr exprB
95113 pure (NotEquals exprA' exprB')
96−evalTransGraphRelationalExpr (Extend extendExpr expr) graph = do
97− extendExpr' <- evalTransGraphExtendTupleExpr extendExpr graph
98− expr' <- evalTransGraphRelationalExpr expr graph
114+processTransGraphRelationalExpr (Extend extendExpr expr) = do
115+ extendExpr' <- processTransGraphExtendTupleExpr extendExpr
116+ expr' <- processTransGraphRelationalExpr expr
99117 pure (Extend extendExpr' expr')
100−evalTransGraphRelationalExpr (With views expr) graph = do
101− evaldViews <- mapM (\(vname, vexpr) -> do
102− vexpr' <- evalTransGraphRelationalExpr vexpr graph
103− pure (vname, vexpr')
118+processTransGraphRelationalExpr (With views expr) = do
119+ evaldViews <- mapM (\(wnexpr, vexpr) -> do
120+ wnexpr' <- processTransGraphWithNameExpr wnexpr
121+ vexpr' <- processTransGraphRelationalExpr vexpr
122+ pure (wnexpr', vexpr')
104123 ) views
105− expr' <- evalTransGraphRelationalExpr expr graph
124+ expr' <- processTransGraphRelationalExpr expr
106125 pure (With evaldViews expr')
107−
108−evalTransGraphTupleExpr :: TransactionGraph -> TransGraphTupleExpr -> Either RelationalError TupleExpr
109−evalTransGraphTupleExpr graph (TupleExpr attrMap) = do
126+
127+processTransGraphTupleExprs :: TransGraphTupleExprs -> TransGraphEvalMonad GraphRefTupleExprs
128+processTransGraphTupleExprs (TupleExprs marker texprs) =
129+ TupleExprs <$> findTransId marker <*> mapM processTransGraphTupleExpr texprs
130+
131+processTransGraphTupleExpr :: TransGraphTupleExpr -> TransGraphEvalMonad GraphRefTupleExpr
132+processTransGraphTupleExpr (TupleExpr attrMap) = do
110133 let attrAssoc = mapM (\(attrName, atomExpr) -> do
111− aExpr <- evalTransGraphAtomExpr graph atomExpr
134+ aExpr <- processTransGraphAtomExpr atomExpr
112135 pure (attrName, aExpr)
113136 ) (M.toList attrMap)
114137 TupleExpr . M.fromList <$> attrAssoc
115138
116−evalTransGraphAtomExpr :: TransactionGraph -> TransGraphAtomExpr -> Either RelationalError AtomExpr
117−evalTransGraphAtomExpr _ (AttributeAtomExpr aname) = pure $ AttributeAtomExpr aname
118−evalTransGraphAtomExpr _ (NakedAtomExpr atom) = pure $ NakedAtomExpr atom
119−evalTransGraphAtomExpr graph (FunctionAtomExpr funcName args tLookup) = do
120− trans <- lookupTransaction graph tLookup
121− --I can't return a FunctionAtomExpr because the function needs to be resolved at a specific context
122− args' <- mapM (evalTransGraphAtomExpr graph) args
123− atom <- runReader (evalAtomExpr emptyTuple (FunctionAtomExpr funcName args' ())) (RelationalExprStateElems (concreteDatabaseContext trans))
124− pure (NakedAtomExpr atom)
125−evalTransGraphAtomExpr graph (RelationAtomExpr expr) = do
126− let expr' = evalTransGraphRelationalExpr expr graph
127− RelationAtomExpr <$> expr'
128−evalTransGraphAtomExpr graph (ConstructedAtomExpr dConsName args tLookup) = do
129− trans <- lookupTransaction graph tLookup
130− args' <- mapM (evalTransGraphAtomExpr graph) args
131− atom <- runReader (evalAtomExpr emptyTuple (ConstructedAtomExpr dConsName args' ())) (RelationalExprStateElems (concreteDatabaseContext trans))
132− pure (NakedAtomExpr atom)
133−
134−evalTransGraphRestrictionPredicateExpr :: TransGraphRestrictionPredicateExpr -> TransactionGraph -> Either RelationalError RestrictionPredicateExpr
135−evalTransGraphRestrictionPredicateExpr TruePredicate _ = pure TruePredicate
136−evalTransGraphRestrictionPredicateExpr (AndPredicate exprA exprB) graph = do
137− exprA' <- evalTransGraphRestrictionPredicateExpr exprA graph
138− exprB' <- evalTransGraphRestrictionPredicateExpr exprB graph
139+processTransGraphAtomExpr :: TransGraphAtomExpr -> TransGraphEvalMonad GraphRefAtomExpr
140+processTransGraphAtomExpr (AttributeAtomExpr aname) = pure $ AttributeAtomExpr aname
141+processTransGraphAtomExpr (NakedAtomExpr atom) = pure $ NakedAtomExpr atom
142+processTransGraphAtomExpr (FunctionAtomExpr funcName args tLookup) =
143+ FunctionAtomExpr funcName <$> mapM processTransGraphAtomExpr args <*> findTransId tLookup
144+processTransGraphAtomExpr (RelationAtomExpr expr) =
145+ RelationAtomExpr <$> processTransGraphRelationalExpr expr
146+processTransGraphAtomExpr (ConstructedAtomExpr dConsName args tLookup) =
147+ ConstructedAtomExpr dConsName <$> mapM processTransGraphAtomExpr args <*> findTransId tLookup
148+evalTransGraphRestrictionPredicateExpr :: TransGraphRestrictionPredicateExpr -> TransGraphEvalMonad GraphRefRestrictionPredicateExpr
149+evalTransGraphRestrictionPredicateExpr TruePredicate = pure TruePredicate
150+evalTransGraphRestrictionPredicateExpr (AndPredicate exprA exprB) = do
151+ exprA' <- evalTransGraphRestrictionPredicateExpr exprA
152+ exprB' <- evalTransGraphRestrictionPredicateExpr exprB
139153 pure (AndPredicate exprA' exprB')
140−evalTransGraphRestrictionPredicateExpr (OrPredicate exprA exprB) graph = do
141− exprA' <- evalTransGraphRestrictionPredicateExpr exprA graph
142− exprB' <- evalTransGraphRestrictionPredicateExpr exprB graph
154+evalTransGraphRestrictionPredicateExpr (OrPredicate exprA exprB) = do
155+ exprA' <- evalTransGraphRestrictionPredicateExpr exprA
156+ exprB' <- evalTransGraphRestrictionPredicateExpr exprB
143157 pure (OrPredicate exprA' exprB')
144−evalTransGraphRestrictionPredicateExpr (NotPredicate expr) graph = do
145− let expr' = evalTransGraphRestrictionPredicateExpr expr graph
158+evalTransGraphRestrictionPredicateExpr (NotPredicate expr) = do
159+ let expr' = evalTransGraphRestrictionPredicateExpr expr
146160 NotPredicate <$> expr'
147−evalTransGraphRestrictionPredicateExpr (RelationalExprPredicate expr) graph = do
148− let expr' = evalTransGraphRelationalExpr expr graph
161+evalTransGraphRestrictionPredicateExpr (RelationalExprPredicate expr) = do
162+ let expr' = processTransGraphRelationalExpr expr
149163 RelationalExprPredicate <$> expr'
150−evalTransGraphRestrictionPredicateExpr (AtomExprPredicate expr) graph = do
151− let expr' = evalTransGraphAtomExpr graph expr
164+evalTransGraphRestrictionPredicateExpr (AtomExprPredicate expr) = do
165+ let expr' = processTransGraphAtomExpr expr
152166 AtomExprPredicate <$> expr'
153−evalTransGraphRestrictionPredicateExpr (AttributeEqualityPredicate attrName expr) graph = do
154− let expr' = evalTransGraphAtomExpr graph expr
167+evalTransGraphRestrictionPredicateExpr (AttributeEqualityPredicate attrName expr) = do
168+ let expr' = processTransGraphAtomExpr expr
155169 AttributeEqualityPredicate attrName <$> expr'
156170
157−evalTransGraphExtendTupleExpr :: TransGraphExtendTupleExpr -> TransactionGraph -> Either RelationalError ExtendTupleExpr
158−evalTransGraphExtendTupleExpr (AttributeExtendTupleExpr attrName expr) graph = do
159− let expr' = evalTransGraphAtomExpr graph expr
160− AttributeExtendTupleExpr attrName <$> expr'
161−
162−evalTransGraphAttributeExpr :: TransactionGraph -> TransGraphAttributeExpr -> Either RelationalError AttributeExpr
163−evalTransGraphAttributeExpr graph (AttributeAndTypeNameExpr attrName tCons tLookup) = do
164− trans <- lookupTransaction graph tLookup
165− aType <- atomTypeForTypeConstructor tCons (typeConstructorMapping (concreteDatabaseContext trans)) M.empty
166− pure (NakedAttributeExpr (Attribute attrName aType))
167−evalTransGraphAttributeExpr _ (NakedAttributeExpr attr) = pure (NakedAttributeExpr attr)
168−
169−evalTransAttributeNames :: TransGraphAttributeNames -> TransactionGraph -> Either RelationalError AttributeNames
170−evalTransAttributeNames (AttributeNames names) _ = Right (AttributeNames names)
171−evalTransAttributeNames (InvertedAttributeNames names) _ = Right (InvertedAttributeNames names)
172−evalTransAttributeNames (UnionAttributeNames namesA namesB) graph = do
173− nA <- evalTransAttributeNames namesA graph
174− nB <- evalTransAttributeNames namesB graph
175− Right (UnionAttributeNames nA nB)
176−evalTransAttributeNames (IntersectAttributeNames namesA namesB) graph = do
177− nA <- evalTransAttributeNames namesA graph
178− nB <- evalTransAttributeNames namesB graph
179− Right (IntersectAttributeNames nA nB)
180−evalTransAttributeNames (RelationalExprAttributeNames expr) graph = do
181− evaldExpr <- evalTransGraphRelationalExpr expr graph
182− Right (RelationalExprAttributeNames evaldExpr)
171+processTransGraphExtendTupleExpr :: TransGraphExtendTupleExpr -> TransGraphEvalMonad GraphRefExtendTupleExpr
172+processTransGraphExtendTupleExpr (AttributeExtendTupleExpr attrName expr) =
173+ AttributeExtendTupleExpr attrName <$> processTransGraphAtomExpr expr
174+
175+processTransGraphAttributeExpr :: TransGraphAttributeExpr -> TransGraphEvalMonad GraphRefAttributeExpr
176+processTransGraphAttributeExpr (AttributeAndTypeNameExpr attrName tCons tLookup) =
177+ AttributeAndTypeNameExpr attrName tCons <$> findTransId tLookup
178+processTransGraphAttributeExpr (NakedAttributeExpr attr) = pure (NakedAttributeExpr attr)
179+
180+processTransGraphAttributeNames :: TransGraphAttributeNames -> TransGraphEvalMonad GraphRefAttributeNames
181+processTransGraphAttributeNames (AttributeNames names) = pure (AttributeNames names)
182+processTransGraphAttributeNames (InvertedAttributeNames names) = pure (InvertedAttributeNames names)
183+processTransGraphAttributeNames (UnionAttributeNames namesA namesB) = do
184+ nA <- processTransGraphAttributeNames namesA
185+ nB <- processTransGraphAttributeNames namesB
186+ pure (UnionAttributeNames nA nB)
187+processTransGraphAttributeNames (IntersectAttributeNames namesA namesB) = do
188+ nA <- processTransGraphAttributeNames namesA
189+ nB <- processTransGraphAttributeNames namesB
190+ pure (IntersectAttributeNames nA nB)
191+processTransGraphAttributeNames (RelationalExprAttributeNames expr) = do
192+ evaldExpr <- processTransGraphRelationalExpr expr
193+ pure (RelationalExprAttributeNames evaldExpr)
194+
195+processTransGraphWithNameExpr :: TransGraphWithNameExpr -> TransGraphEvalMonad GraphRefWithNameExpr
196+processTransGraphWithNameExpr (WithNameExpr rvname tlookup) =
197+ WithNameExpr rvname <$> findTransId tlookup
src/lib/ProjectM36/Transaction.hs+19−16
@@ -3,28 +3,25 @@ import ProjectM36.Base
33import qualified Data.Set as S
44import qualified Data.UUID as U
55import Data.Time.Clock
6+import qualified Data.List.NonEmpty as NE
67
7−transactionTimestamp :: Transaction -> UTCTime
8−transactionTimestamp (Transaction _ (TransactionInfo _ _ stamp) _) = stamp
9−transactionTimestamp (Transaction _ (MergeTransactionInfo _ _ _ stamp) _) = stamp
8+parentIds :: Transaction -> S.Set TransactionId
9+parentIds (Transaction _ tinfo _) = S.fromList (NE.toList (parents tinfo))
1010
11−transactionParentIds :: Transaction -> S.Set TransactionId
12−transactionParentIds (Transaction _ (TransactionInfo pId _ _) _) = S.singleton pId
13−transactionParentIds (Transaction _ (MergeTransactionInfo pId1 pId2 _ _) _) = S.fromList [pId1, pId2]
11+rootParent :: TransactionParents
12+rootParent = singleParent U.nil
1413
15−transactionChildIds :: Transaction -> S.Set TransactionId
16−transactionChildIds (Transaction _ (TransactionInfo _ children _) _) = children
17−transactionChildIds (Transaction _ (MergeTransactionInfo _ _ children _) _) = children
18−
19−-- | Create a new transaction which is identical to the original except that a new set of child transaction ids is added.
20−transactionSetChildren :: Transaction -> S.Set TransactionId -> Transaction
21−transactionSetChildren t@(Transaction _ (TransactionInfo pId _ stamp) schemas') childIds = Transaction (transactionId t) (TransactionInfo pId childIds stamp) schemas'
22−transactionSetChildren t@(Transaction _ (MergeTransactionInfo pId1 pId2 _ stamp) schemas') childIds = Transaction (transactionId t) (MergeTransactionInfo pId1 pId2 childIds stamp) schemas'
14+singleParent :: TransactionId -> TransactionParents
15+singleParent tid = tid NE.:| []
2316
2417-- | Return the same transaction but referencing only the specific child transactions. This is useful when traversing a graph and returning a subgraph. This doesn't filter parent transactions because it assumes a head-to-root traversal.
2518filterTransactionInfoTransactions :: S.Set TransactionId -> TransactionInfo -> TransactionInfo
26−filterTransactionInfoTransactions filterIds (TransactionInfo parentId childIds stamp) = TransactionInfo (filterParent parentId filterIds) (S.intersection childIds filterIds) stamp
27−filterTransactionInfoTransactions filterIds (MergeTransactionInfo parentIdA parentIdB childIds stamp) = MergeTransactionInfo (filterParent parentIdA filterIds) (filterParent parentIdB filterIds) (S.intersection childIds filterIds) stamp
19+filterTransactionInfoTransactions filterIds tinfo =
20+ TransactionInfo { parents = case
21+ NE.filter (`S.member` filterIds) (parents tinfo) of
22+ [] -> rootParent
23+ xs -> NE.fromList xs,
24+ stamp = stamp tinfo }
2825
2926filterParent :: TransactionId -> S.Set TransactionId -> TransactionId
3027filterParent parentId validIds = if S.member parentId validIds then parentId else U.nil
@@ -44,3 +41,9 @@ schemas (Transaction _ _ schemas') = schemas'
4441-- | Returns all subschemas which are isomorphic or sub-isomorphic to the concrete schema.
4542subschemas :: Transaction -> Subschemas
4643subschemas (Transaction _ _ (Schemas _ sschemas)) = sschemas
44+
45+fresh :: TransactionId -> UTCTime -> Schemas -> Transaction
46+fresh freshId stamp' = Transaction freshId (TransactionInfo rootParent stamp')
47+
48+timestamp :: Transaction -> UTCTime
49+timestamp (Transaction _ tinfo _) = stamp tinfo
src/lib/ProjectM36/Transaction/Persist.hs+11−24
@@ -9,7 +9,6 @@ import ProjectM36.Persist (writeBSFileSync, DiskSync, renameSync)
99import qualified Data.Map as M
1010import qualified Data.HashSet as HS
1111import qualified Data.Binary as B
12−--import qualified Data.ByteString as BS
1312import System.FilePath
1413import System.Directory
1514import qualified Data.Text as T
@@ -17,10 +16,6 @@ import Control.Monad
1716import ProjectM36.ScriptSession
1817import ProjectM36.AtomFunctions.Basic (precompiledAtomFunctions)
1918
20−import Codec.Compression.GZip
21−import qualified Data.ByteString as BS
22−import qualified Data.ByteString.Lazy as BSL
23−
2419#ifdef PM36_HASKELL_SCRIPTING
2520import GHC
2621import Control.Exception
@@ -41,8 +36,8 @@ transactionDir dbdir transId = dbdir </> show transId
4136transactionInfoPath :: FilePath -> FilePath
4237transactionInfoPath transdir = transdir </> "info"
4338
44−relvarsDir :: FilePath -> FilePath
45−relvarsDir transdir = transdir </> "relvars"
39+relvarsPath :: FilePath -> FilePath
40+relvarsPath transdir = transdir </> "relvars"
4641
4742incDepsDir :: FilePath -> FilePath
4843incDepsDir transdir = transdir </> "incdeps"
@@ -90,7 +85,7 @@ writeTransaction sync dbdir trans = do
9085 transDirExists <- doesDirectoryExist finalTransDir
9186 unless transDirExists $ do
9287 --create sub directories
93− mapM_ createDirectory [tempTransDir, relvarsDir tempTransDir, incDepsDir tempTransDir, dbcFuncsDir tempTransDir]
88+ mapM_ createDirectory [tempTransDir, incDepsDir tempTransDir, dbcFuncsDir tempTransDir]
9489 writeRelVars sync tempTransDir (relationVariables context)
9590 writeIncDeps sync tempTransDir (inclusionDependencies context)
9691 writeAtomFuncs sync tempTransDir (atomFunctions context)
@@ -100,23 +95,15 @@ writeTransaction sync dbdir trans = do
10095 B.encodeFile (transactionInfoPath tempTransDir) (transactionInfo trans)
10196 --move the temp directory to final location
10297 renameSync sync tempTransDir finalTransDir
103−
104−writeRelVar :: DiskSync -> FilePath -> (RelVarName, Relation) -> IO ()
105−writeRelVar sync transDir (relvarName, rel) = do
106− let relvarPath = relvarsDir transDir </> T.unpack relvarName
107− writeBSFileSync sync relvarPath (compress (B.encode rel))
108−
109−writeRelVars :: DiskSync -> FilePath -> M.Map RelVarName Relation -> IO ()
110−writeRelVars sync transDir relvars = mapM_ (writeRelVar sync transDir) $ M.toList relvars
11198
112−readRelVars :: FilePath -> IO (M.Map RelVarName Relation)
113−readRelVars transDir = do
114− let relvarsPath = relvarsDir transDir
115− relvarNames <- getDirectoryNames relvarsPath
116− let relvars = mapM (\name -> do
117− rel <- B.decode . decompress . BSL.fromStrict <$> BS.readFile (relvarsPath </> name)
118− return (T.pack name, rel)) relvarNames
119− M.fromList <$> relvars
99+writeRelVars :: DiskSync -> FilePath -> RelationVariables -> IO ()
100+writeRelVars sync transDir relvars = do
101+ let path = relvarsPath transDir
102+ writeBSFileSync sync path (B.encode relvars)
103+
104+readRelVars :: FilePath -> IO RelationVariables
105+readRelVars transDir =
106+ B.decodeFile (relvarsPath transDir)
120107
121108writeAtomFuncs :: DiskSync -> FilePath -> AtomFunctions -> IO ()
122109writeAtomFuncs sync transDir funcs = do
src/lib/ProjectM36/Transaction/Type.hsnew file+8
@@ -0,0 +1,8 @@
1+module ProjectM36.Transaction where
2+import ProjectM36.Base
3+
4+uid :: Transaction -> TransactionId
5+uid (Transaction uid _ _) = uid
6+
7+info :: Transaction -> TransactionInfo
8+info (Transaction _ info _) = info
src/lib/ProjectM36/TransactionDiffs.hsnew file+12
@@ -0,0 +1,12 @@
1+module ProjectM36.TransactionDiffs where
2+import ProjectM36.Base
3+import Data.List.NonEmpty
4+import Data.UUID as U
5+
6+{-
7+root :: TransactionDiffs
8+root = single U.nil NoOperation
9+
10+single :: TransactionId -> TransactionDiffExpr -> TransactionDiffs
11+single tid expr = (tid, expr) :| []
12+-}
src/lib/ProjectM36/TransactionGraph.hs+168−141
@@ -1,21 +1,24 @@
1−{-# LANGUAGE DeriveAnyClass, DeriveGeneric, CPP #-}
1+{-# LANGUAGE DeriveAnyClass, DeriveGeneric, CPP, FlexibleContexts #-}
22module ProjectM36.TransactionGraph where
33import ProjectM36.Base
44import ProjectM36.Error
55import ProjectM36.Transaction
6+import ProjectM36.TransactionInfo as TI
67import ProjectM36.Relation
78import ProjectM36.TupleSet
89import ProjectM36.Tuple
910import ProjectM36.RelationalExpression
1011import ProjectM36.TransactionGraph.Merge
1112import qualified ProjectM36.DisconnectedTransaction as Discon
13+import qualified ProjectM36.Attribute as A
1214
15+import Control.Monad.Except hiding (join)
16+import Control.Monad.Reader hiding (join)
1317import qualified Data.Vector as V
14−import qualified ProjectM36.Attribute as A
1518import qualified Data.UUID as U
1619import qualified Data.Set as S
1720import qualified Data.Map as M
18−import Control.Monad
21+import qualified Data.List.NonEmpty as NE
1922import Data.Time.Clock
2023import qualified Data.Text as T
2124import GHC.Generics
@@ -26,12 +29,7 @@ import Data.Monoid
2629#endif
2730import Control.Arrow
2831import Data.Maybe
29−
30−{-
31−--import Debug.Trace
32−import Control.Monad.Reader
33−import qualified ProjectM36.DisconnectedTransaction as D
34−-}
32+import Data.UUID.V4
3533
3634-- | Record a lookup for a specific transaction in the graph.
3735data TransactionIdLookup = TransactionIdLookup TransactionId |
@@ -64,13 +62,21 @@ data ROTransactionGraphOperator = ShowGraph
6462 deriving Show
6563
6664bootstrapTransactionGraph :: UTCTime -> TransactionId -> DatabaseContext -> TransactionGraph
67−bootstrapTransactionGraph stamp freshId context = TransactionGraph bootstrapHeads bootstrapTransactions
65+bootstrapTransactionGraph stamp' freshId context = TransactionGraph bootstrapHeads bootstrapTransactions
6866 where
6967 bootstrapHeads = M.singleton "master" freshTransaction
7068 newSchemas = Schemas context M.empty
71− freshTransaction = Transaction freshId (TransactionInfo U.nil S.empty stamp) newSchemas
69+ freshTransaction = fresh freshId stamp' newSchemas
7270 bootstrapTransactions = S.singleton freshTransaction
7371
72+-- | Create a transaction graph from a context.
73+freshTransactionGraph :: DatabaseContext -> IO (TransactionGraph, TransactionId)
74+freshTransactionGraph ctx = do
75+ now <- getCurrentTime
76+ freshId <- nextRandom
77+ pure (bootstrapTransactionGraph now freshId ctx, freshId)
78+
79+
7480emptyTransactionGraph :: TransactionGraph
7581emptyTransactionGraph = TransactionGraph M.empty S.empty
7682
@@ -88,75 +94,76 @@ headNameForTransaction transaction (TransactionGraph heads _) = if M.null matchi
8894 where
8995 matchingTrans = M.filter (transaction ==) heads
9096
91−transactionForId :: TransactionId -> TransactionGraph -> Either RelationalError Transaction
92−transactionForId tid graph
93− | tid == U.nil =
94− Left RootTransactionTraversalError
95− | S.null matchingTrans =
96− Left $ NoSuchTransactionError tid
97− | otherwise =
98− Right $ head (S.toList matchingTrans)
99− where
100− matchingTrans = S.filter (\(Transaction idMatch _ _) -> idMatch == tid) (transactionsForGraph graph)
101−
10297transactionsForIds :: S.Set TransactionId -> TransactionGraph -> Either RelationalError (S.Set Transaction)
10398transactionsForIds idSet graph =
10499 S.fromList <$> forM (S.toList idSet) (`transactionForId` graph)
105100
106−isRootTransaction :: Transaction -> TransactionGraph -> Bool
107−isRootTransaction (Transaction _ (TransactionInfo pId _ _) _) _ = U.null pId
108−isRootTransaction (Transaction _ MergeTransactionInfo{} _) _ = False
101+-- | A root transaction terminates a graph and has no parents.
102+isRootTransaction :: Transaction -> Bool
103+isRootTransaction trans = parentIds trans == S.singleton U.nil
104+
105+rootTransactions :: TransactionGraph -> S.Set Transaction
106+rootTransactions graph = S.filter isRootTransaction (transactionsForGraph graph)
109107
110108-- the first transaction has no parent - all other do have parents- merges have two parents
111109parentTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
112−parentTransactions (Transaction _ (TransactionInfo pId _ _) _) graph =
113− S.singleton <$> transactionForId pId graph
114−
115−
116−parentTransactions (Transaction _ (MergeTransactionInfo pId1 pId2 _ _) _ ) graph = transactionsForIds (S.fromList [pId1, pId2]) graph
117−
110+parentTransactions trans = transactionsForIds (parentIds trans)
118111
119112childTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
120−childTransactions (Transaction _ (TransactionInfo _ children _) _) = transactionsForIds children
121−childTransactions (Transaction _ (MergeTransactionInfo _ _ children _) _) = transactionsForIds children
113+childTransactions trans graph = transactionsForIds childIds graph
114+ where
115+ childIds = S.map transactionId (S.filter filt (transactionsForGraph graph))
116+ filt trans' = S.member (transactionId trans) (parentIds trans')
122117
123118-- create a new commit and add it to the heads
124119-- technically, the new head could be added to an existing commit, but by adding a new commit, the new head is unambiguously linked to a new commit (with a context indentical to its parent)
125120addBranch :: UTCTime -> TransactionId -> HeadName -> TransactionId -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
126−addBranch stamp newId newBranchName branchPointId graph = do
121+addBranch stamp' newId newBranchName branchPointId graph = do
127122 parentTrans <- transactionForId branchPointId graph
128− let newTrans = Transaction newId (TransactionInfo branchPointId S.empty stamp) (schemas parentTrans)
123+ let newTrans = Transaction newId (TI.singleParent branchPointId stamp') (schemas parentTrans)
129124 addTransactionToGraph newBranchName newTrans graph
130125
131126--adds a disconnected transaction to a transaction graph at some head
132127addDisconnectedTransaction :: UTCTime -> TransactionId -> HeadName -> DisconnectedTransaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
133−addDisconnectedTransaction stamp newId headName (DisconnectedTransaction parentId schemas' _) = addTransactionToGraph headName (Transaction newId (TransactionInfo parentId S.empty stamp) schemas')
134−
128+addDisconnectedTransaction stamp' newId headName (DisconnectedTransaction parentId schemas' _) = addTransactionToGraph headName newTrans
129+ where
130+ newTrans = Transaction newId (TI.singleParent parentId stamp') schemas'
135131
136132addTransactionToGraph :: HeadName -> Transaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
137133addTransactionToGraph headName newTrans graph = do
138− let parentIds = transactionParentIds newTrans
139− childIds = transactionChildIds newTrans
134+ let parentIds' = parentIds newTrans
140135 newId = transactionId newTrans
141136 validateIds ids = mapM (`transactionForId` graph) (S.toList ids)
142− addChildTransaction trans = transactionSetChildren trans (S.insert newId (transactionChildIds trans))
137+ childTs <- childTransactions newTrans graph
143138 --validate that the parent transactions are in the graph
144− _ <- validateIds parentIds
145− when (S.size parentIds < 1) (Left $ NewTransactionMissingParentError newId)
139+ _ <- validateIds parentIds'
140+ when (S.size parentIds' < 1) (Left $ NewTransactionMissingParentError newId)
146141 --if the headName already exists, ensure that it refers to a parent
147142 case transactionForHead headName graph of
148143 Nothing -> pure () -- any headName is OK
149− Just trans -> when (S.notMember (transactionId trans) parentIds) (Left (HeadNameSwitchingHeadProhibitedError headName))
144+ Just trans -> when (S.notMember (transactionId trans) parentIds') (Left (HeadNameSwitchingHeadProhibitedError headName))
150145 --validate that the transaction has no children
151− unless (S.null childIds) (Left $ NewTransactionMayNotHaveChildrenError newId)
146+ unless (S.null childTs) (Left $ NewTransactionMayNotHaveChildrenError newId)
152147 --validate that the trasaction's id is unique
153148 when (isRight (transactionForId newId graph)) (Left (TransactionIdInUseError newId))
154− --update the parent transactions to point to the new transaction
155− parents <- mapM (`transactionForId` graph) (S.toList parentIds)
156− let updatedParents = S.map addChildTransaction (S.fromList parents)
157− updatedTransSet = S.insert newTrans (S.union updatedParents (transactionsForGraph graph))
158− updatedHeads = M.insert headName newTrans (transactionHeadsForGraph graph)
159− pure (newTrans, TransactionGraph updatedHeads updatedTransSet)
149+ --replace all references to UncommittedTransactionMarker to new transaction id
150+ let newTrans' = newTransUncommittedReplace newTrans
151+ updatedTransSet = S.insert newTrans' (transactionsForGraph graph)
152+ updatedHeads = M.insert headName newTrans' (transactionHeadsForGraph graph)
153+ pure (newTrans', TransactionGraph updatedHeads updatedTransSet)
154+
155+--replace all occurrences of the uncommitted context marker
156+newTransUncommittedReplace :: Transaction -> Transaction
157+newTransUncommittedReplace trans@(Transaction tid tinfo (Schemas ctx sschemas)) =
158+ Transaction tid tinfo (Schemas fixedContext sschemas)
159+ where
160+ uncommittedReplace UncommittedContextMarker = TransactionMarker tid
161+ uncommittedReplace marker = marker
162+ relvars = relationVariables (concreteDatabaseContext trans)
163+ fixedRelvars = M.map (fmap uncommittedReplace) relvars
164+ fixedContext = ctx { relationVariables = fixedRelvars }
165+
166+
160167
161168validateGraph :: TransactionGraph -> Maybe [RelationalError]
162169validateGraph graph@(TransactionGraph _ transSet) = do
@@ -228,7 +235,7 @@ evalGraphOp _ _ discon graph (WalkBackToTime backTime) = do
228235 case transactionForId jumpDest graph of
229236 Left err -> Left err
230237 Right trans -> do
231− let disconnectedTrans = DisconnectedTransaction (transactionId trans) (schemas trans) False
238+ let disconnectedTrans = Discon.freshTransaction (transactionId trans) (schemas trans)
232239 Right (disconnectedTrans, graph)
233240
234241-- add new head pointing to branchPoint
@@ -246,15 +253,15 @@ evalGraphOp newId discon@(DisconnectedTransaction parentId disconContext) graph
246253
247254-- create a new commit and add it to the heads
248255-- technically, the new head could be added to an existing commit, but by adding a new commit, the new head is unambiguously linked to a new commit (with a context indentical to its parent)
249−evalGraphOp stamp newId (DisconnectedTransaction parentId schemas' _) graph (Branch newBranchName) = do
250− let newDiscon = DisconnectedTransaction newId schemas' False
251− case addBranch stamp newId newBranchName parentId graph of
256+evalGraphOp stamp' newId (DisconnectedTransaction parentId schemas' _) graph (Branch newBranchName) = do
257+ let newDiscon = Discon.freshTransaction newId schemas'
258+ case addBranch stamp' newId newBranchName parentId graph of
252259 Left err -> Left err
253260 Right (_, newGraph) -> Right (newDiscon, newGraph)
254261
255262-- add the disconnected transaction to the graph
256263-- affects graph and disconnectedtransaction- the new disconnectedtransaction's parent is the freshly committed transaction
257−evalGraphOp stamp newTransId discon@(DisconnectedTransaction parentId schemas' _) graph Commit = case transactionForId parentId graph of
264+evalGraphOp stamp' newTransId discon@(DisconnectedTransaction parentId schemas' _) graph Commit = case transactionForId parentId graph of
258265 Left err -> Left err
259266 Right parentTransaction -> case headNameForTransaction parentTransaction graph of
260267 Nothing -> Left $ TransactionIsNotAHeadError parentId
@@ -262,17 +269,20 @@ evalGraphOp stamp newTransId discon@(DisconnectedTransaction parentId schemas' _
262269 Left err-> Left err
263270 Right (_, updatedGraph) -> Right (newDisconnectedTrans, updatedGraph)
264271 where
265− newDisconnectedTrans = DisconnectedTransaction newTransId schemas' False
266− maybeUpdatedGraph = addDisconnectedTransaction stamp newTransId headName discon graph
272+ newDisconnectedTrans = Discon.freshTransaction newTransId schemas'
273+ maybeUpdatedGraph = addDisconnectedTransaction stamp' newTransId headName discon graph
267274
268275-- refresh the disconnected transaction, return the same graph
269276evalGraphOp _ _ (DisconnectedTransaction parentId _ _) graph Rollback = case transactionForId parentId graph of
270277 Left err -> Left err
271278 Right parentTransaction -> Right (newDiscon, graph)
272279 where
273− newDiscon = DisconnectedTransaction parentId (schemas parentTransaction) False
280+ newDiscon = Discon.freshTransaction parentId (schemas parentTransaction)
274281
275−evalGraphOp stamp newId (DisconnectedTransaction parentId _ _) graph (MergeTransactions mergeStrategy headNameA headNameB) = mergeTransactions stamp newId parentId mergeStrategy (headNameA, headNameB) graph
282+evalGraphOp stamp' newId (DisconnectedTransaction parentId _ _) graph (MergeTransactions mergeStrategy headNameA headNameB) =
283+ runGraphRefRelationalExprM env $ mergeTransactions stamp' newId parentId mergeStrategy (headNameA, headNameB)
284+ where
285+ env = freshGraphRefRelationalExprEnv Nothing graph
276286
277287evalGraphOp _ _ discon graph@(TransactionGraph graphHeads transSet) (DeleteBranch branchName) = case transactionForHead branchName graph of
278288 Nothing -> Left (NoSuchHeadNameError branchName)
@@ -294,7 +304,7 @@ graphAsRelation (DisconnectedTransaction parentId _ _) graph@(TransactionGraph _
294304 tupleGenerator transaction = case transactionParentsRelation transaction graph of
295305 Left err -> Left err
296306 Right parentTransRel -> Right [TextAtom $ T.pack $ show (transactionId transaction),
297− DateTimeAtom (transactionTimestamp transaction),
307+ DateTimeAtom (timestamp transaction),
298308 RelationAtom parentTransRel,
299309 BoolAtom $ parentId == transactionId transaction,
300310 TextAtom $ fromMaybe "" (headNameForTransaction transaction graph)
@@ -302,7 +312,7 @@ graphAsRelation (DisconnectedTransaction parentId _ _) graph@(TransactionGraph _
302312
303313transactionParentsRelation :: Transaction -> TransactionGraph -> Either RelationalError Relation
304314transactionParentsRelation trans graph =
305− if isRootTransaction trans graph then
315+ if isRootTransaction trans then
306316 mkRelation attrs emptyTupleSet
307317 else do
308318 parentTransSet <- parentTransactions trans graph
@@ -321,39 +331,45 @@ evalROGraphOp discon graph ShowGraph = do
321331-}
322332
323333-- | Execute the merge strategy against the transactions, returning a new transaction which can be then added to the transaction graph
324−createMergeTransaction :: UTCTime -> TransactionId -> MergeStrategy -> TransactionGraph -> (Transaction, Transaction) -> Either MergeError Transaction
325−createMergeTransaction stamp newId (SelectedBranchMergeStrategy selectedBranch) graph t2@(trans1, trans2) = do
326− let selectedTrans = validateHeadName selectedBranch graph t2
327− Transaction newId (MergeTransactionInfo (transactionId trans1) (transactionId trans2) S.empty stamp) . schemas <$> selectedTrans
334+createMergeTransaction :: UTCTime -> TransactionId -> MergeStrategy -> (Transaction, Transaction) -> GraphRefRelationalExprM Transaction
335+createMergeTransaction stamp' newId (SelectedBranchMergeStrategy selectedBranch) t2@(trans1, trans2) = do
336+ graph <- gfGraph
337+ selectedTrans <- validateHeadName selectedBranch graph t2
338+ pure $ Transaction newId (TransactionInfo {
339+ parents = NE.fromList [transactionId trans1,
340+ transactionId trans2],
341+ stamp = stamp' }) (schemas selectedTrans)
328342
329343-- merge functions, relvars, individually
330−createMergeTransaction stamp newId strat@UnionMergeStrategy graph t2 = createUnionMergeTransaction stamp newId strat graph t2
344+createMergeTransaction stamp' newId strat@UnionMergeStrategy t2 =
345+ createUnionMergeTransaction stamp' newId strat t2
331346
332347-- merge function, relvars, but, on error, just take the component from the preferred branch
333−createMergeTransaction stamp newId strat@(UnionPreferMergeStrategy _) graph t2 = createUnionMergeTransaction stamp newId strat graph t2
348+createMergeTransaction stamp' newId strat@(UnionPreferMergeStrategy _) t2 =
349+ createUnionMergeTransaction stamp' newId strat t2
334350
335351-- | Returns the correct Transaction for the branch name in the graph and ensures that it is one of the two transaction arguments in the tuple.
336−validateHeadName :: HeadName -> TransactionGraph -> (Transaction, Transaction) -> Either MergeError Transaction
352+validateHeadName :: HeadName -> TransactionGraph -> (Transaction, Transaction) -> GraphRefRelationalExprM Transaction
337353validateHeadName headName graph (t1, t2) =
338354 case transactionForHead headName graph of
339− Nothing -> Left SelectedHeadMismatchMergeError
355+ Nothing -> throwError (MergeTransactionError SelectedHeadMismatchMergeError)
340356 Just trans -> if trans /= t1 && trans /= t2 then
341− Left SelectedHeadMismatchMergeError
357+ throwError (MergeTransactionError SelectedHeadMismatchMergeError)
342358 else
343− Right trans
359+ pure trans
344360
345361-- Algorithm: start at one transaction and work backwards up the parents. If there is a node we have not yet visited as a child, then walk that up to its head. If that branch contains the goal transaction, then we have completed a valid subgraph traversal.
346362subGraphOfFirstCommonAncestor :: TransactionGraph -> TransactionHeads -> Transaction -> Transaction -> S.Set Transaction -> Either RelationalError TransactionGraph
347−subGraphOfFirstCommonAncestor origGraph resultHeads currentTrans goalTrans traverseSet = do
348− let currentid = transactionId currentTrans
363+subGraphOfFirstCommonAncestor origGraph resultHeads currentTrans' goalTrans traverseSet = do
364+ let currentid = transactionId currentTrans'
349365 goalid = transactionId goalTrans
350− if currentTrans == goalTrans then
366+ if currentTrans' == goalTrans then
351367 Right (TransactionGraph resultHeads traverseSet) -- add filter
352368 --catch root transaction to improve error?
353369 else do
354− currentTransChildren <- S.fromList <$> mapM (`transactionForId` origGraph) (S.toList (transactionChildIds currentTrans))
355− let searchChildren = S.difference (S.insert currentTrans traverseSet) currentTransChildren
356− searchChild start = pathToTransaction origGraph start goalTrans (S.insert currentTrans traverseSet)
370+ currentTransChildren <- childTransactions currentTrans' origGraph
371+ let searchChildren = S.difference (S.insert currentTrans' traverseSet) currentTransChildren
372+ searchChild start = pathToTransaction origGraph start goalTrans (S.insert currentTrans' traverseSet)
357373 childSearches = map searchChild (S.toList searchChildren)
358374 errors = lefts childSearches
359375 pathsFound = rights childSearches
@@ -362,17 +378,16 @@ subGraphOfFirstCommonAncestor origGraph resultHeads currentTrans goalTrans trave
362378 unless (null realErrors) (Left (head realErrors))
363379 -- if no paths found, search the parent
364380 if null pathsFound then
365− case oneParent currentTrans of
381+ case oneParent currentTrans' of
366382 Left RootTransactionTraversalError -> Left (NoCommonTransactionAncestorError currentid goalid)
367383 Left err -> Left err
368384 Right currentTransParent ->
369− subGraphOfFirstCommonAncestor origGraph resultHeads currentTransParent goalTrans (S.insert currentTrans traverseSet)
385+ subGraphOfFirstCommonAncestor origGraph resultHeads currentTransParent goalTrans (S.insert currentTrans' traverseSet)
370386 else -- we found a path
371387 Right (TransactionGraph resultHeads (S.unions (traverseSet : pathsFound)))
372388 where
373− oneParent (Transaction _ (TransactionInfo parentId _ _) _) = transactionForId parentId origGraph
374− oneParent (Transaction _ (MergeTransactionInfo parentId _ _ _) _) = transactionForId parentId origGraph
375−
389+ oneParent (Transaction _ tinfo _) = transactionForId (NE.head (parents tinfo)) origGraph
390+
376391-- | Search from a past graph point to all following heads for a specific transaction. If found, return the transaction path, otherwise a RelationalError.
377392pathToTransaction :: TransactionGraph -> Transaction -> Transaction -> S.Set Transaction -> Either RelationalError (S.Set Transaction)
378393pathToTransaction graph currentTransaction targetTransaction accumTransSet = do
@@ -380,11 +395,11 @@ pathToTransaction graph currentTransaction targetTransaction accumTransSet = do
380395 if transactionId targetTransaction == transactionId currentTransaction then
381396 Right accumTransSet
382397 else do
383− currentTransChildren <- mapM (`transactionForId` graph) (S.toList (transactionChildIds currentTransaction))
398+ currentTransChildren <- childTransactions currentTransaction graph
384399 if null currentTransChildren then
385400 Left (FailedToFindTransactionError targetId)
386401 else do
387− let searches = map (\t -> pathToTransaction graph t targetTransaction (S.insert t accumTransSet)) currentTransChildren
402+ let searches = map (\t -> pathToTransaction graph t targetTransaction (S.insert t accumTransSet)) (S.toList currentTransChildren)
388403 let realErrors = filter (/= FailedToFindTransactionError targetId) (lefts searches)
389404 paths = rights searches
390405 if not (null realErrors) then -- found some real errors
@@ -394,35 +409,39 @@ pathToTransaction graph currentTransaction targetTransaction accumTransSet = do
394409 else --we have some paths!
395410 Right (S.unions paths)
396411
397−mergeTransactions :: UTCTime -> TransactionId -> TransactionId -> MergeStrategy -> (HeadName, HeadName) -> TransactionGraph -> Either RelationalError (DisconnectedTransaction, TransactionGraph)
398−mergeTransactions stamp newId parentId mergeStrategy (headNameA, headNameB) graph = do
412+mergeTransactions :: UTCTime -> TransactionId -> TransactionId -> MergeStrategy -> (HeadName, HeadName) -> GraphRefRelationalExprM (DisconnectedTransaction, TransactionGraph)
413+mergeTransactions stamp' newId parentId mergeStrategy (headNameA, headNameB) = do
414+ graph <- gfGraph
399415 let transactionForHeadErr name = case transactionForHead name graph of
400− Nothing -> Left (NoSuchHeadNameError name)
401− Just t -> Right t
402− transA <- transactionForHeadErr headNameA
403− transB <- transactionForHeadErr headNameB
404− disconParent <- transactionForId parentId graph
416+ Nothing -> throwError (NoSuchHeadNameError name)
417+ Just t -> pure t
418+ runE e = case e of
419+ Left e' -> throwError e'
420+ Right v -> pure v
421+ transA <- transactionForHeadErr headNameA
422+ transB <- transactionForHeadErr headNameB
423+ disconParent <- gfTransForId parentId
405424 let subHeads = M.filterWithKey (\k _ -> k `elem` [headNameA, headNameB]) (transactionHeadsForGraph graph)
406− subGraph <- subGraphOfFirstCommonAncestor graph subHeads transA transB S.empty
407− subGraph' <- filterSubGraph subGraph subHeads
408− case createMergeTransaction stamp newId mergeStrategy subGraph' (transA, transB) of
409− Left err -> Left (MergeTransactionError err)
410− Right mergedTrans -> case checkConstraints (concreteDatabaseContext mergedTrans) of
411− Left err -> Left err
412− Right _ -> case headNameForTransaction disconParent graph of
413− Nothing -> Left (TransactionIsNotAHeadError parentId)
425+ subGraph <- runE $ subGraphOfFirstCommonAncestor graph subHeads transA transB S.empty
426+ subGraph' <- runE $ filterSubGraph subGraph subHeads
427+ mergedTrans <- local (const (freshGraphRefRelationalExprEnv Nothing subGraph')) $ createMergeTransaction stamp' newId mergeStrategy (transA, transB)
428+ case headNameForTransaction disconParent graph of
429+ Nothing -> throwError (TransactionIsNotAHeadError parentId)
414430 Just headName -> do
415− (newTrans, newGraph) <- addTransactionToGraph headName mergedTrans graph
416− let newGraph' = TransactionGraph (transactionHeadsForGraph newGraph) (transactionsForGraph newGraph)
417− newDiscon = DisconnectedTransaction newId (schemas newTrans) False
418− pure (newDiscon, newGraph')
431+ (newTrans, newGraph) <- runE $ addTransactionToGraph headName mergedTrans graph
432+ case checkConstraints (concreteDatabaseContext mergedTrans) newId graph of
433+ Left err -> throwError err
434+ Right _ -> do
435+ let newGraph' = TransactionGraph (transactionHeadsForGraph newGraph) (transactionsForGraph newGraph)
436+ newDiscon = Discon.freshTransaction newId (schemas newTrans)
437+ pure (newDiscon, newGraph')
419438
420439--TEMPORARY COPY/PASTE
421440showTransactionStructureX :: Transaction -> TransactionGraph -> String
422441showTransactionStructureX trans graph = headInfo ++ " " ++ show (transactionId trans) ++ " " ++ parentTransactionsInfo
423442 where
424443 headInfo = maybe "" show (headNameForTransaction trans graph)
425− parentTransactionsInfo = if isRootTransaction trans graph then "root" else case parentTransactions trans graph of
444+ parentTransactionsInfo = if isRootTransaction trans then "root" else case parentTransactions trans graph of
426445 Left err -> show err
427446 Right parentTransSet -> concat $ S.toList $ S.map (show . transactionId) parentTransSet
428447
@@ -441,25 +460,29 @@ filterSubGraph graph heads = Right $ TransactionGraph newHeads newTransSet
441460 newHeads = M.map (filterTransaction validIds) heads
442461
443462--helper function for commonalities in union merge
444−createUnionMergeTransaction :: UTCTime -> TransactionId -> MergeStrategy -> TransactionGraph -> (Transaction, Transaction) -> Either MergeError Transaction
445−createUnionMergeTransaction stamp newId strategy graph (t1,t2) = do
463+createUnionMergeTransaction :: UTCTime -> TransactionId -> MergeStrategy -> (Transaction, Transaction) -> GraphRefRelationalExprM Transaction
464+createUnionMergeTransaction stamp' newId strategy (t1,t2) = do
446465 let contextA = concreteDatabaseContext t1
447466 contextB = concreteDatabaseContext t2
448−
467+ liftMergeE x = case x of
468+ Left e -> throwError (MergeTransactionError e)
469+ Right t -> pure t
470+
471+ graph <- gfGraph
449472 preference <- case strategy of
450473 UnionMergeStrategy -> pure PreferNeither
451474 UnionPreferMergeStrategy preferBranch ->
452475 case transactionForHead preferBranch graph of
453− Nothing -> Left (PreferredHeadMissingMergeError preferBranch)
476+ Nothing -> throwError (MergeTransactionError (PreferredHeadMissingMergeError preferBranch))
454477 Just preferredTrans -> pure $ if t1 == preferredTrans then PreferFirst else PreferSecond
455− badStrat -> Left (InvalidMergeStrategyError badStrat)
478+ badStrat -> throwError (MergeTransactionError (InvalidMergeStrategyError badStrat))
456479
457− incDeps <- unionMergeMaps preference (inclusionDependencies contextA) (inclusionDependencies contextB)
480+ incDeps <- liftMergeE $ unionMergeMaps preference (inclusionDependencies contextA) (inclusionDependencies contextB)
458481 relVars <- unionMergeRelVars preference (relationVariables contextA) (relationVariables contextB)
459− atomFuncs <- unionMergeAtomFunctions preference (atomFunctions contextA) (atomFunctions contextB)
460− notifs <- unionMergeMaps preference (notifications contextA) (notifications contextB)
461− types <- unionMergeTypeConstructorMapping preference (typeConstructorMapping contextA) (typeConstructorMapping contextB)
462− dbcFuncs <- unionMergeDatabaseContextFunctions preference (dbcFunctions contextA) (dbcFunctions contextB)
482+ atomFuncs <- liftMergeE $ unionMergeAtomFunctions preference (atomFunctions contextA) (atomFunctions contextB)
483+ notifs <- liftMergeE $ unionMergeMaps preference (notifications contextA) (notifications contextB)
484+ types <- liftMergeE $ unionMergeTypeConstructorMapping preference (typeConstructorMapping contextA) (typeConstructorMapping contextB)
485+ dbcFuncs <- liftMergeE $ unionMergeDatabaseContextFunctions preference (dbcFunctions contextA) (dbcFunctions contextB)
463486 -- TODO: add merge of subschemas
464487 let newContext = DatabaseContext {
465488 inclusionDependencies = incDeps,
@@ -470,7 +493,10 @@ createUnionMergeTransaction stamp newId strategy graph (t1,t2) = do
470493 typeConstructorMapping = types
471494 }
472495 newSchemas = Schemas newContext (subschemas t1)
473− pure (Transaction newId (MergeTransactionInfo (transactionId t1) (transactionId t2) S.empty stamp) newSchemas)
496+ pure (Transaction newId (TransactionInfo {
497+ parents = NE.fromList [transactionId t1,
498+ transactionId t2],
499+ stamp = stamp'}) newSchemas)
474500
475501lookupTransaction :: TransactionGraph -> TransactionIdLookup -> Either RelationalError Transaction
476502lookupTransaction graph (TransactionIdLookup tid) = transactionForId tid graph
@@ -487,56 +513,57 @@ backtrackGraph :: TransactionGraph -> TransactionId -> TransactionIdHeadBacktrac
487513-- tilde, step back one parent link- if a choice must be made, choose the "first" link arbitrarily
488514backtrackGraph graph currentTid (TransactionIdHeadParentBacktrack steps) = do
489515 trans <- transactionForId currentTid graph
490− let parents = S.toAscList (transactionParentIds trans)
491− if null parents then
492− Left RootTransactionTraversalError
493− else do
494− parentTrans <- transactionForId (head parents) graph
495− if steps == 1 then
496− pure (transactionId parentTrans)
497− else
498− backtrackGraph graph (transactionId parentTrans) (TransactionIdHeadParentBacktrack (steps - 1))
516+
517+ let parentIds' = S.toAscList (parentIds trans)
518+ case parentIds' of
519+ [] -> Left RootTransactionTraversalError
520+ firstParentId:_ -> do
521+ parentTrans <- transactionForId firstParentId graph
522+ if steps == 1 then
523+ pure (transactionId parentTrans)
524+ else
525+ backtrackGraph graph (transactionId parentTrans) (TransactionIdHeadParentBacktrack (steps - 1))
499526
500527backtrackGraph graph currentTid (TransactionIdHeadBranchBacktrack steps) = do
501528 trans <- transactionForId currentTid graph
502− let parents = transactionParentIds trans
503− if S.size parents < 1 then
529+ let parentIds' = parentIds trans
530+ if S.size parentIds' < 1 then
504531 Left RootTransactionTraversalError
505− else if S.size parents < steps then
506− Left (ParentCountTraversalError (S.size parents) steps)
532+ else if S.size parentIds' < steps then
533+ Left (ParentCountTraversalError (S.size parentIds') steps)
507534 else
508− pure (S.elemAt (steps - 1) parents)
535+ pure (S.elemAt (steps - 1) parentIds')
509536
510−backtrackGraph graph currentTid btrack@(TransactionStampHeadBacktrack stamp) = do
537+backtrackGraph graph currentTid btrack@(TransactionStampHeadBacktrack stamp') = do
511538 trans <- transactionForId currentTid graph
512− let parents = transactionParentIds trans
513− if transactionTimestamp trans <= stamp then
539+ let parentIds' = parentIds trans
540+ if timestamp trans <= stamp' then
514541 pure currentTid
515− else if S.null parents then
542+ else if S.null parentIds' then
516543 Left RootTransactionTraversalError
517544 else
518− let arbitraryParent = head (S.toList parents) in
545+ let arbitraryParent = head (S.toList parentIds') in
519546 backtrackGraph graph arbitraryParent btrack
520547
521548-- | Create a temporary branch for commit, merge the result to head, delete the temporary branch. This is useful to atomically commit a transaction, avoiding a TransactionIsNotHeadError but trading it for a potential MergeError.
522549--this is not a GraphOp because it combines multiple graph operations
523550autoMergeToHead :: UTCTime -> (TransactionId, TransactionId, TransactionId) -> DisconnectedTransaction -> HeadName -> MergeStrategy -> TransactionGraph -> Either RelationalError (DisconnectedTransaction, TransactionGraph)
524−autoMergeToHead stamp (tempBranchTransId, tempCommitTransId, mergeTransId) discon mergeToHeadName strat graph = do
551+autoMergeToHead stamp' (tempBranchTransId, tempCommitTransId, mergeTransId) discon mergeToHeadName strat graph = do
525552 let tempBranchName = "mergebranch_" <> U.toText tempBranchTransId
526553 --create the temp branch
527− (discon', graph') <- evalGraphOp stamp tempBranchTransId discon graph (Branch tempBranchName)
554+ (discon', graph') <- evalGraphOp stamp' tempBranchTransId discon graph (Branch tempBranchName)
528555
529556 --commit to the new branch- possible future optimization: don't require fsync for this- create a temp commit type
530− (discon'', graph'') <- evalGraphOp stamp tempCommitTransId discon' graph' Commit
557+ (discon'', graph'') <- evalGraphOp stamp' tempCommitTransId discon' graph' Commit
531558
532559 --jump to merge head
533− (discon''', graph''') <- evalGraphOp stamp tempBranchTransId discon'' graph'' (JumpToHead mergeToHeadName)
560+ (discon''', graph''') <- evalGraphOp stamp' tempBranchTransId discon'' graph'' (JumpToHead mergeToHeadName)
534561
535562 --create the merge
536− (discon'''', graph'''') <- evalGraphOp stamp mergeTransId discon''' graph''' (MergeTransactions strat tempBranchName mergeToHeadName)
563+ (discon'''', graph'''') <- evalGraphOp stamp' mergeTransId discon''' graph''' (MergeTransactions strat tempBranchName mergeToHeadName)
537564
538565 --delete the temp branch
539− (discon''''', graph''''') <- evalGraphOp stamp tempBranchTransId discon'''' graph'''' (DeleteBranch tempBranchName)
566+ (discon''''', graph''''') <- evalGraphOp stamp' tempBranchTransId discon'''' graph'''' (DeleteBranch tempBranchName)
540567 {-
541568 let rel = runReader (evalRelationalExpr (RelationVariable "s" ())) (mkRelationalExprState $ D.concreteDatabaseContext discon'''')
542569 traceShowM rel
@@ -544,4 +571,4 @@ autoMergeToHead stamp (tempBranchTransId, tempCommitTransId, mergeTransId) disco
544571
545572 pure (discon''''', graph''''')
546573
547−
574+
src/lib/ProjectM36/TransactionGraph/Merge.hs+16−12
@@ -2,10 +2,11 @@
22module ProjectM36.TransactionGraph.Merge where
33import ProjectM36.Base
44import ProjectM36.Error
5+import ProjectM36.RelationalExpression
6+import Control.Monad.Except hiding (join)
57import qualified Data.Set as S
68import qualified Data.Map as M
79import qualified ProjectM36.TypeConstructorDef as TCD
8−import ProjectM36.Relation
910import Control.Monad (foldM)
1011import qualified Data.HashSet as HS
1112
@@ -22,19 +23,22 @@ unionMergeMaps prefer mapA mapB = case prefer of
2223 Left StrategyViolatesComponentMergeError
2324
2425-- perform the merge even if the attributes are different- is this what we want? Obviously, we need finer-grained merge options.
25−unionMergeRelation :: MergePreference -> Relation -> Relation -> Either MergeError Relation
26−unionMergeRelation prefer relA relB = case relA `union` relB of
27− Right unionRel -> pure unionRel
28− Left (AttributeNamesMismatchError _) -> preferredRelVar
29− Left _ -> Left StrategyViolatesRelationVariableMergeError
30− where
31− preferredRelVar = case prefer of
32− PreferFirst -> Right relA
33− PreferSecond -> Right relB
34− PreferNeither -> Left StrategyViolatesRelationVariableMergeError
26+unionMergeRelation :: MergePreference -> GraphRefRelationalExpr -> GraphRefRelationalExpr -> GraphRefRelationalExprM GraphRefRelationalExpr
27+unionMergeRelation prefer relA relB = do
28+ let unioned = Union relA relB
29+ mergeErr = MergeTransactionError StrategyViolatesRelationVariableMergeError
30+ preferredRelVar =
31+ case prefer of
32+ PreferFirst -> pure relA
33+ PreferSecond -> pure relB
34+ PreferNeither -> throwError mergeErr
35+ handler AttributeNamesMismatchError{} = preferredRelVar
36+ handler _err' = throwError mergeErr
37+ --typecheck first?
38+ (evalGraphRefRelationalExpr unioned >> pure (Union relA relB)) `catchError` handler
3539
3640--try to execute unions against the relvars contents -- if a relvar only appears in one context, include it
37−unionMergeRelVars :: MergePreference -> RelationVariables -> RelationVariables -> Either MergeError RelationVariables
41+unionMergeRelVars :: MergePreference -> RelationVariables -> RelationVariables -> GraphRefRelationalExprM RelationVariables
3842unionMergeRelVars prefer relvarsA relvarsB = do
3943 let allNames = S.toList (S.union (M.keysSet relvarsA) (M.keysSet relvarsB))
4044 foldM (\acc name -> do
src/lib/ProjectM36/TransactionGraph/Persist.hs+6−5
@@ -1,8 +1,8 @@
11module ProjectM36.TransactionGraph.Persist where
22import ProjectM36.Error
3−import ProjectM36.TransactionGraph
43import ProjectM36.Transaction
54import ProjectM36.Transaction.Persist
5+import ProjectM36.RelationalExpression
66import ProjectM36.Base
77import ProjectM36.ScriptSession
88import ProjectM36.Persist (writeFileSync, renameSync, DiskSync)
@@ -194,12 +194,12 @@ writeGraphTransactionIdFile sync destDirectory (TransactionGraph _ transSet) = w
194194 uuidInfo = T.intercalate "\n" graphLines
195195 digest = SHA256.hash (encodeUtf8 uuidInfo)
196196 graphLines = S.toList $ S.map graphLine transSet
197− epochTime = realToFrac . utcTimeToPOSIXSeconds . transactionTimestamp :: Transaction -> Double
197+ epochTime = realToFrac . utcTimeToPOSIXSeconds . timestamp :: Transaction -> Double
198198 graphLine trans = U.toText (transactionId trans)
199199 <> " "
200200 <> T.pack (show (epochTime trans))
201201 <> " "
202− <> T.intercalate " " (S.toList (S.map U.toText $ transactionParentIds trans))
202+ <> T.intercalate " " (S.toList (S.map U.toText $ parentIds trans))
203203
204204readGraphTransactionIdFileDigest :: FilePath -> IO LockFileHash
205205readGraphTransactionIdFileDigest dbdir = do
@@ -209,8 +209,8 @@ readGraphTransactionIdFileDigest dbdir = do
209209readGraphTransactionIdFile :: FilePath -> IO (Either PersistenceError [(TransactionId, UTCTime, [TransactionId])])
210210readGraphTransactionIdFile dbdir = do
211211 --read in all transactions' uuids
212− let grapher line = let tid:epochText:parentIds = T.words line in
213− (readUUID tid, readEpoch epochText, map readUUID parentIds)
212+ let grapher line = let tid:epochText:parentIds' = T.words line in
213+ (readUUID tid, readEpoch epochText, map readUUID parentIds')
214214 readUUID uuidText = fromMaybe (error "failed to read uuid") (U.fromText uuidText)
215215 readEpoch t = posixSecondsToUTCTime (realToFrac (either (error "failed to read epoch") fst (double t)))
216216 Right . map grapher . T.lines <$> readUTF8FileOrError (transactionLogPath dbdir)
@@ -225,3 +225,4 @@ readUTF8FileOrError pathIn = do
225225 case TE.decodeUtf8' fileBytes of
226226 Left err -> error (show err)
227227 Right utf8Bytes -> pure utf8Bytes
228+
src/lib/ProjectM36/TransactionGraph/Show.hs+3−4
@@ -1,17 +1,16 @@
11module ProjectM36.TransactionGraph.Show where
22import ProjectM36.Base
33import ProjectM36.TransactionGraph
4−import ProjectM36.Transaction
54import qualified Data.Set as S
65
76showTransactionStructure :: Transaction -> TransactionGraph -> String
8−showTransactionStructure trans graph = headInfo ++ " " ++ show (transactionId trans) ++ " p" ++ parentTransactionsInfo ++ " c" ++ childTransactionsInfo
7+showTransactionStructure trans graph = headInfo ++ " " ++ show (transactionId trans) ++ " p" ++ parentTransactionsInfo
98 where
109 headInfo = maybe "" show (headNameForTransaction trans graph)
11− parentTransactionsInfo = if isRootTransaction trans graph then "root" else case parentTransactions trans graph of
10+ parentTransactionsInfo = if isRootTransaction trans then "root" else case parentTransactions trans graph of
1211 Left err -> show err
1312 Right parentTransSet -> concat $ S.toList $ S.map (show . transactionId) parentTransSet
14− childTransactionsInfo = show (S.toList (transactionChildIds trans))
13+
1514
1615showGraphStructure :: TransactionGraph -> String
1716showGraphStructure graph@(TransactionGraph _ transSet) = S.foldr folder "" transSet
src/lib/ProjectM36/TransactionGraph/Types.hsnew file+12
@@ -0,0 +1,12 @@
1+module ProjectM36.TransactionGraph.Types where
2+import ProjectM36.Base
3+
4+transactions :: TransactionGraph -> S.Set Transaction
5+transactions (TransactionGraph _ t) = t
6+
7+transactionHeads :: TransactionGraph -> TransactionHeads
8+transactionHeads (TransactionGraph heads _) = heads
9+
10+transactionInfoDiffs :: TransactionInfo -> TransactionDiffs
11+transactionInfoDiffs (TransactionInfo _ d _) = d
12+transactionInfoDiffs (MergeTransactionInfo _ _ d _ ) = d
src/lib/ProjectM36/TransactionInfo.hsnew file+10
@@ -0,0 +1,10 @@
1+module ProjectM36.TransactionInfo where
2+import ProjectM36.Base
3+import Data.Time.Clock
4+import Data.List.NonEmpty
5+
6+-- | Create a TransactionInfo with just one parent transaction ID.
7+singleParent :: TransactionId -> UTCTime -> TransactionInfo
8+singleParent tid stamp' = TransactionInfo {
9+ parents = tid :| [],
10+ stamp = stamp' }
src/lib/ProjectM36/WithNameExpr.hsnew file+84
@@ -0,0 +1,84 @@
1+module ProjectM36.WithNameExpr where
2+import ProjectM36.Base
3+
4+-- substitute all instances of With-based macros to remove macro context
5+-- ideally, we would use a different relational expr type to "prove" that the with macros can no longer exist
6+type WithNameAssocs = [(GraphRefWithNameExpr, GraphRefRelationalExpr)]
7+-- | Drop macros into the relational expression wherever they are referenced.
8+substituteWithNameMacros ::
9+ WithNameAssocs ->
10+ GraphRefRelationalExpr ->
11+ GraphRefRelationalExpr
12+substituteWithNameMacros _ e@MakeRelationFromExprs{} = e
13+substituteWithNameMacros _ e@MakeStaticRelation{} = e
14+substituteWithNameMacros _ e@ExistingRelation{} = e
15+substituteWithNameMacros macros e@(RelationVariable rvname tid) =
16+ let
17+ macroFilt (WithNameExpr macroName macroTid, _) = rvname == macroName && tid== macroTid in
18+ case filter macroFilt macros of
19+ [] -> e
20+ [(_,replacement)] -> replacement
21+ _ -> error "more than one macro matched!"
22+substituteWithNameMacros macros (Project attrs expr) =
23+ Project attrs (substituteWithNameMacros macros expr)
24+substituteWithNameMacros macros (Union exprA exprB) =
25+ Union (substituteWithNameMacros macros exprA) (substituteWithNameMacros macros exprB)
26+substituteWithNameMacros macros (Join exprA exprB) =
27+ Join (substituteWithNameMacros macros exprA) (substituteWithNameMacros macros exprB)
28+substituteWithNameMacros macros (Rename attrA attrB expr) =
29+ Rename attrA attrB (substituteWithNameMacros macros expr)
30+substituteWithNameMacros macros (Difference exprA exprB) =
31+ Difference (substituteWithNameMacros macros exprA) (substituteWithNameMacros macros exprB)
32+substituteWithNameMacros macros (Group attrs attr expr) =
33+ Group attrs attr (substituteWithNameMacros macros expr)
34+substituteWithNameMacros macros (Ungroup attr expr) =
35+ Ungroup attr (substituteWithNameMacros macros expr)
36+substituteWithNameMacros macros (Restrict pred' expr) =
37+ Restrict (substituteWithNameMacrosRestrictionPredicate macros pred') (substituteWithNameMacros macros expr)
38+substituteWithNameMacros macros (Equals exprA exprB) =
39+ Equals (substituteWithNameMacros macros exprA) (substituteWithNameMacros macros exprB)
40+substituteWithNameMacros macros (NotEquals exprA exprB) =
41+ NotEquals (substituteWithNameMacros macros exprA) (substituteWithNameMacros macros exprB)
42+substituteWithNameMacros macros (Extend extendTup expr) =
43+ Extend (substituteWitNameMacrosExtendTupleExpr macros extendTup) (substituteWithNameMacros macros expr)
44+substituteWithNameMacros macros (With moreMacros expr) =
45+ --collect and override existing macros and recurse
46+ let newMacros = foldr macroFolder macros moreMacros
47+ macroFolder (wnexpr, mexpr) acc = filter (\(w,_) -> w /= wnexpr) acc ++ [(wnexpr, mexpr)] in
48+ --scan for a match- if it exists, replace it (representing a with clause at a lower level
49+ substituteWithNameMacros newMacros expr
50+
51+
52+substituteWithNameMacrosRestrictionPredicate :: WithNameAssocs -> GraphRefRestrictionPredicateExpr -> GraphRefRestrictionPredicateExpr
53+substituteWithNameMacrosRestrictionPredicate macros pred' =
54+ let sub = substituteWithNameMacrosRestrictionPredicate macros in
55+ case pred' of
56+ TruePredicate -> pred'
57+ AndPredicate exprA exprB ->
58+ AndPredicate (sub exprA) (sub exprB)
59+ OrPredicate exprA exprB ->
60+ OrPredicate (sub exprA) (sub exprB)
61+ NotPredicate expr ->
62+ NotPredicate (sub expr)
63+ RelationalExprPredicate reexpr ->
64+ RelationalExprPredicate (substituteWithNameMacros macros reexpr)
65+ AtomExprPredicate atomExpr ->
66+ AtomExprPredicate (substituteWithNameMacrosAtomExpr macros atomExpr)
67+ AttributeEqualityPredicate attrName atomExpr ->
68+ AttributeEqualityPredicate attrName (substituteWithNameMacrosAtomExpr macros atomExpr)
69+
70+substituteWitNameMacrosExtendTupleExpr :: WithNameAssocs -> GraphRefExtendTupleExpr -> GraphRefExtendTupleExpr
71+substituteWitNameMacrosExtendTupleExpr macros (AttributeExtendTupleExpr attrName atomExpr) =
72+ AttributeExtendTupleExpr attrName (substituteWithNameMacrosAtomExpr macros atomExpr)
73+
74+substituteWithNameMacrosAtomExpr :: WithNameAssocs -> GraphRefAtomExpr -> GraphRefAtomExpr
75+substituteWithNameMacrosAtomExpr macros atomExpr =
76+ case atomExpr of
77+ e@AttributeAtomExpr{} -> e
78+ e@NakedAtomExpr{} -> e
79+ FunctionAtomExpr fname atomExprs tid ->
80+ FunctionAtomExpr fname (map (substituteWithNameMacrosAtomExpr macros) atomExprs) tid
81+ RelationAtomExpr reExpr ->
82+ RelationAtomExpr (substituteWithNameMacros macros reExpr)
83+ ConstructedAtomExpr dconsName atomExprs tid ->
84+ ConstructedAtomExpr dconsName (map (substituteWithNameMacrosAtomExpr macros) atomExprs) tid
stack.ghc8.8.yamlnew file+54
@@ -0,0 +1,54 @@
1+# This file was automatically generated by stack init
2+# For more information, see: http://docs.haskellstack.org/en/stable/yaml_configuration/
3+
4+# Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2)
5+resolver: lts-15.11
6+
7+# Local packages, usually specified by relative directory name
8+packages:
9+- '.'
10+# Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3)
11+extra-deps:
12+- git: https://github.com/agentm/distributed-process.git
13+ commit: 688a5b96d4b7b36d90c3db676230b2c943604288
14+- extended-reals-0.2.3.0
15+- data-interval-2.0.1
16+- stm-containers-1.1.0.4
17+- distributed-static-0.3.9
18+- stm-hamt-1.2.0.4
19+- distributed-process-async-0.2.6
20+- distributed-process-client-server-0.2.5.1
21+- distributed-process-extras-0.3.5
22+- rset-1.0.0
23+- hashable-time-0.2.0.2
24+- network-transport-tcp-0.7.0
25+- megaparsec-7.0.5
26+- network-2.8.0.1
27+
28+# Override default flag values for local packages and extra-deps
29+flags:
30+ project-m36:
31+ stack: true
32+
33+# Extra package databases containing global packages
34+extra-package-dbs: []
35+
36+# Control whether we use the GHC we find on the path
37+# system-ghc: true
38+
39+# Require a specific version of stack, using version ranges
40+# require-stack-version: -any # Default
41+# require-stack-version: >= 1.0.0
42+
43+# Override the architecture used by stack, especially useful on Windows
44+# arch: i386
45+# arch: x86_64
46+
47+# Extra directories used by stack for building
48+# extra-include-dirs: [/path/to/dir]
49+# extra-lib-dirs: [/path/to/dir]
50+
51+# Allow a newer minor version of GHC than the snapshot specifies
52+# compiler-check: newer-minor
53+
54+allow-newer: true
test/IsomorphicSchema.hs+37−27
@@ -4,13 +4,13 @@ import ProjectM36.Error
44import ProjectM36.IsomorphicSchema
55import ProjectM36.Relation
66import ProjectM36.RelationalExpression
7+import ProjectM36.TransactionGraph
8+import ProjectM36.StaticOptimizer
79import qualified ProjectM36.DatabaseContext as DBC
810import qualified ProjectM36.Attribute as A
911import System.Exit
1012import qualified Data.Map as M
1113import qualified Data.Set as S
12−import Control.Monad.State
13−import Control.Monad.Trans.Reader
1414
1515testList :: Test
1616testList = TestList [testIsoRename, testIsoRestrict, testIsoUnion, testSchemaValidation]
@@ -36,7 +36,7 @@ assertMaybe x msg = case x of
3636testSchemaValidation :: Test
3737testSchemaValidation = TestCase $ do
3838 let potentialSchema = DBC.basicDatabaseContext {
39− relationVariables = M.singleton "anotherRel" relationTrue
39+ relationVariables = M.singleton "anotherRel" (ExistingRelation relationTrue)
4040 }
4141 -- missing relvars failure
4242 morphs = [IsoRename "true" "true", IsoRename "false" "false"]
@@ -50,15 +50,17 @@ testSchemaValidation = TestCase $ do
5050testIsoRename :: Test
5151testIsoRename = TestCase $ do
5252 -- create a schema with two relvars and rename one while the other remains the same in the isomorphic schema
53− let schemaA = mkRelationalExprState DBC.empty {
54− relationVariables = M.fromList [("employee", relationTrue),
55− ("department", relationFalse)]
53+ let ctx = DBC.empty {
54+ relationVariables = M.fromList [("employee", ExistingRelation relationTrue),
55+ ("department", ExistingRelation relationFalse)]
5656 }
57− isomorphsAtoB = [IsoRename "emp" "employee",
57+ (graph, _) <- freshTransactionGraph ctx
58+ let isomorphsAtoB = [IsoRename "emp" "employee",
5859 IsoRename "department" "department"]
59− unionExpr = Union (RelationVariable "emp" ()) (RelationVariable "department" ())
60+ unionExpr = Union (RelationVariable "emp" ()) (RelationVariable "department" ())
61+ env = mkRelationalExprEnv ctx graph
6062 relExpr <- assertEither (applyRelationalExprSchemaIsomorphs isomorphsAtoB unionExpr)
61− let relResult = runReader (evalRelationalExpr relExpr) schemaA
63+ let relResult = optimizeAndEvalRelationalExpr env relExpr
6264 assertEqual "employee relation morph" (Right relationTrue) relResult
6365
6466testIsoRestrict :: Test
@@ -67,62 +69,70 @@ testIsoRestrict = TestCase $ do
6769 -- the virtual schema has an employee
6870 let empattrs = A.attributesFromList [Attribute "name" TextAtomType,
6971 Attribute "boss" TextAtomType]
72+ (graph, transId) <- freshTransactionGraph DBC.empty
7073 emprel <- assertEither $ mkRelationFromList empattrs
7174 [[TextAtom "Steve", TextAtom ""],
7275 [TextAtom "Cindy", TextAtom "Steve"],
7376 [TextAtom "Sam", TextAtom "Steve"]]
7477 let predicate = AttributeEqualityPredicate "boss" (NakedAtomExpr (TextAtom ""))
75− bossRel <- assertEither $ runReader (evalRelationalExpr (Restrict predicate (ExistingRelation emprel))) (mkRelationalExprState DBC.empty)
76− nonBossRel <- assertEither $ runReader (evalRelationalExpr (Restrict (NotPredicate predicate) (ExistingRelation emprel))) (mkRelationalExprState DBC.empty)
78+ reenv = mkRelationalExprEnv DBC.empty graph
79+ bossRel <- assertEither $ runRelationalExprM reenv (evalRelationalExpr (Restrict predicate (ExistingRelation emprel)))
80+ nonBossRel <- assertEither $ runRelationalExprM reenv (evalRelationalExpr (Restrict (NotPredicate predicate) (ExistingRelation emprel)))
7781
78− let schemaA = mkRelationalExprState DBC.empty {
79− relationVariables = M.fromList [("nonboss", nonBossRel),
80− ("boss", bossRel)]
82+ let schemaA = mkRelationalExprEnv baseContext graph
83+ baseContext = DBC.empty {
84+ relationVariables = M.fromList [("nonboss", ExistingRelation nonBossRel),
85+ ("boss", ExistingRelation bossRel)]
8186 }
82−
8387 isomorphsAtoB = [IsoRestrict "employee" predicate ("boss", "nonboss")]
8488 bossq = RelationVariable "boss" ()
8589 nonbossq = RelationVariable "nonboss" ()
8690 employeeq = RelationVariable "employee" ()
8791 unionq = Union bossq nonbossq
8892 empExpr <- assertEither (applyRelationalExprSchemaIsomorphs isomorphsAtoB employeeq)
89− let empResult = runReader (evalRelationalExpr empExpr) schemaA
90− unionResult = runReader (evalRelationalExpr unionq) schemaA
93+ let empResult = evalRelExpr (evalRelationalExpr empExpr)
94+ unionResult = evalRelExpr (evalRelationalExpr unionq)
95+ evalRelExpr = runRelationalExprM schemaA
9196 assertEqual "boss/nonboss isorestrict" unionResult empResult
9297 --execute database context expr
9398 bobRel <- assertEither (mkRelationFromList empattrs [[TextAtom "Bob", TextAtom ""]])
9499 let schemaBInsertExpr = Insert "employee" (ExistingRelation bobRel)
95100 schemaBInsertExpr' <- assertEither (processDatabaseContextExprInSchema (Schema isomorphsAtoB) schemaBInsertExpr)
96− let (postInsertContext,_,_) = execState (evalDatabaseContextExpr schemaBInsertExpr') (freshDatabaseState (stateElemsContext schemaA))
97− expectedRel = runReader (evalRelationalExpr (Union (RelationVariable "boss" ()) (RelationVariable "nonboss" ()))) (mkRelationalExprState postInsertContext)
101+ let Right dbcState = evalDBCExpr schemaBInsertExpr'
102+ postInsertContext = dbc_context dbcState
103+ evalDBCExpr expr' = runDatabaseContextEvalMonad baseContext dbcenv (optimizeAndEvalDatabaseContextExpr False expr')
104+ dbcenv = mkDatabaseContextEvalEnv transId graph
105+ expectedRel = runRelationalExprM postInsertEnv (evalRelationalExpr (Union (RelationVariable "boss" ()) (RelationVariable "nonboss" ())))
106+ postInsertEnv = mkRelationalExprEnv postInsertContext graph
98107 --execute the expression against the schema and compare against the base context
99108 processedExpr <- assertEither (processRelationalExprInSchema (Schema isomorphsAtoB) (RelationVariable "employee" ()))
100− let processedRel = runReader (evalRelationalExpr processedExpr) (mkRelationalExprState postInsertContext)
109+ let processedRel = runRelationalExprM postInsertEnv (evalRelationalExpr processedExpr)
101110 assertEqual "insert bob boss" expectedRel processedRel
102111
103112testIsoUnion :: Test
104113testIsoUnion = TestCase $ do
105114 --create motors relation which is split into low-power (<50 horsepower) and high-power (>=50 horsepower) motors
106115 --the schema is contains the split relvars
116+ (graph, _) <- freshTransactionGraph DBC.empty
107117 motorsRel <- assertEither $ mkRelationFromList (A.attributesFromList [Attribute "name" TextAtomType,
108118 Attribute "power" IntegerAtomType])
109119 [[TextAtom "Puny", IntegerAtom 10],
110120 [TextAtom "Scooter", IntegerAtom 49],
111121 [TextAtom "Auto", IntegerAtom 200],
112122 [TextAtom "Tractor", IntegerAtom 500]]
113− let baseSchema = mkRelationalExprState DBC.basicDatabaseContext {
114− relationVariables = M.singleton "motor" motorsRel
115− }
123+ let env = mkRelationalExprEnv DBC.basicDatabaseContext {
124+ relationVariables = M.singleton "motor" (ExistingRelation motorsRel)
125+ } graph
116126 splitPredicate = AtomExprPredicate (FunctionAtomExpr "lt" [AttributeAtomExpr "power", NakedAtomExpr (IntegerAtom 50)] ())
117127 splitIsomorphs = [IsoUnion ("lowpower", "highpower") splitPredicate "motor",
118128 IsoRename "true" "true",
119129 IsoRename "false" "false"]
120− lowmotors = runReader (evalRelationalExpr (Restrict splitPredicate (RelationVariable "motor" ()))) baseSchema
121− highmotors = runReader (evalRelationalExpr (Restrict (NotPredicate splitPredicate) (RelationVariable "motor" ()))) baseSchema
122− relResult expr = runReader (evalRelationalExpr expr) baseSchema
130+ lowmotors = runRelationalExprM env (evalRelationalExpr (Restrict splitPredicate (RelationVariable "motor" ())))
131+ highmotors = runRelationalExprM env (evalRelationalExpr (Restrict (NotPredicate splitPredicate) (RelationVariable "motor" ())))
132+ relResult expr = runRelationalExprM env (evalRelationalExpr expr)
123133 lowpowerExpr <- assertEither (processRelationalExprInSchema (Schema splitIsomorphs) (RelationVariable "lowpower" ()))
124134 lowpowerRel <- assertEither (relResult lowpowerExpr)
125135 highpowerExpr <- assertEither (processRelationalExprInSchema (Schema splitIsomorphs) (RelationVariable "highpower" ()))
126136 highpowerRel <- assertEither (relResult highpowerExpr)
127137 assertEqual "lowpower relation difference" (Right lowpowerRel) lowmotors
128− assertEqual "highpower relation difference" (Right highpowerRel) highmotors
138+ assertEqual "highpower relation difference" (Right highpowerRel) highmotors
test/Relation/Atomable.hs+1−2
@@ -104,7 +104,7 @@ testHaskell2DB = TestCase $ do
104104 let createRelExpr = Assign "x" rel
105105
106106 rel = MakeRelationFromExprs Nothing
107− [TupleExpr (M.singleton "a1" (NakedAtomExpr atomVal))]
107+ (TupleExprs () [TupleExpr (M.singleton "a1" (NakedAtomExpr atomVal))])
108108 atomVal = toAtom exampleVal
109109 exampleVal = Test1C 10
110110 checkExecuteDatabaseContextExpr sessionId dbconn createRelExpr
@@ -194,4 +194,3 @@ testManyFields = TestCase $ do
194194
195195 let example5 = Test9_5C 1 2 3 4 5
196196 assertEqual "five fields product type" example5 (fromAtom (toAtom example5))
197−
test/Relation/Basic.hs+10−4
@@ -7,9 +7,9 @@ import ProjectM36.TupleSet
77import ProjectM36.Attribute hiding (null, attributeNames)
88import ProjectM36.DataTypes.Primitive
99import ProjectM36.RelationalExpression
10+import ProjectM36.TransactionGraph
1011import ProjectM36.Tuple
1112import qualified ProjectM36.DatabaseContext as DBC
12−import Control.Monad.Trans.Reader
1313import qualified ProjectM36.Attribute as A
1414import qualified Data.Map as M
1515import qualified Data.Vector as V
@@ -94,8 +94,12 @@ testMkRelation1 = TestCase $ assertEqual "key mismatch" expectedError (mkRelatio
9494
9595testMkRelationFromExprsBadAttrs :: Test
9696testMkRelationFromExprsBadAttrs = TestCase $ do
97− let context = DBC.empty
98− case runReader (evalRelationalExpr (MakeRelationFromExprs (Just [AttributeAndTypeNameExpr "badAttr1" (PrimitiveTypeConstructor "Int" IntAtomType) ()]) [TupleExpr (M.singleton "badAttr2" (NakedAtomExpr (IntAtom 1)))])) (mkRelationalExprState context) of
97+ let context = DBC.empty
98+ (graph,_) <- freshTransactionGraph context
99+ let reenv = mkRelationalExprEnv context graph
100+ reExpr = MakeRelationFromExprs (Just [AttributeAndTypeNameExpr "badAttr1" (PrimitiveTypeConstructor "Int" IntAtomType) ()]) (TupleExprs () [TupleExpr (M.singleton "badAttr2" (NakedAtomExpr (IntAtom 1)))])
101+ evald = runRelationalExprM reenv (evalRelationalExpr reExpr)
102+ case evald of
99103 Left err -> assertEqual "tuple type mismatch" (TupleAttributeTypeMismatchError (A.attributesFromList [Attribute "badAttr2" IntAtomType])) err
100104 Right _ -> assertFailure "expected tuple type mismatch"
101105
@@ -108,5 +112,7 @@ testDuplicateAttributes = TestCase $ do
108112
109113testExistingRelationType :: Test
110114testExistingRelationType = TestCase $ do
111− let typeResult = runReader (typeForRelationalExpr (ExistingRelation relationTrue)) (RelationalExprStateElems DBC.empty)
115+ (graph, _) <- freshTransactionGraph dateExamples
116+ let typeResult = runRelationalExprM reenv (typeForRelationalExpr (ExistingRelation relationTrue))
117+ reenv = mkRelationalExprEnv dateExamples graph
112118 assertEqual "ExistingRelation with tuples type" (Right relationFalse) typeResult
test/Relation/StaticOptimizer.hs+76−51
@@ -1,13 +1,12 @@
11import ProjectM36.Base
22import ProjectM36.Relation
3−import ProjectM36.RelationalExpression
3+import ProjectM36.TransactionGraph
44import ProjectM36.DateExamples
5−import ProjectM36.Error
65import ProjectM36.TupleSet
76import ProjectM36.StaticOptimizer
7+import ProjectM36.DatabaseContext as DBC
88import System.Exit
99import Control.Monad.State
10−import Control.Monad.Trans.Reader
1110import Test.HUnit
1211import qualified Data.Set as S
1312
@@ -16,100 +15,126 @@ main = do
1615 tcounts <- runTestTT (TestList tests)
1716 if errors tcounts + failures tcounts > 0 then exitFailure else exitSuccess
1817 where
19− tests = relationOptTests ++ databaseOptTests ++ [testTransitiveOptimization,
20− testImpossiblePredicates,
21− testJoinElimination,
22− testRestrictionPredicateCollapse,
23− testPushDownRestrictionPredicate]
24− optDBCTest = map (\(name, expr, unopt) -> TestCase $ assertEqual name expr (evalState (applyStaticDatabaseOptimization unopt) (freshDatabaseState dateExamples)))
25− relvar nam = RelationVariable nam ()
26− startState = mkRelationalExprState dateExamples
27− optRelTest = map (\(name, expr, unopt) -> TestCase $ assertEqual name expr (runReader (applyStaticRelationalOptimization unopt) startState))
28− relationOptTests = optRelTest [
29− ("StaticProject", Right $ relvar "s", Project (AttributeNames (attributeNames suppliersRel)) (relvar "s")),
30− ("StaticUnion", Right $ relvar "s", Union (relvar "s") (relvar "s")),
31− ("StaticJoin", Right $ relvar "s", Join (relvar "s") (relvar "s"))
32− ]
33− databaseOptTests = optDBCTest [
34− ("StaticAssign",
35− Right $ Assign "z" (relvar "s"),
36− Assign "z" (Restrict TruePredicate (relvar "s"))
37− ),
38− ("StaticMultipleExpr",
39− Right $ MultipleExpr [Assign "z" (relvar "s"),
40− Assign "z" (relvar "s")],
41− MultipleExpr [Assign "z" (Restrict TruePredicate (relvar "s")),
42− Assign "z" (Restrict TruePredicate (relvar "s"))])
43− ]
18+ tests = [testTransitiveOptimization,
19+ testImpossiblePredicates,
20+ testJoinElimination,
21+ testRestrictionPredicateCollapse,
22+ testPushDownRestrictionPredicate,
23+ testOptimizeRelationalExpr,
24+ testOptimizeDatabaseContextExpr]
25+
26+testOptimizeDatabaseContextExpr :: Test
27+testOptimizeDatabaseContextExpr = TestCase $ do
28+ (graph, transId) <- freshTransactionGraph DBC.empty
29+ let relvar nam = RelationVariable nam ()
30+ relvarId nam = RelationVariable nam UncommittedContextMarker
31+ testInfos = [
32+ ("StaticAssign",
33+ Right $ Assign "z" (relvarId "s"),
34+ Assign "z" (Restrict TruePredicate (relvar "s"))
35+ ),
36+ ("StaticMultipleExpr",
37+ Right $ MultipleExpr [Assign "z" (relvarId "s"),
38+ Assign "z" (relvarId "s")],
39+ MultipleExpr [Assign "z" (Restrict TruePredicate (relvar "s")),
40+ Assign "z" (Restrict TruePredicate (relvar "s"))])
41+ ]
42+ forM_ testInfos $ \(name, expectedExpr, unoptExpr) -> do
43+ let optExpr = runGraphRefSOptDatabaseContextExprM transId dateExamples graph (optimizeDatabaseContextExpr unoptExpr)
44+ assertEqual name expectedExpr optExpr
4445
4546testTransitiveOptimization :: Test
4647testTransitiveOptimization = TestCase $ do
48+ (graph, _) <- freshTransactionGraph dateExamples
4749 let expr atomexpr = AndPredicate (AttributeEqualityPredicate "a" (NakedAtomExpr (IntegerAtom 300))) (AttributeEqualityPredicate "b" atomexpr)
4850 exprIn = expr (AttributeAtomExpr "a")
4951 exprOut = expr (NakedAtomExpr (IntegerAtom 300))
50− assertEqual "transitive property" (Right exprOut) (runReader (applyStaticPredicateOptimization exprIn) (RelationalExprStateElems dateExamples))
51−
52−optRelExpr :: RelationalExpr -> Either RelationalError RelationalExpr
53−optRelExpr expr = runReader (applyStaticRelationalOptimization expr) (RelationalExprStateElems dateExamples)
52+ optExpr = runGraphRefSOptRelationalExprM Nothing graph (applyStaticPredicateOptimization exprIn)
53+ assertEqual "transitive property" (Right exprOut) optExpr
54+
55+
56+testOptimizeRelationalExpr :: Test
57+testOptimizeRelationalExpr = TestCase $ do
58+ (graph, _) <- freshTransactionGraph dateExamples
59+ let relvarId nam = RelationVariable nam UncommittedContextMarker
60+ relationOptTests = [
61+ ("StaticProject",
62+ Right $ relvarId "s",
63+ Project (AttributeNames (attributeNames suppliersRel)) (relvarId "s")),
64+ ("StaticUnion",
65+ Right $ relvarId "s",
66+ Union (relvarId "s") (relvarId "s")),
67+ ("StaticJoin",
68+ Right $ relvarId "s",
69+ Join (relvarId "s") (relvarId "s"))
70+ ]
71+ forM_ relationOptTests $ \(name, expr, unoptExpr) -> do
72+ let optExpr = runGraphRefSOptRelationalExprM (Just dateExamples) graph (optimizeGraphRefRelationalExpr unoptExpr)
73+ assertEqual name expr optExpr
5474
5575testImpossiblePredicates :: Test
5676testImpossiblePredicates = TestCase $ do
77+ (graph, _) <- freshTransactionGraph dateExamples
5778 let tautTrue = Restrict (AtomExprPredicate (NakedAtomExpr (BoolAtom True))) rel
5879 tautTrue2 = Restrict TruePredicate rel
5980 tautFalse = Restrict (AtomExprPredicate (NakedAtomExpr (BoolAtom False))) rel
6081 tautFalse2 = Restrict (NotPredicate TruePredicate) rel
6182 emptyRel = MakeStaticRelation (attributes suppliersRel) emptyTupleSet
62− rel = RelationVariable "s" ()
83+ rel = RelationVariable "s" UncommittedContextMarker
84+ optRelExpr expr = optimizeGraphRefRelationalExpr' (Just dateExamples) graph expr
6385 assertEqual "remove tautology where true" (Right rel) (optRelExpr tautTrue)
6486 assertEqual "remove tautology where true2" (Right rel) (optRelExpr tautTrue2)
6587 assertEqual "remove tautology where false" (Right emptyRel) (optRelExpr tautFalse)
6688 assertEqual "remove tautology where false2" (Right emptyRel) (optRelExpr tautFalse2)
67−
68−optJoinExpr :: RelationalExpr -> Either RelationalError RelationalExpr
69−optJoinExpr expr = runReader (applyStaticJoinElimination expr) (RelationalExprStateElems dateExamples)
7089
7190testJoinElimination :: Test
7291testJoinElimination = TestCase $ do
92+ (graph, _) <- freshTransactionGraph dateExamples
93+ let marker = UncommittedContextMarker
94+ optJoinExpr expr = runGraphRefSOptRelationalExprM (Just dateExamples) graph (applyStaticJoinElimination expr)
95+ relvar rv = RelationVariable rv marker
96+
7397 -- (s join sp){s#,qty,p#} == sp
74− let unoptExpr = Project (AttributeNames (S.fromList ["p#", "qty", "s#"])) (Join (RelationVariable "sp" ()) (RelationVariable "s" ()))
75− assertEqual "remove extraneous join" (Right (RelationVariable "sp" ())) (optJoinExpr unoptExpr)
98+ let unoptExpr = Project (AttributeNames (S.fromList ["p#", "qty", "s#"])) (Join (relvar "sp") (relvar "s"))
99+ assertEqual "remove extraneous join" (Right (relvar "sp")) (optJoinExpr unoptExpr)
76100
77101-- (sp join s){s#,qty,p#} == sp --flip relvar names
78− let unoptExpr2 = Project (AttributeNames (S.fromList ["p#", "qty", "s#"])) (Join (RelationVariable "s" ()) (RelationVariable "sp" ()))
79− assertEqual "remove extraneous join2" (Right (RelationVariable "sp" ())) (optJoinExpr unoptExpr2)
102+ let unoptExpr2 = Project (AttributeNames (S.fromList ["p#", "qty", "s#"])) (Join (relvar "s") (relvar "sp"))
103+ assertEqual "remove extraneous join2" (Right (relvar "sp")) (optJoinExpr unoptExpr2)
80104
81105-- (sp join s){s#,qty,sname} != sp - attribute from s included
82− let expr3 = Project (AttributeNames (S.fromList ["p#", "qty", "sname"])) (Join (RelationVariable "sp" ()) (RelationVariable "s" ()))
106+ let expr3 = Project (AttributeNames (S.fromList ["p#", "qty", "sname"])) (Join (relvar "sp") (relvar "s"))
83107 assertEqual "retain join" (Right expr3) (optJoinExpr expr3)
84108
85109 -- (sp join s){qty} != sp --projection does not include foreign key attribute
86− let expr4 = Project (AttributeNames (S.singleton "qty")) (Join (RelationVariable "sp" ()) (RelationVariable "s" ()))
110+ let expr4 = Project (AttributeNames (S.singleton "qty")) (Join (relvar "sp") (relvar "s"))
87111 assertEqual "retain join2" (Right expr4) (optJoinExpr expr4)
88112
89113 -- (sp join s){s#,qty} == sp - a subset of attributes in the projection are in the foreign key
90− let expr5 = Project (AttributeNames (S.fromList ["s#", "qty"])) (Join (RelationVariable "sp" ()) (RelationVariable "s" ()))
91− let expect5 = Project (AttributeNames (S.fromList ["s#", "qty"])) (RelationVariable "sp" ())
114+ let expr5 = Project (AttributeNames (S.fromList ["s#", "qty"])) (Join (relvar "sp") (relvar "s"))
115+ let expect5 = Project (AttributeNames (S.fromList ["s#", "qty"])) (relvar "sp")
92116 assertEqual "remove extraneous join (attribute subset)" (Right expect5) (optJoinExpr expr5)
93117
94118 -- (s join p){s#,p#} != sp -- no foreign key
95− let expr6 = Project (AttributeNames (S.fromList ["s#", "p#"])) (Join (RelationVariable "s" ()) (RelationVariable "p" ()))
119+ let expr6 = Project (AttributeNames (S.fromList ["s#", "p#"])) (Join (relvar "s") (relvar "p"))
96120 assertEqual "retain join- no foreign key" (Right expr6) (optJoinExpr expr6)
97121
98122 -- (s join p){s#,qty,p#,sname} != sp -- all sp attributes plus one s attributes
99− let expr7 = Project (AttributeNames (S.fromList ["s#", "p#", "qty", "sname"])) (Join (RelationVariable "s" ()) (RelationVariable "sp" ()))
123+ let expr7 = Project (AttributeNames (S.fromList ["s#", "p#", "qty", "sname"])) (Join (relvar "s") (relvar "sp"))
100124 assertEqual "retain join- attributes from both relvars" (Right expr7) (optJoinExpr expr7)
101125
102126testRestrictionPredicateCollapse :: Test
103127testRestrictionPredicateCollapse = TestCase $ do
104128 let expr1 = Restrict (NotPredicate TruePredicate) rv
105− rv = RelationVariable "a" ()
129+ rv = RelationVariable "a" UncommittedContextMarker
130+ relvar name = RelationVariable name UncommittedContextMarker
106131 assertEqual "one restriction" expr1 (applyStaticRestrictionCollapse expr1)
107132
108133 let attrEqualsPred = AttributeEqualityPredicate "x" (NakedAtomExpr (IntAtom 3))
109− expr2 = Restrict TruePredicate (Restrict attrEqualsPred (RelationVariable "a" ()))
110− assertEqual "two restrictions" (Restrict (AndPredicate TruePredicate attrEqualsPred) (RelationVariable "a" ())) (applyStaticRestrictionCollapse expr2)
134+ expr2 = Restrict TruePredicate (Restrict attrEqualsPred (relvar "a"))
135+ assertEqual "two restrictions" (Restrict (AndPredicate TruePredicate attrEqualsPred) (relvar "a")) (applyStaticRestrictionCollapse expr2)
111136
112− let expr3 = Restrict pred1 (Restrict pred2 (Restrict pred3 (RelationVariable "a" ())))
137+ let expr3 = Restrict pred1 (Restrict pred2 (Restrict pred3 (relvar "a")))
113138 pred1 = NotPredicate TruePredicate
114139 pred2 = attrEqualsPred
115140 pred3 = AtomExprPredicate (NakedAtomExpr (BoolAtom True))
@@ -123,7 +148,7 @@ testRestrictionPredicateCollapse = TestCase $ do
123148testPushDownRestrictionPredicate :: Test
124149testPushDownRestrictionPredicate = TestCase $ do
125150 -- (s union relation{tuple{city "Boston", s# "S6", sname "Stevens", status 50}}) where status = 30 == (s where status=30) union (relation{tuple{city "Boston", s# "S6", sname "Stevens", status 50}} where status=30)
126− let rvs = RelationVariable "s" ()
151+ let rvs = RelationVariable "s" UncommittedContextMarker
127152 let status30 = AttributeEqualityPredicate "status" (NakedAtomExpr (IntAtom 30))
128153 relationsOrError = do
129154 stevens <- mkRelationFromList (attributes suppliersRel) [[TextAtom "S6", TextAtom "Stevens", IntegerAtom 50, TextAtom "Boston"]]
test/Relation/Tupleable.hs+1−1
@@ -1,4 +1,4 @@
1−{-# LANGUAGE DeriveGeneric, FlexibleInstances, FlexibleContexts, TypeOperators, DeriveAnyClass #-}
1+{-# LANGUAGE DeriveGeneric, FlexibleInstances, FlexibleContexts, DeriveAnyClass #-}
22import Test.HUnit
33import ProjectM36.Tupleable
44import ProjectM36.Atomable
test/Server/Main.hs+2−1
@@ -147,10 +147,11 @@ testPlanForDatabaseContextExpr sessionId conn = TestCase $ do
147147 let attrExprs = [AttributeAndTypeNameExpr "x" (PrimitiveTypeConstructor "Int" IntAtomType) ()]
148148 testrv = "testrv"
149149 dbExpr = Define testrv attrExprs
150+ expected = Define testrv [AttributeAndTypeNameExpr "x" (PrimitiveTypeConstructor "Int" IntAtomType) UncommittedContextMarker]
150151 planResult <- planForDatabaseContextExpr sessionId conn dbExpr
151152 case planResult of
152153 Left err -> assertFailure (show err)
153− Right plan -> assertEqual "planForDatabaseContextExpr failure" dbExpr plan
154+ Right plan -> assertEqual "planForDatabaseContextExpr failure" expected plan
154155
155156testTransactionGraphAsRelation :: SessionId -> Connection -> Test
156157testTransactionGraphAsRelation sessionId conn = TestCase $ do
test/Server/WebSocket.hs+3−2
@@ -57,13 +57,14 @@ waitForListenSocket secondsToTry port =
5757 if secondsToTry <= 0 then
5858 throw WaitForSocketListenException
5959 else do
60− hostaddr <- inet_addr "127.0.0.1"
60+ --hostaddr <- inet_addr "127.0.0.1"
61+ hostaddr:_ <- getAddrInfo Nothing (Just "127.0.0.1") (Just (show port))
6162 sock <- socket AF_INET Stream defaultProtocol
6263 let handler :: IOException -> IO ()
6364 handler _ = do
6465 threadDelay 1000000
6566 waitForListenSocket (secondsToTry - 1) port
66− catch (connect sock (SockAddrInet port hostaddr)) handler
67+ catch (connect sock (addrAddress hostaddr)) handler
6768
6869main :: IO ()
6970main = do
test/TransactionGraph/Merge.hs+60−39
@@ -10,6 +10,7 @@ import ProjectM36.Key
1010import qualified ProjectM36.DisconnectedTransaction as Discon
1111import qualified ProjectM36.DatabaseContext as DBC
1212import ProjectM36.RelationalExpression
13+import ProjectM36.StaticOptimizer
1314
1415import qualified Data.ByteString.Lazy as BS
1516import System.Exit
@@ -20,8 +21,7 @@ import qualified Data.Map as M
2021import Data.Maybe
2122import Data.Time.Clock
2223import Data.Time.Calendar
23−
24−import Control.Monad.State
24+import qualified Data.List.NonEmpty as NE
2525
2626{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}
2727
@@ -61,9 +61,10 @@ basicTransactionGraph = do
6161 uuidA = fakeUUID 10
6262 uuidB = fakeUUID 11
6363 uuidRoot = fakeUUID 1
64+ parents' = uuidRoot NE.:| []
6465 rootContext = concreteDatabaseContext rootTrans
65− (_, bsGraph') <- addTransaction "branchA" (createTrans uuidA (TransactionInfo uuidRoot S.empty testTime) rootContext) bsGraph
66− (_, bsGraph'') <- addTransaction "branchB" (createTrans uuidB (TransactionInfo uuidRoot S.empty testTime) rootContext) bsGraph'
66+ (_, bsGraph') <- addTransaction "branchA" (createTrans uuidA (TransactionInfo parents' testTime) rootContext) bsGraph
67+ (_, bsGraph'') <- addTransaction "branchB" (createTrans uuidB (TransactionInfo parents' testTime) rootContext) bsGraph'
6768 pure bsGraph''
6869
6970addTransaction :: HeadName -> Transaction -> TransactionGraph -> IO (Transaction, TransactionGraph)
@@ -107,7 +108,7 @@ testSubGraphToFirstAncestorSnipBranch = TestCase $ do
107108 baseGraph <- basicTransactionGraph
108109 transA <- assertMaybe (transactionForHead "branchA" baseGraph) "failed to get branchA"
109110 transB <- assertMaybe (transactionForHead "branchB" baseGraph) "failed to get branchB"
110− (_, graph) <- addTransaction "branchC" (createTrans (fakeUUID 12) (TransactionInfo (fakeUUID 1) S.empty testTime) (concreteDatabaseContext transA)) baseGraph
111+ (_, graph) <- addTransaction "branchC" (createTrans (fakeUUID 12) (TransactionInfo (fakeUUID 1 NE.:| []) testTime) (concreteDatabaseContext transA)) baseGraph
111112 subgraph <- assertEither $ subGraphOfFirstCommonAncestor graph (transactionHeadsForGraph baseGraph) transA transB S.empty
112113 assertGraph subgraph
113114 let graphEq graphArg = S.map transactionId (transactionsForGraph graphArg)
@@ -121,16 +122,18 @@ testSubGraphToFirstAncestorMoreTransactions = TestCase $ do
121122
122123-- add another relvar to branchB
123124 branchBTrans <- assertMaybe (transactionForHead "branchB" graph) "failed to get branchB head"
124− (updatedBranchBContext,_,_) <- case runState (evalDatabaseContextExpr (Assign "branchBOnlyRelvar" (ExistingRelation relationTrue))) (freshDatabaseState (concreteDatabaseContext branchBTrans)) of
125− (Left err, _) -> assertFailure (show err) >> undefined
126− (Right (), context) -> pure context
127− (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans) S.empty testTime) updatedBranchBContext) graph
125+ let env = mkDatabaseContextEvalEnv (transactionId branchBTrans) graph
126+ branchBContext = concreteDatabaseContext branchBTrans
127+ updatedBranchBContext <- case runDatabaseContextEvalMonad branchBContext env (optimizeAndEvalDatabaseContextExpr True (Assign "branchBOnlyRelvar" (ExistingRelation relationTrue))) of
128+ Left err -> assertFailure (show err) >> undefined
129+ Right st -> pure $ dbc_context st
130+ (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans NE.:| []) testTime) updatedBranchBContext) graph
128131 branchBTrans' <- assertMaybe (transactionForHead "branchB" graph') "failed to get branchB head"
129132 assertEqual "branchB id 3" (fakeUUID 3) (transactionId branchBTrans')
130133
131134 -- add another transaction to branchA
132135 branchATrans <- assertMaybe (transactionForHead "branchA" graph) "failed to get branchA head"
133− (_, graph'') <- addTransaction "branchA" (createTrans (fakeUUID 4) (TransactionInfo (transactionId branchATrans) S.empty testTime) (concreteDatabaseContext branchATrans)) graph'
136+ (_, graph'') <- addTransaction "branchA" (createTrans (fakeUUID 4) (TransactionInfo (transactionId branchATrans NE.:| []) testTime) (concreteDatabaseContext branchATrans)) graph'
134137 branchATrans' <- assertMaybe (transactionForHead "branchA" graph'') "failed to get branchA head"
135138 assertEqual "branchA id 4" (fakeUUID 4) (transactionId branchATrans')
136139
@@ -148,12 +151,15 @@ testSelectedBranchMerge = TestCase $ do
148151
149152 -- add another relvar to branchB
150153 branchBTrans <- assertMaybe (transactionForHead "branchB" graph) "failed to get branchB head"
151− (updatedBranchBContext,_,_) <- case runState (evalDatabaseContextExpr (Assign "branchBOnlyRelvar" (ExistingRelation relationTrue))) (freshDatabaseState (concreteDatabaseContext branchBTrans)) of
152− (Left err, _) -> assertFailure (show err) >> undefined
153− (Right (), context) -> pure context
154− (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans) S.empty testTime) updatedBranchBContext) graph
154+ let env = mkDatabaseContextEvalEnv (transactionId branchBTrans) graph
155+ branchBContext = concreteDatabaseContext branchBTrans
156+ updatedBranchBContext <- case runDatabaseContextEvalMonad branchBContext env (optimizeAndEvalDatabaseContextExpr True (Assign "branchBOnlyRelvar" (ExistingRelation relationTrue))) of
157+ Left err -> assertFailure (show err) >> undefined
158+ Right st -> pure (dbc_context st)
159+ (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans NE.:| []) testTime) updatedBranchBContext) graph
155160 --create the merge transaction in the graph
156− let eGraph' = mergeTransactions testTime (fakeUUID 4) (fakeUUID 10) (SelectedBranchMergeStrategy "branchA") ("branchA", "branchB") graph'
161+ let eGraph' = runGraphRefRelationalExprM gfEnv $ mergeTransactions testTime (fakeUUID 4) (fakeUUID 10) (SelectedBranchMergeStrategy "branchA") ("branchA", "branchB")
162+ gfEnv = freshGraphRefRelationalExprEnv Nothing graph'
157163
158164 (_, graph'') <- assertEither eGraph'
159165
@@ -175,18 +181,22 @@ testUnionPreferMergeStrategy = TestCase $ do
175181 branchBTrans <- assertMaybe (transactionForHead "branchB" graph) "branchBTrans"
176182 branchBRelVar <- assertEither $ mkRelationFromList (attributesFromList [Attribute "conflict" IntAtomType]) []
177183 let branchAContext = (concreteDatabaseContext branchATrans) {relationVariables = M.insert conflictRelVarName branchARelVar (relationVariables (concreteDatabaseContext branchATrans))}
178− branchARelVar = relationTrue
179− branchBContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.insert conflictRelVarName branchBRelVar (relationVariables (concreteDatabaseContext branchBTrans))}
180− conflictRelVarName = "conflictRelVar"
184+ branchARelVar = ExistingRelation relationTrue
185+ branchBContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.insert conflictRelVarName (ExistingRelation branchBRelVar) (relationVariables (concreteDatabaseContext branchBTrans))}
186+ conflictRelVarName = "conflictRelVar"
181187
182− (_, graph') <- addTransaction "branchA" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchATrans) S.empty testTime) branchAContext) graph
183− (_, graph'') <- addTransaction "branchB" (createTrans (fakeUUID 4) (TransactionInfo (transactionId branchBTrans) S.empty testTime) branchBContext) graph'
188+ (_, graph') <- addTransaction "branchA" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchATrans NE.:| []) testTime) branchAContext) graph
189+ (_, graph'') <- addTransaction "branchB" (createTrans (fakeUUID 4) (TransactionInfo (transactionId branchBTrans NE.:| []) testTime) branchBContext) graph'
184190 -- validate that the conflict is hidden because we preferred a branch
185− let merged = mergeTransactions testTime (fakeUUID 5) (fakeUUID 3) (UnionPreferMergeStrategy "branchB") ("branchA", "branchB") graph''
191+ let merged = runGraphRefRelationalExprM env $ mergeTransactions testTime (fakeUUID 5) (fakeUUID 3) (UnionPreferMergeStrategy "branchB") ("branchA", "branchB")
192+ env = freshGraphRefRelationalExprEnv Nothing graph''
186193 case merged of
187194 Left err -> assertFailure ("expected merge success: " ++ show err)
188− Right (discon, _) ->
189− assertEqual "branchB relvar preferred in conflict" (Just branchBRelVar) (M.lookup conflictRelVarName (relationVariables (Discon.concreteDatabaseContext discon)))
195+ Right (discon, _) -> do
196+ let Just rvExpr = M.lookup conflictRelVarName (relationVariables (Discon.concreteDatabaseContext discon))
197+ reEnv = freshGraphRefRelationalExprEnv (Just (Discon.concreteDatabaseContext discon)) graph
198+ let eRvRel = runGraphRefRelationalExprM reEnv (evalGraphRefRelationalExpr rvExpr)
199+ assertEqual "branchB relvar preferred in conflict" (Right branchBRelVar) eRvRel
190200
191201-- try various individual component conflicts and check for merge failure
192202testUnionMergeStrategy :: Test
@@ -201,31 +211,41 @@ testUnionMergeStrategy = TestCase $ do
201211
202212 let updatedBranchBContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.insert branchBOnlyRelVarName branchBOnlyRelVar (relationVariables (concreteDatabaseContext branchBTrans)) }
203213 updatedBranchAContext = (concreteDatabaseContext branchATrans) {inclusionDependencies = M.insert branchAOnlyIncDepName branchAOnlyIncDep (inclusionDependencies (concreteDatabaseContext branchATrans)) }
204− branchBOnlyRelVar = relationTrue
214+ branchBOnlyRelVar = ExistingRelation relationTrue
205215 branchAOnlyIncDepName = "branchAOnlyIncDep"
206216 branchBOnlyRelVarName = "branchBOnlyRelVar"
207217 branchAOnlyIncDep = InclusionDependency (ExistingRelation relationTrue) (ExistingRelation relationTrue)
208− (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans) S.empty testTime) updatedBranchBContext) graph
209− (discon, _) <- assertEither $ mergeTransactions testTime (fakeUUID 5) (fakeUUID 10) UnionMergeStrategy ("branchA", "branchB") graph'
210− assertEqual "branchBOnlyRelVar should appear in the merge" (M.lookup branchBOnlyRelVarName (relationVariables (Discon.concreteDatabaseContext discon))) (Just relationTrue)
211− (_, graph'') <- addTransaction "branchA" (createTrans (fakeUUID 4) (TransactionInfo (transactionId branchATrans) S.empty testTime) updatedBranchAContext) graph'
212− let eMergeGraph = mergeTransactions testTime (fakeUUID 5) (fakeUUID 3) UnionMergeStrategy ("branchA", "branchB") graph''
218+ (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans NE.:| []) testTime) updatedBranchBContext) graph
219+ let env = freshGraphRefRelationalExprEnv Nothing graph'
220+ (discon, _) <- assertEither $ runGraphRefRelationalExprM env $ mergeTransactions testTime (fakeUUID 5) (fakeUUID 10) UnionMergeStrategy ("branchA", "branchB")
221+ let Just rvExpr = M.lookup branchBOnlyRelVarName (relationVariables (Discon.concreteDatabaseContext discon))
222+ Right rvRel = runGraphRefRelationalExprM reEnv (evalGraphRefRelationalExpr rvExpr)
223+ reEnv = freshGraphRefRelationalExprEnv (Just (Discon.concreteDatabaseContext discon)) graph
224+
225+ assertEqual "branchBOnlyRelVar should appear in the merge" relationTrue rvRel
226+ (_, graph'') <- addTransaction "branchA" (createTrans (fakeUUID 4) (TransactionInfo (transactionId branchATrans NE.:| []) testTime) updatedBranchAContext) graph'
227+ let eMergeGraph = runGraphRefRelationalExprM gfEnv $ mergeTransactions testTime (fakeUUID 5) (fakeUUID 3) UnionMergeStrategy ("branchA", "branchB")
228+ gfEnv = freshGraphRefRelationalExprEnv Nothing graph''
213229 case eMergeGraph of
214230 Left err -> assertFailure ("expected merge success: " ++ show err)
215231 Right (_, mergeGraph) -> do
216232 -- check that the new merge transaction has the correct parents
217233 mergeTrans <- assertEither $ transactionForId (fakeUUID 5) mergeGraph
218234 let mergeContext = concreteDatabaseContext mergeTrans
219− assertEqual "merge transaction parents" (transactionParentIds mergeTrans) (S.fromList [fakeUUID 3, fakeUUID 4])
235+ assertEqual "merge transaction parents" (parentIds mergeTrans) (S.fromList [fakeUUID 3, fakeUUID 4])
220236 -- check that the new merge tranasction has elements from both A and B branches
221− assertEqual "merge transaction relvars" (Just relationTrue) (M.lookup branchBOnlyRelVarName (relationVariables mergeContext))
237+ let Just rvExpr' = M.lookup branchBOnlyRelVarName (relationVariables mergeContext)
238+ Right rvRel' = runGraphRefRelationalExprM reEnv' (evalGraphRefRelationalExpr rvExpr')
239+ reEnv' = freshGraphRefRelationalExprEnv (Just mergeContext) graph
240+ assertEqual "merge transaction relvars" relationTrue rvRel'
222241 assertEqual "merge transaction incdeps" (Just branchAOnlyIncDep) (M.lookup branchAOnlyIncDepName (inclusionDependencies mergeContext))
223242 -- test an expected conflict- add branchBOnlyRelVar with same name but different attributes
224243 conflictRelVar <- assertEither $ mkRelationFromList (attributesFromList [Attribute "conflict" IntAtomType]) []
225− let conflictContextA = updatedBranchAContext {relationVariables = M.insert branchBOnlyRelVarName conflictRelVar (relationVariables updatedBranchAContext) }
244+ let conflictContextA = updatedBranchAContext {relationVariables = M.insert branchBOnlyRelVarName (ExistingRelation conflictRelVar) (relationVariables updatedBranchAContext) }
226245 conflictBranchATrans <- assertMaybe (transactionForHead "branchA" graph'') "retrieving head transaction for expected conflict"
227− (_, graph''') <- addTransaction "branchA" (createTrans (fakeUUID 6) (TransactionInfo (transactionId conflictBranchATrans) S.empty testTime) conflictContextA) graph''
228− let failingMerge = mergeTransactions testTime (fakeUUID 5) (fakeUUID 3) UnionMergeStrategy ("branchA", "branchB") graph'''
246+ (_, graph''') <- addTransaction "branchA" (createTrans (fakeUUID 6) (TransactionInfo (transactionId conflictBranchATrans NE.:| []) testTime) conflictContextA) graph''
247+ let failingMerge = runGraphRefRelationalExprM gfEnv' $ mergeTransactions testTime (fakeUUID 5) (fakeUUID 3) UnionMergeStrategy ("branchA", "branchB")
248+ gfEnv' = freshGraphRefRelationalExprEnv Nothing graph'''
229249 case failingMerge of
230250 Right _ -> assertFailure "expected merge failure"
231251 Left err -> assertEqual "merge failure" err (MergeTransactionError StrategyViolatesRelationVariableMergeError)
@@ -244,20 +264,21 @@ testUnionMergeIncDepViolation = TestCase $ do
244264 rvName = "x"
245265 Right branchArv = eRel 2
246266 Right branchBrv = eRel 3
247− branchAContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.singleton rvName branchArv}
248− branchBContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.singleton rvName branchBrv,
267+ branchAContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.singleton rvName (ExistingRelation branchArv)}
268+ branchBContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.singleton rvName (ExistingRelation branchBrv),
249269 inclusionDependencies = M.singleton incDepName incDep}
250270 incDepName = "x_key"
251271 incDep = inclusionDependencyForKey (AttributeNames (S.singleton "x")) (RelationVariable "x" ())
252272
253273
254274 --add the rv in new commits to both branches
255− (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans) S.empty testTime) branchBContext) graph
275+ (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans NE.:| []) testTime) branchBContext) graph
256276
257− (_, graph'') <- addTransaction "branchA" (createTrans (fakeUUID 4) (TransactionInfo (transactionId branchATrans) S.empty testTime) branchAContext) graph'
277+ (_, graph'') <- addTransaction "branchA" (createTrans (fakeUUID 4) (TransactionInfo (transactionId branchATrans NE.:| []) testTime) branchAContext) graph'
258278
259279 --check that the union merge fails due to a violated constraint
260− let eMerge = mergeTransactions testTime (fakeUUID 5) (fakeUUID 3) UnionMergeStrategy ("branchA", "branchB") graph''
280+ let eMerge = runGraphRefRelationalExprM env $ mergeTransactions testTime (fakeUUID 5) (fakeUUID 3) UnionMergeStrategy ("branchA", "branchB")
281+ env = freshGraphRefRelationalExprEnv Nothing graph''
261282 case eMerge of
262283 Left (InclusionDependencyCheckError incDepName') -> assertEqual "incdep violation name" incDepName incDepName'
263284 Left err -> assertFailure ("other error: " ++ show err)
test/TransactionGraph/Persist.hs+8−8
@@ -32,8 +32,8 @@ testList = TestList [testBootstrapDB,
3232 testFunctionPersistence]
3333
3434
35−stamp :: UTCTime
36−stamp = UTCTime (fromGregorian 1980 01 01) (secondsToDiffTime 1000)
35+stamp' :: UTCTime
36+stamp' = UTCTime (fromGregorian 1980 01 01) (secondsToDiffTime 1000)
3737
3838{- bootstrap a database, ensure that it can be read -}
3939testBootstrapDB :: Test
@@ -41,7 +41,7 @@ testBootstrapDB = TestCase $ withSystemTempDirectory "m36testdb" $ \tempdir -> d
4141 let dbdir = tempdir </> "dbdir"
4242 freshId <- nextRandom
4343
44− _ <- bootstrapDatabaseDir NoDiskSync dbdir (bootstrapTransactionGraph stamp freshId dateExamples)
44+ _ <- bootstrapDatabaseDir NoDiskSync dbdir (bootstrapTransactionGraph stamp' freshId dateExamples)
4545 loadedGraph <- transactionGraphLoad dbdir emptyTransactionGraph Nothing
4646 assertBool "transactionGraphLoad" $ isRight loadedGraph
4747
@@ -51,17 +51,17 @@ testDBSimplePersistence = TestCase $ withSystemTempDirectory "m36testdb" $ \temp
5151 let dbdir = tempdir </> "dbdir"
5252 freshId <- nextRandom
5353
54− let graph = bootstrapTransactionGraph stamp freshId dateExamples
54+ let graph = bootstrapTransactionGraph stamp' freshId dateExamples
5555 _ <- bootstrapDatabaseDir NoDiskSync dbdir graph
5656 case transactionForHead "master" graph of
5757 Nothing -> assertFailure "Failed to retrieve head transaction for master branch."
5858 Just headTrans ->
59− case interpretDatabaseContextExpr (concreteDatabaseContext headTrans) "x:=s" of
59+ case interpretDatabaseContextExpr (concreteDatabaseContext headTrans) (transactionId headTrans) graph "x:=s" of
6060 Left err -> assertFailure (show err)
6161 Right context' -> do
6262 freshId' <- nextRandom
63− let newdiscon = DisconnectedTransaction (transactionId headTrans) (Schemas context' M.empty) True
64− addTrans = addDisconnectedTransaction stamp freshId' "master" newdiscon graph
63+ let newdiscon = DisconnectedTransaction (transactionId headTrans) (Schemas context' M.empty) False
64+ addTrans = addDisconnectedTransaction stamp' freshId' "master" newdiscon graph
6565 --add a transaction to the graph
6666 case addTrans of
6767 Left err -> assertFailure (show err)
@@ -99,7 +99,7 @@ testFunctionPersistence = TestCase $
9999 conn2 <- assertIOEither $ connectProjectM36 connInfo
100100 sess2 <- assertIOEither $ createSessionAtHead conn2 "master"
101101
102− res <- executeRelationalExpr sess2 conn2 (MakeRelationFromExprs Nothing [TupleExpr (M.singleton "a" (FunctionAtomExpr "testdisk" [NakedAtomExpr (IntAtom 3)] ()))])
102+ res <- executeRelationalExpr sess2 conn2 (MakeRelationFromExprs Nothing (TupleExprs () [TupleExpr (M.singleton "a" (FunctionAtomExpr "testdisk" [NakedAtomExpr (IntAtom 3)] ()))]))
103103 let expectedRel = mkRelationFromList (attributesFromList [Attribute "a" IntAtomType]) [[IntAtom 3]]
104104 assertEqual "testdisk dbc function run" expectedRel res
105105
test/TutorialD/Interpreter.hs+50−21
@@ -9,6 +9,7 @@ import ProjectM36.TupleSet
99import ProjectM36.Error
1010import ProjectM36.DatabaseContext
1111import ProjectM36.AtomFunctions.Primitive
12+import ProjectM36.RelationalExpression
1213import ProjectM36.DataTypes.Either
1314import ProjectM36.DataTypes.Interval
1415import ProjectM36.DataTypes.NonEmptyList
@@ -39,8 +40,9 @@ main = do
3940 tcounts <- runTestTT (TestList tests)
4041 if errors tcounts + failures tcounts > 0 then exitFailure else exitSuccess
4142 where
42− tests = map (\(tutd, expected) -> TestCase $ assertTutdEqual basicDatabaseContext expected tutd) simpleRelTests ++
43− map (\(tutd, expected) -> TestCase $ assertTutdEqual dateExamples expected tutd) dateExampleRelTests ++ [
43+ tests = [
44+ simpleRelTests,
45+ dateExampleRelTests,
4446 transactionGraphBasicTest,
4547 transactionGraphAddCommitTest,
4648 transactionRollbackTest,
@@ -78,7 +80,13 @@ main = do
7880 testBasicList,
7981 testRelationDeclarationMismatch
8082 ]
81− simpleRelTests = [("x:=true", Right relationTrue),
83+
84+simpleRelTests :: Test
85+simpleRelTests = TestCase $ do
86+ (graph, transId) <- freshTransactionGraph dateExamples
87+ mapM_ (\(tutd, expected) -> assertTutdEqual basicDatabaseContext transId graph expected tutd) testTups
88+ where
89+ testTups = [("x:=true", Right relationTrue),
8290 ("x:=false", Right relationFalse),
8391 ("x:=true union false", Right relationTrue),
8492 ("x:=true minus false", Right relationTrue),
@@ -117,24 +125,30 @@ main = do
117125 simpleBAttributes = A.attributesFromList [Attribute "d" IntegerAtomType]
118126 simpleCAttributes = A.attributesFromList [Attribute "a" TextAtomType, Attribute "b" IntegerAtomType]
119127 simpleDAttributes = A.attributesFromList [Attribute "a" IntegerAtomType, Attribute "b" IntegerAtomType]
120− maybeTextAtomType = ConstructedAtomType "Maybe" (M.singleton "a" TextAtomType)
128+ simpleProjectionAttributes = A.attributesFromList [Attribute "c" IntegerAtomType]
129+ nestedRelationAttributes = A.attributesFromList [Attribute "a" IntegerAtomType, Attribute "b" (RelationAtomType $ A.attributesFromList [Attribute "a" IntegerAtomType])]
130+
131+dateExampleRelTests :: Test
132+dateExampleRelTests = TestCase $ do
133+ (graph, transId) <- freshTransactionGraph dateExamples
134+ mapM_ (\(tutd, expected) -> assertTutdEqual dateExamples transId graph expected tutd) testTups
135+ where
136+ simpleEitherIntTextAttributes = A.attributesFromList [Attribute "a" (eitherAtomType IntegerAtomType TextAtomType)]
121137 maybeIntegerAtomType = ConstructedAtomType "Maybe" (M.singleton "a" IntegerAtomType)
138+ maybeTextAtomType = ConstructedAtomType "Maybe" (M.singleton "a" TextAtomType)
122139 simpleMaybeTextAttributes = A.attributesFromList [Attribute "a" maybeTextAtomType]
123140 simpleMaybeIntAttributes = A.attributesFromList [Attribute "a" maybeIntegerAtomType]
124− simpleEitherIntTextAttributes = A.attributesFromList [Attribute "a" (eitherAtomType IntegerAtomType TextAtomType)]
125− simpleProjectionAttributes = A.attributesFromList [Attribute "c" IntegerAtomType]
126− nestedRelationAttributes = A.attributesFromList [Attribute "a" IntegerAtomType, Attribute "b" (RelationAtomType $ A.attributesFromList [Attribute "a" IntegerAtomType])]
127− extendTestAttributes = A.attributesFromList [Attribute "a" IntegerAtomType, Attribute "b" $ RelationAtomType (R.attributes suppliersRel)]
128141 byteStringAttributes = A.attributesFromList [Attribute "y" ByteStringAtomType]
129− groupCountAttrs = A.attributesFromList [Attribute "z" IntegerAtomType]
130142 minMaxAttrs = A.attributesFromList [Attribute "s#" TextAtomType, Attribute "z" IntegerAtomType]
143+ groupCountAttrs = A.attributesFromList [Attribute "z" IntegerAtomType]
131144 updateParisPlus10 = relMap (\tuple -> do
132145 statusAtom <- atomForAttributeName "status" tuple
133146 cityAtom <- atomForAttributeName "city" tuple
134147 if cityAtom == TextAtom "Paris" then
135148 Right $ updateTupleWithAtoms (M.singleton "status" (IntegerAtom (castInteger statusAtom + 10))) tuple
136149 else Right tuple) suppliersRel
137− dateExampleRelTests = [("x:=s where true", Right suppliersRel),
150+ extendTestAttributes = A.attributesFromList [Attribute "a" IntegerAtomType, Attribute "b" $ RelationAtomType (R.attributes suppliersRel)]
151+ testTups = [("x:=s where true", Right suppliersRel),
138152 ("x:=s where city = \"London\"", restrict (\tuple -> pure $ atomForAttributeName "city" tuple == Right (TextAtom "London")) suppliersRel),
139153 ("x:=s where false", Right $ Relation (R.attributes suppliersRel) emptyTupleSet),
140154 ("x:=p where color=\"Blue\" and city=\"Paris\"", mkRelationFromList (R.attributes productsRel) [[TextAtom "P5", TextAtom "Cam", TextAtom "Blue", IntegerAtom 12, TextAtom "Paris"]]),
@@ -177,14 +191,17 @@ main = do
177191 ("x:=relation{tuple{a fromGregorian(2017,05,30)}}", mkRelationFromList (A.attributesFromList [Attribute "a" DayAtomType]) [[DayAtom (fromGregorian 2017 05 30)]])
178192 ]
179193
180−assertTutdEqual :: DatabaseContext -> Either RelationalError Relation -> Text -> Assertion
181−assertTutdEqual databaseContext expected tutd = assertEqual (unpack tutd) expected interpreted
194+assertTutdEqual :: DatabaseContext -> TransactionId -> TransactionGraph -> Either RelationalError Relation -> Text -> Assertion
195+assertTutdEqual databaseContext transId graph expected tutd = assertEqual (unpack tutd) expected interpreted
182196 where
183− interpreted = case interpretDatabaseContextExpr databaseContext tutd of
197+ interpreted = case interpretDatabaseContextExpr databaseContext transId graph tutd of
184198 Left err -> Left err
185199 Right context -> case M.lookup "x" (relationVariables context) of
186200 Nothing -> Left $ RelVarNotDefinedError "x"
187− Just rel -> Right rel
201+ Just relExpr -> do
202+ let env = freshGraphRefRelationalExprEnv (Just context) graph
203+ runGraphRefRelationalExprM env (evalGraphRefRelationalExpr relExpr)
204+
188205
189206transactionGraphBasicTest :: Test
190207transactionGraphBasicTest = TestCase $ do
@@ -212,7 +229,7 @@ transactionGraphAddCommitTest = TestCase $ do
212229 commit sessionId dbconn >>= eitherFail
213230 discon <- disconnectedTransaction_ sessionId dbconn
214231 let context = Discon.concreteDatabaseContext discon
215− assertEqual "ensure x was added" (M.lookup "x" (relationVariables context)) (Just suppliersRel)
232+ assertEqual "ensure x was added" (M.lookup "x" (relationVariables context)) (Just (RelationVariable "s" UncommittedContextMarker))
216233
217234transactionRollbackTest :: Test
218235transactionRollbackTest = TestCase $ do
@@ -254,12 +271,16 @@ transactionBranchTest = TestCase $ do
254271
255272-- test that overlapping attribute names with different types fail with an error
256273failJoinTest :: Test
257−failJoinTest = TestCase $ assertTutdEqual basicDatabaseContext err "x:=relation{tuple{test 4}} join relation{tuple{test \"test\"}}"
274+failJoinTest = TestCase $ do
275+ (graph, transId) <- freshTransactionGraph dateExamples
276+ assertTutdEqual basicDatabaseContext transId graph err "x:=relation{tuple{test 4}} join relation{tuple{test \"test\"}}"
258277 where
259278 err = Left (TupleAttributeTypeMismatchError (A.attributesFromList [Attribute "test" IntegerAtomType]))
260279
261280simpleJoinTest :: Test
262−simpleJoinTest = TestCase $ assertTutdEqual dateExamples joinedRel "x:=s join sp"
281+simpleJoinTest = TestCase $ do
282+ (graph, transId) <- freshTransactionGraph dateExamples
283+ assertTutdEqual dateExamples transId graph joinedRel "x:=s join sp"
263284 where
264285 attrs = A.attributesFromList [Attribute "city" TextAtomType,
265286 Attribute "qty" IntegerAtomType,
@@ -386,7 +407,9 @@ testTransGraphRelationalExpr = TestCase $ do
386407 _ -> assertFailure "failed to delete branch"
387408
388409testMultiAttributeRename :: Test
389−testMultiAttributeRename = TestCase $ assertTutdEqual dateExamples renamedRel "x:=s rename {city as town, status as price} where false"
410+testMultiAttributeRename = TestCase $ do
411+ (graph, transId) <- freshTransactionGraph dateExamples
412+ assertTutdEqual dateExamples transId graph renamedRel "x:=s rename {city as town, status as price} where false"
390413 where
391414 sattrs = attributesFromList [Attribute "town" TextAtomType,
392415 Attribute "sname" TextAtomType,
@@ -471,16 +494,22 @@ testEmptyCommits = TestCase $ do
471494 assertEqual "empty insert empty commit" (Right False) dirty'
472495 Right () <- commit sessionId dbconn
473496
474− --update no tuples
497+ --update no tuples- since we defer the restriction, we can only assume the context is dirty, perhaps the constraint checker could offer an optimization here
475498 Right () <- executeDatabaseContextExpr sessionId dbconn (Update "s" (M.singleton "sname" (NakedAtomExpr (TextAtom "Bob"))) (AttributeEqualityPredicate "sname" (NakedAtomExpr (TextAtom "Mike"))))
476499 dirty'' <- disconnectedTransactionIsDirty sessionId dbconn
477− assertEqual "empty update empty commit" (Right False) dirty''
500+ assertEqual "empty update empty commit" (Right True) dirty''
501+ Right () <- commit sessionId dbconn
502+
503+ --assign the same rel expr
504+ Right () <- executeDatabaseContextExpr sessionId dbconn (Assign "true" (ExistingRelation relationTrue))
505+ sameREdirty <- disconnectedTransactionIsDirty sessionId dbconn
506+ assertEqual "same relexpr assigned" (Right False) sameREdirty
478507 Right () <- commit sessionId dbconn
479508
480509 --delete no tuples
481510 Right () <- executeDatabaseContextExpr sessionId dbconn (Delete "s" (AttributeEqualityPredicate "sname" (NakedAtomExpr (TextAtom "Mike"))))
482511 dirty''' <- disconnectedTransactionIsDirty sessionId dbconn
483− assertEqual "empty delete empty commit" (Right False) dirty'''
512+ assertEqual "empty delete empty commit" (Right True) dirty'''
484513
485514testIntervalAtom :: Test
486515testIntervalAtom = TestCase $ do
test/TutorialD/Interpreter/AtomFunctionScript.hs+7−7
@@ -36,7 +36,7 @@ testBasicAtomFunction = TestCase $ do
3636 funcAtomExpr = FunctionAtomExpr "mkTest" [NakedAtomExpr (IntegerAtom 3)] ()
3737 tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
3838 expectedResult = mkRelationFromList (V.fromList attrs) [[IntegerAtom 3]]
39− result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
39+ result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) (TupleExprs () tupleExprs))
4040
4141 assertEqual "simple atom function equality" expectedResult result
4242
@@ -47,7 +47,7 @@ testExceptionAtomFunction = TestCase $ do
4747 executeTutorialD sess conn "addatomfunction \"mkTest\" Integer -> Either AtomFunctionError Integer \"\"\"(\\(IntegerAtom x:xs) -> (error (show 1))) :: [Atom] -> Either AtomFunctionError Atom\"\"\""
4848 let attrs = [Attribute "x" IntegerAtomType]
4949 funcAtomExpr = FunctionAtomExpr "mkTest" [NakedAtomExpr (IntegerAtom 3)] ()
50− tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
50+ tupleExprs = TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)]
5151 result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
5252 assertBool "catch error exception from script" (case result of
5353 Left (UnhandledExceptionError _) -> True
@@ -64,7 +64,7 @@ testNoArgumentAtomFunction = TestCase $ do
6464 executeTutorialD sess conn "addatomfunction \"mkTest\" Either AtomFunctionError Integer \"\"\"(\\x -> pure (IntegerAtom 5)) :: [Atom] -> Either AtomFunctionError Atom\"\"\""
6565 let attrs = [Attribute "x" IntegerAtomType]
6666 funcAtomExpr = FunctionAtomExpr "mkTest" [] ()
67− tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
67+ tupleExprs = TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)]
6868 expectedResult = mkRelationFromList (V.fromList attrs) [[IntegerAtom 5]]
6969 result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
7070 assertEqual "no argument scripted function" expectedResult result
@@ -73,7 +73,7 @@ testArgumentTypeMismatch :: Test
7373testArgumentTypeMismatch = TestCase $ do
7474 (sess, conn) <- dateExamplesConnection emptyNotificationCallback
7575 executeTutorialD sess conn "addatomfunction \"mkTest\" Integer -> Either AtomFunctionError Integer \"\"\"(\\(IntegerAtom x:_) -> pure $ TextAtom \"wrong type\") :: [Atom] -> Either AtomFunctionError Atom\"\"\""
76− let tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
76+ let tupleExprs = TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)]
7777 funcAtomExpr = FunctionAtomExpr "mkTest" [NakedAtomExpr (IntegerAtom 3)] ()
7878 attrs = [Attribute "x" IntegerAtomType]
7979 expectedResult = Left (AtomTypeMismatchError IntegerAtomType TextAtomType)
@@ -87,7 +87,7 @@ testPolymorphicReturnType = TestCase $ do
8787 let funcAtomExpr = FunctionAtomExpr "fromMaybe" [NakedAtomExpr (IntegerAtom 5),
8888 NakedAtomExpr maybeAtom] ()
8989 maybeAtom = ConstructedAtom "Just" (maybeAtomType IntegerAtomType) [IntegerAtom 3]
90− relExpr = MakeRelationFromExprs Nothing [TupleExpr (M.singleton "x" funcAtomExpr)]
90+ relExpr = MakeRelationFromExprs Nothing (TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)])
9191 mRelType <- typeForRelationalExpr sess conn relExpr
9292 case mRelType of
9393 Left err -> assertFailure (show err)
@@ -101,7 +101,7 @@ testPolymorphicReturnType = TestCase $ do
101101 let failFuncAtomExpr = FunctionAtomExpr "fromMaybe" [NakedAtomExpr (IntegerAtom 5),
102102 NakedAtomExpr mismatchAtom] ()
103103 mismatchAtom = ConstructedAtom "Just" (maybeAtomType TextAtomType) [TextAtom "fail"]
104− failRelExpr = MakeRelationFromExprs Nothing [TupleExpr (M.singleton "x" failFuncAtomExpr)]
104+ failRelExpr = MakeRelationFromExprs Nothing (TupleExprs () [TupleExpr (M.singleton "x" failFuncAtomExpr)])
105105 mFailRes <- executeRelationalExpr sess conn failRelExpr
106106 let expectedErr = Left (AtomFunctionTypeVariableMismatch "a" IntegerAtomType TextAtomType)
107107 assertEqual "expected type variable mismatch" expectedErr mFailRes
@@ -113,7 +113,7 @@ testScriptedTypeVariable = TestCase $ do
113113 executeTutorialD sess conn "addatomfunction \"idTest\" a -> Either AtomFunctionError a \"(\\\\(x:_) -> pure x) :: [Atom] -> Either AtomFunctionError Atom\""
114114 let attrs = [Attribute "x" IntegerAtomType]
115115 funcAtomExpr = FunctionAtomExpr "idTest" [NakedAtomExpr (IntegerAtom 3)] ()
116− tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
116+ tupleExprs = TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)]
117117 expectedResult = mkRelationFromList (V.fromList attrs) [[IntegerAtom 3]]
118118 result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
119119
test/TutorialD/Interpreter/Import/TutorialD.hs+2−2
@@ -20,10 +20,10 @@ testTutdImport = TestCase $
2020 hClose handle
2121 let expectedExpr = MultipleExpr [
2222 Assign "x" (MakeRelationFromExprs Nothing
23− [TupleExpr (M.fromList [("a", NakedAtomExpr $ IntegerAtom 5),
23+ $ TupleExprs () [TupleExpr (M.fromList [("a", NakedAtomExpr $ IntegerAtom 5),
2424 ("b", NakedAtomExpr $ TextAtom "spam")])]),
2525 Assign "y" (MakeRelationFromExprs Nothing
26− [TupleExpr (M.fromList [("b", NakedAtomExpr (TextAtom "漢字"))])])]
26+ $ TupleExprs () [TupleExpr (M.fromList [("b", NakedAtomExpr (TextAtom "漢字"))])])]
2727 imported <- importTutorialD tempPath
2828 assertEqual "import tutd" (Right expectedExpr) imported
2929
test/TutorialD/Interpreter/TestBase.hs+10−13
@@ -2,16 +2,14 @@ module TutorialD.Interpreter.TestBase where
22import ProjectM36.Client
33import TutorialD.Interpreter
44import TutorialD.Interpreter.Base
5−import qualified ProjectM36.Base as Base
65import ProjectM36.DateExamples
6+import ProjectM36.DatabaseContext
77import Test.HUnit
8−import qualified Data.Map as M
98import Data.Text
109
1110dateExamplesConnection :: NotificationCallback -> IO (SessionId, Connection)
1211dateExamplesConnection callback = do
1312 dbconn <- connectProjectM36 (InProcessConnectionInfo NoPersistence callback [])
14− let incDeps = Base.inclusionDependencies dateExamples
1513 case dbconn of
1614 Left err -> error (show err)
1715 Right conn -> do
@@ -19,8 +17,7 @@ dateExamplesConnection callback = do
1917 case eSessionId of
2018 Left err -> error (show err)
2119 Right sessionId -> do
22− mapM_ (\(rvName,rvRel) -> executeDatabaseContextExpr sessionId conn (Assign rvName (Base.ExistingRelation rvRel))) (M.toList (Base.relationVariables dateExamples))
23− mapM_ (\(idName,incDep) -> executeDatabaseContextExpr sessionId conn (AddInclusionDependency idName incDep)) (M.toList incDeps)
20+ executeDatabaseContextExpr sessionId conn (databaseContextAsDatabaseContextExpr dateExamples) >>= eitherFail
2421 --skipping atom functions for now- there are no atom function manipulation operators yet
2522 commit sessionId conn >>= eitherFail
2623 pure (sessionId, conn)
@@ -31,13 +28,13 @@ executeTutorialD sessionId conn tutd = case parseTutorialD tutd of
3128 Right parsed -> do
3229 result <- evalTutorialD sessionId conn UnsafeEvaluation parsed
3330 case result of
34− QuitResult -> putStrLn "yyy" >> assertFailure "quit?"
35− DisplayResult _ -> putStrLn "xxx" >> assertFailure "display?"
36− DisplayIOResult _ -> putStrLn "z" >> assertFailure "displayIO?"
37− DisplayRelationResult _ -> putStrLn "Y" >> assertFailure "displayrelation?"
38− DisplayDataFrameResult _ -> putStrLn "DF" >> assertFailure "displaydataframe?"
39− DisplayParseErrorResult _ _ -> putStrLn "X" >> assertFailure "displayparseerrorresult?"
40− DisplayErrorResult err -> putStrLn "asd" >> assertFailure (show tutd ++ ": " ++ show err)
31+ QuitResult -> assertFailure "quit?"
32+ DisplayResult _ -> assertFailure "display?"
33+ DisplayIOResult _ -> assertFailure "displayIO?"
34+ DisplayRelationResult _ -> assertFailure "displayrelation?"
35+ DisplayDataFrameResult _ -> assertFailure "displaydataframe?"
36+ DisplayParseErrorResult _ _ -> assertFailure "displayparseerrorresult?"
37+ DisplayErrorResult err -> assertFailure (show tutd ++ ": " ++ show err)
4138 QuietSuccessResult -> pure ()
4239
4340expectTutorialDErr :: SessionId -> Connection -> (Text -> Bool) -> Text -> IO ()
@@ -52,7 +49,7 @@ expectTutorialDErr sessionId conn matchFunc tutd = case parseTutorialD tutd of
5249 DisplayRelationResult _ -> assertFailure "displayrelation?"
5350 DisplayDataFrameResult _ -> assertFailure "displaydataframe?"
5451 DisplayParseErrorResult _ _ -> assertFailure "displayparseerrorresult?"
55− DisplayErrorResult err -> assertBool ("match error on: " ++ unpack err) (matchFunc err)
52+ DisplayErrorResult err -> assertBool (unpack tutd ++ " match error on: " ++ unpack err) (matchFunc err)
5653 QuietSuccessResult -> pure ()
5754
5855eitherFail :: Either RelationalError a -> IO ()