Nothing
#' Read a data model from an SQL file from the MySQL Workbench
#'
#' @param f the SQL file to read
#' @param typeRef the reference for type conversion
#' (Default: "MySQLWB"; see [list_type_ref()])
#' @param mysqlcomments if MySQL comments (starting with #) should be removed
#' (Default: TRUE)
#'
#' @details Database, table and field names should be surrounded by "`".
#'
#' @return A [RelDataModel] object
#'
#' @example inst/examples/ex_read_sql.R
#'
#' @export
#'
read_SQL_data_model <- function(f, typeRef = "MySQLWB", mysqlcomments = TRUE) {
############################################################################@
## Helpers ----
###################################################@
getBrackPos <- function(x) {
oBrackPos <- setdiff(gregexpr('[(]', x)[[1]], -1)
if (length(oBrackPos) == 0) {
return(data.frame(s = 1, e = 1)[-1, ])
}
cBrackPos <- setdiff(gregexpr('[)]', x)[[1]], -1)
if (length(cBrackPos) != length(oBrackPos)) {
stop("Closing brackets not matching opening brackets")
}
posBrack <- dplyr::tibble(
pos = c(oBrackPos, cBrackPos),
brack = c(rep("(", length(oBrackPos)), rep(")", length(cBrackPos)))
) |>
dplyr::arrange(.data$pos) |>
dplyr::mutate(oc = ifelse(.data$brack == "(", 1, -1)) |>
dplyr::mutate(ib = cumsum(.data$oc))
e <- which(posBrack$ib == 0)
s <- c(1, e[-length(e)] + 1)
return(dplyr::tibble(
s = posBrack$pos[s],
e = posBrack$pos[e]
))
}
###################################################@
getVarNamePos <- function(x) {
varNamePos <- gregexpr('`[^`]*`', x)[[1]]
if (varNamePos[1] == -1) {
return(dplyr::tibble(s = 1, e = 1)[-1, ])
}
return(dplyr::tibble(
s = varNamePos,
e = varNamePos + attr(varNamePos, "match.length") - 1
))
}
###################################################@
splitByPos <- function(x, pos, keepPos = c('NO', 'right', 'left')) {
keepPos <- match.arg(keepPos)
stopifnot(all(pos > 0), all(pos <= nchar(x)), length(x) == 1)
if (length(pos) == 0) {
return(x)
}
einc <- switch(
keepPos,
"NO" = -1,
"right" = 0,
"left" = -1
)
sinc <- switch(
keepPos,
"NO" = +1,
"right" = +1,
"left" = 0
)
toRet <- list()
s <- 1
for (i in 1:length(pos)) {
p <- pos[i]
e <- p + einc
toRet[[i]] <- substr(x, s, e)
s <- p + sinc
}
e <- nchar(x)
if (s <= e) {
toRet[[i + 1]] <- substr(x, s, e)
} else {
toRet[[i + 1]] <- ""
}
return(unlist(toRet))
}
###################################################@
getSubStatements <- function(x, sep = ",") {
varNamePos <- getVarNamePos(x)
brackPos <- getBrackPos(x)
toExcl <- union(
unlist(apply(varNamePos, 1, function(x) x[1]:x[2])),
unlist(apply(brackPos, 1, function(x) x[1]:x[2]))
)
splitPos <- setdiff(gregexpr(sep, x)[[1]], c(-1, toExcl))
toRet <- splitByPos(x, splitPos, keepPos = "NO")
toRet <- sub("^[[:space:]]*", "", toRet)
toRet <- sub("[[:space:]]*$", "", toRet)
return(toRet)
}
###################################################@
parseCreateTableStatement <- function(cs, typeRef) {
## Table ----
header <- sub("[[:space:]]*[(].*", "", cs)
p <- gregexpr('(`[^`]*`[.])?`[^`]*`', header)[[1]]
tableFullName <- substr(header, p, p + attr(p, "match.length") - 1) |>
strsplit(split = "[.]") |>
unlist()
tableFullName <- gsub("`", "", tableFullName)
if (length(tableFullName) == 1) {
tableFullName <- c("", tableFullName)
}
dbName <- tableFullName[1]
tableName <- tableFullName[2]
tableFullName <- paste0("`", dbName, "`.`", tableName, "`")
##
body <- sub("[^(]*[(]", "", cs)
body <- sub("[)][^)]*$", "", body)
subStatements <- getSubStatements(body, sep = ",")
## Fields ----
fields <- grep("^[`]", subStatements, value = TRUE)
types <- sub(" .*$", "", sub("^.*` *", "", fields))
nullable <- rep(TRUE, length(fields))
nullable[grep("NOT NULL", fields)] <- FALSE
# comPos <- gregexpr(" *COMMENT *'[^']*'", fields)
comPos <- gregexpr(" *COMMENT *'.*'", fields)
comments <- rep("", length(fields))
for (i in 1:length(comPos)) {
p <- comPos[[i]]
if (p != -1) {
toAdd <- substr(fields[i], p, p + attr(p, 'match.length') - 1)
toAdd <- sub(" *COMMENT *'", "", toAdd)
toAdd <- sub("'$", "", toAdd)
toAdd <- gsub('\\\\"', '"', toAdd)
toAdd <- gsub("\\\\'", "'", toAdd)
comments[i] <- toAdd
}
}
fields <- dplyr::tibble(
name = sub("[`].*$", "", sub("[`]", "", fields)),
type = conv_type_ref(types, from = typeRef),
nullable = nullable,
comment = comments
)
## Primary key ----
primaryKey <- grep("^PRIMARY KEY ", subStatements, value = TRUE)
primaryKey <- sub("^PRIMARY KEY [(]", "", primaryKey)
primaryKey <- sub("[)]$", "", primaryKey)
if (length(primaryKey) > 0) {
primaryKey <- getSubStatements(gsub('`', '', primaryKey), sep = ",")
} else {
primaryKey <- NULL
}
## Foreign keys ----
foreignKeys <- grep(
"^CONSTRAINT `[^`]*` FOREIGN KEY [(]",
subStatements,
value = TRUE
)
foreignKeys <- do.call(
c,
lapply(
foreignKeys,
function(x) {
fk <- sub(
'[)].*$',
"",
sub("^CONSTRAINT `[^`]*` FOREIGN KEY [(]", "", x)
)
fk <- gsub('`', '', getSubStatements(fk, sep = ","))
ref <- sub(
"^CONSTRAINT `[^`]*` FOREIGN KEY [(].*[)] REFERENCES ",
"",
x
)
p <- gregexpr('(`[^`]*`[.])?`[^`]*`', ref)[[1]]
refTableFullName <- substr(
ref,
p,
p + attr(p, "match.length") - 1
) |>
strsplit(split = "[.]") |>
unlist()
refTableFullName <- gsub("`", "", refTableFullName)
if (length(refTableFullName) == 1) {
refTableFullName <- c("", refTableFullName)
}
refDB <- refTableFullName[1]
refTable <- refTableFullName[2]
refTableFullName <- paste0("`", refDB, "`.`", refTable, "`")
refKeys <- gsub(
'`',
'',
getSubStatements(
sub(
'^.*[(]',
"",
sub('[)].*$', "", ref)
),
sep = ","
)
)
toRet <- dplyr::tibble(
from = fk,
to = refKeys
)
toRet <- list(list(
refDB = refDB,
refTable = refTable,
key = toRet
))
names(toRet) <- refTableFullName
return(toRet)
}
)
)
## Indexes ----
indexes <- grep("^INDEX ", subStatements, value = TRUE)
indexes <- sub("[)][^)]*$", "", sub("^INDEX [^(]*[(]", "", indexes))
indexes <- lapply(
indexes,
function(x) {
toRet <- getSubStatements(x, sep = ",")
if (toRet[1] == "") {
return(NULL)
}
toRet <- do.call(
rbind,
lapply(
toRet,
function(y) {
toRet <- getSubStatements(y, sep = " ")
toRet <- gsub('`', '', toRet)
return(toRet)
}
)
)
colnames(toRet) <- c("field", "order")
toRet <- list(fields = toRet[, "field"], unique = FALSE)
return(toRet)
}
)
if (length(indexes) > 0) {
indexes <- indexes[which(!unlist(lapply(indexes, is.null)))]
} else {
indexes <- NULL
}
## Unique indexes ----
uindexes <- grep("^UNIQUE INDEX ", subStatements, value = TRUE)
uindexes <- sub(
"[)][^)]*$",
"",
sub("^UNIQUE INDEX [^(]*[(]", "", uindexes)
)
uindexes <- unlist(lapply(
uindexes,
function(x) {
toRet <- getSubStatements(x, sep = ",")
if (toRet[1] == "") {
return(NULL)
}
toRet <- do.call(
rbind,
lapply(
toRet,
function(y) {
toRet <- getSubStatements(y, sep = " ")
toRet <- gsub('`', '', toRet)
return(toRet)
}
)
)
colnames(toRet) <- c("field", "order")
toRet <- toRet[, "field"]
return(toRet)
}
))
fields <- fields |>
dplyr::mutate(unique = ifelse(.data$name %in% uindexes, TRUE, FALSE))
toRet <- list(list(
dbName = dbName,
tableName = tableName,
fields = fields,
primaryKey = primaryKey,
foreignKeys = foreignKeys,
indexes = indexes, #c(indexes, uindexes),
display = list(
x = as.numeric(NA),
y = as.numeric(NA),
color = as.character(NA),
comment = as.character(NA)
)
))
names(toRet) <- tableFullName
return(toRet)
}
############################################################################@
## Main ----
typeRef <- match.arg(typeRef, list_type_ref())
txt <- readLines(f) |>
paste(collapse = "\n")
txt <- gsub("/\\*((?!\\*/)(.|\n))*\\**/", "", txt, perl = T) |>
strsplit(split = "\n") |>
unlist()
txt <- sub("--.*$", "", txt)
if (mysqlcomments) {
txt <- sub("#.*$", "", txt)
}
txt <- sub("^[[:space:]]*", "", txt)
txt <- txt[which(txt != "")] |>
paste(collapse = " ") |>
strsplit(split = ";") |>
unlist()
txt <- sub("^[[:space:]]*", "", txt)
txt <- sub("[[:space:]]*$", "", txt)
txt <- gsub("[[:space:]]+", " ", txt)
tdTxt <- grep("^CREATE TABLE", txt, ignore.case = TRUE, value = TRUE)
unsupported <- grep("`", tdTxt, value = TRUE, invert = TRUE)
if (length(unsupported) > 0) {
stop(
sprintf(
'%s statement%s %s not supported because without "`":\n',
length(unsupported),
ifelse(length(unsupported) > 1, "s", ""),
ifelse(length(unsupported) > 1, "are", "is")
),
paste(" -", unsupported) |> paste(collapse = "\n")
)
}
toRet <- do.call(
c,
lapply(
tdTxt,
parseCreateTableStatement,
typeRef = typeRef
)
)
dbNames <- unlist(lapply(toRet, function(x) x$dbName))
if (length(unique(dbNames)) > 1) {
stop(
"Several database names are mentioned in the SQL model. ",
"There should be only one."
)
}
toRet <- lapply(toRet, function(x) x[which(names(x) != "dbName")])
toRet <- lapply(toRet, RelTableModel)
toRet <- RelDataModel(toRet, checkFK = TRUE)
return(toRet)
}
#' @describeIn read_SQL_data_model
#'
#' Deprecated version of read_SQL_data_model
#'
#' @param ... params for `read_SQL_data_model`
#'
#' @export
readSQLDataModel <- function(...) {
warning("Deprecated. Use read_SQL_data_model instead")
read_SQL_data_model(...)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.