Araq / ormin

Ormin -- An ORM for Nim.
MIT License
153 stars 18 forks source link

Support to custom type map #39

Closed huaxk closed 2 years ago

huaxk commented 4 years ago

Allows custom database types to be mapped to nim types. Proc toNimType should find the corresponding nim type by looking up the table, rather than simply removing the first two letters of the database type, and this implementation can add database types not supported by Ormin in user's code, maybe also overload the buildResult template.

Araq commented 4 years ago

Tests are red with this:

/home/travis/build/Araq/ormin/examples/forum/forum.nim(4, 12) template/generic instantiation of `importModel` from here
/home/travis/build/Araq/ormin/ormin/ormin_sqlite.nim(4, 8) Warning: imported and not used: 'strutils' [UnusedImport]
stack trace: (most recent call last)
/home/travis/build/Araq/ormin/ormin/queries.nim(926, 21) query
/home/travis/build/Araq/ormin/ormin/queries.nim(819, 36) queryImpl
/home/travis/build/Araq/ormin/ormin/queries.nim(664, 17) queryh
/home/travis/build/Araq/ormin/ormin/queries.nim(241, 19) cond
/home/travis/build/Araq/ormin/ormin/queries.nim(294, 45) cond
/home/travis/build/Araq/ormin/ormin/queries.nim(143, 63) toNimType
/home/travis/build/Araq/ormin/ormin/queries.nim(140, 30) toNimType
/home/travis/build/Araq/ormin/nim/lib/pure/collections/tables.nim(263, 7) []
/home/travis/build/Araq/ormin/examples/forum/forum.nim(42, 1) template/generic instantiation of `query` from here
/home/travis/build/Araq/ormin/nim/lib/pure/collections/tables.nim(263, 7) Error: unhandled exception: key not found: dbInet [KeyError]
Araq commented 4 years ago

Please rebase and solve the merge conflicts.

huaxk commented 4 years ago

By the original way, the server side returns type:

  TextMessage* = ref object
    content*: varchar
    creation*: varchar
    author*: integer
    name*: varchar

The varchar type can be easily replaced by kstring type in client side code through definition:

common:
    type
      inet = kstring
      varchar = kstring
      timestamp = kstring

Finally, the following code is generated:

type
  TextMessage* = ref object
    content*: kstring
    creation*: kstring
    author*: int
    name*: kstring

This will produce a lot of intermediate types which only haved by database, such as varchar, integer, boolean, json etc... very easy to have type name conflicts, and user cannot define new type with database, processing new type that ormin has no built-in is important for Ormin's ability to expand, because Ormin can not consider all use cases.

By allowing custom database types map as follow:

var dbtypetables* {.compileTime.} = {
  dbVarchar: "string",
  dbInt: "int",
  dbTimestamp: "DateTime",
  dbFloat: "float",
  dbSerial: "int",
  dbBool: "bool",
  dbJson: "JsonNode",
}.toTable

The type returned by server maps to nim types directly, no intermediate types, just like:

  TextMessage* = ref object
    content*: string
    creation*: string
    author*: int
    name*: string

But how to replace nim type to client side bind type is a problem, it means that replacing string to kstring or cstring.

The first simple way:

common:
    when defined(js):
      type string = cstring  

This is too bad code exactly.

Other way I can think of:

  common:
    typemap:
      kstring = string
    when defined(js):
      type kstring = cstring
    else:
      type kstring = string

Define the type mapping by typemap in common section and replace string type with kstring when generating client code. If that's not so good, can you give me some advice?

huaxk commented 4 years ago

If allow user customize the type, dbInet can be directly converted to the IpAddress type In the fourm example.

import ormin, net
import ormin / ormin_sqlite
from sqlite3 import PStmt, column_bytes, column_text

template bindResult*(db: DbConn; s: PStmt; idx: int; dest: var IpAddress;
                     t: typedesc; name: string) =
  let src = column_text(s, idx.cint)
  dest = parseIpAddress($src)

importModel(DbBackend.sqlite, "forum_model")

static:
  dbTypeMap.add(dbInet, "IpAddress")

var db {.global.} = open("stuff", "", "", "")

let res = query:
  select antibot(ip, answer)

The return type of ip is IpAddress, not a string. This feature will improve Ormin's scalability and flexibility, users can customize Ormin as needed.