Skip to content

Commit f2cce71

Browse files
committed
Add enumerated type support
1 parent 4a093fe commit f2cce71

19 files changed

Lines changed: 669 additions & 66 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
-
55
## 2.1.0 / 2025-11-xx
66

7-
### sqllin-processor
7+
### sqllin-dsl
88

99
* Support typealias of supported types(primitive types, String, ByteArray etc) in generated tables
10+
* Support enumerated types in DSL APIs, includes `=`, `!=`, `<`, `<=`, `>`, `>=` operators
11+
* Support `<`, `<=`, `>`, `>=`, `IN`, `BETWEEN...AND` operators for String
12+
* Support `=`, `!=`, `<`, `<=`, `>`, `>=`, `IN`, `BETWEEN...AND` operators for ByteArray
1013

1114
## 2.0.0 / 2025-10-23
1215

ROADMAP.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
* Support the key word REFERENCE
66
* Support JOIN sub-query
7-
* Support Enum type
87

98
## Medium Priority
109

sqllin-dsl-test/src/androidInstrumentedTest/kotlin/com/ctrip/sqllin/dsl/test/AndroidTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ class AndroidTest {
8585
@Test
8686
fun testStringOperators() = commonTest.testStringOperators()
8787

88+
@Test
89+
fun testEnumOperations() = commonTest.testEnumOperations()
90+
8891
@Before
8992
fun setUp() {
9093
val context = InstrumentationRegistry.getInstrumentation().targetContext

sqllin-dsl-test/src/commonMain/kotlin/com/ctrip/sqllin/dsl/test/Entities.kt

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,30 @@ typealias StudentId = Long
3232
typealias CourseId = Long
3333
typealias Code = Int
3434

35+
/**
36+
* Enum types for testing enum support
37+
*/
38+
39+
/**
40+
* User status enum for testing enum functionality
41+
*/
42+
enum class UserStatus {
43+
ACTIVE,
44+
INACTIVE,
45+
SUSPENDED,
46+
BANNED
47+
}
48+
49+
/**
50+
* Priority level enum for testing enum comparisons
51+
*/
52+
enum class Priority {
53+
LOW,
54+
MEDIUM,
55+
HIGH,
56+
CRITICAL
57+
}
58+
3559
/**
3660
* Book entity
3761
* @author Yuang Qiao
@@ -140,4 +164,30 @@ data class FileData(
140164
result = 31 * result + metadata.hashCode()
141165
return result
142166
}
143-
}
167+
}
168+
169+
/**
170+
* User entity with enum fields for testing enum support
171+
*/
172+
@DBRow("user_account")
173+
@Serializable
174+
data class UserAccount(
175+
@PrimaryKey(isAutoincrement = true) val id: Long?,
176+
val username: String,
177+
val email: String,
178+
val status: UserStatus,
179+
val priority: Priority,
180+
val notes: String?,
181+
)
182+
183+
/**
184+
* Task entity with nullable enum for testing nullable enum support
185+
*/
186+
@DBRow("task")
187+
@Serializable
188+
data class Task(
189+
@PrimaryKey(isAutoincrement = true) val id: Long?,
190+
val title: String,
191+
val priority: Priority?,
192+
val description: String,
193+
)

sqllin-dsl-test/src/commonTest/kotlin/com/ctrip/sqllin/dsl/test/CommonBasicTest.kt

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,195 @@ class CommonBasicTest(private val path: DatabasePath) {
13241324
assertEquals(colBook1, resultsNEQ[0])
13251325
}
13261326

1327+
/**
1328+
* Comprehensive test for enum type support covering all operations:
1329+
* INSERT, SELECT, UPDATE, DELETE, equality/comparison operators,
1330+
* nullable enums, complex conditions, and ORDER BY
1331+
*/
1332+
fun testEnumOperations() = Database(getNewAPIDBConfig(), true).databaseAutoClose { database ->
1333+
// Section 1: Basic INSERT and SELECT
1334+
val user1 = UserAccount(null, "john_doe", "john@example.com", UserStatus.ACTIVE, Priority.HIGH, "VIP user")
1335+
val user2 = UserAccount(null, "jane_smith", "jane@example.com", UserStatus.INACTIVE, Priority.LOW, null)
1336+
database {
1337+
UserAccountTable { table ->
1338+
table INSERT listOf(user1, user2)
1339+
}
1340+
}
1341+
1342+
var selectAll: SelectStatement<UserAccount>? = null
1343+
database {
1344+
selectAll = UserAccountTable SELECT X
1345+
}
1346+
val allUsers = selectAll!!.getResults()
1347+
assertEquals(2, allUsers.size)
1348+
assertEquals(UserStatus.ACTIVE, allUsers[0].status)
1349+
assertEquals(Priority.HIGH, allUsers[0].priority)
1350+
assertEquals(UserStatus.INACTIVE, allUsers[1].status)
1351+
assertEquals(Priority.LOW, allUsers[1].priority)
1352+
1353+
// Section 2: Equality operators (EQ, NEQ)
1354+
val testUsers = listOf(
1355+
UserAccount(null, "user1", "user1@test.com", UserStatus.ACTIVE, Priority.HIGH, null),
1356+
UserAccount(null, "user2", "user2@test.com", UserStatus.INACTIVE, Priority.MEDIUM, null),
1357+
UserAccount(null, "user3", "user3@test.com", UserStatus.ACTIVE, Priority.LOW, null),
1358+
UserAccount(null, "user4", "user4@test.com", UserStatus.SUSPENDED, Priority.CRITICAL, null),
1359+
)
1360+
database {
1361+
UserAccountTable { table ->
1362+
table DELETE X
1363+
table INSERT testUsers
1364+
}
1365+
}
1366+
1367+
var selectEQ: SelectStatement<UserAccount>? = null
1368+
database {
1369+
UserAccountTable {
1370+
selectEQ = it SELECT WHERE (it.status EQ UserStatus.ACTIVE)
1371+
}
1372+
}
1373+
val activeUsers = selectEQ!!.getResults()
1374+
assertEquals(2, activeUsers.size)
1375+
assertEquals(true, activeUsers.all { it.status == UserStatus.ACTIVE })
1376+
1377+
var selectNEQ: SelectStatement<UserAccount>? = null
1378+
database {
1379+
UserAccountTable {
1380+
selectNEQ = it SELECT WHERE (it.status NEQ UserStatus.ACTIVE)
1381+
}
1382+
}
1383+
val nonActiveUsers = selectNEQ!!.getResults()
1384+
assertEquals(2, nonActiveUsers.size)
1385+
assertEquals(false, nonActiveUsers.any { it.status == UserStatus.ACTIVE })
1386+
1387+
// Section 3: Comparison operators (LT, LTE, GT, GTE)
1388+
var selectLT: SelectStatement<UserAccount>? = null
1389+
database {
1390+
UserAccountTable {
1391+
selectLT = it SELECT WHERE (it.priority LT Priority.HIGH)
1392+
}
1393+
}
1394+
assertEquals(2, selectLT!!.getResults().size)
1395+
1396+
var selectGTE: SelectStatement<UserAccount>? = null
1397+
database {
1398+
UserAccountTable {
1399+
selectGTE = it SELECT WHERE (it.priority GTE Priority.HIGH)
1400+
}
1401+
}
1402+
val highPriorityUsers = selectGTE!!.getResults()
1403+
assertEquals(2, highPriorityUsers.size)
1404+
assertEquals(true, highPriorityUsers.all { it.priority == Priority.HIGH || it.priority == Priority.CRITICAL })
1405+
1406+
// Section 4: Nullable enum handling
1407+
val tasks = listOf(
1408+
Task(null, "High priority task", Priority.HIGH, "Important"),
1409+
Task(null, "Unassigned task", null, "No priority set"),
1410+
Task(null, "Low priority task", Priority.LOW, "Can wait"),
1411+
)
1412+
database {
1413+
TaskTable { table ->
1414+
table INSERT tasks
1415+
}
1416+
}
1417+
1418+
var selectNull: SelectStatement<Task>? = null
1419+
database {
1420+
TaskTable {
1421+
selectNull = it SELECT WHERE (it.priority EQ null)
1422+
}
1423+
}
1424+
val nullTasks = selectNull!!.getResults()
1425+
assertEquals(1, nullTasks.size)
1426+
assertEquals("Unassigned task", nullTasks[0].title)
1427+
1428+
var selectNotNull: SelectStatement<Task>? = null
1429+
database {
1430+
TaskTable {
1431+
selectNotNull = it SELECT WHERE (it.priority NEQ null)
1432+
}
1433+
}
1434+
assertEquals(2, selectNotNull!!.getResults().size)
1435+
1436+
// Section 5: UPDATE with enum values
1437+
database {
1438+
UserAccountTable { table ->
1439+
table UPDATE SET {
1440+
status = UserStatus.BANNED
1441+
priority = Priority.CRITICAL
1442+
} WHERE (table.username EQ "user1")
1443+
}
1444+
}
1445+
1446+
var selectUpdated: SelectStatement<UserAccount>? = null
1447+
database {
1448+
UserAccountTable {
1449+
selectUpdated = it SELECT WHERE (it.username EQ "user1")
1450+
}
1451+
}
1452+
val updatedUser = selectUpdated!!.getResults().first()
1453+
assertEquals(UserStatus.BANNED, updatedUser.status)
1454+
assertEquals(Priority.CRITICAL, updatedUser.priority)
1455+
1456+
// Section 6: Complex conditions (AND/OR)
1457+
var selectAND: SelectStatement<UserAccount>? = null
1458+
database {
1459+
UserAccountTable {
1460+
selectAND = it SELECT WHERE (
1461+
(it.status EQ UserStatus.SUSPENDED) AND (it.priority EQ Priority.CRITICAL)
1462+
)
1463+
}
1464+
}
1465+
assertEquals(1, selectAND!!.getResults().size)
1466+
1467+
var selectOR: SelectStatement<UserAccount>? = null
1468+
database {
1469+
UserAccountTable {
1470+
selectOR = it SELECT WHERE (
1471+
(it.status EQ UserStatus.BANNED) OR (it.priority LTE Priority.LOW)
1472+
)
1473+
}
1474+
}
1475+
assertEquals(2, selectOR!!.getResults().size)
1476+
1477+
// Section 7: ORDER BY enum columns
1478+
var selectOrderByASC: SelectStatement<UserAccount>? = null
1479+
database {
1480+
UserAccountTable { table ->
1481+
selectOrderByASC = table SELECT ORDER_BY (priority to ASC)
1482+
}
1483+
}
1484+
val orderedASC = selectOrderByASC!!.getResults()
1485+
assertEquals(Priority.LOW, orderedASC[0].priority)
1486+
assertEquals(Priority.CRITICAL, orderedASC[orderedASC.size - 1].priority)
1487+
1488+
var selectOrderByDESC: SelectStatement<UserAccount>? = null
1489+
database {
1490+
UserAccountTable { table ->
1491+
selectOrderByDESC = table SELECT ORDER_BY (table.status to DESC)
1492+
}
1493+
}
1494+
val orderedDESC = selectOrderByDESC!!.getResults()
1495+
// After UPDATE in Section 5, user1 is BANNED (highest ordinal 3)
1496+
assertEquals(UserStatus.BANNED, orderedDESC[0].status)
1497+
assertEquals(UserStatus.ACTIVE, orderedDESC[orderedDESC.size - 1].status)
1498+
1499+
// Section 8: DELETE with enum WHERE clause
1500+
database {
1501+
UserAccountTable { table ->
1502+
table DELETE WHERE (table.status EQ UserStatus.BANNED)
1503+
}
1504+
}
1505+
1506+
var selectAfterDelete: SelectStatement<UserAccount>? = null
1507+
database {
1508+
UserAccountTable {
1509+
selectAfterDelete = it SELECT X
1510+
}
1511+
}
1512+
val remainingUsers = selectAfterDelete!!.getResults()
1513+
assertEquals(false, remainingUsers.any { it.status == UserStatus.BANNED })
1514+
}
1515+
13271516
private fun getDefaultDBConfig(): DatabaseConfiguration =
13281517
DatabaseConfiguration(
13291518
name = DATABASE_NAME,
@@ -1348,6 +1537,8 @@ class CommonBasicTest(private val path: DatabasePath) {
13481537
CREATE(StudentWithAutoincrementTable)
13491538
CREATE(EnrollmentTable)
13501539
CREATE(FileDataTable)
1540+
CREATE(UserAccountTable)
1541+
CREATE(TaskTable)
13511542
}
13521543
)
13531544
}

sqllin-dsl-test/src/jvmTest/kotlin/com/ctrip/sqllin/dsl/test/JvmTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ class JvmTest {
7979
@Test
8080
fun testStringOperators() = commonTest.testStringOperators()
8181

82+
@Test
83+
fun testEnumOperations() = commonTest.testEnumOperations()
84+
8285
@BeforeTest
8386
fun setUp() {
8487
deleteDatabase(path, CommonBasicTest.DATABASE_NAME)

sqllin-dsl-test/src/nativeTest/kotlin/com/ctrip/sqllin/dsl/test/NativeTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ class NativeTest {
9595
@Test
9696
fun testStringOperators() = commonTest.testStringOperators()
9797

98+
@Test
99+
fun testEnumOperations() = commonTest.testEnumOperations()
100+
98101
@BeforeTest
99102
fun setUp() {
100103
deleteDatabase(path, CommonBasicTest.DATABASE_NAME)

sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/ClauseBlob.kt

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ import com.ctrip.sqllin.dsl.sql.Table
4545
public class ClauseBlob(
4646
valueName: String,
4747
table: Table<*>,
48-
isFunction: Boolean,
49-
) : ClauseElement(valueName, table, isFunction) {
48+
) : ClauseElement(valueName, table, false) {
5049

5150
/**
5251
* Creates an equality comparison condition (=).
@@ -154,10 +153,8 @@ public class ClauseBlob(
154153

155154
private fun appendNullableBlob(notNullSymbol: String, nullSymbol: String, blob: ByteArray?): SelectCondition {
156155
val sql = buildString {
157-
if (!isFunction) {
158-
append(table.tableName)
159-
append('.')
160-
}
156+
append(table.tableName)
157+
append('.')
161158
append(valueName)
162159
if (blob == null) {
163160
append(nullSymbol)
@@ -171,10 +168,8 @@ public class ClauseBlob(
171168

172169
private fun appendBlob(symbol: String, blob: ByteArray): SelectCondition {
173170
val sql = buildString {
174-
if (!isFunction) {
175-
append(table.tableName)
176-
append('.')
177-
}
171+
append(table.tableName)
172+
append('.')
178173
append(valueName)
179174
append(symbol)
180175
}
@@ -209,10 +204,8 @@ public class ClauseBlob(
209204
val parameters = blobs.toMutableList<Any?>()
210205
require(parameters.isNotEmpty()) { "Param 'blobs' must not be empty!!!" }
211206
val sql = buildString {
212-
if (!isFunction) {
213-
append(table.tableName)
214-
append('.')
215-
}
207+
append(table.tableName)
208+
append('.')
216209
append(valueName)
217210
append(" IN (")
218211

@@ -235,10 +228,8 @@ public class ClauseBlob(
235228
*/
236229
internal infix fun between(range: Pair<ByteArray, ByteArray>): SelectCondition {
237230
val sql = buildString {
238-
if (!isFunction) {
239-
append(table.tableName)
240-
append('.')
241-
}
231+
append(table.tableName)
232+
append('.')
242233
append(valueName)
243234
append(" BETWEEN ? AND ?")
244235
}

sqllin-dsl/src/commonMain/kotlin/com/ctrip/sqllin/dsl/sql/clause/ClauseBoolean.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ import com.ctrip.sqllin.dsl.sql.Table
3131
public class ClauseBoolean(
3232
valueName: String,
3333
table: Table<*>,
34-
isFunction: Boolean,
35-
) : ClauseElement(valueName, table, isFunction) {
34+
) : ClauseElement(valueName, table, false) {
3635

3736
/**
3837
* Creates a condition comparing this Boolean column/function to a value.

0 commit comments

Comments
 (0)