+ SQL general expression parser\n+ CONCAT
authorEric Prud'hommeaux <bertails@w3.org>
Sun, 03 Jan 2010 16:11:18 -0500
changeset 103 c1d12b781d00
parent 102 2bec0e8a7aec
child 104 855838f67c98
+ SQL general expression parser\n+ CONCAT
src/main/scala/RDB2RDFMain.scala
src/main/scala/SQL.scala
src/test/scala/SQLTest.scala
--- a/src/main/scala/RDB2RDFMain.scala	Sun Jan 03 03:01:12 2010 -0500
+++ b/src/main/scala/RDB2RDFMain.scala	Sun Jan 03 16:11:18 2010 -0500
@@ -53,14 +53,14 @@
 
   def uriConstraint(state:R2RState, constrainMe:RelAliasAttribute, u:ObjUri, enforeForeignKeys:Boolean):R2RState = {
     // println("equiv+= " + toString(constrainMe) + "=" + value)
-    //R2RState(state.joins, state.varmap, state.exprs + RelationalExpressionEq(constrainMe,RValueTyped(SQLDatatype.INTEGER,Name(u.v.s))))
+    //R2RState(state.joins, state.varmap, state.exprs + RelationalExpressionEq(constrainMe,PrimaryExpressionTyped(SQLDatatype.INTEGER,Name(u.v.s))))
     val relvar = if (enforeForeignKeys) RelAliasAttribute(constrainMe.relalias, Attribute(Name(u.attr.s))) else constrainMe
-    R2RState(state.joins, state.varmap, state.exprs + RelationalExpressionEq(relvar,RValueTyped(SQLDatatype.INTEGER,Name(u.v.s))))
+    R2RState(state.joins, state.varmap, state.exprs + RelationalExpressionEq(PrimaryExpressionAttr(relvar),PrimaryExpressionTyped(SQLDatatype.INTEGER,Name(u.v.s))))
   }
 
   def literalConstraint(state:R2RState, constrainMe:RelAliasAttribute, lit:SparqlLiteral, dt:SQLDatatype):R2RState = {
     // println("equiv+= " + toString(attr) + "=" + lit)
-    R2RState(state.joins, state.varmap, state.exprs + RelationalExpressionEq(constrainMe,RValueTyped(dt,Name(lit.lit.lexicalForm))))    
+    R2RState(state.joins, state.varmap, state.exprs + RelationalExpressionEq(PrimaryExpressionAttr(constrainMe),PrimaryExpressionTyped(dt,Name(lit.lit.lexicalForm))))    
   }
 
   /** varConstraint
@@ -96,7 +96,7 @@
 	state // !!! what about disjoints?
       else {
 	/* Constraint against the initial binding for this variable. */
-	val constraint = RelationalExpressionEq(varToAttribute(state.varmap, v), RValueAttr(constrainMe))
+	val constraint = RelationalExpressionEq(PrimaryExpressionAttr(varToAttribute(state.varmap, v)), PrimaryExpressionAttr(constrainMe))
 	R2RState(state.joins, state.varmap, 
 		 if (varToAttributeDisjoints(state.varmap, v).size > 0) {
 		   state.exprs ++ {varToAttributeDisjoints(state.varmap, v) map ((d) => ExprDisjunction(Set(d, constraint)))}
@@ -176,7 +176,7 @@
 	      val fkaliasattr = RelAliasAttribute(oRelAlias, fkattr)
 	      val state_t = R2RState(state_subjJoin.joins + InnerJoin(AliasedResource(fkrel,oRelAlias)),
 				     state_subjJoin.varmap,
-				     state_subjJoin.exprs + RelationalExpressionEq(fkaliasattr,RValueAttr(objattr)))
+				     state_subjJoin.exprs + RelationalExpressionEq(PrimaryExpressionAttr(fkaliasattr),PrimaryExpressionAttr(objattr)))
 
 	      (fkaliasattr, fkrel, fkdt, state_t)
 	    } else {
@@ -249,7 +249,7 @@
   }
 
   def filter2expr(varmap:Map[Var, SQL2RDFValueMapper], f:SparqlPrimaryExpression):RelationalExpression = {
-    val (lTerm:Term, rTerm:Term, sqlexpr) = f match { // sqlexpr::((RelAliasAttribute,RValueAttr)=>RelationalExpression)
+    val (lTerm:Term, rTerm:Term, sqlexpr) = f match { // sqlexpr::((RelAliasAttribute,PrimaryExpressionAttr)=>RelationalExpression)
       case SparqlPrimaryExpressionEq(l, r) => (l.term, r.term, RelationalExpressionEq(_,_))
       case SparqlPrimaryExpressionLt(l, r) => (l.term, r.term, RelationalExpressionLt(_,_))
     }
@@ -263,11 +263,11 @@
 	val r = rTerm match {
 	  case TermUri(obj) => null // :ObjUri
 	  case TermVar(v) => { // :Var
-	    RValueAttr(varToAttribute(varmap, v))
+	    PrimaryExpressionAttr(varToAttribute(varmap, v))
 	  }
-	  case TermLit(lit) => null // :SparqlLiteral => RValueTyped(SQLDatatype, lit.n)
+	  case TermLit(lit) => null // :SparqlLiteral => PrimaryExpressionTyped(SQLDatatype, lit.n)
 	}
-	sqlexpr(l, r)
+	sqlexpr(PrimaryExpressionAttr(l), r)
       }
       // does not handle FILTER (7 = ?v)
       case TermLit(lit) => error("only SPARQL PrimaryExpressions with a variable on the left have been implemented: punting on " + f)
@@ -288,7 +288,7 @@
       case TriplesBlock(triplepatterns) => {
 	/* Examine each triple, updating the compilation state. */
 	val state2 = triplepatterns.foldLeft(state)((incState,s) => bindOnPredicate(db, incState, s, pk, enforeForeignKeys))
-	val nullExprs = findVars(gp) map (vvar => RelationalExpressionNotNull(varToAttribute(state2.varmap, vvar)))
+	val nullExprs = findVars(gp) map (vvar => RelationalExpressionNotNull(PrimaryExpressionAttr(varToAttribute(state2.varmap, vvar))))
 	R2RState(state2.joins, state2.varmap, state2.exprs ++ nullExprs)
       }
       case TableConjunction(list) => {
@@ -307,9 +307,9 @@
 	  val (outerState, outerDisjoints, no) = incPair
 	  val disjointState = mapGraphPattern(db, emptyState, disjoint, pk, enforeForeignKeys)
 	  val disjointVars = findVars(disjoint)
-	  val disjointNo = NamedAttribute(ConstInt("" + no), AttrAlias(Name("_DISJOINT_")))
+	  val disjointNo = NamedAttribute(PrimaryExpressionTyped(SQLDatatype.INTEGER,Name("" + no)), AttrAlias(Name("_DISJOINT_")))
 	  val disjointNoAliasAttr = RelAliasAttribute(unionAlias, Attribute(Name("_DISJOINT_")))
-	  val disjointCond = RelationalExpressionNe(disjointNoAliasAttr, RValueTyped(SQLDatatype.INTEGER,Name("" + no)))
+	  val disjointCond = RelationalExpressionNe(PrimaryExpressionAttr(disjointNoAliasAttr), PrimaryExpressionTyped(SQLDatatype.INTEGER,Name("" + no)))
 
 	  val attrlist:Set[NamedAttribute] = unionVars.foldLeft(Set(disjointNo))((attrs, v) => {
 	    val attrOrNull = if (disjointState.varmap.contains(v)) varToAttribute(disjointState.varmap, v) else ConstNULL()
@@ -345,7 +345,7 @@
 	      val newConstraints =
 		if (varToAttribute(outerState.varmap, v) != varAliasAttr) {
 		  /* Constraint against binding from earlier GP. */
-		  val constraint = RelationalExpressionEq(varToAttribute(outerState.varmap, v), RValueAttr(varAliasAttr))
+		  val constraint = RelationalExpressionEq(PrimaryExpressionAttr(varToAttribute(outerState.varmap, v)), PrimaryExpressionAttr(varAliasAttr))
 		  if (varToAttributeDisjoints(outerState.varmap, v).size > 0)
 		    // (union0._DISJOINT_ != 0 AND union1._DISJOINT_ != 2) OR union0.x=union1.x
 		    varToAttributeDisjoints(outerState.varmap, v) map ((d) => ExprDisjunction(Set(ExprConjunction(Set(d, disjointCond)), constraint)))
@@ -409,7 +409,7 @@
       	      Map()
       	    val newConstraints = {
       	      /* Constraint against binding from earlier GP. */
-      	      val constraint = RelationalExpressionEq(varToAttribute(state.varmap, v), RValueAttr(varAliasAttr))
+      	      val constraint = RelationalExpressionEq(PrimaryExpressionAttr(varToAttribute(state.varmap, v)), PrimaryExpressionAttr(varAliasAttr))
       	      if (varToAttributeDisjoints(state.varmap, v).size > 0)
       		// (leftJoin0._DISJOINT_ != 0 AND leftJoin1._DISJOINT_ != 2) OR leftJoin0.x=leftJoin1.x
       		varToAttributeDisjoints(state.varmap, v) map ((d) => ExprDisjunction(Set(d, constraint)))
--- a/src/main/scala/SQL.scala	Sun Jan 03 03:01:12 2010 -0500
+++ b/src/main/scala/SQL.scala	Sun Jan 03 16:11:18 2010 -0500
@@ -34,24 +34,24 @@
   // foo, bar
   override def toString = "SELECT "+(attributes mkString (",\n       "))
 }
-case class NamedAttribute(value:RelAliasAttributeORConst, attralias:AttrAlias) {
+case class NamedAttribute(value:RelAliasAttributeORExpression, attralias:AttrAlias) {
   override def toString = value + " AS " + attralias
 }
 //case class RelAttribute(relation:Relation, attribute:Attribute) c.f. ForeignKey
-sealed abstract class RelAliasAttributeORConst
-case class RelAliasAttribute(relalias:RelAlias, attribute:Attribute) extends RelAliasAttributeORConst {
+sealed abstract class RelAliasAttributeORExpression
+case class RelAliasAttribute(relalias:RelAlias, attribute:Attribute) extends RelAliasAttributeORExpression {
   override def toString = relalias + "." + attribute
 }
-sealed abstract class Const extends RelAliasAttributeORConst
-case class ConstNULL() extends Const {
-  override def toString = "NULL"
-}
-case class ConstInt(i:String) extends Const {
-  override def toString = "" + i
-}
-case class ConstChars(s:String) extends Const {
-  override def toString = "\"" + s + "\""
-}
+// sealed abstract class Const extends RelAliasAttributeORPrimaryExpression
+// case class ConstNULL() extends Const {
+//   override def toString = "NULL"
+// }
+// case class ConstInt(i:String) extends Const {
+//   override def toString = "" + i
+// }
+// case class ConstChars(s:String) extends Const {
+//   override def toString = "\"" + s + "\""
+// }
 
 case class Attribute(n:Name) {
   override def toString = n.s /* "'" + n.s + "'" */
@@ -81,7 +81,7 @@
 case class AliasedResource(rel:RelationORSubselect, as:RelAlias) {
   override def toString = rel + " AS " + as
 }
-sealed abstract class Expression
+sealed abstract class Expression extends RelAliasAttributeORExpression
 case class ExprConjunction(exprs:Set[Expression]) extends Expression {
   override def toString = "(" + (exprs mkString (")\n       AND (")) + ")"
 }
@@ -89,25 +89,32 @@
   override def toString = "(" + (exprs mkString (") OR (")) + ")"
 }
 sealed abstract class RelationalExpression extends Expression
-case class RelationalExpressionEq(l:RelAliasAttribute, r:RValue) extends RelationalExpression {
+case class RelationalExpressionEq(l:Expression, r:Expression) extends RelationalExpression {
   override def toString = l + "=" + r
 }
-case class RelationalExpressionNe(l:RelAliasAttribute, r:RValue) extends RelationalExpression {
+case class RelationalExpressionNe(l:Expression, r:Expression) extends RelationalExpression {
   override def toString = l + "!=" + r
 }
-case class RelationalExpressionLt(l:RelAliasAttribute, r:RValue) extends RelationalExpression {
+case class RelationalExpressionLt(l:Expression, r:Expression) extends RelationalExpression {
   override def toString = l + "<" + r
 }
-case class RelationalExpressionNotNull(l:RelAliasAttribute) extends RelationalExpression {
+case class RelationalExpressionNotNull(l:Expression) extends RelationalExpression { // Expression?
   override def toString = l + " IS NOT NULL"
 }
-sealed abstract class RValue
-case class RValueAttr(fqattribute:RelAliasAttribute) extends RValue {
+sealed abstract class PrimaryExpression extends Expression
+case class PrimaryExpressionAttr(fqattribute:RelAliasAttribute) extends PrimaryExpression {
   override def toString = "" + fqattribute
 }
-case class RValueTyped(datatype:SQLDatatype, i:Name) extends RValue {
+case class PrimaryExpressionTyped(datatype:SQLDatatype, i:Name) extends PrimaryExpression {
   override def toString = i.s /* "'" + i.s + "'" */ /* + datatype */
 }
+case class ConstNULL() extends PrimaryExpression {
+  override def toString = "NULL"
+}
+case class Concat(args:List[Expression]) extends PrimaryExpression {
+  override def toString = args.mkString("CONCAT(", ", ", ")")
+}
+
 case class Name(s:String)
 
 object Name {
@@ -146,25 +153,19 @@
     repsep(namedattribute, ",") ^^ { l => AttributeList(l.toSet) }
 
   def namedattribute:Parser[NamedAttribute] =
-    fqattributeORconst ~ "AS" ~ attralias ^^
-    { case fqattributeORconst ~ "AS" ~ attralias =>
-      NamedAttribute(fqattributeORconst, attralias) }
+    fqattributeORprimaryexpression ~ "AS" ~ attralias ^^
+    { case fqattributeORprimaryexpression ~ "AS" ~ attralias =>
+      NamedAttribute(fqattributeORprimaryexpression, attralias) }
 
-  def fqattributeORconst:Parser[RelAliasAttributeORConst] = (
+  def fqattributeORprimaryexpression:Parser[RelAliasAttributeORExpression] = (
       fqattribute ^^ { case fqattribute => fqattribute }
-    | const ^^ { case const => const }
+    | primaryexpression ^^ { case const => const }
   )
 
   def fqattribute:Parser[RelAliasAttribute] =
     relalias ~ "." ~ attribute ^^
     { case relalias ~ "." ~ attribute => RelAliasAttribute(relalias, attribute) }
 
-  def const:Parser[Const] = (
-      "NULL" ^^ { case "NULL" => ConstNULL() }
-    | int ^^ { case i => ConstInt(i) }
-    | chars ^^ { case ch => ConstChars(ch) }
-  )
-
   def attribute:Parser[Attribute] =
     """[a-zA-Z_]\w*""".r ^^ { x => Attribute(Name(x)) }
 
@@ -203,22 +204,25 @@
     { xs => if (xs.size > 1) ExprConjunction(xs.toSet) else xs(0) }
 
   def relationalexpression:Parser[Expression] = (
-      fqattribute ~ "=" ~ rvalue ^^
-      { case fqattribute ~ "=" ~ rvalue => RelationalExpressionEq(fqattribute, rvalue) }
-    | fqattribute ~ "!=" ~ rvalue ^^
-      { case fqattribute ~ "!=" ~ rvalue => RelationalExpressionNe(fqattribute, rvalue) }
-    | fqattribute ~ "<" ~ rvalue ^^
-      { case fqattribute ~ "<" ~ rvalue => RelationalExpressionLt(fqattribute, rvalue) }
-    | fqattribute ~ "IS" ~ "NOT" ~ "NULL" ^^
-      { case fqattribute ~ "IS" ~ "NOT" ~ "NULL" => RelationalExpressionNotNull(fqattribute) }
-    | "(" ~ expression ~ ")" ^^
-      { case "("~x~")" => x }
+      primaryexpression ~ "=" ~ primaryexpression ^^
+      { case primaryexpression ~ "=" ~ rvalue => RelationalExpressionEq(primaryexpression, rvalue) }
+    | primaryexpression ~ "!=" ~ primaryexpression ^^
+      { case primaryexpression ~ "!=" ~ rvalue => RelationalExpressionNe(primaryexpression, rvalue) }
+    | primaryexpression ~ "<" ~ primaryexpression ^^
+      { case primaryexpression ~ "<" ~ rvalue => RelationalExpressionLt(primaryexpression, rvalue) }
+    | primaryexpression ~ "IS" ~ "NOT" ~ "NULL" ^^
+      { case primaryexpression ~ "IS" ~ "NOT" ~ "NULL" => RelationalExpressionNotNull(primaryexpression) }
+    | primaryexpression ^^
+      { case primaryexpression => primaryexpression }
   )
 
-  def rvalue:Parser[RValue] = (
-      fqattribute ^^ { RValueAttr(_) }
-    | """[0-9]+""".r ^^ { x => RValueTyped(SQLDatatype.INTEGER, Name(x)) }
-    | "\"[^\"]*\"".r  ^^ { x => RValueTyped(SQLDatatype.STRING, Name(x.substring(1, x.size - 1))) }
+  def primaryexpression:Parser[Expression] = (
+      fqattribute ^^ { PrimaryExpressionAttr(_) }
+    | int ^^ { i => PrimaryExpressionTyped(SQLDatatype.INTEGER, Name(i)) }
+    | chars  ^^ { x => PrimaryExpressionTyped(SQLDatatype.STRING, Name(x.substring(1, x.size - 1))) }
+    | "NULL" ^^ { case "NULL" => ConstNULL() }
+    | "CONCAT" ~ "(" ~ rep1sep(expression, ",") ~ ")" ^^ { case "CONCAT"~"("~expressions~")" => Concat(expressions) }
+    | "(" ~ expression ~ ")" ^^ { case "("~x~")" => x }
   )
 
 }
--- a/src/test/scala/SQLTest.scala	Sun Jan 03 03:01:12 2010 -0500
+++ b/src/test/scala/SQLTest.scala	Sun Jan 03 16:11:18 2010 -0500
@@ -12,10 +12,10 @@
 R_manager.id=R_emp.manager AND R_emp.lastName IS NOT NULL AND R_manager.lastName IS NOT NULL
 """
     val expected = ExprConjunction(Set(
-      RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
-			     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
-      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))),
-      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))))))
+      RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id")))),
+			     PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
+      RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))),
+      RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName")))))))
     assert(expected === (a.parseAll(a.expression, e).get))
   }
 
@@ -26,10 +26,10 @@
 R_manager.id=R_emp.manager OR R_emp.lastName IS NOT NULL OR R_manager.lastName IS NOT NULL
 """
     val expected = ExprDisjunction(Set(
-      RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
-			     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
-      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))),
-      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))))))
+      RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id")))),
+			     PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
+      RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))),
+      RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName")))))))
     assert(expected === (a.parseAll(a.expression, e).get))
   }
 
@@ -40,10 +40,10 @@
 ( R_manager.id=R_emp.manager OR R_emp.lastName IS NOT NULL OR R_manager.lastName IS NOT NULL )
 """
     val expected = ExprDisjunction(Set(
-      RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
-			     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
-      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))),
-      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))))))
+      RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id")))),
+			     PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
+      RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))),
+      RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName")))))))
     assert(expected === (a.parseAll(a.expression, e).get))
   }
 
@@ -65,10 +65,10 @@
 			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))),
 					InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager")))))),
 			  Some(ExprConjunction(Set(
-			    RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
-						RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
-			    RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))),
-			    RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))))))))
+			    RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id")))),
+						   PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
+			    RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))),
+			    RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName")))))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
@@ -83,9 +83,9 @@
 									      Attribute(Name("lastName"))),
 							    AttrAlias(Name("A_empName"))))),
 			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))))),
-			  Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))),
-							      RValueTyped(SQLDatatype.INTEGER,Name("18"))),
-					  RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))))))
+			  Some(ExprConjunction(Set(RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager")))),
+									  PrimaryExpressionTyped(SQLDatatype.INTEGER,Name("18"))),
+					  RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
@@ -102,11 +102,11 @@
 							    AttrAlias(Name("A_empName"))))),
 			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))),
 					InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager")))))),
-			  Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))),
-							      RValueAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))))),
-					  RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))),
-							      RValueTyped(SQLDatatype.STRING,Name("Johnson"))),
-					  RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))))))
+			  Some(ExprConjunction(Set(RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager")))),
+									  PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))))),
+					  RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName")))),
+								 PrimaryExpressionTyped(SQLDatatype.STRING,Name("Johnson"))),
+					  RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
@@ -134,20 +134,20 @@
 					InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager")))),
 					InnerJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_upper")))),
 					InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_grandManager")))))),
-			  Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_lower")),Attribute(Name("manages"))),
-							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("id"))))),
-					 RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
-							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_lower")),Attribute(Name("manager"))))),
-					 RelationalExpressionLt(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("birthday"))),
-							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("birthday"))))),
-					 RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_upper")),Attribute(Name("manages"))),
-							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))))),
-					 RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("id"))),
-							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_upper")),Attribute(Name("manager"))))),
-					 RelationalExpressionLt(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("birthday"))),
-							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("birthday"))))),
-					 RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))),
-					 RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("lastName"))))))))
+			  Some(ExprConjunction(Set(RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_lower")),Attribute(Name("manages")))),
+									  PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("id"))))),
+					 RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id")))),
+								PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_lower")),Attribute(Name("manager"))))),
+					 RelationalExpressionLt(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("birthday")))),
+								PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("birthday"))))),
+					 RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_upper")),Attribute(Name("manages")))),
+								PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))))),
+					 RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("id")))),
+								PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_upper")),Attribute(Name("manager"))))),
+					 RelationalExpressionLt(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("birthday")))),
+								PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("birthday"))))),
+					 RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))),
+					 RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("lastName")))))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
@@ -181,9 +181,9 @@
 						   InnerJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_above")))),
 						   InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager"))))
 						 )), 
-						 Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("manager"))),
-										    RValueAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))))),
-								RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName")))))))), 
+						 Some(ExprConjunction(Set(RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("manager")))),
+												 PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))))),
+								RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))))))))), 
 					  Select(AttributeList(Set(NamedAttribute(RelAliasAttribute(RelAlias(Name("R_managed")), Attribute(Name("lastName"))),
 										  AttrAlias(Name("A_name"))), 
 								   NamedAttribute(RelAliasAttribute(RelAlias(Name("R_below")), Attribute(Name("manager"))),
@@ -192,14 +192,14 @@
 						   InnerJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_below")))),
 						   InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_managed"))))
 						 )), 
-						 Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_below")),Attribute(Name("manages"))),
-										    RValueAttr(RelAliasAttribute(RelAlias(Name("R_managed")),Attribute(Name("id"))))),
-								RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_managed")),Attribute(Name("lastName"))))))))))),
+						 Some(ExprConjunction(Set(RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_below")),Attribute(Name("manages")))),
+												 PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_managed")),Attribute(Name("id"))))),
+								RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_managed")),Attribute(Name("lastName")))))))))))),
 							RelAlias(Name("R_union1")))))), 
-			  Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_union1")),Attribute(Name("A_who"))),
-							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_who")),Attribute(Name("id"))))),
-					 RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_who")),Attribute(Name("lastName"))),
-							     RValueTyped(SQLDatatype.STRING,Name("Smith")))))))
+			  Some(ExprConjunction(Set(RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_union1")),Attribute(Name("A_who")))),
+									  PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_who")),Attribute(Name("id"))))),
+					 RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_who")),Attribute(Name("lastName")))),
+								PrimaryExpressionTyped(SQLDatatype.STRING,Name("Smith")))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
@@ -217,7 +217,27 @@
 							    AttrAlias(Name("A_bday"))))),
 			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_above")))))),
 			  Some(
-			    RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id"))))))
+			    RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id")))))))
+    assert(expected === (a.parseAll(a.select, e).get))
+  }
+
+  test("parse CONCAT") {
+    val a = Sql()
+    val QuotedBaseURI = "\"http://hr.example/DB/\""
+    val e = """
+SELECT CONCAT(""" + QuotedBaseURI + """, "Employee", "/", "id", ".", R_emp1.id, "#record") AS A_emp1
+       FROM Employee AS R_emp1
+"""
+    val expected = Select(AttributeList(Set(NamedAttribute(Concat(List(PrimaryExpressionTyped(SQLDatatype("String"),Name("http://hr.example/DB/")),
+								       PrimaryExpressionTyped(SQLDatatype("String"),Name("Employee")),
+								       PrimaryExpressionTyped(SQLDatatype("String"),Name("/")),
+								       PrimaryExpressionTyped(SQLDatatype("String"),Name("id")),
+								       PrimaryExpressionTyped(SQLDatatype("String"),Name(".")),
+								       PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp1")),Attribute(Name("id")))),
+								       PrimaryExpressionTyped(SQLDatatype("String"),Name("#record")))),
+							    AttrAlias(Name("A_emp1"))))),
+			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp1")))))),
+			  None)
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
@@ -236,12 +256,12 @@
 			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_above")))))),
 			  Some(
 			    ExprDisjunction(Set(
-			      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id")))),
+			      RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id"))))),
 			      ExprConjunction(Set(
-				RelationalExpressionLt(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id"))),
-						       RValueTyped(SQLDatatype.INTEGER,Name("5"))),
-				RelationalExpressionLt(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id"))),
-						       RValueTyped(SQLDatatype.INTEGER,Name("3")))
+				RelationalExpressionLt(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id")))),
+						       PrimaryExpressionTyped(SQLDatatype.INTEGER,Name("5"))),
+				RelationalExpressionLt(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id")))),
+						       PrimaryExpressionTyped(SQLDatatype.INTEGER,Name("3")))
 			      ))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }
@@ -262,12 +282,11 @@
 							    AttrAlias(Name("A_manageName"))))),
 			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))),
 					LeftOuterJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_mang"))),
-						      RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_mang")),Attribute(Name("emp"))),
-									     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("id"))))
-
+						      RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_mang")),Attribute(Name("emp")))),
+									     PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("id"))))
 									   )))),
 			  Some(
-			      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))
+			      RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))))
 			    ))
     assert(expected === (a.parseAll(a.select, e).get))
   }
@@ -293,9 +312,9 @@
 				    TableList(Set(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))))),
 				    None)),
 			     RelAlias(Name("R_mang"))),
-					 RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_mang")),Attribute(Name("emp"))),
-								RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("id")))))))),
-	     Some(RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))))
+					 RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_mang")),Attribute(Name("emp")))),
+								PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("id")))))))),
+	     Some(RelationalExpressionNotNull(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }