on master help sign in
merge upstream 0.8
315b3aehughjfchen
.travis/install-stack.sh+2−2
@@ -10,11 +10,11 @@ travis_retry() {
10}
10}
11
11
12fetch_stack_osx() {
12fetch_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;
14}
14}
15
15
16fetch_stack_linux() {
16fetch_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';
18}
18}
19
19
20# We need stack to generate cabal files with precise bounds, even for cabal
20# 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+
1# 2019-09-07 (v0.7)
5# 2019-09-07 (v0.7)
2
6
3* fix case sensitivity issue with relation variables in tutd
7* 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.
48
48
491. [Installation and Introduction to Project:M36](docs/introduction_to_projectm36.markdown)
491. [Installation and Introduction to Project:M36](docs/introduction_to_projectm36.markdown)
501. [Introduction to the Relational Algebra](docs/introduction_to_the_relational_algebra.markdown)
501. [Introduction to the Relational Algebra](docs/introduction_to_the_relational_algebra.markdown)
51+1. [TutorialD Cheatsheet](docs/tutd_cheatsheet.markdown)
511. [TutorialD via Jupyter Notebook Walkthrough](jupyter/TutorialD%20Notebook%20Walkthrough.ipynb)
521. [TutorialD via Jupyter Notebook Walkthrough](jupyter/TutorialD%20Notebook%20Walkthrough.ipynb)
521. [TutorialD Tutorial](docs/tutd_tutorial.markdown)
531. [TutorialD Tutorial](docs/tutd_tutorial.markdown)
531. [15 Minute Tutorial](docs/15_minute_tutorial.markdown)
541. [15 Minute Tutorial](docs/15_minute_tutorial.markdown)
@@ -60,6 +61,7 @@ Project:M36 supports multiple frontends which target different audiences.
601. [ACID Database Properties](docs/acid_assessment.markdown)
611. [ACID Database Properties](docs/acid_assessment.markdown)
611. [On NULL (in SQL)](docs/on_null.markdown)
621. [On NULL (in SQL)](docs/on_null.markdown)
621. [Reaching "Out of the Tarpit" with Project:M36](docs/reaching_out_of_the_tarpit.markdown)
631. [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)
63
65
64### Advanced Features
66### Advanced Features
65
67
cabal.project+3−3
@@ -1,9 +1,9 @@
1-- this is needed for GHC8 until distributed-process-extras is updated on Hackage with increased upper bounds
1-- this is needed for GHC8 until distributed-process-extras is updated on Hackage with increased upper bounds
2packages: project-m36.cabal
2packages: 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
4
4
5--necessary for GHC 8.6 support :/
5--necessary for GHC 8.6 support :/
6source-repository-package
6source-repository-package
7 type: git
7 type: git
8− location: https://github.com/haskell-distributed/distributed-process.git
8+ location: https://github.com/agentm/distributed-process.git
9− tag: 660d554f6acd2dba8b605c84e8fa69e45708bc14
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)
2
2
3## Introduction
3## Introduction
4
4
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
35data Blog = Blog {
35data Blog = Blog {
36 title :: T.Text,
36 title :: T.Text,
37 entry :: T.Text,
37 entry :: T.Text,
38− stamp :: UTCTime,
38+ tstamp :: UTCTime,
39 category :: Category --note that this type is an algebraic data type
39 category :: Category --note that this type is an algebraic data type
40 }
40 }
41 deriving (Generic, Show) --derive Generic so that Tupleable can use default instances
41 deriving (Generic, Show) --derive Generic so that Tupleable can use default instances
@@ -105,11 +105,11 @@ insertSampleData :: SessionId -> Connection -> IO ()
105insertSampleData sessionId conn = do
105insertSampleData sessionId conn = do
106 let blogs = [Blog { title = "Haskell Lenses",
106 let blogs = [Blog { title = "Haskell Lenses",
107 entry = "I wear Haskell rose-colored lenses.",
107 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),
109 category = Food },
109 category = Food },
110 Blog { title = "Haskell Monad Analogy",
110 Blog { title = "Haskell Monad Analogy",
111 entry = "Monads are like burritos going through intestines.",
111 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),
113 category = Cats }
113 category = Cats }
114 ]
114 ]
115 comments = [Comment { blogTitle = "Haskell Lenses",
115 comments = [Comment { blogTitle = "Haskell Lenses",
@@ -138,7 +138,7 @@ listBlogs sessionId conn = do
138 Left err -> render500 (toHtml (show err))
138 Left err -> render500 (toHtml (show err))
139 Right blogRel -> do
139 Right blogRel -> do
140 blogs <- liftIO (toList blogRel) >>= mapM (handleWebError . fromTuple) :: ActionM [Blog]
140 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
142 html . renderHtml $ do
142 html . renderHtml $ do
143 h1 "Blog Posts"
143 h1 "Blog Posts"
144 forM_ sortedBlogs $ \blog -> a ! href (toValue $ "/blog/" <> title blog) $ h2 (toHtml (title blog))
144 forM_ sortedBlogs $ \blog -> a ! href (toValue $ "/blog/" <> title blog) $ h2 (toHtml (title blog))
@@ -184,7 +184,7 @@ showBlogEntry sessionId conn = do
184 render $ do
184 render $ do
185 --show blog details
185 --show blog details
186 h1 (toHtml (title blog))
186 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)))
188 p (toHtml (entry blog))
188 p (toHtml (entry blog))
189 hr
189 hr
190 h3 "Comments"
190 h3 "Comments"
examples/hair.hs+5−3
@@ -31,11 +31,13 @@ main = do
31
31
32 --create a relation with the new Hair AtomType
32 --create a relation with the new Hair AtomType
33 let blond = NakedAtomExpr (toAtom Blond)
33 let blond = NakedAtomExpr (toAtom Blond)
34− eCheck $ executeDatabaseContextExpr sessionId conn (Assign "people" (MakeRelationFromExprs Nothing [
34+ eCheck $ executeDatabaseContextExpr sessionId conn
35+ (Assign "people"
36+ (MakeRelationFromExprs Nothing $ TupleExprs () [
35− TupleExpr (M.fromList [("hair", blond), ("name", NakedAtomExpr (TextAtom "Colin"))])]))
37+ TupleExpr (M.fromList [("hair", blond), ("name", NakedAtomExpr (TextAtom "Colin"))])]))
36
38
37 let restrictionPredicate = AttributeEqualityPredicate "hair" blond
39 let restrictionPredicate = AttributeEqualityPredicate "hair" blond
38 peopleRel <- eCheck $ executeRelationalExpr sessionId conn (Restrict restrictionPredicate (RelationVariable "people" ()))
40 peopleRel <- eCheck $ executeRelationalExpr sessionId conn (Restrict restrictionPredicate (RelationVariable "people" ()))
39
41
40 TIO.putStrLn (showRelation peopleRel)
42 TIO.putStrLn (showRelation peopleRel)
41−
43+
project-m36.cabal+10−6
@@ -1,5 +1,5 @@
1Name: project-m36
1Name: project-m36
2−Version: 0.7
2+Version: 0.8
3License: PublicDomain
3License: PublicDomain
4Build-Type: Simple
4Build-Type: Simple
5Homepage: https://github.com/agentm/project-m36
5Homepage: https://github.com/agentm/project-m36
@@ -34,9 +34,9 @@ Flag haskell-scripting
34 Default: True
34 Default: True
35
35
36Library
36Library
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
38 if flag(haskell-scripting)
38 if flag(haskell-scripting)
39− Build-Depends: ghc >= 8.2 && < 8.7
39+ Build-Depends: ghc >= 8.2 && < 8.9
40 CPP-Options: -DPM36_HASKELL_SCRIPTING
40 CPP-Options: -DPM36_HASKELL_SCRIPTING
41 if impl(ghc>= 8) && flag(haskell-scripting)
41 if impl(ghc>= 8) && flag(haskell-scripting)
42 build-depends:
42 build-depends:
@@ -115,7 +115,11 @@ Library
115 ProjectM36.TypeConstructorDef,
115 ProjectM36.TypeConstructorDef,
116 ProjectM36.FileLock,
116 ProjectM36.FileLock,
117 ProjectM36.FSType,
117 ProjectM36.FSType,
118− ProjectM36.Arbitrary
118+ ProjectM36.Arbitrary,
119+ ProjectM36.GraphRefRelationalExpr,
120+ ProjectM36.NormalizeExpr,
121+ ProjectM36.TransactionInfo,
122+ ProjectM36.WithNameExpr
119 GHC-Options: -Wall -rdynamic
123 GHC-Options: -Wall -rdynamic
120 if os(windows)
124 if os(windows)
121 Build-Depends: Win32 >= 2.5.4.1
125 Build-Depends: Win32 >= 2.5.4.1
@@ -134,7 +138,7 @@ Library
134
138
135Executable tutd
139Executable tutd
136 if flag(haskell-scripting)
140 if flag(haskell-scripting)
137− Build-Depends: ghc >= 8.2 && < 8.7
141+ Build-Depends: ghc >= 8.2 && < 8.9
138 Build-Depends: base >=4.8 && <5.0,
142 Build-Depends: base >=4.8 && <5.0,
139 ghc-paths,
143 ghc-paths,
140 project-m36,
144 project-m36,
@@ -199,7 +203,7 @@ Executable tutd
199
203
200Executable project-m36-server
204Executable project-m36-server
201 if flag(haskell-scripting)
205 if flag(haskell-scripting)
202− Build-Depends: ghc >= 8.2 && < 8.7
206+ Build-Depends: ghc >= 8.2 && < 8.9
203 Build-Depends: base,
207 Build-Depends: base,
204 ghc-paths,
208 ghc-paths,
205 project-m36,
209 project-m36,
src/bin/ProjectM36/Server/RemoteCallTypes/Json.hs+6
@@ -22,6 +22,9 @@ instance FromJSON RelationalExpr
22instance ToJSON TupleExpr
22instance ToJSON TupleExpr
23instance FromJSON TupleExpr
23instance FromJSON TupleExpr
24
24
25+instance ToJSON TupleExprs
26+instance FromJSON TupleExprs
27+
25instance ToJSON RestrictionPredicateExpr
28instance ToJSON RestrictionPredicateExpr
26instance FromJSON RestrictionPredicateExpr
29instance FromJSON RestrictionPredicateExpr
27
30
@@ -149,3 +152,6 @@ instance FromJSON PersistenceError
149
152
150instance ToJSON AtomFunctionError
153instance ToJSON AtomFunctionError
151instance FromJSON AtomFunctionError
154instance 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)
50anySingle = anyChar
50anySingle = anyChar
51#endif
51#endif
52
52
53−
54displayOpResult :: TutorialDOperatorResult -> IO ()
53displayOpResult :: TutorialDOperatorResult -> IO ()
55displayOpResult QuitResult = return ()
54displayOpResult QuitResult = return ()
56displayOpResult (DisplayResult out) = TIO.putStrLn out
55displayOpResult (DisplayResult out) = TIO.putStrLn out
@@ -222,4 +221,4 @@ utcTimeP = do
222 timeStr <- quotedString
221 timeStr <- quotedString
223 case parseTimeM True defaultTimeLocale "%Y-%m-%d %H:%M:%S" (T.unpack timeStr) of
222 case parseTimeM True defaultTimeLocale "%Y-%m-%d %H:%M:%S" (T.unpack timeStr) of
224 Nothing -> fail "invalid datetime input, use \"YYYY-MM-DD HH:MM:SS\""
223 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
6import TutorialD.Interpreter.RelationalExpr
6import TutorialD.Interpreter.RelationalExpr
7import TutorialD.Interpreter.Types
7import TutorialD.Interpreter.Types
8import qualified Data.Map as M
8import qualified Data.Map as M
9−import Control.Monad.State
10import ProjectM36.StaticOptimizer
9import ProjectM36.StaticOptimizer
11import qualified ProjectM36.Error as PM36E
10import qualified ProjectM36.Error as PM36E
12import ProjectM36.Error
11import ProjectM36.Error
@@ -192,19 +191,22 @@ executeDatabaseContextFunctionP = do
192databaseExprOpP :: Parser DatabaseContextExpr
191databaseExprOpP :: Parser DatabaseContextExpr
193databaseExprOpP = multipleDatabaseContextExprP
192databaseExprOpP = multipleDatabaseContextExprP
194
193
194+{-
195evalDatabaseContextExpr :: Bool -> DatabaseContext -> DatabaseContextExpr -> Either RelationalError DatabaseContext
195evalDatabaseContextExpr :: Bool -> DatabaseContext -> DatabaseContextExpr -> Either RelationalError DatabaseContext
196evalDatabaseContextExpr useOptimizer context expr = do
196evalDatabaseContextExpr useOptimizer context expr = do
197 optimizedExpr <- evalState (applyStaticDatabaseOptimization expr) (RE.freshDatabaseState context)
197 optimizedExpr <- evalState (applyStaticDatabaseOptimization expr) (RE.freshDatabaseState context)
198 case runState (RE.evalDatabaseContextExpr (if useOptimizer then optimizedExpr else expr)) (RE.freshDatabaseState context) of
198 case runState (RE.evalDatabaseContextExpr (if useOptimizer then optimizedExpr else expr)) (RE.freshDatabaseState context) of
199 (Right (), (context',_, _)) -> Right context'
199 (Right (), (context',_, _)) -> Right context'
200 (Left err, _) -> Left err
200 (Left err, _) -> Left err
201+-}
201
202
202−
203−interpretDatabaseContextExpr :: DatabaseContext -> T.Text -> Either RelationalError DatabaseContext
203+interpretDatabaseContextExpr :: DatabaseContext -> TransactionId -> TransactionGraph -> T.Text -> Either RelationalError DatabaseContext
204−interpretDatabaseContextExpr context tutdstring = case parse databaseExprOpP "" tutdstring of
204+interpretDatabaseContextExpr context transId graph tutdstring =
205+ case parse databaseExprOpP "" tutdstring of
205− Left err -> Left $ PM36E.ParseError (T.pack (show err))
206+ Left err -> Left $ PM36E.ParseError (T.pack (show err))
206− Right parsed ->
207+ Right parsed -> do
207− evalDatabaseContextExpr True context parsed
208+ let env = RE.mkDatabaseContextEvalEnv transId graph
209+ RE.dbc_context <$> RE.runDatabaseContextEvalMonad context env (optimizeAndEvalDatabaseContextExpr True parsed)
208
210
209{-
211{-
210--no optimization
212--no optimization
src/bin/TutorialD/Interpreter/RelationalExpr.hs+10−7
@@ -1,5 +1,4 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2−
3module TutorialD.Interpreter.RelationalExpr where
2module TutorialD.Interpreter.RelationalExpr where
4import Text.Megaparsec
3import Text.Megaparsec
5#if MIN_VERSION_megaparsec(7,0,0)
4#if MIN_VERSION_megaparsec(7,0,0)
@@ -30,7 +29,8 @@ makeRelationP = do
30 reserved "relation"
29 reserved "relation"
31 attrExprs <- try (fmap Just makeAttributeExprsP) <|> pure Nothing
30 attrExprs <- try (fmap Just makeAttributeExprsP) <|> pure Nothing
32 tupleExprs <- braces (sepBy tupleExprP comma) <|> pure []
31 tupleExprs <- braces (sepBy tupleExprP comma) <|> pure []
32+ marker <- parseMarkerP
33− pure $ MakeRelationFromExprs attrExprs tupleExprs
33+ pure $ MakeRelationFromExprs attrExprs (TupleExprs marker tupleExprs)
34
34
35
35
36--abstract data type parser- in this context, the type constructor must not include any type arguments
36--abstract data type parser- in this context, the type constructor must not include any type arguments
@@ -251,12 +251,15 @@ relationAtomExprP = RelationAtomExpr <$> makeRelationP
251withMacroExprP :: RelationalMarkerExpr a => Parser (RelationalExprBase a)
251withMacroExprP :: RelationalMarkerExpr a => Parser (RelationalExprBase a)
252withMacroExprP = do
252withMacroExprP = do
253 reservedOp "with"
253 reservedOp "with"
254− views <- parens (sepBy1 createViewP comma)
254+ views <- parens (sepBy1 createMacroP comma)
255 With views <$> relExprP
255 With views <$> relExprP
256
256
257−createViewP :: RelationalMarkerExpr a => Parser (RelVarName, RelationalExprBase a)
257+createMacroP :: RelationalMarkerExpr a => Parser (WithNameExprBase a, RelationalExprBase a)
258−createViewP = do
258+createMacroP = do
259− name <- relVarNameP
259+ name <- identifier
260 reservedOp "as"
260 reservedOp "as"
261 expr <- relExprP
261 expr <- relExprP
262+ marker <- parseMarkerP
262− pure (name, expr)
263+ pure (WithNameExpr name marker, expr)
264+
265+
src/bin/TutorialD/Interpreter/SchemaOperator.hs+3−3
@@ -42,7 +42,7 @@ isoRestrictP = do
42 reserved "isorestrict"
42 reserved "isorestrict"
43 relVarIn <- qrelVarP
43 relVarIn <- qrelVarP
44 relvarsOut <- isoRestrictOutRelVarsP
44 relvarsOut <- isoRestrictOutRelVarsP
45− IsoRestrict <$> pure relVarIn <*> restrictionPredicateP <*> pure relvarsOut
45+ IsoRestrict relVarIn <$> restrictionPredicateP <*> pure relvarsOut
46
46
47isoRestrictOutRelVarsP :: Parser (RelVarName, RelVarName)
47isoRestrictOutRelVarsP :: Parser (RelVarName, RelVarName)
48isoRestrictOutRelVarsP = (,) <$> qrelVarP <*> qrelVarP
48isoRestrictOutRelVarsP = (,) <$> qrelVarP <*> qrelVarP
@@ -55,7 +55,7 @@ isoUnionP = do
55 reserved "isounion"
55 reserved "isounion"
56 relVarsIn <- isoUnionInRelVarsP
56 relVarsIn <- isoUnionInRelVarsP
57 relVarsOut <- qrelVarP
57 relVarsOut <- qrelVarP
58− IsoUnion <$> pure relVarsIn <*> restrictionPredicateP <*> pure relVarsOut
58+ IsoUnion relVarsIn <$> restrictionPredicateP <*> pure relVarsOut
59
59
60isoRenameP :: Parser SchemaIsomorph
60isoRenameP :: Parser SchemaIsomorph
61isoRenameP = do
61isoRenameP = do
@@ -74,4 +74,4 @@ isoUnionInRelVarsP = (,) <$> qrelVarP <*> qrelVarP
74evalSchemaOperator :: SessionId -> Connection -> SchemaOperator -> IO (Either RelationalError ())
74evalSchemaOperator :: SessionId -> Connection -> SchemaOperator -> IO (Either RelationalError ())
75evalSchemaOperator sessionId conn (ModifySchemaExpr expr) = executeSchemaExpr sessionId conn expr
75evalSchemaOperator sessionId conn (ModifySchemaExpr expr) = executeSchemaExpr sessionId conn expr
76evalSchemaOperator sessionId conn (SetCurrentSchema sname) = setCurrentSchemaName sessionId conn sname
76evalSchemaOperator sessionId conn (SetCurrentSchema sname) = setCurrentSchemaName sessionId conn sname
77−
77+
src/bin/benchmark/bigrel.hs+16−7
@@ -4,28 +4,30 @@ import ProjectM36.Base
4import ProjectM36.Relation
4import ProjectM36.Relation
5import ProjectM36.DateExamples
5import ProjectM36.DateExamples
6import ProjectM36.Error
6import ProjectM36.Error
7+import ProjectM36.StaticOptimizer
7import qualified ProjectM36.Attribute as A
8import qualified ProjectM36.Attribute as A
8import qualified Data.Text as T
9import qualified Data.Text as T
9--import ProjectM36.Relation.Show.CSV
10--import ProjectM36.Relation.Show.CSV
10import ProjectM36.Relation.Show.HTML
11import ProjectM36.Relation.Show.HTML
11−import TutorialD.Interpreter.DatabaseContextExpr (interpretDatabaseContextExpr)
12import ProjectM36.RelationalExpression
12import ProjectM36.RelationalExpression
13+import ProjectM36.TransactionGraph
13--import qualified Data.HashSet as HS
14--import qualified Data.HashSet as HS
14--import qualified Data.ByteString.Lazy.Char8 as BS
15--import qualified Data.ByteString.Lazy.Char8 as BS
15--import qualified Data.IntMap as IM
16--import qualified Data.IntMap as IM
16--import qualified Data.Hashable as Hash
17--import qualified Data.Hashable as Hash
17import qualified Data.Vector as V
18import qualified Data.Vector as V
18import Options.Applicative
19import Options.Applicative
19−import qualified Data.Map as M
20import qualified Data.Text.IO as TIO
20import qualified Data.Text.IO as TIO
21import System.IO
21import System.IO
22−import Control.Monad.State
23import Control.DeepSeq
22import Control.DeepSeq
24import Data.Text hiding (map)
23import Data.Text hiding (map)
24+import Data.Time.Clock
25+import Data.UUID.V4
25#if __GLASGOW_HASKELL__ < 804
26#if __GLASGOW_HASKELL__ < 804
26import Data.Monoid
27import Data.Monoid
27#endif
28#endif
28
29
30+
29{-
31{-
30dumpcsv :: Relation -> IO ()
32dumpcsv :: Relation -> IO ()
31dumpcsv rel = case relationAsCSV rel of
33dumpcsv rel = case relationAsCSV rel of
@@ -62,13 +64,20 @@ matrixRun (BigrelArgs attributeCount tupleCount tutd) =
62 Right rel -> if tutd == "" then
64 Right rel -> if tutd == "" then
63 putStrLn "Done."
65 putStrLn "Done."
64 else do
66 else do
67+ now <- getCurrentTime
68+ tid <- nextRandom
65 let setx = Assign "x" (ExistingRelation (force rel))
69 let setx = Assign "x" (ExistingRelation (force rel))
66− (context,_,_) = execState (evalDatabaseContextExpr setx) (freshDatabaseState dateExamples)
70+ graph = bootstrapTransactionGraph now tid dateExamples
67− interpreted = interpretDatabaseContextExpr context tutd
71+ env = mkDatabaseContextEvalEnv tid graph
72+ eNewState = runDatabaseContextEvalMonad dateExamples env (optimizeAndEvalDatabaseContextExpr True setx)
68 --plan = interpretRODatabaseContextOp context $ ":showplan " ++ tutd
73 --plan = interpretRODatabaseContextOp context $ ":showplan " ++ tutd
69 --displayOpResult plan
74 --displayOpResult plan
70− case interpreted of
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
71− Right context' -> TIO.putStrLn $ relationAsHTML (relationVariables context' M.! "x")
80+ TIO.putStrLn $ relationAsHTML x
72 Left err -> hPrint stderr err
81 Left err -> hPrint stderr err
73
82
74{-
83{-
src/lib/ProjectM36/AtomType.hs+41−2
@@ -221,7 +221,15 @@ findTypeConstructor name = foldr tConsFolder Nothing
221 Just (tCons, dConsList)
221 Just (tCons, dConsList)
222 else
222 else
223 accum
223 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
225resolveAtomType :: AtomType -> AtomType -> Either RelationalError AtomType
233resolveAtomType :: AtomType -> AtomType -> Either RelationalError AtomType
226resolveAtomType (ConstructedAtomType tConsName resolvedTypeVarMap) (ConstructedAtomType _ unresolvedTypeVarMap) =
234resolveAtomType (ConstructedAtomType tConsName resolvedTypeVarMap) (ConstructedAtomType _ unresolvedTypeVarMap) =
227 ConstructedAtomType tConsName <$> resolveAtomTypesInTypeVarMap resolvedTypeVarMap unresolvedTypeVarMap
235 ConstructedAtomType tConsName <$> resolveAtomTypesInTypeVarMap resolvedTypeVarMap unresolvedTypeVarMap
@@ -252,7 +260,7 @@ resolveAtomTypesInTypeVarMap resolvedTypeMap unresolvedTypeMap = do
252 let resolveTypePair resKey resType =
260 let resolveTypePair resKey resType =
253 -- if the key is missing in the unresolved type map, then fill it in with the value from the resolved map
261 -- if the key is missing in the unresolved type map, then fill it in with the value from the resolved map
254 case M.lookup resKey unresolvedTypeMap of
262 case M.lookup resKey unresolvedTypeMap of
255− Just unresType -> case unresType of
263+ Just unresType -> case unresType of
256 --do we need to recurse for RelationAtomType?
264 --do we need to recurse for RelationAtomType?
257 subType@(ConstructedAtomType _ _) -> do
265 subType@(ConstructedAtomType _ _) -> do
258 resSubType <- resolveAtomType resType subType
266 resSubType <- resolveAtomType resType subType
@@ -310,6 +318,15 @@ validateAtomType (RelationAtomType attrs) tConss =
310validateAtomType (TypeVariableType x) _ = Left (TypeConstructorTypeVarMissing x)
318validateAtomType (TypeVariableType x) _ = Left (TypeConstructorTypeVarMissing x)
311validateAtomType _ _ = pure ()
319validateAtomType _ _ = pure ()
312
320
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+
313--ensure that all type vars are fully resolved
330--ensure that all type vars are fully resolved
314validateTypeVarMap :: TypeVarMap -> TypeConstructorMapping -> Either RelationalError ()
331validateTypeVarMap :: TypeVarMap -> TypeConstructorMapping -> Either RelationalError ()
315validateTypeVarMap tvMap tConss = mapM_ (`validateAtomType` tConss) $ M.elems tvMap
332validateTypeVarMap tvMap tConss = mapM_ (`validateAtomType` tConss) $ M.elems tvMap
@@ -414,3 +431,25 @@ resolvedAtomTypeForDataConstructorDefArg _ tvMap (DataConstructorDefTypeVarNameA
414 Nothing -> Left (DataConstructorUsesUndeclaredTypeVariable tvName)
431 Nothing -> Left (DataConstructorUsesUndeclaredTypeVariable tvName)
415 Just typ -> Right typ
432 Just typ -> Right typ
416
433
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
3import qualified Data.Set as S
3import qualified Data.Set as S
4--AttributeNames is a data structure which can represent inverted projection attributes and attribute names derived from relational expressions
4--AttributeNames is a data structure which can represent inverted projection attributes and attribute names derived from relational expressions
5
5
6−empty :: AttributeNames
6+empty :: AttributeNamesBase a
7empty = AttributeNames S.empty
7empty = AttributeNames S.empty
8
8
9−all :: AttributeNames
9+all :: AttributeNamesBase a
10all = InvertedAttributeNames S.empty
10all = 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 #-}
2{-# OPTIONS_GHC -fno-warn-orphans #-}
2{-# OPTIONS_GHC -fno-warn-orphans #-}
3
3
4module ProjectM36.Base where
4module ProjectM36.Base where
@@ -13,6 +13,7 @@ import Data.UUID (UUID)
13import Control.DeepSeq (NFData, rnf)
13import Control.DeepSeq (NFData, rnf)
14import Control.DeepSeq.Generics (genericRnf)
14import Control.DeepSeq.Generics (genericRnf)
15import GHC.Generics (Generic)
15import GHC.Generics (Generic)
16+import GHC.Stack
16import qualified Data.Vector as V
17import qualified Data.Vector as V
17import qualified Data.List as L
18import qualified Data.List as L
18import Data.Text (Text,unpack)
19import Data.Text (Text,unpack)
@@ -24,6 +25,7 @@ import Data.Time.Calendar (Day,toGregorian,fromGregorian)
24import Data.Hashable.Time ()
25import Data.Hashable.Time ()
25import Data.Typeable
26import Data.Typeable
26import Data.ByteString (ByteString)
27import Data.ByteString (ByteString)
28+import qualified Data.List.NonEmpty as NE
27type StringType = Text
29type StringType = Text
28
30
29-- | Database atoms are the smallest, undecomposable units of a tuple. Common examples are integers, text, or unique identity keys.
31-- | 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 |
79 deriving (Eq, NFData, Generic, Binary, Show)
81 deriving (Eq, NFData, Generic, Binary, Show)
80
82
81instance Ord AtomType where
83instance Ord AtomType where
82− compare = undefined
84+ compare = undefined
85+
83type TypeVarMap = M.Map TypeVarName AtomType
86type TypeVarMap = M.Map TypeVarName AtomType
84
87
85instance Hashable TypeVarMap where
88instance Hashable TypeVarMap where
@@ -182,7 +185,7 @@ type RelationalExpr = RelationalExprBase ()
182-- | A relational expression represents query (read) operations on a database.
185-- | A relational expression represents query (read) operations on a database.
183data RelationalExprBase a =
186data RelationalExprBase a =
184 --- | Create a relation from tuple expressions.
187 --- | Create a relation from tuple expressions.
185− MakeRelationFromExprs (Maybe [AttributeExprBase a]) [TupleExprBase a] |
188+ MakeRelationFromExprs (Maybe [AttributeExprBase a]) (TupleExprsBase a) |
186 --- | Create and reference a relation from attributes and a tuple set.
189 --- | Create and reference a relation from attributes and a tuple set.
187 MakeStaticRelation Attributes RelationTupleSet |
190 MakeStaticRelation Attributes RelationTupleSet |
188 --- | Reference an existing relation in Haskell-space.
191 --- | Reference an existing relation in Haskell-space.
@@ -191,7 +194,7 @@ data RelationalExprBase a =
191 --in Tutorial D, relational variables pick up the type of the first relation assigned to them
194 --in Tutorial D, relational variables pick up the type of the first relation assigned to them
192 --relational variables should also be able to be explicitly-typed like in Haskell
195 --relational variables should also be able to be explicitly-typed like in Haskell
193 --- | Reference a relation variable by its name.
196 --- | Reference a relation variable by its name.
194− RelationVariable RelVarName a |
197+ RelationVariable RelVarName a |
195 --- | Create a projection over attribute names. (Note that the 'AttributeNames' structure allows for the names to be inverted.)
198 --- | Create a projection over attribute names. (Note that the 'AttributeNames' structure allows for the names to be inverted.)
196 Project (AttributeNamesBase a) (RelationalExprBase a) |
199 Project (AttributeNamesBase a) (RelationalExprBase a) |
197 --- | Create a union of two relational expressions. The expressions should have identical attributes.
200 --- | Create a union of two relational expressions. The expressions should have identical attributes.
@@ -214,11 +217,20 @@ data RelationalExprBase a =
214 Extend (ExtendTupleExprBase a) (RelationalExprBase a) |
217 Extend (ExtendTupleExprBase a) (RelationalExprBase a) |
215 --Summarize :: AtomExpr -> AttributeName -> RelationalExpr -> RelationalExpr -> RelationalExpr -- a special case of Extend
218 --Summarize :: AtomExpr -> AttributeName -> RelationalExpr -> RelationalExpr -> RelationalExpr -- a special case of Extend
216 --Evaluate relationalExpr with scoped views
219 --Evaluate relationalExpr with scoped views
217− With [(RelVarName,RelationalExprBase a)] (RelationalExprBase a)
220+ With [(WithNameExprBase a, RelationalExprBase a)] (RelationalExprBase a)
218− deriving (Show, Eq, Generic, NFData)
221+ deriving (Show, Eq, Generic, NFData, Foldable, Functor, Traversable)
219
222
220instance Binary RelationalExpr
223instance 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+
222type NotificationName = StringType
234type NotificationName = StringType
223type Notifications = M.Map NotificationName Notification
235type Notifications = M.Map NotificationName Notification
224
236
@@ -262,7 +274,14 @@ data DataConstructorDefArg = DataConstructorDefTypeConstructorArg TypeConstructo
262 deriving (Show, Generic, Binary, Eq, NFData)
274 deriving (Show, Generic, Binary, Eq, NFData)
263
275
264type InclusionDependencies = M.Map IncDepName InclusionDependency
276type 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
266
285
267type SchemaName = StringType
286type SchemaName = StringType
268
287
@@ -305,15 +324,21 @@ type AttributeNameAtomExprMap = M.Map AttributeName AtomExpr
305--used for returning information about individual expressions
324--used for returning information about individual expressions
306type DatabaseContextExprName = StringType
325type DatabaseContextExprName = StringType
307
326
327+type DatabaseContextExpr = DatabaseContextExprBase ()
328+
329+type GraphRefDatabaseContextExpr = DatabaseContextExprBase GraphRefTransactionMarker
330+
331+instance Binary GraphRefDatabaseContextExpr
332+
308-- | Database context expressions modify the database context.
333-- | Database context expressions modify the database context.
309−data DatabaseContextExpr =
334+data DatabaseContextExprBase a =
310 NoOperation |
335 NoOperation |
311− Define RelVarName [AttributeExpr] |
336+ Define RelVarName [AttributeExprBase a] |
312 Undefine RelVarName | --forget existence of relvar X
337 Undefine RelVarName | --forget existence of relvar X
313− Assign RelVarName RelationalExpr |
338+ Assign RelVarName (RelationalExprBase a) |
314− Insert RelVarName RelationalExpr |
339+ Insert RelVarName (RelationalExprBase a) |
315− Delete RelVarName RestrictionPredicateExpr |
340+ Delete RelVarName (RestrictionPredicateExprBase a) |
316− Update RelVarName AttributeNameAtomExprMap RestrictionPredicateExpr |
341+ Update RelVarName AttributeNameAtomExprMap (RestrictionPredicateExprBase a) |
317
342
318 AddInclusionDependency IncDepName InclusionDependency |
343 AddInclusionDependency IncDepName InclusionDependency |
319 RemoveInclusionDependency IncDepName |
344 RemoveInclusionDependency IncDepName |
@@ -329,24 +354,33 @@ data DatabaseContextExpr =
329
354
330 RemoveDatabaseContextFunction DatabaseContextFunctionName |
355 RemoveDatabaseContextFunction DatabaseContextFunctionName |
331
356
332− ExecuteDatabaseContextFunction DatabaseContextFunctionName [AtomExpr] |
357+ ExecuteDatabaseContextFunction DatabaseContextFunctionName [AtomExprBase a] |
333
358
334− MultipleExpr [DatabaseContextExpr]
359+ MultipleExpr [DatabaseContextExprBase a]
335− deriving (Show, Eq, Binary, Generic)
360+ deriving (Show, Eq, Generic)
361+
362+instance Binary DatabaseContextExpr
336
363
337type ObjModuleName = StringType
364type ObjModuleName = StringType
338type ObjFunctionName = StringType
365type ObjFunctionName = StringType
339type Range = (Int,Int)
366type Range = (Int,Int)
340-- | Adding an atom function should be nominally a DatabaseExpr except for the fact that it cannot be performed purely. Thus, we create the DatabaseContextIOExpr.
367-- | Adding an atom function should be nominally a DatabaseExpr except for the fact that it cannot be performed purely. Thus, we create the DatabaseContextIOExpr.
368+data DatabaseContextIOExprBase a =
341−data DatabaseContextIOExpr = AddAtomFunction AtomFunctionName [TypeConstructor] AtomFunctionBodyScript |
369+ AddAtomFunction AtomFunctionName [TypeConstructor] AtomFunctionBodyScript |
342− LoadAtomFunctions ObjModuleName ObjFunctionName FilePath |
370+ LoadAtomFunctions ObjModuleName ObjFunctionName FilePath |
343− AddDatabaseContextFunction DatabaseContextFunctionName [TypeConstructor] DatabaseContextFunctionBodyScript |
371+ AddDatabaseContextFunction DatabaseContextFunctionName [TypeConstructor] DatabaseContextFunctionBodyScript |
344− LoadDatabaseContextFunctions ObjModuleName ObjFunctionName FilePath |
372+ LoadDatabaseContextFunctions ObjModuleName ObjFunctionName FilePath |
345− CreateArbitraryRelation RelVarName [AttributeExpr] Range
373+ CreateArbitraryRelation RelVarName [AttributeExprBase a] Range
346 deriving (Show, Eq, Generic, Binary)
374 deriving (Show, Eq, Generic, Binary)
347
375
376+type GraphRefDatabaseContextIOExpr = DatabaseContextIOExprBase GraphRefTransactionMarker
377+
378+type DatabaseContextIOExpr = DatabaseContextIOExprBase ()
379+
348type RestrictionPredicateExpr = RestrictionPredicateExprBase ()
380type RestrictionPredicateExpr = RestrictionPredicateExprBase ()
349
381
382+type GraphRefRestrictionPredicateExpr = RestrictionPredicateExprBase GraphRefTransactionMarker
383+
350-- | Restriction predicates are boolean algebra components which, when composed, indicate whether or not a tuple should be retained during a restriction (filtering) operation.
384-- | Restriction predicates are boolean algebra components which, when composed, indicate whether or not a tuple should be retained during a restriction (filtering) operation.
351data RestrictionPredicateExprBase a =
385data RestrictionPredicateExprBase a =
352 TruePredicate |
386 TruePredicate |
@@ -356,7 +390,7 @@ data RestrictionPredicateExprBase a =
356 RelationalExprPredicate (RelationalExprBase a) | --type must be same as true and false relations (no attributes)
390 RelationalExprPredicate (RelationalExprBase a) | --type must be same as true and false relations (no attributes)
357 AtomExprPredicate (AtomExprBase a) | --atom must evaluate to boolean
391 AtomExprPredicate (AtomExprBase a) | --atom must evaluate to boolean
358 AttributeEqualityPredicate AttributeName (AtomExprBase a) -- relationalexpr must result in relation with single tuple
392 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)
360
394
361instance Binary RestrictionPredicateExpr
395instance Binary RestrictionPredicateExpr
362
396
@@ -369,17 +403,26 @@ type TransactionHeads = M.Map HeadName Transaction
369-- | The transaction graph is the global database's state which references every committed transaction.
403-- | The transaction graph is the global database's state which references every committed transaction.
370data TransactionGraph = TransactionGraph TransactionHeads (S.Set Transaction)
404data TransactionGraph = TransactionGraph TransactionHeads (S.Set Transaction)
371
405
372−transactionsForGraph :: TransactionGraph -> S.Set Transaction
373−transactionsForGraph (TransactionGraph _ t) = t
374−
375transactionHeadsForGraph :: TransactionGraph -> TransactionHeads
406transactionHeadsForGraph :: TransactionGraph -> TransactionHeads
376−transactionHeadsForGraph (TransactionGraph heads _) = heads
407+transactionHeadsForGraph (TransactionGraph hs _) = hs
408+
409+transactionsForGraph :: TransactionGraph -> S.Set Transaction
410+transactionsForGraph (TransactionGraph _ ts) = ts
377
411
378-- | Every transaction has context-specific information attached to it.
412-- | Every transaction has context-specific information attached to it.
413+-- The `TransactionDiff`s represent child/edge relationships to previous transactions (branches or continuations of the same branch).
379−data TransactionInfo = TransactionInfo TransactionId (S.Set TransactionId) UTCTime | -- 1 parent + n children
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
380− MergeTransactionInfo TransactionId TransactionId (S.Set TransactionId) UTCTime -- 2 parents, n children
422+ MergeTransactionInfo TransactionId TransactionId TransactionDiffs UTCTime -- 2 parents, n children
381− deriving(Show, Generic)
423+ deriving (Show, Generic)
382−
424+-}
425+
383instance Binary TransactionInfo
426instance Binary TransactionInfo
384
427
385-- | Every set of modifications made to the database are atomically committed to the transaction graph as a transaction.
428-- | 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
387
430
388data Transaction = Transaction TransactionId TransactionInfo Schemas
431data Transaction = Transaction TransactionId TransactionInfo Schemas
389
432
390−--instance Binary Transaction
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.
391−
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+
392type DirtyFlag = Bool
437type DirtyFlag = Bool
393
438
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.
439+type TransactionDiffExpr = DatabaseContextExpr
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
397
440
398transactionId :: Transaction -> TransactionId
441transactionId :: Transaction -> TransactionId
399transactionId (Transaction tid _ _) = tid
442transactionId (Transaction tid _ _) = tid
@@ -409,24 +452,27 @@ instance Ord Transaction where
409
452
410type AtomExpr = AtomExprBase ()
453type AtomExpr = AtomExprBase ()
411
454
455+type GraphRefAtomExpr = AtomExprBase GraphRefTransactionMarker
456+
412-- | An atom expression represents an action to take when extending a relation or when statically defining a relation or a new tuple.
457-- | An atom expression represents an action to take when extending a relation or when statically defining a relation or a new tuple.
413data AtomExprBase a = AttributeAtomExpr AttributeName |
458data AtomExprBase a = AttributeAtomExpr AttributeName |
414 NakedAtomExpr Atom |
459 NakedAtomExpr Atom |
415 FunctionAtomExpr AtomFunctionName [AtomExprBase a] a |
460 FunctionAtomExpr AtomFunctionName [AtomExprBase a] a |
416 RelationAtomExpr (RelationalExprBase a) |
461 RelationAtomExpr (RelationalExprBase a) |
417 ConstructedAtomExpr DataConstructorName [AtomExprBase a] a
462 ConstructedAtomExpr DataConstructorName [AtomExprBase a] a
418− deriving (Eq,Show,Generic, NFData)
463+ deriving (Eq,Show,Generic, NFData, Foldable, Functor, Traversable)
419
464
420instance Binary AtomExpr
465instance Binary AtomExpr
421
466
422-- | Used in tuple creation when creating a relation.
467-- | Used in tuple creation when creating a relation.
423data ExtendTupleExprBase a = AttributeExtendTupleExpr AttributeName (AtomExprBase a)
468data ExtendTupleExprBase a = AttributeExtendTupleExpr AttributeName (AtomExprBase a)
424− deriving (Show, Eq, Generic, NFData)
469+ deriving (Show, Eq, Generic, NFData, Foldable, Functor, Traversable)
425
470
426−type ExtendTupleExpr = ExtendTupleExprBase ()
471+type ExtendTupleExpr = ExtendTupleExprBase ()
472+type GraphRefExtendTupleExpr = ExtendTupleExprBase GraphRefTransactionMarker
427
473
428instance Binary ExtendTupleExpr
474instance Binary ExtendTupleExpr
429−
475+
430--enumerates the list of functions available to be run as part of tuple expressions
476--enumerates the list of functions available to be run as part of tuple expressions
431type AtomFunctions = HS.HashSet AtomFunction
477type AtomFunctions = HS.HashSet AtomFunction
432
478
@@ -471,9 +517,12 @@ data AttributeNamesBase a = AttributeNames (S.Set AttributeName) |
471 UnionAttributeNames (AttributeNamesBase a) (AttributeNamesBase a) |
517 UnionAttributeNames (AttributeNamesBase a) (AttributeNamesBase a) |
472 IntersectAttributeNames (AttributeNamesBase a) (AttributeNamesBase a) |
518 IntersectAttributeNames (AttributeNamesBase a) (AttributeNamesBase a) |
473 RelationalExprAttributeNames (RelationalExprBase a) -- use attribute names from the relational expression's type
519 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)
475
521
476−type AttributeNames = AttributeNamesBase ()
522+type AttributeNames = AttributeNamesBase ()
523+
524+type GraphRefAttributeNames = AttributeNamesBase GraphRefTransactionMarker
525+
477instance Binary AttributeNames
526instance Binary AttributeNames
478
527
479-- | The persistence strategy is a global database option which represents how to persist the database in the filesystem, if at all.
528-- | 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
483 deriving (Show, Read)
532 deriving (Show, Read)
484
533
485type AttributeExpr = AttributeExprBase ()
534type AttributeExpr = AttributeExprBase ()
535+type GraphRefAttributeExpr = AttributeExprBase GraphRefTransactionMarker
486
536
487-- | Create attributes dynamically.
537-- | Create attributes dynamically.
488data AttributeExprBase a = AttributeAndTypeNameExpr AttributeName TypeConstructor a |
538data AttributeExprBase a = AttributeAndTypeNameExpr AttributeName TypeConstructor a |
489 NakedAttributeExpr Attribute
539 NakedAttributeExpr Attribute
490− deriving (Eq, Show, Generic, Binary, NFData)
540+ deriving (Eq, Show, Generic, Binary, NFData, Foldable, Functor, Traversable)
491
541
492-- | Dynamically create a tuple from attribute names and 'AtomExpr's.
542-- | Dynamically create a tuple from attribute names and 'AtomExpr's.
493newtype TupleExprBase a = TupleExpr (M.Map AttributeName (AtomExprBase a))
543newtype TupleExprBase a = TupleExpr (M.Map AttributeName (AtomExprBase a))
494− deriving (Eq, Show, Generic, NFData)
544+ deriving (Eq, Show, Generic, NFData, Foldable, Functor, Traversable)
495
545
496−instance Binary TupleExpr
546+instance Binary TupleExpr
497−
547+
498−type TupleExpr = TupleExprBase ()
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
499
560
500data MergeStrategy =
561data MergeStrategy =
501 -- | 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)
562 -- | 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
568atomTypeVars (ConstructedAtomType _ tvMap) = M.keysSet tvMap
629atomTypeVars (ConstructedAtomType _ tvMap) = M.keysSet tvMap
569atomTypeVars (TypeVariableType nam) = S.singleton nam
630atomTypeVars (TypeVariableType nam) = S.singleton nam
570
631
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 #-}
2{-|
2{-|
3Module: ProjectM36.Client
3Module: ProjectM36.Client
4
4
@@ -46,8 +46,10 @@ module ProjectM36.Client
46 PersistenceStrategy(..),
46 PersistenceStrategy(..),
47 RelationalExpr,
47 RelationalExpr,
48 RelationalExprBase(..),
48 RelationalExprBase(..),
49+ DatabaseContextExprBase(..),
49− DatabaseContextExpr(..),
50+ DatabaseContextExpr,
51+ DatabaseContextIOExprBase(..),
50− DatabaseContextIOExpr(..),
52+ DatabaseContextIOExpr,
51 Attribute(..),
53 Attribute(..),
52 MergeStrategy(..),
54 MergeStrategy(..),
53 attributesFromList,
55 attributesFromList,
@@ -97,6 +99,7 @@ module ProjectM36.Client
97 AtomType(..),
99 AtomType(..),
98 Atomable(..),
100 Atomable(..),
99 TupleExprBase(..),
101 TupleExprBase(..),
102+ TupleExprsBase(..),
100 AtomExprBase(..),
103 AtomExprBase(..),
101 RestrictionPredicateExprBase(..),
104 RestrictionPredicateExprBase(..),
102 withTransaction
105 withTransaction
@@ -112,7 +115,6 @@ import qualified ProjectM36.DataFrame as DF
112import ProjectM36.DatabaseContextFunction as DCF
115import ProjectM36.DatabaseContextFunction as DCF
113import qualified ProjectM36.IsomorphicSchema as Schema
116import qualified ProjectM36.IsomorphicSchema as Schema
114import Control.Monad.State
117import Control.Monad.State
115−import Control.Monad.Trans.Reader
116import qualified ProjectM36.RelationalExpression as RE
118import qualified ProjectM36.RelationalExpression as RE
117import ProjectM36.DatabaseContext (basicDatabaseContext)
119import ProjectM36.DatabaseContext (basicDatabaseContext)
118import qualified ProjectM36.TransactionGraph as Graph
120import qualified ProjectM36.TransactionGraph as Graph
@@ -120,21 +122,20 @@ import ProjectM36.TransactionGraph
120import qualified ProjectM36.Transaction as Trans
122import qualified ProjectM36.Transaction as Trans
121import ProjectM36.TransactionGraph.Persist
123import ProjectM36.TransactionGraph.Persist
122import ProjectM36.Attribute
124import ProjectM36.Attribute
123−import ProjectM36.TransGraphRelationalExpression (TransGraphRelationalExpr, evalTransGraphRelationalExpr)
125+import ProjectM36.TransGraphRelationalExpression as TGRE (TransGraphRelationalExpr)
124import ProjectM36.Persist (DiskSync(..))
126import ProjectM36.Persist (DiskSync(..))
125import ProjectM36.FileLock
127import ProjectM36.FileLock
128+import ProjectM36.NormalizeExpr
126import ProjectM36.Notifications
129import ProjectM36.Notifications
127import ProjectM36.Server.RemoteCallTypes
130import ProjectM36.Server.RemoteCallTypes
128import qualified ProjectM36.DisconnectedTransaction as Discon
131import qualified ProjectM36.DisconnectedTransaction as Discon
129import ProjectM36.Relation (typesAsRelation)
132import ProjectM36.Relation (typesAsRelation)
130import ProjectM36.ScriptSession (initScriptSession, ScriptSession)
133import ProjectM36.ScriptSession (initScriptSession, ScriptSession)
131import qualified ProjectM36.Relation as R
134import qualified ProjectM36.Relation as R
132−import qualified ProjectM36.DatabaseContext as DBC
133import Control.Exception.Base
135import Control.Exception.Base
134import GHC.Conc.Sync
136import GHC.Conc.Sync
135
137
136import Network.Transport (Transport(closeTransport))
138import Network.Transport (Transport(closeTransport))
137−
138#if MIN_VERSION_network_transport_tcp(0,7,0)
139#if MIN_VERSION_network_transport_tcp(0,7,0)
139import Network.Transport.TCP (createTransport, defaultTCPParameters, defaultTCPAddr)
140import Network.Transport.TCP (createTransport, defaultTCPParameters, defaultTCPAddr)
140import Network.Transport.TCP.Internal (encodeEndPointAddress)
141import Network.Transport.TCP.Internal (encodeEndPointAddress)
@@ -155,7 +156,6 @@ import Control.Distributed.Process (ProcessId, Process, receiveWait, send, match
155import Control.Exception (IOException, handle, AsyncException, throwIO, fromException, Exception)
156import Control.Exception (IOException, handle, AsyncException, throwIO, fromException, Exception)
156import Control.Concurrent.MVar
157import Control.Concurrent.MVar
157import qualified Data.Map as M
158import qualified Data.Map as M
158−import Control.Distributed.Process.Serializable (Serializable)
159#if MIN_VERSION_stm_containers(1,0,0)
159#if MIN_VERSION_stm_containers(1,0,0)
160import qualified StmContainers.Map as StmMap
160import qualified StmContainers.Map as StmMap
161import qualified StmContainers.Set as StmSet
161import qualified StmContainers.Set as StmSet
@@ -171,6 +171,7 @@ import GHC.Generics (Generic)
171import Control.DeepSeq (force)
171import Control.DeepSeq (force)
172import System.IO
172import System.IO
173import Data.Time.Clock
173import Data.Time.Clock
174+import Data.Typeable
174
175
175type Hostname = String
176type Hostname = String
176
177
@@ -315,9 +316,9 @@ connectProjectM36 :: ConnectionInfo -> IO (Either ConnectionError Connection)
315--create a new in-memory database/transaction graph
316--create a new in-memory database/transaction graph
316connectProjectM36 (InProcessConnectionInfo strat notificationCallback ghcPkgPaths) = do
317connectProjectM36 (InProcessConnectionInfo strat notificationCallback ghcPkgPaths) = do
317 freshId <- nextRandom
318 freshId <- nextRandom
318− stamp <- getCurrentTime
319+ tstamp <- getCurrentTime
319 let bootstrapContext = basicDatabaseContext
320 let bootstrapContext = basicDatabaseContext
320− freshGraph = bootstrapTransactionGraph stamp freshId bootstrapContext
321+ freshGraph = bootstrapTransactionGraph tstamp freshId bootstrapContext
321 case strat of
322 case strat of
322 --create date examples graph for now- probably should be empty context in the future
323 --create date examples graph for now- probably should be empty context in the future
323 NoPersistence -> do
324 NoPersistence -> do
@@ -410,7 +411,7 @@ createSessionAtCommit_ commitId newSessionId (InProcessConnection conf) = do
410 let sessions = ipSessions conf
411 let sessions = ipSessions conf
411 graphTvar = ipTransactionGraph conf
412 graphTvar = ipTransactionGraph conf
412 graph <- readTVar graphTvar
413 graph <- readTVar graphTvar
413− case transactionForId commitId graph of
414+ case RE.transactionForId commitId graph of
414 Left err -> pure (Left err)
415 Left err -> pure (Left err)
415 Right transaction -> do
416 Right transaction -> do
416 let freshDiscon = DisconnectedTransaction commitId (Trans.schemas transaction) False
417 let freshDiscon = DisconnectedTransaction commitId (Trans.schemas transaction) False
@@ -498,7 +499,7 @@ safeLogin login procId = do
498 Left (_ :: ServerError) -> pure False
499 Left (_ :: ServerError) -> pure False
499 Right val -> pure val
500 Right val -> pure val
500
501
501−remoteCall :: (Serializable a, Serializable b) => Connection -> a -> IO b
502+remoteCall :: (Binary a, Binary b, Typeable a, Typeable b) => Connection -> a -> IO b
502remoteCall (InProcessConnection _ ) _ = error "remoteCall called on local connection"
503remoteCall (InProcessConnection _ ) _ = error "remoteCall called on local connection"
503remoteCall (RemoteProcessConnection conf) arg = runProcessResult localNode $ do
504remoteCall (RemoteProcessConnection conf) arg = runProcessResult localNode $ do
504 ret <- safeCall serverProcessId arg
505 ret <- safeCall serverProcessId arg
@@ -573,13 +574,14 @@ executeRelationalExpr sessionId (InProcessConnection conf) expr = excEither $ at
573 Right expr
574 Right expr
574 case expr' of
575 case expr' of
575 Left err -> pure (Left err)
576 Left err -> pure (Left err)
576− Right expr'' -> case optimizeRelationalExpr (Sess.concreteDatabaseContext session) expr'' of
577+ Right expr'' -> do
577− Left err -> pure (Left err)
578+ let graphTvar = ipTransactionGraph conf
578− Right optExpr -> do
579+ graph <- readTVar graphTvar
579− let evald = runReader (RE.evalRelationalExpr optExpr) (RE.mkRelationalExprState (Sess.concreteDatabaseContext session))
580+ let reEnv = RE.mkRelationalExprEnv (Sess.concreteDatabaseContext session) graph
580− case evald of
581+ case optimizeAndEvalRelationalExpr reEnv expr'' 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
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+
583executeRelationalExpr sessionId conn@(RemoteProcessConnection _) relExpr = remoteCall conn (ExecuteRelationalExpr sessionId relExpr)
585executeRelationalExpr sessionId conn@(RemoteProcessConnection _) relExpr = remoteCall conn (ExecuteRelationalExpr sessionId relExpr)
584
586
585-- | 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.
587-- | 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
596 Schema.processDatabaseContextExprInSchema schema expr
598 Schema.processDatabaseContextExprInSchema schema expr
597 case expr' of
599 case expr' of
598 Left err -> pure (Left err)
600 Left err -> pure (Left err)
599− Right expr'' -> case runState (do
601+ Right expr'' -> do
600− eOptExpr <- applyStaticDatabaseOptimization expr''
602+ graph <- readTVar (ipTransactionGraph conf)
601− case eOptExpr of
602− Left err -> pure (Left err)
603− Right optExpr ->
604− RE.evalDatabaseContextExpr optExpr) (RE.freshDatabaseState (Sess.concreteDatabaseContext session)) of
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
605− (Left err,_) -> pure (Left err)
607+ Left err -> pure (Left err)
608+ Right newState ->
606− (Right (), (_,_,False)) -> pure (Right ()) --optimization- if nothing was dirtied, nothing to do
609+ if not (RE.dbc_dirty newState) then --nothing dirtied, nothing to do
607− (Right (), (!context',_,True)) -> do
610+ pure (Right ())
611+ else do
608− let newDiscon = DisconnectedTransaction (Sess.parentId session) newSchemas True
612+ let newDiscon = DisconnectedTransaction (Sess.parentId session) newSchemas True
613+ context' = RE.dbc_context newState
609− newSubschemas = Schema.processDatabaseContextExprSchemasUpdate (Sess.subschemas session) expr
614+ newSubschemas = Schema.processDatabaseContextExprSchemasUpdate (Sess.subschemas session) expr
610− newSchemas = Schemas context' newSubschemas
615+ newSchemas = Schemas context' newSubschemas
611− newSession = Session newDiscon (Sess.schemaName session)
616+ newSession = Session newDiscon (Sess.schemaName session)
612− StmMap.insert newSession sessionId sessions
617+ StmMap.insert newSession sessionId sessions
613− pure (Right ())
618+ pure (Right ())
614executeDatabaseContextExpr sessionId conn@(RemoteProcessConnection _) dbExpr = remoteCall conn (ExecuteDatabaseContextExpr sessionId dbExpr)
619executeDatabaseContextExpr sessionId conn@(RemoteProcessConnection _) dbExpr = remoteCall conn (ExecuteDatabaseContextExpr sessionId dbExpr)
615
620
616-- | 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.
621-- | 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
620 id1 <- nextRandom
625 id1 <- nextRandom
621 id2 <- nextRandom
626 id2 <- nextRandom
622 id3 <- nextRandom
627 id3 <- nextRandom
623− stamp <- getCurrentTime
628+ tstamp <- getCurrentTime
624 commitLock_ sessionId conf $ \graph -> do
629 commitLock_ sessionId conf $ \graph -> do
625 eSession <- sessionForSessionId sessionId sessions
630 eSession <- sessionForSessionId sessionId sessions
626 case eSession of
631 case eSession of
@@ -631,10 +636,10 @@ autoMergeToHead sessionId (InProcessConnection conf) strat headName' = do
631 Just headTrans -> do
636 Just headTrans -> do
632 --attempt fast-forward commit, if possible
637 --attempt fast-forward commit, if possible
633 let graphInfo = if Sess.parentId session == transactionId headTrans then do
638 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
635 pure (ret, [id1])
640 pure (ret, [id1])
636 else do
641 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
638 pure (ret, [id1,id2,id3])
643 pure (ret, [id1,id2,id3])
639 case graphInfo of
644 case graphInfo of
640 Left err -> pure (Left err)
645 Left err -> pure (Left err)
@@ -652,13 +657,18 @@ executeDatabaseContextIOExpr sessionId (InProcessConnection conf) expr = excEith
652 case eSession of
657 case eSession of
653 Left err -> pure (Left err)
658 Left err -> pure (Left err)
654 Right session -> do
659 Right session -> do
660+ graph <- readTVarIO (ipTransactionGraph conf)
661+ let env = RE.DatabaseContextIOEvalEnv transId graph scriptSession
662+ transId = Sess.parentId session
655− res <- RE.evalDatabaseContextIOExpr scriptSession (Sess.concreteDatabaseContext session) expr
663+ context = Sess.concreteDatabaseContext session
664+ res <- RE.runDatabaseContextIOEvalMonad env context (optimizeAndEvalDatabaseContextIOExpr expr)
656 case res of
665 case res of
657 Left err -> pure (Left err)
666 Left err -> pure (Left err)
658− Right context' -> do
667+ Right newState -> do
659− let newDiscon = DisconnectedTransaction (Sess.parentId session) newSchemas True
668+ let newDiscon = DisconnectedTransaction (Sess.parentId session) newSchemas False
660 newSchemas = Schemas context' (Sess.subschemas session)
669 newSchemas = Schemas context' (Sess.subschemas session)
661 newSession = Session newDiscon (Sess.schemaName session)
670 newSession = Session newDiscon (Sess.schemaName session)
671+ context' = RE.dbc_context newState
662 atomically $ StmMap.insert newSession sessionId sessions
672 atomically $ StmMap.insert newSession sessionId sessions
663 pure (Right ())
673 pure (Right ())
664executeDatabaseContextIOExpr sessionId conn@(RemoteProcessConnection _) dbExpr = remoteCall conn (ExecuteDatabaseContextIOExpr sessionId dbExpr)
674executeDatabaseContextIOExpr sessionId conn@(RemoteProcessConnection _) dbExpr = remoteCall conn (ExecuteDatabaseContextIOExpr sessionId dbExpr)
@@ -678,12 +688,18 @@ executeGraphExprSTM_ freshId sessionId session sessions graphExpr graph graphTVa
678-}
688-}
679
689
680-- process notifications for commits
690-- process notifications for commits
691+executeCommitExprSTM_
692+ :: TransactionGraph
693+ -> DatabaseContext
694+ -> DatabaseContext
695+ -> ClientNodes
681−executeCommitExprSTM_ :: DatabaseContext -> DatabaseContext -> ClientNodes -> STM (EvaluatedNotifications, ClientNodes)
696+ -> STM (EvaluatedNotifications, ClientNodes)
682−executeCommitExprSTM_ oldContext newContext nodes = do
697+executeCommitExprSTM_ graph oldContext newContext nodes = do
683 let nots = notifications oldContext
698 let nots = notifications oldContext
684− fireNots = notificationChanges nots oldContext newContext
699+ fireNots = notificationChanges nots graph oldContext newContext
685 evaldNots = M.map mkEvaldNot fireNots
700 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+
687 mkEvaldNot notif = EvaluatedNotification { notification = notif,
703 mkEvaldNot notif = EvaluatedNotification { notification = notif,
688 reportOldRelation = evalInContext (reportOldExpr notif) oldContext,
704 reportOldRelation = evalInContext (reportOldExpr notif) oldContext,
689 reportNewRelation = evalInContext (reportNewExpr notif) newContext}
705 reportNewRelation = evalInContext (reportNewExpr notif) newContext}
@@ -698,18 +714,18 @@ executeGraphExpr :: SessionId -> Connection -> TransactionGraphOperator -> IO (E
698executeGraphExpr sessionId (InProcessConnection conf) graphExpr = excEither $ do
714executeGraphExpr sessionId (InProcessConnection conf) graphExpr = excEither $ do
699 let sessions = ipSessions conf
715 let sessions = ipSessions conf
700 freshId <- nextRandom
716 freshId <- nextRandom
701− stamp <- getCurrentTime
717+ tstamp <- getCurrentTime
702 commitLock_ sessionId conf $ \updatedGraph -> do
718 commitLock_ sessionId conf $ \updatedGraph -> do
703 eSession <- sessionForSessionId sessionId sessions
719 eSession <- sessionForSessionId sessionId sessions
704 case eSession of
720 case eSession of
705 Left err -> pure (Left err)
721 Left err -> pure (Left err)
706 Right session -> do
722 Right session -> do
707 let discon = Sess.disconnectedTransaction session
723 let discon = Sess.disconnectedTransaction session
708− case evalGraphOp stamp freshId discon updatedGraph graphExpr of
724+ case evalGraphOp tstamp freshId discon updatedGraph graphExpr of
709 Left err -> pure (Left err)
725 Left err -> pure (Left err)
710 Right (discon', graph') -> do
726 Right (discon', graph') -> do
711 --if freshId appears in the graph, then we need to pass it on
727 --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')]
713 pure (Right (discon', graph', transIds))
729 pure (Right (discon', graph', transIds))
714{-
730{-
715executeGraphExpr sessionId (InProcessConnection conf) graphExpr = excEither $ do
731executeGraphExpr sessionId (InProcessConnection conf) graphExpr = excEither $ do
@@ -789,11 +805,10 @@ executeTransGraphRelationalExpr :: SessionId -> Connection -> TransGraphRelation
789executeTransGraphRelationalExpr _ (InProcessConnection conf) tgraphExpr = excEither . atomically $ do
805executeTransGraphRelationalExpr _ (InProcessConnection conf) tgraphExpr = excEither . atomically $ do
790 let graphTvar = ipTransactionGraph conf
806 let graphTvar = ipTransactionGraph conf
791 graph <- readTVar graphTvar
807 graph <- readTVar graphTvar
792− case evalTransGraphRelationalExpr tgraphExpr graph of
808+ pure $ force $ optimizeAndEvalTransGraphRelationalExpr graph tgraphExpr
793− Left err -> pure (Left err)
794− Right relExpr -> case runReader (RE.evalRelationalExpr relExpr) (RE.mkRelationalExprState DBC.empty) of
809+{- case runReader (RE.evalRelationalExpr relExpr) (RE.mkRelationalExprState DBC.empty) of
795 Left err -> pure (Left err)
810 Left err -> pure (Left err)
796− Right rel -> pure (force (Right rel))
811+ Right rel -> pure (force (Right rel))-}
797executeTransGraphRelationalExpr sessionId conn@(RemoteProcessConnection _) tgraphExpr = remoteCall conn (ExecuteTransGraphRelationalExpr sessionId tgraphExpr)
812executeTransGraphRelationalExpr sessionId conn@(RemoteProcessConnection _) tgraphExpr = remoteCall conn (ExecuteTransGraphRelationalExpr sessionId tgraphExpr)
798
813
799-- | Schema expressions manipulate the isomorphic schemas for the current 'DatabaseContext'.
814-- | Schema expressions manipulate the isomorphic schemas for the current 'DatabaseContext'.
@@ -805,13 +820,16 @@ executeSchemaExpr sessionId (InProcessConnection conf) schemaExpr = atomically $
805 Left err -> pure (Left err)
820 Left err -> pure (Left err)
806 Right (session, _) -> do
821 Right (session, _) -> do
807 let subschemas' = subschemas session
822 let subschemas' = subschemas session
823+ transId = Sess.parentId session
824+ context = Sess.concreteDatabaseContext session
825+ graph <- readTVar (ipTransactionGraph conf)
808− case Schema.evalSchemaExpr schemaExpr (Sess.concreteDatabaseContext session) subschemas' of
826+ case Schema.evalSchemaExpr schemaExpr context transId graph subschemas' of
809 Left err -> pure (Left err)
827 Left err -> pure (Left err)
810 Right (newSubschemas, newContext) -> do
828 Right (newSubschemas, newContext) -> do
811 --hm- maybe we should start using lenses
829 --hm- maybe we should start using lenses
812 let discon = Sess.disconnectedTransaction session
830 let discon = Sess.disconnectedTransaction session
813 newSchemas = Schemas newContext newSubschemas
831 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)
815 StmMap.insert newSession sessionId sessions
833 StmMap.insert newSession sessionId sessions
816 pure (Right ())
834 pure (Right ())
817executeSchemaExpr sessionId conn@(RemoteProcessConnection _) schemaExpr = remoteCall conn (ExecuteSchemaExpr sessionId schemaExpr)
835executeSchemaExpr sessionId conn@(RemoteProcessConnection _) schemaExpr = remoteCall conn (ExecuteSchemaExpr sessionId schemaExpr)
@@ -861,7 +879,10 @@ typeForRelationalExprSTM sessionId (InProcessConnection conf) relExpr = do
861 Schema.processRelationalExprInSchema schema relExpr
879 Schema.processRelationalExprInSchema schema relExpr
862 case processed of
880 case processed of
863 Left err -> pure (Left err)
881 Left err -> pure (Left err)
882+ Right relExpr' -> do
883+ graph <- readTVar (ipTransactionGraph conf)
864− Right relExpr' -> pure $ runReader (RE.typeForRelationalExpr relExpr') (RE.mkRelationalExprState (Sess.concreteDatabaseContext session))
884+ let reEnv = RE.mkRelationalExprEnv (Sess.concreteDatabaseContext session) graph
885+ pure $ RE.runRelationalExprM reEnv (RE.typeForRelationalExpr relExpr')
865
886
866typeForRelationalExprSTM _ _ _ = error "typeForRelationalExprSTM called on non-local connection"
887typeForRelationalExprSTM _ _ _ = error "typeForRelationalExprSTM called on non-local connection"
867
888
@@ -894,17 +915,22 @@ typeConstructorMapping sessionId (InProcessConnection conf) = do
894typeConstructorMapping sessionId conn@(RemoteProcessConnection _) = remoteCall conn (RetrieveTypeConstructorMapping sessionId)
915typeConstructorMapping sessionId conn@(RemoteProcessConnection _) = remoteCall conn (RetrieveTypeConstructorMapping sessionId)
895
916
896-- | 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.
917-- | 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)
898planForDatabaseContextExpr sessionId (InProcessConnection conf) dbExpr = do
919planForDatabaseContextExpr sessionId (InProcessConnection conf) dbExpr = do
899 let sessions = ipSessions conf
920 let sessions = ipSessions conf
900 atomically $ do
921 atomically $ do
922+ graph <- readTVar (ipTransactionGraph conf)
901 eSession <- sessionAndSchema sessionId sessions
923 eSession <- sessionAndSchema sessionId sessions
902 case eSession of
924 case eSession of
903 Left err -> pure $ Left err
925 Left err -> pure $ Left err
926+ Right (session, _) ->
904− Right (session, _) -> if schemaName session == defaultSchemaName then
927+ if schemaName session == defaultSchemaName then do
905− pure $ evalState (applyStaticDatabaseOptimization dbExpr) (RE.freshDatabaseState (Sess.concreteDatabaseContext session))
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)
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
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-}
907− pure (Right dbExpr)
933+ pure (Left NonConcreteSchemaPlanError)
908
934
909planForDatabaseContextExpr sessionId conn@(RemoteProcessConnection _) dbExpr = remoteCall conn (RetrievePlanForDatabaseContextExpr sessionId dbExpr)
935planForDatabaseContextExpr sessionId conn@(RemoteProcessConnection _) dbExpr = remoteCall conn (RetrievePlanForDatabaseContextExpr sessionId dbExpr)
910
936
@@ -931,17 +957,20 @@ relationVariablesAsRelation :: SessionId -> Connection -> IO (Either RelationalE
931relationVariablesAsRelation sessionId (InProcessConnection conf) = do
957relationVariablesAsRelation sessionId (InProcessConnection conf) = do
932 let sessions = ipSessions conf
958 let sessions = ipSessions conf
933 atomically $ do
959 atomically $ do
960+ graph <- readTVar (ipTransactionGraph conf)
934 eSession <- sessionAndSchema sessionId sessions
961 eSession <- sessionAndSchema sessionId sessions
935 case eSession of
962 case eSession of
936 Left err -> pure (Left err)
963 Left err -> pure (Left err)
937 Right (session, schema) -> do
964 Right (session, schema) -> do
938 let context = Sess.concreteDatabaseContext session
965 let context = Sess.concreteDatabaseContext session
939 if Sess.schemaName session == defaultSchemaName then
966 if Sess.schemaName session == defaultSchemaName then
940− pure $ R.relationVariablesAsRelation (relationVariables context)
967+ pure $ RE.relationVariablesAsRelation context graph
941 else
968 else
942− case Schema.relationVariablesInSchema schema context of
969+ case Schema.relationVariablesInSchema schema of
943 Left err -> pure (Left err)
970 Left err -> pure (Left err)
971+ Right relvars -> do
972+ let schemaContext = context {relationVariables = relvars }
944− Right relvars -> pure $ R.relationVariablesAsRelation relvars
973+ pure $ RE.relationVariablesAsRelation schemaContext graph
945
974
946relationVariablesAsRelation sessionId conn@(RemoteProcessConnection _) = remoteCall conn (RetrieveRelationVariableSummary sessionId)
975relationVariablesAsRelation sessionId conn@(RemoteProcessConnection _) = remoteCall conn (RetrieveRelationVariableSummary sessionId)
947
976
@@ -987,7 +1016,7 @@ headNameSTM_ sessionId sessions graphTvar = do
987 eSession <- sessionForSessionId sessionId sessions
1016 eSession <- sessionForSessionId sessionId sessions
988 case eSession of
1017 case eSession of
989 Left err -> pure (Left err)
1018 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
991 Left err -> pure (Left err)
1020 Left err -> pure (Left err)
992 Right parentTrans -> case headNameForTransaction parentTrans graph of
1021 Right parentTrans -> case headNameForTransaction parentTrans graph of
993 Nothing -> pure (Left UnknownHeadError)
1022 Nothing -> pure (Left UnknownHeadError)
@@ -1099,11 +1128,11 @@ commitLock_ sessionId conf stmBlock = do
1099 writeTVar graphTvar graph'
1128 writeTVar graphTvar graph'
1100 let newSession = Session discon' (Sess.schemaName session)
1129 let newSession = Session discon' (Sess.schemaName session)
1101 StmMap.insert newSession sessionId sessions
1130 StmMap.insert newSession sessionId sessions
1102− case transactionForId (Sess.parentId session) oldGraph of
1131+ case RE.transactionForId (Sess.parentId session) oldGraph of
1103 Left err -> pure $ Left err
1132 Left err -> pure $ Left err
1104 Right previousTrans ->
1133 Right previousTrans ->
1105 if not (Prelude.null transactionIdsToPersist) then do
1134 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
1107 nodesToNotify <- stmSetToList nodes
1136 nodesToNotify <- stmSetToList nodes
1108 pure $ Right (evaldNots, nodesToNotify, graph', transactionIdsToPersist)
1137 pure $ Right (evaldNots, nodesToNotify, graph', transactionIdsToPersist)
1109 else pure (Right (M.empty, [], graph', []))
1138 else pure (Right (M.empty, [], graph', []))
@@ -1165,8 +1194,7 @@ executeDataFrameExpr sessionId conn@(InProcessConnection _) dfExpr = do
1165 Left err -> pure (Left err)
1194 Left err -> pure (Left err)
1166 Right attrs -> do
1195 Right attrs -> do
1167 let attrOrders = zipWith
1196 let attrOrders = zipWith
1168− (\attr' order'
1169− -> DF.AttributeOrder (attributeName attr') order')
1197+ (DF.AttributeOrder . attributeName)
1170 attrs
1198 attrs
1171 orders
1199 orders
1172 case DF.sortDataFrameBy attrOrders . DF.toDataFrame $ rel of
1200 case DF.sortDataFrameBy attrOrders . DF.toDataFrame $ rel of
src/lib/ProjectM36/Client/Simple.hs+2−1
@@ -25,7 +25,8 @@ module ProjectM36.Client.Simple (
25 C.PersistenceStrategy(..),
25 C.PersistenceStrategy(..),
26 C.NotificationCallback,
26 C.NotificationCallback,
27 C.emptyNotificationCallback,
27 C.emptyNotificationCallback,
28− C.DatabaseContextExpr(..),
28+ C.DatabaseContextExprBase(..),
29+ C.DatabaseContextExpr,
29 C.RelationalExprBase(..)
30 C.RelationalExprBase(..)
30 ) where
31 ) where
31
32
src/lib/ProjectM36/DatabaseContext.hs+9−4
@@ -1,5 +1,6 @@
1module ProjectM36.DatabaseContext where
1module ProjectM36.DatabaseContext where
2import ProjectM36.Base
2import ProjectM36.Base
3+import Control.Monad (void)
3import qualified Data.Map as M
4import qualified Data.Map as M
4import qualified Data.HashSet as HS
5import qualified Data.HashSet as HS
5import ProjectM36.DataTypes.Basic
6import ProjectM36.DataTypes.Basic
@@ -14,20 +15,24 @@ empty = DatabaseContext { inclusionDependencies = M.empty,
14 atomFunctions = HS.empty,
15 atomFunctions = HS.empty,
15 dbcFunctions = HS.empty,
16 dbcFunctions = HS.empty,
16 typeConstructorMapping = [] }
17 typeConstructorMapping = [] }
18+
19+-- | Remove TransactionId markers on GraphRefRelationalExpr
20+stripGraphRefRelationalExpr :: GraphRefRelationalExpr -> RelationalExpr
21+stripGraphRefRelationalExpr = void
17
22
18-- | convert an existing database context into its constituent expression.
23-- | convert an existing database context into its constituent expression.
19databaseContextAsDatabaseContextExpr :: DatabaseContext -> DatabaseContextExpr
24databaseContextAsDatabaseContextExpr :: DatabaseContext -> DatabaseContextExpr
20databaseContextAsDatabaseContextExpr context = MultipleExpr $ relVarsExprs ++ incDepsExprs ++ funcsExprs
25databaseContextAsDatabaseContextExpr context = MultipleExpr $ relVarsExprs ++ incDepsExprs ++ funcsExprs
21 where
26 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))
23 incDepsExprs :: [DatabaseContextExpr]
28 incDepsExprs :: [DatabaseContextExpr]
24 incDepsExprs = map (uncurry AddInclusionDependency) (M.toList (inclusionDependencies context))
29 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-}
26
31
27basicDatabaseContext :: DatabaseContext
32basicDatabaseContext :: DatabaseContext
28basicDatabaseContext = DatabaseContext { inclusionDependencies = M.empty,
33basicDatabaseContext = DatabaseContext { inclusionDependencies = M.empty,
29− relationVariables = M.fromList [("true", relationTrue),
34+ relationVariables = M.fromList [("true", ExistingRelation relationTrue),
30− ("false", relationFalse)],
35+ ("false", ExistingRelation relationFalse)],
31 atomFunctions = basicAtomFunctions,
36 atomFunctions = basicAtomFunctions,
32 dbcFunctions = basicDatabaseContextFunctions,
37 dbcFunctions = basicDatabaseContextFunctions,
33 notifications = M.empty,
38 notifications = M.empty,
src/lib/ProjectM36/DatabaseContextFunctionUtils.hs+15−8
@@ -3,13 +3,20 @@ import ProjectM36.RelationalExpression
3import ProjectM36.Base
3import ProjectM36.Base
4import ProjectM36.DatabaseContextFunctionError
4import ProjectM36.DatabaseContextFunctionError
5import ProjectM36.Error
5import ProjectM36.Error
6−import Control.Monad.State
6+import ProjectM36.StaticOptimizer
7−import Control.Monad.Trans.Reader
8
7
9−executeDatabaseContextExpr :: DatabaseContextExpr -> DatabaseContext -> Either DatabaseContextFunctionError DatabaseContext
8+executeDatabaseContextExpr :: DatabaseContextExpr -> TransactionId -> TransactionGraph -> DatabaseContext -> Either DatabaseContextFunctionError DatabaseContext
10−executeDatabaseContextExpr expr context = case runState (evalDatabaseContextExpr expr) (freshDatabaseState context) of
9+executeDatabaseContextExpr expr transId graph context' =
11− (Right (), (context', _, _)) -> pure context'
10+ case run of
11+ Right st -> pure (dbc_context st)
12− (Left err, _) -> error (show err)
12+ Left err -> error (show err)
13+ where
14+ env = mkDatabaseContextEvalEnv transId graph
15+ run = runDatabaseContextEvalMonad context' env (optimizeAndEvalDatabaseContextExpr True expr)
13
16
14−executeRelationalExpr :: RelationalExpr -> DatabaseContext -> Either RelationalError Relation
17+executeRelationalExpr :: RelationalExpr -> DatabaseContext -> TransactionGraph -> Either RelationalError Relation
15−executeRelationalExpr expr context = runReader (evalRelationalExpr expr) (mkRelationalExprState context)
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,
16 atomFunctions = basicAtomFunctions,
16 atomFunctions = basicAtomFunctions,
17 typeConstructorMapping = basicTypeConstructorMapping }
17 typeConstructorMapping = basicTypeConstructorMapping }
18 where -- these must be lower case now that data constructors are in play
18 where -- these must be lower case now that data constructors are in play
19− dateRelVars = M.fromList [("s", suppliers),
19+ dateRelVars = M.fromList [("s", ExistingRelation suppliers),
20− ("p", products),
20+ ("p", ExistingRelation products),
21− ("sp", supplierProducts)]
21+ ("sp", ExistingRelation supplierProducts)]
22 suppliers = suppliersRel
22 suppliers = suppliersRel
23 products = productsRel
23 products = productsRel
24 supplierProducts = supplierProductsRel
24 supplierProducts = supplierProductsRel
src/lib/ProjectM36/DisconnectedTransaction.hs+7−1
@@ -8,4 +8,10 @@ schemas :: DisconnectedTransaction -> Schemas
8schemas (DisconnectedTransaction _ s _) = s
8schemas (DisconnectedTransaction _ s _) = s
9
9
10parentId :: DisconnectedTransaction -> TransactionId
10parentId :: 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)
96 | SchemaCreationError SchemaError
96 | SchemaCreationError SchemaError
97
97
98 | ImproperDatabaseStateError
98 | ImproperDatabaseStateError
99+
100+ | NonConcreteSchemaPlanError
101+
102+ | NoUncommittedContextInEvalError
103+ | TupleExprsReferenceMultipleMarkersError
99
104
100 | MultipleErrors [RelationalError]
105 | MultipleErrors [RelationalError]
101 deriving (Show,Eq,Generic,Binary,Typeable, NFData)
106 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
3import ProjectM36.Base
3import ProjectM36.Base
4import ProjectM36.Error
4import ProjectM36.Error
5import ProjectM36.MiscUtils
5import ProjectM36.MiscUtils
6−import ProjectM36.RelationalExpression
7import ProjectM36.Relation
6import ProjectM36.Relation
7+import ProjectM36.NormalizeExpr
8+import ProjectM36.RelationalExpression
8import qualified ProjectM36.AttributeNames as AN
9import qualified ProjectM36.AttributeNames as AN
9import Control.Monad
10import Control.Monad
10−import Control.Monad.State
11−import Control.Monad.Trans.Reader
12import GHC.Generics
11import GHC.Generics
13import Data.Binary
12import Data.Binary
14import qualified Data.Map as M
13import qualified Data.Map as M
@@ -256,15 +255,15 @@ inclusionDependencyInSchema schema (InclusionDependency rexprA rexprB) = do
256inclusionDependenciesInSchema :: Schema -> InclusionDependencies -> Either RelationalError InclusionDependencies
255inclusionDependenciesInSchema :: Schema -> InclusionDependencies -> Either RelationalError InclusionDependencies
257inclusionDependenciesInSchema schema incDeps = M.fromList <$> mapM (\(depName, dep) -> inclusionDependencyInSchema schema dep >>= \newDep -> pure (depName, newDep)) (M.toList incDeps)
256inclusionDependenciesInSchema schema incDeps = M.fromList <$> mapM (\(depName, dep) -> inclusionDependencyInSchema schema dep >>= \newDep -> pure (depName, newDep)) (M.toList incDeps)
258
257
259−relationVariablesInSchema :: Schema -> DatabaseContext -> Either RelationalError RelationVariables
258+relationVariablesInSchema :: Schema -> Either RelationalError RelationVariables
260−relationVariablesInSchema schema@(Schema morphs) context = foldM transform M.empty morphs
259+relationVariablesInSchema schema@(Schema morphs) = foldM transform M.empty morphs
261 where
260 where
262 transform newRvMap morph = do
261 transform newRvMap morph = do
263 let rvNames = isomorphInRelVarNames morph
262 let rvNames = isomorphInRelVarNames morph
264 rvAssocs <- mapM (\rv -> do
263 rvAssocs <- mapM (\rv -> do
265 expr' <- processRelationalExprInSchema schema (RelationVariable rv ())
264 expr' <- processRelationalExprInSchema schema (RelationVariable rv ())
266− rel <- runReader (evalRelationalExpr expr') (RelationalExprStateElems context)
265+ let gfExpr = runProcessExprM UncommittedContextMarker (processRelationalExpr expr')
267− pure (rv, rel)) rvNames
266+ pure (rv, gfExpr)) rvNames
268 pure (M.union newRvMap (M.fromList rvAssocs))
267 pure (M.union newRvMap (M.fromList rvAssocs))
269
268
270
269
@@ -312,24 +311,24 @@ createIncDepsForIsomorph sname (IsoRestrict origRv predi (rvTrue, rvFalse)) = le
312createIncDepsForIsomorph _ _ = M.empty
311createIncDepsForIsomorph _ _ = M.empty
313
312
314-- 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
313-- 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)
314+evalSchemaExpr :: SchemaExpr -> DatabaseContext -> TransactionId -> TransactionGraph -> Subschemas -> Either RelationalError (Subschemas, DatabaseContext)
316−evalSchemaExpr (AddSubschema sname morphs) context sschemas =
315+evalSchemaExpr (AddSubschema sname morphs) context transId graph sschemas =
317 if M.member sname sschemas then
316 if M.member sname sschemas then
318 Left (SubschemaNameInUseError sname)
317 Left (SubschemaNameInUseError sname)
319− else case valid of
318+ else
319+ case validateSchema (Schema morphs) context of
320− Just err -> Left (SchemaCreationError err)
320+ Just err -> Left (SchemaCreationError err)
321− Nothing ->
321+ Nothing -> do
322− let newSchemas = M.insert sname newSchema sschemas
322+ let newSchemas = M.insert sname newSchema sschemas
323+ newSchema = Schema morphs
323− moreIncDeps = foldr (\morph acc -> M.union acc (createIncDepsForIsomorph sname morph)) M.empty morphs
324+ moreIncDeps = foldr (\morph acc -> M.union acc (createIncDepsForIsomorph sname morph)) M.empty morphs
324− incDepExprs = MultipleExpr (map (uncurry AddInclusionDependency) (M.toList moreIncDeps))
325+ incDepExprs = MultipleExpr (map (uncurry AddInclusionDependency) (M.toList moreIncDeps))
325− in
326+ dbenv = mkDatabaseContextEvalEnv transId graph
326− case runState (evalDatabaseContextExpr incDepExprs) (context, M.empty, False) of
327+ dbstate <- runDatabaseContextEvalMonad context dbenv (evalGraphRefDatabaseContextExpr incDepExprs)
327− (Left err, _) -> Left err
328+ pure (newSchemas, dbc_context dbstate)
328− (Right (), (newContext,_,_)) -> pure (newSchemas, newContext) --need to propagate dirty flag here
329+--need to propagate dirty flag here
329− where
330+
330− newSchema = Schema morphs
331− valid = validateSchema newSchema context
332−evalSchemaExpr (RemoveSubschema sname) context sschemas = if M.member sname sschemas then
331+evalSchemaExpr (RemoveSubschema sname) context _ _ sschemas = if M.member sname sschemas then
333 pure (M.delete sname sschemas, context)
332 pure (M.delete sname sschemas, context)
334 else
333 else
335 Left (SubschemaNameNotInUseError sname)
334 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 @@
1module ProjectM36.Notifications where
1module ProjectM36.Notifications where
2import ProjectM36.Base
2import ProjectM36.Base
3+import ProjectM36.Error
3import ProjectM36.RelationalExpression
4import ProjectM36.RelationalExpression
4−import Control.Monad.Trans.Reader
5+import ProjectM36.StaticOptimizer
5import qualified Data.Map as M
6import qualified Data.Map as M
6import Data.Either (isRight)
7import Data.Either (isRight)
7
8
8-- | Returns the notifications which should be triggered based on the transition from the first 'DatabaseContext' to the second 'DatabaseContext'.
9-- | 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 :: Notifications -> TransactionGraph -> DatabaseContext -> DatabaseContext -> Notifications
10−notificationChanges nots context1 context2 = M.filter notificationFilter nots
11+notificationChanges nots graph context1 context2 = M.filter notificationFilter nots
11 where
12 where
12− notificationFilter (Notification chExpr _ _) = let oldChangeEval = evalChangeExpr chExpr (RelationalExprStateElems context1)
13− newChangeEval = evalChangeExpr chExpr (RelationalExprStateElems context2) in
14− oldChangeEval /= newChangeEval && isRight oldChangeEval
13+ notificationFilter (Notification chExpr _ _) = oldChangeEval /= newChangeEval && isRight oldChangeEval
15− evalChangeExpr chExpr = runReader (evalRelationalExpr chExpr)
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
16
21
17
22
src/lib/ProjectM36/Relation.hs+2−18
@@ -4,8 +4,6 @@ import qualified Data.Set as S
4import qualified Data.HashSet as HS
4import qualified Data.HashSet as HS
5import Control.Monad
5import Control.Monad
6import qualified Data.Vector as V
6import qualified Data.Vector as V
7−import qualified Data.Map as M
8−import ProjectM36.AtomType
9import ProjectM36.Base
7import ProjectM36.Base
10import ProjectM36.Tuple
8import ProjectM36.Tuple
11import qualified ProjectM36.Attribute as A
9import qualified ProjectM36.Attribute as A
@@ -97,7 +95,7 @@ union (Relation attrs1 tupSet1) (Relation attrs2 tupSet2) =
97 where
95 where
98 newtuples = RelationTupleSet $ HS.toList . HS.fromList $ asList tupSet1 ++ map (reorderTuple attrs1) (asList tupSet2)
96 newtuples = RelationTupleSet $ HS.toList . HS.fromList $ asList tupSet1 ++ map (reorderTuple attrs1) (asList tupSet2)
99
97
100−project :: S.Set AttributeName -> Relation -> Either RelationalError Relation
98+project :: S.Set AttributeName -> Relation -> Either RelationalError Relation
101project attrNames rel@(Relation _ tupSet) = do
99project attrNames rel@(Relation _ tupSet) = do
102 newAttrs <- A.projectionAttributesForNames attrNames (attributes rel)
100 newAttrs <- A.projectionAttributesForNames attrNames (attributes rel)
103 let newAttrNameSet = A.attributeNameSet newAttrs
101 let newAttrNameSet = A.attributeNameSet newAttrs
@@ -304,20 +302,6 @@ typesAsRelation types = mkRelationFromTuples attrs tuples
304 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
302 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
305 Left err -> error ("mkRelationFromTuples pooped " ++ show err)
303 Left err -> error ("mkRelationFromTuples pooped " ++ show err)
306 Right rel -> RelationAtom rel
304 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))]
321
305
322-- | 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.
306-- | 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.
323randomizeTupleOrder :: MonadRandom m => Relation -> m Relation
307randomizeTupleOrder :: MonadRandom m => Relation -> m Relation
@@ -330,4 +314,4 @@ oneTuple (Relation _ (RelationTupleSet [])) = Nothing
330oneTuple (Relation _ (RelationTupleSet (x:_))) = Just x
314oneTuple (Relation _ (RelationTupleSet (x:_))) = Just x
331
315
332tuplesList :: Relation -> [RelationTuple]
316tuplesList :: 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 @@
1{-# LANGUAGE ScopedTypeVariables #-}
1{-# LANGUAGE ScopedTypeVariables #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE FlexibleContexts #-}
2module ProjectM36.RelationalExpression where
4module ProjectM36.RelationalExpression where
3import ProjectM36.Relation
5import ProjectM36.Relation
4import ProjectM36.Tuple
6import ProjectM36.Tuple
5import ProjectM36.TupleSet
7import ProjectM36.TupleSet
6import ProjectM36.Base
8import ProjectM36.Base
9+import qualified Data.UUID as U
7import ProjectM36.Error
10import ProjectM36.Error
8import ProjectM36.AtomType
11import ProjectM36.AtomType
9−import ProjectM36.Attribute (emptyAttributes)
12+import ProjectM36.Attribute (emptyAttributes, attributesFromList)
10import ProjectM36.ScriptSession
13import ProjectM36.ScriptSession
11import ProjectM36.DataTypes.Primitive
14import ProjectM36.DataTypes.Primitive
12import ProjectM36.AtomFunction
15import ProjectM36.AtomFunction
13import ProjectM36.DatabaseContextFunction
16import ProjectM36.DatabaseContextFunction
14import ProjectM36.Arbitrary
17import ProjectM36.Arbitrary
18+import ProjectM36.GraphRefRelationalExpr
19+import ProjectM36.Transaction
15import qualified ProjectM36.Attribute as A
20import qualified ProjectM36.Attribute as A
16import qualified Data.Map as M
21import qualified Data.Map as M
17import qualified Data.HashSet as HS
22import qualified Data.HashSet as HS
18import qualified Data.Set as S
23import qualified Data.Set as S
19import Control.Monad.State hiding (join)
24import Control.Monad.State hiding (join)
25+import Data.Bifunctor (second)
20import Data.Maybe
26import Data.Maybe
21import Data.Either
27import Data.Either
22import Data.Char (isUpper)
28import Data.Char (isUpper)
29+import Data.Time
30+import qualified Data.List.NonEmpty as NE
31+import Data.Functor.Identity
23import qualified Data.Text as T
32import qualified Data.Text as T
24import qualified Data.Vector as V
33import qualified Data.Vector as V
25import qualified ProjectM36.TypeConstructorDef as TCD
34import qualified ProjectM36.TypeConstructorDef as TCD
26−import Control.Monad.Trans.Except
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)
27−import Control.Monad.Trans.Reader
38+import Control.Monad.Trans.Except (except)
39+import Control.Monad.Reader as R hiding (join)
40+import ProjectM36.NormalizeExpr
41+import ProjectM36.WithNameExpr
28import Test.QuickCheck
42import Test.QuickCheck
29−
30#ifdef PM36_HASKELL_SCRIPTING
43#ifdef PM36_HASKELL_SCRIPTING
31−import GHC
44+import GHC hiding (getContext)
32import Control.Exception
45import Control.Exception
33import GHC.Paths
46import GHC.Paths
34#endif
47#endif
35
48
36−
37data DatabaseContextExprDetails = CountUpdatedTuples
49data DatabaseContextExprDetails = CountUpdatedTuples
38
50
39databaseContextExprDetailsFunc :: DatabaseContextExprDetails -> ResultAccumFunc
51databaseContextExprDetailsFunc :: DatabaseContextExprDetails -> ResultAccumFunc
@@ -49,32 +61,63 @@ databaseContextExprDetailsFunc CountUpdatedTuples _ relIn = Relation attrs newTu
49 Right ts -> ts
61 Right ts -> ts
50
62
51-- | Used to start a fresh database state for a new database context expression.
63-- | Used to start a fresh database state for a new database context expression.
52−freshDatabaseState :: DatabaseContext -> DatabaseStateElems
64+mkDatabaseContextEvalState :: DatabaseContext -> DatabaseContextEvalState
65+mkDatabaseContextEvalState context = DatabaseContextEvalState {
66+ dbc_context = context,
67+ dbc_accum = M.empty,
68+ dbc_dirty = False
53−freshDatabaseState ctx = (ctx, M.empty, False) --future work: propagate return accumulator
69+ } --future work: propagate return accumulator
54
70
55-- we need to pass around a higher level RelationTuple and Attributes in order to solve #52
71-- 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
72+data RelationalExprEnv = RelationalExprEnv {
57− RelationalExprStateAttrsElems DatabaseContext Attributes | --used when evaluating the type of a relexpr
73+ re_context :: DatabaseContext,
58− RelationalExprStateElems DatabaseContext --used by default at the top level of evaluation
74+ re_graph :: TransactionGraph,
59−
75+ re_extra :: Maybe (Either RelationTuple Attributes)
60−instance Show RelationalExprStateElems where
76+ }
61− show (RelationalExprStateTupleElems _ tup) = "RelationalExprStateTupleElems " ++ show tup
77+
62− show (RelationalExprStateAttrsElems _ attrs) = "RelationalExprStateAttrsElems" ++ show attrs
78+envTuple :: GraphRefRelationalExprEnv -> RelationTuple
63− show (RelationalExprStateElems _) = "RelationalExprStateElems"
79+envTuple e = fromLeft emptyTuple (fromMaybe (Left emptyTuple) (gre_extra e))
64−
80+
65−mkRelationalExprState :: DatabaseContext -> RelationalExprStateElems
81+envAttributes :: GraphRefRelationalExprEnv -> Attributes
66−mkRelationalExprState = RelationalExprStateElems
82+envAttributes e = fromRight emptyAttributes (fromMaybe (Right emptyAttributes) (gre_extra e))
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)
73
83
74−mergeAttributesIntoRelationalExprState :: Attributes -> RelationalExprStateElems -> RelationalExprStateElems
84+instance Show RelationalExprEnv where
75−mergeAttributesIntoRelationalExprState attrs (RelationalExprStateElems ctx) = RelationalExprStateAttrsElems ctx attrs
85+ show e@RelationalExprEnv{} = "RelationalExprEnv " ++ show (re_extra e)
76−mergeAttributesIntoRelationalExprState _ st@(RelationalExprStateTupleElems _ _) = st
86+
77−mergeAttributesIntoRelationalExprState attrsIn (RelationalExprStateAttrsElems ctx attrs) = RelationalExprStateAttrsElems ctx (A.union attrsIn attrs)
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)))
78
121
79type ResultAccumName = StringType
122type ResultAccumName = StringType
80
123
@@ -84,36 +127,103 @@ data ResultAccum = ResultAccum { resultAccumFunc :: ResultAccumFunc,
84 resultAccumResult :: Relation
127 resultAccumResult :: Relation
85 }
128 }
86
129
130+data DatabaseContextEvalState = DatabaseContextEvalState {
131+ dbc_context :: DatabaseContext, --new, alterable context for a new transaction
87−type DatabaseStateElems = (DatabaseContext, M.Map ResultAccumName ResultAccum, DirtyFlag)
132+ dbc_accum :: M.Map ResultAccumName ResultAccum,
133+ dbc_dirty :: DirtyFlag
134+ }
88
135
89−type DatabaseState a = State DatabaseStateElems a
136+data DatabaseContextEvalEnv = DatabaseContextEvalEnv
137+ { dce_transId :: TransactionId,
138+ dce_graph :: TransactionGraph
139+ }
90
140
91−getStateContext :: DatabaseState DatabaseContext
141+mkDatabaseContextEvalEnv :: TransactionId -> TransactionGraph -> DatabaseContextEvalEnv
92−getStateContext = do
142+mkDatabaseContextEvalEnv = DatabaseContextEvalEnv
93− (ctx,_, _) <- get
143+
94− pure ctx
144+type DatabaseContextEvalMonad a = RWST DatabaseContextEvalEnv () DatabaseContextEvalState (ExceptT RelationalError Identity) a
95−
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+
96−putStateContext :: DatabaseContext -> DatabaseState ()
165+putStateContext :: DatabaseContext -> DatabaseContextEvalMonad ()
97−putStateContext ctx = do
166+putStateContext ctx' = do
98− (_, accum, _) <- get
167+ s <- get
99− put (ctx, accum, True)
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+ }
100
177
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
102
205
103−stateElemsContext :: RelationalExprStateElems -> DatabaseContext
206+envContext :: RelationalExprEnv -> DatabaseContext
104−stateElemsContext (RelationalExprStateTupleElems ctx _) = ctx
207+envContext = re_context
105−stateElemsContext (RelationalExprStateElems ctx) = ctx
106−stateElemsContext (RelationalExprStateAttrsElems ctx _) = ctx
107
208
108−setStateElemsContext :: RelationalExprStateElems -> DatabaseContext -> RelationalExprStateElems
209+setEnvContext :: RelationalExprEnv -> DatabaseContext -> RelationalExprEnv
109−setStateElemsContext (RelationalExprStateTupleElems _ tup) ctx = RelationalExprStateTupleElems ctx tup
210+setEnvContext e ctx = e { re_context = ctx }
110−setStateElemsContext (RelationalExprStateElems _) ctx = RelationalExprStateElems ctx
111−setStateElemsContext (RelationalExprStateAttrsElems _ attrs) ctx = RelationalExprStateAttrsElems ctx attrs
112
211
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+{-
113--relvar state is needed in evaluation of relational expression but only as read-only in order to extract current relvar values
223--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)
115evalRelationalExpr (RelationVariable name _) = do
225evalRelationalExpr (RelationVariable name _) = do
116− relvarTable <- fmap (relationVariables . stateElemsContext) ask
226+ relvarTable <- fmap (relationVariables . envContext) askEnv
117 return $ case M.lookup name relvarTable of
227 return $ case M.lookup name relvarTable of
118 Just res -> Right res
228 Just res -> Right res
119 Nothing -> Left $ RelVarNotDefinedError name
229 Nothing -> Left $ RelVarNotDefinedError name
@@ -125,7 +235,7 @@ evalRelationalExpr (Project attrNames expr) = do
125 Right attrNameSet -> do
235 Right attrNameSet -> do
126 rel <- evalRelationalExpr expr
236 rel <- evalRelationalExpr expr
127 case rel of
237 case rel of
128− Right rel2 -> pure $ project attrNameSet rel2
238+ Right rel2 -> pure $ Right (Project (AttributeNames attrNameSet) rel2)
129 Left err -> pure $ Left err
239 Left err -> pure $ Left err
130
240
131evalRelationalExpr (Union exprA exprB) = do
241evalRelationalExpr (Union exprA exprB) = do
@@ -135,7 +245,7 @@ evalRelationalExpr (Union exprA exprB) = do
135 Left err -> return $ Left err
245 Left err -> return $ Left err
136 Right relA2 -> case relB of
246 Right relA2 -> case relB of
137 Left err -> return $ Left err
247 Left err -> return $ Left err
138− Right relB2 -> return $ union relA2 relB2
248+ Right relB2 -> return $ Right (Union relA2 relB2)
139
249
140evalRelationalExpr (Join exprA exprB) = do
250evalRelationalExpr (Join exprA exprB) = do
141 relA <- evalRelationalExpr exprA
251 relA <- evalRelationalExpr exprA
@@ -144,7 +254,7 @@ evalRelationalExpr (Join exprA exprB) = do
144 Left err -> return $ Left err
254 Left err -> return $ Left err
145 Right relA2 -> case relB of
255 Right relA2 -> case relB of
146 Left err -> return $ Left err
256 Left err -> return $ Left err
147− Right relB2 -> return $ join relA2 relB2
257+ Right relB2 -> return $ Right (Join relA2 relB2)
148
258
149evalRelationalExpr (Difference exprA exprB) = do
259evalRelationalExpr (Difference exprA exprB) = do
150 relA <- evalRelationalExpr exprA
260 relA <- evalRelationalExpr exprA
@@ -153,33 +263,34 @@ evalRelationalExpr (Difference exprA exprB) = do
153 Left err -> return $ Left err
263 Left err -> return $ Left err
154 Right relA2 -> case relB of
264 Right relA2 -> case relB of
155 Left err -> return $ Left err
265 Left err -> return $ Left err
156− Right relB2 -> return $ difference relA2 relB2
266+ Right relB2 -> return $ Right (Difference relA2 relB2)
157
267
158evalRelationalExpr (MakeStaticRelation attributeSet tupleSet) =
268evalRelationalExpr (MakeStaticRelation attributeSet tupleSet) =
159 case mkRelation attributeSet tupleSet of
269 case mkRelation attributeSet tupleSet of
160− Right rel -> return $ Right rel
270+ Right rel -> return $ Right (ExistingRelation rel)
161 Left err -> return $ Left err
271 Left err -> return $ Left err
162
272
163evalRelationalExpr (MakeRelationFromExprs mAttrExprs tupleExprs) = do
273evalRelationalExpr (MakeRelationFromExprs mAttrExprs tupleExprs) = do
164− currentContext <- fmap stateElemsContext ask
274+ currentContext <- fmap envContext askEnv
165 let tConss = typeConstructorMapping currentContext
275 let tConss = typeConstructorMapping currentContext
166 -- 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
276 -- 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
167 runExceptT $ do
277 runExceptT $ do
168 mAttrs <- case mAttrExprs of
278 mAttrs <- case mAttrExprs of
169 Just _ ->
279 Just _ ->
170− Just . A.attributesFromList <$> mapM (either throwE pure . evalAttrExpr tConss) (fromMaybe [] mAttrExprs)
280+ Just . A.attributesFromList <$> mapM evalGraphRefAttrExpr (fromMaybe [] mAttrExprs)
171 Nothing -> pure Nothing
281 Nothing -> pure Nothing
172 tuples <- mapM (liftE . evalTupleExpr mAttrs) tupleExprs
282 tuples <- mapM (liftE . evalTupleExpr mAttrs) tupleExprs
173 let attrs = fromMaybe firstTupleAttrs mAttrs
283 let attrs = fromMaybe firstTupleAttrs mAttrs
174 firstTupleAttrs = if null tuples then A.emptyAttributes else tupleAttributes (head tuples)
284 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)
176
287
177−evalRelationalExpr (ExistingRelation rel) = pure (Right rel)
288+evalRelationalExpr (ExistingRelation rel) = pure (Right (ExistingRelation rel))
178
289
179evalRelationalExpr (Rename oldAttrName newAttrName relExpr) = do
290evalRelationalExpr (Rename oldAttrName newAttrName relExpr) = do
180 evald <- evalRelationalExpr relExpr
291 evald <- evalRelationalExpr relExpr
181 case evald of
292 case evald of
182− Right rel -> return $ rename oldAttrName newAttrName rel
293+ Right expr -> return $ Right (Rename oldAttrName newAttrName expr)
183 Left err -> return $ Left err
294 Left err -> return $ Left err
184
295
185evalRelationalExpr (Group oldAttrNames newAttrName relExpr) = do
296evalRelationalExpr (Group oldAttrNames newAttrName relExpr) = do
@@ -189,35 +300,32 @@ evalRelationalExpr (Group oldAttrNames newAttrName relExpr) = do
189 Right oldAttrNameSet -> do
300 Right oldAttrNameSet -> do
190 evald <- evalRelationalExpr relExpr
301 evald <- evalRelationalExpr relExpr
191 case evald of
302 case evald of
192− Right rel -> return $ group oldAttrNameSet newAttrName rel
303+ Right expr -> return $ Right (Group (AttributeNames oldAttrNameSet) newAttrName expr)
193 Left err -> return $ Left err
304 Left err -> return $ Left err
194
305
195evalRelationalExpr (Ungroup attrName relExpr) = do
306evalRelationalExpr (Ungroup attrName relExpr) = do
196 evald <- evalRelationalExpr relExpr
307 evald <- evalRelationalExpr relExpr
197 case evald of
308 case evald of
198− Right rel -> return $ ungroup attrName rel
309+ Right expr -> return $ Right (Ungroup attrName expr)
199 Left err -> return $ Left err
310 Left err -> return $ Left err
200
311
201evalRelationalExpr (Restrict predicateExpr relExpr) = do
312evalRelationalExpr (Restrict predicateExpr relExpr) = do
202 evald <- evalRelationalExpr relExpr
313 evald <- evalRelationalExpr relExpr
314+ pred <- processRestrictionPredicateExpr predicateExpr
203 case evald of
315 case evald of
204 Left err -> return $ Left err
316 Left err -> return $ Left err
205− Right rel -> do
317+ Right expr ->
206− eFilterFunc <- predicateRestrictionFilter (attributes rel) predicateExpr
318+ pure $ Right (Restrict pred expr)
207− case eFilterFunc of
208− Left err -> return $ Left err
209− Right filterfunc ->
210− pure (restrict filterfunc rel)
211
319
212evalRelationalExpr (Equals relExprA relExprB) = do
320evalRelationalExpr (Equals relExprA relExprB) = do
213 evaldA <- evalRelationalExpr relExprA
321 evaldA <- evalRelationalExpr relExprA
214 evaldB <- evalRelationalExpr relExprB
322 evaldB <- evalRelationalExpr relExprB
215 case evaldA of
323 case evaldA of
216 Left err -> return $ Left err
324 Left err -> return $ Left err
217− Right relA -> case evaldB of
325+ Right exprA -> case evaldB of
218 Left err -> return $ Left err
326 Left err -> return $ Left err
219− Right relB -> return $ Right $ if relA == relB then relationTrue else relationFalse
327+ Right exprB -> return $ Right (Equals exprA exprB)
220−
328+{-
221evalRelationalExpr (With views mainExpr) = do
329evalRelationalExpr (With views mainExpr) = do
222 rstate <- ask
330 rstate <- ask
223
331
@@ -231,310 +339,325 @@ evalRelationalExpr (With views mainExpr) = do
231 case foldM addScopedView (stateElemsContext rstate) views of
339 case foldM addScopedView (stateElemsContext rstate) views of
232 Left err -> return $ Left err
340 Left err -> return $ Left err
233 Right ctx'' -> do
341 Right ctx'' -> do
234− let evalMainExpr expr = runReader (evalRelationalExpr expr) (RelationalExprStateElems ctx'')
342+ let evalMainExpr expr = runReader (evalRelationalExpr expr) (RelationalExprEnv ctx'')
235 case evalMainExpr mainExpr of
343 case evalMainExpr mainExpr of
236 Left err -> return $ Left err
344 Left err -> return $ Left err
237 Right rel -> return $ Right rel
345 Right rel -> return $ Right rel
238−
346+-}
239--warning: copy-pasta from above- refactor
347--warning: copy-pasta from above- refactor
240evalRelationalExpr (NotEquals relExprA relExprB) = do
348evalRelationalExpr (NotEquals relExprA relExprB) = do
241 evaldA <- evalRelationalExpr relExprA
349 evaldA <- evalRelationalExpr relExprA
242 evaldB <- evalRelationalExpr relExprB
350 evaldB <- evalRelationalExpr relExprB
243 case evaldA of
351 case evaldA of
244 Left err -> return $ Left err
352 Left err -> return $ Left err
245− Right relA -> case evaldB of
353+ Right exprA -> case evaldB of
246 Left err -> return $ Left err
354 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)
248
356
249-- extending a relation adds a single attribute with the results of the per-tuple expression evaluated
357-- extending a relation adds a single attribute with the results of the per-tuple expression evaluated
250evalRelationalExpr (Extend tupleExpression relExpr) = do
358evalRelationalExpr (Extend tupleExpression relExpr) = do
251− rstate <- ask
252− let evald = runReader (evalRelationalExpr relExpr) rstate
359+ eExpr <- evalRelationalExpr relExpr
253− case evald of
360+ case eExpr of
254 Left err -> pure (Left err)
361 Left err -> pure (Left err)
255− Right rel -> do
362+ Right expr -> do
256− tupProc <- extendTupleExpressionProcessor rel tupleExpression
363+ tupProc <- processExtendTupleExpr tupleExpression
257− case tupProc of
364+ pure (Right (Extend tupProc expr))
258− Left err -> pure (Left err)
365+-}
259− Right (newAttrs, tupProc') -> pure $ relMogrify tupProc' newAttrs rel
260−
261--helper function to process relation variable creation/assignment
366--helper function to process relation variable creation/assignment
262−setRelVar :: RelVarName -> Relation -> DatabaseState (Either RelationalError ())
367+setRelVar :: RelVarName -> GraphRefRelationalExpr -> DatabaseContextEvalMonad ()
263−setRelVar relVarName rel = do
368+setRelVar relVarName relExpr = do
264 currentContext <- getStateContext
369 currentContext <- getStateContext
370+ --prevent recursive relvar definition
265− let newRelVars = M.insert relVarName rel $ relationVariables currentContext
371+ let newRelVars = M.insert relVarName relExpr $ relationVariables currentContext
266 potentialContext = currentContext { relationVariables = newRelVars }
372 potentialContext = currentContext { relationVariables = newRelVars }
267−
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
268− case checkConstraints potentialContext of
380+ case checkConstraints potentialContext tid graph of
269− Left err -> pure (Left err)
381+ Left err -> dbErr err
270− Right _ -> do
271− putStateContext potentialContext
382+ Right _ -> putStateContext potentialContext
272− return (Right ())
273
383
274-- 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
384-- 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 ()
276deleteRelVar relVarName = do
386deleteRelVar relVarName = do
277 currContext <- getStateContext
387 currContext <- getStateContext
278 let relVars = relationVariables currContext
388 let relVars = relationVariables currContext
279 if M.notMember relVarName relVars then
389 if M.notMember relVarName relVars then
280− pure (Right ())
390+ pure ()
281 else do
391 else do
282 let newRelVars = M.delete relVarName relVars
392 let newRelVars = M.delete relVarName relVars
283 newContext = currContext { relationVariables = newRelVars }
393 newContext = currContext { relationVariables = newRelVars }
284 putStateContext newContext
394 putStateContext newContext
285− pure (Right ())
395+ pure ()
286
396
287−evalDatabaseContextExpr :: DatabaseContextExpr -> DatabaseState (Either RelationalError ())
397+evalGraphRefDatabaseContextExpr :: GraphRefDatabaseContextExpr -> DatabaseContextEvalMonad ()
288−evalDatabaseContextExpr NoOperation = pure (Right ())
398+evalGraphRefDatabaseContextExpr NoOperation = pure ()
289
399
290−evalDatabaseContextExpr (Define relVarName attrExprs) = do
400+evalGraphRefDatabaseContextExpr (Define relVarName attrExprs) = do
401+ context <- getStateContext
291 relvars <- fmap relationVariables getStateContext
402 relvars <- fmap relationVariables getStateContext
292 tConss <- fmap typeConstructorMapping getStateContext
403 tConss <- fmap typeConstructorMapping getStateContext
293− let eAttrs = map (evalAttrExpr tConss) attrExprs
404+ graph <- dbcGraph
294− attrErrs = lefts eAttrs
405+ let eAttrs = runGraphRefRelationalExprM gfEnv (mapM evalGraphRefAttrExpr attrExprs)
295− attrsList = rights eAttrs
406+ gfEnv = freshGraphRefRelationalExprEnv (Just context) graph
296− if not (null attrErrs) then
407+ case eAttrs of
297− pure (Left (someErrors attrErrs))
408+ Left err -> dbErr err
298− else do
409+ Right attrsList -> do
299− let atomTypeErrs = lefts $ map ((`validateAtomType` tConss) . A.atomType) attrsList
410+ lift $ except $ validateAttributes tConss (A.attributesFromList attrsList)
300− if not (null atomTypeErrs) then
301− pure (Left (someErrors atomTypeErrs))
302− else
303− case M.member relVarName relvars of
411+ case M.member relVarName relvars of
304− True -> pure (Left (RelVarAlreadyDefinedError relVarName))
412+ True -> dbErr (RelVarAlreadyDefinedError relVarName)
305− False -> setRelVar relVarName emptyRelation >> pure (Right ())
413+ False -> setRelVar relVarName (ExistingRelation emptyRelation)
306 where
414 where
307 attrs = A.attributesFromList attrsList
415 attrs = A.attributesFromList attrsList
308 emptyRelation = Relation attrs emptyTupleSet
416 emptyRelation = Relation attrs emptyTupleSet
309
417
310−evalDatabaseContextExpr (Undefine relVarName) = deleteRelVar relVarName
418+evalGraphRefDatabaseContextExpr (Undefine relVarName) = deleteRelVar relVarName
311
419
312−evalDatabaseContextExpr (Assign relVarName expr) = do
420+evalGraphRefDatabaseContextExpr (Assign relVarName expr) = do
313− -- in the future, it would be nice to get types from the RelationalExpr instead of needing to evaluate it
421+ graph <- re_graph <$> dbcRelationalExprEnv
314 context <- getStateContext
422 context <- getStateContext
315− let existingRelVar = M.lookup relVarName relVarTable
423+ let existingRelVar = M.lookup relVarName (relationVariables context)
316− relVarTable = relationVariables context
424+ reEnv = freshGraphRefRelationalExprEnv (Just context) graph
317− value = runReader (evalRelationalExpr expr) (RelationalExprStateElems context)
425+ eNewExprType = runGraphRefRelationalExprM reEnv (typeForGraphRefRelationalExpr expr)
318− case value of
319− Left err -> pure (Left err)
320− Right rel -> case existingRelVar of
426+ case existingRelVar of
321− Nothing -> setRelVar relVarName rel
427+ Nothing -> do
322− Just existingRel -> let expectedAttributes = attributes existingRel
428+ case runGraphRefRelationalExprM reEnv (typeForGraphRefRelationalExpr expr) of
323− foundAttributes = attributes rel in
429+ Left err -> dbErr err
324− if A.attributesEqual expectedAttributes foundAttributes then
430+ Right reltype -> do
325− setRelVar relVarName rel
431+ lift $ except $ validateAttributes (typeConstructorMapping context) (attributes reltype)
326− else
432+ setRelVar relVarName expr
327− pure (Left (RelationTypeMismatchError expectedAttributes foundAttributes))
433+ Just existingRel -> do
328−
434+ let eExpectedType = runGraphRefRelationalExprM reEnv (typeForGraphRefRelationalExpr existingRel)
329−evalDatabaseContextExpr (Insert relVarName relExpr) = do
435+ case eExpectedType of
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)
436+ Left err -> dbErr err
337− Right unioned' -> case origRel of
437+ Right expectedType ->
438+ case eNewExprType of
338− Left err -> pure (Left err)
439+ Left err -> dbErr err
339− Right origRel' -> if cardinality unioned' == cardinality origRel' then --no tuples actually inserted
440+ Right newExprType -> do
340− pure (Right ())
441+ if newExprType == expectedType then do
442+ lift $ except $ validateAttributes (typeConstructorMapping context) (attributes newExprType)
443+ setRelVar relVarName expr
341− else
444+ else
342− evalDatabaseContextExpr $ Assign relVarName (ExistingRelation unioned')
445+ dbErr (RelationTypeMismatchError (attributes expectedType) (attributes newExprType))
343
446
344−evalDatabaseContextExpr (Delete relVarName predicate) = do
447+evalGraphRefDatabaseContextExpr (Insert relVarName relExpr) = do
345− context <- getStateContext
448+ gfExpr <- relVarByName relVarName
346− let rv = RelationVariable relVarName ()
449+ evalGraphRefDatabaseContextExpr (Assign relVarName
347− let updatedRel = runReader (evalRelationalExpr (Restrict (NotPredicate predicate) rv)) (RelationalExprStateElems context)
450+ (Union
348− origRel = runReader (evalRelationalExpr rv) (RelationalExprStateElems context)
451+ gfExpr
349− case updatedRel of
452+ relExpr))
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'
357
453
454+evalGraphRefDatabaseContextExpr (Delete relVarName predicate) = do
455+ gfExpr <- relVarByName relVarName
456+ setRelVar relVarName (Restrict (NotPredicate predicate) gfExpr)
457+
358--union of restricted+updated portion and the unrestricted+unupdated portion
458--union of restricted+updated portion and the unrestricted+unupdated portion
359−evalDatabaseContextExpr (Update relVarName atomExprMap restrictionPredicateExpr) = do
459+evalGraphRefDatabaseContextExpr (Update relVarName atomExprMap pred') = do
360− context <- getStateContext
460+ rvExpr <- relVarByName relVarName
361− let relVarTable = relationVariables context
461+ let unrestrictedPortion = Restrict (NotPredicate pred') rvExpr
362− case M.lookup relVarName relVarTable of
462+ tmpAttr attr = "_tmp_" <> attr --this could certainly be improved to verify that there is no attribute name conflict
363− Nothing -> pure (Left (RelVarNotDefinedError relVarName))
463+ updateAttr nam atomExpr = Extend (AttributeExtendTupleExpr (tmpAttr nam) atomExpr)
364− Just rel ->
464+ projectAndRename attr expr = Rename (tmpAttr attr) attr (Project (InvertedAttributeNames (S.singleton attr)) expr)
365− case runReader (predicateRestrictionFilter (attributes rel) restrictionPredicateExpr) (RelationalExprStateElems context) of
465+ restrictedPortion = Restrict pred' rvExpr
366− Left err -> pure (Left err)
466+ updated = foldr (\(oldname, atomExpr) accum ->
367− Right predicateFunc -> do
467+ let procAtomExpr = runProcessExprM UncommittedContextMarker (processAtomExpr atomExpr) in
368− let ret = do
468+ updateAttr oldname procAtomExpr accum
369− restrictedPortion <- restrict predicateFunc rel
469+ ) restrictedPortion (M.toList atomExprMap)
370− if cardinality restrictedPortion == Finite 0 then
470+ -- the atomExprMap could reference other attributes, so we must perform multi-pass folds
371− pure Nothing
471+ updatedPortion = foldr projectAndRename updated (M.keys atomExprMap)
372− else do
472+ setRelVar relVarName (Union unrestrictedPortion updatedPortion)
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
381
473
382−evalDatabaseContextExpr (AddInclusionDependency newDepName newDep) = do
474+evalGraphRefDatabaseContextExpr (AddInclusionDependency newDepName newDep) = do
383 currContext <- getStateContext
475 currContext <- getStateContext
476+ transId <- dbcTransId
477+ graph <- dbcGraph
384 let currDeps = inclusionDependencies currContext
478 let currDeps = inclusionDependencies currContext
385 newDeps = M.insert newDepName newDep currDeps
479 newDeps = M.insert newDepName newDep currDeps
386 if M.member newDepName currDeps then
480 if M.member newDepName currDeps then
387− pure (Left (InclusionDependencyNameInUseError newDepName))
481+ dbErr (InclusionDependencyNameInUseError newDepName)
388 else do
482 else do
389 let potentialContext = currContext { inclusionDependencies = newDeps }
483 let potentialContext = currContext { inclusionDependencies = newDeps }
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
390− case checkConstraints potentialContext of
486+ case checkConstraints potentialContext transId graph of
391− Left err -> pure (Left err)
487+ Left err -> dbErr err
392− Right _ -> do
488+ Right _ ->
393 putStateContext potentialContext
489 putStateContext potentialContext
394− return (Right ())
395
490
396−evalDatabaseContextExpr (RemoveInclusionDependency depName) = do
491+evalGraphRefDatabaseContextExpr (RemoveInclusionDependency depName) = do
397 currContext <- getStateContext
492 currContext <- getStateContext
398 let currDeps = inclusionDependencies currContext
493 let currDeps = inclusionDependencies currContext
399 newDeps = M.delete depName currDeps
494 newDeps = M.delete depName currDeps
400 if M.notMember depName currDeps then
495 if M.notMember depName currDeps then
401− pure (Left (InclusionDependencyNameNotInUseError depName))
496+ dbErr (InclusionDependencyNameNotInUseError depName)
402− else do
497+ else
403 putStateContext $ currContext {inclusionDependencies = newDeps }
498 putStateContext $ currContext {inclusionDependencies = newDeps }
404− return (Right ())
405
499
406-- | Add a notification which will send the resultExpr when triggerExpr changes between commits.
500-- | 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
408 currentContext <- getStateContext
502 currentContext <- getStateContext
409 let nots = notifications currentContext
503 let nots = notifications currentContext
410 if M.member notName nots then
504 if M.member notName nots then
411− pure (Left (NotificationNameInUseError notName))
505+ dbErr (NotificationNameInUseError notName)
412 else do
506 else do
413 let newNotifications = M.insert notName newNotification nots
507 let newNotifications = M.insert notName newNotification nots
414 newNotification = Notification { changeExpr = triggerExpr,
508 newNotification = Notification { changeExpr = triggerExpr,
415 reportOldExpr = resultOldExpr,
509 reportOldExpr = resultOldExpr,
416 reportNewExpr = resultNewExpr}
510 reportNewExpr = resultNewExpr}
417 putStateContext $ currentContext { notifications = newNotifications }
511 putStateContext $ currentContext { notifications = newNotifications }
418− return (Right ())
419
512
420−evalDatabaseContextExpr (RemoveNotification notName) = do
513+evalGraphRefDatabaseContextExpr (RemoveNotification notName) = do
421 currentContext <- getStateContext
514 currentContext <- getStateContext
422 let nots = notifications currentContext
515 let nots = notifications currentContext
423 if M.notMember notName nots then
516 if M.notMember notName nots then
424− pure (Left (NotificationNameNotInUseError notName))
517+ dbErr (NotificationNameNotInUseError notName)
425 else do
518 else do
426 let newNotifications = M.delete notName nots
519 let newNotifications = M.delete notName nots
427 putStateContext $ currentContext { notifications = newNotifications }
520 putStateContext $ currentContext { notifications = newNotifications }
428− pure (Right ())
521+
429
522
430-- | Adds type and data constructors to the database context.
523-- | Adds type and data constructors to the database context.
431-- validate that the type *and* constructor names are unique! not yet implemented!
524-- validate that the type *and* constructor names are unique! not yet implemented!
432−evalDatabaseContextExpr (AddTypeConstructor tConsDef dConsDefList) = do
525+evalGraphRefDatabaseContextExpr (AddTypeConstructor tConsDef dConsDefList) = do
433 currentContext <- getStateContext
526 currentContext <- getStateContext
434 let oldTypes = typeConstructorMapping currentContext
527 let oldTypes = typeConstructorMapping currentContext
435 tConsName = TCD.name tConsDef
528 tConsName = TCD.name tConsDef
436 -- validate that the constructor's types exist
529 -- validate that the constructor's types exist
437 case validateTypeConstructorDef tConsDef dConsDefList oldTypes of
530 case validateTypeConstructorDef tConsDef dConsDefList oldTypes of
438− Left err -> pure (Left err)
531+ Left err -> throwError err
439− Right _ | T.null tConsName || not (isUpper (T.head tConsName)) -> pure (Left (InvalidAtomTypeName tConsName))
532+ Right () | T.null tConsName || not (isUpper (T.head tConsName)) -> dbErr (InvalidAtomTypeName tConsName)
440− | isJust (findTypeConstructor tConsName oldTypes) -> pure (Left (AtomTypeNameInUseError tConsName))
533+ | isJust (findTypeConstructor tConsName oldTypes) -> dbErr (AtomTypeNameInUseError tConsName)
441− | otherwise -> do
534+ | otherwise -> do
442− let newTypes = oldTypes ++ [(tConsDef, dConsDefList)]
535+ let newTypes = oldTypes ++ [(tConsDef, dConsDefList)]
443− putStateContext $ currentContext { typeConstructorMapping = newTypes }
536+ putStateContext $ currentContext { typeConstructorMapping = newTypes }
444− pure (Right ())
445
537
446-- | 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.
538-- | 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
448 currentContext <- getStateContext
540 currentContext <- getStateContext
449 let oldTypes = typeConstructorMapping currentContext
541 let oldTypes = typeConstructorMapping currentContext
450 if isNothing (findTypeConstructor tConsName oldTypes) then
542 if isNothing (findTypeConstructor tConsName oldTypes) then
451− pure (Left (AtomTypeNameNotInUseError tConsName))
543+ dbErr (AtomTypeNameNotInUseError tConsName)
452 else do
544 else do
453 let newTypes = filter (\(tCons, _) -> TCD.name tCons /= tConsName) oldTypes
545 let newTypes = filter (\(tCons, _) -> TCD.name tCons /= tConsName) oldTypes
454 putStateContext $ currentContext { typeConstructorMapping = newTypes }
546 putStateContext $ currentContext { typeConstructorMapping = newTypes }
455− pure (Right ())
456
547
457−evalDatabaseContextExpr (MultipleExpr exprs) = do
548+evalGraphRefDatabaseContextExpr (MultipleExpr exprs) =
458 --the multiple expressions must pass the same context around- not the old unmodified context
549 --the multiple expressions must pass the same context around- not the old unmodified context
459− evald <- forM exprs evalDatabaseContextExpr
550+ mapM_ evalGraphRefDatabaseContextExpr exprs
460− --some lifting magic needed here
551+
461− case lefts evald of
462− [] -> pure (Right ())
463− errs -> pure (Left (someErrors errs))
464−
465−evalDatabaseContextExpr (RemoveAtomFunction funcName) = do
552+evalGraphRefDatabaseContextExpr (RemoveAtomFunction funcName) = do
466 currentContext <- getStateContext
553 currentContext <- getStateContext
467 let atomFuncs = atomFunctions currentContext
554 let atomFuncs = atomFunctions currentContext
468 case atomFunctionForName funcName atomFuncs of
555 case atomFunctionForName funcName atomFuncs of
469− Left err -> pure (Left err)
556+ Left err -> dbErr err
557+ Right realFunc ->
470− Right realFunc -> if isScriptedAtomFunction realFunc then do
558+ if isScriptedAtomFunction realFunc then do
471− let updatedFuncs = HS.delete realFunc atomFuncs
559+ let updatedFuncs = HS.delete realFunc atomFuncs
472− putStateContext (currentContext {atomFunctions = updatedFuncs })
560+ putStateContext (currentContext {atomFunctions = updatedFuncs })
473− pure (Right ())
474− else
561+ else
475− pure (Left (PrecompiledFunctionRemoveError funcName))
562+ dbErr (PrecompiledFunctionRemoveError funcName)
476
563
477−evalDatabaseContextExpr (RemoveDatabaseContextFunction funcName) = do
564+evalGraphRefDatabaseContextExpr (RemoveDatabaseContextFunction funcName) = do
478 context <- getStateContext
565 context <- getStateContext
479 let dbcFuncs = dbcFunctions context
566 let dbcFuncs = dbcFunctions context
480 case databaseContextFunctionForName funcName dbcFuncs of
567 case databaseContextFunctionForName funcName dbcFuncs of
481− Left err -> pure (Left err)
568+ Left err -> dbErr err
569+ Right realFunc ->
482− Right realFunc -> if isScriptedDatabaseContextFunction realFunc then do
570+ if isScriptedDatabaseContextFunction realFunc then do
483− let updatedFuncs = HS.delete realFunc dbcFuncs
571+ let updatedFuncs = HS.delete realFunc dbcFuncs
484− putStateContext (context { dbcFunctions = updatedFuncs })
572+ putStateContext (context { dbcFunctions = updatedFuncs })
485− pure (Right ())
486− else
573+ else
487− pure (Left (PrecompiledFunctionRemoveError funcName))
574+ dbErr (PrecompiledFunctionRemoveError funcName)
488
575
489−evalDatabaseContextExpr (ExecuteDatabaseContextFunction funcName atomArgExprs) = do
576+evalGraphRefDatabaseContextExpr (ExecuteDatabaseContextFunction funcName atomArgExprs) = do
490 context <- getStateContext
577 context <- getStateContext
578+ graph <- dbcGraph
491 --resolve atom arguments
579 --resolve atom arguments
492− let relExprState = mkRelationalExprState context
580+ let eAtomTypes = mapM (runGraphRefRelationalExprM gfEnv . typeForGraphRefAtomExpr emptyAttributes) atomArgExprs
493− eAtomTypes = map (\atomExpr -> runReader (typeFromAtomExpr emptyAttributes atomExpr) relExprState) atomArgExprs
494 eFunc = databaseContextFunctionForName funcName (dbcFunctions context)
581 eFunc = databaseContextFunctionForName funcName (dbcFunctions context)
582+ gfEnv = freshGraphRefRelationalExprEnv (Just context) graph
495 case eFunc of
583 case eFunc of
496− Left err -> pure (Left err)
584+ Left err -> dbErr err
497 Right func -> do
585 Right func -> do
498 let expectedArgCount = length (dbcFuncType func)
586 let expectedArgCount = length (dbcFuncType func)
499 actualArgCount = length atomArgExprs
587 actualArgCount = length atomArgExprs
500 if expectedArgCount /= actualArgCount then
588 if expectedArgCount /= actualArgCount then
501− pure (Left (FunctionArgumentCountMismatchError expectedArgCount actualArgCount))
589+ dbErr (FunctionArgumentCountMismatchError expectedArgCount actualArgCount)
502 else
590 else
503 --check that the atom types are valid
591 --check that the atom types are valid
504− case lefts eAtomTypes of
592+ case eAtomTypes of
505− _:_ -> pure (Left (someErrors (lefts eAtomTypes)))
593+ Left err -> dbErr err
506− [] -> do
594+ Right atomTypes -> do
507− let atomTypes = rights eAtomTypes
508− let mValidTypes = zipWith
595+ let mValidTypes = zipWith (\ expType actType
509− (\expType actType
510− -> case atomTypeVerify expType actType of
596+ -> case atomTypeVerify expType actType of
511− Left err -> Just err
597+ Left err -> Just err
512− Right _ -> Nothing)
598+ Right _ -> Nothing)
513− (dbcFuncType func)
599+ (dbcFuncType func) atomTypes
514− atomTypes
515− typeErrors = catMaybes mValidTypes
600+ typeErrors = catMaybes mValidTypes
516− eAtomArgs = map (\arg -> runReader (evalAtomExpr emptyTuple arg) relExprState) atomArgExprs
601+ eAtomArgs = map (runGraphRefRelationalExprM gfEnv . evalGraphRefAtomExpr emptyTuple) atomArgExprs
517 if length (lefts eAtomArgs) > 1 then
602 if length (lefts eAtomArgs) > 1 then
518− pure (Left (someErrors (lefts eAtomArgs)))
603+ dbErr (someErrors (lefts eAtomArgs))
519 else if not (null typeErrors) then
604 else if not (null typeErrors) then
520− pure (Left (someErrors typeErrors))
605+ dbErr (someErrors typeErrors)
521 else
606 else
522 case evalDatabaseContextFunction func (rights eAtomArgs) context of
607 case evalDatabaseContextFunction func (rights eAtomArgs) context of
523− Left err -> pure (Left err)
608+ Left err -> dbErr err
524− Right newContext -> putStateContext newContext >> pure (Right ())
609+ Right newContext -> putStateContext newContext
525−
610+data DatabaseContextIOEvalEnv = DatabaseContextIOEvalEnv
526−evalDatabaseContextIOExpr :: Maybe ScriptSession -> DatabaseContext -> DatabaseContextIOExpr -> IO (Either RelationalError DatabaseContext)
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 ())
527−#if !defined(PM36_HASKELL_SCRIPTING)
648+#if !defined(PM36_HASKELL_SCRIPTING)
528−evalDatabaseContextIOExpr _ _ AddAtomFunction{} = pure (Left (ScriptError ScriptCompilationDisabledError))
649+evalGraphRefDatabaseContextIOExpr AddAtomFunction{} = pure (Left (ScriptError ScriptCompilationDisabledError))
529−evalDatabaseContextIOExpr _ _ AddDatabaseContextFunction{} = pure (Left (ScriptError ScriptCompilationDisabledError))
650+evalGraphRefDatabaseContextIOExpr AddDatabaseContextFunction{} = pure (Left (ScriptError ScriptCompilationDisabledError))
530−evalDatabaseContextIOExpr _ _ LoadAtomFunctions{} = pure (Left (ScriptError ScriptCompilationDisabledError))
651+evalGraphRefDatabaseContextIOExpr LoadAtomFunctions{} = pure (Left (ScriptError ScriptCompilationDisabledError))
531−evalDatabaseContextIOExpr _ _ LoadDatabaseContextFunctions{} = pure (Left (ScriptError ScriptCompilationDisabledError))
652+evalGraphRefDatabaseContextIOExpr LoadDatabaseContextFunctions{} = pure (Left (ScriptError ScriptCompilationDisabledError))
532#else
653#else
533−evalDatabaseContextIOExpr mScriptSession currentContext (AddAtomFunction funcName funcType script) =
654+evalGraphRefDatabaseContextIOExpr (AddAtomFunction funcName funcType script) = do
534− case mScriptSession of
655+ eScriptSession <- requireScriptSession
535− Nothing -> pure (Left (ScriptError ScriptCompilationDisabledError))
656+ currentContext <- getDBCIOContext
657+ case eScriptSession of
658+ Left err -> pure (Left err)
536− Just scriptSession -> do
659+ Right scriptSession -> do
537− res <- try $ runGhc (Just libdir) $ do
660+ res <- liftIO $ try $ runGhc (Just libdir) $ do
538 setSession (hscEnv scriptSession)
661 setSession (hscEnv scriptSession)
539 let atomFuncs = atomFunctions currentContext
662 let atomFuncs = atomFunctions currentContext
540 case extractAtomFunctionType funcType of
663 case extractAtomFunctionType funcType of
@@ -560,12 +683,13 @@ evalDatabaseContextIOExpr mScriptSession currentContext (AddAtomFunction funcNam
560 Left (exc :: SomeException) -> pure $ Left (ScriptError (OtherScriptCompilationError (show exc)))
683 Left (exc :: SomeException) -> pure $ Left (ScriptError (OtherScriptCompilationError (show exc)))
561 Right eContext -> case eContext of
684 Right eContext -> case eContext of
562 Left err -> pure (Left err)
685 Left err -> pure (Left err)
563− Right context' -> pure (Right context')
686+ Right context' -> putDBCIOContext context'
564−
565−evalDatabaseContextIOExpr mScriptSession currentContext (AddDatabaseContextFunction funcName funcType script) =
687+evalGraphRefDatabaseContextIOExpr (AddDatabaseContextFunction funcName funcType script) = do
566− case mScriptSession of
688+ eScriptSession <- requireScriptSession
567− Nothing -> pure (Left (ScriptError ScriptCompilationDisabledError))
689+ currentContext <- getDBCIOContext
690+ case eScriptSession of
691+ Left err -> pure (Left err)
568− Just scriptSession -> do
692+ Right scriptSession -> do
569 --validate that the function signature is of the form x -> y -> ... -> DatabaseContext -> DatabaseContext
693 --validate that the function signature is of the form x -> y -> ... -> DatabaseContext -> DatabaseContext
570 let last2Args = reverse (take 2 (reverse funcType))
694 let last2Args = reverse (take 2 (reverse funcType))
571 atomArgs = take (length funcType - 2) funcType
695 atomArgs = take (length funcType - 2) funcType
@@ -575,7 +699,7 @@ evalDatabaseContextIOExpr mScriptSession currentContext (AddDatabaseContextFunct
575 if last2Args /= [ADTypeConstructor "DatabaseContext" [], dbContextTypeCons] then
699 if last2Args /= [ADTypeConstructor "DatabaseContext" [], dbContextTypeCons] then
576 pure (Left (ScriptError (TypeCheckCompilationError expectedType actualType)))
700 pure (Left (ScriptError (TypeCheckCompilationError expectedType actualType)))
577 else do
701 else do
578− res <- try $ runGhc (Just libdir) $ do
702+ res <- liftIO $ try $ runGhc (Just libdir) $ do
579 setSession (hscEnv scriptSession)
703 setSession (hscEnv scriptSession)
580 eCompiledFunc <- compileScript (dbcFunctionBodyType scriptSession) script
704 eCompiledFunc <- compileScript (dbcFunctionBodyType scriptSession) script
581 pure $ case eCompiledFunc of
705 pure $ case eCompiledFunc of
@@ -600,68 +724,93 @@ evalDatabaseContextIOExpr mScriptSession currentContext (AddDatabaseContextFunct
600 Left (exc :: SomeException) -> pure $ Left (ScriptError (OtherScriptCompilationError (show exc)))
724 Left (exc :: SomeException) -> pure $ Left (ScriptError (OtherScriptCompilationError (show exc)))
601 Right eContext -> case eContext of
725 Right eContext -> case eContext of
602 Left err -> pure (Left err)
726 Left err -> pure (Left err)
603− Right context' -> pure (Right context')
727+ Right context' -> putDBCIOContext context'
604−evalDatabaseContextIOExpr _ currentContext (LoadAtomFunctions modName funcName modPath) = do
728+evalGraphRefDatabaseContextIOExpr (LoadAtomFunctions modName funcName modPath) = do
729+ currentContext <- getDBCIOContext
605− eLoadFunc <- loadAtomFunctions (T.unpack modName) (T.unpack funcName) modPath
730+ eLoadFunc <- liftIO $ loadAtomFunctions (T.unpack modName) (T.unpack funcName) modPath
606 case eLoadFunc of
731 case eLoadFunc of
607 Left LoadSymbolError -> pure (Left LoadFunctionError)
732 Left LoadSymbolError -> pure (Left LoadFunctionError)
608 Right atomFunctionListFunc -> let newContext = currentContext { atomFunctions = mergedFuncs }
733 Right atomFunctionListFunc -> let newContext = currentContext { atomFunctions = mergedFuncs }
609 mergedFuncs = HS.union (atomFunctions currentContext) (HS.fromList atomFunctionListFunc)
734 mergedFuncs = HS.union (atomFunctions currentContext) (HS.fromList atomFunctionListFunc)
610− in pure (Right newContext)
735+ in putDBCIOContext newContext
611−evalDatabaseContextIOExpr _ currentContext (LoadDatabaseContextFunctions modName funcName modPath) = do
736+evalGraphRefDatabaseContextIOExpr (LoadDatabaseContextFunctions modName funcName modPath) = do
737+ currentContext <- getDBCIOContext
612− eLoadFunc <- loadDatabaseContextFunctions (T.unpack modName) (T.unpack funcName) modPath
738+ eLoadFunc <- liftIO $ loadDatabaseContextFunctions (T.unpack modName) (T.unpack funcName) modPath
613 case eLoadFunc of
739 case eLoadFunc of
614 Left LoadSymbolError -> pure (Left LoadFunctionError)
740 Left LoadSymbolError -> pure (Left LoadFunctionError)
615 Right dbcListFunc -> let newContext = currentContext { dbcFunctions = mergedFuncs }
741 Right dbcListFunc -> let newContext = currentContext { dbcFunctions = mergedFuncs }
616 mergedFuncs = HS.union (dbcFunctions currentContext) (HS.fromList dbcListFunc)
742 mergedFuncs = HS.union (dbcFunctions currentContext) (HS.fromList dbcListFunc)
617− in pure (Right newContext)
743+ in putDBCIOContext newContext
618−#endif
619−
620−evalDatabaseContextIOExpr _ currentContext (CreateArbitraryRelation relVarName attrExprs range) =
744+evalGraphRefDatabaseContextIOExpr (CreateArbitraryRelation relVarName attrExprs range) = do
621 --Define
745 --Define
622− case runState ( evalDatabaseContextExpr (Define relVarName attrExprs)) (freshDatabaseState currentContext) of
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
623− (Left err,_) -> pure (Left err)
753+ Left err -> pure (Left err)
624− (Right (), elems@(ctx,_,_)) -> do
754+ Right dbstate -> do
625 --Assign
755 --Assign
626 let existingRelVar = M.lookup relVarName relVarTable
756 let existingRelVar = M.lookup relVarName relVarTable
627− relVarTable = relationVariables ctx
757+ relVarTable = relationVariables (dbc_context dbstate)
628 case existingRelVar of
758 case existingRelVar of
629 Nothing -> pure $ Left (RelVarNotDefinedError relVarName)
759 Nothing -> pure $ Left (RelVarNotDefinedError relVarName)
630 Just existingRel -> do
760 Just existingRel -> 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
631− let expectedAttributes = attributes existingRel
765+ let expectedAttributes = attributes relType
632− let tcMap = typeConstructorMapping ctx
766+ tcMap = typeConstructorMapping (dbc_context dbstate)
633− eitherRel <- generate $ runReaderT (arbitraryRelation expectedAttributes range) tcMap
767+ eitherRel <- liftIO $ generate $ runReaderT (arbitraryRelation expectedAttributes range) tcMap
634− case eitherRel of
768+ case eitherRel of
635− Left err -> pure $ Left err
769+ Left err -> pure $ Left err
636− Right rel ->
770+ Right rel ->
637− case runState (setRelVar relVarName rel) elems of
771+ case runDatabaseContextEvalMonad currentContext evalEnv (setRelVar relVarName (ExistingRelation rel)) of
638− (Left err,_) -> pure (Left err)
772+ Left err -> pure (Left err)
639− (Right (), (ctx',_,_)) -> pure $ Right ctx'
773+ Right dbstate' -> putDBCIOContext (dbc_context dbstate')
640−
774+#endif
775+{-
641−updateTupleWithAtomExprs :: M.Map AttributeName AtomExpr -> DatabaseContext -> RelationTuple -> Either RelationalError RelationTuple
776+updateTupleWithAtomExprs :: M.Map AttributeName AtomExpr -> DatabaseContext -> TransactionGraph -> RelationTuple -> Either RelationalError RelationTuple
642−updateTupleWithAtomExprs exprMap context tupIn = do
777+updateTupleWithAtomExprs exprMap context graph tupIn = do
643 --resolve all atom exprs
778 --resolve all atom exprs
779+ let
644 atomsAssoc <- mapM (\(attrName, atomExpr) -> do
780 atomsAssoc <- mapM (\(attrName, atomExpr) -> do
781+ let atom = unimplemented
645− atom <- runReader (evalAtomExpr tupIn atomExpr) (RelationalExprStateElems context)
782+ --atom <- runReader (evalAtomExpr tupIn atomExpr) (mkRelationalExprState context graph)
646 pure (attrName, atom)
783 pure (attrName, atom)
647 ) (M.toList exprMap)
784 ) (M.toList exprMap)
648 pure (updateTupleWithAtoms (M.fromList atomsAssoc) tupIn)
785 pure (updateTupleWithAtoms (M.fromList atomsAssoc) tupIn)
649−
786+-}
650--run verification on all constraints
787--run verification on all constraints
651−checkConstraints :: DatabaseContext -> Either RelationalError ()
788+checkConstraints :: DatabaseContext -> TransactionId -> TransactionGraph -> Either RelationalError ()
652−checkConstraints context =
789+checkConstraints context transId graph@(TransactionGraph graphHeads transSet) =
653 mapM_ (uncurry checkIncDep) (M.toList deps)
790 mapM_ (uncurry checkIncDep) (M.toList deps)
654 where
791 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+
655 deps = inclusionDependencies context
798 deps = inclusionDependencies context
656− eval expr = evalReader (evalRelationalExpr expr)
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
657− evalReader f = runReader f (RelationalExprStateElems context)
658 checkIncDep depName (InclusionDependency subsetExpr supersetExpr) = do
800 checkIncDep depName (InclusionDependency subsetExpr supersetExpr) = do
801+ let process = runProcessExprM UncommittedContextMarker
802+ gfSubsetExpr = process (processRelationalExpr subsetExpr)
803+ gfSupersetExpr = process (processRelationalExpr supersetExpr)
659 --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.
804 --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)
805+ let gfEnv = freshGraphRefRelationalExprEnv (Just context) graph
661− typeSuper <- evalReader (typeForRelationalExpr supersetExpr)
806+ runGfRel = runGraphRefRelationalExprM gfEnv
807+ typeSub <- runGfRel (typeForGraphRefRelationalExpr gfSubsetExpr)
808+ typeSuper <- runGfRel (typeForGraphRefRelationalExpr gfSupersetExpr)
662 when (typeSub /= typeSuper) (Left (RelationTypeMismatchError (attributes typeSub) (attributes typeSuper)))
809 when (typeSub /= typeSuper) (Left (RelationTypeMismatchError (attributes typeSub) (attributes typeSuper)))
663− let checkExpr = Equals supersetExpr (Union subsetExpr supersetExpr)
810+ let checkExpr = Equals gfSupersetExpr (Union gfSubsetExpr gfSupersetExpr)
664− case eval checkExpr of
811+ gfEvald = runGraphRefRelationalExprM gfEnv' (evalGraphRefRelationalExpr checkExpr)
812+ gfEnv' = freshGraphRefRelationalExprEnv (Just context) potentialGraph
813+ case gfEvald of
665 Left err -> Left err
814 Left err -> Left err
666 Right resultRel -> if resultRel == relationTrue then
815 Right resultRel -> if resultRel == relationTrue then
667 pure ()
816 pure ()
@@ -671,91 +820,90 @@ checkConstraints context =
671-- 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
820-- 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
672-- furthermore, the type of a relational expression is the resultant header of the evaluated empty-tupled relation
821-- furthermore, the type of a relational expression is the resultant header of the evaluated empty-tupled relation
673
822
674−typeForRelationalExpr :: RelationalExpr -> RelationalExprState (Either RelationalError Relation)
823+typeForRelationalExpr :: RelationalExpr -> RelationalExprM Relation
675typeForRelationalExpr expr = do
824typeForRelationalExpr expr = do
676− rstate <- ask
677− let context = stateElemsContext rstate
678 --replace the relationVariables context element with a cloned set of relation devoid of tuples
825 --replace the relationVariables context element with a cloned set of relation devoid of tuples
679− let context' = contextWithEmptyTupleSets context
680− rstate' = setStateElemsContext rstate context'
681 --evalRelationalExpr could still return an existing relation with tuples, so strip them
826 --evalRelationalExpr could still return an existing relation with tuples, so strip them
682− pure $ case runReader (evalRelationalExpr expr) rstate' of
827+ graph <- reGraph
683− Left err -> Left err
828+ context <- reContext
684− Right typeRel -> Right (relationWithEmptyTupleSet typeRel)
829+ let gfExpr = runProcessExprM UncommittedContextMarker (processRelationalExpr expr)
830+ gfEnv = freshGraphRefRelationalExprEnv (Just context) graph
831+ runGf = runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr gfExpr)
832+ lift $ except runGf
685
833
686--returns a database context with all tuples removed
834--returns a database context with all tuples removed
687--this is useful for type checking and optimization
835--this is useful for type checking and optimization
836+{-
688contextWithEmptyTupleSets :: DatabaseContext -> DatabaseContext
837contextWithEmptyTupleSets :: DatabaseContext -> DatabaseContext
689contextWithEmptyTupleSets contextIn = contextIn { relationVariables = relVars }
838contextWithEmptyTupleSets contextIn = contextIn { relationVariables = relVars }
690 where
839 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+-}
692
842
693liftE :: (Monad m) => m (Either a b) -> ExceptT a m b
843liftE :: (Monad m) => m (Either a b) -> ExceptT a m b
694liftE v = do
844liftE v = do
695 y <- lift v
845 y <- lift v
696 case y of
846 case y of
697− Left err -> throwE err
847+ Left err -> throwError err
698 Right val -> pure val
848 Right val -> pure val
699
849
700{- used for restrictions- take the restrictionpredicate and return the corresponding filter function -}
850{- used for restrictions- take the restrictionpredicate and return the corresponding filter function -}
701−predicateRestrictionFilter :: Attributes -> RestrictionPredicateExpr -> RelationalExprState (Either RelationalError RestrictionFilter)
851+predicateRestrictionFilter :: Attributes -> GraphRefRestrictionPredicateExpr -> GraphRefRelationalExprM RestrictionFilter
702−predicateRestrictionFilter attrs (AndPredicate expr1 expr2) =
852+predicateRestrictionFilter attrs (AndPredicate expr1 expr2) = do
703− runExceptT $ do
704− expr1v <- liftE (predicateRestrictionFilter attrs expr1)
853+ expr1v <- predicateRestrictionFilter attrs expr1
705− expr2v <- liftE (predicateRestrictionFilter attrs expr2)
854+ expr2v <- predicateRestrictionFilter attrs expr2
706− pure (\x -> do
855+ pure (\x -> do
707− ev1 <- expr1v x
856+ ev1 <- expr1v x
708− ev2 <- expr2v x
857+ ev2 <- expr2v x
709− pure (ev1 && ev2))
858+ pure (ev1 && ev2))
710
859
711−predicateRestrictionFilter attrs (OrPredicate expr1 expr2) =
860+predicateRestrictionFilter attrs (OrPredicate expr1 expr2) = do
712− runExceptT $ do
713− expr1v <- liftE (predicateRestrictionFilter attrs expr1)
861+ expr1v <- predicateRestrictionFilter attrs expr1
714− expr2v <- liftE (predicateRestrictionFilter attrs expr2)
862+ expr2v <- predicateRestrictionFilter attrs expr2
715 pure (\x -> do
863 pure (\x -> do
716 ev1 <- expr1v x
864 ev1 <- expr1v x
717 ev2 <- expr2v x
865 ev2 <- expr2v x
718 pure (ev1 || ev2))
866 pure (ev1 || ev2))
719
867
720−predicateRestrictionFilter _ TruePredicate = pure (Right (\_ -> pure True))
868+predicateRestrictionFilter _ TruePredicate = pure (\_ -> pure True)
721
869
722−predicateRestrictionFilter attrs (NotPredicate expr) =
870+predicateRestrictionFilter attrs (NotPredicate expr) = do
723− runExceptT $ do
724− exprv <- liftE (predicateRestrictionFilter attrs expr)
871+ exprv <- predicateRestrictionFilter attrs expr
725− pure (fmap not . exprv)
872+ pure (fmap not . exprv)
726
873
727--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
874--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
728predicateRestrictionFilter _ (RelationalExprPredicate relExpr) = do
875predicateRestrictionFilter _ (RelationalExprPredicate relExpr) = do
729− rstate <- ask
876+ renv <- askEnv
730− pure (Right (\tup -> case runReader (evalRelationalExpr relExpr) (mergeTuplesIntoRelationalExprState tup rstate) of
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
731 Left err -> Left err
882 Left err -> Left err
732 Right rel -> if arity rel /= 0 then
883 Right rel -> if arity rel /= 0 then
733 Left (PredicateExpressionError "Relational restriction filter must evaluate to 'true' or 'false'")
884 Left (PredicateExpressionError "Relational restriction filter must evaluate to 'true' or 'false'")
734 else
885 else
735− pure (rel == relationTrue)))
886+ pure (rel == relationTrue))
736
887
737predicateRestrictionFilter attrs (AttributeEqualityPredicate attrName atomExpr) = do
888predicateRestrictionFilter attrs (AttributeEqualityPredicate attrName atomExpr) = do
738− rstate <- ask
889+ env <- askEnv
739− let (attrs', ctxtup') = case rstate of
890+ let attrs' = A.union attrs (envAttributes env)
740− RelationalExprStateElems _ -> (attrs, emptyTuple)
891+ ctxtup' = envTuple env
741− RelationalExprStateAttrsElems _ _ -> (attrs, emptyTuple)
742− RelationalExprStateTupleElems _ ctxtup -> (A.union attrs (tupleAttributes ctxtup), ctxtup)
743− runExceptT $ do
744− atomExprType <- liftE (typeFromAtomExpr attrs' atomExpr)
892+ atomExprType <- typeForGraphRefAtomExpr attrs' atomExpr
745− attr <- either throwE pure $ case A.attributeForName attrName attrs of
893+ attr <- lift $ except $ case A.attributeForName attrName attrs of
746 Right attr -> Right attr
894 Right attr -> Right attr
747 Left (NoSuchAttributeNamesError _) -> case A.attributeForName attrName (tupleAttributes ctxtup') of
895 Left (NoSuchAttributeNamesError _) -> case A.attributeForName attrName (tupleAttributes ctxtup') of
748 Right ctxattr -> Right ctxattr
896 Right ctxattr -> Right ctxattr
749 Left err2@(NoSuchAttributeNamesError _) -> Left err2
897 Left err2@(NoSuchAttributeNamesError _) -> Left err2
750 Left err -> Left err
898 Left err -> Left err
751 Left err -> Left err
899 Left err -> Left err
752− if atomExprType /= A.atomType attr then
900+ if atomExprType /= A.atomType attr then
753− throwE (TupleAttributeTypeMismatchError (A.attributesFromList [attr]))
901+ throwError (TupleAttributeTypeMismatchError (A.attributesFromList [attr]))
754− else
902+ else
755 pure $ \tupleIn -> let evalAndCmp atomIn = case atomEvald of
903 pure $ \tupleIn -> let evalAndCmp atomIn = case atomEvald of
756 Right atomCmp -> atomCmp == atomIn
904 Right atomCmp -> atomCmp == atomIn
757 Left _ -> False
905 Left _ -> False
758− atomEvald = runReader (evalAtomExpr tupleIn atomExpr) rstate
906+ atomEvald = runGraphRefRelationalExprM env (evalGraphRefAtomExpr tupleIn atomExpr)
759 in
907 in
760 pure $ case atomForAttributeName attrName tupleIn of
908 pure $ case atomForAttributeName attrName tupleIn of
761 Left (NoSuchAttributeNamesError _) -> case atomForAttributeName attrName ctxtup' of
909 Left (NoSuchAttributeNamesError _) -> case atomForAttributeName attrName ctxtup' of
@@ -766,16 +914,15 @@ predicateRestrictionFilter attrs (AttributeEqualityPredicate attrName atomExpr)
766-- in the future, it would be useful to do typechecking on the attribute and atom expr filters in advance
914-- in the future, it would be useful to do typechecking on the attribute and atom expr filters in advance
767predicateRestrictionFilter attrs (AtomExprPredicate atomExpr) = do
915predicateRestrictionFilter attrs (AtomExprPredicate atomExpr) = do
768 --merge attrs into the state attributes
916 --merge attrs into the state attributes
769− rstate <- ask
917+ renv <- askEnv
770− runExceptT $ do
771− aType <- liftE (typeFromAtomExpr attrs atomExpr)
918+ aType <- typeForGraphRefAtomExpr attrs atomExpr
772− if aType /= BoolAtomType then
919+ if aType /= BoolAtomType then
773− throwE (AtomTypeMismatchError aType BoolAtomType)
920+ throwError (AtomTypeMismatchError aType BoolAtomType)
774− else
921+ else
775 pure (\tupleIn ->
922 pure (\tupleIn ->
776− case runReader (evalAtomExpr tupleIn atomExpr) rstate of
923+ case runGraphRefRelationalExprM renv (evalGraphRefAtomExpr tupleIn atomExpr) of
777− Left err -> Left err
924+ Left err -> Left err
778− Right boolAtomValue -> pure (boolAtomValue == BoolAtom True))
925+ Right boolAtomValue -> pure (boolAtomValue == BoolAtom True))
779
926
780tupleExprCheckNewAttrName :: AttributeName -> Relation -> Either RelationalError Relation
927tupleExprCheckNewAttrName :: AttributeName -> Relation -> Either RelationalError Relation
781tupleExprCheckNewAttrName attrName rel = if isRight (attributeForName attrName rel) then
928tupleExprCheckNewAttrName attrName rel = if isRight (attributeForName attrName rel) then
@@ -783,31 +930,83 @@ tupleExprCheckNewAttrName attrName rel = if isRight (attributeForName attrName r
783 else
930 else
784 Right rel
931 Right rel
785
932
786−extendTupleExpressionProcessor :: Relation -> ExtendTupleExpr -> RelationalExprState (Either RelationalError (Attributes, RelationTuple -> Either RelationalError RelationTuple))
933+extendGraphRefTupleExpressionProcessor :: Relation -> GraphRefExtendTupleExpr -> GraphRefRelationalExprM (Attributes, RelationTuple -> Either RelationalError RelationTuple)
787−extendTupleExpressionProcessor relIn (AttributeExtendTupleExpr newAttrName atomExpr) = do
934+extendGraphRefTupleExpressionProcessor relIn (AttributeExtendTupleExpr newAttrName atomExpr) =
788− rstate <- ask
935+-- renv <- askEnv
789 -- check that the attribute name is not in use
936 -- check that the attribute name is not in use
790 case tupleExprCheckNewAttrName newAttrName relIn of
937 case tupleExprCheckNewAttrName newAttrName relIn of
791− Left err -> pure (Left err)
938+ Left err -> throwError err
792− Right _ -> runExceptT $ do
939+ Right _ -> do
793− atomExprType <- liftE (typeFromAtomExpr (attributes relIn) atomExpr)
940+ atomExprType <- typeForGraphRefAtomExpr (attributes relIn) atomExpr
794− atomExprType' <- liftE (verifyAtomExprTypes relIn atomExpr atomExprType)
941+ atomExprType' <- verifyGraphRefAtomExprTypes relIn atomExpr atomExprType
795 let newAttrs = A.attributesFromList [Attribute newAttrName atomExprType']
942 let newAttrs = A.attributesFromList [Attribute newAttrName atomExprType']
796 newAndOldAttrs = A.addAttributes (attributes relIn) newAttrs
943 newAndOldAttrs = A.addAttributes (attributes relIn) newAttrs
797− pure (newAndOldAttrs, \tup -> let substate = mergeTuplesIntoRelationalExprState tup rstate in case runReader (evalAtomExpr tup atomExpr) substate of
944+ env <- ask
798− Left err -> Left err
945+ pure (newAndOldAttrs, \tup -> do
946+ let gfEnv = mergeTuplesIntoGraphRefRelationalExprEnv tup env
947+ atom <- runGraphRefRelationalExprM gfEnv (evalGraphRefAtomExpr tup atomExpr)
799− Right atom -> Right (tupleAtomExtend newAttrName atom tup)
948+ Right (tupleAtomExtend newAttrName atom tup)
800 )
949 )
801
950
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+{-
802−evalAtomExpr :: RelationTuple -> AtomExpr -> RelationalExprState (Either RelationalError Atom)
999+evalAtomExpr :: RelationTuple -> AtomExpr -> RelationalExprM Atom
1000+evalAtomExpr tupIn expr =
1001+ -}
1002+{-
1003+
803evalAtomExpr tupIn (AttributeAtomExpr attrName) = case atomForAttributeName attrName tupIn of
1004evalAtomExpr tupIn (AttributeAtomExpr attrName) = case atomForAttributeName attrName tupIn of
804 Right atom -> pure (Right atom)
1005 Right atom -> pure (Right atom)
805 err@(Left (NoSuchAttributeNamesError _)) -> do
1006 err@(Left (NoSuchAttributeNamesError _)) -> do
806 rstate <- ask
1007 rstate <- ask
807− case rstate of
1008+ let stateTup = stateTuple rstate
808− RelationalExprStateElems _ -> pure err
809− RelationalExprStateAttrsElems _ _ -> pure err
810− RelationalExprStateTupleElems _ ctxtup -> pure (atomForAttributeName attrName ctxtup)
1009+ pure (atomForAttributeName attrName stateTup)
811 Left err -> pure (Left err)
1010 Left err -> pure (Left err)
812evalAtomExpr _ (NakedAtomExpr atom) = pure (Right atom)
1011evalAtomExpr _ (NakedAtomExpr atom) = pure (Right atom)
813evalAtomExpr tupIn (FunctionAtomExpr funcName arguments ()) = do
1012evalAtomExpr tupIn (FunctionAtomExpr funcName arguments ()) = do
@@ -847,131 +1046,170 @@ evalAtomExpr tupIn cons@(ConstructedAtomExpr dConsName dConsArgs ()) = runExcept
847 aType <- either throwE pure (runReader (typeFromAtomExpr (tupleAttributes tupIn) cons) newState)
1046 aType <- either throwE pure (runReader (typeFromAtomExpr (tupleAttributes tupIn) cons) newState)
848 argAtoms <- mapM (\arg -> either throwE pure (runReader (evalAtomExpr tupIn arg) newState)) dConsArgs
1047 argAtoms <- mapM (\arg -> either throwE pure (runReader (evalAtomExpr tupIn arg) newState)) dConsArgs
849 pure (ConstructedAtom dConsName aType argAtoms)
1048 pure (ConstructedAtom dConsName aType argAtoms)
850−
1049+-}
851−typeFromAtomExpr :: Attributes -> AtomExpr -> RelationalExprState (Either RelationalError AtomType)
1050+typeForGraphRefAtomExpr :: Attributes -> GraphRefAtomExpr -> GraphRefRelationalExprM AtomType
852−typeFromAtomExpr attrs (AttributeAtomExpr attrName) = do
1051+typeForGraphRefAtomExpr attrs (AttributeAtomExpr attrName) = do
853− rstate <- ask
1052+ renv <- askEnv
854 case A.atomTypeForAttributeName attrName attrs of
1053 case A.atomTypeForAttributeName attrName attrs of
855− Right aType -> pure (Right aType)
1054+ Right aType -> pure aType
856− Left err@(NoSuchAttributeNamesError _) -> case rstate of
1055+ Left err@(NoSuchAttributeNamesError _) ->
1056+ let envTup = envTuple renv
1057+ envAttrs = envAttributes renv in
857− RelationalExprStateAttrsElems _ attrs' -> case A.attributeForName attrName attrs' of
1058+ case A.attributeForName attrName envAttrs of
858− Left err' -> pure (Left err')
859− Right attr -> pure (Right (A.atomType attr))
1059+ Right attr -> pure (A.atomType attr)
860− RelationalExprStateElems _ -> pure (Left err)
861− RelationalExprStateTupleElems _ tup -> case atomForAttributeName attrName tup of
1060+ Left _ -> case atomForAttributeName attrName envTup of
862− Left err' -> pure (Left err')
1061+ Right atom -> pure (atomTypeForAtom atom)
863− Right atom -> pure (Right (atomTypeForAtom atom))
1062+ Left _ -> --throwError (traceStack (show ("typeForGRAtomExpr", attrs, envTup)) err)
1063+ throwError err
864− Left err -> pure (Left err)
1064+ Left err -> throwError err
1065+
865−typeFromAtomExpr _ (NakedAtomExpr atom) = pure (Right (atomTypeForAtom atom))
1066+typeForGraphRefAtomExpr _ (NakedAtomExpr atom) = pure (atomTypeForAtom atom)
866−typeFromAtomExpr attrs (FunctionAtomExpr funcName atomArgs _) = do
1067+typeForGraphRefAtomExpr attrs (FunctionAtomExpr funcName atomArgs transId) = do
867− context <- fmap stateElemsContext ask
1068+ funcs <- atomFunctions <$> gfDatabaseContextForMarker transId
868− let funcs = atomFunctions context
869 case atomFunctionForName funcName funcs of
1069 case atomFunctionForName funcName funcs of
870− Left err -> pure (Left err)
1070+ Left err -> throwError err
871 Right func -> do
1071 Right func -> do
872 let funcRetType = last (atomFuncType func)
1072 let funcRetType = last (atomFuncType func)
873 funcArgTypes = init (atomFuncType func)
1073 funcArgTypes = init (atomFuncType func)
874− eArgTypes <- mapM (typeFromAtomExpr attrs) atomArgs
1074+ argTypes <- mapM (typeForGraphRefAtomExpr attrs) atomArgs
875− case lefts eArgTypes of
876− errs@(_:_) -> pure (Left (someErrors errs))
877− [] -> do
878− let eTvMap = resolveTypeVariables funcArgTypes argTypes
1075+ let eTvMap = resolveTypeVariables funcArgTypes argTypes
879− argTypes = rights eArgTypes
880− case eTvMap of
1076+ case eTvMap of
881− Left err -> pure (Left err)
1077+ Left err -> throwError err
882− Right tvMap -> pure (resolveFunctionReturnValue funcName tvMap funcRetType)
1078+ Right tvMap -> lift $ except $ resolveFunctionReturnValue funcName tvMap funcRetType
883−typeFromAtomExpr attrs (RelationAtomExpr relExpr) = runExceptT $ do
1079+typeForGraphRefAtomExpr attrs (RelationAtomExpr relExpr) = do
884− rstate <- lift ask
1080+ relType <- R.local (mergeAttributesIntoGraphRefRelationalExprEnv attrs) (typeForGraphRefRelationalExpr relExpr)
885− relType <- either throwE pure (runReader (typeForRelationalExpr relExpr) (mergeAttributesIntoRelationalExprState attrs rstate))
886 pure (RelationAtomType (attributes relType))
1081 pure (RelationAtomType (attributes relType))
887-- grab the type of the data constructor, then validate that the args match the expected types
1082-- grab the type of the data constructor, then validate that the args match the expected types
888−typeFromAtomExpr attrs (ConstructedAtomExpr dConsName dConsArgs _) =
1083+typeForGraphRefAtomExpr attrs (ConstructedAtomExpr dConsName dConsArgs tid) =
889− runExceptT $ do
1084+ do
890− argsTypes <- mapM (liftE . typeFromAtomExpr attrs) dConsArgs
1085+ argsTypes <- mapM (typeForGraphRefAtomExpr attrs) dConsArgs
891− context <- fmap stateElemsContext (lift ask)
1086+ tConsMap <- typeConstructorMapping <$> gfDatabaseContextForMarker tid
892− either throwE pure (atomTypeForDataConstructor (typeConstructorMapping context) dConsName argsTypes)
1087+ lift $ except $ atomTypeForDataConstructor tConsMap dConsName argsTypes
893
1088
894-- | Validate that the type of the AtomExpr matches the expected type.
1089-- | Validate that the type of the AtomExpr matches the expected type.
895−verifyAtomExprTypes :: Relation -> AtomExpr -> AtomType -> RelationalExprState (Either RelationalError AtomType)
1090+verifyGraphRefAtomExprTypes :: Relation -> GraphRefAtomExpr -> AtomType -> GraphRefRelationalExprM AtomType
896−verifyAtomExprTypes relIn (AttributeAtomExpr attrName) expectedType = runExceptT $ do
1091+verifyGraphRefAtomExprTypes relIn (AttributeAtomExpr attrName) expectedType = do
897− rstate <- lift ask
1092+ env <- askEnv
898 case A.atomTypeForAttributeName attrName (attributes relIn) of
1093 case A.atomTypeForAttributeName attrName (attributes relIn) of
899− Right aType -> either throwE pure (atomTypeVerify expectedType aType)
1094+ Right aType -> lift $ except $ atomTypeVerify expectedType aType
900− (Left err@(NoSuchAttributeNamesError _)) -> case rstate of
1095+ (Left err@(NoSuchAttributeNamesError _)) ->
901− RelationalExprStateTupleElems _ _ -> throwE err
1096+ let attrs' = envAttributes env in
902− RelationalExprStateElems _ -> throwE err
1097+ if attrs' == emptyAttributes then
1098+ throwError err
1099+ else
903− RelationalExprStateAttrsElems _ attrs -> case A.attributeForName attrName attrs of
1100+ case A.attributeForName attrName attrs' of
904− Left err' -> throwE err'
1101+ Left err' -> throwError err'
905− Right attrType -> either throwE pure (atomTypeVerify expectedType (A.atomType attrType))
1102+ Right attrType -> lift $ except $ atomTypeVerify expectedType (A.atomType attrType)
906− Left err -> throwE err
1103+ Left err -> throwError err
1104+
1105+verifyGraphRefAtomExprTypes _ (NakedAtomExpr atom) expectedType =
907−verifyAtomExprTypes _ (NakedAtomExpr atom) expectedType = pure (atomTypeVerify expectedType (atomTypeForAtom atom))
1106+ lift $ except $ atomTypeVerify expectedType (atomTypeForAtom atom)
908−verifyAtomExprTypes relIn (FunctionAtomExpr funcName funcArgExprs _) expectedType = do
1107+verifyGraphRefAtomExprTypes relIn (FunctionAtomExpr funcName funcArgExprs tid) expectedType = do
909− rstate <- ask
1108+ context <- gfDatabaseContextForMarker tid
910 let functions = atomFunctions context
1109 let functions = atomFunctions context
911− context = stateElemsContext rstate
912− runExceptT $ do
913− func <- either throwE pure (atomFunctionForName funcName functions)
1110+ func <- lift $ except $ atomFunctionForName funcName functions
914− let expectedArgTypes = atomFuncType func
1111+ let expectedArgTypes = atomFuncType func
915− funcArgTypes <- mapM (\(atomExpr,expectedType2,argCount) -> case runReader (verifyAtomExprTypes relIn atomExpr expectedType2) rstate of
1112+ funcArgVerifier (atomExpr, expectedType2, argCount) = do
1113+ let handler :: RelationalError -> GraphRefRelationalExprM AtomType
916− Left (AtomTypeMismatchError expSubType actSubType) -> throwE (AtomFunctionTypeError funcName argCount expSubType actSubType)
1114+ handler (AtomTypeMismatchError expSubType actSubType) = throwError (AtomFunctionTypeError funcName argCount expSubType actSubType)
917− Left err -> throwE err
1115+ handler err = throwError err
918− Right x -> pure x
1116+ verifyGraphRefAtomExprTypes relIn atomExpr expectedType2 `catchError` handler
919− ) $ zip3 funcArgExprs expectedArgTypes [1..]
1117+ funcArgTypes <- mapM funcArgVerifier $ zip3 funcArgExprs expectedArgTypes [1..]
920− if length funcArgTypes /= length expectedArgTypes - 1 then
1118+ if length funcArgTypes /= length expectedArgTypes - 1 then
921− throwE (AtomTypeCountError funcArgTypes expectedArgTypes)
1119+ throwError (AtomTypeCountError funcArgTypes expectedArgTypes)
922 else
1120 else
923− either throwE pure (atomTypeVerify expectedType (last expectedArgTypes))
1121+ lift $ except $ atomTypeVerify expectedType (last expectedArgTypes)
924−verifyAtomExprTypes relIn (RelationAtomExpr relationExpr) expectedType = runExceptT $ do
1122+verifyGraphRefAtomExprTypes relIn (RelationAtomExpr relationExpr) expectedType =
925− rstate <- lift ask
1123+ do
926− relType <- either throwE pure (runReader (typeForRelationalExpr relationExpr) (mergeAttributesIntoRelationalExprState (attributes relIn) rstate))
1124+ let mergedAttrsEnv e = mergeAttributesIntoGraphRefRelationalExprEnv (attributes relIn) e
1125+ relType <- R.local mergedAttrsEnv (typeForGraphRefRelationalExpr relationExpr)
927− either throwE pure (atomTypeVerify expectedType (RelationAtomType (attributes relType)))
1126+ lift $ except $ atomTypeVerify expectedType (RelationAtomType (attributes relType))
928−verifyAtomExprTypes rel cons@ConstructedAtomExpr{} expectedType = runExceptT $ do
1127+verifyGraphRefAtomExprTypes rel cons@ConstructedAtomExpr{} expectedType = do
929− cType <- liftE (typeFromAtomExpr (attributes rel) cons)
1128+ cType <- typeForGraphRefAtomExpr (attributes rel) cons
930− either throwE pure (atomTypeVerify expectedType cType)
1129+ lift $ except $ atomTypeVerify expectedType cType
931
1130
932-- | Look up the type's name and create a new attribute.
1131-- | Look up the type's name and create a new attribute.
933−evalAttrExpr :: TypeConstructorMapping -> AttributeExpr -> Either RelationalError Attribute
1132+evalGraphRefAttrExpr :: GraphRefAttributeExpr -> GraphRefRelationalExprM Attribute
934−evalAttrExpr aTypes (AttributeAndTypeNameExpr attrName tCons ()) = do
1133+evalGraphRefAttrExpr (AttributeAndTypeNameExpr attrName tCons transId) = do
1134+ tConsMap <- typeConstructorMapping <$> gfDatabaseContextForMarker transId
935− aType <- atomTypeForTypeConstructorValidate True tCons aTypes M.empty
1135+ aType <- lift $ except $ atomTypeForTypeConstructorValidate True tCons tConsMap M.empty
936− validateAtomType aType aTypes
1136+ lift $ except $ validateAtomType aType tConsMap
937− Right (Attribute attrName aType)
1137+ pure $ Attribute attrName aType
938
1138
939−evalAttrExpr _ (NakedAttributeExpr attr) = Right attr
1139+evalGraphRefAttrExpr (NakedAttributeExpr attr) = pure attr
940−
1140+
941−evalTupleExpr :: Maybe Attributes -> TupleExpr -> RelationalExprState (Either RelationalError RelationTuple)
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
942−evalTupleExpr mAttrs (TupleExpr tupMap) = do
1178+evalGraphRefTupleExpr mAttrs (TupleExpr tupMap) = do
943− context <- fmap stateElemsContext ask
944− runExceptT $ do
945 -- 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
1179 -- 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
946 -- 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
1180 -- 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
1181+ let attrs = fromMaybe A.emptyAttributes mAttrs
948− attrAtoms <- mapM (\(attrName, aExpr) -> do
1182+ resolveOneAtom (attrName, aExpr) =
1183+ do
949− --provided when the relation header is available
1184+ --provided when the relation header is available
950− let eExpectedAtomType = A.atomTypeForAttributeName attrName attrs
1185+ let eExpectedAtomType = A.atomTypeForAttributeName attrName attrs
951− unresolvedType <- liftE (typeFromAtomExpr attrs aExpr)
1186+ unresolvedType <- typeForGraphRefAtomExpr attrs aExpr
952− resolvedType <- case eExpectedAtomType of
1187+ resolvedType <- case eExpectedAtomType of
953 Left _ -> pure unresolvedType
1188 Left _ -> pure unresolvedType
954− Right typeHint -> either throwE pure (resolveAtomType typeHint unresolvedType)
1189+ Right typeHint -> lift $ except $ resolveAtomType typeHint unresolvedType
955 --resolve atom typevars based on resolvedType?
1190 --resolve atom typevars based on resolvedType?
956− newAtom <- liftE (evalAtomExpr emptyTuple aExpr)
1191+ newAtom <- evalGraphRefAtomExpr emptyTuple aExpr
957− pure (attrName, newAtom, resolvedType)
1192+ pure (attrName, newAtom, resolvedType)
958− ) (M.toList tupMap)
1193+ attrAtoms <- mapM resolveOneAtom (M.toList tupMap)
959− let tupAttrs = A.attributesFromList $ map (\(attrName, _, aType) -> Attribute attrName aType) attrAtoms
1194+ let tupAttrs = A.attributesFromList $ map (\(attrName, _, aType) -> Attribute attrName aType) attrAtoms
960− atoms = V.fromList $ map (\(_, atom, _) -> atom) attrAtoms
1195+ atoms = V.fromList $ map (\(_, atom, _) -> atom) attrAtoms
961− tup = mkRelationTuple tupAttrs atoms
1196+ tup = mkRelationTuple tupAttrs atoms
962− tConss = typeConstructorMapping context
963− finalAttrs = fromMaybe tupAttrs mAttrs
1197+ finalAttrs = fromMaybe tupAttrs mAttrs
964 --verify that the attributes match
1198 --verify that the attributes match
965− when (A.attributeNameSet finalAttrs /= A.attributeNameSet tupAttrs) $ throwE (TupleAttributeTypeMismatchError tupAttrs)
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
966− tup' <- either throwE pure (resolveTypesInTuple finalAttrs tConss (reorderTuple finalAttrs tup))
1201+ --tup' <- lift $ except (resolveTypesInTuple finalAttrs tConss (reorderTuple finalAttrs tup))
1202+ let tup' = reorderTuple finalAttrs tup
1203+ --TODO: restore type resolution
967− _ <- either throwE pure (validateTuple tup' tConss)
1204+-- _ <- lift $ except (validateTuple tup' tConss)
968− pure tup'
1205+ pure tup'
969
1206
1207+{-
970−evalAttributeNames :: AttributeNames -> RelationalExpr -> RelationalExprState (Either RelationalError (S.Set AttributeName))
1208+evalAttributeNames :: AttributeNames -> RelationalExpr -> RelationalExprM (Either RelationalError (S.Set AttributeName))
971evalAttributeNames attrNames expr = do
1209evalAttributeNames attrNames expr = do
972 eExprType <- typeForRelationalExpr expr
1210 eExprType <- typeForRelationalExpr expr
973 case eExprType of
1211 case eExprType of
974− Left err -> pure (Left err)
1212+ Left err -> throwError err
975 Right exprTyp -> do
1213 Right exprTyp -> do
976 let typeNameSet = S.fromList (V.toList (A.attributeNames (attributes exprTyp)))
1214 let typeNameSet = S.fromList (V.toList (A.attributeNames (attributes exprTyp)))
977 case attrNames of
1215 case attrNames of
@@ -1014,4 +1252,278 @@ evalAttributeNames attrNames expr = do
1014 case eAttrExprType of
1252 case eAttrExprType of
1015 Left err -> pure (Left err)
1253 Left err -> pure (Left err)
1016 Right attrExprType -> pure (Right (A.attributeNameSet (attributes attrExprType)))
1254 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
80 let ghcVersion = projectVersion dflags
80 let ghcVersion = projectVersion dflags
81
81
82 sandboxPkgPaths <- liftIO $ concat <$> mapM glob [
82 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"
84 ".cabal-sandbox/*ghc-" ++ ghcVersion ++ "-packages.conf.d",
84 ".cabal-sandbox/*ghc-" ++ ghcVersion ++ "-packages.conf.d",
85 ".stack-work/install/*/*/" ++ ghcVersion ++ "/pkgdb",
85 ".stack-work/install/*/*/" ++ ghcVersion ++ "/pkgdb",
86 ".stack-work/install/*/pkgdb/", --windows stack build
86 ".stack-work/install/*/pkgdb/", --windows stack build
87 "C:/sr/snapshots/b201cfe6/pkgdb", --windows stack build- ideally, we could run `stack path --snapshot-pkg-db, but this is sufficient to pass CI
87 "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",
88+ homeDir </> ".stack/snapshots/*/*/" ++ ghcVersion ++ "/pkgdb"
89− homeDir </> ".cabal/store/ghc-" ++ ghcVersion ++ "/package.db"
89+ --homeDir </> ".cabal/store/ghc-" ++ ghcVersion ++ "/package.db"
90 ]
90 ]
91
91
92 let localPkgPaths = map PkgConfFile (ghcPkgPaths ++ sandboxPkgPaths)
92 let localPkgPaths = map PkgConfFile (ghcPkgPaths ++ sandboxPkgPaths)
src/lib/ProjectM36/Server.hs+1−1
@@ -9,7 +9,7 @@ import ProjectM36.FSType
9
9
10import Control.Monad.IO.Class (liftIO)
10import Control.Monad.IO.Class (liftIO)
11#if MIN_VERSION_network_transport_tcp(0,7,0)
11#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)
13#else
13#else
14import Network.Transport.TCP (createTransport, defaultTCPParameters)
14import Network.Transport.TCP (createTransport, defaultTCPParameters)
15#endif
15#endif
src/lib/ProjectM36/Server/EntryPoints.hs+4−3
@@ -8,12 +8,13 @@ import Control.Distributed.Process (Process, ProcessId)
8import Control.Distributed.Process.ManagedProcess (ProcessReply)
8import Control.Distributed.Process.ManagedProcess (ProcessReply)
9import Control.Distributed.Process.ManagedProcess.Server (reply)
9import Control.Distributed.Process.ManagedProcess.Server (reply)
10import Control.Distributed.Process.Async (async, task, waitCancelTimeout, AsyncResult(..))
10import Control.Distributed.Process.Async (async, task, waitCancelTimeout, AsyncResult(..))
11−import Control.Distributed.Process.Serializable (Serializable)
12import Control.Monad.IO.Class (liftIO)
11import Control.Monad.IO.Class (liftIO)
13import Data.Map
12import Data.Map
14import Control.Concurrent (threadDelay)
13import Control.Concurrent (threadDelay)
14+import Data.Typeable
15+import Data.Binary
15
16
16−timeoutOrDie :: Serializable a => Timeout -> IO a -> Process (Either ServerError a)
17+timeoutOrDie :: (Binary a, Typeable a) => Timeout -> IO a -> Process (Either ServerError a)
17timeoutOrDie micros act =
18timeoutOrDie micros act =
18 if micros == 0 then
19 if micros == 0 then
19 liftIO act >>= \x -> pure (Right x)
20 liftIO act >>= \x -> pure (Right x)
@@ -83,7 +84,7 @@ handleRetrieveInclusionDependencies ti sessionId conn = do
83 ret <- timeoutOrDie ti (inclusionDependencies sessionId conn)
84 ret <- timeoutOrDie ti (inclusionDependencies sessionId conn)
84 reply ret conn
85 reply ret conn
85
86
86−handleRetrievePlanForDatabaseContextExpr :: Timeout -> SessionId -> Connection -> DatabaseContextExpr -> Reply (Either RelationalError DatabaseContextExpr)
87+handleRetrievePlanForDatabaseContextExpr :: Timeout -> SessionId -> Connection -> DatabaseContextExpr -> Reply (Either RelationalError GraphRefDatabaseContextExpr)
87handleRetrievePlanForDatabaseContextExpr ti sessionId conn dbExpr = do
88handleRetrievePlanForDatabaseContextExpr ti sessionId conn dbExpr = do
88 ret <- timeoutOrDie ti (planForDatabaseContextExpr sessionId conn dbExpr)
89 ret <- timeoutOrDie ti (planForDatabaseContextExpr sessionId conn dbExpr)
89 reply ret conn
90 reply ret conn
src/lib/ProjectM36/Session.hs+3−2
@@ -3,6 +3,7 @@ import ProjectM36.Base
3import Data.UUID
3import Data.UUID
4import qualified Data.Map as M
4import qualified Data.Map as M
5import ProjectM36.Error
5import ProjectM36.Error
6+import qualified ProjectM36.DisconnectedTransaction as Discon
6
7
7type SessionId = UUID
8type SessionId = UUID
8
9
@@ -18,7 +19,7 @@ disconnectedTransaction :: Session -> DisconnectedTransaction
18disconnectedTransaction (Session discon _) = discon
19disconnectedTransaction (Session discon _) = discon
19
20
20isDirty :: Session -> DirtyFlag
21isDirty :: Session -> DirtyFlag
21−isDirty (Session (DisconnectedTransaction _ _ dirtyFlag) _) = dirtyFlag
22+isDirty (Session discon _) = Discon.isDirty discon
22
23
23concreteDatabaseContext :: Session -> DatabaseContext
24concreteDatabaseContext :: Session -> DatabaseContext
24concreteDatabaseContext (Session (DisconnectedTransaction _ (Schemas context _) _) _) = context
25concreteDatabaseContext (Session (DisconnectedTransaction _ (Schemas context _) _) _) = context
@@ -40,4 +41,4 @@ setSchemaName sname session = if sname == defaultSchemaName || M.member sname (s
40 pure (Session (disconnectedTransaction session) sname)
41 pure (Session (disconnectedTransaction session) sname)
41 else
42 else
42 Left (SubschemaNameNotInUseError sname)
43 Left (SubschemaNameNotInUseError sname)
43−
44+
src/lib/ProjectM36/Sessions.hs+1−1
@@ -7,7 +7,7 @@ import qualified StmContainers.Set as StmSet
7#else
7#else
8import qualified STMContainers.Map as StmMap
8import qualified STMContainers.Map as StmMap
9import qualified STMContainers.Set as StmSet
9import qualified STMContainers.Set as StmSet
10−#endif
10+#endif
11import ProjectM36.Attribute
11import ProjectM36.Attribute
12import ProjectM36.Base
12import ProjectM36.Base
13import ProjectM36.Session
13import ProjectM36.Session
src/lib/ProjectM36/StaticOptimizer.hs+369−249
@@ -1,289 +1,402 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE FlexibleContexts #-}
1module ProjectM36.StaticOptimizer where
3module ProjectM36.StaticOptimizer where
2import ProjectM36.Base
4import ProjectM36.Base
3−import ProjectM36.RelationalExpression
5+import ProjectM36.GraphRefRelationalExpr
4import ProjectM36.Relation
6import ProjectM36.Relation
7+import ProjectM36.RelationalExpression
8+import ProjectM36.TransGraphRelationalExpression as TGRE hiding (askGraph)
5import ProjectM36.Error
9import ProjectM36.Error
10+import ProjectM36.Transaction
11+import ProjectM36.NormalizeExpr
6import qualified ProjectM36.Attribute as A
12import qualified ProjectM36.Attribute as A
7import qualified ProjectM36.AttributeNames as AS
13import qualified ProjectM36.AttributeNames as AS
8import ProjectM36.TupleSet
14import ProjectM36.TupleSet
9import Control.Monad.State
15import Control.Monad.State
10−import Data.Either (rights, lefts)
16+import Control.Monad.Reader
17+import Control.Monad.Except
11−import Control.Monad.Trans.Reader
18+import Control.Monad.Trans.Except
19+import Data.Functor.Identity
12import qualified Data.Map as M
20import qualified Data.Map as M
13import qualified Data.Set as S
21import qualified Data.Set as S
14
22
15-- the static optimizer performs optimizations which need not take any specific-relation statistics into account
23-- the static optimizer performs optimizations which need not take any specific-relation statistics into account
16−optimizeRelationalExpr :: DatabaseContext -> RelationalExpr -> Either RelationalError RelationalExpr
24+
17−optimizeRelationalExpr context expr = runReader (optimizeRelationalExprReader expr) (mkRelationalExprState context)
25+--import Debug.Trace
18−
26+
19−optimizeRelationalExprReader :: RelationalExpr -> RelationalExprState (Either RelationalError RelationalExpr)
27+data GraphRefSOptRelationalExprEnv =
20−optimizeRelationalExprReader expr = do
28+ GraphRefSOptRelationalExprEnv
21− eOptExpr <- applyStaticRelationalOptimization expr
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)
22 case eOptExpr of
112 case eOptExpr of
23 Left err -> pure (Left err)
113 Left err -> pure (Left err)
24 Right optExpr ->
114 Right 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
25− applyStaticJoinElimination (applyStaticRestrictionPushdown (applyStaticRestrictionCollapse optExpr))
173+ let optExpr' = applyStaticRestrictionPushdown (applyStaticRestrictionCollapse optExpr)
174+ applyStaticJoinElimination optExpr'
26
175
27−optimizeDatabaseContextExpr :: DatabaseContext -> DatabaseContextExpr -> Either RelationalError DatabaseContextExpr
28−optimizeDatabaseContextExpr context dbExpr = evalState (applyStaticDatabaseOptimization dbExpr) (freshDatabaseState context)
29-- apply optimizations which merely remove steps to become no-ops: example: projection of a relation across all of its attributes => original relation
176-- apply optimizations which merely remove steps to become no-ops: example: projection of a relation across all of its attributes => original relation
30
177
31--should optimizations offer the possibility to pure errors? If they perform the up-front type-checking, maybe so
178--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)
179+optimizeGraphRefRelationalExpr :: GraphRefRelationalExpr -> GraphRefSOptRelationalExprM GraphRefRelationalExpr
33−applyStaticRelationalOptimization e@(MakeStaticRelation _ _) = pure $ Right e
180+optimizeGraphRefRelationalExpr e@(MakeStaticRelation _ _) = pure e
34
181
35−applyStaticRelationalOptimization e@(MakeRelationFromExprs _ _) = pure $ Right e
182+optimizeGraphRefRelationalExpr e@(MakeRelationFromExprs _ _) = pure e
36
183
37−applyStaticRelationalOptimization e@(ExistingRelation _) = pure $ Right e
184+optimizeGraphRefRelationalExpr e@(ExistingRelation _) = pure e
38
185
39−applyStaticRelationalOptimization e@(RelationVariable _ _) = pure $ Right e
186+optimizeGraphRefRelationalExpr e@(RelationVariable _ _) = pure e
40
187
41--remove project of attributes which removes no attributes
188--remove project of attributes which removes no attributes
42−applyStaticRelationalOptimization (Project attrNameSet expr) = do
189+optimizeGraphRefRelationalExpr (Project attrNameSet expr) = do
43− relType <- typeForRelationalExpr expr
190+ graph <- askGraph
191+ mctx <- askMaybeContext
192+ let relType = runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr expr)
193+ gfEnv = freshGraphRefRelationalExprEnv mctx graph
44 case relType of
194 case relType of
45− Left err -> pure $ Left err
195+ Left err -> throwError err
46 Right relType2
196 Right relType2
47− | AS.all == attrNameSet ->
197+ | AS.all == attrNameSet ->
48− applyStaticRelationalOptimization expr
198+ optimizeGraphRefRelationalExpr expr
49 | AttributeNames (attributeNames relType2) == attrNameSet ->
199 | AttributeNames (attributeNames relType2) == attrNameSet ->
50− applyStaticRelationalOptimization expr
200+ optimizeGraphRefRelationalExpr expr
51 | otherwise -> do
201 | otherwise -> do
52− optimizedSubExpression <- applyStaticRelationalOptimization expr
202+ optSubExpr <- optimizeGraphRefRelationalExpr expr
53− case optimizedSubExpression of
54− Left err -> pure $ Left err
55− Right optSubExpr -> pure (Right (Project attrNameSet optSubExpr))
203+ pure (Project attrNameSet optSubExpr)
56
204
57−applyStaticRelationalOptimization (Union exprA exprB) = do
205+optimizeGraphRefRelationalExpr (Union exprA exprB) = do
58− eOptExprA <- applyStaticRelationalOptimization exprA
206+ optExprA <- optimizeGraphRefRelationalExpr exprA
59− eOptExprB <- applyStaticRelationalOptimization exprB
207+ optExprB <- optimizeGraphRefRelationalExpr 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)
208+ -- (x where pred1) union (x where pred2) -> (x where pred1 or pred2)
65− case (optExprA, optExprB) of
209+ case (optExprA, optExprB) of
66− (Restrict predA (RelationVariable nameA ()),
210+ (Restrict predA (RelationVariable nameA sA),
67− Restrict predB (RelationVariable nameB ())) | nameA == nameB -> pure (Right (Restrict (AndPredicate predA predB) (RelationVariable nameA ())))
211+ Restrict predB (RelationVariable nameB sB)) | nameA == nameB && sA == sB -> pure (Restrict (AndPredicate predA predB) (RelationVariable nameA sA))
68 _ -> if optExprA == optExprB then
212 _ -> if optExprA == optExprB then
69− pure (Right optExprA)
213+ pure optExprA
70 else
214 else
71− pure $ Right $ Union optExprA optExprB
215+ pure $ Union optExprA optExprB
72
216
73−applyStaticRelationalOptimization (Join exprA exprB) = do
217+optimizeGraphRefRelationalExpr (Join exprA exprB) = do
74− eOptExprA <- applyStaticRelationalOptimization exprA
218+ optExprA <- optimizeGraphRefRelationalExpr exprA
75− eOptExprB <- applyStaticRelationalOptimization exprB
219+ optExprB <- optimizeGraphRefRelationalExpr 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
220+ -- if the relvars to join are the same but with predicates, then just AndPredicate the predicates
82− case (optExprA, optExprB) of
221+ case (optExprA, optExprB) of
83− (Restrict predA (RelationVariable nameA ()),
222+ (Restrict predA (RelationVariable nameA sA),
84− Restrict predB (RelationVariable nameB ())) | nameA == nameB -> pure (Right (Restrict (AndPredicate predA predB) (RelationVariable nameA ())))
223+ Restrict predB (RelationVariable nameB sB)) | nameA == nameB && sA == sB -> pure (Restrict (AndPredicate predA predB) (RelationVariable nameA sA))
85 _ -> if optExprA == optExprB then --A join A == A
224 _ -> if optExprA == optExprB then --A join A == A
86− pure (Right optExprA)
225+ pure optExprA
87 else
226 else
88− pure (Right (Join optExprA optExprB))
227+ pure (Join optExprA optExprB)
89
228
90−applyStaticRelationalOptimization (Difference exprA exprB) = do
229+optimizeGraphRefRelationalExpr (Difference exprA exprB) = do
91− optExprA <- applyStaticRelationalOptimization exprA
230+ graph <- askGraph
92− optExprB <- applyStaticRelationalOptimization exprB
231+ context <- askMaybeContext
93− case optExprA of
232+ optExprA <- optimizeGraphRefRelationalExpr exprA
94− Left err -> pure $ Left err
233+ optExprB <- optimizeGraphRefRelationalExpr exprB
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
234+ if optExprA == optExprB then do --A difference A == A where false
98− eEmptyRel <- typeForRelationalExpr optExprA2
235+ let eEmptyRel = runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr optExprA)
236+ gfEnv = freshGraphRefRelationalExprEnv context graph
99− case eEmptyRel of
237+ case eEmptyRel of
100− Left err -> pure (Left err)
238+ Left err -> throwError err
101− Right emptyRel -> pure (Right (ExistingRelation emptyRel))
239+ Right emptyRel -> pure (ExistingRelation emptyRel)
102− else
240+ else
103− pure $ Right (Difference optExprA2 optExprB2)
241+ pure (Difference optExprA optExprB)
104
242
105−applyStaticRelationalOptimization e@Rename{} = pure $ Right e
243+optimizeGraphRefRelationalExpr e@Rename{} = pure e
106
244
107−applyStaticRelationalOptimization (Group oldAttrNames newAttrName expr) =
245+optimizeGraphRefRelationalExpr (Group oldAttrNames newAttrName expr) =
108− pure $ Right $ Group oldAttrNames newAttrName expr
246+ pure $ Group oldAttrNames newAttrName expr
109
247
110−applyStaticRelationalOptimization (Ungroup attrName expr) =
248+optimizeGraphRefRelationalExpr (Ungroup attrName expr) =
111− pure $ Right $ Ungroup attrName expr
249+ pure $ Ungroup attrName expr
112
250
113--remove restriction of nothing
251--remove restriction of nothing
114−applyStaticRelationalOptimization (Restrict predicate expr) = do
252+optimizeGraphRefRelationalExpr (Restrict predicate expr) = do
253+ graph <- askGraph
254+ mctx <- askMaybeContext
115 optimizedPredicate <- applyStaticPredicateOptimization predicate
255 optimizedPredicate <- applyStaticPredicateOptimization predicate
116 case optimizedPredicate of
256 case optimizedPredicate of
117− Left err -> pure $ Left err
257+ optimizedPredicate' | isTrueExpr optimizedPredicate' -> optimizeGraphRefRelationalExpr expr -- remove predicate entirely
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
258+ optimizedPredicate' | isFalseExpr optimizedPredicate' -> do -- replace where false predicate with empty relation with attributes from relexpr
121− attributesRel <- typeForRelationalExpr expr
259+ let attributesRel = runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr expr)
260+ gfEnv = freshGraphRefRelationalExprEnv mctx graph
122 case attributesRel of
261 case attributesRel of
123− Left err -> pure $ Left err
262+ Left err -> throwError err
124− Right attributesRelA -> pure $ Right $ MakeStaticRelation (attributes attributesRelA) emptyTupleSet
263+ Right attributesRelA -> pure $ MakeStaticRelation (attributes attributesRelA) emptyTupleSet
125 | otherwise -> do
264 | otherwise -> do
126− optimizedSubExpression <- applyStaticRelationalOptimization expr
265+ optSubExpr <- optimizeGraphRefRelationalExpr expr
127− case optimizedSubExpression of
128− Left err -> pure $ Left err
129− Right optSubExpr -> pure $ Right $ Restrict optimizedPredicate2 optSubExpr
266+ pure $ Restrict optimizedPredicate' optSubExpr
130
267
131−applyStaticRelationalOptimization e@(Equals _ _) = pure $ Right e
268+optimizeGraphRefRelationalExpr e@(Equals _ _) = pure e
132
269
133−applyStaticRelationalOptimization e@(NotEquals _ _) = pure $ Right e
270+optimizeGraphRefRelationalExpr e@(NotEquals _ _) = pure e
134
271
135−applyStaticRelationalOptimization e@(Extend _ _) = pure $ Right e
272+optimizeGraphRefRelationalExpr e@(Extend _ _) = pure e
136
273
137−applyStaticRelationalOptimization e@(With _ _) = pure $ Right e
274+optimizeGraphRefRelationalExpr e@(With _ _) = pure e
138
275
139−applyStaticDatabaseOptimization :: DatabaseContextExpr -> DatabaseState (Either RelationalError DatabaseContextExpr)
276+-- database context expr
140−applyStaticDatabaseOptimization x@NoOperation = pure $ Right x
277+optimizeGraphRefDatabaseContextExpr :: GraphRefDatabaseContextExpr -> GraphRefSOptDatabaseContextExprM GraphRefDatabaseContextExpr
141−applyStaticDatabaseOptimization x@(Define _ _) = pure $ Right x
278+optimizeGraphRefDatabaseContextExpr x@NoOperation = pure x
279+optimizeGraphRefDatabaseContextExpr x@(Define _ _) = pure x
142
280
143−applyStaticDatabaseOptimization x@(Undefine _) = pure $ Right x
281+optimizeGraphRefDatabaseContextExpr x@(Undefine _) = pure x
144
282
145−applyStaticDatabaseOptimization (Assign name expr) = do
283+optimizeGraphRefDatabaseContextExpr (Assign name expr) = do
146− context <- getStateContext
284+ optExpr <- liftGraphRefRelExpr (fullOptimizeGraphRefRelationalExpr expr)
147− let optimizedExpr = optimizeRelationalExpr context expr
285+ pure $ Assign name optExpr
148− case optimizedExpr of
149− Left err -> pure $ Left err
150− Right optimizedExpr2 -> pure $ Right (Assign name optimizedExpr2)
151
286
152−applyStaticDatabaseOptimization (Insert targetName expr) = do
287+optimizeGraphRefDatabaseContextExpr (Insert targetName expr) = do
153− context <- getStateContext
288+ optimizedExpr <- liftGraphRefRelExpr (fullOptimizeGraphRefRelationalExpr expr)
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
289+ if isEmptyRelationExpr optimizedExpr then -- if we are trying to insert an empty relation, do nothing
158− pure (Right NoOperation)
290+ pure NoOperation
159− else
291+ else
160− case optimizedExpr2 of
292+ case optimizedExpr of
161− -- if the target relvar and the insert relvar are the same, there is nothing to do
293+ -- if the target relvar and the insert relvar are the same, there is nothing to do
162− -- insert s s -> NoOperation
294+ -- insert s s -> NoOperation
163− RelationVariable insName () | insName == targetName -> pure (Right NoOperation)
295+ RelationVariable insName _ | insName == targetName -> pure NoOperation
164− _ -> pure $ Right (Insert targetName optimizedExpr2)
296+ _ -> pure (Insert targetName optimizedExpr)
165
297
166−applyStaticDatabaseOptimization (Delete name predicate) = do
298+optimizeGraphRefDatabaseContextExpr (Delete name predicate) =
167− context <- getStateContext
168− let optimizedPredicate = runReader (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
299+ Delete name <$> liftGraphRefRelExpr (applyStaticPredicateOptimization predicate)
169− case optimizedPredicate of
170− Left err -> pure $ Left err
171− Right optimizedPredicate2 -> pure $ Right (Delete name optimizedPredicate2)
172
300
173−applyStaticDatabaseOptimization (Update name upmap predicate) = do
301+optimizeGraphRefDatabaseContextExpr (Update name upmap predicate) =
174− context <- getStateContext
175− let optimizedPredicate = runReader (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
302+ Update name upmap <$> liftGraphRefRelExpr (applyStaticPredicateOptimization predicate)
176− case optimizedPredicate of
177− Left err -> pure $ Left err
178− Right optimizedPredicate2 -> pure $ Right (Update name upmap optimizedPredicate2)
179
303
180−applyStaticDatabaseOptimization dep@(AddInclusionDependency _ _) = pure $ Right dep
304+optimizeGraphRefDatabaseContextExpr dep@(AddInclusionDependency _ _) = pure dep
181
305
182−applyStaticDatabaseOptimization (RemoveInclusionDependency name) = pure $ Right (RemoveInclusionDependency name)
306+optimizeGraphRefDatabaseContextExpr (RemoveInclusionDependency name) = pure (RemoveInclusionDependency name)
183
307
184−applyStaticDatabaseOptimization (AddNotification name triggerExpr resultOldExpr resultNewExpr) = do
308+optimizeGraphRefDatabaseContextExpr (AddNotification name triggerExpr resultOldExpr resultNewExpr) =
185− context <- getStateContext
309+ --we can't optimize these expressions until they run
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))
310+ pure (AddNotification name triggerExpr resultOldExpr resultNewExpr)
191
311
192−applyStaticDatabaseOptimization notif@(RemoveNotification _) = pure (Right notif)
312+optimizeGraphRefDatabaseContextExpr notif@(RemoveNotification _) = pure notif
193
313
194−applyStaticDatabaseOptimization c@(AddTypeConstructor _ _) = pure (Right c)
314+optimizeGraphRefDatabaseContextExpr c@(AddTypeConstructor _ _) = pure c
195−applyStaticDatabaseOptimization c@(RemoveTypeConstructor _) = pure (Right c)
315+optimizeGraphRefDatabaseContextExpr c@(RemoveTypeConstructor _) = pure c
196−applyStaticDatabaseOptimization c@(RemoveAtomFunction _) = pure (Right c)
316+optimizeGraphRefDatabaseContextExpr c@(RemoveAtomFunction _) = pure c
197−applyStaticDatabaseOptimization c@(RemoveDatabaseContextFunction _) = pure (Right c)
317+optimizeGraphRefDatabaseContextExpr c@(RemoveDatabaseContextFunction _) = pure c
198−applyStaticDatabaseOptimization c@(ExecuteDatabaseContextFunction _ _) = pure (Right c)
318+optimizeGraphRefDatabaseContextExpr c@(ExecuteDatabaseContextFunction _ _) = pure c
199
319
200--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
320--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
201
321
202--applyStaticDatabaseOptimization (MultipleExpr exprs) = pure $ Right $ MultipleExpr exprs
322--applyStaticDatabaseOptimization (MultipleExpr exprs) = pure $ Right $ MultipleExpr exprs
203--for multiple expressions, we must evaluate
323--for multiple expressions, we must evaluate
204−applyStaticDatabaseOptimization (MultipleExpr exprs) = do
324+optimizeGraphRefDatabaseContextExpr (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
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
215− _ <- evalDatabaseContextExpr expr
326+ context <- askContext
216− applyStaticDatabaseOptimization expr
327+ graph <- askGraph
217− --this error handling could be improved with some lifting presumably
328+ parentId <- askTransId
218− --restore original context
329+
219−
330+ let emptyRvs ctx = ctx { relationVariables = mkEmptyRelVars (relationVariables ctx) }
220−applyStaticPredicateOptimization :: RestrictionPredicateExpr -> RelationalExprState (Either RelationalError RestrictionPredicateExpr)
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
221applyStaticPredicateOptimization predi = do
346applyStaticPredicateOptimization predi = do
222− eOptPred <- case predi of
347+ optPred <- case predi of
223-- where x and x => where x
348-- where x and x => where x
224 AndPredicate pred1 pred2 -> do
349 AndPredicate pred1 pred2 -> do
225− eOptPred1 <- applyStaticPredicateOptimization pred1
350+ optPredA <- applyStaticPredicateOptimization pred1
226− case eOptPred1 of
227− Left err -> pure (Left err)
228− Right optPred1 -> do
229− eOptPred2 <- applyStaticPredicateOptimization pred2
351+ optPredB <- applyStaticPredicateOptimization pred2
230− case eOptPred2 of
352+ if optPredA == optPredB then
231− Left err -> pure (Left err)
353+ pure optPredA
232− Right optPred2 ->
233− if optPred1 == optPred2 then
234− pure (Right optPred1)
235− else
354+ else
236− pure (Right (AndPredicate optPred1 optPred2))
355+ pure (AndPredicate optPredA optPredB)
237-- where x or x => where x
356-- where x or x => where x
238 OrPredicate pred1 pred2 -> do
357 OrPredicate pred1 pred2 -> do
239− eOptPred1 <- applyStaticPredicateOptimization pred1
358+ optPredA <- applyStaticPredicateOptimization pred1
240− case eOptPred1 of
241− Left err -> pure (Left err)
242− Right optPred1 -> do
243− eOptPred2 <- applyStaticPredicateOptimization pred2
359+ optPredB <- applyStaticPredicateOptimization pred2
244− case eOptPred2 of
360+ if (optPredA == optPredB) || isTrueExpr optPredA then
245− Left err -> pure (Left err)
361+ pure optPredA
246− Right optPred2 | optPred1 == optPred2 -> pure (Right optPred1)
362+ else if isTrueExpr optPredB then
247− | isTrueExpr optPred1 -> pure (Right optPred1) -- True or x -> True
363+ pure optPredB
248− | isTrueExpr optPred2 -> pure (Right optPred2)
364+ else
249− | otherwise -> pure (Right (OrPredicate optPred1 optPred2))
365+ pure (OrPredicate optPredA optPredB)
250 AttributeEqualityPredicate attrNameA (AttributeAtomExpr attrNameB) ->
366 AttributeEqualityPredicate attrNameA (AttributeAtomExpr attrNameB) ->
251 if attrNameA == attrNameB then
367 if attrNameA == attrNameB then
252− pure (Right TruePredicate)
368+ pure TruePredicate
253 else
369 else
254− pure (Right predi)
370+ pure predi
255− AttributeEqualityPredicate{} -> pure (Right predi)
371+ AttributeEqualityPredicate{} -> pure predi
256− TruePredicate -> pure $ Right predi
372+ TruePredicate -> pure predi
257− NotPredicate{} -> pure $ Right predi
373+ NotPredicate{} -> pure predi
258− RelationalExprPredicate{} -> pure (Right predi)
374+ RelationalExprPredicate{} -> pure predi
259− AtomExprPredicate{} -> pure (Right predi)
375+ AtomExprPredicate{} -> pure predi
260− case eOptPred of
261− Left err -> pure (Left err)
262− Right optPred ->
263− let attrMap = findStaticRestrictionPredicates optPred in
376+ let attrMap = findStaticRestrictionPredicates optPred
264− pure (Right (replaceStaticAtomExprs optPred attrMap))
377+ pure (replaceStaticAtomExprs optPred attrMap)
265
378
266--determines if an atom expression is tautologically true
379--determines if an atom expression is tautologically true
267−isTrueExpr :: RestrictionPredicateExpr -> Bool
380+isTrueExpr :: RestrictionPredicateExprBase a -> Bool
268isTrueExpr TruePredicate = True
381isTrueExpr TruePredicate = True
269isTrueExpr (AtomExprPredicate (NakedAtomExpr (BoolAtom True))) = True
382isTrueExpr (AtomExprPredicate (NakedAtomExpr (BoolAtom True))) = True
270isTrueExpr _ = False
383isTrueExpr _ = False
271
384
272--determines if an atom expression is tautologically false
385--determines if an atom expression is tautologically false
273−isFalseExpr :: RestrictionPredicateExpr -> Bool
386+isFalseExpr :: RestrictionPredicateExprBase a -> Bool
274isFalseExpr (NotPredicate expr) = isTrueExpr expr
387isFalseExpr (NotPredicate expr) = isTrueExpr expr
275isFalseExpr (AtomExprPredicate (NakedAtomExpr (BoolAtom False))) = True
388isFalseExpr (AtomExprPredicate (NakedAtomExpr (BoolAtom False))) = True
276isFalseExpr _ = False
389isFalseExpr _ = False
277
390
278-- determine if the created relation can statically be determined to be empty
391-- determine if the created relation can statically be determined to be empty
279−isEmptyRelationExpr :: RelationalExpr -> Bool
392+isEmptyRelationExpr :: RelationalExprBase a -> Bool
280−isEmptyRelationExpr (MakeRelationFromExprs _ []) = True
393+isEmptyRelationExpr (MakeRelationFromExprs _ (TupleExprs _ [])) = True
281isEmptyRelationExpr (MakeStaticRelation _ tupSet) = null (asList tupSet)
394isEmptyRelationExpr (MakeStaticRelation _ tupSet) = null (asList tupSet)
282isEmptyRelationExpr (ExistingRelation rel) = rel == emptyRelationWithAttrs (attributes rel)
395isEmptyRelationExpr (ExistingRelation rel) = rel == emptyRelationWithAttrs (attributes rel)
283isEmptyRelationExpr _ = False
396isEmptyRelationExpr _ = False
284
397
285--transitive static variable optimization
398--transitive static variable optimization
286−replaceStaticAtomExprs :: RestrictionPredicateExpr -> M.Map AttributeName AtomExpr -> RestrictionPredicateExpr
399+replaceStaticAtomExprs :: GraphRefRestrictionPredicateExpr -> M.Map AttributeName GraphRefAtomExpr -> GraphRefRestrictionPredicateExpr
287replaceStaticAtomExprs predIn replaceMap = case predIn of
400replaceStaticAtomExprs predIn replaceMap = case predIn of
288 AttributeEqualityPredicate newAttrName (AttributeAtomExpr matchName) -> case M.lookup matchName replaceMap of
401 AttributeEqualityPredicate newAttrName (AttributeAtomExpr matchName) -> case M.lookup matchName replaceMap of
289 Nothing -> predIn
402 Nothing -> predIn
@@ -296,7 +409,7 @@ replaceStaticAtomExprs predIn replaceMap = case predIn of
296 RelationalExprPredicate{} -> predIn
409 RelationalExprPredicate{} -> predIn
297 AtomExprPredicate{} -> predIn
410 AtomExprPredicate{} -> predIn
298-- 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
411-- 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
300findStaticRestrictionPredicates (AttributeEqualityPredicate attrName atomExpr) =
413findStaticRestrictionPredicates (AttributeEqualityPredicate attrName atomExpr) =
301 case atomExpr of
414 case atomExpr of
302 val@NakedAtomExpr{} -> M.singleton attrName val
415 val@NakedAtomExpr{} -> M.singleton attrName val
@@ -320,60 +433,65 @@ isStaticAtomExpr FunctionAtomExpr{} = False
320isStaticAtomExpr RelationAtomExpr{} = False
433isStaticAtomExpr RelationAtomExpr{} = False
321
434
322--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
435--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
324applyStaticJoinElimination expr@(Project attrNameSet (Join exprA exprB)) = do
437applyStaticJoinElimination expr@(Project attrNameSet (Join exprA exprB)) = do
325− relState <- ask
438+ graph <- askGraph
326− eProjType <- typeForRelationalExpr expr
439+ case inSameTransaction exprA exprB of
327− eTypeA <- typeForRelationalExpr exprA
440+ -- the sub exprs are in different transactions or none at all, so we cannot extract inclusion dependencies across transaction boundaries
328− eTypeB <- typeForRelationalExpr exprB
441+ Nothing -> pure expr
329− case eProjType of
442+ Just marker -> do
330− Left err -> pure (Left err)
443+ commonContext <- case marker of
331− Right projType ->
444+ UncommittedContextMarker -> askContext
332− case eTypeA of
445+ TransactionMarker tid -> concreteDatabaseContext <$> lift (except (transactionForId tid graph))
333− Left err -> pure (Left err)
446+ let typeForExpr e = lift $ except $ runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr e)
334− Right typeA ->
447+ gfEnv = freshGraphRefRelationalExprEnv (Just commonContext) graph
335− case eTypeB of
448+
336− Left err -> pure (Left err)
449+ projType <- typeForExpr expr
337− Right typeB -> do
450+ typeA <- typeForExpr exprA
451+ typeB <- typeForExpr exprB
338− let matchesProjectionAttributes
452+ let matchesProjectionAttributes
339− | attrNames projType `S.isSubsetOf` attrNames typeA =
453+ | attrNames projType `S.isSubsetOf` attrNames typeA =
340− Just ((exprA, typeA), (exprB, typeB))
454+ Just ((exprA, typeA), (exprB, typeB))
341− | attrNames projType `S.isSubsetOf` attrNames typeB =
455+ | attrNames projType `S.isSubsetOf` attrNames typeB =
342− Just ((exprB, typeB), (exprA, typeA))
456+ Just ((exprB, typeB), (exprA, typeA))
343− | otherwise =
457+ | otherwise =
344− Nothing
458+ Nothing
345− attrNames = A.attributeNameSet . attributes
459+ attrNames = A.attributeNameSet . attributes
346− case matchesProjectionAttributes of
460+ case matchesProjectionAttributes of
347− Nothing -> -- this optimization does not apply
461+ Nothing -> -- this optimization does not apply
348− pure (Right expr)
462+ pure expr
349− Just ((joinedExpr, joinedType), (unjoinedExpr, _)) -> do
463+ Just ((joinedExpr, joinedType), (unjoinedExpr, _)) -> do
464+ --lookup transaction
350− --scan inclusion dependencies for a foreign key relationship
465+ --scan inclusion dependencies for a foreign key relationship
351− incDeps <- inclusionDependencies . stateElemsContext <$> ask
466+ let incDeps = inclusionDependencies commonContext
352− let fkConstraint = foldM isFkConstraint False incDeps
467+ fkConstraint = foldM isFkConstraint False incDeps
353− --search for matching fk constraint
468+ --search for matching fk constraint
354− isFkConstraint acc (InclusionDependency (Project subattrNames subrv) (Project _ superrv)) =
469+ isFkConstraint acc (InclusionDependency (Project subAttrNames subrv) (Project _ superrv)) = do
355− case runReader (evalAttributeNames subattrNames expr) relState of
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
356− Left _ -> pure acc
475+ Left _ -> pure acc
357− Right subAttrNameSet ->
476+ Right subAttrNameSet ->
358− pure (acc || (joinedExpr == subrv &&
477+ pure (acc || (joinedExpr == gfSubRv &&
359− unjoinedExpr == superrv &&
478+ unjoinedExpr == gfSuperRv &&
360− -- the fk attribute is one of the projection attributes
479+ -- the fk attribute is one of the projection attributes
361− A.attributeNamesContained subAttrNameSet (A.attributeNameSet (attributes joinedType))
480+ A.attributeNamesContained subAttrNameSet (A.attributeNameSet (attributes joinedType))
362 ))
481 ))
363− isFkConstraint acc _ = pure acc
482+ isFkConstraint acc _ = pure acc
364− case fkConstraint of
483+ case fkConstraint of
365− Right True -> --join elimination optimization applies
484+ Right True -> --join elimination optimization applies
366− applyStaticRelationalOptimization (Project attrNameSet joinedExpr)
485+ optimizeGraphRefRelationalExpr (Project attrNameSet joinedExpr)
367− Right False -> --join elimination optimization does not apply
486+ Right False -> --join elimination optimization does not apply
368− pure (Right expr)
487+ pure expr
369− Left err ->
488+ Left err -> throwError err
370− pure (Left err)
371
489
372−applyStaticJoinElimination expr = pure (Right expr)
490+applyStaticJoinElimination expr = pure expr
373
491
374--restriction collapse converts chained restrictions into (Restrict (And pred1 pred2 pred3...))
492--restriction collapse converts chained restrictions into (Restrict (And pred1 pred2 pred3...))
375 --this optimization should be fairly uncontroversial- performing a tuple scan once is cheaper than twice- parallelization can still take place
493 --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
377applyStaticRestrictionCollapse expr =
495applyStaticRestrictionCollapse expr =
378 case expr of
496 case expr of
379 MakeRelationFromExprs _ _ -> expr
497 MakeRelationFromExprs _ _ -> expr
@@ -410,14 +528,14 @@ applyStaticRestrictionCollapse expr =
410 andPreds = foldr (\(Restrict subpred _) acc -> AndPredicate acc subpred) firstPred (tail restrictions) in
528 andPreds = foldr (\(Restrict subpred _) acc -> AndPredicate acc subpred) firstPred (tail restrictions) in
411 Restrict andPreds optFinalExpr
529 Restrict andPreds optFinalExpr
412
530
413−sequentialRestrictions :: RelationalExpr -> [RelationalExpr]
531+sequentialRestrictions :: RelationalExprBase a -> [RelationalExprBase a]
414sequentialRestrictions expr@(Restrict _ subexpr) = expr:sequentialRestrictions subexpr
532sequentialRestrictions expr@(Restrict _ subexpr) = expr:sequentialRestrictions subexpr
415sequentialRestrictions _ = []
533sequentialRestrictions _ = []
416
534
417--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
535--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
418-- x{proj} where c1 -> (x where c1){proj} #project on fewer tuples
536-- x{proj} where c1 -> (x where c1){proj} #project on fewer tuples
419-- (x union y) where c -> (x where c) union (y where c) #with a selective restriction, fewer tuples will need to be joined
537-- (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
421applyStaticRestrictionPushdown expr = case expr of
539applyStaticRestrictionPushdown expr = case expr of
422 MakeRelationFromExprs _ _ -> expr
540 MakeRelationFromExprs _ _ -> expr
423 MakeStaticRelation _ _ -> expr
541 MakeStaticRelation _ _ -> expr
@@ -454,4 +572,6 @@ applyStaticRestrictionPushdown expr = case expr of
454 Extend n sub ->
572 Extend n sub ->
455 Extend n (applyStaticRestrictionPushdown sub)
573 Extend n (applyStaticRestrictionPushdown sub)
456
574
457−
575+-- no optimizations available
576+optimizeDatabaseContextIOExpr :: GraphRefDatabaseContextIOExpr -> GraphRefSOptDatabaseContextExprM GraphRefDatabaseContextIOExpr
577+optimizeDatabaseContextIOExpr = pure
src/lib/ProjectM36/TransGraphRelationalExpression.hs+137−122
@@ -4,13 +4,12 @@
4module ProjectM36.TransGraphRelationalExpression where
4module ProjectM36.TransGraphRelationalExpression where
5import ProjectM36.Base
5import ProjectM36.Base
6import ProjectM36.TransactionGraph
6import ProjectM36.TransactionGraph
7−import ProjectM36.Transaction
8−import ProjectM36.RelationalExpression
9import ProjectM36.Error
7import ProjectM36.Error
10−import ProjectM36.Tuple
11−import ProjectM36.AtomType
12import qualified Data.Map as M
8import qualified Data.Map as M
13import Control.Monad.Trans.Reader
9import Control.Monad.Trans.Reader
10+import Control.Monad.Trans.Class
11+import Control.Monad.Trans.Except
12+import Data.Functor.Identity
14import Data.Binary
13import Data.Binary
15
14
16-- | The TransGraphRelationalExpression is equivalent to a relational expression except that relation variables can reference points in the transaction graph (at previous points in time).
15-- | 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
30
29
31instance Binary TransGraphTupleExpr
30instance Binary TransGraphTupleExpr
32
31
32+type TransGraphTupleExprs = TupleExprsBase TransactionIdLookup
33+
34+instance Binary TransGraphTupleExprs
35+
33type TransGraphRestrictionPredicateExpr = RestrictionPredicateExprBase TransactionIdLookup
36type TransGraphRestrictionPredicateExpr = RestrictionPredicateExprBase TransactionIdLookup
34
37
35instance Binary TransGraphRestrictionPredicateExpr
38instance Binary TransGraphRestrictionPredicateExpr
@@ -40,143 +43,155 @@ instance Binary TransGraphAtomExpr
40
43
41type TransGraphAttributeExpr = AttributeExprBase TransactionIdLookup
44type TransGraphAttributeExpr = AttributeExprBase TransactionIdLookup
42
45
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+
43-- 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.
73-- 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
74+processTransGraphRelationalExpr :: TransGraphRelationalExpr -> TransGraphEvalMonad GraphRefRelationalExpr
45−evalTransGraphRelationalExpr (MakeRelationFromExprs mAttrExprs tupleExprs) graph = do
75+processTransGraphRelationalExpr (MakeRelationFromExprs mAttrExprs tupleExprs) = do
46− tupleExprs' <- mapM (evalTransGraphTupleExpr graph) tupleExprs
76+ tupleExprs' <- processTransGraphTupleExprs tupleExprs
47 case mAttrExprs of
77 case mAttrExprs of
48 Nothing -> pure (MakeRelationFromExprs Nothing tupleExprs')
78 Nothing -> pure (MakeRelationFromExprs Nothing tupleExprs')
49 Just attrExprs -> do
79 Just attrExprs -> do
50− attrExprs' <- mapM (evalTransGraphAttributeExpr graph) attrExprs
80+ attrExprs' <- mapM processTransGraphAttributeExpr attrExprs
51 pure (MakeRelationFromExprs (Just attrExprs') tupleExprs')
81 pure (MakeRelationFromExprs (Just attrExprs') tupleExprs')
52−evalTransGraphRelationalExpr (MakeStaticRelation attrs tupSet) _ = pure (MakeStaticRelation attrs tupSet)
82+processTransGraphRelationalExpr (MakeStaticRelation attrs tupSet) = pure (MakeStaticRelation attrs tupSet)
53−evalTransGraphRelationalExpr (ExistingRelation rel) _ = pure (ExistingRelation rel)
83+processTransGraphRelationalExpr (ExistingRelation rel) = pure (ExistingRelation rel)
54−evalTransGraphRelationalExpr (RelationVariable rvname transLookup) graph = do
84+processTransGraphRelationalExpr (RelationVariable rvname transLookup) =
55− trans <- lookupTransaction graph transLookup
85+ RelationVariable rvname <$> findTransId transLookup
56− rel <- runReader (evalRelationalExpr (RelationVariable rvname ())) (RelationalExprStateElems (concreteDatabaseContext trans))
57− pure (ExistingRelation rel)
58−evalTransGraphRelationalExpr (Project transAttrNames expr) graph = do
86+processTransGraphRelationalExpr (Project transAttrNames expr) =
59− expr' <- evalTransGraphRelationalExpr expr graph
87+ Project <$> processTransGraphAttributeNames transAttrNames <*> processTransGraphRelationalExpr expr
60− attrNames <- evalTransAttributeNames transAttrNames graph
88+processTransGraphRelationalExpr (Union exprA exprB) =
61− pure (Project attrNames expr')
89+ Union <$> processTransGraphRelationalExpr exprA <*> processTransGraphRelationalExpr exprB
62−evalTransGraphRelationalExpr (Union exprA exprB) graph = do
90+processTransGraphRelationalExpr (Join exprA exprB) =
63− exprA' <- evalTransGraphRelationalExpr exprA graph
91+ Join <$> processTransGraphRelationalExpr exprA <*> processTransGraphRelationalExpr exprB
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
92+processTransGraphRelationalExpr (Rename attrName1 attrName2 expr) =
71− let expr' = evalTransGraphRelationalExpr expr graph
72− Rename attrName1 attrName2 <$> expr'
93+ Rename attrName1 attrName2 <$> processTransGraphRelationalExpr expr
73−evalTransGraphRelationalExpr (Difference exprA exprB) graph = do
94+processTransGraphRelationalExpr (Difference exprA exprB) =
74− exprA' <- evalTransGraphRelationalExpr exprA graph
95+ Difference <$> processTransGraphRelationalExpr exprA <*> processTransGraphRelationalExpr exprB
75− exprB' <- evalTransGraphRelationalExpr exprB graph
76− pure (Difference exprA' exprB')
77−evalTransGraphRelationalExpr (Group transAttrNames attrName expr) graph = do
96+processTransGraphRelationalExpr (Group transAttrNames attrName expr) =
78− expr' <- evalTransGraphRelationalExpr expr graph
97+ Group <$>
79− attrNames <- evalTransAttributeNames transAttrNames graph
98+ processTransGraphAttributeNames transAttrNames <*>
80− pure (Group attrNames attrName expr')
99+ pure attrName <*>
81−evalTransGraphRelationalExpr (Ungroup attrName expr) graph = do
100+ processTransGraphRelationalExpr expr
82− let expr' = evalTransGraphRelationalExpr expr graph
101+processTransGraphRelationalExpr (Ungroup attrName expr) =
83− Ungroup attrName <$> expr'
102+ Ungroup attrName <$> processTransGraphRelationalExpr expr
84−evalTransGraphRelationalExpr (Restrict predicateExpr expr) graph = do
103+processTransGraphRelationalExpr (Restrict predicateExpr expr) =
85− expr' <- evalTransGraphRelationalExpr expr graph
86− predicateExpr' <- evalTransGraphRestrictionPredicateExpr predicateExpr graph
104+ Restrict <$> evalTransGraphRestrictionPredicateExpr predicateExpr <*>
87− pure (Restrict predicateExpr' expr')
105+ processTransGraphRelationalExpr expr
88−evalTransGraphRelationalExpr (Equals exprA exprB) graph = do
106+processTransGraphRelationalExpr (Equals exprA exprB) = do
89− exprA' <- evalTransGraphRelationalExpr exprA graph
107+ exprA' <- processTransGraphRelationalExpr exprA
90− exprB' <- evalTransGraphRelationalExpr exprB graph
108+ exprB' <- processTransGraphRelationalExpr exprB
91 pure (Equals exprA' exprB')
109 pure (Equals exprA' exprB')
92−evalTransGraphRelationalExpr (NotEquals exprA exprB) graph = do
110+processTransGraphRelationalExpr (NotEquals exprA exprB) = do
93− exprA' <- evalTransGraphRelationalExpr exprA graph
111+ exprA' <- processTransGraphRelationalExpr exprA
94− exprB' <- evalTransGraphRelationalExpr exprB graph
112+ exprB' <- processTransGraphRelationalExpr exprB
95 pure (NotEquals exprA' exprB')
113 pure (NotEquals exprA' exprB')
96−evalTransGraphRelationalExpr (Extend extendExpr expr) graph = do
114+processTransGraphRelationalExpr (Extend extendExpr expr) = do
97− extendExpr' <- evalTransGraphExtendTupleExpr extendExpr graph
115+ extendExpr' <- processTransGraphExtendTupleExpr extendExpr
98− expr' <- evalTransGraphRelationalExpr expr graph
116+ expr' <- processTransGraphRelationalExpr expr
99 pure (Extend extendExpr' expr')
117 pure (Extend extendExpr' expr')
100−evalTransGraphRelationalExpr (With views expr) graph = do
118+processTransGraphRelationalExpr (With views expr) = do
101− evaldViews <- mapM (\(vname, vexpr) -> do
119+ evaldViews <- mapM (\(wnexpr, vexpr) -> do
102− vexpr' <- evalTransGraphRelationalExpr vexpr graph
120+ wnexpr' <- processTransGraphWithNameExpr wnexpr
121+ vexpr' <- processTransGraphRelationalExpr vexpr
103− pure (vname, vexpr')
122+ pure (wnexpr', vexpr')
104 ) views
123 ) views
105− expr' <- evalTransGraphRelationalExpr expr graph
124+ expr' <- processTransGraphRelationalExpr expr
106 pure (With evaldViews expr')
125 pure (With evaldViews expr')
107−
126+
108−evalTransGraphTupleExpr :: TransactionGraph -> TransGraphTupleExpr -> Either RelationalError TupleExpr
127+processTransGraphTupleExprs :: TransGraphTupleExprs -> TransGraphEvalMonad GraphRefTupleExprs
128+processTransGraphTupleExprs (TupleExprs marker texprs) =
129+ TupleExprs <$> findTransId marker <*> mapM processTransGraphTupleExpr texprs
130+
131+processTransGraphTupleExpr :: TransGraphTupleExpr -> TransGraphEvalMonad GraphRefTupleExpr
109−evalTransGraphTupleExpr graph (TupleExpr attrMap) = do
132+processTransGraphTupleExpr (TupleExpr attrMap) = do
110 let attrAssoc = mapM (\(attrName, atomExpr) -> do
133 let attrAssoc = mapM (\(attrName, atomExpr) -> do
111− aExpr <- evalTransGraphAtomExpr graph atomExpr
134+ aExpr <- processTransGraphAtomExpr atomExpr
112 pure (attrName, aExpr)
135 pure (attrName, aExpr)
113 ) (M.toList attrMap)
136 ) (M.toList attrMap)
114 TupleExpr . M.fromList <$> attrAssoc
137 TupleExpr . M.fromList <$> attrAssoc
115
138
116−evalTransGraphAtomExpr :: TransactionGraph -> TransGraphAtomExpr -> Either RelationalError AtomExpr
139+processTransGraphAtomExpr :: TransGraphAtomExpr -> TransGraphEvalMonad GraphRefAtomExpr
117−evalTransGraphAtomExpr _ (AttributeAtomExpr aname) = pure $ AttributeAtomExpr aname
140+processTransGraphAtomExpr (AttributeAtomExpr aname) = pure $ AttributeAtomExpr aname
118−evalTransGraphAtomExpr _ (NakedAtomExpr atom) = pure $ NakedAtomExpr atom
141+processTransGraphAtomExpr (NakedAtomExpr atom) = pure $ NakedAtomExpr atom
119−evalTransGraphAtomExpr graph (FunctionAtomExpr funcName args tLookup) = do
142+processTransGraphAtomExpr (FunctionAtomExpr funcName args tLookup) =
120− trans <- lookupTransaction graph tLookup
143+ FunctionAtomExpr funcName <$> mapM processTransGraphAtomExpr args <*> findTransId 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
144+processTransGraphAtomExpr (RelationAtomExpr expr) =
126− let expr' = evalTransGraphRelationalExpr expr graph
127− RelationAtomExpr <$> expr'
145+ RelationAtomExpr <$> processTransGraphRelationalExpr expr
128−evalTransGraphAtomExpr graph (ConstructedAtomExpr dConsName args tLookup) = do
146+processTransGraphAtomExpr (ConstructedAtomExpr dConsName args tLookup) =
129− trans <- lookupTransaction graph tLookup
147+ ConstructedAtomExpr dConsName <$> mapM processTransGraphAtomExpr args <*> findTransId 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
148+evalTransGraphRestrictionPredicateExpr :: TransGraphRestrictionPredicateExpr -> TransGraphEvalMonad GraphRefRestrictionPredicateExpr
135−evalTransGraphRestrictionPredicateExpr TruePredicate _ = pure TruePredicate
149+evalTransGraphRestrictionPredicateExpr TruePredicate = pure TruePredicate
136−evalTransGraphRestrictionPredicateExpr (AndPredicate exprA exprB) graph = do
150+evalTransGraphRestrictionPredicateExpr (AndPredicate exprA exprB) = do
137− exprA' <- evalTransGraphRestrictionPredicateExpr exprA graph
151+ exprA' <- evalTransGraphRestrictionPredicateExpr exprA
138− exprB' <- evalTransGraphRestrictionPredicateExpr exprB graph
152+ exprB' <- evalTransGraphRestrictionPredicateExpr exprB
139 pure (AndPredicate exprA' exprB')
153 pure (AndPredicate exprA' exprB')
140−evalTransGraphRestrictionPredicateExpr (OrPredicate exprA exprB) graph = do
154+evalTransGraphRestrictionPredicateExpr (OrPredicate exprA exprB) = do
141− exprA' <- evalTransGraphRestrictionPredicateExpr exprA graph
155+ exprA' <- evalTransGraphRestrictionPredicateExpr exprA
142− exprB' <- evalTransGraphRestrictionPredicateExpr exprB graph
156+ exprB' <- evalTransGraphRestrictionPredicateExpr exprB
143 pure (OrPredicate exprA' exprB')
157 pure (OrPredicate exprA' exprB')
144−evalTransGraphRestrictionPredicateExpr (NotPredicate expr) graph = do
158+evalTransGraphRestrictionPredicateExpr (NotPredicate expr) = do
145− let expr' = evalTransGraphRestrictionPredicateExpr expr graph
159+ let expr' = evalTransGraphRestrictionPredicateExpr expr
146 NotPredicate <$> expr'
160 NotPredicate <$> expr'
147−evalTransGraphRestrictionPredicateExpr (RelationalExprPredicate expr) graph = do
161+evalTransGraphRestrictionPredicateExpr (RelationalExprPredicate expr) = do
148− let expr' = evalTransGraphRelationalExpr expr graph
162+ let expr' = processTransGraphRelationalExpr expr
149 RelationalExprPredicate <$> expr'
163 RelationalExprPredicate <$> expr'
150−evalTransGraphRestrictionPredicateExpr (AtomExprPredicate expr) graph = do
164+evalTransGraphRestrictionPredicateExpr (AtomExprPredicate expr) = do
151− let expr' = evalTransGraphAtomExpr graph expr
165+ let expr' = processTransGraphAtomExpr expr
152 AtomExprPredicate <$> expr'
166 AtomExprPredicate <$> expr'
153−evalTransGraphRestrictionPredicateExpr (AttributeEqualityPredicate attrName expr) graph = do
167+evalTransGraphRestrictionPredicateExpr (AttributeEqualityPredicate attrName expr) = do
154− let expr' = evalTransGraphAtomExpr graph expr
168+ let expr' = processTransGraphAtomExpr expr
155 AttributeEqualityPredicate attrName <$> expr'
169 AttributeEqualityPredicate attrName <$> expr'
156
170
157−evalTransGraphExtendTupleExpr :: TransGraphExtendTupleExpr -> TransactionGraph -> Either RelationalError ExtendTupleExpr
171+processTransGraphExtendTupleExpr :: TransGraphExtendTupleExpr -> TransGraphEvalMonad GraphRefExtendTupleExpr
158−evalTransGraphExtendTupleExpr (AttributeExtendTupleExpr attrName expr) graph = do
172+processTransGraphExtendTupleExpr (AttributeExtendTupleExpr attrName expr) =
159− let expr' = evalTransGraphAtomExpr graph expr
160− AttributeExtendTupleExpr attrName <$> expr'
173+ AttributeExtendTupleExpr attrName <$> processTransGraphAtomExpr expr
161−
174+
162−evalTransGraphAttributeExpr :: TransactionGraph -> TransGraphAttributeExpr -> Either RelationalError AttributeExpr
175+processTransGraphAttributeExpr :: TransGraphAttributeExpr -> TransGraphEvalMonad GraphRefAttributeExpr
163−evalTransGraphAttributeExpr graph (AttributeAndTypeNameExpr attrName tCons tLookup) = do
176+processTransGraphAttributeExpr (AttributeAndTypeNameExpr attrName tCons tLookup) =
164− trans <- lookupTransaction graph tLookup
177+ AttributeAndTypeNameExpr attrName tCons <$> findTransId tLookup
165− aType <- atomTypeForTypeConstructor tCons (typeConstructorMapping (concreteDatabaseContext trans)) M.empty
166− pure (NakedAttributeExpr (Attribute attrName aType))
167−evalTransGraphAttributeExpr _ (NakedAttributeExpr attr) = pure (NakedAttributeExpr attr)
178+processTransGraphAttributeExpr (NakedAttributeExpr attr) = pure (NakedAttributeExpr attr)
168−
179+
169−evalTransAttributeNames :: TransGraphAttributeNames -> TransactionGraph -> Either RelationalError AttributeNames
180+processTransGraphAttributeNames :: TransGraphAttributeNames -> TransGraphEvalMonad GraphRefAttributeNames
170−evalTransAttributeNames (AttributeNames names) _ = Right (AttributeNames names)
181+processTransGraphAttributeNames (AttributeNames names) = pure (AttributeNames names)
171−evalTransAttributeNames (InvertedAttributeNames names) _ = Right (InvertedAttributeNames names)
182+processTransGraphAttributeNames (InvertedAttributeNames names) = pure (InvertedAttributeNames names)
172−evalTransAttributeNames (UnionAttributeNames namesA namesB) graph = do
183+processTransGraphAttributeNames (UnionAttributeNames namesA namesB) = do
173− nA <- evalTransAttributeNames namesA graph
184+ nA <- processTransGraphAttributeNames namesA
174− nB <- evalTransAttributeNames namesB graph
185+ nB <- processTransGraphAttributeNames namesB
175− Right (UnionAttributeNames nA nB)
186+ pure (UnionAttributeNames nA nB)
176−evalTransAttributeNames (IntersectAttributeNames namesA namesB) graph = do
187+processTransGraphAttributeNames (IntersectAttributeNames namesA namesB) = do
177− nA <- evalTransAttributeNames namesA graph
188+ nA <- processTransGraphAttributeNames namesA
178− nB <- evalTransAttributeNames namesB graph
189+ nB <- processTransGraphAttributeNames namesB
179− Right (IntersectAttributeNames nA nB)
190+ pure (IntersectAttributeNames nA nB)
180−evalTransAttributeNames (RelationalExprAttributeNames expr) graph = do
191+processTransGraphAttributeNames (RelationalExprAttributeNames expr) = do
181− evaldExpr <- evalTransGraphRelationalExpr expr graph
192+ evaldExpr <- processTransGraphRelationalExpr expr
182− Right (RelationalExprAttributeNames evaldExpr)
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
3import qualified Data.Set as S
3import qualified Data.Set as S
4import qualified Data.UUID as U
4import qualified Data.UUID as U
5import Data.Time.Clock
5import Data.Time.Clock
6+import qualified Data.List.NonEmpty as NE
6
7
7−transactionTimestamp :: Transaction -> UTCTime
8+parentIds :: Transaction -> S.Set TransactionId
8−transactionTimestamp (Transaction _ (TransactionInfo _ _ stamp) _) = stamp
9+parentIds (Transaction _ tinfo _) = S.fromList (NE.toList (parents tinfo))
9−transactionTimestamp (Transaction _ (MergeTransactionInfo _ _ _ stamp) _) = stamp
10
10
11−transactionParentIds :: Transaction -> S.Set TransactionId
11+rootParent :: TransactionParents
12−transactionParentIds (Transaction _ (TransactionInfo pId _ _) _) = S.singleton pId
12+rootParent = singleParent U.nil
13−transactionParentIds (Transaction _ (MergeTransactionInfo pId1 pId2 _ _) _) = S.fromList [pId1, pId2]
14
13
15−transactionChildIds :: Transaction -> S.Set TransactionId
14+singleParent :: TransactionId -> TransactionParents
16−transactionChildIds (Transaction _ (TransactionInfo _ children _) _) = children
15+singleParent tid = tid NE.:| []
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'
23
16
24-- | 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.
17-- | 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.
25filterTransactionInfoTransactions :: S.Set TransactionId -> TransactionInfo -> TransactionInfo
18filterTransactionInfoTransactions :: S.Set TransactionId -> TransactionInfo -> TransactionInfo
26−filterTransactionInfoTransactions filterIds (TransactionInfo parentId childIds stamp) = TransactionInfo (filterParent parentId filterIds) (S.intersection childIds filterIds) stamp
19+filterTransactionInfoTransactions filterIds tinfo =
27−filterTransactionInfoTransactions filterIds (MergeTransactionInfo parentIdA parentIdB childIds stamp) = MergeTransactionInfo (filterParent parentIdA filterIds) (filterParent parentIdB filterIds) (S.intersection childIds filterIds) stamp
20+ TransactionInfo { parents = case
21+ NE.filter (`S.member` filterIds) (parents tinfo) of
22+ [] -> rootParent
23+ xs -> NE.fromList xs,
24+ stamp = stamp tinfo }
28
25
29filterParent :: TransactionId -> S.Set TransactionId -> TransactionId
26filterParent :: TransactionId -> S.Set TransactionId -> TransactionId
30filterParent parentId validIds = if S.member parentId validIds then parentId else U.nil
27filterParent parentId validIds = if S.member parentId validIds then parentId else U.nil
@@ -44,3 +41,9 @@ schemas (Transaction _ _ schemas') = schemas'
44-- | Returns all subschemas which are isomorphic or sub-isomorphic to the concrete schema.
41-- | Returns all subschemas which are isomorphic or sub-isomorphic to the concrete schema.
45subschemas :: Transaction -> Subschemas
42subschemas :: Transaction -> Subschemas
46subschemas (Transaction _ _ (Schemas _ sschemas)) = sschemas
43subschemas (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)
9import qualified Data.Map as M
9import qualified Data.Map as M
10import qualified Data.HashSet as HS
10import qualified Data.HashSet as HS
11import qualified Data.Binary as B
11import qualified Data.Binary as B
12−--import qualified Data.ByteString as BS
13import System.FilePath
12import System.FilePath
14import System.Directory
13import System.Directory
15import qualified Data.Text as T
14import qualified Data.Text as T
@@ -17,10 +16,6 @@ import Control.Monad
17import ProjectM36.ScriptSession
16import ProjectM36.ScriptSession
18import ProjectM36.AtomFunctions.Basic (precompiledAtomFunctions)
17import ProjectM36.AtomFunctions.Basic (precompiledAtomFunctions)
19
18
20−import Codec.Compression.GZip
21−import qualified Data.ByteString as BS
22−import qualified Data.ByteString.Lazy as BSL
23−
24#ifdef PM36_HASKELL_SCRIPTING
19#ifdef PM36_HASKELL_SCRIPTING
25import GHC
20import GHC
26import Control.Exception
21import Control.Exception
@@ -41,8 +36,8 @@ transactionDir dbdir transId = dbdir </> show transId
41transactionInfoPath :: FilePath -> FilePath
36transactionInfoPath :: FilePath -> FilePath
42transactionInfoPath transdir = transdir </> "info"
37transactionInfoPath transdir = transdir </> "info"
43
38
44−relvarsDir :: FilePath -> FilePath
39+relvarsPath :: FilePath -> FilePath
45−relvarsDir transdir = transdir </> "relvars"
40+relvarsPath transdir = transdir </> "relvars"
46
41
47incDepsDir :: FilePath -> FilePath
42incDepsDir :: FilePath -> FilePath
48incDepsDir transdir = transdir </> "incdeps"
43incDepsDir transdir = transdir </> "incdeps"
@@ -90,7 +85,7 @@ writeTransaction sync dbdir trans = do
90 transDirExists <- doesDirectoryExist finalTransDir
85 transDirExists <- doesDirectoryExist finalTransDir
91 unless transDirExists $ do
86 unless transDirExists $ do
92 --create sub directories
87 --create sub directories
93− mapM_ createDirectory [tempTransDir, relvarsDir tempTransDir, incDepsDir tempTransDir, dbcFuncsDir tempTransDir]
88+ mapM_ createDirectory [tempTransDir, incDepsDir tempTransDir, dbcFuncsDir tempTransDir]
94 writeRelVars sync tempTransDir (relationVariables context)
89 writeRelVars sync tempTransDir (relationVariables context)
95 writeIncDeps sync tempTransDir (inclusionDependencies context)
90 writeIncDeps sync tempTransDir (inclusionDependencies context)
96 writeAtomFuncs sync tempTransDir (atomFunctions context)
91 writeAtomFuncs sync tempTransDir (atomFunctions context)
@@ -100,23 +95,15 @@ writeTransaction sync dbdir trans = do
100 B.encodeFile (transactionInfoPath tempTransDir) (transactionInfo trans)
95 B.encodeFile (transactionInfoPath tempTransDir) (transactionInfo trans)
101 --move the temp directory to final location
96 --move the temp directory to final location
102 renameSync sync tempTransDir finalTransDir
97 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
111
98
112−readRelVars :: FilePath -> IO (M.Map RelVarName Relation)
99+writeRelVars :: DiskSync -> FilePath -> RelationVariables -> IO ()
113−readRelVars transDir = do
100+writeRelVars sync transDir relvars = do
114− let relvarsPath = relvarsDir transDir
101+ let path = relvarsPath transDir
115− relvarNames <- getDirectoryNames relvarsPath
102+ writeBSFileSync sync path (B.encode relvars)
116− let relvars = mapM (\name -> do
103+
117− rel <- B.decode . decompress . BSL.fromStrict <$> BS.readFile (relvarsPath </> name)
104+readRelVars :: FilePath -> IO RelationVariables
118− return (T.pack name, rel)) relvarNames
105+readRelVars transDir =
119− M.fromList <$> relvars
106+ B.decodeFile (relvarsPath transDir)
120
107
121writeAtomFuncs :: DiskSync -> FilePath -> AtomFunctions -> IO ()
108writeAtomFuncs :: DiskSync -> FilePath -> AtomFunctions -> IO ()
122writeAtomFuncs sync transDir funcs = do
109writeAtomFuncs 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 #-}
2module ProjectM36.TransactionGraph where
2module ProjectM36.TransactionGraph where
3import ProjectM36.Base
3import ProjectM36.Base
4import ProjectM36.Error
4import ProjectM36.Error
5import ProjectM36.Transaction
5import ProjectM36.Transaction
6+import ProjectM36.TransactionInfo as TI
6import ProjectM36.Relation
7import ProjectM36.Relation
7import ProjectM36.TupleSet
8import ProjectM36.TupleSet
8import ProjectM36.Tuple
9import ProjectM36.Tuple
9import ProjectM36.RelationalExpression
10import ProjectM36.RelationalExpression
10import ProjectM36.TransactionGraph.Merge
11import ProjectM36.TransactionGraph.Merge
11import qualified ProjectM36.DisconnectedTransaction as Discon
12import qualified ProjectM36.DisconnectedTransaction as Discon
13+import qualified ProjectM36.Attribute as A
12
14
15+import Control.Monad.Except hiding (join)
16+import Control.Monad.Reader hiding (join)
13import qualified Data.Vector as V
17import qualified Data.Vector as V
14−import qualified ProjectM36.Attribute as A
15import qualified Data.UUID as U
18import qualified Data.UUID as U
16import qualified Data.Set as S
19import qualified Data.Set as S
17import qualified Data.Map as M
20import qualified Data.Map as M
18−import Control.Monad
21+import qualified Data.List.NonEmpty as NE
19import Data.Time.Clock
22import Data.Time.Clock
20import qualified Data.Text as T
23import qualified Data.Text as T
21import GHC.Generics
24import GHC.Generics
@@ -26,12 +29,7 @@ import Data.Monoid
26#endif
29#endif
27import Control.Arrow
30import Control.Arrow
28import Data.Maybe
31import Data.Maybe
29−
32+import Data.UUID.V4
30−{-
31−--import Debug.Trace
32−import Control.Monad.Reader
33−import qualified ProjectM36.DisconnectedTransaction as D
34−-}
35
33
36-- | Record a lookup for a specific transaction in the graph.
34-- | Record a lookup for a specific transaction in the graph.
37data TransactionIdLookup = TransactionIdLookup TransactionId |
35data TransactionIdLookup = TransactionIdLookup TransactionId |
@@ -64,13 +62,21 @@ data ROTransactionGraphOperator = ShowGraph
64 deriving Show
62 deriving Show
65
63
66bootstrapTransactionGraph :: UTCTime -> TransactionId -> DatabaseContext -> TransactionGraph
64bootstrapTransactionGraph :: UTCTime -> TransactionId -> DatabaseContext -> TransactionGraph
67−bootstrapTransactionGraph stamp freshId context = TransactionGraph bootstrapHeads bootstrapTransactions
65+bootstrapTransactionGraph stamp' freshId context = TransactionGraph bootstrapHeads bootstrapTransactions
68 where
66 where
69 bootstrapHeads = M.singleton "master" freshTransaction
67 bootstrapHeads = M.singleton "master" freshTransaction
70 newSchemas = Schemas context M.empty
68 newSchemas = Schemas context M.empty
71− freshTransaction = Transaction freshId (TransactionInfo U.nil S.empty stamp) newSchemas
69+ freshTransaction = fresh freshId stamp' newSchemas
72 bootstrapTransactions = S.singleton freshTransaction
70 bootstrapTransactions = S.singleton freshTransaction
73
71
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+
74emptyTransactionGraph :: TransactionGraph
80emptyTransactionGraph :: TransactionGraph
75emptyTransactionGraph = TransactionGraph M.empty S.empty
81emptyTransactionGraph = TransactionGraph M.empty S.empty
76
82
@@ -88,75 +94,76 @@ headNameForTransaction transaction (TransactionGraph heads _) = if M.null matchi
88 where
94 where
89 matchingTrans = M.filter (transaction ==) heads
95 matchingTrans = M.filter (transaction ==) heads
90
96
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−
102transactionsForIds :: S.Set TransactionId -> TransactionGraph -> Either RelationalError (S.Set Transaction)
97transactionsForIds :: S.Set TransactionId -> TransactionGraph -> Either RelationalError (S.Set Transaction)
103transactionsForIds idSet graph =
98transactionsForIds idSet graph =
104 S.fromList <$> forM (S.toList idSet) (`transactionForId` graph)
99 S.fromList <$> forM (S.toList idSet) (`transactionForId` graph)
105
100
101+-- | A root transaction terminates a graph and has no parents.
106−isRootTransaction :: Transaction -> TransactionGraph -> Bool
102+isRootTransaction :: Transaction -> Bool
107−isRootTransaction (Transaction _ (TransactionInfo pId _ _) _) _ = U.null pId
103+isRootTransaction trans = parentIds trans == S.singleton U.nil
108−isRootTransaction (Transaction _ MergeTransactionInfo{} _) _ = False
104+
105+rootTransactions :: TransactionGraph -> S.Set Transaction
106+rootTransactions graph = S.filter isRootTransaction (transactionsForGraph graph)
109
107
110-- the first transaction has no parent - all other do have parents- merges have two parents
108-- the first transaction has no parent - all other do have parents- merges have two parents
111parentTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
109parentTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
112−parentTransactions (Transaction _ (TransactionInfo pId _ _) _) graph =
110+parentTransactions trans = transactionsForIds (parentIds trans)
113− S.singleton <$> transactionForId pId graph
114−
115−
116−parentTransactions (Transaction _ (MergeTransactionInfo pId1 pId2 _ _) _ ) graph = transactionsForIds (S.fromList [pId1, pId2]) graph
117−
118
111
119childTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
112childTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
120−childTransactions (Transaction _ (TransactionInfo _ children _) _) = transactionsForIds children
113+childTransactions trans graph = transactionsForIds childIds graph
121−childTransactions (Transaction _ (MergeTransactionInfo _ _ children _) _) = transactionsForIds children
114+ where
115+ childIds = S.map transactionId (S.filter filt (transactionsForGraph graph))
116+ filt trans' = S.member (transactionId trans) (parentIds trans')
122
117
123-- create a new commit and add it to the heads
118-- create a new commit and add it to the heads
124-- 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)
119-- 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)
125addBranch :: UTCTime -> TransactionId -> HeadName -> TransactionId -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
120addBranch :: 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
127 parentTrans <- transactionForId branchPointId graph
122 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)
129 addTransactionToGraph newBranchName newTrans graph
124 addTransactionToGraph newBranchName newTrans graph
130
125
131--adds a disconnected transaction to a transaction graph at some head
126--adds a disconnected transaction to a transaction graph at some head
132addDisconnectedTransaction :: UTCTime -> TransactionId -> HeadName -> DisconnectedTransaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
127addDisconnectedTransaction :: 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')
128+addDisconnectedTransaction stamp' newId headName (DisconnectedTransaction parentId schemas' _) = addTransactionToGraph headName newTrans
134−
129+ where
130+ newTrans = Transaction newId (TI.singleParent parentId stamp') schemas'
135
131
136addTransactionToGraph :: HeadName -> Transaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
132addTransactionToGraph :: HeadName -> Transaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
137addTransactionToGraph headName newTrans graph = do
133addTransactionToGraph headName newTrans graph = do
138− let parentIds = transactionParentIds newTrans
134+ let parentIds' = parentIds newTrans
139− childIds = transactionChildIds newTrans
140 newId = transactionId newTrans
135 newId = transactionId newTrans
141 validateIds ids = mapM (`transactionForId` graph) (S.toList ids)
136 validateIds ids = mapM (`transactionForId` graph) (S.toList ids)
142− addChildTransaction trans = transactionSetChildren trans (S.insert newId (transactionChildIds trans))
137+ childTs <- childTransactions newTrans graph
143 --validate that the parent transactions are in the graph
138 --validate that the parent transactions are in the graph
144− _ <- validateIds parentIds
139+ _ <- validateIds parentIds'
145− when (S.size parentIds < 1) (Left $ NewTransactionMissingParentError newId)
140+ when (S.size parentIds' < 1) (Left $ NewTransactionMissingParentError newId)
146 --if the headName already exists, ensure that it refers to a parent
141 --if the headName already exists, ensure that it refers to a parent
147 case transactionForHead headName graph of
142 case transactionForHead headName graph of
148 Nothing -> pure () -- any headName is OK
143 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))
150 --validate that the transaction has no children
145 --validate that the transaction has no children
151− unless (S.null childIds) (Left $ NewTransactionMayNotHaveChildrenError newId)
146+ unless (S.null childTs) (Left $ NewTransactionMayNotHaveChildrenError newId)
152 --validate that the trasaction's id is unique
147 --validate that the trasaction's id is unique
153 when (isRight (transactionForId newId graph)) (Left (TransactionIdInUseError newId))
148 when (isRight (transactionForId newId graph)) (Left (TransactionIdInUseError newId))
154− --update the parent transactions to point to the new transaction
149+ --replace all references to UncommittedTransactionMarker to new transaction id
155− parents <- mapM (`transactionForId` graph) (S.toList parentIds)
150+ let newTrans' = newTransUncommittedReplace newTrans
156− let updatedParents = S.map addChildTransaction (S.fromList parents)
157− updatedTransSet = S.insert newTrans (S.union updatedParents (transactionsForGraph graph))
151+ updatedTransSet = S.insert newTrans' (transactionsForGraph graph)
158− updatedHeads = M.insert headName newTrans (transactionHeadsForGraph graph)
152+ updatedHeads = M.insert headName newTrans' (transactionHeadsForGraph graph)
159− pure (newTrans, TransactionGraph updatedHeads updatedTransSet)
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+
160
167
161validateGraph :: TransactionGraph -> Maybe [RelationalError]
168validateGraph :: TransactionGraph -> Maybe [RelationalError]
162validateGraph graph@(TransactionGraph _ transSet) = do
169validateGraph graph@(TransactionGraph _ transSet) = do
@@ -228,7 +235,7 @@ evalGraphOp _ _ discon graph (WalkBackToTime backTime) = do
228 case transactionForId jumpDest graph of
235 case transactionForId jumpDest graph of
229 Left err -> Left err
236 Left err -> Left err
230 Right trans -> do
237 Right trans -> do
231− let disconnectedTrans = DisconnectedTransaction (transactionId trans) (schemas trans) False
238+ let disconnectedTrans = Discon.freshTransaction (transactionId trans) (schemas trans)
232 Right (disconnectedTrans, graph)
239 Right (disconnectedTrans, graph)
233
240
234-- add new head pointing to branchPoint
241-- add new head pointing to branchPoint
@@ -246,15 +253,15 @@ evalGraphOp newId discon@(DisconnectedTransaction parentId disconContext) graph
246
253
247-- create a new commit and add it to the heads
254-- create a new commit and add it to the heads
248-- 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)
255-- 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
256+evalGraphOp stamp' newId (DisconnectedTransaction parentId schemas' _) graph (Branch newBranchName) = do
250− let newDiscon = DisconnectedTransaction newId schemas' False
257+ let newDiscon = Discon.freshTransaction newId schemas'
251− case addBranch stamp newId newBranchName parentId graph of
258+ case addBranch stamp' newId newBranchName parentId graph of
252 Left err -> Left err
259 Left err -> Left err
253 Right (_, newGraph) -> Right (newDiscon, newGraph)
260 Right (_, newGraph) -> Right (newDiscon, newGraph)
254
261
255-- add the disconnected transaction to the graph
262-- add the disconnected transaction to the graph
256-- affects graph and disconnectedtransaction- the new disconnectedtransaction's parent is the freshly committed transaction
263-- 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
258 Left err -> Left err
265 Left err -> Left err
259 Right parentTransaction -> case headNameForTransaction parentTransaction graph of
266 Right parentTransaction -> case headNameForTransaction parentTransaction graph of
260 Nothing -> Left $ TransactionIsNotAHeadError parentId
267 Nothing -> Left $ TransactionIsNotAHeadError parentId
@@ -262,17 +269,20 @@ evalGraphOp stamp newTransId discon@(DisconnectedTransaction parentId schemas' _
262 Left err-> Left err
269 Left err-> Left err
263 Right (_, updatedGraph) -> Right (newDisconnectedTrans, updatedGraph)
270 Right (_, updatedGraph) -> Right (newDisconnectedTrans, updatedGraph)
264 where
271 where
265− newDisconnectedTrans = DisconnectedTransaction newTransId schemas' False
272+ newDisconnectedTrans = Discon.freshTransaction newTransId schemas'
266− maybeUpdatedGraph = addDisconnectedTransaction stamp newTransId headName discon graph
273+ maybeUpdatedGraph = addDisconnectedTransaction stamp' newTransId headName discon graph
267
274
268-- refresh the disconnected transaction, return the same graph
275-- refresh the disconnected transaction, return the same graph
269evalGraphOp _ _ (DisconnectedTransaction parentId _ _) graph Rollback = case transactionForId parentId graph of
276evalGraphOp _ _ (DisconnectedTransaction parentId _ _) graph Rollback = case transactionForId parentId graph of
270 Left err -> Left err
277 Left err -> Left err
271 Right parentTransaction -> Right (newDiscon, graph)
278 Right parentTransaction -> Right (newDiscon, graph)
272 where
279 where
273− newDiscon = DisconnectedTransaction parentId (schemas parentTransaction) False
280+ newDiscon = Discon.freshTransaction parentId (schemas parentTransaction)
274
281
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
276
286
277evalGraphOp _ _ discon graph@(TransactionGraph graphHeads transSet) (DeleteBranch branchName) = case transactionForHead branchName graph of
287evalGraphOp _ _ discon graph@(TransactionGraph graphHeads transSet) (DeleteBranch branchName) = case transactionForHead branchName graph of
278 Nothing -> Left (NoSuchHeadNameError branchName)
288 Nothing -> Left (NoSuchHeadNameError branchName)
@@ -294,7 +304,7 @@ graphAsRelation (DisconnectedTransaction parentId _ _) graph@(TransactionGraph _
294 tupleGenerator transaction = case transactionParentsRelation transaction graph of
304 tupleGenerator transaction = case transactionParentsRelation transaction graph of
295 Left err -> Left err
305 Left err -> Left err
296 Right parentTransRel -> Right [TextAtom $ T.pack $ show (transactionId transaction),
306 Right parentTransRel -> Right [TextAtom $ T.pack $ show (transactionId transaction),
297− DateTimeAtom (transactionTimestamp transaction),
307+ DateTimeAtom (timestamp transaction),
298 RelationAtom parentTransRel,
308 RelationAtom parentTransRel,
299 BoolAtom $ parentId == transactionId transaction,
309 BoolAtom $ parentId == transactionId transaction,
300 TextAtom $ fromMaybe "" (headNameForTransaction transaction graph)
310 TextAtom $ fromMaybe "" (headNameForTransaction transaction graph)
@@ -302,7 +312,7 @@ graphAsRelation (DisconnectedTransaction parentId _ _) graph@(TransactionGraph _
302
312
303transactionParentsRelation :: Transaction -> TransactionGraph -> Either RelationalError Relation
313transactionParentsRelation :: Transaction -> TransactionGraph -> Either RelationalError Relation
304transactionParentsRelation trans graph =
314transactionParentsRelation trans graph =
305− if isRootTransaction trans graph then
315+ if isRootTransaction trans then
306 mkRelation attrs emptyTupleSet
316 mkRelation attrs emptyTupleSet
307 else do
317 else do
308 parentTransSet <- parentTransactions trans graph
318 parentTransSet <- parentTransactions trans graph
@@ -321,39 +331,45 @@ evalROGraphOp discon graph ShowGraph = do
321-}
331-}
322
332
323-- | Execute the merge strategy against the transactions, returning a new transaction which can be then added to the transaction graph
333-- | 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
334+createMergeTransaction :: UTCTime -> TransactionId -> MergeStrategy -> (Transaction, Transaction) -> GraphRefRelationalExprM Transaction
325−createMergeTransaction stamp newId (SelectedBranchMergeStrategy selectedBranch) graph t2@(trans1, trans2) = do
335+createMergeTransaction stamp' newId (SelectedBranchMergeStrategy selectedBranch) t2@(trans1, trans2) = do
336+ graph <- gfGraph
326− let selectedTrans = validateHeadName selectedBranch graph t2
337+ selectedTrans <- validateHeadName selectedBranch graph t2
327− Transaction newId (MergeTransactionInfo (transactionId trans1) (transactionId trans2) S.empty stamp) . schemas <$> selectedTrans
338+ pure $ Transaction newId (TransactionInfo {
339+ parents = NE.fromList [transactionId trans1,
340+ transactionId trans2],
341+ stamp = stamp' }) (schemas selectedTrans)
328
342
329-- merge functions, relvars, individually
343-- 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
331
346
332-- merge function, relvars, but, on error, just take the component from the preferred branch
347-- 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
334
350
335-- | 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.
351-- | 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
337validateHeadName headName graph (t1, t2) =
353validateHeadName headName graph (t1, t2) =
338 case transactionForHead headName graph of
354 case transactionForHead headName graph of
339− Nothing -> Left SelectedHeadMismatchMergeError
355+ Nothing -> throwError (MergeTransactionError SelectedHeadMismatchMergeError)
340 Just trans -> if trans /= t1 && trans /= t2 then
356 Just trans -> if trans /= t1 && trans /= t2 then
341− Left SelectedHeadMismatchMergeError
357+ throwError (MergeTransactionError SelectedHeadMismatchMergeError)
342 else
358 else
343− Right trans
359+ pure trans
344
360
345-- 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.
361-- 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.
346subGraphOfFirstCommonAncestor :: TransactionGraph -> TransactionHeads -> Transaction -> Transaction -> S.Set Transaction -> Either RelationalError TransactionGraph
362subGraphOfFirstCommonAncestor :: TransactionGraph -> TransactionHeads -> Transaction -> Transaction -> S.Set Transaction -> Either RelationalError TransactionGraph
347−subGraphOfFirstCommonAncestor origGraph resultHeads currentTrans goalTrans traverseSet = do
363+subGraphOfFirstCommonAncestor origGraph resultHeads currentTrans' goalTrans traverseSet = do
348− let currentid = transactionId currentTrans
364+ let currentid = transactionId currentTrans'
349 goalid = transactionId goalTrans
365 goalid = transactionId goalTrans
350− if currentTrans == goalTrans then
366+ if currentTrans' == goalTrans then
351 Right (TransactionGraph resultHeads traverseSet) -- add filter
367 Right (TransactionGraph resultHeads traverseSet) -- add filter
352 --catch root transaction to improve error?
368 --catch root transaction to improve error?
353 else do
369 else do
354− currentTransChildren <- S.fromList <$> mapM (`transactionForId` origGraph) (S.toList (transactionChildIds currentTrans))
370+ currentTransChildren <- childTransactions currentTrans' origGraph
355− let searchChildren = S.difference (S.insert currentTrans traverseSet) currentTransChildren
371+ let searchChildren = S.difference (S.insert currentTrans' traverseSet) currentTransChildren
356− searchChild start = pathToTransaction origGraph start goalTrans (S.insert currentTrans traverseSet)
372+ searchChild start = pathToTransaction origGraph start goalTrans (S.insert currentTrans' traverseSet)
357 childSearches = map searchChild (S.toList searchChildren)
373 childSearches = map searchChild (S.toList searchChildren)
358 errors = lefts childSearches
374 errors = lefts childSearches
359 pathsFound = rights childSearches
375 pathsFound = rights childSearches
@@ -362,17 +378,16 @@ subGraphOfFirstCommonAncestor origGraph resultHeads currentTrans goalTrans trave
362 unless (null realErrors) (Left (head realErrors))
378 unless (null realErrors) (Left (head realErrors))
363 -- if no paths found, search the parent
379 -- if no paths found, search the parent
364 if null pathsFound then
380 if null pathsFound then
365− case oneParent currentTrans of
381+ case oneParent currentTrans' of
366 Left RootTransactionTraversalError -> Left (NoCommonTransactionAncestorError currentid goalid)
382 Left RootTransactionTraversalError -> Left (NoCommonTransactionAncestorError currentid goalid)
367 Left err -> Left err
383 Left err -> Left err
368 Right currentTransParent ->
384 Right currentTransParent ->
369− subGraphOfFirstCommonAncestor origGraph resultHeads currentTransParent goalTrans (S.insert currentTrans traverseSet)
385+ subGraphOfFirstCommonAncestor origGraph resultHeads currentTransParent goalTrans (S.insert currentTrans' traverseSet)
370 else -- we found a path
386 else -- we found a path
371 Right (TransactionGraph resultHeads (S.unions (traverseSet : pathsFound)))
387 Right (TransactionGraph resultHeads (S.unions (traverseSet : pathsFound)))
372 where
388 where
373− oneParent (Transaction _ (TransactionInfo parentId _ _) _) = transactionForId parentId origGraph
389+ oneParent (Transaction _ tinfo _) = transactionForId (NE.head (parents tinfo)) origGraph
374− oneParent (Transaction _ (MergeTransactionInfo parentId _ _ _) _) = transactionForId parentId origGraph
390+
375−
376-- | Search from a past graph point to all following heads for a specific transaction. If found, return the transaction path, otherwise a RelationalError.
391-- | Search from a past graph point to all following heads for a specific transaction. If found, return the transaction path, otherwise a RelationalError.
377pathToTransaction :: TransactionGraph -> Transaction -> Transaction -> S.Set Transaction -> Either RelationalError (S.Set Transaction)
392pathToTransaction :: TransactionGraph -> Transaction -> Transaction -> S.Set Transaction -> Either RelationalError (S.Set Transaction)
378pathToTransaction graph currentTransaction targetTransaction accumTransSet = do
393pathToTransaction graph currentTransaction targetTransaction accumTransSet = do
@@ -380,11 +395,11 @@ pathToTransaction graph currentTransaction targetTransaction accumTransSet = do
380 if transactionId targetTransaction == transactionId currentTransaction then
395 if transactionId targetTransaction == transactionId currentTransaction then
381 Right accumTransSet
396 Right accumTransSet
382 else do
397 else do
383− currentTransChildren <- mapM (`transactionForId` graph) (S.toList (transactionChildIds currentTransaction))
398+ currentTransChildren <- childTransactions currentTransaction graph
384 if null currentTransChildren then
399 if null currentTransChildren then
385 Left (FailedToFindTransactionError targetId)
400 Left (FailedToFindTransactionError targetId)
386 else do
401 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)
388 let realErrors = filter (/= FailedToFindTransactionError targetId) (lefts searches)
403 let realErrors = filter (/= FailedToFindTransactionError targetId) (lefts searches)
389 paths = rights searches
404 paths = rights searches
390 if not (null realErrors) then -- found some real errors
405 if not (null realErrors) then -- found some real errors
@@ -394,35 +409,39 @@ pathToTransaction graph currentTransaction targetTransaction accumTransSet = do
394 else --we have some paths!
409 else --we have some paths!
395 Right (S.unions paths)
410 Right (S.unions paths)
396
411
397−mergeTransactions :: UTCTime -> TransactionId -> TransactionId -> MergeStrategy -> (HeadName, HeadName) -> TransactionGraph -> Either RelationalError (DisconnectedTransaction, TransactionGraph)
412+mergeTransactions :: UTCTime -> TransactionId -> TransactionId -> MergeStrategy -> (HeadName, HeadName) -> GraphRefRelationalExprM (DisconnectedTransaction, TransactionGraph)
398−mergeTransactions stamp newId parentId mergeStrategy (headNameA, headNameB) graph = do
413+mergeTransactions stamp' newId parentId mergeStrategy (headNameA, headNameB) = do
414+ graph <- gfGraph
399 let transactionForHeadErr name = case transactionForHead name graph of
415 let transactionForHeadErr name = case transactionForHead name graph of
400− Nothing -> Left (NoSuchHeadNameError name)
416+ Nothing -> throwError (NoSuchHeadNameError name)
401− Just t -> Right t
417+ Just t -> pure t
418+ runE e = case e of
419+ Left e' -> throwError e'
420+ Right v -> pure v
402− transA <- transactionForHeadErr headNameA
421+ transA <- transactionForHeadErr headNameA
403− transB <- transactionForHeadErr headNameB
422+ transB <- transactionForHeadErr headNameB
404− disconParent <- transactionForId parentId graph
423+ disconParent <- gfTransForId parentId
405 let subHeads = M.filterWithKey (\k _ -> k `elem` [headNameA, headNameB]) (transactionHeadsForGraph graph)
424 let subHeads = M.filterWithKey (\k _ -> k `elem` [headNameA, headNameB]) (transactionHeadsForGraph graph)
406− subGraph <- subGraphOfFirstCommonAncestor graph subHeads transA transB S.empty
425+ subGraph <- runE $ subGraphOfFirstCommonAncestor graph subHeads transA transB S.empty
407− subGraph' <- filterSubGraph subGraph subHeads
426+ subGraph' <- runE $ filterSubGraph subGraph subHeads
408− case createMergeTransaction stamp newId mergeStrategy subGraph' (transA, transB) of
427+ mergedTrans <- local (const (freshGraphRefRelationalExprEnv Nothing subGraph')) $ createMergeTransaction stamp' newId mergeStrategy (transA, transB)
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
428+ case headNameForTransaction disconParent graph of
413− Nothing -> Left (TransactionIsNotAHeadError parentId)
429+ Nothing -> throwError (TransactionIsNotAHeadError parentId)
414 Just headName -> do
430 Just headName -> do
415− (newTrans, newGraph) <- addTransactionToGraph headName mergedTrans graph
431+ (newTrans, newGraph) <- runE $ addTransactionToGraph headName mergedTrans graph
432+ case checkConstraints (concreteDatabaseContext mergedTrans) newId graph of
433+ Left err -> throwError err
434+ Right _ -> do
416− let newGraph' = TransactionGraph (transactionHeadsForGraph newGraph) (transactionsForGraph newGraph)
435+ let newGraph' = TransactionGraph (transactionHeadsForGraph newGraph) (transactionsForGraph newGraph)
417− newDiscon = DisconnectedTransaction newId (schemas newTrans) False
436+ newDiscon = Discon.freshTransaction newId (schemas newTrans)
418− pure (newDiscon, newGraph')
437+ pure (newDiscon, newGraph')
419
438
420--TEMPORARY COPY/PASTE
439--TEMPORARY COPY/PASTE
421showTransactionStructureX :: Transaction -> TransactionGraph -> String
440showTransactionStructureX :: Transaction -> TransactionGraph -> String
422showTransactionStructureX trans graph = headInfo ++ " " ++ show (transactionId trans) ++ " " ++ parentTransactionsInfo
441showTransactionStructureX trans graph = headInfo ++ " " ++ show (transactionId trans) ++ " " ++ parentTransactionsInfo
423 where
442 where
424 headInfo = maybe "" show (headNameForTransaction trans graph)
443 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
426 Left err -> show err
445 Left err -> show err
427 Right parentTransSet -> concat $ S.toList $ S.map (show . transactionId) parentTransSet
446 Right parentTransSet -> concat $ S.toList $ S.map (show . transactionId) parentTransSet
428
447
@@ -441,25 +460,29 @@ filterSubGraph graph heads = Right $ TransactionGraph newHeads newTransSet
441 newHeads = M.map (filterTransaction validIds) heads
460 newHeads = M.map (filterTransaction validIds) heads
442
461
443--helper function for commonalities in union merge
462--helper function for commonalities in union merge
444−createUnionMergeTransaction :: UTCTime -> TransactionId -> MergeStrategy -> TransactionGraph -> (Transaction, Transaction) -> Either MergeError Transaction
463+createUnionMergeTransaction :: UTCTime -> TransactionId -> MergeStrategy -> (Transaction, Transaction) -> GraphRefRelationalExprM Transaction
445−createUnionMergeTransaction stamp newId strategy graph (t1,t2) = do
464+createUnionMergeTransaction stamp' newId strategy (t1,t2) = do
446 let contextA = concreteDatabaseContext t1
465 let contextA = concreteDatabaseContext t1
447 contextB = concreteDatabaseContext t2
466 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
449 preference <- case strategy of
472 preference <- case strategy of
450 UnionMergeStrategy -> pure PreferNeither
473 UnionMergeStrategy -> pure PreferNeither
451 UnionPreferMergeStrategy preferBranch ->
474 UnionPreferMergeStrategy preferBranch ->
452 case transactionForHead preferBranch graph of
475 case transactionForHead preferBranch graph of
453− Nothing -> Left (PreferredHeadMissingMergeError preferBranch)
476+ Nothing -> throwError (MergeTransactionError (PreferredHeadMissingMergeError preferBranch))
454 Just preferredTrans -> pure $ if t1 == preferredTrans then PreferFirst else PreferSecond
477 Just preferredTrans -> pure $ if t1 == preferredTrans then PreferFirst else PreferSecond
455− badStrat -> Left (InvalidMergeStrategyError badStrat)
478+ badStrat -> throwError (MergeTransactionError (InvalidMergeStrategyError badStrat))
456
479
457− incDeps <- unionMergeMaps preference (inclusionDependencies contextA) (inclusionDependencies contextB)
480+ incDeps <- liftMergeE $ unionMergeMaps preference (inclusionDependencies contextA) (inclusionDependencies contextB)
458 relVars <- unionMergeRelVars preference (relationVariables contextA) (relationVariables contextB)
481 relVars <- unionMergeRelVars preference (relationVariables contextA) (relationVariables contextB)
459− atomFuncs <- unionMergeAtomFunctions preference (atomFunctions contextA) (atomFunctions contextB)
482+ atomFuncs <- liftMergeE $ unionMergeAtomFunctions preference (atomFunctions contextA) (atomFunctions contextB)
460− notifs <- unionMergeMaps preference (notifications contextA) (notifications contextB)
483+ notifs <- liftMergeE $ unionMergeMaps preference (notifications contextA) (notifications contextB)
461− types <- unionMergeTypeConstructorMapping preference (typeConstructorMapping contextA) (typeConstructorMapping contextB)
484+ types <- liftMergeE $ unionMergeTypeConstructorMapping preference (typeConstructorMapping contextA) (typeConstructorMapping contextB)
462− dbcFuncs <- unionMergeDatabaseContextFunctions preference (dbcFunctions contextA) (dbcFunctions contextB)
485+ dbcFuncs <- liftMergeE $ unionMergeDatabaseContextFunctions preference (dbcFunctions contextA) (dbcFunctions contextB)
463 -- TODO: add merge of subschemas
486 -- TODO: add merge of subschemas
464 let newContext = DatabaseContext {
487 let newContext = DatabaseContext {
465 inclusionDependencies = incDeps,
488 inclusionDependencies = incDeps,
@@ -470,7 +493,10 @@ createUnionMergeTransaction stamp newId strategy graph (t1,t2) = do
470 typeConstructorMapping = types
493 typeConstructorMapping = types
471 }
494 }
472 newSchemas = Schemas newContext (subschemas t1)
495 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)
474
500
475lookupTransaction :: TransactionGraph -> TransactionIdLookup -> Either RelationalError Transaction
501lookupTransaction :: TransactionGraph -> TransactionIdLookup -> Either RelationalError Transaction
476lookupTransaction graph (TransactionIdLookup tid) = transactionForId tid graph
502lookupTransaction graph (TransactionIdLookup tid) = transactionForId tid graph
@@ -487,56 +513,57 @@ backtrackGraph :: TransactionGraph -> TransactionId -> TransactionIdHeadBacktrac
487-- tilde, step back one parent link- if a choice must be made, choose the "first" link arbitrarily
513-- tilde, step back one parent link- if a choice must be made, choose the "first" link arbitrarily
488backtrackGraph graph currentTid (TransactionIdHeadParentBacktrack steps) = do
514backtrackGraph graph currentTid (TransactionIdHeadParentBacktrack steps) = do
489 trans <- transactionForId currentTid graph
515 trans <- transactionForId currentTid graph
516+
490− let parents = S.toAscList (transactionParentIds trans)
517+ let parentIds' = S.toAscList (parentIds trans)
491− if null parents then
518+ case parentIds' of
492− Left RootTransactionTraversalError
519+ [] -> Left RootTransactionTraversalError
493− else do
520+ firstParentId:_ -> do
494− parentTrans <- transactionForId (head parents) graph
521+ parentTrans <- transactionForId firstParentId graph
495− if steps == 1 then
522+ if steps == 1 then
496− pure (transactionId parentTrans)
523+ pure (transactionId parentTrans)
497− else
524+ else
498− backtrackGraph graph (transactionId parentTrans) (TransactionIdHeadParentBacktrack (steps - 1))
525+ backtrackGraph graph (transactionId parentTrans) (TransactionIdHeadParentBacktrack (steps - 1))
499
526
500backtrackGraph graph currentTid (TransactionIdHeadBranchBacktrack steps) = do
527backtrackGraph graph currentTid (TransactionIdHeadBranchBacktrack steps) = do
501 trans <- transactionForId currentTid graph
528 trans <- transactionForId currentTid graph
502− let parents = transactionParentIds trans
529+ let parentIds' = parentIds trans
503− if S.size parents < 1 then
530+ if S.size parentIds' < 1 then
504 Left RootTransactionTraversalError
531 Left RootTransactionTraversalError
505− else if S.size parents < steps then
532+ else if S.size parentIds' < steps then
506− Left (ParentCountTraversalError (S.size parents) steps)
533+ Left (ParentCountTraversalError (S.size parentIds') steps)
507 else
534 else
508− pure (S.elemAt (steps - 1) parents)
535+ pure (S.elemAt (steps - 1) parentIds')
509
536
510−backtrackGraph graph currentTid btrack@(TransactionStampHeadBacktrack stamp) = do
537+backtrackGraph graph currentTid btrack@(TransactionStampHeadBacktrack stamp') = do
511 trans <- transactionForId currentTid graph
538 trans <- transactionForId currentTid graph
512− let parents = transactionParentIds trans
539+ let parentIds' = parentIds trans
513− if transactionTimestamp trans <= stamp then
540+ if timestamp trans <= stamp' then
514 pure currentTid
541 pure currentTid
515− else if S.null parents then
542+ else if S.null parentIds' then
516 Left RootTransactionTraversalError
543 Left RootTransactionTraversalError
517 else
544 else
518− let arbitraryParent = head (S.toList parents) in
545+ let arbitraryParent = head (S.toList parentIds') in
519 backtrackGraph graph arbitraryParent btrack
546 backtrackGraph graph arbitraryParent btrack
520
547
521-- | 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.
548-- | 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.
522--this is not a GraphOp because it combines multiple graph operations
549--this is not a GraphOp because it combines multiple graph operations
523autoMergeToHead :: UTCTime -> (TransactionId, TransactionId, TransactionId) -> DisconnectedTransaction -> HeadName -> MergeStrategy -> TransactionGraph -> Either RelationalError (DisconnectedTransaction, TransactionGraph)
550autoMergeToHead :: 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
525 let tempBranchName = "mergebranch_" <> U.toText tempBranchTransId
552 let tempBranchName = "mergebranch_" <> U.toText tempBranchTransId
526 --create the temp branch
553 --create the temp branch
527− (discon', graph') <- evalGraphOp stamp tempBranchTransId discon graph (Branch tempBranchName)
554+ (discon', graph') <- evalGraphOp stamp' tempBranchTransId discon graph (Branch tempBranchName)
528
555
529 --commit to the new branch- possible future optimization: don't require fsync for this- create a temp commit type
556 --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
531
558
532 --jump to merge head
559 --jump to merge head
533− (discon''', graph''') <- evalGraphOp stamp tempBranchTransId discon'' graph'' (JumpToHead mergeToHeadName)
560+ (discon''', graph''') <- evalGraphOp stamp' tempBranchTransId discon'' graph'' (JumpToHead mergeToHeadName)
534
561
535 --create the merge
562 --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)
537
564
538 --delete the temp branch
565 --delete the temp branch
539− (discon''''', graph''''') <- evalGraphOp stamp tempBranchTransId discon'''' graph'''' (DeleteBranch tempBranchName)
566+ (discon''''', graph''''') <- evalGraphOp stamp' tempBranchTransId discon'''' graph'''' (DeleteBranch tempBranchName)
540 {-
567 {-
541 let rel = runReader (evalRelationalExpr (RelationVariable "s" ())) (mkRelationalExprState $ D.concreteDatabaseContext discon'''')
568 let rel = runReader (evalRelationalExpr (RelationVariable "s" ())) (mkRelationalExprState $ D.concreteDatabaseContext discon'''')
542 traceShowM rel
569 traceShowM rel
@@ -544,4 +571,4 @@ autoMergeToHead stamp (tempBranchTransId, tempCommitTransId, mergeTransId) disco
544
571
545 pure (discon''''', graph''''')
572 pure (discon''''', graph''''')
546
573
547−
574+
src/lib/ProjectM36/TransactionGraph/Merge.hs+16−12
@@ -2,10 +2,11 @@
2module ProjectM36.TransactionGraph.Merge where
2module ProjectM36.TransactionGraph.Merge where
3import ProjectM36.Base
3import ProjectM36.Base
4import ProjectM36.Error
4import ProjectM36.Error
5+import ProjectM36.RelationalExpression
6+import Control.Monad.Except hiding (join)
5import qualified Data.Set as S
7import qualified Data.Set as S
6import qualified Data.Map as M
8import qualified Data.Map as M
7import qualified ProjectM36.TypeConstructorDef as TCD
9import qualified ProjectM36.TypeConstructorDef as TCD
8−import ProjectM36.Relation
9import Control.Monad (foldM)
10import Control.Monad (foldM)
10import qualified Data.HashSet as HS
11import qualified Data.HashSet as HS
11
12
@@ -22,19 +23,22 @@ unionMergeMaps prefer mapA mapB = case prefer of
22 Left StrategyViolatesComponentMergeError
23 Left StrategyViolatesComponentMergeError
23
24
24-- perform the merge even if the attributes are different- is this what we want? Obviously, we need finer-grained merge options.
25-- 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 :: MergePreference -> GraphRefRelationalExpr -> GraphRefRelationalExpr -> GraphRefRelationalExprM GraphRefRelationalExpr
26−unionMergeRelation prefer relA relB = case relA `union` relB of
27+unionMergeRelation prefer relA relB = do
27− Right unionRel -> pure unionRel
28+ let unioned = Union relA relB
28− Left (AttributeNamesMismatchError _) -> preferredRelVar
29− Left _ -> Left StrategyViolatesRelationVariableMergeError
29+ mergeErr = MergeTransactionError StrategyViolatesRelationVariableMergeError
30− where
31− preferredRelVar = case prefer of
30+ preferredRelVar =
31+ case prefer of
32− PreferFirst -> Right relA
32+ PreferFirst -> pure relA
33− PreferSecond -> Right relB
33+ PreferSecond -> pure relB
34− PreferNeither -> Left StrategyViolatesRelationVariableMergeError
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
35
39
36--try to execute unions against the relvars contents -- if a relvar only appears in one context, include it
40--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
38unionMergeRelVars prefer relvarsA relvarsB = do
42unionMergeRelVars prefer relvarsA relvarsB = do
39 let allNames = S.toList (S.union (M.keysSet relvarsA) (M.keysSet relvarsB))
43 let allNames = S.toList (S.union (M.keysSet relvarsA) (M.keysSet relvarsB))
40 foldM (\acc name -> do
44 foldM (\acc name -> do
src/lib/ProjectM36/TransactionGraph/Persist.hs+6−5
@@ -1,8 +1,8 @@
1module ProjectM36.TransactionGraph.Persist where
1module ProjectM36.TransactionGraph.Persist where
2import ProjectM36.Error
2import ProjectM36.Error
3−import ProjectM36.TransactionGraph
4import ProjectM36.Transaction
3import ProjectM36.Transaction
5import ProjectM36.Transaction.Persist
4import ProjectM36.Transaction.Persist
5+import ProjectM36.RelationalExpression
6import ProjectM36.Base
6import ProjectM36.Base
7import ProjectM36.ScriptSession
7import ProjectM36.ScriptSession
8import ProjectM36.Persist (writeFileSync, renameSync, DiskSync)
8import ProjectM36.Persist (writeFileSync, renameSync, DiskSync)
@@ -194,12 +194,12 @@ writeGraphTransactionIdFile sync destDirectory (TransactionGraph _ transSet) = w
194 uuidInfo = T.intercalate "\n" graphLines
194 uuidInfo = T.intercalate "\n" graphLines
195 digest = SHA256.hash (encodeUtf8 uuidInfo)
195 digest = SHA256.hash (encodeUtf8 uuidInfo)
196 graphLines = S.toList $ S.map graphLine transSet
196 graphLines = S.toList $ S.map graphLine transSet
197− epochTime = realToFrac . utcTimeToPOSIXSeconds . transactionTimestamp :: Transaction -> Double
197+ epochTime = realToFrac . utcTimeToPOSIXSeconds . timestamp :: Transaction -> Double
198 graphLine trans = U.toText (transactionId trans)
198 graphLine trans = U.toText (transactionId trans)
199 <> " "
199 <> " "
200 <> T.pack (show (epochTime trans))
200 <> T.pack (show (epochTime trans))
201 <> " "
201 <> " "
202− <> T.intercalate " " (S.toList (S.map U.toText $ transactionParentIds trans))
202+ <> T.intercalate " " (S.toList (S.map U.toText $ parentIds trans))
203
203
204readGraphTransactionIdFileDigest :: FilePath -> IO LockFileHash
204readGraphTransactionIdFileDigest :: FilePath -> IO LockFileHash
205readGraphTransactionIdFileDigest dbdir = do
205readGraphTransactionIdFileDigest dbdir = do
@@ -209,8 +209,8 @@ readGraphTransactionIdFileDigest dbdir = do
209readGraphTransactionIdFile :: FilePath -> IO (Either PersistenceError [(TransactionId, UTCTime, [TransactionId])])
209readGraphTransactionIdFile :: FilePath -> IO (Either PersistenceError [(TransactionId, UTCTime, [TransactionId])])
210readGraphTransactionIdFile dbdir = do
210readGraphTransactionIdFile dbdir = do
211 --read in all transactions' uuids
211 --read in all transactions' uuids
212− let grapher line = let tid:epochText:parentIds = T.words line in
212+ let grapher line = let tid:epochText:parentIds' = T.words line in
213− (readUUID tid, readEpoch epochText, map readUUID parentIds)
213+ (readUUID tid, readEpoch epochText, map readUUID parentIds')
214 readUUID uuidText = fromMaybe (error "failed to read uuid") (U.fromText uuidText)
214 readUUID uuidText = fromMaybe (error "failed to read uuid") (U.fromText uuidText)
215 readEpoch t = posixSecondsToUTCTime (realToFrac (either (error "failed to read epoch") fst (double t)))
215 readEpoch t = posixSecondsToUTCTime (realToFrac (either (error "failed to read epoch") fst (double t)))
216 Right . map grapher . T.lines <$> readUTF8FileOrError (transactionLogPath dbdir)
216 Right . map grapher . T.lines <$> readUTF8FileOrError (transactionLogPath dbdir)
@@ -225,3 +225,4 @@ readUTF8FileOrError pathIn = do
225 case TE.decodeUtf8' fileBytes of
225 case TE.decodeUtf8' fileBytes of
226 Left err -> error (show err)
226 Left err -> error (show err)
227 Right utf8Bytes -> pure utf8Bytes
227 Right utf8Bytes -> pure utf8Bytes
228+
src/lib/ProjectM36/TransactionGraph/Show.hs+3−4
@@ -1,17 +1,16 @@
1module ProjectM36.TransactionGraph.Show where
1module ProjectM36.TransactionGraph.Show where
2import ProjectM36.Base
2import ProjectM36.Base
3import ProjectM36.TransactionGraph
3import ProjectM36.TransactionGraph
4−import ProjectM36.Transaction
5import qualified Data.Set as S
4import qualified Data.Set as S
6
5
7showTransactionStructure :: Transaction -> TransactionGraph -> String
6showTransactionStructure :: 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
9 where
8 where
10 headInfo = maybe "" show (headNameForTransaction trans graph)
9 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
12 Left err -> show err
11 Left err -> show err
13 Right parentTransSet -> concat $ S.toList $ S.map (show . transactionId) parentTransSet
12 Right parentTransSet -> concat $ S.toList $ S.map (show . transactionId) parentTransSet
14− childTransactionsInfo = show (S.toList (transactionChildIds trans))
13+
15
14
16showGraphStructure :: TransactionGraph -> String
15showGraphStructure :: TransactionGraph -> String
17showGraphStructure graph@(TransactionGraph _ transSet) = S.foldr folder "" transSet
16showGraphStructure 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
4import ProjectM36.IsomorphicSchema
4import ProjectM36.IsomorphicSchema
5import ProjectM36.Relation
5import ProjectM36.Relation
6import ProjectM36.RelationalExpression
6import ProjectM36.RelationalExpression
7+import ProjectM36.TransactionGraph
8+import ProjectM36.StaticOptimizer
7import qualified ProjectM36.DatabaseContext as DBC
9import qualified ProjectM36.DatabaseContext as DBC
8import qualified ProjectM36.Attribute as A
10import qualified ProjectM36.Attribute as A
9import System.Exit
11import System.Exit
10import qualified Data.Map as M
12import qualified Data.Map as M
11import qualified Data.Set as S
13import qualified Data.Set as S
12−import Control.Monad.State
13−import Control.Monad.Trans.Reader
14
14
15testList :: Test
15testList :: Test
16testList = TestList [testIsoRename, testIsoRestrict, testIsoUnion, testSchemaValidation]
16testList = TestList [testIsoRename, testIsoRestrict, testIsoUnion, testSchemaValidation]
@@ -36,7 +36,7 @@ assertMaybe x msg = case x of
36testSchemaValidation :: Test
36testSchemaValidation :: Test
37testSchemaValidation = TestCase $ do
37testSchemaValidation = TestCase $ do
38 let potentialSchema = DBC.basicDatabaseContext {
38 let potentialSchema = DBC.basicDatabaseContext {
39− relationVariables = M.singleton "anotherRel" relationTrue
39+ relationVariables = M.singleton "anotherRel" (ExistingRelation relationTrue)
40 }
40 }
41 -- missing relvars failure
41 -- missing relvars failure
42 morphs = [IsoRename "true" "true", IsoRename "false" "false"]
42 morphs = [IsoRename "true" "true", IsoRename "false" "false"]
@@ -50,15 +50,17 @@ testSchemaValidation = TestCase $ do
50testIsoRename :: Test
50testIsoRename :: Test
51testIsoRename = TestCase $ do
51testIsoRename = TestCase $ do
52 -- create a schema with two relvars and rename one while the other remains the same in the isomorphic schema
52 -- 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 {
53+ let ctx = DBC.empty {
54− relationVariables = M.fromList [("employee", relationTrue),
54+ relationVariables = M.fromList [("employee", ExistingRelation relationTrue),
55− ("department", relationFalse)]
55+ ("department", ExistingRelation relationFalse)]
56 }
56 }
57+ (graph, _) <- freshTransactionGraph ctx
57− isomorphsAtoB = [IsoRename "emp" "employee",
58+ let isomorphsAtoB = [IsoRename "emp" "employee",
58 IsoRename "department" "department"]
59 IsoRename "department" "department"]
59− unionExpr = Union (RelationVariable "emp" ()) (RelationVariable "department" ())
60+ unionExpr = Union (RelationVariable "emp" ()) (RelationVariable "department" ())
61+ env = mkRelationalExprEnv ctx graph
60 relExpr <- assertEither (applyRelationalExprSchemaIsomorphs isomorphsAtoB unionExpr)
62 relExpr <- assertEither (applyRelationalExprSchemaIsomorphs isomorphsAtoB unionExpr)
61− let relResult = runReader (evalRelationalExpr relExpr) schemaA
63+ let relResult = optimizeAndEvalRelationalExpr env relExpr
62 assertEqual "employee relation morph" (Right relationTrue) relResult
64 assertEqual "employee relation morph" (Right relationTrue) relResult
63
65
64testIsoRestrict :: Test
66testIsoRestrict :: Test
@@ -67,62 +69,70 @@ testIsoRestrict = TestCase $ do
67 -- the virtual schema has an employee
69 -- the virtual schema has an employee
68 let empattrs = A.attributesFromList [Attribute "name" TextAtomType,
70 let empattrs = A.attributesFromList [Attribute "name" TextAtomType,
69 Attribute "boss" TextAtomType]
71 Attribute "boss" TextAtomType]
72+ (graph, transId) <- freshTransactionGraph DBC.empty
70 emprel <- assertEither $ mkRelationFromList empattrs
73 emprel <- assertEither $ mkRelationFromList empattrs
71 [[TextAtom "Steve", TextAtom ""],
74 [[TextAtom "Steve", TextAtom ""],
72 [TextAtom "Cindy", TextAtom "Steve"],
75 [TextAtom "Cindy", TextAtom "Steve"],
73 [TextAtom "Sam", TextAtom "Steve"]]
76 [TextAtom "Sam", TextAtom "Steve"]]
74 let predicate = AttributeEqualityPredicate "boss" (NakedAtomExpr (TextAtom ""))
77 let predicate = AttributeEqualityPredicate "boss" (NakedAtomExpr (TextAtom ""))
78+ reenv = mkRelationalExprEnv DBC.empty graph
75− bossRel <- assertEither $ runReader (evalRelationalExpr (Restrict predicate (ExistingRelation emprel))) (mkRelationalExprState DBC.empty)
79+ bossRel <- assertEither $ runRelationalExprM reenv (evalRelationalExpr (Restrict predicate (ExistingRelation emprel)))
76− nonBossRel <- assertEither $ runReader (evalRelationalExpr (Restrict (NotPredicate predicate) (ExistingRelation emprel))) (mkRelationalExprState DBC.empty)
80+ nonBossRel <- assertEither $ runRelationalExprM reenv (evalRelationalExpr (Restrict (NotPredicate predicate) (ExistingRelation emprel)))
77
81
78− let schemaA = mkRelationalExprState DBC.empty {
82+ let schemaA = mkRelationalExprEnv baseContext graph
83+ baseContext = DBC.empty {
79− relationVariables = M.fromList [("nonboss", nonBossRel),
84+ relationVariables = M.fromList [("nonboss", ExistingRelation nonBossRel),
80− ("boss", bossRel)]
85+ ("boss", ExistingRelation bossRel)]
81 }
86 }
82−
83 isomorphsAtoB = [IsoRestrict "employee" predicate ("boss", "nonboss")]
87 isomorphsAtoB = [IsoRestrict "employee" predicate ("boss", "nonboss")]
84 bossq = RelationVariable "boss" ()
88 bossq = RelationVariable "boss" ()
85 nonbossq = RelationVariable "nonboss" ()
89 nonbossq = RelationVariable "nonboss" ()
86 employeeq = RelationVariable "employee" ()
90 employeeq = RelationVariable "employee" ()
87 unionq = Union bossq nonbossq
91 unionq = Union bossq nonbossq
88 empExpr <- assertEither (applyRelationalExprSchemaIsomorphs isomorphsAtoB employeeq)
92 empExpr <- assertEither (applyRelationalExprSchemaIsomorphs isomorphsAtoB employeeq)
89− let empResult = runReader (evalRelationalExpr empExpr) schemaA
93+ let empResult = evalRelExpr (evalRelationalExpr empExpr)
90− unionResult = runReader (evalRelationalExpr unionq) schemaA
94+ unionResult = evalRelExpr (evalRelationalExpr unionq)
95+ evalRelExpr = runRelationalExprM schemaA
91 assertEqual "boss/nonboss isorestrict" unionResult empResult
96 assertEqual "boss/nonboss isorestrict" unionResult empResult
92 --execute database context expr
97 --execute database context expr
93 bobRel <- assertEither (mkRelationFromList empattrs [[TextAtom "Bob", TextAtom ""]])
98 bobRel <- assertEither (mkRelationFromList empattrs [[TextAtom "Bob", TextAtom ""]])
94 let schemaBInsertExpr = Insert "employee" (ExistingRelation bobRel)
99 let schemaBInsertExpr = Insert "employee" (ExistingRelation bobRel)
95 schemaBInsertExpr' <- assertEither (processDatabaseContextExprInSchema (Schema isomorphsAtoB) schemaBInsertExpr)
100 schemaBInsertExpr' <- assertEither (processDatabaseContextExprInSchema (Schema isomorphsAtoB) schemaBInsertExpr)
96− let (postInsertContext,_,_) = execState (evalDatabaseContextExpr schemaBInsertExpr') (freshDatabaseState (stateElemsContext schemaA))
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
97− expectedRel = runReader (evalRelationalExpr (Union (RelationVariable "boss" ()) (RelationVariable "nonboss" ()))) (mkRelationalExprState postInsertContext)
105+ expectedRel = runRelationalExprM postInsertEnv (evalRelationalExpr (Union (RelationVariable "boss" ()) (RelationVariable "nonboss" ())))
106+ postInsertEnv = mkRelationalExprEnv postInsertContext graph
98 --execute the expression against the schema and compare against the base context
107 --execute the expression against the schema and compare against the base context
99 processedExpr <- assertEither (processRelationalExprInSchema (Schema isomorphsAtoB) (RelationVariable "employee" ()))
108 processedExpr <- assertEither (processRelationalExprInSchema (Schema isomorphsAtoB) (RelationVariable "employee" ()))
100− let processedRel = runReader (evalRelationalExpr processedExpr) (mkRelationalExprState postInsertContext)
109+ let processedRel = runRelationalExprM postInsertEnv (evalRelationalExpr processedExpr)
101 assertEqual "insert bob boss" expectedRel processedRel
110 assertEqual "insert bob boss" expectedRel processedRel
102
111
103testIsoUnion :: Test
112testIsoUnion :: Test
104testIsoUnion = TestCase $ do
113testIsoUnion = TestCase $ do
105 --create motors relation which is split into low-power (<50 horsepower) and high-power (>=50 horsepower) motors
114 --create motors relation which is split into low-power (<50 horsepower) and high-power (>=50 horsepower) motors
106 --the schema is contains the split relvars
115 --the schema is contains the split relvars
116+ (graph, _) <- freshTransactionGraph DBC.empty
107 motorsRel <- assertEither $ mkRelationFromList (A.attributesFromList [Attribute "name" TextAtomType,
117 motorsRel <- assertEither $ mkRelationFromList (A.attributesFromList [Attribute "name" TextAtomType,
108 Attribute "power" IntegerAtomType])
118 Attribute "power" IntegerAtomType])
109 [[TextAtom "Puny", IntegerAtom 10],
119 [[TextAtom "Puny", IntegerAtom 10],
110 [TextAtom "Scooter", IntegerAtom 49],
120 [TextAtom "Scooter", IntegerAtom 49],
111 [TextAtom "Auto", IntegerAtom 200],
121 [TextAtom "Auto", IntegerAtom 200],
112 [TextAtom "Tractor", IntegerAtom 500]]
122 [TextAtom "Tractor", IntegerAtom 500]]
113− let baseSchema = mkRelationalExprState DBC.basicDatabaseContext {
123+ let env = mkRelationalExprEnv DBC.basicDatabaseContext {
114− relationVariables = M.singleton "motor" motorsRel
124+ relationVariables = M.singleton "motor" (ExistingRelation motorsRel)
115− }
125+ } graph
116 splitPredicate = AtomExprPredicate (FunctionAtomExpr "lt" [AttributeAtomExpr "power", NakedAtomExpr (IntegerAtom 50)] ())
126 splitPredicate = AtomExprPredicate (FunctionAtomExpr "lt" [AttributeAtomExpr "power", NakedAtomExpr (IntegerAtom 50)] ())
117 splitIsomorphs = [IsoUnion ("lowpower", "highpower") splitPredicate "motor",
127 splitIsomorphs = [IsoUnion ("lowpower", "highpower") splitPredicate "motor",
118 IsoRename "true" "true",
128 IsoRename "true" "true",
119 IsoRename "false" "false"]
129 IsoRename "false" "false"]
120− lowmotors = runReader (evalRelationalExpr (Restrict splitPredicate (RelationVariable "motor" ()))) baseSchema
130+ lowmotors = runRelationalExprM env (evalRelationalExpr (Restrict splitPredicate (RelationVariable "motor" ())))
121− highmotors = runReader (evalRelationalExpr (Restrict (NotPredicate splitPredicate) (RelationVariable "motor" ()))) baseSchema
131+ highmotors = runRelationalExprM env (evalRelationalExpr (Restrict (NotPredicate splitPredicate) (RelationVariable "motor" ())))
122− relResult expr = runReader (evalRelationalExpr expr) baseSchema
132+ relResult expr = runRelationalExprM env (evalRelationalExpr expr)
123 lowpowerExpr <- assertEither (processRelationalExprInSchema (Schema splitIsomorphs) (RelationVariable "lowpower" ()))
133 lowpowerExpr <- assertEither (processRelationalExprInSchema (Schema splitIsomorphs) (RelationVariable "lowpower" ()))
124 lowpowerRel <- assertEither (relResult lowpowerExpr)
134 lowpowerRel <- assertEither (relResult lowpowerExpr)
125 highpowerExpr <- assertEither (processRelationalExprInSchema (Schema splitIsomorphs) (RelationVariable "highpower" ()))
135 highpowerExpr <- assertEither (processRelationalExprInSchema (Schema splitIsomorphs) (RelationVariable "highpower" ()))
126 highpowerRel <- assertEither (relResult highpowerExpr)
136 highpowerRel <- assertEither (relResult highpowerExpr)
127 assertEqual "lowpower relation difference" (Right lowpowerRel) lowmotors
137 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
104 let createRelExpr = Assign "x" rel
104 let createRelExpr = Assign "x" rel
105
105
106 rel = MakeRelationFromExprs Nothing
106 rel = MakeRelationFromExprs Nothing
107− [TupleExpr (M.singleton "a1" (NakedAtomExpr atomVal))]
107+ (TupleExprs () [TupleExpr (M.singleton "a1" (NakedAtomExpr atomVal))])
108 atomVal = toAtom exampleVal
108 atomVal = toAtom exampleVal
109 exampleVal = Test1C 10
109 exampleVal = Test1C 10
110 checkExecuteDatabaseContextExpr sessionId dbconn createRelExpr
110 checkExecuteDatabaseContextExpr sessionId dbconn createRelExpr
@@ -194,4 +194,3 @@ testManyFields = TestCase $ do
194
194
195 let example5 = Test9_5C 1 2 3 4 5
195 let example5 = Test9_5C 1 2 3 4 5
196 assertEqual "five fields product type" example5 (fromAtom (toAtom example5))
196 assertEqual "five fields product type" example5 (fromAtom (toAtom example5))
197−
test/Relation/Basic.hs+10−4
@@ -7,9 +7,9 @@ import ProjectM36.TupleSet
7import ProjectM36.Attribute hiding (null, attributeNames)
7import ProjectM36.Attribute hiding (null, attributeNames)
8import ProjectM36.DataTypes.Primitive
8import ProjectM36.DataTypes.Primitive
9import ProjectM36.RelationalExpression
9import ProjectM36.RelationalExpression
10+import ProjectM36.TransactionGraph
10import ProjectM36.Tuple
11import ProjectM36.Tuple
11import qualified ProjectM36.DatabaseContext as DBC
12import qualified ProjectM36.DatabaseContext as DBC
12−import Control.Monad.Trans.Reader
13import qualified ProjectM36.Attribute as A
13import qualified ProjectM36.Attribute as A
14import qualified Data.Map as M
14import qualified Data.Map as M
15import qualified Data.Vector as V
15import qualified Data.Vector as V
@@ -94,8 +94,12 @@ testMkRelation1 = TestCase $ assertEqual "key mismatch" expectedError (mkRelatio
94
94
95testMkRelationFromExprsBadAttrs :: Test
95testMkRelationFromExprsBadAttrs :: Test
96testMkRelationFromExprsBadAttrs = TestCase $ do
96testMkRelationFromExprsBadAttrs = TestCase $ do
97− let context = DBC.empty
97+ let context = DBC.empty
98+ (graph,_) <- freshTransactionGraph context
99+ let reenv = mkRelationalExprEnv context graph
98− case runReader (evalRelationalExpr (MakeRelationFromExprs (Just [AttributeAndTypeNameExpr "badAttr1" (PrimitiveTypeConstructor "Int" IntAtomType) ()]) [TupleExpr (M.singleton "badAttr2" (NakedAtomExpr (IntAtom 1)))])) (mkRelationalExprState context) of
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
99 Left err -> assertEqual "tuple type mismatch" (TupleAttributeTypeMismatchError (A.attributesFromList [Attribute "badAttr2" IntAtomType])) err
103 Left err -> assertEqual "tuple type mismatch" (TupleAttributeTypeMismatchError (A.attributesFromList [Attribute "badAttr2" IntAtomType])) err
100 Right _ -> assertFailure "expected tuple type mismatch"
104 Right _ -> assertFailure "expected tuple type mismatch"
101
105
@@ -108,5 +112,7 @@ testDuplicateAttributes = TestCase $ do
108
112
109testExistingRelationType :: Test
113testExistingRelationType :: Test
110testExistingRelationType = TestCase $ do
114testExistingRelationType = TestCase $ do
115+ (graph, _) <- freshTransactionGraph dateExamples
111− let typeResult = runReader (typeForRelationalExpr (ExistingRelation relationTrue)) (RelationalExprStateElems DBC.empty)
116+ let typeResult = runRelationalExprM reenv (typeForRelationalExpr (ExistingRelation relationTrue))
117+ reenv = mkRelationalExprEnv dateExamples graph
112 assertEqual "ExistingRelation with tuples type" (Right relationFalse) typeResult
118 assertEqual "ExistingRelation with tuples type" (Right relationFalse) typeResult
test/Relation/StaticOptimizer.hs+76−51
@@ -1,13 +1,12 @@
1import ProjectM36.Base
1import ProjectM36.Base
2import ProjectM36.Relation
2import ProjectM36.Relation
3−import ProjectM36.RelationalExpression
3+import ProjectM36.TransactionGraph
4import ProjectM36.DateExamples
4import ProjectM36.DateExamples
5−import ProjectM36.Error
6import ProjectM36.TupleSet
5import ProjectM36.TupleSet
7import ProjectM36.StaticOptimizer
6import ProjectM36.StaticOptimizer
7+import ProjectM36.DatabaseContext as DBC
8import System.Exit
8import System.Exit
9import Control.Monad.State
9import Control.Monad.State
10−import Control.Monad.Trans.Reader
11import Test.HUnit
10import Test.HUnit
12import qualified Data.Set as S
11import qualified Data.Set as S
13
12
@@ -16,100 +15,126 @@ main = do
16 tcounts <- runTestTT (TestList tests)
15 tcounts <- runTestTT (TestList tests)
17 if errors tcounts + failures tcounts > 0 then exitFailure else exitSuccess
16 if errors tcounts + failures tcounts > 0 then exitFailure else exitSuccess
18 where
17 where
19− tests = relationOptTests ++ databaseOptTests ++ [testTransitiveOptimization,
18+ tests = [testTransitiveOptimization,
20− testImpossiblePredicates,
19+ testImpossiblePredicates,
21− testJoinElimination,
20+ testJoinElimination,
22− testRestrictionPredicateCollapse,
21+ testRestrictionPredicateCollapse,
23− testPushDownRestrictionPredicate]
22+ testPushDownRestrictionPredicate,
24− optDBCTest = map (\(name, expr, unopt) -> TestCase $ assertEqual name expr (evalState (applyStaticDatabaseOptimization unopt) (freshDatabaseState dateExamples)))
23+ testOptimizeRelationalExpr,
25− relvar nam = RelationVariable nam ()
24+ testOptimizeDatabaseContextExpr]
26− startState = mkRelationalExprState dateExamples
25+
27− optRelTest = map (\(name, expr, unopt) -> TestCase $ assertEqual name expr (runReader (applyStaticRelationalOptimization unopt) startState))
26+testOptimizeDatabaseContextExpr :: Test
28− relationOptTests = optRelTest [
27+testOptimizeDatabaseContextExpr = TestCase $ do
29− ("StaticProject", Right $ relvar "s", Project (AttributeNames (attributeNames suppliersRel)) (relvar "s")),
28+ (graph, transId) <- freshTransactionGraph DBC.empty
30− ("StaticUnion", Right $ relvar "s", Union (relvar "s") (relvar "s")),
29+ let relvar nam = RelationVariable nam ()
31− ("StaticJoin", Right $ relvar "s", Join (relvar "s") (relvar "s"))
30+ relvarId nam = RelationVariable nam UncommittedContextMarker
32− ]
31+ testInfos = [
33− databaseOptTests = optDBCTest [
34− ("StaticAssign",
32+ ("StaticAssign",
35− Right $ Assign "z" (relvar "s"),
33+ Right $ Assign "z" (relvarId "s"),
36− Assign "z" (Restrict TruePredicate (relvar "s"))
34+ Assign "z" (Restrict TruePredicate (relvar "s"))
37− ),
35+ ),
38− ("StaticMultipleExpr",
36+ ("StaticMultipleExpr",
39− Right $ MultipleExpr [Assign "z" (relvar "s"),
37+ Right $ MultipleExpr [Assign "z" (relvarId "s"),
40− Assign "z" (relvar "s")],
38+ Assign "z" (relvarId "s")],
41− MultipleExpr [Assign "z" (Restrict TruePredicate (relvar "s")),
39+ MultipleExpr [Assign "z" (Restrict TruePredicate (relvar "s")),
42− Assign "z" (Restrict TruePredicate (relvar "s"))])
40+ Assign "z" (Restrict TruePredicate (relvar "s"))])
43− ]
41+ ]
42+ forM_ testInfos $ \(name, expectedExpr, unoptExpr) -> do
43+ let optExpr = runGraphRefSOptDatabaseContextExprM transId dateExamples graph (optimizeDatabaseContextExpr unoptExpr)
44+ assertEqual name expectedExpr optExpr
44
45
45testTransitiveOptimization :: Test
46testTransitiveOptimization :: Test
46testTransitiveOptimization = TestCase $ do
47testTransitiveOptimization = TestCase $ do
48+ (graph, _) <- freshTransactionGraph dateExamples
47 let expr atomexpr = AndPredicate (AttributeEqualityPredicate "a" (NakedAtomExpr (IntegerAtom 300))) (AttributeEqualityPredicate "b" atomexpr)
49 let expr atomexpr = AndPredicate (AttributeEqualityPredicate "a" (NakedAtomExpr (IntegerAtom 300))) (AttributeEqualityPredicate "b" atomexpr)
48 exprIn = expr (AttributeAtomExpr "a")
50 exprIn = expr (AttributeAtomExpr "a")
49 exprOut = expr (NakedAtomExpr (IntegerAtom 300))
51 exprOut = expr (NakedAtomExpr (IntegerAtom 300))
52+ optExpr = runGraphRefSOptRelationalExprM Nothing graph (applyStaticPredicateOptimization exprIn)
50− assertEqual "transitive property" (Right exprOut) (runReader (applyStaticPredicateOptimization exprIn) (RelationalExprStateElems dateExamples))
53+ assertEqual "transitive property" (Right exprOut) optExpr
51−
54+
52−optRelExpr :: RelationalExpr -> Either RelationalError RelationalExpr
55+
53−optRelExpr expr = runReader (applyStaticRelationalOptimization expr) (RelationalExprStateElems dateExamples)
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
54
74
55testImpossiblePredicates :: Test
75testImpossiblePredicates :: Test
56testImpossiblePredicates = TestCase $ do
76testImpossiblePredicates = TestCase $ do
77+ (graph, _) <- freshTransactionGraph dateExamples
57 let tautTrue = Restrict (AtomExprPredicate (NakedAtomExpr (BoolAtom True))) rel
78 let tautTrue = Restrict (AtomExprPredicate (NakedAtomExpr (BoolAtom True))) rel
58 tautTrue2 = Restrict TruePredicate rel
79 tautTrue2 = Restrict TruePredicate rel
59 tautFalse = Restrict (AtomExprPredicate (NakedAtomExpr (BoolAtom False))) rel
80 tautFalse = Restrict (AtomExprPredicate (NakedAtomExpr (BoolAtom False))) rel
60 tautFalse2 = Restrict (NotPredicate TruePredicate) rel
81 tautFalse2 = Restrict (NotPredicate TruePredicate) rel
61 emptyRel = MakeStaticRelation (attributes suppliersRel) emptyTupleSet
82 emptyRel = MakeStaticRelation (attributes suppliersRel) emptyTupleSet
62− rel = RelationVariable "s" ()
83+ rel = RelationVariable "s" UncommittedContextMarker
84+ optRelExpr expr = optimizeGraphRefRelationalExpr' (Just dateExamples) graph expr
63 assertEqual "remove tautology where true" (Right rel) (optRelExpr tautTrue)
85 assertEqual "remove tautology where true" (Right rel) (optRelExpr tautTrue)
64 assertEqual "remove tautology where true2" (Right rel) (optRelExpr tautTrue2)
86 assertEqual "remove tautology where true2" (Right rel) (optRelExpr tautTrue2)
65 assertEqual "remove tautology where false" (Right emptyRel) (optRelExpr tautFalse)
87 assertEqual "remove tautology where false" (Right emptyRel) (optRelExpr tautFalse)
66 assertEqual "remove tautology where false2" (Right emptyRel) (optRelExpr tautFalse2)
88 assertEqual "remove tautology where false2" (Right emptyRel) (optRelExpr tautFalse2)
67−
68−optJoinExpr :: RelationalExpr -> Either RelationalError RelationalExpr
69−optJoinExpr expr = runReader (applyStaticJoinElimination expr) (RelationalExprStateElems dateExamples)
70
89
71testJoinElimination :: Test
90testJoinElimination :: Test
72testJoinElimination = TestCase $ do
91testJoinElimination = 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+
73 -- (s join sp){s#,qty,p#} == sp
97 -- (s join sp){s#,qty,p#} == sp
74− let unoptExpr = Project (AttributeNames (S.fromList ["p#", "qty", "s#"])) (Join (RelationVariable "sp" ()) (RelationVariable "s" ()))
98+ let unoptExpr = Project (AttributeNames (S.fromList ["p#", "qty", "s#"])) (Join (relvar "sp") (relvar "s"))
75− assertEqual "remove extraneous join" (Right (RelationVariable "sp" ())) (optJoinExpr unoptExpr)
99+ assertEqual "remove extraneous join" (Right (relvar "sp")) (optJoinExpr unoptExpr)
76
100
77-- (sp join s){s#,qty,p#} == sp --flip relvar names
101-- (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" ()))
102+ let unoptExpr2 = Project (AttributeNames (S.fromList ["p#", "qty", "s#"])) (Join (relvar "s") (relvar "sp"))
79− assertEqual "remove extraneous join2" (Right (RelationVariable "sp" ())) (optJoinExpr unoptExpr2)
103+ assertEqual "remove extraneous join2" (Right (relvar "sp")) (optJoinExpr unoptExpr2)
80
104
81-- (sp join s){s#,qty,sname} != sp - attribute from s included
105-- (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"))
83 assertEqual "retain join" (Right expr3) (optJoinExpr expr3)
107 assertEqual "retain join" (Right expr3) (optJoinExpr expr3)
84
108
85 -- (sp join s){qty} != sp --projection does not include foreign key attribute
109 -- (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"))
87 assertEqual "retain join2" (Right expr4) (optJoinExpr expr4)
111 assertEqual "retain join2" (Right expr4) (optJoinExpr expr4)
88
112
89 -- (sp join s){s#,qty} == sp - a subset of attributes in the projection are in the foreign key
113 -- (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" ()))
114+ let expr5 = Project (AttributeNames (S.fromList ["s#", "qty"])) (Join (relvar "sp") (relvar "s"))
91− let expect5 = Project (AttributeNames (S.fromList ["s#", "qty"])) (RelationVariable "sp" ())
115+ let expect5 = Project (AttributeNames (S.fromList ["s#", "qty"])) (relvar "sp")
92 assertEqual "remove extraneous join (attribute subset)" (Right expect5) (optJoinExpr expr5)
116 assertEqual "remove extraneous join (attribute subset)" (Right expect5) (optJoinExpr expr5)
93
117
94 -- (s join p){s#,p#} != sp -- no foreign key
118 -- (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"))
96 assertEqual "retain join- no foreign key" (Right expr6) (optJoinExpr expr6)
120 assertEqual "retain join- no foreign key" (Right expr6) (optJoinExpr expr6)
97
121
98 -- (s join p){s#,qty,p#,sname} != sp -- all sp attributes plus one s attributes
122 -- (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"))
100 assertEqual "retain join- attributes from both relvars" (Right expr7) (optJoinExpr expr7)
124 assertEqual "retain join- attributes from both relvars" (Right expr7) (optJoinExpr expr7)
101
125
102testRestrictionPredicateCollapse :: Test
126testRestrictionPredicateCollapse :: Test
103testRestrictionPredicateCollapse = TestCase $ do
127testRestrictionPredicateCollapse = TestCase $ do
104 let expr1 = Restrict (NotPredicate TruePredicate) rv
128 let expr1 = Restrict (NotPredicate TruePredicate) rv
105− rv = RelationVariable "a" ()
129+ rv = RelationVariable "a" UncommittedContextMarker
130+ relvar name = RelationVariable name UncommittedContextMarker
106 assertEqual "one restriction" expr1 (applyStaticRestrictionCollapse expr1)
131 assertEqual "one restriction" expr1 (applyStaticRestrictionCollapse expr1)
107
132
108 let attrEqualsPred = AttributeEqualityPredicate "x" (NakedAtomExpr (IntAtom 3))
133 let attrEqualsPred = AttributeEqualityPredicate "x" (NakedAtomExpr (IntAtom 3))
109− expr2 = Restrict TruePredicate (Restrict attrEqualsPred (RelationVariable "a" ()))
134+ expr2 = Restrict TruePredicate (Restrict attrEqualsPred (relvar "a"))
110− assertEqual "two restrictions" (Restrict (AndPredicate TruePredicate attrEqualsPred) (RelationVariable "a" ())) (applyStaticRestrictionCollapse expr2)
135+ assertEqual "two restrictions" (Restrict (AndPredicate TruePredicate attrEqualsPred) (relvar "a")) (applyStaticRestrictionCollapse expr2)
111
136
112− let expr3 = Restrict pred1 (Restrict pred2 (Restrict pred3 (RelationVariable "a" ())))
137+ let expr3 = Restrict pred1 (Restrict pred2 (Restrict pred3 (relvar "a")))
113 pred1 = NotPredicate TruePredicate
138 pred1 = NotPredicate TruePredicate
114 pred2 = attrEqualsPred
139 pred2 = attrEqualsPred
115 pred3 = AtomExprPredicate (NakedAtomExpr (BoolAtom True))
140 pred3 = AtomExprPredicate (NakedAtomExpr (BoolAtom True))
@@ -123,7 +148,7 @@ testRestrictionPredicateCollapse = TestCase $ do
123testPushDownRestrictionPredicate :: Test
148testPushDownRestrictionPredicate :: Test
124testPushDownRestrictionPredicate = TestCase $ do
149testPushDownRestrictionPredicate = TestCase $ do
125 -- (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)
150 -- (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
127 let status30 = AttributeEqualityPredicate "status" (NakedAtomExpr (IntAtom 30))
152 let status30 = AttributeEqualityPredicate "status" (NakedAtomExpr (IntAtom 30))
128 relationsOrError = do
153 relationsOrError = do
129 stevens <- mkRelationFromList (attributes suppliersRel) [[TextAtom "S6", TextAtom "Stevens", IntegerAtom 50, TextAtom "Boston"]]
154 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 #-}
2import Test.HUnit
2import Test.HUnit
3import ProjectM36.Tupleable
3import ProjectM36.Tupleable
4import ProjectM36.Atomable
4import ProjectM36.Atomable
test/Server/Main.hs+2−1
@@ -147,10 +147,11 @@ testPlanForDatabaseContextExpr sessionId conn = TestCase $ do
147 let attrExprs = [AttributeAndTypeNameExpr "x" (PrimitiveTypeConstructor "Int" IntAtomType) ()]
147 let attrExprs = [AttributeAndTypeNameExpr "x" (PrimitiveTypeConstructor "Int" IntAtomType) ()]
148 testrv = "testrv"
148 testrv = "testrv"
149 dbExpr = Define testrv attrExprs
149 dbExpr = Define testrv attrExprs
150+ expected = Define testrv [AttributeAndTypeNameExpr "x" (PrimitiveTypeConstructor "Int" IntAtomType) UncommittedContextMarker]
150 planResult <- planForDatabaseContextExpr sessionId conn dbExpr
151 planResult <- planForDatabaseContextExpr sessionId conn dbExpr
151 case planResult of
152 case planResult of
152 Left err -> assertFailure (show err)
153 Left err -> assertFailure (show err)
153− Right plan -> assertEqual "planForDatabaseContextExpr failure" dbExpr plan
154+ Right plan -> assertEqual "planForDatabaseContextExpr failure" expected plan
154
155
155testTransactionGraphAsRelation :: SessionId -> Connection -> Test
156testTransactionGraphAsRelation :: SessionId -> Connection -> Test
156testTransactionGraphAsRelation sessionId conn = TestCase $ do
157testTransactionGraphAsRelation sessionId conn = TestCase $ do
test/Server/WebSocket.hs+3−2
@@ -57,13 +57,14 @@ waitForListenSocket secondsToTry port =
57 if secondsToTry <= 0 then
57 if secondsToTry <= 0 then
58 throw WaitForSocketListenException
58 throw WaitForSocketListenException
59 else do
59 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))
61 sock <- socket AF_INET Stream defaultProtocol
62 sock <- socket AF_INET Stream defaultProtocol
62 let handler :: IOException -> IO ()
63 let handler :: IOException -> IO ()
63 handler _ = do
64 handler _ = do
64 threadDelay 1000000
65 threadDelay 1000000
65 waitForListenSocket (secondsToTry - 1) port
66 waitForListenSocket (secondsToTry - 1) port
66− catch (connect sock (SockAddrInet port hostaddr)) handler
67+ catch (connect sock (addrAddress hostaddr)) handler
67
68
68main :: IO ()
69main :: IO ()
69main = do
70main = do
test/TransactionGraph/Merge.hs+60−39
@@ -10,6 +10,7 @@ import ProjectM36.Key
10import qualified ProjectM36.DisconnectedTransaction as Discon
10import qualified ProjectM36.DisconnectedTransaction as Discon
11import qualified ProjectM36.DatabaseContext as DBC
11import qualified ProjectM36.DatabaseContext as DBC
12import ProjectM36.RelationalExpression
12import ProjectM36.RelationalExpression
13+import ProjectM36.StaticOptimizer
13
14
14import qualified Data.ByteString.Lazy as BS
15import qualified Data.ByteString.Lazy as BS
15import System.Exit
16import System.Exit
@@ -20,8 +21,7 @@ import qualified Data.Map as M
20import Data.Maybe
21import Data.Maybe
21import Data.Time.Clock
22import Data.Time.Clock
22import Data.Time.Calendar
23import Data.Time.Calendar
23−
24+import qualified Data.List.NonEmpty as NE
24−import Control.Monad.State
25
25
26{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}
26{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}
27
27
@@ -61,9 +61,10 @@ basicTransactionGraph = do
61 uuidA = fakeUUID 10
61 uuidA = fakeUUID 10
62 uuidB = fakeUUID 11
62 uuidB = fakeUUID 11
63 uuidRoot = fakeUUID 1
63 uuidRoot = fakeUUID 1
64+ parents' = uuidRoot NE.:| []
64 rootContext = concreteDatabaseContext rootTrans
65 rootContext = concreteDatabaseContext rootTrans
65− (_, bsGraph') <- addTransaction "branchA" (createTrans uuidA (TransactionInfo uuidRoot S.empty testTime) rootContext) bsGraph
66+ (_, bsGraph') <- addTransaction "branchA" (createTrans uuidA (TransactionInfo parents' testTime) rootContext) bsGraph
66− (_, bsGraph'') <- addTransaction "branchB" (createTrans uuidB (TransactionInfo uuidRoot S.empty testTime) rootContext) bsGraph'
67+ (_, bsGraph'') <- addTransaction "branchB" (createTrans uuidB (TransactionInfo parents' testTime) rootContext) bsGraph'
67 pure bsGraph''
68 pure bsGraph''
68
69
69addTransaction :: HeadName -> Transaction -> TransactionGraph -> IO (Transaction, TransactionGraph)
70addTransaction :: HeadName -> Transaction -> TransactionGraph -> IO (Transaction, TransactionGraph)
@@ -107,7 +108,7 @@ testSubGraphToFirstAncestorSnipBranch = TestCase $ do
107 baseGraph <- basicTransactionGraph
108 baseGraph <- basicTransactionGraph
108 transA <- assertMaybe (transactionForHead "branchA" baseGraph) "failed to get branchA"
109 transA <- assertMaybe (transactionForHead "branchA" baseGraph) "failed to get branchA"
109 transB <- assertMaybe (transactionForHead "branchB" baseGraph) "failed to get branchB"
110 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
111 subgraph <- assertEither $ subGraphOfFirstCommonAncestor graph (transactionHeadsForGraph baseGraph) transA transB S.empty
112 subgraph <- assertEither $ subGraphOfFirstCommonAncestor graph (transactionHeadsForGraph baseGraph) transA transB S.empty
112 assertGraph subgraph
113 assertGraph subgraph
113 let graphEq graphArg = S.map transactionId (transactionsForGraph graphArg)
114 let graphEq graphArg = S.map transactionId (transactionsForGraph graphArg)
@@ -121,16 +122,18 @@ testSubGraphToFirstAncestorMoreTransactions = TestCase $ do
121
122
122-- add another relvar to branchB
123-- add another relvar to branchB
123 branchBTrans <- assertMaybe (transactionForHead "branchB" graph) "failed to get branchB head"
124 branchBTrans <- assertMaybe (transactionForHead "branchB" graph) "failed to get branchB head"
125+ let env = mkDatabaseContextEvalEnv (transactionId branchBTrans) graph
126+ branchBContext = concreteDatabaseContext branchBTrans
124− (updatedBranchBContext,_,_) <- case runState (evalDatabaseContextExpr (Assign "branchBOnlyRelvar" (ExistingRelation relationTrue))) (freshDatabaseState (concreteDatabaseContext branchBTrans)) of
127+ updatedBranchBContext <- case runDatabaseContextEvalMonad branchBContext env (optimizeAndEvalDatabaseContextExpr True (Assign "branchBOnlyRelvar" (ExistingRelation relationTrue))) of
125− (Left err, _) -> assertFailure (show err) >> undefined
128+ Left err -> assertFailure (show err) >> undefined
126− (Right (), context) -> pure context
129+ Right st -> pure $ dbc_context st
127− (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans) S.empty testTime) updatedBranchBContext) graph
130+ (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans NE.:| []) testTime) updatedBranchBContext) graph
128 branchBTrans' <- assertMaybe (transactionForHead "branchB" graph') "failed to get branchB head"
131 branchBTrans' <- assertMaybe (transactionForHead "branchB" graph') "failed to get branchB head"
129 assertEqual "branchB id 3" (fakeUUID 3) (transactionId branchBTrans')
132 assertEqual "branchB id 3" (fakeUUID 3) (transactionId branchBTrans')
130
133
131 -- add another transaction to branchA
134 -- add another transaction to branchA
132 branchATrans <- assertMaybe (transactionForHead "branchA" graph) "failed to get branchA head"
135 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'
134 branchATrans' <- assertMaybe (transactionForHead "branchA" graph'') "failed to get branchA head"
137 branchATrans' <- assertMaybe (transactionForHead "branchA" graph'') "failed to get branchA head"
135 assertEqual "branchA id 4" (fakeUUID 4) (transactionId branchATrans')
138 assertEqual "branchA id 4" (fakeUUID 4) (transactionId branchATrans')
136
139
@@ -148,12 +151,15 @@ testSelectedBranchMerge = TestCase $ do
148
151
149 -- add another relvar to branchB
152 -- add another relvar to branchB
150 branchBTrans <- assertMaybe (transactionForHead "branchB" graph) "failed to get branchB head"
153 branchBTrans <- assertMaybe (transactionForHead "branchB" graph) "failed to get branchB head"
151− (updatedBranchBContext,_,_) <- case runState (evalDatabaseContextExpr (Assign "branchBOnlyRelvar" (ExistingRelation relationTrue))) (freshDatabaseState (concreteDatabaseContext branchBTrans)) of
154+ let env = mkDatabaseContextEvalEnv (transactionId branchBTrans) graph
155+ branchBContext = concreteDatabaseContext branchBTrans
156+ updatedBranchBContext <- case runDatabaseContextEvalMonad branchBContext env (optimizeAndEvalDatabaseContextExpr True (Assign "branchBOnlyRelvar" (ExistingRelation relationTrue))) of
152− (Left err, _) -> assertFailure (show err) >> undefined
157+ Left err -> assertFailure (show err) >> undefined
153− (Right (), context) -> pure context
158+ Right st -> pure (dbc_context st)
154− (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans) S.empty testTime) updatedBranchBContext) graph
159+ (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans NE.:| []) testTime) updatedBranchBContext) graph
155 --create the merge transaction in the graph
160 --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'
157
163
158 (_, graph'') <- assertEither eGraph'
164 (_, graph'') <- assertEither eGraph'
159
165
@@ -175,18 +181,22 @@ testUnionPreferMergeStrategy = TestCase $ do
175 branchBTrans <- assertMaybe (transactionForHead "branchB" graph) "branchBTrans"
181 branchBTrans <- assertMaybe (transactionForHead "branchB" graph) "branchBTrans"
176 branchBRelVar <- assertEither $ mkRelationFromList (attributesFromList [Attribute "conflict" IntAtomType]) []
182 branchBRelVar <- assertEither $ mkRelationFromList (attributesFromList [Attribute "conflict" IntAtomType]) []
177 let branchAContext = (concreteDatabaseContext branchATrans) {relationVariables = M.insert conflictRelVarName branchARelVar (relationVariables (concreteDatabaseContext branchATrans))}
183 let branchAContext = (concreteDatabaseContext branchATrans) {relationVariables = M.insert conflictRelVarName branchARelVar (relationVariables (concreteDatabaseContext branchATrans))}
178− branchARelVar = relationTrue
184+ branchARelVar = ExistingRelation relationTrue
179− branchBContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.insert conflictRelVarName branchBRelVar (relationVariables (concreteDatabaseContext branchBTrans))}
185+ branchBContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.insert conflictRelVarName (ExistingRelation branchBRelVar) (relationVariables (concreteDatabaseContext branchBTrans))}
180− conflictRelVarName = "conflictRelVar"
186+ conflictRelVarName = "conflictRelVar"
181
187
182− (_, graph') <- addTransaction "branchA" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchATrans) S.empty testTime) branchAContext) graph
188+ (_, graph') <- addTransaction "branchA" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchATrans NE.:| []) testTime) branchAContext) graph
183− (_, graph'') <- addTransaction "branchB" (createTrans (fakeUUID 4) (TransactionInfo (transactionId branchBTrans) S.empty testTime) branchBContext) graph'
189+ (_, graph'') <- addTransaction "branchB" (createTrans (fakeUUID 4) (TransactionInfo (transactionId branchBTrans NE.:| []) testTime) branchBContext) graph'
184 -- validate that the conflict is hidden because we preferred a branch
190 -- 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''
186 case merged of
193 case merged of
187 Left err -> assertFailure ("expected merge success: " ++ show err)
194 Left err -> assertFailure ("expected merge success: " ++ show err)
188− Right (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)
189− assertEqual "branchB relvar preferred in conflict" (Just branchBRelVar) (M.lookup conflictRelVarName (relationVariables (Discon.concreteDatabaseContext discon)))
199+ assertEqual "branchB relvar preferred in conflict" (Right branchBRelVar) eRvRel
190
200
191-- try various individual component conflicts and check for merge failure
201-- try various individual component conflicts and check for merge failure
192testUnionMergeStrategy :: Test
202testUnionMergeStrategy :: Test
@@ -201,31 +211,41 @@ testUnionMergeStrategy = TestCase $ do
201
211
202 let updatedBranchBContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.insert branchBOnlyRelVarName branchBOnlyRelVar (relationVariables (concreteDatabaseContext branchBTrans)) }
212 let updatedBranchBContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.insert branchBOnlyRelVarName branchBOnlyRelVar (relationVariables (concreteDatabaseContext branchBTrans)) }
203 updatedBranchAContext = (concreteDatabaseContext branchATrans) {inclusionDependencies = M.insert branchAOnlyIncDepName branchAOnlyIncDep (inclusionDependencies (concreteDatabaseContext branchATrans)) }
213 updatedBranchAContext = (concreteDatabaseContext branchATrans) {inclusionDependencies = M.insert branchAOnlyIncDepName branchAOnlyIncDep (inclusionDependencies (concreteDatabaseContext branchATrans)) }
204− branchBOnlyRelVar = relationTrue
214+ branchBOnlyRelVar = ExistingRelation relationTrue
205 branchAOnlyIncDepName = "branchAOnlyIncDep"
215 branchAOnlyIncDepName = "branchAOnlyIncDep"
206 branchBOnlyRelVarName = "branchBOnlyRelVar"
216 branchBOnlyRelVarName = "branchBOnlyRelVar"
207 branchAOnlyIncDep = InclusionDependency (ExistingRelation relationTrue) (ExistingRelation relationTrue)
217 branchAOnlyIncDep = InclusionDependency (ExistingRelation relationTrue) (ExistingRelation relationTrue)
208− (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans) S.empty testTime) updatedBranchBContext) graph
218+ (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans NE.:| []) testTime) updatedBranchBContext) graph
219+ let env = freshGraphRefRelationalExprEnv Nothing graph'
209− (discon, _) <- assertEither $ mergeTransactions testTime (fakeUUID 5) (fakeUUID 10) UnionMergeStrategy ("branchA", "branchB") 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+
210− assertEqual "branchBOnlyRelVar should appear in the merge" (M.lookup branchBOnlyRelVarName (relationVariables (Discon.concreteDatabaseContext discon))) (Just relationTrue)
225+ assertEqual "branchBOnlyRelVar should appear in the merge" relationTrue rvRel
211− (_, graph'') <- addTransaction "branchA" (createTrans (fakeUUID 4) (TransactionInfo (transactionId branchATrans) S.empty testTime) updatedBranchAContext) graph'
226+ (_, graph'') <- addTransaction "branchA" (createTrans (fakeUUID 4) (TransactionInfo (transactionId branchATrans NE.:| []) testTime) updatedBranchAContext) graph'
212− let eMergeGraph = mergeTransactions testTime (fakeUUID 5) (fakeUUID 3) UnionMergeStrategy ("branchA", "branchB") graph''
227+ let eMergeGraph = runGraphRefRelationalExprM gfEnv $ mergeTransactions testTime (fakeUUID 5) (fakeUUID 3) UnionMergeStrategy ("branchA", "branchB")
228+ gfEnv = freshGraphRefRelationalExprEnv Nothing graph''
213 case eMergeGraph of
229 case eMergeGraph of
214 Left err -> assertFailure ("expected merge success: " ++ show err)
230 Left err -> assertFailure ("expected merge success: " ++ show err)
215 Right (_, mergeGraph) -> do
231 Right (_, mergeGraph) -> do
216 -- check that the new merge transaction has the correct parents
232 -- check that the new merge transaction has the correct parents
217 mergeTrans <- assertEither $ transactionForId (fakeUUID 5) mergeGraph
233 mergeTrans <- assertEither $ transactionForId (fakeUUID 5) mergeGraph
218 let mergeContext = concreteDatabaseContext mergeTrans
234 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])
220 -- check that the new merge tranasction has elements from both A and B branches
236 -- 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'
222 assertEqual "merge transaction incdeps" (Just branchAOnlyIncDep) (M.lookup branchAOnlyIncDepName (inclusionDependencies mergeContext))
241 assertEqual "merge transaction incdeps" (Just branchAOnlyIncDep) (M.lookup branchAOnlyIncDepName (inclusionDependencies mergeContext))
223 -- test an expected conflict- add branchBOnlyRelVar with same name but different attributes
242 -- test an expected conflict- add branchBOnlyRelVar with same name but different attributes
224 conflictRelVar <- assertEither $ mkRelationFromList (attributesFromList [Attribute "conflict" IntAtomType]) []
243 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) }
226 conflictBranchATrans <- assertMaybe (transactionForHead "branchA" graph'') "retrieving head transaction for expected conflict"
245 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''
246+ (_, graph''') <- addTransaction "branchA" (createTrans (fakeUUID 6) (TransactionInfo (transactionId conflictBranchATrans NE.:| []) testTime) conflictContextA) graph''
228− let failingMerge = mergeTransactions testTime (fakeUUID 5) (fakeUUID 3) UnionMergeStrategy ("branchA", "branchB") graph'''
247+ let failingMerge = runGraphRefRelationalExprM gfEnv' $ mergeTransactions testTime (fakeUUID 5) (fakeUUID 3) UnionMergeStrategy ("branchA", "branchB")
248+ gfEnv' = freshGraphRefRelationalExprEnv Nothing graph'''
229 case failingMerge of
249 case failingMerge of
230 Right _ -> assertFailure "expected merge failure"
250 Right _ -> assertFailure "expected merge failure"
231 Left err -> assertEqual "merge failure" err (MergeTransactionError StrategyViolatesRelationVariableMergeError)
251 Left err -> assertEqual "merge failure" err (MergeTransactionError StrategyViolatesRelationVariableMergeError)
@@ -244,20 +264,21 @@ testUnionMergeIncDepViolation = TestCase $ do
244 rvName = "x"
264 rvName = "x"
245 Right branchArv = eRel 2
265 Right branchArv = eRel 2
246 Right branchBrv = eRel 3
266 Right branchBrv = eRel 3
247− branchAContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.singleton rvName branchArv}
267+ branchAContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.singleton rvName (ExistingRelation branchArv)}
248− branchBContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.singleton rvName branchBrv,
268+ branchBContext = (concreteDatabaseContext branchBTrans) {relationVariables = M.singleton rvName (ExistingRelation branchBrv),
249 inclusionDependencies = M.singleton incDepName incDep}
269 inclusionDependencies = M.singleton incDepName incDep}
250 incDepName = "x_key"
270 incDepName = "x_key"
251 incDep = inclusionDependencyForKey (AttributeNames (S.singleton "x")) (RelationVariable "x" ())
271 incDep = inclusionDependencyForKey (AttributeNames (S.singleton "x")) (RelationVariable "x" ())
252
272
253
273
254 --add the rv in new commits to both branches
274 --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
256
276
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'
258
278
259 --check that the union merge fails due to a violated constraint
279 --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''
261 case eMerge of
282 case eMerge of
262 Left (InclusionDependencyCheckError incDepName') -> assertEqual "incdep violation name" incDepName incDepName'
283 Left (InclusionDependencyCheckError incDepName') -> assertEqual "incdep violation name" incDepName incDepName'
263 Left err -> assertFailure ("other error: " ++ show err)
284 Left err -> assertFailure ("other error: " ++ show err)
test/TransactionGraph/Persist.hs+8−8
@@ -32,8 +32,8 @@ testList = TestList [testBootstrapDB,
32 testFunctionPersistence]
32 testFunctionPersistence]
33
33
34
34
35−stamp :: UTCTime
35+stamp' :: UTCTime
36−stamp = UTCTime (fromGregorian 1980 01 01) (secondsToDiffTime 1000)
36+stamp' = UTCTime (fromGregorian 1980 01 01) (secondsToDiffTime 1000)
37
37
38{- bootstrap a database, ensure that it can be read -}
38{- bootstrap a database, ensure that it can be read -}
39testBootstrapDB :: Test
39testBootstrapDB :: Test
@@ -41,7 +41,7 @@ testBootstrapDB = TestCase $ withSystemTempDirectory "m36testdb" $ \tempdir -> d
41 let dbdir = tempdir </> "dbdir"
41 let dbdir = tempdir </> "dbdir"
42 freshId <- nextRandom
42 freshId <- nextRandom
43
43
44− _ <- bootstrapDatabaseDir NoDiskSync dbdir (bootstrapTransactionGraph stamp freshId dateExamples)
44+ _ <- bootstrapDatabaseDir NoDiskSync dbdir (bootstrapTransactionGraph stamp' freshId dateExamples)
45 loadedGraph <- transactionGraphLoad dbdir emptyTransactionGraph Nothing
45 loadedGraph <- transactionGraphLoad dbdir emptyTransactionGraph Nothing
46 assertBool "transactionGraphLoad" $ isRight loadedGraph
46 assertBool "transactionGraphLoad" $ isRight loadedGraph
47
47
@@ -51,17 +51,17 @@ testDBSimplePersistence = TestCase $ withSystemTempDirectory "m36testdb" $ \temp
51 let dbdir = tempdir </> "dbdir"
51 let dbdir = tempdir </> "dbdir"
52 freshId <- nextRandom
52 freshId <- nextRandom
53
53
54− let graph = bootstrapTransactionGraph stamp freshId dateExamples
54+ let graph = bootstrapTransactionGraph stamp' freshId dateExamples
55 _ <- bootstrapDatabaseDir NoDiskSync dbdir graph
55 _ <- bootstrapDatabaseDir NoDiskSync dbdir graph
56 case transactionForHead "master" graph of
56 case transactionForHead "master" graph of
57 Nothing -> assertFailure "Failed to retrieve head transaction for master branch."
57 Nothing -> assertFailure "Failed to retrieve head transaction for master branch."
58 Just headTrans ->
58 Just headTrans ->
59− case interpretDatabaseContextExpr (concreteDatabaseContext headTrans) "x:=s" of
59+ case interpretDatabaseContextExpr (concreteDatabaseContext headTrans) (transactionId headTrans) graph "x:=s" of
60 Left err -> assertFailure (show err)
60 Left err -> assertFailure (show err)
61 Right context' -> do
61 Right context' -> do
62 freshId' <- nextRandom
62 freshId' <- nextRandom
63− let newdiscon = DisconnectedTransaction (transactionId headTrans) (Schemas context' M.empty) True
63+ let newdiscon = DisconnectedTransaction (transactionId headTrans) (Schemas context' M.empty) False
64− addTrans = addDisconnectedTransaction stamp freshId' "master" newdiscon graph
64+ addTrans = addDisconnectedTransaction stamp' freshId' "master" newdiscon graph
65 --add a transaction to the graph
65 --add a transaction to the graph
66 case addTrans of
66 case addTrans of
67 Left err -> assertFailure (show err)
67 Left err -> assertFailure (show err)
@@ -99,7 +99,7 @@ testFunctionPersistence = TestCase $
99 conn2 <- assertIOEither $ connectProjectM36 connInfo
99 conn2 <- assertIOEither $ connectProjectM36 connInfo
100 sess2 <- assertIOEither $ createSessionAtHead conn2 "master"
100 sess2 <- assertIOEither $ createSessionAtHead conn2 "master"
101
101
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)] ()))]))
103 let expectedRel = mkRelationFromList (attributesFromList [Attribute "a" IntAtomType]) [[IntAtom 3]]
103 let expectedRel = mkRelationFromList (attributesFromList [Attribute "a" IntAtomType]) [[IntAtom 3]]
104 assertEqual "testdisk dbc function run" expectedRel res
104 assertEqual "testdisk dbc function run" expectedRel res
105
105
test/TutorialD/Interpreter.hs+50−21
@@ -9,6 +9,7 @@ import ProjectM36.TupleSet
9import ProjectM36.Error
9import ProjectM36.Error
10import ProjectM36.DatabaseContext
10import ProjectM36.DatabaseContext
11import ProjectM36.AtomFunctions.Primitive
11import ProjectM36.AtomFunctions.Primitive
12+import ProjectM36.RelationalExpression
12import ProjectM36.DataTypes.Either
13import ProjectM36.DataTypes.Either
13import ProjectM36.DataTypes.Interval
14import ProjectM36.DataTypes.Interval
14import ProjectM36.DataTypes.NonEmptyList
15import ProjectM36.DataTypes.NonEmptyList
@@ -39,8 +40,9 @@ main = do
39 tcounts <- runTestTT (TestList tests)
40 tcounts <- runTestTT (TestList tests)
40 if errors tcounts + failures tcounts > 0 then exitFailure else exitSuccess
41 if errors tcounts + failures tcounts > 0 then exitFailure else exitSuccess
41 where
42 where
42− tests = map (\(tutd, expected) -> TestCase $ assertTutdEqual basicDatabaseContext expected tutd) simpleRelTests ++
43+ tests = [
43− map (\(tutd, expected) -> TestCase $ assertTutdEqual dateExamples expected tutd) dateExampleRelTests ++ [
44+ simpleRelTests,
45+ dateExampleRelTests,
44 transactionGraphBasicTest,
46 transactionGraphBasicTest,
45 transactionGraphAddCommitTest,
47 transactionGraphAddCommitTest,
46 transactionRollbackTest,
48 transactionRollbackTest,
@@ -78,7 +80,13 @@ main = do
78 testBasicList,
80 testBasicList,
79 testRelationDeclarationMismatch
81 testRelationDeclarationMismatch
80 ]
82 ]
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
81− simpleRelTests = [("x:=true", Right relationTrue),
89+ testTups = [("x:=true", Right relationTrue),
82 ("x:=false", Right relationFalse),
90 ("x:=false", Right relationFalse),
83 ("x:=true union false", Right relationTrue),
91 ("x:=true union false", Right relationTrue),
84 ("x:=true minus false", Right relationTrue),
92 ("x:=true minus false", Right relationTrue),
@@ -117,24 +125,30 @@ main = do
117 simpleBAttributes = A.attributesFromList [Attribute "d" IntegerAtomType]
125 simpleBAttributes = A.attributesFromList [Attribute "d" IntegerAtomType]
118 simpleCAttributes = A.attributesFromList [Attribute "a" TextAtomType, Attribute "b" IntegerAtomType]
126 simpleCAttributes = A.attributesFromList [Attribute "a" TextAtomType, Attribute "b" IntegerAtomType]
119 simpleDAttributes = A.attributesFromList [Attribute "a" IntegerAtomType, Attribute "b" IntegerAtomType]
127 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)]
121 maybeIntegerAtomType = ConstructedAtomType "Maybe" (M.singleton "a" IntegerAtomType)
137 maybeIntegerAtomType = ConstructedAtomType "Maybe" (M.singleton "a" IntegerAtomType)
138+ maybeTextAtomType = ConstructedAtomType "Maybe" (M.singleton "a" TextAtomType)
122 simpleMaybeTextAttributes = A.attributesFromList [Attribute "a" maybeTextAtomType]
139 simpleMaybeTextAttributes = A.attributesFromList [Attribute "a" maybeTextAtomType]
123 simpleMaybeIntAttributes = A.attributesFromList [Attribute "a" maybeIntegerAtomType]
140 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)]
128 byteStringAttributes = A.attributesFromList [Attribute "y" ByteStringAtomType]
141 byteStringAttributes = A.attributesFromList [Attribute "y" ByteStringAtomType]
129− groupCountAttrs = A.attributesFromList [Attribute "z" IntegerAtomType]
130 minMaxAttrs = A.attributesFromList [Attribute "s#" TextAtomType, Attribute "z" IntegerAtomType]
142 minMaxAttrs = A.attributesFromList [Attribute "s#" TextAtomType, Attribute "z" IntegerAtomType]
143+ groupCountAttrs = A.attributesFromList [Attribute "z" IntegerAtomType]
131 updateParisPlus10 = relMap (\tuple -> do
144 updateParisPlus10 = relMap (\tuple -> do
132 statusAtom <- atomForAttributeName "status" tuple
145 statusAtom <- atomForAttributeName "status" tuple
133 cityAtom <- atomForAttributeName "city" tuple
146 cityAtom <- atomForAttributeName "city" tuple
134 if cityAtom == TextAtom "Paris" then
147 if cityAtom == TextAtom "Paris" then
135 Right $ updateTupleWithAtoms (M.singleton "status" (IntegerAtom (castInteger statusAtom + 10))) tuple
148 Right $ updateTupleWithAtoms (M.singleton "status" (IntegerAtom (castInteger statusAtom + 10))) tuple
136 else Right tuple) suppliersRel
149 else Right tuple) suppliersRel
150+ extendTestAttributes = A.attributesFromList [Attribute "a" IntegerAtomType, Attribute "b" $ RelationAtomType (R.attributes suppliersRel)]
137− dateExampleRelTests = [("x:=s where true", Right suppliersRel),
151+ testTups = [("x:=s where true", Right suppliersRel),
138 ("x:=s where city = \"London\"", restrict (\tuple -> pure $ atomForAttributeName "city" tuple == Right (TextAtom "London")) suppliersRel),
152 ("x:=s where city = \"London\"", restrict (\tuple -> pure $ atomForAttributeName "city" tuple == Right (TextAtom "London")) suppliersRel),
139 ("x:=s where false", Right $ Relation (R.attributes suppliersRel) emptyTupleSet),
153 ("x:=s where false", Right $ Relation (R.attributes suppliersRel) emptyTupleSet),
140 ("x:=p where color=\"Blue\" and city=\"Paris\"", mkRelationFromList (R.attributes productsRel) [[TextAtom "P5", TextAtom "Cam", TextAtom "Blue", IntegerAtom 12, TextAtom "Paris"]]),
154 ("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
177 ("x:=relation{tuple{a fromGregorian(2017,05,30)}}", mkRelationFromList (A.attributesFromList [Attribute "a" DayAtomType]) [[DayAtom (fromGregorian 2017 05 30)]])
191 ("x:=relation{tuple{a fromGregorian(2017,05,30)}}", mkRelationFromList (A.attributesFromList [Attribute "a" DayAtomType]) [[DayAtom (fromGregorian 2017 05 30)]])
178 ]
192 ]
179
193
180−assertTutdEqual :: DatabaseContext -> Either RelationalError Relation -> Text -> Assertion
194+assertTutdEqual :: DatabaseContext -> TransactionId -> TransactionGraph -> Either RelationalError Relation -> Text -> Assertion
181−assertTutdEqual databaseContext expected tutd = assertEqual (unpack tutd) expected interpreted
195+assertTutdEqual databaseContext transId graph expected tutd = assertEqual (unpack tutd) expected interpreted
182 where
196 where
183− interpreted = case interpretDatabaseContextExpr databaseContext tutd of
197+ interpreted = case interpretDatabaseContextExpr databaseContext transId graph tutd of
184 Left err -> Left err
198 Left err -> Left err
185 Right context -> case M.lookup "x" (relationVariables context) of
199 Right context -> case M.lookup "x" (relationVariables context) of
186 Nothing -> Left $ RelVarNotDefinedError "x"
200 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+
188
205
189transactionGraphBasicTest :: Test
206transactionGraphBasicTest :: Test
190transactionGraphBasicTest = TestCase $ do
207transactionGraphBasicTest = TestCase $ do
@@ -212,7 +229,7 @@ transactionGraphAddCommitTest = TestCase $ do
212 commit sessionId dbconn >>= eitherFail
229 commit sessionId dbconn >>= eitherFail
213 discon <- disconnectedTransaction_ sessionId dbconn
230 discon <- disconnectedTransaction_ sessionId dbconn
214 let context = Discon.concreteDatabaseContext discon
231 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))
216
233
217transactionRollbackTest :: Test
234transactionRollbackTest :: Test
218transactionRollbackTest = TestCase $ do
235transactionRollbackTest = TestCase $ do
@@ -254,12 +271,16 @@ transactionBranchTest = TestCase $ do
254
271
255-- test that overlapping attribute names with different types fail with an error
272-- test that overlapping attribute names with different types fail with an error
256failJoinTest :: Test
273failJoinTest :: Test
274+failJoinTest = TestCase $ do
275+ (graph, transId) <- freshTransactionGraph dateExamples
257−failJoinTest = TestCase $ assertTutdEqual basicDatabaseContext err "x:=relation{tuple{test 4}} join relation{tuple{test \"test\"}}"
276+ assertTutdEqual basicDatabaseContext transId graph err "x:=relation{tuple{test 4}} join relation{tuple{test \"test\"}}"
258 where
277 where
259 err = Left (TupleAttributeTypeMismatchError (A.attributesFromList [Attribute "test" IntegerAtomType]))
278 err = Left (TupleAttributeTypeMismatchError (A.attributesFromList [Attribute "test" IntegerAtomType]))
260
279
261simpleJoinTest :: Test
280simpleJoinTest :: Test
281+simpleJoinTest = TestCase $ do
282+ (graph, transId) <- freshTransactionGraph dateExamples
262−simpleJoinTest = TestCase $ assertTutdEqual dateExamples joinedRel "x:=s join sp"
283+ assertTutdEqual dateExamples transId graph joinedRel "x:=s join sp"
263 where
284 where
264 attrs = A.attributesFromList [Attribute "city" TextAtomType,
285 attrs = A.attributesFromList [Attribute "city" TextAtomType,
265 Attribute "qty" IntegerAtomType,
286 Attribute "qty" IntegerAtomType,
@@ -386,7 +407,9 @@ testTransGraphRelationalExpr = TestCase $ do
386 _ -> assertFailure "failed to delete branch"
407 _ -> assertFailure "failed to delete branch"
387
408
388testMultiAttributeRename :: Test
409testMultiAttributeRename :: Test
410+testMultiAttributeRename = TestCase $ do
411+ (graph, transId) <- freshTransactionGraph dateExamples
389−testMultiAttributeRename = TestCase $ assertTutdEqual dateExamples renamedRel "x:=s rename {city as town, status as price} where false"
412+ assertTutdEqual dateExamples transId graph renamedRel "x:=s rename {city as town, status as price} where false"
390 where
413 where
391 sattrs = attributesFromList [Attribute "town" TextAtomType,
414 sattrs = attributesFromList [Attribute "town" TextAtomType,
392 Attribute "sname" TextAtomType,
415 Attribute "sname" TextAtomType,
@@ -471,16 +494,22 @@ testEmptyCommits = TestCase $ do
471 assertEqual "empty insert empty commit" (Right False) dirty'
494 assertEqual "empty insert empty commit" (Right False) dirty'
472 Right () <- commit sessionId dbconn
495 Right () <- commit sessionId dbconn
473
496
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
475 Right () <- executeDatabaseContextExpr sessionId dbconn (Update "s" (M.singleton "sname" (NakedAtomExpr (TextAtom "Bob"))) (AttributeEqualityPredicate "sname" (NakedAtomExpr (TextAtom "Mike"))))
498 Right () <- executeDatabaseContextExpr sessionId dbconn (Update "s" (M.singleton "sname" (NakedAtomExpr (TextAtom "Bob"))) (AttributeEqualityPredicate "sname" (NakedAtomExpr (TextAtom "Mike"))))
476 dirty'' <- disconnectedTransactionIsDirty sessionId dbconn
499 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
478 Right () <- commit sessionId dbconn
507 Right () <- commit sessionId dbconn
479
508
480 --delete no tuples
509 --delete no tuples
481 Right () <- executeDatabaseContextExpr sessionId dbconn (Delete "s" (AttributeEqualityPredicate "sname" (NakedAtomExpr (TextAtom "Mike"))))
510 Right () <- executeDatabaseContextExpr sessionId dbconn (Delete "s" (AttributeEqualityPredicate "sname" (NakedAtomExpr (TextAtom "Mike"))))
482 dirty''' <- disconnectedTransactionIsDirty sessionId dbconn
511 dirty''' <- disconnectedTransactionIsDirty sessionId dbconn
483− assertEqual "empty delete empty commit" (Right False) dirty'''
512+ assertEqual "empty delete empty commit" (Right True) dirty'''
484
513
485testIntervalAtom :: Test
514testIntervalAtom :: Test
486testIntervalAtom = TestCase $ do
515testIntervalAtom = TestCase $ do
test/TutorialD/Interpreter/AtomFunctionScript.hs+7−7
@@ -36,7 +36,7 @@ testBasicAtomFunction = TestCase $ do
36 funcAtomExpr = FunctionAtomExpr "mkTest" [NakedAtomExpr (IntegerAtom 3)] ()
36 funcAtomExpr = FunctionAtomExpr "mkTest" [NakedAtomExpr (IntegerAtom 3)] ()
37 tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
37 tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
38 expectedResult = mkRelationFromList (V.fromList attrs) [[IntegerAtom 3]]
38 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))
40
40
41 assertEqual "simple atom function equality" expectedResult result
41 assertEqual "simple atom function equality" expectedResult result
42
42
@@ -47,7 +47,7 @@ testExceptionAtomFunction = TestCase $ do
47 executeTutorialD sess conn "addatomfunction \"mkTest\" Integer -> Either AtomFunctionError Integer \"\"\"(\\(IntegerAtom x:xs) -> (error (show 1))) :: [Atom] -> Either AtomFunctionError Atom\"\"\""
47 executeTutorialD sess conn "addatomfunction \"mkTest\" Integer -> Either AtomFunctionError Integer \"\"\"(\\(IntegerAtom x:xs) -> (error (show 1))) :: [Atom] -> Either AtomFunctionError Atom\"\"\""
48 let attrs = [Attribute "x" IntegerAtomType]
48 let attrs = [Attribute "x" IntegerAtomType]
49 funcAtomExpr = FunctionAtomExpr "mkTest" [NakedAtomExpr (IntegerAtom 3)] ()
49 funcAtomExpr = FunctionAtomExpr "mkTest" [NakedAtomExpr (IntegerAtom 3)] ()
50− tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
50+ tupleExprs = TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)]
51 result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
51 result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
52 assertBool "catch error exception from script" (case result of
52 assertBool "catch error exception from script" (case result of
53 Left (UnhandledExceptionError _) -> True
53 Left (UnhandledExceptionError _) -> True
@@ -64,7 +64,7 @@ testNoArgumentAtomFunction = TestCase $ do
64 executeTutorialD sess conn "addatomfunction \"mkTest\" Either AtomFunctionError Integer \"\"\"(\\x -> pure (IntegerAtom 5)) :: [Atom] -> Either AtomFunctionError Atom\"\"\""
64 executeTutorialD sess conn "addatomfunction \"mkTest\" Either AtomFunctionError Integer \"\"\"(\\x -> pure (IntegerAtom 5)) :: [Atom] -> Either AtomFunctionError Atom\"\"\""
65 let attrs = [Attribute "x" IntegerAtomType]
65 let attrs = [Attribute "x" IntegerAtomType]
66 funcAtomExpr = FunctionAtomExpr "mkTest" [] ()
66 funcAtomExpr = FunctionAtomExpr "mkTest" [] ()
67− tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
67+ tupleExprs = TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)]
68 expectedResult = mkRelationFromList (V.fromList attrs) [[IntegerAtom 5]]
68 expectedResult = mkRelationFromList (V.fromList attrs) [[IntegerAtom 5]]
69 result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
69 result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
70 assertEqual "no argument scripted function" expectedResult result
70 assertEqual "no argument scripted function" expectedResult result
@@ -73,7 +73,7 @@ testArgumentTypeMismatch :: Test
73testArgumentTypeMismatch = TestCase $ do
73testArgumentTypeMismatch = TestCase $ do
74 (sess, conn) <- dateExamplesConnection emptyNotificationCallback
74 (sess, conn) <- dateExamplesConnection emptyNotificationCallback
75 executeTutorialD sess conn "addatomfunction \"mkTest\" Integer -> Either AtomFunctionError Integer \"\"\"(\\(IntegerAtom x:_) -> pure $ TextAtom \"wrong type\") :: [Atom] -> Either AtomFunctionError Atom\"\"\""
75 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)]
77 funcAtomExpr = FunctionAtomExpr "mkTest" [NakedAtomExpr (IntegerAtom 3)] ()
77 funcAtomExpr = FunctionAtomExpr "mkTest" [NakedAtomExpr (IntegerAtom 3)] ()
78 attrs = [Attribute "x" IntegerAtomType]
78 attrs = [Attribute "x" IntegerAtomType]
79 expectedResult = Left (AtomTypeMismatchError IntegerAtomType TextAtomType)
79 expectedResult = Left (AtomTypeMismatchError IntegerAtomType TextAtomType)
@@ -87,7 +87,7 @@ testPolymorphicReturnType = TestCase $ do
87 let funcAtomExpr = FunctionAtomExpr "fromMaybe" [NakedAtomExpr (IntegerAtom 5),
87 let funcAtomExpr = FunctionAtomExpr "fromMaybe" [NakedAtomExpr (IntegerAtom 5),
88 NakedAtomExpr maybeAtom] ()
88 NakedAtomExpr maybeAtom] ()
89 maybeAtom = ConstructedAtom "Just" (maybeAtomType IntegerAtomType) [IntegerAtom 3]
89 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)])
91 mRelType <- typeForRelationalExpr sess conn relExpr
91 mRelType <- typeForRelationalExpr sess conn relExpr
92 case mRelType of
92 case mRelType of
93 Left err -> assertFailure (show err)
93 Left err -> assertFailure (show err)
@@ -101,7 +101,7 @@ testPolymorphicReturnType = TestCase $ do
101 let failFuncAtomExpr = FunctionAtomExpr "fromMaybe" [NakedAtomExpr (IntegerAtom 5),
101 let failFuncAtomExpr = FunctionAtomExpr "fromMaybe" [NakedAtomExpr (IntegerAtom 5),
102 NakedAtomExpr mismatchAtom] ()
102 NakedAtomExpr mismatchAtom] ()
103 mismatchAtom = ConstructedAtom "Just" (maybeAtomType TextAtomType) [TextAtom "fail"]
103 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)])
105 mFailRes <- executeRelationalExpr sess conn failRelExpr
105 mFailRes <- executeRelationalExpr sess conn failRelExpr
106 let expectedErr = Left (AtomFunctionTypeVariableMismatch "a" IntegerAtomType TextAtomType)
106 let expectedErr = Left (AtomFunctionTypeVariableMismatch "a" IntegerAtomType TextAtomType)
107 assertEqual "expected type variable mismatch" expectedErr mFailRes
107 assertEqual "expected type variable mismatch" expectedErr mFailRes
@@ -113,7 +113,7 @@ testScriptedTypeVariable = TestCase $ do
113 executeTutorialD sess conn "addatomfunction \"idTest\" a -> Either AtomFunctionError a \"(\\\\(x:_) -> pure x) :: [Atom] -> Either AtomFunctionError Atom\""
113 executeTutorialD sess conn "addatomfunction \"idTest\" a -> Either AtomFunctionError a \"(\\\\(x:_) -> pure x) :: [Atom] -> Either AtomFunctionError Atom\""
114 let attrs = [Attribute "x" IntegerAtomType]
114 let attrs = [Attribute "x" IntegerAtomType]
115 funcAtomExpr = FunctionAtomExpr "idTest" [NakedAtomExpr (IntegerAtom 3)] ()
115 funcAtomExpr = FunctionAtomExpr "idTest" [NakedAtomExpr (IntegerAtom 3)] ()
116− tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
116+ tupleExprs = TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)]
117 expectedResult = mkRelationFromList (V.fromList attrs) [[IntegerAtom 3]]
117 expectedResult = mkRelationFromList (V.fromList attrs) [[IntegerAtom 3]]
118 result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
118 result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
119
119
test/TutorialD/Interpreter/Import/TutorialD.hs+2−2
@@ -20,10 +20,10 @@ testTutdImport = TestCase $
20 hClose handle
20 hClose handle
21 let expectedExpr = MultipleExpr [
21 let expectedExpr = MultipleExpr [
22 Assign "x" (MakeRelationFromExprs Nothing
22 Assign "x" (MakeRelationFromExprs Nothing
23− [TupleExpr (M.fromList [("a", NakedAtomExpr $ IntegerAtom 5),
23+ $ TupleExprs () [TupleExpr (M.fromList [("a", NakedAtomExpr $ IntegerAtom 5),
24 ("b", NakedAtomExpr $ TextAtom "spam")])]),
24 ("b", NakedAtomExpr $ TextAtom "spam")])]),
25 Assign "y" (MakeRelationFromExprs Nothing
25 Assign "y" (MakeRelationFromExprs Nothing
26− [TupleExpr (M.fromList [("b", NakedAtomExpr (TextAtom "漢字"))])])]
26+ $ TupleExprs () [TupleExpr (M.fromList [("b", NakedAtomExpr (TextAtom "漢字"))])])]
27 imported <- importTutorialD tempPath
27 imported <- importTutorialD tempPath
28 assertEqual "import tutd" (Right expectedExpr) imported
28 assertEqual "import tutd" (Right expectedExpr) imported
29
29
test/TutorialD/Interpreter/TestBase.hs+10−13
@@ -2,16 +2,14 @@ module TutorialD.Interpreter.TestBase where
2import ProjectM36.Client
2import ProjectM36.Client
3import TutorialD.Interpreter
3import TutorialD.Interpreter
4import TutorialD.Interpreter.Base
4import TutorialD.Interpreter.Base
5−import qualified ProjectM36.Base as Base
6import ProjectM36.DateExamples
5import ProjectM36.DateExamples
6+import ProjectM36.DatabaseContext
7import Test.HUnit
7import Test.HUnit
8−import qualified Data.Map as M
9import Data.Text
8import Data.Text
10
9
11dateExamplesConnection :: NotificationCallback -> IO (SessionId, Connection)
10dateExamplesConnection :: NotificationCallback -> IO (SessionId, Connection)
12dateExamplesConnection callback = do
11dateExamplesConnection callback = do
13 dbconn <- connectProjectM36 (InProcessConnectionInfo NoPersistence callback [])
12 dbconn <- connectProjectM36 (InProcessConnectionInfo NoPersistence callback [])
14− let incDeps = Base.inclusionDependencies dateExamples
15 case dbconn of
13 case dbconn of
16 Left err -> error (show err)
14 Left err -> error (show err)
17 Right conn -> do
15 Right conn -> do
@@ -19,8 +17,7 @@ dateExamplesConnection callback = do
19 case eSessionId of
17 case eSessionId of
20 Left err -> error (show err)
18 Left err -> error (show err)
21 Right sessionId -> do
19 Right sessionId -> do
22− mapM_ (\(rvName,rvRel) -> executeDatabaseContextExpr sessionId conn (Assign rvName (Base.ExistingRelation rvRel))) (M.toList (Base.relationVariables dateExamples))
20+ executeDatabaseContextExpr sessionId conn (databaseContextAsDatabaseContextExpr dateExamples) >>= eitherFail
23− mapM_ (\(idName,incDep) -> executeDatabaseContextExpr sessionId conn (AddInclusionDependency idName incDep)) (M.toList incDeps)
24 --skipping atom functions for now- there are no atom function manipulation operators yet
21 --skipping atom functions for now- there are no atom function manipulation operators yet
25 commit sessionId conn >>= eitherFail
22 commit sessionId conn >>= eitherFail
26 pure (sessionId, conn)
23 pure (sessionId, conn)
@@ -31,13 +28,13 @@ executeTutorialD sessionId conn tutd = case parseTutorialD tutd of
31 Right parsed -> do
28 Right parsed -> do
32 result <- evalTutorialD sessionId conn UnsafeEvaluation parsed
29 result <- evalTutorialD sessionId conn UnsafeEvaluation parsed
33 case result of
30 case result of
34− QuitResult -> putStrLn "yyy" >> assertFailure "quit?"
31+ QuitResult -> assertFailure "quit?"
35− DisplayResult _ -> putStrLn "xxx" >> assertFailure "display?"
32+ DisplayResult _ -> assertFailure "display?"
36− DisplayIOResult _ -> putStrLn "z" >> assertFailure "displayIO?"
33+ DisplayIOResult _ -> assertFailure "displayIO?"
37− DisplayRelationResult _ -> putStrLn "Y" >> assertFailure "displayrelation?"
34+ DisplayRelationResult _ -> assertFailure "displayrelation?"
38− DisplayDataFrameResult _ -> putStrLn "DF" >> assertFailure "displaydataframe?"
35+ DisplayDataFrameResult _ -> assertFailure "displaydataframe?"
39− DisplayParseErrorResult _ _ -> putStrLn "X" >> assertFailure "displayparseerrorresult?"
36+ DisplayParseErrorResult _ _ -> assertFailure "displayparseerrorresult?"
40− DisplayErrorResult err -> putStrLn "asd" >> assertFailure (show tutd ++ ": " ++ show err)
37+ DisplayErrorResult err -> assertFailure (show tutd ++ ": " ++ show err)
41 QuietSuccessResult -> pure ()
38 QuietSuccessResult -> pure ()
42
39
43expectTutorialDErr :: SessionId -> Connection -> (Text -> Bool) -> Text -> IO ()
40expectTutorialDErr :: SessionId -> Connection -> (Text -> Bool) -> Text -> IO ()
@@ -52,7 +49,7 @@ expectTutorialDErr sessionId conn matchFunc tutd = case parseTutorialD tutd of
52 DisplayRelationResult _ -> assertFailure "displayrelation?"
49 DisplayRelationResult _ -> assertFailure "displayrelation?"
53 DisplayDataFrameResult _ -> assertFailure "displaydataframe?"
50 DisplayDataFrameResult _ -> assertFailure "displaydataframe?"
54 DisplayParseErrorResult _ _ -> assertFailure "displayparseerrorresult?"
51 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)
56 QuietSuccessResult -> pure ()
53 QuietSuccessResult -> pure ()
57
54
58eitherFail :: Either RelationalError a -> IO ()
55eitherFail :: Either RelationalError a -> IO ()