Compare commits

...

36 Commits
0.1.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
1d7c955805 Bugfix: don't try to cull connections from an empty pool.
The first step in culling connections was to take a subset of the pool's
connections from index 0 to numToCull. This leads to an error if
numToCull is also zero.
2022-06-04 10:46:00 -05:00
9625ac6a5e withPool executes provided statement block in a try/finally to ensure the connection is released. 2022-04-25 18:22:34 -05:00
d4540a1de7 Make column ordering explicit in createRecord return.
The Nim [Row][nim-row] implementation only supports positional
identification of columns. In other words, there is nothing to tell us
which column is in which position. Because of this, we always create SQL
statements which explicitly name the columns we wish to receive so that
we know the order of columns and can rebuild models appropriately.

`createRule` wasn't doing this but naively using `RETURNING *`. This
still works as long as the field ordering in the Nim model class match
the default column ordering returned by the database, but confuses
columns otherwise. This fixes that by specifying explicitly the column
ordering as we do in other places.

[nim-row]: https://nim-lang.org/docs/db_postgres.html#Row
2022-03-11 12:54:14 -06:00
3e19b3628d Use a connection provider rather than long-lived connections.
The previous implementation expected to work with a context object that
contained a field `conn` that represented a valid database connection.
However, this required the caller to manage the connection's lifecycle
(close, renew, etc.). Now we expect to receive a context object that
provides a `withConn` procedure or template that accepts a statement
block and provides a `conn` variable to that code block. For example:

    createRecord(db: DbContext): Record =
        # withConn must be defined for DbContext
        db.withConn:
          # conn must be injected into the statement block context
          conn.exec(sql("INSERT INTO..."))

In addition, this change provides a connection pooling mechanism
(`DbConnPool`) as a default implementation for this hypothetical
DbContext. There is also a new function `initPool` that will create an
DbConnPool instance.

Callers of this library can modify their DbContext objects to extend
DbConnPool or simply be a type alias of DbConnPool.
2022-02-07 11:38:37 -06:00
8aad3cdb79 Make the logging namespace GC-safe. 2022-01-22 20:23:30 -06:00
f7791b6f60 Add logging statments (behind namespaced logger). 2022-01-13 16:22:15 -06:00
279d9aa7fd Expose a number of useful utility methods and macros. 2021-08-02 05:54:56 -05:00
d90372127b Further fix for ISO8601 date parsing.
Recognize versions of timestamps with 'T' as the date/time separator.
For example, compare:

    '2021-08-01 23:14:00-05:00'
    '2021-08-01T23:14:00-05:00'

This commit adds support for the second flavor (and it's variations).
2021-08-01 23:14:18 -05:00
2b78727356 Fix for PostgreSQL timestamp with timezone fields.
The previous fix for PostgreSQL timestamp fields matched fields with and
without timezones, but did not properly parse values from fields that
included the timezone. Now we check for the presence of the timezone in
the date string and choose a format string to parse it correctly.
2021-07-05 11:24:06 -05:00
445c86f97e Shuffle variable declarations to make functions GC-safe. 2021-07-02 20:34:29 -05:00
126167fdaf Fix name mapping in field lookups generation. 2021-07-02 20:16:49 -05:00
ff0c5e5305 Update to support Nim 1.4.x+ 2021-04-02 13:58:08 -05:00
bdd62cad66 Add support for float data type. 2020-02-16 21:50:43 -06:00
b496b10578 Bump version number for 0.2.0 release. 2020-02-09 04:22:59 -06:00
c6430baa9a Support more ID types, allow creating records with known IDs.
* Previously all ID types needed to have the `isZero` function defined.
  This was named after the UUID.isZero function but does not describe
  the general case. Non-numeric is types will not be "zero," they will
  be "empty." Renamed to `isEmpty` and implemented basic versions of
  this for `int`, `string`, and `UUID`.

* Previously newly created records were not allowed to have ids set.
  Fiber required them to be set in the database and the caller to
  retrieve the newly generated ID from the newly created record. This is
  not really a decision that Fiber should make in a general case. There
  are valid reasons for both possibilities (creating the id in the
  application code vs. creating the id in the database layer). As a
  more general-purpose solution Fiber now leaves this to the caller.

  If the caller provides an id value, Fiber will include it in the
  INSERT statement. If the id is unset, Fiber will not include it at
  all, allowing it to be generated in the database.
2020-01-02 18:55:46 -06:00
cd52c9860d Add support for enum values. 2020-01-02 18:55:38 -06:00
af755a8a8d Round out support for Option type model fields.
The Option type has two forms depending on the type of the wrapped value
(see https://nim-lang.org/docs/options.html#Option). We only supported
one of these previously. This commit adds support for the other type as
well.

Additionally, this fixes a compile error that was introduced into the
use of `isSome` in the generated code after Nim 1.0.
2020-01-02 18:51:38 -06:00
1f57e0dccc Fix support for PostgreSQL timestamp fields.
PostgreSQL uses a format similar to IS8601 but allows values expressed
with tenths or hundreths of seconds rather than just milliseconds.
`2020-01-01 12:34:98.3+00` as opposed to `2020-01-01 12:34:98.300+00`,
for example. The `times` module in the Nim stdlib supports only
milliseconds with exactly three digits. To bridge this gap we detect the
two unsupported cases and pad the fractional seconds out to millisecond
precision.
2020-01-02 18:46:54 -06:00
61e06842af Use the more intelligent pluralization method we built when generating findXsByField lookup functions. 2020-01-02 18:46:03 -06:00
934bb26cf3 Make the DB type object generic (not PmApiDb). 2020-01-02 18:44:44 -06:00
126c4f1c7c Make record not found error messages more descriptive. 2020-01-02 18:42:48 -06:00
18 changed files with 6869 additions and 109 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 # Package
version = "0.1.2" version = "3.1.1"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Lightweight Postgres ORM for Nim." description = "Lightweight Postgres ORM for Nim."
license = "GPL-3.0" license = "GPL-3.0"
@ -10,4 +10,5 @@ srcDir = "src"
# Dependencies # Dependencies
requires "nim >= 1.0.4" requires @["nim >= 1.4.0", "uuids"]
requires "namespaced_logging >= 1.0.0"

View File

@ -1,10 +1,327 @@
import db_postgres, macros, options, sequtils, strutils, uuids # Fiber ORM
#
# Copyright 2019-2024 Jonathan Bernard <jonathan@jdbernard.com>
from unicode import capitalize ## 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 import ./fiber_orm/util
type NotFoundError* = object of CatchableError export pool, util
type
PagedRecords*[T] = object
pagination*: Option[PaginationParams]
records*: seq[T]
totalRecords*: int
DbUpdateError* = object of CatchableError ##\
## Error types raised when a DB modification fails.
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 = proc newMutateClauses(): MutateClauses =
return MutateClauses( return MutateClauses(
@ -12,100 +329,269 @@ proc newMutateClauses(): MutateClauses =
placeholders: @[], placeholders: @[],
values: @[]) 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() var mc = newMutateClauses()
populateMutateClauses(rec, true, mc) populateMutateClauses(rec, true, mc)
# Confusingly, getRow allows inserts and updates. We use it to get back the ID let sqlStmt =
# we want from the row.
let newRow = db.getRow(sql(
"INSERT INTO " & tableName(rec) & "INSERT INTO " & tableName(rec) &
" (" & mc.columns.join(",") & ") " & " (" & mc.columns.join(",") & ") " &
" VALUES (" & mc.placeholders.join(",") & ") " & " VALUES (" & mc.placeholders.join(",") & ") " &
" RETURNING *"), mc.values) " RETURNING " & columnNamesForModel(rec).join(",")
logQuery("createRecord", sqlStmt)
let newRow = db.getRow(sql(sqlStmt), mc.values)
result = rowToModel(T, newRow) 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() var mc = newMutateClauses()
populateMutateClauses(rec, false, mc) populateMutateClauses(rec, false, mc)
let setClause = zip(mc.columns, mc.placeholders).mapIt(it.a & " = " & it.b).join(",") let setClause = zip(mc.columns, mc.placeholders).mapIt(it[0] & " = " & it[1]).join(",")
let numRowsUpdated = db.execAffectedRows(sql( let sqlStmt =
"UPDATE " & tableName(rec) & "UPDATE " & tableName(rec) &
" SET " & setClause & " SET " & setClause &
" WHERE id = ? "), mc.values.concat(@[$rec.id])) " WHERE id = ? "
logQuery("updateRecord", sqlStmt, [("id", $rec.id)])
let numRowsUpdated = db.execAffectedRows(sql(sqlStmt), mc.values.concat(@[$rec.id]))
return numRowsUpdated > 0; return numRowsUpdated > 0;
template deleteRecord*(db: DbConn, modelType: type, id: typed): untyped = proc createOrUpdateRecord*[D: DbConnType, T](db: D, rec: T): T =
db.tryExec(sql("DELETE FROM " & tableName(modelType) & " WHERE id = ?"), $id) ## 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.
proc deleteRecord*[T](db: DbConn, rec: T): bool = let findRecordStmt = "SELECT id FROM " & tableName(rec) & " WHERE id = ?"
return db.tryExec(sql("DELETE FROM " & tableName(rec) & " WHERE id = ?"), $rec.id) logQuery("createOrUpdateRecord", findRecordStmt, [("id", $rec.id)])
let rows = db.getAllRows(sql(findRecordStmt), [$rec.id])
template getRecord*(db: DbConn, modelType: type, id: typed): untyped = if rows.len == 0: result = createRecord(db, rec)
let row = db.getRow(sql( 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 = ?"
logQuery("deleteRecord", sqlStmt, [("id", $id)])
db.tryExec(sql(sqlStmt), $id)
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 = ?"
logQuery("deleteRecord", sqlStmt, [("id", $rec.id)])
return db.tryExec(sql(sqlStmt), $rec.id)
template getRecord*[D: DbConnType](db: D, modelType: type, id: typed): untyped =
## Fetch a record by id.
let sqlStmt =
"SELECT " & columnNamesForModel(modelType).join(",") & "SELECT " & columnNamesForModel(modelType).join(",") &
" FROM " & tableName(modelType) & " FROM " & tableName(modelType) &
" WHERE id = ?"), @[$id]) " WHERE id = ?"
if row.allIt(it.len == 0): logQuery("getRecord", sqlStmt, [("id", $id)])
raise newException(NotFoundError, "no record for id " & $id) let row = db.getRow(sql(sqlStmt), @[$id])
if allIt(row, it.len == 0):
raise newException(NotFoundError, "no " & modelName(modelType) & " record for id " & $id)
rowToModel(modelType, row) rowToModel(modelType, row)
template findRecordsWhere*(db: DbConn, modelType: type, whereClause: string, values: varargs[string, dbFormat]): untyped = template findRecordsWhere*[D: DbConnType](
db.getAllRows(sql( 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(",") & "SELECT " & columnNamesForModel(modelType).join(",") &
" FROM " & tableName(modelType) & " FROM " & tableName(modelType) &
" WHERE " & whereClause), values) " WHERE " & whereClause
.mapIt(rowToModel(modelType, it))
template getAllRecords*(db: DbConn, modelType: type): untyped = var countStmt =
db.getAllRows(sql( "SELECT COUNT(*) FROM " & tableName(modelType) &
" WHERE " & whereClause
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(",") & "SELECT " & columnNamesForModel(modelType).join(",") &
" FROM " & tableName(modelType))) " FROM " & tableName(modelType)
.mapIt(rowToModel(modelType, it))
template findRecordsBy*(db: DbConn, modelType: type, lookups: seq[tuple[field: string, value: string]]): untyped = var countStmt = "SELECT COUNT(*) FROM " & tableName(modelType)
db.getAllRows(sql(
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)
var fetchStmt =
"SELECT " & columnNamesForModel(modelType).join(",") & "SELECT " & columnNamesForModel(modelType).join(",") &
" FROM " & tableName(modelType) & " FROM " & tableName(modelType) &
" WHERE " & lookups.mapIt(it.field & " = ?").join(" AND ")), " WHERE " & whereClause
lookups.mapIt(it.value))
.mapIt(rowToModel(modelType, it))
macro generateProcsForModels*(modelTypes: openarray[type]): untyped = 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() result = newStmtList()
for t in modelTypes: 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 modelName = $(t.getType[1])
let getName = ident("get" & modelName) let getName = ident("get" & modelName)
let getAllName = ident("getAll" & modelName & "s") let getIfExistsName = ident("get" & modelName & "IfItExists")
let findWhereName = ident("find" & modelName & "sWhere") let getAllName = ident("getAll" & pluralize(modelName))
let findWhereName = ident("find" & pluralize(modelName) & "Where")
let createName = ident("create" & modelName) let createName = ident("create" & modelName)
let updateName = ident("update" & modelName) let updateName = ident("update" & modelName)
let createOrUpdateName = ident("createOrUpdate" & modelName)
let deleteName = ident("delete" & modelName) let deleteName = ident("delete" & modelName)
let idType = typeOfColumn(t, "id") let idType = typeOfColumn(t, "id")
result.add quote do: result.add quote do:
proc `getName`*(db: PMApiDb, id: `idType`): `t` = getRecord(db.conn, `t`, id) proc `getName`*(db: `dbType`, id: `idType`): `t` =
proc `getAllName`*(db: PMApiDb): seq[`t`] = getAllRecords(db.conn, `t`) db.withConnection conn: result = getRecord(conn, `t`, id)
proc `findWhereName`*(db: PMApiDb, whereClause: string, values: varargs[string, dbFormat]): seq[`t`] =
return findRecordsWhere(db.conn, `t`, whereClause, values)
proc `createName`*(db: PMApiDb, rec: `t`): `t` = createRecord(db.conn, rec)
proc `updateName`*(db: PMApiDb, rec: `t`): bool = updateRecord(db.conn, rec)
proc `deleteName`*(db: PMApiDb, rec: `t`): bool = deleteRecord(db.conn, rec)
proc `deleteName`*(db: PMApiDb, id: `idType`): bool = deleteRecord(db.conn, `t`, id)
macro generateLookup*(modelType: type, fields: seq[string]): untyped = proc `getIfExistsName`*(db: `dbType`, id: `idType`): Option[`t`] =
db.withConnection conn:
try: result = some(getRecord(conn, `t`, id))
except NotFoundError: result = none[`t`]()
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.withConnection conn: result = createRecord(conn, rec)
proc `updateName`*(db: `dbType`, rec: `t`): bool =
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.withConnection conn: result = deleteRecord(conn, rec)
proc `deleteName`*(db: `dbType`, id: `idType`): bool =
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 fieldNames = fields[1].mapIt($it)
let procName = ident("find" & $modelType.getType[1] & "sBy" & fieldNames.mapIt(it.capitalize).join("And")) let procName = ident("find" & pluralize($modelType.getType[1]) & "By" & fieldNames.mapIt(it.capitalize).join("And"))
# Create proc skeleton # Create proc skeleton
result = quote do: result = quote do:
proc `procName`*(db: PMApiDb): seq[`modelType`] = proc `procName`*(db: `dbType`): PagedRecords[`modelType`] =
return findRecordsBy(db.conn, `modelType`) db.withConnection conn: result = findRecordsBy(conn, `modelType`)
var callParams = quote do: @[] var callParams = quote do: @[]
@ -115,12 +601,27 @@ macro generateLookup*(modelType: type, fields: seq[string]): untyped =
paramTuple.add(newColonExpr(ident("field"), newLit(identNameToDb(n)))) paramTuple.add(newColonExpr(ident("field"), newLit(identNameToDb(n))))
paramTuple.add(newColonExpr(ident("value"), ident(n))) paramTuple.add(newColonExpr(ident("value"), ident(n)))
# Add the parameter to the outer call (the generated proc)
# result[3] is ProcDef -> [3]: FormalParams
result[3].add(newIdentDefs(ident(n), ident("string"))) result[3].add(newIdentDefs(ident(n), ident("string")))
# Build up the AST for the inner procedure call
callParams[1].add(paramTuple) callParams[1].add(paramTuple)
result[6][0][0].add(callParams) # Add the optional pagination parameters to the generated proc definition
result[3].add(newIdentDefs(
ident("pagination"), newEmptyNode(),
quote do: none[PaginationParams]()))
macro generateProcsForFieldLookups*(modelsAndFields: openarray[tuple[t: type, fields: seq[string]]]): untyped = # Add the call params to the inner procedure call
# result[6][0][1][0][1] is
# ProcDef -> [6]: StmtList (body) -> [0]: Command ->
# [2]: StmtList (withConnection body) -> [0]: Asgn (result =) ->
# [1]: Call (inner findRecords invocation)
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() result = newStmtList()
for i in modelsAndFields: for i in modelsAndFields:
@ -131,20 +632,65 @@ macro generateProcsForFieldLookups*(modelsAndFields: openarray[tuple[t: type, fi
# Create proc skeleton # Create proc skeleton
let procDefAST = quote do: let procDefAST = quote do:
proc `procName`*(db: PMApiDb): seq[`modelType`] = proc `procName`*(db: `dbType`): PagedRecords[`modelType`] =
return findRecordsBy(db.conn, `modelType`) db.withConnection conn: result = findRecordsBy(conn, `modelType`)
var callParams = quote do: @[] var callParams = quote do: @[]
# Add dynamic parameters for the proc definition and inner proc call # Add dynamic parameters for the proc definition and inner proc call
for n in fieldNames: for n in fieldNames:
let paramTuple = newNimNode(nnkPar) let paramTuple = newNimNode(nnkPar)
paramTuple.add(newColonExpr(ident("field"), newLit(n))) paramTuple.add(newColonExpr(ident("field"), newLit(identNameToDb(n))))
paramTuple.add(newColonExpr(ident("value"), ident(n))) paramTuple.add(newColonExpr(ident("value"), ident(n)))
procDefAST[3].add(newIdentDefs(ident(n), ident("string"))) procDefAST[3].add(newIdentDefs(ident(n), ident("string")))
callParams[1].add(paramTuple) callParams[1].add(paramTuple)
procDefAST[6][0][0].add(callParams) # 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 result.add procDefAST
proc initPool*[D: DbConnType](
connect: proc(): D,
poolSize = 10,
hardCap = false,
healthCheckQuery = "SELECT 'true' AS alive"): DbConnPool[D] =
## 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, body: untyped) =
db.withConnection conn:
conn.exec(sql"BEGIN TRANSACTION")
try:
body
conn.exec(sql"COMMIT")
except:
conn.exec(sql"ROLLBACK")
raise getCurrentException()

View File

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

114
src/fiber_orm/pool.nim Normal file
View File

@ -0,0 +1,114 @@
# Fiber ORM
#
# Copyright 2019 Jonathan Bernard <jonathan@jdbernard.com>
## 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*[D: DbConnType] = object
connect*: () -> D ## 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.
PooledDbConn[D: DbConnType] = ref object
conn: D
id: int
free: bool
DbConnPool*[D: DbConnType] = ref object
## Database connection pool
conns: seq[PooledDbConn[D]]
cfg: DbConnPoolConfig[D]
lastId: int
proc initDbConnPool*[D: DbConnType](cfg: DbConnPoolConfig[D]): DbConnPool[D] =
result = DbConnPool[D](
conns: @[],
cfg: cfg)
proc newConn[D: DbConnType](pool: DbConnPool[D]): PooledDbConn[D] =
pool.lastId += 1
{.gcsafe.}:
let conn = pool.cfg.connect()
result = PooledDbConn[D](
conn: conn,
id: pool.lastId,
free: true)
pool.conns.add(result)
proc maintain[D: DbConnType](pool: DbConnPool[D]): void =
pool.conns.keepIf(proc (pc: PooledDbConn[D]): bool =
if not pc.free: return true
try:
discard getRow(pc.conn, sql(pool.cfg.healthCheckQuery), [])
return true
except:
try: pc.conn.close() # try to close the connection
except: discard ""
return false
)
let freeConns = pool.conns.filterIt(it.free)
if pool.conns.len > pool.cfg.poolSize and freeConns.len > 0:
let numToCull = min(freeConns.len, pool.conns.len - pool.cfg.poolSize)
if numToCull > 0:
let toCull = freeConns[0..numToCull]
pool.conns.keepIf((pc) => toCull.allIt(it.id != pc.id))
for culled in toCull:
try: culled.conn.close()
except: discard ""
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)
let reserved =
if freeConns.len > 0: freeConns[0]
else: pool.newConn()
reserved.free = false
return (id: reserved.id, conn: reserved.conn)
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 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,34 +1,71 @@
import json, macros, options, sequtils, strutils, times, timeutils, unicode, # Fiber ORM
uuids #
# Copyright 2019 Jonathan Bernard <jonathan@jdbernard.com>
const UNDERSCORE_RUNE = "_".toRunes[0] ## Utility methods used internally by Fiber ORM.
const PG_TIMESTAMP_FORMATS = [ import std/[json, macros, options, sequtils, strutils, times, unicode]
"yyyy-MM-dd HH:mm:sszz", import uuids
"yyyy-MM-dd HH:mm:ss'.'fzz",
"yyyy-MM-dd HH:mm:ss'.'ffzz", import std/nre except toSeq
"yyyy-MM-dd HH:mm:ss'.'fffzz"
]
type type
PaginationParams* = object
pageSize*: int
offset*: int
orderBy*: Option[seq[string]]
MutateClauses* = object 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] columns*: seq[string]
placeholders*: seq[string] placeholders*: seq[string]
values*: 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 # TODO: more complete implementation
# see https://github.com/blakeembrey/pluralize # see https://github.com/blakeembrey/pluralize
proc pluralize(name: string): string = 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[^2..^1] == "ey": return name[0..^3] & "ies"
if name[^1] == 'y': return name[0..^2] & "ies" if name[^1] == 'y': return name[0..^2] & "ies"
return name & "s" return name & "s"
macro modelName*(model: object): string = macro modelName*(model: object): string =
## For a given concrete record object, return the name of the `model class`_
return newStrLitNode($model.getTypeInst) return newStrLitNode($model.getTypeInst)
macro modelName*(modelType: type): string = macro modelName*(modelType: type): string =
## Get the name of a given `model class`_
return newStrLitNode($modelType.getType[1]) return newStrLitNode($modelType.getType[1])
proc identNameToDb*(name: string): string = 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 let nameInRunes = name.toRunes
var prev: Rune var prev: Rune
var resultRunes = newSeq[Rune]() var resultRunes = newSeq[Rune]()
@ -46,35 +83,82 @@ proc identNameToDb*(name: string): string =
return $resultRunes return $resultRunes
proc dbNameToIdent*(name: string): string = proc dbNameToIdent*(name: string): string =
## Map a DB name to a Nim identifier name. See the `rules for name mapping`_
let parts = name.split("_") let parts = name.split("_")
return @[parts[0]].concat(parts[1..^1].mapIt(capitalize(it))).join("") return @[parts[0]].concat(parts[1..^1].mapIt(capitalize(it))).join("")
proc tableName*(modelType: type): string = proc tableName*(modelType: type): string =
## Get the `table name`_ for a given `model class`_
return pluralize(modelName(modelType).identNameToDb) return pluralize(modelName(modelType).identNameToDb)
proc tableName*[T](rec: T): string = proc tableName*[T](rec: T): string =
## Get the `table name`_ for a given record.
return pluralize(modelName(rec).identNameToDb) 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 = proc dbFormat*[T](list: seq[T]): string =
## Format a `seq` for inclusion in a SQL Query.
return "{" & list.mapIt(dbFormat(it)).join(",") & "}" 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 type DbArrayParseState = enum
expectStart, inQuote, inVal, expectEnd expectStart, inQuote, inVal, expectEnd
proc parsePGDatetime*(val: string): DateTime = 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",
"yyyy-MM-dd'T'HH:mm:ss",
"yyyy-MM-dd HH:mm:sszz",
"yyyy-MM-dd'T'HH:mm:sszz",
"yyyy-MM-dd HH:mm:ss'.'fff",
"yyyy-MM-dd'T'HH:mm:ss'.'fff",
"yyyy-MM-dd HH:mm:ss'.'fffzz",
"yyyy-MM-dd'T'HH:mm:ss'.'fffzz",
"yyyy-MM-dd HH:mm:ss'.'fffzzz",
"yyyy-MM-dd'T'HH:mm:ss'.'fffzzz",
]
var correctedVal = val;
# 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')[0..2]
else: correctedVal = c[0] & alignLeft(c[2], 3, '0')[0..2] & c[3]
var errStr = "" var errStr = ""
# Try to parse directly using known format strings.
for df in PG_TIMESTAMP_FORMATS: for df in PG_TIMESTAMP_FORMATS:
try: return val.parse(df) try: return correctedVal.parse(df)
except: errStr &= "\n" & getCurrentExceptionMsg() 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] = proc parseDbArray*(val: string): seq[string] =
## Parse a Postgres array column into a Nim seq[string]
result = newSeq[string]() result = newSeq[string]()
var parseState = DbArrayParseState.expectStart var parseState = DbArrayParseState.expectStart
@ -132,26 +216,46 @@ proc parseDbArray*(val: string): seq[string] =
if not (parseState == inQuote) and curStr.len > 0: if not (parseState == inQuote) and curStr.len > 0:
result.add(curStr) 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.typeKind == ntyObject:
if t.getType == UUID.getType: if t.getTypeInst == Option.getType:
result = quote do: parseUUID(`value`) 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]
innerType = innerType[1] # now we can take the field type from the first symbol
elif t.getType == DateTime.getType:
result = quote do: parsePGDatetime(`value`)
elif t.getTypeInst == Option.getType:
let innerType = t.getTypeImpl[2][0][0][1]
let parseStmt = createParseStmt(innerType, value) let parseStmt = createParseStmt(innerType, value)
result = quote do: result = quote do:
if `value`.len == 0: none[`innerType`]() if `value`.len == 0: none[`innerType`]()
else: some(`parseStmt`) 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 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: elif t.typeKind == ntyRef:
if $t.getTypeInst == "JsonNode": if $t.getTypeInst == "JsonNode":
@ -173,33 +277,89 @@ proc createParseStmt*(t, value: NimNode): NimNode =
elif t.typeKind == ntyInt: elif t.typeKind == ntyInt:
result = quote do: parseInt(`value`) result = quote do: parseInt(`value`)
elif t.typeKind == ntyFloat:
result = quote do: parseFloat(`value`)
elif t.typeKind == ntyBool: elif t.typeKind == ntyBool:
result = quote do: "true".startsWith(`value`.toLower) result = quote do: "true".startsWith(`value`.toLower)
elif t.typeKind == ntyEnum:
let innerType = t.getTypeInst
result = quote do: parseEnum[`innerType`](`value`)
else: else:
error "Unknown value type: " & $t.typeKind error "Unknown value type: " & $t.typeKind
template walkFieldDefs*(t: NimNode, body: untyped) = func fields(t: NimNode): seq[tuple[fieldIdent: NimNode, fieldType: NimNode]] =
let tTypeImpl = t.getTypeImpl #[
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 debugEcho "T.GET_TYPE: " & t.getType.treeRepr
if tTypeImpl.typeKind == ntyObject: nodeToItr = tTypeImpl[2] debugEcho "T.GET_TYPE[1].GET_TYPE: " & t.getType[1].getType.treeRepr
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 & ")."
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 # ignore AST nodes that are not field definitions
if fieldDef.kind == nnkIdentDefs: if fieldDef.kind == nnkIdentDefs: result.add((fieldDef[0], fieldDef[1]))
let fieldIdent {.inject.} = fieldDef[0] elif fieldDef.kind == nnkSym: result.add((fieldDef, fieldDef.getTypeInst))
let fieldType {.inject.} = fieldDef[1] else: error "unknown object field definition AST: $#" % $fieldDef.kind
body
elif fieldDef.kind == nnkSym: template walkFieldDefs*(t: NimNode, body: untyped) =
let fieldIdent {.inject.} = fieldDef ## Iterate over every field of the given Nim object, yielding and defining
let fieldType {.inject.} = fieldDef.getType ## `fieldIdent` and `fieldType`, the name of the field as a Nim Ident node
body ## 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] = macro columnNamesForModel*(modelType: typed): seq[string] =
## Return the column names corresponding to the the fields of the given
## `model class`_
var columnNames = newSeq[string]() var columnNames = newSeq[string]()
modelType.walkFieldDefs: modelType.walkFieldDefs:
@ -208,6 +368,8 @@ macro columnNamesForModel*(modelType: typed): seq[string] =
result = newLit(columnNames) result = newLit(columnNames)
macro rowToModel*(modelType: typed, row: seq[string]): untyped = 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 # Create the object constructor AST node
result = newNimNode(nnkObjConstr).add(modelType) result = newNimNode(nnkObjConstr).add(modelType)
@ -221,6 +383,7 @@ macro rowToModel*(modelType: typed, row: seq[string]): untyped =
createParseStmt(fieldType, itemLookup))) createParseStmt(fieldType, itemLookup)))
idx += 1 idx += 1
#[
macro listFields*(t: typed): untyped = macro listFields*(t: typed): untyped =
var fields: seq[tuple[n: string, t: string]] = @[] var fields: seq[tuple[n: string, t: string]] = @[]
t.walkFieldDefs: t.walkFieldDefs:
@ -228,8 +391,10 @@ macro listFields*(t: typed): untyped =
else: fields.add((n: $fieldIdent, t: $fieldType)) else: fields.add((n: $fieldIdent, t: $fieldType))
result = newLit(fields) result = newLit(fields)
]#
proc typeOfColumn*(modelType: NimNode, colName: string): NimNode = proc typeOfColumn*(modelType: NimNode, colName: string): NimNode =
## Given a model type and a column name, return the Nim type for that column.
modelType.walkFieldDefs: modelType.walkFieldDefs:
if $fieldIdent != colName: continue if $fieldIdent != colName: continue
@ -241,13 +406,18 @@ proc typeOfColumn*(modelType: NimNode, colName: string): NimNode =
else: error "Unknown column type: " & $fieldType.getTypeInst else: error "Unknown column type: " & $fieldType.getTypeInst
else: return fieldType else: return fieldType
raise newException(Exception, raise newException(Exception,
"model of type '" & $modelType & "' has no column named '" & colName & "'") "model of type '" & $modelType & "' has no column named '" & colName & "'")
proc isZero(val: int): bool = return val == 0 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 = 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() result = newStmtList()
@ -257,23 +427,23 @@ macro populateMutateClauses*(t: typed, newRecord: bool, mc: var MutateClauses):
# grab the field, it's string name, and it's type # grab the field, it's string name, and it's type
let fieldName = $fieldIdent let fieldName = $fieldIdent
# we do not update the ID, but we do check: if we're creating a new # We only add clauses for the ID field if we're creating a new record and
# record, we should not have an existing ID # the caller provided a value..
if fieldName == "id": if fieldName == "id":
result.add quote do: result.add quote do:
if `newRecord` and not `t`.id.isZero: if `newRecord` and not `t`.id.isEmpty:
raise newException( `mc`.columns.add(identNameToDb(`fieldName`))
AssertionError, `mc`.placeholders.add("?")
"Trying to create a new record, but the record already has an ID (" & $(`t`.id) & ").") `mc`.values.add(dbFormat(`t`.`fieldIdent`))
# if we're looking at an optional field, add logic to check for presence # if we're looking at an optional field, add logic to check for presence
elif fieldType.kind == nnkBracketExpr and elif fieldType.kind == nnkBracketExpr and
fieldType.len > 0 and fieldType.len > 0 and
fieldType[0] == Option.getType: fieldType[0] == Option.getType:
result.add quote do: result.add quote do:
`mc`.columns.add(identNameToDb(`fieldName`)) `mc`.columns.add(identNameToDb(`fieldName`))
if `t`.`fieldIdent`.isSome: if isSome(`t`.`fieldIdent`):
`mc`.placeholders.add("?") `mc`.placeholders.add("?")
`mc`.values.add(dbFormat(`t`.`fieldIdent`.get)) `mc`.values.add(dbFormat(`t`.`fieldIdent`.get))
else: else:
@ -285,3 +455,20 @@ macro populateMutateClauses*(t: typed, newRecord: bool, mc: var MutateClauses):
`mc`.columns.add(identNameToDb(`fieldName`)) `mc`.columns.add(identNameToDb(`fieldName`))
`mc`.placeholders.add("?") `mc`.placeholders.add("?")
`mc`.values.add(dbFormat(`t`.`fieldIdent`)) `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