% -*- Mode: Prolog -*- % -------------------------------------------------------------------------------------- % % This Prolog to SQL compiler may be distributed free of charge provided that it is % not used in commercial applications without written consent of the author, and % that the copyright notice remains unchanged. % % (C) Copyright by Christoph Draxler, Munich % Version 1.1 of Dec. 21st 1992 % % I would like to keep in my hands the further development and distribution of the % compiler. This does not mean that I don't want other people to suggest or even % implement improvements - quite on the contrary: I greatly appreciate contributions % and if they make sense to me I will incorporate them into the compiler (with due % credits given!). % % For further development of the compiler, address your requests, comments and % criticism to the author: % % Christoph Draxler % CIS Centre for Information and Speech Processing % Ludwig-Maximilians-University Munich % Wagmuellerstr. 23 % D 80538 Munich % Tel : ++49 / +89 / 211 06 64 (-60) % Fax : ++49 / +89 / 211 06 74 % Mail: draxler@cis.uni-muenchen.de % % % A report describing the implementation is available upon request from the % author. % % % RELEASE INFORMATION % =================== % Current version is v. 1.1 of Dec. 21st 1992. % Version 1.0 Sept. 3 1992 % -------------------------------------------------------------------------------------- :- module(myddas_prolog2sql,[ translate/3, queries_atom/2 ]). :- use_module(lists,[ append/3, member/2 ]). :- use_module(myddas_prolog2sql_optimizer,[ optimize_sql/2 ]). % -------------------------------------------------------------------------------------- % % Top level predicate translate/3 organizes the compilation and constructs a % Prolog term representation of the SQL query. % % -------------------------------------------------------------------------------------- #ifdef MYDDAS_STATS translate(ProjectionTerm,DatabaseGoal,SQLQueryTermOpt):- c_db_stats_walltime(Start), translate_(ProjectionTerm,DatabaseGoal,SQLQueryTermOpt), c_db_stats_walltime(End), c_db_stats_translate(Start,End). translate_(ProjectionTerm,DatabaseGoal,SQLQueryTermOpt):- #else translate(ProjectionTerm,DatabaseGoal,SQLQueryTermOpt):- #endif % --- initialize variable identifiers and range variables for relations ----- init_gensym(var), init_gensym(rel), % --- tokenize projection term and database goal ---------------------------- tokenize_term(DatabaseGoal,TokenDatabaseGoal), tokenize_term(ProjectionTerm,TokenProjectionTerm), % --- lexical analysis: reordering of goals for disjunctive normalized form - disjunction(TokenDatabaseGoal,Disjunction), % --- code generation --------------------------------------------------------------- query_generation(Disjunction,TokenProjectionTerm,SQLQueryTerm), % --- optimize sql ------------------------------------------------------------------ optimize_sql(SQLQueryTerm,SQLQueryTermOpt). % --- disjunction(Goal,Disjunction) ---------------------------------------------------- % % turns original goal into disjunctive normalized form by computing all conjunctions % and collecting them in a list % % -------------------------------------------------------------------------------------- disjunction(Goal,Disjunction):- findall(Conjunction,linearize(Goal,Conjunction),Disjunction). % --- linearize(Goal,ConjunctionList) -------------------------------------------------- % % Returns a conjunction of base goals for a complex disjunctive or conjunctive goal % Yields several solutions upon backtracking for disjunctive goals % % -------------------------------------------------------------------------------------- linearize(((A,B),C),(LinA,(LinB,LinC))):- % --- transform left-linear to right-linear conjunction (',' is associative) ---- linearize(A,LinA), linearize(B,LinB), linearize(C,LinC). linearize((A,B),(LinA,LinB)):- A \= (_,_), % --- make sure A is not a conjunction ------------------------------------------ linearize(A,LinA), linearize(B,LinB). % ILP %linearize((A;B),LinA):- linearize((A;_),LinA):- linearize(A,LinA). % ILP %linearize((A;B),LinB):- linearize((_;B),LinB):- linearize(B,LinB). linearize(not A, not LinA):- linearize(A,LinA). linearize(Var^A, Var^LinA):- linearize(A,LinA). linearize(A,A):- A \= (_,_), A \= (_;_), A \= _^_, A \= not(_). % --- tokenize_term(Term,TokenizedTerm) ------------------------------------------------- % % If Term is a % % - variable, then this variable is instantiated with a unique identifier % of the form '$var$'(VarId), and TokenizedTerm is bound to the same % term '$var$'(VarId). % % - constant, then TokenizedTerm is bound to '$const$'(Term). % % - complex term, then the term is decomposed, its arguments are tokenized, % and TokenizedTerm is bound to the result of the composition of the original % functor and the tokenized arguments. % % -------------------------------------------------------------------------------------- tokenize_term('$var$'(VarId),'$var$'(VarId)):- var(VarId), % --- uninstantiated variable: instantiate it with unique identifier. gensym(var,VarId). tokenize_term('$var$'(VarId),'$var$'(VarId)):- nonvar(VarId). tokenize_term(Constant,'$const$'(Constant)):- nonvar(Constant), functor(Constant,_,0). tokenize_term(Term,TokenizedTerm):- nonvar(Term), Term \= '$var$'(_), Term \= '$const$'(_), Term =.. [Functor|Arguments], Arguments \= [], tokenize_arguments(Arguments,TokenArguments), TokenizedTerm =.. [Functor|TokenArguments]. % --- tokenize_arguments(Arguments,TokenizedArguments) --------------------------------- % % organizes tokenization of arguments by traversing list and calling tokenize_term % for each element of the list. % % -------------------------------------------------------------------------------------- tokenize_arguments([],[]). tokenize_arguments([FirstArg|RestArgs],[TokFirstArg|TokRestArgs]):- tokenize_term(FirstArg,TokFirstArg), tokenize_arguments(RestArgs,TokRestArgs). % --- query_generation(ListOfConjunctions, ProjectionTerm, ListOfQueries) -------------- % % For each Conjunction translate the pair (ProjectionTerm,Conjunction) to an SQL query % and connect each such query through a UNION-operator to result in the ListOfQueries. % % A Conjunction consists of positive or negative subgoals. Each subgoal is translated % as follows: % - the functor of a goal that is not a comparison operation is translated to % a relation name with a range variable % - negated goals are translated to NOT EXISTS-subqueries with * projection % - comparison operations are translated to comparison operations in the WHERE-clause % - aggregate function terms are translated to aggregate function (sub)queries % % The arguments of a goal are translated as follows: % - variables of a goal are translated to qualified attributes % - variables occurring in several goals are translated to equality comparisons % (equi join) in the WHERE-clause % - constant arguments are translated to equality comparisons in the WHERE-clause % % Special treatment of arithmetic functions: % - arithmetic functions are identified through the Prolog is/2 operator % - an arithmetic function may contain an unbound variable only on its left side % - the right side of the is/2 operator may consist of % * bound variables (bound through occurrence within a positive database goal, or % bound through preceeding arithmetic function), or of % * constants (numbers, i.e. integers, reals) % % The following RESTRICTION holds: % % - the binding of variables follows Prolog: variables are bound by positive base goals % and on the left side of the is/2 predicate - comparison operations, negated goals % and right sides of the is/2 predicate do not return variable bindings and may even % require all arguments to be bound for a safe evaluation. % % -------------------------------------------------------------------------------------- query_generation([],_,[]). query_generation([Conjunction|Conjunctions],ProjectionTerm,[Query|Queries]):- projection_term_variables(ProjectionTerm,InitDict), ( Conjunction =.. [once|Arguments] -> [Args] = Arguments, translate_conjunction(Args,SQLFrom,SQLWhereTemp,InitDict,Dict), append(SQLWhereTemp,[once],SQLWhere) ; translate_conjunction(Conjunction,SQLFrom,SQLWhere,InitDict,Dict) ), translate_projection(ProjectionTerm,Dict,SQLSelect), Query = query(SQLSelect,SQLFrom,SQLWhere), query_generation(Conjunctions,ProjectionTerm,Queries). % --- translate_goal(Goal,SQLFrom,SQLWhere,Dict,NewDict) ------------------------------- % % translates a % % - positive database goal to the associated FROM- and WHERE clause of an SQL query % - a negated goal to a negated existential subquery % - an arithmetic goal to an arithmetic expression or an aggregate function query % - a comparison goal to a comparison expression % - a negated comparison goal to a comparison expression with the opposite comparison % operator % % -------------------------------------------------------------------------------------- translate_goal(SimpleGoal,[SQLFrom],SQLWhere,Dict,NewDict):- % --- positive goal binds variables - these bindings are held in the dictionary ----- functor(SimpleGoal,Functor,Arity), translate_functor(Functor,Arity,SQLFrom), SimpleGoal =.. [Functor|Arguments], translate_arguments(Arguments,SQLFrom,1,SQLWhere,Dict,NewDict). translate_goal(Result is Expression,[],SQLWhere,Dict,NewDict):- translate_arithmetic_function(Result,Expression,SQLWhere,Dict,NewDict). translate_goal(not NegatedGoals,[],SQLNegatedSubquery,Dict,Dict):- % --- negated goals do not bind variables - hence Dict is returned unchanged -------- functor(NegatedGoals,Functor,_), not comparison(Functor,_), translate_conjunction(NegatedGoals,SQLFrom,SQLWhere,Dict,_), SQLNegatedSubquery = [negated_existential_subquery([*],SQLFrom,SQLWhere)]. translate_goal(not ComparisonGoal,[],SQLCompOp,Dict,Dict):- % --- comparison operations do not bind variables - Dict is returned unchanged ------ ComparisonGoal =.. [ComparisonOperator,LeftArg,RightArg], comparison(ComparisonOperator,SQLOperator), negated_comparison(SQLOperator,SQLNegOperator), translate_comparison(LeftArg,RightArg,SQLNegOperator,Dict,SQLCompOp). %% EXAMPLE: db_prolog_select([Gene1],(once((eval(Gene1,_,_),exists(eval(Gene1),eval(Gene1,_,_)))))). translate_goal(exists(ProjectionTerm,ExistsGoals),SQLFrom,SQLExistsSubquery,Dict,Dict):- % --- exists goals do not bind variables - hence Dict is returned unchanged -------- functor(ExistsGoals,Functor,_), not comparison(Functor,_), translate_projection(ProjectionTerm,Dict,SQLSelect), translate_conjunction(ExistsGoals,SQLFrom,SQLWhere,Dict,_), SQLExistsSubquery = [existential_subquery(SQLSelect,SQLFrom,SQLWhere)]. translate_goal(exists(ExistsGoals),SQLFrom,SQLExistsSubquery,Dict,Dict):- % --- exists goals do not bind variables - hence Dict is returned unchanged -------- functor(ExistsGoals,Functor,_), not comparison(Functor,_), translate_conjunction(ExistsGoals,SQLFrom,SQLWhere,Dict,_), SQLExistsSubquery = [existential_subquery([*],SQLFrom,SQLWhere)]. translate_goal(ComparisonGoal,[],SQLCompOp,Dict,Dict):- % --- comparison operations do not bind variables - Dict is returned unchanged ------ ComparisonGoal =.. [ComparisonOperator,LeftArg,RightArg], comparison(ComparisonOperator,SQLOperator), translate_comparison(LeftArg,RightArg,SQLOperator,Dict,SQLCompOp). %DISTINCT translate_goal(distinct(Goal),List,SQL,Dict,DistinctDict):-!, translate_goal(Goal,List,SQL,Dict,NewDict), add_distinct_statement(NewDict,DistinctDict). %DEBUG add_distinct_statement(Dict,Dict):- append([_],[1,2],_). % --- translate_conjunction(Conjunction,SQLFrom,SQLWhere,Dict,NewDict) ----------------- % % translates a conjunction of goals (represented as a list of goals preceeded by % existentially quantified variables) to FROM- and WHERE-clause of an SQL query. % A dictionary containing the associated SQL table and attribute names is built up % as an accumulator pair (arguments Dict and NewDict) % % -------------------------------------------------------------------------------------- translate_conjunction('$var$'(VarId)^Goal,SQLFrom,SQLWhere,Dict,NewDict):- #ifdef DEBUG_TRANSLATE write('translate_conjuntion clause 1'),nl, #endif % --- add info on existentially quantified variables to dictionary here ------------- add_to_dictionary(VarId,_,_,_,existential,Dict,TmpDict), translate_conjunction(Goal,SQLFrom,SQLWhere,TmpDict,NewDict). translate_conjunction(Goal,SQLFrom,SQLWhere,Dict,NewDict):- #ifdef DEBUG_TRANSLATE write('translate_conjuntion clause 2'),nl, #endif Goal \= (_,_), translate_goal(Goal,SQLFrom,SQLWhere,Dict,NewDict). translate_conjunction((Goal,Conjunction),SQLFrom,SQLWhere,Dict,NewDict):- #ifdef DEBUG_TRANSLATE write('translate_conjuntion clause 3'),nl, #endif translate_goal(Goal,FromBegin,WhereBegin,Dict,TmpDict), translate_conjunction(Conjunction,FromRest,WhereRest,TmpDict,NewDict), append(FromBegin,FromRest,SQLFrom), append(WhereBegin,WhereRest,SQLWhere). % --- translate_arithmetic_function(Result,Expression,SQLWhere,Dict,NewDict) ----------- % % Arithmetic functions (left side of is/2 operator is bound to value of expression on % right side) may be called with either % % - Result unbound: then Result is bound to the value of the evaluation of Expression % - Result bound: then an equality condition is returned between the value of Result % and the value of the evaluation of Expression. % % Only the equality test shows up in the WHERE clause of an SQLquery. % % -------------------------------------------------------------------------------------- translate_arithmetic_function('$var$'(VarId),Expression,[],Dict,NewDict):- % assigment of value of arithmetic expression to variable - does not % show up in WHERE-part, but expression corresponding to % variable must be stored in Dict for projection translation evaluable_expression(Expression,Dict,ArithExpression,Type), add_to_dictionary(VarId,is,ArithExpression,Type,all,Dict,NewDict). translate_arithmetic_function('$var$'(VarId),Expression,ArithComparison,Dict,Dict):- % --- test whether left side evaluates to right side: return equality comparison ---- % Left side consists of qualified attribute, i.e. range variable must not be % arithmetic operator is/2 lookup(VarId,Dict,PrevRangeVar,PrevAtt,_), not (PrevRangeVar = is), % test whether type of attribute is numeric - if not, there's no sense in % continuing the translation %type_compatible(PrevType,number), evaluable_expression(Expression,Dict,ArithExpression,_), %type_compatible(ExprType,number), ArithComparison = [comp(att(PrevRangeVar,PrevAtt),'=',ArithExpression)]. translate_arithmetic_function('$var$'(VarId),Expression,ArithComparison,Dict,Dict):- % --- test whether left side evaluates to right side: return equality comparison ---- % Left side consists of arithmetic expression, i.e. VarId is stored in Dict as % belonging to arithmetic expression which is expressed as RangeVar-argument % of lookup returning is/2. Type information is implicit through the is/2 functor lookup(VarId,Dict,is,LeftExpr,_), %type_compatible(Type,number), evaluable_expression(Expression,Dict,RightExpr,_), %type_compatible(ExprType,number), ArithComparison = [comp(LeftExpr,'=',RightExpr)]. translate_arithmetic_function('$const$'(Constant),Expression,ArithComparison,Dict,Dict):- % --- is/2 used to test whether left side evaluates to right side ------------------- get_type('$const$'(Constant),_), %type_compatible(ConstantType,number), evaluable_expression(Expression,Dict,ArithExpression,_), %type_compatible(ExprType,number), ArithComparison = [comp('$const$'(Constant),'=',ArithExpression)]. % --- translate_comparison(LeftArg,RightArg,CompOp,Dict,SQLComparison) --------- % % translates the left and right arguments of a comparison term into the % appropriate comparison operation in SQL. The result type of each % argument expression is checked for type compatibility % % ------------------------------------------------------------------------------ translate_comparison(LeftArg,RightArg,CompOp,Dict,Comparison):- evaluable_expression(LeftArg,Dict,LeftTerm,_), evaluable_expression(RightArg,Dict,RightTerm,_), %type_compatible(LeftArgType,RightArgType), Comparison = [comp(LeftTerm,CompOp,RightTerm)]. % --- translate_functor(Functor,QualifiedTableName) ------------------------------------ % % translate_functor searches for the matching relation table name for % a given functor and creates a unique range variable to result in % a unique qualified relation table name. % % -------------------------------------------------------------------------------------- translate_functor(Functor,Arity,rel(TableName,RangeVariable)):- relation(Functor,Arity,TableName), gensym(rel,RangeVariable). % --- translate_arguments(Arguments,RelTable,ArgPos,Conditions,Dict) ------------------- % % translate_arguments organizes the translation of term arguments. One % term argument after the other is taken from the list of term arguments % until the list is exhausted. % % -------------------------------------------------------------------------------------- translate_arguments([],_,_,[],Dict,Dict). translate_arguments([Arg|Args],SQLTable,Position,SQLWhere,Dict,NewDict):- translate_argument(Arg,SQLTable,Position,Where,Dict,TmpDict), NewPosition is Position + 1, translate_arguments(Args,SQLTable,NewPosition,RestWhere,TmpDict,NewDict), append(Where,RestWhere,SQLWhere). % --- translate_argument(Argument,RelTable,Position,Condition,Dict) -------------------- % % The first occurrence of a variable leads to its associated SQL attribute information % to be recorded in the Dict. Any further occurrence creates an equi-join condition % between the current attribute and the previously recorded attribute. % Constant arguments always translate to equality comparisons between an attribute and % the constant value. % % -------------------------------------------------------------------------------------- translate_argument('$var$'(VarId),rel(SQLTable,RangeVar),Position,[],Dict,NewDict):- attribute(Position,SQLTable,Attribute,Type), add_to_dictionary(VarId,RangeVar,Attribute,Type,all,Dict,NewDict). translate_argument('$var$'(VarId),rel(SQLTable,RangeVar),Position,AttComparison,Dict,Dict):- % --- Variable occurred previously - retrieve first occurrence data from dictionary - lookup(VarId,Dict,PrevRangeVar,PrevAtt,_), attribute(Position,SQLTable,Attribute,_), % type_compatible(PrevType,Type), AttComparison = [comp(att(RangeVar,Attribute),=,att(PrevRangeVar,PrevAtt))]. translate_argument('$const$'(Constant),rel(SQLTable,RangeVar),Position,ConstComparison,Dict,Dict):- % --- Equality comparison of constant value and attribute in table ------------------ attribute(Position,SQLTable,Attribute,_), %get_type('$const$'(Constant),ConstType), %type_compatible(ConstType,Type), ConstComparison = [comp(att(RangeVar,Attribute),=,'$const$'(Constant))]. % --- projection_term_variables(ProjectionTerm,Dict) ----------------------------------- % % extracts all variables from the ProjectionTerm and places them into the % Dict as a dict/4 term with their Identifier, a non instantiated RangeVar and % Attribute argument, and the keyword existential for the type of quantification % % -------------------------------------------------------------------------------------- %% ERRO?? %projection_term_variables('$const(_)$',[]). projection_term_variables('$const$'(_),[]). projection_term_variables('$var$'(VarId),[dict(VarId,_,_,_,existential)]). projection_term_variables(ProjectionTerm,ProjectionTermVariables):- ProjectionTerm =.. [Functor|ProjectionTermList], not (Functor = '$var$'), not (ProjectionTermList = []), projection_list_vars(ProjectionTermList,ProjectionTermVariables). projection_list_vars([],[]). projection_list_vars(['$var$'(VarId)|RestArgs],[dict(VarId,_,_,_,existential)|RestVars]):- projection_list_vars(RestArgs,RestVars). projection_list_vars(['$const$'(_)|RestArgs],Vars):- projection_list_vars(RestArgs,Vars). % -------------------------------------------------------------------------------------- % RESTRICTION! ProjectionTerm underlies the following restrictions: % % - ProjectionTerm must have a functor other than the built-in % operators, i.e. ',',';', etc. are not allowed % % - only variables and constants are allowed as arguments, % i.e. no structured terms % % -------------------------------------------------------------------------------------- translate_projection('$var$'(VarId),Dict,SelectList):- projection_arguments(['$var$'(VarId)],SelectList,Dict). translate_projection('$const$'(Const),_,['$const$'(Const)]). translate_projection(ProjectionTerm,Dict,SelectList):- ProjectionTerm =.. [Functor|Arguments], not (Functor = '$var$'), not (Functor = '$const$'), not (Arguments = []), projection_arguments(Arguments,SelectList,Dict). projection_arguments([],[],_). projection_arguments([Arg|RestArgs],[Att|RestAtts],Dict):- retrieve_argument(Arg,Att,Dict), projection_arguments(RestArgs,RestAtts,Dict). % - retrieve_argument(Argument,SQLAttribute,Dictionary) -------------------------------- % % retrieves the mapping of an argument to the appropriate SQL construct, i.e. % % - qualified attribute names for variables in base goals % - arithmetic expressions for variables in arithmetic goals % - constant values for constants % % -------------------------------------------------------------------------------------- retrieve_argument('$var$'(VarId),Attribute,Dict):- lookup(VarId,Dict,TableName,AttName,_), ( TableName = is -> Attribute = AttName ; Attribute = att(TableName,AttName) ). retrieve_argument('$const$'(Constant),'$const$'(Constant),_). % --- lookup(Key,Dict,Value) ----------------------------------------------------------- lookup(VarId,Dict,RangeVar,Attribute,Type):- member(dict(VarId,RangeVar,Attribute,Type,Quant),Dict), ( Quant = all -> true ; nonvar(RangeVar), nonvar(Attribute) ). % --- add_to_dictionary(Key,RangeVar,Attribute,Quantifier,Dict,NewDict) ---------------- add_to_dictionary(Key,RangeVar,Attribute,Type,_,Dict,Dict):- member(dict(Key,RangeVar,Attribute,Type,existential),Dict). add_to_dictionary(Key,RangeVar,Attribute,Type,Quantifier,Dict,NewDict):- not member(dict(Key,_,_,_,_),Dict), NewDict = [dict(Key,RangeVar,Attribute,Type,Quantifier)|Dict]. % --- aggregate_function(AggregateFunctionTerm,Dict,AggregateFunctionQuery) ------------ % % aggregate_function discerns five Prolog aggregate function terms: count, avg, min, % max, and sum. Each such term is has two arguments: a variable indicating the attribute % over which the function is to be computed, and a goal argument which must contain in % at least one argument position the variable: % % e.g. avg(Seats,plane(Type,Seats)) % % These aggregate function terms correspond to the SQL built-in aggregate functions. % % RESTRICTION: AggregateGoal may only be conjunction of (positive or negative) base % goals % % -------------------------------------------------------------------------------------- aggregate_function(AggregateFunctionTerm,Dict,AggregateFunctionExpression):- AggregateFunctionTerm =..[AggFunctor,AggVar,AggGoal], aggregate_functor(AggFunctor,SQLFunction), conjunction(AggGoal,AggConjunction), aggregate_query_generation(SQLFunction,AggVar,AggConjunction,Dict,AggregateFunctionExpression). conjunction(Goal,Conjunction):- disjunction(Goal,[Conjunction]). % --- aggregate_query_generation(Function,FunctionVariable,AggGoal,Dict,AggregateQuery) % % compiles the function variable (representing the attribute over which the aggregate % function is to be computed) and the aggregate goal (representing the selection and % join conditions for the computation of the aggregate function) to an SQL aggregate % function subquery. % % -------------------------------------------------------------------------------------- % ILP % aggregate_query_generation(count,'$const$'('*'),AggGoal,Dict,AggregateQuery):- % translate_conjunction(AggGoal,SQLFrom,SQLWhere,Dict,TmpDict), % AggregateQuery = agg_query(Function,(count,['$const$'(*)]),SQLFrom,SQLWhere,[]). aggregate_query_generation(count,'$const$'('*'),AggGoal,Dict,AggregateQuery):- translate_conjunction(AggGoal,SQLFrom,SQLWhere,Dict,_), % ATTENTION! It is assumed that in count(*) aggregate query terms there cannot be % free variables because '*' stands for "all arguments" AggregateQuery = agg_query(_,(count,['$const$'(*)]),SQLFrom,SQLWhere,[]). %DISTINCT aggregate_query_generation(countdistinct,'$const$'('*'),AggGoal,Dict,AggregateQuery):- translate_conjunction(AggGoal,SQLFrom,SQLWhere,Dict,_), % ATTENTION! It is assumed that in count(*) aggregate query terms there cannot be % free variables because '*' stands for "all arguments" AggregateQuery = agg_query(_,(countdistinct,['$const$'(*)]),SQLFrom,SQLWhere,[]). aggregate_query_generation(Function,FunctionVariable,AggGoal,Dict,AggregateQuery):- translate_conjunction(AggGoal,SQLFrom,SQLWhere,Dict,TmpDict), % --- only variables occurring in the aggregate goal are relevant to the translation % of the function variable and the free variables in the goal. % Thus subtract from TmpDict all entries of Dict set_difference(TmpDict,Dict,AggDict), translate_projection(FunctionVariable,AggDict,SQLSelect), translate_grouping(FunctionVariable,AggDict,SQLGroup), AggregateQuery = agg_query(Function,SQLSelect,SQLFrom,SQLWhere,SQLGroup). % --- translate_grouping(FunctionVariable,Dict,SQLGroup) ------------------------------- % % finds the free variables in the aggregate function term and collects their % corresponding SQL qualified attributes in the SQLGroup list. % % -------------------------------------------------------------------------------------- translate_grouping(FunctionVariable,Dict,SQLGroup):- free_vars(FunctionVariable,Dict,FreeVariables), translate_free_vars(FreeVariables,SQLGroup). % --- free_vars(FunctionVariable,Dict,FreeVarList) ------------------------------------- % % A Variable is free if it neither occurs as the FunctionVariable, nor is stored as % existentially quantified (through ^/2 in the original goal) in the dictionary % % FreeVars contains for each variable the relevant attribute and relation information % contained in the dictionary % % -------------------------------------------------------------------------------------- % ILP % free_vars(FunctionVariable,Dict,FreeVarList):- % projection_term_variables(FunctionVariable,FunctionVariableList), % findall((Var,Table,Attribute), % (member(dict(Var,Table,Attribute,Type,all),Dict), % not member(dict(Var,_,_,_,_),FunctionVariableList) % ), % FreeVarList). free_vars(FunctionVariable,Dict,FreeVarList):- projection_term_variables(FunctionVariable,FunctionVariableList), findall((Var,Table,Attribute), (member(dict(Var,Table,Attribute,_,all),Dict), not member(dict(Var,_,_,_,_),FunctionVariableList) ), FreeVarList). % --- function_variable_list(FunctionVariable,FunctionVariableList) -------------------- % % extracts the list of variables which occur in the function variable term % % RESTRICTION: FunctionVariable may only contain one single variable. % % -------------------------------------------------------------------------------------- function_variable_list('$var$'(VarId),[VarId]). % --- translate_free_vars(FreeVars,SQLGroup) ------------------------------------------- % % translates dictionary information on free variables to SQLGroup of aggregate % function query % % -------------------------------------------------------------------------------------- translate_free_vars([],[]). % ILP %translate_free_vars([(VarId,Table,Attribute)|FreeVars],[att(Table,Attribute)|SQLGroups]):- translate_free_vars([(_,Table,Attribute)|FreeVars],[att(Table,Attribute)|SQLGroups]):- translate_free_vars(FreeVars,SQLGroups). % --- evaluable_expression(ExpressionTerm,Dictionary,Expression,Type) -------------------- % % evaluable_expression constructs SQL arithmetic expressions with qualified attribute names % from the Prolog arithmetic expression term and the information stored in the dictionary. % % The type of an evaluable function is returned in the argument Type. % % The dictionary is not changed because it is used for lookup only. % evaluable_expression(AggregateFunctionTerm,Dictionary,AggregateFunctionExpression,number):- aggregate_function(AggregateFunctionTerm,Dictionary,AggregateFunctionExpression). evaluable_expression(LeftExp + RightExp,Dictionary,LeftAr + RightAr,number):- evaluable_expression(LeftExp,Dictionary,LeftAr,number), evaluable_expression(RightExp,Dictionary,RightAr,number). evaluable_expression(LeftExp - RightExp,Dictionary,LeftAr - RightAr,number):- evaluable_expression(LeftExp,Dictionary,LeftAr,number), evaluable_expression(RightExp,Dictionary,RightAr,number). evaluable_expression(LeftExp * RightExp,Dictionary,LeftAr * RightAr,number):- evaluable_expression(LeftExp,Dictionary,LeftAr,number), evaluable_expression(RightExp,Dictionary,RightAr,number). evaluable_expression(LeftExp / RightExp,Dictionary, LeftAr / RightAr,number):- evaluable_expression(LeftExp,Dictionary,LeftAr,number), evaluable_expression(RightExp,Dictionary,RightAr,number). evaluable_expression('$var$'(VarId),Dictionary,att(RangeVar,Attribute),Type):- lookup(VarId,Dictionary,RangeVar,Attribute,Type), RangeVar \= is. evaluable_expression('$var$'(VarId),Dictionary,ArithmeticExpression,Type):- lookup(VarId,Dictionary,is,ArithmeticExpression,Type). evaluable_expression('$const$'(Const),_,'$const$'(Const),ConstType):- get_type('$const$'(Const),ConstType). % -------------------------------------------------------------------------------------- % % Output to screen predicates - rather crude at the moment % % -------------------------------------------------------------------------------------- % --- printqueries(Code) --------------------------------------------------------------- printqueries([Query]):- nl, print_query(Query), write(';'), nl, nl. printqueries([Query|Queries]):- not (Queries = []), nl, print_query(Query), nl, write('UNION '), nl, printqueries(Queries). % --- print_query(QueryCode) ----------------------------------------------------------- print_query(query([agg_query(Function,Select,From,Where,Group)],_,_)):- % --- ugly rule here: aggregate function only in SELECT Part of query ---- !, print_query(agg_query(Function,Select,From,Where,Group)). print_query(query(Select,From,Where)):- print_clause('SELECT',Select,','), nl, print_clause('FROM',From,','), nl, print_clause('WHERE',Where,'AND'), nl. print_query(agg_query(Function,Select,From,Where,Group)):- print_clause('SELECT',Function,Select,','), nl, print_clause('FROM',From,','), nl, print_clause('WHERE',Where,'AND'), nl, print_clause('GROUP BY',Group,','). print_query(negated_existential_subquery(Select,From,Where)):- write('NOT EXISTS'), nl, write('('), print_clause('SELECT',Select,','), nl, print_clause('FROM',From,','), nl, print_clause('WHERE',Where,'AND'), nl, write(')'). print_query(existential_subquery(Select,From,Where)):- write('EXISTS'), nl, write('('), print_clause('SELECT',Select,','), nl, print_clause('FROM',From,','), nl, print_clause('WHERE',Where,'AND'), nl, write(')'). % --- print_clause(Keyword,ClauseCode,Separator) --------------------------------------- % % with % Keyword one of SELECT, FROM, WHERE, or GROUP BY, % ClauseCode the code corresponding to the appropriate clause of an SQL query, and % Separator indicating the character(s) through which the items of a clause % are separated from each other (',' or 'AND'). % % -------------------------------------------------------------------------------------- % ILP % print_clause(Keyword,[],_). print_clause(_,[],_). print_clause(Keyword,[Column|RestColumns],Separator):- write(Keyword), write(' '), print_clause([Column|RestColumns],Separator). print_clause(Keyword,Function,[Column],Separator):- write(Keyword), write(' '), write(Function), write('('), print_clause([Column],Separator), write(')'). % --- print_clause(ClauseCode,Separator) ----------------------------------------------- print_clause([Item],_):- print_column(Item). print_clause([Item,NextItem|RestItems],Separator):- print_column(Item), write(' '), write(Separator), write(' '), print_clause([NextItem|RestItems],Separator). % --- print_column(ColumnCode) -------------------------------- print_column('*'):- write('*'). print_column(att(RangeVar,Attribute)):- write(RangeVar), write('.'), write(Attribute). print_column(rel(Relation,RangeVar)):- write(Relation), write(' '), write(RangeVar). print_column('$const$'(String)):- get_type('$const$'(String),string), write('"'), write(String), write('"'). print_column('$const$'(Number)):- get_type('$const$'(Number),NumType), type_compatible(NumType,number), write(Number). print_column(comp(LeftArg,Operator,RightArg)):- print_column(LeftArg), write(' '), write(Operator), write(' '), print_column(RightArg). print_column(LeftExpr * RightExpr):- print_column(LeftExpr), write('*'), print_column(RightExpr). print_column(LeftExpr / RightExpr):- print_column(LeftExpr), write('/'), print_column(RightExpr). print_column(LeftExpr + RightExpr):- print_column(LeftExpr), write('+'), print_column(RightExpr). print_column(LeftExpr - RightExpr):- print_column(LeftExpr), write('-'), print_column(RightExpr). print_column(agg_query(Function,Select,From,Where,Group)):- nl, write('('), print_query(agg_query(Function,Select,From,Where,Group)), write(')'). print_column(negated_existential_subquery(Select,From,Where)):- print_query(negated_existential_subquery(Select,From,Where)). print_column(existential_subquery(Select,From,Where)):- print_query(existential_subquery(Select,From,Where)). % --- queries_atom(Queries,QueryAtom) ---------------------------- % % queries_atom(Queries,QueryAtom) returns in its second argument % the SQL query as a Prolog atom. For efficiency reasons, a list % of ASCII codes is ceated as a difference list, and it is then % transformed to an atom by name/2 % ---------------------------------------------------------------- #ifdef MYDDAS_STATS queries_atom(Queries,QueryAtom):- c_db_stats_walltime(Start), queries_atom_(Queries,QueryAtom), c_db_stats_walltime(End), c_db_stats_translate(Start,End). queries_atom_(Queries,QueryAtom):- #else queries_atom(Queries,QueryAtom):- #endif queries_atom(Queries,QueryList,[]), name(QueryAtom,QueryList). queries_atom([Query],QueryList,Diff):- query_atom(Query,QueryList,Diff). queries_atom([Query|Queries],QueryList,Diff):- Queries \= [], query_atom(Query,QueryList,X1), column_atom('UNION ',X1,X2), queries_atom(Queries,X2,Diff). % --- query_atom(QueryCode) -------------------------------- query_atom(query([agg_query(Function,Select,From,Where,Group)],_,_),QueryList,Diff):- % --- ugly rule here: aggregate function only in SELECT Part of query ---- !, query_atom(agg_query(Function,Select,From,Where,Group),QueryList,Diff). query_atom(query(Select,From,Where),QueryList,Diff):- clause_atom('SELECT',Select,',',QueryList,X1), clause_atom('FROM',From,',',X1,X2), clause_atom('WHERE',Where,'AND',X2,Diff). query_atom(agg_query(Function,Select,From,Where,Group),QueryList,Diff):- clause_atom('SELECT',Function,Select,',',QueryList,X1), clause_atom('FROM',From,',',X1,X2), clause_atom('WHERE',Where,'AND',X2,X3), %ILP : PARA NAO POR OS GROUP BYS. FAZER FLAG PARA ISTO clause_atom('GROUP BY',Group,',',X3,Diff). query_atom(negated_existential_subquery(Select,From,Where),QueryList,Diff):- column_atom('NOT EXISTS(',QueryList,X1), clause_atom('SELECT',Select,',',X1,X2), clause_atom('FROM',From,',',X2,X3), clause_atom('WHERE',Where,'AND',X3,X4), column_atom(')',X4,Diff). query_atom(existential_subquery(Select,From,Where),QueryList,Diff):- column_atom('EXISTS(',QueryList,X1), clause_atom('SELECT',Select,',',X1,X2), clause_atom('FROM',From,',',X2,X3), clause_atom('WHERE',Where,'AND',X3,X4), column_atom('LIMIT 1)',X4,Diff). % --- clause_atom(Keyword,ClauseCode,Junctor,CurrAtom,QueryAtom) ------------- % % with % Keyword one of SELECT, FROM, WHERE, or GROUP BY, % ClauseCode the code corresponding to the appropriate clause of an SQL query, and % Junctor indicating the character(s) through which the items of a clause % are separated from each other (',' or 'AND'). % ILP % clause_atom(Keyword,[],_,QueryList,QueryList). clause_atom(_,[],_,QueryList,QueryList). % case there is no WHERE condition clause_atom(_,[once],_,QueryList,Diff):-!, column_atom(' ',QueryList,X1), column_atom('LIMIT 1',X1,X2), column_atom(' ',X2,Diff). clause_atom(Keyword,[Column|RestColumns],Junctor,QueryList,Diff):- column_atom(Keyword,QueryList,X1), column_atom(' ',X1,X2), clause_atom([Column|RestColumns],Junctor,X2,X3), column_atom(' ',X3,Diff). %DISTINCT clause_atom(Keyword,'COUNTDISTINCT',[Column],Junctor,QueryList,Diff):-!, column_atom(Keyword,QueryList,X1), column_atom(' ',X1,X2), column_atom('COUNT',X2,X3), column_atom('(DISTINCT ',X3,X4), clause_atom([Column],Junctor,X4,X5), column_atom(') ',X5,Diff). clause_atom(Keyword,Function,[Column],Junctor,QueryList,Diff):- column_atom(Keyword,QueryList,X1), column_atom(' ',X1,X2), column_atom(Function,X2,X3), column_atom('(',X3,X4), clause_atom([Column],Junctor,X4,X5), column_atom(') ',X5,Diff). % --- clause_atom(ClauseCode,Junctor) -------------------------------- clause_atom([once],_,QueryList,Diff):-!, column_atom(' LIMIT 1 ',QueryList,Diff). clause_atom([Item],_,QueryList,Diff):- column_atom(Item,QueryList,Diff). clause_atom([Item,NextItem|RestItems],Junctor,QueryList,Diff):- column_atom(Item,QueryList,X1), column_atom(' ',X1,X2), ( NextItem = once -> X4 = X2 ; column_atom(Junctor,X2,X3), column_atom(' ',X3,X4) ), clause_atom([NextItem|RestItems],Junctor,X4,Diff). column_atom(att(RangeVar,Attribute),QueryList,Diff):- column_atom(RangeVar,QueryList,X1), column_atom('.',X1,X2), column_atom(Attribute,X2,Diff). column_atom(rel(Relation,RangeVar),QueryList,Diff):- column_atom('`',QueryList,X0), column_atom(Relation,X0,X1), column_atom('` ',X1,X2), column_atom(RangeVar,X2,Diff). column_atom('$const$'(String),QueryList,Diff):- get_type('$const$'(String),string), column_atom('"',QueryList,X1), column_atom(String,X1,X2), column_atom('"',X2,Diff). column_atom('$const$'(Number),QueryList,Diff):- get_type('$const$'(Number),NumType), type_compatible(NumType,number), column_atom(Number,QueryList,Diff). column_atom(comp(LeftArg,Operator,RightArg),QueryList,Diff):- column_atom(LeftArg,QueryList,X1), column_atom(' ',X1,X2), column_atom(Operator,X2,X3), column_atom(' ',X3,X4), column_atom(RightArg,X4,Diff). column_atom(LeftExpr * RightExpr,QueryList,Diff):- column_atom(LeftExpr,QueryList,X1), column_atom('*',X1,X2), column_atom(RightExpr,X2,Diff). column_atom(LeftExpr + RightExpr,QueryList,Diff):- column_atom(LeftExpr,QueryList,X1), column_atom('+',X1,X2), column_atom(RightExpr,X2,Diff). column_atom(LeftExpr - RightExpr,QueryList,Diff):- column_atom(LeftExpr,QueryList,X1), column_atom('-',X1,X2), column_atom(RightExpr,X2,Diff). column_atom(LeftExpr / RightExpr,QueryList,Diff):- column_atom(LeftExpr,QueryList,X1), column_atom('/',X1,X2), column_atom(RightExpr,X2,Diff). column_atom(agg_query(Function,Select,From,Where,Group),QueryList,Diff):- column_atom('(',QueryList,X1), query_atom(agg_query(Function,Select,From,Where,Group),X1,X2), column_atom(')',X2,Diff). column_atom(negated_existential_subquery(Select,From,Where),QueryList,Diff):- query_atom(negated_existential_subquery(Select,From,Where),QueryList,Diff). column_atom(existential_subquery(Select,From,Where),QueryList,Diff):- query_atom(existential_subquery(Select,From,Where),QueryList,Diff). column_atom(Atom,List,Diff):- atom(Atom), name(Atom,X1), append(X1,Diff,List). column_atom(Number,List,Diff) :- number(Number), name(Number,X1), append(X1,Diff,List). % --- gensym(Root,Symbol) ---------------------------------------------------- % % SEPIA 3.2. version - other Prolog implementations provide gensym/2 % and init_gensym/1 as built-ins. */ % % (C) Christoph Draxler, Aug. 1992 % % init_gensym(Atom) :- set_value(Atom,'@'). gensym(Atom,Var) :- var(Var), get_value(Atom,Value), char_code(Value,Code), NewCode is Code + 1, char_code(Var,NewCode), set_value(Atom,Var). % --- auxiliary predicates (some of them may be built-in... -------------------- repeat_n(N):- integer(N), N > 0, repeat_1(N). repeat_1(1):-!. repeat_1(_). repeat_1(N):- N1 is N-1, repeat_1(N1). % --- set_difference(SetA,SetB,Difference) -------------------------------------------- % % SetA - SetB = Difference set_difference([],_,[]). set_difference([Element|RestSet],Set,[Element|RestDifference]):- not member(Element,Set), set_difference(RestSet,Set,RestDifference). set_difference([Element|RestSet],Set,RestDifference):- member(Element,Set), set_difference(RestSet,Set,RestDifference). % --- Mapping of Prolog operators to SQL operators ------------------------------------- comparison(=,=). comparison(<,<). comparison(=<,'<='). comparison(>=,'>='). comparison(>,>). comparison(@<,<). comparison(@>,>). negated_comparison(=,'<>'). negated_comparison(\=,=). negated_comparison(>,'<='). negated_comparison(=<,>). negated_comparison(<,>=). negated_comparison(>=,<). % --- aggregate_function(PrologFunctor,SQLFunction) ----------------- aggregate_functor(avg,'AVG'). aggregate_functor(min,'MIN'). aggregate_functor(max,'MAX'). aggregate_functor(sum,'SUM'). aggregate_functor(count,'COUNT'). aggregate_functor(countdistinct,'COUNTDISTINCT'). % --- type system -------------------------------------------------------------- % % A rudimentary type system is provided for consistency checking during the % translation and for output formatting % % The basic types are string and number. number has the subtypes integer and % real. % % ------------------------------------------------------------------------------ type_compatible(Type,Type):- is_type(Type). type_compatible(SubType,Type):- subtype(SubType,Type). type_compatible(Type,SubType):- subtype(SubType,Type). % --- subtype(SubType,SuperType) ----------------------------------------------- % % Simple type hierarchy checking % % ------------------------------------------------------------------------------ subtype(SubType,SuperType):- is_subtype(SubType,SuperType). subtype(SubType,SuperType):- is_subtype(SubType,InterType), subtype(InterType,SuperType). % --- is_type(Type) ------------------------------------------------------------ % % Type names % % ------------------------------------------------------------------------------ is_type(number). is_type(integer). is_type(real). is_type(string). is_type(natural). % --- is_subtype(SubType,SuperType) -------------------------------------------- % % Simple type hierarchy for numeric types % % ------------------------------------------------------------------------------ is_subtype(integer,number). is_subtype(real,number). is_subtype(natural,integer). % --- get_type(Constant,Type) -------------------------------------------------- % % Prolog implementation specific definition of type retrieval % sepia Prolog version given here % % ------------------------------------------------------------------------------ get_type('$const$'(Constant),integer):- integer(Constant),!. get_type('$const$'(Constant),real):- number(Constant),!. get_type('$const$'(Constant),string):- atom(Constant).