table_test.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. package storage
  2. import (
  3. "fmt"
  4. "testing"
  5. "time"
  6. )
  7. // TestSelectWithLimitStopsBeforeAllPages verifies that a limited scan stops
  8. // consuming pages as soon as the limit is satisfied instead of reading the
  9. // whole table.
  10. func TestSelectWithLimitStopsBeforeAllPages(t *testing.T) {
  11. kv := newTestKVServer(t)
  12. defer kv.close()
  13. pool := newTestKVPool(kv, 4, 5*time.Second)
  14. defer pool.Close()
  15. schemas := NewSchemaManager(pool, "testdb")
  16. tables := NewTableManager(pool, schemas, "testdb")
  17. if err := schemas.CreateTable(&Schema{
  18. Name: "t",
  19. Columns: []Column{
  20. {Name: "id", Type: "INTEGER", Nullable: false, PrimaryKey: true},
  21. {Name: "name", Type: "TEXT", Nullable: true},
  22. },
  23. }); err != nil {
  24. t.Fatalf("create table: %v", err)
  25. }
  26. const n = 20
  27. for i := int64(1); i <= n; i++ {
  28. if err := tables.Insert("t", Row{"id": i, "name": fmt.Sprintf("n%d", i)}); err != nil {
  29. t.Fatalf("insert %d: %v", i, err)
  30. }
  31. }
  32. // Force small pages so early termination is observable.
  33. kv.maxScanPage = 2
  34. _, nextsBefore, _ := kv.scanStats()
  35. rows, err := tables.SelectWithLimit("t", nil, 3, 0)
  36. if err != nil {
  37. t.Fatalf("SelectWithLimit: %v", err)
  38. }
  39. _, nextsAfter, _ := kv.scanStats()
  40. if len(rows) != 3 {
  41. t.Fatalf("SelectWithLimit returned %d rows, want 3", len(rows))
  42. }
  43. fullPages := (n + 1) / 2 // ceil(n / pageSize)
  44. if got := nextsAfter - nextsBefore; got >= fullPages {
  45. t.Fatalf("SelectWithLimit consumed %d pages, want < %d (should stop early)", got, fullPages)
  46. }
  47. }
  48. // TestDropTableDeletesDurableRows verifies that a direct DropTable removes all
  49. // durable row keys, not just the schema entry.
  50. func TestDropTableDeletesDurableRows(t *testing.T) {
  51. kv := newTestKVServer(t)
  52. defer kv.close()
  53. pool := newTestKVPool(kv, 4, 5*time.Second)
  54. defer pool.Close()
  55. schemas := NewSchemaManager(pool, "testdb")
  56. tables := NewTableManager(pool, schemas, "testdb")
  57. if err := schemas.CreateTable(&Schema{
  58. Name: "t",
  59. Columns: []Column{
  60. {Name: "id", Type: "INTEGER", Nullable: false, PrimaryKey: true},
  61. },
  62. }); err != nil {
  63. t.Fatalf("create table: %v", err)
  64. }
  65. for i := int64(1); i <= 5; i++ {
  66. if err := tables.Insert("t", Row{"id": i}); err != nil {
  67. t.Fatalf("insert %d: %v", i, err)
  68. }
  69. }
  70. if got := kv.countKeys("testdb:_data:t:"); got != 5 {
  71. t.Fatalf("expected 5 durable rows before drop, got %d", got)
  72. }
  73. if err := schemas.DropTable("t"); err != nil {
  74. t.Fatalf("DropTable: %v", err)
  75. }
  76. if got := kv.countKeys("testdb:_data:t:"); got != 0 {
  77. t.Fatalf("expected 0 durable rows after drop, got %d", got)
  78. }
  79. if kv.hasKey("testdb:_schema:t") {
  80. t.Fatalf("schema key still present after drop")
  81. }
  82. if kv.hasKey("testdb:_sys:rowid:t") {
  83. t.Fatalf("rowid counter key still present after drop")
  84. }
  85. }
  86. // TestScanKeysReturnsKeysOnly verifies that the key-only scan constructor
  87. // returns keys with empty values.
  88. func TestScanKeysReturnsKeysOnly(t *testing.T) {
  89. kv := newTestKVServer(t)
  90. defer kv.close()
  91. c := kv.client()
  92. defer c.Close()
  93. if _, err := c.Put([]byte("p:a"), []byte("value-a")); err != nil {
  94. t.Fatalf("put a: %v", err)
  95. }
  96. if _, err := c.Put([]byte("p:b"), []byte("value-b")); err != nil {
  97. t.Fatalf("put b: %v", err)
  98. }
  99. scan, err := c.ScanKeys([]byte("p:"))
  100. if err != nil {
  101. t.Fatalf("ScanKeys: %v", err)
  102. }
  103. defer scan.Close()
  104. entries, done, err := scan.Next()
  105. if err != nil {
  106. t.Fatalf("next: %v", err)
  107. }
  108. if !done || len(entries) != 2 {
  109. t.Fatalf("done=%v len=%d, want done and 2 entries", done, len(entries))
  110. }
  111. for _, e := range entries {
  112. if len(e.Key) == 0 {
  113. t.Fatalf("expected non-empty key")
  114. }
  115. if len(e.Value) != 0 {
  116. t.Fatalf("key-only scan returned a value %q for key %q", e.Value, e.Key)
  117. }
  118. }
  119. }
  120. // TestCountFastFirstDerivationUsesKeyOnlyScan verifies that the first-time
  121. // COUNT(*) derivation issues a key-only scan rather than pulling row values.
  122. func TestCountFastFirstDerivationUsesKeyOnlyScan(t *testing.T) {
  123. kv := newTestKVServer(t)
  124. defer kv.close()
  125. pool := newTestKVPool(kv, 4, 5*time.Second)
  126. defer pool.Close()
  127. schemas := NewSchemaManager(pool, "testdb")
  128. tables := NewTableManager(pool, schemas, "testdb")
  129. if err := schemas.CreateTable(&Schema{
  130. Name: "t",
  131. Columns: []Column{
  132. {Name: "id", Type: "INTEGER", Nullable: false, PrimaryKey: true},
  133. },
  134. }); err != nil {
  135. t.Fatalf("create table: %v", err)
  136. }
  137. for i := int64(1); i <= 5; i++ {
  138. if err := tables.Insert("t", Row{"id": i}); err != nil {
  139. t.Fatalf("insert %d: %v", i, err)
  140. }
  141. }
  142. before := kv.keyOnlyOpenCount()
  143. got, err := tables.CountFast("t")
  144. if err != nil {
  145. t.Fatalf("CountFast: %v", err)
  146. }
  147. after := kv.keyOnlyOpenCount()
  148. if got != 5 {
  149. t.Fatalf("CountFast = %d, want 5", got)
  150. }
  151. if after-before != 1 {
  152. t.Fatalf("expected CountFast first derivation to use one key-only scan, got %d", after-before)
  153. }
  154. }
  155. func TestInsertBulkStringPrimaryKeyCount(t *testing.T) {
  156. kv := newTestKVServer(t)
  157. defer kv.close()
  158. pool := newTestKVPool(kv, 4, 5*time.Second)
  159. defer pool.Close()
  160. schemas := NewSchemaManager(pool, "testdb")
  161. tables := NewTableManager(pool, schemas, "testdb")
  162. if err := schemas.CreateTable(&Schema{
  163. Name: "labels",
  164. Columns: []Column{
  165. {Name: "id", Type: "TEXT", Nullable: false, PrimaryKey: true},
  166. {Name: "value", Type: "BLOB", Nullable: true},
  167. },
  168. }); err != nil {
  169. t.Fatalf("create table: %v", err)
  170. }
  171. n, err := tables.InsertBulk("labels", []Row{
  172. {"id": "a", "value": []byte{0, 1, 2}},
  173. {"id": "b", "value": []byte{'|', '\r', '\n'}},
  174. })
  175. if err != nil {
  176. t.Fatalf("InsertBulk: %v", err)
  177. }
  178. if n != 2 {
  179. t.Fatalf("InsertBulk count = %d, want 2", n)
  180. }
  181. rows, err := tables.Select("labels", nil)
  182. if err != nil {
  183. t.Fatalf("Select: %v", err)
  184. }
  185. if len(rows) != 2 {
  186. t.Fatalf("Select returned %d rows, want 2", len(rows))
  187. }
  188. if n, err := tables.InsertBulk("labels", []Row{{"id": "a", "value": "duplicate"}}); err == nil || n != 0 {
  189. t.Fatalf("existing duplicate: n=%d err=%v", n, err)
  190. }
  191. if n, err := tables.InsertBulk("labels", []Row{{"id": "c"}, {"id": "c"}}); err == nil || n != 0 {
  192. t.Fatalf("batch duplicate: n=%d err=%v", n, err)
  193. }
  194. if _, err := tables.GetByPK("labels", "c"); err == nil {
  195. t.Fatal("duplicate batch persisted a row")
  196. }
  197. }
  198. func TestSelectByIndexUsesPointReadsAfterBuild(t *testing.T) {
  199. kv := newTestKVServer(t)
  200. defer kv.close()
  201. pool := newTestKVPool(kv, 4, 5*time.Second)
  202. defer pool.Close()
  203. schemas := NewSchemaManager(pool, "testdb")
  204. tables := NewTableManager(pool, schemas, "testdb")
  205. if err := schemas.CreateTable(&Schema{
  206. Name: "items",
  207. Columns: []Column{
  208. {Name: "id", Type: "INTEGER", PrimaryKey: true},
  209. {Name: "kind", Type: "TEXT"},
  210. },
  211. }); err != nil {
  212. t.Fatal(err)
  213. }
  214. if err := schemas.CreateIndex(&Index{Name: "idx_kind", Table: "items", Columns: []IndexColumn{{Name: "kind"}}}); err != nil {
  215. t.Fatal(err)
  216. }
  217. for i := int64(1); i <= 20; i++ {
  218. if err := tables.Insert("items", Row{"id": i, "kind": fmt.Sprintf("k%d", i%2)}); err != nil {
  219. t.Fatal(err)
  220. }
  221. }
  222. if rows, err := tables.SelectByIndex("items", "idx_kind", "k1"); err != nil || len(rows) != 10 {
  223. t.Fatalf("initial indexed select: len=%d err=%v", len(rows), err)
  224. }
  225. opensBefore, _, _ := kv.scanStats()
  226. _, multiGetsBefore := kv.readStats()
  227. if rows, err := tables.SelectByIndex("items", "idx_kind", "k1"); err != nil || len(rows) != 10 {
  228. t.Fatalf("cached indexed select: len=%d err=%v", len(rows), err)
  229. }
  230. opensAfter, _, _ := kv.scanStats()
  231. _, multiGetsAfter := kv.readStats()
  232. if opensAfter != opensBefore {
  233. t.Fatalf("indexed select opened %d table scans after index build", opensAfter-opensBefore)
  234. }
  235. if multiGetsAfter-multiGetsBefore != 1 {
  236. t.Fatalf("indexed select issued %d multi-get requests, want 1", multiGetsAfter-multiGetsBefore)
  237. }
  238. }
  239. func TestPrimaryKeyMutationsDoNotScan(t *testing.T) {
  240. kv := newTestKVServer(t)
  241. defer kv.close()
  242. pool := newTestKVPool(kv, 4, 5*time.Second)
  243. defer pool.Close()
  244. schemas := NewSchemaManager(pool, "testdb")
  245. tables := NewTableManager(pool, schemas, "testdb")
  246. if err := schemas.CreateTable(&Schema{
  247. Name: "items",
  248. Columns: []Column{
  249. {Name: "id", Type: "TEXT", PrimaryKey: true},
  250. {Name: "value", Type: "INTEGER"},
  251. },
  252. }); err != nil {
  253. t.Fatal(err)
  254. }
  255. for i := 0; i < 20; i++ {
  256. if err := tables.Insert("items", Row{"id": fmt.Sprintf("item-%d", i), "value": int64(i)}); err != nil {
  257. t.Fatal(err)
  258. }
  259. }
  260. opensBefore, _, _ := kv.scanStats()
  261. oldRow, updated, err := tables.UpdateByPK("items", "item-10", func(Row) (Row, error) {
  262. return Row{"value": int64(99)}, nil
  263. })
  264. if err != nil || !updated || oldRow["value"] != int64(10) {
  265. t.Fatalf("point update: updated=%v old=%v err=%v", updated, oldRow, err)
  266. }
  267. deletedRow, deleted, err := tables.DeleteByPK("items", "item-11")
  268. if err != nil || !deleted || deletedRow["value"] != int64(11) {
  269. t.Fatalf("point delete: deleted=%v old=%v err=%v", deleted, deletedRow, err)
  270. }
  271. opensAfter, _, _ := kv.scanStats()
  272. if opensAfter != opensBefore {
  273. t.Fatalf("primary-key mutations opened %d scans", opensAfter-opensBefore)
  274. }
  275. row, err := tables.GetByPK("items", "item-10")
  276. if err != nil || row["value"] != int64(99) {
  277. t.Fatalf("updated row=%v err=%v", row, err)
  278. }
  279. if _, err := tables.GetByPK("items", "item-11"); err != ErrKeyNotFound {
  280. t.Fatalf("deleted row error=%v, want ErrKeyNotFound", err)
  281. }
  282. }
  283. func TestCountFastResetsAfterDirectDropAndRecreate(t *testing.T) {
  284. kv := newTestKVServer(t)
  285. defer kv.close()
  286. pool := newTestKVPool(kv, 2, 5*time.Second)
  287. defer pool.Close()
  288. schemas := NewSchemaManager(pool, "testdb")
  289. tables := NewTableManager(pool, schemas, "testdb")
  290. create := func() {
  291. if err := schemas.CreateTable(&Schema{Name: "events", Columns: []Column{{Name: "id", Type: "INTEGER", PrimaryKey: true}}}); err != nil {
  292. t.Fatal(err)
  293. }
  294. }
  295. create()
  296. if err := tables.Insert("events", Row{"id": int64(1)}); err != nil {
  297. t.Fatal(err)
  298. }
  299. if count, err := tables.CountFast("events"); err != nil || count != 1 {
  300. t.Fatalf("initial count=%d err=%v", count, err)
  301. }
  302. if err := schemas.DropTable("events"); err != nil {
  303. t.Fatal(err)
  304. }
  305. create()
  306. if count, err := tables.CountFast("events"); err != nil || count != 0 {
  307. t.Fatalf("recreated count=%d err=%v", count, err)
  308. }
  309. }