Skip to content

Commit 68fd0ee

Browse files
committed
feat(database)!: typed value-model filter DSL (breaking)
Replace the stringly-typed FilterBuilder with a type-safe, value-based DSL. - Column<T> typed tokens; Exposed/Ktorm-vocab infix operators (eq/neq/ greater/greaterEq/less/lessEq/within/inList/like/ilike/matches/isNull/…). - Filter is now an immutable value tree rendered by a single recursive walk (toParams/toGroupTerm), removing the old flattenLogical/isLogicalKey string re-parsing. - Query modifiers split out of the filter block: reads take QueryBuilder (where{} + orderBy/limit/offset/range), mutations take WhereBuilder. 'not { limit() }' is no longer expressible. - Fixes the silent duplicate limit/offset bug (last-wins, regression-tested). - Migrated database client (~25 methods), sync keyset query, samples, and the db/sync/e2e test suites. BCV api dumps regenerated. BREAKING CHANGE: FilterBuilder, filters{}, OrderDirection and NullsPlacement are removed. Use Column<T> with where{}/query{} and Order/Nulls instead.
1 parent 2fd968a commit 68fd0ee

17 files changed

Lines changed: 761 additions & 1530 deletions

File tree

e2e-tests/src/test/kotlin/io/github/androidpoet/supabase/e2e/DatabaseE2eTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.github.androidpoet.supabase.e2e
22

33
import io.github.androidpoet.supabase.client.Supabase
4+
import io.github.androidpoet.supabase.core.models.Column
45
import io.github.androidpoet.supabase.database.createDatabaseClient
56
import kotlinx.coroutines.test.runTest
67
import kotlinx.serialization.json.Json
@@ -42,7 +43,7 @@ class DatabaseE2eTest {
4243
.select(
4344
table = E2E_MESSAGES_TABLE,
4445
) {
45-
eq("body", messageBody)
46+
where { Column<String>("body") eq messageBody }
4647
}.unwrap("select")
4748

4849
client.close()

samples/chat-compose/src/main/java/io/github/androidpoet/supabase/sample/chat/data/DemoRepository.kt

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import io.github.androidpoet.supabase.auth.signOutCurrentSession
1313
import io.github.androidpoet.supabase.auth.signUpWithEmailAndSaveSession
1414
import io.github.androidpoet.supabase.client.defaultJson
1515
import io.github.androidpoet.supabase.client.deserialize
16+
import io.github.androidpoet.supabase.core.models.Column
17+
import io.github.androidpoet.supabase.core.models.Order
1618
import io.github.androidpoet.supabase.core.result.SupabaseError
1719
import io.github.androidpoet.supabase.core.result.SupabaseResult
1820
import io.github.androidpoet.supabase.core.result.map
@@ -38,6 +40,16 @@ import kotlinx.serialization.json.JsonObject
3840
import kotlinx.serialization.json.JsonPrimitive
3941
import kotlinx.serialization.json.buildJsonObject
4042

43+
/** Typed column tokens for the demo tables (what codegen would emit). */
44+
private object ChatRooms {
45+
val name: Column<String> = Column("name")
46+
}
47+
48+
private object ChatMessages {
49+
val roomId: Column<String> = Column("room_id")
50+
val createdAt: Column<String> = Column("created_at")
51+
}
52+
4153
class DemoRepository(
4254
private val auth: AuthClient,
4355
private val sessionManager: SessionManager,
@@ -79,7 +91,7 @@ class DemoRepository(
7991

8092
suspend fun loadRooms(): SupabaseResult<List<ChatRoom>> =
8193
database.selectTyped(table = "chat_rooms") {
82-
order("name", ascending = true)
94+
orderBy(ChatRooms.name, Order.ASC)
8395
}
8496

8597
suspend fun createRoom(name: String): SupabaseResult<ChatRoom> =
@@ -96,9 +108,11 @@ class DemoRepository(
96108
pageSize: Int,
97109
): SupabaseResult<List<ChatMessage>> =
98110
database.selectTyped(table = "chat_messages") {
99-
eq("room_id", roomId)
100-
if (beforeCreatedAt != null) lt("created_at", beforeCreatedAt)
101-
order("created_at", ascending = false)
111+
where {
112+
ChatMessages.roomId eq roomId
113+
if (beforeCreatedAt != null) ChatMessages.createdAt less beforeCreatedAt
114+
}
115+
orderBy(ChatMessages.createdAt, Order.DESC)
102116
limit(pageSize)
103117
}
104118

@@ -178,13 +192,13 @@ class DemoRepository(
178192
suspend fun buildDatabaseReport(roomId: String): SupabaseResult<String> {
179193
val head =
180194
database.selectHead(table = "chat_messages", count = CountOption.EXACT) {
181-
eq("room_id", roomId)
195+
where { ChatMessages.roomId eq roomId }
182196
}
183197
if (head is SupabaseResult.Failure) return head
184198

185199
val csv =
186200
database.selectCsv(table = "chat_rooms", columns = "id,name") {
187-
order("name", ascending = true)
201+
orderBy(ChatRooms.name, Order.ASC)
188202
limit(5)
189203
}
190204
return when (csv) {

supabase-core/api/android/supabase-core.api

Lines changed: 73 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
public final class io/github/androidpoet/supabase/core/models/Column {
2+
public static final synthetic fun box-impl (Ljava/lang/String;)Lio/github/androidpoet/supabase/core/models/Column;
3+
public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String;
4+
public fun equals (Ljava/lang/Object;)Z
5+
public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z
6+
public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z
7+
public final fun getName ()Ljava/lang/String;
8+
public fun hashCode ()I
9+
public static fun hashCode-impl (Ljava/lang/String;)I
10+
public fun toString ()Ljava/lang/String;
11+
public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String;
12+
public final synthetic fun unbox-impl ()Ljava/lang/String;
13+
}
14+
115
public final class io/github/androidpoet/supabase/core/models/ErrorResponse {
216
public static final field Companion Lio/github/androidpoet/supabase/core/models/ErrorResponse$Companion;
317
public fun <init> ()V
@@ -33,97 +47,31 @@ public final class io/github/androidpoet/supabase/core/models/ErrorResponse$Comp
3347
public final fun serializer ()Lkotlinx/serialization/KSerializer;
3448
}
3549

36-
public final class io/github/androidpoet/supabase/core/models/FilterBuilder {
37-
public fun <init> ()V
38-
public final fun and (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
39-
public static synthetic fun and$default (Lio/github/androidpoet/supabase/core/models/FilterBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
40-
public final fun build ()Ljava/util/List;
41-
public final fun containedBy (Ljava/lang/String;Ljava/lang/String;)V
42-
public final fun containedBy (Ljava/lang/String;Ljava/util/List;)V
43-
public final fun contains (Ljava/lang/String;Ljava/lang/String;)V
44-
public final fun contains (Ljava/lang/String;Ljava/util/List;)V
45-
public final fun eq (Ljava/lang/String;Ljava/lang/Number;)V
46-
public final fun eq (Ljava/lang/String;Ljava/lang/String;)V
47-
public final fun eq (Ljava/lang/String;Z)V
48-
public final fun filter (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
49-
public final fun gt (Ljava/lang/String;Ljava/lang/Number;)V
50-
public final fun gt (Ljava/lang/String;Ljava/lang/String;)V
51-
public final fun gte (Ljava/lang/String;Ljava/lang/Number;)V
52-
public final fun gte (Ljava/lang/String;Ljava/lang/String;)V
53-
public final fun ilike (Ljava/lang/String;Ljava/lang/String;)V
54-
public final fun ilikeAllOf (Ljava/lang/String;Ljava/util/List;)V
55-
public final fun ilikeAnyOf (Ljava/lang/String;Ljava/util/List;)V
56-
public final fun imatch (Ljava/lang/String;Ljava/lang/String;)V
57-
public final fun in (Ljava/lang/String;Ljava/util/List;)V
58-
public final fun inAny (Ljava/lang/String;Ljava/util/List;)V
59-
public final fun inNumbers (Ljava/lang/String;Ljava/util/List;)V
60-
public final fun is (Ljava/lang/String;Ljava/lang/Boolean;)V
61-
public final fun is (Ljava/lang/String;Ljava/lang/String;)V
62-
public final fun isDistinct (Ljava/lang/String;Ljava/lang/Boolean;)V
63-
public final fun isDistinct (Ljava/lang/String;Ljava/lang/Number;)V
64-
public final fun isDistinct (Ljava/lang/String;Ljava/lang/String;)V
65-
public final fun like (Ljava/lang/String;Ljava/lang/String;)V
66-
public final fun likeAllOf (Ljava/lang/String;Ljava/util/List;)V
67-
public final fun likeAnyOf (Ljava/lang/String;Ljava/util/List;)V
68-
public final fun limit (ILjava/lang/String;)V
69-
public static synthetic fun limit$default (Lio/github/androidpoet/supabase/core/models/FilterBuilder;ILjava/lang/String;ILjava/lang/Object;)V
70-
public final fun lt (Ljava/lang/String;Ljava/lang/Number;)V
71-
public final fun lt (Ljava/lang/String;Ljava/lang/String;)V
72-
public final fun lte (Ljava/lang/String;Ljava/lang/Number;)V
73-
public final fun lte (Ljava/lang/String;Ljava/lang/String;)V
74-
public final fun match (Ljava/lang/String;Ljava/lang/String;)V
75-
public final fun match (Ljava/util/Map;)V
76-
public final fun neq (Ljava/lang/String;Ljava/lang/Number;)V
77-
public final fun neq (Ljava/lang/String;Ljava/lang/String;)V
78-
public final fun neq (Ljava/lang/String;Z)V
79-
public final fun not (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
80-
public final fun not (Lkotlin/jvm/functions/Function1;)V
81-
public final fun notExtendLeft (Ljava/lang/String;Ljava/lang/String;)V
82-
public final fun notExtendRight (Ljava/lang/String;Ljava/lang/String;)V
83-
public final fun offset (ILjava/lang/String;)V
84-
public static synthetic fun offset$default (Lio/github/androidpoet/supabase/core/models/FilterBuilder;ILjava/lang/String;ILjava/lang/Object;)V
85-
public final fun or (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
86-
public static synthetic fun or$default (Lio/github/androidpoet/supabase/core/models/FilterBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
87-
public final fun order (Ljava/lang/String;Lio/github/androidpoet/supabase/core/models/OrderDirection;Lio/github/androidpoet/supabase/core/models/NullsPlacement;Ljava/lang/String;)V
88-
public final fun order (Ljava/lang/String;ZLjava/lang/Boolean;Ljava/lang/String;)V
89-
public static synthetic fun order$default (Lio/github/androidpoet/supabase/core/models/FilterBuilder;Ljava/lang/String;Lio/github/androidpoet/supabase/core/models/OrderDirection;Lio/github/androidpoet/supabase/core/models/NullsPlacement;Ljava/lang/String;ILjava/lang/Object;)V
90-
public static synthetic fun order$default (Lio/github/androidpoet/supabase/core/models/FilterBuilder;Ljava/lang/String;ZLjava/lang/Boolean;Ljava/lang/String;ILjava/lang/Object;)V
91-
public final fun overlaps (Ljava/lang/String;Ljava/lang/String;)V
92-
public final fun overlaps (Ljava/lang/String;Ljava/util/List;)V
93-
public final fun range (IILjava/lang/String;)V
94-
public static synthetic fun range$default (Lio/github/androidpoet/supabase/core/models/FilterBuilder;IILjava/lang/String;ILjava/lang/Object;)V
95-
public final fun rangeAdj (Ljava/lang/String;Ljava/lang/String;)V
96-
public final fun rangeGt (Ljava/lang/String;Ljava/lang/String;)V
97-
public final fun rangeGte (Ljava/lang/String;Ljava/lang/String;)V
98-
public final fun rangeLt (Ljava/lang/String;Ljava/lang/String;)V
99-
public final fun rangeLte (Ljava/lang/String;Ljava/lang/String;)V
100-
public final fun strictlyLeft (Ljava/lang/String;Ljava/lang/String;)V
101-
public final fun strictlyRight (Ljava/lang/String;Ljava/lang/String;)V
102-
public final fun textSearch (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/androidpoet/supabase/core/models/TextSearchType;)V
103-
public static synthetic fun textSearch$default (Lio/github/androidpoet/supabase/core/models/FilterBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/androidpoet/supabase/core/models/TextSearchType;ILjava/lang/Object;)V
50+
public abstract interface class io/github/androidpoet/supabase/core/models/Filter {
10451
}
10552

10653
public abstract interface annotation class io/github/androidpoet/supabase/core/models/FilterDsl : java/lang/annotation/Annotation {
10754
}
10855

10956
public final class io/github/androidpoet/supabase/core/models/FiltersKt {
110-
public static final fun filters (Lkotlin/jvm/functions/Function1;)Ljava/util/List;
57+
public static final fun query (Lkotlin/jvm/functions/Function1;)Ljava/util/List;
58+
public static final fun where (Lkotlin/jvm/functions/Function1;)Ljava/util/List;
11159
}
11260

113-
public final class io/github/androidpoet/supabase/core/models/NullsPlacement : java/lang/Enum {
114-
public static final field FIRST Lio/github/androidpoet/supabase/core/models/NullsPlacement;
115-
public static final field LAST Lio/github/androidpoet/supabase/core/models/NullsPlacement;
61+
public final class io/github/androidpoet/supabase/core/models/Nulls : java/lang/Enum {
62+
public static final field FIRST Lio/github/androidpoet/supabase/core/models/Nulls;
63+
public static final field LAST Lio/github/androidpoet/supabase/core/models/Nulls;
11664
public static fun getEntries ()Lkotlin/enums/EnumEntries;
117-
public static fun valueOf (Ljava/lang/String;)Lio/github/androidpoet/supabase/core/models/NullsPlacement;
118-
public static fun values ()[Lio/github/androidpoet/supabase/core/models/NullsPlacement;
65+
public static fun valueOf (Ljava/lang/String;)Lio/github/androidpoet/supabase/core/models/Nulls;
66+
public static fun values ()[Lio/github/androidpoet/supabase/core/models/Nulls;
11967
}
12068

121-
public final class io/github/androidpoet/supabase/core/models/OrderDirection : java/lang/Enum {
122-
public static final field ASCENDING Lio/github/androidpoet/supabase/core/models/OrderDirection;
123-
public static final field DESCENDING Lio/github/androidpoet/supabase/core/models/OrderDirection;
69+
public final class io/github/androidpoet/supabase/core/models/Order : java/lang/Enum {
70+
public static final field ASC Lio/github/androidpoet/supabase/core/models/Order;
71+
public static final field DESC Lio/github/androidpoet/supabase/core/models/Order;
12472
public static fun getEntries ()Lkotlin/enums/EnumEntries;
125-
public static fun valueOf (Ljava/lang/String;)Lio/github/androidpoet/supabase/core/models/OrderDirection;
126-
public static fun values ()[Lio/github/androidpoet/supabase/core/models/OrderDirection;
73+
public static fun valueOf (Ljava/lang/String;)Lio/github/androidpoet/supabase/core/models/Order;
74+
public static fun values ()[Lio/github/androidpoet/supabase/core/models/Order;
12775
}
12876

12977
public final class io/github/androidpoet/supabase/core/models/PostgrestResponse {
@@ -156,6 +104,19 @@ public final class io/github/androidpoet/supabase/core/models/PostgrestResponse$
156104
public final fun serializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
157105
}
158106

107+
public final class io/github/androidpoet/supabase/core/models/QueryBuilder {
108+
public fun <init> ()V
109+
public final fun limit (ILjava/lang/String;)V
110+
public static synthetic fun limit$default (Lio/github/androidpoet/supabase/core/models/QueryBuilder;ILjava/lang/String;ILjava/lang/Object;)V
111+
public final fun offset (ILjava/lang/String;)V
112+
public static synthetic fun offset$default (Lio/github/androidpoet/supabase/core/models/QueryBuilder;ILjava/lang/String;ILjava/lang/Object;)V
113+
public final fun orderBy-7Jr2Q9A (Ljava/lang/String;Lio/github/androidpoet/supabase/core/models/Order;Lio/github/androidpoet/supabase/core/models/Nulls;Ljava/lang/String;)V
114+
public static synthetic fun orderBy-7Jr2Q9A$default (Lio/github/androidpoet/supabase/core/models/QueryBuilder;Ljava/lang/String;Lio/github/androidpoet/supabase/core/models/Order;Lio/github/androidpoet/supabase/core/models/Nulls;Ljava/lang/String;ILjava/lang/Object;)V
115+
public final fun range (IILjava/lang/String;)V
116+
public static synthetic fun range$default (Lio/github/androidpoet/supabase/core/models/QueryBuilder;IILjava/lang/String;ILjava/lang/Object;)V
117+
public final fun where (Lkotlin/jvm/functions/Function1;)V
118+
}
119+
159120
public final class io/github/androidpoet/supabase/core/models/TextSearchType : java/lang/Enum {
160121
public static final field Phrase Lio/github/androidpoet/supabase/core/models/TextSearchType;
161122
public static final field Plain Lio/github/androidpoet/supabase/core/models/TextSearchType;
@@ -166,6 +127,39 @@ public final class io/github/androidpoet/supabase/core/models/TextSearchType : j
166127
public static fun values ()[Lio/github/androidpoet/supabase/core/models/TextSearchType;
167128
}
168129

130+
public final class io/github/androidpoet/supabase/core/models/WhereBuilder {
131+
public fun <init> ()V
132+
public final fun and (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
133+
public static synthetic fun and$default (Lio/github/androidpoet/supabase/core/models/WhereBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
134+
public final fun containedBy--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
135+
public final fun contains--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
136+
public final fun eq--PLe0oE (Ljava/lang/String;Ljava/lang/Object;)V
137+
public final fun greater--PLe0oE (Ljava/lang/String;Ljava/lang/Comparable;)V
138+
public final fun greaterEq--PLe0oE (Ljava/lang/String;Ljava/lang/Comparable;)V
139+
public final fun ilike--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
140+
public final fun imatches--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
141+
public final fun inList--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
142+
public final fun isDistinctFrom--PLe0oE (Ljava/lang/String;Ljava/lang/Object;)V
143+
public final fun isExactly--PLe0oE (Ljava/lang/String;Z)V
144+
public final fun isNotNull-kHnLTmE (Ljava/lang/String;)V
145+
public final fun isNull-kHnLTmE (Ljava/lang/String;)V
146+
public final fun less--PLe0oE (Ljava/lang/String;Ljava/lang/Comparable;)V
147+
public final fun lessEq--PLe0oE (Ljava/lang/String;Ljava/lang/Comparable;)V
148+
public final fun like--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
149+
public final fun likeAllOf--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
150+
public final fun likeAnyOf--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
151+
public final fun matches--PLe0oE (Ljava/lang/String;Ljava/lang/String;)V
152+
public final fun neq--PLe0oE (Ljava/lang/String;Ljava/lang/Object;)V
153+
public final fun not (Lkotlin/jvm/functions/Function1;)V
154+
public final fun or (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
155+
public static synthetic fun or$default (Lio/github/androidpoet/supabase/core/models/WhereBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
156+
public final fun overlaps--PLe0oE (Ljava/lang/String;Ljava/util/List;)V
157+
public final fun raw-elUkyCk (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
158+
public final fun textSearch-7Jr2Q9A (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/androidpoet/supabase/core/models/TextSearchType;)V
159+
public static synthetic fun textSearch-7Jr2Q9A$default (Lio/github/androidpoet/supabase/core/models/WhereBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/androidpoet/supabase/core/models/TextSearchType;ILjava/lang/Object;)V
160+
public final fun within--PLe0oE (Ljava/lang/String;Lkotlin/ranges/ClosedRange;)V
161+
}
162+
169163
public final class io/github/androidpoet/supabase/core/paging/Paginator {
170164
public fun <init> (ILkotlin/jvm/functions/Function3;)V
171165
public synthetic fun <init> (ILkotlin/jvm/functions/Function3;ILkotlin/jvm/internal/DefaultConstructorMarker;)V

0 commit comments

Comments
 (0)