Add createOrUpdateRecord and record method generators.
				
					
				
			`createOrUpdateRecord` implements upsert: update an existing record if it exists or create a new record if not. A new error `DbUpdateError` was added to be raised when an existing record does exist but was not able to be updated.
This commit is contained in:
		| @@ -107,6 +107,7 @@ | |||||||
| ## | ## | ||||||
| ##    proc createTodoItem*(db: TodoDB, rec: TodoItem): TodoItem; | ##    proc createTodoItem*(db: TodoDB, rec: TodoItem): TodoItem; | ||||||
| ##    proc updateTodoItem*(db: TodoDB, rec: TodoItem): bool; | ##    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, rec: TodoItem): bool; | ||||||
| ##    proc deleteTodoItem*(db: TodoDB, id: UUID): bool; | ##    proc deleteTodoItem*(db: TodoDB, id: UUID): bool; | ||||||
| ## | ## | ||||||
| @@ -312,6 +313,9 @@ type | |||||||
|     records*: seq[T] |     records*: seq[T] | ||||||
|     totalRecords*: int |     totalRecords*: int | ||||||
|  |  | ||||||
|  |   DbUpdateError* = object of CatchableError ##\ | ||||||
|  |     ## Error types raised when a DB modification fails. | ||||||
|  |  | ||||||
|   NotFoundError* = object of CatchableError ##\ |   NotFoundError* = object of CatchableError ##\ | ||||||
|     ## Error type raised when no record matches a given ID |     ## Error type raised when no record matches a given ID | ||||||
|  |  | ||||||
| @@ -366,6 +370,24 @@ proc updateRecord*[D: DbConnType, T](db: D, rec: T): bool = | |||||||
|  |  | ||||||
|   return numRowsUpdated > 0; |   return numRowsUpdated > 0; | ||||||
|  |  | ||||||
|  | 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 = ?" | ||||||
|  |   log().debug "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 = | template deleteRecord*[D: DbConnType](db: D, modelType: type, id: typed): untyped = | ||||||
|   ## Delete a record by id. |   ## Delete a record by id. | ||||||
|   let sqlStmt = "DELETE FROM " & tableName(modelType) & " WHERE id = ?" |   let sqlStmt = "DELETE FROM " & tableName(modelType) & " WHERE id = ?" | ||||||
| @@ -516,6 +538,7 @@ macro generateProcsForModels*(dbType: type, modelTypes: openarray[type]): untype | |||||||
|   ##    proc deleteTodoItem*(db: TodoDB, rec: TodoItem): bool; |   ##    proc deleteTodoItem*(db: TodoDB, rec: TodoItem): bool; | ||||||
|   ##    proc deleteTodoItem*(db: TodoDB, id: idType): bool; |   ##    proc deleteTodoItem*(db: TodoDB, id: idType): bool; | ||||||
|   ##    proc updateTodoItem*(db: TodoDB, rec: TodoItem): bool; |   ##    proc updateTodoItem*(db: TodoDB, rec: TodoItem): bool; | ||||||
|  |   ##    proc createOrUpdateTodoItem*(db: TodoDB, rec: TodoItem): bool; | ||||||
|   ## |   ## | ||||||
|   ##    proc findTodoItemsWhere*( |   ##    proc findTodoItemsWhere*( | ||||||
|   ##      db: TodoDB, whereClause: string, values: varargs[string]): TodoItem; |   ##      db: TodoDB, whereClause: string, values: varargs[string]): TodoItem; | ||||||
| @@ -537,6 +560,7 @@ macro generateProcsForModels*(dbType: type, modelTypes: openarray[type]): untype | |||||||
|     let findWhereName = ident("find" & pluralize(modelName) & "Where") |     let findWhereName = ident("find" & pluralize(modelName) & "Where") | ||||||
|     let createName = ident("create" & modelName) |     let createName = ident("create" & modelName) | ||||||
|     let updateName = ident("update" & modelName) |     let updateName = ident("update" & modelName) | ||||||
|  |     let createOrUpdateName = ident("createOrUpdate" & modelName) | ||||||
|     let deleteName = ident("delete" & modelName) |     let deleteName = ident("delete" & modelName) | ||||||
|     let idType = typeOfColumn(t, "id") |     let idType = typeOfColumn(t, "id") | ||||||
|     result.add quote do: |     result.add quote do: | ||||||
| @@ -560,6 +584,9 @@ macro generateProcsForModels*(dbType: type, modelTypes: openarray[type]): untype | |||||||
|       proc `updateName`*(db: `dbType`, rec: `t`): bool = |       proc `updateName`*(db: `dbType`, rec: `t`): bool = | ||||||
|         db.withConn: result = updateRecord(conn, rec) |         db.withConn: result = updateRecord(conn, rec) | ||||||
|  |  | ||||||
|  |       proc `createOrUpdateName`*(db: `dbType`, rec: `t`): `t` = | ||||||
|  |         db.inTransaction: result = createOrUpdateRecord(conn, rec) | ||||||
|  |  | ||||||
|       proc `deleteName`*(db: `dbType`, rec: `t`): bool = |       proc `deleteName`*(db: `dbType`, rec: `t`): bool = | ||||||
|         db.withConn: result = deleteRecord(conn, rec) |         db.withConn: result = deleteRecord(conn, rec) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -102,7 +102,7 @@ proc dbFormat*[T](list: seq[T]): string = | |||||||
|  |  | ||||||
| proc dbFormat*[T](item: T): string = | proc dbFormat*[T](item: T): string = | ||||||
|   ## For all other types, fall back on a defined `$` function to create a |   ## 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> |   ## string version of the value we can include in an SQL query. | ||||||
|   return $item |   return $item | ||||||
|  |  | ||||||
| type DbArrayParseState = enum | type DbArrayParseState = enum | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user