git.iko.soy/rowtype-yoga/purescript-yoga-scylladb
on main help sign in
20 files changed+267−33
since splitsnapshots
unifiedsplit
README.md+1 compile-fail/AllowFilteringOnInsert.purs+16 compile-fail/DeleteThenSet.purs+15 compile-fail/DoubleAllowFiltering.purs+15 compile-fail/DoubleLimit.purs+15 compile-fail/DoubleOrderBy.purs+15 compile-fail/IfNotExistsOnDelete.purs+15 compile-fail/OrderByAfterLimit.purs+15 compile-fail/SelectNonexistentColumn.purs+15 compile-fail/SetNonexistentColumn.purs+15 compile-fail/WhereAfterLimit.purs+15 compile-fail/WhereAfterOrderBy.purs+15 compile-fail/WhereNonexistentParam.purs+15 compile-fail/WithTTLAfterSelect.purs+15 spago.lock+15−6 spago.yaml+6−3 src/Yoga/ScyllaDB/Schema.purs+6−20 src/Yoga/ScyllaDB/ScyllaDB.purs+11−2 test/Yoga/ScyllaDB/Schema.test.js+2 src/Yoga/ScyllaDB/Schema.test.purs → test/Yoga/ScyllaDB/Schema.test.purs+30−2
README.mdnew file+1
@@ -0,0 +1 @@
1+
compile-fail/AllowFilteringOnInsert.pursnew file+16
@@ -0,0 +1,16 @@
1+-- expect: HasClause "select"
2+module CompileFail.AllowFilteringOnInsert where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+import Unsafe.Coerce (unsafeCoerce)
10+
11+type UsersTable = S.Table "users"
12+ ( id :: Scylla.UUID # S.PartitionKey
13+ , name :: String
14+ )
15+
16+bad = S.from (Proxy :: Proxy UsersTable) # S.insert { id: (unsafeCoerce "" :: Scylla.UUID), name: "x" } # S.allowFiltering
compile-fail/DeleteThenSet.pursnew file+15
@@ -0,0 +1,15 @@
1+-- expect: Lacks "delete"
2+module CompileFail.DeleteThenSet where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+
10+type UsersTable = S.Table "users"
11+ ( id :: Scylla.UUID # S.PartitionKey
12+ , name :: String
13+ )
14+
15+bad = S.from (Proxy :: Proxy UsersTable) # S.delete # S.set { name: "x" }
compile-fail/DoubleAllowFiltering.pursnew file+15
@@ -0,0 +1,15 @@
1+-- expect: Lacks "allowFiltering"
2+module CompileFail.DoubleAllowFiltering where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+
10+type UsersTable = S.Table "users"
11+ ( id :: Scylla.UUID # S.PartitionKey
12+ , name :: String
13+ )
14+
15+bad = S.from (Proxy :: Proxy UsersTable) # S.selectAll # S.where_ @"name = $name" # S.allowFiltering # S.allowFiltering
compile-fail/DoubleLimit.pursnew file+15
@@ -0,0 +1,15 @@
1+-- expect: Lacks "limit"
2+module CompileFail.DoubleLimit where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+
10+type UsersTable = S.Table "users"
11+ ( id :: Scylla.UUID # S.PartitionKey
12+ , name :: String
13+ )
14+
15+bad = S.from (Proxy :: Proxy UsersTable) # S.selectAll # S.limit @"10" # S.limit @"5"
compile-fail/DoubleOrderBy.pursnew file+15
@@ -0,0 +1,15 @@
1+-- expect: Lacks "orderBy"
2+module CompileFail.DoubleOrderBy where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+
10+type UsersTable = S.Table "users"
11+ ( id :: Scylla.UUID # S.PartitionKey
12+ , name :: String
13+ )
14+
15+bad = S.from (Proxy :: Proxy UsersTable) # S.selectAll # S.orderBy @"name" # S.orderBy @"id"
compile-fail/IfNotExistsOnDelete.pursnew file+15
@@ -0,0 +1,15 @@
1+-- expect: HasClause "insert"
2+module CompileFail.IfNotExistsOnDelete where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+
10+type UsersTable = S.Table "users"
11+ ( id :: Scylla.UUID # S.PartitionKey
12+ , name :: String
13+ )
14+
15+bad = S.from (Proxy :: Proxy UsersTable) # S.delete # S.where_ @"id = $id" # S.ifNotExists
compile-fail/OrderByAfterLimit.pursnew file+15
@@ -0,0 +1,15 @@
1+-- expect: Lacks "limit"
2+module CompileFail.OrderByAfterLimit where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+
10+type UsersTable = S.Table "users"
11+ ( id :: Scylla.UUID # S.PartitionKey
12+ , name :: String
13+ )
14+
15+bad = S.from (Proxy :: Proxy UsersTable) # S.selectAll # S.limit @"10" # S.orderBy @"name"
compile-fail/SelectNonexistentColumn.pursnew file+15
@@ -0,0 +1,15 @@
1+-- expect: Column not found in table
2+module CompileFail.SelectNonexistentColumn where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+
10+type UsersTable = S.Table "users"
11+ ( id :: Scylla.UUID # S.PartitionKey
12+ , name :: String
13+ )
14+
15+bad = S.from (Proxy :: Proxy UsersTable) # S.select @"nonexistent"
compile-fail/SetNonexistentColumn.pursnew file+15
@@ -0,0 +1,15 @@
1+-- expect: Could not match type
2+module CompileFail.SetNonexistentColumn where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+
10+type UsersTable = S.Table "users"
11+ ( id :: Scylla.UUID # S.PartitionKey
12+ , name :: String
13+ )
14+
15+bad = S.from (Proxy :: Proxy UsersTable) # S.set { bogus: "x" }
compile-fail/WhereAfterLimit.pursnew file+15
@@ -0,0 +1,15 @@
1+-- expect: Lacks "limit"
2+module CompileFail.WhereAfterLimit where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+
10+type UsersTable = S.Table "users"
11+ ( id :: Scylla.UUID # S.PartitionKey
12+ , name :: String
13+ )
14+
15+bad = S.from (Proxy :: Proxy UsersTable) # S.selectAll # S.limit @"10" # S.where_ @"id = $id"
compile-fail/WhereAfterOrderBy.pursnew file+15
@@ -0,0 +1,15 @@
1+-- expect: Lacks "orderBy"
2+module CompileFail.WhereAfterOrderBy where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+
10+type UsersTable = S.Table "users"
11+ ( id :: Scylla.UUID # S.PartitionKey
12+ , name :: String
13+ )
14+
15+bad = S.from (Proxy :: Proxy UsersTable) # S.selectAll # S.orderBy @"name" # S.where_ @"id = $id"
compile-fail/WhereNonexistentParam.pursnew file+15
@@ -0,0 +1,15 @@
1+-- expect: Column not found in table
2+module CompileFail.WhereNonexistentParam where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+
10+type UsersTable = S.Table "users"
11+ ( id :: Scylla.UUID # S.PartitionKey
12+ , name :: String
13+ )
14+
15+bad = S.from (Proxy :: Proxy UsersTable) # S.selectAll # S.where_ @"bogus = $bogus"
compile-fail/WithTTLAfterSelect.pursnew file+15
@@ -0,0 +1,15 @@
1+-- expect: Lacks "select"
2+module CompileFail.WithTTLAfterSelect where
3+import Prelude
4+
5+import Yoga.ScyllaDB.Schema as S
6+import Yoga.ScyllaDB.ScyllaDB as Scylla
7+import Type.Function (type (#))
8+import Type.Proxy (Proxy(..))
9+
10+type UsersTable = S.Table "users"
11+ ( id :: Scylla.UUID # S.PartitionKey
12+ , name :: String
13+ )
14+
15+bad = S.from (Proxy :: Proxy UsersTable) # S.selectAll # S.withTTL @"3600"
spago.lock+15−6
@@ -20,6 +20,9 @@
2020 {
2121 "either": ">=6.1.0 <7.0.0"
2222 },
23+ {
24+ "exceptions": ">=6.1.0 <7.0.0"
25+ },
2326 {
2427 "foldable-traversable": ">=6.0.0 <7.0.0"
2528 },
@@ -54,7 +57,13 @@
5457 "prelude": ">=6.0.2 <7.0.0"
5558 },
5659 {
57− "type-equality": ">=4.0.1 <5.0.0"
60+ "record": ">=4.0.0 <5.0.0"
61+ },
62+ {
63+ "strings": ">=6.0.1 <7.0.0"
64+ },
65+ {
66+ "transformers": ">=6.1.0 <7.0.0"
5867 },
5968 {
6069 "typelevel-prelude": ">=7.0.0 <8.0.0"
@@ -63,7 +72,7 @@
6372 "unsafe-coerce": ">=6.0.0 <7.0.0"
6473 },
6574 {
66− "yoga-json": ">=5.2.0 <6.0.0"
75+ "yoga-json": ">=5.2.1 <6.0.0"
6776 },
6877 {
6978 "yoga-sql-types": ">=0.2.0 <0.3.0"
@@ -950,8 +959,8 @@
950959 },
951960 "yoga-json": {
952961 "type": "registry",
953− "version": "5.2.0",
954− "integrity": "sha256-M+kkz6E1Gf+C4bKnhqoBFIoVcc74loi3rLULePbB204=",
962+ "version": "5.2.1",
963+ "integrity": "sha256-12I7qIf27oKV/D/YRcHtGpN8n7GQ0Bm3uzvI4ZjOQrs=",
955964 "dependencies": [
956965 "arrays",
957966 "bifunctors",
@@ -1018,8 +1027,8 @@
10181027 },
10191028 "yoga-test-docker": {
10201029 "type": "registry",
1021− "version": "0.1.1",
1022− "integrity": "sha256-BKyHoGahs1D5PbjqSKL+6vvUJQ62mbpXYJOF9UwaGb8=",
1030+ "version": "0.1.2",
1031+ "integrity": "sha256-J/jgHvvwb04ZOhicRGucmrQhaokrYTc9XJ2dutsktq8=",
10231032 "dependencies": [
10241033 "aff",
10251034 "datetime",
spago.yaml+6−3
@@ -3,7 +3,7 @@ workspace: {}
33package:
44 name: yoga-scylladb
55 publish:
6− version: 0.1.0
6+ version: 0.1.1
77 license: MIT
88 location:
99 githubOwner: rowtype-yoga
@@ -14,6 +14,7 @@ package:
1414 - datetime: ">=6.1.0 <7.0.0"
1515 - effect: ">=4.0.0 <5.0.0"
1616 - either: ">=6.1.0 <7.0.0"
17+ - exceptions: ">=6.1.0 <7.0.0"
1718 - foldable-traversable: ">=6.0.0 <7.0.0"
1819 - foreign: ">=7.0.0 <8.0.0"
1920 - foreign-object: ">=4.1.0 <5.0.0"
@@ -25,10 +26,12 @@ package:
2526 - nullable: ">=6.0.0 <7.0.0"
2627 - ordered-collections: ">=3.2.0 <4.0.0"
2728 - prelude: ">=6.0.2 <7.0.0"
28− - type-equality: ">=4.0.1 <5.0.0"
29+ - record: ">=4.0.0 <5.0.0"
30+ - strings: ">=6.0.1 <7.0.0"
31+ - transformers: ">=6.1.0 <7.0.0"
2932 - typelevel-prelude: ">=7.0.0 <8.0.0"
3033 - unsafe-coerce: ">=6.0.0 <7.0.0"
31− - yoga-json: ">=5.2.0 <6.0.0"
34+ - yoga-json: ">=5.2.1 <6.0.0"
3235 - yoga-sql-types: ">=0.2.0 <0.3.0"
3336 test:
3437 main: Test.Scylladb.Main
src/Yoga/ScyllaDB/Schema.purs+6−20
@@ -9,7 +9,6 @@ import Data.Either (Either(..))
99import Data.Foldable (foldl)
1010import Data.Map as Map
1111import Data.Maybe (Maybe(..))
12−import Data.Nullable (toNullable)
1312import Data.String.Regex as Regex
1413import Data.String.Regex.Flags as RegexFlags
1514import Data.Symbol (class IsSymbol, reflectSymbol)
@@ -486,22 +485,8 @@ else instance HasAnyDMLRL (RL.Cons "delete" Unit rest)
486485else instance HasAnyDMLRL rest => HasAnyDMLRL (RL.Cons label typ rest)
487486else instance Fail (Text "WHERE requires a preceding SELECT, UPDATE (set), or DELETE") => HasAnyDMLRL RL.Nil
488487
489−-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
490−-- FieldToCQLValue: convert a PureScript value to Foreign for CQL
491−-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
492−
493−class FieldToCQLValue :: Type -> Constraint
494−class FieldToCQLValue a where
495− fieldToCQLValue :: a -> Foreign
496−
497−instance FieldToCQLValue a => FieldToCQLValue (Maybe a) where
498− fieldToCQLValue = toNullable >>> unsafeCoerce
499−else instance FieldToCQLValue DateTime where
500− fieldToCQLValue = unsafeCoerce
501−else instance FieldToCQLValue Scylla.UUID where
502− fieldToCQLValue = unsafeCoerce
503−else instance FieldToCQLValue a where
504− fieldToCQLValue = unsafeCoerce
488+-- Values use the public Scylla.ToCQLValue extension point. Maybe must unwrap
489+-- through that instance so wrapped domain values keep their database encoding.
505490
506491-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
507492-- RecordValuesRL: walk a RowList and extract values from a record
@@ -517,12 +502,12 @@ instance RecordValuesRL RL.Nil row where
517502instance
518503 ( IsSymbol name
519504 , Row.Cons name typ rest row
520− , FieldToCQLValue typ
505+ , Scylla.ToCQLValue typ
521506 , RecordValuesRL tail row
522507 ) =>
523508 RecordValuesRL (RL.Cons name typ tail) row where
524509 recordValuesRL _ rec =
525− [ fieldToCQLValue (Record.get (Proxy :: Proxy name) rec) ]
510+ [ Scylla.toCQLValue (Record.get (Proxy :: Proxy name) rec) ]
526511 <> recordValuesRL (Proxy :: Proxy tail) rec
527512
528513-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -772,7 +757,8 @@ else instance
772757class ResolveParamType :: Symbol -> RL.RowList Type -> Type -> Constraint
773758class ResolveParamType name rl typ | name rl -> typ
774759
775−instance (ExtractType entry typ) => ResolveParamType name (RL.Cons name entry tail) typ
760+instance Fail (Text "Column not found in table") => ResolveParamType name RL.Nil typ
761+else instance (ExtractType entry typ) => ResolveParamType name (RL.Cons name entry tail) typ
776762else instance ResolveParamType name tail typ => ResolveParamType name (RL.Cons other entry tail) typ
777763
778764-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
src/Yoga/ScyllaDB/ScyllaDB.purs+11−2
@@ -3,9 +3,10 @@ module Yoga.ScyllaDB.ScyllaDB where
33import Prelude
44
55import Data.Array as Array
6−import Data.Maybe (Maybe)
6+import Data.DateTime (DateTime)
7+import Data.Maybe (Maybe(..))
78import Data.Newtype (class Newtype)
8−import Data.Nullable (Nullable)
9+import Data.Nullable (Nullable, toNullable)
910import Data.Nullable as Nullable
1011import Data.Time.Duration (Milliseconds)
1112import Effect (Effect)
@@ -307,6 +308,14 @@ instance ToCQLValue Foreign where
307308instance ToCQLValue UUID where
308309 toCQLValue = unsafeCoerce
309310
311+instance ToCQLValue DateTime where
312+ toCQLValue = unsafeCoerce
313+
314+instance ToCQLValue a => ToCQLValue (Maybe a) where
315+ toCQLValue = case _ of
316+ Nothing -> unsafeCoerce (toNullable Nothing)
317+ Just value -> toCQLValue value
318+
310319instance ToCQLValue a => ToCQLValue (Array a) where
311320 toCQLValue arr = unsafeCoerce (map toCQLValue arr)
312321
test/Yoga/ScyllaDB/Schema.test.jsnew file+2
@@ -0,0 +1,2 @@
1+export const foreignTypeName = (value) => value === null ? "null" : typeof value;
2+export const foreignStringValue = (value) => value;
src/Yoga/ScyllaDB/Schema.test.purs → test/Yoga/ScyllaDB/Schema.test.pursrenamed+30−2
@@ -2,17 +2,23 @@ module Yoga.ScyllaDB.Schema.Test where
22
33import Prelude
44
5−import Foreign (unsafeToForeign)
5+import Data.Array as Array
6+import Data.Maybe (Maybe(..), fromJust)
7+import Foreign (Foreign, unsafeToForeign)
8+import Partial.Unsafe (unsafePartial)
69import Unsafe.Coerce (unsafeCoerce)
710import Type.Function (type (#))
811import Type.Proxy (Proxy(..))
912import Data.DateTime (DateTime)
10−import Data.Maybe (Maybe)
1113import Test.Spec (Spec, describe, it)
1214import Test.Spec.Assertions (shouldEqual)
1315import Yoga.ScyllaDB.ScyllaDB as Scylla
1416import Yoga.ScyllaDB.Schema as S
1517
18+foreign import foreignTypeName :: Foreign -> String
19+foreign import foreignStringValue :: Foreign -> String
20+
21+
1622type UsersTable = S.Table "users"
1723 ( id :: Scylla.UUID # S.PartitionKey
1824 , name :: String
@@ -33,6 +39,15 @@ type EventsTable = S.Table "events"
3339 , value :: Number
3440 )
3541
42+newtype EncodedValue = EncodedValue String
43+
44+instance Scylla.ToCQLValue EncodedValue where
45+ toCQLValue _ = unsafeToForeign "encoded"
46+
47+type EncodedTable = S.Table "encoded"
48+ ( value :: Maybe EncodedValue
49+ )
50+
3651spec :: Spec Unit
3752spec = do
3853 describe "ScyllaDB Schema" do
@@ -112,6 +127,19 @@ spec = do
112127 it "maps Maybe Int to int" do
113128 S.cqlTypeName (Proxy :: Proxy (Maybe Int)) `shouldEqual` "int"
114129
130+ describe "ToCQLValue" do
131+
132+ it "unwraps Maybe through the inner CQL value conversion" do
133+ let encoded = Scylla.toCQLValue (Just (EncodedValue "raw") :: Maybe EncodedValue)
134+ foreignTypeName encoded `shouldEqual` "string"
135+ foreignStringValue encoded `shouldEqual` "encoded"
136+ foreignTypeName (Scylla.toCQLValue (Nothing :: Maybe EncodedValue)) `shouldEqual` "null"
137+
138+ it "uses ToCQLValue when extracting typed insert values" do
139+ let S.Q query = S.from (Proxy :: Proxy EncodedTable) # S.insert { value: Just (EncodedValue "raw") }
140+ Array.length query.values `shouldEqual` 1
141+ foreignStringValue (unsafePartial $ fromJust $ Array.head query.values) `shouldEqual` "encoded"
142+
115143 describe "Builder: SELECT" do
116144
117145 it "generates SELECT * with builder" do