Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
053d505988 | |||
1b87e7fec1 | |||
a689c3ec85 | |||
f99c5dc329 | |||
|
f037790d07 | ||
|
8818ec83ab | ||
262aacc0a9 | |||
2386599913 | |||
21e74f82e3 | |||
9743ac15d9 | |||
e239ac2b7f |
@@ -6,7 +6,7 @@ everything, but it tries to be useful.
|
||||
## Installation
|
||||
|
||||
```clojure
|
||||
(load "https://veitheller.de/git/carpentry/sqlite3@0.0.1")
|
||||
(load "git@git.veitheller.de:carpentry/sqlite3.git@0.0.6")
|
||||
```
|
||||
|
||||
## Usage
|
||||
@@ -15,7 +15,7 @@ The module `SQLite3` provides facilities for opening, closing, and querying
|
||||
databases.
|
||||
|
||||
```clojure
|
||||
(load "https://veitheller.de/git/carpentry/sqlite3@0.0.1")
|
||||
(load "git@git.veitheller.de:carpentry/sqlite3.git@0.0.6")
|
||||
|
||||
; opening DBs can fail, for the purposes of this example we
|
||||
; ignore that
|
||||
|
@@ -9,8 +9,8 @@
|
||||
<body>
|
||||
<div class="content">
|
||||
<div class="logo">
|
||||
<a href="http://github.com/carp-lang/Carp">
|
||||
<img src="logo.png">
|
||||
<a href="">
|
||||
<img src="">
|
||||
</a>
|
||||
<div class="title">
|
||||
sqlite3
|
||||
@@ -18,148 +18,180 @@
|
||||
<div class="index">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="SQLite3.html">
|
||||
SQLite3
|
||||
</a>
|
||||
<details>
|
||||
<summary>
|
||||
<a href="SQLite3.html">
|
||||
SQLite3
|
||||
</a>
|
||||
</summary>
|
||||
<ul>
|
||||
<li>
|
||||
<details>
|
||||
<summary>
|
||||
<a href="SQLite3.Type.html">
|
||||
Type
|
||||
</a>
|
||||
</summary>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="SQLite3.Type.SQLiteColumn.html">
|
||||
SQLiteColumn
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h1>
|
||||
SQLite3
|
||||
</h1>
|
||||
<div class="module-description">
|
||||
<p>is a simple high-level wrapper around SQLite3. It doesn’t intend
|
||||
<div class="module">
|
||||
<h1>
|
||||
SQLite3
|
||||
</h1>
|
||||
<div class="module-description">
|
||||
<p>is a simple high-level wrapper around SQLite3. It doesn’t intend
|
||||
to wrap everything, but it tries to be useful.</p>
|
||||
<h2>Installation</h2>
|
||||
<pre><code class="language-clojure">(load "https://veitheller.de/git/carpentry/sqlite3@0.0.1")
|
||||
<pre><code class="language-clojure">(load "git@veitheller.de:git/carpentry/sqlite3.git@0.0.6")
|
||||
</code></pre>
|
||||
<h2>Usage</h2>
|
||||
<p>The module <code>SQLite3</code> provides facilities for opening, closing, and querying
|
||||
databases.</p>
|
||||
<pre><code class="language-clojure">(load "https://veitheller.de/git/carpentry/sqlite3@0.0.1")
|
||||
<pre><code class="language-clojure">(load "git@veitheller.de:git/carpentry/sqlite3.git@0.0.6")
|
||||
|
||||
; opening DBs can fail, for the purposes of this example we
|
||||
; ignore that
|
||||
(let-do [db (Result.unsafe-from-success (SQLite3.open "db"))]
|
||||
; we can prepare statements
|
||||
(println* &(SQLite3.query &db "INSERT INTO mytable VALUES (?1, ?2);"
|
||||
&[(to-sqlite3 @"hello") (to-sqlite3 100)]))
|
||||
; and query things
|
||||
(println* &(SQLite3.query &db "SELECT * from mytable;" &[]))
|
||||
(SQLite3.close db)
|
||||
(defn main []
|
||||
(let-do [db (Result.unsafe-from-success (SQLite3.open "db"))]
|
||||
; Let's make sure our table is there
|
||||
(ignore
|
||||
(SQLite3.query &db
|
||||
"CREATE TABLE IF NOT EXISTS mytable (name TEXT, age INT)"
|
||||
&[]))
|
||||
|
||||
; we can prepare statements
|
||||
(ignore
|
||||
(SQLite3.query &db
|
||||
"INSERT INTO mytable VALUES (?1, ?2);"
|
||||
&[(to-sqlite3 @"Carp") (to-sqlite3 4)]))
|
||||
|
||||
; and query things
|
||||
(println* &(SQLite3.query &db "SELECT * from mytable;" &[]))
|
||||
(SQLite3.close db)))
|
||||
</code></pre>
|
||||
<p>Because <code>open</code> and <code>query</code> return <code>Result</code> types, we could also use
|
||||
combinators!</p>
|
||||
|
||||
</div>
|
||||
<div class="binder">
|
||||
<a class="anchor" href="#SQLite">
|
||||
<h3 id="SQLite">
|
||||
SQLite
|
||||
</h3>
|
||||
</a>
|
||||
<div class="description">
|
||||
doc-stub
|
||||
</div>
|
||||
<p class="sig">
|
||||
a
|
||||
</p>
|
||||
<span>
|
||||
|
||||
</span>
|
||||
<p class="doc">
|
||||
<p>is the opaque database type. You’ll need one of those to query
|
||||
<div class="binder">
|
||||
<a class="anchor" href="#SQLite">
|
||||
<h3 id="SQLite">
|
||||
SQLite
|
||||
</h3>
|
||||
</a>
|
||||
<div class="description">
|
||||
meta-stub
|
||||
</div>
|
||||
<p class="sig">
|
||||
a
|
||||
</p>
|
||||
<span>
|
||||
|
||||
</span>
|
||||
<p class="doc">
|
||||
<p>is the opaque database type. You’ll need one of those to query
|
||||
anything.</p>
|
||||
<p>It can be obtained by using <a href="#open">open</a>.</p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
<div class="binder">
|
||||
<a class="anchor" href="#Type">
|
||||
<h3 id="Type">
|
||||
Type
|
||||
</h3>
|
||||
</a>
|
||||
<div class="description">
|
||||
module
|
||||
</p>
|
||||
</div>
|
||||
<p class="sig">
|
||||
Module
|
||||
</p>
|
||||
<span>
|
||||
|
||||
</span>
|
||||
<p class="doc">
|
||||
<p>represent all the SQLite types we can represent.</p>
|
||||
<p>The constructors are <code>Null</code>, <code>Integer</code>, <code>Floating</code>, <code>Text</code>, and <code>Blob</code>. Most
|
||||
primitive Carp types can be casted to appropriate SQLite types by using the
|
||||
<code>to-sqlite3</code> interface.</p>
|
||||
<div class="binder">
|
||||
<a class="anchor" href="#Type">
|
||||
<h3 id="Type">
|
||||
<a href="SQLite3.Type.html">
|
||||
Type
|
||||
</a>
|
||||
</h3>
|
||||
</a>
|
||||
<div class="description">
|
||||
module
|
||||
</div>
|
||||
<p class="sig">
|
||||
Module
|
||||
</p>
|
||||
<span>
|
||||
|
||||
</span>
|
||||
<p class="doc">
|
||||
|
||||
</p>
|
||||
</div>
|
||||
<div class="binder">
|
||||
<a class="anchor" href="#close">
|
||||
<h3 id="close">
|
||||
close
|
||||
</h3>
|
||||
</a>
|
||||
<div class="description">
|
||||
external
|
||||
</div>
|
||||
<p class="sig">
|
||||
(Fn [SQLite] ())
|
||||
</p>
|
||||
<span>
|
||||
|
||||
</span>
|
||||
<p class="doc">
|
||||
<p>closes a database.</p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
<div class="binder">
|
||||
<a class="anchor" href="#close">
|
||||
<h3 id="close">
|
||||
close
|
||||
</h3>
|
||||
</a>
|
||||
<div class="description">
|
||||
external
|
||||
</p>
|
||||
</div>
|
||||
<p class="sig">
|
||||
(λ [SQLite] ())
|
||||
</p>
|
||||
<span>
|
||||
|
||||
</span>
|
||||
<p class="doc">
|
||||
<p>closes a database.</p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
<div class="binder">
|
||||
<a class="anchor" href="#open">
|
||||
<h3 id="open">
|
||||
open
|
||||
</h3>
|
||||
</a>
|
||||
<div class="description">
|
||||
defn
|
||||
</div>
|
||||
<p class="sig">
|
||||
(λ [&String] (Result SQLite String))
|
||||
</p>
|
||||
<pre class="args">
|
||||
(open s)
|
||||
</pre>
|
||||
<p class="doc">
|
||||
<p>opens a database with the filename <code>s</code>.</p>
|
||||
<div class="binder">
|
||||
<a class="anchor" href="#open">
|
||||
<h3 id="open">
|
||||
open
|
||||
</h3>
|
||||
</a>
|
||||
<div class="description">
|
||||
defn
|
||||
</div>
|
||||
<p class="sig">
|
||||
(Fn [(Ref String a)] (Result SQLite String))
|
||||
</p>
|
||||
<pre class="args">
|
||||
(open s)
|
||||
</pre>
|
||||
<p class="doc">
|
||||
<p>opens a database with the filename <code>s</code>.</p>
|
||||
<p>If it fails, we return an error message using <code>Result.Error</code>.</p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
<div class="binder">
|
||||
<a class="anchor" href="#query">
|
||||
<h3 id="query">
|
||||
query
|
||||
</h3>
|
||||
</a>
|
||||
<div class="description">
|
||||
defn
|
||||
</p>
|
||||
</div>
|
||||
<p class="sig">
|
||||
(λ [(Ref SQLite), &String, (Ref (Array Type))] (Result (Array (Array Type)) String))
|
||||
</p>
|
||||
<pre class="args">
|
||||
(query db s p)
|
||||
</pre>
|
||||
<p class="doc">
|
||||
<p>queries the database <code>db</code> using the query <code>s</code> and the parameters
|
||||
<div class="binder">
|
||||
<a class="anchor" href="#query">
|
||||
<h3 id="query">
|
||||
query
|
||||
</h3>
|
||||
</a>
|
||||
<div class="description">
|
||||
defn
|
||||
</div>
|
||||
<p class="sig">
|
||||
(Fn [(Ref SQLite a), (Ref String b), (Ref (Array SQLite3.Type) c)] (Result (Array (Array SQLite3.Type)) String))
|
||||
</p>
|
||||
<pre class="args">
|
||||
(query db s p)
|
||||
</pre>
|
||||
<p class="doc">
|
||||
<p>queries the database <code>db</code> using the query <code>s</code> and the parameters
|
||||
<code>p</code>.</p>
|
||||
<p>If it fails, we return an error message using <code>Result.Error</code>.</p>
|
||||
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
15
gendocs.carp
15
gendocs.carp
@@ -1,13 +1,10 @@
|
||||
(load "sqlite3.carp")
|
||||
|
||||
(defndynamic gendocs []
|
||||
(do
|
||||
(Project.config "title" "sqlite3")
|
||||
(Project.config "docs-directory" "./docs/")
|
||||
(Project.config "docs-logo" "")
|
||||
(Project.config "docs-styling" "style.css")
|
||||
(Project.config "docs-generate-index" false)
|
||||
(save-docs SQLite3)))
|
||||
(Project.config "title" "sqlite3")
|
||||
(Project.config "docs-directory" "./docs/")
|
||||
(Project.config "docs-logo" "")
|
||||
(Project.config "docs-styling" "style.css")
|
||||
(Project.config "docs-generate-index" false)
|
||||
(save-docs SQLite3)
|
||||
|
||||
(gendocs)
|
||||
(quit)
|
||||
|
81
sqlite3.carp
81
sqlite3.carp
@@ -7,7 +7,7 @@ to wrap everything, but it tries to be useful.
|
||||
## Installation
|
||||
|
||||
```clojure
|
||||
(load \"https://veitheller.de/git/carpentry/sqlite3@0.0.1\")
|
||||
(load \"git@veitheller.de:git/carpentry/sqlite3.git@0.0.6\")
|
||||
```
|
||||
|
||||
## Usage
|
||||
@@ -16,17 +16,27 @@ The module `SQLite3` provides facilities for opening, closing, and querying
|
||||
databases.
|
||||
|
||||
```clojure
|
||||
(load \"https://veitheller.de/git/carpentry/sqlite3@0.0.1\")
|
||||
(load \"git@veitheller.de:git/carpentry/sqlite3.git@0.0.6\")
|
||||
|
||||
; opening DBs can fail, for the purposes of this example we
|
||||
; ignore that
|
||||
(let-do [db (Result.unsafe-from-success (SQLite3.open \"db\"))]
|
||||
; we can prepare statements
|
||||
(println* &(SQLite3.query &db \"INSERT INTO mytable VALUES (?1, ?2);\"
|
||||
&[(to-sqlite3 @\"hello\") (to-sqlite3 100)]))
|
||||
; and query things
|
||||
(println* &(SQLite3.query &db \"SELECT * from mytable;\" &[]))
|
||||
(SQLite3.close db)
|
||||
(defn main []
|
||||
(let-do [db (Result.unsafe-from-success (SQLite3.open \"db\"))]
|
||||
; Let's make sure our table is there
|
||||
(ignore
|
||||
(SQLite3.query &db
|
||||
\"CREATE TABLE IF NOT EXISTS mytable (name TEXT, age INT)\"
|
||||
&[]))
|
||||
|
||||
; we can prepare statements
|
||||
(ignore
|
||||
(SQLite3.query &db
|
||||
\"INSERT INTO mytable VALUES (?1, ?2);\"
|
||||
&[(to-sqlite3 @\"Carp\") (to-sqlite3 4)]))
|
||||
|
||||
; and query things
|
||||
(println* &(SQLite3.query &db \"SELECT * from mytable;\" &[]))
|
||||
(SQLite3.close db)))
|
||||
```
|
||||
|
||||
Because `open` and `query` return `Result` types, we could also use
|
||||
@@ -64,8 +74,7 @@ primitive Carp types can be casted to appropriate SQLite types by using the
|
||||
(Integer [Int])
|
||||
(Floating [Double])
|
||||
(Text [String])
|
||||
(Blob [String])
|
||||
)
|
||||
(Blob [String]))
|
||||
|
||||
(private SQLiteColumn)
|
||||
(hidden SQLiteColumn)
|
||||
@@ -77,8 +86,11 @@ primitive Carp types can be casted to appropriate SQLite types by using the
|
||||
(register int (Fn [Int] SQLiteColumn) "SQLiteColumn_int")
|
||||
(register float (Fn [Double] SQLiteColumn) "SQLiteColumn_float")
|
||||
(register text (Fn [String] SQLiteColumn) "SQLiteColumn_text")
|
||||
(register blob (Fn [String] SQLiteColumn) "SQLiteColumn_blob")
|
||||
)
|
||||
(register blob (Fn [String] SQLiteColumn) "SQLiteColumn_blob"))
|
||||
|
||||
(defn prn [s]
|
||||
(SQLite3.Type.str s))
|
||||
(implements prn SQLite3.Type.prn)
|
||||
|
||||
(defn to-sqlite3-internal [x]
|
||||
(match x
|
||||
@@ -86,8 +98,7 @@ primitive Carp types can be casted to appropriate SQLite types by using the
|
||||
(Integer i) (SQLiteColumn.int i)
|
||||
(Floating f) (SQLiteColumn.float f)
|
||||
(Text s) (SQLiteColumn.text s)
|
||||
(Blob s) (SQLiteColumn.blob s)))
|
||||
)
|
||||
(Blob s) (SQLiteColumn.blob s))))
|
||||
|
||||
(defmodule SQLiteColumn
|
||||
(register tag (Fn [&SQLiteColumn] Int) "SQLiteColumn_tag")
|
||||
@@ -116,8 +127,7 @@ primitive Carp types can be casted to appropriate SQLite types by using the
|
||||
a (Array.allocate l)]
|
||||
(for [i 0 l]
|
||||
(Array.aset-uninitialized! &a i (SQLiteColumn.to-carp (nth &r i))))
|
||||
a))
|
||||
)
|
||||
a)))
|
||||
|
||||
(private SQLiteRes)
|
||||
(hidden SQLiteRes)
|
||||
@@ -126,28 +136,27 @@ primitive Carp types can be casted to appropriate SQLite types by using the
|
||||
(register ok? (Fn [&SQLiteRes] Bool) "SQLiteRes_is_ok")
|
||||
(register length (Fn [&SQLiteRes] Int) "SQLiteRes_length")
|
||||
(register nth (Fn [&SQLiteRes Int] SQLiteRow) "SQLiteRes_nth")
|
||||
(register error (Fn [SQLiteRes] (Ptr Char)) "SQLiteRes_error")
|
||||
(register error (Fn [SQLiteRes] (Ptr CChar)) "SQLiteRes_error")
|
||||
|
||||
(defn to-array [r]
|
||||
(let-do [l (length &r)
|
||||
a (Array.allocate l)]
|
||||
(for [i 0 l]
|
||||
(Array.aset-uninitialized! &a i (SQLiteRow.to-carp (nth &r i))))
|
||||
a))
|
||||
)
|
||||
a)))
|
||||
|
||||
(private init)
|
||||
(hidden init)
|
||||
(register init (Fn [] SQLite))
|
||||
(private open-)
|
||||
(hidden open-)
|
||||
(register open- (Fn [&SQLite (Ptr Char)] Int) "SQLite3_open_c")
|
||||
(register open- (Fn [&SQLite (Ptr CChar)] Int) "SQLite3_open_c")
|
||||
(private exec-)
|
||||
(hidden exec-)
|
||||
(register exec- (Fn [&SQLite (Ptr Char) (Array SQLiteColumn)] SQLiteRes) "SQLite3_exec_c")
|
||||
(register exec- (Fn [&SQLite (Ptr CChar) &(Array SQLiteColumn)] SQLiteRes) "SQLite3_exec_c")
|
||||
(private error-)
|
||||
(hidden error-)
|
||||
(register error- (Fn [SQLite] (Ptr Char)) "SQLite3_error")
|
||||
(register error- (Fn [SQLite] (Ptr CChar)) "SQLite3_error")
|
||||
|
||||
(doc open "opens a database with the filename `s`.
|
||||
|
||||
@@ -164,31 +173,37 @@ If it fails, we return an error message using `Result.Error`.")
|
||||
|
||||
If it fails, we return an error message using `Result.Error`.")
|
||||
(defn query [db s p]
|
||||
(let [r (exec- db (cstr s) (Array.copy-map &(fn [x] (Type.to-sqlite3-internal @x)) p))]
|
||||
(let [r (exec- db (cstr s) &(Array.copy-map &(fn [x] (Type.to-sqlite3-internal @x)) p))]
|
||||
(if (SQLiteRes.ok? &r)
|
||||
(Result.Success (SQLiteRes.to-array r))
|
||||
(Result.Error (from-cstr (SQLiteRes.error r))))))
|
||||
|
||||
(doc close "closes a database.")
|
||||
(register close (Fn [SQLite] ()) "SQLite3_close_c")
|
||||
)
|
||||
(register close (Fn [SQLite] ()) "SQLite3_close_c"))
|
||||
|
||||
(definterface to-sqlite3 (Fn [a] SQLIte3.Type))
|
||||
(definterface to-sqlite3 (Fn [a] SQLite3.Type))
|
||||
|
||||
(defmodule Bool
|
||||
(defn to-sqlite3 [b] (SQLite3.Type.Integer (if b 1 0))))
|
||||
(defn to-sqlite3 [b] (SQLite3.Type.Integer (if b 1 0)))
|
||||
(implements to-sqlite3 Bool.to-sqlite3))
|
||||
|
||||
(defmodule Int
|
||||
(defn to-sqlite3 [i] (SQLite3.Type.Integer i)))
|
||||
(defn to-sqlite3 [i] (SQLite3.Type.Integer i))
|
||||
(implements to-sqlite3 Int.to-sqlite3))
|
||||
|
||||
(defmodule Long
|
||||
(defn to-sqlite3 [l] (SQLite3.Type.Integer (to-int (the Long l)))))
|
||||
(defn to-sqlite3 [l] (SQLite3.Type.Integer (to-int (the Long l))))
|
||||
(implements to-sqlite3 Long.to-sqlite3))
|
||||
|
||||
(defmodule Float
|
||||
(defn to-sqlite3 [f] (SQLite3.Type.Floating (Double.from-float f))))
|
||||
(defn to-sqlite3 [f] (SQLite3.Type.Floating (Double.from-float f)))
|
||||
(implements to-sqlite3 Float.to-sqlite3))
|
||||
|
||||
(defmodule Double
|
||||
(defn to-sqlite3 [d] (SQLite3.Type.Floating d)))
|
||||
(defn to-sqlite3 [d] (SQLite3.Type.Floating d))
|
||||
(implements to-sqlite3 Double.to-sqlite3))
|
||||
|
||||
(defmodule String
|
||||
(defn to-sqlite3 [s] (SQLite3.Type.Text s)))
|
||||
(defn to-sqlite3 [s] (SQLite3.Type.Text s))
|
||||
(implements to-sqlite3 String.to-sqlite3))
|
||||
|
||||
|
@@ -176,14 +176,14 @@ const char* SQLite3_exec_internal(sqlite3_stmt* s, SQLiteRows* rows) {
|
||||
break;
|
||||
case SQLITE_TEXT: {
|
||||
len = sqlite3_column_bytes(s, i);
|
||||
c->s = CARP_MALLOC(len);
|
||||
c->s = CARP_MALLOC(len+1);
|
||||
memcpy(c->s, sqlite3_column_text(s, i), len);
|
||||
c->s[len] = '\0';
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
len = sqlite3_column_bytes(s, i);
|
||||
c->s = CARP_MALLOC(len);
|
||||
c->s = CARP_MALLOC(len+1);
|
||||
memcpy(c->s, sqlite3_column_blob(s, i), len);
|
||||
c->s[len] = '\0';
|
||||
break;
|
||||
@@ -218,12 +218,12 @@ static const char* SQLite3_exec_ignore(sqlite3_stmt* s) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* SQLite3_bind(sqlite3_stmt* s, Array p) {
|
||||
const char* SQLite3_bind(sqlite3_stmt* s, Array* p) {
|
||||
int res;
|
||||
const char* err = NULL;
|
||||
|
||||
for (int i = 0; i < p.len; i++) {
|
||||
SQLiteColumn val = ((SQLiteColumn*)p.data)[i];
|
||||
for (int i = 0; i < p->len; i++) {
|
||||
SQLiteColumn val = ((SQLiteColumn*)p->data)[i];
|
||||
|
||||
switch (val.tag) {
|
||||
case SQLITE_NULL:
|
||||
@@ -252,7 +252,7 @@ const char* SQLite3_bind(sqlite3_stmt* s, Array p) {
|
||||
return err;
|
||||
}
|
||||
|
||||
SQLiteRes SQLite3_exec_c(SQLite* db, const char* stmt, Array p) {
|
||||
SQLiteRes SQLite3_exec_c(SQLite* db, const char* stmt, Array* p) {
|
||||
sqlite3_stmt* s = NULL;
|
||||
sqlite3_stmt* n = NULL;
|
||||
const char* err;
|
||||
|
Reference in New Issue
Block a user