This is an automated email from the ASF dual-hosted git repository.

paleolimbot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git


The following commit(s) were added to refs/heads/main by this push:
     new c75e983  feat(r): Add driver logging utility (#694)
c75e983 is described below

commit c75e983f7235ecb7ebd4af20aabb3c558224864a
Author: Dewey Dunnington <[email protected]>
AuthorDate: Fri May 19 08:31:27 2023 -0400

    feat(r): Add driver logging utility (#694)
    
    Initially I was hoping this would be able to wrap an arbitrary driver
    and log the call + the return code, but that was too hard and I only
    need a tiny subset of that behaviour to test #693. After this PR you can
    verify that cleanup calls actually take place:
    
    ``` r
    library(adbcdrivermanager)
    
    db <- adbc_database_init(adbc_driver_log(), key = "value")
    #> LogDriverInitFunc()
    #> LogDriverInitFunc()
    #> LogDatabaseNew()
    #> LogDatabaseSetOption()
    #> LogDatabaseInit()
    con <- adbc_connection_init(db, key = "value")
    #> LogConnectionNew()
    #> LogConnectionInit()
    #> LogConnectionSetOption()
    stmt <- adbc_statement_init(con, key = "value")
    #> LogStatementNew()
    #> LogStatementSetOption()
    try(adbc_statement_execute_query(stmt))
    #> LogStatementExecuteQuery()
    #> Error in stop_for_error(result$status, error) :
    #>   ADBC_STATUS_NOT_IMPLEMENTED (2)
    adbc_statement_release(stmt)
    #> LogStatementRelease()
    adbc_connection_release(con)
    #> LogConnectionRelease()
    adbc_database_release(db)
    #> LogDatabaseRelease()
    #> LogDriverRelease()
    ```
    
    <sup>Created on 2023-05-18 with [reprex
    v2.0.2](https://reprex.tidyverse.org)</sup>
---
 .pre-commit-config.yaml                            |   2 +
 dev/release/rat_exclude_files.txt                  |   1 +
 r/adbcdrivermanager/NAMESPACE                      |   4 +
 r/adbcdrivermanager/R/driver_log.R                 |  74 +++++
 r/adbcdrivermanager/man/adbc_driver_log.Rd         |  27 ++
 r/adbcdrivermanager/src/driver_log.c               | 343 +++++++++++++++++++++
 r/adbcdrivermanager/src/init.c                     |   4 +-
 r/adbcdrivermanager/src/radbc.cc                   |  17 +
 .../tests/testthat/_snaps/driver_log.md            |  41 +++
 .../tests/testthat/test-driver_log.R               |  28 ++
 10 files changed, 540 insertions(+), 1 deletion(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index aacf930..8353583 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -28,7 +28,9 @@ repos:
     - id: check-yaml
       exclude: ci/conda/meta.yaml
     - id: end-of-file-fixer
+      exclude: r/adbcdrivermanager/tests/testthat/_snaps/driver_log.md
     - id: trailing-whitespace
+      exclude: r/adbcdrivermanager/tests/testthat/_snaps/driver_log.md
   - repo: https://github.com/pocc/pre-commit-hooks
     rev: v1.3.5
     hooks:
diff --git a/dev/release/rat_exclude_files.txt 
b/dev/release/rat_exclude_files.txt
index d13c65c..f8e26fb 100644
--- a/dev/release/rat_exclude_files.txt
+++ b/dev/release/rat_exclude_files.txt
@@ -16,6 +16,7 @@ rat.txt
 r/adbcdrivermanager/DESCRIPTION
 r/adbcdrivermanager/NAMESPACE
 r/adbcdrivermanager/.Rbuildignore
+r/adbcdrivermanager/tests/testthat/_snaps/*
 r/adbcsqlite/DESCRIPTION
 r/adbcsqlite/NAMESPACE
 r/adbcsqlite/.Rbuildignore
diff --git a/r/adbcdrivermanager/NAMESPACE b/r/adbcdrivermanager/NAMESPACE
index cf653a2..c13dd38 100644
--- a/r/adbcdrivermanager/NAMESPACE
+++ b/r/adbcdrivermanager/NAMESPACE
@@ -6,10 +6,13 @@ S3method("$<-",adbc_xptr)
 S3method("[[",adbc_error)
 S3method("[[",adbc_xptr)
 S3method("[[<-",adbc_xptr)
+S3method(adbc_connection_init,adbc_database_log)
 S3method(adbc_connection_init,adbc_database_monkey)
 S3method(adbc_connection_init,default)
+S3method(adbc_database_init,adbc_driver_log)
 S3method(adbc_database_init,adbc_driver_monkey)
 S3method(adbc_database_init,default)
+S3method(adbc_statement_init,adbc_connection_log)
 S3method(adbc_statement_init,adbc_connection_monkey)
 S3method(adbc_statement_init,default)
 S3method(length,adbc_error)
@@ -38,6 +41,7 @@ export(adbc_database_init_default)
 export(adbc_database_release)
 export(adbc_database_set_options)
 export(adbc_driver)
+export(adbc_driver_log)
 export(adbc_driver_monkey)
 export(adbc_driver_void)
 export(adbc_statement_bind)
diff --git a/r/adbcdrivermanager/R/driver_log.R 
b/r/adbcdrivermanager/R/driver_log.R
new file mode 100644
index 0000000..b7b70f0
--- /dev/null
+++ b/r/adbcdrivermanager/R/driver_log.R
@@ -0,0 +1,74 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+#' Log calls to another driver
+#'
+#' Useful for debugging or ensuring that certain calls occur during
+#' initialization and/or cleanup. The current logging output should not
+#' be considered stable and may change in future releases.
+#'
+#'
+#' @return An object of class 'adbc_driver_log'
+#' @export
+#'
+#' @examples
+#' drv <- adbc_driver_log()
+#' db <- adbc_database_init(drv, key = "value")
+#' con <- adbc_connection_init(db, key = "value")
+#' stmt <- adbc_statement_init(con, key = "value")
+#' try(adbc_statement_execute_query(stmt))
+#' adbc_statement_release(stmt)
+#' adbc_connection_release(con)
+#' adbc_database_release(db)
+#'
+adbc_driver_log <- function() {
+  if (is.null(internal_driver_env$log)) {
+    internal_driver_env$log <- adbc_driver(
+      .Call(RAdbcLogDriverInitFunc),
+      subclass = "adbc_driver_log"
+    )
+  }
+
+  internal_driver_env$log
+}
+
+#' @export
+adbc_database_init.adbc_driver_log <- function(driver, ...) {
+  adbc_database_init_default(
+    driver,
+    options = list(...),
+    subclass = "adbc_database_log"
+  )
+}
+
+#' @export
+adbc_connection_init.adbc_database_log <- function(database, ...) {
+  adbc_connection_init_default(
+    database,
+    options = list(...),
+    subclass = "adbc_connection_log"
+  )
+}
+
+#' @export
+adbc_statement_init.adbc_connection_log <- function(connection, ...) {
+  adbc_statement_init_default(
+    connection,
+    options = list(...),
+    subclass = "adbc_statement_log"
+  )
+}
diff --git a/r/adbcdrivermanager/man/adbc_driver_log.Rd 
b/r/adbcdrivermanager/man/adbc_driver_log.Rd
new file mode 100644
index 0000000..fbd3c76
--- /dev/null
+++ b/r/adbcdrivermanager/man/adbc_driver_log.Rd
@@ -0,0 +1,27 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/driver_log.R
+\name{adbc_driver_log}
+\alias{adbc_driver_log}
+\title{Log calls to another driver}
+\usage{
+adbc_driver_log()
+}
+\value{
+An object of class 'adbc_driver_log'
+}
+\description{
+Useful for debugging or ensuring that certain calls occur during
+initialization and/or cleanup. The current logging output should not
+be considered stable and may change in future releases.
+}
+\examples{
+drv <- adbc_driver_log()
+db <- adbc_database_init(drv, key = "value")
+con <- adbc_connection_init(db, key = "value")
+stmt <- adbc_statement_init(con, key = "value")
+try(adbc_statement_execute_query(stmt))
+adbc_statement_release(stmt)
+adbc_connection_release(con)
+adbc_database_release(db)
+
+}
diff --git a/r/adbcdrivermanager/src/driver_log.c 
b/r/adbcdrivermanager/src/driver_log.c
new file mode 100644
index 0000000..5a0520d
--- /dev/null
+++ b/r/adbcdrivermanager/src/driver_log.c
@@ -0,0 +1,343 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+
+#define R_NO_REMAP
+#include <R.h>
+#include <Rinternals.h>
+
+#include <string.h>
+
+#include <adbc.h>
+
+struct LogDriverPrivate {
+  char token[1024];
+};
+
+struct LogDatabasePrivate {
+  char token[1024];
+};
+
+struct LogConnectionPrivate {
+  char token[1024];
+};
+
+struct LogStatementPrivate {
+  char token[1024];
+};
+
+static void ResetError(struct AdbcError* error) {
+  memset(error, 0, sizeof(struct AdbcError));
+}
+
+static void SetErrorConst(struct AdbcError* error, const char* value) {
+  if (error == NULL) {
+    return;
+  }
+
+  ResetError(error);
+  error->message = (char*)value;
+}
+
+static AdbcStatusCode LogDriverRelease(struct AdbcDriver* driver,
+                                       struct AdbcError* error) {
+  Rprintf("LogDriverRelease()\n");
+  if (driver->private_data == NULL) {
+    return ADBC_STATUS_OK;
+  }
+
+  free(driver->private_data);
+  driver->private_data = NULL;
+  return ADBC_STATUS_OK;
+}
+
+static AdbcStatusCode LogDatabaseNew(struct AdbcDatabase* database,
+                                     struct AdbcError* error) {
+  Rprintf("LogDatabaseNew()\n");
+
+  struct LogDatabasePrivate* database_private =
+      (struct LogDatabasePrivate*)malloc(sizeof(struct LogDatabasePrivate));
+  if (database_private == NULL) {
+    SetErrorConst(error, "failed to allocate LogDatabasePrivate");
+    return ADBC_STATUS_INTERNAL;
+  }
+
+  memset(database_private, 0, sizeof(struct LogDatabasePrivate));
+  database->private_data = database_private;
+  return ADBC_STATUS_OK;
+}
+
+static AdbcStatusCode LogDatabaseInit(struct AdbcDatabase* database,
+                                      struct AdbcError* error) {
+  Rprintf("LogDatabaseInit()\n");
+  return ADBC_STATUS_OK;
+}
+
+static AdbcStatusCode LogDatabaseSetOption(struct AdbcDatabase* database, 
const char* key,
+                                           const char* value, struct 
AdbcError* error) {
+  Rprintf("LogDatabaseSetOption()\n");
+  return ADBC_STATUS_OK;
+}
+
+static AdbcStatusCode LogDatabaseRelease(struct AdbcDatabase* database,
+                                         struct AdbcError* error) {
+  Rprintf("LogDatabaseRelease()\n");
+  if (database->private_data == NULL) {
+    return ADBC_STATUS_OK;
+  }
+
+  free(database->private_data);
+  database->private_data = NULL;
+  return ADBC_STATUS_OK;
+}
+
+static AdbcStatusCode LogConnectionCommit(struct AdbcConnection* connection,
+                                          struct AdbcError* error) {
+  Rprintf("LogConnectionCommit()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+static AdbcStatusCode LogConnectionGetInfo(struct AdbcConnection* connection,
+                                           uint32_t* info_codes, size_t 
info_codes_length,
+                                           struct ArrowArrayStream* stream,
+                                           struct AdbcError* error) {
+  Rprintf("LogConnectionGetInfo()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+static AdbcStatusCode LogConnectionGetObjects(
+    struct AdbcConnection* connection, int depth, const char* catalog,
+    const char* db_schema, const char* table_name, const char** table_types,
+    const char* column_name, struct ArrowArrayStream* stream, struct 
AdbcError* error) {
+  Rprintf("LogConnectionGetObjects()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+static AdbcStatusCode LogConnectionGetTableSchema(
+    struct AdbcConnection* connection, const char* catalog, const char* 
db_schema,
+    const char* table_name, struct ArrowSchema* schema, struct AdbcError* 
error) {
+  Rprintf("LogConnectionGetTableSchema()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+static AdbcStatusCode LogConnectionGetTableTypes(struct AdbcConnection* 
connection,
+                                                 struct ArrowArrayStream* 
stream,
+                                                 struct AdbcError* error) {
+  Rprintf("LogConnectionGetTableTypes()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+static AdbcStatusCode LogConnectionInit(struct AdbcConnection* connection,
+                                        struct AdbcDatabase* database,
+                                        struct AdbcError* error) {
+  Rprintf("LogConnectionInit()\n");
+  return ADBC_STATUS_OK;
+}
+
+static AdbcStatusCode LogConnectionNew(struct AdbcConnection* connection,
+                                       struct AdbcError* error) {
+  Rprintf("LogConnectionNew()\n");
+
+  struct LogConnectionPrivate* connection_private =
+      (struct LogConnectionPrivate*)malloc(sizeof(struct 
LogConnectionPrivate));
+  if (connection_private == NULL) {
+    SetErrorConst(error, "failed to allocate LogConnectionPrivate");
+    return ADBC_STATUS_INTERNAL;
+  }
+
+  memset(connection_private, 0, sizeof(struct LogConnectionPrivate));
+  connection->private_data = connection_private;
+  return ADBC_STATUS_OK;
+}
+
+static AdbcStatusCode LogConnectionReadPartition(struct AdbcConnection* 
connection,
+                                                 const uint8_t* 
serialized_partition,
+                                                 size_t serialized_length,
+                                                 struct ArrowArrayStream* out,
+                                                 struct AdbcError* error) {
+  Rprintf("LogConnectionReadPartition()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+static AdbcStatusCode LogConnectionRelease(struct AdbcConnection* connection,
+                                           struct AdbcError* error) {
+  Rprintf("LogConnectionRelease()\n");
+  if (connection->private_data == NULL) {
+    return ADBC_STATUS_OK;
+  }
+
+  free(connection->private_data);
+  connection->private_data = NULL;
+  return ADBC_STATUS_OK;
+}
+
+static AdbcStatusCode LogConnectionRollback(struct AdbcConnection* connection,
+                                            struct AdbcError* error) {
+  Rprintf("LogConnectionRollback()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+static AdbcStatusCode LogConnectionSetOption(struct AdbcConnection* connection,
+                                             const char* key, const char* 
value,
+                                             struct AdbcError* error) {
+  Rprintf("LogConnectionSetOption()\n");
+  return ADBC_STATUS_OK;
+}
+
+static AdbcStatusCode LogStatementBind(struct AdbcStatement* statement,
+                                       struct ArrowArray* values,
+                                       struct ArrowSchema* schema,
+                                       struct AdbcError* error) {
+  Rprintf("LogStatementBind()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}  // NOLINT(whitespace/indent)
+
+static AdbcStatusCode LogStatementBindStream(struct AdbcStatement* statement,
+                                             struct ArrowArrayStream* stream,
+                                             struct AdbcError* error) {
+  Rprintf("LogStatementBindStream()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+static AdbcStatusCode LogStatementExecutePartitions(struct AdbcStatement* 
statement,
+                                                    struct ArrowSchema* schema,
+                                                    struct AdbcPartitions* 
partitions,
+                                                    int64_t* rows_affected,
+                                                    struct AdbcError* error) {
+  Rprintf("LogStatementExecutePartitions()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}  // NOLINT(whitespace/indent)
+
+static AdbcStatusCode LogStatementExecuteQuery(struct AdbcStatement* statement,
+                                               struct ArrowArrayStream* out,
+                                               int64_t* rows_affected,
+                                               struct AdbcError* error) {
+  Rprintf("LogStatementExecuteQuery()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+static AdbcStatusCode LogStatementGetParameterSchema(struct AdbcStatement* 
statement,
+                                                     struct ArrowSchema* 
schema,
+                                                     struct AdbcError* error) {
+  Rprintf("LogStatementGetParameterSchema()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+static AdbcStatusCode LogStatementNew(struct AdbcConnection* connection,
+                                      struct AdbcStatement* statement,
+                                      struct AdbcError* error) {
+  Rprintf("LogStatementNew()\n");
+  struct LogStatementPrivate* statement_private =
+      (struct LogStatementPrivate*)malloc(sizeof(struct LogStatementPrivate));
+  if (statement_private == NULL) {
+    SetErrorConst(error, "failed to allocate LogStatementPrivate");
+    return ADBC_STATUS_INTERNAL;
+  }
+
+  memset(statement_private, 0, sizeof(struct LogStatementPrivate));
+  statement->private_data = statement_private;
+  return ADBC_STATUS_OK;
+}
+
+static AdbcStatusCode LogStatementPrepare(struct AdbcStatement* statement,
+                                          struct AdbcError* error) {
+  Rprintf("LogStatementPrepare()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+static AdbcStatusCode LogStatementRelease(struct AdbcStatement* statement,
+                                          struct AdbcError* error) {
+  Rprintf("LogStatementRelease()\n");
+  if (statement->private_data == NULL) {
+    return ADBC_STATUS_OK;
+  }
+
+  free(statement->private_data);
+  statement->private_data = NULL;
+  return ADBC_STATUS_OK;
+}
+
+static AdbcStatusCode LogStatementSetOption(struct AdbcStatement* statement,
+                                            const char* key, const char* value,
+                                            struct AdbcError* error) {
+  Rprintf("LogStatementSetOption()\n");
+  return ADBC_STATUS_OK;
+}
+
+static AdbcStatusCode LogStatementSetSqlQuery(struct AdbcStatement* statement,
+                                              const char* query,
+                                              struct AdbcError* error) {
+  Rprintf("LogStatementSetSqlQuery()\n");
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+static AdbcStatusCode LogDriverInitFunc(int version, void* raw_driver,
+                                        struct AdbcError* error) {
+  Rprintf("LogDriverInitFunc()\n");
+  if (version != ADBC_VERSION_1_0_0) return ADBC_STATUS_NOT_IMPLEMENTED;
+  struct AdbcDriver* driver = (struct AdbcDriver*)raw_driver;
+  memset(driver, 0, sizeof(struct AdbcDriver));
+
+  struct LogDriverPrivate* driver_private =
+      (struct LogDriverPrivate*)malloc(sizeof(struct LogDriverPrivate));
+  if (driver_private == NULL) {
+    SetErrorConst(error, "failed to allocate LogDriverPrivate");
+    return ADBC_STATUS_INTERNAL;
+  }
+
+  memset(driver_private, 0, sizeof(struct LogDriverPrivate));
+  driver->private_data = driver_private;
+
+  driver->DatabaseInit = &LogDatabaseInit;
+  driver->DatabaseNew = LogDatabaseNew;
+  driver->DatabaseRelease = LogDatabaseRelease;
+  driver->DatabaseSetOption = LogDatabaseSetOption;
+
+  driver->ConnectionCommit = LogConnectionCommit;
+  driver->ConnectionGetInfo = LogConnectionGetInfo;
+  driver->ConnectionGetObjects = LogConnectionGetObjects;
+  driver->ConnectionGetTableSchema = LogConnectionGetTableSchema;
+  driver->ConnectionGetTableTypes = LogConnectionGetTableTypes;
+  driver->ConnectionInit = LogConnectionInit;
+  driver->ConnectionNew = LogConnectionNew;
+  driver->ConnectionReadPartition = LogConnectionReadPartition;
+  driver->ConnectionRelease = LogConnectionRelease;
+  driver->ConnectionRollback = LogConnectionRollback;
+  driver->ConnectionSetOption = LogConnectionSetOption;
+
+  driver->StatementBind = LogStatementBind;
+  driver->StatementBindStream = LogStatementBindStream;
+  driver->StatementExecutePartitions = LogStatementExecutePartitions;
+  driver->StatementExecuteQuery = LogStatementExecuteQuery;
+  driver->StatementGetParameterSchema = LogStatementGetParameterSchema;
+  driver->StatementNew = LogStatementNew;
+  driver->StatementPrepare = LogStatementPrepare;
+  driver->StatementRelease = LogStatementRelease;
+  driver->StatementSetOption = LogStatementSetOption;
+  driver->StatementSetSqlQuery = LogStatementSetSqlQuery;
+
+  driver->release = LogDriverRelease;
+
+  return ADBC_STATUS_OK;
+}
+
+SEXP RAdbcLogDriverInitFunc() {
+  SEXP xptr =
+      PROTECT(R_MakeExternalPtrFn((DL_FUNC)LogDriverInitFunc, R_NilValue, 
R_NilValue));
+  Rf_setAttrib(xptr, R_ClassSymbol, Rf_mkString("adbc_driver_init_func"));
+  UNPROTECT(1);
+  return xptr;
+}
diff --git a/r/adbcdrivermanager/src/init.c b/r/adbcdrivermanager/src/init.c
index 3ad706c..de2f95f 100644
--- a/r/adbcdrivermanager/src/init.c
+++ b/r/adbcdrivermanager/src/init.c
@@ -20,6 +20,7 @@
 #include <Rinternals.h>
 
 /* generated by tools/make-callentries.R */
+SEXP RAdbcLogDriverInitFunc();
 SEXP RAdbcMonkeyDriverInitFunc();
 SEXP RAdbcVoidDriverInitFunc();
 SEXP RAdbcAllocateError(SEXP shelter_sexp);
@@ -27,7 +28,7 @@ SEXP RAdbcErrorProxy(SEXP error_xptr);
 SEXP RAdbcStatusCodeMessage(SEXP status_sexp);
 SEXP RAdbcLoadDriver(SEXP driver_name_sexp, SEXP entrypoint_sexp);
 SEXP RAdbcLoadDriverFromInitFunc(SEXP driver_init_func_xptr);
-SEXP RAdbcDatabaseNew(SEXP driver_xptr);
+SEXP RAdbcDatabaseNew(SEXP driver_init_func_xptr);
 SEXP RAdbcDatabaseSetOption(SEXP database_xptr, SEXP key_sexp, SEXP value_sexp,
                             SEXP error_xptr);
 SEXP RAdbcDatabaseInit(SEXP database_xptr, SEXP error_xptr);
@@ -71,6 +72,7 @@ SEXP RAdbcStatementExecutePartitions(SEXP statement_xptr, 
SEXP out_schema_xptr,
 SEXP RAdbcXptrEnv(SEXP xptr);
 
 static const R_CallMethodDef CallEntries[] = {
+    {"RAdbcLogDriverInitFunc", (DL_FUNC)&RAdbcLogDriverInitFunc, 0},
     {"RAdbcMonkeyDriverInitFunc", (DL_FUNC)&RAdbcMonkeyDriverInitFunc, 0},
     {"RAdbcVoidDriverInitFunc", (DL_FUNC)&RAdbcVoidDriverInitFunc, 0},
     {"RAdbcAllocateError", (DL_FUNC)&RAdbcAllocateError, 1},
diff --git a/r/adbcdrivermanager/src/radbc.cc b/r/adbcdrivermanager/src/radbc.cc
index 70c3f60..1b977cf 100644
--- a/r/adbcdrivermanager/src/radbc.cc
+++ b/r/adbcdrivermanager/src/radbc.cc
@@ -44,6 +44,22 @@ static void adbc_error_stop(int code, AdbcError* error, 
const char* context) {
   }
 }
 
+static void finalize_driver_xptr(SEXP driver_xptr) {
+  auto driver = reinterpret_cast<AdbcDriver*>(R_ExternalPtrAddr(driver_xptr));
+  if (driver == nullptr) {
+    return;
+  }
+
+  if (driver->release != nullptr) {
+    AdbcError error;
+    int status = driver->release(driver, &error);
+    adbc_error_warn(status, &error, "finalize_driver_xptr()");
+  }
+
+  adbc_xptr_default_finalize<AdbcDriver>(driver_xptr);
+  R_SetExternalPtrAddr(driver_xptr, nullptr);
+}
+
 static void finalize_database_xptr(SEXP database_xptr) {
   auto database = 
reinterpret_cast<AdbcDatabase*>(R_ExternalPtrAddr(database_xptr));
   if (database == nullptr) {
@@ -83,6 +99,7 @@ extern "C" SEXP RAdbcLoadDriverFromInitFunc(SEXP 
driver_init_func_xptr) {
   }
 
   SEXP driver_xptr = PROTECT(adbc_allocate_xptr<AdbcDriver>());
+  R_RegisterCFinalizer(driver_xptr, &finalize_driver_xptr);
   auto driver = adbc_from_xptr<AdbcDriver>(driver_xptr);
 
   AdbcError error;
diff --git a/r/adbcdrivermanager/tests/testthat/_snaps/driver_log.md 
b/r/adbcdrivermanager/tests/testthat/_snaps/driver_log.md
new file mode 100644
index 0000000..4658a3f
--- /dev/null
+++ b/r/adbcdrivermanager/tests/testthat/_snaps/driver_log.md
@@ -0,0 +1,41 @@
+# The log driver logs
+
+    Code
+      db <- adbc_database_init(adbc_driver_log(), key = "value")
+    Output
+      LogDriverInitFunc()
+      LogDriverInitFunc()
+      LogDatabaseNew()
+      LogDatabaseSetOption()
+      LogDatabaseInit()
+    Code
+      con <- adbc_connection_init(db, key = "value")
+    Output
+      LogConnectionNew()
+      LogConnectionInit()
+      LogConnectionSetOption()
+    Code
+      stmt <- adbc_statement_init(con, key = "value")
+    Output
+      LogStatementNew()
+      LogStatementSetOption()
+    Code
+      try(adbc_statement_execute_query(stmt))
+    Output
+      LogStatementExecuteQuery()
+      Error in stop_for_error(result$status, error) : 
+        ADBC_STATUS_NOT_IMPLEMENTED (2)
+    Code
+      adbc_statement_release(stmt)
+    Output
+      LogStatementRelease()
+    Code
+      adbc_connection_release(con)
+    Output
+      LogConnectionRelease()
+    Code
+      adbc_database_release(db)
+    Output
+      LogDatabaseRelease()
+      LogDriverRelease()
+
diff --git a/r/adbcdrivermanager/tests/testthat/test-driver_log.R 
b/r/adbcdrivermanager/tests/testthat/test-driver_log.R
new file mode 100644
index 0000000..9470443
--- /dev/null
+++ b/r/adbcdrivermanager/tests/testthat/test-driver_log.R
@@ -0,0 +1,28 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+test_that("The log driver logs", {
+  expect_snapshot({
+    db <- adbc_database_init(adbc_driver_log(), key = "value")
+    con <- adbc_connection_init(db, key = "value")
+    stmt <- adbc_statement_init(con, key = "value")
+    try(adbc_statement_execute_query(stmt))
+    adbc_statement_release(stmt)
+    adbc_connection_release(con)
+    adbc_database_release(db)
+  })
+})

Reply via email to