blob.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // Copyright (c) 2018 David Crawshaw <david@zentus.com>
  2. //
  3. // Permission to use, copy, modify, and distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. package sqlite
  15. // #include <blocking_step.h>
  16. // #include <sqlite3.h>
  17. // #include <stdlib.h>
  18. // #include <stdint.h>
  19. import "C"
  20. import (
  21. "errors"
  22. "fmt"
  23. "io"
  24. "unsafe"
  25. )
  26. var cmain = C.CString("main")
  27. var ctemp = C.CString("temp")
  28. // OpenBlob opens a blob in a particular {database,table,column,row}.
  29. //
  30. // https://www.sqlite.org/c3ref/blob_open.html
  31. func (conn *Conn) OpenBlob(dbn, table, column string, row int64, write bool) (*Blob, error) {
  32. var cdb *C.char
  33. switch dbn {
  34. case "", "main":
  35. cdb = cmain
  36. case "temp":
  37. cdb = ctemp
  38. default:
  39. cdb = C.CString(dbn)
  40. defer C.free(unsafe.Pointer(cdb))
  41. }
  42. var flags C.int
  43. if write {
  44. flags = 1
  45. }
  46. ctable := C.CString(table)
  47. ccolumn := C.CString(column)
  48. defer func() {
  49. C.free(unsafe.Pointer(ctable))
  50. C.free(unsafe.Pointer(ccolumn))
  51. }()
  52. blob := &Blob{conn: conn}
  53. for {
  54. conn.count++
  55. if err := conn.interrupted("Conn.OpenBlob", ""); err != nil {
  56. return nil, err
  57. }
  58. switch res := C.sqlite3_blob_open(conn.conn, cdb, ctable, ccolumn,
  59. C.sqlite3_int64(row), flags, &blob.blob); res {
  60. case C.SQLITE_LOCKED_SHAREDCACHE:
  61. if res := C.wait_for_unlock_notify(
  62. conn.conn, conn.unlockNote); res != C.SQLITE_OK {
  63. return nil, conn.reserr("Conn.OpenBlob(Wait)", "", res)
  64. }
  65. // loop
  66. case C.SQLITE_OK:
  67. blob.size = int64(C.sqlite3_blob_bytes(blob.blob))
  68. return blob, nil
  69. default:
  70. return nil, conn.extreserr("Conn.OpenBlob", "", res)
  71. }
  72. }
  73. }
  74. // Blob provides streaming access to SQLite blobs.
  75. type Blob struct {
  76. conn *Conn
  77. blob *C.sqlite3_blob
  78. size int64
  79. }
  80. func (blob *Blob) Reopen(rowid int64) (err error) {
  81. rc := C.sqlite3_blob_reopen(blob.blob, C.sqlite3_int64(rowid))
  82. err = blob.conn.reserr("Blob.Reopen", "", rc)
  83. if err != nil {
  84. return
  85. }
  86. blob.setSize()
  87. return
  88. }
  89. func (blob *Blob) setSize() {
  90. blob.size = int64(C.sqlite3_blob_bytes(blob.blob))
  91. }
  92. // https://www.sqlite.org/c3ref/blob_read.html
  93. func (blob *Blob) ReadAt(p []byte, off int64) (n int, err error) {
  94. if blob.blob == nil {
  95. return 0, ErrBlobClosed
  96. }
  97. if off < 0 {
  98. err = fmt.Errorf("bad offset %v", off)
  99. return
  100. }
  101. if off >= blob.size {
  102. err = io.EOF
  103. return
  104. }
  105. if err := blob.conn.interrupted("Blob.ReadAt", ""); err != nil {
  106. return 0, err
  107. }
  108. if int64(len(p)) > blob.size-off {
  109. p = p[:blob.size-off]
  110. }
  111. lenp := C.int(len(p))
  112. res := C.sqlite3_blob_read(blob.blob, unsafe.Pointer(&p[0]), lenp, C.int(off))
  113. if err := blob.conn.reserr("Blob.ReadAt", "", res); err != nil {
  114. return 0, err
  115. }
  116. n = len(p)
  117. if off+int64(len(p)) >= blob.size {
  118. err = io.EOF
  119. }
  120. return
  121. }
  122. // https://www.sqlite.org/c3ref/blob_write.html
  123. func (blob *Blob) WriteAt(p []byte, off int64) (n int, err error) {
  124. if blob.blob == nil {
  125. return 0, ErrBlobClosed
  126. }
  127. if err := blob.conn.interrupted("Blob.WriteAt", ""); err != nil {
  128. return 0, err
  129. }
  130. lenp := C.int(len(p))
  131. res := C.sqlite3_blob_write(blob.blob, unsafe.Pointer(&p[0]), lenp, C.int(off))
  132. if err := blob.conn.reserr("Blob.WriteAt", "", res); err != nil {
  133. return 0, err
  134. }
  135. return len(p), nil
  136. }
  137. // Size returns the total size of a blob.
  138. func (blob *Blob) Size() int64 {
  139. return blob.size
  140. }
  141. // https://www.sqlite.org/c3ref/blob_close.html
  142. func (blob *Blob) Close() error {
  143. if blob.blob == nil {
  144. return ErrBlobClosed
  145. }
  146. err := blob.conn.reserr("Blob.Close", "", C.sqlite3_blob_close(blob.blob))
  147. blob.blob = nil
  148. return err
  149. }
  150. var ErrBlobClosed = errors.New("blob closed")