github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/driver/driver_test.go (about) 1 package driver 2 3 import ( 4 "bytes" 5 "context" 6 "database/sql" 7 "errors" 8 "math" 9 "net/url" 10 "path/filepath" 11 "testing" 12 "time" 13 14 "github.com/ncruces/go-sqlite3" 15 _ "github.com/ncruces/go-sqlite3/embed" 16 "github.com/ncruces/go-sqlite3/internal/util" 17 _ "github.com/ncruces/go-sqlite3/tests/testcfg" 18 "github.com/ncruces/go-sqlite3/vfs" 19 ) 20 21 func Test_Open_dir(t *testing.T) { 22 t.Parallel() 23 24 db, err := sql.Open("sqlite3", ".") 25 if err != nil { 26 t.Fatal(err) 27 } 28 defer db.Close() 29 30 _, err = db.Conn(context.TODO()) 31 if err == nil { 32 t.Fatal("want error") 33 } 34 if !errors.Is(err, sqlite3.CANTOPEN) { 35 t.Errorf("got %v, want sqlite3.CANTOPEN", err) 36 } 37 } 38 39 func Test_Open_pragma(t *testing.T) { 40 t.Parallel() 41 42 db, err := sql.Open("sqlite3", "file::memory:?_pragma=busy_timeout(1000)") 43 if err != nil { 44 t.Fatal(err) 45 } 46 defer db.Close() 47 48 var timeout int 49 err = db.QueryRow(`PRAGMA busy_timeout`).Scan(&timeout) 50 if err != nil { 51 t.Fatal(err) 52 } 53 if timeout != 1000 { 54 t.Errorf("got %v, want 1000", timeout) 55 } 56 } 57 58 func Test_Open_pragma_invalid(t *testing.T) { 59 t.Parallel() 60 61 db, err := sql.Open("sqlite3", "file::memory:?_pragma=busy_timeout+1000") 62 if err != nil { 63 t.Fatal(err) 64 } 65 defer db.Close() 66 67 _, err = db.Conn(context.TODO()) 68 if err == nil { 69 t.Fatal("want error") 70 } 71 var serr *sqlite3.Error 72 if !errors.As(err, &serr) { 73 t.Fatalf("got %T, want sqlite3.Error", err) 74 } 75 if rc := serr.Code(); rc != sqlite3.ERROR { 76 t.Errorf("got %d, want sqlite3.ERROR", rc) 77 } 78 if got := err.Error(); got != `sqlite3: invalid _pragma: sqlite3: SQL logic error: near "1000": syntax error` { 79 t.Error("got message:", got) 80 } 81 } 82 83 func Test_Open_txLock(t *testing.T) { 84 if !vfs.SupportsFileLocking { 85 t.Skip("skipping without locks") 86 } 87 t.Parallel() 88 89 db, err := sql.Open("sqlite3", "file:"+ 90 filepath.ToSlash(filepath.Join(t.TempDir(), "test.db"))+ 91 "?_txlock=exclusive&_pragma=busy_timeout(0)") 92 if err != nil { 93 t.Fatal(err) 94 } 95 defer db.Close() 96 97 tx1, err := db.Begin() 98 if err != nil { 99 t.Fatal(err) 100 } 101 102 _, err = db.Begin() 103 if err == nil { 104 t.Error("want error") 105 } 106 if !errors.Is(err, sqlite3.BUSY) { 107 t.Errorf("got %v, want sqlite3.BUSY", err) 108 } 109 var terr interface{ Temporary() bool } 110 if !errors.As(err, &terr) || !terr.Temporary() { 111 t.Error("not temporary", err) 112 } 113 114 err = tx1.Commit() 115 if err != nil { 116 t.Fatal(err) 117 } 118 } 119 120 func Test_Open_txLock_invalid(t *testing.T) { 121 t.Parallel() 122 123 _, err := sql.Open("sqlite3", "file::memory:?_txlock=xclusive") 124 if err == nil { 125 t.Fatal("want error") 126 } 127 if got := err.Error(); got != `sqlite3: invalid _txlock: xclusive` { 128 t.Error("got message:", got) 129 } 130 } 131 132 func Test_BeginTx(t *testing.T) { 133 if !vfs.SupportsFileLocking { 134 t.Skip("skipping without locks") 135 } 136 t.Parallel() 137 138 ctx, cancel := context.WithCancel(context.Background()) 139 defer cancel() 140 141 db, err := sql.Open("sqlite3", "file:"+ 142 filepath.ToSlash(filepath.Join(t.TempDir(), "test.db"))+ 143 "?_txlock=exclusive&_pragma=busy_timeout(0)") 144 if err != nil { 145 t.Fatal(err) 146 } 147 defer db.Close() 148 149 _, err = db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted}) 150 if err.Error() != string(util.IsolationErr) { 151 t.Error("want isolationErr") 152 } 153 154 tx1, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) 155 if err != nil { 156 t.Fatal(err) 157 } 158 159 tx2, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) 160 if err != nil { 161 t.Fatal(err) 162 } 163 164 _, err = tx1.Exec(`CREATE TABLE test (col)`) 165 if err == nil { 166 t.Error("want error") 167 } 168 if !errors.Is(err, sqlite3.READONLY) { 169 t.Errorf("got %v, want sqlite3.READONLY", err) 170 } 171 172 err = tx2.Commit() 173 if err != nil { 174 t.Fatal(err) 175 } 176 177 err = tx1.Commit() 178 if err != nil { 179 t.Fatal(err) 180 } 181 } 182 183 func Test_Prepare(t *testing.T) { 184 t.Parallel() 185 186 db, err := sql.Open("sqlite3", ":memory:") 187 if err != nil { 188 t.Fatal(err) 189 } 190 defer db.Close() 191 192 var serr *sqlite3.Error 193 _, err = db.Prepare(`SELECT`) 194 if err == nil { 195 t.Error("want error") 196 } 197 if !errors.As(err, &serr) { 198 t.Fatalf("got %T, want sqlite3.Error", err) 199 } 200 if rc := serr.Code(); rc != sqlite3.ERROR { 201 t.Errorf("got %d, want sqlite3.ERROR", rc) 202 } 203 if got := err.Error(); got != `sqlite3: SQL logic error: incomplete input` { 204 t.Error("got message:", got) 205 } 206 207 _, err = db.Prepare(`SELECT 1; `) 208 if err.Error() != string(util.TailErr) { 209 t.Error("want tailErr") 210 } 211 212 _, err = db.Prepare(`SELECT 1; SELECT`) 213 if err.Error() != string(util.TailErr) { 214 t.Error("want tailErr") 215 } 216 217 _, err = db.Prepare(`SELECT 1; SELECT 2`) 218 if err.Error() != string(util.TailErr) { 219 t.Error("want tailErr") 220 } 221 } 222 223 func Test_QueryRow_named(t *testing.T) { 224 t.Parallel() 225 226 ctx, cancel := context.WithCancel(context.Background()) 227 defer cancel() 228 229 db, err := sql.Open("sqlite3", ":memory:") 230 if err != nil { 231 t.Fatal(err) 232 } 233 defer db.Close() 234 235 conn, err := db.Conn(ctx) 236 if err != nil { 237 t.Fatal(err) 238 } 239 defer conn.Close() 240 241 stmt, err := conn.PrepareContext(ctx, `SELECT ?, ?5, :AAA, @AAA, $AAA`) 242 if err != nil { 243 t.Fatal(err) 244 } 245 defer stmt.Close() 246 247 date := time.Now() 248 row := stmt.QueryRow(true, sql.Named("AAA", math.Pi), nil /*3*/, nil /*4*/, date /*5*/) 249 250 var first bool 251 var fifth time.Time 252 var colon, at, dollar float32 253 err = row.Scan(&first, &fifth, &colon, &at, &dollar) 254 if err != nil { 255 t.Fatal(err) 256 } 257 258 if first != true { 259 t.Errorf("want true, got %v", first) 260 } 261 if colon != math.Pi { 262 t.Errorf("want π, got %v", colon) 263 } 264 if at != math.Pi { 265 t.Errorf("want π, got %v", at) 266 } 267 if dollar != math.Pi { 268 t.Errorf("want π, got %v", dollar) 269 } 270 if !fifth.Equal(date) { 271 t.Errorf("want %v, got %v", date, fifth) 272 } 273 } 274 275 func Test_QueryRow_blob_null(t *testing.T) { 276 t.Parallel() 277 278 db, err := sql.Open("sqlite3", ":memory:") 279 if err != nil { 280 t.Fatal(err) 281 } 282 defer db.Close() 283 284 rows, err := db.Query(` 285 SELECT NULL UNION ALL 286 SELECT x'cafe' UNION ALL 287 SELECT x'babe' UNION ALL 288 SELECT NULL 289 `) 290 if err != nil { 291 t.Fatal(err) 292 } 293 defer rows.Close() 294 295 want := [][]byte{nil, {0xca, 0xfe}, {0xba, 0xbe}, nil} 296 for i := 0; rows.Next(); i++ { 297 var buf sql.RawBytes 298 err = rows.Scan(&buf) 299 if err != nil { 300 t.Fatal(err) 301 } 302 if !bytes.Equal(buf, want[i]) { 303 t.Errorf("got %q, want %q", buf, want[i]) 304 } 305 } 306 } 307 308 func Test_time(t *testing.T) { 309 t.Parallel() 310 311 for _, fmt := range []string{"auto", "sqlite", "rfc3339", time.ANSIC} { 312 t.Run(fmt, func(t *testing.T) { 313 db, err := sql.Open("sqlite3", "file::memory:?_timefmt="+url.QueryEscape(fmt)) 314 if err != nil { 315 t.Fatal(err) 316 } 317 defer db.Close() 318 319 twosday := time.Date(2022, 2, 22, 22, 22, 22, 0, time.UTC) 320 321 _, err = db.Exec(`CREATE TABLE test (at DATETIME)`) 322 if err != nil { 323 t.Fatal(err) 324 } 325 326 _, err = db.Exec(`INSERT INTO test VALUES (?)`, twosday) 327 if err != nil { 328 t.Fatal(err) 329 } 330 331 var got time.Time 332 err = db.QueryRow(`SELECT * FROM test`).Scan(&got) 333 if err != nil { 334 t.Fatal(err) 335 } 336 337 if !got.Equal(twosday) { 338 t.Errorf("got: %v", got) 339 } 340 }) 341 } 342 }