Compare commits
	
		
			20 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| bb36bba864 | |||
| f54bf6e974 | |||
| e1fa2480d0 | |||
| b8c64cc693 | |||
| aa02f9f5b1 | |||
| 9d1cc4bbec | |||
| af44d48df1 | |||
| 2030fd4490 | |||
| 0599d41061 | |||
| fb74d84cb7 | |||
| fbd20de71f | |||
| 540d0d2f67 | |||
| a05555ee67 | |||
| 454fc8c47a | |||
| 004e3c19bb | |||
| 4e0a173c76 | |||
| bd5d8c98b8 | |||
| 7d45346bb6 | |||
| a3dbb0bbbc | |||
| 9bf3c4f3ec | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,4 @@ | ||||
| *.sw? | ||||
| nimcache/ | ||||
| nimble.develop | ||||
| nimble.paths | ||||
|   | ||||
							
								
								
									
										10
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Makefile
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										277
									
								
								README.rst
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										202
									
								
								docs/README.html
									
									
									
									
									
										Normal 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 "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, | ||||
| );</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">""</span><span class="Punctuation">,</span> <span class="StringLit">""</span><span class="Punctuation">,</span> <span class="StringLit">""</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">"todoItemId"</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> <-> <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> <-> <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>   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">""</span><span class="Punctuation">,</span> <span class="StringLit">""</span><span class="Punctuation">,</span> <span class="StringLit">""</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
									
								
							
							
						
						
									
										2041
									
								
								docs/dochack.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										619
									
								
								docs/fiber_orm.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										619
									
								
								docs/fiber_orm.html
									
									
									
									
									
										Normal 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> | ||||
|         <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 = "SELECT \'true\' AS alive"): DbConnPool">initPool(connect: proc (): DbConn; poolSize = 10; hardCap = false; | ||||
|          healthCheckQuery = "SELECT \'true\' AS alive"): 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> | ||||
|     <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L1" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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 "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, | ||||
| );</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">""</span><span class="Punctuation">,</span> <span class="StringLit">""</span><span class="Punctuation">,</span> <span class="StringLit">""</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">"todoItemId"</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> <-> <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> <-> <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">"John Mann"</span><span class="Punctuation">,</span> | ||||
|   <span class="Identifier">summary</span><span class="Punctuation">:</span> <span class="StringLit">"Create a grocery list."</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">"John Mann"</span><span class="Punctuation">,</span> | ||||
|   <span class="Identifier">summary</span><span class="Punctuation">:</span> <span class="StringLit">"Create a grocery list."</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>   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">""</span><span class="Punctuation">,</span> <span class="StringLit">""</span><span class="Punctuation">,</span> <span class="StringLit">""</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 | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L299" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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> | ||||
|  | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L314" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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>. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L359" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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">"SELECT \'true\' AS alive"</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> | ||||
|  | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L539" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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>. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L337" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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">"owner"</span><span class="Punctuation">,</span> <span class="StringLit">"priority"</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> | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L468" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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> | ||||
|  | ||||
|  | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L510" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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> | ||||
|  | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L414" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L353" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L403" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L382" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L394" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L367" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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> | ||||
|  | ||||
|  | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L567" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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
									
								
							
							
						
						
									
										25
									
								
								docs/fiber_orm.idx
									
									
									
									
									
										Normal 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 = "SELECT \'true\' AS alive"): 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
									
								
							
							
						
						
									
										303
									
								
								docs/fiber_orm/pool.html
									
									
									
									
									
										Normal 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> | ||||
|         <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*: () -> 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> | ||||
|     <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> | ||||
|   <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 | ||||
|   <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> | ||||
|   <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">-></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> | ||||
|  | ||||
|  | ||||
|   <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> | ||||
|   <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> | ||||
|  | ||||
|  | ||||
|   <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> | ||||
|   <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. | ||||
|   <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> | ||||
|   <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> | ||||
|  | ||||
|   <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> | ||||
|   <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> | ||||
|  | ||||
|   <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> | ||||
|   <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
									
								
							
							
						
						
									
										6
									
								
								docs/fiber_orm/pool.idx
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										524
									
								
								docs/fiber_orm/util.html
									
									
									
									
									
										Normal 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> | ||||
|         <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> | ||||
|     <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> | ||||
|   <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. | ||||
|   <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> | ||||
|   <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. | ||||
|   <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> | ||||
|   <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. | ||||
|   <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> | ||||
|   <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. | ||||
|   <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> | ||||
|   <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> | ||||
|   <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> | ||||
|   <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. | ||||
|   <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> | ||||
|   <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> | ||||
|   <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> | ||||
|   <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> | ||||
|  | ||||
|   <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> | ||||
|   <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] | ||||
|   <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> | ||||
|   <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. | ||||
|   <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> | ||||
|   <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. | ||||
|   <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> | ||||
|   <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> | ||||
|   <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> | ||||
|   <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. | ||||
|   <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> | ||||
|   <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. | ||||
|   <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> | ||||
|   <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> | ||||
|   <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> | ||||
|   <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> | ||||
|  | ||||
|  | ||||
|   <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> | ||||
|   <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> | ||||
|   <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> | ||||
|   <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> | ||||
|   <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> | ||||
|   <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. | ||||
|   <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> | ||||
|   <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> | ||||
|   <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> | ||||
|   <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. | ||||
|   <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> | ||||
|   <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
									
								
							
							
						
						
									
										21
									
								
								docs/fiber_orm/util.idx
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										619
									
								
								docs/index.html
									
									
									
									
									
										Normal 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> | ||||
|         <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 = "SELECT \'true\' AS alive"): DbConnPool">initPool(connect: proc (): DbConn; poolSize = 10; hardCap = false; | ||||
|          healthCheckQuery = "SELECT \'true\' AS alive"): 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> | ||||
|     <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L1" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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 "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, | ||||
| );</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">""</span><span class="Punctuation">,</span> <span class="StringLit">""</span><span class="Punctuation">,</span> <span class="StringLit">""</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">"todoItemId"</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> <-> <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> <-> <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">"John Mann"</span><span class="Punctuation">,</span> | ||||
|   <span class="Identifier">summary</span><span class="Punctuation">:</span> <span class="StringLit">"Create a grocery list."</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">"John Mann"</span><span class="Punctuation">,</span> | ||||
|   <span class="Identifier">summary</span><span class="Punctuation">:</span> <span class="StringLit">"Create a grocery list."</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>   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">""</span><span class="Punctuation">,</span> <span class="StringLit">""</span><span class="Punctuation">,</span> <span class="StringLit">""</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 | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L299" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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> | ||||
|  | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L314" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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>. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L359" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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">"SELECT \'true\' AS alive"</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> | ||||
|  | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L539" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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>. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L337" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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">"owner"</span><span class="Punctuation">,</span> <span class="StringLit">"priority"</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> | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L468" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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> | ||||
|  | ||||
|  | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L510" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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> | ||||
|  | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L414" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L353" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L403" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L382" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L394" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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. | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L367" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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> | ||||
|  | ||||
|  | ||||
|   <a | ||||
| href="https://github.com/jdbernard/fiber-orm/tree/version-1-6/src/fiber_orm.nim#L567" | ||||
| class="link-seesrc" target="_blank">Source</a> | ||||
|   <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
									
								
							
							
						
						
									
										1016
									
								
								docs/nimdoc.out.css
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										246
									
								
								docs/theindex.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								docs/theindex.html
									
									
									
									
									
										Normal 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 = "SELECT \'true\' AS alive"): DbConnPool" href="fiber_orm.html#initPool%2Cproc%29%2Cint%2Cstring">fiber_orm: initPool(connect: proc (): DbConn; poolSize = 10; hardCap = false; | ||||
|          healthCheckQuery = "SELECT \'true\' AS alive"): 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> | ||||
| @@ -1,6 +1,6 @@ | ||||
| # Package | ||||
|  | ||||
| version       = "1.0.2" | ||||
| version       = "4.1.0" | ||||
| author        = "Jonathan Bernard" | ||||
| description   = "Lightweight Postgres ORM for Nim." | ||||
| license       = "GPL-3.0" | ||||
| @@ -10,5 +10,5 @@ srcDir        = "src" | ||||
|  | ||||
| # Dependencies | ||||
|  | ||||
| requires "nim >= 1.4.0" | ||||
| requires "https://git.jdb-software.com/jdb/nim-namespaced-logging.git" | ||||
| requires @["nim >= 1.4.0", "uuids"] | ||||
| requires "namespaced_logging >= 2.0.2" | ||||
|   | ||||
| @@ -1,28 +1,313 @@ | ||||
| import std/db_postgres, std/macros, std/options, std/sequtils, std/strutils | ||||
| import namespaced_logging, uuids | ||||
| # Fiber ORM | ||||
| # | ||||
| # Copyright 2019-2024 Jonathan Bernard <jonathan@jdbernard.com> | ||||
|  | ||||
| ## Lightweight ORM supporting the `Postgres`_ and `SQLite`_ databases in Nim. | ||||
| ## It supports a simple, opinionated model mapper to generate SQL queries based | ||||
| ## on Nim objects. It also includes a simple connection pooling implementation. | ||||
| ## | ||||
| ## Fiber ORM is not intended to be a 100% all-cases-covered ORM that handles | ||||
| ## every potential data access pattern one might wish to implement. It is best | ||||
| ## thought of as a collection of common SQL generation patterns. It is intended | ||||
| ## to cover 90% of the common queries and functions one might write when | ||||
| ## implementing an SQL-based access layer. It is expected that there may be a | ||||
| ## few more complicated queries that need to be implemented to handle specific | ||||
| ## access patterns. | ||||
| ## | ||||
| ## The simple mapping pattern provided by Fiber ORM also works well on top of | ||||
| ## databases that encapsulate data access logic in SQL with, for example, | ||||
| ## views. | ||||
| ## | ||||
| ## .. _Postgres: https://nim-lang.org/docs/db_postgres.html | ||||
| ## .. _SQLite: https://nim-lang.org/docs/db_sqlite.html | ||||
| ## | ||||
| ## Basic Usage | ||||
| ## =========== | ||||
| ## | ||||
| ## Consider a simple TODO list application that keeps track of TODO items as | ||||
| ## well as time logged against those items. | ||||
| ## | ||||
| ## Example DB Schema | ||||
| ## ----------------- | ||||
| ## | ||||
| ## You might have a schema such as: | ||||
| ## | ||||
| ## .. code-block:: SQL | ||||
| ##    create extension if not exists "pgcrypto"; | ||||
| ## | ||||
| ##    create table todo_items columns ( | ||||
| ##      id uuid not null primary key default gen_random_uuid(), | ||||
| ##      owner varchar not null, | ||||
| ##      summary varchar not null, | ||||
| ##      details varchar default null, | ||||
| ##      priority integer not null default 0, | ||||
| ##      related_todo_item_ids uuid[] not null default '{}' | ||||
| ##    ); | ||||
| ## | ||||
| ##    create table time_entries columns ( | ||||
| ##      id uuid not null primary key default gen_random_uuid(), | ||||
| ##      todo_item_id uuid not null references todo_items (id) on delete cascade, | ||||
| ##      start timestamp with timezone not null default current_timestamp, | ||||
| ##      stop timestamp with timezone default null, | ||||
| ##    ); | ||||
| ## | ||||
| ## Example Model Definitions | ||||
| ## ------------------------- | ||||
| ## | ||||
| ## Models may be defined as: | ||||
| ## | ||||
| ## .. code-block:: Nim | ||||
| ##    # models.nim | ||||
| ##    import std/options, std/times | ||||
| ##    import uuids | ||||
| ## | ||||
| ##    type | ||||
| ##      TodoItem* = object | ||||
| ##        id*: UUID | ||||
| ##        owner*: string | ||||
| ##        summary*: string | ||||
| ##        details*: Option[string] | ||||
| ##        priority*: int | ||||
| ##        relatedTodoItemIds*: seq[UUID] | ||||
| ## | ||||
| ##      TimeEntry* = object | ||||
| ##        id*: UUID | ||||
| ##        todoItemId*: Option[UUID] | ||||
| ##        start*: DateTime | ||||
| ##        stop*: Option[DateTime] | ||||
| ## | ||||
| ## Example Fiber ORM Usage | ||||
| ## ----------------------- | ||||
| ## | ||||
| ## Using Fiber ORM we can generate a data access layer with: | ||||
| ## | ||||
| ## .. code-block:: Nim | ||||
| ##    # db.nim | ||||
| ##    import std/db_postgres | ||||
| ##    import fiber_orm | ||||
| ##    import ./models.nim | ||||
| ## | ||||
| ##    type TodoDB* = DbConnPool | ||||
| ## | ||||
| ##    proc initDb*(connString: string): TodoDB = | ||||
| ##      result = fiber_orm.initPool( | ||||
| ##        connect = proc(): DbConn = open("", "", "", connString), | ||||
| ##        poolSize = 20, | ||||
| ##        hardCap = false) | ||||
| ## | ||||
| ## | ||||
| ##    generateProcsForModels(TodoDB, [TodoItem, TimeEntry]) | ||||
| ## | ||||
| ##    generateLookup(TodoDB, TimeEntry, @["todoItemId"]) | ||||
| ## | ||||
| ## This will generate the following procedures: | ||||
| ## | ||||
| ## .. code-block:: Nim | ||||
| ##    proc getTodoItem*(db: TodoDB, id: UUID): TodoItem; | ||||
| ## | ||||
| ##    proc createTodoItem*(db: TodoDB, rec: TodoItem): TodoItem; | ||||
| ##    proc updateTodoItem*(db: TodoDB, rec: TodoItem): bool; | ||||
| ##    proc createOrUpdateTodoItem*(db: TodoDB, rec: TodoItem): bool; | ||||
| ##    proc deleteTodoItem*(db: TodoDB, rec: TodoItem): bool; | ||||
| ##    proc deleteTodoItem*(db: TodoDB, id: UUID): bool; | ||||
| ## | ||||
| ##    proc getAllTodoItems*(db: TodoDB, | ||||
| ##      pagination = none[PaginationParams]()): seq[TodoItem]; | ||||
| ## | ||||
| ##    proc findTodoItemsWhere*(db: TodoDB, whereClause: string, | ||||
| ##      values: varargs[string, dbFormat], pagination = none[PaginationParams]() | ||||
| ##      ): seq[TodoItem]; | ||||
| ## | ||||
| ##    proc getTimeEntry*(db: TodoDB, id: UUID): TimeEntry; | ||||
| ##    proc createTimeEntry*(db: TodoDB, rec: TimeEntry): TimeEntry; | ||||
| ##    proc updateTimeEntry*(db: TodoDB, rec: TimeEntry): bool; | ||||
| ##    proc deleteTimeEntry*(db: TodoDB, rec: TimeEntry): bool; | ||||
| ##    proc deleteTimeEntry*(db: TodoDB, id: UUID): bool; | ||||
| ## | ||||
| ##    proc getAllTimeEntries*(db: TodoDB, | ||||
| ##      pagination = none[PaginationParams]()): seq[TimeEntry]; | ||||
| ## | ||||
| ##    proc findTimeEntriesWhere*(db: TodoDB, whereClause: string, | ||||
| ##      values: varargs[string, dbFormat], pagination = none[PaginationParams]() | ||||
| ##      ): seq[TimeEntry]; | ||||
| ## | ||||
| ##    proc findTimeEntriesByTodoItemId(db: TodoDB, todoItemId: UUID, | ||||
| ##      pagination = none[PaginationParams]()): seq[TimeEntry]; | ||||
| ## | ||||
| ## Object-Relational Modeling | ||||
| ## ========================== | ||||
| ## | ||||
| ## Model Class | ||||
| ## ----------- | ||||
| ## | ||||
| ## Fiber ORM uses simple Nim `object`s and `ref object`s as model classes. | ||||
| ## Fiber ORM expects there to be one table for each model class. | ||||
| ## | ||||
| ## Name Mapping | ||||
| ## ```````````` | ||||
| ## Fiber ORM uses `snake_case` for database identifiers (column names, table | ||||
| ## names, etc.) and `camelCase` for Nim identifiers. We automatically convert | ||||
| ## model names to and from table names (`TodoItem` <-> `todo_items`), as well | ||||
| ## as column names (`userId` <-> `user_id`). | ||||
| ## | ||||
| ## Notice that table names are automatically pluralized from model class names. | ||||
| ## In the above example, you have: | ||||
| ## | ||||
| ## ===========    ================ | ||||
| ## Model Class    Table Name | ||||
| ## ===========    ================ | ||||
| ## TodoItem       todo_items | ||||
| ## TimeEntry      time_entries | ||||
| ## ===========    ================ | ||||
| ## | ||||
| ## Because Nim is style-insensitive, you can generall refer to model classes | ||||
| ## and fields using `snake_case`, `camelCase`, or `PascalCase` in your code and | ||||
| ## expect Fiber ORM to be able to map the names to DB identifier names properly | ||||
| ## (though FiberORM will always use `camelCase` internally). | ||||
| ## | ||||
| ## See the `identNameToDb`_, `dbNameToIdent`_, `tableName`_ and `dbFormat`_ | ||||
| ## procedures in the `fiber_orm/util`_ module for details. | ||||
| ## | ||||
| ## .. _identNameToDb: fiber_orm/util.html#identNameToDb,string | ||||
| ## .. _dbNameToIdent: fiber_orm/util.html#dbNameToIdent,string | ||||
| ## .. _tableName: fiber_orm/util.html#tableName,type | ||||
| ## .. _dbFormat: fiber_orm/util.html#dbFormat,DateTime | ||||
| ## .. _util: fiber_orm/util.html | ||||
| ## | ||||
| ## ID Field | ||||
| ## ```````` | ||||
| ## | ||||
| ## Fiber ORM expects every model class to have a field named `id`, with a | ||||
| ## corresponding `id` column in the model table. This field must be either a | ||||
| ## `string`, `integer`, or `UUID`_. | ||||
| ## | ||||
| ## When creating a new record the `id` field will be omitted if it is empty | ||||
| ## (`Option.isNone`_, `UUID.isZero`_, value of `0`, or only whitespace).  This | ||||
| ## is intended to allow for cases like the example where the database may | ||||
| ## generate an ID when a new record is inserted. If a non-zero value is | ||||
| ## provided, the create call will include the `id` field in the `INSERT` query. | ||||
| ## | ||||
| ## For example, to allow the database to create the id: | ||||
| ## | ||||
| ## .. code-block:: Nim | ||||
| ##    let item = TodoItem( | ||||
| ##      owner: "John Mann", | ||||
| ##      summary: "Create a grocery list.", | ||||
| ##      details: none[string](), | ||||
| ##      priority: 0, | ||||
| ##      relatedTodoItemIds: @[]) | ||||
| ## | ||||
| ##    let itemWithId = db.createTodoItem(item) | ||||
| ##    echo $itemWithId.id # generated in the database | ||||
| ## | ||||
| ## And to create it in code: | ||||
| ## | ||||
| ## .. code-block:: Nim | ||||
| ##    import uuids | ||||
| ## | ||||
| ##    let item = TodoItem( | ||||
| ##      id: genUUID(), | ||||
| ##      owner: "John Mann", | ||||
| ##      summary: "Create a grocery list.", | ||||
| ##      details: none[string](), | ||||
| ##      priority: 0, | ||||
| ##      relatedTodoItemIds: @[]) | ||||
| ## | ||||
| ##    let itemInDb = db.createTodoItem(item) | ||||
| ##    echo $itemInDb.id # will be the same as what was provided | ||||
| ## | ||||
| ## .. _Option.isNone: https://nim-lang.org/docs/options.html#isNone,Option[T] | ||||
| ## .. _UUID.isZero: https://github.com/pragmagic/uuids/blob/8cb8720b567c6bcb261bd1c0f7491bdb5209ad06/uuids.nim#L72 | ||||
| ## | ||||
| ## Supported Data Types | ||||
| ## -------------------- | ||||
| ## | ||||
| ## The following Nim data types are supported by Fiber ORM: | ||||
| ## | ||||
| ## ===============  ======================  ================= | ||||
| ## Nim Type         Postgres Type           SQLite Type | ||||
| ## ===============  ======================  ================= | ||||
| ## `string`         `varchar`_ | ||||
| ## `int`            `integer`_ | ||||
| ## `float`          `double`_ | ||||
| ## `bool`           `boolean`_ | ||||
| ## `DateTime`_      `timestamp`_ | ||||
| ## `seq[]`          `array`_ | ||||
| ## `UUID`_          `uuid (pg)`_ | ||||
| ## `Option`_        *allows* `NULL` [#f1]_ | ||||
| ## `JsonNode`_      `jsonb`_ | ||||
| ## ===============  ======================  ================= | ||||
| ## | ||||
| ## .. [#f1] Note that this implies that all `NULL`-able fields should be typed | ||||
| ##          as optional using `Option[fieldType]`. Conversely, any fields with | ||||
| ##          non-optional types should also be constrained to be `NOT NULL` in | ||||
| ##          the database schema. | ||||
| ## | ||||
| ## .. _DateTime: https://nim-lang.org/docs/times.html#DateTime | ||||
| ## .. _UUID: https://github.com/pragmagic/uuids | ||||
| ## .. _Option: https://nim-lang.org/docs/options.html#Option | ||||
| ## .. _JsonNode: https://nim-lang.org/docs/json.html#JsonNode | ||||
| ## | ||||
| ## .. _varchar: https://www.postgresql.org/docs/current/datatype-character.html | ||||
| ## .. _integer: https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-INT | ||||
| ## .. _double: https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-FLOAT | ||||
| ## .. _boolean: https://www.postgresql.org/docs/current/datatype-boolean.html | ||||
| ## .. _timestamp: https://www.postgresql.org/docs/current/datatype-datetime.html | ||||
| ## .. _array: https://www.postgresql.org/docs/current/arrays.html | ||||
| ## .. _uuid (pg): https://www.postgresql.org/docs/current/datatype-uuid.html | ||||
| ## .. _jsonb: https://www.postgresql.org/docs/current/datatype-json.html | ||||
| ## | ||||
| ## Database Object | ||||
| ## =============== | ||||
| ## | ||||
| ## Many of the Fiber ORM macros expect a database object type to be passed. | ||||
| ## In the example above the `pool.DbConnPool`_ object is used as database | ||||
| ## object type (aliased as `TodoDB`). This is the intended usage pattern, but | ||||
| ## anything can be passed as the database object type so long as there is a | ||||
| ## defined `withConnection` template that provides a `conn: DbConn` object | ||||
| ## to the provided statement body. | ||||
| ## | ||||
| ## For example, a valid database object implementation that opens a new | ||||
| ## connection for every request might look like this: | ||||
| ## | ||||
| ## .. code-block:: Nim | ||||
| ##    import db_connector/db_postgres | ||||
| ## | ||||
| ##    type TodoDB* = object | ||||
| ##      connString: string | ||||
| ## | ||||
| ##    template withConnection*(db: TodoDB, stmt: untyped): untyped = | ||||
| ##      block: | ||||
| ##        let conn = open("", "", "", db.connString) | ||||
| ##        try: stmt | ||||
| ##        finally: close(conn) | ||||
| ## | ||||
| ## .. _pool.DbConnPool: fiber_orm/pool.html#DbConnPool | ||||
| ## | ||||
| import std/[json, macros, options, sequtils, strutils] | ||||
| import db_connector/db_common | ||||
| import uuids | ||||
|  | ||||
| from std/unicode import capitalize | ||||
|  | ||||
| import ./fiber_orm/pool | ||||
| import ./fiber_orm/util | ||||
| import ./fiber_orm/db_common as fiber_db_common | ||||
| import ./fiber_orm/[pool, util] | ||||
| import ./fiber_orm/private/logging | ||||
|  | ||||
| export | ||||
|   pool, | ||||
|   util.columnNamesForModel, | ||||
|   util.dbFormat, | ||||
|   util.dbNameToIdent, | ||||
|   util.identNameToDb, | ||||
|   util.modelName, | ||||
|   util.rowToModel, | ||||
|   util.tableName | ||||
| export pool, util | ||||
| export logging.enableDbLogging | ||||
|  | ||||
| type NotFoundError* = object of CatchableError | ||||
| type | ||||
|   PagedRecords*[T] = object | ||||
|     pagination*: Option[PaginationParams] | ||||
|     records*: seq[T] | ||||
|     totalRecords*: int | ||||
|  | ||||
| var logNs {.threadvar.}: LoggingNamespace | ||||
|   DbUpdateError* = object of CatchableError | ||||
|     ## Error types raised when a DB modification fails. | ||||
|  | ||||
| template log(): untyped = | ||||
|   if logNs.isNil: logNs = initLoggingNamespace(name = "fiber_orm", level = lvlNotice) | ||||
|   logNs | ||||
|   NotFoundError* = object of CatchableError | ||||
|     ## Error type raised when no record matches a given ID | ||||
|  | ||||
| proc newMutateClauses(): MutateClauses = | ||||
|   return MutateClauses( | ||||
| @@ -30,7 +315,15 @@ proc newMutateClauses(): MutateClauses = | ||||
|     placeholders: @[], | ||||
|     values: @[]) | ||||
|  | ||||
| proc createRecord*[T](db: DbConn, rec: T): T = | ||||
| proc createRecord*[D: DbConnType, T](db: D, rec: T): T = | ||||
|   ## Create a new record. `rec` is expected to be a `model class`_. The `id` | ||||
|   ## field is only set if it is non-empty (see `ID Field`_ for details). | ||||
|   ## | ||||
|   ## Returns the newly created record. | ||||
|   ## | ||||
|   ## .. _model class: #objectminusrelational-modeling-model-class | ||||
|   ## .. _ID Field: #model-class-id-field | ||||
|  | ||||
|   var mc = newMutateClauses() | ||||
|   populateMutateClauses(rec, true, mc) | ||||
|  | ||||
| @@ -40,12 +333,14 @@ proc createRecord*[T](db: DbConn, rec: T): T = | ||||
|     " VALUES (" & mc.placeholders.join(",") & ") " & | ||||
|     " RETURNING " & columnNamesForModel(rec).join(",") | ||||
|  | ||||
|   log().debug "createRecord: [" & sqlStmt & "]" | ||||
|   logQuery("createRecord", sqlStmt) | ||||
|  | ||||
|   let newRow = db.getRow(sql(sqlStmt), mc.values) | ||||
|  | ||||
|   result = rowToModel(T, newRow) | ||||
|  | ||||
| proc updateRecord*[T](db: DbConn, rec: T): bool = | ||||
| proc updateRecord*[D: DbConnType, T](db: D, rec: T): bool = | ||||
|   ## Update a record by id. `rec` is expected to be a `model class`_. | ||||
|   var mc = newMutateClauses() | ||||
|   populateMutateClauses(rec, false, mc) | ||||
|  | ||||
| @@ -55,28 +350,51 @@ proc updateRecord*[T](db: DbConn, rec: T): bool = | ||||
|     " SET " & setClause & | ||||
|     " WHERE id = ? " | ||||
|  | ||||
|   log().debug "updateRecord: [" & sqlStmt & "] id: " & $rec.id | ||||
|   logQuery("updateRecord", sqlStmt, [("id", $rec.id)]) | ||||
|   let numRowsUpdated = db.execAffectedRows(sql(sqlStmt), mc.values.concat(@[$rec.id])) | ||||
|  | ||||
|   return numRowsUpdated > 0; | ||||
|  | ||||
| template deleteRecord*(db: DbConn, modelType: type, id: typed): untyped = | ||||
| proc createOrUpdateRecord*[D: DbConnType, T](db: D, rec: T): T = | ||||
|   ## Create or update a record. `rec` is expected to be a `model class`_. If | ||||
|   ## the `id` field is unset, or if there is no existing record with the given | ||||
|   ## id, a new record is inserted. Otherwise, the existing record is updated. | ||||
|   ## | ||||
|   ## Note that this does not perform partial updates, all fields are updated. | ||||
|  | ||||
|   let findRecordStmt = "SELECT id FROM " & tableName(rec) & " WHERE id = ?" | ||||
|   logQuery("createOrUpdateRecord", findRecordStmt, [("id", $rec.id)]) | ||||
|   let rows = db.getAllRows(sql(findRecordStmt), [$rec.id]) | ||||
|  | ||||
|   if rows.len == 0: result = createRecord(db, rec) | ||||
|   else: | ||||
|     result = rec | ||||
|     if not updateRecord(db, rec): | ||||
|       raise newException(DbUpdateError, | ||||
|         "unable to update " & modelName(rec) & " for id " & $rec.id) | ||||
|  | ||||
| template deleteRecord*[D: DbConnType](db: D, modelType: type, id: typed): untyped = | ||||
|   ## Delete a record by id. | ||||
|   let sqlStmt = "DELETE FROM " & tableName(modelType) & " WHERE id = ?" | ||||
|   log().debug "deleteRecord: [" & sqlStmt & "] id: " & $id | ||||
|   logQuery("deleteRecord", sqlStmt, [("id", $id)]) | ||||
|   db.tryExec(sql(sqlStmt), $id) | ||||
|  | ||||
| proc deleteRecord*[T](db: DbConn, rec: T): bool = | ||||
| proc deleteRecord*[D: DbConnType, T](db: D, rec: T): bool = | ||||
|   ## Delete a record by `id`_. | ||||
|   ## | ||||
|   ## .. _id: #model-class-id-field | ||||
|   let sqlStmt = "DELETE FROM " & tableName(rec) & " WHERE id = ?" | ||||
|   log().debug "deleteRecord: [" & sqlStmt & "] id: " & $rec.id | ||||
|   logQuery("deleteRecord", sqlStmt, [("id", $rec.id)]) | ||||
|   return db.tryExec(sql(sqlStmt), $rec.id) | ||||
|  | ||||
| template getRecord*(db: DbConn, modelType: type, id: typed): untyped = | ||||
| template getRecord*[D: DbConnType](db: D, modelType: type, id: typed): untyped = | ||||
|   ## Fetch a record by id. | ||||
|   let sqlStmt = | ||||
|     "SELECT " & columnNamesForModel(modelType).join(",") & | ||||
|     " FROM " & tableName(modelType) & | ||||
|     " WHERE id = ?" | ||||
|  | ||||
|   log().debug "getRecord: [" & sqlStmt & "] id: " & $id | ||||
|   logQuery("getRecord", sqlStmt, [("id", $id)]) | ||||
|   let row = db.getRow(sql(sqlStmt), @[$id]) | ||||
|  | ||||
|   if allIt(row, it.len == 0): | ||||
| @@ -84,76 +402,285 @@ template getRecord*(db: DbConn, modelType: type, id: typed): untyped = | ||||
|  | ||||
|   rowToModel(modelType, row) | ||||
|  | ||||
| template findRecordsWhere*(db: DbConn, modelType: type, whereClause: string, values: varargs[string, dbFormat]): untyped = | ||||
| template tryGetRecord*[D: DbConnType](db: D, modelType: type, id: typed): untyped = | ||||
|   ## Fetch a record by id. | ||||
|   let sqlStmt = | ||||
|     "SELECT " & columnNamesForModel(modelType).join(",") & | ||||
|     " FROM " & tableName(modelType) & | ||||
|     " WHERE id = ?" | ||||
|  | ||||
|   logQuery("tryGetRecord", sqlStmt, [("id", $id)]) | ||||
|   let row = db.getRow(sql(sqlStmt), @[$id]) | ||||
|  | ||||
|   if allIt(row, it.len == 0): none[modelType]() | ||||
|   else: some(rowToModel(modelType, row)) | ||||
|  | ||||
| template findRecordsWhere*[D: DbConnType]( | ||||
|     db: D, | ||||
|     modelType: type, | ||||
|     whereClause: string, | ||||
|     values: varargs[string, dbFormat], | ||||
|     page: Option[PaginationParams]): untyped = | ||||
|   ## Find all records matching a given `WHERE` clause. The number of elements in | ||||
|   ## the `values` array must match the number of placeholders (`?`) in the | ||||
|   ## provided `WHERE` clause. | ||||
|   var fetchStmt = | ||||
|     "SELECT " & columnNamesForModel(modelType).join(",") & | ||||
|     " FROM " & tableName(modelType) & | ||||
|     " WHERE " & whereClause | ||||
|  | ||||
|   log().debug "findRecordsWhere: [" & sqlStmt & "] values: (" & values.join(", ") & ")" | ||||
|   db.getAllRows(sql(sqlStmt), values).mapIt(rowToModel(modelType, it)) | ||||
|   var countStmt = | ||||
|     "SELECT COUNT(*) FROM " & tableName(modelType) & | ||||
|     " WHERE " & whereClause | ||||
|  | ||||
| template getAllRecords*(db: DbConn, modelType: type): untyped = | ||||
|   let sqlStmt = | ||||
|   if page.isSome: fetchStmt &= getPagingClause(page.get) | ||||
|  | ||||
|   logQuery("findRecordsWhere", fetchStmt, [("values", values.join(", "))]) | ||||
|   let records = db.getAllRows(sql(fetchStmt), values).mapIt(rowToModel(modelType, it)) | ||||
|  | ||||
|   PagedRecords[modelType]( | ||||
|     pagination: page, | ||||
|     records: records, | ||||
|     totalRecords: | ||||
|       if page.isNone: records.len | ||||
|       else: db.getRow(sql(countStmt), values)[0].parseInt) | ||||
|  | ||||
| template getAllRecords*[D: DbConnType]( | ||||
|     db: D, | ||||
|     modelType: type, | ||||
|     page: Option[PaginationParams]): untyped = | ||||
|   ## Fetch all records of the given type. | ||||
|   var fetchStmt = | ||||
|     "SELECT " & columnNamesForModel(modelType).join(",") & | ||||
|     " FROM " & tableName(modelType) | ||||
|  | ||||
|   log().debug "getAllRecords: [" & sqlStmt & "]" | ||||
|   db.getAllRows(sql(sqlStmt)).mapIt(rowToModel(modelType, it)) | ||||
|   var countStmt = "SELECT COUNT(*) FROM " & tableName(modelType) | ||||
|  | ||||
| template findRecordsBy*(db: DbConn, modelType: type, lookups: seq[tuple[field: string, value: string]]): untyped = | ||||
|   let sqlStmt = | ||||
|     "SELECT " & columnNamesForModel(modelType).join(",") & | ||||
|     " FROM " & tableName(modelType) & | ||||
|     " WHERE " & lookups.mapIt(it.field & " = ?").join(" AND ") | ||||
|   if page.isSome: fetchStmt &= getPagingClause(page.get) | ||||
|  | ||||
|   logQuery("getAllRecords", fetchStmt) | ||||
|   let records = db.getAllRows(sql(fetchStmt)).mapIt(rowToModel(modelType, it)) | ||||
|  | ||||
|   PagedRecords[modelType]( | ||||
|     pagination: page, | ||||
|     records: records, | ||||
|     totalRecords: | ||||
|       if page.isNone: records.len | ||||
|       else: db.getRow(sql(countStmt))[0].parseInt) | ||||
|  | ||||
|  | ||||
| template findRecordsBy*[D: DbConnType]( | ||||
|     db: D, | ||||
|     modelType: type, | ||||
|     lookups: seq[tuple[field: string, value: string]], | ||||
|     page: Option[PaginationParams]): untyped = | ||||
|   ## Find all records matching the provided lookup values. | ||||
|   let whereClause = lookups.mapIt(it.field & " = ?").join(" AND ") | ||||
|   let values = lookups.mapIt(it.value) | ||||
|  | ||||
|   log().debug "findRecordsBy: [" & sqlStmt & "] values (" & values.join(", ") & ")" | ||||
|   db.getAllRows(sql(sqlStmt), values).mapIt(rowToModel(modelType, it)) | ||||
|   var fetchStmt = | ||||
|     "SELECT " & columnNamesForModel(modelType).join(",") & | ||||
|     " FROM " & tableName(modelType) & | ||||
|     " WHERE " & whereClause | ||||
|  | ||||
|   var countStmt = | ||||
|     "SELECT COUNT(*) FROM " & tableName(modelType) & | ||||
|     " WHERE " & whereClause | ||||
|  | ||||
|   if page.isSome: fetchStmt &= getPagingClause(page.get) | ||||
|  | ||||
|   logQuery("findRecordsBy", fetchStmt, [("values", values.join(", "))]) | ||||
|   let records = db.getAllRows(sql(fetchStmt), values).mapIt(rowToModel(modelType, it)) | ||||
|  | ||||
|   PagedRecords[modelType]( | ||||
|     pagination: page, | ||||
|     records: records, | ||||
|     totalRecords: | ||||
|       if page.isNone: records.len | ||||
|       else: db.getRow(sql(countStmt), values)[0].parseInt) | ||||
|  | ||||
|  | ||||
| template associate*[D: DbConnType, I, J]( | ||||
|     db: D, | ||||
|     joinTableName: string, | ||||
|     rec1: I, | ||||
|     rec2: J): void = | ||||
|   ## Associate two records via a join table. | ||||
|  | ||||
|   let insertStmt = | ||||
|     "INSERT INTO " & joinTableName & | ||||
|     " (" & tableName(I) & "_id, " & tableName(J) & "_id) " & | ||||
|     " VALUES (?, ?)" | ||||
|  | ||||
|   logQuery("associate", insertStmt, [("id1", $rec1.id), ("id2", $rec2.id)]) | ||||
|   db.exec(sql(insertStmt), [$rec1.id, $rec2.id]) | ||||
|  | ||||
|  | ||||
| template findViaJoinTable*[D: DbConnType, L]( | ||||
|     db: D, | ||||
|     joinTableName: string, | ||||
|     targetType: type, | ||||
|     rec: L, | ||||
|     page: Option[PaginationParams]): untyped = | ||||
|   ## Find all records of `targetType` that are associated with `rec` via a | ||||
|   ## join table. | ||||
|   let columns = columnNamesForModel(targetType).mapIt("t." & it).join(",") | ||||
|  | ||||
|   var fetchStmt = | ||||
|     "SELECT " & columns & | ||||
|     " FROM " & tableName(targetType) & " AS t " & | ||||
|     " JOIN " & joinTableName & " AS j " & | ||||
|     " ON t.id = jt." & tableName(targetType) & "_id " & | ||||
|     " WHERE jt." & tableName(rec) & "_id = ?" | ||||
|  | ||||
|   var countStmt = | ||||
|     "SELECT COUNT(*) FROM " & joinTableName & | ||||
|     " WHERE " & tableName(rec) & "_id = ?" | ||||
|  | ||||
|   if page.isSome: fetchStmt &= getPagingClause(page.get) | ||||
|  | ||||
|   logQuery("findViaJoinTable", fetchStmt, [("id", $rec.id)]) | ||||
|   let records = db.getAllRows(sql(fetchStmt), $rec.id) | ||||
|     .mapIt(rowToModel(targetType, it)) | ||||
|  | ||||
|   PagedRecords[targetType]( | ||||
|     pagination: page, | ||||
|     records: records, | ||||
|     totalRecords: | ||||
|       if page.isNone: records.len | ||||
|       else: db.getRow(sql(countStmt))[0].parseInt) | ||||
|  | ||||
| template findViaJoinTable*[D: DbConnType]( | ||||
|     db: D, | ||||
|     joinTableName: string, | ||||
|     targetType: type, | ||||
|     lookupType: type, | ||||
|     id: typed, | ||||
|     page: Option[PaginationParams]): untyped = | ||||
|   ## Find all records of `targetType` that are associated with a record of | ||||
|   ## `lookupType` via a join table. | ||||
|   let columns = columnNamesForModel(targetType).mapIt("t." & it).join(",") | ||||
|  | ||||
|   var fetchStmt = | ||||
|     "SELECT " & columns & | ||||
|     " FROM " & tableName(targetType) & " AS t " & | ||||
|     " JOIN " & joinTableName & " AS j " & | ||||
|     " ON t.id = jt." & tableName(targetType) & "_id " & | ||||
|     " WHERE jt." & tableName(lookupType) & "_id = ?" | ||||
|  | ||||
|   var countStmt = | ||||
|     "SELECT COUNT(*) FROM " & joinTableName & | ||||
|     " WHERE " & tableName(lookupType) & "_id = ?" | ||||
|  | ||||
|   if page.isSome: fetchStmt &= getPagingClause(page.get) | ||||
|  | ||||
|   logQuery("findViaJoinTable", fetchStmt, [("id", $id)]) | ||||
|   let records = db.getAllRows(sql(fetchStmt), $id) | ||||
|     .mapIt(rowToModel(targetType, it)) | ||||
|  | ||||
|   PagedRecords[targetType]( | ||||
|     pagination: page, | ||||
|     records: records, | ||||
|     totalRecords: | ||||
|       if page.isNone: records.len | ||||
|       else: db.getRow(sql(countStmt))[0].parseInt) | ||||
|  | ||||
| macro generateProcsForModels*(dbType: type, modelTypes: openarray[type]): untyped = | ||||
|   ## Generate all standard access procedures for the given model types. For a | ||||
|   ## `model class`_ named `TodoItem`, this will generate the following | ||||
|   ## procedures: | ||||
|   ## | ||||
|   ## .. code-block:: Nim | ||||
|   ##    proc getTodoItem*(db: TodoDB, id: idType): TodoItem; | ||||
|   ##    proc getAllTodoItems*(db: TodoDB): TodoItem; | ||||
|   ##    proc createTodoItem*(db: TodoDB, rec: TodoItem): TodoItem; | ||||
|   ##    proc deleteTodoItem*(db: TodoDB, rec: TodoItem): bool; | ||||
|   ##    proc deleteTodoItem*(db: TodoDB, id: idType): bool; | ||||
|   ##    proc updateTodoItem*(db: TodoDB, rec: TodoItem): bool; | ||||
|   ##    proc createOrUpdateTodoItem*(db: TodoDB, rec: TodoItem): bool; | ||||
|   ## | ||||
|   ##    proc findTodoItemsWhere*( | ||||
|   ##      db: TodoDB, whereClause: string, values: varargs[string]): TodoItem; | ||||
|   ## | ||||
|   ## `dbType` is expected to be some type that has a defined `withConnection` | ||||
|   ## procedure (see `Database Object`_ for details). | ||||
|   ## | ||||
|   ## .. _Database Object: #database-object | ||||
|   result = newStmtList() | ||||
|  | ||||
|   for t in modelTypes: | ||||
|     if t.getType[1].typeKind == ntyRef: | ||||
|       raise newException(ValueError, | ||||
|         "fiber_orm model object must be objects, not refs") | ||||
|  | ||||
|     let modelName = $(t.getType[1]) | ||||
|     let getName = ident("get" & modelName) | ||||
|     let getAllName = ident("getAll" & modelName & "s") | ||||
|     let findWhereName = ident("find" & modelName & "sWhere") | ||||
|     let tryGetName = ident("tryGet" & modelName) | ||||
|     let getIfExistsName = ident("get" & modelName & "IfItExists") | ||||
|     let getAllName = ident("getAll" & pluralize(modelName)) | ||||
|     let findWhereName = ident("find" & pluralize(modelName) & "Where") | ||||
|     let createName = ident("create" & modelName) | ||||
|     let updateName = ident("update" & modelName) | ||||
|     let createOrUpdateName = ident("createOrUpdate" & modelName) | ||||
|     let deleteName = ident("delete" & modelName) | ||||
|     let idType = typeOfColumn(t, "id") | ||||
|     result.add quote do: | ||||
|       proc `getName`*(db: `dbType`, id: `idType`): `t` = | ||||
|         db.withConn: result = getRecord(conn, `t`, id) | ||||
|         db.withConnection conn: result = getRecord(conn, `t`, id) | ||||
|  | ||||
|       proc `getAllName`*(db: `dbType`): seq[`t`] = | ||||
|         db.withConn: result = getAllRecords(conn, `t`) | ||||
|       proc `tryGetName`*(db: `dbType`, id: `idType`): Option[`t`] = | ||||
|         db.withConnection conn: result = tryGetRecord(conn, `t`, id) | ||||
|  | ||||
|       proc `findWhereName`*(db: `dbType`, whereClause: string, values: varargs[string, dbFormat]): seq[`t`] = | ||||
|         db.withConn: | ||||
|           result = findRecordsWhere(conn, `t`, whereClause, values) | ||||
|       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.withConn: result = createRecord(conn, rec) | ||||
|         db.withConnection conn: result = createRecord(conn, rec) | ||||
|  | ||||
|       proc `updateName`*(db: `dbType`, rec: `t`): bool = | ||||
|         db.withConn: result = updateRecord(conn, rec) | ||||
|         db.withConnection conn: result = updateRecord(conn, rec) | ||||
|  | ||||
|       proc `createOrUpdateName`*(db: `dbType`, rec: `t`): `t` = | ||||
|         db.inTransaction: result = createOrUpdateRecord(conn, rec) | ||||
|  | ||||
|       proc `deleteName`*(db: `dbType`, rec: `t`): bool = | ||||
|         db.withConn: result = deleteRecord(conn, rec) | ||||
|         db.withConnection conn: result = deleteRecord(conn, rec) | ||||
|  | ||||
|       proc `deleteName`*(db: `dbType`, id: `idType`): bool = | ||||
|         db.withConn: result = deleteRecord(conn, `t`, id) | ||||
|         db.withConnection conn: result = deleteRecord(conn, `t`, id) | ||||
|  | ||||
| macro generateLookup*(dbType: type, modelType: type, fields: seq[string]): untyped = | ||||
|   ## Create a lookup procedure for a given set of field names. For example, | ||||
|   ## given the TODO database demostrated above, | ||||
|   ## | ||||
|   ## .. code-block:: Nim | ||||
|   ##    generateLookup(TodoDB, TodoItem, ["owner", "priority"]) | ||||
|   ## | ||||
|   ## will generate the following procedure: | ||||
|   ## | ||||
|   ## .. code-block:: Nim | ||||
|   ##    proc findTodoItemsByOwnerAndPriority*(db: SampleDB, | ||||
|   ##      owner: string, priority: int): seq[TodoItem] | ||||
|   let fieldNames = fields[1].mapIt($it) | ||||
|   let procName = ident("find" & pluralize($modelType.getType[1]) & "By" & fieldNames.mapIt(it.capitalize).join("And")) | ||||
|  | ||||
|   # Create proc skeleton | ||||
|   result = quote do: | ||||
|     proc `procName`*(db: `dbType`): seq[`modelType`] = | ||||
|       db.withConn: result = findRecordsBy(conn, `modelType`) | ||||
|     proc `procName`*(db: `dbType`): PagedRecords[`modelType`] = | ||||
|       db.withConnection conn: result = findRecordsBy(conn, `modelType`) | ||||
|  | ||||
|   var callParams = quote do: @[] | ||||
|  | ||||
| @@ -170,12 +697,18 @@ macro generateLookup*(dbType: type, modelType: type, fields: seq[string]): untyp | ||||
|     # Build up the AST for the inner procedure call | ||||
|     callParams[1].add(paramTuple) | ||||
|  | ||||
|   # Add the optional pagination parameters to the generated proc definition | ||||
|   result[3].add(newIdentDefs( | ||||
|     ident("pagination"), newEmptyNode(), | ||||
|     quote do: none[PaginationParams]())) | ||||
|  | ||||
|   # Add the call params to the inner procedure call | ||||
|   # result[6][0][1][0][1] is | ||||
|   #   ProcDef -> [6]: StmtList (body) -> [0]: Call -> | ||||
|   #     [1]: StmtList (withConn body) -> [0]: Asgn (result =) -> | ||||
|   #   ProcDef -> [6]: StmtList (body) -> [0]: Command -> | ||||
|   #     [2]: StmtList (withConnection body) -> [0]: Asgn (result =) -> | ||||
|   #     [1]: Call (inner findRecords invocation) | ||||
|   result[6][0][1][0][1].add(callParams) | ||||
|   result[6][0][2][0][1].add(callParams) | ||||
|   result[6][0][2][0][1].add(quote do: pagination) | ||||
|  | ||||
| macro generateProcsForFieldLookups*(dbType: type, modelsAndFields: openarray[tuple[t: type, fields: seq[string]]]): untyped = | ||||
|   result = newStmtList() | ||||
| @@ -188,8 +721,8 @@ macro generateProcsForFieldLookups*(dbType: type, modelsAndFields: openarray[tup | ||||
|  | ||||
|     # Create proc skeleton | ||||
|     let procDefAST = quote do: | ||||
|       proc `procName`*(db: `dbType`): seq[`modelType`] = | ||||
|         db.withConn: result = findRecordsBy(conn, `modelType`) | ||||
|       proc `procName`*(db: `dbType`): PagedRecords[`modelType`] = | ||||
|         db.withConnection conn: result = findRecordsBy(conn, `modelType`) | ||||
|  | ||||
|     var callParams = quote do: @[] | ||||
|  | ||||
| @@ -202,24 +735,115 @@ macro generateProcsForFieldLookups*(dbType: type, modelsAndFields: openarray[tup | ||||
|       procDefAST[3].add(newIdentDefs(ident(n), ident("string"))) | ||||
|       callParams[1].add(paramTuple) | ||||
|  | ||||
|     # Add the optional pagination parameters to the generated proc definition | ||||
|     procDefAST[3].add(newIdentDefs( | ||||
|       ident("pagination"), newEmptyNode(), | ||||
|       quote do: none[PaginationParams]())) | ||||
|  | ||||
|     procDefAST[6][0][1][0][1].add(callParams) | ||||
|     procDefAST[6][0][1][0][1].add(quote do: pagination) | ||||
|  | ||||
|     result.add procDefAST | ||||
|  | ||||
| proc initPool*( | ||||
|     connect: proc(): DbConn, | ||||
|     poolSize = 10, | ||||
|     hardCap = false, | ||||
|     healthCheckQuery = "SELECT 'true' AS alive"): DbConnPool = | ||||
| macro generateJoinTableProcs*( | ||||
|     dbType, model1Type, model2Type: type, | ||||
|     joinTableName: string): untyped = | ||||
|   ## Generate lookup procedures for a pair of models with a join table. For | ||||
|   ## example, given the TODO database demonstrated above, where `TodoItem` and | ||||
|   ## `TimeEntry` have a many-to-many relationship, you might have a join table | ||||
|   ## `todo_items_time_entries` with columns `todo_item_id` and `time_entry_id`. | ||||
|   ## This macro will generate the following procedures: | ||||
|   ## | ||||
|   ## .. code-block:: Nim | ||||
|   ##    proc findTodoItemsByTimeEntry*(db: SampleDB, timeEntry: TimeEntry): seq[TodoItem] | ||||
|   ##    proc findTimeEntriesByTodoItem*(db: SampleDB, todoItem: TodoItem): seq[TimeEntry] | ||||
|   ## | ||||
|   ## `dbType` is expected to be some type that has a defined `withConnection` | ||||
|   ## procedure (see `Database Object`_ for details). | ||||
|   ## | ||||
|   ## .. _Database Object: #database-object | ||||
|   result = newStmtList() | ||||
|  | ||||
|   initDbConnPool(DbConnPoolConfig( | ||||
|     connect: connect, | ||||
|     poolSize: poolSize, | ||||
|     hardCap: hardCap, | ||||
|     healthCheckQuery: healthCheckQuery)) | ||||
|   if model1Type.getType[1].typeKind == ntyRef or | ||||
|      model2Type.getType[1].typeKind == ntyRef: | ||||
|     raise newException(ValueError, | ||||
|       "fiber_orm model object must be objects, not refs") | ||||
|  | ||||
| template inTransaction*(db: DbConnPool, body: untyped) = | ||||
|   pool.withConn(db): | ||||
|   let model1Name = $(model1Type.getType[1]) | ||||
|   let model2Name = $(model2Type.getType[1]) | ||||
|   let getModel1Name = ident("get" & pluralize(model1Name) & "By" & model2Name) | ||||
|   let getModel2Name = ident("get" & pluralize(model2Name) & "By" & model1Name) | ||||
|   let id1Type = typeOfColumn(model1Type, "id") | ||||
|   let id2Type = typeOfColumn(model2Type, "id") | ||||
|   let joinTableNameNode = newStrLitNode($joinTableName) | ||||
|  | ||||
|   result.add quote do: | ||||
|     proc `getModel1Name`*( | ||||
|         db: `dbType`, | ||||
|         id: `id2Type`, | ||||
|         pagination = none[PaginationParams]()): PagedRecords[`model1Type`] = | ||||
|       db.withConnection conn: | ||||
|         result = findViaJoinTable( | ||||
|           conn, | ||||
|           `joinTableNameNode`, | ||||
|           `model1Type`, | ||||
|           `model2Type`, | ||||
|           id, | ||||
|           pagination) | ||||
|  | ||||
|     proc `getModel1Name`*( | ||||
|         db: `dbType`, | ||||
|         rec: `model2Type`, | ||||
|         pagination = none[PaginationParams]()): PagedRecords[`model1Type`] = | ||||
|       db.withConnection conn: | ||||
|         result = findViaJoinTable( | ||||
|           conn, | ||||
|           `joinTableNameNode`, | ||||
|           `model1Type`, | ||||
|           rec, | ||||
|           pagination) | ||||
|  | ||||
|     proc `getModel2Name`*( | ||||
|         db: `dbType`, | ||||
|         id: `id1Type`, | ||||
|         pagination = none[PaginationParams]()): Pagedrecords[`model2Type`] = | ||||
|       db.withConnection conn: | ||||
|         result = findViaJoinTable( | ||||
|           conn, | ||||
|           `joinTableNameNode`, | ||||
|           `model2Type`, | ||||
|           `model1Type`, | ||||
|           id, | ||||
|           pagination) | ||||
|  | ||||
|     proc `getModel2Name`*( | ||||
|         db: `dbType`, | ||||
|         rec: `model1Type`, | ||||
|         pagination = none[PaginationParams]()): Pagedrecords[`model2Type`] = | ||||
|       db.withConnection conn: | ||||
|         result = findViaJoinTable( | ||||
|           conn, | ||||
|           `joinTableNameNode`, | ||||
|           `model2Type`, | ||||
|           rec, | ||||
|           pagination) | ||||
|  | ||||
|     proc associate*( | ||||
|         db: `dbType`, | ||||
|         rec1: `model1Type`, | ||||
|         rec2: `model2Type`): void = | ||||
|       db.withConnection conn: | ||||
|         associate(conn, `joinTableNameNode`, rec1, rec2) | ||||
|  | ||||
|     proc associate*( | ||||
|         db: `dbType`, | ||||
|         rec2: `model2Type`, | ||||
|         rec1: `model1Type`): void = | ||||
|       db.withConnection conn: | ||||
|         associate(conn, `joinTableNameNode`, rec1, rec2) | ||||
|  | ||||
| template inTransaction*(db, body: untyped) = | ||||
|   db.withConnection conn: | ||||
|     conn.exec(sql"BEGIN TRANSACTION") | ||||
|     try: | ||||
|       body | ||||
|   | ||||
							
								
								
									
										3
									
								
								src/fiber_orm/db_common.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/fiber_orm/db_common.nim
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| import db_connector/[db_postgres, db_sqlite] | ||||
|  | ||||
| type DbConnType* = db_postgres.DbConn or db_sqlite.DbConn | ||||
| @@ -1,99 +1,114 @@ | ||||
| import std/db_postgres, std/sequtils, std/strutils, std/sugar | ||||
| # Fiber ORM | ||||
| # | ||||
| # Copyright 2019 Jonathan Bernard <jonathan@jdbernard.com> | ||||
|  | ||||
| import namespaced_logging | ||||
| ## Simple database connection pooling implementation compatible with Fiber ORM. | ||||
|  | ||||
| when (NimMajor, NimMinor, NimPatch) < (2, 0, 0): | ||||
|   when not defined(gcArc) and not defined (gcOrc): | ||||
|     {.error: "fiber_orm requires either --mm:arc or --mm:orc.".} | ||||
|  | ||||
| import std/[deques, locks, sequtils, 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 | ||||
| import ./private/logging | ||||
|  | ||||
| type | ||||
|   DbConnPoolConfig* = object | ||||
|     connect*: () -> DbConn | ||||
|     poolSize*: int | ||||
|     hardCap*: bool | ||||
|     healthCheckQuery*: string | ||||
|   DbConnPool*[D: DbConnType] = ptr DbConnPoolObj[D] | ||||
|  | ||||
|   PooledDbConn = ref object | ||||
|     conn: DbConn | ||||
|     id: int | ||||
|     free: bool | ||||
|   DbConnPoolObj[D: DbConnType] = object | ||||
|     ## Database connection pool | ||||
|     connect: proc (): D {.raises: [DbError].} | ||||
|     healthCheckQuery: SqlQuery | ||||
|     entries: Deque[D] | ||||
|     cond: Cond | ||||
|     lock: Lock | ||||
|  | ||||
|   DbConnPool* = ref object | ||||
|     conns: seq[PooledDbConn] | ||||
|     cfg: DbConnPoolConfig | ||||
|     lastId: int | ||||
|  | ||||
| var logNs {.threadvar.}: LoggingNamespace | ||||
| proc close*[D: DbConnType](pool: DbConnPool[D]) = | ||||
|   ## Safely close all connections and release resources for the given pool. | ||||
|   getLogger("pool").debug("closing connection pool") | ||||
|   withLock(pool.lock): | ||||
|     while pool.entries.len > 0: close(pool.entries.popFirst()) | ||||
|  | ||||
| template log(): untyped = | ||||
|   if logNs.isNil: logNs = initLoggingNamespace(name = "fiber_orm/pool", level = lvlNotice) | ||||
|   logNs | ||||
|   deinitLock(pool.lock) | ||||
|   deinitCond(pool.cond) | ||||
|   `=destroy`(pool[]) | ||||
|   deallocShared(pool) | ||||
|  | ||||
| proc initDbConnPool*(cfg: DbConnPoolConfig): DbConnPool = | ||||
|   log().debug("Initializing new pool (size: " & $cfg.poolSize) | ||||
|   result = DbConnPool( | ||||
|     conns: @[], | ||||
|     cfg: cfg) | ||||
|  | ||||
| proc newConn(pool: DbConnPool): PooledDbConn = | ||||
|   log().debug("Creating a new connection to add to the pool.") | ||||
|   pool.lastId += 1 | ||||
|   let conn = pool.cfg.connect() | ||||
|   result = PooledDbConn( | ||||
|     conn: conn, | ||||
|     id: pool.lastId, | ||||
|     free: true) | ||||
|   pool.conns.add(result) | ||||
| proc newDbConnPool*[D: DbConnType]( | ||||
|     poolSize: int, | ||||
|     connectFunc: proc(): D {.raises: [DbError].}, | ||||
|     healthCheckQuery = "SELECT 1;"): 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. | ||||
|   ## * `healthCheckQuery` should be a simple and fast SQL query that the pool | ||||
|   ##   can use to test the liveliness of pooled connections. By default it uses | ||||
|   ##   `SELECT 1;` | ||||
|   ## | ||||
|   ## .. _Example Fiber ORM Usage: ../fiber_orm.html#basic-usage-example-fiber-orm-usage | ||||
|  | ||||
| proc maintain(pool: DbConnPool): void = | ||||
|   log().debug("Maintaining pool. $# connections." % [$pool.conns.len]) | ||||
|   pool.conns.keepIf(proc (pc: PooledDbConn): bool = | ||||
|     if not pc.free: return true | ||||
|   result = cast[DbConnPool[D]](allocShared0(sizeof(DbConnPoolObj[D]))) | ||||
|   initCond(result.cond) | ||||
|   initLock(result.lock) | ||||
|   result.entries = initDeque[D](poolSize) | ||||
|   result.connect = connectFunc | ||||
|   result.healthCheckQuery = sql(healthCheckQuery) | ||||
|  | ||||
|     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 | ||||
|   ) | ||||
|   log().debug( | ||||
|     "Pruned dead connections. $# connections remaining." % | ||||
|     [$pool.conns.len]) | ||||
|   try: | ||||
|     for _ in 0 ..< poolSize: result.entries.addLast(connectFunc()) | ||||
|   except DbError as ex: | ||||
|     try: result.close() | ||||
|     except: discard | ||||
|     getLogger("pool").error( | ||||
|       msg = "unable to initialize connection pool", | ||||
|       err = ex) | ||||
|     raise ex | ||||
|  | ||||
|   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 "" | ||||
|       log().debug( | ||||
|         "Trimming pool size. Culled $# free connections. $# connections remaining." % | ||||
|         [$toCull.len, $pool.conns.len]) | ||||
| proc take*[D: DbConnType](pool: DbConnPool[D]): D {.raises: [DbError], gcsafe.} = | ||||
|   ## 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. | ||||
|   withLock(pool.lock): | ||||
|     while pool.entries.len == 0: wait(pool.cond, pool.lock) | ||||
|     result = pool.entries.popFirst() | ||||
|  | ||||
| proc take*(pool: DbConnPool): tuple[id: int, conn: DbConn] = | ||||
|   pool.maintain | ||||
|   let freeConns = pool.conns.filterIt(it.free) | ||||
|     # check that the connection is healthy | ||||
|     try: discard getRow(result, pool.healthCheckQuery, []) | ||||
|     except DbError: | ||||
|       {.gcsafe.}: | ||||
|         # if it's not, let's try to close it and create a new connection | ||||
|         try: | ||||
|           getLogger("pool").info( | ||||
|             "pooled connection failed health check, opening a new connection") | ||||
|           close(result) | ||||
|         except: discard | ||||
|         result = pool.connect() | ||||
|  | ||||
|   log().debug( | ||||
|     "Providing a new connection ($# currently free)." % [$freeConns.len]) | ||||
|  | ||||
|   let reserved = | ||||
|     if freeConns.len > 0: freeConns[0] | ||||
|     else: pool.newConn() | ||||
| proc release*[D: DbConnType](pool: DbConnPool[D], conn: D) {.raises: [], gcsafe.} = | ||||
|   ## Release a connection back to the pool. | ||||
|   withLock(pool.lock): | ||||
|     pool.entries.addLast(conn) | ||||
|   signal(pool.cond) | ||||
|  | ||||
|   reserved.free = false | ||||
|   log().debug("Reserve connection $#" % [$reserved.id]) | ||||
|   return (id: reserved.id, conn: reserved.conn) | ||||
|  | ||||
| proc release*(pool: DbConnPool, connId: int): void = | ||||
|   log().debug("Reclaiming released connaction $#" % [$connId]) | ||||
|   let foundConn = pool.conns.filterIt(it.id == connId) | ||||
|   if foundConn.len > 0: foundConn[0].free = true | ||||
|  | ||||
| template withConn*(pool: DbConnPool, stmt: untyped): untyped = | ||||
|   let (connId, conn {.inject.}) = take(pool) | ||||
|   try: stmt | ||||
|   finally: release(pool, connId) | ||||
| template withConnection*[D: DbConnType](pool: DbConnPool[D], conn, stmt: untyped): untyped = | ||||
|   ## Convenience template to provide a connection from the pool for use in a | ||||
|   ## statement block, automatically releasing that connnection when done. | ||||
|   block: | ||||
|     let conn = take(pool) | ||||
|     try: stmt | ||||
|     finally: release(pool, conn) | ||||
|   | ||||
							
								
								
									
										34
									
								
								src/fiber_orm/private/logging.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/fiber_orm/private/logging.nim
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| import std/[json, options] | ||||
|  | ||||
| import namespaced_logging | ||||
|  | ||||
| export namespaced_logging.log | ||||
| export namespaced_logging.debug | ||||
| export namespaced_logging.info | ||||
| export namespaced_logging.notice | ||||
| export namespaced_logging.warn | ||||
| export namespaced_logging.error | ||||
| export namespaced_logging.fatal | ||||
|  | ||||
| var logService {.threadvar.}: Option[ThreadLocalLogService] | ||||
| var logger {.threadvar.}: Option[Logger] | ||||
|  | ||||
| proc makeQueryLogEntry( | ||||
|     m: string, | ||||
|     sql: string, | ||||
|     args: openArray[(string, string)] = []): JsonNode = | ||||
|   result = %*{ "method": m, "sql": sql } | ||||
|   for (k, v) in args: result[k] = %v | ||||
|  | ||||
| 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") | ||||
|   logger.debug(makeQueryLogEntry(methodName, sqlStmt, args)) | ||||
|  | ||||
| proc enableDbLogging*(svc: ThreadLocalLogService) = | ||||
|   logService = some(svc) | ||||
|  | ||||
| proc getLogger*(scope: string): Option[Logger] = | ||||
|   logService.getLogger("fiber_orm/" & scope) | ||||
| @@ -1,29 +1,70 @@ | ||||
| import json, macros, options, sequtils, strutils, times, timeutils, unicode, | ||||
|   uuids | ||||
| # Fiber ORM | ||||
| # | ||||
| # Copyright 2019 Jonathan Bernard <jonathan@jdbernard.com> | ||||
|  | ||||
| import nre except toSeq | ||||
| ## Utility methods used internally by Fiber ORM. | ||||
| import std/[json, macros, options, sequtils, strutils, times, unicode] | ||||
| import uuids | ||||
|  | ||||
| import std/nre except toSeq | ||||
|  | ||||
| type | ||||
|   PaginationParams* = object | ||||
|     pageSize*: int | ||||
|     offset*: int | ||||
|     orderBy*: Option[seq[string]] | ||||
|  | ||||
|   MutateClauses* = object | ||||
|     ## Data structure to hold information about the clauses that should be | ||||
|     ## added to a query. How these clauses are used will depend on the query. | ||||
|     ## This common data structure provides the information needed to create | ||||
|     ## WHERE clauses, UPDATE clauses, etc. | ||||
|     columns*: seq[string] | ||||
|     placeholders*: seq[string] | ||||
|     values*: seq[string] | ||||
|  | ||||
| const ISO_8601_FORMATS = @[ | ||||
|   "yyyy-MM-dd'T'HH:mm:ssz", | ||||
|   "yyyy-MM-dd'T'HH:mm:sszzz", | ||||
|   "yyyy-MM-dd'T'HH:mm:ss'.'fffzzz", | ||||
|   "yyyy-MM-dd'T'HH:mm:ss'.'ffffzzz", | ||||
|   "yyyy-MM-dd HH:mm:ssz", | ||||
|   "yyyy-MM-dd HH:mm:sszzz", | ||||
|   "yyyy-MM-dd HH:mm:ss'.'fffzzz", | ||||
|   "yyyy-MM-dd HH:mm:ss'.'ffffzzz" | ||||
| ] | ||||
|  | ||||
| proc parseIso8601(val: string): DateTime = | ||||
|   var errString = "" | ||||
|   for df in ISO_8601_FORMATS: | ||||
|     try: return val.parse(df) | ||||
|     except: errString &= "\n" & getCurrentExceptionMsg() | ||||
|   raise newException(Exception, "Could not parse date. Tried:" & errString) | ||||
|  | ||||
| proc formatIso8601(d: DateTime): string = | ||||
|   return d.format(ISO_8601_FORMATS[2]) | ||||
|  | ||||
|  | ||||
| # TODO: more complete implementation | ||||
| # see https://github.com/blakeembrey/pluralize | ||||
| proc pluralize*(name: string): string = | ||||
|   ## Return the plural form of the given name. | ||||
|   if name[^2..^1] == "ey": return name[0..^3] & "ies" | ||||
|   if name[^1] == 'y': return name[0..^2] & "ies" | ||||
|   return name & "s" | ||||
|  | ||||
| macro modelName*(model: object): string = | ||||
|   ## For a given concrete record object, return the name of the `model class`_ | ||||
|   return newStrLitNode($model.getTypeInst) | ||||
|  | ||||
| macro modelName*(modelType: type): string = | ||||
|   ## Get the name of a given `model class`_ | ||||
|   return newStrLitNode($modelType.getType[1]) | ||||
|  | ||||
|  | ||||
| proc identNameToDb*(name: string): string = | ||||
|   ## Map a Nim identifier name to a DB name. See the `rules for name mapping`_ | ||||
|   ## | ||||
|   ## TODO link above | ||||
|   const UNDERSCORE_RUNE = "_".toRunes[0] | ||||
|   let nameInRunes = name.toRunes | ||||
|   var prev: Rune | ||||
| @@ -42,28 +83,40 @@ proc identNameToDb*(name: string): string = | ||||
|   return $resultRunes | ||||
|  | ||||
| proc dbNameToIdent*(name: string): string = | ||||
|   ## Map a DB name to a Nim identifier name. See the `rules for name mapping`_ | ||||
|   let parts = name.split("_") | ||||
|   return @[parts[0]].concat(parts[1..^1].mapIt(capitalize(it))).join("") | ||||
|  | ||||
| proc tableName*(modelType: type): string = | ||||
|   ## Get the `table name`_ for a given `model class`_ | ||||
|   return pluralize(modelName(modelType).identNameToDb) | ||||
|  | ||||
| proc tableName*[T](rec: T): string = | ||||
|   ## Get the `table name`_ for a given record. | ||||
|   return pluralize(modelName(rec).identNameToDb) | ||||
|  | ||||
| proc dbFormat*(s: string): string = return s | ||||
| proc dbFormat*(s: string): string = | ||||
|   ## Format a string for inclusion in a SQL Query. | ||||
|   return s | ||||
|  | ||||
| proc dbFormat*(dt: DateTime): string = return dt.formatIso8601 | ||||
| proc dbFormat*(dt: DateTime): string = | ||||
|   ## Format a DateTime for inclusion in a SQL Query. | ||||
|   return dt.formatIso8601 | ||||
|  | ||||
| proc dbFormat*[T](list: seq[T]): string = | ||||
|   ## Format a `seq` for inclusion in a SQL Query. | ||||
|   return "{" & list.mapIt(dbFormat(it)).join(",") & "}" | ||||
|  | ||||
| proc dbFormat*[T](item: T): string = return $item | ||||
| proc dbFormat*[T](item: T): string = | ||||
|   ## For all other types, fall back on a defined `$` function to create a | ||||
|   ## string version of the value we can include in an SQL query. | ||||
|   return $item | ||||
|  | ||||
| type DbArrayParseState = enum | ||||
|   expectStart, inQuote, inVal, expectEnd | ||||
|  | ||||
| proc parsePGDatetime*(val: string): DateTime = | ||||
|   ## Parse a Postgres datetime value into a Nim DateTime object. | ||||
|  | ||||
|   const PG_TIMESTAMP_FORMATS = [ | ||||
|     "yyyy-MM-dd HH:mm:ss", | ||||
| @@ -80,18 +133,20 @@ proc parsePGDatetime*(val: string): DateTime = | ||||
|  | ||||
|   var correctedVal = val; | ||||
|  | ||||
|   # PostgreSQL will truncate any trailing 0's in the millisecond value leading | ||||
|   # to values like `2020-01-01 16:42.3+00`. This cannot currently be parsed by | ||||
|   # the standard times format as it expects exactly three digits for | ||||
|   # millisecond values. So we have to detect this and pad out the millisecond | ||||
|   # value to 3 digits. | ||||
|   let PG_PARTIAL_FORMAT_REGEX = re"(\d{4}-\d{2}-\d{2}( |'T')\d{2}:\d{2}:\d{2}\.)(\d{1,2})(\S+)?" | ||||
|   # The Nim `times#format` function only recognizes 3-digit millisecond values | ||||
|   # but PostgreSQL will sometimes send 1-2 digits, truncating any trailing 0's, | ||||
|   # or sometimes provide more than three digits of preceision in the millisecond value leading | ||||
|   # to values like `2020-01-01 16:42.3+00` or `2025-01-06 00:56:00.9007+00`. | ||||
|   # This cannot currently be parsed by the standard times format as it expects | ||||
|   # exactly three digits for millisecond values. So we have to detect this and | ||||
|   # coerce the millisecond value to exactly 3 digits. | ||||
|   let PG_PARTIAL_FORMAT_REGEX = re"(\d{4}-\d{2}-\d{2}( |'T')\d{2}:\d{2}:\d{2}\.)(\d+)(\S+)?" | ||||
|   let match = val.match(PG_PARTIAL_FORMAT_REGEX) | ||||
|  | ||||
|   if match.isSome: | ||||
|     let c = match.get.captures | ||||
|     if c.toSeq.len == 2: correctedVal = c[0] & alignLeft(c[2], 3, '0') | ||||
|     else: correctedVal = c[0] & alignLeft(c[2], 3, '0') & c[3] | ||||
|     if c.toSeq.len == 2: correctedVal = c[0] & alignLeft(c[2], 3, '0')[0..2] | ||||
|     else: correctedVal = c[0] & alignLeft(c[2], 3, '0')[0..2] & c[3] | ||||
|  | ||||
|   var errStr = "" | ||||
|  | ||||
| @@ -100,9 +155,10 @@ proc parsePGDatetime*(val: string): DateTime = | ||||
|     try: return correctedVal.parse(df) | ||||
|     except: errStr &= "\n\t" & getCurrentExceptionMsg() | ||||
|  | ||||
|   raise newException(ValueError, "Cannot parse PG date. Tried:" & errStr) | ||||
|   raise newException(ValueError, "Cannot parse PG date '" & correctedVal & "'. Tried:" & errStr) | ||||
|  | ||||
| proc parseDbArray*(val: string): seq[string] = | ||||
|   ## Parse a Postgres array column into a Nim seq[string] | ||||
|   result = newSeq[string]() | ||||
|  | ||||
|   var parseState = DbArrayParseState.expectStart | ||||
| @@ -160,18 +216,14 @@ proc parseDbArray*(val: string): seq[string] = | ||||
|   if not (parseState == inQuote) and curStr.len > 0: | ||||
|     result.add(curStr) | ||||
|  | ||||
| proc createParseStmt*(t, value: NimNode): NimNode = | ||||
| func createParseStmt*(t, value: NimNode): NimNode = | ||||
|   ## Utility method to create the Nim code required to parse a value coming from | ||||
|   ## the a database query. This is used by functions like `rowToModel` to parse | ||||
|   ## the dataabase columns into the Nim object fields. | ||||
|  | ||||
|   #echo "Creating parse statment for ", t.treeRepr | ||||
|   if t.typeKind == ntyObject: | ||||
|  | ||||
|     if t.getType == UUID.getType: | ||||
|       result = quote do: parseUUID(`value`) | ||||
|  | ||||
|     elif t.getType == DateTime.getType: | ||||
|       result = quote do: parsePGDatetime(`value`) | ||||
|  | ||||
|     elif t.getTypeInst == Option.getType: | ||||
|     if t.getTypeInst == Option.getType: | ||||
|       var innerType = t.getTypeImpl[2][0] # start at the first RecList | ||||
|       # If the value is a non-pointer type, there is another inner RecList | ||||
|       if innerType.kind == nnkRecList: innerType = innerType[0] | ||||
| @@ -182,7 +234,33 @@ proc createParseStmt*(t, value: NimNode): NimNode = | ||||
|         if `value`.len == 0:  none[`innerType`]() | ||||
|         else:                 some(`parseStmt`) | ||||
|  | ||||
|     else: error "Unknown value object type: " & $t.getTypeInst | ||||
|     elif t.getType == UUID.getType: | ||||
|       result = quote do: parseUUID(`value`) | ||||
|  | ||||
|     elif t.getType == DateTime.getType: | ||||
|       result = quote do: parsePGDatetime(`value`) | ||||
|  | ||||
|     else: error "Cannot parse column with unknown 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 "Cannot parse column with unknown generic instance type: " & $t.getTypeInst | ||||
|  | ||||
|   elif t.typeKind == ntyDistinct: | ||||
|     result = quote do: | ||||
|       block: | ||||
|         let tmp: `t` = `value` | ||||
|         tmp | ||||
|  | ||||
|   elif t.typeKind == ntyRef: | ||||
|  | ||||
| @@ -190,7 +268,7 @@ proc createParseStmt*(t, value: NimNode): NimNode = | ||||
|       result = quote do: parseJson(`value`) | ||||
|  | ||||
|     else: | ||||
|       error "Unknown ref type: " & $t.getTypeInst | ||||
|       error "Cannot parse column with unknown ref type: " & $t.getTypeInst | ||||
|  | ||||
|   elif t.typeKind == ntySequence: | ||||
|     let innerType = t[1] | ||||
| @@ -209,36 +287,85 @@ proc createParseStmt*(t, value: NimNode): NimNode = | ||||
|     result = quote do: parseFloat(`value`) | ||||
|  | ||||
|   elif t.typeKind == ntyBool: | ||||
|     result = quote do: "true".startsWith(`value`.toLower) | ||||
|     result = quote do: "true".startsWith(`value`.toLower) or `value` == "1" | ||||
|  | ||||
|   elif t.typeKind == ntyEnum: | ||||
|     let innerType = t.getTypeInst | ||||
|     result = quote do: parseEnum[`innerType`](`value`) | ||||
|  | ||||
|   else: | ||||
|     error "Unknown value type: " & $t.typeKind | ||||
|     error "Cannot parse column with unknown value type: " & $t.typeKind | ||||
|  | ||||
| func fields(t: NimNode): seq[tuple[fieldIdent: NimNode, fieldType: NimNode]] = | ||||
|   #[ | ||||
|   debugEcho "T: " & t.treeRepr | ||||
|   debugEcho "T.kind: " & $t.kind | ||||
|   debugEcho "T.typeKind: " & $t.typeKind | ||||
|   debugEcho "T.GET_TYPE[1]: " & t.getType[1].treeRepr | ||||
|   debugEcho "T.GET_TYPE[1].kind: " & $t.getType[1].kind | ||||
|   debugEcho "T.GET_TYPE[1].typeKind: " & $t.getType[1].typeKind | ||||
|  | ||||
|   debugEcho "T.GET_TYPE: " & t.getType.treeRepr | ||||
|   debugEcho "T.GET_TYPE[1].GET_TYPE: " & t.getType[1].getType.treeRepr | ||||
|   ]# | ||||
|  | ||||
|   # Get the object type AST, with base object (if present) and record list. | ||||
|   var objDefAst: NimNode | ||||
|   if t.typeKind == ntyObject: objDefAst = t.getType | ||||
|   elif t.typeKind == ntyTypeDesc: | ||||
|     # In this case we have a type AST that is like: | ||||
|     # BracketExpr | ||||
|     #   Sym "typeDesc" | ||||
|     #   Sym "ModelType" | ||||
|     objDefAst = t. | ||||
|       getType[1].         # get the Sym "ModelType" | ||||
|       getType             # get the object definition type | ||||
|  | ||||
|     if objDefAst.kind != nnkObjectTy: | ||||
|       error ("unable to enumerate the fields for model type '$#', " & | ||||
|         "tried to resolve the type of the provided symbol to an object " & | ||||
|         "definition (nnkObjectTy) but got a '$#'.\pAST:\p$#") % [ | ||||
|           $t, $objDefAst.kind, objDefAst.treeRepr ] | ||||
|   else: | ||||
|     error ("unable to enumerate the fields for model type '$#', " & | ||||
|       "expected a symbol with type ntyTypeDesc but got a '$#'.\pAST:\p$#") % [ | ||||
|         $t, $t.typeKind, t.treeRepr ] | ||||
|  | ||||
|   # At this point objDefAst should look something like: | ||||
|   # ObjectTy | ||||
|   #   Empty | ||||
|   #   Sym "BaseObject"" | Empty | ||||
|   #   RecList | ||||
|   #     Sym "field1" | ||||
|   #     Sym "field2" | ||||
|   #     ... | ||||
|  | ||||
|   if objDefAst[1].kind == nnkSym: | ||||
|     # We have a base class symbol, let's recurse and try and resolve the fields | ||||
|     # for the base class | ||||
|     for fieldDef in objDefAst[1].fields: result.add(fieldDef) | ||||
|  | ||||
|   for fieldDef in objDefAst[2].children: | ||||
|     # objDefAst[2] is a RecList of | ||||
|     # ignore AST nodes that are not field definitions | ||||
|     if fieldDef.kind == nnkIdentDefs: result.add((fieldDef[0], fieldDef[1])) | ||||
|     elif fieldDef.kind == nnkSym: result.add((fieldDef, fieldDef.getTypeInst)) | ||||
|     else: error "unknown object field definition AST: $#" % $fieldDef.kind | ||||
|  | ||||
| template walkFieldDefs*(t: NimNode, body: untyped) = | ||||
|   let tTypeImpl = t.getTypeImpl | ||||
|   ## Iterate over every field of the given Nim object, yielding and defining | ||||
|   ## `fieldIdent` and `fieldType`, the name of the field as a Nim Ident node | ||||
|   ## and the type of the field as a Nim Type node respectively. | ||||
|   for (fieldIdent {.inject.}, fieldType {.inject.}) in t.fields: body | ||||
|  | ||||
|   var nodeToItr: NimNode | ||||
|   if tTypeImpl.typeKind == ntyObject: nodeToItr = tTypeImpl[2] | ||||
|   elif tTypeImpl.typeKind == ntyTypeDesc: nodeToItr = tTypeImpl.getType[1].getType[2] | ||||
|   else: error $t & " is not an object or type desc (it's a " & $tTypeImpl.typeKind & ")." | ||||
|  | ||||
|   for fieldDef {.inject.} in nodeToItr.children: | ||||
|     # ignore AST nodes that are not field definitions | ||||
|     if fieldDef.kind == nnkIdentDefs: | ||||
|       let fieldIdent {.inject.} = fieldDef[0] | ||||
|       let fieldType {.inject.} = fieldDef[1] | ||||
|       body | ||||
|  | ||||
|     elif fieldDef.kind == nnkSym: | ||||
|       let fieldIdent {.inject.} = fieldDef | ||||
|       let fieldType {.inject.} = fieldDef.getType | ||||
|       body | ||||
| #[ TODO: replace walkFieldDefs with things like this: | ||||
| func columnNamesForModel*(modelType: typedesc): seq[string] = | ||||
|   modelType.fields.mapIt(identNameToDb($it[0])) | ||||
| ]# | ||||
|  | ||||
| macro columnNamesForModel*(modelType: typed): seq[string] = | ||||
|   ## Return the column names corresponding to the the fields of the given | ||||
|   ## `model class`_ | ||||
|   var columnNames = newSeq[string]() | ||||
|  | ||||
|   modelType.walkFieldDefs: | ||||
| @@ -247,6 +374,8 @@ macro columnNamesForModel*(modelType: typed): seq[string] = | ||||
|   result = newLit(columnNames) | ||||
|  | ||||
| macro rowToModel*(modelType: typed, row: seq[string]): untyped = | ||||
|   ## Return a new Nim model object of type `modelType` populated with the | ||||
|   ## values returned in the given database `row` | ||||
|  | ||||
|   # Create the object constructor AST node | ||||
|   result = newNimNode(nnkObjConstr).add(modelType) | ||||
| @@ -260,6 +389,7 @@ macro rowToModel*(modelType: typed, row: seq[string]): untyped = | ||||
|       createParseStmt(fieldType, itemLookup))) | ||||
|     idx += 1 | ||||
|  | ||||
| #[ | ||||
| macro listFields*(t: typed): untyped = | ||||
|   var fields: seq[tuple[n: string, t: string]] = @[] | ||||
|   t.walkFieldDefs: | ||||
| @@ -267,8 +397,10 @@ macro listFields*(t: typed): untyped = | ||||
|     else: fields.add((n: $fieldIdent, t: $fieldType)) | ||||
|  | ||||
|   result = newLit(fields) | ||||
| ]# | ||||
|  | ||||
| proc typeOfColumn*(modelType: NimNode, colName: string): NimNode = | ||||
|   ## Given a model type and a column name, return the Nim type for that column. | ||||
|   modelType.walkFieldDefs: | ||||
|     if $fieldIdent != colName: continue | ||||
|  | ||||
| @@ -287,8 +419,11 @@ proc typeOfColumn*(modelType: NimNode, colName: string): NimNode = | ||||
| proc isEmpty(val: int): bool = return val == 0 | ||||
| proc isEmpty(val: UUID): bool = return val.isZero | ||||
| proc isEmpty(val: string): bool = return val.isEmptyOrWhitespace | ||||
| proc isEmpty[T](val: Option[T]): bool = return val.isNone | ||||
|  | ||||
| macro populateMutateClauses*(t: typed, newRecord: bool, mc: var MutateClauses): untyped = | ||||
|   ## Given a record type, create the datastructure used to generate SQL clauses | ||||
|   ## for the fields of this record type. | ||||
|  | ||||
|   result = newStmtList() | ||||
|  | ||||
| @@ -309,8 +444,8 @@ macro populateMutateClauses*(t: typed, newRecord: bool, mc: var MutateClauses): | ||||
|  | ||||
|       # if we're looking at an optional field, add logic to check for presence | ||||
|       elif fieldType.kind == nnkBracketExpr and | ||||
|          fieldType.len > 0 and | ||||
|          fieldType[0] == Option.getType: | ||||
|            fieldType.len > 0 and | ||||
|            fieldType[0] == Option.getType: | ||||
|  | ||||
|         result.add quote do: | ||||
|           `mc`.columns.add(identNameToDb(`fieldName`)) | ||||
| @@ -326,3 +461,20 @@ macro populateMutateClauses*(t: typed, newRecord: bool, mc: var MutateClauses): | ||||
|           `mc`.columns.add(identNameToDb(`fieldName`)) | ||||
|           `mc`.placeholders.add("?") | ||||
|           `mc`.values.add(dbFormat(`t`.`fieldIdent`)) | ||||
|  | ||||
|  | ||||
| proc getPagingClause*(page: PaginationParams): string = | ||||
|   ## Given a `PaginationParams` object, return the SQL clause necessary to | ||||
|   ## limit the number of records returned by a query. | ||||
|   result = "" | ||||
|   if page.orderBy.isSome: | ||||
|     let orderByClause = page.orderBy.get.map(identNameToDb).join(",") | ||||
|     result &= " ORDER BY " & orderByClause | ||||
|   else: | ||||
|     result &= " ORDER BY id" | ||||
|  | ||||
|   result &= " LIMIT " & $page.pageSize & " OFFSET " & $page.offset | ||||
|  | ||||
| ## .. _model class: ../fiber_orm.html#objectminusrelational-modeling-model-class | ||||
| ## .. _rules for name mapping: ../fiber_orm.html | ||||
| ## .. _table name: ../fiber_orm.html | ||||
|   | ||||
		Reference in New Issue
	
	Block a user