diff --git a/.env b/.env index a156a271..869aab0f 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ -# SQLITE_HOST="/root/project/db.sqlite3" -SQLITE_HOST=":memory:" +SQLITE_HOST="/root/project/db.sqlite3" +# SQLITE_HOST=":memory:" MY_HOST=mysql MARIA_HOST=mariadb MY_PORT=3306 diff --git a/documents/schema_builder.md b/documents/schema_builder.md index 2ea6dcd0..2f34844b 100644 --- a/documents/schema_builder.md +++ b/documents/schema_builder.md @@ -36,6 +36,7 @@ from database import rdb rdb.schema([ table("auth", [ Column().increments("id"), + Column().uuid("uuid"), Column().string("name"), Column().timestamps() ]), @@ -43,6 +44,7 @@ rdb.schema([ Column().increments("id"), Column().string("name"), Column().foreign("auth_id").reference("id").on("auth").onDelete(SET_NULL) + Column().strForeign("uuid").reference("uuid").on("auth").onDelete(SET_NULL) ]) ]) ``` @@ -187,6 +189,7 @@ template seeder*(rdb:Rdb, tableName, column:string, body:untyped):untyped ## char |Command|Description| |---|---| +|`uuid("name")`|VARCHAR indexed and unique column.| |`char("name", 100)`|CHAR equivalent column with an optional length.| |`string("name")`|VARCHAR equivalent column.| |`string("name", 100)`|VARCHAR equivalent column with a optional length.| @@ -224,10 +227,15 @@ template seeder*(rdb:Rdb, tableName, column:string, body:untyped):untyped ## Foreign Key Constraints For example, let's define a `user_id` column on the table that references the `id` column on a `users` table: ```nim -Schema().foreign("user_id") -.reference("id") -.on("users") -.onDelete(SET_NULL) +Column().foreign("user_id") + .reference("id") + .on("users") + .onDelete(SET_NULL) + +Column().strForeign("uuid") + .reference("uuid") + .on("users") + .onDelete(SET_NULL) ``` arg of `onDelete` is enum diff --git a/src/allographer/schema_builder/column.nim b/src/allographer/schema_builder/column.nim index e93ca3fa..8b255f07 100644 --- a/src/allographer/schema_builder/column.nim +++ b/src/allographer/schema_builder/column.nim @@ -57,6 +57,7 @@ type rdbEnumField = "rdbEnumField" rdbJson = "rdbJson" rdbForeign = "rdbForeign" + rdbStrForeign = "rdbStrForeign" ForeignOnDelete* = enum RESTRICT = "RESTRICT" @@ -182,6 +183,14 @@ proc string*(self:Column, name:string, length=255):Column = self.info = %*{"maxLength": length} return self +proc uuid*(self:Column, name:string):Column = + self.name = name + self.typ = rdbString + self.isUnique = true + self.isIndex = true + self.info = %*{"maxLength": 255} + return self + proc text*(self:Column, name:string):Column = self.name = name self.typ = rdbText @@ -262,12 +271,24 @@ proc foreign*(self:Column, name:string):Column = self.name = name self.previousName = name self.typ = rdbForeign + self.isUnique = true + self.isIndex = true + return self + +proc strForeign*(self:Column, name:string, length=255):Column = + self.name = name + self.previousName = name + self.typ = rdbStrForeign + self.isUnique = true + self.isIndex = true + self.info = %*{"maxLength": length} return self proc reference*(self:Column, column:string):Column = - self.info = %*{ - "column": column - } + if self.info.isNil: + self.info = %*{"column": column} + else: + self.info["column"] = %column return self proc on*(self:Column, table:string):Column = diff --git a/src/allographer/schema_builder/generators/mysql_generators.nim b/src/allographer/schema_builder/generators/mysql_generators.nim index 0a5230c3..c3c14f74 100644 --- a/src/allographer/schema_builder/generators/mysql_generators.nim +++ b/src/allographer/schema_builder/generators/mysql_generators.nim @@ -1,4 +1,4 @@ -import json, strformat +import json, strformat, strutils import ../column, generator_util import ../../utils @@ -367,6 +367,12 @@ proc foreignColumnGenerator*(name:string, isDefault:bool, default:int):string = if isDefault: result.add(&" DEFAULT {default}") +proc strForeignColumnGenerator*(name:string, maxLength:int, isDefault:bool, default:string):string = + result = &"`{name}` VARCHAR({maxLength})" + result.add(" UNIQUE") + if isDefault: + result.add(&" DEFAULT {default}") + proc foreignGenerator*(name:string, table:string, column:string, foreignOnDelete:ForeignOnDelete):string = var onDeleteString = "RESTRICT" @@ -409,5 +415,6 @@ proc alterDeleteForeignGenerator*(table, column:string):string = proc indexGenerate*(table, column:string):string = var table = table + let smallTable = table.toLowerAscii() myWrapUpper(table) - return &"CREATE INDEX {column}_index ON {table}({column})" + return &"CREATE INDEX {smallTable}_{column}_index ON {table}({column})" diff --git a/src/allographer/schema_builder/generators/postgres_generators.nim b/src/allographer/schema_builder/generators/postgres_generators.nim index 32690b11..e265fa70 100644 --- a/src/allographer/schema_builder/generators/postgres_generators.nim +++ b/src/allographer/schema_builder/generators/postgres_generators.nim @@ -1,4 +1,4 @@ -import json, strformat +import json, strformat, strutils import ../column import generator_util import ../../utils @@ -391,6 +391,12 @@ proc foreignColumnGenerator*(name:string, isDefault:bool, default:int):string = if isDefault: result.add(&" DEFAULT {default}") +proc strForeignColumnGenerator*(name:string, maxLength:int, isDefault:bool, default:string):string = + result = &"\"{name}\" VARCHAR({maxLength})" + result.add(" UNIQUE") + if isDefault: + result.add(&" DEFAULT {default}") + proc foreignGenerator*(table, column, refTable, refColumn:string, foreignOnDelete:ForeignOnDelete):string = var onDeleteString = "RESTRICT" @@ -435,5 +441,6 @@ proc alterDeleteForeignGenerator*(table, column:string):string = proc indexGenerate*(table, column:string):string = var table = table + let smallTable = table.toLowerAscii() pgWrapUpper(table) - return &"CREATE INDEX {column}_index ON {table}({column})" + return &"CREATE INDEX {smallTable}_{column}_index ON {table}({column})" diff --git a/src/allographer/schema_builder/generators/sqlite_generators.nim b/src/allographer/schema_builder/generators/sqlite_generators.nim index 2c79be35..38f5744d 100644 --- a/src/allographer/schema_builder/generators/sqlite_generators.nim +++ b/src/allographer/schema_builder/generators/sqlite_generators.nim @@ -1,4 +1,4 @@ -import json, strformat +import json, strformat, strutils import ../column import ../../utils from db_common import DbError @@ -276,6 +276,12 @@ proc foreignColumnGenerator*(name:string, isDefault:bool, default:int):string = if isDefault: result.add(&" DEFAULT {default}") +proc strForeignColumnGenerator*(name:string, isDefault:bool, default:string):string = + result = &"'{name}' VARCHAR" + result.add(" UNIQUE") + if isDefault: + result.add(&" DEFAULT {default}") + proc foreignGenerator*(name:string, table:string, column:string, foreignOnDelete:ForeignOnDelete):string = var onDeleteString = "RESTRICT" @@ -294,5 +300,6 @@ proc alterAddForeignGenerator*(table:string, column:string):string = proc indexGenerate*(table, column:string):string = var table = table + let smallTable = table.toLowerAscii() liteWrapUpper(table) - return &"CREATE INDEX {column}_index ON {table}({column})" + return &"CREATE INDEX {smallTable}_{column}_index ON {table}({column})" diff --git a/src/allographer/schema_builder/migrates/mysql_migrate.nim b/src/allographer/schema_builder/migrates/mysql_migrate.nim index 7f17cbd4..b9dd203d 100644 --- a/src/allographer/schema_builder/migrates/mysql_migrate.nim +++ b/src/allographer/schema_builder/migrates/mysql_migrate.nim @@ -253,10 +253,19 @@ proc generateColumnString*(column:Column):string = columnString.add( foreignColumnGenerator(column.name, column.isDefault, column.defaultInt) ) + of rdbStrForeign: + columnString.add( + strForeignColumnGenerator( + column.name, + column.info["maxLength"].getInt, + column.isDefault, + column.defaultString + ) + ) return columnString proc generateForeignString(column:Column):string = - if column.typ == rdbForeign: + if column.typ == rdbForeign or column.typ == rdbStrForeign: return foreignGenerator( column.name, column.info["table"].getStr(), @@ -265,7 +274,7 @@ proc generateForeignString(column:Column):string = ) proc generateAlterForeignString(table:string, column:Column):string = - if column.typ == rdbForeign: + if column.typ == rdbForeign or column.typ == rdbStrForeign: return alterAddForeignGenerator( table, column.name, @@ -282,7 +291,7 @@ proc migrate*(this:Table):string = columnString.add( generateColumnString(column) ) - if column.typ == rdbForeign: + if column.typ == rdbForeign or column.typ == rdbStrForeign: if columnString.len > 0 or foreignString.len > 0: foreignString.add(", ") foreignString.add( generateForeignString(column) diff --git a/src/allographer/schema_builder/migrates/postgres_migrate.nim b/src/allographer/schema_builder/migrates/postgres_migrate.nim index 648df586..388327a1 100644 --- a/src/allographer/schema_builder/migrates/postgres_migrate.nim +++ b/src/allographer/schema_builder/migrates/postgres_migrate.nim @@ -273,10 +273,19 @@ proc generateColumnString*(column:Column, tableName=""):string = columnString.add( foreignColumnGenerator(column.name, column.isDefault, column.defaultInt) ) + of rdbStrForeign: + columnString.add( + strForeignColumnGenerator( + column.name, + column.info["maxLength"].getInt, + column.isDefault, + column.defaultString + ) + ) return columnString proc generateForeignString(table:string, column:Column):string = - if column.typ == rdbForeign: + if column.typ == rdbForeign or column.typ == rdbStrForeign: return foreignGenerator( table, column.name, @@ -286,7 +295,7 @@ proc generateForeignString(table:string, column:Column):string = ) proc generateAlterForeignString(table:string, column:Column):string = - if column.typ == rdbForeign: + if column.typ == rdbForeign or column.typ == rdbStrForeign: return alterAddForeignGenerator( table, column.name, @@ -303,7 +312,7 @@ proc migrate*(self:Table):string = columnString.add( generateColumnString(column, self.name) ) - if column.typ == rdbForeign: + if column.typ == rdbForeign or column.typ == rdbStrForeign: if columnString.len > 0 or foreignString.len > 0: foreignString.add(", ") foreignString.add( generateForeignString(self.name, column) diff --git a/src/allographer/schema_builder/migrates/sqlite_migrate.nim b/src/allographer/schema_builder/migrates/sqlite_migrate.nim index 042dadc7..238a36b2 100644 --- a/src/allographer/schema_builder/migrates/sqlite_migrate.nim +++ b/src/allographer/schema_builder/migrates/sqlite_migrate.nim @@ -252,10 +252,14 @@ proc generateColumnString*(column:Column):string = columnString.add( foreignColumnGenerator(column.name, column.isDefault, column.defaultInt) ) + of rdbStrForeign: + columnString.add( + strForeignColumnGenerator(column.name, column.isDefault, column.defaultString) + ) return columnString proc generateForeignString(column:Column):string = - if column.typ == rdbForeign: + if column.typ == rdbForeign or column.typ == rdbStrForeign: return foreignGenerator( column.name, column.info["table"].getStr(), @@ -264,7 +268,7 @@ proc generateForeignString(column:Column):string = ) proc generateAlterForeignString(column:Column):string = - if column.typ == rdbForeign: + if column.typ == rdbForeign or column.typ == rdbStrForeign: return alterAddForeignGenerator( column.info["table"].getStr(), column.info["column"].getStr(), @@ -278,7 +282,7 @@ proc migrate*(this:Table):string = columnString.add( generateColumnString(column) ) - if column.typ == rdbForeign: + if column.typ == rdbForeign or column.typ == rdbStrForeign: if columnString.len > 0 or foreignString.len > 0: foreignString.add(", ") foreignString.add( generateForeignString(column) diff --git a/tests/connections.nim b/tests/connections.nim index c3804bc6..ba3f6148 100644 --- a/tests/connections.nim +++ b/tests/connections.nim @@ -15,7 +15,7 @@ let timeout = getEnv("DB_TIMEOUT").parseInt let - sqliteDb = dbopen(SQLite3, sqliteHost, maxConnections=maxConnections, shouldDisplayLog=true) + sqliteDb = dbopen(SQLite3, ":memory:", maxConnections=maxConnections, shouldDisplayLog=true) # mysqlDb = dbopen(MySQL, database, user, password, mysqlHost, mysqlPort, maxConnections, timeout) # mariaDb = dbopen(MariaDB, database, user, password, mariadbHost, mysqlPort, maxConnections, timeout) # postgresDb = dbopen(PostgreSQL, database, user, password, pgHost, pgPort, maxConnections, timeout) diff --git a/tests/test_schema_builder.nim b/tests/test_schema_builder.nim index 63e317ad..d611091e 100644 --- a/tests/test_schema_builder.nim +++ b/tests/test_schema_builder.nim @@ -2,7 +2,7 @@ discard """ cmd: "nim c -d:reset -r $file" """ -import unittest, json, times, os, strutils, asyncdispatch, distros +import unittest, json, times, os, strutils, asyncdispatch, distros, oids import ../src/allographer/schema_builder import ../src/allographer/query_builder import ../src/allographer/connection @@ -19,8 +19,9 @@ block: sqliteDb.schema( table("foreigh_table", [ Column().increments("id"), + Column().uuid("uuid"), Column().string("name"), - ], reset=true), + ]), table("schema_builder", [ Column().increments("increments_column"), Column().integer("integer_column").unique().default(1).unsigned().index(), @@ -46,19 +47,21 @@ block: Column().softDelete(), Column().foreign("foreign_id").reference("id").on("foreigh_table").onDelete(SET_NULL), + Column().strForeign("uuid").reference("uuid").on("foreigh_table").onDelete(SET_NULL), Column().binary("binary_column").unique().default().unsigned().index(), Column().boolean("boolean_column").unique().default().index(), Column().enumField("enumField_column", ["a", "b"]).unique().default().index(), Column().json("json_column").unique().default(%*{"key": "value"}).unsigned().index(), - ], reset=true) + ]) ) if detectOs(Ubuntu): mysqlDb.schema( table("foreigh_table", [ Column().increments("id"), + Column().uuid("uuid"), Column().string("name"), - ], reset=true), + ]), table("schema_builder", [ Column().increments("increments_column"), Column().integer("integer_column").unique().default(1).unsigned(), @@ -84,18 +87,20 @@ block: Column().softDelete(), Column().foreign("foreign_id").reference("id").on("foreigh_table").onDelete(SET_NULL), + Column().strForeign("uuid").reference("uuid").on("foreigh_table").onDelete(SET_NULL), Column().binary("binary_column"), Column().boolean("boolean_column").unique().default(), Column().enumField("enumField_column", ["a", "b"]).unique().default("a"), Column().json("json_column"), - ], reset=true) + ]) ) mariaDb.schema( table("foreigh_table", [ Column().increments("id"), + Column().uuid("uuid"), Column().string("name"), - ], reset=true), + ]), table("schema_builder", [ Column().increments("increments_column"), Column().integer("integer_column").unique().default(1).unsigned(), @@ -121,18 +126,20 @@ block: Column().softDelete(), Column().foreign("foreign_id").reference("id").on("foreigh_table").onDelete(SET_NULL), + Column().strForeign("uuid").reference("uuid").on("foreigh_table").onDelete(SET_NULL), Column().binary("binary_column"), Column().boolean("boolean_column").unique().default(), Column().enumField("enumField_column", ["a", "b"]).unique().default("a"), Column().json("json_column"), - ], reset=true) + ]) ) postgresDb.schema( table("foreigh_table", [ Column().increments("id"), + Column().uuid("uuid"), Column().string("name"), - ], reset=true), + ]), table("schema_builder", [ Column().increments("increments_column"), Column().integer("integer_column").unique().default(1).unsigned(), @@ -158,12 +165,13 @@ block: Column().softDelete(), Column().foreign("foreign_id").reference("id").on("foreigh_table").onDelete(SET_NULL), + Column().strForeign("uuid").reference("uuid").on("foreigh_table").onDelete(SET_NULL), Column().binary("binary_column").unique().default(), Column().boolean("boolean_column").unique().default(), Column().enumField("enumField_column", ["a", "b"]).unique().default(), Column().json("json_column").default(%*{"key": "value"}), - ], reset=true) + ]) ) block: @@ -175,6 +183,11 @@ block: else: @[sqliteDb, mariaDb, postgresDb] for rdb in list: + let uuid = $genOid() + await rdb.table("foreigh_table").insert(%*{ + "uuid": uuid, + "name": "a" + }) await rdb.table("schema_builder").insert(%*{ "increments_column": 1, "integer_column": 1, @@ -193,12 +206,19 @@ block: "datetime_column": "2020-01-01".parse("yyyy-MM-dd").format("yyyy-MM-dd HH:MM:ss"), "time_column": "2020-01-01".parse("yyyy-MM-dd").format("HH:MM:ss"), "timestamp_column": "2020-01-01".parse("yyyy-MM-dd").format("yyyy-MM-dd HH:MM:ss"), + "foreign_id": 1, + "uuid": uuid, "binary_column": "a", "boolean_column": true, "enumField_column": "a", "json_column": {"key": "value"} }) - echo await rdb.table("schema_builder").get() + # echo await rdb.table("schema_builder").get() + echo await rdb.table("schema_builder") + .select("schema_builder.foreign_id", "schema_builder.uuid") + .join("foreigh_table", "foreigh_table.uuid", "=", "schema_builder.uuid") + .where("foreigh_table.id", "=", 1) + .get() assert true rdb.alter( drop("schema_builder")