Skip to content

Commit d79d90b

Browse files
committed
Basically working, but type failures
1 parent d7ea36b commit d79d90b

File tree

2 files changed

+202
-53
lines changed

2 files changed

+202
-53
lines changed

sqlite3_session.go

Lines changed: 96 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -58,111 +58,156 @@ func (s *Session) AttachSession(tableName string) error {
5858
// Delete deletes a session object.
5959
func (s *Session) DeleteSession() error {
6060
if s.session != nil {
61-
// Call sqlite3session_delete to free the session object
6261
C.sqlite3session_delete(s.session)
63-
s.session = nil // Set session to nil to avoid double deletion
62+
s.session = nil
6463
}
6564
return nil
6665
}
6766

67+
// Changeset represents a changeset object.
6868
type Changeset struct {
69-
cs []byte
69+
b []byte
7070
}
7171

72-
type ChangesetIterator struct {
73-
iter *C.sqlite3_changeset_iter
74-
}
75-
76-
// Changeset generates a changeset from a session object.
77-
func (s *Session) Changeset() (*Changeset, error) {
72+
// NewChangeset returns a changeset from a session object.
73+
func NewChangeset(s *Session) (*Changeset, error) {
7874
var nChangeset C.int
7975
var pChangeset unsafe.Pointer
8076

81-
// Call sqlite3session_changeset
8277
rc := C.sqlite3session_changeset(s.session, &nChangeset, &pChangeset)
8378
if rc != C.SQLITE_OK {
8479
return nil, fmt.Errorf("sqlite3session_changeset: %s", C.GoString(C.sqlite3_errstr(rc)))
8580
}
86-
defer C.sqlite3_free(pChangeset) // Free the changeset buffer after use
81+
defer C.sqlite3_free(pChangeset)
8782

88-
// copy the changeset buffer to a Go byte slice, because cgo can nuke its memory at any time
83+
// Copy the changeset buffer to a Go byte slice, because cgo
84+
// does not support Go slices with C memory.
8985
changeset := C.GoBytes(pChangeset, nChangeset)
90-
return &Changeset{cs: changeset}, nil
86+
return &Changeset{b: changeset}, nil
9187
}
9288

93-
// ChangesetStart creates and initializes a changeset iterator.
94-
func ChangesetStart(changeset []byte) (*ChangesetIterator, error) {
95-
var iter *C.sqlite3_changeset_iter
89+
// ChangesetIterator represents a changeset iterator object.
90+
type ChangesetIterator struct {
91+
iter *C.sqlite3_changeset_iter
92+
}
9693

97-
// Call sqlite3changeset_start
98-
rc := C.sqlite3changeset_start(&iter, C.int(len(changeset)), unsafe.Pointer(&changeset[0]))
94+
// NewChangesetIterator creates a new changeset iterator object.
95+
func NewChangesetIterator(cs *Changeset) (*ChangesetIterator, error) {
96+
var iter *C.sqlite3_changeset_iter
97+
ptr := unsafe.Pointer(nil)
98+
if len(cs.b) > 0 {
99+
ptr = unsafe.Pointer(&cs.b[0])
100+
}
101+
rc := C.sqlite3changeset_start(&iter, C.int(len(cs.b)), ptr)
99102
if rc != C.SQLITE_OK {
100103
return nil, fmt.Errorf("sqlite3changeset_start: %s", C.GoString(C.sqlite3_errstr(rc)))
101104
}
102-
103105
return &ChangesetIterator{iter: iter}, nil
104106
}
105107

106-
func (cs *Changeset) Start() (*ChangesetIterator, error) {
107-
return ChangesetStart(cs.cs)
108-
}
109-
110-
// ChangesetNext moves the changeset iterator to the next change.
111-
func (ci *ChangesetIterator) ChangesetNext() (bool, error) {
108+
// Next moves the changeset iterator to the next change.
109+
func (ci *ChangesetIterator) Next() (bool, error) {
112110
rc := C.sqlite3changeset_next(ci.iter)
113111
if rc == C.SQLITE_DONE {
114112
return false, nil // No more changes
115113
}
116-
if rc != C.SQLITE_OK {
114+
if rc != C.SQLITE_ROW {
117115
return false, fmt.Errorf("sqlite3changeset_next: %s", C.GoString(C.sqlite3_errstr(rc)))
118116
}
119117
return true, nil
120118
}
121119

122-
// ChangesetOp returns the type of change (INSERT, UPDATE, or DELETE) that the iterator points to.
123-
func (ci *ChangesetIterator) ChangesetOp() (string, int, int, bool, error) {
120+
// Op returns the current Operation from a Changeset Iterator
121+
func (ci *ChangesetIterator) Op() (tblName string, numCol int, oper int, indirect bool, err error) {
124122
var tableName *C.char
125123
var nCol C.int
126124
var op C.int
127-
var indirect C.int
125+
var ind C.int
128126

129-
rc := C.sqlite3changeset_op(ci.iter, &tableName, &nCol, &op, &indirect)
127+
rc := C.sqlite3changeset_op(ci.iter, &tableName, &nCol, &op, &ind)
130128
if rc != C.SQLITE_OK {
131129
return "", 0, 0, false, fmt.Errorf("sqlite3changeset_op: %s", C.GoString(C.sqlite3_errstr(rc)))
132130
}
133-
134-
return C.GoString(tableName), int(nCol), int(op), indirect != 0, nil
131+
return C.GoString(tableName), int(nCol), int(op), ind != 0, nil
135132
}
136133

137-
// ChangesetOld retrieves the old value for the specified column in the change payload.
138-
func (ci *ChangesetIterator) ChangesetOld(column int) (*C.sqlite3_value, error) {
139-
var value *C.sqlite3_value
134+
// Old retrieves the old value for the specified column in the change payload.
135+
func (ci *ChangesetIterator) Old(dest []any) error {
136+
for i := 0; i < len(dest); i++ {
137+
var val *C.sqlite3_value
138+
var src any
140139

141-
rc := C.sqlite3changeset_old(ci.iter, C.int(column), &value)
142-
if rc != C.SQLITE_OK {
143-
return nil, fmt.Errorf("sqlite3changeset_old: %s", C.GoString(C.sqlite3_errstr(rc)))
144-
}
140+
rc := C.sqlite3changeset_old(ci.iter, C.int(i), &val)
141+
if rc != C.SQLITE_OK {
142+
return fmt.Errorf("sqlite3changeset_old: %s", C.GoString(C.sqlite3_errstr(rc)))
143+
}
144+
145+
switch C.sqlite3_value_type(val) {
146+
case C.SQLITE_INTEGER:
147+
src = int64(C.sqlite3_value_int64(val))
148+
case C.SQLITE_FLOAT:
149+
src = float64(C.sqlite3_value_double(val))
150+
case C.SQLITE_BLOB:
151+
len := C.sqlite3_value_bytes(val)
152+
blobptr := C.sqlite3_value_blob(val)
153+
src = C.GoBytes(blobptr, len)
154+
case C.SQLITE_TEXT:
155+
len := C.sqlite3_value_bytes(val)
156+
cstrptr := unsafe.Pointer(C.sqlite3_value_text(val))
157+
src = C.GoBytes(cstrptr, len)
158+
case C.SQLITE_NULL:
159+
src = nil
160+
}
145161

146-
return value, nil
162+
err := convertAssign(&dest[i], src)
163+
if err != nil {
164+
return err
165+
}
166+
}
167+
return nil
147168
}
148169

149-
// ChangesetNew retrieves the new value for the specified column in the change payload.
150-
func (ci *ChangesetIterator) ChangesetNew(column int) (*C.sqlite3_value, error) {
151-
var value *C.sqlite3_value
170+
// New retrieves the new value for the specified column in the change payload.
171+
func (ci *ChangesetIterator) New(dest []any) error {
172+
for i := 0; i < len(dest); i++ {
173+
var val *C.sqlite3_value
174+
var src any
152175

153-
rc := C.sqlite3changeset_new(ci.iter, C.int(column), &value)
154-
if rc != C.SQLITE_OK {
155-
return nil, fmt.Errorf("sqlite3changeset_new: %s", C.GoString(C.sqlite3_errstr(rc)))
156-
}
176+
rc := C.sqlite3changeset_new(ci.iter, C.int(i), &val)
177+
if rc != C.SQLITE_OK {
178+
return fmt.Errorf("sqlite3changeset_new: %s", C.GoString(C.sqlite3_errstr(rc)))
179+
}
180+
181+
switch C.sqlite3_value_type(val) {
182+
case C.SQLITE_INTEGER:
183+
src = int64(C.sqlite3_value_int64(val))
184+
case C.SQLITE_FLOAT:
185+
src = float64(C.sqlite3_value_double(val))
186+
case C.SQLITE_BLOB:
187+
len := C.sqlite3_value_bytes(val)
188+
blobptr := C.sqlite3_value_blob(val)
189+
src = C.GoBytes(blobptr, len)
190+
case C.SQLITE_TEXT:
191+
len := C.sqlite3_value_bytes(val)
192+
cstrptr := unsafe.Pointer(C.sqlite3_value_text(val))
193+
src = C.GoBytes(cstrptr, len)
194+
case C.SQLITE_NULL:
195+
src = nil
196+
}
157197

158-
return value, nil
198+
err := convertAssign(&dest[i], src)
199+
if err != nil {
200+
return err
201+
}
202+
}
203+
return nil
159204
}
160205

161-
// ChangesetFinalize deletes a changeset iterator.
162-
func (ci *ChangesetIterator) ChangesetFinalize() error {
206+
// Finalize deletes a changeset iterator.
207+
func (ci *ChangesetIterator) Finalize() error {
163208
if ci.iter != nil {
164209
rc := C.sqlite3changeset_finalize(ci.iter)
165-
ci.iter = nil // Prevent double finalization
210+
ci.iter = nil
166211
if rc != C.SQLITE_OK {
167212
return fmt.Errorf("sqlite3changeset_finalize: %s", C.GoString(C.sqlite3_errstr(rc)))
168213
}

sqlite3_session_test.go

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
)
88

99
func Test_EmptyChangeset(t *testing.T) {
10-
// Open a new in-memory SQLite database
1110
db, err := sql.Open("sqlite3", ":memory:")
1211
if err != nil {
1312
t.Fatal("Failed to open", err)
@@ -48,7 +47,7 @@ func Test_EmptyChangeset(t *testing.T) {
4847
t.Fatalf("Failed to attach session to table: %v", err)
4948
}
5049

51-
changeset, err := session.Changeset()
50+
changeset, err := NewChangeset(session)
5251
if err != nil {
5352
t.Fatalf("Failed to generate changeset: %v", err)
5453
}
@@ -67,3 +66,108 @@ func Test_EmptyChangeset(t *testing.T) {
6766
t.Fatalf("Failed to finalize changeset iterator: %v", err)
6867
}
6968
}
69+
70+
func Test_Changeset_OneRow(t *testing.T) {
71+
db, err := sql.Open("sqlite3", ":memory:")
72+
if err != nil {
73+
t.Fatal("Failed to open", err)
74+
}
75+
err = db.Ping()
76+
if err != nil {
77+
t.Fatal("Failed to ping", err)
78+
}
79+
defer db.Close()
80+
81+
ctx := context.Background()
82+
conn, err := db.Conn(ctx)
83+
if err != nil {
84+
t.Fatal("Failed to get connection to database:", err)
85+
}
86+
defer conn.Close()
87+
88+
_, err = conn.ExecContext(ctx, `CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)`)
89+
if err != nil {
90+
t.Fatalf("Failed to create table: %v", err)
91+
}
92+
93+
var session *Session
94+
if err := conn.Raw(func(raw any) error {
95+
var err error
96+
session, err = raw.(*SQLiteConn).CreateSession("main")
97+
return err
98+
}); err != nil {
99+
t.Fatal("Failed to create session:", err)
100+
}
101+
defer func() {
102+
if err := session.DeleteSession(); err != nil {
103+
t.Errorf("Failed to delete session: %v", err)
104+
}
105+
}()
106+
107+
err = session.AttachSession("test_table")
108+
if err != nil {
109+
t.Fatalf("Failed to attach session to table: %v", err)
110+
}
111+
112+
_, err = conn.ExecContext(ctx, `INSERT INTO test_table (value) VALUES ('test')`)
113+
if err != nil {
114+
t.Fatalf("Failed to insert row: %v", err)
115+
}
116+
117+
changeset, err := NewChangeset(session)
118+
if err != nil {
119+
t.Fatalf("Failed to generate changeset: %v", err)
120+
}
121+
122+
iter, err := NewChangesetIterator(changeset)
123+
if err != nil {
124+
t.Fatalf("Failed to create changeset iterator: %v", err)
125+
}
126+
if b, err := iter.Next(); err != nil {
127+
t.Fatalf("Failed to get next changeset: %v", err)
128+
} else if !b {
129+
t.Fatalf("changeset does not contain changes: %v", b)
130+
}
131+
132+
tblName, nCol, op, indirect, err := iter.Op()
133+
if err != nil {
134+
t.Fatalf("Failed to get changeset operation: %v", err)
135+
}
136+
if tblName != "test_table" {
137+
t.Fatalf("Expected table name 'test_table', got '%s'", tblName)
138+
}
139+
if nCol != 2 {
140+
t.Fatalf("Expected 2 columns, got %d", nCol)
141+
}
142+
if op != SQLITE_INSERT {
143+
t.Fatalf("Expected operation 1, got %d", op)
144+
}
145+
if indirect {
146+
t.Fatalf("Expected indirect false, got true")
147+
}
148+
149+
dest := make([]any, nCol)
150+
if err := iter.New(dest); err != nil {
151+
t.Fatalf("Failed to get new row: %v", err)
152+
}
153+
if v, ok := dest[0].(int64); !ok {
154+
t.Fatalf("Expected int64, got %T", dest[0])
155+
} else if v != 1 {
156+
t.Fatalf("Expected 1, got %d", v)
157+
}
158+
if v, ok := dest[1].(string); !ok {
159+
t.Fatalf("Expected string, got %T", dest[1])
160+
} else if v != "test" {
161+
t.Fatalf("Expected test, got %s", v)
162+
}
163+
164+
if b, err := iter.Next(); err != nil {
165+
t.Fatalf("Failed to get next changeset: %v", err)
166+
} else if b {
167+
t.Fatalf("changeset contains more changes: %v", b)
168+
}
169+
170+
if err := iter.Finalize(); err != nil {
171+
t.Fatalf("Failed to finalize changeset iterator: %v", err)
172+
}
173+
}

0 commit comments

Comments
 (0)