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 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; | ||||
| ## | ||||
| @@ -312,6 +313,9 @@ type | ||||
|     records*: seq[T] | ||||
|     totalRecords*: int | ||||
|  | ||||
|   DbUpdateError* = object of CatchableError ##\ | ||||
|     ## Error types raised when a DB modification fails. | ||||
|  | ||||
|   NotFoundError* = object of CatchableError ##\ | ||||
|     ## Error type raised when no record matches a given ID | ||||
|  | ||||
| @@ -366,6 +370,24 @@ proc updateRecord*[D: DbConnType, T](db: D, rec: T): bool = | ||||
|  | ||||
|   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 = | ||||
|   ## Delete a record by 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, 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; | ||||
| @@ -537,6 +560,7 @@ macro generateProcsForModels*(dbType: type, modelTypes: openarray[type]): untype | ||||
|     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: | ||||
| @@ -560,6 +584,9 @@ macro generateProcsForModels*(dbType: type, modelTypes: openarray[type]): untype | ||||
|       proc `updateName`*(db: `dbType`, rec: `t`): bool = | ||||
|         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 = | ||||
|         db.withConn: result = deleteRecord(conn, rec) | ||||
|  | ||||
|   | ||||
| @@ -102,7 +102,7 @@ proc dbFormat*[T](list: seq[T]): string = | ||||
|  | ||||
| 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> | ||||
|   ## string version of the value we can include in an SQL query. | ||||
|   return $item | ||||
|  | ||||
| type DbArrayParseState = enum | ||||
|   | ||||
		Reference in New Issue
	
	Block a user