/example_sql_test.go

http://github.com/mattn/go-oci8 · Go · 818 lines · 620 code · 116 blank · 82 comment · 202 complexity · 25136a8261a72fb3e4f766f9aabd0d76 MD5 · raw file

  1. package oci8_test
  2. import (
  3. "bytes"
  4. "context"
  5. "database/sql"
  6. "fmt"
  7. "log"
  8. "os"
  9. "strings"
  10. "time"
  11. "github.com/mattn/go-oci8"
  12. )
  13. func Example_sqlSelect() {
  14. // Example shows how to do a basic select
  15. // For testing, check if database tests are disabled
  16. if oci8.TestDisableDatabase {
  17. fmt.Println(1)
  18. return
  19. }
  20. oci8.Driver.Logger = log.New(os.Stderr, "oci8 ", log.Ldate|log.Ltime|log.LUTC|log.Lshortfile)
  21. var openString string
  22. // [username/[password]@]host[:port][/service_name][?param1=value1&...&paramN=valueN]
  23. if len(oci8.TestUsername) > 0 {
  24. if len(oci8.TestPassword) > 0 {
  25. openString = oci8.TestUsername + "/" + oci8.TestPassword + "@"
  26. } else {
  27. openString = oci8.TestUsername + "@"
  28. }
  29. }
  30. openString += oci8.TestHostValid
  31. // A normal simple Open to localhost would look like:
  32. // db, err := sql.Open("oci8", "127.0.0.1")
  33. // For testing, need to use additional variables
  34. db, err := sql.Open("oci8", openString)
  35. if err != nil {
  36. fmt.Printf("Open error is not nil: %v", err)
  37. return
  38. }
  39. if db == nil {
  40. fmt.Println("db is nil")
  41. return
  42. }
  43. // defer close database
  44. defer func() {
  45. err = db.Close()
  46. if err != nil {
  47. fmt.Println("Close error is not nil:", err)
  48. }
  49. }()
  50. ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second)
  51. err = db.PingContext(ctx)
  52. cancel()
  53. if err != nil {
  54. fmt.Println("PingContext error is not nil:", err)
  55. return
  56. }
  57. var rows *sql.Rows
  58. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  59. defer cancel()
  60. rows, err = db.QueryContext(ctx, "select 1 from dual")
  61. if err != nil {
  62. fmt.Println("QueryContext error is not nil:", err)
  63. return
  64. }
  65. // defer close rows
  66. defer func() {
  67. err = rows.Close()
  68. if err != nil {
  69. fmt.Println("Close error is not nil:", err)
  70. }
  71. }()
  72. if !rows.Next() {
  73. fmt.Println("no Next rows")
  74. return
  75. }
  76. dest := make([]interface{}, 1)
  77. destPointer := make([]interface{}, 1)
  78. destPointer[0] = &dest[0]
  79. err = rows.Scan(destPointer...)
  80. if err != nil {
  81. fmt.Println("Scan error is not nil:", err)
  82. return
  83. }
  84. if len(dest) != 1 {
  85. fmt.Println("len dest != 1")
  86. return
  87. }
  88. data, ok := dest[0].(float64)
  89. if !ok {
  90. fmt.Println("dest type not float64")
  91. return
  92. }
  93. if data != 1 {
  94. fmt.Println("data not equal to 1")
  95. return
  96. }
  97. if rows.Next() {
  98. fmt.Println("has Next rows")
  99. return
  100. }
  101. err = rows.Err()
  102. if err != nil {
  103. fmt.Println("Err error is not nil:", err)
  104. return
  105. }
  106. fmt.Println(data)
  107. // output: 1
  108. }
  109. func Example_sqlFunction() {
  110. // Example shows how to do a function call with binds
  111. // For testing, check if database tests are disabled
  112. if oci8.TestDisableDatabase {
  113. fmt.Println(3)
  114. return
  115. }
  116. oci8.Driver.Logger = log.New(os.Stderr, "oci8 ", log.Ldate|log.Ltime|log.LUTC|log.Lshortfile)
  117. var openString string
  118. // [username/[password]@]host[:port][/service_name][?param1=value1&...&paramN=valueN]
  119. if len(oci8.TestUsername) > 0 {
  120. if len(oci8.TestPassword) > 0 {
  121. openString = oci8.TestUsername + "/" + oci8.TestPassword + "@"
  122. } else {
  123. openString = oci8.TestUsername + "@"
  124. }
  125. }
  126. openString += oci8.TestHostValid
  127. // A normal simple Open to localhost would look like:
  128. // db, err := sql.Open("oci8", "127.0.0.1")
  129. // For testing, need to use additional variables
  130. db, err := sql.Open("oci8", openString)
  131. if err != nil {
  132. fmt.Printf("Open error is not nil: %v", err)
  133. return
  134. }
  135. if db == nil {
  136. fmt.Println("db is nil")
  137. return
  138. }
  139. // defer close database
  140. defer func() {
  141. err = db.Close()
  142. if err != nil {
  143. fmt.Println("Close error is not nil:", err)
  144. }
  145. }()
  146. number := int64(2)
  147. query := `
  148. declare
  149. function ADD_ONE(p_number INTEGER) return INTEGER as
  150. begin
  151. return p_number + 1;
  152. end ADD_ONE;
  153. begin
  154. :num1 := ADD_ONE(:num1);
  155. end;`
  156. ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second)
  157. _, err = db.ExecContext(ctx, query, sql.Out{Dest: &number, In: true})
  158. cancel()
  159. if err != nil {
  160. fmt.Println("ExecContext error is not nil:", err)
  161. return
  162. }
  163. if number != 3 {
  164. fmt.Println("number != 3")
  165. return
  166. }
  167. fmt.Println(number)
  168. // output: 3
  169. }
  170. func Example_sqlInsert() {
  171. // Example shows how to do a single insert
  172. // For testing, check if database tests are disabled
  173. if oci8.TestDisableDatabase || oci8.TestDisableDestructive {
  174. fmt.Println(1)
  175. return
  176. }
  177. oci8.Driver.Logger = log.New(os.Stderr, "oci8 ", log.Ldate|log.Ltime|log.LUTC|log.Lshortfile)
  178. var openString string
  179. // [username/[password]@]host[:port][/service_name][?param1=value1&...&paramN=valueN]
  180. if len(oci8.TestUsername) > 0 {
  181. if len(oci8.TestPassword) > 0 {
  182. openString = oci8.TestUsername + "/" + oci8.TestPassword + "@"
  183. } else {
  184. openString = oci8.TestUsername + "@"
  185. }
  186. }
  187. openString += oci8.TestHostValid
  188. // A normal simple Open to localhost would look like:
  189. // db, err := sql.Open("oci8", "127.0.0.1")
  190. // For testing, need to use additional variables
  191. db, err := sql.Open("oci8", openString)
  192. if err != nil {
  193. fmt.Printf("Open error is not nil: %v", err)
  194. return
  195. }
  196. if db == nil {
  197. fmt.Println("db is nil")
  198. return
  199. }
  200. // defer close database
  201. defer func() {
  202. err = db.Close()
  203. if err != nil {
  204. fmt.Println("Close error is not nil:", err)
  205. }
  206. }()
  207. // create table
  208. tableName := "E_INSERT_" + oci8.TestTimeString
  209. query := "create table " + tableName + " ( A INTEGER )"
  210. ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second)
  211. _, err = db.ExecContext(ctx, query)
  212. cancel()
  213. if err != nil {
  214. fmt.Println("ExecContext error is not nil:", err)
  215. return
  216. }
  217. // insert row
  218. var result sql.Result
  219. query = "insert into " + tableName + " ( A ) values (:1)"
  220. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  221. result, err = db.ExecContext(ctx, query, 1)
  222. cancel()
  223. if err != nil {
  224. fmt.Println("ExecContext error is not nil:", err)
  225. return
  226. }
  227. // can see number of RowsAffected if wanted
  228. var rowsAffected int64
  229. rowsAffected, err = result.RowsAffected()
  230. if err != nil {
  231. fmt.Println("RowsAffected error is not nil:", err)
  232. return
  233. }
  234. // drop table
  235. query = "drop table " + tableName
  236. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  237. _, err = db.ExecContext(ctx, query)
  238. cancel()
  239. if err != nil {
  240. fmt.Println("ExecContext error is not nil:", err)
  241. return
  242. }
  243. fmt.Println(rowsAffected)
  244. // output: 1
  245. }
  246. func Example_sqlManyInserts() {
  247. // Example shows how to do a many inserts
  248. // For testing, check if database tests are disabled
  249. if oci8.TestDisableDatabase || oci8.TestDisableDestructive {
  250. fmt.Println(3)
  251. return
  252. }
  253. oci8.Driver.Logger = log.New(os.Stderr, "oci8 ", log.Ldate|log.Ltime|log.LUTC|log.Lshortfile)
  254. var openString string
  255. // [username/[password]@]host[:port][/service_name][?param1=value1&...&paramN=valueN]
  256. if len(oci8.TestUsername) > 0 {
  257. if len(oci8.TestPassword) > 0 {
  258. openString = oci8.TestUsername + "/" + oci8.TestPassword + "@"
  259. } else {
  260. openString = oci8.TestUsername + "@"
  261. }
  262. }
  263. openString += oci8.TestHostValid
  264. // A normal simple Open to localhost would look like:
  265. // db, err := sql.Open("oci8", "127.0.0.1")
  266. // For testing, need to use additional variables
  267. db, err := sql.Open("oci8", openString)
  268. if err != nil {
  269. fmt.Printf("Open error is not nil: %v", err)
  270. return
  271. }
  272. if db == nil {
  273. fmt.Println("db is nil")
  274. return
  275. }
  276. // defer close database
  277. defer func() {
  278. err = db.Close()
  279. if err != nil {
  280. fmt.Println("Close error is not nil:", err)
  281. }
  282. }()
  283. // create table
  284. tableName := "E_MANY_INSERT_" + oci8.TestTimeString
  285. query := "create table " + tableName + " ( A INTEGER )"
  286. ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second)
  287. _, err = db.ExecContext(ctx, query)
  288. cancel()
  289. if err != nil {
  290. fmt.Println("ExecContext error is not nil:", err)
  291. return
  292. }
  293. // prepare insert query statement
  294. var stmt *sql.Stmt
  295. query = "insert into " + tableName + " ( A ) values (:1)"
  296. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  297. stmt, err = db.PrepareContext(ctx, query)
  298. cancel()
  299. if err != nil {
  300. fmt.Println("PrepareContext error is not nil:", err)
  301. return
  302. }
  303. // insert 3 rows
  304. for i := 0; i < 3; i++ {
  305. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  306. _, err = stmt.ExecContext(ctx, i)
  307. cancel()
  308. if err != nil {
  309. stmt.Close()
  310. fmt.Println("ExecContext error is not nil:", err)
  311. return
  312. }
  313. }
  314. // close insert query statement
  315. err = stmt.Close()
  316. if err != nil {
  317. fmt.Println("Close error is not nil:", err)
  318. return
  319. }
  320. // select count/number of rows
  321. var rows *sql.Rows
  322. query = "select count(1) from " + tableName
  323. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  324. defer cancel()
  325. rows, err = db.QueryContext(ctx, query)
  326. if err != nil {
  327. fmt.Println("QueryContext error is not nil:", err)
  328. return
  329. }
  330. // defer close rows
  331. defer func() {
  332. err = rows.Close()
  333. if err != nil {
  334. fmt.Println("Close error is not nil:", err)
  335. }
  336. }()
  337. if !rows.Next() {
  338. fmt.Println("no Next rows")
  339. return
  340. }
  341. var count int64
  342. err = rows.Scan(&count)
  343. if err != nil {
  344. fmt.Println("Scan error is not nil:", err)
  345. return
  346. }
  347. if count != 3 {
  348. fmt.Println("count not equal to 3")
  349. return
  350. }
  351. if rows.Next() {
  352. fmt.Println("has Next rows")
  353. return
  354. }
  355. err = rows.Err()
  356. if err != nil {
  357. fmt.Println("Err error is not nil:", err)
  358. return
  359. }
  360. // drop table
  361. query = "drop table " + tableName
  362. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  363. _, err = db.ExecContext(ctx, query)
  364. cancel()
  365. if err != nil {
  366. fmt.Println("ExecContext error is not nil:", err)
  367. return
  368. }
  369. fmt.Println(count)
  370. // output: 3
  371. }
  372. func Example_sqlClob() {
  373. // Example shows how write and read a clob
  374. // For testing, check if database tests are disabled
  375. if oci8.TestDisableDatabase || oci8.TestDisableDestructive {
  376. fmt.Println("done")
  377. return
  378. }
  379. oci8.Driver.Logger = log.New(os.Stderr, "oci8 ", log.Ldate|log.Ltime|log.LUTC|log.Lshortfile)
  380. var openString string
  381. // [username/[password]@]host[:port][/service_name][?param1=value1&...&paramN=valueN]
  382. if len(oci8.TestUsername) > 0 {
  383. if len(oci8.TestPassword) > 0 {
  384. openString = oci8.TestUsername + "/" + oci8.TestPassword + "@"
  385. } else {
  386. openString = oci8.TestUsername + "@"
  387. }
  388. }
  389. openString += oci8.TestHostValid
  390. // A normal simple Open to localhost would look like:
  391. // db, err := sql.Open("oci8", "127.0.0.1")
  392. // For testing, need to use additional variables
  393. db, err := sql.Open("oci8", openString)
  394. if err != nil {
  395. fmt.Printf("Open error is not nil: %v", err)
  396. return
  397. }
  398. if db == nil {
  399. fmt.Println("db is nil")
  400. return
  401. }
  402. // defer close database
  403. defer func() {
  404. err = db.Close()
  405. if err != nil {
  406. fmt.Println("Close error is not nil:", err)
  407. }
  408. }()
  409. // create table
  410. tableName := "E_CLOB_" + oci8.TestTimeString
  411. query := "create table " + tableName + " ( A CLOB )"
  412. ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second)
  413. _, err = db.ExecContext(ctx, query)
  414. cancel()
  415. if err != nil {
  416. fmt.Println("ExecContext error is not nil:", err)
  417. return
  418. }
  419. // insert row
  420. query = "insert into " + tableName + " ( A ) values (:1)"
  421. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  422. _, err = db.ExecContext(ctx, query, strings.Repeat("abcdefghijklmnopqrstuvwxyz", 200))
  423. cancel()
  424. if err != nil {
  425. fmt.Println("ExecContext error is not nil:", err)
  426. return
  427. }
  428. var rows *sql.Rows
  429. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  430. defer cancel()
  431. rows, err = db.QueryContext(ctx, "select A from "+tableName)
  432. if err != nil {
  433. fmt.Println("QueryContext error is not nil:", err)
  434. return
  435. }
  436. // defer close rows
  437. defer func() {
  438. err = rows.Close()
  439. if err != nil {
  440. fmt.Println("Close error is not nil:", err)
  441. }
  442. }()
  443. if !rows.Next() {
  444. fmt.Println("no Next rows")
  445. return
  446. }
  447. var aString string
  448. err = rows.Scan(&aString)
  449. if err != nil {
  450. fmt.Println("Scan error is not nil:", err)
  451. return
  452. }
  453. if len(aString) != 5200 {
  454. fmt.Println("len aString != 5200")
  455. return
  456. }
  457. if rows.Next() {
  458. fmt.Println("has Next rows")
  459. return
  460. }
  461. err = rows.Err()
  462. if err != nil {
  463. fmt.Println("Err error is not nil:", err)
  464. return
  465. }
  466. // drop table
  467. query = "drop table " + tableName
  468. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  469. _, err = db.ExecContext(ctx, query)
  470. cancel()
  471. if err != nil {
  472. fmt.Println("ExecContext error is not nil:", err)
  473. return
  474. }
  475. fmt.Println("done")
  476. // output: done
  477. }
  478. func Example_sqlBlob() {
  479. // Example shows how write and read a blob
  480. // For testing, check if database tests are disabled
  481. if oci8.TestDisableDatabase || oci8.TestDisableDestructive {
  482. fmt.Println("done")
  483. return
  484. }
  485. oci8.Driver.Logger = log.New(os.Stderr, "oci8 ", log.Ldate|log.Ltime|log.LUTC|log.Lshortfile)
  486. var openString string
  487. // [username/[password]@]host[:port][/service_name][?param1=value1&...&paramN=valueN]
  488. if len(oci8.TestUsername) > 0 {
  489. if len(oci8.TestPassword) > 0 {
  490. openString = oci8.TestUsername + "/" + oci8.TestPassword + "@"
  491. } else {
  492. openString = oci8.TestUsername + "@"
  493. }
  494. }
  495. openString += oci8.TestHostValid
  496. // A normal simple Open to localhost would look like:
  497. // db, err := sql.Open("oci8", "127.0.0.1")
  498. // For testing, need to use additional variables
  499. db, err := sql.Open("oci8", openString)
  500. if err != nil {
  501. fmt.Printf("Open error is not nil: %v", err)
  502. return
  503. }
  504. if db == nil {
  505. fmt.Println("db is nil")
  506. return
  507. }
  508. // defer close database
  509. defer func() {
  510. err = db.Close()
  511. if err != nil {
  512. fmt.Println("Close error is not nil:", err)
  513. }
  514. }()
  515. // create table
  516. tableName := "E_BLOB_" + oci8.TestTimeString
  517. query := "create table " + tableName + " ( A BLOB )"
  518. ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second)
  519. _, err = db.ExecContext(ctx, query)
  520. cancel()
  521. if err != nil {
  522. fmt.Println("ExecContext error is not nil:", err)
  523. return
  524. }
  525. // insert row
  526. query = "insert into " + tableName + " ( A ) values (:1)"
  527. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  528. _, err = db.ExecContext(ctx, query, bytes.Repeat([]byte("abcdefghijklmnopqrstuvwxyz"), 200))
  529. cancel()
  530. if err != nil {
  531. fmt.Println("ExecContext error is not nil:", err)
  532. return
  533. }
  534. var rows *sql.Rows
  535. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  536. defer cancel()
  537. rows, err = db.QueryContext(ctx, "select A from "+tableName)
  538. if err != nil {
  539. fmt.Println("QueryContext error is not nil:", err)
  540. return
  541. }
  542. // defer close rows
  543. defer func() {
  544. err = rows.Close()
  545. if err != nil {
  546. fmt.Println("Close error is not nil:", err)
  547. }
  548. }()
  549. if !rows.Next() {
  550. fmt.Println("no Next rows")
  551. return
  552. }
  553. var theBytes []byte
  554. err = rows.Scan(&theBytes)
  555. if err != nil {
  556. fmt.Println("Scan error is not nil:", err)
  557. return
  558. }
  559. if len(theBytes) != 5200 {
  560. fmt.Println("len theBytes != 5200")
  561. return
  562. }
  563. if rows.Next() {
  564. fmt.Println("has Next rows")
  565. return
  566. }
  567. err = rows.Err()
  568. if err != nil {
  569. fmt.Println("Err error is not nil:", err)
  570. return
  571. }
  572. // drop table
  573. query = "drop table " + tableName
  574. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  575. _, err = db.ExecContext(ctx, query)
  576. cancel()
  577. if err != nil {
  578. fmt.Println("ExecContext error is not nil:", err)
  579. return
  580. }
  581. fmt.Println("done")
  582. // output: done
  583. }
  584. func Example_sqlRowid() {
  585. // Example shows a few ways to get rowid
  586. // For testing, check if database tests are disabled
  587. if oci8.TestDisableDatabase || oci8.TestDisableDestructive {
  588. fmt.Println("done")
  589. return
  590. }
  591. oci8.Driver.Logger = log.New(os.Stderr, "oci8 ", log.Ldate|log.Ltime|log.LUTC|log.Lshortfile)
  592. var openString string
  593. // [username/[password]@]host[:port][/service_name][?param1=value1&...&paramN=valueN]
  594. if len(oci8.TestUsername) > 0 {
  595. if len(oci8.TestPassword) > 0 {
  596. openString = oci8.TestUsername + "/" + oci8.TestPassword + "@"
  597. } else {
  598. openString = oci8.TestUsername + "@"
  599. }
  600. }
  601. openString += oci8.TestHostValid
  602. // A normal simple Open to localhost would look like:
  603. // db, err := sql.Open("oci8", "127.0.0.1")
  604. // For testing, need to use additional variables
  605. db, err := sql.Open("oci8", openString)
  606. if err != nil {
  607. fmt.Printf("Open error is not nil: %v", err)
  608. return
  609. }
  610. if db == nil {
  611. fmt.Println("db is nil")
  612. return
  613. }
  614. // defer close database
  615. defer func() {
  616. err = db.Close()
  617. if err != nil {
  618. fmt.Println("Close error is not nil:", err)
  619. }
  620. }()
  621. // create table
  622. tableName := "E_ROWID_" + oci8.TestTimeString
  623. query := "create table " + tableName + " ( A INTEGER )"
  624. ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second)
  625. _, err = db.ExecContext(ctx, query)
  626. cancel()
  627. if err != nil {
  628. fmt.Println("ExecContext error is not nil:", err)
  629. return
  630. }
  631. // insert row and get rowid from returning
  632. var rowid1 string // rowid will be put into here
  633. var result sql.Result
  634. query = "insert into " + tableName + " ( A ) values (:1) returning rowid into :rowid1"
  635. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  636. result, err = db.ExecContext(ctx, query, 1, sql.Named("rowid1", sql.Out{Dest: &rowid1}))
  637. cancel()
  638. if err != nil {
  639. fmt.Println("ExecContext error is not nil:", err)
  640. return
  641. }
  642. // get rowid from LastInsertId
  643. var rowid2 string // rowid will be put into here
  644. var id int64
  645. id, err = result.LastInsertId()
  646. if err != nil {
  647. fmt.Println("LastInsertId error is not nil:", err)
  648. return
  649. }
  650. rowid2 = oci8.GetLastInsertId(id)
  651. // select rowid
  652. var rowid3 string // rowid will be put into here
  653. var rows *sql.Rows
  654. query = "select rowid from " + tableName
  655. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  656. defer cancel()
  657. rows, err = db.QueryContext(ctx, query)
  658. if err != nil {
  659. fmt.Println("QueryContext error is not nil:", err)
  660. return
  661. }
  662. // defer close rows
  663. defer func() {
  664. err = rows.Close()
  665. if err != nil {
  666. fmt.Println("Close error is not nil:", err)
  667. }
  668. }()
  669. if !rows.Next() {
  670. fmt.Println("no Next rows")
  671. return
  672. }
  673. err = rows.Scan(&rowid3)
  674. if err != nil {
  675. fmt.Println("Scan error is not nil:", err)
  676. return
  677. }
  678. if rows.Next() {
  679. fmt.Println("has Next rows")
  680. return
  681. }
  682. err = rows.Err()
  683. if err != nil {
  684. fmt.Println("Err error is not nil:", err)
  685. return
  686. }
  687. // drop table
  688. query = "drop table " + tableName
  689. ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
  690. _, err = db.ExecContext(ctx, query)
  691. cancel()
  692. if err != nil {
  693. fmt.Println("ExecContext error is not nil:", err)
  694. return
  695. }
  696. if len(rowid1) != len(rowid2) || len(rowid2) != len(rowid3) {
  697. fmt.Println("rowid len is not equal", rowid1, rowid2, rowid3)
  698. return
  699. }
  700. fmt.Println("done")
  701. // output: done
  702. }