Skip to content

Commit 6f175f4

Browse files
authored
Merge pull request #7 from mildred/authorizer
Implement authorizer and add ability to run dynamic SQL statements
2 parents b3a6871 + 267bb79 commit 6f175f4

File tree

2 files changed

+242
-4
lines changed

2 files changed

+242
-4
lines changed

src/easy_sqlite3.nim

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
import easy_sqlite3/[bindings,macros]
22
export macros
33

4-
export raw, len, toOpenArray, SQLiteError, SQLiteBlob, Statement, Database, OpenFlag, enableSharedCache, initDatabase, exec, execM, changes, lastInsertRowid, `[]=`, reset, step, withColumnBlob, getParameterIndex, getColumnType, getColumn, unpack, `=destroy`
4+
export raw, len, toOpenArray, SQLiteError, SQLiteBlob, Statement, Database,
5+
SqliteDataType, OpenFlag, enableSharedCache, initDatabase, exec, execM,
6+
changes, lastInsertRowid, `[]=`, reset, step, withColumnBlob,
7+
getParameterIndex, getColumnType, getColumn, ColumnDef, columns, `[]`,
8+
unpack, `=destroy`, newStatement, rows, setAuthorizer,
9+
AuthorizerActionCode, AuthorizerRequest, AuthorizerResult, RawAuthorizer,
10+
Authorizer

src/easy_sqlite3/bindings.nim

+235-3
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,102 @@ func compileTimeHash[T](original: static[T]): CachedHash[T] =
6060
type Statement* = object
6161
raw*: ptr RawStatement
6262

63+
type
64+
AuthorizerResult* {.pure, size: sizeof(cint).} = enum
65+
ok = 0,
66+
deny = 1,
67+
ignore = 2
68+
AuthorizerActionCode* {.pure, size: sizeof(cint).} = enum
69+
# action code # arg3 arg4
70+
copy = 0, # No longer used
71+
create_index = 1, # Index Name Table Name
72+
create_table = 2, # Table Name NULL
73+
create_temp_index = 3, # Index Name Table Name
74+
create_temp_table = 4, # Table Name NULL
75+
create_temp_trigger = 5, # Trigger Name Table Name
76+
create_temp_view = 6, # View Name NULL
77+
create_trigger = 7, # Trigger Name Table Name
78+
create_view = 8, # View Name NULL
79+
delete = 9, # Table Name NULL
80+
drop_index = 10, # Index Name Table Name
81+
drop_table = 11, # Table Name NULL
82+
drop_temp_index = 12, # Index Name Table Name
83+
drop_temp_table = 13, # Table Name NULL
84+
drop_temp_trigger = 14, # Trigger Name Table Name
85+
drop_temp_view = 15, # View Name NULL
86+
drop_trigger = 16, # Trigger Name Table Name
87+
drop_view = 17, # View Name NULL
88+
insert = 18, # Table Name NULL
89+
pragma = 19, # Pragma Name 1st arg or NULL
90+
read = 20, # Table Name Column Name
91+
select = 21, # NULL NULL
92+
transaction = 22, # Operation NULL
93+
update = 23, # Table Name Column Name
94+
attach = 24, # Filename NULL
95+
detach = 25, # Database Name NULL
96+
alter_table = 26, # Database Name Table Name
97+
reindex = 27, # Index Name NULL
98+
analyze = 28, # Table Name NULL
99+
create_vtable = 29, # Table Name Module Name
100+
drop_vtable = 30, # Table Name Module Name
101+
function = 31, # NULL Function Name
102+
savepoint = 32, # Operation Savepoint Name
103+
recursive = 33, # NULL NULL
104+
AuthorizerRequest* = ref object
105+
case action_code*: AuthorizerActionCode
106+
of create_index, create_temp_index, drop_index, drop_temp_index:
107+
index_name*: string
108+
index_table_name*: string
109+
of create_table, create_temp_table, delete, drop_table, drop_temp_table, insert, analyze:
110+
table_name*: string
111+
of create_temp_trigger, create_trigger, drop_temp_trigger, drop_trigger:
112+
trigger_name*: string
113+
trigger_table_name*: string
114+
of create_temp_view, create_view, drop_temp_view, drop_view:
115+
view_name*: string
116+
of pragma:
117+
pragma_name*: string
118+
pragma_arg*: Option[string]
119+
of read, update:
120+
target_table_name*: string
121+
column_name*: string
122+
of select, recursive, copy:
123+
discard
124+
of transaction:
125+
transaction_operation*: string
126+
of attach:
127+
filename*: string
128+
of detach:
129+
database_name*: string
130+
of alter_table:
131+
alter_database_name*: string
132+
alter_table_name*: string
133+
of reindex:
134+
reindex_index_name*: string
135+
of create_vtable, drop_vtable:
136+
vtable_name*: string
137+
module_name*: string
138+
of function:
139+
# no arg3
140+
function_name*: string
141+
of savepoint:
142+
savepoint_operation*: string
143+
savepoint_name*: string
144+
SqliteRawAuthorizer* = proc(
145+
userdata: pointer,
146+
action_code: AuthorizerActionCode,
147+
arg3, arg4, arg5, arg6: cstring): AuthorizerResult {.cdecl.}
148+
RawAuthorizer* = proc(
149+
action_code: AuthorizerActionCode,
150+
arg3, arg4, arg5, arg6: Option[string]): AuthorizerResult
151+
Authorizer* = proc(request: AuthorizerRequest): AuthorizerResult
152+
WrapAuthorizer = object
153+
authorizer: RawAuthorizer
154+
63155
type Database* = object
64156
raw*: ptr RawDatabase
65157
stmtcache: Table[CachedHash[string], ref Statement]
158+
authorizer: ref WrapAuthorizer
66159

67160
type ResultCode* {.pure.} = enum
68161
sr_ok = 0,
@@ -224,7 +317,7 @@ type SqliteDestroctor* = proc (p: pointer) {.cdecl.}
224317
const StaticDestructor* = cast[SqliteDestroctor](0)
225318
const TransientDestructor* = cast[SqliteDestroctor](-1)
226319

227-
type SqliteDateType* = enum
320+
type SqliteDataType* = enum
228321
dt_integer = 1,
229322
dt_float = 2,
230323
dt_text = 3,
@@ -374,6 +467,7 @@ proc sqlite3_prepare_v3*(db: ptr RawDatabase, sql: cstring, nbyte: int, flags: P
374467
proc sqlite3_finalize*(st: ptr RawStatement): ResultCode {.sqlite3linkage.}
375468
proc sqlite3_reset*(st: ptr RawStatement): ResultCode {.sqlite3linkage.}
376469
proc sqlite3_step*(st: ptr RawStatement): ResultCode {.sqlite3linkage.}
470+
proc sqlite3_set_authorizer*(db: ptr RawDatabase, auth: SqliteRawAuthorizer, userdata: pointer): ResultCode {.sqlite3linkage.}
377471
proc sqlite3_bind_parameter_index*(st: ptr RawStatement, name: cstring): int {.sqlite3linkage.}
378472
proc sqlite3_bind_blob64*(st: ptr RawStatement, idx: int, buffer: pointer, len: int, free: SqliteDestroctor): ResultCode {.sqlite3linkage.}
379473
proc sqlite3_bind_double*(st: ptr RawStatement, idx: int, value: float64): ResultCode {.sqlite3linkage.}
@@ -385,7 +479,9 @@ proc sqlite3_bind_pointer*(st: ptr RawStatement, idx: int, val: pointer, name: c
385479
proc sqlite3_bind_zeroblob64*(st: ptr RawStatement, idx: int, len: int): ResultCode {.sqlite3linkage.}
386480
proc sqlite3_changes*(st: ptr RawDatabase): int {.sqlite3linkage.}
387481
proc sqlite3_last_insert_rowid*(st: ptr RawDatabase): int {.sqlite3linkage.}
388-
proc sqlite3_column_type*(st: ptr RawStatement, idx: int): SqliteDateType {.sqlite3linkage.}
482+
proc sqlite3_column_count*(st: ptr RawStatement): int {.sqlite3linkage.}
483+
proc sqlite3_column_type*(st: ptr RawStatement, idx: int): SqliteDataType {.sqlite3linkage.}
484+
proc sqlite3_column_name*(st: ptr RawStatement, idx: int): cstring {.sqlite3linkage.}
389485
proc sqlite3_column_blob*(st: ptr RawStatement, idx: int): pointer {.sqlite3linkage.}
390486
proc sqlite3_column_bytes*(st: ptr RawStatement, idx: int): int {.sqlite3linkage.}
391487
proc sqlite3_column_double*(st: ptr RawStatement, idx: int): float64 {.sqlite3linkage.}
@@ -442,6 +538,107 @@ proc initDatabase*(
442538
sqliteCheck sqlite3_open_v2(filename, addr result.raw, flags, vfs)
443539
result.stmtcache = initTable[CachedHash[string], ref Statement]()
444540

541+
proc toS(s: cstring): Option[string] =
542+
if s == nil:
543+
result = none(string)
544+
else:
545+
result = some($s)
546+
547+
proc setAuthorizer*(db: var Database, callback: RawAuthorizer = nil) =
548+
let userdata: ref WrapAuthorizer = new(WrapAuthorizer)
549+
userdata.authorizer = callback
550+
551+
proc raw_callback(
552+
userdata: pointer,
553+
action_code: AuthorizerActionCode,
554+
arg3, arg4, arg5, arg6: cstring): AuthorizerResult {.cdecl.} =
555+
let callback = cast[ref WrapAuthorizer](userdata).authorizer
556+
callback(action_code, arg3.toS(), arg4.toS(), arg5.toS(), arg6.toS())
557+
558+
var res: ResultCode
559+
if callback == nil:
560+
res = db.raw.sqlite3_set_authorizer(nil, nil)
561+
else:
562+
res = db.raw.sqlite3_set_authorizer(raw_callback, cast[pointer](userdata))
563+
db.authorizer = userdata
564+
if res != ResultCode.sr_ok:
565+
raise newSQLiteError res
566+
567+
proc setAuthorizer*(db: var Database, callback: Authorizer = nil) =
568+
var raw_callback: RawAuthorizer = nil
569+
if callback != nil:
570+
raw_callback = proc(code: AuthorizerActionCode, arg3, arg4, arg5, arg6: Option[string]): AuthorizerResult =
571+
var req: AuthorizerRequest
572+
case code
573+
of create_index, create_temp_index, drop_index, drop_temp_index:
574+
req = AuthorizerRequest(
575+
action_code: code,
576+
index_name: arg3.get,
577+
index_table_name: arg4.get)
578+
of create_table, create_temp_table, delete, drop_table, drop_temp_table, insert, analyze:
579+
req = AuthorizerRequest(
580+
action_code: code,
581+
table_name: arg3.get)
582+
of create_temp_trigger, create_trigger, drop_temp_trigger, drop_trigger:
583+
req = AuthorizerRequest(
584+
action_code: code,
585+
trigger_name: arg3.get,
586+
trigger_table_name: arg4.get)
587+
of create_temp_view, create_view, drop_temp_view, drop_view:
588+
req = AuthorizerRequest(
589+
action_code: code,
590+
view_name: arg3.get)
591+
of pragma:
592+
req = AuthorizerRequest(
593+
action_code: code,
594+
pragma_name: arg3.get,
595+
pragma_arg: arg4)
596+
of read, update:
597+
req = AuthorizerRequest(
598+
action_code: code,
599+
target_table_name: arg3.get,
600+
column_name: arg4.get)
601+
of select, recursive, copy:
602+
req = AuthorizerRequest(action_code: code)
603+
of transaction:
604+
req = AuthorizerRequest(
605+
action_code: code,
606+
transaction_operation: arg3.get)
607+
of attach:
608+
req = AuthorizerRequest(
609+
action_code: code,
610+
filename: arg3.get)
611+
of detach:
612+
req = AuthorizerRequest(
613+
action_code: code,
614+
database_name: arg3.get)
615+
of alter_table:
616+
req = AuthorizerRequest(
617+
action_code: code,
618+
alter_database_name: arg3.get,
619+
alter_table_name: arg4.get)
620+
of reindex:
621+
req = AuthorizerRequest(
622+
action_code: code,
623+
reindex_index_name: arg3.get)
624+
of create_vtable, drop_vtable:
625+
req = AuthorizerRequest(
626+
action_code: code,
627+
vtable_name: arg3.get,
628+
module_name: arg4.get)
629+
of function:
630+
req = AuthorizerRequest(
631+
action_code: code,
632+
# no arg3
633+
function_name: arg4.get)
634+
of savepoint:
635+
req = AuthorizerRequest(
636+
action_code: code,
637+
savepoint_operation: arg3.get,
638+
savepoint_name: arg4.get)
639+
return callback(req)
640+
db.setAuthorizer(raw_callback)
641+
445642
proc changes*(st: var Database): int =
446643
sqlite3_changes st.raw
447644

@@ -497,6 +694,9 @@ proc `[]=`*[T](st: ref Statement, idx: int, val: Option[T]) =
497694
else:
498695
st[idx] = val.get
499696

697+
proc `[]=`*[T](st: ref Statement, name: string, value: T) =
698+
st[st.getParameterIndex(name)] = value
699+
500700
proc reset*(st: ref Statement) =
501701
st.raw.sqliteCheck sqlite3_reset(st.raw)
502702

@@ -515,7 +715,7 @@ proc withColumnBlob*(st: ref Statement, idx: int, recv: proc(vm: openarray[byte]
515715
let l = sqlite3_column_bytes(st.raw, idx)
516716
recv(cast[ptr UncheckedArray[byte]](p).toOpenArray(0, l))
517717

518-
proc getColumnType*(st: ref Statement, idx: int): SqliteDateType =
718+
proc getColumnType*(st: ref Statement, idx: int): SqliteDataType =
519719
sqlite3_column_type(st.raw, idx)
520720

521721
proc getColumn*(st: ref Statement, idx: int, T: typedesc[seq[byte]]): seq[byte] =
@@ -542,6 +742,31 @@ proc getColumn*[T](st: ref Statement, idx: int, _: typedesc[Option[T]]): Option[
542742
else:
543743
some(st.getColumn(idx, T))
544744

745+
type ColumnDef* = object
746+
st*: ref Statement
747+
idx*: int
748+
data_type*: SqliteDataType
749+
name*: string
750+
751+
proc columns*(st: ref Statement): seq[ref ColumnDef] =
752+
result = @[]
753+
var idx = 0
754+
let count = sqlite3_column_count(st.raw)
755+
while idx < count:
756+
let col = new(ColumnDef)
757+
col.st = st
758+
col.idx = idx
759+
col.data_type = sqlite3_column_type(st.raw, idx)
760+
col.name = $sqlite3_column_name(st.raw, idx)
761+
result.add(col)
762+
idx += 1
763+
764+
proc `[]`*(st: ref Statement, idx: int): ref ColumnDef =
765+
result = st.columns[idx]
766+
767+
proc `[]`*[T](col: ref ColumnDef, t: typedesc[T]): T =
768+
result = col.st.getColumn(col.idx, t)
769+
545770
proc unpack*[T: tuple](st: ref Statement, _: typedesc[T]): T =
546771
var idx = 0
547772
for value in result.fields:
@@ -568,3 +793,10 @@ proc execM*(db: var Database, sqls: varargs[string]) {.discardable.} =
568793
except CatchableError:
569794
discard db.exec "ROLLBACK"
570795
raise getCurrentException()
796+
797+
iterator rows*(st: ref Statement): seq[ref ColumnDef] =
798+
try:
799+
while st.step():
800+
yield st.columns()
801+
finally:
802+
st.reset()

0 commit comments

Comments
 (0)