Compare commits

...

15 Commits
1.0.2 ... main

Author SHA1 Message Date
9d1cc4bbec Cache logger instance. 2025-01-20 06:39:02 -06:00
af44d48df1 Extract pagination logic into a common, exported function. Fix PG date parsing (again). 2025-01-10 20:25:49 -06:00
2030fd4490 Use namespaced_logging 1.x for logging (optionally). 2025-01-05 02:06:57 -06:00
0599d41061 Support Nim 2.x, compatibility with waterpark.
- Nim 2.x has moved the DB connectors outside the standard library to
  the `db_connector` package.
- Refactor the pooling implementation and macro expectations to use the
  `withConnection` name instead of `withConn`. This change allows a
  caller to use a [waterpark](https://github.com/guzba/waterpark) pool
  instance instead of the builtin pool instance. Waterpark provides
  better support for multi-threaded environments. The builtin pooling
  mechanism may be deprecated in favor of waterpark in the future.
- Add the `getModelIfItExists` generated proc to the list of standard
  procs we generate. This is a flavour of `getModel` that returns an
  `Option` instead of raising an exception when there is no model for
  the given id.
- Change `PaginationParams#orderBy` to accept a `seq[string]` to allow
  for sorting on multiple fields.
2025-01-03 07:55:05 -06:00
fb74d84cb7 Map names to db ident names for columns passed for ordering in paginated queries. 2023-08-09 09:16:10 -05:00
fbd20de71f Add createOrUpdateRecord and record method generators.
`createOrUpdateRecord` implements upsert: update an existing record if
it exists or create a new record if not. A new error `DbUpdateError` was
added to be raised when an existing record does exist but was not able
to be updated.
2023-08-09 09:13:12 -05:00
540d0d2f67 Fix missing import in pooling implementation. 2023-02-04 19:04:50 -06:00
a05555ee67 WIP - Initial stab at making it generic to support db_sqlite. 2022-11-03 16:38:14 -05:00
454fc8c47a Add support for pagination in functions returning multiple records. 2022-09-08 12:52:21 -05:00
004e3c19bb Correctly pluralize tables names in getAll and findWhere generated procedures. 2022-09-08 10:40:59 -05:00
4e0a173c76 Remove hidden dependency on timeutils (private repo). 2022-09-06 09:50:24 -05:00
bd5d8c98b8 Update README to match main fiber_orm docs. 2022-09-04 12:59:35 -05:00
7d45346bb6 Add generated documentations (for use with GitHub pages). 2022-09-03 21:32:22 -05:00
a3dbb0bbbc Flesh out documentation with a worked example. 2022-09-03 20:53:37 -05:00
9bf3c4f3ec Initial stab at better documentation. 2022-09-02 23:25:52 -05:00
18 changed files with 6699 additions and 159 deletions

10
Makefile Normal file
View File

@ -0,0 +1,10 @@
SOURCES=$(shell find src -type f)
build: $(shell find src -type f)
nimble build
docs: $(shell find src -type f)
nim doc --project --index:on --git.url:https://github.com/jdbernard/fiber-orm --outdir:docs src/fiber_orm
nim rst2html --outdir:docs README.rst
cp docs/fiber_orm.html docs/index.html
.PHONY: docs

277
README.rst Normal file
View File

@ -0,0 +1,277 @@
Fiber ORM
~~~~~~~~~
Lightweight ORM supporting the `Postgres`_ and `SQLite`_ databases in Nim.
It supports a simple, opinionated model mapper to generate SQL queries based
on Nim objects. It also includes a simple connection pooling implementation.
Fiber ORM is not intended to be a 100% all-cases-covered ORM that handles
every potential data access pattern one might wish to implement. It is best
thought of as a collection of common SQL generation patterns. It is intended
to cover 90% of the common queries and functions one might write when
implementing an SQL-based access layer. It is expected that there may be a
few more complicated queries that need to be implemented to handle specific
access patterns.
The simple mapping pattern provided by Fiber ORM also works well on top of
databases that encapsulate data access logic in SQL with, for example,
views.
.. _Postgres: https://nim-lang.org/docs/db_postgres.html
.. _SQLite: https://nim-lang.org/docs/db_sqlite.html
Basic Usage
===========
Consider a simple TODO list application that keeps track of TODO items as
well as time logged against those items.
Example DB Schema
-----------------
You might have a schema such as:
.. code-block:: SQL
create extension if not exists "pgcrypto";
create table todo_items columns (
id uuid not null primary key default gen_random_uuid(),
owner varchar not null,
summary varchar not null,
details varchar default null,
priority integer not null default 0,
related_todo_item_ids uuid[] not null default '{}'
);
create table time_entries columns (
id uuid not null primary key default gen_random_uuid(),
todo_item_id uuid not null references todo_items (id) on delete cascade,
start timestamp with timezone not null default current_timestamp,
stop timestamp with timezone default null,
);
Example Model Definitions
-------------------------
Models may be defined as:
.. code-block:: Nim
# models.nim
import std/[options, times]
import uuids
type
TodoItem* = object
id*: UUID
owner*: string
summary*: string
details*: Option[string]
priority*: int
relatedTodoItemIds*: seq[UUID]
TimeEntry* = object
id*: UUID
todoItemId*: Option[UUID]
start*: DateTime
stop*: Option[DateTime]
Example Fiber ORM Usage
-----------------------
Using Fiber ORM we can generate a data access layer with:
.. code-block:: Nim
# db.nim
import std/[options]
import db_connectors/db_postgres
import fiber_orm
import ./models.nim
type TodoDB* = DbConnPool
proc initDb*(connString: string): TodoDB =
result = fiber_orm.initPool(
connect = proc(): DbConn = open("", "", "", connString),
poolSize = 20,
hardCap = false)
generateProcsForModels(TodoDB, [TodoItem, TimeEntry])
generateLookup(TodoDB, TimeEntry, @["todoItemId"])
This will generate the following procedures:
.. code-block:: Nim
proc getTodoItem*(db: TodoDB, id: UUID): TodoItem;
proc getTodoItemIfItExists*(db: TodoDB, id: UUID): Option[TodoItem];
proc getAllTodoItems*(db: TodoDB): seq[TodoItem];
proc createTodoItem*(db: TodoDB, rec: TodoItem): TodoItem;
proc updateTodoItem*(db: TodoDB, rec: TodoItem): bool;
proc deleteTodoItem*(db: TodoDB, rec: TodoItem): bool;
proc deleteTodoItem*(db: TodoDB, id: UUID): bool;
proc findTodoItemsWhere*(db: TodoDB, whereClause: string,
values: varargs[string, dbFormat]): seq[TodoItem];
proc getTimeEntry*(db: TodoDB, id: UUID): TimeEntry;
proc getTimeEntryIfItExists*(db: TodoDB, id: UUID): Option[TimeEntry];
proc getAllTimeEntries*(db: TodoDB): seq[TimeEntry];
proc createTimeEntry*(db: TodoDB, rec: TimeEntry): TimeEntry;
proc updateTimeEntry*(db: TodoDB, rec: TimeEntry): bool;
proc deleteTimeEntry*(db: TodoDB, rec: TimeEntry): bool;
proc deleteTimeEntry*(db: TodoDB, id: UUID): bool;
proc findTimeEntriesWhere*(db: TodoDB, whereClause: string,
values: varargs[string, dbFormat]): seq[TimeEntry];
proc findTimeEntriesByTodoItemId(db: TodoDB, todoItemId: UUID): seq[TimeEntry];
Object-Relational Modeling
==========================
Model Class
-----------
Fiber ORM uses simple Nim `object`s and `ref object`s as model classes.
Fiber ORM expects there to be one table for each model class.
Name Mapping
````````````
Fiber ORM uses `snake_case` for database identifiers (column names, table
names, etc.) and `camelCase` for Nim identifiers. We automatically convert
model names to and from table names (`TodoItem` <-> `todo_items`), as well
as column names (`userId` <-> `user_id`).
Notice that table names are automatically pluralized from model class names.
In the above example, you have:
=========== ================
Model Class Table Name
=========== ================
TodoItem todo_items
TimeEntry time_entries
=========== ================
Because Nim is style-insensitive, you can generall refer to model classes
and fields using `snake_case`, `camelCase`, or `PascalCase` in your code and
expect Fiber ORM to be able to map the names to DB identifier names properly
(though FiberORM will always use `camelCase` internally).
See the `identNameToDb`_, `dbNameToIdent`_, `tableName`_ and `dbFormat`_
procedures in the `fiber_orm/util`_ module for details.
.. _identNameToDb: fiber_orm/util.html#identNameToDb,string
.. _dbNameToIdent: fiber_orm/util.html#dbNameToIdent,string
.. _tableName: fiber_orm/util.html#tableName,type
.. _dbFormat: fiber_orm/util.html#dbFormat,DateTime
.. _util: fiber_orm/util.html
ID Field
````````
Fiber ORM expects every model class to have a field named `id`, with a
corresponding `id` column in the model table. This field must be either a
`string`, `integer`, or `UUID`_.
When creating a new record the `id` field will be omitted if it is empty
(`Option.isNone`_, `UUID.isZero`_, value of `0`, or only whitespace). This
is intended to allow for cases like the example where the database may
generate an ID when a new record is inserted. If a non-zero value is
provided, the create call will include the `id` field in the `INSERT` query.
For example, to allow the database to create the id:
.. code-block:: Nim
let item = TodoItem(
owner: "John Mann",
summary: "Create a grocery list.",
details: none[string](),
priority: 0,
relatedTodoItemIds: @[])
let itemWithId = db.createTodoItem(item)
echo $itemWithId.id # generated in the database
And to create it in code:
.. code-block:: Nim
import uuids
let item = TodoItem(
id: genUUID(),
owner: "John Mann",
summary: "Create a grocery list.",
details: none[string](),
priority: 0,
relatedTodoItemIds: @[])
let itemInDb = db.createTodoItem(item)
echo $itemInDb.id # will be the same as what was provided
.. _Option.isNone: https://nim-lang.org/docs/options.html#isNone,Option[T]
.. _UUID.isZero: https://github.com/pragmagic/uuids/blob/8cb8720b567c6bcb261bd1c0f7491bdb5209ad06/uuids.nim#L72
Supported Data Types
--------------------
The following Nim data types are supported by Fiber ORM:
=============== ====================== =================
Nim Type Postgres Type SQLite Type
=============== ====================== =================
`string` `varchar`_
`int` `integer`_
`float` `double`_
`bool` `boolean`_
`DateTime`_ `timestamp`_
`seq[]` `array`_
`UUID`_ `uuid (pg)`_
`Option`_ *allows* `NULL` [#f1]_
`JsonNode`_ `jsonb`_
=============== ====================== =================
.. [#f1] Note that this implies that all `NULL`-able fields should be typed
as optional using `Option[fieldType]`. Conversely, any fields with
non-optional types should also be constrained to be `NOT NULL` in
the database schema.
.. _DateTime: https://nim-lang.org/docs/times.html#DateTime
.. _UUID: https://github.com/pragmagic/uuids
.. _Option: https://nim-lang.org/docs/options.html#Option
.. _JsonNode: https://nim-lang.org/docs/json.html#JsonNode
.. _varchar: https://www.postgresql.org/docs/current/datatype-character.html
.. _integer: https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-INT
.. _double: https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-FLOAT
.. _boolean: https://www.postgresql.org/docs/current/datatype-boolean.html
.. _timestamp: https://www.postgresql.org/docs/current/datatype-datetime.html
.. _array: https://www.postgresql.org/docs/current/arrays.html
.. _uuid (pg): https://www.postgresql.org/docs/current/datatype-uuid.html
.. _jsonb: https://www.postgresql.org/docs/current/datatype-json.html
Database Object
===============
Many of the Fiber ORM macros expect a database object type to be passed.
In the example above the `pool.DbConnPool`_ object is used as database
object type (aliased as `TodoDB`). This is the intended usage pattern, but
anything can be passed as the database object type so long as there is a
defined `withConn` template that provides an injected `conn: DbConn` object
to the provided statement body.
For example, a valid database object implementation that opens a new
connection for every request might look like this:
.. code-block:: Nim
import std/db_postgres
type TodoDB* = object
connString: string
template withConn*(db: TodoDB, stmt: untyped): untyped =
let conn {.inject.} = open("", "", "", db.connString)
try: stmt
finally: close(conn)
.. _pool.DbConnPool: fiber_orm/pool.html#DbConnPool

202
docs/README.html Normal file
View File

@ -0,0 +1,202 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>README</title>
<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
if (toggleSwitch !== null) {
toggleSwitch.addEventListener('change', switchTheme, false);
}
var currentTheme = localStorage.getItem('theme');
if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) {
currentTheme = 'dark';
}
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark' && toggleSwitch !== null) {
toggleSwitch.checked = true;
}
}
}
window.addEventListener('DOMContentLoaded', main);
</script>
</head>
<body>
<div class="document" id="documentId">
<div class="container">
<h1 class="title">README</h1>
<h1 id="fiber-orm">Fiber ORM</h1><p>Lightweight ORM supporting the <a class="reference external" href="https://nim-lang.org/docs/db_postgres.html">Postgres</a> and <a class="reference external" href="https://nim-lang.org/docs/db_sqlite.html">SQLite</a> databases in Nim. It supports a simple, opinionated model mapper to generate SQL queries based on Nim objects. It also includes a simple connection pooling implementation.</p>
<p>Fiber ORM is not intended to be a 100% all-cases-covered ORM that handles every potential data access pattern one might wish to implement. It is best thought of as a collection of common SQL generation patterns. It is intended to cover 90% of the common queries and functions one might write when implementing an SQL-based access layer. It is expected that there may be a few more complicated queries that need to be implemented to handle specific access patterns.</p>
<p>The simple mapping pattern provided by Fiber ORM also works well on top of databases that encapsulate data access logic in SQL with, for example, views.</p>
<h2 id="basic-usage">Basic Usage</h2><p>Consider a simple TODO list application that keeps track of TODO items as well as time logged against those items. You might have a schema such as:</p>
<pre class="listing">create extension if not exists &quot;pgcrypto&quot;;
create table todo_items columns (
id uuid not null primary key default gen_random_uuid(),
owner varchar not null,
summary varchar not null,
details varchar default null,
priority integer not null default 0,
related_todo_item_ids uuid[] not null default '{}'
);
create table time_entries columns (
id uuid not null primary key default gen_random_uuid(),
todo_item_id uuid not null references todo_items (id) on delete cascade,
start timestamp with timezone not null default current_timestamp,
stop timestamp with timezone default null,
);</pre><p>Models may be defined as:</p>
<pre class="listing"><span class="Comment"># models.nim</span>
<span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">options</span><span class="Punctuation">,</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">times</span>
<span class="Keyword">import</span> <span class="Identifier">uuids</span>
<span class="Keyword">type</span>
<span class="Identifier">TodoItem</span><span class="Operator">*</span> <span class="Operator">=</span> <span class="Keyword">object</span>
<span class="Identifier">id</span><span class="Operator">*:</span> <span class="Identifier">UUID</span>
<span class="Identifier">owner</span><span class="Operator">*:</span> <span class="Identifier">string</span>
<span class="Identifier">summary</span><span class="Operator">*:</span> <span class="Identifier">string</span>
<span class="Identifier">details</span><span class="Operator">*:</span> <span class="Identifier">Option</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">]</span>
<span class="Identifier">priority</span><span class="Operator">*:</span> <span class="Identifier">int</span>
<span class="Identifier">relatedTodoItemIds</span><span class="Operator">*:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">UUID</span><span class="Punctuation">]</span>
<span class="Identifier">TimeEntry</span><span class="Operator">*</span> <span class="Operator">=</span> <span class="Keyword">object</span>
<span class="Identifier">id</span><span class="Operator">*:</span> <span class="Identifier">UUID</span>
<span class="Identifier">todoItemId</span><span class="Operator">*:</span> <span class="Identifier">Option</span><span class="Punctuation">[</span><span class="Identifier">UUID</span><span class="Punctuation">]</span>
<span class="Identifier">start</span><span class="Operator">*:</span> <span class="Identifier">DateTime</span>
<span class="Identifier">stop</span><span class="Operator">*:</span> <span class="Identifier">Option</span><span class="Punctuation">[</span><span class="Identifier">DateTime</span><span class="Punctuation">]</span></pre><p>Using Fiber ORM we can generate a data access layer with:</p>
<pre class="listing"><span class="Comment"># db.nim</span>
<span class="Keyword">import</span> <span class="Identifier">fiber_orm</span>
<span class="Keyword">import</span> <span class="Operator">./</span><span class="Identifier">models</span><span class="Operator">.</span><span class="Identifier">nim</span>
<span class="Keyword">type</span> <span class="Identifier">TodoDB</span><span class="Operator">*</span> <span class="Operator">=</span> <span class="Identifier">DbConnPool</span>
<span class="Keyword">proc</span> <span class="Identifier">initDb</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">connString</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span> <span class="Operator">=</span>
<span class="Identifier">fiber_orm</span><span class="Operator">.</span><span class="Identifier">initPool</span><span class="Punctuation">(</span><span class="Identifier">connect</span> <span class="Operator">=</span>
<span class="Keyword">proc</span><span class="Punctuation">(</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">DbConn</span> <span class="Operator">=</span> <span class="Identifier">open</span><span class="Punctuation">(</span><span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="Identifier">connString</span><span class="Punctuation">)</span><span class="Punctuation">)</span>
<span class="Identifier">generateProcsForModels</span><span class="Punctuation">(</span><span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Punctuation">[</span><span class="Identifier">TodoItem</span><span class="Punctuation">,</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">]</span><span class="Punctuation">)</span>
<span class="Identifier">generateLookup</span><span class="Punctuation">(</span><span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">,</span> <span class="Operator">@</span><span class="Punctuation">[</span><span class="StringLit">&quot;todoItemId&quot;</span><span class="Punctuation">]</span><span class="Punctuation">)</span></pre><p>This will generate the following procedures:</p>
<pre class="listing"><span class="Keyword">proc</span> <span class="Identifier">getTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">getAllTodoItems</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TodoItem</span><span class="Punctuation">]</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">createTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">updateTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">findTodoItemsWhere</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">whereClause</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">,</span>
<span class="Identifier">values</span><span class="Punctuation">:</span> <span class="Identifier">varargs</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">,</span> <span class="Identifier">dbFormat</span><span class="Punctuation">]</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TodoItem</span><span class="Punctuation">]</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">getTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">getAllTimeEntries</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TimeEntry</span><span class="Punctuation">]</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">createTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">updateTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">findTimeEntriesWhere</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">whereClause</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">,</span>
<span class="Identifier">values</span><span class="Punctuation">:</span> <span class="Identifier">varargs</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">,</span> <span class="Identifier">dbFormat</span><span class="Punctuation">]</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TimeEntry</span><span class="Punctuation">]</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">findTimeEntriesByTodoItemId</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">todoItemId</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TimeEntry</span><span class="Punctuation">]</span><span class="Punctuation">;</span></pre>
<h2 id="objectminusrelational-modeling">Object-Relational Modeling</h2>
<h3 id="model-class">Model Class</h3><p>Fiber ORM uses simple Nim <tt class="docutils literal"><span class="pre">object</span></tt>s and <tt class="docutils literal"><span class="pre">ref object</span></tt>s as model classes. Fiber ORM expects there to be one table for each model class.</p>
<h4 id="name-mapping">Name Mapping</h4><p>Fiber ORM uses <tt class="docutils literal"><span class="pre">snake_case</span></tt> for database identifiers (column names, table names, etc.) and <tt class="docutils literal"><span class="pre">camelCase</span></tt> for Nim identifiers. We automatically convert model names to and from table names (<tt class="docutils literal"><span class="pre">TodoItem</span></tt> &lt;-&gt; <tt class="docutils literal"><span class="pre">todo_items</span></tt>), as well as column names (<tt class="docutils literal"><span class="pre">userId</span></tt> &lt;-&gt; <tt class="docutils literal"><span class="pre">user_id</span></tt>).</p>
<p>Notice that table names are automatically pluralized from model class names. In the above example, you have:</p>
<table border="1" class="docutils"><tr><th>Model Class</th><th>Table Name</th></tr>
<tr><td>TodoItem</td><td>todo_items</td></tr>
<tr><td>TimeEntry</td><td>time_entries</td></tr>
</table><p>Because Nim is style-insensitive, you can generall refer to model classes and fields using <tt class="docutils literal"><span class="pre">snake_case</span></tt>, <tt class="docutils literal"><span class="pre">camelCase</span></tt>, or <tt class="docutils literal"><span class="pre">PascalCase</span></tt> in your code and expect Fiber ORM to be able to map the names to DB identifier names properly (though FiberORM will always use <tt class="docutils literal"><span class="pre">camelCase</span></tt> internally).</p>
<p>See the <a class="reference external" href="fiber_orm/util.html#identNameToDb,string">identNameToDb</a>, <a class="reference external" href="fiber_orm/util.html#dbNameToIdent,string">dbNameToIdent</a>, <a class="reference external" href="fiber_orm/util.html#tableName,type">tableName</a> and <a class="reference external" href="fiber_orm/util.html#dbFormat,DateTime">dbFormat</a> procedures in the <a class="reference internal" href="#fiber">fiber</a> module for details.</p>
<h4 id="id-field">ID Field</h4><p>Fiber ORM expects every model class to have a field named <tt class="docutils literal"><span class="pre">id</span></tt>, with a corresponding <tt class="docutils literal"><span class="pre">id</span></tt> column in the model table. This field must be either a <tt class="docutils literal"><span class="pre">string</span></tt>, <tt class="docutils literal"><span class="pre">integer</span></tt>, or <a class="reference external" href="https://github.com/pragmagic/uuids">UUID</a>.</p>
<p>When creating a new record the <tt class="docutils literal"><span class="pre">id</span></tt> field will be omitted if it is empty (<a class="reference external" href="https://nim-lang.org/docs/options.html#isNone,Option[T]">Option.isNone</a>, <a class="reference external" href="https://github.com/pragmagic/uuids/blob/8cb8720b567c6bcb261bd1c0f7491bdb5209ad06/uuids.nim#L72">UUID.isZero</a>, value of <tt class="docutils literal"><span class="pre">0</span></tt>, or only whitespace). This is intended to allow for cases like the example where the database may generate an ID when a new record is inserted. If a non-zero value is provided, the create call will include the <tt class="docutils literal"><span class="pre">id</span></tt> field in the <tt class="docutils literal"><span class="pre">INSERT</span></tt> query.</p>
<h3 id="supported-data-types">Supported Data Types</h3><p>The following Nim data types are supported by Fiber ORM:</p>
<table border="1" class="docutils"><tr><th>Nim Type</th><th>Postgres Type</th><th>SQLite Type</th></tr>
<tr><td><tt class="docutils literal"><span class="pre">string</span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-character.html">varchar</a></td><td></td></tr>
<tr><td><tt class="docutils literal"><span class="pre">int</span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-INT">integer</a></td><td></td></tr>
<tr><td><tt class="docutils literal"><span class="pre">float</span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-FLOAT">double</a></td><td></td></tr>
<tr><td><tt class="docutils literal"><span class="pre">bool</span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-boolean.html">boolean</a></td><td></td></tr>
<tr><td><a class="reference external" href="https://nim-lang.org/docs/times.html#DateTime">DateTime</a></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-datetime.html">timestamp</a></td><td></td></tr>
<tr><td><tt class="docutils literal"><span class="pre">seq[]</span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/arrays.html">array</a></td><td></td></tr>
<tr><td><a class="reference external" href="https://github.com/pragmagic/uuids">UUID</a></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-uuid.html">uuid (pg)</a></td><td></td></tr>
<tr><td><a class="reference external" href="https://nim-lang.org/docs/options.html#Option">Option</a></td><td><em>allows</em> <tt class="docutils literal"><span class="pre">NULL</span></tt> <sup><strong><a class="reference internal" href="#footnote-f1">[1]</a></strong></sup></td><td></td></tr>
<tr><td><a class="reference external" href="https://nim-lang.org/docs/json.html#JsonNode">JsonNode</a></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-json.html">jsonb</a></td><td></td></tr>
</table><hr class="footnote"><div class="footnote-group">
<div id="footnote-f1"><div class="footnote-label"><sup><strong><a href="#footnote-f1">[1]</a></strong></sup></div> &ensp; Note that this implies that all <tt class="docutils literal"><span class="pre">NULL</span></tt>-able fields should be typed as optional using <tt class="docutils literal"><span class="pre">Option[fieldType]</span></tt>. Conversely, any fields with non-optional types should also be constrained to be <tt class="docutils literal"><span class="pre">NOT NULL</span></tt> in the database schema.
</div>
</div>
<h2 id="database-object">Database Object</h2><p>Many of the Fiber ORM macros expect a database object type to be passed. In the example above the <a class="reference internal" href="#fiber">fiber</a> object is used as database object type (aliased as <tt class="docutils literal"><span class="pre">TodoDB</span></tt>). This is the intended usage pattern, but anything can be passed as the database object type so long as there is a defined <tt class="docutils literal"><span class="pre">withConn</span></tt> template that provides an injected <tt class="docutils literal"><span class="pre">conn: DbConn</span></tt> object to the provided statement body.</p>
<p>For example, a valid database object implementation that opens a new connection for every request might look like this:</p>
<pre class="listing"><span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">db_postgres</span>
<span class="Keyword">type</span> <span class="Identifier">TodoDB</span><span class="Operator">*</span> <span class="Operator">=</span> <span class="Keyword">object</span>
<span class="Identifier">connString</span><span class="Punctuation">:</span> <span class="Identifier">string</span>
<span class="Keyword">template</span> <span class="Identifier">withConn</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">stmt</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span> <span class="Operator">=</span>
<span class="Keyword">let</span> <span class="Identifier">conn</span> <span class="Punctuation">{</span><span class="Operator">.</span><span class="Identifier">inject</span><span class="Operator">.</span><span class="Punctuation">}</span> <span class="Operator">=</span> <span class="Identifier">open</span><span class="Punctuation">(</span><span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="Identifier">db</span><span class="Operator">.</span><span class="Identifier">connString</span><span class="Punctuation">)</span>
<span class="Keyword">try</span><span class="Punctuation">:</span> <span class="Identifier">stmt</span>
<span class="Keyword">finally</span><span class="Punctuation">:</span> <span class="Identifier">close</span><span class="Punctuation">(</span><span class="Identifier">conn</span><span class="Punctuation">)</span></pre>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2022-09-04 02:31:20 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

2041
docs/dochack.js Normal file

File diff suppressed because it is too large Load Diff

619
docs/fiber_orm.html Normal file
View File

@ -0,0 +1,619 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>src/fiber_orm</title>
<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
if (toggleSwitch !== null) {
toggleSwitch.addEventListener('change', switchTheme, false);
}
var currentTheme = localStorage.getItem('theme');
if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) {
currentTheme = 'dark';
}
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark' && toggleSwitch !== null) {
toggleSwitch.checked = true;
}
}
}
window.addEventListener('DOMContentLoaded', main);
</script>
</head>
<body>
<div class="document" id="documentId">
<div class="container">
<h1 class="title">src/fiber_orm</h1>
<div class="row">
<div class="three columns">
<div class="theme-switch-wrapper">
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
&nbsp;&nbsp;&nbsp; <em>Dark Mode</em>
</div>
<div id="global-links">
<ul class="simple">
<li>
<a href="theindex.html">Index</a>
</li>
</ul>
</div>
<div id="searchInputDiv">
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
<ul class="simple simple-toc" id="toc-list">
<li><a class="reference" id="basic-usage_toc" href="#basic-usage">Basic Usage</a></li>
<ul class="simple"><li><a class="reference" id="basic-usage-example-db-schema_toc" href="#basic-usage-example-db-schema">Example DB Schema</a></li>
<li><a class="reference" id="basic-usage-example-model-definitions_toc" href="#basic-usage-example-model-definitions">Example Model Definitions</a></li>
<li><a class="reference" id="basic-usage-example-fiber-orm-usage_toc" href="#basic-usage-example-fiber-orm-usage">Example Fiber ORM Usage</a></li>
</ul><li><a class="reference" id="objectminusrelational-modeling_toc" href="#objectminusrelational-modeling">Object-Relational Modeling</a></li>
<ul class="simple"><li><a class="reference" id="objectminusrelational-modeling-model-class_toc" href="#objectminusrelational-modeling-model-class">Model Class</a></li>
<ul class="simple"><li><a class="reference" id="model-class-name-mapping_toc" href="#model-class-name-mapping">Name Mapping</a></li>
<li><a class="reference" id="model-class-id-field_toc" href="#model-class-id-field">ID Field</a></li>
</ul><li><a class="reference" id="objectminusrelational-modeling-supported-data-types_toc" href="#objectminusrelational-modeling-supported-data-types">Supported Data Types</a></li>
</ul><li><a class="reference" id="database-object_toc" href="#database-object">Database Object</a></li>
<li><a class="reference" id="see-also_toc" href="#see-also">See Also</a></li>
<li>
<a class="reference reference-toplevel" href="#6" id="56">Imports</a>
<ul class="simple simple-toc-section">
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#7" id="57">Types</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#NotFoundError"
title="NotFoundError = object of CatchableError">NotFoundError</a></li>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#12" id="62">Procs</a>
<ul class="simple simple-toc-section">
<ul class="simple nested-toc-section">createRecord
<li><a class="reference" href="#createRecord%2CDbConn%2CT"
title="createRecord[T](db: DbConn; rec: T): T">createRecord[T](db: DbConn; rec: T): T</a></li>
</ul>
<ul class="simple nested-toc-section">deleteRecord
<li><a class="reference" href="#deleteRecord%2CDbConn%2CT"
title="deleteRecord[T](db: DbConn; rec: T): bool">deleteRecord[T](db: DbConn; rec: T): bool</a></li>
</ul>
<ul class="simple nested-toc-section">initPool
<li><a class="reference" href="#initPool%2Cproc%29%2Cint%2Cstring"
title="initPool(connect: proc (): DbConn; poolSize = 10; hardCap = false;
healthCheckQuery = &quot;SELECT \'true\' AS alive&quot;): DbConnPool">initPool(connect: proc (): DbConn; poolSize = 10; hardCap = false;
healthCheckQuery = &quot;SELECT \'true\' AS alive&quot;): DbConnPool</a></li>
</ul>
<ul class="simple nested-toc-section">updateRecord
<li><a class="reference" href="#updateRecord%2CDbConn%2CT"
title="updateRecord[T](db: DbConn; rec: T): bool">updateRecord[T](db: DbConn; rec: T): bool</a></li>
</ul>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#17" id="67">Macros</a>
<ul class="simple simple-toc-section">
<ul class="simple nested-toc-section">generateLookup
<li><a class="reference" href="#generateLookup.m%2Ctype%2Ctype%2Cseq%5Bstring%5D"
title="generateLookup(dbType: type; modelType: type; fields: seq[string]): untyped">generateLookup(dbType: type; modelType: type; fields: seq[string]): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">generateProcsForFieldLookups
<li><a class="reference" href="#generateProcsForFieldLookups.m%2Ctype%2CopenArray%5Btuple%5Btype%2Cseq%5Bstring%5D%5D%5D"
title="generateProcsForFieldLookups(dbType: type; modelsAndFields: openArray[
tuple[t: type, fields: seq[string]]]): untyped">generateProcsForFieldLookups(dbType: type; modelsAndFields: openArray[
tuple[t: type, fields: seq[string]]]): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">generateProcsForModels
<li><a class="reference" href="#generateProcsForModels.m%2Ctype%2CopenArray%5Btype%5D"
title="generateProcsForModels(dbType: type; modelTypes: openArray[type]): untyped">generateProcsForModels(dbType: type; modelTypes: openArray[type]): untyped</a></li>
</ul>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#18" id="68">Templates</a>
<ul class="simple simple-toc-section">
<ul class="simple nested-toc-section">deleteRecord
<li><a class="reference" href="#deleteRecord.t%2CDbConn%2Ctype%2Ctyped"
title="deleteRecord(db: DbConn; modelType: type; id: typed): untyped">deleteRecord(db: DbConn; modelType: type; id: typed): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">findRecordsBy
<li><a class="reference" href="#findRecordsBy.t%2CDbConn%2Ctype%2Cseq%5Btuple%5Bstring%2Cstring%5D%5D"
title="findRecordsBy(db: DbConn; modelType: type;
lookups: seq[tuple[field: string, value: string]]): untyped">findRecordsBy(db: DbConn; modelType: type;
lookups: seq[tuple[field: string, value: string]]): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">findRecordsWhere
<li><a class="reference" href="#findRecordsWhere.t%2CDbConn%2Ctype%2Cstring%2Cvarargs%5Bstring%2CdbFormat%5D"
title="findRecordsWhere(db: DbConn; modelType: type; whereClause: string;
values: varargs[string, dbFormat]): untyped">findRecordsWhere(db: DbConn; modelType: type; whereClause: string;
values: varargs[string, dbFormat]): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">getAllRecords
<li><a class="reference" href="#getAllRecords.t%2CDbConn%2Ctype"
title="getAllRecords(db: DbConn; modelType: type): untyped">getAllRecords(db: DbConn; modelType: type): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">getRecord
<li><a class="reference" href="#getRecord.t%2CDbConn%2Ctype%2Ctyped"
title="getRecord(db: DbConn; modelType: type; id: typed): untyped">getRecord(db: DbConn; modelType: type; id: typed): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">inTransaction
<li><a class="reference" href="#inTransaction.t%2CDbConnPool%2Cuntyped"
title="inTransaction(db: DbConnPool; body: untyped)">inTransaction(db: DbConnPool; body: untyped)</a></li>
</ul>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#19" id="69">Exports</a>
<ul class="simple simple-toc-section">
</ul>
</li>
</ul>
</div>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L1"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L1" class="link-seesrc" target="_blank" >Edit</a>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc"><p>Lightweight ORM supporting the <a class="reference external" href="https://nim-lang.org/docs/db_postgres.html">Postgres</a> and <a class="reference external" href="https://nim-lang.org/docs/db_sqlite.html">SQLite</a> databases in Nim. It supports a simple, opinionated model mapper to generate SQL queries based on Nim objects. It also includes a simple connection pooling implementation.</p>
<p>Fiber ORM is not intended to be a 100% all-cases-covered ORM that handles every potential data access pattern one might wish to implement. It is best thought of as a collection of common SQL generation patterns. It is intended to cover 90% of the common queries and functions one might write when implementing an SQL-based access layer. It is expected that there may be a few more complicated queries that need to be implemented to handle specific access patterns.</p>
<p>The simple mapping pattern provided by Fiber ORM also works well on top of databases that encapsulate data access logic in SQL with, for example, views.</p>
<h1><a class="toc-backref" id="basic-usage" href="#basic-usage">Basic Usage</a></h1><p>Consider a simple TODO list application that keeps track of TODO items as well as time logged against those items.</p>
<h2><a class="toc-backref" id="basic-usage-example-db-schema" href="#basic-usage-example-db-schema">Example DB Schema</a></h2><p>You might have a schema such as:</p>
<pre class="listing">create extension if not exists &quot;pgcrypto&quot;;
create table todo_items columns (
id uuid not null primary key default gen_random_uuid(),
owner varchar not null,
summary varchar not null,
details varchar default null,
priority integer not null default 0,
related_todo_item_ids uuid[] not null default '{}'
);
create table time_entries columns (
id uuid not null primary key default gen_random_uuid(),
todo_item_id uuid not null references todo_items (id) on delete cascade,
start timestamp with timezone not null default current_timestamp,
stop timestamp with timezone default null,
);</pre>
<h2><a class="toc-backref" id="basic-usage-example-model-definitions" href="#basic-usage-example-model-definitions">Example Model Definitions</a></h2><p>Models may be defined as:</p>
<pre class="listing"><span class="Comment"># models.nim</span>
<span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">options</span><span class="Punctuation">,</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">times</span>
<span class="Keyword">import</span> <span class="Identifier">uuids</span>
<span class="Keyword">type</span>
<span class="Identifier">TodoItem</span><span class="Operator">*</span> <span class="Operator">=</span> <span class="Keyword">object</span>
<span class="Identifier">id</span><span class="Operator">*:</span> <span class="Identifier">UUID</span>
<span class="Identifier">owner</span><span class="Operator">*:</span> <span class="Identifier">string</span>
<span class="Identifier">summary</span><span class="Operator">*:</span> <span class="Identifier">string</span>
<span class="Identifier">details</span><span class="Operator">*:</span> <span class="Identifier">Option</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">]</span>
<span class="Identifier">priority</span><span class="Operator">*:</span> <span class="Identifier">int</span>
<span class="Identifier">relatedTodoItemIds</span><span class="Operator">*:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">UUID</span><span class="Punctuation">]</span>
<span class="Identifier">TimeEntry</span><span class="Operator">*</span> <span class="Operator">=</span> <span class="Keyword">object</span>
<span class="Identifier">id</span><span class="Operator">*:</span> <span class="Identifier">UUID</span>
<span class="Identifier">todoItemId</span><span class="Operator">*:</span> <span class="Identifier">Option</span><span class="Punctuation">[</span><span class="Identifier">UUID</span><span class="Punctuation">]</span>
<span class="Identifier">start</span><span class="Operator">*:</span> <span class="Identifier">DateTime</span>
<span class="Identifier">stop</span><span class="Operator">*:</span> <span class="Identifier">Option</span><span class="Punctuation">[</span><span class="Identifier">DateTime</span><span class="Punctuation">]</span></pre>
<h2><a class="toc-backref" id="basic-usage-example-fiber-orm-usage" href="#basic-usage-example-fiber-orm-usage">Example Fiber ORM Usage</a></h2><p>Using Fiber ORM we can generate a data access layer with:</p>
<pre class="listing"><span class="Comment"># db.nim</span>
<span class="Keyword">import</span> <span class="Identifier">fiber_orm</span>
<span class="Keyword">import</span> <span class="Operator">./</span><span class="Identifier">models</span><span class="Operator">.</span><span class="Identifier">nim</span>
<span class="Keyword">type</span> <span class="Identifier">TodoDB</span><span class="Operator">*</span> <span class="Operator">=</span> <span class="Identifier">DbConnPool</span>
<span class="Keyword">proc</span> <span class="Identifier">initDb</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">connString</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span> <span class="Operator">=</span>
<span class="Identifier">result</span> <span class="Operator">=</span> <span class="Identifier">fiber_orm</span><span class="Operator">.</span><span class="Identifier">initPool</span><span class="Punctuation">(</span>
<span class="Identifier">connect</span> <span class="Operator">=</span> <span class="Keyword">proc</span><span class="Punctuation">(</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">DbConn</span> <span class="Operator">=</span> <span class="Identifier">open</span><span class="Punctuation">(</span><span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="Identifier">connString</span><span class="Punctuation">)</span><span class="Punctuation">,</span>
<span class="Identifier">poolSize</span> <span class="Operator">=</span> <span class="DecNumber">20</span><span class="Punctuation">,</span>
<span class="Identifier">hardCap</span> <span class="Operator">=</span> <span class="Identifier">false</span><span class="Punctuation">)</span>
<span class="Identifier">generateProcsForModels</span><span class="Punctuation">(</span><span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Punctuation">[</span><span class="Identifier">TodoItem</span><span class="Punctuation">,</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">]</span><span class="Punctuation">)</span>
<span class="Identifier">generateLookup</span><span class="Punctuation">(</span><span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">,</span> <span class="Operator">@</span><span class="Punctuation">[</span><span class="StringLit">&quot;todoItemId&quot;</span><span class="Punctuation">]</span><span class="Punctuation">)</span></pre><p>This will generate the following procedures:</p>
<pre class="listing"><span class="Keyword">proc</span> <span class="Identifier">getTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">getAllTodoItems</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TodoItem</span><span class="Punctuation">]</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">createTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">updateTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">findTodoItemsWhere</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">whereClause</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">,</span>
<span class="Identifier">values</span><span class="Punctuation">:</span> <span class="Identifier">varargs</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">,</span> <span class="Identifier">dbFormat</span><span class="Punctuation">]</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TodoItem</span><span class="Punctuation">]</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">getTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">getAllTimeEntries</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TimeEntry</span><span class="Punctuation">]</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">createTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">updateTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">findTimeEntriesWhere</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">whereClause</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">,</span>
<span class="Identifier">values</span><span class="Punctuation">:</span> <span class="Identifier">varargs</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">,</span> <span class="Identifier">dbFormat</span><span class="Punctuation">]</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TimeEntry</span><span class="Punctuation">]</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">findTimeEntriesByTodoItemId</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">todoItemId</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TimeEntry</span><span class="Punctuation">]</span><span class="Punctuation">;</span></pre>
<h1><a class="toc-backref" id="objectminusrelational-modeling" href="#objectminusrelational-modeling">Object-Relational Modeling</a></h1>
<h2><a class="toc-backref" id="objectminusrelational-modeling-model-class" href="#objectminusrelational-modeling-model-class">Model Class</a></h2><p>Fiber ORM uses simple Nim <tt class="docutils literal"><span class="pre"><span class="Keyword">object</span></span></tt>s and <tt class="docutils literal"><span class="pre"><span class="Keyword">ref</span> <span class="Keyword">object</span></span></tt>s as model classes. Fiber ORM expects there to be one table for each model class.</p>
<h3><a class="toc-backref" id="model-class-name-mapping" href="#model-class-name-mapping">Name Mapping</a></h3><p>Fiber ORM uses <tt class="docutils literal"><span class="pre"><span class="Identifier">snake_case</span></span></tt> for database identifiers (column names, table names, etc.) and <tt class="docutils literal"><span class="pre"><span class="Identifier">camelCase</span></span></tt> for Nim identifiers. We automatically convert model names to and from table names (<tt class="docutils literal"><span class="pre"><span class="Identifier">TodoItem</span></span></tt> &lt;-&gt; <tt class="docutils literal"><span class="pre"><span class="Identifier">todo_items</span></span></tt>), as well as column names (<tt class="docutils literal"><span class="pre"><span class="Identifier">userId</span></span></tt> &lt;-&gt; <tt class="docutils literal"><span class="pre"><span class="Identifier">user_id</span></span></tt>).</p>
<p>Notice that table names are automatically pluralized from model class names. In the above example, you have:</p>
<table border="1" class="docutils"><tr><th>Model Class</th><th>Table Name</th></tr>
<tr><td>TodoItem</td><td>todo_items</td></tr>
<tr><td>TimeEntry</td><td>time_entries</td></tr>
</table><p>Because Nim is style-insensitive, you can generall refer to model classes and fields using <tt class="docutils literal"><span class="pre"><span class="Identifier">snake_case</span></span></tt>, <tt class="docutils literal"><span class="pre"><span class="Identifier">camelCase</span></span></tt>, or <tt class="docutils literal"><span class="pre"><span class="Identifier">PascalCase</span></span></tt> in your code and expect Fiber ORM to be able to map the names to DB identifier names properly (though FiberORM will always use <tt class="docutils literal"><span class="pre"><span class="Identifier">camelCase</span></span></tt> internally).</p>
<p>See the <a class="reference external" href="fiber_orm/util.html#identNameToDb,string">identNameToDb</a>, <a class="reference external" href="fiber_orm/util.html#dbNameToIdent,string">dbNameToIdent</a>, <a class="reference external" href="fiber_orm/util.html#tableName,type">tableName</a> and <a class="reference external" href="fiber_orm/util.html#dbFormat,DateTime">dbFormat</a> procedures in the <a class="reference internal" href="#fiber">fiber</a> module for details.</p>
<h3><a class="toc-backref" id="model-class-id-field" href="#model-class-id-field">ID Field</a></h3><p>Fiber ORM expects every model class to have a field named <tt class="docutils literal"><span class="pre"><span class="Identifier">id</span></span></tt>, with a corresponding <tt class="docutils literal"><span class="pre"><span class="Identifier">id</span></span></tt> column in the model table. This field must be either a <tt class="docutils literal"><span class="pre"><span class="Identifier">string</span></span></tt>, <tt class="docutils literal"><span class="pre"><span class="Identifier">integer</span></span></tt>, or <a class="reference external" href="https://github.com/pragmagic/uuids">UUID</a>.</p>
<p>When creating a new record the <tt class="docutils literal"><span class="pre"><span class="Identifier">id</span></span></tt> field will be omitted if it is empty (<a class="reference external" href="https://nim-lang.org/docs/options.html#isNone,Option[T]">Option.isNone</a>, <a class="reference external" href="https://github.com/pragmagic/uuids/blob/8cb8720b567c6bcb261bd1c0f7491bdb5209ad06/uuids.nim#L72">UUID.isZero</a>, value of <tt class="docutils literal"><span class="pre"><span class="DecNumber">0</span></span></tt>, or only whitespace). This is intended to allow for cases like the example where the database may generate an ID when a new record is inserted. If a non-zero value is provided, the create call will include the <tt class="docutils literal"><span class="pre"><span class="Identifier">id</span></span></tt> field in the <tt class="docutils literal"><span class="pre"><span class="Identifier">INSERT</span></span></tt> query.</p>
<p>For example, to allow the database to create the id:</p>
<pre class="listing"><span class="Keyword">let</span> <span class="Identifier">item</span> <span class="Operator">=</span> <span class="Identifier">TodoItem</span><span class="Punctuation">(</span>
<span class="Identifier">owner</span><span class="Punctuation">:</span> <span class="StringLit">&quot;John Mann&quot;</span><span class="Punctuation">,</span>
<span class="Identifier">summary</span><span class="Punctuation">:</span> <span class="StringLit">&quot;Create a grocery list.&quot;</span><span class="Punctuation">,</span>
<span class="Identifier">details</span><span class="Punctuation">:</span> <span class="Identifier">none</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">]</span><span class="Punctuation">(</span><span class="Punctuation">)</span><span class="Punctuation">,</span>
<span class="Identifier">priority</span><span class="Punctuation">:</span> <span class="DecNumber">0</span><span class="Punctuation">,</span>
<span class="Identifier">relatedTodoItemIds</span><span class="Punctuation">:</span> <span class="Operator">@</span><span class="Punctuation">[</span><span class="Punctuation">]</span><span class="Punctuation">)</span>
<span class="Keyword">let</span> <span class="Identifier">itemWithId</span> <span class="Operator">=</span> <span class="Identifier">db</span><span class="Operator">.</span><span class="Identifier">createTodoItem</span><span class="Punctuation">(</span><span class="Identifier">item</span><span class="Punctuation">)</span>
<span class="Identifier">echo</span> <span class="Operator">$</span><span class="Identifier">itemWithId</span><span class="Operator">.</span><span class="Identifier">id</span> <span class="Comment"># generated in the database</span></pre><p>And to create it in code:</p>
<pre class="listing"><span class="Keyword">import</span> <span class="Identifier">uuids</span>
<span class="Keyword">let</span> <span class="Identifier">item</span> <span class="Operator">=</span> <span class="Identifier">TodoItem</span><span class="Punctuation">(</span>
<span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">genUUID</span><span class="Punctuation">(</span><span class="Punctuation">)</span><span class="Punctuation">,</span>
<span class="Identifier">owner</span><span class="Punctuation">:</span> <span class="StringLit">&quot;John Mann&quot;</span><span class="Punctuation">,</span>
<span class="Identifier">summary</span><span class="Punctuation">:</span> <span class="StringLit">&quot;Create a grocery list.&quot;</span><span class="Punctuation">,</span>
<span class="Identifier">details</span><span class="Punctuation">:</span> <span class="Identifier">none</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">]</span><span class="Punctuation">(</span><span class="Punctuation">)</span><span class="Punctuation">,</span>
<span class="Identifier">priority</span><span class="Punctuation">:</span> <span class="DecNumber">0</span><span class="Punctuation">,</span>
<span class="Identifier">relatedTodoItemIds</span><span class="Punctuation">:</span> <span class="Operator">@</span><span class="Punctuation">[</span><span class="Punctuation">]</span><span class="Punctuation">)</span>
<span class="Keyword">let</span> <span class="Identifier">itemInDb</span> <span class="Operator">=</span> <span class="Identifier">db</span><span class="Operator">.</span><span class="Identifier">createTodoItem</span><span class="Punctuation">(</span><span class="Identifier">item</span><span class="Punctuation">)</span>
<span class="Identifier">echo</span> <span class="Operator">$</span><span class="Identifier">itemInDb</span><span class="Operator">.</span><span class="Identifier">id</span> <span class="Comment"># will be the same as what was provided</span></pre>
<h2><a class="toc-backref" id="objectminusrelational-modeling-supported-data-types" href="#objectminusrelational-modeling-supported-data-types">Supported Data Types</a></h2><p>The following Nim data types are supported by Fiber ORM:</p>
<table border="1" class="docutils"><tr><th>Nim Type</th><th>Postgres Type</th><th>SQLite Type</th></tr>
<tr><td><tt class="docutils literal"><span class="pre"><span class="Identifier">string</span></span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-character.html">varchar</a></td><td></td></tr>
<tr><td><tt class="docutils literal"><span class="pre"><span class="Identifier">int</span></span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-INT">integer</a></td><td></td></tr>
<tr><td><tt class="docutils literal"><span class="pre"><span class="Identifier">float</span></span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-FLOAT">double</a></td><td></td></tr>
<tr><td><tt class="docutils literal"><span class="pre"><span class="Identifier">bool</span></span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-boolean.html">boolean</a></td><td></td></tr>
<tr><td><a class="reference external" href="https://nim-lang.org/docs/times.html#DateTime">DateTime</a></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-datetime.html">timestamp</a></td><td></td></tr>
<tr><td><tt class="docutils literal"><span class="pre"><span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Punctuation">]</span></span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/arrays.html">array</a></td><td></td></tr>
<tr><td><a class="reference external" href="https://github.com/pragmagic/uuids">UUID</a></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-uuid.html">uuid (pg)</a></td><td></td></tr>
<tr><td><a class="reference external" href="https://nim-lang.org/docs/options.html#Option">Option</a></td><td><em>allows</em> <tt class="docutils literal"><span class="pre"><span class="Identifier">NULL</span></span></tt> <sup><strong><a class="reference internal" href="#footnote-f1">[1]</a></strong></sup></td><td></td></tr>
<tr><td><a class="reference external" href="https://nim-lang.org/docs/json.html#JsonNode">JsonNode</a></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-json.html">jsonb</a></td><td></td></tr>
</table><hr class="footnote"><div class="footnote-group">
<div id="footnote-f1"><div class="footnote-label"><sup><strong><a href="#footnote-f1">[1]</a></strong></sup></div> &ensp; Note that this implies that all <tt class="docutils literal"><span class="pre"><span class="Identifier">NULL</span></span></tt>-able fields should be typed as optional using <tt class="docutils literal"><span class="pre"><span class="Identifier">Option</span><span class="Punctuation">[</span><span class="Identifier">fieldType</span><span class="Punctuation">]</span></span></tt>. Conversely, any fields with non-optional types should also be constrained to be <tt class="docutils literal"><span class="pre"><span class="Keyword">NOT</span> <span class="Identifier">NULL</span></span></tt> in the database schema.
</div>
</div>
<h1><a class="toc-backref" id="database-object" href="#database-object">Database Object</a></h1><p>Many of the Fiber ORM macros expect a database object type to be passed. In the example above the <a class="reference external" href="fiber_orm/pool.html#DbConnPool">pool.DbConnPool</a> object is used as database object type (aliased as <tt class="docutils literal"><span class="pre"><span class="Identifier">TodoDB</span></span></tt>). This is the intended usage pattern, but anything can be passed as the database object type so long as there is a defined <tt class="docutils literal"><span class="pre"><span class="Identifier">withConn</span></span></tt> template that provides an injected <tt class="docutils literal"><span class="pre"><span class="Identifier">conn</span><span class="Punctuation">:</span> <span class="Identifier">DbConn</span></span></tt> object to the provided statement body.</p>
<p>For example, a valid database object implementation that opens a new connection for every request might look like this:</p>
<pre class="listing"><span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">db_postgres</span>
<span class="Keyword">type</span> <span class="Identifier">TodoDB</span><span class="Operator">*</span> <span class="Operator">=</span> <span class="Keyword">object</span>
<span class="Identifier">connString</span><span class="Punctuation">:</span> <span class="Identifier">string</span>
<span class="Keyword">template</span> <span class="Identifier">withConn</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">stmt</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span> <span class="Operator">=</span>
<span class="Keyword">let</span> <span class="Identifier">conn</span> <span class="Punctuation">{</span><span class="Operator">.</span><span class="Identifier">inject</span><span class="Operator">.</span><span class="Punctuation">}</span> <span class="Operator">=</span> <span class="Identifier">open</span><span class="Punctuation">(</span><span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="Identifier">db</span><span class="Operator">.</span><span class="Identifier">connString</span><span class="Punctuation">)</span>
<span class="Keyword">try</span><span class="Punctuation">:</span> <span class="Identifier">stmt</span>
<span class="Keyword">finally</span><span class="Punctuation">:</span> <span class="Identifier">close</span><span class="Punctuation">(</span><span class="Identifier">conn</span><span class="Punctuation">)</span></pre>
<h1><a class="toc-backref" id="see-also" href="#see-also">See Also</a></h1><p><a class="reference external" href="fiber_orm/pool.html">fiber_orm/pool</a></p>
</p>
<div class="section" id="6">
<h1><a class="toc-backref" href="#6">Imports</a></h1>
<dl class="item">
<a class="reference external" href="fiber_orm/pool.html">fiber_orm/pool</a>, <a class="reference external" href="fiber_orm/util.html">fiber_orm/util</a>
</dl></div>
<div class="section" id="7">
<h1><a class="toc-backref" href="#7">Types</a></h1>
<dl class="item">
<div id="NotFoundError">
<dt><pre><a href="fiber_orm.html#NotFoundError"><span class="Identifier">NotFoundError</span></a> <span class="Other">=</span> <span class="Keyword">object</span> <span class="Keyword">of</span> <span class="Identifier">CatchableError</span></pre></dt>
<dd>
Error type raised when no record matches a given ID
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L299"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L299" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="12">
<h1><a class="toc-backref" href="#12">Procs</a></h1>
<dl class="item">
<div id="createRecord,DbConn,T">
<dt><pre><span class="Keyword">proc</span> <a href="#createRecord%2CDbConn%2CT"><span class="Identifier">createRecord</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">rec</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span></pre></dt>
<dd>
<p>Create a new record. <tt class="docutils literal"><span class="pre"><span class="Identifier">rec</span></span></tt> is expected to be a <a class="reference external" href="#objectminusrelational-modeling-model-class">model class</a>. The <tt class="docutils literal"><span class="pre"><span class="Identifier">id</span></span></tt> field is only set if it is non-empty (see <a class="reference external" href="#model-class-id-field">ID Field</a> for details).</p>
<p>Returns the newly created record.</p>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L314"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L314" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="deleteRecord,DbConn,T">
<dt><pre><span class="Keyword">proc</span> <a href="#deleteRecord%2CDbConn%2CT"><span class="Identifier">deleteRecord</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">rec</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">bool</span></pre></dt>
<dd>
Delete a record by <a class="reference external" href="#model-class-id-field">id</a>.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L359"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L359" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="initPool,proc),int,string">
<dt><pre><span class="Keyword">proc</span> <a href="#initPool%2Cproc%29%2Cint%2Cstring"><span class="Identifier">initPool</span></a><span class="Other">(</span><span class="Identifier">connect</span><span class="Other">:</span> <span class="Keyword">proc</span> <span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">poolSize</span> <span class="Other">=</span> <span class="DecNumber">10</span><span class="Other">;</span> <span class="Identifier">hardCap</span> <span class="Other">=</span> <span class="Identifier">false</span><span class="Other">;</span>
<span class="Identifier">healthCheckQuery</span> <span class="Other">=</span> <span class="StringLit">&quot;SELECT \'true\' AS alive&quot;</span><span class="Other">)</span><span class="Other">:</span> <a href="fiber_orm/pool.html#DbConnPool"><span class="Identifier">DbConnPool</span></a> {.
<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
Initialize a new DbConnPool. See the <tt class="docutils literal"><span class="pre"><span class="Identifier">initDb</span></span></tt> procedure in the <a class="reference external" href="#basic-usage-example-fiber-orm-usage">Example Fiber ORM Usage</a> for an example<ul class="simple"><li><tt class="docutils literal"><span class="pre"><span class="Identifier">connect</span></span></tt> must be a factory which creates a new <tt class="docutils literal"><span class="pre"><span class="Identifier">DbConn</span></span></tt>.</li>
<li><tt class="docutils literal"><span class="pre"><span class="Identifier">poolSize</span></span></tt> sets the desired capacity of the connection pool.</li>
<li><p><tt class="docutils literal"><span class="pre"><span class="Identifier">hardCap</span></span></tt> defaults to <tt class="docutils literal"><span class="pre"><span class="Identifier">false</span></span></tt>. When <tt class="docutils literal"><span class="pre"><span class="Identifier">false</span></span></tt>, the pool can grow beyond the configured capacity, but will release connections down to the its capacity (no less than <tt class="docutils literal"><span class="pre"><span class="Identifier">poolSize</span></span></tt>).</p>
<p>When <tt class="docutils literal"><span class="pre"><span class="Identifier">true</span></span></tt> the pool will not create more than its configured capacity. It a connection is requested, none are free, and the pool is at capacity, this will result in an Error being raised.</p>
</li>
<li><tt class="docutils literal"><span class="pre"><span class="Identifier">healthCheckQuery</span></span></tt> should be a simple and fast SQL query that the pool can use to test the liveliness of pooled connections.</li>
</ul>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L539"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L539" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="updateRecord,DbConn,T">
<dt><pre><span class="Keyword">proc</span> <a href="#updateRecord%2CDbConn%2CT"><span class="Identifier">updateRecord</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">rec</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">bool</span></pre></dt>
<dd>
Update a record by id. <tt class="docutils literal"><span class="pre"><span class="Identifier">rec</span></span></tt> is expected to be a <a class="reference external" href="#objectminusrelational-modeling-model-class">model class</a>.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L337"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L337" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="17">
<h1><a class="toc-backref" href="#17">Macros</a></h1>
<dl class="item">
<div id="generateLookup.m,type,type,seq[string]">
<dt><pre><span class="Keyword">macro</span> <a href="#generateLookup.m%2Ctype%2Ctype%2Cseq%5Bstring%5D"><span class="Identifier">generateLookup</span></a><span class="Other">(</span><span class="Identifier">dbType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">fields</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Create a lookup procedure for a given set of field names. For example, given the TODO database demostrated above,<pre class="listing"><span class="Identifier">generateLookup</span><span class="Punctuation">(</span><span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">TodoItem</span><span class="Punctuation">,</span> <span class="Punctuation">[</span><span class="StringLit">&quot;owner&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;priority&quot;</span><span class="Punctuation">]</span><span class="Punctuation">)</span></pre><p>will generate the following procedure:</p>
<pre class="listing"><span class="Keyword">proc</span> <span class="Identifier">findTodoItemsByOwnerAndPriority</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">SampleDB</span><span class="Punctuation">,</span>
<span class="Identifier">owner</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">,</span> <span class="Identifier">priority</span><span class="Punctuation">:</span> <span class="Identifier">int</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TodoItem</span><span class="Punctuation">]</span></pre>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L468"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L468" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="generateProcsForFieldLookups.m,type,openArray[tuple[type,seq[string]]]">
<dt><pre><span class="Keyword">macro</span> <a href="#generateProcsForFieldLookups.m%2Ctype%2CopenArray%5Btuple%5Btype%2Cseq%5Bstring%5D%5D%5D"><span class="Identifier">generateProcsForFieldLookups</span></a><span class="Other">(</span><span class="Identifier">dbType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">modelsAndFields</span><span class="Other">:</span> <span class="Identifier">openArray</span><span class="Other">[</span>
<span class="Keyword">tuple</span><span class="Other">[</span><span class="Identifier">t</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">,</span> <span class="Identifier">fields</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">]</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L510"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L510" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="generateProcsForModels.m,type,openArray[type]">
<dt><pre><span class="Keyword">macro</span> <a href="#generateProcsForModels.m%2Ctype%2CopenArray%5Btype%5D"><span class="Identifier">generateProcsForModels</span></a><span class="Other">(</span><span class="Identifier">dbType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">modelTypes</span><span class="Other">:</span> <span class="Identifier">openArray</span><span class="Other">[</span><span class="Identifier">type</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Generate all standard access procedures for the given model types. For a <a class="reference external" href="#objectminusrelational-modeling-model-class">model class</a> named <tt class="docutils literal"><span class="pre"><span class="Identifier">TodoItem</span></span></tt>, this will generate the following procedures:<pre class="listing"><span class="Keyword">proc</span> <span class="Identifier">getTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">idType</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">getAllTodoItems</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">createTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">idType</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">updateTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">findTodoItemsWhere</span><span class="Operator">*</span><span class="Punctuation">(</span>
<span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">whereClause</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">,</span> <span class="Identifier">values</span><span class="Punctuation">:</span> <span class="Identifier">varargs</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">]</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span></pre><p><tt class="docutils literal"><span class="pre"><span class="Identifier">dbType</span></span></tt> is expected to be some type that has a defined <tt class="docutils literal"><span class="pre"><span class="Identifier">withConn</span></span></tt> procedure (see <a class="reference external" href="#database-object">Database Object</a> for details).</p>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L414"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L414" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="18">
<h1><a class="toc-backref" href="#18">Templates</a></h1>
<dl class="item">
<div id="deleteRecord.t,DbConn,type,typed">
<dt><pre><span class="Keyword">template</span> <a href="#deleteRecord.t%2CDbConn%2Ctype%2Ctyped"><span class="Identifier">deleteRecord</span></a><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">id</span><span class="Other">:</span> <span class="Identifier">typed</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Delete a record by id.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L353"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L353" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="findRecordsBy.t,DbConn,type,seq[tuple[string,string]]">
<dt><pre><span class="Keyword">template</span> <a href="#findRecordsBy.t%2CDbConn%2Ctype%2Cseq%5Btuple%5Bstring%2Cstring%5D%5D"><span class="Identifier">findRecordsBy</span></a><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span>
<span class="Identifier">lookups</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Keyword">tuple</span><span class="Other">[</span><span class="Identifier">field</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">,</span> <span class="Identifier">value</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">]</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Find all records matching the provided lookup values.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L403"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L403" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="findRecordsWhere.t,DbConn,type,string,varargs[string,dbFormat]">
<dt><pre><span class="Keyword">template</span> <a href="#findRecordsWhere.t%2CDbConn%2Ctype%2Cstring%2Cvarargs%5Bstring%2CdbFormat%5D"><span class="Identifier">findRecordsWhere</span></a><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">whereClause</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span>
<span class="Identifier">values</span><span class="Other">:</span> <span class="Identifier">varargs</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">,</span> <span class="Identifier">dbFormat</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Find all records matching a given <tt class="docutils literal"><span class="pre"><span class="Identifier">WHERE</span></span></tt> clause. The number of elements in the <tt class="docutils literal"><span class="pre"><span class="Identifier">values</span></span></tt> array must match the number of placeholders (<tt class="docutils literal"><span class="pre"><span class="Operator">?</span></span></tt>) in the provided <tt class="docutils literal"><span class="pre"><span class="Identifier">WHERE</span></span></tt> clause.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L382"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L382" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="getAllRecords.t,DbConn,type">
<dt><pre><span class="Keyword">template</span> <a href="#getAllRecords.t%2CDbConn%2Ctype"><span class="Identifier">getAllRecords</span></a><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Fetch all records of the given type.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L394"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L394" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="getRecord.t,DbConn,type,typed">
<dt><pre><span class="Keyword">template</span> <a href="#getRecord.t%2CDbConn%2Ctype%2Ctyped"><span class="Identifier">getRecord</span></a><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">id</span><span class="Other">:</span> <span class="Identifier">typed</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Fetch a record by id.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L367"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L367" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="inTransaction.t,DbConnPool,untyped">
<dt><pre><span class="Keyword">template</span> <a href="#inTransaction.t%2CDbConnPool%2Cuntyped"><span class="Identifier">inTransaction</span></a><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <a href="fiber_orm/pool.html#DbConnPool"><span class="Identifier">DbConnPool</span></a><span class="Other">;</span> <span class="Identifier">body</span><span class="Other">:</span> <span class="Identifier">untyped</span><span class="Other">)</span></pre></dt>
<dd>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L567"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L567" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="19">
<h1><a class="toc-backref" href="#19">Exports</a></h1>
<dl class="item">
<a href="fiber_orm/pool.html#DbConnPoolConfig"><span class="Identifier">DbConnPoolConfig</span></a>, <a href="fiber_orm/pool.html#initDbConnPool,DbConnPoolConfig"><span class="Identifier">initDbConnPool</span></a>, <a href="fiber_orm/pool.html#withConn.t,DbConnPool,untyped"><span class="Identifier">withConn</span></a>, <a href="fiber_orm/pool.html#take,DbConnPool"><span class="Identifier">take</span></a>, <a href="fiber_orm/pool.html#DbConnPool"><span class="Identifier">DbConnPool</span></a>, <a href="fiber_orm/pool.html#release,DbConnPool,int"><span class="Identifier">release</span></a>, <a href="fiber_orm/util.html#columnNamesForModel.m,typed"><span class="Identifier">columnNamesForModel</span></a>, <a href="fiber_orm/util.html#dbFormat,DateTime"><span class="Identifier">dbFormat</span></a>, <a href="fiber_orm/util.html#dbFormat,seq[T]"><span class="Identifier">dbFormat</span></a>, <a href="fiber_orm/util.html#dbFormat,string"><span class="Identifier">dbFormat</span></a>, <a href="fiber_orm/util.html#dbFormat,T"><span class="Identifier">dbFormat</span></a>, <a href="fiber_orm/util.html#dbNameToIdent,string"><span class="Identifier">dbNameToIdent</span></a>, <a href="fiber_orm/util.html#identNameToDb,string"><span class="Identifier">identNameToDb</span></a>, <a href="fiber_orm/util.html#modelName.m,type"><span class="Identifier">modelName</span></a>, <a href="fiber_orm/util.html#modelName.m"><span class="Identifier">modelName</span></a>, <a href="fiber_orm/util.html#rowToModel.m,typed,seq[string]"><span class="Identifier">rowToModel</span></a>, <a href="fiber_orm/util.html#tableName,type"><span class="Identifier">tableName</span></a>, <a href="fiber_orm/util.html#tableName,T"><span class="Identifier">tableName</span></a>
</dl></div>
</div>
</div>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2022-09-04 02:31:20 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

25
docs/fiber_orm.idx Normal file
View File

@ -0,0 +1,25 @@
NotFoundError fiber_orm.html#NotFoundError fiber_orm: NotFoundError
createRecord fiber_orm.html#createRecord,DbConn,T fiber_orm: createRecord[T](db: DbConn; rec: T): T
updateRecord fiber_orm.html#updateRecord,DbConn,T fiber_orm: updateRecord[T](db: DbConn; rec: T): bool
deleteRecord fiber_orm.html#deleteRecord.t,DbConn,type,typed fiber_orm: deleteRecord(db: DbConn; modelType: type; id: typed): untyped
deleteRecord fiber_orm.html#deleteRecord,DbConn,T fiber_orm: deleteRecord[T](db: DbConn; rec: T): bool
getRecord fiber_orm.html#getRecord.t,DbConn,type,typed fiber_orm: getRecord(db: DbConn; modelType: type; id: typed): untyped
findRecordsWhere fiber_orm.html#findRecordsWhere.t,DbConn,type,string,varargs[string,dbFormat] fiber_orm: findRecordsWhere(db: DbConn; modelType: type; whereClause: string;\n values: varargs[string, dbFormat]): untyped
getAllRecords fiber_orm.html#getAllRecords.t,DbConn,type fiber_orm: getAllRecords(db: DbConn; modelType: type): untyped
findRecordsBy fiber_orm.html#findRecordsBy.t,DbConn,type,seq[tuple[string,string]] fiber_orm: findRecordsBy(db: DbConn; modelType: type;\n lookups: seq[tuple[field: string, value: string]]): untyped
generateProcsForModels fiber_orm.html#generateProcsForModels.m,type,openArray[type] fiber_orm: generateProcsForModels(dbType: type; modelTypes: openArray[type]): untyped
generateLookup fiber_orm.html#generateLookup.m,type,type,seq[string] fiber_orm: generateLookup(dbType: type; modelType: type; fields: seq[string]): untyped
generateProcsForFieldLookups fiber_orm.html#generateProcsForFieldLookups.m,type,openArray[tuple[type,seq[string]]] fiber_orm: generateProcsForFieldLookups(dbType: type; modelsAndFields: openArray[\n tuple[t: type, fields: seq[string]]]): untyped
initPool fiber_orm.html#initPool,proc),int,string fiber_orm: initPool(connect: proc (): DbConn; poolSize = 10; hardCap = false;\n healthCheckQuery = &quot;SELECT \&apos;true\&apos; AS alive&quot;): DbConnPool
inTransaction fiber_orm.html#inTransaction.t,DbConnPool,untyped fiber_orm: inTransaction(db: DbConnPool; body: untyped)
Basic Usage fiber_orm.html#basic-usage Basic Usage
Example DB Schema fiber_orm.html#basic-usage-example-db-schema Example DB Schema
Example Model Definitions fiber_orm.html#basic-usage-example-model-definitions Example Model Definitions
Example Fiber ORM Usage fiber_orm.html#basic-usage-example-fiber-orm-usage Example Fiber ORM Usage
Object-Relational Modeling fiber_orm.html#objectminusrelational-modeling Object-Relational Modeling
Model Class fiber_orm.html#objectminusrelational-modeling-model-class Model Class
Name Mapping fiber_orm.html#model-class-name-mapping Name Mapping
ID Field fiber_orm.html#model-class-id-field ID Field
Supported Data Types fiber_orm.html#objectminusrelational-modeling-supported-data-types Supported Data Types
Database Object fiber_orm.html#database-object Database Object
See Also fiber_orm.html#see-also See Also

303
docs/fiber_orm/pool.html Normal file
View File

@ -0,0 +1,303 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>src/fiber_orm/pool</title>
<link rel="stylesheet" type="text/css" href="../nimdoc.out.css">
<script type="text/javascript" src="../dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
if (toggleSwitch !== null) {
toggleSwitch.addEventListener('change', switchTheme, false);
}
var currentTheme = localStorage.getItem('theme');
if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) {
currentTheme = 'dark';
}
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark' && toggleSwitch !== null) {
toggleSwitch.checked = true;
}
}
}
window.addEventListener('DOMContentLoaded', main);
</script>
</head>
<body>
<div class="document" id="documentId">
<div class="container">
<h1 class="title">src/fiber_orm/pool</h1>
<div class="row">
<div class="three columns">
<div class="theme-switch-wrapper">
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
&nbsp;&nbsp;&nbsp; <em>Dark Mode</em>
</div>
<div id="global-links">
<ul class="simple">
<li>
<a href="../theindex.html">Index</a>
</li>
</ul>
</div>
<div id="searchInputDiv">
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
<ul class="simple simple-toc" id="toc-list">
<li>
<a class="reference reference-toplevel" href="#7" id="57">Types</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#DbConnPool"
title="DbConnPool = ref object
conns: seq[PooledDbConn]
cfg: DbConnPoolConfig
lastId: int">DbConnPool</a></li>
<li><a class="reference" href="#DbConnPoolConfig"
title="DbConnPoolConfig = object
connect*: () -&gt; DbConn ## Factory procedure to create a new DBConn
poolSize*: int ## The pool capacity.
hardCap*: bool ## Is the pool capacity a hard cap?
##
## When `false`, the pool can grow beyond the configured
## capacity, but will release connections down to the its
## capacity (no less than `poolSize`).
##
## When `true` the pool will not create more than its
## configured capacity. It a connection is requested, none
## are free, and the pool is at capacity, this will result
## in an Error being raised.
healthCheckQuery*: string ## Should be a simple and fast SQL query that the
## pool can use to test the liveliness of pooled
## connections.">DbConnPoolConfig</a></li>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#12" id="62">Procs</a>
<ul class="simple simple-toc-section">
<ul class="simple nested-toc-section">initDbConnPool
<li><a class="reference" href="#initDbConnPool%2CDbConnPoolConfig"
title="initDbConnPool(cfg: DbConnPoolConfig): DbConnPool">initDbConnPool(cfg: DbConnPoolConfig): DbConnPool</a></li>
</ul>
<ul class="simple nested-toc-section">release
<li><a class="reference" href="#release%2CDbConnPool%2Cint"
title="release(pool: DbConnPool; connId: int): void">release(pool: DbConnPool; connId: int): void</a></li>
</ul>
<ul class="simple nested-toc-section">take
<li><a class="reference" href="#take%2CDbConnPool"
title="take(pool: DbConnPool): tuple[id: int, conn: DbConn]">take(pool: DbConnPool): tuple[id: int, conn: DbConn]</a></li>
</ul>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#18" id="68">Templates</a>
<ul class="simple simple-toc-section">
<ul class="simple nested-toc-section">withConn
<li><a class="reference" href="#withConn.t%2CDbConnPool%2Cuntyped"
title="withConn(pool: DbConnPool; stmt: untyped): untyped">withConn(pool: DbConnPool; stmt: untyped): untyped</a></li>
</ul>
</ul>
</li>
</ul>
</div>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/pool.nim#L1"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/pool.nim#L1" class="link-seesrc" target="_blank" >Edit</a>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc">Simple database connection pooling implementation compatible with Fiber ORM.</p>
<div class="section" id="7">
<h1><a class="toc-backref" href="#7">Types</a></h1>
<dl class="item">
<div id="DbConnPool">
<dt><pre><a href="pool.html#DbConnPool"><span class="Identifier">DbConnPool</span></a> <span class="Other">=</span> <span class="Keyword">ref</span> <span class="Keyword">object</span>
<span class="Identifier">conns</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">PooledDbConn</span><span class="Other">]</span>
<span class="Identifier">cfg</span><span class="Other">:</span> <a href="pool.html#DbConnPoolConfig"><span class="Identifier">DbConnPoolConfig</span></a>
<span class="Identifier">lastId</span><span class="Other">:</span> <span class="Identifier">int</span>
</pre></dt>
<dd>
Database connection pool
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/pool.nim#L35"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/pool.nim#L35" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="DbConnPoolConfig">
<dt><pre><a href="pool.html#DbConnPoolConfig"><span class="Identifier">DbConnPoolConfig</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
<span class="Identifier">connect</span><span class="Operator">*</span><span class="Other">:</span> <span class="Other">(</span><span class="Other">)</span> <span class="Operator">-&gt;</span> <span class="Identifier">DbConn</span> <span class="Comment">## Factory procedure to create a new DBConn</span>
<span class="Identifier">poolSize</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span> <span class="Comment">## The pool capacity.</span>
<span class="Identifier">hardCap</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">bool</span> <span class="Comment">## Is the pool capacity a hard cap?</span>
<span class="Comment">## </span>
<span class="Comment">## When `false`, the pool can grow beyond the configured</span>
<span class="Comment">## capacity, but will release connections down to the its</span>
<span class="Comment">## capacity (no less than `poolSize`).</span>
<span class="Comment">## </span>
<span class="Comment">## When `true` the pool will not create more than its</span>
<span class="Comment">## configured capacity. It a connection is requested, none</span>
<span class="Comment">## are free, and the pool is at capacity, this will result</span>
<span class="Comment">## in an Error being raised.</span>
<span class="Identifier">healthCheckQuery</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">string</span> <span class="Comment">## Should be a simple and fast SQL query that the</span>
<span class="Comment">## pool can use to test the liveliness of pooled</span>
<span class="Comment">## connections.</span>
</pre></dt>
<dd>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/pool.nim#L13"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/pool.nim#L13" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="12">
<h1><a class="toc-backref" href="#12">Procs</a></h1>
<dl class="item">
<div id="initDbConnPool,DbConnPoolConfig">
<dt><pre><span class="Keyword">proc</span> <a href="#initDbConnPool%2CDbConnPoolConfig"><span class="Identifier">initDbConnPool</span></a><span class="Other">(</span><span class="Identifier">cfg</span><span class="Other">:</span> <a href="pool.html#DbConnPoolConfig"><span class="Identifier">DbConnPoolConfig</span></a><span class="Other">)</span><span class="Other">:</span> <a href="pool.html#DbConnPool"><span class="Identifier">DbConnPool</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span>
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/pool.nim#L47"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/pool.nim#L47" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="release,DbConnPool,int">
<dt><pre><span class="Keyword">proc</span> <a href="#release%2CDbConnPool%2Cint"><span class="Identifier">release</span></a><span class="Other">(</span><span class="Identifier">pool</span><span class="Other">:</span> <a href="pool.html#DbConnPool"><span class="Identifier">DbConnPool</span></a><span class="Other">;</span> <span class="Identifier">connId</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">void</span> {.
<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
Release a connection back to the pool.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/pool.nim#L116"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/pool.nim#L116" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="take,DbConnPool">
<dt><pre><span class="Keyword">proc</span> <a href="#take%2CDbConnPool"><span class="Identifier">take</span></a><span class="Other">(</span><span class="Identifier">pool</span><span class="Other">:</span> <a href="pool.html#DbConnPool"><span class="Identifier">DbConnPool</span></a><span class="Other">)</span><span class="Other">:</span> <span class="Keyword">tuple</span><span class="Other">[</span><span class="Identifier">id</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">,</span> <span class="Identifier">conn</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">]</span> {.
<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">,</span> <span class="Identifier">ReadDbEffect</span><span class="Other">,</span> <span class="Identifier">DbEffect</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
<p>Request a connection from the pool. Returns a DbConn if the pool has free connections, or if it has the capacity to create a new connection. If the pool is configured with a hard capacity limit and is out of free connections, this will raise an Error.</p>
<p>Connections taken must be returned via <tt class="docutils literal"><span class="pre"><span class="Identifier">release</span></span></tt> when the caller is finished using them in order for them to be released back to the pool.</p>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/pool.nim#L94"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/pool.nim#L94" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="18">
<h1><a class="toc-backref" href="#18">Templates</a></h1>
<dl class="item">
<div id="withConn.t,DbConnPool,untyped">
<dt><pre><span class="Keyword">template</span> <a href="#withConn.t%2CDbConnPool%2Cuntyped"><span class="Identifier">withConn</span></a><span class="Other">(</span><span class="Identifier">pool</span><span class="Other">:</span> <a href="pool.html#DbConnPool"><span class="Identifier">DbConnPool</span></a><span class="Other">;</span> <span class="Identifier">stmt</span><span class="Other">:</span> <span class="Identifier">untyped</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
<p>Convenience template to provide a connection from the pool for use in a statement block, automatically releasing that connnection when done.</p>
<p>The provided connection is injected as the variable <tt class="docutils literal"><span class="pre"><span class="Identifier">conn</span></span></tt> in the statement body.</p>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/pool.nim#L122"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/pool.nim#L122" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
</div>
</div>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2022-09-04 02:31:19 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

6
docs/fiber_orm/pool.idx Normal file
View File

@ -0,0 +1,6 @@
DbConnPoolConfig fiber_orm/pool.html#DbConnPoolConfig pool: DbConnPoolConfig
DbConnPool fiber_orm/pool.html#DbConnPool pool: DbConnPool
initDbConnPool fiber_orm/pool.html#initDbConnPool,DbConnPoolConfig pool: initDbConnPool(cfg: DbConnPoolConfig): DbConnPool
take fiber_orm/pool.html#take,DbConnPool pool: take(pool: DbConnPool): tuple[id: int, conn: DbConn]
release fiber_orm/pool.html#release,DbConnPool,int pool: release(pool: DbConnPool; connId: int): void
withConn fiber_orm/pool.html#withConn.t,DbConnPool,untyped pool: withConn(pool: DbConnPool; stmt: untyped): untyped

524
docs/fiber_orm/util.html Normal file
View File

@ -0,0 +1,524 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>src/fiber_orm/util</title>
<link rel="stylesheet" type="text/css" href="../nimdoc.out.css">
<script type="text/javascript" src="../dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
if (toggleSwitch !== null) {
toggleSwitch.addEventListener('change', switchTheme, false);
}
var currentTheme = localStorage.getItem('theme');
if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) {
currentTheme = 'dark';
}
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark' && toggleSwitch !== null) {
toggleSwitch.checked = true;
}
}
}
window.addEventListener('DOMContentLoaded', main);
</script>
</head>
<body>
<div class="document" id="documentId">
<div class="container">
<h1 class="title">src/fiber_orm/util</h1>
<div class="row">
<div class="three columns">
<div class="theme-switch-wrapper">
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
&nbsp;&nbsp;&nbsp; <em>Dark Mode</em>
</div>
<div id="global-links">
<ul class="simple">
<li>
<a href="../theindex.html">Index</a>
</li>
</ul>
</div>
<div id="searchInputDiv">
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
<ul class="simple simple-toc" id="toc-list">
<li>
<a class="reference reference-toplevel" href="#7" id="57">Types</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#MutateClauses"
title="MutateClauses = object
columns*: seq[string]
placeholders*: seq[string]
values*: seq[string]">MutateClauses</a></li>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#12" id="62">Procs</a>
<ul class="simple simple-toc-section">
<ul class="simple nested-toc-section">createParseStmt
<li><a class="reference" href="#createParseStmt%2CNimNode%2CNimNode"
title="createParseStmt(t, value: NimNode): NimNode">createParseStmt(t, value: NimNode): NimNode</a></li>
</ul>
<ul class="simple nested-toc-section">dbFormat
<li><a class="reference" href="#dbFormat%2CDateTime"
title="dbFormat(dt: DateTime): string">dbFormat(dt: DateTime): string</a></li>
<li><a class="reference" href="#dbFormat%2Cstring"
title="dbFormat(s: string): string">dbFormat(s: string): string</a></li>
<li><a class="reference" href="#dbFormat%2CT"
title="dbFormat[T](item: T): string">dbFormat[T](item: T): string</a></li>
<li><a class="reference" href="#dbFormat%2Cseq%5BT%5D"
title="dbFormat[T](list: seq[T]): string">dbFormat[T](list: seq[T]): string</a></li>
</ul>
<ul class="simple nested-toc-section">dbNameToIdent
<li><a class="reference" href="#dbNameToIdent%2Cstring"
title="dbNameToIdent(name: string): string">dbNameToIdent(name: string): string</a></li>
</ul>
<ul class="simple nested-toc-section">identNameToDb
<li><a class="reference" href="#identNameToDb%2Cstring"
title="identNameToDb(name: string): string">identNameToDb(name: string): string</a></li>
</ul>
<ul class="simple nested-toc-section">parseDbArray
<li><a class="reference" href="#parseDbArray%2Cstring"
title="parseDbArray(val: string): seq[string]">parseDbArray(val: string): seq[string]</a></li>
</ul>
<ul class="simple nested-toc-section">parsePGDatetime
<li><a class="reference" href="#parsePGDatetime%2Cstring"
title="parsePGDatetime(val: string): DateTime">parsePGDatetime(val: string): DateTime</a></li>
</ul>
<ul class="simple nested-toc-section">pluralize
<li><a class="reference" href="#pluralize%2Cstring"
title="pluralize(name: string): string">pluralize(name: string): string</a></li>
</ul>
<ul class="simple nested-toc-section">tableName
<li><a class="reference" href="#tableName%2Ctype"
title="tableName(modelType: type): string">tableName(modelType: type): string</a></li>
<li><a class="reference" href="#tableName%2CT"
title="tableName[T](rec: T): string">tableName[T](rec: T): string</a></li>
</ul>
<ul class="simple nested-toc-section">typeOfColumn
<li><a class="reference" href="#typeOfColumn%2CNimNode%2Cstring"
title="typeOfColumn(modelType: NimNode; colName: string): NimNode">typeOfColumn(modelType: NimNode; colName: string): NimNode</a></li>
</ul>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#17" id="67">Macros</a>
<ul class="simple simple-toc-section">
<ul class="simple nested-toc-section">columnNamesForModel
<li><a class="reference" href="#columnNamesForModel.m%2Ctyped"
title="columnNamesForModel(modelType: typed): seq[string]">columnNamesForModel(modelType: typed): seq[string]</a></li>
</ul>
<ul class="simple nested-toc-section">listFields
<li><a class="reference" href="#listFields.m%2Ctyped"
title="listFields(t: typed): untyped">listFields(t: typed): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">modelName
<li><a class="reference" href="#modelName.m"
title="modelName(model: object): string">modelName(model: object): string</a></li>
<li><a class="reference" href="#modelName.m%2Ctype"
title="modelName(modelType: type): string">modelName(modelType: type): string</a></li>
</ul>
<ul class="simple nested-toc-section">populateMutateClauses
<li><a class="reference" href="#populateMutateClauses.m%2Ctyped%2Cbool%2CMutateClauses"
title="populateMutateClauses(t: typed; newRecord: bool; mc: var MutateClauses): untyped">populateMutateClauses(t: typed; newRecord: bool; mc: var MutateClauses): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">rowToModel
<li><a class="reference" href="#rowToModel.m%2Ctyped%2Cseq%5Bstring%5D"
title="rowToModel(modelType: typed; row: seq[string]): untyped">rowToModel(modelType: typed; row: seq[string]): untyped</a></li>
</ul>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#18" id="68">Templates</a>
<ul class="simple simple-toc-section">
<ul class="simple nested-toc-section">walkFieldDefs
<li><a class="reference" href="#walkFieldDefs.t%2CNimNode%2Cuntyped"
title="walkFieldDefs(t: NimNode; body: untyped)">walkFieldDefs(t: NimNode; body: untyped)</a></li>
</ul>
</ul>
</li>
</ul>
</div>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L1"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L1" class="link-seesrc" target="_blank" >Edit</a>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc">Utility methods used internally by Fiber ORM.</p>
<div class="section" id="7">
<h1><a class="toc-backref" href="#7">Types</a></h1>
<dl class="item">
<div id="MutateClauses">
<dt><pre><a href="util.html#MutateClauses"><span class="Identifier">MutateClauses</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
<span class="Identifier">columns</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span>
<span class="Identifier">placeholders</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span>
<span class="Identifier">values</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span>
</pre></dt>
<dd>
Data structure to hold information about the clauses that should be added to a query. How these clauses are used will depend on the query. This common data structure provides the information needed to create WHERE clauses, UPDATE clauses, etc.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L12"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L12" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="12">
<h1><a class="toc-backref" href="#12">Procs</a></h1>
<dl class="item">
<div id="createParseStmt,NimNode,NimNode">
<dt><pre><span class="Keyword">proc</span> <a href="#createParseStmt%2CNimNode%2CNimNode"><span class="Identifier">createParseStmt</span></a><span class="Other">(</span><span class="Identifier">t</span><span class="Other">,</span> <span class="Identifier">value</span><span class="Other">:</span> <span class="Identifier">NimNode</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">NimNode</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
Utility method to create the Nim cod required to parse a value coming from the a database query. This is used by functions like <tt class="docutils literal"><span class="pre"><span class="Identifier">rowToModel</span></span></tt> to parse the dataabase columns into the Nim object fields.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L190"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L190" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="dbFormat,DateTime">
<dt><pre><span class="Keyword">proc</span> <a href="#dbFormat%2CDateTime"><span class="Identifier">dbFormat</span></a><span class="Other">(</span><span class="Identifier">dt</span><span class="Other">:</span> <span class="Identifier">DateTime</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
Format a DateTime for inclusion in a SQL Query.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L75"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L75" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="dbFormat,string">
<dt><pre><span class="Keyword">proc</span> <a href="#dbFormat%2Cstring"><span class="Identifier">dbFormat</span></a><span class="Other">(</span><span class="Identifier">s</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
Format a string for inclusion in a SQL Query.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L71"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L71" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="dbFormat,T">
<dt><pre><span class="Keyword">proc</span> <a href="#dbFormat%2CT"><span class="Identifier">dbFormat</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">item</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
For all other types, fall back on a defined <tt class="docutils literal"><span class="pre"><span class="Operator">$</span></span></tt> function to create a string version of the value we can include in an SQL query&gt;
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L83"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L83" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="dbFormat,seq[T]">
<dt><pre><span class="Keyword">proc</span> <a href="#dbFormat%2Cseq%5BT%5D"><span class="Identifier">dbFormat</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">list</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
Format a <tt class="docutils literal"><span class="pre"><span class="Identifier">seq</span></span></tt> for inclusion in a SQL Query.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L79"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L79" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="dbNameToIdent,string">
<dt><pre><span class="Keyword">proc</span> <a href="#dbNameToIdent%2Cstring"><span class="Identifier">dbNameToIdent</span></a><span class="Other">(</span><span class="Identifier">name</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
Map a DB name to a Nim identifier name. See the <a class="reference external" href="../fiber_orm.html">rules for name mapping</a>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L58"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L58" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="identNameToDb,string">
<dt><pre><span class="Keyword">proc</span> <a href="#identNameToDb%2Cstring"><span class="Identifier">identNameToDb</span></a><span class="Other">(</span><span class="Identifier">name</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
<p>Map a Nim identifier name to a DB name. See the <a class="reference external" href="../fiber_orm.html">rules for name mapping</a></p>
<p>TODO link above</p>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L37"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L37" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="parseDbArray,string">
<dt><pre><span class="Keyword">proc</span> <a href="#parseDbArray%2Cstring"><span class="Identifier">parseDbArray</span></a><span class="Other">(</span><span class="Identifier">val</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
Parse a Postgres array column into a Nim seq[string]
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L131"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L131" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="parsePGDatetime,string">
<dt><pre><span class="Keyword">proc</span> <a href="#parsePGDatetime%2Cstring"><span class="Identifier">parsePGDatetime</span></a><span class="Other">(</span><span class="Identifier">val</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">DateTime</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">KeyError</span><span class="Other">,</span> <span class="Identifier">SyntaxError</span><span class="Other">,</span>
<span class="Identifier">StudyError</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">,</span> <span class="Identifier">RegexInternalError</span><span class="Other">,</span> <span class="Identifier">InvalidUnicodeError</span><span class="Other">]</span><span class="Other">,</span>
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">TimeEffect</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
Parse a Postgres datetime value into a Nim DateTime object.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L91"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L91" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="pluralize,string">
<dt><pre><span class="Keyword">proc</span> <a href="#pluralize%2Cstring"><span class="Identifier">pluralize</span></a><span class="Other">(</span><span class="Identifier">name</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
Return the plural form of the given name.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L23"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L23" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="tableName,type">
<dt><pre><span class="Keyword">proc</span> <a href="#tableName%2Ctype"><span class="Identifier">tableName</span></a><span class="Other">(</span><span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
Get the <a class="reference external" href="../fiber_orm.html">table name</a> for a given <a class="reference external" href="../fiber_orm.html#objectminusrelational-modeling-model-class">model class</a>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L63"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L63" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="tableName,T">
<dt><pre><span class="Keyword">proc</span> <a href="#tableName%2CT"><span class="Identifier">tableName</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">rec</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
Get the <a class="reference external" href="../fiber_orm.html">table name</a> for a given record.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L67"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L67" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="typeOfColumn,NimNode,string">
<dt><pre><span class="Keyword">proc</span> <a href="#typeOfColumn%2CNimNode%2Cstring"><span class="Identifier">typeOfColumn</span></a><span class="Other">(</span><span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">NimNode</span><span class="Other">;</span> <span class="Identifier">colName</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">NimNode</span> {.
<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
Given a model type and a column name, return the Nim type for that column.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L308"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L308" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="17">
<h1><a class="toc-backref" href="#17">Macros</a></h1>
<dl class="item">
<div id="columnNamesForModel.m,typed">
<dt><pre><span class="Keyword">macro</span> <a href="#columnNamesForModel.m%2Ctyped"><span class="Identifier">columnNamesForModel</span></a><span class="Other">(</span><span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">typed</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span></pre></dt>
<dd>
Return the column names corresponding to the the fields of the given <a class="reference external" href="../fiber_orm.html#objectminusrelational-modeling-model-class">model class</a>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L274"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L274" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="listFields.m,typed">
<dt><pre><span class="Keyword">macro</span> <a href="#listFields.m%2Ctyped"><span class="Identifier">listFields</span></a><span class="Other">(</span><span class="Identifier">t</span><span class="Other">:</span> <span class="Identifier">typed</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L300"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L300" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="modelName.m">
<dt><pre><span class="Keyword">macro</span> <a href="#modelName.m"><span class="Identifier">modelName</span></a><span class="Other">(</span><span class="Identifier">model</span><span class="Other">:</span> <span class="Keyword">object</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
For a given concrete record object, return the name of the <a class="reference external" href="../fiber_orm.html#objectminusrelational-modeling-model-class">model class</a>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L29"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L29" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="modelName.m,type">
<dt><pre><span class="Keyword">macro</span> <a href="#modelName.m%2Ctype"><span class="Identifier">modelName</span></a><span class="Other">(</span><span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
Get the name of a given <a class="reference external" href="../fiber_orm.html#objectminusrelational-modeling-model-class">model class</a>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L33"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L33" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="populateMutateClauses.m,typed,bool,MutateClauses">
<dt><pre><span class="Keyword">macro</span> <a href="#populateMutateClauses.m%2Ctyped%2Cbool%2CMutateClauses"><span class="Identifier">populateMutateClauses</span></a><span class="Other">(</span><span class="Identifier">t</span><span class="Other">:</span> <span class="Identifier">typed</span><span class="Other">;</span> <span class="Identifier">newRecord</span><span class="Other">:</span> <span class="Identifier">bool</span><span class="Other">;</span> <span class="Identifier">mc</span><span class="Other">:</span> <span class="Keyword">var</span> <a href="util.html#MutateClauses"><span class="Identifier">MutateClauses</span></a><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Given a record type, create the datastructure used to generate SQL clauses for the fields of this record type.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L330"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L330" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="rowToModel.m,typed,seq[string]">
<dt><pre><span class="Keyword">macro</span> <a href="#rowToModel.m%2Ctyped%2Cseq%5Bstring%5D"><span class="Identifier">rowToModel</span></a><span class="Other">(</span><span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">typed</span><span class="Other">;</span> <span class="Identifier">row</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Return a new Nim model object of type <tt class="docutils literal"><span class="pre"><span class="Identifier">modelType</span></span></tt> populated with the values returned in the given database <tt class="docutils literal"><span class="pre"><span class="Identifier">row</span></span></tt>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L284"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L284" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="18">
<h1><a class="toc-backref" href="#18">Templates</a></h1>
<dl class="item">
<div id="walkFieldDefs.t,NimNode,untyped">
<dt><pre><span class="Keyword">template</span> <a href="#walkFieldDefs.t%2CNimNode%2Cuntyped"><span class="Identifier">walkFieldDefs</span></a><span class="Other">(</span><span class="Identifier">t</span><span class="Other">:</span> <span class="Identifier">NimNode</span><span class="Other">;</span> <span class="Identifier">body</span><span class="Other">:</span> <span class="Identifier">untyped</span><span class="Other">)</span></pre></dt>
<dd>
Iterate over every field of the given Nim object, yielding and defining <tt class="docutils literal"><span class="pre"><span class="Identifier">fieldIdent</span></span></tt> and <tt class="docutils literal"><span class="pre"><span class="Identifier">fieldType</span></span></tt>, the name of the field as a Nim Ident node and the type of the field as a Nim Type node respectively.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm/util.nim#L251"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm/util.nim#L251" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
</div>
</div>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2022-09-04 02:31:20 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

21
docs/fiber_orm/util.idx Normal file
View File

@ -0,0 +1,21 @@
MutateClauses fiber_orm/util.html#MutateClauses util: MutateClauses
pluralize fiber_orm/util.html#pluralize,string util: pluralize(name: string): string
modelName fiber_orm/util.html#modelName.m util: modelName(model: object): string
modelName fiber_orm/util.html#modelName.m,type util: modelName(modelType: type): string
identNameToDb fiber_orm/util.html#identNameToDb,string util: identNameToDb(name: string): string
dbNameToIdent fiber_orm/util.html#dbNameToIdent,string util: dbNameToIdent(name: string): string
tableName fiber_orm/util.html#tableName,type util: tableName(modelType: type): string
tableName fiber_orm/util.html#tableName,T util: tableName[T](rec: T): string
dbFormat fiber_orm/util.html#dbFormat,string util: dbFormat(s: string): string
dbFormat fiber_orm/util.html#dbFormat,DateTime util: dbFormat(dt: DateTime): string
dbFormat fiber_orm/util.html#dbFormat,seq[T] util: dbFormat[T](list: seq[T]): string
dbFormat fiber_orm/util.html#dbFormat,T util: dbFormat[T](item: T): string
parsePGDatetime fiber_orm/util.html#parsePGDatetime,string util: parsePGDatetime(val: string): DateTime
parseDbArray fiber_orm/util.html#parseDbArray,string util: parseDbArray(val: string): seq[string]
createParseStmt fiber_orm/util.html#createParseStmt,NimNode,NimNode util: createParseStmt(t, value: NimNode): NimNode
walkFieldDefs fiber_orm/util.html#walkFieldDefs.t,NimNode,untyped util: walkFieldDefs(t: NimNode; body: untyped)
columnNamesForModel fiber_orm/util.html#columnNamesForModel.m,typed util: columnNamesForModel(modelType: typed): seq[string]
rowToModel fiber_orm/util.html#rowToModel.m,typed,seq[string] util: rowToModel(modelType: typed; row: seq[string]): untyped
listFields fiber_orm/util.html#listFields.m,typed util: listFields(t: typed): untyped
typeOfColumn fiber_orm/util.html#typeOfColumn,NimNode,string util: typeOfColumn(modelType: NimNode; colName: string): NimNode
populateMutateClauses fiber_orm/util.html#populateMutateClauses.m,typed,bool,MutateClauses util: populateMutateClauses(t: typed; newRecord: bool; mc: var MutateClauses): untyped

619
docs/index.html Normal file
View File

@ -0,0 +1,619 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>src/fiber_orm</title>
<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
if (toggleSwitch !== null) {
toggleSwitch.addEventListener('change', switchTheme, false);
}
var currentTheme = localStorage.getItem('theme');
if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) {
currentTheme = 'dark';
}
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark' && toggleSwitch !== null) {
toggleSwitch.checked = true;
}
}
}
window.addEventListener('DOMContentLoaded', main);
</script>
</head>
<body>
<div class="document" id="documentId">
<div class="container">
<h1 class="title">src/fiber_orm</h1>
<div class="row">
<div class="three columns">
<div class="theme-switch-wrapper">
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
&nbsp;&nbsp;&nbsp; <em>Dark Mode</em>
</div>
<div id="global-links">
<ul class="simple">
<li>
<a href="theindex.html">Index</a>
</li>
</ul>
</div>
<div id="searchInputDiv">
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
<ul class="simple simple-toc" id="toc-list">
<li><a class="reference" id="basic-usage_toc" href="#basic-usage">Basic Usage</a></li>
<ul class="simple"><li><a class="reference" id="basic-usage-example-db-schema_toc" href="#basic-usage-example-db-schema">Example DB Schema</a></li>
<li><a class="reference" id="basic-usage-example-model-definitions_toc" href="#basic-usage-example-model-definitions">Example Model Definitions</a></li>
<li><a class="reference" id="basic-usage-example-fiber-orm-usage_toc" href="#basic-usage-example-fiber-orm-usage">Example Fiber ORM Usage</a></li>
</ul><li><a class="reference" id="objectminusrelational-modeling_toc" href="#objectminusrelational-modeling">Object-Relational Modeling</a></li>
<ul class="simple"><li><a class="reference" id="objectminusrelational-modeling-model-class_toc" href="#objectminusrelational-modeling-model-class">Model Class</a></li>
<ul class="simple"><li><a class="reference" id="model-class-name-mapping_toc" href="#model-class-name-mapping">Name Mapping</a></li>
<li><a class="reference" id="model-class-id-field_toc" href="#model-class-id-field">ID Field</a></li>
</ul><li><a class="reference" id="objectminusrelational-modeling-supported-data-types_toc" href="#objectminusrelational-modeling-supported-data-types">Supported Data Types</a></li>
</ul><li><a class="reference" id="database-object_toc" href="#database-object">Database Object</a></li>
<li><a class="reference" id="see-also_toc" href="#see-also">See Also</a></li>
<li>
<a class="reference reference-toplevel" href="#6" id="56">Imports</a>
<ul class="simple simple-toc-section">
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#7" id="57">Types</a>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#NotFoundError"
title="NotFoundError = object of CatchableError">NotFoundError</a></li>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#12" id="62">Procs</a>
<ul class="simple simple-toc-section">
<ul class="simple nested-toc-section">createRecord
<li><a class="reference" href="#createRecord%2CDbConn%2CT"
title="createRecord[T](db: DbConn; rec: T): T">createRecord[T](db: DbConn; rec: T): T</a></li>
</ul>
<ul class="simple nested-toc-section">deleteRecord
<li><a class="reference" href="#deleteRecord%2CDbConn%2CT"
title="deleteRecord[T](db: DbConn; rec: T): bool">deleteRecord[T](db: DbConn; rec: T): bool</a></li>
</ul>
<ul class="simple nested-toc-section">initPool
<li><a class="reference" href="#initPool%2Cproc%29%2Cint%2Cstring"
title="initPool(connect: proc (): DbConn; poolSize = 10; hardCap = false;
healthCheckQuery = &quot;SELECT \'true\' AS alive&quot;): DbConnPool">initPool(connect: proc (): DbConn; poolSize = 10; hardCap = false;
healthCheckQuery = &quot;SELECT \'true\' AS alive&quot;): DbConnPool</a></li>
</ul>
<ul class="simple nested-toc-section">updateRecord
<li><a class="reference" href="#updateRecord%2CDbConn%2CT"
title="updateRecord[T](db: DbConn; rec: T): bool">updateRecord[T](db: DbConn; rec: T): bool</a></li>
</ul>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#17" id="67">Macros</a>
<ul class="simple simple-toc-section">
<ul class="simple nested-toc-section">generateLookup
<li><a class="reference" href="#generateLookup.m%2Ctype%2Ctype%2Cseq%5Bstring%5D"
title="generateLookup(dbType: type; modelType: type; fields: seq[string]): untyped">generateLookup(dbType: type; modelType: type; fields: seq[string]): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">generateProcsForFieldLookups
<li><a class="reference" href="#generateProcsForFieldLookups.m%2Ctype%2CopenArray%5Btuple%5Btype%2Cseq%5Bstring%5D%5D%5D"
title="generateProcsForFieldLookups(dbType: type; modelsAndFields: openArray[
tuple[t: type, fields: seq[string]]]): untyped">generateProcsForFieldLookups(dbType: type; modelsAndFields: openArray[
tuple[t: type, fields: seq[string]]]): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">generateProcsForModels
<li><a class="reference" href="#generateProcsForModels.m%2Ctype%2CopenArray%5Btype%5D"
title="generateProcsForModels(dbType: type; modelTypes: openArray[type]): untyped">generateProcsForModels(dbType: type; modelTypes: openArray[type]): untyped</a></li>
</ul>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#18" id="68">Templates</a>
<ul class="simple simple-toc-section">
<ul class="simple nested-toc-section">deleteRecord
<li><a class="reference" href="#deleteRecord.t%2CDbConn%2Ctype%2Ctyped"
title="deleteRecord(db: DbConn; modelType: type; id: typed): untyped">deleteRecord(db: DbConn; modelType: type; id: typed): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">findRecordsBy
<li><a class="reference" href="#findRecordsBy.t%2CDbConn%2Ctype%2Cseq%5Btuple%5Bstring%2Cstring%5D%5D"
title="findRecordsBy(db: DbConn; modelType: type;
lookups: seq[tuple[field: string, value: string]]): untyped">findRecordsBy(db: DbConn; modelType: type;
lookups: seq[tuple[field: string, value: string]]): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">findRecordsWhere
<li><a class="reference" href="#findRecordsWhere.t%2CDbConn%2Ctype%2Cstring%2Cvarargs%5Bstring%2CdbFormat%5D"
title="findRecordsWhere(db: DbConn; modelType: type; whereClause: string;
values: varargs[string, dbFormat]): untyped">findRecordsWhere(db: DbConn; modelType: type; whereClause: string;
values: varargs[string, dbFormat]): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">getAllRecords
<li><a class="reference" href="#getAllRecords.t%2CDbConn%2Ctype"
title="getAllRecords(db: DbConn; modelType: type): untyped">getAllRecords(db: DbConn; modelType: type): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">getRecord
<li><a class="reference" href="#getRecord.t%2CDbConn%2Ctype%2Ctyped"
title="getRecord(db: DbConn; modelType: type; id: typed): untyped">getRecord(db: DbConn; modelType: type; id: typed): untyped</a></li>
</ul>
<ul class="simple nested-toc-section">inTransaction
<li><a class="reference" href="#inTransaction.t%2CDbConnPool%2Cuntyped"
title="inTransaction(db: DbConnPool; body: untyped)">inTransaction(db: DbConnPool; body: untyped)</a></li>
</ul>
</ul>
</li>
<li>
<a class="reference reference-toplevel" href="#19" id="69">Exports</a>
<ul class="simple simple-toc-section">
</ul>
</li>
</ul>
</div>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L1"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L1" class="link-seesrc" target="_blank" >Edit</a>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc"><p>Lightweight ORM supporting the <a class="reference external" href="https://nim-lang.org/docs/db_postgres.html">Postgres</a> and <a class="reference external" href="https://nim-lang.org/docs/db_sqlite.html">SQLite</a> databases in Nim. It supports a simple, opinionated model mapper to generate SQL queries based on Nim objects. It also includes a simple connection pooling implementation.</p>
<p>Fiber ORM is not intended to be a 100% all-cases-covered ORM that handles every potential data access pattern one might wish to implement. It is best thought of as a collection of common SQL generation patterns. It is intended to cover 90% of the common queries and functions one might write when implementing an SQL-based access layer. It is expected that there may be a few more complicated queries that need to be implemented to handle specific access patterns.</p>
<p>The simple mapping pattern provided by Fiber ORM also works well on top of databases that encapsulate data access logic in SQL with, for example, views.</p>
<h1><a class="toc-backref" id="basic-usage" href="#basic-usage">Basic Usage</a></h1><p>Consider a simple TODO list application that keeps track of TODO items as well as time logged against those items.</p>
<h2><a class="toc-backref" id="basic-usage-example-db-schema" href="#basic-usage-example-db-schema">Example DB Schema</a></h2><p>You might have a schema such as:</p>
<pre class="listing">create extension if not exists &quot;pgcrypto&quot;;
create table todo_items columns (
id uuid not null primary key default gen_random_uuid(),
owner varchar not null,
summary varchar not null,
details varchar default null,
priority integer not null default 0,
related_todo_item_ids uuid[] not null default '{}'
);
create table time_entries columns (
id uuid not null primary key default gen_random_uuid(),
todo_item_id uuid not null references todo_items (id) on delete cascade,
start timestamp with timezone not null default current_timestamp,
stop timestamp with timezone default null,
);</pre>
<h2><a class="toc-backref" id="basic-usage-example-model-definitions" href="#basic-usage-example-model-definitions">Example Model Definitions</a></h2><p>Models may be defined as:</p>
<pre class="listing"><span class="Comment"># models.nim</span>
<span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">options</span><span class="Punctuation">,</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">times</span>
<span class="Keyword">import</span> <span class="Identifier">uuids</span>
<span class="Keyword">type</span>
<span class="Identifier">TodoItem</span><span class="Operator">*</span> <span class="Operator">=</span> <span class="Keyword">object</span>
<span class="Identifier">id</span><span class="Operator">*:</span> <span class="Identifier">UUID</span>
<span class="Identifier">owner</span><span class="Operator">*:</span> <span class="Identifier">string</span>
<span class="Identifier">summary</span><span class="Operator">*:</span> <span class="Identifier">string</span>
<span class="Identifier">details</span><span class="Operator">*:</span> <span class="Identifier">Option</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">]</span>
<span class="Identifier">priority</span><span class="Operator">*:</span> <span class="Identifier">int</span>
<span class="Identifier">relatedTodoItemIds</span><span class="Operator">*:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">UUID</span><span class="Punctuation">]</span>
<span class="Identifier">TimeEntry</span><span class="Operator">*</span> <span class="Operator">=</span> <span class="Keyword">object</span>
<span class="Identifier">id</span><span class="Operator">*:</span> <span class="Identifier">UUID</span>
<span class="Identifier">todoItemId</span><span class="Operator">*:</span> <span class="Identifier">Option</span><span class="Punctuation">[</span><span class="Identifier">UUID</span><span class="Punctuation">]</span>
<span class="Identifier">start</span><span class="Operator">*:</span> <span class="Identifier">DateTime</span>
<span class="Identifier">stop</span><span class="Operator">*:</span> <span class="Identifier">Option</span><span class="Punctuation">[</span><span class="Identifier">DateTime</span><span class="Punctuation">]</span></pre>
<h2><a class="toc-backref" id="basic-usage-example-fiber-orm-usage" href="#basic-usage-example-fiber-orm-usage">Example Fiber ORM Usage</a></h2><p>Using Fiber ORM we can generate a data access layer with:</p>
<pre class="listing"><span class="Comment"># db.nim</span>
<span class="Keyword">import</span> <span class="Identifier">fiber_orm</span>
<span class="Keyword">import</span> <span class="Operator">./</span><span class="Identifier">models</span><span class="Operator">.</span><span class="Identifier">nim</span>
<span class="Keyword">type</span> <span class="Identifier">TodoDB</span><span class="Operator">*</span> <span class="Operator">=</span> <span class="Identifier">DbConnPool</span>
<span class="Keyword">proc</span> <span class="Identifier">initDb</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">connString</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span> <span class="Operator">=</span>
<span class="Identifier">result</span> <span class="Operator">=</span> <span class="Identifier">fiber_orm</span><span class="Operator">.</span><span class="Identifier">initPool</span><span class="Punctuation">(</span>
<span class="Identifier">connect</span> <span class="Operator">=</span> <span class="Keyword">proc</span><span class="Punctuation">(</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">DbConn</span> <span class="Operator">=</span> <span class="Identifier">open</span><span class="Punctuation">(</span><span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="Identifier">connString</span><span class="Punctuation">)</span><span class="Punctuation">,</span>
<span class="Identifier">poolSize</span> <span class="Operator">=</span> <span class="DecNumber">20</span><span class="Punctuation">,</span>
<span class="Identifier">hardCap</span> <span class="Operator">=</span> <span class="Identifier">false</span><span class="Punctuation">)</span>
<span class="Identifier">generateProcsForModels</span><span class="Punctuation">(</span><span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Punctuation">[</span><span class="Identifier">TodoItem</span><span class="Punctuation">,</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">]</span><span class="Punctuation">)</span>
<span class="Identifier">generateLookup</span><span class="Punctuation">(</span><span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">,</span> <span class="Operator">@</span><span class="Punctuation">[</span><span class="StringLit">&quot;todoItemId&quot;</span><span class="Punctuation">]</span><span class="Punctuation">)</span></pre><p>This will generate the following procedures:</p>
<pre class="listing"><span class="Keyword">proc</span> <span class="Identifier">getTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">getAllTodoItems</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TodoItem</span><span class="Punctuation">]</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">createTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">updateTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">findTodoItemsWhere</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">whereClause</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">,</span>
<span class="Identifier">values</span><span class="Punctuation">:</span> <span class="Identifier">varargs</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">,</span> <span class="Identifier">dbFormat</span><span class="Punctuation">]</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TodoItem</span><span class="Punctuation">]</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">getTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">getAllTimeEntries</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TimeEntry</span><span class="Punctuation">]</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">createTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">updateTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TimeEntry</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTimeEntry</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">findTimeEntriesWhere</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">whereClause</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">,</span>
<span class="Identifier">values</span><span class="Punctuation">:</span> <span class="Identifier">varargs</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">,</span> <span class="Identifier">dbFormat</span><span class="Punctuation">]</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TimeEntry</span><span class="Punctuation">]</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">findTimeEntriesByTodoItemId</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">todoItemId</span><span class="Punctuation">:</span> <span class="Identifier">UUID</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TimeEntry</span><span class="Punctuation">]</span><span class="Punctuation">;</span></pre>
<h1><a class="toc-backref" id="objectminusrelational-modeling" href="#objectminusrelational-modeling">Object-Relational Modeling</a></h1>
<h2><a class="toc-backref" id="objectminusrelational-modeling-model-class" href="#objectminusrelational-modeling-model-class">Model Class</a></h2><p>Fiber ORM uses simple Nim <tt class="docutils literal"><span class="pre"><span class="Keyword">object</span></span></tt>s and <tt class="docutils literal"><span class="pre"><span class="Keyword">ref</span> <span class="Keyword">object</span></span></tt>s as model classes. Fiber ORM expects there to be one table for each model class.</p>
<h3><a class="toc-backref" id="model-class-name-mapping" href="#model-class-name-mapping">Name Mapping</a></h3><p>Fiber ORM uses <tt class="docutils literal"><span class="pre"><span class="Identifier">snake_case</span></span></tt> for database identifiers (column names, table names, etc.) and <tt class="docutils literal"><span class="pre"><span class="Identifier">camelCase</span></span></tt> for Nim identifiers. We automatically convert model names to and from table names (<tt class="docutils literal"><span class="pre"><span class="Identifier">TodoItem</span></span></tt> &lt;-&gt; <tt class="docutils literal"><span class="pre"><span class="Identifier">todo_items</span></span></tt>), as well as column names (<tt class="docutils literal"><span class="pre"><span class="Identifier">userId</span></span></tt> &lt;-&gt; <tt class="docutils literal"><span class="pre"><span class="Identifier">user_id</span></span></tt>).</p>
<p>Notice that table names are automatically pluralized from model class names. In the above example, you have:</p>
<table border="1" class="docutils"><tr><th>Model Class</th><th>Table Name</th></tr>
<tr><td>TodoItem</td><td>todo_items</td></tr>
<tr><td>TimeEntry</td><td>time_entries</td></tr>
</table><p>Because Nim is style-insensitive, you can generall refer to model classes and fields using <tt class="docutils literal"><span class="pre"><span class="Identifier">snake_case</span></span></tt>, <tt class="docutils literal"><span class="pre"><span class="Identifier">camelCase</span></span></tt>, or <tt class="docutils literal"><span class="pre"><span class="Identifier">PascalCase</span></span></tt> in your code and expect Fiber ORM to be able to map the names to DB identifier names properly (though FiberORM will always use <tt class="docutils literal"><span class="pre"><span class="Identifier">camelCase</span></span></tt> internally).</p>
<p>See the <a class="reference external" href="fiber_orm/util.html#identNameToDb,string">identNameToDb</a>, <a class="reference external" href="fiber_orm/util.html#dbNameToIdent,string">dbNameToIdent</a>, <a class="reference external" href="fiber_orm/util.html#tableName,type">tableName</a> and <a class="reference external" href="fiber_orm/util.html#dbFormat,DateTime">dbFormat</a> procedures in the <a class="reference internal" href="#fiber">fiber</a> module for details.</p>
<h3><a class="toc-backref" id="model-class-id-field" href="#model-class-id-field">ID Field</a></h3><p>Fiber ORM expects every model class to have a field named <tt class="docutils literal"><span class="pre"><span class="Identifier">id</span></span></tt>, with a corresponding <tt class="docutils literal"><span class="pre"><span class="Identifier">id</span></span></tt> column in the model table. This field must be either a <tt class="docutils literal"><span class="pre"><span class="Identifier">string</span></span></tt>, <tt class="docutils literal"><span class="pre"><span class="Identifier">integer</span></span></tt>, or <a class="reference external" href="https://github.com/pragmagic/uuids">UUID</a>.</p>
<p>When creating a new record the <tt class="docutils literal"><span class="pre"><span class="Identifier">id</span></span></tt> field will be omitted if it is empty (<a class="reference external" href="https://nim-lang.org/docs/options.html#isNone,Option[T]">Option.isNone</a>, <a class="reference external" href="https://github.com/pragmagic/uuids/blob/8cb8720b567c6bcb261bd1c0f7491bdb5209ad06/uuids.nim#L72">UUID.isZero</a>, value of <tt class="docutils literal"><span class="pre"><span class="DecNumber">0</span></span></tt>, or only whitespace). This is intended to allow for cases like the example where the database may generate an ID when a new record is inserted. If a non-zero value is provided, the create call will include the <tt class="docutils literal"><span class="pre"><span class="Identifier">id</span></span></tt> field in the <tt class="docutils literal"><span class="pre"><span class="Identifier">INSERT</span></span></tt> query.</p>
<p>For example, to allow the database to create the id:</p>
<pre class="listing"><span class="Keyword">let</span> <span class="Identifier">item</span> <span class="Operator">=</span> <span class="Identifier">TodoItem</span><span class="Punctuation">(</span>
<span class="Identifier">owner</span><span class="Punctuation">:</span> <span class="StringLit">&quot;John Mann&quot;</span><span class="Punctuation">,</span>
<span class="Identifier">summary</span><span class="Punctuation">:</span> <span class="StringLit">&quot;Create a grocery list.&quot;</span><span class="Punctuation">,</span>
<span class="Identifier">details</span><span class="Punctuation">:</span> <span class="Identifier">none</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">]</span><span class="Punctuation">(</span><span class="Punctuation">)</span><span class="Punctuation">,</span>
<span class="Identifier">priority</span><span class="Punctuation">:</span> <span class="DecNumber">0</span><span class="Punctuation">,</span>
<span class="Identifier">relatedTodoItemIds</span><span class="Punctuation">:</span> <span class="Operator">@</span><span class="Punctuation">[</span><span class="Punctuation">]</span><span class="Punctuation">)</span>
<span class="Keyword">let</span> <span class="Identifier">itemWithId</span> <span class="Operator">=</span> <span class="Identifier">db</span><span class="Operator">.</span><span class="Identifier">createTodoItem</span><span class="Punctuation">(</span><span class="Identifier">item</span><span class="Punctuation">)</span>
<span class="Identifier">echo</span> <span class="Operator">$</span><span class="Identifier">itemWithId</span><span class="Operator">.</span><span class="Identifier">id</span> <span class="Comment"># generated in the database</span></pre><p>And to create it in code:</p>
<pre class="listing"><span class="Keyword">import</span> <span class="Identifier">uuids</span>
<span class="Keyword">let</span> <span class="Identifier">item</span> <span class="Operator">=</span> <span class="Identifier">TodoItem</span><span class="Punctuation">(</span>
<span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">genUUID</span><span class="Punctuation">(</span><span class="Punctuation">)</span><span class="Punctuation">,</span>
<span class="Identifier">owner</span><span class="Punctuation">:</span> <span class="StringLit">&quot;John Mann&quot;</span><span class="Punctuation">,</span>
<span class="Identifier">summary</span><span class="Punctuation">:</span> <span class="StringLit">&quot;Create a grocery list.&quot;</span><span class="Punctuation">,</span>
<span class="Identifier">details</span><span class="Punctuation">:</span> <span class="Identifier">none</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">]</span><span class="Punctuation">(</span><span class="Punctuation">)</span><span class="Punctuation">,</span>
<span class="Identifier">priority</span><span class="Punctuation">:</span> <span class="DecNumber">0</span><span class="Punctuation">,</span>
<span class="Identifier">relatedTodoItemIds</span><span class="Punctuation">:</span> <span class="Operator">@</span><span class="Punctuation">[</span><span class="Punctuation">]</span><span class="Punctuation">)</span>
<span class="Keyword">let</span> <span class="Identifier">itemInDb</span> <span class="Operator">=</span> <span class="Identifier">db</span><span class="Operator">.</span><span class="Identifier">createTodoItem</span><span class="Punctuation">(</span><span class="Identifier">item</span><span class="Punctuation">)</span>
<span class="Identifier">echo</span> <span class="Operator">$</span><span class="Identifier">itemInDb</span><span class="Operator">.</span><span class="Identifier">id</span> <span class="Comment"># will be the same as what was provided</span></pre>
<h2><a class="toc-backref" id="objectminusrelational-modeling-supported-data-types" href="#objectminusrelational-modeling-supported-data-types">Supported Data Types</a></h2><p>The following Nim data types are supported by Fiber ORM:</p>
<table border="1" class="docutils"><tr><th>Nim Type</th><th>Postgres Type</th><th>SQLite Type</th></tr>
<tr><td><tt class="docutils literal"><span class="pre"><span class="Identifier">string</span></span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-character.html">varchar</a></td><td></td></tr>
<tr><td><tt class="docutils literal"><span class="pre"><span class="Identifier">int</span></span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-INT">integer</a></td><td></td></tr>
<tr><td><tt class="docutils literal"><span class="pre"><span class="Identifier">float</span></span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-FLOAT">double</a></td><td></td></tr>
<tr><td><tt class="docutils literal"><span class="pre"><span class="Identifier">bool</span></span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-boolean.html">boolean</a></td><td></td></tr>
<tr><td><a class="reference external" href="https://nim-lang.org/docs/times.html#DateTime">DateTime</a></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-datetime.html">timestamp</a></td><td></td></tr>
<tr><td><tt class="docutils literal"><span class="pre"><span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Punctuation">]</span></span></tt></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/arrays.html">array</a></td><td></td></tr>
<tr><td><a class="reference external" href="https://github.com/pragmagic/uuids">UUID</a></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-uuid.html">uuid (pg)</a></td><td></td></tr>
<tr><td><a class="reference external" href="https://nim-lang.org/docs/options.html#Option">Option</a></td><td><em>allows</em> <tt class="docutils literal"><span class="pre"><span class="Identifier">NULL</span></span></tt> <sup><strong><a class="reference internal" href="#footnote-f1">[1]</a></strong></sup></td><td></td></tr>
<tr><td><a class="reference external" href="https://nim-lang.org/docs/json.html#JsonNode">JsonNode</a></td><td><a class="reference external" href="https://www.postgresql.org/docs/current/datatype-json.html">jsonb</a></td><td></td></tr>
</table><hr class="footnote"><div class="footnote-group">
<div id="footnote-f1"><div class="footnote-label"><sup><strong><a href="#footnote-f1">[1]</a></strong></sup></div> &ensp; Note that this implies that all <tt class="docutils literal"><span class="pre"><span class="Identifier">NULL</span></span></tt>-able fields should be typed as optional using <tt class="docutils literal"><span class="pre"><span class="Identifier">Option</span><span class="Punctuation">[</span><span class="Identifier">fieldType</span><span class="Punctuation">]</span></span></tt>. Conversely, any fields with non-optional types should also be constrained to be <tt class="docutils literal"><span class="pre"><span class="Keyword">NOT</span> <span class="Identifier">NULL</span></span></tt> in the database schema.
</div>
</div>
<h1><a class="toc-backref" id="database-object" href="#database-object">Database Object</a></h1><p>Many of the Fiber ORM macros expect a database object type to be passed. In the example above the <a class="reference external" href="fiber_orm/pool.html#DbConnPool">pool.DbConnPool</a> object is used as database object type (aliased as <tt class="docutils literal"><span class="pre"><span class="Identifier">TodoDB</span></span></tt>). This is the intended usage pattern, but anything can be passed as the database object type so long as there is a defined <tt class="docutils literal"><span class="pre"><span class="Identifier">withConn</span></span></tt> template that provides an injected <tt class="docutils literal"><span class="pre"><span class="Identifier">conn</span><span class="Punctuation">:</span> <span class="Identifier">DbConn</span></span></tt> object to the provided statement body.</p>
<p>For example, a valid database object implementation that opens a new connection for every request might look like this:</p>
<pre class="listing"><span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">db_postgres</span>
<span class="Keyword">type</span> <span class="Identifier">TodoDB</span><span class="Operator">*</span> <span class="Operator">=</span> <span class="Keyword">object</span>
<span class="Identifier">connString</span><span class="Punctuation">:</span> <span class="Identifier">string</span>
<span class="Keyword">template</span> <span class="Identifier">withConn</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">stmt</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span> <span class="Operator">=</span>
<span class="Keyword">let</span> <span class="Identifier">conn</span> <span class="Punctuation">{</span><span class="Operator">.</span><span class="Identifier">inject</span><span class="Operator">.</span><span class="Punctuation">}</span> <span class="Operator">=</span> <span class="Identifier">open</span><span class="Punctuation">(</span><span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;&quot;</span><span class="Punctuation">,</span> <span class="Identifier">db</span><span class="Operator">.</span><span class="Identifier">connString</span><span class="Punctuation">)</span>
<span class="Keyword">try</span><span class="Punctuation">:</span> <span class="Identifier">stmt</span>
<span class="Keyword">finally</span><span class="Punctuation">:</span> <span class="Identifier">close</span><span class="Punctuation">(</span><span class="Identifier">conn</span><span class="Punctuation">)</span></pre>
<h1><a class="toc-backref" id="see-also" href="#see-also">See Also</a></h1><p><a class="reference external" href="fiber_orm/pool.html">fiber_orm/pool</a></p>
</p>
<div class="section" id="6">
<h1><a class="toc-backref" href="#6">Imports</a></h1>
<dl class="item">
<a class="reference external" href="fiber_orm/pool.html">fiber_orm/pool</a>, <a class="reference external" href="fiber_orm/util.html">fiber_orm/util</a>
</dl></div>
<div class="section" id="7">
<h1><a class="toc-backref" href="#7">Types</a></h1>
<dl class="item">
<div id="NotFoundError">
<dt><pre><a href="fiber_orm.html#NotFoundError"><span class="Identifier">NotFoundError</span></a> <span class="Other">=</span> <span class="Keyword">object</span> <span class="Keyword">of</span> <span class="Identifier">CatchableError</span></pre></dt>
<dd>
Error type raised when no record matches a given ID
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L299"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L299" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="12">
<h1><a class="toc-backref" href="#12">Procs</a></h1>
<dl class="item">
<div id="createRecord,DbConn,T">
<dt><pre><span class="Keyword">proc</span> <a href="#createRecord%2CDbConn%2CT"><span class="Identifier">createRecord</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">rec</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span></pre></dt>
<dd>
<p>Create a new record. <tt class="docutils literal"><span class="pre"><span class="Identifier">rec</span></span></tt> is expected to be a <a class="reference external" href="#objectminusrelational-modeling-model-class">model class</a>. The <tt class="docutils literal"><span class="pre"><span class="Identifier">id</span></span></tt> field is only set if it is non-empty (see <a class="reference external" href="#model-class-id-field">ID Field</a> for details).</p>
<p>Returns the newly created record.</p>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L314"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L314" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="deleteRecord,DbConn,T">
<dt><pre><span class="Keyword">proc</span> <a href="#deleteRecord%2CDbConn%2CT"><span class="Identifier">deleteRecord</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">rec</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">bool</span></pre></dt>
<dd>
Delete a record by <a class="reference external" href="#model-class-id-field">id</a>.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L359"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L359" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="initPool,proc),int,string">
<dt><pre><span class="Keyword">proc</span> <a href="#initPool%2Cproc%29%2Cint%2Cstring"><span class="Identifier">initPool</span></a><span class="Other">(</span><span class="Identifier">connect</span><span class="Other">:</span> <span class="Keyword">proc</span> <span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">poolSize</span> <span class="Other">=</span> <span class="DecNumber">10</span><span class="Other">;</span> <span class="Identifier">hardCap</span> <span class="Other">=</span> <span class="Identifier">false</span><span class="Other">;</span>
<span class="Identifier">healthCheckQuery</span> <span class="Other">=</span> <span class="StringLit">&quot;SELECT \'true\' AS alive&quot;</span><span class="Other">)</span><span class="Other">:</span> <a href="fiber_orm/pool.html#DbConnPool"><span class="Identifier">DbConnPool</span></a> {.
<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
Initialize a new DbConnPool. See the <tt class="docutils literal"><span class="pre"><span class="Identifier">initDb</span></span></tt> procedure in the <a class="reference external" href="#basic-usage-example-fiber-orm-usage">Example Fiber ORM Usage</a> for an example<ul class="simple"><li><tt class="docutils literal"><span class="pre"><span class="Identifier">connect</span></span></tt> must be a factory which creates a new <tt class="docutils literal"><span class="pre"><span class="Identifier">DbConn</span></span></tt>.</li>
<li><tt class="docutils literal"><span class="pre"><span class="Identifier">poolSize</span></span></tt> sets the desired capacity of the connection pool.</li>
<li><p><tt class="docutils literal"><span class="pre"><span class="Identifier">hardCap</span></span></tt> defaults to <tt class="docutils literal"><span class="pre"><span class="Identifier">false</span></span></tt>. When <tt class="docutils literal"><span class="pre"><span class="Identifier">false</span></span></tt>, the pool can grow beyond the configured capacity, but will release connections down to the its capacity (no less than <tt class="docutils literal"><span class="pre"><span class="Identifier">poolSize</span></span></tt>).</p>
<p>When <tt class="docutils literal"><span class="pre"><span class="Identifier">true</span></span></tt> the pool will not create more than its configured capacity. It a connection is requested, none are free, and the pool is at capacity, this will result in an Error being raised.</p>
</li>
<li><tt class="docutils literal"><span class="pre"><span class="Identifier">healthCheckQuery</span></span></tt> should be a simple and fast SQL query that the pool can use to test the liveliness of pooled connections.</li>
</ul>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L539"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L539" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="updateRecord,DbConn,T">
<dt><pre><span class="Keyword">proc</span> <a href="#updateRecord%2CDbConn%2CT"><span class="Identifier">updateRecord</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">rec</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">bool</span></pre></dt>
<dd>
Update a record by id. <tt class="docutils literal"><span class="pre"><span class="Identifier">rec</span></span></tt> is expected to be a <a class="reference external" href="#objectminusrelational-modeling-model-class">model class</a>.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L337"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L337" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="17">
<h1><a class="toc-backref" href="#17">Macros</a></h1>
<dl class="item">
<div id="generateLookup.m,type,type,seq[string]">
<dt><pre><span class="Keyword">macro</span> <a href="#generateLookup.m%2Ctype%2Ctype%2Cseq%5Bstring%5D"><span class="Identifier">generateLookup</span></a><span class="Other">(</span><span class="Identifier">dbType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">fields</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Create a lookup procedure for a given set of field names. For example, given the TODO database demostrated above,<pre class="listing"><span class="Identifier">generateLookup</span><span class="Punctuation">(</span><span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">TodoItem</span><span class="Punctuation">,</span> <span class="Punctuation">[</span><span class="StringLit">&quot;owner&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;priority&quot;</span><span class="Punctuation">]</span><span class="Punctuation">)</span></pre><p>will generate the following procedure:</p>
<pre class="listing"><span class="Keyword">proc</span> <span class="Identifier">findTodoItemsByOwnerAndPriority</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">SampleDB</span><span class="Punctuation">,</span>
<span class="Identifier">owner</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">,</span> <span class="Identifier">priority</span><span class="Punctuation">:</span> <span class="Identifier">int</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">seq</span><span class="Punctuation">[</span><span class="Identifier">TodoItem</span><span class="Punctuation">]</span></pre>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L468"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L468" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="generateProcsForFieldLookups.m,type,openArray[tuple[type,seq[string]]]">
<dt><pre><span class="Keyword">macro</span> <a href="#generateProcsForFieldLookups.m%2Ctype%2CopenArray%5Btuple%5Btype%2Cseq%5Bstring%5D%5D%5D"><span class="Identifier">generateProcsForFieldLookups</span></a><span class="Other">(</span><span class="Identifier">dbType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">modelsAndFields</span><span class="Other">:</span> <span class="Identifier">openArray</span><span class="Other">[</span>
<span class="Keyword">tuple</span><span class="Other">[</span><span class="Identifier">t</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">,</span> <span class="Identifier">fields</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">]</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L510"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L510" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="generateProcsForModels.m,type,openArray[type]">
<dt><pre><span class="Keyword">macro</span> <a href="#generateProcsForModels.m%2Ctype%2CopenArray%5Btype%5D"><span class="Identifier">generateProcsForModels</span></a><span class="Other">(</span><span class="Identifier">dbType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">modelTypes</span><span class="Other">:</span> <span class="Identifier">openArray</span><span class="Other">[</span><span class="Identifier">type</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Generate all standard access procedures for the given model types. For a <a class="reference external" href="#objectminusrelational-modeling-model-class">model class</a> named <tt class="docutils literal"><span class="pre"><span class="Identifier">TodoItem</span></span></tt>, this will generate the following procedures:<pre class="listing"><span class="Keyword">proc</span> <span class="Identifier">getTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">idType</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">getAllTodoItems</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">createTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">deleteTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">id</span><span class="Punctuation">:</span> <span class="Identifier">idType</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">updateTodoItem</span><span class="Operator">*</span><span class="Punctuation">(</span><span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">rec</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">bool</span><span class="Punctuation">;</span>
<span class="Keyword">proc</span> <span class="Identifier">findTodoItemsWhere</span><span class="Operator">*</span><span class="Punctuation">(</span>
<span class="Identifier">db</span><span class="Punctuation">:</span> <span class="Identifier">TodoDB</span><span class="Punctuation">,</span> <span class="Identifier">whereClause</span><span class="Punctuation">:</span> <span class="Identifier">string</span><span class="Punctuation">,</span> <span class="Identifier">values</span><span class="Punctuation">:</span> <span class="Identifier">varargs</span><span class="Punctuation">[</span><span class="Identifier">string</span><span class="Punctuation">]</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">TodoItem</span><span class="Punctuation">;</span></pre><p><tt class="docutils literal"><span class="pre"><span class="Identifier">dbType</span></span></tt> is expected to be some type that has a defined <tt class="docutils literal"><span class="pre"><span class="Identifier">withConn</span></span></tt> procedure (see <a class="reference external" href="#database-object">Database Object</a> for details).</p>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L414"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L414" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="18">
<h1><a class="toc-backref" href="#18">Templates</a></h1>
<dl class="item">
<div id="deleteRecord.t,DbConn,type,typed">
<dt><pre><span class="Keyword">template</span> <a href="#deleteRecord.t%2CDbConn%2Ctype%2Ctyped"><span class="Identifier">deleteRecord</span></a><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">id</span><span class="Other">:</span> <span class="Identifier">typed</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Delete a record by id.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L353"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L353" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="findRecordsBy.t,DbConn,type,seq[tuple[string,string]]">
<dt><pre><span class="Keyword">template</span> <a href="#findRecordsBy.t%2CDbConn%2Ctype%2Cseq%5Btuple%5Bstring%2Cstring%5D%5D"><span class="Identifier">findRecordsBy</span></a><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span>
<span class="Identifier">lookups</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><span class="Keyword">tuple</span><span class="Other">[</span><span class="Identifier">field</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">,</span> <span class="Identifier">value</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">]</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Find all records matching the provided lookup values.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L403"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L403" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="findRecordsWhere.t,DbConn,type,string,varargs[string,dbFormat]">
<dt><pre><span class="Keyword">template</span> <a href="#findRecordsWhere.t%2CDbConn%2Ctype%2Cstring%2Cvarargs%5Bstring%2CdbFormat%5D"><span class="Identifier">findRecordsWhere</span></a><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">whereClause</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span>
<span class="Identifier">values</span><span class="Other">:</span> <span class="Identifier">varargs</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">,</span> <span class="Identifier">dbFormat</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Find all records matching a given <tt class="docutils literal"><span class="pre"><span class="Identifier">WHERE</span></span></tt> clause. The number of elements in the <tt class="docutils literal"><span class="pre"><span class="Identifier">values</span></span></tt> array must match the number of placeholders (<tt class="docutils literal"><span class="pre"><span class="Operator">?</span></span></tt>) in the provided <tt class="docutils literal"><span class="pre"><span class="Identifier">WHERE</span></span></tt> clause.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L382"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L382" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="getAllRecords.t,DbConn,type">
<dt><pre><span class="Keyword">template</span> <a href="#getAllRecords.t%2CDbConn%2Ctype"><span class="Identifier">getAllRecords</span></a><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Fetch all records of the given type.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L394"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L394" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="getRecord.t,DbConn,type,typed">
<dt><pre><span class="Keyword">template</span> <a href="#getRecord.t%2CDbConn%2Ctype%2Ctyped"><span class="Identifier">getRecord</span></a><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <span class="Identifier">DbConn</span><span class="Other">;</span> <span class="Identifier">modelType</span><span class="Other">:</span> <span class="Identifier">type</span><span class="Other">;</span> <span class="Identifier">id</span><span class="Other">:</span> <span class="Identifier">typed</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
<dd>
Fetch a record by id.
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L367"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L367" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
<div id="inTransaction.t,DbConnPool,untyped">
<dt><pre><span class="Keyword">template</span> <a href="#inTransaction.t%2CDbConnPool%2Cuntyped"><span class="Identifier">inTransaction</span></a><span class="Other">(</span><span class="Identifier">db</span><span class="Other">:</span> <a href="fiber_orm/pool.html#DbConnPool"><span class="Identifier">DbConnPool</span></a><span class="Other">;</span> <span class="Identifier">body</span><span class="Other">:</span> <span class="Identifier">untyped</span><span class="Other">)</span></pre></dt>
<dd>
&nbsp;&nbsp;<a
href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L567"
class="link-seesrc" target="_blank">Source</a>
&nbsp;&nbsp;<a href="https://github.com/jdbernard/fiber-orm/edit/devel/src/fiber_orm.nim#L567" class="link-seesrc" target="_blank" >Edit</a>
</dd>
</div>
</dl></div>
<div class="section" id="19">
<h1><a class="toc-backref" href="#19">Exports</a></h1>
<dl class="item">
<a href="fiber_orm/pool.html#DbConnPoolConfig"><span class="Identifier">DbConnPoolConfig</span></a>, <a href="fiber_orm/pool.html#initDbConnPool,DbConnPoolConfig"><span class="Identifier">initDbConnPool</span></a>, <a href="fiber_orm/pool.html#withConn.t,DbConnPool,untyped"><span class="Identifier">withConn</span></a>, <a href="fiber_orm/pool.html#take,DbConnPool"><span class="Identifier">take</span></a>, <a href="fiber_orm/pool.html#DbConnPool"><span class="Identifier">DbConnPool</span></a>, <a href="fiber_orm/pool.html#release,DbConnPool,int"><span class="Identifier">release</span></a>, <a href="fiber_orm/util.html#columnNamesForModel.m,typed"><span class="Identifier">columnNamesForModel</span></a>, <a href="fiber_orm/util.html#dbFormat,DateTime"><span class="Identifier">dbFormat</span></a>, <a href="fiber_orm/util.html#dbFormat,seq[T]"><span class="Identifier">dbFormat</span></a>, <a href="fiber_orm/util.html#dbFormat,string"><span class="Identifier">dbFormat</span></a>, <a href="fiber_orm/util.html#dbFormat,T"><span class="Identifier">dbFormat</span></a>, <a href="fiber_orm/util.html#dbNameToIdent,string"><span class="Identifier">dbNameToIdent</span></a>, <a href="fiber_orm/util.html#identNameToDb,string"><span class="Identifier">identNameToDb</span></a>, <a href="fiber_orm/util.html#modelName.m,type"><span class="Identifier">modelName</span></a>, <a href="fiber_orm/util.html#modelName.m"><span class="Identifier">modelName</span></a>, <a href="fiber_orm/util.html#rowToModel.m,typed,seq[string]"><span class="Identifier">rowToModel</span></a>, <a href="fiber_orm/util.html#tableName,type"><span class="Identifier">tableName</span></a>, <a href="fiber_orm/util.html#tableName,T"><span class="Identifier">tableName</span></a>
</dl></div>
</div>
</div>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2022-09-04 02:31:20 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

1016
docs/nimdoc.out.css Normal file

File diff suppressed because one or more lines are too long

246
docs/theindex.html Normal file
View File

@ -0,0 +1,246 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
<!-- Google fonts -->
<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
<!-- CSS -->
<title>Index</title>
<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
<script type="text/javascript" src="dochack.js"></script>
<script type="text/javascript">
function main() {
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
}
}
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
if (toggleSwitch !== null) {
toggleSwitch.addEventListener('change', switchTheme, false);
}
var currentTheme = localStorage.getItem('theme');
if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) {
currentTheme = 'dark';
}
if (currentTheme) {
document.documentElement.setAttribute('data-theme', currentTheme);
if (currentTheme === 'dark' && toggleSwitch !== null) {
toggleSwitch.checked = true;
}
}
}
window.addEventListener('DOMContentLoaded', main);
</script>
</head>
<body>
<div class="document" id="documentId">
<div class="container">
<h1 class="title">Index</h1>
Modules: <a href="fiber_orm.html">fiber_orm</a>, <a href="fiber_orm/pool.html">fiber_orm/pool</a>, <a href="fiber_orm/util.html">fiber_orm/util</a>.<br/><p /><h2>API symbols</h2>
<dl><dt><a name="columnNamesForModel" href="#columnNamesForModel"><span>columnNamesForModel:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: columnNamesForModel(modelType: typed): seq[string]" href="fiber_orm/util.html#columnNamesForModel.m%2Ctyped">util: columnNamesForModel(modelType: typed): seq[string]</a></li>
</ul></dd>
<dt><a name="createParseStmt" href="#createParseStmt"><span>createParseStmt:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: createParseStmt(t, value: NimNode): NimNode" href="fiber_orm/util.html#createParseStmt%2CNimNode%2CNimNode">util: createParseStmt(t, value: NimNode): NimNode</a></li>
</ul></dd>
<dt><a name="createRecord" href="#createRecord"><span>createRecord:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: createRecord[T](db: DbConn; rec: T): T" href="fiber_orm.html#createRecord%2CDbConn%2CT">fiber_orm: createRecord[T](db: DbConn; rec: T): T</a></li>
</ul></dd>
<dt><a name="DbConnPool" href="#DbConnPool"><span>DbConnPool:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="pool: DbConnPool" href="fiber_orm/pool.html#DbConnPool">pool: DbConnPool</a></li>
</ul></dd>
<dt><a name="DbConnPoolConfig" href="#DbConnPoolConfig"><span>DbConnPoolConfig:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="pool: DbConnPoolConfig" href="fiber_orm/pool.html#DbConnPoolConfig">pool: DbConnPoolConfig</a></li>
</ul></dd>
<dt><a name="dbFormat" href="#dbFormat"><span>dbFormat:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: dbFormat(dt: DateTime): string" href="fiber_orm/util.html#dbFormat%2CDateTime">util: dbFormat(dt: DateTime): string</a></li>
<li><a class="reference external"
data-doc-search-tag="util: dbFormat[T](list: seq[T]): string" href="fiber_orm/util.html#dbFormat%2Cseq%5BT%5D">util: dbFormat[T](list: seq[T]): string</a></li>
<li><a class="reference external"
data-doc-search-tag="util: dbFormat(s: string): string" href="fiber_orm/util.html#dbFormat%2Cstring">util: dbFormat(s: string): string</a></li>
<li><a class="reference external"
data-doc-search-tag="util: dbFormat[T](item: T): string" href="fiber_orm/util.html#dbFormat%2CT">util: dbFormat[T](item: T): string</a></li>
</ul></dd>
<dt><a name="dbNameToIdent" href="#dbNameToIdent"><span>dbNameToIdent:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: dbNameToIdent(name: string): string" href="fiber_orm/util.html#dbNameToIdent%2Cstring">util: dbNameToIdent(name: string): string</a></li>
</ul></dd>
<dt><a name="deleteRecord" href="#deleteRecord"><span>deleteRecord:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: deleteRecord[T](db: DbConn; rec: T): bool" href="fiber_orm.html#deleteRecord%2CDbConn%2CT">fiber_orm: deleteRecord[T](db: DbConn; rec: T): bool</a></li>
<li><a class="reference external"
data-doc-search-tag="fiber_orm: deleteRecord(db: DbConn; modelType: type; id: typed): untyped" href="fiber_orm.html#deleteRecord.t%2CDbConn%2Ctype%2Ctyped">fiber_orm: deleteRecord(db: DbConn; modelType: type; id: typed): untyped</a></li>
</ul></dd>
<dt><a name="findRecordsBy" href="#findRecordsBy"><span>findRecordsBy:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: findRecordsBy(db: DbConn; modelType: type;
lookups: seq[tuple[field: string, value: string]]): untyped" href="fiber_orm.html#findRecordsBy.t%2CDbConn%2Ctype%2Cseq%5Btuple%5Bstring%2Cstring%5D%5D">fiber_orm: findRecordsBy(db: DbConn; modelType: type;
lookups: seq[tuple[field: string, value: string]]): untyped</a></li>
</ul></dd>
<dt><a name="findRecordsWhere" href="#findRecordsWhere"><span>findRecordsWhere:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: findRecordsWhere(db: DbConn; modelType: type; whereClause: string;
values: varargs[string, dbFormat]): untyped" href="fiber_orm.html#findRecordsWhere.t%2CDbConn%2Ctype%2Cstring%2Cvarargs%5Bstring%2CdbFormat%5D">fiber_orm: findRecordsWhere(db: DbConn; modelType: type; whereClause: string;
values: varargs[string, dbFormat]): untyped</a></li>
</ul></dd>
<dt><a name="generateLookup" href="#generateLookup"><span>generateLookup:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: generateLookup(dbType: type; modelType: type; fields: seq[string]): untyped" href="fiber_orm.html#generateLookup.m%2Ctype%2Ctype%2Cseq%5Bstring%5D">fiber_orm: generateLookup(dbType: type; modelType: type; fields: seq[string]): untyped</a></li>
</ul></dd>
<dt><a name="generateProcsForFieldLookups" href="#generateProcsForFieldLookups"><span>generateProcsForFieldLookups:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: generateProcsForFieldLookups(dbType: type; modelsAndFields: openArray[
tuple[t: type, fields: seq[string]]]): untyped" href="fiber_orm.html#generateProcsForFieldLookups.m%2Ctype%2CopenArray%5Btuple%5Btype%2Cseq%5Bstring%5D%5D%5D">fiber_orm: generateProcsForFieldLookups(dbType: type; modelsAndFields: openArray[
tuple[t: type, fields: seq[string]]]): untyped</a></li>
</ul></dd>
<dt><a name="generateProcsForModels" href="#generateProcsForModels"><span>generateProcsForModels:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: generateProcsForModels(dbType: type; modelTypes: openArray[type]): untyped" href="fiber_orm.html#generateProcsForModels.m%2Ctype%2CopenArray%5Btype%5D">fiber_orm: generateProcsForModels(dbType: type; modelTypes: openArray[type]): untyped</a></li>
</ul></dd>
<dt><a name="getAllRecords" href="#getAllRecords"><span>getAllRecords:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: getAllRecords(db: DbConn; modelType: type): untyped" href="fiber_orm.html#getAllRecords.t%2CDbConn%2Ctype">fiber_orm: getAllRecords(db: DbConn; modelType: type): untyped</a></li>
</ul></dd>
<dt><a name="getRecord" href="#getRecord"><span>getRecord:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: getRecord(db: DbConn; modelType: type; id: typed): untyped" href="fiber_orm.html#getRecord.t%2CDbConn%2Ctype%2Ctyped">fiber_orm: getRecord(db: DbConn; modelType: type; id: typed): untyped</a></li>
</ul></dd>
<dt><a name="identNameToDb" href="#identNameToDb"><span>identNameToDb:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: identNameToDb(name: string): string" href="fiber_orm/util.html#identNameToDb%2Cstring">util: identNameToDb(name: string): string</a></li>
</ul></dd>
<dt><a name="initDbConnPool" href="#initDbConnPool"><span>initDbConnPool:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="pool: initDbConnPool(cfg: DbConnPoolConfig): DbConnPool" href="fiber_orm/pool.html#initDbConnPool%2CDbConnPoolConfig">pool: initDbConnPool(cfg: DbConnPoolConfig): DbConnPool</a></li>
</ul></dd>
<dt><a name="initPool" href="#initPool"><span>initPool:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: initPool(connect: proc (): DbConn; poolSize = 10; hardCap = false;
healthCheckQuery = &quot;SELECT \&apos;true\&apos; AS alive&quot;): DbConnPool" href="fiber_orm.html#initPool%2Cproc%29%2Cint%2Cstring">fiber_orm: initPool(connect: proc (): DbConn; poolSize = 10; hardCap = false;
healthCheckQuery = &quot;SELECT \&apos;true\&apos; AS alive&quot;): DbConnPool</a></li>
</ul></dd>
<dt><a name="inTransaction" href="#inTransaction"><span>inTransaction:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: inTransaction(db: DbConnPool; body: untyped)" href="fiber_orm.html#inTransaction.t%2CDbConnPool%2Cuntyped">fiber_orm: inTransaction(db: DbConnPool; body: untyped)</a></li>
</ul></dd>
<dt><a name="listFields" href="#listFields"><span>listFields:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: listFields(t: typed): untyped" href="fiber_orm/util.html#listFields.m%2Ctyped">util: listFields(t: typed): untyped</a></li>
</ul></dd>
<dt><a name="modelName" href="#modelName"><span>modelName:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: modelName(model: object): string" href="fiber_orm/util.html#modelName.m">util: modelName(model: object): string</a></li>
<li><a class="reference external"
data-doc-search-tag="util: modelName(modelType: type): string" href="fiber_orm/util.html#modelName.m%2Ctype">util: modelName(modelType: type): string</a></li>
</ul></dd>
<dt><a name="MutateClauses" href="#MutateClauses"><span>MutateClauses:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: MutateClauses" href="fiber_orm/util.html#MutateClauses">util: MutateClauses</a></li>
</ul></dd>
<dt><a name="NotFoundError" href="#NotFoundError"><span>NotFoundError:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: NotFoundError" href="fiber_orm.html#NotFoundError">fiber_orm: NotFoundError</a></li>
</ul></dd>
<dt><a name="parseDbArray" href="#parseDbArray"><span>parseDbArray:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: parseDbArray(val: string): seq[string]" href="fiber_orm/util.html#parseDbArray%2Cstring">util: parseDbArray(val: string): seq[string]</a></li>
</ul></dd>
<dt><a name="parsePGDatetime" href="#parsePGDatetime"><span>parsePGDatetime:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: parsePGDatetime(val: string): DateTime" href="fiber_orm/util.html#parsePGDatetime%2Cstring">util: parsePGDatetime(val: string): DateTime</a></li>
</ul></dd>
<dt><a name="pluralize" href="#pluralize"><span>pluralize:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: pluralize(name: string): string" href="fiber_orm/util.html#pluralize%2Cstring">util: pluralize(name: string): string</a></li>
</ul></dd>
<dt><a name="populateMutateClauses" href="#populateMutateClauses"><span>populateMutateClauses:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: populateMutateClauses(t: typed; newRecord: bool; mc: var MutateClauses): untyped" href="fiber_orm/util.html#populateMutateClauses.m%2Ctyped%2Cbool%2CMutateClauses">util: populateMutateClauses(t: typed; newRecord: bool; mc: var MutateClauses): untyped</a></li>
</ul></dd>
<dt><a name="release" href="#release"><span>release:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="pool: release(pool: DbConnPool; connId: int): void" href="fiber_orm/pool.html#release%2CDbConnPool%2Cint">pool: release(pool: DbConnPool; connId: int): void</a></li>
</ul></dd>
<dt><a name="rowToModel" href="#rowToModel"><span>rowToModel:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: rowToModel(modelType: typed; row: seq[string]): untyped" href="fiber_orm/util.html#rowToModel.m%2Ctyped%2Cseq%5Bstring%5D">util: rowToModel(modelType: typed; row: seq[string]): untyped</a></li>
</ul></dd>
<dt><a name="tableName" href="#tableName"><span>tableName:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: tableName[T](rec: T): string" href="fiber_orm/util.html#tableName%2CT">util: tableName[T](rec: T): string</a></li>
<li><a class="reference external"
data-doc-search-tag="util: tableName(modelType: type): string" href="fiber_orm/util.html#tableName%2Ctype">util: tableName(modelType: type): string</a></li>
</ul></dd>
<dt><a name="take" href="#take"><span>take:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="pool: take(pool: DbConnPool): tuple[id: int, conn: DbConn]" href="fiber_orm/pool.html#take%2CDbConnPool">pool: take(pool: DbConnPool): tuple[id: int, conn: DbConn]</a></li>
</ul></dd>
<dt><a name="typeOfColumn" href="#typeOfColumn"><span>typeOfColumn:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: typeOfColumn(modelType: NimNode; colName: string): NimNode" href="fiber_orm/util.html#typeOfColumn%2CNimNode%2Cstring">util: typeOfColumn(modelType: NimNode; colName: string): NimNode</a></li>
</ul></dd>
<dt><a name="updateRecord" href="#updateRecord"><span>updateRecord:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="fiber_orm: updateRecord[T](db: DbConn; rec: T): bool" href="fiber_orm.html#updateRecord%2CDbConn%2CT">fiber_orm: updateRecord[T](db: DbConn; rec: T): bool</a></li>
</ul></dd>
<dt><a name="walkFieldDefs" href="#walkFieldDefs"><span>walkFieldDefs:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="util: walkFieldDefs(t: NimNode; body: untyped)" href="fiber_orm/util.html#walkFieldDefs.t%2CNimNode%2Cuntyped">util: walkFieldDefs(t: NimNode; body: untyped)</a></li>
</ul></dd>
<dt><a name="withConn" href="#withConn"><span>withConn:</span></a></dt><dd><ul class="simple">
<li><a class="reference external"
data-doc-search-tag="pool: withConn(pool: DbConnPool; stmt: untyped): untyped" href="fiber_orm/pool.html#withConn.t%2CDbConnPool%2Cuntyped">pool: withConn(pool: DbConnPool; stmt: untyped): untyped</a></li>
</ul></dd>
</dl>
<div class="row">
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small style="color: var(--hint);">Made with Nim. Generated: 2022-09-04 02:31:20 UTC</small>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,6 +1,6 @@
# Package
version = "1.0.2"
version = "3.1.1"
author = "Jonathan Bernard"
description = "Lightweight Postgres ORM for Nim."
license = "GPL-3.0"
@ -10,5 +10,5 @@ srcDir = "src"
# Dependencies
requires "nim >= 1.4.0"
requires "https://git.jdb-software.com/jdb/nim-namespaced-logging.git"
requires @["nim >= 1.4.0", "uuids"]
requires "namespaced_logging >= 1.0.0"

View File

@ -1,28 +1,327 @@
import std/db_postgres, std/macros, std/options, std/sequtils, std/strutils
# Fiber ORM
#
# Copyright 2019-2024 Jonathan Bernard <jonathan@jdbernard.com>
## Lightweight ORM supporting the `Postgres`_ and `SQLite`_ databases in Nim.
## It supports a simple, opinionated model mapper to generate SQL queries based
## on Nim objects. It also includes a simple connection pooling implementation.
##
## Fiber ORM is not intended to be a 100% all-cases-covered ORM that handles
## every potential data access pattern one might wish to implement. It is best
## thought of as a collection of common SQL generation patterns. It is intended
## to cover 90% of the common queries and functions one might write when
## implementing an SQL-based access layer. It is expected that there may be a
## few more complicated queries that need to be implemented to handle specific
## access patterns.
##
## The simple mapping pattern provided by Fiber ORM also works well on top of
## databases that encapsulate data access logic in SQL with, for example,
## views.
##
## .. _Postgres: https://nim-lang.org/docs/db_postgres.html
## .. _SQLite: https://nim-lang.org/docs/db_sqlite.html
##
## Basic Usage
## ===========
##
## Consider a simple TODO list application that keeps track of TODO items as
## well as time logged against those items.
##
## Example DB Schema
## -----------------
##
## You might have a schema such as:
##
## .. code-block:: SQL
## create extension if not exists "pgcrypto";
##
## create table todo_items columns (
## id uuid not null primary key default gen_random_uuid(),
## owner varchar not null,
## summary varchar not null,
## details varchar default null,
## priority integer not null default 0,
## related_todo_item_ids uuid[] not null default '{}'
## );
##
## create table time_entries columns (
## id uuid not null primary key default gen_random_uuid(),
## todo_item_id uuid not null references todo_items (id) on delete cascade,
## start timestamp with timezone not null default current_timestamp,
## stop timestamp with timezone default null,
## );
##
## Example Model Definitions
## -------------------------
##
## Models may be defined as:
##
## .. code-block:: Nim
## # models.nim
## import std/options, std/times
## import uuids
##
## type
## TodoItem* = object
## id*: UUID
## owner*: string
## summary*: string
## details*: Option[string]
## priority*: int
## relatedTodoItemIds*: seq[UUID]
##
## TimeEntry* = object
## id*: UUID
## todoItemId*: Option[UUID]
## start*: DateTime
## stop*: Option[DateTime]
##
## Example Fiber ORM Usage
## -----------------------
##
## Using Fiber ORM we can generate a data access layer with:
##
## .. code-block:: Nim
## # db.nim
## import std/db_postgres
## import fiber_orm
## import ./models.nim
##
## type TodoDB* = DbConnPool
##
## proc initDb*(connString: string): TodoDB =
## result = fiber_orm.initPool(
## connect = proc(): DbConn = open("", "", "", connString),
## poolSize = 20,
## hardCap = false)
##
##
## generateProcsForModels(TodoDB, [TodoItem, TimeEntry])
##
## generateLookup(TodoDB, TimeEntry, @["todoItemId"])
##
## This will generate the following procedures:
##
## .. code-block:: Nim
## proc getTodoItem*(db: TodoDB, id: UUID): TodoItem;
##
## proc createTodoItem*(db: TodoDB, rec: TodoItem): TodoItem;
## proc updateTodoItem*(db: TodoDB, rec: TodoItem): bool;
## proc createOrUpdateTodoItem*(db: TodoDB, rec: TodoItem): bool;
## proc deleteTodoItem*(db: TodoDB, rec: TodoItem): bool;
## proc deleteTodoItem*(db: TodoDB, id: UUID): bool;
##
## proc getAllTodoItems*(db: TodoDB,
## pagination = none[PaginationParams]()): seq[TodoItem];
##
## proc findTodoItemsWhere*(db: TodoDB, whereClause: string,
## values: varargs[string, dbFormat], pagination = none[PaginationParams]()
## ): seq[TodoItem];
##
## proc getTimeEntry*(db: TodoDB, id: UUID): TimeEntry;
## proc createTimeEntry*(db: TodoDB, rec: TimeEntry): TimeEntry;
## proc updateTimeEntry*(db: TodoDB, rec: TimeEntry): bool;
## proc deleteTimeEntry*(db: TodoDB, rec: TimeEntry): bool;
## proc deleteTimeEntry*(db: TodoDB, id: UUID): bool;
##
## proc getAllTimeEntries*(db: TodoDB,
## pagination = none[PaginationParams]()): seq[TimeEntry];
##
## proc findTimeEntriesWhere*(db: TodoDB, whereClause: string,
## values: varargs[string, dbFormat], pagination = none[PaginationParams]()
## ): seq[TimeEntry];
##
## proc findTimeEntriesByTodoItemId(db: TodoDB, todoItemId: UUID,
## pagination = none[PaginationParams]()): seq[TimeEntry];
##
## Object-Relational Modeling
## ==========================
##
## Model Class
## -----------
##
## Fiber ORM uses simple Nim `object`s and `ref object`s as model classes.
## Fiber ORM expects there to be one table for each model class.
##
## Name Mapping
## ````````````
## Fiber ORM uses `snake_case` for database identifiers (column names, table
## names, etc.) and `camelCase` for Nim identifiers. We automatically convert
## model names to and from table names (`TodoItem` <-> `todo_items`), as well
## as column names (`userId` <-> `user_id`).
##
## Notice that table names are automatically pluralized from model class names.
## In the above example, you have:
##
## =========== ================
## Model Class Table Name
## =========== ================
## TodoItem todo_items
## TimeEntry time_entries
## =========== ================
##
## Because Nim is style-insensitive, you can generall refer to model classes
## and fields using `snake_case`, `camelCase`, or `PascalCase` in your code and
## expect Fiber ORM to be able to map the names to DB identifier names properly
## (though FiberORM will always use `camelCase` internally).
##
## See the `identNameToDb`_, `dbNameToIdent`_, `tableName`_ and `dbFormat`_
## procedures in the `fiber_orm/util`_ module for details.
##
## .. _identNameToDb: fiber_orm/util.html#identNameToDb,string
## .. _dbNameToIdent: fiber_orm/util.html#dbNameToIdent,string
## .. _tableName: fiber_orm/util.html#tableName,type
## .. _dbFormat: fiber_orm/util.html#dbFormat,DateTime
## .. _util: fiber_orm/util.html
##
## ID Field
## ````````
##
## Fiber ORM expects every model class to have a field named `id`, with a
## corresponding `id` column in the model table. This field must be either a
## `string`, `integer`, or `UUID`_.
##
## When creating a new record the `id` field will be omitted if it is empty
## (`Option.isNone`_, `UUID.isZero`_, value of `0`, or only whitespace). This
## is intended to allow for cases like the example where the database may
## generate an ID when a new record is inserted. If a non-zero value is
## provided, the create call will include the `id` field in the `INSERT` query.
##
## For example, to allow the database to create the id:
##
## .. code-block:: Nim
## let item = TodoItem(
## owner: "John Mann",
## summary: "Create a grocery list.",
## details: none[string](),
## priority: 0,
## relatedTodoItemIds: @[])
##
## let itemWithId = db.createTodoItem(item)
## echo $itemWithId.id # generated in the database
##
## And to create it in code:
##
## .. code-block:: Nim
## import uuids
##
## let item = TodoItem(
## id: genUUID(),
## owner: "John Mann",
## summary: "Create a grocery list.",
## details: none[string](),
## priority: 0,
## relatedTodoItemIds: @[])
##
## let itemInDb = db.createTodoItem(item)
## echo $itemInDb.id # will be the same as what was provided
##
## .. _Option.isNone: https://nim-lang.org/docs/options.html#isNone,Option[T]
## .. _UUID.isZero: https://github.com/pragmagic/uuids/blob/8cb8720b567c6bcb261bd1c0f7491bdb5209ad06/uuids.nim#L72
##
## Supported Data Types
## --------------------
##
## The following Nim data types are supported by Fiber ORM:
##
## =============== ====================== =================
## Nim Type Postgres Type SQLite Type
## =============== ====================== =================
## `string` `varchar`_
## `int` `integer`_
## `float` `double`_
## `bool` `boolean`_
## `DateTime`_ `timestamp`_
## `seq[]` `array`_
## `UUID`_ `uuid (pg)`_
## `Option`_ *allows* `NULL` [#f1]_
## `JsonNode`_ `jsonb`_
## =============== ====================== =================
##
## .. [#f1] Note that this implies that all `NULL`-able fields should be typed
## as optional using `Option[fieldType]`. Conversely, any fields with
## non-optional types should also be constrained to be `NOT NULL` in
## the database schema.
##
## .. _DateTime: https://nim-lang.org/docs/times.html#DateTime
## .. _UUID: https://github.com/pragmagic/uuids
## .. _Option: https://nim-lang.org/docs/options.html#Option
## .. _JsonNode: https://nim-lang.org/docs/json.html#JsonNode
##
## .. _varchar: https://www.postgresql.org/docs/current/datatype-character.html
## .. _integer: https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-INT
## .. _double: https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-FLOAT
## .. _boolean: https://www.postgresql.org/docs/current/datatype-boolean.html
## .. _timestamp: https://www.postgresql.org/docs/current/datatype-datetime.html
## .. _array: https://www.postgresql.org/docs/current/arrays.html
## .. _uuid (pg): https://www.postgresql.org/docs/current/datatype-uuid.html
## .. _jsonb: https://www.postgresql.org/docs/current/datatype-json.html
##
## Database Object
## ===============
##
## Many of the Fiber ORM macros expect a database object type to be passed.
## In the example above the `pool.DbConnPool`_ object is used as database
## object type (aliased as `TodoDB`). This is the intended usage pattern, but
## anything can be passed as the database object type so long as there is a
## defined `withConnection` template that provides a `conn: DbConn` object
## to the provided statement body.
##
## For example, a valid database object implementation that opens a new
## connection for every request might look like this:
##
## .. code-block:: Nim
## import db_connector/db_postgres
##
## type TodoDB* = object
## connString: string
##
## template withConnection*(db: TodoDB, stmt: untyped): untyped =
## block:
## let conn = open("", "", "", db.connString)
## try: stmt
## finally: close(conn)
##
## .. _pool.DbConnPool: fiber_orm/pool.html#DbConnPool
##
import std/[json, macros, options, sequtils, strutils]
import db_connector/db_common
import namespaced_logging, uuids
from std/unicode import capitalize
import ./fiber_orm/db_common as fiber_db_common
import ./fiber_orm/pool
import ./fiber_orm/util
export
pool,
util.columnNamesForModel,
util.dbFormat,
util.dbNameToIdent,
util.identNameToDb,
util.modelName,
util.rowToModel,
util.tableName
export pool, util
type NotFoundError* = object of CatchableError
type
PagedRecords*[T] = object
pagination*: Option[PaginationParams]
records*: seq[T]
totalRecords*: int
var logNs {.threadvar.}: LoggingNamespace
DbUpdateError* = object of CatchableError ##\
## Error types raised when a DB modification fails.
template log(): untyped =
if logNs.isNil: logNs = initLoggingNamespace(name = "fiber_orm", level = lvlNotice)
logNs
NotFoundError* = object of CatchableError ##\
## Error type raised when no record matches a given ID
var logService {.threadvar.}: Option[LogService]
var logger {.threadvar.}: Option[Logger]
proc logQuery*(methodName: string, sqlStmt: string, args: openArray[(string, string)] = []) =
# namespaced_logging would do this check for us, but we don't want to even
# build the log object if we're not actually logging
if logService.isNone: return
if logger.isNone: logger = logService.getLogger("fiber_orm/query")
var log = %*{ "method": methodName, "sql": sqlStmt }
for (k, v) in args: log[k] = %v
logger.debug(log)
proc enableDbLogging*(svc: LogService) =
logService = some(svc)
proc newMutateClauses(): MutateClauses =
return MutateClauses(
@ -30,7 +329,15 @@ proc newMutateClauses(): MutateClauses =
placeholders: @[],
values: @[])
proc createRecord*[T](db: DbConn, rec: T): T =
proc createRecord*[D: DbConnType, T](db: D, rec: T): T =
## Create a new record. `rec` is expected to be a `model class`_. The `id`
## field is only set if it is non-empty (see `ID Field`_ for details).
##
## Returns the newly created record.
##
## .. _model class: #objectminusrelational-modeling-model-class
## .. _ID Field: #model-class-id-field
var mc = newMutateClauses()
populateMutateClauses(rec, true, mc)
@ -40,12 +347,13 @@ proc createRecord*[T](db: DbConn, rec: T): T =
" VALUES (" & mc.placeholders.join(",") & ") " &
" RETURNING " & columnNamesForModel(rec).join(",")
log().debug "createRecord: [" & sqlStmt & "]"
logQuery("createRecord", sqlStmt)
let newRow = db.getRow(sql(sqlStmt), mc.values)
result = rowToModel(T, newRow)
proc updateRecord*[T](db: DbConn, rec: T): bool =
proc updateRecord*[D: DbConnType, T](db: D, rec: T): bool =
## Update a record by id. `rec` is expected to be a `model class`_.
var mc = newMutateClauses()
populateMutateClauses(rec, false, mc)
@ -55,28 +363,51 @@ proc updateRecord*[T](db: DbConn, rec: T): bool =
" SET " & setClause &
" WHERE id = ? "
log().debug "updateRecord: [" & sqlStmt & "] id: " & $rec.id
logQuery("updateRecord", sqlStmt, [("id", $rec.id)])
let numRowsUpdated = db.execAffectedRows(sql(sqlStmt), mc.values.concat(@[$rec.id]))
return numRowsUpdated > 0;
template deleteRecord*(db: DbConn, modelType: type, id: typed): untyped =
proc createOrUpdateRecord*[D: DbConnType, T](db: D, rec: T): T =
## Create or update a record. `rec` is expected to be a `model class`_. If
## the `id` field is unset, or if there is no existing record with the given
## id, a new record is inserted. Otherwise, the existing record is updated.
##
## Note that this does not perform partial updates, all fields are updated.
let findRecordStmt = "SELECT id FROM " & tableName(rec) & " WHERE id = ?"
logQuery("createOrUpdateRecord", findRecordStmt, [("id", $rec.id)])
let rows = db.getAllRows(sql(findRecordStmt), [$rec.id])
if rows.len == 0: result = createRecord(db, rec)
else:
result = rec
if not updateRecord(db, rec):
raise newException(DbUpdateError,
"unable to update " & modelName(rec) & " for id " & $rec.id)
template deleteRecord*[D: DbConnType](db: D, modelType: type, id: typed): untyped =
## Delete a record by id.
let sqlStmt = "DELETE FROM " & tableName(modelType) & " WHERE id = ?"
log().debug "deleteRecord: [" & sqlStmt & "] id: " & $id
logQuery("deleteRecord", sqlStmt, [("id", $id)])
db.tryExec(sql(sqlStmt), $id)
proc deleteRecord*[T](db: DbConn, rec: T): bool =
proc deleteRecord*[D: DbConnType, T](db: D, rec: T): bool =
## Delete a record by `id`_.
##
## .. _id: #model-class-id-field
let sqlStmt = "DELETE FROM " & tableName(rec) & " WHERE id = ?"
log().debug "deleteRecord: [" & sqlStmt & "] id: " & $rec.id
logQuery("deleteRecord", sqlStmt, [("id", $rec.id)])
return db.tryExec(sql(sqlStmt), $rec.id)
template getRecord*(db: DbConn, modelType: type, id: typed): untyped =
template getRecord*[D: DbConnType](db: D, modelType: type, id: typed): untyped =
## Fetch a record by id.
let sqlStmt =
"SELECT " & columnNamesForModel(modelType).join(",") &
" FROM " & tableName(modelType) &
" WHERE id = ?"
log().debug "getRecord: [" & sqlStmt & "] id: " & $id
logQuery("getRecord", sqlStmt, [("id", $id)])
let row = db.getRow(sql(sqlStmt), @[$id])
if allIt(row, it.len == 0):
@ -84,76 +415,183 @@ template getRecord*(db: DbConn, modelType: type, id: typed): untyped =
rowToModel(modelType, row)
template findRecordsWhere*(db: DbConn, modelType: type, whereClause: string, values: varargs[string, dbFormat]): untyped =
let sqlStmt =
template findRecordsWhere*[D: DbConnType](
db: D,
modelType: type,
whereClause: string,
values: varargs[string, dbFormat],
page: Option[PaginationParams]): untyped =
## Find all records matching a given `WHERE` clause. The number of elements in
## the `values` array must match the number of placeholders (`?`) in the
## provided `WHERE` clause.
var fetchStmt =
"SELECT " & columnNamesForModel(modelType).join(",") &
" FROM " & tableName(modelType) &
" WHERE " & whereClause
log().debug "findRecordsWhere: [" & sqlStmt & "] values: (" & values.join(", ") & ")"
db.getAllRows(sql(sqlStmt), values).mapIt(rowToModel(modelType, it))
var countStmt =
"SELECT COUNT(*) FROM " & tableName(modelType) &
" WHERE " & whereClause
template getAllRecords*(db: DbConn, modelType: type): untyped =
let sqlStmt =
if page.isSome: fetchStmt &= getPagingClause(page.get)
logQuery("findRecordsWhere", fetchStmt, [("values", values.join(", "))])
let records = db.getAllRows(sql(fetchStmt), values).mapIt(rowToModel(modelType, it))
PagedRecords[modelType](
pagination: page,
records: records,
totalRecords:
if page.isNone: records.len
else: db.getRow(sql(countStmt), values)[0].parseInt)
template getAllRecords*[D: DbConnType](
db: D,
modelType: type,
page: Option[PaginationParams]): untyped =
## Fetch all records of the given type.
var fetchStmt =
"SELECT " & columnNamesForModel(modelType).join(",") &
" FROM " & tableName(modelType)
log().debug "getAllRecords: [" & sqlStmt & "]"
db.getAllRows(sql(sqlStmt)).mapIt(rowToModel(modelType, it))
var countStmt = "SELECT COUNT(*) FROM " & tableName(modelType)
template findRecordsBy*(db: DbConn, modelType: type, lookups: seq[tuple[field: string, value: string]]): untyped =
let sqlStmt =
"SELECT " & columnNamesForModel(modelType).join(",") &
" FROM " & tableName(modelType) &
" WHERE " & lookups.mapIt(it.field & " = ?").join(" AND ")
if page.isSome: fetchStmt &= getPagingClause(page.get)
logQuery("getAllRecords", fetchStmt)
let records = db.getAllRows(sql(fetchStmt)).mapIt(rowToModel(modelType, it))
PagedRecords[modelType](
pagination: page,
records: records,
totalRecords:
if page.isNone: records.len
else: db.getRow(sql(countStmt))[0].parseInt)
template findRecordsBy*[D: DbConnType](
db: D,
modelType: type,
lookups: seq[tuple[field: string, value: string]],
page: Option[PaginationParams]): untyped =
## Find all records matching the provided lookup values.
let whereClause = lookups.mapIt(it.field & " = ?").join(" AND ")
let values = lookups.mapIt(it.value)
log().debug "findRecordsBy: [" & sqlStmt & "] values (" & values.join(", ") & ")"
db.getAllRows(sql(sqlStmt), values).mapIt(rowToModel(modelType, it))
var fetchStmt =
"SELECT " & columnNamesForModel(modelType).join(",") &
" FROM " & tableName(modelType) &
" WHERE " & whereClause
var countStmt =
"SELECT COUNT(*) FROM " & tableName(modelType) &
" WHERE " & whereClause
if page.isSome: fetchStmt &= getPagingClause(page.get)
logQuery("findRecordsBy", fetchStmt, [("values", values.join(", "))])
let records = db.getAllRows(sql(fetchStmt), values).mapIt(rowToModel(modelType, it))
PagedRecords[modelType](
pagination: page,
records: records,
totalRecords:
if page.isNone: records.len
else: db.getRow(sql(countStmt), values)[0].parseInt)
macro generateProcsForModels*(dbType: type, modelTypes: openarray[type]): untyped =
## Generate all standard access procedures for the given model types. For a
## `model class`_ named `TodoItem`, this will generate the following
## procedures:
##
## .. code-block:: Nim
## proc getTodoItem*(db: TodoDB, id: idType): TodoItem;
## proc getAllTodoItems*(db: TodoDB): TodoItem;
## proc createTodoItem*(db: TodoDB, rec: TodoItem): TodoItem;
## proc deleteTodoItem*(db: TodoDB, rec: TodoItem): bool;
## proc deleteTodoItem*(db: TodoDB, id: idType): bool;
## proc updateTodoItem*(db: TodoDB, rec: TodoItem): bool;
## proc createOrUpdateTodoItem*(db: TodoDB, rec: TodoItem): bool;
##
## proc findTodoItemsWhere*(
## db: TodoDB, whereClause: string, values: varargs[string]): TodoItem;
##
## `dbType` is expected to be some type that has a defined `withConnection`
## procedure (see `Database Object`_ for details).
##
## .. _Database Object: #database-object
result = newStmtList()
for t in modelTypes:
if t.getType[1].typeKind == ntyRef:
raise newException(ValueError,
"fiber_orm model object must be objects, not refs")
let modelName = $(t.getType[1])
let getName = ident("get" & modelName)
let getAllName = ident("getAll" & modelName & "s")
let findWhereName = ident("find" & modelName & "sWhere")
let getIfExistsName = ident("get" & modelName & "IfItExists")
let getAllName = ident("getAll" & pluralize(modelName))
let findWhereName = ident("find" & pluralize(modelName) & "Where")
let createName = ident("create" & modelName)
let updateName = ident("update" & modelName)
let createOrUpdateName = ident("createOrUpdate" & modelName)
let deleteName = ident("delete" & modelName)
let idType = typeOfColumn(t, "id")
result.add quote do:
proc `getName`*(db: `dbType`, id: `idType`): `t` =
db.withConn: result = getRecord(conn, `t`, id)
db.withConnection conn: result = getRecord(conn, `t`, id)
proc `getAllName`*(db: `dbType`): seq[`t`] =
db.withConn: result = getAllRecords(conn, `t`)
proc `getIfExistsName`*(db: `dbType`, id: `idType`): Option[`t`] =
db.withConnection conn:
try: result = some(getRecord(conn, `t`, id))
except NotFoundError: result = none[`t`]()
proc `findWhereName`*(db: `dbType`, whereClause: string, values: varargs[string, dbFormat]): seq[`t`] =
db.withConn:
result = findRecordsWhere(conn, `t`, whereClause, values)
proc `getAllName`*(db: `dbType`, pagination = none[PaginationParams]()): PagedRecords[`t`] =
db.withConnection conn: result = getAllRecords(conn, `t`, pagination)
proc `findWhereName`*(
db: `dbType`,
whereClause: string,
values: varargs[string, dbFormat],
pagination = none[PaginationParams]()): PagedRecords[`t`] =
db.withConnection conn:
result = findRecordsWhere(conn, `t`, whereClause, values, pagination)
proc `createName`*(db: `dbType`, rec: `t`): `t` =
db.withConn: result = createRecord(conn, rec)
db.withConnection conn: result = createRecord(conn, rec)
proc `updateName`*(db: `dbType`, rec: `t`): bool =
db.withConn: result = updateRecord(conn, rec)
db.withConnection conn: result = updateRecord(conn, rec)
proc `createOrUpdateName`*(db: `dbType`, rec: `t`): `t` =
db.inTransaction: result = createOrUpdateRecord(conn, rec)
proc `deleteName`*(db: `dbType`, rec: `t`): bool =
db.withConn: result = deleteRecord(conn, rec)
db.withConnection conn: result = deleteRecord(conn, rec)
proc `deleteName`*(db: `dbType`, id: `idType`): bool =
db.withConn: result = deleteRecord(conn, `t`, id)
db.withConnection conn: result = deleteRecord(conn, `t`, id)
macro generateLookup*(dbType: type, modelType: type, fields: seq[string]): untyped =
## Create a lookup procedure for a given set of field names. For example,
## given the TODO database demostrated above,
##
## .. code-block:: Nim
## generateLookup(TodoDB, TodoItem, ["owner", "priority"])
##
## will generate the following procedure:
##
## .. code-block:: Nim
## proc findTodoItemsByOwnerAndPriority*(db: SampleDB,
## owner: string, priority: int): seq[TodoItem]
let fieldNames = fields[1].mapIt($it)
let procName = ident("find" & pluralize($modelType.getType[1]) & "By" & fieldNames.mapIt(it.capitalize).join("And"))
# Create proc skeleton
result = quote do:
proc `procName`*(db: `dbType`): seq[`modelType`] =
db.withConn: result = findRecordsBy(conn, `modelType`)
proc `procName`*(db: `dbType`): PagedRecords[`modelType`] =
db.withConnection conn: result = findRecordsBy(conn, `modelType`)
var callParams = quote do: @[]
@ -170,12 +608,18 @@ macro generateLookup*(dbType: type, modelType: type, fields: seq[string]): untyp
# Build up the AST for the inner procedure call
callParams[1].add(paramTuple)
# Add the optional pagination parameters to the generated proc definition
result[3].add(newIdentDefs(
ident("pagination"), newEmptyNode(),
quote do: none[PaginationParams]()))
# Add the call params to the inner procedure call
# result[6][0][1][0][1] is
# ProcDef -> [6]: StmtList (body) -> [0]: Call ->
# [1]: StmtList (withConn body) -> [0]: Asgn (result =) ->
# ProcDef -> [6]: StmtList (body) -> [0]: Command ->
# [2]: StmtList (withConnection body) -> [0]: Asgn (result =) ->
# [1]: Call (inner findRecords invocation)
result[6][0][1][0][1].add(callParams)
result[6][0][2][0][1].add(callParams)
result[6][0][2][0][1].add(quote do: pagination)
macro generateProcsForFieldLookups*(dbType: type, modelsAndFields: openarray[tuple[t: type, fields: seq[string]]]): untyped =
result = newStmtList()
@ -188,8 +632,8 @@ macro generateProcsForFieldLookups*(dbType: type, modelsAndFields: openarray[tup
# Create proc skeleton
let procDefAST = quote do:
proc `procName`*(db: `dbType`): seq[`modelType`] =
db.withConn: result = findRecordsBy(conn, `modelType`)
proc `procName`*(db: `dbType`): PagedRecords[`modelType`] =
db.withConnection conn: result = findRecordsBy(conn, `modelType`)
var callParams = quote do: @[]
@ -202,24 +646,47 @@ macro generateProcsForFieldLookups*(dbType: type, modelsAndFields: openarray[tup
procDefAST[3].add(newIdentDefs(ident(n), ident("string")))
callParams[1].add(paramTuple)
# Add the optional pagination parameters to the generated proc definition
procDefAST[3].add(newIdentDefs(
ident("pagination"), newEmptyNode(),
quote do: none[PaginationParams]()))
procDefAST[6][0][1][0][1].add(callParams)
procDefAST[6][0][1][0][1].add(quote do: pagination)
result.add procDefAST
proc initPool*(
connect: proc(): DbConn,
proc initPool*[D: DbConnType](
connect: proc(): D,
poolSize = 10,
hardCap = false,
healthCheckQuery = "SELECT 'true' AS alive"): DbConnPool =
healthCheckQuery = "SELECT 'true' AS alive"): DbConnPool[D] =
initDbConnPool(DbConnPoolConfig(
## Initialize a new DbConnPool. See the `initDb` procedure in the `Example
## Fiber ORM Usage`_ for an example
##
## * `connect` must be a factory which creates a new `DbConn`.
## * `poolSize` sets the desired capacity of the connection pool.
## * `hardCap` defaults to `false`.
## When `false`, the pool can grow beyond the configured capacity, but will
## release connections down to the its capacity (no less than `poolSize`).
##
## When `true` the pool will not create more than its configured capacity.
## It a connection is requested, none are free, and the pool is at
## capacity, this will result in an Error being raised.
## * `healthCheckQuery` should be a simple and fast SQL query that the pool
## can use to test the liveliness of pooled connections.
##
## .. _Example Fiber ORM Usage: #basic-usage-example-fiber-orm-usage
initDbConnPool(DbConnPoolConfig[D](
connect: connect,
poolSize: poolSize,
hardCap: hardCap,
healthCheckQuery: healthCheckQuery))
template inTransaction*(db: DbConnPool, body: untyped) =
pool.withConn(db):
template inTransaction*(db, body: untyped) =
db.withConnection conn:
conn.exec(sql"BEGIN TRANSACTION")
try:
body

View File

@ -0,0 +1,3 @@
import db_connector/[db_postgres, db_sqlite]
type DbConnType* = db_postgres.DbConn or db_sqlite.DbConn

View File

@ -1,50 +1,65 @@
import std/db_postgres, std/sequtils, std/strutils, std/sugar
# Fiber ORM
#
# Copyright 2019 Jonathan Bernard <jonathan@jdbernard.com>
import namespaced_logging
## Simple database connection pooling implementation compatible with Fiber ORM.
import std/[sequtils, strutils, sugar]
import db_connector/db_common
from db_connector/db_sqlite import getRow, close
from db_connector/db_postgres import getRow, close
import ./db_common as fiber_db_common
type
DbConnPoolConfig* = object
connect*: () -> DbConn
poolSize*: int
hardCap*: bool
healthCheckQuery*: string
DbConnPoolConfig*[D: DbConnType] = object
connect*: () -> D ## Factory procedure to create a new DBConn
poolSize*: int ## The pool capacity.
PooledDbConn = ref object
conn: DbConn
hardCap*: bool ## Is the pool capacity a hard cap?
##
## When `false`, the pool can grow beyond the
## configured capacity, but will release connections
## down to the its capacity (no less than `poolSize`).
##
## When `true` the pool will not create more than its
## configured capacity. It a connection is requested,
## none are free, and the pool is at capacity, this
## will result in an Error being raised.
healthCheckQuery*: string ## Should be a simple and fast SQL query that the
## pool can use to test the liveliness of pooled
## connections.
PooledDbConn[D: DbConnType] = ref object
conn: D
id: int
free: bool
DbConnPool* = ref object
conns: seq[PooledDbConn]
cfg: DbConnPoolConfig
DbConnPool*[D: DbConnType] = ref object
## Database connection pool
conns: seq[PooledDbConn[D]]
cfg: DbConnPoolConfig[D]
lastId: int
var logNs {.threadvar.}: LoggingNamespace
template log(): untyped =
if logNs.isNil: logNs = initLoggingNamespace(name = "fiber_orm/pool", level = lvlNotice)
logNs
proc initDbConnPool*(cfg: DbConnPoolConfig): DbConnPool =
log().debug("Initializing new pool (size: " & $cfg.poolSize)
result = DbConnPool(
proc initDbConnPool*[D: DbConnType](cfg: DbConnPoolConfig[D]): DbConnPool[D] =
result = DbConnPool[D](
conns: @[],
cfg: cfg)
proc newConn(pool: DbConnPool): PooledDbConn =
log().debug("Creating a new connection to add to the pool.")
proc newConn[D: DbConnType](pool: DbConnPool[D]): PooledDbConn[D] =
pool.lastId += 1
let conn = pool.cfg.connect()
result = PooledDbConn(
conn: conn,
id: pool.lastId,
free: true)
pool.conns.add(result)
{.gcsafe.}:
let conn = pool.cfg.connect()
result = PooledDbConn[D](
conn: conn,
id: pool.lastId,
free: true)
pool.conns.add(result)
proc maintain(pool: DbConnPool): void =
log().debug("Maintaining pool. $# connections." % [$pool.conns.len])
pool.conns.keepIf(proc (pc: PooledDbConn): bool =
proc maintain[D: DbConnType](pool: DbConnPool[D]): void =
pool.conns.keepIf(proc (pc: PooledDbConn[D]): bool =
if not pc.free: return true
try:
@ -55,9 +70,6 @@ proc maintain(pool: DbConnPool): void =
except: discard ""
return false
)
log().debug(
"Pruned dead connections. $# connections remaining." %
[$pool.conns.len])
let freeConns = pool.conns.filterIt(it.free)
if pool.conns.len > pool.cfg.poolSize and freeConns.len > 0:
@ -69,31 +81,34 @@ proc maintain(pool: DbConnPool): void =
for culled in toCull:
try: culled.conn.close()
except: discard ""
log().debug(
"Trimming pool size. Culled $# free connections. $# connections remaining." %
[$toCull.len, $pool.conns.len])
proc take*(pool: DbConnPool): tuple[id: int, conn: DbConn] =
proc take*[D: DbConnType](pool: DbConnPool[D]): tuple[id: int, conn: D] =
## Request a connection from the pool. Returns a DbConn if the pool has free
## connections, or if it has the capacity to create a new connection. If the
## pool is configured with a hard capacity limit and is out of free
## connections, this will raise an Error.
##
## Connections taken must be returned via `release` when the caller is
## finished using them in order for them to be released back to the pool.
pool.maintain
let freeConns = pool.conns.filterIt(it.free)
log().debug(
"Providing a new connection ($# currently free)." % [$freeConns.len])
let reserved =
if freeConns.len > 0: freeConns[0]
else: pool.newConn()
reserved.free = false
log().debug("Reserve connection $#" % [$reserved.id])
return (id: reserved.id, conn: reserved.conn)
proc release*(pool: DbConnPool, connId: int): void =
log().debug("Reclaiming released connaction $#" % [$connId])
proc release*[D: DbConnType](pool: DbConnPool[D], connId: int): void =
## Release a connection back to the pool.
let foundConn = pool.conns.filterIt(it.id == connId)
if foundConn.len > 0: foundConn[0].free = true
template withConn*(pool: DbConnPool, stmt: untyped): untyped =
let (connId, conn {.inject.}) = take(pool)
try: stmt
finally: release(pool, connId)
template withConnection*[D: DbConnType](pool: DbConnPool[D], conn, stmt: untyped): untyped =
## Convenience template to provide a connection from the pool for use in a
## statement block, automatically releasing that connnection when done.
block:
let (connId, conn) = take(pool)
try: stmt
finally: release(pool, connId)

View File

@ -1,29 +1,70 @@
import json, macros, options, sequtils, strutils, times, timeutils, unicode,
uuids
# Fiber ORM
#
# Copyright 2019 Jonathan Bernard <jonathan@jdbernard.com>
import nre except toSeq
## Utility methods used internally by Fiber ORM.
import std/[json, macros, options, sequtils, strutils, times, unicode]
import uuids
import std/nre except toSeq
type
PaginationParams* = object
pageSize*: int
offset*: int
orderBy*: Option[seq[string]]
MutateClauses* = object
## Data structure to hold information about the clauses that should be
## added to a query. How these clauses are used will depend on the query.
## This common data structure provides the information needed to create
## WHERE clauses, UPDATE clauses, etc.
columns*: seq[string]
placeholders*: seq[string]
values*: seq[string]
const ISO_8601_FORMATS = @[
"yyyy-MM-dd'T'HH:mm:ssz",
"yyyy-MM-dd'T'HH:mm:sszzz",
"yyyy-MM-dd'T'HH:mm:ss'.'fffzzz",
"yyyy-MM-dd'T'HH:mm:ss'.'ffffzzz",
"yyyy-MM-dd HH:mm:ssz",
"yyyy-MM-dd HH:mm:sszzz",
"yyyy-MM-dd HH:mm:ss'.'fffzzz",
"yyyy-MM-dd HH:mm:ss'.'ffffzzz"
]
proc parseIso8601(val: string): DateTime =
var errString = ""
for df in ISO_8601_FORMATS:
try: return val.parse(df)
except: errString &= "\n" & getCurrentExceptionMsg()
raise newException(Exception, "Could not parse date. Tried:" & errString)
proc formatIso8601(d: DateTime): string =
return d.format(ISO_8601_FORMATS[2])
# TODO: more complete implementation
# see https://github.com/blakeembrey/pluralize
proc pluralize*(name: string): string =
## Return the plural form of the given name.
if name[^2..^1] == "ey": return name[0..^3] & "ies"
if name[^1] == 'y': return name[0..^2] & "ies"
return name & "s"
macro modelName*(model: object): string =
## For a given concrete record object, return the name of the `model class`_
return newStrLitNode($model.getTypeInst)
macro modelName*(modelType: type): string =
## Get the name of a given `model class`_
return newStrLitNode($modelType.getType[1])
proc identNameToDb*(name: string): string =
## Map a Nim identifier name to a DB name. See the `rules for name mapping`_
##
## TODO link above
const UNDERSCORE_RUNE = "_".toRunes[0]
let nameInRunes = name.toRunes
var prev: Rune
@ -42,28 +83,40 @@ proc identNameToDb*(name: string): string =
return $resultRunes
proc dbNameToIdent*(name: string): string =
## Map a DB name to a Nim identifier name. See the `rules for name mapping`_
let parts = name.split("_")
return @[parts[0]].concat(parts[1..^1].mapIt(capitalize(it))).join("")
proc tableName*(modelType: type): string =
## Get the `table name`_ for a given `model class`_
return pluralize(modelName(modelType).identNameToDb)
proc tableName*[T](rec: T): string =
## Get the `table name`_ for a given record.
return pluralize(modelName(rec).identNameToDb)
proc dbFormat*(s: string): string = return s
proc dbFormat*(s: string): string =
## Format a string for inclusion in a SQL Query.
return s
proc dbFormat*(dt: DateTime): string = return dt.formatIso8601
proc dbFormat*(dt: DateTime): string =
## Format a DateTime for inclusion in a SQL Query.
return dt.formatIso8601
proc dbFormat*[T](list: seq[T]): string =
## Format a `seq` for inclusion in a SQL Query.
return "{" & list.mapIt(dbFormat(it)).join(",") & "}"
proc dbFormat*[T](item: T): string = return $item
proc dbFormat*[T](item: T): string =
## For all other types, fall back on a defined `$` function to create a
## string version of the value we can include in an SQL query.
return $item
type DbArrayParseState = enum
expectStart, inQuote, inVal, expectEnd
proc parsePGDatetime*(val: string): DateTime =
## Parse a Postgres datetime value into a Nim DateTime object.
const PG_TIMESTAMP_FORMATS = [
"yyyy-MM-dd HH:mm:ss",
@ -80,18 +133,20 @@ proc parsePGDatetime*(val: string): DateTime =
var correctedVal = val;
# PostgreSQL will truncate any trailing 0's in the millisecond value leading
# to values like `2020-01-01 16:42.3+00`. This cannot currently be parsed by
# the standard times format as it expects exactly three digits for
# millisecond values. So we have to detect this and pad out the millisecond
# value to 3 digits.
let PG_PARTIAL_FORMAT_REGEX = re"(\d{4}-\d{2}-\d{2}( |'T')\d{2}:\d{2}:\d{2}\.)(\d{1,2})(\S+)?"
# The Nim `times#format` function only recognizes 3-digit millisecond values
# but PostgreSQL will sometimes send 1-2 digits, truncating any trailing 0's,
# or sometimes provide more than three digits of preceision in the millisecond value leading
# to values like `2020-01-01 16:42.3+00` or `2025-01-06 00:56:00.9007+00`.
# This cannot currently be parsed by the standard times format as it expects
# exactly three digits for millisecond values. So we have to detect this and
# coerce the millisecond value to exactly 3 digits.
let PG_PARTIAL_FORMAT_REGEX = re"(\d{4}-\d{2}-\d{2}( |'T')\d{2}:\d{2}:\d{2}\.)(\d+)(\S+)?"
let match = val.match(PG_PARTIAL_FORMAT_REGEX)
if match.isSome:
let c = match.get.captures
if c.toSeq.len == 2: correctedVal = c[0] & alignLeft(c[2], 3, '0')
else: correctedVal = c[0] & alignLeft(c[2], 3, '0') & c[3]
if c.toSeq.len == 2: correctedVal = c[0] & alignLeft(c[2], 3, '0')[0..2]
else: correctedVal = c[0] & alignLeft(c[2], 3, '0')[0..2] & c[3]
var errStr = ""
@ -100,9 +155,10 @@ proc parsePGDatetime*(val: string): DateTime =
try: return correctedVal.parse(df)
except: errStr &= "\n\t" & getCurrentExceptionMsg()
raise newException(ValueError, "Cannot parse PG date. Tried:" & errStr)
raise newException(ValueError, "Cannot parse PG date '" & correctedVal & "'. Tried:" & errStr)
proc parseDbArray*(val: string): seq[string] =
## Parse a Postgres array column into a Nim seq[string]
result = newSeq[string]()
var parseState = DbArrayParseState.expectStart
@ -160,18 +216,14 @@ proc parseDbArray*(val: string): seq[string] =
if not (parseState == inQuote) and curStr.len > 0:
result.add(curStr)
proc createParseStmt*(t, value: NimNode): NimNode =
func createParseStmt*(t, value: NimNode): NimNode =
## Utility method to create the Nim cod required to parse a value coming from
## the a database query. This is used by functions like `rowToModel` to parse
## the dataabase columns into the Nim object fields.
#echo "Creating parse statment for ", t.treeRepr
if t.typeKind == ntyObject:
if t.getType == UUID.getType:
result = quote do: parseUUID(`value`)
elif t.getType == DateTime.getType:
result = quote do: parsePGDatetime(`value`)
elif t.getTypeInst == Option.getType:
if t.getTypeInst == Option.getType:
var innerType = t.getTypeImpl[2][0] # start at the first RecList
# If the value is a non-pointer type, there is another inner RecList
if innerType.kind == nnkRecList: innerType = innerType[0]
@ -182,8 +234,28 @@ proc createParseStmt*(t, value: NimNode): NimNode =
if `value`.len == 0: none[`innerType`]()
else: some(`parseStmt`)
elif t.getType == UUID.getType:
result = quote do: parseUUID(`value`)
elif t.getType == DateTime.getType:
result = quote do: parsePGDatetime(`value`)
else: error "Unknown value object type: " & $t.getTypeInst
elif t.typeKind == ntyGenericInst:
if t.kind == nnkBracketExpr and
t.len > 0 and
t[0] == Option.getType:
var innerType = t.getTypeInst[1]
let parseStmt = createParseStmt(innerType, value)
result = quote do:
if `value`.len == 0: none[`innerType`]()
else: some(`parseStmt`)
else: error "Unknown generic instance type: " & $t.getTypeInst
elif t.typeKind == ntyRef:
if $t.getTypeInst == "JsonNode":
@ -218,27 +290,76 @@ proc createParseStmt*(t, value: NimNode): NimNode =
else:
error "Unknown value type: " & $t.typeKind
template walkFieldDefs*(t: NimNode, body: untyped) =
let tTypeImpl = t.getTypeImpl
func fields(t: NimNode): seq[tuple[fieldIdent: NimNode, fieldType: NimNode]] =
#[
debugEcho "T: " & t.treeRepr
debugEcho "T.kind: " & $t.kind
debugEcho "T.typeKind: " & $t.typeKind
debugEcho "T.GET_TYPE[1]: " & t.getType[1].treeRepr
debugEcho "T.GET_TYPE[1].kind: " & $t.getType[1].kind
debugEcho "T.GET_TYPE[1].typeKind: " & $t.getType[1].typeKind
var nodeToItr: NimNode
if tTypeImpl.typeKind == ntyObject: nodeToItr = tTypeImpl[2]
elif tTypeImpl.typeKind == ntyTypeDesc: nodeToItr = tTypeImpl.getType[1].getType[2]
else: error $t & " is not an object or type desc (it's a " & $tTypeImpl.typeKind & ")."
debugEcho "T.GET_TYPE: " & t.getType.treeRepr
debugEcho "T.GET_TYPE[1].GET_TYPE: " & t.getType[1].getType.treeRepr
]#
for fieldDef {.inject.} in nodeToItr.children:
# Get the object type AST, with base object (if present) and record list.
var objDefAst: NimNode
if t.typeKind == ntyObject: objDefAst = t.getType
elif t.typeKind == ntyTypeDesc:
# In this case we have a type AST that is like:
# BracketExpr
# Sym "typeDesc"
# Sym "ModelType"
objDefAst = t.
getType[1]. # get the Sym "ModelType"
getType # get the object definition type
if objDefAst.kind != nnkObjectTy:
error ("unable to enumerate the fields for model type '$#', " &
"tried to resolve the type of the provided symbol to an object " &
"definition (nnkObjectTy) but got a '$#'.\pAST:\p$#") % [
$t, $objDefAst.kind, objDefAst.treeRepr ]
else:
error ("unable to enumerate the fields for model type '$#', " &
"expected a symbol with type ntyTypeDesc but got a '$#'.\pAST:\p$#") % [
$t, $t.typeKind, t.treeRepr ]
# At this point objDefAst should look something like:
# ObjectTy
# Empty
# Sym "BaseObject"" | Empty
# RecList
# Sym "field1"
# Sym "field2"
# ...
if objDefAst[1].kind == nnkSym:
# We have a base class symbol, let's recurse and try and resolve the fields
# for the base class
for fieldDef in objDefAst[1].fields: result.add(fieldDef)
for fieldDef in objDefAst[2].children:
# objDefAst[2] is a RecList of
# ignore AST nodes that are not field definitions
if fieldDef.kind == nnkIdentDefs:
let fieldIdent {.inject.} = fieldDef[0]
let fieldType {.inject.} = fieldDef[1]
body
if fieldDef.kind == nnkIdentDefs: result.add((fieldDef[0], fieldDef[1]))
elif fieldDef.kind == nnkSym: result.add((fieldDef, fieldDef.getTypeInst))
else: error "unknown object field definition AST: $#" % $fieldDef.kind
elif fieldDef.kind == nnkSym:
let fieldIdent {.inject.} = fieldDef
let fieldType {.inject.} = fieldDef.getType
body
template walkFieldDefs*(t: NimNode, body: untyped) =
## Iterate over every field of the given Nim object, yielding and defining
## `fieldIdent` and `fieldType`, the name of the field as a Nim Ident node
## and the type of the field as a Nim Type node respectively.
for (fieldIdent {.inject.}, fieldType {.inject.}) in t.fields: body
#[ TODO: replace walkFieldDefs with things like this:
func columnNamesForModel*(modelType: typedesc): seq[string] =
modelType.fields.mapIt(identNameToDb($it[0]))
]#
macro columnNamesForModel*(modelType: typed): seq[string] =
## Return the column names corresponding to the the fields of the given
## `model class`_
var columnNames = newSeq[string]()
modelType.walkFieldDefs:
@ -247,6 +368,8 @@ macro columnNamesForModel*(modelType: typed): seq[string] =
result = newLit(columnNames)
macro rowToModel*(modelType: typed, row: seq[string]): untyped =
## Return a new Nim model object of type `modelType` populated with the
## values returned in the given database `row`
# Create the object constructor AST node
result = newNimNode(nnkObjConstr).add(modelType)
@ -260,6 +383,7 @@ macro rowToModel*(modelType: typed, row: seq[string]): untyped =
createParseStmt(fieldType, itemLookup)))
idx += 1
#[
macro listFields*(t: typed): untyped =
var fields: seq[tuple[n: string, t: string]] = @[]
t.walkFieldDefs:
@ -267,8 +391,10 @@ macro listFields*(t: typed): untyped =
else: fields.add((n: $fieldIdent, t: $fieldType))
result = newLit(fields)
]#
proc typeOfColumn*(modelType: NimNode, colName: string): NimNode =
## Given a model type and a column name, return the Nim type for that column.
modelType.walkFieldDefs:
if $fieldIdent != colName: continue
@ -287,8 +413,11 @@ proc typeOfColumn*(modelType: NimNode, colName: string): NimNode =
proc isEmpty(val: int): bool = return val == 0
proc isEmpty(val: UUID): bool = return val.isZero
proc isEmpty(val: string): bool = return val.isEmptyOrWhitespace
proc isEmpty[T](val: Option[T]): bool = return val.isNone
macro populateMutateClauses*(t: typed, newRecord: bool, mc: var MutateClauses): untyped =
## Given a record type, create the datastructure used to generate SQL clauses
## for the fields of this record type.
result = newStmtList()
@ -309,8 +438,8 @@ macro populateMutateClauses*(t: typed, newRecord: bool, mc: var MutateClauses):
# if we're looking at an optional field, add logic to check for presence
elif fieldType.kind == nnkBracketExpr and
fieldType.len > 0 and
fieldType[0] == Option.getType:
fieldType.len > 0 and
fieldType[0] == Option.getType:
result.add quote do:
`mc`.columns.add(identNameToDb(`fieldName`))
@ -326,3 +455,20 @@ macro populateMutateClauses*(t: typed, newRecord: bool, mc: var MutateClauses):
`mc`.columns.add(identNameToDb(`fieldName`))
`mc`.placeholders.add("?")
`mc`.values.add(dbFormat(`t`.`fieldIdent`))
proc getPagingClause*(page: PaginationParams): string =
## Given a `PaginationParams` object, return the SQL clause necessary to
## limit the number of records returned by a query.
result = ""
if page.orderBy.isSome:
let orderByClause = page.orderBy.get.map(identNameToDb).join(",")
result &= " ORDER BY " & orderByClause
else:
result &= " ORDER BY id"
result &= " LIMIT " & $page.pageSize & " OFFSET " & $page.offset
## .. _model class: ../fiber_orm.html#objectminusrelational-modeling-model-class
## .. _rules for name mapping: ../fiber_orm.html
## .. _table name: ../fiber_orm.html