Skip to content

Commit

Permalink
feat: enhance query builder with improved alias handling (#285)
Browse files Browse the repository at this point in the history
* feat: enhance query builder with improved alias handling

- Updated `quote` function to handle SQL expressions with aliases more robustly.
- Added support for quoting function expressions like `COUNT(id) as count` and `MAX(id) as maxId`.
- Improved test coverage by adding a test case for `select count` in both v1 and v2 test suites.
- Ensured consistent quoting of standalone identifiers and expressions in query generation.

This change improves the flexibility and correctness of SQL query generation, particularly in handling complex expressions with aliases.

* v0.30.1
  • Loading branch information
itsumura-h authored Oct 12, 2024
1 parent 2e45dce commit 4ab094f
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 15 deletions.
2 changes: 1 addition & 1 deletion allographer.nimble
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Package

version = "0.30.0"
version = "0.30.1"
author = "Hidenobu Itsumura @dumblepytech1 as 'medy'"
description = "A Nim query builder library inspired by Laravel/PHP and Orator/Python"
license = "MIT"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,35 @@ import ../postgres_types

proc quote(input:string):string =
## `User.id as userId` => `"User"."id" as "userId"`
##
## `COUNT(id) as count` => `COUNT("id") as "count"`
##
## `MAX(id) as maxId` => `MAX("id") as "maxId"`
##

var tmp = newSeq[string]()
for row in input.split("."):
if row.contains(" as "):
let c = row.split(" as ")
tmp.add(&"\"{c[0]}\" as \"{c[1]}\"")
for segment in input.split("."):
if segment.contains(" as "):
# Split once on ' as ' to separate the expression and its alias
let parts = segment.split(" as ", maxsplit = 1)
let expression = parts[0]
let alias = parts[1]
if expression.contains("("):
# Extract function name and column name using index-based slicing
let funcStart = expression.find('(')
let funcEnd = expression.find(')', funcStart)
let funcName = expression[0 ..< funcStart]
let columnName = expression[funcStart + 1 ..< funcEnd]
tmp.add(&"{funcName}(\"{columnName}\") as \"{alias}\"")
else:
# Quote the expression and alias
tmp.add(&"\"{expression}\" as \"{alias}\"")
elif segment.contains("("):
# Leave functions without alias as is
tmp.add(segment)
else:
tmp.add(&"\"{row}\"")
# Quote standalone identifiers
tmp.add(&"\"{segment}\"")
return tmp.join(".")


Expand All @@ -31,8 +53,7 @@ proc selectSql*(self: PostgresQuery): PostgresQuery =
for i, item in self.query["select"].getElems():
if i > 0: queryString.add(",")
var column = item.getStr()
if column != "*":
column = quote(column)
if column != "*": column = quote(column)
queryString.add(&" {column}")
else:
queryString.add(" *")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,35 @@ import ../postgres_types

proc quote(input:string):string =
## `User.id as userId` => `"User"."id" as "userId"`
##
## `COUNT(id) as count` => `COUNT("id") as "count"`
##
## `MAX(id) as maxId` => `MAX("id") as "maxId"`
##

var tmp = newSeq[string]()
for row in input.split("."):
if row.contains(" as "):
let c = row.split(" as ")
tmp.add(&"\"{c[0]}\" as \"{c[1]}\"")
for segment in input.split("."):
if segment.contains(" as "):
# Split once on ' as ' to separate the expression and its alias
let parts = segment.split(" as ", maxsplit = 1)
let expression = parts[0]
let alias = parts[1]
if expression.contains("("):
# Extract function name and column name using index-based slicing
let funcStart = expression.find('(')
let funcEnd = expression.find(')', funcStart)
let funcName = expression[0 ..< funcStart]
let columnName = expression[funcStart + 1 ..< funcEnd]
tmp.add(&"{funcName}(\"{columnName}\") as \"{alias}\"")
else:
# Quote the expression and alias
tmp.add(&"\"{expression}\" as \"{alias}\"")
elif segment.contains("("):
# Leave functions without alias as is
tmp.add(segment)
else:
tmp.add(&"\"{row}\"")
# Quote standalone identifiers
tmp.add(&"\"{segment}\"")
return tmp.join(".")


Expand All @@ -30,8 +52,7 @@ proc selectSql*(self: PostgresQuery): PostgresQuery =
for i, item in self.query["select"].getElems():
if i > 0: queryString.add(",")
var column = item.getStr()
if column != "*":
column = quote(column)
if column != "*": column = quote(column)
queryString.add(&" {column}")
else:
queryString.add(" *")
Expand Down
5 changes: 5 additions & 0 deletions tests/v1/postgres/test_query.nim
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ suite($rdb & " get"):
check t == @[%*{"email":"user10@example.com"}]


test("select count"):
var t = rdb.select("COUNT(id) as count").table("user").get().waitFor
check t[0]["count"].getInt() == 10


test("where"):
var t = rdb.table("user").where("auth_id", "=", "1").get().waitFor
check t == @[
Expand Down
7 changes: 7 additions & 0 deletions tests/v2/postgres/test_query.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ discard """
cmd: "nim c -d:reset -d:ssl -r $file"
"""

# nim c -d:reset -d:ssl -r tests/v2/postgres/test_query.nim

import std/unittest
import std/asyncdispatch
import std/httpclient
Expand Down Expand Up @@ -128,6 +130,11 @@ suite($rdb & " get"):
check t == @[%*{"email":"user10@example.com"}]


test("select count"):
var t = rdb.select("COUNT(id) as count").table("user").get().waitFor
check t[0]["count"].getInt() == 10


test("where"):
var t = rdb.table("user").where("auth_id", "=", "1").get().waitFor
check t == @[
Expand Down

0 comments on commit 4ab094f

Please sign in to comment.