Commits

Anonymous committed 28abb0a

Added Postgresql.get_escaped_value

Comments (0)

Files changed (3)

lib/postgresql.ml

   external binary_tuples : result -> bool = "PQbinaryTuples_stub" "noalloc"
 
   external getvalue : result -> int -> int -> string = "PQgetvalue_stub"
+  external get_escaped_value : result -> int -> int -> string = "PQgetescvalue_stub"
 
   external getisnull :
     result -> int -> int -> bool = "PQgetisnull_stub" "noalloc"
 (* Escaping *)
 
 external unescape_bytea_stub : string -> string = "PQunescapeBytea_stub"
+external unescape_bytea_9x_stub : string -> string = "PQunescapeBytea9x_stub"
 
 let unescape_bytea str =
   let str_len = String.length str in
   else
     (* This is the new Postgresql 9.0 hex format for encoding bytea.  Older
        clients do not recognize this format, but servers send it by default. *)
-    let rec count_hex ~pos ~count =
-      if pos = str_len then count
-      else
-        match str.[pos] with
-        | ' ' | '\t' | '\n' | '\r' | '\b' -> count_hex ~pos:(pos + 1) ~count
-        | '0'..'9' | 'a'..'f' | 'A'..'F' ->
-            let pos = pos + 1 in
-            if pos = str_len then
-              failwith "Postgresql.unescape_bytea: incomplete hex code"
-            else
-              begin match str.[pos] with
-              | '0'..'9' | 'a'..'f' | 'A'..'F' ->
-                  count_hex ~pos:(pos + 1) ~count:(count + 1)
-              | _ ->
-                  failwith "Postgresql.unescape_bytea: short hex code"
-              end
-        | _ -> failwith "Postgresql.unescape_bytea: illegal hex code"
-    in
-    let n_grps = count_hex ~pos:2 ~count:0 in
-    let res = String.create n_grps in
-    let rec res_loop ~str_pos ~res_pos =
-      if res_pos = n_grps then res
-      else
-        match str.[str_pos] with
-        | ' ' | '\t' | '\n' | '\r' | '\b' ->
-            res_loop ~str_pos:(str_pos + 1) ~res_pos
-        | c ->
-            let unhexdigit = function
-              | '0'..'9' as c -> Char.code c - Char.code '0'
-              | 'a'..'f' as c -> Char.code c - Char.code 'a' + 10
-              | 'A'..'F' as c -> Char.code c - Char.code 'A' + 10
-              | _ -> assert false
-            in
-            let high = unhexdigit c lsl 4 in
-            let str_pos = str_pos + 1 in
-            let low = unhexdigit str.[str_pos] in
-            let res_char = Char.chr (high + low) in
-            res.[res_pos] <- res_char;
-            res_loop ~str_pos:(str_pos + 1) ~res_pos:(res_pos + 1)
-    in
-    res_loop ~str_pos:2 ~res_pos:0
-
+    unescape_bytea_9x_stub str
 
 (* Query results *)
 
     check_tuple tuple;
     Stub.getvalue res tuple field
 
+  method get_escaped_value tuple field =
+    check_field field;
+    check_tuple tuple;
+    Stub.get_escaped_value res tuple field
+
   method getisnull tuple field =
     check_field field; check_tuple tuple;
     Stub.getisnull res tuple field

lib/postgresql.mli

       @raise Error if field out of range.
   *)
 
+  method get_escaped_value : int -> int -> string
+  (** [#get_escaped_value tuple field] @return escaped value of [field] in [tuple].
+
+      @raise Error if tuple out of range.
+      @raise Error if field out of range.
+  *)
+
   method getisnull : int -> int -> bool
   (** [#getisnull tuple field] tests for a NULL-value of [field] in [tuple].
 

lib/postgresql_stubs.c

 #endif
 
 #include <string.h>
+#include <ctype.h>
 
 #include <caml/mlvalues.h>
 #include <caml/alloc.h>
   return Val_unit;
 }
 
+CAMLprim value unescape_bytea_9x (char *s);
+CAMLprim value unescape_bytea(char *s);
+
 
 /* Conversion functions */
 
   CAMLreturn(v_str);
 }
 
+CAMLprim value PQgetescvalue_stub(value v_res, value v_tup_num, value v_field_num)
+{
+  CAMLparam1(v_res);
+  value v_str;
+  PGresult *res = get_res(v_res);
+  size_t field_num = Long_val(v_field_num);
+  size_t tup_num = Long_val(v_tup_num);
+  char *str = PQgetvalue(res, tup_num, field_num);
+  if (PQfformat(res, field_num) == 0) {
+    if (str != NULL && *str == '\\' && *(str+1) == 'x') {
+      v_str = unescape_bytea_9x(str);
+    } else {
+      v_str = unescape_bytea(str);
+    }
+  } else {
+    /* Assume binary format! */
+    size_t len = PQgetlength(res, tup_num, field_num);
+    v_str = len ? v_empty_string : caml_alloc_string(len);
+    memcpy(String_val(v_str), str, len);
+  }
+  CAMLreturn(v_str);
+}
+
 noalloc_field_info(PQgetlength, Val_long)
 noalloc_field_info(PQgetisnull, Val_bool)
 
   return v_res;
 }
 
-CAMLprim value PQunescapeBytea_stub(value v_from)
+CAMLprim value unescape_bytea(char *s)
 {
   size_t len;
   value v_res;
-  char *buf =
-    (char *) PQunescapeBytea((unsigned char *) String_val(v_from), &len);
-  if (buf == NULL)
+  char *buf = (char *) PQunescapeBytea((unsigned char*) s, &len);
+  if (buf == NULL) {
     caml_failwith("Postgresql.unescape_bytea: illegal bytea string");
+    return Val_unit;
+  }
   v_res = caml_alloc_string(len);
   memcpy(String_val(v_res), buf, len);
   PQfreemem(buf);
   return v_res;
 }
 
+CAMLprim value PQunescapeBytea_stub(value v_from)
+{
+  return unescape_bytea(String_val(v_from));
+}
+
+int unhexdigit(char c) {
+  if ((c) >= '0' && (c) <= '9') {
+    return (c) - '0';
+  } else if ((c) >= 'a' && (c) <= 'f')
+    return (c) - 'a' + 10;
+  else if ((c) >= 'A' && (c) <= 'F')
+    return (c) - 'A' + 10;
+  else {
+    caml_failwith("Postgresql.unescape_bytea_9x: invalid hex character");
+    return '\0';
+  }
+}
+
+CAMLprim value unescape_bytea_9x (char *s)
+{
+  if (s != NULL && *s == '\\' && *(s+1) == 'x') {
+    value v_res;
+    size_t len = 0;
+    int s_index = 0;
+    int s_length = 0;
+    char *ptr;
+    int result_index = 0;
+    s += 2; /* skip \x */
+
+    /* compute unescaped string length */
+    ptr = s;
+    while(*ptr != '\0') {
+      s_length++;
+      if (!isspace(*ptr ))
+        len++;
+      ptr++;
+    }
+    len /= 2;
+
+    v_res = caml_alloc_string(len);
+    ptr = String_val(v_res);
+    while (s_index < s_length) {
+      char low;
+      char high = s[s_index];
+      s_index++;
+      if (isspace(high)) {
+        continue;
+      }
+      low = s[s_index];
+      s_index++;
+      ptr[result_index] = (char)(((unhexdigit(high)) << 4) | (unhexdigit(low)));
+      result_index++;
+    }
+    return v_res;
+  } else {
+    caml_failwith("Postgresql.unescape_bytea_9x: hex prefix not found");
+    return Val_unit;
+  }
+}
+
+CAMLprim value PQunescapeBytea9x_stub(value v_from)
+{
+  return unescape_bytea_9x(String_val(v_from));
+}
 
 /* Asynchronous Notification */