driver_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  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. }
  256. func TestNULL(t *testing.T) {
  257. if !getEnv() {
  258. t.Logf("MySQL-Server not running on %s. Skipping TestNULL", netAddr)
  259. return
  260. }
  261. db, err := sql.Open("mysql", dsn)
  262. if err != nil {
  263. t.Fatalf("Error connecting: %v", err)
  264. }
  265. defer db.Close()
  266. nullStmt, err := db.Prepare("SELECT NULL")
  267. if err != nil {
  268. t.Fatal(err)
  269. }
  270. defer nullStmt.Close()
  271. nonNullStmt, err := db.Prepare("SELECT 1")
  272. if err != nil {
  273. t.Fatal(err)
  274. }
  275. defer nonNullStmt.Close()
  276. // NullBool
  277. var nb sql.NullBool
  278. // Invalid
  279. err = nullStmt.QueryRow().Scan(&nb)
  280. if err != nil {
  281. t.Fatal(err)
  282. }
  283. if nb.Valid {
  284. t.Error("Valid NullBool which should be invalid")
  285. }
  286. // Valid
  287. err = nonNullStmt.QueryRow().Scan(&nb)
  288. if err != nil {
  289. t.Fatal(err)
  290. }
  291. if !nb.Valid {
  292. t.Error("Invalid NullBool which should be valid")
  293. } else if nb.Bool != true {
  294. t.Errorf("Unexpected NullBool value: %t (should be true)", nb.Bool)
  295. }
  296. // NullFloat64
  297. var nf sql.NullFloat64
  298. // Invalid
  299. err = nullStmt.QueryRow().Scan(&nf)
  300. if err != nil {
  301. t.Fatal(err)
  302. }
  303. if nf.Valid {
  304. t.Error("Valid NullFloat64 which should be invalid")
  305. }
  306. // Valid
  307. err = nonNullStmt.QueryRow().Scan(&nf)
  308. if err != nil {
  309. t.Fatal(err)
  310. }
  311. if !nf.Valid {
  312. t.Error("Invalid NullFloat64 which should be valid")
  313. } else if nf.Float64 != float64(1) {
  314. t.Errorf("Unexpected NullFloat64 value: %f (should be 1.0)", nf.Float64)
  315. }
  316. // NullInt64
  317. var ni sql.NullInt64
  318. // Invalid
  319. err = nullStmt.QueryRow().Scan(&ni)
  320. if err != nil {
  321. t.Fatal(err)
  322. }
  323. if ni.Valid {
  324. t.Error("Valid NullInt64 which should be invalid")
  325. }
  326. // Valid
  327. err = nonNullStmt.QueryRow().Scan(&ni)
  328. if err != nil {
  329. t.Fatal(err)
  330. }
  331. if !ni.Valid {
  332. t.Error("Invalid NullInt64 which should be valid")
  333. } else if ni.Int64 != int64(1) {
  334. t.Errorf("Unexpected NullInt64 value: %d (should be 1)", ni.Int64)
  335. }
  336. // NullString
  337. var ns sql.NullString
  338. // Invalid
  339. err = nullStmt.QueryRow().Scan(&ns)
  340. if err != nil {
  341. t.Fatal(err)
  342. }
  343. if ns.Valid {
  344. t.Error("Valid NullString which should be invalid")
  345. }
  346. // Valid
  347. err = nonNullStmt.QueryRow().Scan(&ns)
  348. if err != nil {
  349. t.Fatal(err)
  350. }
  351. if !ns.Valid {
  352. t.Error("Invalid NullString which should be valid")
  353. } else if ns.String != `1` {
  354. t.Error("Unexpected NullString value:" + ns.String + " (should be `1`)")
  355. }
  356. }
  357. // Special cases
  358. func TestRowsClose(t *testing.T) {
  359. if !getEnv() {
  360. t.Logf("MySQL-Server not running on %s. Skipping TestRowsClose", netAddr)
  361. return
  362. }
  363. db, err := sql.Open("mysql", dsn)
  364. if err != nil {
  365. t.Fatalf("Error connecting: %v", err)
  366. }
  367. defer db.Close()
  368. rows, err := db.Query("SELECT 1")
  369. if err != nil {
  370. t.Fatal(err)
  371. }
  372. err = rows.Close()
  373. if err != nil {
  374. t.Fatal(err)
  375. }
  376. if rows.Next() {
  377. t.Fatal("Unexpected row after rows.Close()")
  378. }
  379. err = rows.Err()
  380. if err != nil {
  381. t.Fatal(err)
  382. }
  383. }
  384. // dangling statements
  385. // http://code.google.com/p/go/issues/detail?id=3865
  386. func TestCloseStmtBeforeRows(t *testing.T) {
  387. if !getEnv() {
  388. t.Logf("MySQL-Server not running on %s. Skipping TestCloseStmtBeforeRows", netAddr)
  389. return
  390. }
  391. db, err := sql.Open("mysql", dsn)
  392. if err != nil {
  393. t.Fatalf("Error connecting: %v", err)
  394. }
  395. defer db.Close()
  396. stmt, err := db.Prepare("SELECT 1")
  397. if err != nil {
  398. t.Fatal(err)
  399. }
  400. rows, err := stmt.Query()
  401. if err != nil {
  402. stmt.Close()
  403. t.Fatal(err)
  404. }
  405. defer rows.Close()
  406. err = stmt.Close()
  407. if err != nil {
  408. t.Fatal(err)
  409. }
  410. if !rows.Next() {
  411. t.Fatal("Getting row failed")
  412. } else {
  413. err = rows.Err()
  414. if err != nil {
  415. t.Fatal(err)
  416. }
  417. var out bool
  418. err = rows.Scan(&out)
  419. if err != nil {
  420. t.Fatalf("Error on rows.Scan(): %v", err)
  421. }
  422. if out != true {
  423. t.Errorf("true != %t", out)
  424. }
  425. }
  426. }
  427. // It is valid to have multiple Rows for the same Stmt
  428. // http://code.google.com/p/go/issues/detail?id=3734
  429. func TestStmtMultiRows(t *testing.T) {
  430. if !getEnv() {
  431. t.Logf("MySQL-Server not running on %s. Skipping TestStmtMultiRows", netAddr)
  432. return
  433. }
  434. db, err := sql.Open("mysql", dsn)
  435. if err != nil {
  436. t.Fatalf("Error connecting: %v", err)
  437. }
  438. defer db.Close()
  439. stmt, err := db.Prepare("SELECT 1 UNION SELECT 0")
  440. if err != nil {
  441. t.Fatal(err)
  442. }
  443. rows1, err := stmt.Query()
  444. if err != nil {
  445. stmt.Close()
  446. t.Fatal(err)
  447. }
  448. defer rows1.Close()
  449. rows2, err := stmt.Query()
  450. if err != nil {
  451. stmt.Close()
  452. t.Fatal(err)
  453. }
  454. defer rows2.Close()
  455. var out bool
  456. // 1
  457. if !rows1.Next() {
  458. t.Fatal("1st rows1.Next failed")
  459. } else {
  460. err = rows1.Err()
  461. if err != nil {
  462. t.Fatal(err)
  463. }
  464. err = rows1.Scan(&out)
  465. if err != nil {
  466. t.Fatalf("Error on rows.Scan(): %v", err)
  467. }
  468. if out != true {
  469. t.Errorf("true != %t", out)
  470. }
  471. }
  472. if !rows2.Next() {
  473. t.Fatal("1st rows2.Next failed")
  474. } else {
  475. err = rows2.Err()
  476. if err != nil {
  477. t.Fatal(err)
  478. }
  479. err = rows2.Scan(&out)
  480. if err != nil {
  481. t.Fatalf("Error on rows.Scan(): %v", err)
  482. }
  483. if out != true {
  484. t.Errorf("true != %t", out)
  485. }
  486. }
  487. // 2
  488. if !rows1.Next() {
  489. t.Fatal("2nd rows1.Next failed")
  490. } else {
  491. err = rows1.Err()
  492. if err != nil {
  493. t.Fatal(err)
  494. }
  495. err = rows1.Scan(&out)
  496. if err != nil {
  497. t.Fatalf("Error on rows.Scan(): %v", err)
  498. }
  499. if out != false {
  500. t.Errorf("false != %t", out)
  501. }
  502. if rows1.Next() {
  503. t.Fatal("Unexpected row on rows1")
  504. }
  505. err = rows1.Close()
  506. if err != nil {
  507. t.Fatal(err)
  508. }
  509. }
  510. if !rows2.Next() {
  511. t.Fatal("2nd rows2.Next failed")
  512. } else {
  513. err = rows2.Err()
  514. if err != nil {
  515. t.Fatal(err)
  516. }
  517. err = rows2.Scan(&out)
  518. if err != nil {
  519. t.Fatalf("Error on rows.Scan(): %v", err)
  520. }
  521. if out != false {
  522. t.Errorf("false != %t", out)
  523. }
  524. if rows2.Next() {
  525. t.Fatal("Unexpected row on rows2")
  526. }
  527. err = rows2.Close()
  528. if err != nil {
  529. t.Fatal(err)
  530. }
  531. }
  532. }
  533. var canStop bool
  534. func doStuff(t *testing.T) {
  535. db, err := sql.Open("mysql", dsn)
  536. if err != nil {
  537. t.Fatalf("Error connecting: %v", err)
  538. }
  539. defer db.Close()
  540. for !canStop {
  541. _, err := db.Exec("SELECT 1")
  542. if err != nil {
  543. canStop = true
  544. t.Fatalf(err.Error())
  545. }
  546. }
  547. }
  548. func TestConcurrent(t *testing.T) {
  549. if os.Getenv("MYSQL_TEST_CONCURRENT") != "1" {
  550. t.Log("CONCURRENT env var not set. Skipping TestConcurrent")
  551. return
  552. }
  553. if !getEnv() {
  554. t.Logf("MySQL-Server not running on %s. Skipping TestConcurrent", netAddr)
  555. return
  556. }
  557. fmt.Println("Run")
  558. canStop = false
  559. for i := 0; i < 500; i++ {
  560. go doStuff(t)
  561. }
  562. time.Sleep(3 * time.Second)
  563. canStop = true
  564. }