Skip to content

Commit 84da938

Browse files
committed
implementing Row level TableScan Test
1 parent e7aa877 commit 84da938

File tree

5 files changed

+216
-26
lines changed

5 files changed

+216
-26
lines changed

src/main/kotlin/simpledb/record/rowlock/RecordPage.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,32 @@ class RecordPage(private val tx: TransactionImpl, private val blk: BlockId, priv
2020
return tx.getString(blk, fldPos)
2121
}
2222

23-
fun setInt(slot: Int, fldName: String, value: Int) {
23+
fun setInt(slot: Int, fldName: String, value: Int): Boolean {
2424
tx.wLatchPage(blk)
2525
try {
26-
tx.lockExclusive(blk, slot)
26+
if (!tx.lockExclusive(blk, slot)) {
27+
return false
28+
}
2729
val fldPos = offset(slot) + layout.offset(fldName)
2830
tx.setInt(blk, fldPos, value, true)
2931
} finally {
3032
tx.wUnlatchPage(blk)
3133
}
34+
return true
3235
}
3336

34-
fun setString(slot: Int, fldName: String, value: String) {
37+
fun setString(slot: Int, fldName: String, value: String): Boolean {
3538
tx.wLatchPage(blk)
3639
try {
37-
tx.lockExclusive(blk, slot)
40+
if (!tx.lockExclusive(blk, slot)) {
41+
return false
42+
}
3843
val fldPos = offset(slot) + layout.offset(fldName)
3944
tx.setString(blk, fldPos, value, true)
4045
} finally {
4146
tx.wUnlatchPage(blk)
4247
}
48+
return true
4349
}
4450

4551
fun delete(slot: Int) {

src/main/kotlin/simpledb/record/rowlock/TableScan.kt

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package simpledb.record.rowlock
22

33
import simpledb.file.BlockId
44
import simpledb.query.Constant
5-
import simpledb.query.UpdateScan
65
import simpledb.record.Layout
76
import simpledb.record.RID
87
import simpledb.tx.rowlock.TransactionImpl
@@ -12,7 +11,7 @@ class TableScan(
1211
private val tx: TransactionImpl,
1312
tableName: String,
1413
private val layout: Layout
15-
) : UpdateScan {
14+
) {
1615
private var rp: RecordPage? = null
1716
private val filename: String
1817
private var currentSlot = 0
@@ -26,13 +25,13 @@ class TableScan(
2625
}
2726
}
2827

29-
override fun close() {
28+
fun close() {
3029
if (rp != null) {
3130
tx.unpin(rp!!.block())
3231
}
3332
}
3433

35-
override fun getVal(fieldName: String): Constant {
34+
fun getVal(fieldName: String): Constant {
3635
return if (layout.schema()
3736
.type(fieldName) == Types.INTEGER
3837
) {
@@ -42,11 +41,11 @@ class TableScan(
4241
}
4342
}
4443

45-
override fun beforeFirst() {
44+
fun beforeFirst() {
4645
moveToBlock(0)
4746
}
4847

49-
override fun next(): Boolean {
48+
fun next(): Boolean {
5049
currentSlot = rp!!.nextAfter(currentSlot)
5150
while (currentSlot < 0) {
5251
if (atLastBlock()) {
@@ -58,19 +57,19 @@ class TableScan(
5857
return true
5958
}
6059

61-
override fun getInt(fldname: String): Int {
60+
fun getInt(fldname: String): Int {
6261
return rp!!.getInt(currentSlot, fldname)
6362
}
6463

65-
override fun getString(fldname: String): String {
64+
fun getString(fldname: String): String {
6665
return rp!!.getString(currentSlot, fldname)
6766
}
6867

69-
override fun hasField(fldname: String): Boolean {
68+
fun hasField(fldname: String): Boolean {
7069
return layout.schema().hasField(fldname)
7170
}
7271

73-
override fun setVal(fieldName: String, value: Constant) {
72+
fun setVal(fieldName: String, value: Constant) {
7473
if (layout.schema().type(fieldName) == Types.INTEGER) {
7574
setInt(fieldName, value.asInt())
7675
} else {
@@ -81,15 +80,13 @@ class TableScan(
8180
}
8281
}
8382

84-
override fun setInt(fldname: String, value: Int) {
83+
fun setInt(fldname: String, value: Int): Boolean =
8584
rp!!.setInt(currentSlot, fldname, value)
86-
}
8785

88-
override fun setString(fldname: String, value: String) {
86+
fun setString(fldname: String, value: String): Boolean =
8987
rp!!.setString(currentSlot, fldname, value)
90-
}
9188

92-
override fun insert() {
89+
fun insert() {
9390
currentSlot = rp!!.insertAfter(currentSlot)
9491
while (currentSlot < 0) {
9592
if (atLastBlock()) {
@@ -101,18 +98,18 @@ class TableScan(
10198
}
10299
}
103100

104-
override fun delete() {
101+
fun delete() {
105102
rp!!.delete(currentSlot)
106103
}
107104

108-
override fun moveToRid(rid: RID) {
105+
fun moveToRid(rid: RID) {
109106
close()
110107
val blk = BlockId(filename, rid.blockNumber())
111108
rp = RecordPage(tx, blk, layout)
112109
currentSlot = rid.slot()
113110
}
114111

115-
override fun getRid(): RID {
112+
fun getRid(): RID {
116113
return RID(rp!!.block().number(), currentSlot)
117114
}
118115

src/main/kotlin/simpledb/tx/concurrency/rowlock/ConcurrencyMgr.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@ class ConcurrencyMgr {
4040
}
4141
}
4242

43-
fun lockExclusive(tx: TransactionImpl, rid: RID) {
43+
fun lockExclusive(tx: TransactionImpl, rid: RID): Boolean =
4444
lockTblRowLock.lockExclusive(tx, rid)
45-
}
4645

4746
fun lockShared(tx: TransactionImpl, rid: RID) {
4847
lockTblRowLock.lockShared(tx, rid)

src/main/kotlin/simpledb/tx/rowlock/TransactionImpl.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,8 @@ class TransactionImpl(private val fm: FileMgr, lm: LogMgr?, private val bm: Buff
116116
concurMgr.lockShared(this, getRid(blk, slot))
117117
}
118118

119-
fun lockExclusive(blk: BlockId, slot: Int) {
119+
fun lockExclusive(blk: BlockId, slot: Int): Boolean =
120120
concurMgr.lockExclusive(this, getRid(blk, slot))
121-
}
122121

123122
fun rLatchPage(blk: BlockId) {
124123
concurMgr.rLatchPage(blk)
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package simpledb.record.rowlock
2+
3+
import org.hamcrest.MatcherAssert.assertThat
4+
import org.hamcrest.Matchers.`is`
5+
import org.junit.jupiter.api.BeforeEach
6+
import org.junit.jupiter.api.Nested
7+
import org.junit.jupiter.api.Test
8+
import simpledb.record.Layout
9+
import simpledb.record.Schema
10+
import simpledb.server.SimpleDB
11+
import simpledb.tx.rowlock.TransactionImpl
12+
import java.io.File
13+
import kotlin.concurrent.thread
14+
import kotlin.math.roundToInt
15+
16+
class TableScanTest {
17+
// layout definition (A int, B varchar(9))
18+
private val layout = Layout(Schema().apply {
19+
this.addIntField("A")
20+
this.addStringField("B", 9)
21+
})
22+
23+
@BeforeEach
24+
fun before() {
25+
File("dbdir", "tableScanTest").deleteRecursively()
26+
}
27+
28+
@Test
29+
fun insertAndNextAll() {
30+
val db = SimpleDB("dbdir/tableScanTest", 400, 8)
31+
val tx = TransactionImpl(db.fileMgr(), db.logMgr(), db.bufferMgr())
32+
33+
val ts = TableScan(tx, "T", layout)
34+
35+
// insert operation
36+
val inserted = mutableListOf<Int>()
37+
ts.beforeFirst()
38+
for (i in 0..49) {
39+
ts.insert()
40+
val n = (Math.random() * 50).roundToInt()
41+
inserted.add(n)
42+
ts.setInt("A", n)
43+
ts.setString("B", "rec$n")
44+
}
45+
46+
// read operation
47+
ts.beforeFirst()
48+
var idx = 0
49+
while (ts.next()) {
50+
val n = inserted[idx++]
51+
val a = ts.getInt("A")
52+
val b = ts.getString("B")
53+
assertThat(a, `is`(n))
54+
assertThat(b, `is`("rec$n"))
55+
}
56+
57+
ts.close()
58+
tx.rollback()
59+
}
60+
61+
@Nested
62+
inner class Concurrent1 {
63+
private val aValues = listOf(1, 5, 10)
64+
private lateinit var db: SimpleDB
65+
66+
@BeforeEach
67+
fun before() {
68+
db = SimpleDB("dbdir/tableScanTest", 400, 8)
69+
val tx = TransactionImpl(db.fileMgr(), db.logMgr(), db.bufferMgr())
70+
71+
val ts = TableScan(tx, "T", layout)
72+
73+
// insert operation
74+
ts.beforeFirst()
75+
for (aValue in aValues) {
76+
ts.insert()
77+
ts.setInt("A", aValue)
78+
ts.setString("B", "rec$aValue")
79+
}
80+
81+
ts.close()
82+
tx.commit()
83+
}
84+
85+
@Test
86+
fun nextSucceedWhenSLockAcquiredByOtherTx() {
87+
val otherTx = TransactionImpl(db.fileMgr(), db.logMgr(), db.bufferMgr())
88+
val tsByOtherTx = TableScan(otherTx, "T", layout)
89+
tsByOtherTx.beforeFirst()
90+
91+
// read and lock shared all records
92+
var idx = 0
93+
while (tsByOtherTx.next()) {
94+
val a = tsByOtherTx.getInt("A")
95+
val aValue = aValues[idx++]
96+
assertThat(a, `is`(aValue))
97+
}
98+
99+
val tx = TransactionImpl(db.fileMgr(), db.logMgr(), db.bufferMgr())
100+
val ts = TableScan(tx, "T", layout)
101+
ts.beforeFirst()
102+
val t = thread {
103+
var idx = 0
104+
while (ts.next()) {
105+
val aValue = aValues[idx++]
106+
val a = ts.getInt("A")
107+
assertThat(a, `is`(aValue))
108+
}
109+
}
110+
t.join(10)
111+
assertThat(t.isAlive, `is`(false))
112+
113+
tsByOtherTx.close()
114+
otherTx.rollback()
115+
116+
ts.close()
117+
tx.rollback()
118+
}
119+
120+
@Test
121+
fun setStringFailWhenSLockAcquiredByOtherTx() {
122+
val otherTx = TransactionImpl(db.fileMgr(), db.logMgr(), db.bufferMgr())
123+
val tsByOtherTx = TableScan(otherTx, "T", layout)
124+
tsByOtherTx.beforeFirst()
125+
126+
// read and lock shared all records
127+
var idx = 0
128+
while (tsByOtherTx.next()) {
129+
val a = tsByOtherTx.getInt("A")
130+
val aValue = aValues[idx++]
131+
assertThat(a, `is`(aValue))
132+
}
133+
134+
val tx = TransactionImpl(db.fileMgr(), db.logMgr(), db.bufferMgr())
135+
val ts = TableScan(tx, "T", layout)
136+
ts.beforeFirst()
137+
while (ts.next()) {
138+
assertThat(ts.setString("B", "new b value"), `is`(false))
139+
}
140+
141+
tsByOtherTx.close()
142+
otherTx.rollback()
143+
144+
ts.close()
145+
tx.rollback()
146+
}
147+
148+
@Test
149+
fun setStringSucceedWhenSLockAcquiredAndReleaseByOtherTx() {
150+
val otherTx = TransactionImpl(db.fileMgr(), db.logMgr(), db.bufferMgr())
151+
val tsByOtherTx = TableScan(otherTx, "T", layout)
152+
tsByOtherTx.beforeFirst()
153+
154+
// read and lock shared all records
155+
var idx = 0
156+
while (tsByOtherTx.next()) {
157+
val a = tsByOtherTx.getInt("A")
158+
val aValue = aValues[idx++]
159+
assertThat(a, `is`(aValue))
160+
}
161+
162+
val tx = TransactionImpl(db.fileMgr(), db.logMgr(), db.bufferMgr())
163+
val ts = TableScan(tx, "T", layout)
164+
ts.beforeFirst()
165+
while (ts.next()) {
166+
assertThat(ts.setString("B", "new b value"), `is`(false))
167+
}
168+
169+
tsByOtherTx.close()
170+
otherTx.rollback()
171+
172+
ts.beforeFirst()
173+
while (ts.next()) {
174+
assertThat(ts.setString("B", "new b value"), `is`(true))
175+
}
176+
177+
ts.beforeFirst()
178+
idx = 0
179+
while (ts.next()) {
180+
assertThat(ts.getInt("A"), `is`(aValues[idx++]))
181+
assertThat(ts.getString("B"), `is`("new b value"))
182+
}
183+
184+
ts.close()
185+
tx.rollback()
186+
}
187+
}
188+
189+
}

0 commit comments

Comments
 (0)