1385 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
			
		
		
	
	
			1385 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
| % -*- 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.
 | |
| %
 | |
| % --------------------------------------------------------------------------------------
 | |
| 
 | |
| :- dynamic attribute/4.
 | |
| 
 | |
| 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).
 | |
| 
 | |
| 
 |