Hi,

I have changed the code for RbaseXClient.R in such a way that it
complies more to the server protocol and to the R-style of coding.

- 'execute' has been renamed to 'command'
- All the functions in the Command Protocol section return a list with
the complete server response. This makes it much easier to handle errors
(see lines 8-14 in TestRBaseXClient.R).

In the Query Command Protocol section, the functions 'more()' (as being
called by results) and  'full()' now return the results, prefixed with a
byte that represents the Type ID.

Cheers,

Ben Engbers

library(utils)
library(R6)
library(openssl)
library(dplyr)
library(purrr)
library(stringr)
library(magrittr)

BasexClient <- R6Class("BasexClient",
  public = list(
    initialize = function(host, port, username, password) {
      private$sock <- socketConnection(host = "localhost", port = 1984L, 
                                    open = "w+b", server = FALSE, blocking = TRUE, 
                                    encoding = "utf-8")
      private$response <- self$str_receive()
      splitted <-strsplit(private$response, "\\:")
      ifelse(length(splitted[[1]]) > 1,
             { code <- paste(username, splitted[[1]][1],password, sep=":")
             nonce <- splitted[[1]][2]
             },
             { code <- password
             nonce <- splitted[[1]][1]
             }
      )
      code <- md5(paste(md5(code), nonce, sep = ""))
      class(code) <- "hash"
      private$void_send(username)
      private$void_send(code)
      if (!self$bool_test_sock()) stop("Access denied")},
    command = function(command) {
      private$void_send(command)
      private$result <- self$str_receive()
      private$info <-   self$str_receive()
      if (length(private$info) > 0) cat(private$info, "\n")
      return(list(result = private$result %>% strsplit("\n", fixed = TRUE), 
                  info = private$info, 
                  success = self$bool_test_sock()))
    },
    query = function(query) {
      return(list(query = Query$new(query, private$get_sock()), success = self$bool_test_sock()))
      },
    create = function(name, input) {
      if (missing(input)) input <- ""
      writeBin(as.raw(0x08), private$sock)
      writeBin(private$raw_terminated_string(name), private$sock)
      writeBin(private$raw_terminated_string(input), private$sock)
      private$info <- self$str_receive()
      return(list(info = private$info, success = self$bool_test_sock()))
    },
    add = function(name, path, input) {
      writeBin(as.raw(0x09), private$sock)
      writeBin(private$raw_terminated_string(name), private$sock)
      writeBin(private$raw_terminated_string(path), private$sock)
      writeBin(private$raw_terminated_string(input), private$sock)
      private$info <- self$str_receive()
      return(list(info = private$info, success = self$bool_test_sock()))
      #      success <- self$bool_test_sock()
    },
    replace = function(path, input) {
      writeBin(as.raw(0x0C), private$sock)
      writeBin(private$raw_terminated_string(path), private$sock)
      writeBin(private$raw_terminated_string(input), private$sock)
      private$info <- self$str_receive()
      return(list(info = private$info, success = self$bool_test_sock()))
      #      success <- self$bool_test_sock()
    },
    store = function(path, input) {
      writeBin(as.raw(0x0D), private$sock)
      writeBin(private$raw_terminated_string(path), private$sock)
      writeBin(private$raw_terminated_string(input), private$sock)
      private$info <- self$str_receive()
      #      success <- self$bool_test_sock()
    },
    
    bool_test_sock = function(socket) {
      if (missing(socket)) socket <- private$get_sock()
      test <- readBin(socket, what = "raw", n =1)
      return(test == 0x00)
    },
    finalize = function() {
      private$close_sock()},
    print = function(...) {
      cat("Socket: ", private$get_sock(), "\n", sep = "")
      invisible(self)},
    str_receive = function(input, output) {
      if (missing(input)) input   <- private$get_sock()
      if (missing(output)) output <- raw(0)
      while ((rd <- readBin(input, what = "raw", n =1)) > 0) {
        if (rd == 0xff) next
        output <- c(output, rd)
      }
      ret <- rawToChar(output)
      return(ret)},
    term_string = function(string) {
      return(charToRaw(string) %>% append(0) %>% as.raw())}
  ),
  
  private = list(
    result = NULL,
    info = NULL,
    sock = NULL,
    response = NULL,
    get_sock = function() { private$sock },
    close_sock = function() { close(private$sock)},
    raw_terminated_string = function(string) {
      return(charToRaw(string) %>% append(0) %>% as.raw())},
    void_send = function(code, todo, input) {
      if (missing(todo)) {                                    # 'code' to be handled is either a string, hash or stream
        classTest <- class(code)
        if (class(code) == "character" || (class(code) == "hash")) private$void_send0term_string(code)
      } else {
        writeBin(as.raw(code), private$get_sock())
        private$void_send0term_string(todo)
        private$void_send_stream(input)
      }},
    void_send0term_string = function(string) {
      zero_term <- self$term_string(string)
      writeBin(zero_term, private$get_sock())},
    void_send_stream = function(input) {
      rd_id <- 1
      end <- length(input)
      zero_term <- raw()
      while (rd_id < end) { 
        rd <- c(input[rd_id])
        if (rd == 255 || rd == 0) zero_term <- c(zero_term, c(0x00))
        rd_id <- rd_id + 1
        zero_term <- c(zero_term, rd)
      }
      zero_term <- c(zero_term, c(0x00))
      writeBin(zero_term, private$get_sock())}
  )
)

Query <- R6Class("Query",
  inherit = BasexClient,
  
  public = list(
    str_id = NULL,
    raw_id = NULL,
    initialize = function(query, sock) {  
      private$sock <- sock
      out_stream <- super$get_sock()
      writeBin(as.raw(0x00), out_stream)
      writeBin(super$term_string(query), out_stream)
      self$str_id <- super$str_receive()
      self$raw_id <- super$term_string(self$str_id)},
    close = function() { 
      private$req_exe(0x02, self$raw_id)
      if (!private$req_success) cat("Query \'", self$str_id, "\' could not be closed.", "\n")
      return(private$req_success)
    },
    bind = function(name, value, type) {
      socket <- super$get_sock()
      if (missing(type)) type = ""
      private$write_code_ID(0x03, self$raw_id)
      name  %>% charToRaw() %>% append(0) %>% as.raw() %>% writeBin(socket)
      value %>% charToRaw() %>% append(0) %>% as.raw() %>% writeBin(socket)
      type  %>% charToRaw() %>% append(0) %>% as.raw() %>% writeBin(socket)
      private$req_result <- super$str_receive()
      private$req_success <- super$bool_test_sock()
      return(private$req_success)
    },
    execute = function() {
      private$req_exe(0x05, self$raw_id)
      result <- private$req_result %>% private$clean()
      return(result)
    },  
    more = function() {
      if (is.null(private$cache)) {
        in_stream <- out_stream <- super$get_sock()
        writeBin(as.raw(0x04), out_stream)
        writeBin(self$raw_id, out_stream)
        cache <- c()
        while ((rd <- readBin(in_stream, what = "raw", n =1)) > 0) {
          cache <- c(cache, as.character(rd))
          cache <- c(cache, super$str_receive())
        }
        private$req_success <- super$bool_test_sock()
        private$cache <- cache
        private$pos <- 0
      }
      if ( length(private$cache) > private$pos) return(TRUE)
      else
      { private$cache <- NULL
        return(FALSE)
      }},
    next_row = function() {      
      if (self$more()) {
        private$pos <- private$pos + 1
        result <- private$cache[private$pos]
      }
      return(result)},
    info = function() { 
      private$req_exe(0x06, self$raw_id)
      result <- private$req_result %>% private$clean()
      return(result)},  
    options = function() { 
      private$req_exe(0x07, self$raw_id)
      res <- private$req_result 
      res <- ifelse(length(private$req_result) > 1,
      private$req_result %>% private$clean(), "No options set")},  
    updating = function() { 
      private$req_exe(0x1E, self$raw_id)
      result <- private$req_result %>% as.logical()
      return(result)},  
    full = function() { 
      in_stream <- out_stream <- super$get_sock()
      writeBin(as.raw(0x1F), out_stream)
      writeBin(self$raw_id, out_stream)
      cache <- c()
      while ((rd <- readBin(in_stream, what = "raw", n =1)) > 0) {
        cache <- c(cache, as.character(rd))
        cache <- c(cache, super$str_receive())
      }
      private$req_success <- super$bool_test_sock()
      result <- cache
      return(result)},  
  
    print = function(...) {
      cat("Query-ID: ", self$str_id, "\n", sep = "")
      invisible(self)}
  ),
  private = list(
    cache = NULL,
    pos = NULL,
    req_result = NULL,
    req_success = NULL,
    write_code_ID = function(id_code, arg) {
      out_stream <- super$get_sock()
      writeBin(as.raw(id_code), out_stream)
      writeBin(arg, out_stream)},
    req_exe = function(id_code, arg) {
      private$write_code_ID(id_code, arg)
      private$req_result <- super$str_receive()
      private$req_success <- super$bool_test_sock()
    }, 
    receive_more = function(input, output) {
      if (missing(input)) input   <- private$get_sock()
      if (missing(output)) output <- raw(0)
      while ((rd <- readBin(input, what = "raw", n =1)) > 0) {
        if (rd == 0xff) next
        output <- c(output, rd)
      }
      ret <- rawToChar(output)
      return(ret)},
    clean = function(input) {
      result <- input %>% strsplit("\n", fixed = TRUE) 
      if ((result[[1]][1]  == "")) result <- result[[1]][2]
      return(result)
    }
  )
)
source("RbaseXClient.R")

BasexClient$undebug("query")

# Test 'command'
Session <- BasexClient$new("localhost", 1984, "admin", "admin")
test <- Session$command("OPEN test1")
if (!test$success) {
  test <- Session$create("test1", "<xml>Create test1</xml>")
  if (test$success) {cat( test$info, "\n")
  } else {
    cat("Could not create database\n")
  }
}
cat("Create database \"test2\" with empty resources\n")
Session$create("test2")
print(Session$command("list")$result)
print(Session$command("xquery 1 to 5")$result)
Session$command("CLOSE")
Session$command("DROP DB test2")
Session$command("DROP DB test1")

# Test 'query'
cat("Test Query1\n\n")
query1 <- "for $i in 1 to 2 return <xml>Text { $i }</xml>"
query1 <- Session$query(query1)
cat("query1$query$options():", query1$query$options(), sep = " ", "\n")
cat("query1$query$execute(): ")
print(query1$query$execute())
cat("query1$query$info():", query1$query$info(), sep = " ", "\n")
cat("query1$query$updating():", query1$query$updating(), sep = " ", "\n")
cat("query1$query$full(): ")
print(query1$query$full())
if (query1$query$close()) {
  cat("query1 closed\n")
} else {
  cat("query1 could not be closed")}
cat("\n")

cat("Test Query2\n\n")

query2 <- "for $i in 3 to 4 return <xml>Text { $i }</xml>"
query2 <- Session$query(query2)$query
print(query2)
while (query2$more()) {
  cat(query2$next_row(), "\n")
}
query2$close()

cat("Test Query3\n\n")
query3 <- "declare variable $name external; for $i in 5 to 6 return element { $name } { $i }"
query3 <- Session$query(query3)$query
query3$bind("$name", "number", "")
print(query3)
print(query3$execute())
print(query3$info())

Reply via email to