def asSQL
: (String, Seq
[Any
])
def %==(other
: Expression
) = new EqualToExpression
(this, other
) def %!=(other
: Expression
) = new NotEqualToExpression
(this, other
) def %<(other
: Expression
) = new SmallerThanExpression
(this, other
) def %>(other
: Expression
) = new GreaterThanExpression
(this, other
) def %<=(other
: Expression
) = new SmallerThanOrEqualToExpression
(this, other
) def %>=(other
: Expression
) = new GreaterThanOrEqualToExpression
(this, other
) }
(relation.schema.name + "." + relation.name + "." + name, Seq.empty)
}
abstract class BinaryExpression
(a
: Expression, b
: Expression, operator
: String
) extends Expression
{ val (aSQL, bindings1
) = a.
asSQL val (bSQL, bindings2
) = b.
asSQL
("(" + aSQL + ") " + operator + " (" + bSQL + ")", bindings1 ++ bindings2)
}
}
class EqualToExpression
(a
: Expression, b
: Expression
) extends BinaryExpression
(a, b,
"=") class NotEqualToExpression
(a
: Expression, b
: Expression
) extends BinaryExpression
(a, b,
"<>") class SmallerThanExpression
(a
: Expression, b
: Expression
) extends BinaryExpression
(a, b,
"<") class GreaterThanExpression
(a
: Expression, b
: Expression
) extends BinaryExpression
(a, b,
">") class SmallerThanOrEqualToExpression
(a
: Expression, b
: Expression
) extends BinaryExpression
(a, b,
"<=") class GreaterThanOrEqualToExpression
(a
: Expression, b
: Expression
) extends BinaryExpression
(a, b,
">=")
("?", Array(value))
}
class Query
(relation
: Relation, fields
: Seq
[Expression
], filters
: Seq
[Expression
]) extends Expression
{ val (fieldSQLs, bindings1
) = fields.
map(_.
asSQL).
unzip val selectStr
= "SELECT (" + fieldSQLs.
mkString("), (") +
")\n"
val fromStr
= s
" FROM ${relation.schema.name}.${relation.name}\n"
val (filterSQLs, bindings2
) = filters.
map(_.
asSQL).
unzip if (filterSQLs.
length > 0) " WHERE (" + filterSQLs.mkString(")\n AND (") + ")\n"
""
(selectStr + fromStr + whereStr, bindings1 ++ bindings2)
}
def select
(newFields
: Expression
*) = new Query
(relation, fields ++ newFields, filters
)
def where
(newFilters
: Expression
*) = new Query
(relation, fields, filters ++ newFilters
) }
def from
(relation
: Relation
) = new Query
(relation, Array
[Expression
](), Array
[Expression
]()) }
def main
(args
: Array
[String
]) { val public
= new Schema
("public") val users
= new Relation
(public,
"users") val userId
= new Attribute
(users,
"id") val userName
= new Attribute
(users,
"name") val userEmailAddress
= new Attribute
(users,
"email_address") val userAge
= new Attribute
(users,
"age")
Query.from(users)
.
where(userAge
%> new IntExpression
(20)) .select(userId, userName, userEmailAddress)
val (sql, bindings
) = query.
asSQL println(sql)
bindings.foreach(println(_))
}
}
dHJhaXQgRXhwcmVzc2lvbiB7CiAgICBkZWYgYXNTUUw6IChTdHJpbmcsIFNlcVtBbnldKQoKICAgIGRlZiAlPT0ob3RoZXI6IEV4cHJlc3Npb24pID0gbmV3IEVxdWFsVG9FeHByZXNzaW9uKHRoaXMsIG90aGVyKQogICAgZGVmICUhPShvdGhlcjogRXhwcmVzc2lvbikgPSBuZXcgTm90RXF1YWxUb0V4cHJlc3Npb24odGhpcywgb3RoZXIpCiAgICBkZWYgJTwob3RoZXI6IEV4cHJlc3Npb24pID0gbmV3IFNtYWxsZXJUaGFuRXhwcmVzc2lvbih0aGlzLCBvdGhlcikKICAgIGRlZiAlPihvdGhlcjogRXhwcmVzc2lvbikgPSBuZXcgR3JlYXRlclRoYW5FeHByZXNzaW9uKHRoaXMsIG90aGVyKQogICAgZGVmICU8PShvdGhlcjogRXhwcmVzc2lvbikgPSBuZXcgU21hbGxlclRoYW5PckVxdWFsVG9FeHByZXNzaW9uKHRoaXMsIG90aGVyKQogICAgZGVmICU+PShvdGhlcjogRXhwcmVzc2lvbikgPSBuZXcgR3JlYXRlclRoYW5PckVxdWFsVG9FeHByZXNzaW9uKHRoaXMsIG90aGVyKQp9CgpjbGFzcyBTY2hlbWEodmFsIG5hbWU6IFN0cmluZykKCmNsYXNzIFJlbGF0aW9uKHZhbCBzY2hlbWE6IFNjaGVtYSwgdmFsIG5hbWU6IFN0cmluZykKCmNsYXNzIEF0dHJpYnV0ZSh2YWwgcmVsYXRpb246IFJlbGF0aW9uLCB2YWwgbmFtZTogU3RyaW5nKSBleHRlbmRzIEV4cHJlc3Npb24gewogICAgZGVmIGFzU1FMID0KICAgICAgICAocmVsYXRpb24uc2NoZW1hLm5hbWUgKyAiLiIgKyByZWxhdGlvbi5uYW1lICsgIi4iICsgbmFtZSwgU2VxLmVtcHR5KQp9CgphYnN0cmFjdCBjbGFzcyBCaW5hcnlFeHByZXNzaW9uKGE6IEV4cHJlc3Npb24sIGI6IEV4cHJlc3Npb24sIG9wZXJhdG9yOiBTdHJpbmcpIGV4dGVuZHMgRXhwcmVzc2lvbiB7CiAgICBkZWYgYXNTUUwgPSB7CiAgICAgICAgdmFsIChhU1FMLCBiaW5kaW5nczEpID0gYS5hc1NRTAogICAgICAgIHZhbCAoYlNRTCwgYmluZGluZ3MyKSA9IGIuYXNTUUwKCiAgICAgICAgKCIoIiArIGFTUUwgKyAiKSAiICsgb3BlcmF0b3IgKyAiICgiICsgYlNRTCArICIpIiwgYmluZGluZ3MxICsrIGJpbmRpbmdzMikKICAgIH0KfQoKY2xhc3MgRXF1YWxUb0V4cHJlc3Npb24oYTogRXhwcmVzc2lvbiwgYjogRXhwcmVzc2lvbikgZXh0ZW5kcyBCaW5hcnlFeHByZXNzaW9uKGEsIGIsICI9IikKY2xhc3MgTm90RXF1YWxUb0V4cHJlc3Npb24oYTogRXhwcmVzc2lvbiwgYjogRXhwcmVzc2lvbikgZXh0ZW5kcyBCaW5hcnlFeHByZXNzaW9uKGEsIGIsICI8PiIpCmNsYXNzIFNtYWxsZXJUaGFuRXhwcmVzc2lvbihhOiBFeHByZXNzaW9uLCBiOiBFeHByZXNzaW9uKSBleHRlbmRzIEJpbmFyeUV4cHJlc3Npb24oYSwgYiwgIjwiKQpjbGFzcyBHcmVhdGVyVGhhbkV4cHJlc3Npb24oYTogRXhwcmVzc2lvbiwgYjogRXhwcmVzc2lvbikgZXh0ZW5kcyBCaW5hcnlFeHByZXNzaW9uKGEsIGIsICI+IikKY2xhc3MgU21hbGxlclRoYW5PckVxdWFsVG9FeHByZXNzaW9uKGE6IEV4cHJlc3Npb24sIGI6IEV4cHJlc3Npb24pIGV4dGVuZHMgQmluYXJ5RXhwcmVzc2lvbihhLCBiLCAiPD0iKQpjbGFzcyBHcmVhdGVyVGhhbk9yRXF1YWxUb0V4cHJlc3Npb24oYTogRXhwcmVzc2lvbiwgYjogRXhwcmVzc2lvbikgZXh0ZW5kcyBCaW5hcnlFeHByZXNzaW9uKGEsIGIsICI+PSIpCgpjbGFzcyBJbnRFeHByZXNzaW9uKHZhbHVlOiBJbnQpIGV4dGVuZHMgRXhwcmVzc2lvbiB7CiAgICBkZWYgYXNTUUwgPQogICAgICAgICgiPyIsIEFycmF5KHZhbHVlKSkKfQoKY2xhc3MgUXVlcnkocmVsYXRpb246IFJlbGF0aW9uLCBmaWVsZHM6IFNlcVtFeHByZXNzaW9uXSwgZmlsdGVyczogU2VxW0V4cHJlc3Npb25dKSBleHRlbmRzIEV4cHJlc3Npb24gewogICAgZGVmIGFzU1FMID0gewogICAgICAgIHZhbCAoZmllbGRTUUxzLCBiaW5kaW5nczEpID0gZmllbGRzLm1hcChfLmFzU1FMKS51bnppcAogICAgICAgIHZhbCBzZWxlY3RTdHIgPSAiU0VMRUNUICgiICsgZmllbGRTUUxzLm1rU3RyaW5nKCIpLCAoIikgKyAiKVxuIgoKICAgICAgICB2YWwgZnJvbVN0ciA9IHMiICBGUk9NICR7cmVsYXRpb24uc2NoZW1hLm5hbWV9LiR7cmVsYXRpb24ubmFtZX1cbiIKCiAgICAgICAgdmFsIChmaWx0ZXJTUUxzLCBiaW5kaW5nczIpID0gZmlsdGVycy5tYXAoXy5hc1NRTCkudW56aXAKICAgICAgICB2YWwgd2hlcmVTdHIgPQogICAgICAgICAgICBpZiAoZmlsdGVyU1FMcy5sZW5ndGggPiAwKQogICAgICAgICAgICAgICAgIiBXSEVSRSAoIiArIGZpbHRlclNRTHMubWtTdHJpbmcoIilcbiAgIEFORCAoIikgKyAiKVxuIgogICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICAiIgoKICAgICAgICAoc2VsZWN0U3RyICsgZnJvbVN0ciArIHdoZXJlU3RyLCBiaW5kaW5nczEgKysgYmluZGluZ3MyKQogICAgfQoKICAgIGRlZiBzZWxlY3QobmV3RmllbGRzOiBFeHByZXNzaW9uKikgPQogICAgICAgIG5ldyBRdWVyeShyZWxhdGlvbiwgZmllbGRzICsrIG5ld0ZpZWxkcywgZmlsdGVycykKCiAgICBkZWYgd2hlcmUobmV3RmlsdGVyczogRXhwcmVzc2lvbiopID0KICAgICAgICBuZXcgUXVlcnkocmVsYXRpb24sIGZpZWxkcywgZmlsdGVycyArKyBuZXdGaWx0ZXJzKQp9CgpvYmplY3QgUXVlcnkgewogICAgZGVmIGZyb20ocmVsYXRpb246IFJlbGF0aW9uKSA9IG5ldyBRdWVyeShyZWxhdGlvbiwgQXJyYXlbRXhwcmVzc2lvbl0oKSwgQXJyYXlbRXhwcmVzc2lvbl0oKSkKfQoKb2JqZWN0IE1haW4gewogICAgZGVmIG1haW4oYXJnczogQXJyYXlbU3RyaW5nXSkgewogICAgICAgIHZhbCBwdWJsaWMgPSBuZXcgU2NoZW1hKCJwdWJsaWMiKQogICAgICAgIHZhbCB1c2VycyA9IG5ldyBSZWxhdGlvbihwdWJsaWMsICJ1c2VycyIpCiAgICAgICAgdmFsIHVzZXJJZCA9IG5ldyBBdHRyaWJ1dGUodXNlcnMsICJpZCIpCiAgICAgICAgdmFsIHVzZXJOYW1lID0gbmV3IEF0dHJpYnV0ZSh1c2VycywgIm5hbWUiKQogICAgICAgIHZhbCB1c2VyRW1haWxBZGRyZXNzID0gbmV3IEF0dHJpYnV0ZSh1c2VycywgImVtYWlsX2FkZHJlc3MiKQogICAgICAgIHZhbCB1c2VyQWdlID0gbmV3IEF0dHJpYnV0ZSh1c2VycywgImFnZSIpCgogICAgICAgIHZhbCBxdWVyeSA9CiAgICAgICAgICAgIFF1ZXJ5LmZyb20odXNlcnMpCiAgICAgICAgICAgICAgICAgLndoZXJlKHVzZXJBZ2UgJT4gbmV3IEludEV4cHJlc3Npb24oMjApKQogICAgICAgICAgICAgICAgIC5zZWxlY3QodXNlcklkLCB1c2VyTmFtZSwgdXNlckVtYWlsQWRkcmVzcykKCiAgICAgICAgdmFsIChzcWwsIGJpbmRpbmdzKSA9IHF1ZXJ5LmFzU1FMCiAgICAgICAgcHJpbnRsbihzcWwpCiAgICAgICAgYmluZGluZ3MuZm9yZWFjaChwcmludGxuKF8pKQogICAgfQp9Cg==