Skip to content

Commit

Permalink
Merge pull request #43 from darkredz/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
darkredz authored Apr 10, 2022
2 parents d943166 + fc48abf commit 63af067
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 20 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,23 @@ Query()
.order("user.id")
.limit(10, 20)
.toSql()

// Join with subquery
Query().fields("*")
.from("user")
.innerJoin(
Query().fields("id", "user_id", "(total_savings - total_spendings) as balance").from("report"),
"user_wallet"
)
.on("user_wallet.user_id = user.id")
.toSql()
```

Outputs
```sql
SELECT * FROM user LEFT JOIN address ON (address.user_id = user.id )
SELECT user.id as `user-id`, user.name as `user-name`, role.id as `role-id`, role.role_name as `role-role_name`, user.id as `role-user_id`, address.id as `address-id`, address.street1 as `address-street1`, address.street2 as `address-street2`, user.id as `address-user_id` FROM user LEFT JOIN address ON (address.user_id = user.id ) LEFT JOIN user_has_role ON (user_has_role.user_id = user.id ) LEFT JOIN role ON (role.id = user_has_role.role_id ) WHERE user.status > 0 OR user.id NOT IN (1,2,3) GROUP BY role.id, role.name HAVING SUM( role.id ) > 2 AND COUNT( role.id ) < 10 ORDER BY user.id ASC LIMIT 10 OFFSET 20
SELECT * FROM user INNER JOIN ( SELECT id, user_id, (total_savings - total_spendings) as balance FROM report ) as user_wallet ON ( user_wallet.user_id = user.id )
```

#### MySQL Fulltext search
Expand Down Expand Up @@ -752,7 +763,7 @@ Add this to your maven pom.xml
<dependency>
<groupId>io.zeko</groupId>
<artifactId>zeko-sql-builder</artifactId>
<version>1.3.1</version>
<version>1.3.2</version>
</dependency>

<!-- Jasync Mysql driver if needed -->
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>io.zeko</groupId>
<artifactId>zeko-sql-builder</artifactId>
<version>1.3.2-SNAPSHOT</version>
<version>1.3.3-SNAPSHOT</version>
<packaging>jar</packaging>

<name>${project.groupId}:${project.artifactId}</name>
Expand Down
59 changes: 52 additions & 7 deletions src/main/kotlin/io/zeko/db/sql/Query.kt
Original file line number Diff line number Diff line change
Expand Up @@ -175,37 +175,82 @@ open class Query {
}

fun join(table: String): Query {
tableToJoin["join-" + table] = arrayListOf()
tableToJoin["join-@" + table] = arrayListOf()
return this
}

fun join(table: Query, asName: String): Query {
tableToJoin["join-@**" + table.toSql() + "^^$asName"] = arrayListOf()
return this
}

fun fullJoin(table: String): Query {
tableToJoin["full-@join-@" + table] = arrayListOf()
return this
}

fun fullJoin(table: Query, asName: String): Query {
tableToJoin["full-@join-@**" + table.toSql() + "^^$asName"] = arrayListOf()
return this
}

fun leftJoin(table: String): Query {
tableToJoin["left-join-" + table] = arrayListOf()
tableToJoin["left-@join-@" + table] = arrayListOf()
return this
}

fun leftJoin(table: Query, asName: String): Query {
tableToJoin["left-@join-@**" + table.toSql() + "^^$asName"] = arrayListOf()
return this
}

fun leftOuterJoin(table: String): Query {
tableToJoin["left-outer-join-" + table] = arrayListOf()
tableToJoin["left-@outer-@join-@" + table] = arrayListOf()
return this
}

fun leftOuterJoin(table: Query, asName: String): Query {
tableToJoin["left-@outer-@join-@**" + table.toSql() + "^^$asName"] = arrayListOf()
return this
}

fun rightJoin(table: String): Query {
tableToJoin["right-join-" + table] = arrayListOf()
tableToJoin["right-@join-@" + table] = arrayListOf()
return this
}

fun rightJoin(table: Query, asName: String): Query {
tableToJoin["right-@join-@**" + table.toSql() + "^^$asName"] = arrayListOf()
return this
}

fun rightOuterJoin(table: String): Query {
tableToJoin["right-outer-join-" + table] = arrayListOf()
tableToJoin["right-@outer-@join-@" + table] = arrayListOf()
return this
}

fun rightOuterJoin(table: Query, asName: String): Query {
tableToJoin["right-@outer-@join-@**" + table.toSql() + "^^$asName"] = arrayListOf()
return this
}

fun innerJoin(table: String): Query {
tableToJoin["inner-join-" + table] = arrayListOf()
tableToJoin["inner-@join-@" + table] = arrayListOf()
return this
}

fun innerJoin(table: Query, asName: String): Query {
tableToJoin["inner-@join-@**" + table.toSql() + "^^$asName"] = arrayListOf()
return this
}

fun crossJoin(table: String): Query {
tableToJoin["cross-join-" + table] = arrayListOf()
tableToJoin["cross-@join-@" + table] = arrayListOf()
return this
}

fun crossJoin(table: Query, asName: String): Query {
tableToJoin["cross-@join-@**" + table.toSql() + "^^$asName"] = arrayListOf()
return this
}

Expand Down
44 changes: 33 additions & 11 deletions src/main/kotlin/io/zeko/db/sql/QueryPart.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ data class QueryInfo(val sql: String, val columns: List<String>, val sqlFields:

class QueryParts {
private val rgxFindField = "([^\\\"\\ ][a-zA-Z0-9\\_]+[^\\\"\\ ])\\.([^\\\"\\s][a-zA-Z0-9\\_\\-\\=\\`\\~\\:\\.\\,\\|\\*\\^\\#\\@\\\$]+[\\^\"\\s])".toPattern()
private val rgxReplace = "\\\"\$1\\\".\$2"
var linebreak: String = " "
get() = field
set(value) {
Expand Down Expand Up @@ -49,8 +48,9 @@ class QueryParts {
this.custom = customExpression
}

private fun escapeTableName(statement: String): String {
private fun escapeTableName(statement: String, esp: String): String {
val matcher = rgxFindField.matcher(statement)
val rgxReplace = "\\" + esp + "\$1\\" + esp + ".\$2"
return matcher.replaceAll(rgxReplace)
}

Expand Down Expand Up @@ -82,7 +82,7 @@ class QueryParts {
val asTable = if (espTableName) "$esp$subTable$esp" else subTable
fromPart = "(${subParts.sql}) AS $asTable"
if (espTableName) {
fromPart = escapeTableName(fromPart)
fromPart = escapeTableName(fromPart, esp)
}
} else {
fromPart = "(${subParts.sql})"
Expand All @@ -99,8 +99,23 @@ class QueryParts {
private fun buildJoinsPart(esp: String, espTableName: Boolean): String {
var joinsPart = ""
for ((join, conditions) in joins) {
val parts = join.split("-")
val tbl = if (espTableName) "$esp${parts.last()}$esp" else parts.last()
val parts = join.split("-@")
val lastPart = parts.last()
var asName = ""
val tbl = if (espTableName) {
if (lastPart.startsWith("**")) {
asName = lastPart.substring(lastPart.indexOf("^^") + 2)
"( ${lastPart.removePrefix("**").removeSuffix("^^$asName")} )"
} else
"$esp$lastPart$esp"
} else {
if (lastPart.startsWith("**")) {
asName = lastPart.substring(lastPart.indexOf("^^") + 2)
"( ${lastPart.removePrefix("**").removeSuffix("^^$asName")} )"
} else
lastPart
}

val joinStmt = parts.subList(0, parts.size - 1).joinToString(" ").toUpperCase()
var logicStmt = ""

Expand All @@ -110,12 +125,19 @@ class QueryParts {
if (parts.size > 0 && !parts[0].contains(".")) {
s = "${tbl}.${s.trimStart()}"
}
logicStmt += if (espTableName) escapeTableName(s) else s
logicStmt += if (espTableName) escapeTableName(s, esp) else s
}

if (logicStmt != "") {
logicStmt = logicStmt.substring(0, logicStmt.length - 4)
joinsPart += linebreak + "$joinStmt $tbl ON ($logicStmt)"
if (asName.isNotEmpty()) {
if (espTableName) {
asName = "$esp$asName$esp"
}
joinsPart += linebreak + "$joinStmt $tbl as $asName ON ($logicStmt)"
} else {
joinsPart += linebreak + "$joinStmt $tbl ON ($logicStmt)"
}
}
}
return joinsPart
Expand All @@ -125,7 +147,7 @@ class QueryParts {
var wherePart = ""
where.forEach {
val s = "${it.getStatement()} ${it.getOperator()} "
wherePart += linebreak + (if (espTableName) escapeTableName(s) else s).trimEnd()
wherePart += linebreak + (if (espTableName) escapeTableName(s, esp) else s).trimEnd()
}

if (wherePart != "") {
Expand All @@ -139,7 +161,7 @@ class QueryParts {
if (groupBys.size > 0) {
groupBys.forEach {
if (espTableName) {
groupByPart += escapeTableName("$it, ")
groupByPart += escapeTableName("$it, ", esp)
} else {
groupByPart += "$it, "
}
Expand All @@ -155,7 +177,7 @@ class QueryParts {
var havingPart = ""
havings.forEach {
val s = "${it.getStatement()} ${it.getOperator()} "
havingPart += linebreak + (if (espTableName) escapeTableName(s) else s).trimEnd()
havingPart += linebreak + (if (espTableName) escapeTableName(s, esp) else s).trimEnd()
}

if (havingPart != "") {
Expand All @@ -168,7 +190,7 @@ class QueryParts {
var orderPart = ""
order.forEach {
val s = "${it.fieldName} ${it.getDirection()}, "
orderPart += if (espTableName) escapeTableName(s) else s
orderPart += if (espTableName) escapeTableName(s, esp) else s
}

if (orderPart != "") {
Expand Down
17 changes: 17 additions & 0 deletions src/test/kotlin/io/zeko/db/sql/ANSIJoinQuerySpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,23 @@ class ANSIJoinQuerySpec : Spek({
LIMIT 10 OFFSET 20
""".trimIndent().replace("\n", ""), sql)
}

it("should match sql with one table inner join a subquery") {
val sql = ANSIQuery().fields("*")
.from("user")
.innerJoin(
ANSIQuery().fields("id", "user_id", "(total_savings - total_spendings) as balance").from("report"),
"user_wallet"
)
.on("user_wallet.user_id = user.id")
.toSql()
debug(sql)
assertEquals("""
SELECT * FROM "user" INNER JOIN (
SELECT id, user_id, (total_savings - total_spendings) as balance FROM "report" ) as "user_wallet"
ON ( "user_wallet".user_id = "user".id )
""".trimIndent().replace("\n", ""), sql)
}
}
}
})
17 changes: 17 additions & 0 deletions src/test/kotlin/io/zeko/db/sql/MySQLJoinQuerySpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,23 @@ class MySQLJoinQuerySpec : Spek({
"GROUP BY role.id, role.name " +
"HAVING SUM( role.id ) > 2 AND COUNT( role.id ) < 10 ORDER BY user.id ASC LIMIT 10 OFFSET 20", sql)
}

it("should match sql with one table inner join a subquery") {
val sql = Query().fields("*")
.from("user")
.innerJoin(
Query().fields("id", "user_id", "(total_savings - total_spendings) as balance").from("report"),
"user_wallet"
)
.on("user_wallet.user_id = user.id")
.toSql()
debug(sql)
assertEquals("""
SELECT * FROM user INNER JOIN (
SELECT id, user_id, (total_savings - total_spendings) as balance FROM report ) as user_wallet
ON ( user_wallet.user_id = user.id )
""".trimIndent().replace("\n", ""), sql)
}
}
}
})

0 comments on commit 63af067

Please sign in to comment.