Skip to content

Commit

Permalink
Releas version 0.1.1
Browse files Browse the repository at this point in the history
* schema gen utility 

* setting ConnectionHandler self$snakeCaseToCamelCase

* readr dependency
  • Loading branch information
azimov authored Dec 2, 2022
1 parent 6b0c507 commit c7bf3e6
Show file tree
Hide file tree
Showing 15 changed files with 258 additions and 56 deletions.
5 changes: 3 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: ResultModelManager
Title: Result Model Manager (RMM) for OHDSI packages
Version: 0.1.0
Version: 0.1.1
Authors@R:
person("Jamie", "Gilbert", , "gilbert@ohdsi.org", role = c("aut", "cre"))
Description: Database data model management utilities for OHDSI packages.
Expand All @@ -17,7 +17,8 @@ Imports:
ParallelLogger,
checkmate,
DBI,
pool
pool,
readr
Suggests:
testthat (>= 3.0.0),
RSQLite,
Expand Down
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
export(ConnectionHandler)
export(DataMigrationManager)
export(PooledConnectionHandler)
export(generateSqlSchema)
import(DatabaseConnector)
import(R6)
import(checkmate)
Expand All @@ -13,3 +14,4 @@ importFrom(SqlRender,render)
importFrom(SqlRender,translate)
importFrom(pool,dbPool)
importFrom(pool,poolClose)
importFrom(readr,read_csv)
9 changes: 8 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# ResultModelManager 0.0.1
# ResultModelManager 0.1.1

Changes:
1. Added snakeCaseToCamelCase parameter to public in connectionHandlers so it can be defined once if required

2. Added schema generator function that creates sql from csv files with table defs

# ResultModelManager 0.1.0

Initial version
24 changes: 14 additions & 10 deletions R/ConnectionHandler.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
#'
#' @field connectionDetails DatabaseConnector connectionDetails object
#' @field con DatabaseConnector connection object
#' @field isActive Is connection active or not
#' @field isActive Is connection active or not#'
#' @field snakeCaseToCamelCase (Optional) Boolean. return the results columns in camel case (default)
#'
#' @import checkmate
#' @import R6
Expand All @@ -35,13 +36,15 @@ ConnectionHandler <- R6::R6Class(
connectionDetails = NULL,
con = NULL,
isActive = FALSE,
snakeCaseToCamelCase = TRUE,
#'
#' @param connectionDetails DatabaseConnector::connectionDetails class
#' @param loadConnection Boolean option to load connection right away
initialize = function(connectionDetails, loadConnection = TRUE) {
#' @param snakeCaseToCamelCase (Optional) Boolean. return the results columns in camel case (default)
initialize = function(connectionDetails, loadConnection = TRUE, snakeCaseToCamelCase = TRUE) {
checkmate::assertClass(connectionDetails, "connectionDetails")
self$connectionDetails <- connectionDetails

self$snakeCaseToCamelCase <- snakeCaseToCamelCase
if (loadConnection) {
self$initConnection()
}
Expand Down Expand Up @@ -75,11 +78,13 @@ ConnectionHandler <- R6::R6Class(
#' Connects automatically if it isn't yet loaded
#' @returns DatabaseConnector Connection instance
getConnection = function() {
if (is.null(self$con))
if (is.null(self$con)) {
self$initConnection()
}

if (!self$dbIsValid())
if (!self$dbIsValid()) {
self$initConnection()
}

return(self$con)
},
Expand Down Expand Up @@ -128,14 +133,14 @@ ConnectionHandler <- R6::R6Class(
#' You may wish to ignore it.
#' @param ... Additional query parameters
#' @returns boolean TRUE if connection is valid
queryDb = function(sql, snakeCaseToCamelCase = TRUE, overrideRowLimit = FALSE, ...) {
queryDb = function(sql, snakeCaseToCamelCase = self$snakeCaseToCamelCase, overrideRowLimit = FALSE, ...) {
# Limit row count is intended for web applications that may cause a denial of service if they consume too many
# resources.
limitRowCount <- as.integer(Sys.getenv("LIMIT_ROW_COUNT"))
if (!is.na(limitRowCount) & limitRowCount > 0 & !overrideRowLimit) {
sql <- SqlRender::render("SELECT TOP @limit_row_count * FROM (@query) result;",
query = gsub(";$", "", sql), # Remove last semi-colon
limit_row_count = limitRowCount
query = gsub(";$", "", sql), # Remove last semi-colon
limit_row_count = limitRowCount
)
}
sql <- self$renderTranslateSql(sql, ...)
Expand Down Expand Up @@ -186,7 +191,7 @@ ConnectionHandler <- R6::R6Class(
#' Does not translate or render sql.
#' @param sql sql query string
#' @param snakeCaseToCamelCase (Optional) Boolean. return the results columns in camel case (default)
queryFunction = function(sql, snakeCaseToCamelCase = TRUE) {
queryFunction = function(sql, snakeCaseToCamelCase = self$snakeCaseToCamelCase) {
DatabaseConnector::querySql(self$getConnection(), sql, snakeCaseToCamelCase = snakeCaseToCamelCase)
},

Expand All @@ -200,4 +205,3 @@ ConnectionHandler <- R6::R6Class(
}
)
)

37 changes: 20 additions & 17 deletions R/DataMigrationManager.R
Original file line number Diff line number Diff line change
Expand Up @@ -213,14 +213,14 @@ DataMigrationManager <- R6::R6Class(
# load list of migrations
migrations <- self$getStatus()
# execute migrations that haven't been executed yet
migrations <- migrations[!migrations$executed,]
migrations <- migrations[!migrations$executed, ]
if (nrow(migrations) > 0) {
if (is.null(stopMigrationVersion)) {
stopMigrationVersion <- max(migrations$migrationOrder)
}

for (i in 1:nrow(migrations)) {
migration <- migrations[i,]
migration <- migrations[i, ]
if (isTRUE(migration$migrationOrder <= stopMigrationVersion)) {
private$executeMigration(migration)
}
Expand Down Expand Up @@ -250,10 +250,11 @@ DataMigrationManager <- R6::R6Class(
# Load, render, translate and execute sql
if (self$isPackage()) {
sql <- SqlRender::loadRenderTranslateSql(file.path(self$migrationPath, migration$migrationFile),
dbms = private$connectionDetails$dbms,
database_schema = self$databaseSchema,
table_prefix = self$tablePrefix,
packageName = self$packageName)
dbms = private$connectionDetails$dbms,
database_schema = self$databaseSchema,
table_prefix = self$tablePrefix,
packageName = self$packageName
)
private$connectionHandler$executeSql(sql)
} else {
# Check to see if a file for database platform exists
Expand All @@ -264,8 +265,9 @@ DataMigrationManager <- R6::R6Class(
sql <- SqlRender::readSql(file.path(self$migrationPath, "sql_server", migration$migrationFile))
}
private$connectionHandler$executeSql(sql,
database_schema = self$databaseSchema,
table_prefix = self$tablePrefix)
database_schema = self$databaseSchema,
table_prefix = self$tablePrefix
)
}
private$logInfo("Saving migration: ", migration$migrationFile)
# Save migration in set of migrations
Expand All @@ -275,10 +277,10 @@ DataMigrationManager <- R6::R6Class(
VALUES ('@migration_file', @order);
"
private$connectionHandler$executeSql(iSql,
database_schema = self$databaseSchema,
migration_file = migration$migrationFile,
table_prefix = self$tablePrefix,
order = migration$migrationOrder
database_schema = self$databaseSchema,
migration_file = migration$migrationFile,
table_prefix = self$tablePrefix,
order = migration$migrationOrder
)
private$logInfo("Migration complete ", migration$migrationFile)
},
Expand All @@ -293,8 +295,9 @@ DataMigrationManager <- R6::R6Class(
);"

private$connectionHandler$executeSql(sql,
database_schema = self$databaseSchema,
table_prefix = self$tablePrefix)
database_schema = self$databaseSchema,
table_prefix = self$tablePrefix
)
private$logInfo("Migrations table created")
},
getCompletedMigrations = function() {
Expand All @@ -306,8 +309,9 @@ DataMigrationManager <- R6::R6Class(
{DEFAULT @migration = migration}
SELECT migration_file, migration_order FROM @database_schema.@table_prefix@migration ORDER BY migration_order;"
migrationsExecuted <- private$connectionHandler$queryDb(sql,
database_schema = self$databaseSchema,
table_prefix = self$tablePrefix)
database_schema = self$databaseSchema,
table_prefix = self$tablePrefix
)

return(migrationsExecuted)
},
Expand All @@ -329,7 +333,6 @@ DataMigrationManager <- R6::R6Class(
ParallelLogger::logError(...)
}
},

logInfo = function(...) {
if (isUnitTest() | isRmdCheck()) {
writeLines(text = .makeMessage(...))
Expand Down
2 changes: 1 addition & 1 deletion R/PooledConnectionHandler.R
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ PooledConnectionHandler <- R6::R6Class(
#' Overrides ConnectionHandler Call. Does not translate or render sql.
#' @param sql sql query string
#' @param snakeCaseToCamelCase (Optional) Boolean. return the results columns in camel case (default)
queryFunction = function(sql, snakeCaseToCamelCase = TRUE) {
queryFunction = function(sql, snakeCaseToCamelCase = self$snakeCaseToCamelCase) {
data <- DatabaseConnector::dbGetQuery(self$getConnection(), sql)
if (snakeCaseToCamelCase) {
colnames(data) <- SqlRender::snakeCaseToCamelCase(colnames(data))
Expand Down
92 changes: 92 additions & 0 deletions R/SchemaGenerator.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Copyright 2022 Observational Health Data Sciences and Informatics
#
# This file is part of CohortDiagnostics
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

.writeFieldDefinition <- function(field) {
field <- as.list(field)
str <- paste("\t", field$columnName, toupper(field$dataType))

if (field$primaryKey == "yes") {
str <- paste(str, "NOT NULL")
}

str
}

#' Schema generator
#' @description
#' Take a csv schema definition and create a basic sql script with it.
#'
#' @param csvFilepath Path to schema file. Csv file must have the columns:
#' "table_name", "colum_name", "data_type", "is_required", "primary_key"
#' Note -
#' @param sqlOutputPath File to write sql to.
#' @param overwrite Boolean - overwrite existing file?
#' @export
#'
#' @importFrom readr read_csv
#' @return
#' string containing the sql for the table
generateSqlSchema <- function(csvFilepath,
sqlOutputPath = NULL,
overwrite = FALSE) {
if (!is.null(sqlOutputPath) && (file.exists(sqlOutputPath) & !overwrite)) {
stop("Output file ", sqlOutputPath, "already exists. Set overwrite = TRUE to continue")
}

checkmate::assertFileExists(csvFilepath)
schemaDefinition <- readr::read_csv(csvFilepath, show_col_types = FALSE)
colnames(schemaDefinition) <- SqlRender::snakeCaseToCamelCase(colnames(schemaDefinition))
requiredFields <- c("tableName", "columnName", "dataType", "isRequired", "primaryKey")
checkmate::assertNames(colnames(schemaDefinition), must.include = requiredFields)

tableSqlStr <- "
CREATE TABLE @database_schema.@table_prefix@table_name (
@table_fields
);
"
fullScript <- ""
defs <- "{DEFAULT @table_prefix = ''}\n"

for (table in unique(schemaDefinition$tableName)) {
tableFields <- schemaDefinition[schemaDefinition$tableName == table, ]
fieldDefinitions <- apply(tableFields, 1, .writeFieldDefinition)

primaryKeyFields <- tableFields[tableFields$primaryKey == "yes", ]
if (nrow(primaryKeyFields)) {
pkeyField <- paste0("\tPRIMARY KEY(", paste(primaryKeyFields$columnName, collapse = ","), ")")
fieldDefinitions <- c(fieldDefinitions, pkeyField)
}

fieldDefinitions <- paste(fieldDefinitions, collapse = ",\n")
tableString <- SqlRender::render(tableSqlStr,
table_name = paste0("@", table),
table_fields = fieldDefinitions
)

tableDefStr <- paste0("{DEFAULT @", table, " = ", table, "}\n")
defs <- paste0(defs, tableDefStr)

fullScript <- paste(fullScript, tableString)
}

# Get fields for each table
lines <- paste(defs, fullScript)
if (!is.null(sqlOutputPath)) {
writeLines(lines, sqlOutputPath)
}

lines
}
Binary file modified extras/ResultModelManager.pdf
Binary file not shown.
21 changes: 16 additions & 5 deletions man/ConnectionHandler.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit c7bf3e6

Please sign in to comment.