PageRenderTime 1603ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/sqlite3_go18_test.go

http://github.com/mattn/go-sqlite3
Go | 503 lines | 421 code | 64 blank | 18 comment | 149 complexity | d50b38b83d54ed9ac3e2759125b41a9b MD5 | raw file
  1. // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
  2. //
  3. // Use of this source code is governed by an MIT-style
  4. // license that can be found in the LICENSE file.
  5. // +build go1.8,cgo
  6. package sqlite3
  7. import (
  8. "context"
  9. "database/sql"
  10. "fmt"
  11. "io/ioutil"
  12. "math/rand"
  13. "os"
  14. "sync"
  15. "testing"
  16. "time"
  17. )
  18. func TestNamedParams(t *testing.T) {
  19. tempFilename := TempFilename(t)
  20. defer os.Remove(tempFilename)
  21. db, err := sql.Open("sqlite3", tempFilename)
  22. if err != nil {
  23. t.Fatal("Failed to open database:", err)
  24. }
  25. defer db.Close()
  26. _, err = db.Exec(`
  27. create table foo (id integer, name text, extra text);
  28. `)
  29. if err != nil {
  30. t.Error("Failed to call db.Query:", err)
  31. }
  32. _, err = db.Exec(`insert into foo(id, name, extra) values(:id, :name, :name)`, sql.Named("name", "foo"), sql.Named("id", 1))
  33. if err != nil {
  34. t.Error("Failed to call db.Exec:", err)
  35. }
  36. row := db.QueryRow(`select id, extra from foo where id = :id and extra = :extra`, sql.Named("id", 1), sql.Named("extra", "foo"))
  37. if row == nil {
  38. t.Error("Failed to call db.QueryRow")
  39. }
  40. var id int
  41. var extra string
  42. err = row.Scan(&id, &extra)
  43. if err != nil {
  44. t.Error("Failed to db.Scan:", err)
  45. }
  46. if id != 1 || extra != "foo" {
  47. t.Error("Failed to db.QueryRow: not matched results")
  48. }
  49. }
  50. var (
  51. testTableStatements = []string{
  52. `DROP TABLE IF EXISTS test_table`,
  53. `
  54. CREATE TABLE IF NOT EXISTS test_table (
  55. key1 VARCHAR(64) PRIMARY KEY,
  56. key_id VARCHAR(64) NOT NULL,
  57. key2 VARCHAR(64) NOT NULL,
  58. key3 VARCHAR(64) NOT NULL,
  59. key4 VARCHAR(64) NOT NULL,
  60. key5 VARCHAR(64) NOT NULL,
  61. key6 VARCHAR(64) NOT NULL,
  62. data BLOB NOT NULL
  63. );`,
  64. }
  65. letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  66. )
  67. func randStringBytes(n int) string {
  68. b := make([]byte, n)
  69. for i := range b {
  70. b[i] = letterBytes[rand.Intn(len(letterBytes))]
  71. }
  72. return string(b)
  73. }
  74. func initDatabase(t *testing.T, db *sql.DB, rowCount int64) {
  75. for _, query := range testTableStatements {
  76. _, err := db.Exec(query)
  77. if err != nil {
  78. t.Fatal(err)
  79. }
  80. }
  81. for i := int64(0); i < rowCount; i++ {
  82. query := `INSERT INTO test_table
  83. (key1, key_id, key2, key3, key4, key5, key6, data)
  84. VALUES
  85. (?, ?, ?, ?, ?, ?, ?, ?);`
  86. args := []interface{}{
  87. randStringBytes(50),
  88. fmt.Sprint(i),
  89. randStringBytes(50),
  90. randStringBytes(50),
  91. randStringBytes(50),
  92. randStringBytes(50),
  93. randStringBytes(50),
  94. randStringBytes(50),
  95. randStringBytes(2048),
  96. }
  97. _, err := db.Exec(query, args...)
  98. if err != nil {
  99. t.Fatal(err)
  100. }
  101. }
  102. }
  103. func TestShortTimeout(t *testing.T) {
  104. srcTempFilename := TempFilename(t)
  105. defer os.Remove(srcTempFilename)
  106. db, err := sql.Open("sqlite3", srcTempFilename)
  107. if err != nil {
  108. t.Fatal(err)
  109. }
  110. defer db.Close()
  111. initDatabase(t, db, 100)
  112. ctx, cancel := context.WithTimeout(context.Background(), 1*time.Microsecond)
  113. defer cancel()
  114. query := `SELECT key1, key_id, key2, key3, key4, key5, key6, data
  115. FROM test_table
  116. ORDER BY key2 ASC`
  117. _, err = db.QueryContext(ctx, query)
  118. if err != nil && err != context.DeadlineExceeded {
  119. t.Fatal(err)
  120. }
  121. if ctx.Err() != nil && ctx.Err() != context.DeadlineExceeded {
  122. t.Fatal(ctx.Err())
  123. }
  124. }
  125. func TestExecContextCancel(t *testing.T) {
  126. srcTempFilename := TempFilename(t)
  127. defer os.Remove(srcTempFilename)
  128. db, err := sql.Open("sqlite3", srcTempFilename)
  129. if err != nil {
  130. t.Fatal(err)
  131. }
  132. defer db.Close()
  133. ts := time.Now()
  134. initDatabase(t, db, 1000)
  135. spent := time.Since(ts)
  136. const minTestTime = 100 * time.Millisecond
  137. if spent < minTestTime {
  138. t.Skipf("test will be too racy (spent=%s < min=%s) as ExecContext below will be too fast.",
  139. spent.String(), minTestTime.String(),
  140. )
  141. }
  142. // expected to be extremely slow query
  143. q := `
  144. INSERT INTO test_table (key1, key_id, key2, key3, key4, key5, key6, data)
  145. SELECT t1.key1 || t2.key1, t1.key_id || t2.key_id, t1.key2 || t2.key2, t1.key3 || t2.key3, t1.key4 || t2.key4, t1.key5 || t2.key5, t1.key6 || t2.key6, t1.data || t2.data
  146. FROM test_table t1 LEFT OUTER JOIN test_table t2`
  147. // expect query above take ~ same time as setup above
  148. // This is racy: the context must be valid so sql/db.ExecContext calls the sqlite3 driver.
  149. // It starts the query, the context expires, then calls sqlite3_interrupt
  150. ctx, cancel := context.WithTimeout(context.Background(), minTestTime/2)
  151. defer cancel()
  152. ts = time.Now()
  153. r, err := db.ExecContext(ctx, q)
  154. // racy check
  155. if r != nil {
  156. n, err := r.RowsAffected()
  157. t.Logf("query should not have succeeded: rows=%d; err=%v; duration=%s",
  158. n, err, time.Since(ts).String())
  159. }
  160. if err != context.DeadlineExceeded {
  161. t.Fatal(err, ctx.Err())
  162. }
  163. }
  164. func TestQueryRowContextCancel(t *testing.T) {
  165. srcTempFilename := TempFilename(t)
  166. defer os.Remove(srcTempFilename)
  167. db, err := sql.Open("sqlite3", srcTempFilename)
  168. if err != nil {
  169. t.Fatal(err)
  170. }
  171. defer db.Close()
  172. initDatabase(t, db, 100)
  173. const query = `SELECT key_id FROM test_table ORDER BY key2 ASC`
  174. var keyID string
  175. unexpectedErrors := make(map[string]int)
  176. for i := 0; i < 10000; i++ {
  177. ctx, cancel := context.WithCancel(context.Background())
  178. row := db.QueryRowContext(ctx, query)
  179. cancel()
  180. // it is fine to get "nil" as context cancellation can be handled with delay
  181. if err := row.Scan(&keyID); err != nil && err != context.Canceled {
  182. if err.Error() == "sql: Rows are closed" {
  183. // see https://github.com/golang/go/issues/24431
  184. // fixed in 1.11.1 to properly return context error
  185. continue
  186. }
  187. unexpectedErrors[err.Error()]++
  188. }
  189. }
  190. for errText, count := range unexpectedErrors {
  191. t.Error(errText, count)
  192. }
  193. }
  194. func TestQueryRowContextCancelParallel(t *testing.T) {
  195. srcTempFilename := TempFilename(t)
  196. defer os.Remove(srcTempFilename)
  197. db, err := sql.Open("sqlite3", srcTempFilename)
  198. if err != nil {
  199. t.Fatal(err)
  200. }
  201. db.SetMaxOpenConns(10)
  202. db.SetMaxIdleConns(5)
  203. defer db.Close()
  204. initDatabase(t, db, 100)
  205. const query = `SELECT key_id FROM test_table ORDER BY key2 ASC`
  206. wg := sync.WaitGroup{}
  207. defer wg.Wait()
  208. testCtx, cancel := context.WithCancel(context.Background())
  209. defer cancel()
  210. for i := 0; i < 10; i++ {
  211. wg.Add(1)
  212. go func() {
  213. defer wg.Done()
  214. var keyID string
  215. for {
  216. select {
  217. case <-testCtx.Done():
  218. return
  219. default:
  220. }
  221. ctx, cancel := context.WithCancel(context.Background())
  222. row := db.QueryRowContext(ctx, query)
  223. cancel()
  224. _ = row.Scan(&keyID) // see TestQueryRowContextCancel
  225. }
  226. }()
  227. }
  228. var keyID string
  229. for i := 0; i < 10000; i++ {
  230. // note that testCtx is not cancelled during query execution
  231. row := db.QueryRowContext(testCtx, query)
  232. if err := row.Scan(&keyID); err != nil {
  233. t.Fatal(i, err)
  234. }
  235. }
  236. }
  237. func TestExecCancel(t *testing.T) {
  238. db, err := sql.Open("sqlite3", ":memory:")
  239. if err != nil {
  240. t.Fatal(err)
  241. }
  242. defer db.Close()
  243. if _, err = db.Exec("create table foo (id integer primary key)"); err != nil {
  244. t.Fatal(err)
  245. }
  246. for n := 0; n < 100; n++ {
  247. ctx, cancel := context.WithCancel(context.Background())
  248. _, err = db.ExecContext(ctx, "insert into foo (id) values (?)", n)
  249. cancel()
  250. if err != nil {
  251. t.Fatal(err)
  252. }
  253. }
  254. }
  255. func doTestOpenContext(t *testing.T, option string) (string, error) {
  256. tempFilename := TempFilename(t)
  257. url := tempFilename + option
  258. defer func() {
  259. err := os.Remove(tempFilename)
  260. if err != nil {
  261. t.Error("temp file remove error:", err)
  262. }
  263. }()
  264. db, err := sql.Open("sqlite3", url)
  265. if err != nil {
  266. return "Failed to open database:", err
  267. }
  268. defer func() {
  269. err = db.Close()
  270. if err != nil {
  271. t.Error("db close error:", err)
  272. }
  273. }()
  274. ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second)
  275. err = db.PingContext(ctx)
  276. cancel()
  277. if err != nil {
  278. return "ping error:", err
  279. }
  280. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  281. _, err = db.ExecContext(ctx, "drop table foo")
  282. cancel()
  283. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  284. _, err = db.ExecContext(ctx, "create table foo (id integer)")
  285. cancel()
  286. if err != nil {
  287. return "Failed to create table:", err
  288. }
  289. if stat, err := os.Stat(tempFilename); err != nil || stat.IsDir() {
  290. return "Failed to create ./foo.db", nil
  291. }
  292. return "", nil
  293. }
  294. func TestOpenContext(t *testing.T) {
  295. cases := map[string]bool{
  296. "": true,
  297. "?_txlock=immediate": true,
  298. "?_txlock=deferred": true,
  299. "?_txlock=exclusive": true,
  300. "?_txlock=bogus": false,
  301. }
  302. for option, expectedPass := range cases {
  303. result, err := doTestOpenContext(t, option)
  304. if result == "" {
  305. if !expectedPass {
  306. errmsg := fmt.Sprintf("_txlock error not caught at dbOpen with option: %s", option)
  307. t.Fatal(errmsg)
  308. }
  309. } else if expectedPass {
  310. if err == nil {
  311. t.Fatal(result)
  312. } else {
  313. t.Fatal(result, err)
  314. }
  315. }
  316. }
  317. }
  318. func TestFileCopyTruncate(t *testing.T) {
  319. var err error
  320. tempFilename := TempFilename(t)
  321. defer func() {
  322. err = os.Remove(tempFilename)
  323. if err != nil {
  324. t.Error("temp file remove error:", err)
  325. }
  326. }()
  327. db, err := sql.Open("sqlite3", tempFilename)
  328. if err != nil {
  329. t.Fatal("open error:", err)
  330. }
  331. defer func() {
  332. err = db.Close()
  333. if err != nil {
  334. t.Error("db close error:", err)
  335. }
  336. }()
  337. ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second)
  338. err = db.PingContext(ctx)
  339. cancel()
  340. if err != nil {
  341. t.Fatal("ping error:", err)
  342. }
  343. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  344. _, err = db.ExecContext(ctx, "drop table foo")
  345. cancel()
  346. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  347. _, err = db.ExecContext(ctx, "create table foo (id integer)")
  348. cancel()
  349. if err != nil {
  350. t.Fatal("create table error:", err)
  351. }
  352. // copy db to new file
  353. var data []byte
  354. data, err = ioutil.ReadFile(tempFilename)
  355. if err != nil {
  356. t.Fatal("read file error:", err)
  357. }
  358. var f *os.File
  359. f, err = os.Create(tempFilename + "-db-copy")
  360. if err != nil {
  361. t.Fatal("create file error:", err)
  362. }
  363. defer func() {
  364. err = os.Remove(tempFilename + "-db-copy")
  365. if err != nil {
  366. t.Error("temp file moved remove error:", err)
  367. }
  368. }()
  369. _, err = f.Write(data)
  370. if err != nil {
  371. f.Close()
  372. t.Fatal("write file error:", err)
  373. }
  374. err = f.Close()
  375. if err != nil {
  376. t.Fatal("close file error:", err)
  377. }
  378. // truncate current db file
  379. f, err = os.OpenFile(tempFilename, os.O_WRONLY|os.O_TRUNC, 0666)
  380. if err != nil {
  381. t.Fatal("open file error:", err)
  382. }
  383. err = f.Close()
  384. if err != nil {
  385. t.Fatal("close file error:", err)
  386. }
  387. // test db after file truncate
  388. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  389. err = db.PingContext(ctx)
  390. cancel()
  391. if err != nil {
  392. t.Fatal("ping error:", err)
  393. }
  394. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  395. _, err = db.ExecContext(ctx, "drop table foo")
  396. cancel()
  397. if err == nil {
  398. t.Fatal("drop table no error")
  399. }
  400. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  401. _, err = db.ExecContext(ctx, "create table foo (id integer)")
  402. cancel()
  403. if err != nil {
  404. t.Fatal("create table error:", err)
  405. }
  406. err = db.Close()
  407. if err != nil {
  408. t.Error("db close error:", err)
  409. }
  410. // test copied file
  411. db, err = sql.Open("sqlite3", tempFilename+"-db-copy")
  412. if err != nil {
  413. t.Fatal("open error:", err)
  414. }
  415. defer func() {
  416. err = db.Close()
  417. if err != nil {
  418. t.Error("db close error:", err)
  419. }
  420. }()
  421. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  422. err = db.PingContext(ctx)
  423. cancel()
  424. if err != nil {
  425. t.Fatal("ping error:", err)
  426. }
  427. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  428. _, err = db.ExecContext(ctx, "drop table foo")
  429. cancel()
  430. if err != nil {
  431. t.Fatal("drop table error:", err)
  432. }
  433. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  434. _, err = db.ExecContext(ctx, "create table foo (id integer)")
  435. cancel()
  436. if err != nil {
  437. t.Fatal("create table error:", err)
  438. }
  439. }