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,27 @@
|
||||
import std/asyncdispatch
|
||||
import std/os
|
||||
import std/strutils
|
||||
import ../../src/allographer/connection
|
||||
|
||||
proc envStringDefault(key, defaultValue: string): string =
|
||||
let value = getEnv(key)
|
||||
if value.len == 0:
|
||||
return defaultValue
|
||||
return value
|
||||
|
||||
proc envIntDefault(key: string, defaultValue: int): int =
|
||||
let value = getEnv(key)
|
||||
if value.len == 0:
|
||||
return defaultValue
|
||||
return value.parseInt
|
||||
|
||||
let
|
||||
database = envStringDefault("DB_DATABASE", "test")
|
||||
user = envStringDefault("DB_USER", "user")
|
||||
password = envStringDefault("DB_PASSWORD", "pass")
|
||||
surrealHost = envStringDefault("SURREAL_HOST", "http://surreal")
|
||||
surrealPort = envIntDefault("SURREAL_PORT", 8000)
|
||||
maxConnections = envIntDefault("DB_MAX_CONNECTION", 5)
|
||||
timeout = envIntDefault("DB_TIMEOUT", 30)
|
||||
|
||||
let surreal* = dbOpen(SurrealDB, database, database, user, password, surrealHost, surrealPort, maxConnections, timeout, shouldDisplayLog=true).waitFor()
|
||||
@@ -0,0 +1,16 @@
|
||||
import std/asyncdispatch
|
||||
import std/os
|
||||
import std/strutils
|
||||
import ../../src/allographer/connection
|
||||
|
||||
proc envIntDefault(key: string, defaultValue: int): int =
|
||||
let value = getEnv(key)
|
||||
if value.len == 0:
|
||||
return defaultValue
|
||||
return value.parseInt
|
||||
|
||||
let
|
||||
maxConnections = envIntDefault("DB_MAX_CONNECTION", 5)
|
||||
timeout = envIntDefault("DB_TIMEOUT", 30)
|
||||
|
||||
let surreal* = dbOpen(SurrealDB, "test", "test", "user", "pass", "http://surreal", 8000, maxConnections, timeout, shouldDisplayLog=true).waitFor()
|
||||
@@ -0,0 +1,97 @@
|
||||
discard """
|
||||
cmd: "nim c -d:reset -d:ssl $file"
|
||||
"""
|
||||
# nim c -r -d:reset -d:ssl tests/surrealdb/test_autoincrements.nim
|
||||
|
||||
import std/unittest
|
||||
import std/asyncdispatch
|
||||
import std/json
|
||||
import std/options
|
||||
import std/strformat
|
||||
import ../../src/allographer/query_builder
|
||||
import ../../src/allographer/schema_builder
|
||||
import ./connections
|
||||
import ../clear_tables
|
||||
|
||||
|
||||
let rdb = surreal
|
||||
|
||||
suite("autoincrements"):
|
||||
test("sequence table is not exists"):
|
||||
rdb.raw("REMOVE TABLE _autoincrement_sequences").exec().waitFor()
|
||||
|
||||
var info = rdb.raw("INFO FOR DB").info().waitFor()
|
||||
echo "info: ", info.pretty
|
||||
check not info[0]["result"]["tables"].contains("_autoincrement_sequences")
|
||||
|
||||
rdb.create(
|
||||
table("user", [
|
||||
Column.increments("index"),
|
||||
Column.string("name")
|
||||
])
|
||||
)
|
||||
|
||||
info = rdb.raw("INFO FOR DB").info().waitFor()
|
||||
echo "info: ", info.pretty
|
||||
check info[0]["result"]["tables"].contains("_autoincrement_sequences")
|
||||
|
||||
let id = rdb.table("user").insertId(%*{"name": "alice"}).waitFor()
|
||||
var user = rdb.table("user").find(id).waitFor().get()
|
||||
check user["name"].getStr == "alice"
|
||||
check user["index"].getInt == 1
|
||||
|
||||
rdb.update(id, %*{"name": "updated"}).waitFor()
|
||||
|
||||
user = rdb.table("user").find(id).waitFor().get()
|
||||
check user["name"].getStr == "updated"
|
||||
check user["index"].getInt == 1
|
||||
|
||||
|
||||
test("sequence table is exists"):
|
||||
var info = rdb.raw("INFO FOR DB").info().waitFor()
|
||||
check info[0]["result"]["tables"].contains("_autoincrement_sequences")
|
||||
|
||||
rdb.create(
|
||||
table("user", [
|
||||
Column.increments("index"),
|
||||
Column.string("name")
|
||||
])
|
||||
)
|
||||
|
||||
let id = rdb.table("user").insertId(%*{"name": "alice"}).waitFor()
|
||||
var user = rdb.table("user").find(id).waitFor().get()
|
||||
check user["name"].getStr == "alice"
|
||||
check user["index"].getInt == 1
|
||||
|
||||
rdb.update(id, %*{"name": "updated"}).waitFor()
|
||||
|
||||
user = rdb.table("user").find(id).waitFor().get()
|
||||
check user["name"].getStr == "updated"
|
||||
check user["index"].getInt == 1
|
||||
|
||||
|
||||
test("cycle"):
|
||||
rdb.create(
|
||||
table("user", [
|
||||
Column.increments("index"),
|
||||
Column.string("name")
|
||||
])
|
||||
)
|
||||
|
||||
var users: seq[JsonNode]
|
||||
for i in 1..100:
|
||||
users.add(%*{"name": &"user{i}"})
|
||||
rdb.table("user").insert(users).waitFor()
|
||||
|
||||
var user = rdb.table("user").where("index", "=", 1).first().waitFor().get()
|
||||
check user["index"].getInt == 1
|
||||
|
||||
user = rdb.table("user").where("index", "=", 50).first().waitFor().get()
|
||||
check user["index"].getInt == 50
|
||||
|
||||
user = rdb.table("user").where("index", "=", 100).first().waitFor().get()
|
||||
check user["index"].getInt == 100
|
||||
|
||||
echo rdb.table("user").orderBy("index", Asc).limit(20).get().waitFor()
|
||||
|
||||
clearTables(rdb).waitFor()
|
||||
@@ -0,0 +1,106 @@
|
||||
discard """
|
||||
cmd: "nim c -d:reset $file"
|
||||
"""
|
||||
|
||||
# nim c -d:reset -r tests/surrealdb/test_create_schema.nim
|
||||
|
||||
import std/unittest
|
||||
import std/asyncdispatch
|
||||
import std/os
|
||||
import std/strutils
|
||||
import std/json
|
||||
import ../../src/allographer/schema_builder
|
||||
import ./connections
|
||||
import ../clear_tables
|
||||
|
||||
|
||||
let rdb = surreal
|
||||
let schemaFilePath = getCurrentDir() / "schema.nim"
|
||||
clearTables(rdb).waitFor()
|
||||
|
||||
suite "Schema output after migration":
|
||||
setup:
|
||||
# schema.nim ファイルの削除
|
||||
if fileExists(schemaFilePath):
|
||||
removeFile(schemaFilePath)
|
||||
|
||||
rdb.create(
|
||||
table("int_relation", [
|
||||
Column.increments("index")
|
||||
]),
|
||||
table("str_relation", [
|
||||
Column.uuid("uuid")
|
||||
]),
|
||||
table("test_schema_output", [
|
||||
Column.increments("index"),
|
||||
Column.integer("integer"),
|
||||
Column.smallInteger("smallInteger"),
|
||||
Column.mediumInteger("mediumInteger"),
|
||||
Column.bigInteger("bigInteger"),
|
||||
Column.decimal("decimal", 10, 3),
|
||||
Column.double("double", 10, 3),
|
||||
Column.float("float"),
|
||||
Column.uuid("uuid"),
|
||||
Column.char("char", 255),
|
||||
Column.string("string"),
|
||||
Column.text("text"),
|
||||
Column.mediumText("mediumText"),
|
||||
Column.longText("longText"),
|
||||
Column.date("date"),
|
||||
Column.datetime("datetime"),
|
||||
Column.timestamp("timestamp"),
|
||||
Column.timestamps(),
|
||||
Column.softDelete(),
|
||||
Column.binary("binary"),
|
||||
Column.boolean("boolean"),
|
||||
Column.enumField("enumField", ["A", "B", "C"]),
|
||||
Column.json("json"),
|
||||
Column.foreign("int_relation_id").reference("id").onTable("int_relation").onDelete(SET_NULL),
|
||||
Column.strForeign("str_relation_id").reference("uuid").onTable("str_relation").onDelete(SET_NULL)
|
||||
])
|
||||
)
|
||||
|
||||
|
||||
test "should generate schema.nim file":
|
||||
# スキーマ生成
|
||||
rdb.createSchema(schemaFilePath).waitFor()
|
||||
|
||||
# schema.nim ファイルの存在確認
|
||||
check fileExists(schemaFilePath)
|
||||
|
||||
# ファイル内容の検証
|
||||
let schemaContent = readFile(schemaFilePath)
|
||||
echo "schemaContent: ", schemaContent
|
||||
check schemaContent.contains("type TestSchemaOutputTable* = object")
|
||||
check schemaContent.contains("## test_schema_output")
|
||||
check schemaContent.contains("integer*: int")
|
||||
check schemaContent.contains("smallInteger*: int")
|
||||
check schemaContent.contains("mediumInteger*: int")
|
||||
check schemaContent.contains("bigInteger*: int")
|
||||
check schemaContent.contains("decimal*: float")
|
||||
check schemaContent.contains("double*: float")
|
||||
check schemaContent.contains("float*: float")
|
||||
check schemaContent.contains("uuid*: string")
|
||||
check schemaContent.contains("char*: string")
|
||||
check schemaContent.contains("string*: string")
|
||||
check schemaContent.contains("text*: string")
|
||||
check schemaContent.contains("mediumText*: string")
|
||||
check schemaContent.contains("longText*: string")
|
||||
check schemaContent.contains("date*: string")
|
||||
check schemaContent.contains("datetime*: string")
|
||||
check schemaContent.contains("timestamp*: string")
|
||||
check schemaContent.contains("created_at*: string")
|
||||
check schemaContent.contains("updated_at*: string")
|
||||
check schemaContent.contains("deleted_at*: string")
|
||||
check schemaContent.contains("binary*: string")
|
||||
check schemaContent.contains("boolean*: bool")
|
||||
check schemaContent.contains("enumField*: string")
|
||||
check schemaContent.contains("json*: JsonNode")
|
||||
check schemaContent.contains("int_relation_id*: string")
|
||||
check schemaContent.contains("str_relation_id*: string")
|
||||
|
||||
|
||||
clearTables(rdb).waitFor()
|
||||
# schema.nim ファイルの削除
|
||||
if fileExists(schemaFilePath):
|
||||
removeFile(schemaFilePath)
|
||||
@@ -0,0 +1,26 @@
|
||||
discard """
|
||||
cmd: "nim c -d:reset $file"
|
||||
"""
|
||||
|
||||
import std/json
|
||||
import std/unittest
|
||||
import ../../src/allographer/query_builder/libs/surreal/surreal_lib
|
||||
|
||||
|
||||
suite "SurrealDB lib helpers":
|
||||
test "numToAlphabet":
|
||||
check numToAlphabet(1) == "a"
|
||||
check numToAlphabet(26) == "z"
|
||||
check numToAlphabet(27) == "aa"
|
||||
|
||||
test "questionToDaller":
|
||||
check questionToDaller("SELECT * FROM user WHERE name = ? AND age = ?") ==
|
||||
"SELECT * FROM user WHERE name = $a AND age = $b"
|
||||
|
||||
test "dbFormat json args":
|
||||
let sql = dbFormat(
|
||||
"SELECT * FROM user WHERE name = ? AND age = ?",
|
||||
%*["alice", 42]
|
||||
)
|
||||
check sql ==
|
||||
"LET $a = \"alice\"; LET $b = 42; SELECT * FROM user WHERE name = $a AND age = $b"
|
||||
@@ -0,0 +1,25 @@
|
||||
discard """
|
||||
cmd: "nim c -d:reset $file"
|
||||
"""
|
||||
|
||||
import std/[asyncdispatch, os, strutils, unittest]
|
||||
import ../../src/allographer/connection
|
||||
import ../../src/allographer/query_builder
|
||||
|
||||
proc envIntDefault(key: string, defaultValue: int): int =
|
||||
let value = getEnv(key)
|
||||
if value.len == 0:
|
||||
return defaultValue
|
||||
return value.parseInt
|
||||
|
||||
|
||||
suite "SurrealDB pool waiter (notify)":
|
||||
test "pool size 1: two concurrent raw gets both complete":
|
||||
let timeout = envIntDefault("DB_TIMEOUT", 30)
|
||||
let rdb = dbOpen(SurrealDB, "test", "test", "user", "pass", "http://surreal", 8000,
|
||||
maxConnections = 1, timeout).waitFor()
|
||||
proc sel(): Future[void] {.async.} =
|
||||
discard await rdb.raw("RETURN 1;").get()
|
||||
let a = sel()
|
||||
let b = sel()
|
||||
waitFor all(a, b)
|
||||
@@ -0,0 +1,98 @@
|
||||
discard """
|
||||
cmd: "nim c -d:reset $file"
|
||||
"""
|
||||
|
||||
import std/asyncdispatch
|
||||
import std/deques
|
||||
import std/json
|
||||
import std/tables
|
||||
import std/unittest
|
||||
import ../../src/allographer/query_builder/libs/surreal/surreal_lib
|
||||
import ../../src/allographer/query_builder/libs/surreal/surreal_rdb
|
||||
import ../../src/allographer/query_builder/log
|
||||
import ../../src/allographer/query_builder/models/surreal/surreal_exec
|
||||
import ../../src/allographer/query_builder/models/surreal/surreal_types
|
||||
|
||||
|
||||
proc newTestSurreal(): SurrealConnections =
|
||||
let pools = Connections(
|
||||
conns: @[
|
||||
Connection(
|
||||
conn: SurrealConn(),
|
||||
isBusy: false,
|
||||
createdAt: 0,
|
||||
)
|
||||
],
|
||||
timeout: 30,
|
||||
waiters: initDeque[Future[void]](),
|
||||
preparedCache: initTable[string, SurrealPreparedEntry](),
|
||||
)
|
||||
result = SurrealConnections(
|
||||
log: LogSetting(
|
||||
shouldDisplayLog: false,
|
||||
shouldOutputLogFile: false,
|
||||
logDir: "",
|
||||
),
|
||||
pools: pools,
|
||||
)
|
||||
|
||||
|
||||
suite "SurrealDB prepared statement":
|
||||
test "dbFormatPrepared":
|
||||
var args = newJArray()
|
||||
args.add(%*"user:alice")
|
||||
args.add(%*"2026-04-03T00:00:00Z")
|
||||
args.add(newJNull())
|
||||
let sql = dbFormatPrepared(
|
||||
questionToDaller("""SELECT * FROM "user" WHERE "id" = ? AND "submit_at" >= ? AND "address" IS ?"""),
|
||||
args
|
||||
)
|
||||
check sql ==
|
||||
"""LET $a = user:alice; LET $b = <datetime>"2026-04-03T00:00:00Z"; LET $c = NONE; SELECT * FROM "user" WHERE "id" = $a AND "submit_at" >= $b AND "address" IS $c"""
|
||||
|
||||
|
||||
test "prepare cache lifecycle":
|
||||
let rdb = newTestSurreal()
|
||||
let sql = """SELECT * FROM "user" WHERE "id" = ?"""
|
||||
|
||||
let stmt1 = rdb.prepare(sql)
|
||||
check stmt1.nArgs == 1
|
||||
check stmt1.entry.normalizedSql == questionToDaller(sql)
|
||||
check stmt1.entry.refCount == 1
|
||||
check rdb.pools.preparedCache.len == 1
|
||||
|
||||
let stmt2 = rdb.prepare(sql)
|
||||
check stmt2.entry == stmt1.entry
|
||||
check stmt1.entry.refCount == 2
|
||||
|
||||
waitFor stmt1.close()
|
||||
check stmt1.isClosed
|
||||
check stmt1.entry.refCount == 1
|
||||
|
||||
waitFor stmt2.close()
|
||||
check stmt2.isClosed
|
||||
check stmt2.entry.refCount == 0
|
||||
|
||||
let stmt4 = rdb.prepare(sql)
|
||||
check rdb.pools.preparedCache.len == 1
|
||||
waitFor rdb.flushStmt(stmt4)
|
||||
check stmt4.isClosed
|
||||
check rdb.pools.preparedCache.len == 0
|
||||
|
||||
let stmt3 = rdb.prepare(sql)
|
||||
check stmt3.entry != stmt1.entry
|
||||
waitFor stmt3.close()
|
||||
waitFor rdb.clearStmtCache()
|
||||
check rdb.pools.preparedCache.len == 0
|
||||
|
||||
|
||||
test "withConn context":
|
||||
let rdb = newTestSurreal()
|
||||
|
||||
waitFor rdb.withConn(
|
||||
proc(ctx: SurrealPreparedContext): Future[void] {.async.} =
|
||||
check ctx.owner == rdb
|
||||
check ctx.connI == 0
|
||||
)
|
||||
|
||||
check not rdb.pools.conns[0].isBusy
|
||||
@@ -0,0 +1,507 @@
|
||||
discard """
|
||||
cmd: "nim c -d:reset $file"
|
||||
"""
|
||||
|
||||
# nim c -r -d:reset tests/surrealdb/test_query.nim
|
||||
|
||||
import std/unittest
|
||||
import std/asyncdispatch
|
||||
import std/json
|
||||
import std/options
|
||||
import std/strformat
|
||||
import std/times
|
||||
import ../../src/allographer/schema_builder
|
||||
import ../../src/allographer/query_builder
|
||||
import ./connections
|
||||
import ../clear_tables
|
||||
|
||||
|
||||
let rdb = surreal
|
||||
|
||||
|
||||
proc isNullish(node: JsonNode, key: string): bool =
|
||||
not node.hasKey(key) or node[key].kind == JNull
|
||||
|
||||
# =============================================================================
|
||||
# test
|
||||
# =============================================================================
|
||||
|
||||
proc setup(rdb:SurrealConnections) =
|
||||
# rdb.raw("REMOVE TABLE auth; REMOVE TABLE user").exec().waitFor
|
||||
# rdb.raw("DEFINE TABLE auth SCHEMAFULL; DEFINE TABLE user SCHEMAFULL").exec().waitFor
|
||||
rdb.create([
|
||||
table("auth",[
|
||||
Column.increments("index"),
|
||||
Column.string("name")
|
||||
]),
|
||||
table("user",[
|
||||
Column.increments("index"),
|
||||
Column.string("name").nullable(),
|
||||
Column.string("email").nullable(),
|
||||
Column.string("address").nullable(),
|
||||
Column.date("submit_on"),
|
||||
Column.datetime("submit_at"),
|
||||
Column.foreign("auth").reference("id").onTable("auth").onDelete(SET_NULL).nullable()
|
||||
])
|
||||
])
|
||||
|
||||
# seeder
|
||||
var adminId, userId:SurrealId
|
||||
seeder(rdb, "auth"):
|
||||
adminId = rdb.table("auth").insertId(%*{"name":"admin"}).waitFor()
|
||||
userId = rdb.table("auth").insertId(%*{"name":"user"}).waitFor()
|
||||
|
||||
seeder(rdb, "user"):
|
||||
var users: seq[JsonNode]
|
||||
for i in 1..10:
|
||||
let authId = if i mod 2 == 0: userId else: adminId
|
||||
let month = if i > 9: $i else: &"0{i}"
|
||||
users.add(
|
||||
%*{
|
||||
"name": &"user{i}",
|
||||
"email": &"user{i}@example.com",
|
||||
"address": newJNull(),
|
||||
"auth": authId,
|
||||
"submit_on": &"2020-{month}-01",
|
||||
"submit_at": &"2020-{month}-01T00:00:00Z",
|
||||
}
|
||||
)
|
||||
|
||||
rdb.table("user").insert(users).waitFor()
|
||||
|
||||
|
||||
setup(rdb)
|
||||
|
||||
suite($rdb & " get"):
|
||||
test("get"):
|
||||
let t = rdb.table("user").orderBy("name", Asc).get().waitFor()
|
||||
echo t[0]
|
||||
check t[0]["index"].getInt() == 1
|
||||
check t[0]["name"].getStr() == "user1"
|
||||
check t[0]["email"].getStr() == "user1@example.com"
|
||||
check isNullish(t[0], "address")
|
||||
check t[0]["submit_on"].getStr().parse("yyyy-MM-dd'T'hh:mm:ss'Z'").format("yyyy-MM-dd") == "2020-01-01"
|
||||
check t[0]["submit_at"].getStr() == "2020-01-01T00:00:00Z"
|
||||
|
||||
|
||||
test("first"):
|
||||
var t = rdb.table("user").where("name", "=", "user1").orderBy("name", Asc).first().waitFor().get()
|
||||
check t["index"].getInt() == 1
|
||||
check t["name"].getStr() == "user1"
|
||||
check t["email"].getStr() == "user1@example.com"
|
||||
check isNullish(t, "address")
|
||||
check t["submit_on"].getStr().parse("yyyy-MM-dd'T'hh:mm:ss'Z'").format("yyyy-MM-dd") == "2020-01-01"
|
||||
check t["submit_at"].getStr() == "2020-01-01T00:00:00Z"
|
||||
|
||||
|
||||
test("find"):
|
||||
var t = rdb.table("user").where("name", "=", "user1").orderBy("name", Asc).first().waitFor().get()
|
||||
let id = SurrealId.new(t["id"].getStr())
|
||||
t = rdb.table("user").find(id).waitFor().get()
|
||||
check t["index"].getInt() == 1
|
||||
check t["name"].getStr() == "user1"
|
||||
check t["email"].getStr() == "user1@example.com"
|
||||
check isNullish(t, "address")
|
||||
check t["submit_on"].getStr().parse("yyyy-MM-dd'T'hh:mm:ss'Z'").format("yyyy-MM-dd") == "2020-01-01"
|
||||
check t["submit_at"].getStr() == "2020-01-01T00:00:00Z"
|
||||
|
||||
|
||||
test("columns"):
|
||||
let columns = rdb.table("user").columns().waitFor()
|
||||
check columns == @["address", "auth", "email", "index", "name", "submit_at", "submit_on"]
|
||||
|
||||
|
||||
test("select"):
|
||||
var t = rdb.select("name", "email").table("user").orderBy("name", Asc).get().waitFor()
|
||||
check t[0] == %*{"name": "user1", "email": "user1@example.com"}
|
||||
|
||||
|
||||
test("selectAs"):
|
||||
var t = rdb.select("name as user_name", "email").table("user").orderBy("user_name", Asc).get().waitFor()
|
||||
check t[0] == %*{"user_name": "user1", "email": "user1@example.com"}
|
||||
|
||||
|
||||
test("selectLike"):
|
||||
let users = rdb.table("user").where("email", "CONTAINS", "10").get().waitFor()
|
||||
check users[0]["email"].getStr() == "user10@example.com"
|
||||
|
||||
var user10 = rdb.raw("SELECT * FROM user WHERE string::starts_with(email, \"user10\")").first().waitFor()
|
||||
check user10.get()["email"].getStr() == "user10@example.com"
|
||||
|
||||
user10 = rdb.raw("SELECT * FROM user WHERE string::ends_with(email, \"10@example.com\")").first().waitFor()
|
||||
check user10.get()["email"].getStr() == "user10@example.com"
|
||||
|
||||
|
||||
test("select count"):
|
||||
var t = rdb.raw("SELECT count(`id`) as `count` FROM `user` GROUP ALL").get().waitFor
|
||||
echo t
|
||||
check t[0]["count"].getInt() == 10
|
||||
|
||||
|
||||
test("select max"):
|
||||
var t = rdb.raw("SELECT math::max(`index`) as `max` FROM `user` GROUP ALL").get().waitFor
|
||||
echo t
|
||||
check t[0]["max"].getInt() == 10
|
||||
|
||||
|
||||
test("where string"):
|
||||
let admin = rdb.table("auth").where("name", "=", "admin").first().waitFor().get()
|
||||
let adminId = SurrealId.new(admin["id"].getStr())
|
||||
|
||||
var t = rdb.table("user").where("auth", "=", adminId.rawId()).orderBy("index", Asc).get().waitFor()
|
||||
const ids = [1,3,5,7,9]
|
||||
for i, row in t:
|
||||
check row["index"].getInt() == ids[i]
|
||||
check row["name"].getStr() == &"user{ids[i]}"
|
||||
check row["email"].getStr() == &"user{ids[i]}@example.com"
|
||||
check isNullish(row, "address")
|
||||
check row["submit_on"].getStr() == &"2020-0{ids[i]}-01T00:00:00Z"
|
||||
|
||||
|
||||
test("where int"):
|
||||
var t = rdb.table("user").where("index", "=", 1).orderBy("index", Asc).first().waitFor().get()
|
||||
check t["index"].getInt() == 1
|
||||
check t["name"].getStr() == "user1"
|
||||
check t["email"].getStr() == "user1@example.com"
|
||||
check isNullish(t, "address")
|
||||
check t["submit_on"].getStr() == "2020-01-01T00:00:00Z"
|
||||
|
||||
|
||||
test("orWhere"):
|
||||
let admin = rdb.table("auth").where("name", "=", "admin").first().waitFor().get()
|
||||
let adminId = SurrealId.new(admin["id"].getStr())
|
||||
|
||||
var t = rdb.table("user").where("auth", "=", adminId).orWhere("name", "=", "user2").orderBy("index", Asc).get().waitFor()
|
||||
const ids = [1,2,3,5,7,9]
|
||||
for i, row in t:
|
||||
check row["index"].getInt() == ids[i]
|
||||
check row["name"].getStr() == &"user{ids[i]}"
|
||||
check row["email"].getStr() == &"user{ids[i]}@example.com"
|
||||
check isNullish(row, "address")
|
||||
check row["submit_on"].getStr() == &"2020-0{ids[i]}-01T00:00:00Z"
|
||||
|
||||
|
||||
test("whereBetween"):
|
||||
var t = rdb
|
||||
.table("user")
|
||||
.whereBetween("index", [6, 9])
|
||||
.orderBy("index", Asc)
|
||||
.get()
|
||||
.waitFor()
|
||||
|
||||
const ids = [6,7,8,9]
|
||||
for i, row in t:
|
||||
check row["index"].getInt() == ids[i]
|
||||
check row["name"].getStr() == &"user{ids[i]}"
|
||||
check row["email"].getStr() == &"user{ids[i]}@example.com"
|
||||
check isNullish(row, "address")
|
||||
check row["submit_on"].getStr() == &"2020-0{ids[i]}-01T00:00:00Z"
|
||||
|
||||
|
||||
test("whereNotBetween"):
|
||||
var t = rdb
|
||||
.table("user")
|
||||
.whereNotBetween("index", [2, 6])
|
||||
.orderBy("index", Asc)
|
||||
.get()
|
||||
.waitFor()
|
||||
|
||||
const ids = [1,7,8,9]
|
||||
for i, row in t:
|
||||
check row["index"].getInt() == ids[i]
|
||||
check row["name"].getStr() == &"user{ids[i]}"
|
||||
check row["email"].getStr() == &"user{ids[i]}@example.com"
|
||||
check isNullish(row, "address")
|
||||
check row["submit_on"].getStr() == &"2020-0{ids[i]}-01T00:00:00Z"
|
||||
|
||||
|
||||
test("whereIn"):
|
||||
var t = rdb
|
||||
.table("user")
|
||||
.whereIn("index", @[5, 6, 7])
|
||||
.orderBy("index", Asc)
|
||||
.get()
|
||||
.waitFor()
|
||||
|
||||
const ids = [5,6,7]
|
||||
for i, row in t:
|
||||
check row["index"].getInt() == ids[i]
|
||||
check row["name"].getStr() == &"user{ids[i]}"
|
||||
check row["email"].getStr() == &"user{ids[i]}@example.com"
|
||||
check isNullish(row, "address")
|
||||
check row["submit_on"].getStr() == &"2020-0{ids[i]}-01T00:00:00Z"
|
||||
|
||||
|
||||
test("whereNotIn"):
|
||||
var t = rdb
|
||||
.table("user")
|
||||
.whereNotIn("index", @[4, 5, 6, 7, 10])
|
||||
.orderBy("index", Asc)
|
||||
.get()
|
||||
.waitFor()
|
||||
|
||||
const ids = [1,2,3,8,9]
|
||||
for i, row in t:
|
||||
check row["index"].getInt() == ids[i]
|
||||
check row["name"].getStr() == &"user{ids[i]}"
|
||||
check row["email"].getStr() == &"user{ids[i]}@example.com"
|
||||
check isNullish(row, "address")
|
||||
check row["submit_on"].getStr() == &"2020-0{ids[i]}-01T00:00:00Z"
|
||||
|
||||
|
||||
test("whereNull"):
|
||||
rdb.table("user").insert(%*{"email": "user11@example.com"}).waitFor()
|
||||
var t = rdb
|
||||
.table("user")
|
||||
.whereNull("name")
|
||||
.first()
|
||||
.waitFor()
|
||||
check t.get()["index"].getInt() == 11
|
||||
|
||||
setUp(rdb)
|
||||
|
||||
# TODO: not work
|
||||
# test("groupBy"):
|
||||
# var t = rdb
|
||||
# .select("auth.name")
|
||||
# .table("user")
|
||||
# .groupBy("auth.name")
|
||||
# .get()
|
||||
# .waitFor()
|
||||
# echo t
|
||||
|
||||
|
||||
test("orderBy"):
|
||||
var res = rdb.table("user").orderBy("name", Asc).get().waitFor()
|
||||
check res[0]["name"].getStr() == "user1"
|
||||
|
||||
res = rdb.table("user").orderBy("name", Desc).get().waitFor()
|
||||
check res[0]["name"].getStr() == "user9"
|
||||
|
||||
res = rdb.table("user").orderBy("index", Numeric, Asc).get().waitFor()
|
||||
check res[0]["name"].getStr() == "user1"
|
||||
|
||||
res = rdb.table("user").orderBy("index", Numeric, Desc).get().waitFor()
|
||||
check res[0]["name"].getStr() == "user10"
|
||||
|
||||
res = rdb.table("user").orderBy("name", Collate, Asc).get().waitFor()
|
||||
check res[0]["name"].getStr() == "user1"
|
||||
|
||||
res = rdb.table("user").orderBy("name", Collate, Desc).get().waitFor()
|
||||
check res[0]["name"].getStr() == "user9"
|
||||
|
||||
|
||||
test("fetch"):
|
||||
var res = rdb.table("user").where("name", "=", "user1").fetch("auth").first().waitFor().get()
|
||||
check res["auth"]["name"].getStr() == "admin"
|
||||
res = rdb.table("user").where("name", "=", "user2").fetch("auth").first().waitFor().get()
|
||||
check res["auth"]["name"].getStr() == "user"
|
||||
|
||||
|
||||
test("resultIsNull"):
|
||||
check rdb.table("user").where("id", "=", 50).first().waitFor().isSome() == false
|
||||
check newSeq[JsonNode](0) == rdb.table("user").where("id", "=", 50).get().waitFor()
|
||||
|
||||
|
||||
suite($rdb & " insert"):
|
||||
setup:
|
||||
setup(rdb)
|
||||
|
||||
test("insert row"):
|
||||
rdb.table("user").insert(%*{"name": "Alice"}).waitFor
|
||||
let t = rdb.table("user").orderBy("index", Desc).first().waitFor().get()
|
||||
check t["name"].getStr() == "Alice"
|
||||
|
||||
|
||||
test("insert rows"):
|
||||
rdb.table("user").insert(@[
|
||||
%*{"name": "Alice"},
|
||||
%*{"name": "Bob"},
|
||||
]).waitFor
|
||||
let t = rdb.table("user").orderBy("index", Desc).limit(2).get().waitFor()
|
||||
check t[0]["name"].getStr() == "Bob"
|
||||
check t[1]["name"].getStr() == "Alice"
|
||||
|
||||
|
||||
test("insertId row"):
|
||||
var id = rdb.table("user").insertId(%*{"name": "Alice"}).waitFor
|
||||
echo id
|
||||
let t = rdb.table("user").find(id).waitFor().get()
|
||||
check t["name"].getStr() == "Alice"
|
||||
|
||||
|
||||
test("insertId rows"):
|
||||
let ids = rdb.table("user").insertId(@[
|
||||
%*{"name": "Alice"},
|
||||
%*{"name": "Bob"},
|
||||
]).waitFor
|
||||
|
||||
var t = rdb.table("user").find(ids[0]).waitFor().get()
|
||||
check t["name"].getStr == "Alice"
|
||||
|
||||
t = rdb.table("user").find(ids[1]).waitFor().get()
|
||||
check t["name"].getStr == "Bob"
|
||||
|
||||
|
||||
test("insertNil"):
|
||||
var id = rdb.table("user").insertId(%*{
|
||||
"name": "Alice",
|
||||
"email": nil,
|
||||
"address": ""
|
||||
})
|
||||
.waitFor
|
||||
|
||||
var res = rdb.table("user").find(id).waitFor
|
||||
check not res.get.hasKey("email") or res.get["email"].kind == JNull
|
||||
|
||||
res = rdb.table("user").whereNull("email").first().waitFor
|
||||
check not res.get.hasKey("email") or res.get["email"].kind == JNull
|
||||
|
||||
|
||||
|
||||
test("insert [T]"):
|
||||
type User = object
|
||||
name:string
|
||||
|
||||
let user = User(name: "Alice")
|
||||
rdb.table("user").insert(user).waitFor
|
||||
|
||||
let row = rdb.table("user").orderBy("index", Desc).first().waitFor().get()
|
||||
check row["name"].getStr() == "Alice"
|
||||
|
||||
|
||||
test("insert seq[T]"):
|
||||
type User = object
|
||||
name:string
|
||||
|
||||
let users = @[
|
||||
User(name: "Alice"),
|
||||
User(name: "Bob"),
|
||||
]
|
||||
|
||||
rdb.table("user").insert(users).waitFor
|
||||
let t = rdb.table("user").orderBy("index", Desc).limit(2).get().waitFor()
|
||||
check t[0]["name"].getStr() == "Bob"
|
||||
check t[1]["name"].getStr() == "Alice"
|
||||
|
||||
|
||||
suite($rdb & " update"):
|
||||
setup:
|
||||
setup(rdb)
|
||||
|
||||
|
||||
test("update"):
|
||||
var user1 = rdb.table("user").where("index", "=", 1).first().waitFor().get()
|
||||
let user1Id = SurrealId.new(user1["id"].getStr())
|
||||
rdb.table("user").where("id", "=", user1Id).update(%*{"name": "Alice"}).waitFor()
|
||||
user1 = rdb.table("user").find(user1Id).waitFor().get()
|
||||
check user1["name"].getStr() == "Alice"
|
||||
|
||||
|
||||
test("update [T]"):
|
||||
type User = object
|
||||
name:string
|
||||
|
||||
let user = User(name: "Alice")
|
||||
var user1 = rdb.table("user").where("index", "=", 1).first().waitFor().get()
|
||||
let user1Id = SurrealId.new(user1["id"].getStr())
|
||||
rdb.table("user").where("id", "=", user1Id).update(user).waitFor()
|
||||
user1 = rdb.table("user").find(user1Id).waitFor().get()
|
||||
check user1["name"].getStr() == "Alice"
|
||||
|
||||
|
||||
test("update merge"):
|
||||
var user1 = rdb.table("user").where("index", "=", 1).first().waitFor().get()
|
||||
let user1Id = SurrealId.new(user1["id"].getStr())
|
||||
rdb.update(user1Id, %*{"name": "updated"}).waitFor()
|
||||
user1 = rdb.table("user").find(user1Id).waitFor().get()
|
||||
check user1["name"].getStr() == "updated"
|
||||
|
||||
|
||||
test("update merge [T]"):
|
||||
type User = object
|
||||
name:string
|
||||
|
||||
|
||||
var user1 = rdb.table("user").where("index", "=", 1).first().waitFor().get()
|
||||
let user1Id = SurrealId.new(user1["id"].getStr())
|
||||
let user = User(name: "updated")
|
||||
rdb.update(user1Id, user).waitFor()
|
||||
user1 = rdb.table("user").find(user1Id).waitFor().get()
|
||||
check user1["name"].getStr() == "updated"
|
||||
|
||||
|
||||
suite($rdb & " delete"):
|
||||
setup:
|
||||
setup(rdb)
|
||||
|
||||
test("delete"):
|
||||
let user1 = rdb.table("user").where("index", "=", 1).first().waitFor().get()
|
||||
let user1Id = SurrealId.new(user1["id"].getStr())
|
||||
rdb.table("user").where("name", "=", "user1").delete().waitFor()
|
||||
check rdb.table("user").find(user1Id).waitFor().isSome() == false
|
||||
|
||||
test("delete id"):
|
||||
let user1 = rdb.table("user").where("index", "=", 1).first().waitFor().get()
|
||||
let user1Id = SurrealId.new(user1["id"].getStr())
|
||||
rdb.table("user").delete(user1Id).waitFor
|
||||
check rdb.table("user").find(user1Id).waitFor().isSome() == false
|
||||
|
||||
|
||||
suite($rdb & " rawQuery"):
|
||||
setup(rdb)
|
||||
|
||||
test("get"):
|
||||
let sql = &"SELECT * FROM `user` WHERE `index` = ?"
|
||||
let res = rdb.raw(sql, %*[1]).get().waitFor
|
||||
check res[0]["name"].getStr == "user1"
|
||||
|
||||
|
||||
test("first"):
|
||||
let sql = &"SELECT * FROM `user` WHERE `index` = ?"
|
||||
let res = rdb.raw(sql, %*[1]).first().waitFor().get()
|
||||
check res["name"].getStr() == "user1"
|
||||
|
||||
|
||||
test("exec"):
|
||||
var sql = "SELECT * FROM `user` WHERE `index` = ?"
|
||||
let user1 = rdb.raw(sql, %*[1]).first().waitFor().get()
|
||||
let user1Id = SurrealId.new(user1["id"].getStr())
|
||||
|
||||
sql = "UPDATE `user` SET `name` = ? WHERE `id` = ?"
|
||||
rdb.raw(sql, %*["updated", user1Id.rawId()]).exec().waitFor
|
||||
|
||||
sql = &"SELECT * FROM `user` WHERE `id` = ?"
|
||||
let res = rdb.raw(sql, %[user1Id.rawId()]).get().waitFor
|
||||
check res[0]["name"].getStr == "updated"
|
||||
|
||||
|
||||
setup(rdb)
|
||||
suite($rdb & " aggregates"):
|
||||
|
||||
test("count"):
|
||||
var t = rdb.table("user").count().waitFor()
|
||||
check t == 10
|
||||
|
||||
test("max"):
|
||||
var t = rdb.table("user").max("name", Collate).waitFor()
|
||||
check t == "user9"
|
||||
var t2 = rdb.table("user").max("index", Numeric).waitFor()
|
||||
check t2 == "10"
|
||||
|
||||
test("min"):
|
||||
var t = rdb.table("user").min("name", Collate).waitFor()
|
||||
check t == "user1"
|
||||
var t2 = rdb.table("user").min("index", Numeric).waitFor()
|
||||
check t2 == "1"
|
||||
|
||||
test("avg"):
|
||||
let t = rdb.table("user").avg("index").waitFor()
|
||||
check t == 5.5
|
||||
|
||||
test("sum"):
|
||||
let t = rdb.table("user").sum("index").waitFor()
|
||||
check t == 55.0
|
||||
|
||||
|
||||
clearTables(rdb).waitFor()
|
||||
@@ -0,0 +1,532 @@
|
||||
discard """
|
||||
cmd: "nim c -d:reset $file"
|
||||
"""
|
||||
|
||||
# nim c -r -d:reset -d:ssl tests/surrealdb/test_schema.nim
|
||||
|
||||
import std/unittest
|
||||
import std/asyncdispatch
|
||||
import std/json
|
||||
import std/options
|
||||
import std/strutils
|
||||
import ../../src/allographer/schema_builder
|
||||
import ../../src/allographer/query_builder
|
||||
import ./connections
|
||||
import ../clear_tables
|
||||
|
||||
|
||||
proc normalizeInfoDef(s: string): string =
|
||||
result = s
|
||||
result = result.replace("`rand`::uuid()", "rand::uuid()")
|
||||
result = result.replace("1.1f", "1.1")
|
||||
result = result.replace("| none", "| NONE")
|
||||
result = result.replace("| None", "| NONE")
|
||||
result = result.replace(" PERMISSIONS FULL", "")
|
||||
result = result.replace("{ key: 'value' }", "{\"key\":\"value\"}")
|
||||
result = result.replace("{ key: \"value\" }", "{\"key\":\"value\"}")
|
||||
result = result.replace("{key:'value'}", "{\"key\":\"value\"}")
|
||||
|
||||
|
||||
proc `==`(a, b: string): bool =
|
||||
cmp(normalizeInfoDef(a), normalizeInfoDef(b)) == 0
|
||||
|
||||
|
||||
proc expectInfoEntries(node: JsonNode, entries: seq[tuple[key: string, expected: string]]) =
|
||||
for entry in entries:
|
||||
check normalizeInfoDef(node[entry.key].getStr()) == normalizeInfoDef(entry.expected)
|
||||
|
||||
|
||||
suite("SurrealDB create table"):
|
||||
test("create table"):
|
||||
surreal.create(
|
||||
table("relation", [
|
||||
Column.uuid("uuid").unique(),
|
||||
]),
|
||||
table("TypeIndex", [
|
||||
Column.increments("index"),
|
||||
Column.integer("integer").unsigned().index().default(1),
|
||||
Column.smallInteger("smallInteger").unsigned().index().default(1),
|
||||
Column.mediumInteger("mediumInteger").unsigned().index().default(1),
|
||||
Column.bigInteger("bigInteger").unsigned().index().default(1),
|
||||
Column.decimal("decimal", 10, 3).unsigned().index().default(1.1),
|
||||
Column.double("double", 10, 3).unsigned().index().default(1.1),
|
||||
Column.float("float").unsigned().index().default(1.1),
|
||||
Column.uuid("uuid").index().default("A"),
|
||||
Column.char("char", 256).index().default("A"),
|
||||
Column.string("string").index().default("A"),
|
||||
Column.text("text").index().default("A"),
|
||||
Column.mediumText("mediumText").index().default("A"),
|
||||
Column.longText("longText").index().default("A"),
|
||||
Column.date("date").index().default(),
|
||||
Column.datetime("datetime1").index().default(Current),
|
||||
Column.datetime("datetime2").index().default(CurrentOnUpdate),
|
||||
Column.timestamp("timestamp").index().default(),
|
||||
Column.timestamps(),
|
||||
Column.softDelete(),
|
||||
Column.binary("binary").index().default("A"),
|
||||
Column.boolean("boolean").index().default(true),
|
||||
Column.enumField("enumField", ["A", "B", "C"]).index().default("A"),
|
||||
Column.json("json").index().default(%*{"key":"value"}),
|
||||
Column.foreign("relation").reference("id").onTable("relation").onDelete(SET_NULL).nullable(),
|
||||
]),
|
||||
table("TypeUnique", [
|
||||
Column.increments("index"),
|
||||
Column.integer("integer").unsigned().index().unique().unique().default(1),
|
||||
Column.smallInteger("smallInteger").unsigned().index().unique().unique().default(1),
|
||||
Column.mediumInteger("mediumInteger").unsigned().index().unique().unique().default(1),
|
||||
Column.bigInteger("bigInteger").unsigned().index().unique().unique().default(1),
|
||||
Column.decimal("decimal", 10, 3).unsigned().index().unique().unique().default(1.1),
|
||||
Column.double("double", 10, 3).unsigned().index().unique().unique().default(1.1),
|
||||
Column.float("float").unsigned().index().unique().unique().default(1.1),
|
||||
Column.uuid("uuid").index().unique().unique().default("A"),
|
||||
Column.char("char", 256).index().unique().unique().default("A"),
|
||||
Column.string("string").index().unique().unique().default("A"),
|
||||
Column.text("text").index().unique().unique().default("A"),
|
||||
Column.mediumText("mediumText").index().unique().unique().default("A"),
|
||||
Column.longText("longText").index().unique().unique().default("A"),
|
||||
Column.date("date").index().unique().unique().default(),
|
||||
Column.datetime("datetime1").index().unique().unique().default(Current),
|
||||
Column.datetime("datetime2").index().unique().unique().default(CurrentOnUpdate),
|
||||
Column.timestamp("timestamp").index().unique().unique().default(),
|
||||
Column.timestamps(),
|
||||
Column.softDelete(),
|
||||
Column.binary("binary").index().unique().unique().default("A"),
|
||||
Column.boolean("boolean").index().unique().unique().default(true),
|
||||
Column.enumField("enumField", ["A", "B", "C"]).index().unique().default("A"),
|
||||
Column.json("json").index().unique().default(%*{"key":"value"}),
|
||||
Column.foreign("relation").reference("id").onTable("relation").onDelete(SET_NULL).nullable(),
|
||||
]),
|
||||
)
|
||||
|
||||
block:
|
||||
let info = surreal.raw(""" INFO FOR TABLE TypeIndex """).info().waitFor()
|
||||
let resultNode = info[0]["result"]
|
||||
let fields = resultNode["fields"]
|
||||
expectInfoEntries(fields, @[
|
||||
("index", "DEFINE FIELD index ON TypeIndex TYPE int VALUE $value OR (SELECT max_index FROM _autoincrement_sequences WHERE `table` = 'TypeIndex' AND column = 'index' LIMIT 1)[0].max_index + 1"),
|
||||
("integer", "DEFINE FIELD integer ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("smallInteger", "DEFINE FIELD smallInteger ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("mediumInteger", "DEFINE FIELD mediumInteger ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("bigInteger", "DEFINE FIELD bigInteger ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("decimal", "DEFINE FIELD decimal ON TypeIndex TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("double", "DEFINE FIELD double ON TypeIndex TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("float", "DEFINE FIELD float ON TypeIndex TYPE float VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("uuid", "DEFINE FIELD uuid ON TypeIndex TYPE string VALUE $value OR rand::uuid() ASSERT $value != NONE"),
|
||||
("char", "DEFINE FIELD char ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"),
|
||||
("string", "DEFINE FIELD string ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"),
|
||||
("text", "DEFINE FIELD text ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("mediumText", "DEFINE FIELD mediumText ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("longText", "DEFINE FIELD longText ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("date", "DEFINE FIELD date ON TypeIndex TYPE datetime VALUE $value OR <datetime> '1970-01-01T00:00:00Z' ASSERT $value != NONE"),
|
||||
("datetime1", "DEFINE FIELD datetime1 ON TypeIndex TYPE datetime VALUE $value OR time::now() ASSERT $value != NONE"),
|
||||
("datetime2", "DEFINE FIELD datetime2 ON TypeIndex TYPE datetime VALUE time::now() ASSERT $value != NONE"),
|
||||
("timestamp", "DEFINE FIELD timestamp ON TypeIndex TYPE datetime VALUE $value OR <datetime> '1970-01-01T00:00:00Z' ASSERT $value != NONE"),
|
||||
("created_at", "DEFINE FIELD created_at ON TypeIndex TYPE datetime VALUE $value OR time::now()"),
|
||||
("updated_at", "DEFINE FIELD updated_at ON TypeIndex TYPE datetime VALUE time::now()"),
|
||||
("deleted_at", "DEFINE FIELD deleted_at ON TypeIndex TYPE datetime"),
|
||||
("binary", "DEFINE FIELD binary ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("boolean", "DEFINE FIELD boolean ON TypeIndex TYPE bool VALUE $value OR true ASSERT $value != NONE"),
|
||||
("enumField", "DEFINE FIELD enumField ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value INSIDE ['A', 'B', 'C'] AND $value != NONE"),
|
||||
("json", "DEFINE FIELD json ON TypeIndex TYPE object FLEXIBLE VALUE $value OR { key: 'value' } ASSERT $value != NONE"),
|
||||
("relation", "DEFINE FIELD relation ON TypeIndex TYPE record<relation> | NONE VALUE $value OR NONE"),
|
||||
])
|
||||
|
||||
let indexs = if resultNode.hasKey("indexes"): resultNode["indexes"] else: resultNode["ix"]
|
||||
expectInfoEntries(indexs, @[
|
||||
("TypeIndex_integer_index", "DEFINE INDEX TypeIndex_integer_index ON TypeIndex FIELDS integer"),
|
||||
("TypeIndex_smallInteger_index", "DEFINE INDEX TypeIndex_smallInteger_index ON TypeIndex FIELDS smallInteger"),
|
||||
("TypeIndex_mediumInteger_index", "DEFINE INDEX TypeIndex_mediumInteger_index ON TypeIndex FIELDS mediumInteger"),
|
||||
("TypeIndex_bigInteger_index", "DEFINE INDEX TypeIndex_bigInteger_index ON TypeIndex FIELDS bigInteger"),
|
||||
("TypeIndex_decimal_index", "DEFINE INDEX TypeIndex_decimal_index ON TypeIndex FIELDS decimal"),
|
||||
("TypeIndex_double_index", "DEFINE INDEX TypeIndex_double_index ON TypeIndex FIELDS double"),
|
||||
("TypeIndex_float_index", "DEFINE INDEX TypeIndex_float_index ON TypeIndex FIELDS float"),
|
||||
("TypeIndex_uuid_unique", "DEFINE INDEX TypeIndex_uuid_unique ON TypeIndex FIELDS uuid UNIQUE"),
|
||||
("TypeIndex_char_index", "DEFINE INDEX TypeIndex_char_index ON TypeIndex FIELDS char"),
|
||||
("TypeIndex_string_index", "DEFINE INDEX TypeIndex_string_index ON TypeIndex FIELDS string"),
|
||||
("TypeIndex_text_index", "DEFINE INDEX TypeIndex_text_index ON TypeIndex FIELDS text"),
|
||||
("TypeIndex_mediumText_index", "DEFINE INDEX TypeIndex_mediumText_index ON TypeIndex FIELDS mediumText"),
|
||||
("TypeIndex_longText_index", "DEFINE INDEX TypeIndex_longText_index ON TypeIndex FIELDS longText"),
|
||||
("TypeIndex_date_index", "DEFINE INDEX TypeIndex_date_index ON TypeIndex FIELDS date"),
|
||||
("TypeIndex_datetime1_index", "DEFINE INDEX TypeIndex_datetime1_index ON TypeIndex FIELDS datetime1"),
|
||||
("TypeIndex_datetime2_index", "DEFINE INDEX TypeIndex_datetime2_index ON TypeIndex FIELDS datetime2"),
|
||||
("TypeIndex_timestamp_index", "DEFINE INDEX TypeIndex_timestamp_index ON TypeIndex FIELDS timestamp"),
|
||||
("TypeIndex_created_at_index", "DEFINE INDEX TypeIndex_created_at_index ON TypeIndex FIELDS created_at"),
|
||||
("TypeIndex_updated_at_index", "DEFINE INDEX TypeIndex_updated_at_index ON TypeIndex FIELDS updated_at"),
|
||||
("TypeIndex_deleted_at_index", "DEFINE INDEX TypeIndex_deleted_at_index ON TypeIndex FIELDS deleted_at"),
|
||||
("TypeIndex_binary_index", "DEFINE INDEX TypeIndex_binary_index ON TypeIndex FIELDS binary"),
|
||||
("TypeIndex_boolean_index", "DEFINE INDEX TypeIndex_boolean_index ON TypeIndex FIELDS boolean"),
|
||||
("TypeIndex_enumField_index", "DEFINE INDEX TypeIndex_enumField_index ON TypeIndex FIELDS enumField"),
|
||||
("TypeIndex_json_index", "DEFINE INDEX TypeIndex_json_index ON TypeIndex FIELDS json"),
|
||||
])
|
||||
|
||||
block:
|
||||
let info = surreal.raw(""" INFO FOR TABLE TypeUnique """).info().waitFor()
|
||||
let resultNode = info[0]["result"]
|
||||
let fields = resultNode["fields"]
|
||||
expectInfoEntries(fields, @[
|
||||
("index", "DEFINE FIELD index ON TypeUnique TYPE int VALUE $value OR (SELECT max_index FROM _autoincrement_sequences WHERE `table` = 'TypeUnique' AND column = 'index' LIMIT 1)[0].max_index + 1"),
|
||||
("integer", "DEFINE FIELD integer ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("smallInteger", "DEFINE FIELD smallInteger ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("mediumInteger", "DEFINE FIELD mediumInteger ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("bigInteger", "DEFINE FIELD bigInteger ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("decimal", "DEFINE FIELD decimal ON TypeUnique TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("double", "DEFINE FIELD double ON TypeUnique TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("float", "DEFINE FIELD float ON TypeUnique TYPE float VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("uuid", "DEFINE FIELD uuid ON TypeUnique TYPE string VALUE $value OR rand::uuid() ASSERT $value != NONE"),
|
||||
("char", "DEFINE FIELD char ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"),
|
||||
("string", "DEFINE FIELD string ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"),
|
||||
("text", "DEFINE FIELD text ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("mediumText", "DEFINE FIELD mediumText ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("longText", "DEFINE FIELD longText ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("date", "DEFINE FIELD date ON TypeUnique TYPE datetime VALUE $value OR <datetime> '1970-01-01T00:00:00Z' ASSERT $value != NONE"),
|
||||
("datetime1", "DEFINE FIELD datetime1 ON TypeUnique TYPE datetime VALUE $value OR time::now() ASSERT $value != NONE"),
|
||||
("datetime2", "DEFINE FIELD datetime2 ON TypeUnique TYPE datetime VALUE time::now() ASSERT $value != NONE"),
|
||||
("timestamp", "DEFINE FIELD timestamp ON TypeUnique TYPE datetime VALUE $value OR <datetime> '1970-01-01T00:00:00Z' ASSERT $value != NONE"),
|
||||
("created_at", "DEFINE FIELD created_at ON TypeUnique TYPE datetime VALUE $value OR time::now()"),
|
||||
("updated_at", "DEFINE FIELD updated_at ON TypeUnique TYPE datetime VALUE time::now()"),
|
||||
("deleted_at", "DEFINE FIELD deleted_at ON TypeUnique TYPE datetime"),
|
||||
("binary", "DEFINE FIELD binary ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("boolean", "DEFINE FIELD boolean ON TypeUnique TYPE bool VALUE $value OR true ASSERT $value != NONE"),
|
||||
("enumField", "DEFINE FIELD enumField ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value INSIDE ['A', 'B', 'C'] AND $value != NONE"),
|
||||
("json", "DEFINE FIELD json ON TypeUnique TYPE object FLEXIBLE VALUE $value OR { key: 'value' } ASSERT $value != NONE"),
|
||||
("relation", "DEFINE FIELD relation ON TypeUnique TYPE record<relation> | NONE VALUE $value OR NONE"),
|
||||
])
|
||||
|
||||
let indexs = resultNode["indexes"]
|
||||
expectInfoEntries(indexs, @[
|
||||
("TypeUnique_integer_unique", "DEFINE INDEX TypeUnique_integer_unique ON TypeUnique FIELDS integer UNIQUE"),
|
||||
("TypeUnique_smallInteger_unique", "DEFINE INDEX TypeUnique_smallInteger_unique ON TypeUnique FIELDS smallInteger UNIQUE"),
|
||||
("TypeUnique_mediumInteger_unique", "DEFINE INDEX TypeUnique_mediumInteger_unique ON TypeUnique FIELDS mediumInteger UNIQUE"),
|
||||
("TypeUnique_bigInteger_unique", "DEFINE INDEX TypeUnique_bigInteger_unique ON TypeUnique FIELDS bigInteger UNIQUE"),
|
||||
("TypeUnique_decimal_unique", "DEFINE INDEX TypeUnique_decimal_unique ON TypeUnique FIELDS decimal UNIQUE"),
|
||||
("TypeUnique_double_unique", "DEFINE INDEX TypeUnique_double_unique ON TypeUnique FIELDS double UNIQUE"),
|
||||
("TypeUnique_float_unique", "DEFINE INDEX TypeUnique_float_unique ON TypeUnique FIELDS float UNIQUE"),
|
||||
("TypeUnique_uuid_unique", "DEFINE INDEX TypeUnique_uuid_unique ON TypeUnique FIELDS uuid UNIQUE"),
|
||||
("TypeUnique_char_unique", "DEFINE INDEX TypeUnique_char_unique ON TypeUnique FIELDS char UNIQUE"),
|
||||
("TypeUnique_string_unique", "DEFINE INDEX TypeUnique_string_unique ON TypeUnique FIELDS string UNIQUE"),
|
||||
("TypeUnique_text_unique", "DEFINE INDEX TypeUnique_text_unique ON TypeUnique FIELDS text UNIQUE"),
|
||||
("TypeUnique_mediumText_unique", "DEFINE INDEX TypeUnique_mediumText_unique ON TypeUnique FIELDS mediumText UNIQUE"),
|
||||
("TypeUnique_longText_unique", "DEFINE INDEX TypeUnique_longText_unique ON TypeUnique FIELDS longText UNIQUE"),
|
||||
("TypeUnique_date_unique", "DEFINE INDEX TypeUnique_date_unique ON TypeUnique FIELDS date UNIQUE"),
|
||||
("TypeUnique_datetime1_unique", "DEFINE INDEX TypeUnique_datetime1_unique ON TypeUnique FIELDS datetime1 UNIQUE"),
|
||||
("TypeUnique_datetime2_unique", "DEFINE INDEX TypeUnique_datetime2_unique ON TypeUnique FIELDS datetime2 UNIQUE"),
|
||||
("TypeUnique_timestamp_unique", "DEFINE INDEX TypeUnique_timestamp_unique ON TypeUnique FIELDS timestamp UNIQUE"),
|
||||
("TypeUnique_created_at_index", "DEFINE INDEX TypeUnique_created_at_index ON TypeUnique FIELDS created_at"),
|
||||
("TypeUnique_updated_at_index", "DEFINE INDEX TypeUnique_updated_at_index ON TypeUnique FIELDS updated_at"),
|
||||
("TypeUnique_deleted_at_index", "DEFINE INDEX TypeUnique_deleted_at_index ON TypeUnique FIELDS deleted_at"),
|
||||
("TypeUnique_binary_unique", "DEFINE INDEX TypeUnique_binary_unique ON TypeUnique FIELDS binary UNIQUE"),
|
||||
("TypeUnique_boolean_unique", "DEFINE INDEX TypeUnique_boolean_unique ON TypeUnique FIELDS boolean UNIQUE"),
|
||||
("TypeUnique_enumField_unique", "DEFINE INDEX TypeUnique_enumField_unique ON TypeUnique FIELDS enumField UNIQUE"),
|
||||
("TypeUnique_json_unique", "DEFINE INDEX TypeUnique_json_unique ON TypeUnique FIELDS json UNIQUE"),
|
||||
])
|
||||
|
||||
|
||||
test("autoincrement"):
|
||||
surreal.create(
|
||||
table("test",[
|
||||
Column.increments("index"),
|
||||
Column.integer("index2").autoIncrement(),
|
||||
Column.string("string")
|
||||
])
|
||||
)
|
||||
|
||||
surreal.table("test").insert(%*{"string": "a"}).waitFor
|
||||
surreal.table("test").insert(%*{"string": "b"}).waitFor
|
||||
surreal.table("test").insert(%*{"string": "c"}).waitFor
|
||||
surreal.table("test").where("string", "=", "b").delete().waitFor
|
||||
surreal.table("test").insert(%*{"string": "d"}).waitFor
|
||||
|
||||
let data = surreal.table("test").orderBy("index", Asc).get().waitFor
|
||||
for row in data:
|
||||
if row["string"].getStr == "a":
|
||||
check row["index"].getInt == 1
|
||||
check row["index2"].getInt == 1
|
||||
|
||||
if row["string"].getStr == "c":
|
||||
check row["index"].getInt == 3
|
||||
check row["index2"].getInt == 3
|
||||
|
||||
if row["string"].getStr == "d":
|
||||
check row["index2"].getInt == 4
|
||||
|
||||
|
||||
suite("Datetime"):
|
||||
test("datetime default"):
|
||||
surreal.create(
|
||||
table("test", [
|
||||
Column.string("name"),
|
||||
Column.datetime("created_at").default(Current),
|
||||
Column.datetime("updated_at").default(CurrentOnUpdate),
|
||||
])
|
||||
)
|
||||
|
||||
surreal.table("test").insert(%*{"name": "alice"}).waitFor()
|
||||
|
||||
var alice = surreal.table("test").first().waitFor().get()
|
||||
let aliceId = SurrealId.new(alice["id"].getStr())
|
||||
echo alice
|
||||
let aliceCreatedAt1 = alice["created_at"].getStr()
|
||||
let aliceUpdatedAt1 = alice["updated_at"].getStr()
|
||||
|
||||
surreal.table("test").where("id", "=", aliceId).update(%*{"name": "updated"}).waitFor()
|
||||
|
||||
alice = surreal.table("test").find(aliceId).waitFor().get()
|
||||
echo alice
|
||||
let aliceCreatedAt2 = alice["created_at"].getStr()
|
||||
let aliceUpdatedAt2 = alice["updated_at"].getStr()
|
||||
|
||||
check aliceCreatedAt1 == aliceCreatedAt2
|
||||
check aliceUpdatedAt1 != aliceUpdatedAt2
|
||||
|
||||
|
||||
test("timestamps"):
|
||||
surreal.create(
|
||||
table("test", [
|
||||
Column.string("name"),
|
||||
Column.timestamps()
|
||||
])
|
||||
)
|
||||
|
||||
surreal.table("test").insert(%*{"name": "alice"}).waitFor()
|
||||
|
||||
var alice = surreal.table("test").first().waitFor().get()
|
||||
let aliceId = SurrealId.new(alice["id"].getStr())
|
||||
let aliceCreatedAt1 = alice["created_at"].getStr()
|
||||
let aliceUpdatedAt1 = alice["updated_at"].getStr()
|
||||
|
||||
surreal.table("test").where("id", "=", aliceId).update(%*{"name": "updated"}).waitFor()
|
||||
|
||||
alice = surreal.table("test").find(aliceId).waitFor().get()
|
||||
let aliceCreatedAt2 = alice["created_at"].getStr()
|
||||
let aliceUpdatedAt2 = alice["updated_at"].getStr()
|
||||
|
||||
check aliceCreatedAt1 == aliceCreatedAt2
|
||||
check aliceUpdatedAt1 != aliceUpdatedAt2
|
||||
|
||||
|
||||
|
||||
suite("SurrealDB alter table"):
|
||||
setup:
|
||||
surreal.create(
|
||||
table("relation", [
|
||||
Column.uuid("uuid").unique(),
|
||||
]),
|
||||
table("TypeUnique", [
|
||||
Column.integer("num"),
|
||||
Column.string("str"),
|
||||
]),
|
||||
table("TypeIndex", [
|
||||
Column.integer("num"),
|
||||
Column.string("str"),
|
||||
])
|
||||
)
|
||||
|
||||
|
||||
test("add column"):
|
||||
surreal.alter(
|
||||
table("TypeIndex", [
|
||||
Column.increments("index").add(),
|
||||
Column.integer("integer").unsigned().index().default(1).add(),
|
||||
Column.smallInteger("smallInteger").unsigned().index().default(1).add(),
|
||||
Column.mediumInteger("mediumInteger").unsigned().index().default(1).add(),
|
||||
Column.bigInteger("bigInteger").unsigned().index().default(1).add(),
|
||||
Column.decimal("decimal", 10, 3).unsigned().index().default(1.1).add(),
|
||||
Column.double("double", 10, 3).unsigned().index().default(1.1).add(),
|
||||
Column.float("float").unsigned().index().default(1.1).add(),
|
||||
Column.uuid("uuid").index().default("A").add(),
|
||||
Column.char("char", 256).index().default("A").add(),
|
||||
Column.string("string").index().default("A").add(),
|
||||
Column.text("text").index().default("A").add(),
|
||||
Column.mediumText("mediumText").index().default("A").add(),
|
||||
Column.longText("longText").index().default("A").add(),
|
||||
Column.date("date").index().default().add(),
|
||||
Column.datetime("datetime1").index().default(Current).add(),
|
||||
Column.datetime("datetime2").index().default(CurrentOnUpdate).add(),
|
||||
Column.timestamp("timestamp").index().default().add(),
|
||||
Column.timestamps().add(),
|
||||
Column.softDelete().add(),
|
||||
Column.binary("binary").index().default("A").add(),
|
||||
Column.boolean("boolean").index().default(true).add(),
|
||||
Column.enumField("enumField", ["A", "B", "C"]).index().default("A").add(),
|
||||
Column.json("json").index().default(%*{"key":"value"}).add(),
|
||||
Column.foreign("relation").reference("id").onTable("relation").onDelete(SET_NULL).nullable().add(),
|
||||
]),
|
||||
table("TypeUnique", [
|
||||
Column.increments("index").add(),
|
||||
Column.integer("integer").unsigned().index().unique().unique().default(1).add(),
|
||||
Column.smallInteger("smallInteger").unsigned().index().unique().unique().default(1).add(),
|
||||
Column.mediumInteger("mediumInteger").unsigned().index().unique().unique().default(1).add(),
|
||||
Column.bigInteger("bigInteger").unsigned().index().unique().unique().default(1).add(),
|
||||
Column.decimal("decimal", 10, 3).unsigned().index().unique().unique().default(1.1).add(),
|
||||
Column.double("double", 10, 3).unsigned().index().unique().unique().default(1.1).add(),
|
||||
Column.float("float").unsigned().index().unique().unique().default(1.1).add(),
|
||||
Column.uuid("uuid").index().unique().unique().default("A").add(),
|
||||
Column.char("char", 256).index().unique().unique().default("A").add(),
|
||||
Column.string("string").index().unique().unique().default("A").add(),
|
||||
Column.text("text").index().unique().unique().default("A").add(),
|
||||
Column.mediumText("mediumText").index().unique().unique().default("A").add(),
|
||||
Column.longText("longText").index().unique().unique().default("A").add(),
|
||||
Column.date("date").index().unique().unique().default().add(),
|
||||
Column.datetime("datetime1").index().unique().unique().default(Current).add(),
|
||||
Column.datetime("datetime2").index().unique().unique().default(CurrentOnUpdate).add(),
|
||||
Column.timestamp("timestamp").index().unique().unique().default().add(),
|
||||
Column.timestamps().add(),
|
||||
Column.softDelete().add(),
|
||||
Column.binary("binary").index().unique().unique().default("A").add(),
|
||||
Column.boolean("boolean").index().unique().unique().default(true).add(),
|
||||
Column.enumField("enumField", ["A", "B", "C"]).index().unique().default("A").add(),
|
||||
Column.json("json").index().unique().default(%*{"key":"value"}).add(),
|
||||
Column.foreign("relation").reference("id").onTable("relation").onDelete(SET_NULL).nullable().add(),
|
||||
]),
|
||||
)
|
||||
|
||||
block:
|
||||
let info = surreal.raw(""" INFO FOR TABLE TypeIndex """).info().waitFor()
|
||||
let resultNode = info[0]["result"]
|
||||
let fields = resultNode["fields"]
|
||||
expectInfoEntries(fields, @[
|
||||
("index", "DEFINE FIELD index ON TypeIndex TYPE int VALUE $value OR (SELECT max_index FROM _autoincrement_sequences WHERE `table` = 'TypeIndex' AND column = 'index' LIMIT 1)[0].max_index + 1"),
|
||||
("integer", "DEFINE FIELD integer ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("smallInteger", "DEFINE FIELD smallInteger ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("mediumInteger", "DEFINE FIELD mediumInteger ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("bigInteger", "DEFINE FIELD bigInteger ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("decimal", "DEFINE FIELD decimal ON TypeIndex TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("double", "DEFINE FIELD double ON TypeIndex TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("float", "DEFINE FIELD float ON TypeIndex TYPE float VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("uuid", "DEFINE FIELD uuid ON TypeIndex TYPE string VALUE $value OR rand::uuid() ASSERT $value != NONE"),
|
||||
("char", "DEFINE FIELD char ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"),
|
||||
("string", "DEFINE FIELD string ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"),
|
||||
("text", "DEFINE FIELD text ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("mediumText", "DEFINE FIELD mediumText ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("longText", "DEFINE FIELD longText ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("date", "DEFINE FIELD date ON TypeIndex TYPE datetime VALUE $value OR <datetime> '1970-01-01T00:00:00Z' ASSERT $value != NONE"),
|
||||
("datetime1", "DEFINE FIELD datetime1 ON TypeIndex TYPE datetime VALUE $value OR time::now() ASSERT $value != NONE"),
|
||||
("datetime2", "DEFINE FIELD datetime2 ON TypeIndex TYPE datetime VALUE time::now() ASSERT $value != NONE"),
|
||||
("timestamp", "DEFINE FIELD timestamp ON TypeIndex TYPE datetime VALUE $value OR <datetime> '1970-01-01T00:00:00Z' ASSERT $value != NONE"),
|
||||
("created_at", "DEFINE FIELD created_at ON TypeIndex TYPE datetime VALUE $value OR time::now()"),
|
||||
("updated_at", "DEFINE FIELD updated_at ON TypeIndex TYPE datetime VALUE time::now()"),
|
||||
("deleted_at", "DEFINE FIELD deleted_at ON TypeIndex TYPE datetime"),
|
||||
("binary", "DEFINE FIELD binary ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("boolean", "DEFINE FIELD boolean ON TypeIndex TYPE bool VALUE $value OR true ASSERT $value != NONE"),
|
||||
("enumField", "DEFINE FIELD enumField ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value INSIDE ['A', 'B', 'C'] AND $value != NONE"),
|
||||
("json", "DEFINE FIELD json ON TypeIndex TYPE object FLEXIBLE VALUE $value OR { key: 'value' } ASSERT $value != NONE"),
|
||||
("relation", "DEFINE FIELD relation ON TypeIndex TYPE record<relation> | NONE VALUE $value OR NONE"),
|
||||
])
|
||||
|
||||
let indexs = resultNode["indexes"]
|
||||
expectInfoEntries(indexs, @[
|
||||
("TypeIndex_integer_index", "DEFINE INDEX TypeIndex_integer_index ON TypeIndex FIELDS integer"),
|
||||
("TypeIndex_smallInteger_index", "DEFINE INDEX TypeIndex_smallInteger_index ON TypeIndex FIELDS smallInteger"),
|
||||
("TypeIndex_mediumInteger_index", "DEFINE INDEX TypeIndex_mediumInteger_index ON TypeIndex FIELDS mediumInteger"),
|
||||
("TypeIndex_bigInteger_index", "DEFINE INDEX TypeIndex_bigInteger_index ON TypeIndex FIELDS bigInteger"),
|
||||
("TypeIndex_decimal_index", "DEFINE INDEX TypeIndex_decimal_index ON TypeIndex FIELDS decimal"),
|
||||
("TypeIndex_double_index", "DEFINE INDEX TypeIndex_double_index ON TypeIndex FIELDS double"),
|
||||
("TypeIndex_float_index", "DEFINE INDEX TypeIndex_float_index ON TypeIndex FIELDS float"),
|
||||
("TypeIndex_uuid_unique", "DEFINE INDEX TypeIndex_uuid_unique ON TypeIndex FIELDS uuid UNIQUE"),
|
||||
("TypeIndex_char_index", "DEFINE INDEX TypeIndex_char_index ON TypeIndex FIELDS char"),
|
||||
("TypeIndex_string_index", "DEFINE INDEX TypeIndex_string_index ON TypeIndex FIELDS string"),
|
||||
("TypeIndex_text_index", "DEFINE INDEX TypeIndex_text_index ON TypeIndex FIELDS text"),
|
||||
("TypeIndex_mediumText_index", "DEFINE INDEX TypeIndex_mediumText_index ON TypeIndex FIELDS mediumText"),
|
||||
("TypeIndex_longText_index", "DEFINE INDEX TypeIndex_longText_index ON TypeIndex FIELDS longText"),
|
||||
("TypeIndex_date_index", "DEFINE INDEX TypeIndex_date_index ON TypeIndex FIELDS date"),
|
||||
("TypeIndex_datetime1_index", "DEFINE INDEX TypeIndex_datetime1_index ON TypeIndex FIELDS datetime1"),
|
||||
("TypeIndex_datetime2_index", "DEFINE INDEX TypeIndex_datetime2_index ON TypeIndex FIELDS datetime2"),
|
||||
("TypeIndex_timestamp_index", "DEFINE INDEX TypeIndex_timestamp_index ON TypeIndex FIELDS timestamp"),
|
||||
("TypeIndex_created_at_index", "DEFINE INDEX TypeIndex_created_at_index ON TypeIndex FIELDS created_at"),
|
||||
("TypeIndex_updated_at_index", "DEFINE INDEX TypeIndex_updated_at_index ON TypeIndex FIELDS updated_at"),
|
||||
("TypeIndex_deleted_at_index", "DEFINE INDEX TypeIndex_deleted_at_index ON TypeIndex FIELDS deleted_at"),
|
||||
("TypeIndex_binary_index", "DEFINE INDEX TypeIndex_binary_index ON TypeIndex FIELDS binary"),
|
||||
("TypeIndex_boolean_index", "DEFINE INDEX TypeIndex_boolean_index ON TypeIndex FIELDS boolean"),
|
||||
("TypeIndex_enumField_index", "DEFINE INDEX TypeIndex_enumField_index ON TypeIndex FIELDS enumField"),
|
||||
("TypeIndex_json_index", "DEFINE INDEX TypeIndex_json_index ON TypeIndex FIELDS json"),
|
||||
])
|
||||
|
||||
block:
|
||||
let info = surreal.raw(""" INFO FOR TABLE TypeUnique """).info().waitFor()
|
||||
let resultNode = info[0]["result"]
|
||||
let fields = resultNode["fields"]
|
||||
expectInfoEntries(fields, @[
|
||||
("index", "DEFINE FIELD index ON TypeUnique TYPE int VALUE $value OR (SELECT max_index FROM _autoincrement_sequences WHERE `table` = 'TypeUnique' AND column = 'index' LIMIT 1)[0].max_index + 1"),
|
||||
("integer", "DEFINE FIELD integer ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("smallInteger", "DEFINE FIELD smallInteger ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("mediumInteger", "DEFINE FIELD mediumInteger ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("bigInteger", "DEFINE FIELD bigInteger ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("decimal", "DEFINE FIELD decimal ON TypeUnique TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("double", "DEFINE FIELD double ON TypeUnique TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("float", "DEFINE FIELD float ON TypeUnique TYPE float VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"),
|
||||
("uuid", "DEFINE FIELD uuid ON TypeUnique TYPE string VALUE $value OR rand::uuid() ASSERT $value != NONE"),
|
||||
("char", "DEFINE FIELD char ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"),
|
||||
("string", "DEFINE FIELD string ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"),
|
||||
("text", "DEFINE FIELD text ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("mediumText", "DEFINE FIELD mediumText ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("longText", "DEFINE FIELD longText ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("date", "DEFINE FIELD date ON TypeUnique TYPE datetime VALUE $value OR <datetime> '1970-01-01T00:00:00Z' ASSERT $value != NONE"),
|
||||
("datetime1", "DEFINE FIELD datetime1 ON TypeUnique TYPE datetime VALUE $value OR time::now() ASSERT $value != NONE"),
|
||||
("datetime2", "DEFINE FIELD datetime2 ON TypeUnique TYPE datetime VALUE time::now() ASSERT $value != NONE"),
|
||||
("timestamp", "DEFINE FIELD timestamp ON TypeUnique TYPE datetime VALUE $value OR <datetime> '1970-01-01T00:00:00Z' ASSERT $value != NONE"),
|
||||
("created_at", "DEFINE FIELD created_at ON TypeUnique TYPE datetime VALUE $value OR time::now()"),
|
||||
("updated_at", "DEFINE FIELD updated_at ON TypeUnique TYPE datetime VALUE time::now()"),
|
||||
("deleted_at", "DEFINE FIELD deleted_at ON TypeUnique TYPE datetime"),
|
||||
("binary", "DEFINE FIELD binary ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"),
|
||||
("boolean", "DEFINE FIELD boolean ON TypeUnique TYPE bool VALUE $value OR true ASSERT $value != NONE"),
|
||||
("enumField", "DEFINE FIELD enumField ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value INSIDE ['A', 'B', 'C'] AND $value != NONE"),
|
||||
("json", "DEFINE FIELD json ON TypeUnique TYPE object FLEXIBLE VALUE $value OR { key: 'value' } ASSERT $value != NONE"),
|
||||
("relation", "DEFINE FIELD relation ON TypeUnique TYPE record<relation> | NONE VALUE $value OR NONE"),
|
||||
])
|
||||
|
||||
let indexs = if resultNode.hasKey("indexes"): resultNode["indexes"] else: resultNode["ix"]
|
||||
expectInfoEntries(indexs, @[
|
||||
("TypeUnique_integer_unique", "DEFINE INDEX TypeUnique_integer_unique ON TypeUnique FIELDS integer UNIQUE"),
|
||||
("TypeUnique_smallInteger_unique", "DEFINE INDEX TypeUnique_smallInteger_unique ON TypeUnique FIELDS smallInteger UNIQUE"),
|
||||
("TypeUnique_mediumInteger_unique", "DEFINE INDEX TypeUnique_mediumInteger_unique ON TypeUnique FIELDS mediumInteger UNIQUE"),
|
||||
("TypeUnique_bigInteger_unique", "DEFINE INDEX TypeUnique_bigInteger_unique ON TypeUnique FIELDS bigInteger UNIQUE"),
|
||||
("TypeUnique_decimal_unique", "DEFINE INDEX TypeUnique_decimal_unique ON TypeUnique FIELDS decimal UNIQUE"),
|
||||
("TypeUnique_double_unique", "DEFINE INDEX TypeUnique_double_unique ON TypeUnique FIELDS double UNIQUE"),
|
||||
("TypeUnique_float_unique", "DEFINE INDEX TypeUnique_float_unique ON TypeUnique FIELDS float UNIQUE"),
|
||||
("TypeUnique_uuid_unique", "DEFINE INDEX TypeUnique_uuid_unique ON TypeUnique FIELDS uuid UNIQUE"),
|
||||
("TypeUnique_char_unique", "DEFINE INDEX TypeUnique_char_unique ON TypeUnique FIELDS char UNIQUE"),
|
||||
("TypeUnique_string_unique", "DEFINE INDEX TypeUnique_string_unique ON TypeUnique FIELDS string UNIQUE"),
|
||||
("TypeUnique_text_unique", "DEFINE INDEX TypeUnique_text_unique ON TypeUnique FIELDS text UNIQUE"),
|
||||
("TypeUnique_mediumText_unique", "DEFINE INDEX TypeUnique_mediumText_unique ON TypeUnique FIELDS mediumText UNIQUE"),
|
||||
("TypeUnique_longText_unique", "DEFINE INDEX TypeUnique_longText_unique ON TypeUnique FIELDS longText UNIQUE"),
|
||||
("TypeUnique_date_unique", "DEFINE INDEX TypeUnique_date_unique ON TypeUnique FIELDS date UNIQUE"),
|
||||
("TypeUnique_datetime1_unique", "DEFINE INDEX TypeUnique_datetime1_unique ON TypeUnique FIELDS datetime1 UNIQUE"),
|
||||
("TypeUnique_datetime2_unique", "DEFINE INDEX TypeUnique_datetime2_unique ON TypeUnique FIELDS datetime2 UNIQUE"),
|
||||
("TypeUnique_timestamp_unique", "DEFINE INDEX TypeUnique_timestamp_unique ON TypeUnique FIELDS timestamp UNIQUE"),
|
||||
("TypeUnique_created_at_index", "DEFINE INDEX TypeUnique_created_at_index ON TypeUnique FIELDS created_at"),
|
||||
("TypeUnique_updated_at_index", "DEFINE INDEX TypeUnique_updated_at_index ON TypeUnique FIELDS updated_at"),
|
||||
("TypeUnique_deleted_at_index", "DEFINE INDEX TypeUnique_deleted_at_index ON TypeUnique FIELDS deleted_at"),
|
||||
("TypeUnique_binary_unique", "DEFINE INDEX TypeUnique_binary_unique ON TypeUnique FIELDS binary UNIQUE"),
|
||||
("TypeUnique_boolean_unique", "DEFINE INDEX TypeUnique_boolean_unique ON TypeUnique FIELDS boolean UNIQUE"),
|
||||
("TypeUnique_enumField_unique", "DEFINE INDEX TypeUnique_enumField_unique ON TypeUnique FIELDS enumField UNIQUE"),
|
||||
("TypeUnique_json_unique", "DEFINE INDEX TypeUnique_json_unique ON TypeUnique FIELDS json UNIQUE"),
|
||||
])
|
||||
|
||||
|
||||
test("drop column"):
|
||||
surreal.alter(
|
||||
table("TypeIndex", [
|
||||
Column.dropColumn("str")
|
||||
])
|
||||
)
|
||||
|
||||
let columns = surreal.table("TypeIndex").columns().waitFor
|
||||
check not columns.contains("str")
|
||||
|
||||
|
||||
test("drop table"):
|
||||
surreal.create(
|
||||
table("TypeIndex", [
|
||||
Column.integer("num")
|
||||
])
|
||||
)
|
||||
|
||||
surreal.drop(
|
||||
table("TypeIndex")
|
||||
)
|
||||
|
||||
let res = surreal.table("TypeIndex").first().waitFor()
|
||||
check not res.isSome
|
||||
|
||||
|
||||
clearTables(surreal).waitFor()
|
||||
Reference in New Issue
Block a user