Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 82 additions & 2 deletions Optimizer/calloptimizer.pl
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,87 @@


/*
6 Shortcuts and Aliases
6 Normalizing SQL Identifiers Typed at the Prolog Toplevel

The interactive SecondoPL shell is the plain Prolog toplevel, so a capitalized
identifier within an SQL query is read as a Prolog variable and not as the name
of a relation or an attribute. A query such as

---- sql select count(*) from Trains where Trip passes mehringdamm.
----

therefore used to reach the optimizer with an unbound relation instead of
~trains~.

The other interfaces (the TTY, the client/server protocol and the JavaGUI) all
down-case the first character of every identifier before the query text is
handed to the optimizer. Because the toplevel reader has already turned the
identifiers into variables, we do the same one step later: the toplevel hook
~expand\_query/4~ reports the variable names, and every named variable
occurring in the SQL part of the goal is bound to the atom the user meant.

Only the argument holding the SQL query is treated this way, so output
arguments such as the plan and the cost of ~optimize/3~, the object name of a
~let~ and the executable rest of ~sql/2~ keep their spelling. Variables whose
name starts with an underscore are not reported by the reader and hence stay
free -- that is the escape hatch for an SQL argument that really should contain
a Prolog variable.

*/

% Position of the SQL query within the goals that can be typed at the toplevel.
sqlGoalArg(sql, 1, 1).
sqlGoalArg(sql, 2, 1).
sqlGoalArg(optimize, 1, 1).
sqlGoalArg(optimize, 3, 1).
sqlGoalArg(streamOptimize, 3, 1).
sqlGoalArg(mOptimize, 3, 1).
sqlGoalArg(mStreamOptimize, 3, 1).
sqlGoalArg(let, 2, 2).
sqlGoalArg(let, 3, 2).

% Down-case the first character only, exactly as the C++ interfaces do.
downcaseFirstChar(Name, Ident) :-
atom_chars(Name, [First | Rest]),
downcase_atom(First, Lower),
atom_chars(Ident, [Lower | Rest]).

identicalMember(X, [Y | _]) :-
X == Y, !.

identicalMember(X, [_ | Ys]) :-
identicalMember(X, Ys).

/*
Bind those toplevel variables that occur in the SQL part of the goal. The
bindings of all other variables are passed on, so that the toplevel still
reports them.

*/

bindSqlIdentifiers([], _, []).

bindSqlIdentifiers([Name = Var | Bindings], SqlVars, Rest) :-
var(Var),
identicalMember(Var, SqlVars), !,
downcaseFirstChar(Name, Ident),
Var = Ident,
bindSqlIdentifiers(Bindings, SqlVars, Rest).

bindSqlIdentifiers([Binding | Bindings], SqlVars, [Binding | Rest]) :-
bindSqlIdentifiers(Bindings, SqlVars, Rest).

user:expand_query(Goal, Goal, Bindings, Rest) :-
Bindings \= [],
functor(Goal, Functor, Arity),
sqlGoalArg(Functor, Arity, ArgNo),
arg(ArgNo, Goal, Sql),
term_variables(Sql, SqlVars),
SqlVars \= [],
bindSqlIdentifiers(Bindings, SqlVars, Rest).

/*
7 Shortcuts and Aliases

*/

Expand All @@ -1134,7 +1214,7 @@


/*
7 Testing
8 Testing

*/

Expand Down
8 changes: 7 additions & 1 deletion Optimizer/distributed.pl
Original file line number Diff line number Diff line change
Expand Up @@ -1358,8 +1358,14 @@
relation is not listed in SEC2DISTRIBUTED the unchanged name is returned in
Variable ~ORel~

*/
The first clause requires a bound argument. Without that test an unbound
~DRel~ unifies with the pattern of the clause head again and again, so that a
relation name the caller could not resolve ends in a stack overflow instead of
in the ``Unknown relation'' message of ~lookupRel/2~.

*/
removeDistributedSuffix(DRel as _, ORel) :-
nonvar(DRel),
removeDistributedSuffix(DRel, ORel),!.

removeDistributedSuffix(DRel, ORel) :-
Expand Down
Loading