Added R methods and their c counterparts for connection and database management

This commit is contained in:
2020-10-02 13:12:03 +01:00
parent 91013ea064
commit 907d848791
19 changed files with 847 additions and 28 deletions

View File

@@ -1,2 +1,25 @@
# Generated by roxygen2: do not edit by hand # Generated by roxygen2: do not edit by hand
export(TYPE_BLOB)
export(TYPE_BOOL)
export(TYPE_DOUBLE)
export(TYPE_FLOAT)
export(TYPE_INT16)
export(TYPE_INT32)
export(TYPE_INT64)
export(TYPE_INT8)
export(TYPE_RAW)
export(TYPE_STRING)
export(TYPE_TIMESTAMP)
export(TYPE_UINT16)
export(TYPE_UINT32)
export(TYPE_UINT64)
export(TYPE_UINT8)
export(modb_conn_ref)
export(modb_connect)
export(modb_connectionExists)
export(modb_connectionId)
export(modb_connectionInfo)
export(modb_connectionName)
export(modb_disconnect)
useDynLib(rmodb, .registration = TRUE, .fixes = "c_")

30
R/Defines.R Normal file
View File

@@ -0,0 +1,30 @@
#' @export
TYPE_RAW <- bitwShiftL(0, 0)
#' @export
TYPE_BOOL <- bitwShiftL(1, 0)
#' @export
TYPE_INT8 <- bitwShiftL(1, 1)
#' @export
TYPE_UINT8 <- bitwShiftL(1, 2)
#' @export
TYPE_INT16 <- bitwShiftL(1, 3)
#' @export
TYPE_UINT16 <- bitwShiftL(1, 4)
#' @export
TYPE_INT32 <- bitwShiftL(1, 5)
#' @export
TYPE_UINT32 <- bitwShiftL(1, 6)
#' @export
TYPE_INT64 <- bitwShiftL(1, 7)
#' @export
TYPE_UINT64 <- bitwShiftL(1, 8)
#' @export
TYPE_FLOAT <- bitwShiftL(1, 9)
#' @export
TYPE_DOUBLE <- bitwShiftL(1, 10)
#' @export
TYPE_STRING <- bitwShiftL(1, 11)
#' @export
TYPE_BLOB <- bitwShiftL(1, 12)
#' @export
TYPE_TIMESTAMP <- bitwShiftL(1, 13)

226
R/Manage.R Normal file
View File

@@ -0,0 +1,226 @@
# Connection management --------------------------------------------------------
#' MODB Connection References
#'
#' \code{modb_conn_ref} checks for and/or validates parameters that may be a
#' connection reference. Connections in MODB may be named on creation or must
#' be referenced by the unique ID that is returned by the
#' \link{\code{modb_connect}} method.
#'
#' @param conn_id Integer. The ID of the connection returned by modb_connect
#' @param conn_name String. The name provided when creating a connection.
#' @param conn_ref Expected to be either a conn_id or a conn_name
#' @param args list(...) Arguments passed to a calling function in the va scope.
#' @seealso \link{\code{modb_connect}}
#' @export
modb_conn_ref <- function(conn_id, conn_name, conn_ref, args = NULL) {
if (length(args) != 0) {
if ("conn_ref" %in% ls(args)) {
conn_ref <- args$conn_ref
}
if ("conn_id" %in% ls(args)) {
conn_id <- args$conn_id
}
if ("conn_name" %in% ls(args)) {
conn_name <- args$conn_name
}
}
if (!missing(conn_ref)) {
if (checkmate::test_string(conn_ref)) {
return(conn_ref)
} else if (checkmate::test_int(conn_ref)) {
return(as.integer(conn_ref))
} else {
stop("invalid connection ref provided (must be string(name) or int(id))")
}
} else if (!missing(conn_id)) {
if (!checkmate::test_null(conn_id)) {
checkmate::assert_int(conn_id)
return(as.integer(conn_id))
}
} else if (!missing(conn_name)) {
if (!checkmate::test_null(conn_name)) {
checkmate::assert_string(conn_name)
return(conn_name)
}
}
stop("one of the arguments \"conn_ref\", \"conn_id\" or \"conn_name\" must be provided")
}
#' MODB Connections
#'
#' \code{modb_connect} creates a new database connection and attmepts to open a
#' connection to a database with the details provided.
#'
#' @param username String. Username to use when connecting to the database.
#' @param password String. Password to use when connecting to the database.
#' @param database String. The database to use.
#' @param host String. The hostname or IP address to connect to.
#' Either host & port or socket may be used
#' @param port Integer. The port number to connect on.
#' @param socket String. The socket path to connect to,
#' e.g. /var/run/mysqld/mysqld.sock
#' @param name String. A name for the connection which can be used to identify
#' the connection later on. Alternatively the id returned
#' can be used.
#' @return The id of the connection
#' @export
modb_connect <- function(username, password, database,
host = "localhost", port = 3306,
socket = NULL, conn_name = NULL) {
checkmate::assert_string(username)
checkmate::assert_string(password)
checkmate::assert_string(database)
if (checkmate::test_null(socket)) {
checkmate::assert_string(host)
checkmate::assert_int(port, lower = 1, upper = 65535)
res <- .Call(
c_modb_connectToHost,
conn_name,
host,
as.integer(port),
username,
password,
database
)
} else {
res <- .Call(
c_modb_connectToSocket, conn_name, socket, username, password, database
)
}
if (res < 0) {
stop("failed to create connection")
}
return(res)
}
#' MODB Connections
#'
#' modb_disconnect closes a database connection and cleans up the instance
#'
#' @param ... conn_id, conn_name or conn_ref required. See \link{modb_conn_ref}
#' @export
modb_disconnect <- function(...) {
res <- .Call(c_modb_disconnect, modb_conn_ref(args = list(...)))
return(invisible(res))
}
#' MODB Connections
#'
#' Fetches information on a connection such as the last query, insert id and
#' number of queries run
#'
#' @param ... conn_id, conn_name or conn_ref required. See \link{modb_conn_ref}
#' @return The details of the connection as a list.
#' @export
modb_connectionInfo <- function(stop_on_error = TRUE, ...) {
conn_ref <- modb_conn_ref(args = list(...))
utils::str(conn_ref)
res <- .Call(c_modb_connectionInfo, conn_ref)
if (length(res) == sum(is.na(res))) {
if (stop_on_error) {
stop("invalid connection reference")
} else {
warning("invalid connection reference")
return(NA)
}
}
return(res)
}
#' @describeIn modb_connectionInfo Determines if a connection with the name or
#' id provided exists
#' @param ... conn_id, conn_name or conn_ref required. See \link{modb_conn_ref}
#' @return TRUE if the connection exists, FALSE if not.
#' @export
modb_connectionExists <- function(...) {
res <- .Call(c_modb_connectionInfo, modb_conn_ref(args = list(...)))
return(!(length(res) == sum(is.na(res))))
}
#' @describeIn modb_connectionInfo Finds a connection id via the name
#' @param conn_name String. The name provided when creating a connection.
#' @return The ID of the connection.
#' @export
modb_connectionId <- function(conn_name, stop_on_error = TRUE) {
info <- modb_connectionInfo(stop_on_error, conn_ref = conn_name)
if (length(info) == sum(is.na(info))) {
return(NA)
}
return(info$id)
}
#' @describeIn modb_connectionInfo Finds a connection name via the id
#' @param conn_id Integer. The id of the connection, returned by modb_connect.
#' @return The name of the connection.
#' @export
modb_connectionName <- function(conn_id, stop_on_error = TRUE) {
info <- modb_connectionInfo(stop_on_error, conn_ref = conn_id)
if (length(info) == sum(is.na(info))) {
return(NA)
}
return(info$name)
}
# Database management ----------------------------------------------------------
#' @export
modb_exists <- function(modb_name, ...) {
conn_ref <- modb_conn_ref(args = list(...))
res <- .Call(c_modb_exists, conn_ref, modb_name)
return(res)
}
#' @export
modb_create <- function(modb_name, extended_meta = NULL, ...) {
checkmate::assert_string(modb_name)
# If a dataframe: data.frame(name = c(1, 2, 3), type = c('a', 'b', 'c'), nullable = c(1,0,1), stringsAsFactors=F)
# extended_meta %>% rowwise() %>% transmute(r = list(c("A" = A, "B" = B, "C" = C))) %>% first()
if (!checkmate::test_null(extended_meta)) {
for (idx in 1:length(extended_meta)) {
meta_col <- extended_meta[[idx]]
if (!("name" %in% names(meta_col))) {
stop("missing name in extended_meta definition")
} else {
checkmate::assert_string(meta_col$name)
}
if (!("type" %in% names(meta_col))) {
stop("missing type in extended_meta definition")
} else {
type <- meta_col$type
checkmate::assert_int(type, lower = 0, upper = TYPE_TIMESTAMP)
checkmate::assert_true(type == 1 || type %% 2 == 0)
meta_col$type <- as.integer(type)
}
if (!("nullable" %in% names(meta_col))) {
meta_col$nullable = FALSE
} else {
checkmate::assert_logical(meta_col$nullable)
}
extended_meta[[idx]] <- meta_col
}
}
conn_ref <- modb_conn_ref(args = list(...))
res <- .Call(c_modb_create, conn_ref, modb_name, extended_meta)
return(res)
}
#' @export
modb_destroy <- function(modb_name, ...) {
conn_ref <- modb_conn_ref(args = list(...))
res <- .Call(c_modb_destroy, conn_ref, modb_name)
return(res)
}

20
R/RMODB.R Normal file
View File

@@ -0,0 +1,20 @@
#' RMODB: Metadata-Object pair database
#'
#' RMODB implements a metadata-object database for storing R objects in
#' MySQL. R objects are serialized into a binary memory buffer which is then
#' written into a MySQL database along with metadata such as creation
#' timestamp, update timestamp, owner, title etc. The metadata fields are
#' customisable on creation of the database. the metadata fields are cleartext
#' and queryable allowing objects to be found and returned. On returning an
#' object, it is de-serialised using the reverseof the serialize to binary
#' memory process.
#'
#' @docType package
#' @name rmodb
#' @useDynLib rmodb, .registration = TRUE, .fixes = "c_"
NULL
#> NULL
.onUnload <- function(nspath) {
# clear all connections
}

2
cleanup Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
rm -f src/Makevars configure.log

81
configure vendored Executable file
View File

@@ -0,0 +1,81 @@
#!/usr/bin/env bash
# Anticonf script by Jeroen Ooms (2020)
# The script will try 'mariadb_config' and 'mysql_config' to find required
# cflags and ldflags. Make sure this executable is in PATH when installing
# the package. Alternatively, you can set INCLUDE_DIR and LIB_DIR manually:
# R CMD INSTALL --configure-vars='INCLUDE_DIR=/.../include LIB_DIR=/.../lib'
# Library settings
PKG_DEB_NAME="libmariadbclient-dev | libmariadb-client-lgpl-dev"
PKG_RPM_NAME="mariadb-connector-c-devel | mariadb-devel | mysql-devel"
PKG_CSW_NAME="mysql56_dev"
PKG_BREW_NAME="mariadb-connector-c"
PKG_TEST_HEADER="<mysql.h>"
PKG_LIBS="-lmysqlclient"
# Use mysql_config (on Solaris /opt/csw/bin must be in PATH)
if [ $(command -v mariadb_config) ]; then
PKGCONFIG_CFLAGS=$(mariadb_config --cflags)
PKGCONFIG_LIBS=$(mariadb_config --libs)
elif [ $(command -v mysql_config) ]; then
PKGCONFIG_CFLAGS=$(mysql_config --cflags)
PKGCONFIG_LIBS=$(mysql_config --libs)
fi
# Note that cflags may be empty in case of success
if [ "$INCLUDE_DIR" ] || [ "$LIB_DIR" ]; then
echo "Found INCLUDE_DIR and/or LIB_DIR!"
PKG_CFLAGS="-I$INCLUDE_DIR $PKG_CFLAGS"
PKG_LIBS="-L$LIB_DIR $PKG_LIBS"
elif [ "$PKGCONFIG_CFLAGS" ] || [ "$PKGCONFIG_LIBS" ]; then
echo "Found mysql_config cflags and libs!"
PKG_CFLAGS=${PKGCONFIG_CFLAGS}
PKG_LIBS=${PKGCONFIG_LIBS}
# Workaround for homebrew linkin bug
if [[ "$OSTYPE" == "darwin"* ]]; then
PKG_LIBS="-L/usr/local/opt/openssl/lib $PKG_LIBS"
fi
elif [[ "$OSTYPE" == "darwin"* ]]; then
if [ $(command -v brew) ]; then
BREWDIR=$(brew --prefix)
else
curl -sfL "https://jeroen.github.io/autobrew/$PKG_BREW_NAME" > autobrew
source autobrew
fi
fi
# Find compiler
CC=$(${R_HOME}/bin/R CMD config CC)
CFLAGS=$(${R_HOME}/bin/R CMD config CFLAGS)
CPPFLAGS=$(${R_HOME}/bin/R CMD config CPPFLAGS)
# For debugging
echo "Using PKG_CFLAGS=$PKG_CFLAGS"
echo "Using PKG_LIBS=$PKG_LIBS"
# Test configuration
echo "#include $PKG_TEST_HEADER" | ${CC} ${CPPFLAGS} ${PKG_CFLAGS} ${CFLAGS} -E -xc - >/dev/null 2> configure.log
# Customize the error
if [ $? -ne 0 ]; then
echo "-----------------------------[ ANTICONF ]-----------------------------"
echo "Configure could not find suitable mysql/mariadb client library. Try installing:"
echo " * deb: $PKG_DEB_NAME (Debian, Ubuntu)"
echo " * rpm: $PKG_RPM_NAME (Fedora, CentOS, RHEL)"
echo " * csw: $PKG_CSW_NAME (Solaris)"
echo " * brew: $PKG_BREW_NAME (OSX)"
echo "If you already have a mysql client library installed, verify that either"
echo "mariadb_config or mysql_config is on your PATH. If these are unavailable"
echo "you can also set INCLUDE_DIR and LIB_DIR manually via:"
echo "R CMD INSTALL --configure-vars='INCLUDE_DIR=... LIB_DIR=...'"
echo "--------------------------[ ERROR MESSAGE ]----------------------------"
cat configure.log
echo "-----------------------------------------------------------------------"
exit 1
fi
# Write to Makevars
sed -e "s|@cflags@|$PKG_CFLAGS|" -e "s|@libs@|$PKG_LIBS|" src/Makevars.in > src/Makevars
# Success
exit 0

26
man/modb_conn_ref.Rd Normal file
View File

@@ -0,0 +1,26 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/Manage.R
\name{modb_conn_ref}
\alias{modb_conn_ref}
\title{MODB Connection References}
\usage{
modb_conn_ref(conn_id, conn_name, conn_ref, args = NULL)
}
\arguments{
\item{conn_id}{Integer. The ID of the connection returned by modb_connect}
\item{conn_name}{String. The name provided when creating a connection.}
\item{conn_ref}{Expected to be either a conn_id or a conn_name}
\item{args}{list(...) Arguments passed to a calling function in the va scope.}
}
\description{
\code{modb_conn_ref} checks for and/or validates parameters that may be a
connection reference. Connections in MODB may be named on creation or must
be referenced by the unique ID that is returned by the
\link{\code{modb_connect}} method.
}
\seealso{
\link{\code{modb_connect}}
}

42
man/modb_connect.Rd Normal file
View File

@@ -0,0 +1,42 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/Manage.R
\name{modb_connect}
\alias{modb_connect}
\title{MODB Connections}
\usage{
modb_connect(
username,
password,
database,
host = "localhost",
port = 3306,
socket = NULL,
conn_name = NULL
)
}
\arguments{
\item{username}{String. Username to use when connecting to the database.}
\item{password}{String. Password to use when connecting to the database.}
\item{database}{String. The database to use.}
\item{host}{String. The hostname or IP address to connect to.
Either host & port or socket may be used}
\item{port}{Integer. The port number to connect on.}
\item{socket}{String. The socket path to connect to,
e.g. /var/run/mysqld/mysqld.sock}
\item{name}{String. A name for the connection which can be used to identify
the connection later on. Alternatively the id returned
can be used.}
}
\value{
The id of the connection
}
\description{
\code{modb_connect} creates a new database connection and attmepts to open a
connection to a database with the details provided.
}

View File

@@ -0,0 +1,47 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/Manage.R
\name{modb_connectionInfo}
\alias{modb_connectionInfo}
\alias{modb_connectionExists}
\alias{modb_connectionId}
\alias{modb_connectionName}
\title{MODB Connections}
\usage{
modb_connectionInfo(stop_on_error = TRUE, ...)
modb_connectionExists(...)
modb_connectionId(conn_name, stop_on_error = TRUE)
modb_connectionName(conn_id, stop_on_error = TRUE)
}
\arguments{
\item{...}{conn_id, conn_name or conn_ref required. See \link{modb_conn_ref}}
\item{conn_name}{String. The name provided when creating a connection.}
\item{conn_id}{Integer. The id of the connection, returned by modb_connect.}
}
\value{
The details of the connection as a list.
TRUE if the connection exists, FALSE if not.
The ID of the connection.
The name of the connection.
}
\description{
Fetches information on a connection such as the last query, insert id and
number of queries run
}
\section{Functions}{
\itemize{
\item \code{modb_connectionExists}: Determines if a connection with the name or
id provided exists
\item \code{modb_connectionId}: Finds a connection id via the name
\item \code{modb_connectionName}: Finds a connection name via the id
}}

14
man/modb_disconnect.Rd Normal file
View File

@@ -0,0 +1,14 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/Manage.R
\name{modb_disconnect}
\alias{modb_disconnect}
\title{MODB Connections}
\usage{
modb_disconnect(...)
}
\arguments{
\item{...}{conn_id, conn_name or conn_ref required. See \link{modb_conn_ref}}
}
\description{
modb_disconnect closes a database connection and cleans up the instance
}

16
man/rmodb.Rd Normal file
View File

@@ -0,0 +1,16 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/RMODB.R
\docType{package}
\name{rmodb}
\alias{rmodb}
\title{RMODB: Metadata-Object pair database}
\description{
RMODB implements a metadata-object database for storing R objects in
MySQL. R objects are serialized into a binary memory buffer which is then
written into a MySQL database along with metadata such as creation
timestamp, update timestamp, owner, title etc. The metadata fields are
customisable on creation of the database. the metadata fields are cleartext
and queryable allowing objects to be found and returned. On returning an
object, it is de-serialised using the reverseof the serialize to binary
memory process.
}

7
src/Makevars.in Normal file
View File

@@ -0,0 +1,7 @@
PKG_CPPFLAGS=@cflags@
PKG_LIBS=@libs@
all: clean
clean:
rm -f $(SHLIB) $(OBJECTS)

22
src/R_list_item.c Normal file
View File

@@ -0,0 +1,22 @@
#include <string.h>
#include "R_list_item.h"
SEXP R_listItem(SEXP list, const char *name)
{
SEXP r_names = Rf_getAttrib(list, R_NamesSymbol);
SEXP r_name;
for (int i = 0; i < Rf_length(list); i++) {
if (TYPEOF(r_names) == STRSXP) {
r_name = STRING_ELT(r_names, i);
} else {
r_name = VECTOR_ELT(r_names, i);
}
if (strcmp(Rf_translateCharUTF8(r_name), name) == 0) {
return VECTOR_ELT(list, i);
}
}
return R_NilValue;
}

8
src/R_list_item.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef H__R_LIST_ITEM__
#define H__R_LIST_ITEM__
#include <Rinternals.h>
SEXP R_listItem(SEXP list, const char *name);
#endif // H__R_LIST_ITEM__

240
src/R_modb_manage.c Normal file
View File

@@ -0,0 +1,240 @@
#include <stdio.h>
#include <string.h>
#include "R_modb_manage.h"
#include "R_list_item.h"
#include "db_connection.h"
#include "modb.h"
struct stored_conn_t *getConn(SEXP r_conn_ref)
{
struct stored_conn_t *sconn = 0;
if (Rf_isString(r_conn_ref)) {
sconn = connectionByName(Rf_translateCharUTF8(STRING_ELT(r_conn_ref, 0)));
} else if (Rf_isInteger(r_conn_ref)) {
sconn = connectionById(Rf_asInteger(r_conn_ref));
} else {
Rf_error("Neither name or id provided");
}
return sconn;
}
SEXP modb_connectionInfo(SEXP r_conn_ref)
{
struct stored_conn_t *sconn;
SEXP res, conn_name, last_qry, names;
if ((sconn = getConn(r_conn_ref)) == 0) {
return R_NilValue;
}
conn_name = PROTECT(Rf_allocVector(STRSXP, 1));
SET_STRING_ELT(conn_name, 0, PROTECT(Rf_mkChar(sconn->name)));
last_qry = PROTECT(Rf_allocVector(STRSXP, 1));
if (sconn->last_qry != 0) {
SET_STRING_ELT(last_qry, 0, PROTECT(Rf_mkChar(sconn->last_qry)));
} else {
SET_STRING_ELT(last_qry, 0, PROTECT(NA_STRING));
}
res = PROTECT(Rf_allocVector(VECSXP, 4));
SET_VECTOR_ELT(res, 0, PROTECT(Rf_ScalarInteger(sconn->conn_id)));
SET_VECTOR_ELT(res, 1, conn_name);
SET_VECTOR_ELT(res, 2, last_qry);
SET_VECTOR_ELT(res, 3, PROTECT(Rf_ScalarInteger((int)sconn->num_queries)));
names = PROTECT(Rf_allocVector(STRSXP, 4));
SET_STRING_ELT(names, 0, PROTECT(Rf_mkChar("id")));
SET_STRING_ELT(names, 1, PROTECT(Rf_mkChar("name")));
SET_STRING_ELT(names, 2, PROTECT(Rf_mkChar("num_queries")));
SET_STRING_ELT(names, 3, PROTECT(Rf_mkChar("last_query")));
Rf_setAttrib(res, R_NamesSymbol, names);
UNPROTECT(12);
return res;
}
SEXP modb_connectToHost(SEXP r_name, SEXP r_host, SEXP r_port,
SEXP r_username, SEXP r_password, SEXP r_database)
{
struct stored_conn_t *sconn;
const char *name = 0, *host, *user, *pass, *db;
unsigned int port;
if (!Rf_isNull(r_name)) {
name = Rf_translateCharUTF8(STRING_ELT(r_name, 0));
}
host = Rf_translateCharUTF8(STRING_ELT(r_host, 0));
port = (unsigned int)Rf_asInteger(r_port);
user = Rf_translateCharUTF8(STRING_ELT(r_username, 0));
pass = Rf_translateCharUTF8(STRING_ELT(r_password, 0));
db = Rf_translateCharUTF8(STRING_ELT(r_database, 0));
sconn = createStoredConnection(name);
if (sconn == 0) {
return R_NilValue;
}
if (connectToHost(sconn, host, port, user, pass, db) < 0) {
destroyStoredConnection(sconn);
return R_NilValue;
}
return Rf_ScalarInteger(sconn->conn_id);
}
SEXP modb_connectToSocket(SEXP r_name, SEXP r_socket,
SEXP r_username, SEXP r_password, SEXP r_database)
{
struct stored_conn_t *sconn;
const char *name = 0, *sock, *user, *pass, *db;
if (!Rf_isNull(r_name)) {
name = Rf_translateCharUTF8(STRING_ELT(r_name, 0));
}
sock = Rf_translateCharUTF8(STRING_ELT(r_socket, 0));
user = Rf_translateCharUTF8(STRING_ELT(r_username, 0));
pass = Rf_translateCharUTF8(STRING_ELT(r_password, 0));
db = Rf_translateCharUTF8(STRING_ELT(r_database, 0));
sconn = createStoredConnection(name);
if (sconn == 0) {
return R_NilValue;
}
if (connectToSocket(sconn, sock, user, pass, db) < 0) {
destroyStoredConnection(sconn);
return R_NilValue;
}
return Rf_ScalarInteger(sconn->conn_id);
}
SEXP modb_disconnect(SEXP r_conn_ref)
{
struct stored_conn_t *sconn;
int conn_id;
if ((sconn = getConn(r_conn_ref)) == 0) {
return R_NilValue;
}
conn_id = sconn->conn_id;
closeConnection(sconn);
destroyStoredConnection(sconn);
return Rf_ScalarInteger(conn_id);
}
SEXP modb_exists(SEXP r_conn_ref, SEXP r_name)
{
struct stored_conn_t *sconn;
struct modb_t modb;
if ((sconn = getConn(r_conn_ref)) == 0) {
Rf_error("invalid connection reference\n");
}
modb.name = Rf_translateCharUTF8(STRING_ELT(r_name, 0));
modb.name_len = strlen(modb.name);
return Rf_ScalarLogical(modbExists(sconn, &modb) > 0);
}
SEXP modb_create(SEXP r_conn_ref, SEXP r_name, SEXP r_extra_meta)
{
struct stored_conn_t *sconn;
struct modb_t modb;
struct column_data_t **cols;
size_t n_cols;
SEXP r_col, r_col_name, r_col_type, r_col_null;
if ((sconn = getConn(r_conn_ref)) == 0) {
Rf_error("invalid connection reference\n");
}
modb.name = Rf_translateCharUTF8(STRING_ELT(r_name, 0));
modb.name_len = strlen(modb.name);
if (modbExists(sconn, &modb) == 0) {
Rf_warning("an MODB instance named '%s' already exists\n", modb.name);
return Rf_ScalarLogical(FALSE);
}
if (modbCreate(sconn, &modb) != 0) {
modb_destroy(r_conn_ref, r_name);
Rf_warning("failed to create MODB instance");
return Rf_ScalarLogical(FALSE);
}
if (modbAccountingCreate(sconn, &modb) != 0) {
modb_destroy(r_conn_ref, r_name);
Rf_warning("failed to create MODB instance");
return Rf_ScalarLogical(FALSE);
}
if (!Rf_isNull(r_extra_meta)) {
n_cols = (size_t)Rf_length(r_extra_meta);
cols = (struct column_data_t **)calloc(sizeof(struct column_data_t *), n_cols);
if (cols == 0) {
modb_destroy(r_conn_ref, r_name);
Rf_warning("failed to create MODB instance");
return Rf_ScalarLogical(FALSE);
}
for (size_t i = 0; i < n_cols; i++) {
r_col = VECTOR_ELT(r_extra_meta, (int)i);
r_col_name = STRING_ELT(R_listItem(r_col, "name"), 0);
r_col_type = R_listItem(r_col, "type");
r_col_null = R_listItem(r_col, "nullable");
*(cols + i) = initEmptyColumn(
(e_column_type)(unsigned int)Rf_asInteger(r_col_type),
Rf_asLogical(r_col_null),
Rf_translateCharUTF8(r_col_name),
0, 0, 0);
if (*(cols + i) == 0) {
freeColumns(cols, i);
modb_destroy(r_conn_ref, r_name);
Rf_warning("failed to create MODB instance");
return Rf_ScalarLogical(FALSE);
}
}
if (modbMetaExtCreate(sconn, &modb, cols, n_cols) != 0) {
freeColumns(cols, n_cols);
modb_destroy(r_conn_ref, r_name);
Rf_warning("failed to create MODB instance");
return Rf_ScalarLogical(FALSE);
}
freeColumns(cols, n_cols);
}
return Rf_ScalarLogical(TRUE);
}
SEXP modb_destroy(SEXP r_conn_ref, SEXP r_name)
{
struct stored_conn_t *sconn;
struct modb_t modb;
if ((sconn = getConn(r_conn_ref)) == 0) {
Rf_error("invalid connection reference\n");
}
modb.name = Rf_translateCharUTF8(STRING_ELT(r_name, 0));
modb.name_len = strlen(modb.name);
if (modbMetaExtExists(sconn, &modb) > 0) {
modbMetaExtDestroy(sconn, &modb);
}
modbAccountingDestroy(sconn, &modb);
modbDestroy(sconn, &modb);
return Rf_ScalarLogical(TRUE);
}

20
src/R_modb_manage.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef H__R_MODB_MANAGE__
#define H__R_MODB_MANAGE__
#include <Rinternals.h>
SEXP modb_connectionInfo(SEXP r_conn_ref);
SEXP modb_connectToHost(SEXP r_name, SEXP r_host, SEXP r_port,
SEXP r_username, SEXP r_password, SEXP r_database);
SEXP modb_connectToSocket(SEXP r_name, SEXP r_socket,
SEXP r_username, SEXP r_password, SEXP r_database);
SEXP modb_disconnect(SEXP r_conn_ref);
SEXP modb_exists(SEXP r_conn_ref, SEXP r_name);
SEXP modb_create(SEXP r_conn_ref, SEXP r_name, SEXP r_extra_meta);
SEXP modb_destroy(SEXP r_conn_ref, SEXP r_name);
#endif // H__R_MODB_MANAGE__

22
src/initR.c Normal file
View File

@@ -0,0 +1,22 @@
#include <Rinternals.h>
#include "R_modb_manage.h"
static const R_CallMethodDef callMethods[] = {
{"modb_connectionInfo", (DL_FUNC) &modb_connectionInfo, 1},
{"modb_connectToHost", (DL_FUNC) &modb_connectToHost, 6},
{"modb_connectToSocket", (DL_FUNC) &modb_connectToSocket, 5},
{"modb_disconnect", (DL_FUNC) &modb_disconnect, 1},
{"modb_exists", (DL_FUNC) &modb_exists, 2},
{"modb_create", (DL_FUNC) &modb_create, 3},
{"modb_destroy", (DL_FUNC) &modb_destroy, 2},
{NULL, NULL, 0}
};
void R_init_rmodb(DllInfo *dll)
{
R_registerRoutines(dll, NULL, callMethods, NULL, NULL);
R_useDynamicSymbols(dll, FALSE);
R_forceSymbols(dll, TRUE);
}

View File

@@ -7,28 +7,3 @@
#include "strext.h" #include "strext.h"
struct modb_t *modbAlloc(const char *name, size_t name_len)
{
struct modb_t *modb = (struct modb_t *)malloc(sizeof(struct modb_t));
if (modb == 0) {
fprintf(stderr, "[%d]malloc: (%d) %s\n", __LINE__, errno, strerror(errno));
return 0;
}
if (strmemcpy(name, name_len, &modb->name, &modb->name_len) != 0) {
modbRelease(&modb);
return 0;
}
return modb;
}
void modbRelease(struct modb_t **modb)
{
if ((*modb)->name != 0) {
free((*modb)->name);
(*modb)->name = 0;
}
free(*modb);
*modb = 0;
}

View File

@@ -5,11 +5,9 @@
#include <stdint.h> #include <stdint.h>
struct modb_t { struct modb_t {
char *name; const char *name;
size_t name_len; size_t name_len;
}; };
struct modb_t *modbAlloc(const char *name, size_t name_len);
void modbRelease(struct modb_t **modb);
#endif // H__MODB_TYPES__ #endif // H__MODB_TYPES__