PersistentTable - Maple Help

Overview of the PersistentTable package

 Calling Sequence PersistentTable[command](arguments) command(arguments)

Description

 • The PersistentTable package provides a connection object that behaves somewhat like a table, except it is (by default) backed by a file containing an SQLite table. As a consequence, any information stored in the table persists when Maple is shut down or restarted. Furthermore, there is some extra functionality for searching through the stored information.
 • The interface is deliberately kept simple; it does not come close to giving access to all of SQLite's functionality. If you need more functionality, you can use the package Database[SQLite]. However, the PersistentTable package is easier to use for programmers who have experience with Maple, but not much experience with SQL. Conversely, if you don't need persistent storage or the extra searching functionality provided by this package, you are probably better off using a plain table.

List of PersistentTable Package Commands

 • The commands in the PersistentTable package are:

 • A PersistentTable object has a fixed number of columns with associated types, say k. The first column, or the first n columns for some positive integer n <= k, are taken to make up the primary key of the object. This means that when viewed as a key/value store, the object's keys are all expression sequences of n Maple expressions, and the values are expression sequences of n - k Maple expressions.

Caveats

 • Storing values in columns with data type anything converts them to the so-called "dot m" format, as generated by the %m specifier for the sprintf command and as described in the Format,m help page. Retrieving them translates them back to Maple. If your value will not survive such a process (for example, if it involves locals from modules), use of persistent tables will lead to unexpected results.
 • If float columns are used, be aware that the float operations done inside SQLite, and conversion to SQLite and back, do not necessarily comply with the rules with respect to precision that hold in Maple. Similarly, any constraints involving equality of expressions involving floating-point computation should be viewed with suspicion.

 • Internally, if there are multiple connection objects for the same file at one time in a single Maple session (presumably, though not necessarily, with a different prefix), and the file is specified in the same way for all invocations of Open (in particular, the string is the same after being run through FileTools[AbsolutePath]), only a single connection object is created through SQLite[Open]. It is required that either all of these connections are read-only, or none of them are. (Connections to different files can, of course, have different read-only status.)  Referring to the same file through different paths is not advised. SQLite enforces certain restrictions as to opening the same file in different concurrent Maple sessions; this is, therefore, also not advised.
 • This is the only thread safety issue - persistent tables are thread safe as of Maple 2021 otherwise.

Examples

 > $\mathrm{with}\left(\mathrm{PersistentTable}\right):$

Generate three file names that we can overwrite.

 > $\mathrm{path1}≔\mathrm{FileTools}:-\mathrm{JoinPath}\left(\left[\mathrm{FileTools}\left[\mathrm{TemporaryDirectory}\right]\left(\right),"foo.mks"\right]\right)$
 ${\mathrm{path1}}{≔}{"/tmp/mpldoc11/foo.mks"}$ (1)
 > $\mathrm{path2}≔\mathrm{FileTools}:-\mathrm{JoinPath}\left(\left[\mathrm{FileTools}\left[\mathrm{TemporaryDirectory}\right]\left(\right),"bar.mks"\right]\right)$
 ${\mathrm{path2}}{≔}{"/tmp/mpldoc11/bar.mks"}$ (2)
 > $\mathrm{path3}≔\mathrm{FileTools}:-\mathrm{JoinPath}\left(\left[\mathrm{FileTools}\left[\mathrm{TemporaryDirectory}\right]\left(\right),"baz.mks"\right]\right)$
 ${\mathrm{path3}}{≔}{"/tmp/mpldoc11/baz.mks"}$ (3)

Make sure these files do not currently exist.

 > $\mathrm{FileTools}\left[\mathrm{Remove}\right]\left(\mathrm{op}\left(\mathrm{select}\left(\mathrm{FileTools}\left[\mathrm{Exists}\right],\left[\mathrm{path1},\mathrm{path2},\mathrm{path3}\right]\right)\right)\right)$

Create the first persistent table: entries are indexed by two integers and the value is an arbitrary Maple expression.

 > $\mathrm{store1}≔\mathrm{Open}\left(\mathrm{path1},\mathrm{style}=\left[\mathrm{k1}::'\mathrm{integer}',\mathrm{k2}::'\mathrm{integer}',v::\mathrm{anything},\mathrm{primarykey}=2\right]\right)$
 ${\mathrm{store1}}{≔}{\mathrm{<< 3-column persistent table at /tmp/mpldoc11/foo.mks >>}}$ (4)

The expression that is the value can be, for example, a list.

 > $\mathrm{Set}\left(\mathrm{store1},\left[3,5\right],\left[\left[2,4,6\right],\left[1,3\right]\right]\right)$

One can access this value using the Get command or indexing.

 > $\mathrm{Get}\left(\mathrm{store1},\left[3,5\right]\right)$
 $\left[\left[{2}{,}{4}{,}{6}\right]{,}\left[{1}{,}{3}\right]\right]$ (5)
 > $\mathrm{store1}\left[3,5\right]$
 $\left[\left[{2}{,}{4}{,}{6}\right]{,}\left[{1}{,}{3}\right]\right]$ (6)

The Has and Count commands give us information about the existence of entries.

 > $\mathrm{Has}\left(\mathrm{store1},3,5\right)$
 ${\mathrm{true}}$ (7)
 > $\mathrm{Has}\left(\mathrm{store1},4,5\right)$
 ${\mathrm{false}}$ (8)
 > $\mathrm{Count}\left(\mathrm{store1},\mathrm{k1}=3\right)$
 ${1}$ (9)
 > $\mathrm{Count}\left(\mathrm{store1},\mathrm{k1}=4\right)$
 ${0}$ (10)
 > $\mathrm{Set}\left(\mathrm{store1},\left[3,5\right],\left[\left[2,4\right]\right]\right)$
 > $\mathrm{store1}\left[3,5\right]$
 $\left[\left[{2}{,}{4}\right]\right]$ (11)
 > $\mathrm{store1}\left[3,6\right]≔\left[\left[x,y\right]\right]$
 ${{\mathrm{store1}}}_{{3}{,}{6}}{≔}\left[\left[{x}{,}{y}\right]\right]$ (12)
 > $\mathrm{Count}\left(\mathrm{store1},\mathrm{k1}=3\right)$
 ${2}$ (13)

We can use the RawCommand command to manage transactions.

 > $\mathrm{RawCommand}\left(\mathrm{store1},"BEGIN TRANSACTION"\right)$
 > $\mathrm{store1}\left[3,2\right]≔\left[\left[\left[6\right]\right]\right]$
 ${{\mathrm{store1}}}_{{3}{,}{2}}{≔}\left[\left[\left[{6}\right]\right]\right]$ (14)
 > $\mathrm{Count}\left(\mathrm{store1},\mathrm{k1}=3\right)$
 ${3}$ (15)
 > $\mathrm{RawCommand}\left(\mathrm{store1},"ROLLBACK TRANSACTION"\right)$
 > $\mathrm{Count}\left(\mathrm{store1},\mathrm{k1}=3\right)$
 ${2}$ (16)
 > $\mathrm{store1}\left[3,2\right]≔\left[\left[\left[6\right]\right]\right]$
 ${{\mathrm{store1}}}_{{3}{,}{2}}{≔}\left[\left[\left[{6}\right]\right]\right]$ (17)
 > $\mathrm{Count}\left(\mathrm{store1},\mathrm{k1}=3\right)$
 ${3}$ (18)

The Get command generates an error if you try to access an entry that does not exist. Indexing is the same. The MaybeGet command instead returns a default value that you specify.

 > $\mathrm{MaybeGet}\left(\mathrm{store1},\left[3,6\right],\mathrm{FAIL}\right)$
 $\left[\left[{x}{,}{y}\right]\right]$ (19)
 > $\mathrm{MaybeGet}\left(\mathrm{store1},\left[3,3\right],\mathrm{FAIL}\right)$
 ${\mathrm{FAIL}}$ (20)
 > $\mathrm{store2}≔\mathrm{Open}\left(\mathrm{path2},\mathrm{style}=\left[\mathrm{k1}::'\mathrm{integer}',\mathrm{k2}::'\mathrm{integer}',v::\mathrm{anything},b::\mathrm{boolean},\mathrm{primarykey}=2\right]\right)$
 ${\mathrm{store2}}{≔}{\mathrm{<< 4-column persistent table at /tmp/mpldoc11/bar.mks >>}}$ (21)
 > $\mathrm{Get}\left(\mathrm{store2},\left[3,5\right]\right)$
 > $\mathrm{store2}\left[1,4\right]$

The GetAll command returns all rows satisfying some criterion. The GetKeys command returns information from the same rows, but returns only the primary key columns.

 > $\mathrm{store2}\left[1,4\right]≔\left[\mathrm{sqrt}\left(5\right)\right],\mathrm{true}$
 ${{\mathrm{store2}}}_{{1}{,}{4}}{≔}\left[\sqrt{{5}}\right]{,}{\mathrm{true}}$ (22)
 > $\mathrm{GetAll}\left(\mathrm{store2},b=\mathrm{true}\right)$
 $\left\{\left[{1}{,}{4}{,}\left[\sqrt{{5}}\right]{,}{\mathrm{true}}\right]\right\}$ (23)
 > $\mathrm{GetKeys}\left(\mathrm{store2},b=\mathrm{true}\right)$
 $\left\{\left[{1}{,}{4}\right]\right\}$ (24)
 > $\mathrm{Close}\left(\mathrm{store1}\right);$$\mathrm{Close}\left(\mathrm{store2}\right)$

You can't open two connections to the same file with different readonly settings, even if they have different prefixes:

 > $\mathrm{store3}≔\mathrm{Open}\left(\mathrm{path3},\mathrm{style}=\left[k::\mathrm{anything},v::\mathrm{anything}\right]\right)$
 ${\mathrm{store3}}{≔}{\mathrm{<< 2-column persistent table at /tmp/mpldoc11/baz.mks >>}}$ (25)
 > $\mathrm{store4}≔\mathrm{Open}\left(\mathrm{path3},'\mathrm{prefix}'="duck",\mathrm{style}=\left[\mathrm{k1}::'\mathrm{integer}',\mathrm{k2}::'\mathrm{integer}',\mathrm{v1}::\mathrm{anything},\mathrm{v2}::'\mathrm{integer}',\mathrm{v3}::\mathrm{anything},\mathrm{primarykey}=2,'\mathrm{index}'\left(\mathrm{v2}\right)\right],'\mathrm{readonly}'=\mathrm{true}\right)$
 > $\mathrm{store4}≔\mathrm{Open}\left(\mathrm{path3},'\mathrm{prefix}'="duck",\mathrm{style}=\left[\mathrm{k1}::'\mathrm{integer}',\mathrm{k2}::'\mathrm{integer}',\mathrm{v1}::\mathrm{anything},\mathrm{v2}::'\mathrm{integer}',\mathrm{v3}::\mathrm{anything},\mathrm{primarykey}=2,'\mathrm{index}'\left(\mathrm{v2}\right)\right]\right)$
 ${\mathrm{store4}}{≔}{\mathrm{<< 5-column persistent table at /tmp/mpldoc11/baz.mks with prefix duck_ >>}}$ (26)
 > $\mathrm{store5}≔\mathrm{Open}\left(\mathrm{path3},'\mathrm{prefix}'="drijfsijs",\mathrm{style}=\left[\mathrm{k1}::'\mathrm{integer}',\mathrm{v1}::\mathrm{float},\mathrm{v2}::\mathrm{float}\right]\right)$
 ${\mathrm{store5}}{≔}{\mathrm{<< 3-column persistent table at /tmp/mpldoc11/baz.mks with prefix drijfsijs_ >>}}$ (27)
 > $\mathrm{store3}\left[1+x\right]≔y$
 ${{\mathrm{store3}}}_{{1}{+}{x}}{≔}{y}$ (28)
 > $\mathrm{store3}\left[\left[3,4\right]\right]≔y$
 ${{\mathrm{store3}}}_{\left[{3}{,}{4}\right]}{≔}{y}$ (29)
 > $\mathrm{Has}\left(\mathrm{store3},1\right)$
 ${\mathrm{false}}$ (30)
 > $\mathrm{Has}\left(\mathrm{store3},1+x\right)$
 ${\mathrm{true}}$ (31)
 > $\mathrm{Has}\left(\mathrm{store4},3,4\right)$
 ${\mathrm{false}}$ (32)
 > $\mathrm{store4}\left[3,4\right]≔"duck",2,"bill"$
 ${{\mathrm{store4}}}_{{3}{,}{4}}{≔}{"duck"}{,}{2}{,}{"bill"}$ (33)
 > $\mathrm{store4}\left[2,4\right]≔"goose",3,"egg"$
 ${{\mathrm{store4}}}_{{2}{,}{4}}{≔}{"goose"}{,}{3}{,}{"egg"}$ (34)
 > $\mathrm{Has}\left(\mathrm{store4},3,4\right)$
 ${\mathrm{true}}$ (35)
 > $\mathrm{store3}\left[1+x\right]$
 ${y}$ (36)
 > $\mathrm{store4}\left[3,4\right]$
 ${"duck"}{,}{2}{,}{"bill"}$ (37)
 > $\mathrm{store4}\left[2,4\right]$
 ${"goose"}{,}{3}{,}{"egg"}$ (38)
 > $\mathrm{GetAll}\left(\mathrm{store4},\mathrm{v2}=3\right)$
 $\left\{\left[{2}{,}{4}{,}{"goose"}{,}{3}{,}{"egg"}\right]\right\}$ (39)
 > $\mathrm{GetAll}\left(\mathrm{store4},\mathrm{v2}=4\right)$
 ${\varnothing }$ (40)

Because the key and value are arbitrary Maple expressions, you cannot use inequalities when calling GetAll on store3: the comparison needs to be carried out inside the database engine, and it does not understand Maple expressions.

 > $\mathrm{GetAll}\left(\mathrm{store3},k<5\right)$

However, store4 supports arbitrary polynomial conditions involving its integer columns:

 > $\mathrm{GetAll}\left(\mathrm{store4},{\mathrm{k1}}^{3}<{\left(\mathrm{k2}+\frac{\mathrm{v2}}{2}\right)}^{2}\right)$
 $\left\{\left[{2}{,}{4}{,}{"goose"}{,}{3}{,}{"egg"}\right]\right\}$ (41)
 > $\mathrm{Set}\left(\mathrm{store5},\left[3\right],2.1,3.5\right)$
 > $\mathrm{Set}\left(\mathrm{store5},\left[7\right],Float\left(\mathrm{\infty }\right),7.4\right)$
 > $\mathrm{GetAll}\left(\mathrm{store5}\right)$
 $\left\{\left[{3}{,}{2.10000000000000009}{,}{3.50000000000000000}\right]{,}\left[{7}{,}{Float}{}\left({\mathrm{\infty }}\right){,}{7.40000000000000036}\right]\right\}$ (42)
 > $\mathrm{GetAll}\left(\mathrm{store5},\mathrm{k1}<\mathrm{v1}\right)$
 $\left\{\left[{7}{,}{Float}{}\left({\mathrm{\infty }}\right){,}{7.40000000000000036}\right]\right\}$ (43)
 > $\mathrm{Close}\left(\mathrm{store3}\right);$$\mathrm{Close}\left(\mathrm{store4}\right);$$\mathrm{Close}\left(\mathrm{store5}\right)$
 > $\mathrm{restart}$
 > $\mathrm{with}\left(\mathrm{PersistentTable}\right):$
 > $\mathrm{path1}≔\mathrm{FileTools}:-\mathrm{JoinPath}\left(\left[\mathrm{FileTools}\left[\mathrm{TemporaryDirectory}\right]\left(\right),"foo.mks"\right]\right)$
 ${\mathrm{path1}}{≔}{"/tmp/mpldoc11/foo.mks"}$ (44)
 > $\mathrm{path2}≔\mathrm{FileTools}:-\mathrm{JoinPath}\left(\left[\mathrm{FileTools}\left[\mathrm{TemporaryDirectory}\right]\left(\right),"bar.mks"\right]\right)$
 ${\mathrm{path2}}{≔}{"/tmp/mpldoc11/bar.mks"}$ (45)
 > $\mathrm{path3}≔\mathrm{FileTools}:-\mathrm{JoinPath}\left(\left[\mathrm{FileTools}\left[\mathrm{TemporaryDirectory}\right]\left(\right),"baz.mks"\right]\right)$
 ${\mathrm{path3}}{≔}{"/tmp/mpldoc11/baz.mks"}$ (46)

The files should still be present.

 > $\mathrm{store1}≔\mathrm{Open}\left(\mathrm{path1}\right)$
 ${\mathrm{store1}}{≔}{\mathrm{<< 3-column persistent table at /tmp/mpldoc11/foo.mks >>}}$ (47)
 > $\mathrm{store1}\left[3,5\right]$
 $\left[\left[{2}{,}{4}\right]\right]$ (48)
 > $\mathrm{Count}\left(\mathrm{store1},\mathrm{k1}=3\right)$
 ${3}$ (49)
 > $\mathrm{Count}\left(\mathrm{store1},\mathrm{k1}=4\right)$
 ${0}$ (50)
 > $\mathrm{MaybeGet}\left(\mathrm{store1},\left[3,6\right],\mathrm{FAIL}\right)$
 $\left[\left[{x}{,}{y}\right]\right]$ (51)
 > $\mathrm{MaybeGet}\left(\mathrm{store1},\left[3,3\right],\mathrm{FAIL}\right)$
 ${\mathrm{FAIL}}$ (52)
 > $\mathrm{store2}≔\mathrm{Open}\left(\mathrm{path2},\mathrm{readonly}\right)$
 ${\mathrm{store2}}{≔}{\mathrm{<< 4-column persistent table at /tmp/mpldoc11/bar.mks >>}}$ (53)
 > $\mathrm{store2}\left[1,4\right]$
 $\left[\sqrt{{5}}\right]{,}{\mathrm{true}}$ (54)
 > $\mathrm{store2}\left[1,4\right]≔6$
 > $\mathrm{GetAll}\left(\mathrm{store2},b=\mathrm{true}\right)$
 $\left\{\left[{1}{,}{4}{,}\left[\sqrt{{5}}\right]{,}{\mathrm{true}}\right]\right\}$ (55)
 > $\mathrm{Close}\left(\mathrm{store1}\right);$$\mathrm{Close}\left(\mathrm{store2}\right)$
 > $\mathrm{store3}≔\mathrm{Open}\left(\mathrm{path3}\right)$
 ${\mathrm{store3}}{≔}{\mathrm{<< 2-column persistent table at /tmp/mpldoc11/baz.mks >>}}$ (56)
 > $\mathrm{store4}≔\mathrm{Open}\left(\mathrm{path3},'\mathrm{prefix}'="duck"\right)$
 ${\mathrm{store4}}{≔}{\mathrm{<< 5-column persistent table at /tmp/mpldoc11/baz.mks with prefix duck_ >>}}$ (57)
 > $\mathrm{Has}\left(\mathrm{store3},1\right)$
 ${\mathrm{false}}$ (58)
 > $\mathrm{Has}\left(\mathrm{store3},1+x\right)$
 ${\mathrm{true}}$ (59)
 > $\mathrm{Has}\left(\mathrm{store4},3,4\right)$
 ${\mathrm{true}}$ (60)
 > $\mathrm{store3}\left[1+x\right]$
 ${y}$ (61)
 > $\mathrm{store4}\left[3,4\right]$
 ${"duck"}{,}{2}{,}{"bill"}$ (62)
 > $\mathrm{store4}\left[2,4\right]$
 ${"goose"}{,}{3}{,}{"egg"}$ (63)
 > $\mathrm{GetAll}\left(\mathrm{store4},\mathrm{v2}=3\right)$
 $\left\{\left[{2}{,}{4}{,}{"goose"}{,}{3}{,}{"egg"}\right]\right\}$ (64)
 > $\mathrm{GetAll}\left(\mathrm{store4},\mathrm{v2}=4\right)$
 ${\varnothing }$ (65)
 > $\mathrm{GetAll}\left(\mathrm{store4},{\mathrm{k1}}^{3}<{\left(\mathrm{k2}+\frac{\mathrm{v2}}{2}\right)}^{2}\right)$
 $\left\{\left[{2}{,}{4}{,}{"goose"}{,}{3}{,}{"egg"}\right]\right\}$ (66)
 > $\mathrm{Close}\left(\mathrm{store3}\right);$$\mathrm{Close}\left(\mathrm{store4}\right)$
 > $\mathrm{FileTools}:-\mathrm{Remove}\left(\mathrm{path1},\mathrm{path2},\mathrm{path3}\right)$

Compatibility

 • The PersistentTable package was introduced in Maple 2021.