feat(clients): add nim-allographer client with BaraDB driver support
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
- Pure Nim wire protocol client (async + sync, no C FFI) - Query Builder: fluent API, SQL generator, execution, transactions - Schema Builder: create/alter/drop tables, column operations - Connection pool with aging and timeout handling - ORM integration via compile-time DB_BARADB switch - Tests: test_open, test_query - Documentation: README.md, PLAN_BARADB.md
This commit is contained in:
@@ -0,0 +1,773 @@
|
||||
Example: Query Builder for RDB
|
||||
===
|
||||
[back](../../README.md)
|
||||
|
||||
## index
|
||||
<!--ts-->
|
||||
* [Example: Query Builder for RDB](#example-query-builder-for-rdb)
|
||||
* [index](#index)
|
||||
* [Create Connection](#create-connection)
|
||||
* [SELECT](#select)
|
||||
* [return JsonNode](#return-jsonnode)
|
||||
* [return Object](#return-object)
|
||||
* [get](#get)
|
||||
* [first](#first)
|
||||
* [find](#find)
|
||||
* [columns](#columns)
|
||||
* [join](#join)
|
||||
* [where](#where)
|
||||
* [orWhere](#orwhere)
|
||||
* [whereBetween](#wherebetween)
|
||||
* [whereNotBetween](#wherenotbetween)
|
||||
* [whereIn](#wherein)
|
||||
* [whereNotIn](#wherenotin)
|
||||
* [whereNull](#wherenull)
|
||||
* [groupBy_having](#groupby_having)
|
||||
* [orderBy](#orderby)
|
||||
* [limit_offset](#limit_offset)
|
||||
* [paginate](#paginate)
|
||||
* [fastPaginate](#fastpaginate)
|
||||
* [INSERT](#insert)
|
||||
* [Return ID Insert](#return-id-insert)
|
||||
* [UPDATE](#update)
|
||||
* [DELETE](#delete)
|
||||
* [Plain Response](#plain-response)
|
||||
* [Raw SQL](#raw-sql)
|
||||
* [Prepared Statement](#prepared-statement)
|
||||
* [Aggregates](#aggregates)
|
||||
* [Transaction](#transaction)
|
||||
|
||||
<!-- Created by https://github.com/ekalinin/github-markdown-toc -->
|
||||
<!-- Added by: root, at: Mon Jul 17 07:46:13 UTC 2023 -->
|
||||
|
||||
<!--te-->
|
||||
---
|
||||
|
||||
## Create Connection
|
||||
```nim
|
||||
import allographer/connection
|
||||
|
||||
let maxConnections = 95
|
||||
let timeout = 30
|
||||
|
||||
# Using connection URL (Recommended)
|
||||
let rdb = dbOpen(PostgreSQL, "postgresql://user:password@localhost:5432/database", maxConnections, timeout)
|
||||
|
||||
# Optional pool aging controls
|
||||
# let rdb = dbOpen(
|
||||
# PostgreSQL,
|
||||
# "postgresql://user:password@localhost:5432/database",
|
||||
# maxConnections,
|
||||
# timeout,
|
||||
# maxConnectionLifetime = 300,
|
||||
# maxConnectionIdleTime = 300,
|
||||
# )
|
||||
|
||||
# Using positional arguments
|
||||
# let rdb = dbOpen(PostgreSQL, "database", "user", "password", "localhost", 5432, maxConnections, timeout)
|
||||
|
||||
# SQLite
|
||||
# let rdb = dbOpen(SQLite3, "/path/to/db.sqlite3", maxConnections, timeout)
|
||||
# let rdb = dbOpen(SQLite3, ":memory:", maxConnections, timeout)
|
||||
|
||||
# MySQL / MariaDB
|
||||
# let rdb = dbOpen(MySQL, "mysql://user:password@localhost:3306/database", maxConnections, timeout)
|
||||
# let rdb = dbOpen(MariaDB, "mariadb://user:password@localhost:3306/database", maxConnections, timeout)
|
||||
```
|
||||
|
||||
## SELECT
|
||||
[to index](#index)
|
||||
|
||||
When it returns following table
|
||||
|
||||
|id|float|char|datetime|null|is_admin|
|
||||
|---|---|---|---|---|---|
|
||||
|1|3.14|char|2019-01-01 12:00:00.1234||1|
|
||||
|
||||
### return JsonNode
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
echo rdb.table("test")
|
||||
.select("id", "float", "char", "datetime", "null", "is_admin")
|
||||
.get()
|
||||
.await
|
||||
```
|
||||
|
||||
```nim
|
||||
>> @[
|
||||
{
|
||||
"id": 1, # JInt
|
||||
"float": 3.14, # JFloat
|
||||
"char": "char", # JString
|
||||
"datetime": "2019-01-01 12:00:00.1234", # JString
|
||||
"null": null # JNull
|
||||
"is_admin": true # JBool
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### return Object
|
||||
If object is defined and set arg for `orm`, response will be an object as ORM
|
||||
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
type Typ = ref object
|
||||
id: int
|
||||
float: float
|
||||
char: string
|
||||
datetime: string
|
||||
null: string
|
||||
is_admin: bool
|
||||
|
||||
var rows = rdb.table("test")
|
||||
.select("id", "float", "char", "datetime", "null", "is_admin")
|
||||
.get()
|
||||
.orm(Typ)
|
||||
.await
|
||||
```
|
||||
|
||||
```nim
|
||||
echo rows[0].id
|
||||
>> 1 # int
|
||||
|
||||
echo rows[0].float
|
||||
>> 3.14 # float
|
||||
|
||||
echo rows[0].char
|
||||
>> "char" # string
|
||||
|
||||
echo rows[0].datetime
|
||||
>> "2019-01-01 12:00:00.1234" # string
|
||||
|
||||
echo rows[0].null
|
||||
>> "" # string
|
||||
|
||||
echo rows[0].is_admin
|
||||
>> true # bool
|
||||
```
|
||||
|
||||
If DB response is empty, `get` return empty seq, `find` and `first` return optional object.
|
||||
```nim
|
||||
let response = await rdb.table("test").get().orm(Typ).await
|
||||
assert response.len == 0
|
||||
|
||||
let response = await rdb.raw("select * from users").get().orm(Typ).await
|
||||
assert response.len == 0
|
||||
|
||||
let response = await rdb.table("test").find(1).orm(Typ).await
|
||||
assert response.type == Option[Typ]
|
||||
assert response.isSome == false
|
||||
|
||||
let response = await rdb.table("test").first().orm(Typ).await
|
||||
assert response.type == Option[Typ]
|
||||
assert response.isSome == false
|
||||
```
|
||||
|
||||
### get
|
||||
Retrieving all row from a table
|
||||
```nim
|
||||
let users = rdb.table("users").get().await
|
||||
for user in users:
|
||||
echo user["name"]
|
||||
```
|
||||
|
||||
### first
|
||||
Retrieving a single row from a table. This returns `Option[JsonNode]`
|
||||
```nim
|
||||
let user = rdb
|
||||
.table("users")
|
||||
.where("name", "=", "John")
|
||||
.first()
|
||||
.await
|
||||
if user.isSome:
|
||||
echo user.get["name"]
|
||||
```
|
||||
|
||||
### find
|
||||
Retrieve a single row by its primary key. This returns `Option[JsonNode]`
|
||||
```nim
|
||||
let user = rdb.table("users").find(1).await
|
||||
if user.isSome:
|
||||
echo user.get["name"]
|
||||
```
|
||||
|
||||
If the column name of a promary key is not "id", specify this in 2nd arg of `find`
|
||||
```nim
|
||||
let user = rdb.table("users").find(1, "user_id").await
|
||||
if user.isSome:
|
||||
echo user.get["name"]
|
||||
```
|
||||
|
||||
### columns
|
||||
Retrieve columns from table.
|
||||
```nim
|
||||
let columns = rdb.table("users").columns().await
|
||||
columns == @["id", "name", "email"]
|
||||
```
|
||||
|
||||
|
||||
### join
|
||||
```nim
|
||||
let users = rdb
|
||||
.table("users")
|
||||
.select("users.id", "contacts.phone", "orders.price")
|
||||
.join("contacts", "users.id", "=", "contacts.user_id")
|
||||
.join("orders", "users.id", "=", "orders.user_id")
|
||||
.get()
|
||||
.await
|
||||
```
|
||||
|
||||
### where
|
||||
```nim
|
||||
let users = rdb.table("users").where("age", ">", 25).get().await
|
||||
```
|
||||
|
||||
### orWhere
|
||||
```nim
|
||||
let users = rdb
|
||||
.table("users")
|
||||
.where("age", ">", 25)
|
||||
.orWhere("name", "=", "John")
|
||||
.get()
|
||||
.await
|
||||
```
|
||||
|
||||
### whereBetween
|
||||
```nim
|
||||
let users = rdb
|
||||
.table("users")
|
||||
.whereBetween("age", [25, 35])
|
||||
.get()
|
||||
.await
|
||||
```
|
||||
|
||||
### whereNotBetween
|
||||
```nim
|
||||
let users = rdb
|
||||
.table("users")
|
||||
.whereNotBetween("age", [25, 35])
|
||||
.get()
|
||||
.await
|
||||
```
|
||||
|
||||
### whereIn
|
||||
```nim
|
||||
let users = rdb
|
||||
.table("users")
|
||||
.whereIn("id", @[1, 2, 3])
|
||||
.get()
|
||||
.await
|
||||
```
|
||||
|
||||
### whereNotIn
|
||||
```nim
|
||||
let users = rdb
|
||||
.table("users")
|
||||
.whereNotIn("id", @[1, 2, 3])
|
||||
.get()
|
||||
.await
|
||||
```
|
||||
|
||||
### whereNull
|
||||
```nim
|
||||
let users = rdb
|
||||
.table("users")
|
||||
.whereNull("updated_at")
|
||||
.get()
|
||||
.await
|
||||
```
|
||||
|
||||
### groupBy_having
|
||||
```nim
|
||||
let users = rdb
|
||||
.table("users")
|
||||
.group_by("count")
|
||||
.having("count", ">", 100)
|
||||
.get()
|
||||
.await
|
||||
```
|
||||
|
||||
### orderBy
|
||||
```nim
|
||||
let users = rdb
|
||||
.table("users")
|
||||
.orderBy("name", Desc)
|
||||
.get()
|
||||
.await
|
||||
```
|
||||
2nd arg of `orderBy` is Enum. `Desc` or `Asc`
|
||||
|
||||
|
||||
### limit_offset
|
||||
```nim
|
||||
let users = rdb
|
||||
.table("users")
|
||||
.offset(10)
|
||||
.limit(5)
|
||||
.get()
|
||||
.await
|
||||
```
|
||||
|
||||
### paginate
|
||||
```nim
|
||||
rdb.table("users").delete(2).await
|
||||
let users = rdb
|
||||
.table("users")
|
||||
.select("id", "name")
|
||||
.paginate(3, 1)
|
||||
.await
|
||||
```
|
||||
arg1... Numer of items per page
|
||||
arg2... Numer of page(option)(1 is set by default)
|
||||
|
||||
```nim
|
||||
echo users
|
||||
>> {
|
||||
"count":3,
|
||||
"currentPage":[
|
||||
{"id":1,"name":"user1"},
|
||||
{"id":3,"name":"user3"},
|
||||
{"id":4,"name":"user4"}
|
||||
],
|
||||
"hasMorePages":true,
|
||||
"lastPage":3,
|
||||
"nextPage":2,
|
||||
"perPage":3,
|
||||
"previousPage":1,
|
||||
"total":9
|
||||
}
|
||||
```
|
||||
|
||||
|ATTRIBUTE|DESCRIPTION|
|
||||
|---|---|
|
||||
|count|number of results on the current page|
|
||||
|currentPage|results of current page|
|
||||
|hasMorePages|Returns `True` if there is more pages else `False`|
|
||||
|lastPage|The number of the last page|
|
||||
|nextPage|The number of the next page if it exists else equel to lastPage|
|
||||
|perPage|The number of results per page|
|
||||
|previousPage|The number of the previous page if it exists else 1|
|
||||
|total|The total number of results|
|
||||
|
||||
|
||||
### fastPaginate
|
||||
It run faster than `paginate()` because it doesn't use `offset`.
|
||||
|
||||
|sample URL|usage|result items|
|
||||
|---|---|---|
|
||||
|/users?items=5|`fastPaginate(5)`|1,2,3,4,5|
|
||||
|/users?items=5&since=6|`fastPaginateNext(5, 6)`|6,7,8,9,10|
|
||||
|/users?items=5&until=5|`fastPaginateBack(5, 5)`|1,2,3,4,5|
|
||||
|
||||
```nim
|
||||
proc fastPaginate(this:Rdb, display:int, key="id", order:Order=Asc): JsonNode
|
||||
```
|
||||
- display...Numer of items per page.
|
||||
- key...Name of a primary key column (option). default is `id`.
|
||||
- order...Asc or Desc (option). default is `Asc`.
|
||||
|
||||
```nim
|
||||
proc fastPaginateNext(this:Rdb, display:int, id:int, key="id", order:Order=Asc): JsonNode
|
||||
|
||||
proc fastPaginateBack(this:Rdb, display:int, id:int, key="id", order:Order=Asc): JsonNode
|
||||
```
|
||||
- display...Numer of items per page.
|
||||
- id...Value of primary key. It should be larger than 0.
|
||||
- key...Name of a primary key column (option). default is `id`.
|
||||
- order...Asc or Desc (option). default is `Asc`.
|
||||
|
||||
```nim
|
||||
var users = rdb.table("users").select("id", "name").fastPaginate(3).await
|
||||
|
||||
>> {
|
||||
"previousId":0,
|
||||
"hasPreviousId": false,
|
||||
"currentPage":[
|
||||
{"id":1,"name":"user1"},
|
||||
{"id":2,"name":"user2"},
|
||||
{"id":3,"name":"user3"},
|
||||
],
|
||||
"nextId":4,
|
||||
"hasNextId": true
|
||||
}
|
||||
```
|
||||
```nim
|
||||
users = rdb.table("users")
|
||||
.select("id", "name")
|
||||
.fastPaginateNext(3, users["nextId"].getInt)
|
||||
.await
|
||||
|
||||
>> {
|
||||
"previousId":4,
|
||||
"hasPreviousId": true,
|
||||
"currentPage":[
|
||||
{"id":5,"name":"user5"},
|
||||
{"id":6,"name":"user6"},
|
||||
{"id":7,"name":"user7"}
|
||||
],
|
||||
"nextId":8,
|
||||
"hasNextId": true
|
||||
}
|
||||
```
|
||||
```nim
|
||||
users = rdb.table("users")
|
||||
.select("id", "name")
|
||||
.fastPaginateBack(3, users["previousId"].getInt)
|
||||
.await
|
||||
|
||||
>> {
|
||||
"previousId":0,
|
||||
"hasPreviousId": false,
|
||||
"currentPage":[
|
||||
{"id":1,"name":"user1"},
|
||||
{"id":2,"name":"user2"},
|
||||
{"id":3,"name":"user3"},
|
||||
],
|
||||
"nextId":4,
|
||||
"hasNextId": true
|
||||
}
|
||||
```
|
||||
|
||||
order Desc
|
||||
```nim
|
||||
echo rdb.table("users")
|
||||
.select("id", "name")
|
||||
.fastPaginateNext(3, 5, order=Desc)
|
||||
.await
|
||||
|
||||
>> {
|
||||
"previousId":6,
|
||||
"hasPreviousId":true,
|
||||
"currentPage":[
|
||||
{"id":5,"name":"user5"},
|
||||
{"id":4,"name":"user4"},
|
||||
{"id":3,"name":"user3"}
|
||||
],
|
||||
"nextId":2,
|
||||
"hasNextId":true
|
||||
}
|
||||
```
|
||||
|
||||
paginate with `join` and `where`
|
||||
```nim
|
||||
echo rdb.table("users")
|
||||
.select("users.id", "users.name", "users.auth_id")
|
||||
.join("auth", "auth.id", "=", "users.auth_id")
|
||||
.where("auth.id", "=", 2)
|
||||
.fastPaginate(3, key="users.id")
|
||||
.await
|
||||
|
||||
>> {
|
||||
"previousId":0,
|
||||
"hasPreviousId":false,
|
||||
"currentPage":[
|
||||
{"id":4,"name":"user4","auth_id":2},
|
||||
{"id":6,"name":"user6","auth_id":2},
|
||||
{"id":8,"name":"user8","auth_id":2}
|
||||
],
|
||||
"nextId":8,
|
||||
"hasNextId":true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## INSERT
|
||||
[to index](#index)
|
||||
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
rdb.table("users").insert(%*{
|
||||
"name": "John",
|
||||
"email": "John@gmail.com"
|
||||
})
|
||||
.await
|
||||
|
||||
>> INSERT INTO users (name, email) VALUES ("John", "John@gmail.com")
|
||||
```
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
rdb.table("users").insert(
|
||||
@[
|
||||
%*{"name": "John", "email": "John@gmail.com", "address": "London"},
|
||||
%*{"name": "Paul", "email": "Paul@gmail.com", "address": "London"},
|
||||
%*{"name": "George", "email": "George@gmail.com", "address": "London"},
|
||||
]
|
||||
)
|
||||
.await
|
||||
|
||||
>> INSERT INTO users (name, email, address) VALUES ("John", "John@gmail.com", "London"), ("Paul", "Paul@gmail.com", "London"), ("George", "George@gmail.com", "London")
|
||||
```
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
echo rdb.table("users").insert(
|
||||
@[
|
||||
%*{"name": "John", "email": "John@gmail.com", "address": "London"},
|
||||
%*{"name": "Paul", "email": "Paul@gmail.com", "address": "London"},
|
||||
%*{"name": "George", "birth_date": "1943-02-25", "address": "London"},
|
||||
]
|
||||
)
|
||||
.await
|
||||
|
||||
>> INSERT INTO users (name, email, address) VALUES ("John", "John@gmail.com", "London")
|
||||
>> INSERT INTO users (name, email, address) VALUES ("Paul", "Paul@gmail.com", "London")
|
||||
>> INSERT INTO users (name, birth_date, address) VALUES ("George", "1960-1-1", "London")
|
||||
```
|
||||
|
||||
### Return ID Insert
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
echo rdb.table("users").insertId(%*{
|
||||
"name": "John",
|
||||
"email": "John@gmail.com"
|
||||
})
|
||||
.await
|
||||
|
||||
>> INSERT INTO users (name, email) VALUES ("John", "John@gmail.com")
|
||||
>> 1 # ID of new row is return
|
||||
```
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
echo rdb.table("users").insertId(
|
||||
@[
|
||||
%*{"name": "John", "email": "John@gmail.com", "address": "London"},
|
||||
%*{"name": "Paul", "email": "Paul@gmail.com", "address": "London"},
|
||||
%*{"name": "George", "email": "George@gmail.com", "address": "London"},
|
||||
]
|
||||
)
|
||||
.await
|
||||
|
||||
>> INSERT INTO users (name, email, address) VALUES ("John", "John@gmail.com", "London"), ("Paul", "Paul@gmail.com", "London"), ("George", "George@gmail.com", "London")
|
||||
>> @[1, 2] # Seq of ID of new row is return
|
||||
```
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
echo rdb.table("users").insertId(
|
||||
@[
|
||||
%*{"name": "John", "email": "John@gmail.com", "address": "London"},
|
||||
%*{"name": "Paul", "email": "Paul@gmail.com", "address": "London"},
|
||||
%*{"name": "George", "birth_date": "1943-02-25", "address": "London"},
|
||||
]
|
||||
)
|
||||
.await
|
||||
|
||||
>> INSERT INTO users (name, email, address) VALUES ("John", "John@gmail.com", "London")
|
||||
>> INSERT INTO users (name, email, address) VALUES ("Paul", "Paul@gmail.com", "London")
|
||||
>> INSERT INTO users (name, birth_date, address) VALUES ("George", "1960-1-1", "London")
|
||||
>> @[1, 2, 3] # Seq of ID of new row is return
|
||||
```
|
||||
|
||||
|
||||
## UPDATE
|
||||
[to index](#index)
|
||||
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
rdb
|
||||
.table("users")
|
||||
.where("id", "=", 100)
|
||||
.update(%*{"name": "Mick", "address": "NY"})
|
||||
.await
|
||||
|
||||
>> UPDATE users SET name = "Mick", address = "NY" WHERE id = 100
|
||||
```
|
||||
|
||||
## DELETE
|
||||
[to index](#index)
|
||||
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
rdb
|
||||
.table("users")
|
||||
.delete(1)
|
||||
.await
|
||||
|
||||
>> DELETE FROM users WHERE id = 1
|
||||
```
|
||||
|
||||
If column name of primary key is not exactory "id", you can specify it's name.
|
||||
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
rdb
|
||||
.table("users")
|
||||
.delete(1, key="user_id")
|
||||
.await
|
||||
|
||||
>> DELETE FROM users WHERE user_id = 1
|
||||
```
|
||||
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
rdb
|
||||
.table("users")
|
||||
.where("address", "=", "London")
|
||||
.delete()
|
||||
.await
|
||||
|
||||
>> DELETE FROM users WHERE address = "London"
|
||||
```
|
||||
|
||||
## Plain Response
|
||||
[to index](#INDEX)
|
||||
|
||||
`Plain` response doesn't have it's column name but it run faster than `JsonNode` response
|
||||
|
||||
```nim
|
||||
echo rdb.table("users").get().await
|
||||
>> @[
|
||||
%*{"id": 1, "name": "user1", "email": "user1@gmail.com"},
|
||||
%*{"id": 2, "name": "user2", "email": "user2@gmail.com"},
|
||||
%*{"id": 3, "name": "user3", "email": "user3@gmail.com"}
|
||||
]
|
||||
|
||||
echo rdb.table("users").getPlain().await
|
||||
>> @[
|
||||
@["1", "user1", "user1@gmail.com"],
|
||||
@["2", "user2", "user2@gmail.com"],
|
||||
@["3", "user3", "user3@gmail.com"],
|
||||
]
|
||||
```
|
||||
|
||||
```nim
|
||||
echo rdb.table("users").find(1).await
|
||||
>> %*{"id": 1, "name": "user1", "email": "user1@gmail.com"}
|
||||
|
||||
echo rdb.table("users").findPlain(1).await
|
||||
>> @["1", "user1", "user1@gmail.com"]
|
||||
```
|
||||
|
||||
```nim
|
||||
echo rdb.table("users").first().await
|
||||
>> %*{"id": 1, "name": "user1", "email": "user1@gmail.com"}
|
||||
|
||||
echo rdb.table("users").firstPlain().await
|
||||
>> @["1", "user1", "user1@gmail.com"]
|
||||
```
|
||||
|
||||
## Raw SQL
|
||||
[to index](#INDEX)
|
||||
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
let sql = """
|
||||
SELECT ProductName
|
||||
FROM Product
|
||||
WHERE Id IN (SELECT ProductId
|
||||
FROM OrderItem
|
||||
WHERE Quantity > 100)
|
||||
"""
|
||||
echo rdb.raw(sql).get().await
|
||||
echo rdb.raw(sql).getPlain().await
|
||||
echo rdb.raw(sql).first().await
|
||||
echo rdb.raw(sql).firstPlain().await
|
||||
```
|
||||
```nim
|
||||
let sql = "UPDATE users SET name = ? where id = ?"
|
||||
rdb.raw(sql, "John", "1").exec().await
|
||||
```
|
||||
|
||||
## Prepared Statement
|
||||
[to index](#index)
|
||||
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
let selectStmt = rdb.prepare("""SELECT "id", "name" FROM "users" WHERE "id" = ?""")
|
||||
let updateStmt = rdb.prepare("""UPDATE "users" SET "name" = ? WHERE "id" = ?""")
|
||||
|
||||
let row = await selectStmt.first(@["1"])
|
||||
await updateStmt.exec(@["John", "1"])
|
||||
|
||||
# close() is logical close.
|
||||
await selectStmt.close()
|
||||
await updateStmt.close()
|
||||
```
|
||||
|
||||
SurrealDB では server-side prepare ではなく、`LET` を使った client-side template reuse として同じ API を提供します。
|
||||
|
||||
In PostgreSQL, a context API can be used to ensure multiple operations use the same connection.
|
||||
|
||||
```nim
|
||||
await rdb.withConn(
|
||||
proc(ctx: PostgresPreparedContext): Future[void] {.async.} =
|
||||
discard await selectStmt.first(ctx, @["1"])
|
||||
await updateStmt.exec(ctx, @["John", "1"])
|
||||
)
|
||||
```
|
||||
|
||||
In PostgreSQL, APIs to physically clear the prepared statement cache are also available.
|
||||
|
||||
```nim
|
||||
await rdb.flushStmt("""SELECT "id", "name" FROM "users" WHERE "id" = ?""")
|
||||
await rdb.clearStmtCache()
|
||||
```
|
||||
|
||||
## Aggregates
|
||||
[to index](#index)
|
||||
|
||||
Except of `count`, these functions return `Option` type.
|
||||
|
||||
```nim
|
||||
import allographer/query_builder
|
||||
|
||||
echo rdb.table("users").count().await
|
||||
>> 10 # int
|
||||
|
||||
let response = await rdb.table("users").max("name").await
|
||||
if response.isSome:
|
||||
echo response.get
|
||||
>> "user9" # string
|
||||
|
||||
let response = await rdb.table("users").max("id").await
|
||||
if response.isSome:
|
||||
echo response.get
|
||||
>> "10" # string
|
||||
|
||||
let response = await rdb.table("users").min("name").await
|
||||
if response.isSome:
|
||||
echo response.get
|
||||
>> "user1" # string
|
||||
|
||||
let response = await rdb.table("users").min("id").await
|
||||
if response.isSome:
|
||||
echo response.get
|
||||
>> "1" # string
|
||||
|
||||
let response = await rdb.table("users").avg("id").await
|
||||
if response.isSome:
|
||||
echo response.get
|
||||
>> 5.5 # float
|
||||
|
||||
let response = await rdb.table("users").sum("id").await
|
||||
if response.isSome:
|
||||
echo response.get
|
||||
>> 55.0 # float
|
||||
```
|
||||
|
||||
## Transaction
|
||||
[to index](#index)
|
||||
|
||||
```nim
|
||||
transaction:
|
||||
var user = rdb.table("users").select("id").where("name", "=", "user3").first().await
|
||||
if user.isSome:
|
||||
var id = user.get["id"].getInt()
|
||||
echo id
|
||||
user = rdb.table("users").select("name", "email").find(id).await
|
||||
if user.isSome:
|
||||
echo user.get
|
||||
```
|
||||
If all code in transaction block success, `COMMIT` is run otherwise `ROLLBACK`
|
||||
Reference in New Issue
Block a user