Re: a Scheme48 wrapper for Sqlite3

"Ivan Shmakov" <[email protected]>
Newsgroups gmane.lisp.scheme.scheme48
Message-ID <[email protected]>
>>>>> "IS" == Ivan Shmakov <[email protected]> writes:

 IS> I've made some changes to the code, but I'm currently too busy to
 IS> finish the work.  I'm therefore posting the code in its current
 IS> state in the hope that it might be useful to someone.

	I've reworked the code a bit, and the user-visible changes are
	roughly as follows.

    * The code is now rewritten to utilize the new Scheme48 FFI (for
      to-be Scheme 1.9.)

    * The error-signalling variants are now provided for a number of
      functions; these variants are: `sqlite3-open', `sqlite3-close',
      `sqlite3-prepare-2', `sqlite3-prepare',
      `sqlite3-prepared-finalize!', `sqlite3-prepared-reset!',
      `sqlite3-prepared-step!'.

    * The interfaces were extended by `sqlite3-column-name' (with error
      signalling) and `sqlite3-column-name-1' (without), mostly to allow
      for `sqlite3-exec' to be reimplemented.

    * The `sqlite3-exec' is now reimplemented on top of the rest of the
      interface; both `sqlite3-exec-1' and sqlite3_s48_exec () are gone.

    * An example makefile is provided.

	I've tested it with Scheme48 (as of [1]), and it appears to
	build and work correctly:

cd ~/devel/scheme48-sqlite/
make -k 
cc -g -Wall -I/.../include  -c -o sqlite3-int.o sqlite3-int.c
sqlite3-int.c: In function 'sqlite3_s48_libversion':
sqlite3-int.c:85: warning: passing argument 2 of 's48_enter_byte_string_2' discards qualifiers from pointer target type
sqlite3-int.c: In function 'sqlite3_s48_errmsg':
sqlite3-int.c:167: warning: passing argument 2 of 's48_enter_byte_string_2' discards qualifiers from pointer target type
sqlite3-int.c: In function 'sqlite3_s48_column_name':
sqlite3-int.c:274: warning: passing argument 2 of 's48_enter_byte_string_2' discards qualifiers from pointer target type
sqlite3-int.c: In function 'sqlite3_s48_column_blob':
sqlite3-int.c:288: warning: passing argument 2 of 's48_enter_byte_vector_2' discards qualifiers from pointer target type
cc -g -Wall -o sqlite3-int.so sqlite3-int.o -shared -lsqlite3

Compilation finished at Mon Sep  8 11:41:52

Welcome to Scheme 48 1.9T (made by ivan on Mon Sep  8 11:38:33 NOVST 2008)
Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees.
Please report bugs to [email protected].
Get more information at http://www.s48.org/.
Type ,? (comma question-mark) for help.
> ,config ,load sqlite3-packages.scm 
> ,new-package 
264> ,structure foo (export)
foo> ,open sqlite3 

warning: Structure has undefined exports [check-structure]
         #{structure 263 sqlite3-low}
         (sqlite3-exec-1)
foo> (define d (sqlite3-open "/tmp/foo.db"))
; no values returned
foo> (define (show . args)
       (write args)
       (newline))
; no values returned
foo> (sqlite3-exec d "SELECT 'foo' UNION SELECT 'bar'"
		   show 'unused)
(unused 1 #(#{byte-vector 98 97 114}) #(#{OS-string ("US-ASCII" "ANSI_X3.4-1968") "'foo'"}))
(unused 1 #(#{byte-vector 102 111 111}) #(#{OS-string ("US-ASCII" "ANSI_X3.4-1968") "'foo'"}))
#{Unspecific}
foo> 

[1] http://www.deinprogramm.de/cgi-bin/hgs48.cgi/rev/808c9731148689766698a36fbe8dc22bdda94f16

 IS> The things to be done are, roughly:

[...]

 IS> * an exception should be raised on receiving ``bad'' result code
 IS> (the functions needing the attention with regard to this issue
 IS> are currently given suffix `-1' in the code);

 IS> * remove sqlite3_s48_exec () and (sqlite3-exec-1 ...); implement
 IS> `sqlite3-exec' on top of `sqlite3-prepare' and
 IS> `sqlite3-prepared-step!'.

	I believe that these two are now done.

[...]

;;; "sqlite3.scm" -- Scheme48 wrapper for Sqlite3  -*- Scheme -*-
;; Features: sqlite3

;;; Copyright (C) 2008 Ivan Shmakov

;;; Code:

(import-dynamic-externals "./sqlite3-int")

(import-lambda-definition-2 sqlite3-s48-box ())

(import-lambda-definition-2 sqlite3-s48-libversion ())
(import-lambda-definition-2 sqlite3-s48-libversion-number ())

(import-lambda-definition-2 sqlite3-s48-free (pointer))

(import-lambda-definition-2 sqlite3-s48-open  (filename sqlite3-ptr))
(import-lambda-definition-2 sqlite3-s48-close (sqlite3-ptr))

(import-lambda-definition-2 sqlite3-s48-errcode (sqlite3-ptr))
(import-lambda-definition-2 sqlite3-s48-errmsg  (sqlite3-ptr))

(import-lambda-definition-2 sqlite3-s48-prepare-1
                            (sqlite3-ptr sql utf-16? prepared-ptr u-pair))
(import-lambda-definition-2 sqlite3-s48-finalize  (sqlite3-prepared-ptr))
(import-lambda-definition-2 sqlite3-s48-reset     (sqlite3-prepared-ptr))
(import-lambda-definition-2 sqlite3-s48-step      (sqlite3-prepared-ptr))

(import-lambda-definition-2 sqlite3-s48-data-count (sqlite3-prepared-ptr))
(import-lambda-definition-2 sqlite3-s48-column-type
                            (sqlite3-prepared-ptr column-index))
(import-lambda-definition-2 sqlite3-s48-column-name
                            (sqlite3-prepared-ptr column-index))
(import-lambda-definition-2 sqlite3-s48-column-blob
                            (sqlite3-prepared-ptr column-index))

;;; Utility

(define (vector-map! vec proc src-1 . srcs)
  (do ((l   (vector-length src-1))
       (i   0 (+ 1 i)))
      ((>=  i l))
    (let ((ref (lambda (vec) (vector-ref vec i))))
      (vector-set! vec i (apply proc (ref src-1)
                                (map  ref srcs))))))

;;; Types

(define-enumerated-type sqlite3-result :sqlite3-result
  sqlite3-result?
  sqlite3-results
  sqlite3-result-name
  sqlite3-result-code
  (ok           ;   0 SQLITE_OK
   error        ;   1 SQLITE_ERROR
   internal     ;   2 SQLITE_INTERNAL
   perm         ;   3 SQLITE_PERM
   abort        ;   4 SQLITE_ABORT
   busy         ;   5 SQLITE_BUSY
   locked       ;   6 SQLITE_LOCKED
   nomem        ;   7 SQLITE_NOMEM
   readonly     ;   8 SQLITE_READONLY
   interrupt    ;   9 SQLITE_INTERRUPT
   ioerr        ;  10 SQLITE_IOERR
   corrupt      ;  11 SQLITE_CORRUPT
   notfound     ;  12 SQLITE_NOTFOUND   (Internal Only)
   full         ;  13 SQLITE_FULL
   cantopen     ;  14 SQLITE_CANTOPEN
   protocol     ;  15 SQLITE_PROTOCOL
   empty        ;  16 SQLITE_EMPTY      (Internal Only)
   schema       ;  17 SQLITE_SCHEMA
   toobig       ;  18 SQLITE_TOOBIG
   constraint   ;  19 SQLITE_CONSTRAINT
   mismatch     ;  20 SQLITE_MISMATCH
   misuse       ;  21 SQLITE_MISUSE
   nolfs        ;  22 SQLITE_NOLFS
   auth         ;  23 SQLITE_AUTH
   n/a-24 n/a-25 n/a-26 n/a-27 n/a-28 n/a-29 n/a-30 n/a-31 n/a-32 n/a-33
   n/a-34 n/a-35 n/a-36 n/a-37 n/a-38 n/a-39 n/a-40 n/a-41 n/a-42 n/a-43
   n/a-44 n/a-45 n/a-46 n/a-47 n/a-48 n/a-49 n/a-50 n/a-51 n/a-52 n/a-53
   n/a-54 n/a-55 n/a-56 n/a-57 n/a-58 n/a-59 n/a-60 n/a-61 n/a-62 n/a-63
   n/a-64 n/a-65 n/a-66 n/a-67 n/a-68 n/a-69 n/a-70 n/a-71 n/a-72 n/a-73
   n/a-74 n/a-75 n/a-76 n/a-77 n/a-78 n/a-79 n/a-80 n/a-81 n/a-82 n/a-83
   n/a-84 n/a-85 n/a-86 n/a-87 n/a-88 n/a-89 n/a-90 n/a-91 n/a-92 n/a-93
   n/a-94 n/a-95 n/a-96 n/a-97 n/a-98 n/a-99
   row          ; 100 SQLITE_ROW
   done))       ; 101 SQLITE_DONE

; (define (sqlite3-result? object)
;   ;; .
;   (and (sqlite3-result-1? object)
;        (let ((i (sqlite3-result-index-1 object)))
;          ;; ignore all the non-assigned result codes
;          (or (<= i 23) (>= i 100)))))

(define (sqlite3-result<-integer code)
  ;; .
  (vector-ref sqlite3-results code))

(define-enumerated-type sqlite3-type :sqlite3-type
  sqlite3-type?
  sqlite3-types
  sqlite3-type-name
  sqlite3-type-code
  (integer      ; 1 SQLITE_INTEGER
   float        ; 2 SQLITE_FLOAT
   text         ; 3 SQLITE_TEXT
   blob         ; 4 SQLITE_BLOB
   null))       ; 5 SQLITE_NULL

(define (sqlite3-type<-integer code)
  ;; .
  (vector-ref sqlite3-types code))

(define-record-type sqlite3 :sqlite3
  (make-sqlite3 box)
  sqlite3?
  (box sqlite3-box set-sqlite3-box!))

(define-record-discloser :sqlite3
  (lambda (sqlite3)
    (list 'sqlite3)))

(define-record-type sqlite3-prepared :sqlite3-prepared
  (make-sqlite3-prepared box finished?)
  sqlite3-prepared?
  (box          sqlite3-prepared-box    set-sqlite3-prepared-box!)
  (finished?    sqlite3-prepared-finished?
                set-sqlite3-prepared-finished?!))

(define-record-discloser :sqlite3-prepared
  (lambda (sqlite3-prepared)
    (list 'sqlite3-prepared
          `(finished? ,(sqlite3-prepared-finished? sqlite3-prepared)))))

;; NB: meaningless
(define-record-resumer :sqlite3 #f)
(define-record-resumer :sqlite3-prepared #f)

;;; Simple wrappers

(define (make-call-wrapper proc callback name)
  (lambda args
    (call-with-values (lambda () (apply proc args))
      (lambda (r . rest)
        (apply callback
               (lambda (error-message)
                 (assertion-violation name
                                      error-message
                                      ;; FIXME: order of arguments here?
                                      (cons r rest)
                                      (sqlite3-result<-integer r) r))
               r rest)))))

(define make-call-ok-wrapper
  (let ((ok-code (sqlite3-result-code (sqlite3-result ok))))
    (lambda (proc callback name error-message)
      (define (check-and-call signal r . rest)
        (if (= r ok-code)
            (apply callback r rest)
            (signal error-message)))
      (make-call-wrapper proc check-and-call name))))

;;; Obtaining static information

(define (sqlite3-libversion)
  (x->os-string (sqlite3-s48-libversion)))

(define sqlite3-libversion-number sqlite3-s48-libversion-number)

(define (sqlite3-libversion-list)
  (let* ((n   (sqlite3-libversion-number))
         (z   (remainder    n          1000))
         (ye3 (remainder (- n    z) 1000000))
         (xe6            (- n (+ z ye3)))
         (y   (/ ye3 1000))
         (x   (/ xe6 1000000)))
    (list x y z)))

;;; Opening and closing database

(define (sqlite3-open-1 filename)
  (let* ((box (sqlite3-s48-box))
         (r   (sqlite3-s48-open (os-string->byte-vector
                                 (x->os-string filename))
                                box))
         (o   (make-sqlite3 box)))
    (add-finalizer! o sqlite3-close-1)
    ;; .
    (values r o)))

(define (sqlite3-close-1 sqlite3)
  ;; .
  (cond ((sqlite3-box sqlite3)
         => (lambda (box)
              (let ((r (sqlite3-s48-close box)))
                ;; FIXME: SQLITE_BUSY code is silently ignored
                (if (not (= r (sqlite3-result-code
                               (sqlite3-result busy))))
                    (set-sqlite3-box! sqlite3 #f))
                r)))))

(define sqlite3-open
  (make-call-ok-wrapper sqlite3-open-1
                        (lambda (r o) o)
                        'sqlite3-open
                        "sqlite3-s48-open failed"))

(define sqlite3-close
  (make-call-ok-wrapper sqlite3-close-1
                        (lambda (r) (unspecific))
                        'sqlite3-close
                        "sqlite3-s48-close failed"))

;;; Database status

(define (sqlite3-last-result sqlite3)
  (let ((r (sqlite3-s48-errcode (sqlite3-box sqlite3))))
    ;; .
    (sqlite3-result<-integer r)))

(define (sqlite3-last-error-message sqlite3)
  (let ((b (sqlite3-s48-errmsg  (sqlite3-box sqlite3))))
    ;; .
    (byte-vector->os-string b)))

;;; Preparing statements

(define (sqlite3-prepare-1 sqlite3 sql)
  (let* ((box (sqlite3-s48-box))
         (u-x (cons #f #f))
         (r   (sqlite3-s48-prepare-1 (sqlite3-box sqlite3)
                                     (os-string->byte-vector
                                      (x->os-string sql))
                                     #f ; utf-16?
                                     box u-x))
         (o   (make-sqlite3-prepared box #f)))
    (add-finalizer! o sqlite3-prepared-finalize-1!)
    ;; .
    (values r o (car u-x))))

(define (sqlite3-prepared-finalize-1! prepared)
  ;; .
  (cond ((sqlite3-prepared-box prepared)
         => (lambda (box)
              (let ((r (sqlite3-s48-finalize box)))
                ;; NB: statement is finalized irrespective of `r'
                (set-sqlite3-prepared-box! prepared #f)
                r)))))

(define (sqlite3-prepared-reset-1! prepared)
  (let ((r (sqlite3-s48-reset (sqlite3-prepared-box prepared))))
    ;; FIXME: should check `r' here?
    (set-sqlite3-prepared-finished?! prepared #f)
    ;; .
    r))

(define (sqlite3-prepared-step-1! prepared)
  (if (sqlite3-prepared-finished? prepared)
      (assertion-violation 'sqlite3-prepared-step-1!
                           "prepared statement already done"
                           prepared))
  (let ((r (sqlite3-s48-step  (sqlite3-prepared-box prepared))))
    (if (= r (sqlite3-result-code (sqlite3-result done)))
        (set-sqlite3-prepared-finished?! prepared #t))
    ;; .
    r))

(define sqlite3-prepare-2
  (make-call-ok-wrapper sqlite3-prepare-1
                        (lambda (r o index)
                          (values o index))
                        'sqlite3-prepare-2
                        "sqlite3-s48-prepare-1 failed"))

(define (sqlite3-prepare sqlite3 sql)
  (call-with-values (lambda () (sqlite3-prepare-2 sqlite3 sql))
    (lambda (prepared index)
      prepared)))

(define sqlite3-prepared-finalize!
  (make-call-ok-wrapper sqlite3-prepared-finalize-1!
                        (lambda (r) (unspecific))
                        'sqlite3-prepared-finalize!
                        "sqlite3-s48-finalize failed"))

(define sqlite3-prepared-reset!
  (make-call-ok-wrapper sqlite3-prepared-reset-1!
                        (lambda (r) (unspecific))
                        'sqlite3-prepared-reset!
                        "sqlite3-s48-reset failed"))

(define sqlite3-prepared-step!
  (let ((row-code  (sqlite3-result-code (sqlite3-result row)))
        (done-code (sqlite3-result-code (sqlite3-result done))))
    (make-call-wrapper sqlite3-prepared-step-1!
                       (lambda (signal r)
                         (cond ((= r row-code)  #t)
                               ((= r done-code) #f)
                               (else
                                (signal "sqlite3-s48-reset failed"))))
                       'sqlite3-prepared-step!)))

;;; Fetching values

(define (sqlite3-columns prepared)
  ;; .
  (sqlite3-s48-data-count (sqlite3-prepared-box prepared)))

(define (check-index prepared index)
  (if (or (negative? index)
          (>= index (sqlite3-columns prepared)))
      (assertion-violation 'sqlite3-column-type
                           "column index out of range"
                           index)))

(define (sqlite3-column-type-1 prepared index)
  (check-index prepared index)
  ;; .
  (sqlite3-s48-column-type  (sqlite3-prepared-box prepared)
                            index))

(define (sqlite3-column-type prepared index)
  ;; .
  (sqlite3-type<-integer (sqlite3-column-type-1 prepared index)))

(define (sqlite3-column-name-1 prepared index)
  (check-index prepared index)
  ;; .
  (sqlite3-s48-column-name  (sqlite3-prepared-box prepared)
                            index))

(define (sqlite3-column-name prepared index)
  ;; .
  (x->os-string (sqlite3-column-name-1 prepared index)))

(define (sqlite3-column-blob prepared index)
  (check-index prepared index)
  ;; .
  (sqlite3-s48-column-blob  (sqlite3-prepared-box prepared)
                            index))

;;; Executing queries

(define (sqlite3-exec sqlite3 sql callback cb-data)
  (let ((prepared (sqlite3-prepare sqlite3 sql)))
    (let loop ()
      (if (sqlite3-prepared-step! prepared)
          (let ((columns (sqlite3-columns prepared)))
            (do ((i     (+ -1 columns) (+ -1 i))
                 (vals  (make-vector columns))
                 (names (make-vector columns)))
                ((negative? i)
                 (callback cb-data columns vals names))
              ;; FIXME: should x->os-string be applied here?
              (vector-set! vals  i (sqlite3-column-blob prepared i))
              (vector-set! names i (sqlite3-column-name prepared i)))
            (loop))))))

;;; sqlite3.scm ends here

;;; "sqlite3-packages.scm" -- ???  -*- Scheme -*-

;;; Copyright (C) 2008 Ivan Shmakov

;;; Code:

(define-interface sqlite3-interface
  (export sqlite3?
          sqlite3-prepared?
          sqlite3-libversion
          sqlite3-libversion-list
          sqlite3-open
          sqlite3-close
          sqlite3-last-result
          sqlite3-last-error-message
          sqlite3-prepare
          sqlite3-prepare-2
          sqlite3-prepared-finalize!
          sqlite3-prepared-reset!
          sqlite3-prepared-step!
          sqlite3-prepared-finished?
          sqlite3-columns
          sqlite3-column-type
          sqlite3-column-name
          sqlite3-column-blob
          sqlite3-exec))

(define-interface sqlite3-low-interface
  (export (sqlite3-result :syntax)
          sqlite3-result-code
          sqlite3-result-name
          sqlite3-libversion-number
          sqlite3-open-1
          sqlite3-close-1
          sqlite3-prepare-1
          sqlite3-prepared-finalize-1!
          sqlite3-prepared-reset-1!
          sqlite3-prepared-step-1!
          sqlite3-column-type-1
          sqlite3-column-name-1
          sqlite3-exec-1))

(define-interface chicken-sqlite3-interface
  (export sqlite3:open
          sqlite3:close
          sqlite3:exec
          sqlite3:first-result
          sqlite3:first-row
          sqlite3:for-each-row
          sqlite3:map-row))

(define-structures ((sqlite3            sqlite3-interface)
                    (sqlite3-low        sqlite3-low-interface))
  (open scheme
        ;; exceptions
        (subset exceptions
                (assertion-violation))
        ;; externals
        external-calls
        load-dynamic-externals
        ;; data structures
        (subset util (unspecific))
        byte-vectors
        define-record-types
        finite-types
        os-strings)
  (files sqlite3))

;;; sqlite3-packages.scm ends here

/*** sqlite3-int.c --- ?  -*- C -*- */

/*** Copyright (C) 2008 Ivan Shmakov */

/*** Code: */

#include <stddef.h>             /* for size_t */

#include <scheme48.h>

#include <sqlite3.h>

typedef s48_call_t  x48_call;
typedef s48_ref_t   x48_ref;

static x48_ref   sqlite3_s48_box (x48_call);

static x48_ref   sqlite3_s48_libversion (x48_call);
static x48_ref   sqlite3_s48_libversion_number (x48_call);

static x48_ref   sqlite3_s48_free (x48_call, x48_ref);

static x48_ref   sqlite3_s48_open   (x48_call, x48_ref, x48_ref);
static x48_ref   sqlite3_s48_open16 (x48_call, x48_ref, x48_ref);
static x48_ref   sqlite3_s48_close (x48_call, x48_ref);

static x48_ref   sqlite3_s48_errcode (x48_call, x48_ref sqlite3_ptr);
static x48_ref   sqlite3_s48_errmsg  (x48_call, x48_ref sqlite3_ptr);

static x48_ref   sqlite3_s48_prepare_1 (x48_call,
                                        x48_ref, x48_ref, x48_ref,
                                        x48_ref, x48_ref);
static x48_ref   sqlite3_s48_finalize (x48_call, x48_ref);
static x48_ref   sqlite3_s48_reset (x48_call, x48_ref);
static x48_ref   sqlite3_s48_step (x48_call, x48_ref);

static x48_ref   sqlite3_s48_data_count (x48_call, x48_ref);
static x48_ref   sqlite3_s48_column_type (x48_call, x48_ref, x48_ref);
static x48_ref   sqlite3_s48_column_name (x48_call, x48_ref, x48_ref);
static x48_ref   sqlite3_s48_column_blob (x48_call, x48_ref, x48_ref);

void
s48_on_load (void)
{
  S48_EXPORT_FUNCTION (sqlite3_s48_box);

  S48_EXPORT_FUNCTION (sqlite3_s48_libversion);
  S48_EXPORT_FUNCTION (sqlite3_s48_libversion_number);

  S48_EXPORT_FUNCTION (sqlite3_s48_free);

  S48_EXPORT_FUNCTION (sqlite3_s48_open);
  S48_EXPORT_FUNCTION (sqlite3_s48_open16);
  S48_EXPORT_FUNCTION (sqlite3_s48_close);

  S48_EXPORT_FUNCTION (sqlite3_s48_errcode);
  S48_EXPORT_FUNCTION (sqlite3_s48_errmsg);

  S48_EXPORT_FUNCTION (sqlite3_s48_prepare_1);
  S48_EXPORT_FUNCTION (sqlite3_s48_finalize);
  S48_EXPORT_FUNCTION (sqlite3_s48_reset);
  S48_EXPORT_FUNCTION (sqlite3_s48_step);

  S48_EXPORT_FUNCTION (sqlite3_s48_data_count);
  S48_EXPORT_FUNCTION (sqlite3_s48_column_type);
  S48_EXPORT_FUNCTION (sqlite3_s48_column_name);
  S48_EXPORT_FUNCTION (sqlite3_s48_column_blob);

  /* . */
}

static x48_ref
sqlite3_s48_box (x48_call c)
{
  /* NB: it's assumed that sizeof (ANY_TYPE *) is the same */
  /* . */
  return s48_enter_pointer_2 (c, (void *)0);
}

static x48_ref
sqlite3_s48_libversion (x48_call c)
{
  /* . */
  return
    s48_enter_byte_string_2 (c, sqlite3_libversion ());
}

static x48_ref
sqlite3_s48_libversion_number (x48_call c)
{
  /* . */
  return
    s48_enter_long_2 (c, sqlite3_libversion_number ());
}

static x48_ref
sqlite3_s48_free (x48_call c, x48_ref ptr)
{
  sqlite3_free (s48_extract_value_2 (c, ptr, void *));
  /* . */
  return s48_unspecific_2 (c);
}

/*** Opening and closing database */

static x48_ref
sqlite3_s48_open (x48_call c, x48_ref filename, x48_ref sqlite3_ptr)
{
  /* NB: is filename expected to be in UTF-8? */
  const void *c_filename = s48_extract_byte_vector_2 (c, filename);
  sqlite3 *o;
  int r;

  r = sqlite3_open (c_filename, &o);
  s48_unsafe_set_value_2 (c, sqlite3_ptr, sqlite3 *, o);

  /* . */
  return
    s48_enter_long_2 (c, r);
}

static x48_ref
sqlite3_s48_open16 (x48_call c, x48_ref filename, x48_ref sqlite3_ptr)
{
  /* NB: filename is expected to be in UTF-16 */
  const void *c_filename = s48_extract_byte_vector_2 (c, filename);
  sqlite3 *o;
  int r;

  r = sqlite3_open16 (c_filename, &o);
  s48_unsafe_set_value_2 (c, sqlite3_ptr, sqlite3 *, o);

  /* . */
  return
    s48_enter_long_2 (c, r);
}

static x48_ref
sqlite3_s48_close (x48_call c, x48_ref sqlite3_ptr)
{
  sqlite3 *o = s48_unsafe_extract_value_2 (c, sqlite3_ptr, sqlite3 *);

  /* . */
  return
    s48_enter_long_2 (c, sqlite3_close (o));
}

/*** Database status */

static x48_ref
sqlite3_s48_errcode (x48_call c, x48_ref sqlite3_ptr)
{
  sqlite3 *o = s48_unsafe_extract_value_2 (c, sqlite3_ptr, sqlite3 *);

  /* . */
  return
    s48_enter_long_2 (c, sqlite3_errcode (o));
}

static x48_ref
sqlite3_s48_errmsg (x48_call c, x48_ref sqlite3_ptr)
{
  sqlite3 *o = s48_unsafe_extract_value_2 (c, sqlite3_ptr, sqlite3 *);

  /* . */
  return
    s48_enter_byte_string_2 (c, sqlite3_errmsg (o));
}

/*** Preparing statements */

static x48_ref
sqlite3_s48_prepare_1 (x48_call c,
                       x48_ref  sqlite3_ptr,
                       x48_ref  sql,
                       x48_ref  utf_16_p,
                       x48_ref  prepared_ptr,
                       x48_ref  used_pair)
{
  sqlite3 *o = s48_unsafe_extract_value_2 (c, sqlite3_ptr, sqlite3 *);
  const char *c_sql = s48_extract_byte_vector_2 (c, sql);
  const char *c_sql_tail;
  const size_t c_sql_size = s48_byte_vector_length_2 (c, sql);
  sqlite3_stmt *prepared;
  int r;

  s48_check_pair_2 (c, used_pair);

  r = (s48_extract_boolean_2 (c, utf_16_p)
       ? sqlite3_prepare16 (o, c_sql, c_sql_size,
                            &prepared, (const void **)&c_sql_tail)
       : sqlite3_prepare   (o, c_sql, c_sql_size,
                            &prepared, (const char **)&c_sql_tail));
  s48_unsafe_set_value_2 (c, prepared_ptr, sqlite3_stmt *, prepared);
  if (s48_pair_p_2 (c, used_pair)) {
    s48_unsafe_set_car_2 (c, used_pair,
                          s48_enter_long_2 (c, c_sql_tail - c_sql));
  }

  /* . */
  return
    s48_enter_long_2 (c, r);
}

static x48_ref
sqlite3_s48_finalize (x48_call c, x48_ref prepared_ptr)
{
  sqlite3_stmt *p
    = s48_unsafe_extract_value_2 (c, prepared_ptr, sqlite3_stmt *);

  /* . */
  return
    s48_enter_long_2 (c, sqlite3_finalize (p));
}

static x48_ref
sqlite3_s48_reset (x48_call c, x48_ref prepared_ptr)
{
  sqlite3_stmt *p
    = s48_unsafe_extract_value_2 (c, prepared_ptr, sqlite3_stmt *);

  /* . */
  return
    s48_enter_long_2 (c, sqlite3_reset (p));
}

static x48_ref
sqlite3_s48_step (x48_call c, x48_ref prepared_ptr)
{
  sqlite3_stmt *p
    = s48_unsafe_extract_value_2 (c, prepared_ptr, sqlite3_stmt *);

  /* . */
  return
    s48_enter_long_2 (c, sqlite3_step (p));
}

/*** Fetching values */

static x48_ref
sqlite3_s48_data_count (x48_call c, x48_ref prepared_ptr)
{
  sqlite3_stmt *p
    = s48_unsafe_extract_value_2 (c, prepared_ptr, sqlite3_stmt *);

  /* . */
  return
    s48_enter_long_2 (c, sqlite3_data_count (p));
}

static x48_ref
sqlite3_s48_column_type (x48_call c,
                         x48_ref prepared_ptr, x48_ref column)
{
  sqlite3_stmt *p
    = s48_unsafe_extract_value_2 (c, prepared_ptr, sqlite3_stmt *);
  int i = s48_extract_long_2 (c, column);

  /* . */
  return
    s48_enter_long_2 (c, sqlite3_column_type (p, i));
}

static x48_ref
sqlite3_s48_column_name (x48_call c,
                         x48_ref prepared_ptr, x48_ref column)
{
  sqlite3_stmt *p
    = s48_unsafe_extract_value_2 (c, prepared_ptr, sqlite3_stmt *);
  int i = s48_extract_long_2 (c, column);

  /* . */
  return
    s48_enter_byte_string_2 (c, sqlite3_column_name (p, i));
}

static x48_ref
sqlite3_s48_column_blob (x48_call c,
                         x48_ref prepared_ptr, x48_ref column)
{
  sqlite3_stmt *p
    = s48_unsafe_extract_value_2 (c, prepared_ptr, sqlite3_stmt *);
  int i = s48_extract_long_2 (c, column);
  int         l = sqlite3_column_bytes (p, i);
  const void *b = sqlite3_column_blob  (p, i);
  x48_ref v;

  v = s48_enter_byte_vector_2 (c, b, l);

  /* . */
  return v;
}

/*** sqlite3-int.c ends here */

### Makefile  -*- Makefile -*-
CFLAGS    = -g -Wall
CPPFLAGS  = -I$(HOME)/include
LDFLAGS   = -lsqlite3
SHARED_LDFLAGS = -shared $(LDFLAGS)

default: sqlite3-int.so

sqlite3-int.so: sqlite3-int.o
        $(CC) $(CFLAGS) -o $@ $^ $(SHARED_LDFLAGS)
### Makefile ends here
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.