driver_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. package mysql
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "net"
  6. "os"
  7. "sync"
  8. "testing"
  9. "time"
  10. )
  11. var (
  12. dsn string
  13. netAddr string
  14. run bool
  15. once sync.Once
  16. )
  17. // See https://github.com/Go-SQL-Driver/MySQL/wiki/Testing
  18. func getEnv() bool {
  19. once.Do(func() {
  20. user := os.Getenv("MYSQL_TEST_USER")
  21. if user == "" {
  22. user = "root"
  23. }
  24. pass := os.Getenv("MYSQL_TEST_PASS")
  25. prot := os.Getenv("MYSQL_TEST_PROT")
  26. if prot == "" {
  27. prot = "tcp"
  28. }
  29. addr := os.Getenv("MYSQL_TEST_ADDR")
  30. if addr == "" {
  31. addr = "localhost:3306"
  32. }
  33. dbname := os.Getenv("MYSQL_TEST_DBNAME")
  34. if dbname == "" {
  35. dbname = "gotest"
  36. }
  37. netAddr = fmt.Sprintf("%s(%s)", prot, addr)
  38. dsn = fmt.Sprintf("%s:%s@%s/%s?timeout=30s&charset=utf8", user, pass, netAddr, dbname)
  39. c, err := net.Dial(prot, addr)
  40. if err == nil {
  41. run = true
  42. c.Close()
  43. }
  44. })
  45. return run
  46. }
  47. func mustExec(t *testing.T, db *sql.DB, query string, args ...interface{}) (res sql.Result) {
  48. res, err := db.Exec(query, args...)
  49. if err != nil {
  50. t.Fatalf("Error on Exec %q: %v", query, err)
  51. }
  52. return
  53. }
  54. func mustQuery(t *testing.T, db *sql.DB, query string, args ...interface{}) (rows *sql.Rows) {
  55. rows, err := db.Query(query, args...)
  56. if err != nil {
  57. t.Fatalf("Error on Query %q: %v", query, err)
  58. }
  59. return
  60. }
  61. func TestCRUD(t *testing.T) {
  62. if !getEnv() {
  63. t.Logf("MySQL-Server not running on %s. Skipping TestCRUD", netAddr)
  64. return
  65. }
  66. db, err := sql.Open("mysql", dsn)
  67. if err != nil {
  68. t.Fatalf("Error connecting: %v", err)
  69. }
  70. defer db.Close()
  71. mustExec(t, db, "DROP TABLE IF EXISTS test")
  72. // Create Table
  73. mustExec(t, db, "CREATE TABLE test (value BOOL)")
  74. // Test for unexpected data
  75. var out bool
  76. rows := mustQuery(t, db, ("SELECT * FROM test"))
  77. if rows.Next() {
  78. t.Error("unexpected data in empty table")
  79. }
  80. // Create Data
  81. res := mustExec(t, db, ("INSERT INTO test VALUES (1)"))
  82. count, err := res.RowsAffected()
  83. if err != nil {
  84. t.Fatalf("res.RowsAffected() returned error: %v", err)
  85. }
  86. if count != 1 {
  87. t.Fatalf("Expected 1 affected row, got %d", count)
  88. }
  89. id, err := res.LastInsertId()
  90. if err != nil {
  91. t.Fatalf("res.LastInsertId() returned error: %v", err)
  92. }
  93. if id != 0 {
  94. t.Fatalf("Expected InsertID 0, got %d", id)
  95. }
  96. // Read
  97. rows = mustQuery(t, db, ("SELECT value FROM test"))
  98. if rows.Next() {
  99. rows.Scan(&out)
  100. if true != out {
  101. t.Errorf("true != %t", out)
  102. }
  103. if rows.Next() {
  104. t.Error("unexpected data")
  105. }
  106. } else {
  107. t.Error("no data")
  108. }
  109. // Update
  110. res = mustExec(t, db, "UPDATE test SET value = ? WHERE value = ?", false, true)
  111. count, err = res.RowsAffected()
  112. if err != nil {
  113. t.Fatalf("res.RowsAffected() returned error: %v", err)
  114. }
  115. if count != 1 {
  116. t.Fatalf("Expected 1 affected row, got %d", count)
  117. }
  118. // Check Update
  119. rows = mustQuery(t, db, ("SELECT value FROM test"))
  120. if rows.Next() {
  121. rows.Scan(&out)
  122. if false != out {
  123. t.Errorf("false != %t", out)
  124. }
  125. if rows.Next() {
  126. t.Error("unexpected data")
  127. }
  128. } else {
  129. t.Error("no data")
  130. }
  131. // Delete
  132. res = mustExec(t, db, "DELETE FROM test WHERE value = ?", false)
  133. count, err = res.RowsAffected()
  134. if err != nil {
  135. t.Fatalf("res.RowsAffected() returned error: %v", err)
  136. }
  137. if count != 1 {
  138. t.Fatalf("Expected 1 affected row, got %d", count)
  139. }
  140. // Check for unexpected rows
  141. res = mustExec(t, db, "DELETE FROM test")
  142. count, err = res.RowsAffected()
  143. if err != nil {
  144. t.Fatalf("res.RowsAffected() returned error: %v", err)
  145. }
  146. if count != 0 {
  147. t.Fatalf("Expected 0 affected row, got %d", count)
  148. }
  149. }
  150. func TestInt(t *testing.T) {
  151. if !getEnv() {
  152. t.Logf("MySQL-Server not running on %s. Skipping TestInt", netAddr)
  153. return
  154. }
  155. db, err := sql.Open("mysql", dsn)
  156. if err != nil {
  157. t.Fatalf("Error connecting: %v", err)
  158. }
  159. defer db.Close()
  160. mustExec(t, db, "DROP TABLE IF EXISTS test")
  161. types := [5]string{"TINYINT", "SMALLINT", "MEDIUMINT", "INT", "BIGINT"}
  162. in := int64(42)
  163. var out int64
  164. var rows *sql.Rows
  165. // SIGNED
  166. for _, v := range types {
  167. mustExec(t, db, "CREATE TABLE test (value "+v+")")
  168. mustExec(t, db, ("INSERT INTO test VALUES (?)"), in)
  169. rows = mustQuery(t, db, ("SELECT value FROM test"))
  170. if rows.Next() {
  171. rows.Scan(&out)
  172. if in != out {
  173. t.Errorf("%s: %d != %d", v, in, out)
  174. }
  175. } else {
  176. t.Errorf("%s: no data", v)
  177. }
  178. mustExec(t, db, "DROP TABLE IF EXISTS test")
  179. }
  180. // UNSIGNED ZEROFILL
  181. for _, v := range types {
  182. mustExec(t, db, "CREATE TABLE test (value "+v+" ZEROFILL)")
  183. mustExec(t, db, ("INSERT INTO test VALUES (?)"), in)
  184. rows = mustQuery(t, db, ("SELECT value FROM test"))
  185. if rows.Next() {
  186. rows.Scan(&out)
  187. if in != out {
  188. t.Errorf("%s ZEROFILL: %d != %d", v, in, out)
  189. }
  190. } else {
  191. t.Errorf("%s ZEROFILL: no data", v)
  192. }
  193. mustExec(t, db, "DROP TABLE IF EXISTS test")
  194. }
  195. }
  196. func TestFloat(t *testing.T) {
  197. if !getEnv() {
  198. t.Logf("MySQL-Server not running on %s. Skipping TestFloat", netAddr)
  199. return
  200. }
  201. db, err := sql.Open("mysql", dsn)
  202. if err != nil {
  203. t.Fatalf("Error connecting: %v", err)
  204. }
  205. defer db.Close()
  206. mustExec(t, db, "DROP TABLE IF EXISTS test")
  207. types := [2]string{"FLOAT", "DOUBLE"}
  208. in := float32(42.23)
  209. var out float32
  210. var rows *sql.Rows
  211. for _, v := range types {
  212. mustExec(t, db, "CREATE TABLE test (value "+v+")")
  213. mustExec(t, db, ("INSERT INTO test VALUES (?)"), in)
  214. rows = mustQuery(t, db, ("SELECT value FROM test"))
  215. if rows.Next() {
  216. rows.Scan(&out)
  217. if in != out {
  218. t.Errorf("%s: %g != %g", v, in, out)
  219. }
  220. } else {
  221. t.Errorf("%s: no data", v)
  222. }
  223. mustExec(t, db, "DROP TABLE IF EXISTS test")
  224. }
  225. }
  226. func TestString(t *testing.T) {
  227. if !getEnv() {
  228. t.Logf("MySQL-Server not running on %s. Skipping TestString", netAddr)
  229. return
  230. }
  231. db, err := sql.Open("mysql", dsn)
  232. if err != nil {
  233. t.Fatalf("Error connecting: %v", err)
  234. }
  235. defer db.Close()
  236. mustExec(t, db, "DROP TABLE IF EXISTS test")
  237. types := [6]string{"CHAR(255)", "VARCHAR(255)", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT"}
  238. in := "κόσμε üöäßñóùéàâÿœ'îë Árvíztűrő いろはにほへとちりぬるを イロハニホヘト דג סקרן чащах น่าฟังเอย"
  239. var out string
  240. var rows *sql.Rows
  241. for _, v := range types {
  242. mustExec(t, db, "CREATE TABLE test (value "+v+") CHARACTER SET utf8 COLLATE utf8_unicode_ci")
  243. mustExec(t, db, ("INSERT INTO test VALUES (?)"), in)
  244. rows = mustQuery(t, db, ("SELECT value FROM test"))
  245. if rows.Next() {
  246. rows.Scan(&out)
  247. if in != out {
  248. t.Errorf("%s: %s != %s", v, in, out)
  249. }
  250. } else {
  251. t.Errorf("%s: no data", v)
  252. }
  253. mustExec(t, db, "DROP TABLE IF EXISTS test")
  254. }
  255. // BLOB
  256. mustExec(t, db, "CREATE TABLE test (id int, value BLOB) CHARACTER SET utf8 COLLATE utf8_unicode_ci")
  257. id := 2
  258. in = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " +
  259. "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, " +
  260. "sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. " +
  261. "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. " +
  262. "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " +
  263. "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, " +
  264. "sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. " +
  265. "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
  266. mustExec(t, db, ("INSERT INTO test VALUES (?, ?)"), id, in)
  267. err = db.QueryRow("SELECT value FROM test WHERE id = ?", id).Scan(&out)
  268. if err != nil {
  269. t.Fatalf("Error on BLOB-Query: %v", err)
  270. } else if out != in {
  271. t.Errorf("BLOB: %s != %s", in, out)
  272. }
  273. return
  274. }
  275. func TestNULL(t *testing.T) {
  276. if !getEnv() {
  277. t.Logf("MySQL-Server not running on %s. Skipping TestNULL", netAddr)
  278. return
  279. }
  280. db, err := sql.Open("mysql", dsn)
  281. if err != nil {
  282. t.Fatalf("Error connecting: %v", err)
  283. }
  284. defer db.Close()
  285. nullStmt, err := db.Prepare("SELECT NULL")
  286. if err != nil {
  287. t.Fatal(err)
  288. }
  289. defer nullStmt.Close()
  290. nonNullStmt, err := db.Prepare("SELECT 1")
  291. if err != nil {
  292. t.Fatal(err)
  293. }
  294. defer nonNullStmt.Close()
  295. // NullBool
  296. var nb sql.NullBool
  297. // Invalid
  298. err = nullStmt.QueryRow().Scan(&nb)
  299. if err != nil {
  300. t.Fatal(err)
  301. }
  302. if nb.Valid {
  303. t.Error("Valid NullBool which should be invalid")
  304. }
  305. // Valid
  306. err = nonNullStmt.QueryRow().Scan(&nb)
  307. if err != nil {
  308. t.Fatal(err)
  309. }
  310. if !nb.Valid {
  311. t.Error("Invalid NullBool which should be valid")
  312. } else if nb.Bool != true {
  313. t.Errorf("Unexpected NullBool value: %t (should be true)", nb.Bool)
  314. }
  315. // NullFloat64
  316. var nf sql.NullFloat64
  317. // Invalid
  318. err = nullStmt.QueryRow().Scan(&nf)
  319. if err != nil {
  320. t.Fatal(err)
  321. }
  322. if nf.Valid {
  323. t.Error("Valid NullFloat64 which should be invalid")
  324. }
  325. // Valid
  326. err = nonNullStmt.QueryRow().Scan(&nf)
  327. if err != nil {
  328. t.Fatal(err)
  329. }
  330. if !nf.Valid {
  331. t.Error("Invalid NullFloat64 which should be valid")
  332. } else if nf.Float64 != float64(1) {
  333. t.Errorf("Unexpected NullFloat64 value: %f (should be 1.0)", nf.Float64)
  334. }
  335. // NullInt64
  336. var ni sql.NullInt64
  337. // Invalid
  338. err = nullStmt.QueryRow().Scan(&ni)
  339. if err != nil {
  340. t.Fatal(err)
  341. }
  342. if ni.Valid {
  343. t.Error("Valid NullInt64 which should be invalid")
  344. }
  345. // Valid
  346. err = nonNullStmt.QueryRow().Scan(&ni)
  347. if err != nil {
  348. t.Fatal(err)
  349. }
  350. if !ni.Valid {
  351. t.Error("Invalid NullInt64 which should be valid")
  352. } else if ni.Int64 != int64(1) {
  353. t.Errorf("Unexpected NullInt64 value: %d (should be 1)", ni.Int64)
  354. }
  355. // NullString
  356. var ns sql.NullString
  357. // Invalid
  358. err = nullStmt.QueryRow().Scan(&ns)
  359. if err != nil {
  360. t.Fatal(err)
  361. }
  362. if ns.Valid {
  363. t.Error("Valid NullString which should be invalid")
  364. }
  365. // Valid
  366. err = nonNullStmt.QueryRow().Scan(&ns)
  367. if err != nil {
  368. t.Fatal(err)
  369. }
  370. if !ns.Valid {
  371. t.Error("Invalid NullString which should be valid")
  372. } else if ns.String != `1` {
  373. t.Error("Unexpected NullString value:" + ns.String + " (should be `1`)")
  374. }
  375. }
  376. // Special cases
  377. func TestRowsClose(t *testing.T) {
  378. if !getEnv() {
  379. t.Logf("MySQL-Server not running on %s. Skipping TestRowsClose", netAddr)
  380. return
  381. }
  382. db, err := sql.Open("mysql", dsn)
  383. if err != nil {
  384. t.Fatalf("Error connecting: %v", err)
  385. }
  386. defer db.Close()
  387. rows, err := db.Query("SELECT 1")
  388. if err != nil {
  389. t.Fatal(err)
  390. }
  391. err = rows.Close()
  392. if err != nil {
  393. t.Fatal(err)
  394. }
  395. if rows.Next() {
  396. t.Fatal("Unexpected row after rows.Close()")
  397. }
  398. err = rows.Err()
  399. if err != nil {
  400. t.Fatal(err)
  401. }
  402. }
  403. // dangling statements
  404. // http://code.google.com/p/go/issues/detail?id=3865
  405. func TestCloseStmtBeforeRows(t *testing.T) {
  406. if !getEnv() {
  407. t.Logf("MySQL-Server not running on %s. Skipping TestCloseStmtBeforeRows", netAddr)
  408. return
  409. }
  410. db, err := sql.Open("mysql", dsn)
  411. if err != nil {
  412. t.Fatalf("Error connecting: %v", err)
  413. }
  414. defer db.Close()
  415. stmt, err := db.Prepare("SELECT 1")
  416. if err != nil {
  417. t.Fatal(err)
  418. }
  419. rows, err := stmt.Query()
  420. if err != nil {
  421. stmt.Close()
  422. t.Fatal(err)
  423. }
  424. defer rows.Close()
  425. err = stmt.Close()
  426. if err != nil {
  427. t.Fatal(err)
  428. }
  429. if !rows.Next() {
  430. t.Fatal("Getting row failed")
  431. } else {
  432. err = rows.Err()
  433. if err != nil {
  434. t.Fatal(err)
  435. }
  436. var out bool
  437. err = rows.Scan(&out)
  438. if err != nil {
  439. t.Fatalf("Error on rows.Scan(): %v", err)
  440. }
  441. if out != true {
  442. t.Errorf("true != %t", out)
  443. }
  444. }
  445. }
  446. // It is valid to have multiple Rows for the same Stmt
  447. // http://code.google.com/p/go/issues/detail?id=3734
  448. func TestStmtMultiRows(t *testing.T) {
  449. if !getEnv() {
  450. t.Logf("MySQL-Server not running on %s. Skipping TestStmtMultiRows", netAddr)
  451. return
  452. }
  453. db, err := sql.Open("mysql", dsn)
  454. if err != nil {
  455. t.Fatalf("Error connecting: %v", err)
  456. }
  457. defer db.Close()
  458. stmt, err := db.Prepare("SELECT 1 UNION SELECT 0")
  459. if err != nil {
  460. t.Fatal(err)
  461. }
  462. rows1, err := stmt.Query()
  463. if err != nil {
  464. stmt.Close()
  465. t.Fatal(err)
  466. }
  467. defer rows1.Close()
  468. rows2, err := stmt.Query()
  469. if err != nil {
  470. stmt.Close()
  471. t.Fatal(err)
  472. }
  473. defer rows2.Close()
  474. var out bool
  475. // 1
  476. if !rows1.Next() {
  477. t.Fatal("1st rows1.Next failed")
  478. } else {
  479. err = rows1.Err()
  480. if err != nil {
  481. t.Fatal(err)
  482. }
  483. err = rows1.Scan(&out)
  484. if err != nil {
  485. t.Fatalf("Error on rows.Scan(): %v", err)
  486. }
  487. if out != true {
  488. t.Errorf("true != %t", out)
  489. }
  490. }
  491. if !rows2.Next() {
  492. t.Fatal("1st rows2.Next failed")
  493. } else {
  494. err = rows2.Err()
  495. if err != nil {
  496. t.Fatal(err)
  497. }
  498. err = rows2.Scan(&out)
  499. if err != nil {
  500. t.Fatalf("Error on rows.Scan(): %v", err)
  501. }
  502. if out != true {
  503. t.Errorf("true != %t", out)
  504. }
  505. }
  506. // 2
  507. if !rows1.Next() {
  508. t.Fatal("2nd rows1.Next failed")
  509. } else {
  510. err = rows1.Err()
  511. if err != nil {
  512. t.Fatal(err)
  513. }
  514. err = rows1.Scan(&out)
  515. if err != nil {
  516. t.Fatalf("Error on rows.Scan(): %v", err)
  517. }
  518. if out != false {
  519. t.Errorf("false != %t", out)
  520. }
  521. if rows1.Next() {
  522. t.Fatal("Unexpected row on rows1")
  523. }
  524. err = rows1.Close()
  525. if err != nil {
  526. t.Fatal(err)
  527. }
  528. }
  529. if !rows2.Next() {
  530. t.Fatal("2nd rows2.Next failed")
  531. } else {
  532. err = rows2.Err()
  533. if err != nil {
  534. t.Fatal(err)
  535. }
  536. err = rows2.Scan(&out)
  537. if err != nil {
  538. t.Fatalf("Error on rows.Scan(): %v", err)
  539. }
  540. if out != false {
  541. t.Errorf("false != %t", out)
  542. }
  543. if rows2.Next() {
  544. t.Fatal("Unexpected row on rows2")
  545. }
  546. err = rows2.Close()
  547. if err != nil {
  548. t.Fatal(err)
  549. }
  550. }
  551. }
  552. var canStop bool
  553. func doStuff(t *testing.T) {
  554. db, err := sql.Open("mysql", dsn)
  555. if err != nil {
  556. t.Fatalf("Error connecting: %v", err)
  557. }
  558. defer db.Close()
  559. for !canStop {
  560. _, err := db.Exec("SELECT 1")
  561. if err != nil {
  562. canStop = true
  563. t.Fatalf(err.Error())
  564. }
  565. }
  566. }
  567. func TestConcurrent(t *testing.T) {
  568. if os.Getenv("MYSQL_TEST_CONCURRENT") != "1" {
  569. t.Log("CONCURRENT env var not set. Skipping TestConcurrent")
  570. return
  571. }
  572. if !getEnv() {
  573. t.Logf("MySQL-Server not running on %s. Skipping TestConcurrent", netAddr)
  574. return
  575. }
  576. fmt.Println("Run")
  577. canStop = false
  578. for i := 0; i < 500; i++ {
  579. go doStuff(t)
  580. }
  581. time.Sleep(3 * time.Second)
  582. canStop = true
  583. }