driver_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  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?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 != %d", out)
  101. }
  102. if rows.Next() {
  103. t.Error("unexpected data")
  104. }
  105. } else {
  106. t.Error("no data")
  107. }
  108. // Update
  109. 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 != %d", out)
  123. }
  124. if rows.Next() {
  125. t.Error("unexpected data")
  126. }
  127. } else {
  128. t.Error("no data")
  129. }
  130. // Delete
  131. 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. }
  140. func TestInt(t *testing.T) {
  141. if !getEnv() {
  142. t.Logf("MySQL-Server not running on %s. Skipping TestInt", netAddr)
  143. return
  144. }
  145. db, err := sql.Open("mysql", dsn)
  146. if err != nil {
  147. t.Fatalf("Error connecting: %v", err)
  148. }
  149. defer db.Close()
  150. mustExec(t, db, "DROP TABLE IF EXISTS test")
  151. types := [5]string{"TINYINT", "SMALLINT", "MEDIUMINT", "INT", "BIGINT"}
  152. in := int64(42)
  153. var out int64
  154. var rows *sql.Rows
  155. // SIGNED
  156. for _, v := range types {
  157. mustExec(t, db, "CREATE TABLE test (value "+v+")")
  158. mustExec(t, db, ("INSERT INTO test VALUES (?)"), in)
  159. rows = mustQuery(t, db, ("SELECT value FROM test"))
  160. if rows.Next() {
  161. rows.Scan(&out)
  162. if in != out {
  163. t.Errorf("%s: %d != %d", v, in, out)
  164. }
  165. } else {
  166. t.Errorf("%s: no data", v)
  167. }
  168. mustExec(t, db, "DROP TABLE IF EXISTS test")
  169. }
  170. // UNSIGNED ZEROFILL
  171. for _, v := range types {
  172. mustExec(t, db, "CREATE TABLE test (value "+v+" ZEROFILL)")
  173. mustExec(t, db, ("INSERT INTO test VALUES (?)"), in)
  174. rows = mustQuery(t, db, ("SELECT value FROM test"))
  175. if rows.Next() {
  176. rows.Scan(&out)
  177. if in != out {
  178. t.Errorf("%s ZEROFILL: %d != %d", v, in, out)
  179. }
  180. } else {
  181. t.Errorf("%s ZEROFILL: no data", v)
  182. }
  183. mustExec(t, db, "DROP TABLE IF EXISTS test")
  184. }
  185. }
  186. func TestFloat(t *testing.T) {
  187. if !getEnv() {
  188. t.Logf("MySQL-Server not running on %s. Skipping TestFloat", netAddr)
  189. return
  190. }
  191. db, err := sql.Open("mysql", dsn)
  192. if err != nil {
  193. t.Fatalf("Error connecting: %v", err)
  194. }
  195. defer db.Close()
  196. mustExec(t, db, "DROP TABLE IF EXISTS test")
  197. types := [2]string{"FLOAT", "DOUBLE"}
  198. in := float64(42.23)
  199. var out float64
  200. var rows *sql.Rows
  201. for _, v := range types {
  202. mustExec(t, db, "CREATE TABLE test (value "+v+")")
  203. mustExec(t, db, ("INSERT INTO test VALUES (?)"), in)
  204. rows = mustQuery(t, db, ("SELECT value FROM test"))
  205. if rows.Next() {
  206. rows.Scan(&out)
  207. if in != out {
  208. t.Errorf("%s: %d != %d", v, in, out)
  209. }
  210. } else {
  211. t.Errorf("%s: no data", v)
  212. }
  213. mustExec(t, db, "DROP TABLE IF EXISTS test")
  214. }
  215. }
  216. func TestString(t *testing.T) {
  217. if !getEnv() {
  218. t.Logf("MySQL-Server not running on %s. Skipping TestString", netAddr)
  219. return
  220. }
  221. db, err := sql.Open("mysql", dsn)
  222. if err != nil {
  223. t.Fatalf("Error connecting: %v", err)
  224. }
  225. defer db.Close()
  226. mustExec(t, db, "DROP TABLE IF EXISTS test")
  227. types := [6]string{"CHAR(255)", "VARCHAR(255)", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT"}
  228. in := "κόσμε üöäßñóùéàâÿœ'îë Árvíztűrő いろはにほへとちりぬるを イロハニホヘト דג סקרן чащах น่าฟังเอย"
  229. var out string
  230. var rows *sql.Rows
  231. for _, v := range types {
  232. mustExec(t, db, "CREATE TABLE test (value "+v+") CHARACTER SET utf8 COLLATE utf8_unicode_ci")
  233. mustExec(t, db, ("INSERT INTO test VALUES (?)"), in)
  234. rows = mustQuery(t, db, ("SELECT value FROM test"))
  235. if rows.Next() {
  236. rows.Scan(&out)
  237. if in != out {
  238. t.Errorf("%s: %d != %d", v, in, out)
  239. }
  240. } else {
  241. t.Errorf("%s: no data", v)
  242. }
  243. mustExec(t, db, "DROP TABLE IF EXISTS test")
  244. }
  245. }
  246. func TestNULL(t *testing.T) {
  247. if !getEnv() {
  248. t.Logf("MySQL-Server not running on %s. Skipping TestNULL", netAddr)
  249. return
  250. }
  251. db, err := sql.Open("mysql", dsn)
  252. if err != nil {
  253. t.Fatalf("Error connecting: %v", err)
  254. }
  255. defer db.Close()
  256. nullStmt, err := db.Prepare("SELECT NULL")
  257. if err != nil {
  258. t.Fatal(err)
  259. }
  260. defer nullStmt.Close()
  261. nonNullStmt, err := db.Prepare("SELECT 1")
  262. if err != nil {
  263. t.Fatal(err)
  264. }
  265. defer nonNullStmt.Close()
  266. // NullBool
  267. var nb sql.NullBool
  268. // Invalid
  269. err = nullStmt.QueryRow().Scan(&nb)
  270. if err != nil {
  271. t.Fatal(err)
  272. }
  273. if nb.Valid {
  274. t.Error("Valid NullBool which should be invalid")
  275. }
  276. // Valid
  277. err = nonNullStmt.QueryRow().Scan(&nb)
  278. if err != nil {
  279. t.Fatal(err)
  280. }
  281. if !nb.Valid {
  282. t.Error("Invalid NullBool which should be valid")
  283. } else if nb.Bool != true {
  284. t.Errorf("Unexpected NullBool value: %t (should be true)", nb.Bool)
  285. }
  286. // NullFloat64
  287. var nf sql.NullFloat64
  288. // Invalid
  289. err = nullStmt.QueryRow().Scan(&nf)
  290. if err != nil {
  291. t.Fatal(err)
  292. }
  293. if nf.Valid {
  294. t.Error("Valid NullFloat64 which should be invalid")
  295. }
  296. // Valid
  297. err = nonNullStmt.QueryRow().Scan(&nf)
  298. if err != nil {
  299. t.Fatal(err)
  300. }
  301. if !nf.Valid {
  302. t.Error("Invalid NullFloat64 which should be valid")
  303. } else if nf.Float64 != float64(1) {
  304. t.Errorf("Unexpected NullFloat64 value: %f (should be 1.0)", nf.Float64)
  305. }
  306. // NullInt64
  307. var ni sql.NullInt64
  308. // Invalid
  309. err = nullStmt.QueryRow().Scan(&ni)
  310. if err != nil {
  311. t.Fatal(err)
  312. }
  313. if ni.Valid {
  314. t.Error("Valid NullInt64 which should be invalid")
  315. }
  316. // Valid
  317. err = nonNullStmt.QueryRow().Scan(&ni)
  318. if err != nil {
  319. t.Fatal(err)
  320. }
  321. if !ni.Valid {
  322. t.Error("Invalid NullInt64 which should be valid")
  323. } else if ni.Int64 != int64(1) {
  324. t.Errorf("Unexpected NullInt64 value: %d (should be 1)", ni.Int64)
  325. }
  326. // NullString
  327. var ns sql.NullString
  328. // Invalid
  329. err = nullStmt.QueryRow().Scan(&ns)
  330. if err != nil {
  331. t.Fatal(err)
  332. }
  333. if ns.Valid {
  334. t.Error("Valid NullString which should be invalid")
  335. }
  336. // Valid
  337. err = nonNullStmt.QueryRow().Scan(&ns)
  338. if err != nil {
  339. t.Fatal(err)
  340. }
  341. if !ns.Valid {
  342. t.Error("Invalid NullString which should be valid")
  343. } else if ns.String != `1` {
  344. t.Error("Unexpected NullString value:" + ns.String + " (should be `1`)")
  345. }
  346. }
  347. // Special cases
  348. func TestRowsClose(t *testing.T) {
  349. if !getEnv() {
  350. t.Logf("MySQL-Server not running on %s. Skipping TestRowsClose", netAddr)
  351. return
  352. }
  353. db, err := sql.Open("mysql", dsn)
  354. if err != nil {
  355. t.Fatalf("Error connecting: %v", err)
  356. }
  357. defer db.Close()
  358. rows, err := db.Query("SELECT 1")
  359. if err != nil {
  360. t.Fatal(err)
  361. }
  362. err = rows.Close()
  363. if err != nil {
  364. t.Fatal(err)
  365. }
  366. if rows.Next() {
  367. t.Fatal("Unexpected row after rows.Close()")
  368. }
  369. err = rows.Err()
  370. if err != nil {
  371. t.Fatal(err)
  372. }
  373. }
  374. // dangling statements
  375. // http://code.google.com/p/go/issues/detail?id=3865
  376. func TestCloseStmtBeforeRows(t *testing.T) {
  377. if !getEnv() {
  378. t.Logf("MySQL-Server not running on %s. Skipping TestCloseStmtBeforeRows", netAddr)
  379. return
  380. }
  381. db, err := sql.Open("mysql", dsn)
  382. if err != nil {
  383. t.Fatalf("Error connecting: %v", err)
  384. }
  385. defer db.Close()
  386. stmt, err := db.Prepare("SELECT 1")
  387. if err != nil {
  388. t.Fatal(err)
  389. }
  390. rows, err := stmt.Query()
  391. if err != nil {
  392. stmt.Close()
  393. t.Fatal(err)
  394. }
  395. defer rows.Close()
  396. err = stmt.Close()
  397. if err != nil {
  398. t.Fatal(err)
  399. }
  400. if !rows.Next() {
  401. t.Fatal("Getting row failed")
  402. } else {
  403. err = rows.Err()
  404. if err != nil {
  405. t.Fatal(err)
  406. }
  407. var out bool
  408. err = rows.Scan(&out)
  409. if err != nil {
  410. t.Fatalf("Error on rows.Scan(): %v", err)
  411. }
  412. if out != true {
  413. t.Errorf("true != %d", out)
  414. }
  415. }
  416. }
  417. // It is valid to have multiple Rows for the same Stmt
  418. // http://code.google.com/p/go/issues/detail?id=3734
  419. func TestStmtMultiRows(t *testing.T) {
  420. if !getEnv() {
  421. t.Logf("MySQL-Server not running on %s. Skipping TestStmtMultiRows", netAddr)
  422. return
  423. }
  424. db, err := sql.Open("mysql", dsn)
  425. if err != nil {
  426. t.Fatalf("Error connecting: %v", err)
  427. }
  428. defer db.Close()
  429. stmt, err := db.Prepare("SELECT 1 UNION SELECT 0")
  430. if err != nil {
  431. t.Fatal(err)
  432. }
  433. rows1, err := stmt.Query()
  434. if err != nil {
  435. stmt.Close()
  436. t.Fatal(err)
  437. }
  438. defer rows1.Close()
  439. rows2, err := stmt.Query()
  440. if err != nil {
  441. stmt.Close()
  442. t.Fatal(err)
  443. }
  444. defer rows2.Close()
  445. var out bool
  446. // 1
  447. if !rows1.Next() {
  448. t.Fatal("1st rows1.Next failed")
  449. } else {
  450. err = rows1.Err()
  451. if err != nil {
  452. t.Fatal(err)
  453. }
  454. err = rows1.Scan(&out)
  455. if err != nil {
  456. t.Fatalf("Error on rows.Scan(): %v", err)
  457. }
  458. if out != true {
  459. t.Errorf("true != %d", out)
  460. }
  461. }
  462. if !rows2.Next() {
  463. t.Fatal("1st rows2.Next failed")
  464. } else {
  465. err = rows2.Err()
  466. if err != nil {
  467. t.Fatal(err)
  468. }
  469. err = rows2.Scan(&out)
  470. if err != nil {
  471. t.Fatalf("Error on rows.Scan(): %v", err)
  472. }
  473. if out != true {
  474. t.Errorf("true != %d", out)
  475. }
  476. }
  477. // 2
  478. if !rows1.Next() {
  479. t.Fatal("2nd rows1.Next failed")
  480. } else {
  481. err = rows1.Err()
  482. if err != nil {
  483. t.Fatal(err)
  484. }
  485. err = rows1.Scan(&out)
  486. if err != nil {
  487. t.Fatalf("Error on rows.Scan(): %v", err)
  488. }
  489. if out != false {
  490. t.Errorf("false != %d", out)
  491. }
  492. if rows1.Next() {
  493. t.Fatal("Unexpected row on rows1")
  494. }
  495. err = rows1.Close()
  496. if err != nil {
  497. t.Fatal(err)
  498. }
  499. }
  500. if !rows2.Next() {
  501. t.Fatal("2nd rows2.Next failed")
  502. } else {
  503. err = rows2.Err()
  504. if err != nil {
  505. t.Fatal(err)
  506. }
  507. err = rows2.Scan(&out)
  508. if err != nil {
  509. t.Fatalf("Error on rows.Scan(): %v", err)
  510. }
  511. if out != false {
  512. t.Errorf("false != %d", out)
  513. }
  514. if rows2.Next() {
  515. t.Fatal("Unexpected row on rows2")
  516. }
  517. err = rows2.Close()
  518. if err != nil {
  519. t.Fatal(err)
  520. }
  521. }
  522. }