Skip to content

Commit

Permalink
Add support for null column comments and improve schema generation er…
Browse files Browse the repository at this point in the history
…ror handling

- Extend the Column model by adding a new field `isCommentNull` to indicate when a column comment is intended to be null.
- Update the JSON schema output in the `toSchema` proc to include the `isCommentNull` flag.
- Overload the `comment` proc to handle a nil argument by setting `isCommentNull` to true.
- Introduce new PostgreSQL query procs (`addCommentColumn` and `addNullCommentColumn`) to generate appropriate COMMENT queries based on whether a comment is provided or set to null.
- Modify add and change column query functions to conditionally include comment queries using `commentContent` and `isCommentNull`.
- Enhance schema generation in both PostgreSQL and SQLite by accepting a configurable schema file path (defaulting to the current directory) and adding robust error handling.
- Update tests to verify the new behavior for column comments and schema generation.
  • Loading branch information
itsumura-h committed Feb 8, 2025
1 parent c8e7fe1 commit ea2de49
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 75 deletions.
6 changes: 6 additions & 0 deletions src/allographer/schema_builder/models/column.nim
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Column* = ref object
defaultDatetime*: DefaultDateTime
foreignOnDelete*: ForeignOnDelete
commentContent*:string
isCommentNull*:bool
info*: JsonNode
checksum*:string
# alter table
Expand Down Expand Up @@ -51,6 +52,7 @@ proc toSchema*(self:Column):JsonNode =
"defaultJson": self.defaultJson,
"foreignOnDelete": self.foreignOnDelete,
"comment": self.commentContent,
"isCommentNull": self.isCommentNull,
"info": self.info,
"previousName":self.previousName,
"migrationType":self.migrationType,
Expand Down Expand Up @@ -389,6 +391,10 @@ proc comment*(c: Column, value:string): Column =
c.commentContent = value
return c

proc comment*(c: Column, arg:type nil): Column =
c.isCommentNull = true
return c


# =============================================================================
# alter table
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,13 @@ else:
import std/json
import ../../models/table
import ../../models/column
import ../../enums
import ./schema_utils
import ./postgres_query_type
import ./sub/change_column_query


proc changeColumn*(self:PostgresSchema, isReset:bool) =
var queries = changeColumnString(self.table, self.column)

if self.column.isIndex and self.column.typ != rdbIncrements:
queries.add(changeIndexString(self.table, self.column))
let queries = changeColumnString(self.table, self.column)

let schema = $self.column.toSchema()
let checksum = $schema.secureHash()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,14 @@ proc addIndexColumn(column:Column, table:Table):string =
return &"CREATE INDEX IF NOT EXISTS \"{table.name}_{column.name}_index\" ON \"{table.name}\"(\"{column.name}\")"


proc addCommentColumn(column:Column, table:Table):string =
return &"COMMENT ON COLUMN \"{table.name}\".\"{column.name}\" IS '{column.commentContent}'"


proc addNullCommentColumn(column:Column, table:Table):string =
return &"COMMENT ON COLUMN \"{table.name}\".\"{column.name}\" IS NULL"


proc addColumnString*(table:Table, column:Column):seq[string] =
var queries:seq[string]
case column.typ:
Expand Down Expand Up @@ -492,4 +500,9 @@ proc addColumnString*(table:Table, column:Column):seq[string] =
if column.isUpdatedAt:
queries.add(column.addUpdatedAtColumn(table))

if column.commentContent.len > 0:
queries.add(column.addCommentColumn(table))
elif column.isCommentNull:
queries.add(column.addNullCommentColumn(table))

return queries
Original file line number Diff line number Diff line change
Expand Up @@ -347,68 +347,83 @@ proc indexColumn(column:Column, table:Table):string =
return &"CREATE INDEX IF NOT EXISTS \"{table.name}_{column.name}_index\" ON \"{table.name}\"(\"{column.name}\")"


proc addCommentColumn(column:Column, table:Table):string =
return &"COMMENT ON COLUMN \"{table.name}\".\"{column.name}\" IS '{column.commentContent}'"


proc addNullCommentColumn(column:Column, table:Table):string =
return &"COMMENT ON COLUMN \"{table.name}\".\"{column.name}\" IS NULL"


proc changeColumnString*(table:Table, column:Column):seq[string] =
var queries:seq[string]
case column.typ:
# int
of rdbIncrements:
notAllowedType("increments")
queries = column.changeIntColumn(table)
of rdbInteger:
return column.changeIntColumn(table)
queries = column.changeIntColumn(table)
of rdbSmallInteger:
return column.changeSmallIntColumn(table)
queries = column.changeSmallIntColumn(table)
of rdbMediumInteger:
return column.changeMediumIntColumn(table)
queries = column.changeMediumIntColumn(table)
of rdbBigInteger:
return column.changeBigIntColumn(table)
queries = column.changeBigIntColumn(table)
# float
of rdbDecimal:
return column.changeDecimalColumn(table)
queries = column.changeDecimalColumn(table)
of rdbDouble:
return column.changeDecimalColumn(table)
queries = column.changeDecimalColumn(table)
of rdbFloat:
return column.changeFloatColumn(table)
queries = column.changeFloatColumn(table)
# char
of rdbUuid:
return column.changeStringColumn(table)
queries = column.changeStringColumn(table)
of rdbChar:
return column.changeCharColumn(table)
queries = column.changeCharColumn(table)
of rdbString:
return column.changeStringColumn(table)
queries = column.changeStringColumn(table)
# text
of rdbText:
return column.changeTextColumn(table)
queries = column.changeTextColumn(table)
of rdbMediumText:
return column.changeTextColumn(table)
queries = column.changeTextColumn(table)
of rdbLongText:
return column.changeTextColumn(table)
queries = column.changeTextColumn(table)
# date
of rdbDate:
return column.changeDateColumn(table)
queries = column.changeDateColumn(table)
of rdbDatetime:
return column.changeDatetimeColumn(table)
queries = column.changeDatetimeColumn(table)
of rdbTime:
return column.changeTimeColumn(table)
queries = column.changeTimeColumn(table)
of rdbTimestamp:
return column.changeTimestampColumn(table)
queries = column.changeTimestampColumn(table)
of rdbTimestamps:
notAllowedType("timestamps")
of rdbSoftDelete:
notAllowedType("softDelete")
# others
of rdbBinary:
return column.changeBlobColumn(table)
queries = column.changeBlobColumn(table)
of rdbBoolean:
return column.changeBoolColumn(table)
queries = column.changeBoolColumn(table)
of rdbEnumField:
return column.changeEnumColumn(table)
queries = column.changeEnumColumn(table)
of rdbJson:
return column.changeJsonColumn(table)
queries = column.changeJsonColumn(table)
# foreign
of rdbForeign:
notAllowedType("foreign")
of rdbStrForeign:
notAllowedType("strForeign")

if column.isIndex and column.typ != rdbIncrements:
queries.add(indexColumn(column, table))

if column.commentContent.len > 0:
queries.add(column.addCommentColumn(table))
elif column.isCommentNull:
queries.add(column.addNullCommentColumn(table))

proc changeIndexString*(table:Table, column:Column):string =
return column.indexColumn(table)
return queries
24 changes: 18 additions & 6 deletions src/allographer/schema_builder/usecases/postgres/create_schema.nim
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,21 @@ proc generateSchemaCode(tablesInfo: Table[string, seq[tuple[name: string, typ: s


proc createSchema*(rdb: PostgresConnections, schemaPath="") {.async.} =
## create schema.nim
let tablesInfo = rdb.getTableInfo().await
let schemaCode = generateSchemaCode(tablesInfo)

writeFile(schemaPath / "schema.nim", schemaCode)
echo "schema.nim generated successfully"
## if schemaPath is not specified, the schema will be generated in the current directory
##
## Default schema file name is `getCurrentDir() / "schema.nim"`
try:
let tablesInfo = rdb.getTableInfo().await
let schemaCode = generateSchemaCode(tablesInfo)

let schemaFilePath =
if schemaPath == "":
getCurrentDir() / "schema.nim"
else:
schemaPath

writeFile(schemaFilePath, schemaCode)
echo "schema generated successfully in ", schemaFilePath
except Exception as e:
echo "Error generating schema: ", e.msg
raise
15 changes: 11 additions & 4 deletions src/allographer/schema_builder/usecases/sqlite/create_schema.nim
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,21 @@ proc generateSchemaCode(tablesInfo: Table[string, seq[tuple[name: string, typ: s
return code

proc createSchema*(rdb: SqliteConnections, schemaPath="") {.async.} =
## create schema.nim
## if schemaPath is not specified, the schema will be generated in the current directory
##
## Default schema file name is `getCurrentDir() / "schema.nim"`
try:
let tablesInfo = await rdb.getTableInfo()
let schemaCode = generateSchemaCode(tablesInfo)

let schemaFilePath =
if schemaPath == "":
getCurrentDir() / "schema.nim"
else:
schemaPath

writeFile(schemaPath / "schema.nim", schemaCode)
echo "schema.nim generated successfully"

writeFile(schemaFilePath, schemaCode)
echo "schema generated successfully in ", schemaFilePath
except Exception as e:
echo "Error generating schema: ", e.msg
raise
81 changes: 48 additions & 33 deletions tests/clear_tables.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,66 @@ import ../src/allographer/query_builder


proc clearTables*(rdb:PostgresConnections) {.async.} =
let tables = rdb.table("_allographer_migrations").orderBy("id", Desc) .get().await
for table in tables:
let tableName = table["name"].getStr()
if not tableName.startsWith("_"):
rdb.raw(&"DROP TABLE IF EXISTS \"{tableName}\"").exec().await
try:
let tables = rdb.table("_allographer_migrations").orderBy("id", Desc) .get().await
for table in tables:
let tableName = table["name"].getStr()
if not tableName.startsWith("_"):
rdb.raw(&"DROP TABLE IF EXISTS \"{tableName}\"").exec().await

rdb.raw("DROP TABLE IF EXISTS \"_allographer_migrations\"").exec().await
rdb.raw("DROP TABLE IF EXISTS \"_allographer_migrations\"").exec().await
except:
echo "error: ", getCurrentExceptionMsg()


proc clearTables*(rdb:MariaDBConnections) {.async.} =
let tables = rdb.table("_allographer_migrations").orderBy("id", Desc) .get().await
for table in tables:
let tableName = table["name"].getStr()
if not tableName.startsWith("_"):
rdb.raw(&"DROP TABLE IF EXISTS `{tableName}`").exec().await

rdb.raw("DROP TABLE IF EXISTS `_allographer_migrations`").exec().await
try:
let tables = rdb.table("_allographer_migrations").orderBy("id", Desc) .get().await
for table in tables:
let tableName = table["name"].getStr()
if not tableName.startsWith("_"):
rdb.raw(&"DROP TABLE IF EXISTS `{tableName}`").exec().await

rdb.raw("DROP TABLE IF EXISTS `_allographer_migrations`").exec().await
except:
echo "error: ", getCurrentExceptionMsg()


proc clearTables*(rdb:MySQLConnections) {.async.} =
let tables = rdb.table("_allographer_migrations").orderBy("id", Desc) .get().await
for table in tables:
let tableName = table["name"].getStr()
if not tableName.startsWith("_"):
rdb.raw(&"DROP TABLE IF EXISTS `{tableName}`").exec().await

rdb.raw("DROP TABLE IF EXISTS `_allographer_migrations`").exec().await
try:
let tables = rdb.table("_allographer_migrations").orderBy("id", Desc) .get().await
for table in tables:
let tableName = table["name"].getStr()
if not tableName.startsWith("_"):
rdb.raw(&"DROP TABLE IF EXISTS `{tableName}`").exec().await

rdb.raw("DROP TABLE IF EXISTS `_allographer_migrations`").exec().await
except:
echo "error: ", getCurrentExceptionMsg()


proc clearTables*(rdb:SqliteConnections) {.async.} =
let tables = rdb.table("_allographer_migrations").orderBy("id", Desc) .get().await
for table in tables:
let tableName = table["name"].getStr()
if not tableName.startsWith("_"):
rdb.raw(&"DROP TABLE IF EXISTS \"{tableName}\"").exec().await
try:
let tables = rdb.table("_allographer_migrations").orderBy("id", Desc) .get().await
for table in tables:
let tableName = table["name"].getStr()
if not tableName.startsWith("_"):
rdb.raw(&"DROP TABLE IF EXISTS \"{tableName}\"").exec().await

rdb.raw("DROP TABLE IF EXISTS \"_allographer_migrations\"").exec().await
rdb.raw("DROP TABLE IF EXISTS \"_allographer_migrations\"").exec().await
except:
echo "error: ", getCurrentExceptionMsg()


proc clearTables*(rdb:SurrealConnections) {.async.} =
let dbInfo = rdb.raw("INFO FOR DB").info().await
let tables = dbInfo[0]["result"]["tb"]
for (table, _) in tables.pairs:
if not table.startsWith("_"):
rdb.raw(&"REMOVE TABLE {table}").exec().await
try:
let dbInfo = rdb.raw("INFO FOR DB").info().await
let tables = dbInfo[0]["result"]["tb"]
for (table, _) in tables.pairs:
if not table.startsWith("_"):
rdb.raw(&"REMOVE TABLE {table}").exec().await

rdb.raw(&"REMOVE TABLE _allographer_migrations").exec().await
rdb.raw(&"REMOVE TABLE _autoincrement_sequences").exec().await
rdb.raw(&"REMOVE TABLE _allographer_migrations").exec().await
rdb.raw(&"REMOVE TABLE _autoincrement_sequences").exec().await
except:
echo "error: ", getCurrentExceptionMsg()
4 changes: 3 additions & 1 deletion tests/postgres/test_create_schema.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ discard """
cmd: "nim c -d:reset $file"
"""

# nim c -d:reset -r tests/postgres/test_create_schema.nim

import std/unittest
import std/asyncdispatch
import std/os
Expand Down Expand Up @@ -61,7 +63,7 @@ suite "Schema output after migration":

test "should generate schema.nim file":
# スキーマ生成
rdb.createSchema().waitFor()
rdb.createSchema(schemaFilePath).waitFor()

# schema.nim ファイルの存在確認
check fileExists(schemaFilePath)
Expand Down
24 changes: 23 additions & 1 deletion tests/postgres/test_schema.nim
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import ../clear_tables


let rdb = postgres
clearTables(rdb).waitFor()

suite("PostgreSQL create table"):
test("create table"):
Expand Down Expand Up @@ -298,13 +299,34 @@ suite($rdb & " primary"):


suite("Comment"):
test("column comment"):
test("create table with column comment"):
rdb.create(
table("Comment", [
Column.integer("index1").comment("index1 comment"),
Column.string("string").comment("string comment")
])
)

test("alter table change column comment"):
rdb.alter(
table("Comment", [
Column.integer("index1").comment("changed index1 comment").change()
])
)

test("alter table add column comment"):
rdb.alter(
table("Comment", [
Column.string("string2").comment("string2 comment").add()
])
)

test("alter table add null comment"):
rdb.alter(
table("Comment", [
Column.string("string").comment(nil).change()
])
)


clearTables(rdb).waitFor()
Loading

0 comments on commit ea2de49

Please sign in to comment.