diff --git a/solution.pl b/solution.pl new file mode 100644 index 0000000..56ccca3 --- /dev/null +++ b/solution.pl @@ -0,0 +1,188 @@ +/* +Prints the names of all existing tables, one per line (use writeln/1). +A table name is always an atom. +*/ +tables. + +:- dynamic tables/1 +/* +Unify Tables with a list of the names of all existing tables. +*/ +tables(Tables). + +:- dynamic table/2 +/* +When this predicate is executed, the effect will be the creation of a new +table with the specified list of column names (order matters!). +A column name is always an atom. +If a table with the given name already exists, the predicate must throw a +descriptive exception (use throw/1). +All exceptions must have a descriptive error message. +*/ +create table(Table, Cols). + +:- dynamic cols/2 +/* +Unifies Cols with the list of columns for the specified table (in the same +order as they were supplied to create table/2). +If the given table does not exist, the predicate must throw a descriptive +exception (use throw/1). +*/ +cols(Table, Cols). + +:- dynamic row/2 +/* +Unifies Row, one result at a time, with each row in the given Table. +If the given table does not exist, the predicate should fail. +*/ +row(Table, Row). + +:- dynamic rows/1 +/* +Displays all rows in the given table, one per line (use writeln/1). +If the given table does not exist, the predicate must throw a descriptive +exception. +*/ +rows(Table). + +:- dynamic insert/2 +/* +When this predicate is executed, the effect will be the addition of a given +row in the given table. The given row is a list of values for each of the +corresponding columns in the table (in the order in which the columns +were supplied to create table/2). +If the given table does not exist, the predicate must throw a descriptive +exception. +If the row does not have as many elements as the number of columns in +the table, the predicate must throw a descriptive exception. +*/ +insert(Table, Row). + +:- dynamic drop/1 +/* +When this predicate is executed, the effect will be the deletion of the given +table. +Do make sure that all of its rows are deleted as well, so that they don’t +magically reappear again if you would recreate a table with the same name +and signature later on. +If the given table does not exist, the predicate must throw a descriptive +exception. +*/ +drop(Table). + +:- dynamic delete/1 +/* +When this predicate is executed, the effect will be the deletion of all rows +in the given table. The table itself should still exist after, but with no +more rows. +If the given table does not exist, the predicate must throw a descriptive +exception. +*/ +delete(Table). + +:- dynamic delete/2 +/* +When this predicate is executed, the effect will be the deletion of all rows +from the given table that match all of the given conditions. The table +must still exist after. +If the given table does not exist, the predicate must throw a descriptive +exception. +A condition is any Prolog predicate that could have been typed at the +prompt, but which may include selectors. Selectors are terms of the form ++where should be replaced by a column name. +(See tests.pl for some concrete usage examples.) +*/ +delete(Table, Conds). + +:- dynamic selec/4 +/* +Note that the name of this predicate is selec (without t) for the simple +reason that select/4 is already a built-in Prolog predicate. +Table is the name of a single table. +Selectors is either * or a list of selectors. These define the resulting +projection. * means: select all column names from the table. Other +selectors explicitly specify which columns to pick. (See above for what +selectors look like.) For example, +name would select the column named +name. +Conds has the same form as in delete/2 and works the same way: only +rows that match all conditions are selected. +Finally, Projection unifies with /, where: +•is the list of requested selectors. +•is a list of values coming from a single row from the +given table that matches the conditions. This mean this predicate +should be able to backtrack to generate all projections that match +the query. +For example, selec(persons,[+id,+first],[],P) returns as first result +P = [+id, +first]/[0, "Jeffrey"]. +To obtain all projections that match the query, one could use the Prolog +query findall(X, selec(Table, Selectors, Conds, X), Projections). +For example: findall(X, selec(persons,[+last],[],X), Projections) +returns Projections = [[+last]/["Bowman"],[+last]/["Michaels"],. . . ] +Or if you only want the rows (since the selectors are repeated): +findall(X, selec(Table, Selectors, Conds, /X), Projections). +For example: +findall(Values, selec(persons,[+id,+first],[],Values), Projections) +returns: Projections = [[0, "Jeffrey"], [1, "Lorena"], [2, "Joseph"], ... +*/ +selec(Table, Selectors, Conds, Projection) + +:- dynamic selec/3 +/* +Simplified variant of the selec/4 predicate when there are no conditions +to be checked. +*/ +selec(TableOrTables, Selectors, Projection) + +:- dynamic query/2 +/* +where Query is a string whose syntax is defined by the following grammar: +⟨query⟩ ::= ⟨select⟩ | ⟨insert ⟩ +⟨select⟩ ::= SELECT ⟨selectors⟩ FROM ⟨table⟩ [⟨where⟩]; +⟨selectors⟩ ::= * | ⟨cols⟩ +⟨cols⟩ ::= ⟨col ⟩ [, ⟨cols⟩] +⟨where⟩ ::= WHERE ⟨cond ⟩ +⟨insert⟩ ::= INSERT INTO ⟨table⟩ [(⟨cols⟩)] VALUES (⟨values⟩); +⟨values⟩ ::= ⟨value⟩ [, ⟨values⟩] +About the notation: the pipe (|) and square brackets ([]) symbols in the +production rules above denote choice and optionality, respectively. +and denote table and column names (respectively). Ta- +ble names are atoms, while column names should follow the format out- +lined before (+). denotes a Prolog goal, in Prolog syntax +— following the same format as the possible values of items of the Conds +list passed to selec and delete. +denotes a prolog value that can be stored into a row. You do +not need to handle parsing Prolog (and ) by yourself, +we’ll show how to do it below. +The semantics of this predicate is that of the SQL-predicate contained in +the Query string. +A “SELECT” query maps to the selec predicate, while an “INSERT” +query maps to the insert predicate. For “SELECT”, all instances of +(appearing in ) are mapped to the Conds parameter. +map to the Selectors parameter. Tables names (or *) are +mapped to the Table parameter. +For “INSERT”, if the part is absent, the mapping to insert/2 +is straightforward. If is present, you will have to: + 1. reorder the values according to the columns that are present; + 2. fill in the missing columns (if any) with a null default values. + +Example: +query("INSERT INTO cities (+name, +state) +VALUES (\"Tempa\", \"Florida\");"). +maps to (for instance): +insert(cities, ["Tempa", "Florida"]). +When used with “SELECT”: +•query/1 must display the results a bit like the rows predicate would. +You have some flexibility here (displaying the selected column names +is a nice touch for instance). +•query/2 must pass the Result parameter as last parameter to the +selec/4 predicate. +For “INSERT”, nothing needs to be printed by query/1 or in case of +success, and if query/2 is used the Result parameter can be ignored. +*/ +query(Query, Result) + +:- dynamic query/1 +/* +cf. query/2 +*/ +query(Query) \ No newline at end of file