diff --git a/src/sparql/algebra.rs b/src/sparql/algebra.rs index eb6561ce..fdd3ff27 100644 --- a/src/sparql/algebra.rs +++ b/src/sparql/algebra.rs @@ -1,5 +1,6 @@ use model::*; use sparql::model::*; +use std::collections::BTreeSet; use std::fmt; use std::ops::Add; use utils::Escaper; @@ -398,12 +399,24 @@ impl fmt::Display for Expression { } } +impl From for Expression { + fn from(p: NamedNode) -> Self { + Expression::ConstantExpression(p.into()) + } +} + impl From for Expression { fn from(p: Literal) -> Self { Expression::ConstantExpression(p.into()) } } +impl From for Expression { + fn from(v: Variable) -> Self { + Expression::ConstantExpression(v.into()) + } +} + struct SparqlExpression<'a>(&'a Expression); impl<'a> fmt::Display for SparqlExpression<'a> { @@ -794,6 +807,51 @@ impl From for MultiSetPattern { } } +impl MultiSetPattern { + pub fn visible_variables<'a>(&'a self) -> BTreeSet<&'a Variable> { + let mut vars = BTreeSet::default(); + self.add_visible_variables(&mut vars); + vars + } + + fn add_visible_variables<'a>(&'a self, vars: &mut BTreeSet<&'a Variable>) { + match self { + MultiSetPattern::BGP(p) => { + for pattern in p { + if let TermOrVariable::Variable(ref s) = pattern.subject { + vars.insert(s); + } + //TODO: pred + if let TermOrVariable::Variable(ref o) = pattern.object { + vars.insert(o); + } + } + } + MultiSetPattern::Join(a, b) => { + a.add_visible_variables(vars); + b.add_visible_variables(vars); + } + MultiSetPattern::LeftJoin(a, b, _) => { + a.add_visible_variables(vars); + b.add_visible_variables(vars); + } + MultiSetPattern::Filter(_, p) => p.add_visible_variables(vars), + MultiSetPattern::Union(a, b) => { + a.add_visible_variables(vars); + b.add_visible_variables(vars); + } + MultiSetPattern::Graph(_, p) => p.add_visible_variables(vars), + MultiSetPattern::Extend(p, v, _) => { + p.add_visible_variables(vars); + vars.insert(&v); + } + MultiSetPattern::Minus(a, _) => a.add_visible_variables(vars), + MultiSetPattern::ToMultiSet(l) => l.add_visible_variables(vars), + MultiSetPattern::Service(_, p, _) => p.add_visible_variables(vars), + } + } +} + struct SparqlMultiSetPattern<'a>(&'a MultiSetPattern); impl<'a> fmt::Display for SparqlMultiSetPattern<'a> { @@ -854,7 +912,14 @@ impl<'a> fmt::Display for SparqlMultiSetPattern<'a> { SparqlMultiSetPattern(&*a), SparqlMultiSetPattern(&*b) ), - MultiSetPattern::ToMultiSet(l) => write!(f, "{}", SparqlListPattern(&l)), + MultiSetPattern::ToMultiSet(l) => write!( + f, + "{{ {} }}", + SparqlListPattern { + algebra: &l, + dataset: &EMPTY_DATASET + } + ), MultiSetPattern::Service(n, p, s) => if *s { write!( f, @@ -873,14 +938,56 @@ impl<'a> fmt::Display for SparqlMultiSetPattern<'a> { pub enum ListPattern { Data(Vec), ToList(MultiSetPattern), - Group(), - Aggregation(), - AggregateJoin(), - OrderBy(Box), - Project(Box), - Distinct(Box), - Reduced(Box), - Slice(Box, usize, usize), + OrderBy(Box, Vec), + Project(Box, Vec), + Distinct(Box), + Reduced(Box), + Slice(Box, usize, Option), +} + +impl fmt::Display for ListPattern { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ListPattern::Data(bs) => write!( + f, + "{{ {} }}", + bs.iter() + .map(|c| c.to_string()) + .collect::>() + .join(" ") + ), + ListPattern::ToList(l) => write!(f, "{}", l), + ListPattern::OrderBy(l, o) => write!( + f, + "OrderBy({}, ({}))", + l, + o.iter() + .map(|c| c.to_string()) + .collect::>() + .join(", ") + ), + ListPattern::Project(l, pv) => write!( + f, + "Project({}, ({}))", + l, + pv.iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + ), + ListPattern::Distinct(l) => write!(f, "Distinct({})", l), + ListPattern::Reduced(l) => write!(f, "Reduce({})", l), + ListPattern::Slice(l, start, length) => write!( + f, + "Slice({}, {}, {})", + l, + start, + length + .map(|l| l.to_string()) + .unwrap_or_else(|| '?'.to_string()) + ), + } + } } impl Default for ListPattern { @@ -898,22 +1005,190 @@ impl From for ListPattern { } } -impl fmt::Display for ListPattern { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl ListPattern { + pub fn visible_variables<'a>(&'a self) -> BTreeSet<&'a Variable> { + let mut vars = BTreeSet::default(); + self.add_visible_variables(&mut vars); + vars + } + + fn add_visible_variables<'a>(&'a self, vars: &mut BTreeSet<&'a Variable>) { match self { - ListPattern::ToList(l) => write!(f, "{}", l), - _ => Ok(()), //TODO + ListPattern::Data(b) => { + for binding in b { + for (var, _) in binding { + vars.insert(var); + } + } + } + ListPattern::ToList(p) => p.add_visible_variables(vars), + ListPattern::OrderBy(l, _) => l.add_visible_variables(vars), + ListPattern::Project(_, pv) => vars.extend(pv.iter()), + ListPattern::Distinct(l) => l.add_visible_variables(vars), + ListPattern::Reduced(l) => l.add_visible_variables(vars), + ListPattern::Slice(l, _, _) => l.add_visible_variables(vars), } } } -struct SparqlListPattern<'a>(&'a ListPattern); +struct SparqlListPattern<'a> { + algebra: &'a ListPattern, + dataset: &'a Dataset, +} impl<'a> fmt::Display for SparqlListPattern<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.algebra { + ListPattern::Data(bs) => if bs.is_empty() { + Ok(()) + } else { + let vars: Vec<&Variable> = bs[0].iter().map(|(v, _)| v).collect(); + write!(f, "VALUES ( ")?; + for var in &vars { + write!(f, "{} ", var)?; + } + write!(f, ") {{ ")?; + for b in bs { + write!(f, "( ")?; + for var in &vars { + b.get(var) + .map(|v| write!(f, "{} ", v)) + .unwrap_or_else(|| write!(f, "UNDEF "))?; + } + write!(f, ") ")?; + } + write!(f, " }}") + }, + ListPattern::ToList(l) => write!(f, "{}", SparqlMultiSetPattern(&*l)), + ListPattern::OrderBy(l, o) => write!( + f, + "{} ORDER BY {}", + SparqlListPattern { + algebra: &*l, + dataset: self.dataset + }, + o.iter() + .map(|c| SparqlOrderComparator(c).to_string()) + .collect::>() + .join(" ") + ), + ListPattern::Project(l, pv) => write!( + f, + "SELECT {} {} WHERE {{ {} }}", + build_sparql_select_arguments(pv), + self.dataset, + SparqlListPattern { + algebra: &*l, + dataset: &EMPTY_DATASET + } + ), + ListPattern::Distinct(l) => match l.as_ref() { + ListPattern::Project(l, pv) => write!( + f, + "SELECT DISTINCT {} {} WHERE {{ {} }}", + build_sparql_select_arguments(pv), + self.dataset, + SparqlListPattern { + algebra: &*l, + dataset: &EMPTY_DATASET + } + ), + l => write!( + f, + "DISTINCT {}", + SparqlListPattern { + algebra: &l, + dataset: self.dataset + } + ), + }, + ListPattern::Reduced(l) => match l.as_ref() { + ListPattern::Project(l, pv) => write!( + f, + "SELECT REDUCED {} {} WHERE {{ {} }}", + build_sparql_select_arguments(pv), + self.dataset, + SparqlListPattern { + algebra: &*l, + dataset: &EMPTY_DATASET + } + ), + l => write!( + f, + "REDUCED {}", + SparqlListPattern { + algebra: &l, + dataset: self.dataset + } + ), + }, + ListPattern::Slice(l, start, length) => length + .map(|length| { + write!( + f, + "{} LIMIT {} OFFSET {}", + SparqlListPattern { + algebra: &*l, + dataset: self.dataset + }, + start, + length + ) + }) + .unwrap_or_else(|| { + write!( + f, + "{} LIMIT {}", + SparqlListPattern { + algebra: &*l, + dataset: self.dataset + }, + start + ) + }), + } + } +} + +fn build_sparql_select_arguments(args: &Vec) -> String { + if args.is_empty() { + "*".to_owned() + } else { + args.iter() + .map(|v| v.to_string()) + .collect::>() + .join(" ") + } +} + +#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] +pub enum OrderComparator { + Asc(Expression), + Desc(Expression), +} + +impl fmt::Display for OrderComparator { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + OrderComparator::Asc(e) => write!(f, "ASC({})", e), + OrderComparator::Desc(e) => write!(f, "DESC({})", e), + } + } +} + +impl From for OrderComparator { + fn from(e: Expression) -> Self { + OrderComparator::Asc(e) + } +} + +struct SparqlOrderComparator<'a>(&'a OrderComparator); + +impl<'a> fmt::Display for SparqlOrderComparator<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.0 { - ListPattern::ToList(l) => write!(f, "{}", SparqlMultiSetPattern(&l)), - _ => Ok(()), //TODO + OrderComparator::Asc(e) => write!(f, "ASC({})", SparqlExpression(e)), + OrderComparator::Desc(e) => write!(f, "DESC({})", SparqlExpression(e)), } } } @@ -962,103 +1237,46 @@ impl fmt::Display for Dataset { } } -#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] -pub enum SelectionOption { - Distinct, - Reduced, - Default, -} - -impl fmt::Display for SelectionOption { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match self { - SelectionOption::Distinct => write!(f, "DISTINCT"), - SelectionOption::Reduced => write!(f, "REDUCED"), - SelectionOption::Default => Ok(()), - } - } -} - -#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] -pub enum SelectionMember { - Variable(Variable), - Expression(Expression, Variable), -} - -impl fmt::Display for SelectionMember { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match self { - SelectionMember::Variable(v) => write!(f, "{}", v), - SelectionMember::Expression(e, v) => write!(f, "({} AS {})", e, v), - } - } -} - -#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] -pub struct Selection { - pub option: SelectionOption, - pub variables: Option>, -} - -impl fmt::Display for Selection { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - self.variables - .as_ref() - .map(|vars| { - write!( - f, - "{} {}", - self.option, - vars.iter() - .map(|v| v.to_string()) - .collect::>() - .join(" ") - ) - }) - .unwrap_or_else(|| write!(f, "{} *", self.option)) - } +lazy_static! { + static ref EMPTY_DATASET: Dataset = Dataset::default(); } #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] pub enum Query { SelectQuery { - selection: Selection, dataset: Dataset, - filter: ListPattern, + algebra: ListPattern, }, ConstructQuery { construct: Vec, dataset: Dataset, - filter: ListPattern, + algebra: ListPattern, }, DescribeQuery { dataset: Dataset, - filter: ListPattern, + algebra: ListPattern, }, AskQuery { dataset: Dataset, - filter: ListPattern, + algebra: ListPattern, }, } impl fmt::Display for Query { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Query::SelectQuery { - selection, - dataset, - filter, - } => write!( + Query::SelectQuery { dataset, algebra } => write!( f, - "SELECT {} {} WHERE {{ {} }}", - selection, - dataset, - SparqlListPattern(&filter) + "{}", + SparqlListPattern { + algebra: &algebra, + dataset: &dataset + } ), Query::ConstructQuery { construct, dataset, - filter, + algebra, } => write!( f, "CONSTRUCT {{ {} }} {} WHERE {{ {} }}", @@ -1068,19 +1286,28 @@ impl fmt::Display for Query { .collect::>() .join(" . "), dataset, - SparqlListPattern(&filter) + SparqlListPattern { + algebra: &algebra, + dataset: &EMPTY_DATASET + } ), - Query::DescribeQuery { dataset, filter } => write!( + Query::DescribeQuery { dataset, algebra } => write!( f, "DESCRIBE {} WHERE {{ {} }}", dataset, - SparqlListPattern(&filter) + SparqlListPattern { + algebra: &algebra, + dataset: &EMPTY_DATASET + } ), - Query::AskQuery { dataset, filter } => write!( + Query::AskQuery { dataset, algebra } => write!( f, "ASK {} WHERE {{ {} }}", dataset, - SparqlListPattern(&filter) + SparqlListPattern { + algebra: &algebra, + dataset: &EMPTY_DATASET + } ), } } diff --git a/src/sparql/model.rs b/src/sparql/model.rs index 4fd1c844..3b779b99 100644 --- a/src/sparql/model.rs +++ b/src/sparql/model.rs @@ -1,4 +1,5 @@ use model::*; +use std::collections::btree_map; use std::collections::BTreeMap; use std::fmt; use uuid::Uuid; @@ -150,12 +151,20 @@ impl Binding { self.0.insert(var, value); } - pub fn get<'a>(&'a self, key: &'a TermOrVariable) -> Option<&'a Term> { + pub fn get<'a>(&'a self, key: &'a Variable) -> Option<&'a Term> { + self.0.get(key) + } + + pub fn get_or_constant<'a>(&'a self, key: &'a TermOrVariable) -> Option<&'a Term> { match key { TermOrVariable::Term(t) => Some(t), - TermOrVariable::Variable(v) => self.0.get(v), + TermOrVariable::Variable(v) => self.get(v), } } + + pub fn iter<'a>(&'a self) -> btree_map::Iter { + self.0.iter() + } } impl Default for Binding { @@ -163,3 +172,31 @@ impl Default for Binding { Binding(BTreeMap::default()) } } + +impl IntoIterator for Binding { + type Item = (Variable, Term); + type IntoIter = btree_map::IntoIter; + + fn into_iter(self) -> btree_map::IntoIter { + self.0.into_iter() + } +} + +impl<'a> IntoIterator for &'a Binding { + type Item = (&'a Variable, &'a Term); + type IntoIter = btree_map::Iter<'a, Variable, Term>; + + fn into_iter(self) -> btree_map::Iter<'a, Variable, Term> { + self.0.iter() + } +} + +impl fmt::Display for Binding { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{{")?; + for (var, val) in self { + write!(f, " {} → {} ", var, val)?; + } + write!(f, "}}") + } +} diff --git a/src/sparql/parser.rs b/src/sparql/parser.rs index 8446ad49..d4a48e0e 100644 --- a/src/sparql/parser.rs +++ b/src/sparql/parser.rs @@ -132,6 +132,68 @@ mod grammar { }).ok_or("The iterator should not be empty") } + enum SelectionOption { + Distinct, + Reduced, + Default, + } + + enum SelectionMember { + Variable(Variable), + Expression(Expression, Variable), + } + + struct Selection { + pub option: SelectionOption, + pub variables: Option>, + } + + fn build_select( + select: Selection, + wher: MultiSetPattern, + having: Option, + order_by: Option>, + values: Option, + ) -> ListPattern { + let mut p = wher; + if let Some(ex) = having { + p = MultiSetPattern::Filter(ex, Box::new(p)); + } + if let Some(data) = values { + p = MultiSetPattern::Join(Box::new(p), Box::new(data)); + } + let mut pv: Vec = Vec::default(); + match select.variables { + Some(sel_items) => { + for sel_item in sel_items { + match sel_item { + SelectionMember::Variable(v) => pv.push(v), + SelectionMember::Expression(e, v) => if pv.contains(&v) { + //TODO: fail + } else { + p = MultiSetPattern::Extend(Box::new(p), v.clone(), e); + pv.push(v); + }, + } + } + } + None => { + pv.extend(p.visible_variables().into_iter().map(|v| v.clone())) //TODO: is it really useful to do a projection? + } + } + let mut m = ListPattern::from(p); + if let Some(order) = order_by { + m = ListPattern::OrderBy(Box::new(m), order); + } + m = ListPattern::Project(Box::new(m), pv); + match select.option { + SelectionOption::Distinct => m = ListPattern::Distinct(Box::new(m)), + SelectionOption::Reduced => m = ListPattern::Reduced(Box::new(m)), + SelectionOption::Default => (), + } + m + } + enum Either { Left(L), Right(R), diff --git a/src/sparql/sparql_grammar.rustpeg b/src/sparql/sparql_grammar.rustpeg index a29d49c3..6087b893 100644 --- a/src/sparql/sparql_grammar.rustpeg +++ b/src/sparql/sparql_grammar.rustpeg @@ -13,7 +13,7 @@ use std::iter; QueryUnit -> Query = Query //[2] -Query -> Query = _ Prologue _ q:(SelectQuery / ConstructQuery / DescribeQuery / AskQuery) _ ValuesClause _ { //TODO: ValuesClause +Query -> Query = _ Prologue _ q:(SelectQuery / ConstructQuery / DescribeQuery / AskQuery) _ { //TODO: ValuesClause q } @@ -37,21 +37,16 @@ PrefixDecl -> () = "PREFIX"i _ ns:PNAME_NS _ i:IRIREF { } //[7] -SelectQuery -> Query = s:SelectClause _ d:DatasetClauses _ f:WhereClause _ SolutionModifier { //TODO: Modifier +SelectQuery -> Query = s:SelectClause _ d:DatasetClauses _ w:WhereClause _ GroupClause? _ h:HavingClause? _ o:OrderClause? _ l:LimitOffsetClauses? _ v:ValuesClause { //TODO: Modifier Query::SelectQuery { - selection: s, dataset: d, - filter: f + algebra: build_select(s, w, h, o, v) } } //[8] -SubSelect -> MultiSetPattern = s:SelectClause _ f:WhereClause _ SolutionModifier _ ValuesClause { //TODO: Modifiers - MultiSetPattern::default() - /*TODO MultiSetPattern::SubSelectPattern { - selection: s, - filter: Box::new(f) - }*/ +SubSelect -> MultiSetPattern = s:SelectClause _ w:WhereClause _ GroupClause? _ h:HavingClause? _ o:OrderClause? _ l:LimitOffsetClauses? _ v:ValuesClause { //TODO: Modifiers + build_select(s, w, h, o, v).into() } //[9] @@ -74,32 +69,63 @@ SelectClause_member -> SelectionMember = //[10] ConstructQuery -> Query = - "CONSTRUCT"i _ c:ConstructTemplate _ d:DatasetClauses _ f:WhereClause _ SolutionModifier { - Query::ConstructQuery { construct: c, dataset: d, filter: f } + "CONSTRUCT"i _ c:ConstructTemplate _ d:DatasetClauses _ w:WhereClause _ GroupClause? _ h:HavingClause? _ o:OrderClause? _ LimitOffsetClauses? _ v:ValuesClause { + Query::ConstructQuery { + construct: c, + dataset: d, + algebra: build_select(Selection { + option: SelectionOption::Reduced, + variables: None + }, w, h, o, v) + } } / - "CONSTRUCT"i _ d:DatasetClauses _ "WHERE"i _ '{' _ c:ConstructQuery_optional_triple_template _ '}' _ SolutionModifier { + "CONSTRUCT"i _ d:DatasetClauses _ "WHERE"i _ '{' _ c:ConstructQuery_optional_triple_template _ '}' _ GroupClause? _ h:HavingClause? _ o:OrderClause? _ LimitOffsetClauses? _ v:ValuesClause { Query::ConstructQuery { construct: c.clone(), dataset: d, - filter: MultiSetPattern::BGP(c.into_iter().map(|p| PropertyPathPattern::from(p)).collect()).into() + algebra: build_select(Selection { + option: SelectionOption::Reduced, + variables: None + }, MultiSetPattern::BGP(c.into_iter().map(|p| PropertyPathPattern::from(p)).collect()).into(), + h, o, v) } } ConstructQuery_optional_triple_template -> Vec = TriplesTemplate / { Vec::default() } //[11] -DescribeQuery -> Query = "DESCRIBE"i _ ('*' / (VarOrIri _)+) _ d:DatasetClauses f:WhereClause? _ SolutionModifier { - Query::DescribeQuery { - dataset: d, - filter: f.unwrap_or_else(ListPattern::default) +DescribeQuery -> Query = + "DESCRIBE"i _ '*' _ d:DatasetClauses w:WhereClause? _ GroupClause? _ h:HavingClause? _ o:OrderClause? _ LimitOffsetClauses? _ v:ValuesClause { + Query::DescribeQuery { + dataset: d, + algebra: build_select(Selection { + option: SelectionOption::Reduced, + variables: None + }, w.unwrap_or_else(MultiSetPattern::default), h, o, v) + } + } / + "DESCRIBE"i _ p:DescribeQuery_item+ _ d:DatasetClauses w:WhereClause? _ GroupClause? _ h:HavingClause? _ o:OrderClause? _ LimitOffsetClauses? _ v:ValuesClause { + Query::DescribeQuery { + dataset: d, + algebra: build_select(Selection { + option: SelectionOption::Reduced, + variables: Some(p.into_iter().map(|var_or_iri| match var_or_iri { + NamedNodeOrVariable::NamedNode(n) => SelectionMember::Expression(n.into(), Variable::default()), + NamedNodeOrVariable::Variable(v) => SelectionMember::Variable(v) + }).collect()) + }, w.unwrap_or_else(MultiSetPattern::default), h, o, v) + } } -} +DescribeQuery_item -> NamedNodeOrVariable = i:VarOrIri _ { i } //[12] -AskQuery -> Query = "ASK"i _ d:DatasetClauses f:WhereClause _ SolutionModifier { +AskQuery -> Query = "ASK"i _ d:DatasetClauses w:WhereClause _ GroupClause? _ h:HavingClause? _ o:OrderClause? _ LimitOffsetClauses? _ v:ValuesClause { Query::AskQuery { dataset: d, - filter: f + algebra: build_select(Selection { + option: SelectionOption::Reduced, + variables: None + }, w, h, o, v) } } @@ -123,13 +149,10 @@ NamedGraphClause -> Dataset = "NAMED"i _ s:SourceSelector { SourceSelector -> NamedNode = iri //[17] -WhereClause -> ListPattern = "WHERE"i? _ p:GroupGraphPattern { - p.into() +WhereClause -> MultiSetPattern = "WHERE"i? _ p:GroupGraphPattern { + p } -//[18] -SolutionModifier -> () = GroupClause? _ HavingClause? _ OrderClause? _ LimitOffsetClauses? - //[19] GroupClause -> () = "GROUP"i _ "BY"i _ (GroupCondition _)+ @@ -145,10 +168,15 @@ HavingClause -> Expression = "HAVING"i _ e:HavingCondition+ {? HavingCondition -> Expression = Constraint //[23] -OrderClause -> () = "ORDER"i "BY"i _ OrderCondition+ +OrderClause -> Vec = "ORDER"i "BY"i _ c:OrderClause_item+ { c } +OrderClause_item -> OrderComparator = c:OrderCondition _ { c } //[24] -OrderCondition -> () = (( "ASC"i / "DESC"i) _ BrackettedExpression) / (Constraint / Var) +OrderCondition -> OrderComparator = + "ASC"i _ e: BrackettedExpression { OrderComparator::Asc(e) } / + "DESC"i _ e: BrackettedExpression { OrderComparator::Desc(e) } / + e: Constraint { e.into() } / + v: Var { Expression::from(v).into() } //[25] LimitOffsetClauses -> () = LimitClause _ OffsetClause? / OffsetClause _ LimitClause?