-
Notifications
You must be signed in to change notification settings - Fork 0
/
sqlite-scheme.reb
146 lines (135 loc) · 3.49 KB
/
sqlite-scheme.reb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
Rebol [
title: "SQLite scheme (WIP)"
file: %sqlite-scheme.reb
note: {This is just an initial proof of concept}
version: 0.1.0
author: "Oldes"
needs: [
3.13.1 ;; using system/options/modules as extension location
sqlite
]
]
sys/make-scheme [
title: "SQLite database scheme"
name: 'sqlite
spec: make system/standard/port-spec-file []
sqlite: import sqlite
actor: [
open: func [
port[port! url!]
/new
/local path
][
;? port/spec
path: rejoin [
any [select port/spec 'path %./]
any [select port/spec 'target %.db]
]
port/spec/path: copy path: clean-path path
if all [not new not exists? path][
cause-error 'Access 'cannot-open reduce [path "file not exists!"]
]
;; SQLite expect full path in the local format (C:/ on Windows)
;; but Rebol's open function does not accept string...
;; so do this strange thing to get over it
all [
system/platform = 'Windows
path: as file! to-local-file path
]
port/state: make object! [
db: sqlite/open path ;; used to store a database handle
statements: make map! 0 ;; prepared statements
query: ;; last used query
stmt: none ;; last prepared statement
trace-level: 0
]
return port
]
open?: func [port [port!]][
handle? port/state/db
]
close: func [port [port!] /local state][
unless open? port [ cause-error 'Access 'not-open port/spec/ref ]
state: port/state
sqlite/close state/db
foreach [query stmt] state/statements [
sqlite/finalize stmt
]
clear state/statements
state/db:
state/query:
state/stmt: none
port
]
;; WRITE now just executes a query... no result is collected, but may be printed in console
write: func[port [port!] query [string!]][
unless open? port [ cause-error 'Access 'not-open port/spec/ref ]
sqlite/exec port/state/db port/state/query: query
]
;; INSERT is used to prepare a statement, which is then used with other actions
insert: func[port [port!] query [string!] /local ps stmt][
unless open? port [ cause-error 'Access 'not-open port/spec/ref ]
ps: port/state
ps/query: query
either stmt: select ps/statements query [
;; make sure that the statement starts from begining
sqlite/reset stmt
][
;; prepare the new statement and store it for later use
stmt: sqlite/prepare ps/db query
ps/statements/:query: stmt
]
ps/stmt: stmt
port
]
;; TAKE used to get just a single row (or multiple)
take: func[
port [port!]
/part length [integer!]
][
read/part port any [length 1]
]
;; READ used to get all rows if used without refinement (or multiple when used with /part)
read: func[
port [port!]
/part length [integer!]
/local stmt temp data
][
unless open? port [ cause-error 'Access 'not-open port/spec/ref ]
stmt: port/state/stmt
port/data: data: clear any [port/data []]
temp: sqlite/step/rows stmt any [length 0]
either block? temp [
append data temp
unless part [
;; gets all rows
while [
block? temp: sqlite/step/rows stmt 0
][ append data temp ]
sqlite/reset stmt
]
][
return none
]
data
]
;; PICK is a shortcut for READ INSERT "query"
pick: func[
port [port!]
query [string!]
][
read insert port query
]
modify: func[
port [port!]
field [word!]
value [integer!]
][
switch field [
trace-level [
sqlite/trace port/state/db port/state/trace-level: value
]
]
]
]
]