package main /* #cgo LDFLAGS: -lsqlite3 #include #include #include // Open a database file and return a handle. int db_open(const char *path, sqlite3 **db) { return sqlite3_open(path, db); } // Execute a statement that returns no rows. int db_exec(sqlite3 *db, const char *sql, char **errmsg) { return sqlite3_exec(db, sql, NULL, NULL, errmsg); } // Prepare a statement. int db_prepare(sqlite3 *db, const char *sql, sqlite3_stmt **stmt) { return sqlite3_prepare_v2(db, sql, -1, stmt, NULL); } // Bind a text parameter (1-based index). Makes a private copy. int db_bind_text(sqlite3_stmt *stmt, int idx, const char *val) { return sqlite3_bind_text(stmt, idx, val, -1, SQLITE_TRANSIENT); } // Step the statement. int db_step(sqlite3_stmt *stmt) { return sqlite3_step(stmt); } // Retrieve a text column value. const char *db_column_text(sqlite3_stmt *stmt, int col) { return (const char *)sqlite3_column_text(stmt, col); } // Finalize the statement. int db_finalize(sqlite3_stmt *stmt) { return sqlite3_finalize(stmt); } // Close the database. int db_close(sqlite3 *db) { return sqlite3_close(db); } // Return the last error message. const char *db_errmsg(sqlite3 *db) { return sqlite3_errmsg(db); } */ import "C" import ( "fmt" "unsafe" ) // DB wraps a raw sqlite3 pointer. type DB struct { ptr *C.sqlite3 } // openDB opens (or creates) the SQLite file at path. func openDB(path string) (*DB, error) { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) var ptr *C.sqlite3 rc := C.db_open(cpath, &ptr) if rc != C.SQLITE_OK { return nil, fmt.Errorf("sqlite3_open: code %d", rc) } return &DB{ptr: ptr}, nil } // Close closes the database connection. func (db *DB) Close() { if db.ptr != nil { C.db_close(db.ptr) db.ptr = nil } } // Exec runs a statement that returns no rows (DDL, INSERT, etc.). func (db *DB) Exec(sql string, args ...string) error { if len(args) == 0 { csql := C.CString(sql) defer C.free(unsafe.Pointer(csql)) var errmsg *C.char rc := C.db_exec(db.ptr, csql, &errmsg) if rc != C.SQLITE_OK { msg := C.GoString(errmsg) C.sqlite3_free(unsafe.Pointer(errmsg)) return fmt.Errorf("exec: %s", msg) } return nil } // Prepared statement path csql := C.CString(sql) defer C.free(unsafe.Pointer(csql)) var stmt *C.sqlite3_stmt rc := C.db_prepare(db.ptr, csql, &stmt) if rc != C.SQLITE_OK { return fmt.Errorf("prepare: %s", C.GoString(C.db_errmsg(db.ptr))) } defer C.db_finalize(stmt) for i, arg := range args { carg := C.CString(arg) rc = C.db_bind_text(stmt, C.int(i+1), carg) C.free(unsafe.Pointer(carg)) if rc != C.SQLITE_OK { return fmt.Errorf("bind[%d]: %s", i, C.GoString(C.db_errmsg(db.ptr))) } } rc = C.db_step(stmt) if rc != C.SQLITE_DONE { return fmt.Errorf("step: %s", C.GoString(C.db_errmsg(db.ptr))) } return nil } // Row is a map of column-name → string value. type Row map[string]string // Query runs a SELECT and returns all rows. func (db *DB) Query(sql string, args ...string) ([]Row, error) { csql := C.CString(sql) defer C.free(unsafe.Pointer(csql)) var stmt *C.sqlite3_stmt rc := C.db_prepare(db.ptr, csql, &stmt) if rc != C.SQLITE_OK { return nil, fmt.Errorf("prepare: %s", C.GoString(C.db_errmsg(db.ptr))) } defer C.db_finalize(stmt) for i, arg := range args { carg := C.CString(arg) rc = C.db_bind_text(stmt, C.int(i+1), carg) C.free(unsafe.Pointer(carg)) if rc != C.SQLITE_OK { return nil, fmt.Errorf("bind[%d]: %s", i, C.GoString(C.db_errmsg(db.ptr))) } } colCount := int(C.sqlite3_column_count(stmt)) colNames := make([]string, colCount) for i := 0; i < colCount; i++ { colNames[i] = C.GoString(C.sqlite3_column_name(stmt, C.int(i))) } var rows []Row for { rc = C.db_step(stmt) if rc == C.SQLITE_DONE { break } if rc != C.SQLITE_ROW { return nil, fmt.Errorf("step: %s", C.GoString(C.db_errmsg(db.ptr))) } row := make(Row, colCount) for i := 0; i < colCount; i++ { row[colNames[i]] = C.GoString(C.db_column_text(stmt, C.int(i))) } rows = append(rows, row) } return rows, nil }