driver_test.go 12 KB

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