Initial Development #1

Merged
danieljsummers merged 88 commits from v1-rc into main 2025-04-16 01:29:20 +00:00
2 changed files with 228 additions and 80 deletions
Showing only changes of commit 31817d5cec - Show all commits

View File

@ -254,7 +254,7 @@ object Query {
* @param docId The ID of the document (optional, used for type checking) * @param docId The ID of the document (optional, used for type checking)
* @return A query to determine document existence by ID * @return A query to determine document existence by ID
*/ */
fun <TKey> byId(tableName: String, docId: TKey?) = fun <TKey> byId(tableName: String, docId: TKey? = null) =
exists(tableName, Where.byId(docId = docId)) exists(tableName, Where.byId(docId = docId))
/** /**
@ -265,7 +265,7 @@ object Query {
* @param howMatched How fields should be compared (optional, defaults to ALL) * @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to determine document existence for the given fields * @return A query to determine document existence for the given fields
*/ */
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch?) = fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
exists(tableName, Where.byFields(fields, howMatched)) exists(tableName, Where.byFields(fields, howMatched))
/** /**

View File

@ -63,38 +63,50 @@ class QueryTest {
@DisplayName("Where.byFields generates multiple fields w/ default match (PostgreSQL)") @DisplayName("Where.byFields generates multiple fields w/ default match (PostgreSQL)")
fun whereByFieldsMultipleDefaultPostgres() { fun whereByFieldsMultipleDefaultPostgres() {
Configuration.connectionString = pg Configuration.connectionString = pg
assertEquals("data->>'1' = :one AND (data->>'2')::numeric = :two AND data->>'3' = :three", assertEquals(
"data->>'1' = :one AND (data->>'2')::numeric = :two AND data->>'3' = :three",
Query.Where.byFields( Query.Where.byFields(
listOf(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")))) listOf(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three"))
)
)
} }
@Test @Test
@DisplayName("Where.byFields generates multiple fields w/ default match (SQLite)") @DisplayName("Where.byFields generates multiple fields w/ default match (SQLite)")
fun whereByFieldsMultipleDefaultSQLite() { fun whereByFieldsMultipleDefaultSQLite() {
Configuration.connectionString = lite Configuration.connectionString = lite
assertEquals("data->>'1' = :one AND data->>'2' = :two AND data->>'3' = :three", assertEquals(
"data->>'1' = :one AND data->>'2' = :two AND data->>'3' = :three",
Query.Where.byFields( Query.Where.byFields(
listOf(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")))) listOf(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three"))
)
)
} }
@Test @Test
@DisplayName("Where.byFields generates multiple fields w/ ANY match (PostgreSQL)") @DisplayName("Where.byFields generates multiple fields w/ ANY match (PostgreSQL)")
fun whereByFieldsMultipleAnyPostgres() { fun whereByFieldsMultipleAnyPostgres() {
Configuration.connectionString = pg Configuration.connectionString = pg
assertEquals("data->>'1' = :one OR (data->>'2')::numeric = :two OR data->>'3' = :three", assertEquals(
"data->>'1' = :one OR (data->>'2')::numeric = :two OR data->>'3' = :three",
Query.Where.byFields( Query.Where.byFields(
listOf(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")), listOf(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")),
FieldMatch.ANY)) FieldMatch.ANY
)
)
} }
@Test @Test
@DisplayName("Where.byFields generates multiple fields w/ ANY match (SQLite)") @DisplayName("Where.byFields generates multiple fields w/ ANY match (SQLite)")
fun whereByFieldsMultipleAnySQLite() { fun whereByFieldsMultipleAnySQLite() {
Configuration.connectionString = lite Configuration.connectionString = lite
assertEquals("data->>'1' = :one OR data->>'2' = :two OR data->>'3' = :three", assertEquals(
"data->>'1' = :one OR data->>'2' = :two OR data->>'3' = :three",
Query.Where.byFields( Query.Where.byFields(
listOf(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")), listOf(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")),
FieldMatch.ANY)) FieldMatch.ANY
)
)
} }
@Test @Test
@ -201,34 +213,46 @@ class QueryTest {
@DisplayName("byFields generates default field query (PostgreSQL)") @DisplayName("byFields generates default field query (PostgreSQL)")
fun byFieldsMultipleDefaultPostgres() { fun byFieldsMultipleDefaultPostgres() {
Configuration.connectionString = pg Configuration.connectionString = pg
assertEquals("this WHERE data->>'a' = :the_a AND (data->>'b')::numeric = :b_value", assertEquals(
Query.byFields("this", listOf(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")))) "this WHERE data->>'a' = :the_a AND (data->>'b')::numeric = :b_value",
Query.byFields("this", listOf(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")))
)
} }
@Test @Test
@DisplayName("byFields generates default field query (SQLite)") @DisplayName("byFields generates default field query (SQLite)")
fun byFieldsMultipleDefaultSQLite() { fun byFieldsMultipleDefaultSQLite() {
Configuration.connectionString = lite Configuration.connectionString = lite
assertEquals("this WHERE data->>'a' = :the_a AND data->>'b' = :b_value", assertEquals(
Query.byFields("this", listOf(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")))) "this WHERE data->>'a' = :the_a AND data->>'b' = :b_value",
Query.byFields("this", listOf(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")))
)
} }
@Test @Test
@DisplayName("byFields generates ANY field query (PostgreSQL)") @DisplayName("byFields generates ANY field query (PostgreSQL)")
fun byFieldsMultipleAnyPostgres() { fun byFieldsMultipleAnyPostgres() {
Configuration.connectionString = pg Configuration.connectionString = pg
assertEquals("that WHERE data->>'a' = :the_a OR (data->>'b')::numeric = :b_value", assertEquals(
Query.byFields("that", listOf(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")), "that WHERE data->>'a' = :the_a OR (data->>'b')::numeric = :b_value",
FieldMatch.ANY)) Query.byFields(
"that", listOf(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")),
FieldMatch.ANY
)
)
} }
@Test @Test
@DisplayName("byFields generates ANY field query (SQLite)") @DisplayName("byFields generates ANY field query (SQLite)")
fun byFieldsMultipleAnySQLite() { fun byFieldsMultipleAnySQLite() {
Configuration.connectionString = lite Configuration.connectionString = lite
assertEquals("that WHERE data->>'a' = :the_a OR data->>'b' = :b_value", assertEquals(
Query.byFields("that", listOf(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")), "that WHERE data->>'a' = :the_a OR data->>'b' = :b_value",
FieldMatch.ANY)) Query.byFields(
"that", listOf(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")),
FieldMatch.ANY
)
)
} }
// ~~~ Definition ~~~ // ~~~ Definition ~~~
@ -236,8 +260,10 @@ class QueryTest {
@Test @Test
@DisplayName("Definition.ensureTableFor generates correctly") @DisplayName("Definition.ensureTableFor generates correctly")
fun ensureTableFor() = fun ensureTableFor() =
assertEquals("CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)", assertEquals(
Query.Definition.ensureTableFor("my.table", "JSONB"), "CREATE TABLE statement not constructed correctly") "CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)",
Query.Definition.ensureTableFor("my.table", "JSONB"), "CREATE TABLE statement not constructed correctly"
)
@Test @Test
@DisplayName("Definition.ensureTable generates correctly (PostgreSQL)") @DisplayName("Definition.ensureTable generates correctly (PostgreSQL)")
@ -262,39 +288,50 @@ class QueryTest {
@Test @Test
@DisplayName("Definition.ensureKey generates correctly with schema") @DisplayName("Definition.ensureKey generates correctly with schema")
fun ensureKeyWithSchema() = fun ensureKeyWithSchema() =
assertEquals("CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'id'))", assertEquals(
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'id'))",
Query.Definition.ensureKey("test.table", Dialect.POSTGRESQL), Query.Definition.ensureKey("test.table", Dialect.POSTGRESQL),
"CREATE INDEX for key statement with schema not constructed correctly") "CREATE INDEX for key statement with schema not constructed correctly"
)
@Test @Test
@DisplayName("Definition.ensureKey generates correctly without schema") @DisplayName("Definition.ensureKey generates correctly without schema")
fun ensureKeyWithoutSchema() = fun ensureKeyWithoutSchema() =
assertEquals("CREATE UNIQUE INDEX IF NOT EXISTS idx_${tbl}_key ON $tbl ((data->>'id'))", assertEquals(
"CREATE UNIQUE INDEX IF NOT EXISTS idx_${tbl}_key ON $tbl ((data->>'id'))",
Query.Definition.ensureKey(tbl, Dialect.SQLITE), Query.Definition.ensureKey(tbl, Dialect.SQLITE),
"CREATE INDEX for key statement without schema not constructed correctly") "CREATE INDEX for key statement without schema not constructed correctly"
)
@Test @Test
@DisplayName("Definition.ensureIndexOn generates multiple fields and directions") @DisplayName("Definition.ensureIndexOn generates multiple fields and directions")
fun ensureIndexOnMultipleFields() = fun ensureIndexOnMultipleFields() =
assertEquals( assertEquals(
"CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table ((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)", "CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table ((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)",
Query.Definition.ensureIndexOn("test.table", "gibberish", listOf("taco", "guac DESC", "salsa ASC"), Query.Definition.ensureIndexOn(
Dialect.POSTGRESQL), "test.table", "gibberish", listOf("taco", "guac DESC", "salsa ASC"),
"CREATE INDEX for multiple field statement not constructed correctly") Dialect.POSTGRESQL
),
"CREATE INDEX for multiple field statement not constructed correctly"
)
@Test @Test
@DisplayName("Definition.ensureIndexOn generates nested PostgreSQL field") @DisplayName("Definition.ensureIndexOn generates nested PostgreSQL field")
fun ensureIndexOnNestedPostgres() = fun ensureIndexOnNestedPostgres() =
assertEquals("CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data#>>'{a,b,c}'))", assertEquals(
"CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data#>>'{a,b,c}'))",
Query.Definition.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.POSTGRESQL), Query.Definition.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.POSTGRESQL),
"CREATE INDEX for nested PostgreSQL field incorrect") "CREATE INDEX for nested PostgreSQL field incorrect"
)
@Test @Test
@DisplayName("Definition.ensureIndexOn generates nested SQLite field") @DisplayName("Definition.ensureIndexOn generates nested SQLite field")
fun ensureIndexOnNestedSQLite() = fun ensureIndexOnNestedSQLite() =
assertEquals("CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data->'a'->'b'->>'c'))", assertEquals(
"CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data->'a'->'b'->>'c'))",
Query.Definition.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.SQLITE), Query.Definition.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.SQLITE),
"CREATE INDEX for nested SQLite field incorrect") "CREATE INDEX for nested SQLite field incorrect"
)
// ~~~ root functions ~~~ // ~~~ root functions ~~~
@ -319,7 +356,8 @@ class QueryTest {
assertEquals( assertEquals(
"INSERT INTO $tbl VALUES (:data::jsonb || ('{\"id\":' " + "INSERT INTO $tbl VALUES (:data::jsonb || ('{\"id\":' " +
"|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $tbl) || '}')::jsonb)", "|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $tbl) || '}')::jsonb)",
Query.insert(tbl, AutoId.NUMBER)) Query.insert(tbl, AutoId.NUMBER)
)
} }
@Test @Test
@ -329,7 +367,8 @@ class QueryTest {
assertEquals( assertEquals(
"INSERT INTO $tbl VALUES (json_set(:data, '$.id', " + "INSERT INTO $tbl VALUES (json_set(:data, '$.id', " +
"(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $tbl)))", "(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $tbl)))",
Query.insert(tbl, AutoId.NUMBER)) Query.insert(tbl, AutoId.NUMBER)
)
} }
@Test @Test
@ -337,8 +376,10 @@ class QueryTest {
fun insertAutoUUIDPostgres() { fun insertAutoUUIDPostgres() {
Configuration.connectionString = pg Configuration.connectionString = pg
val query = Query.insert(tbl, AutoId.UUID) val query = Query.insert(tbl, AutoId.UUID)
assertTrue(query.startsWith("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\""), assertTrue(
"Query start not correct (actual: $query)") query.startsWith("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\""),
"Query start not correct (actual: $query)"
)
assertTrue(query.endsWith("\"}')"), "Query end not correct") assertTrue(query.endsWith("\"}')"), "Query end not correct")
} }
@ -347,8 +388,10 @@ class QueryTest {
fun insertAutoUUIDSQLite() { fun insertAutoUUIDSQLite() {
Configuration.connectionString = lite Configuration.connectionString = lite
val query = Query.insert(tbl, AutoId.UUID) val query = Query.insert(tbl, AutoId.UUID)
assertTrue(query.startsWith("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '"), assertTrue(
"Query start not correct (actual: $query)") query.startsWith("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '"),
"Query start not correct (actual: $query)"
)
assertTrue(query.endsWith("'))"), "Query end not correct") assertTrue(query.endsWith("'))"), "Query end not correct")
} }
@ -359,12 +402,16 @@ class QueryTest {
Configuration.connectionString = pg Configuration.connectionString = pg
Configuration.idStringLength = 8 Configuration.idStringLength = 8
val query = Query.insert(tbl, AutoId.RANDOM_STRING) val query = Query.insert(tbl, AutoId.RANDOM_STRING)
assertTrue(query.startsWith("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\""), assertTrue(
"Query start not correct (actual: $query)") query.startsWith("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\""),
"Query start not correct (actual: $query)"
)
assertTrue(query.endsWith("\"}')"), "Query end not correct") assertTrue(query.endsWith("\"}')"), "Query end not correct")
assertEquals(8, assertEquals(
8,
query.replace("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\"", "").replace("\"}')", "").length, query.replace("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\"", "").replace("\"}')", "").length,
"Random string length incorrect") "Random string length incorrect"
)
} finally { } finally {
Configuration.idStringLength = 16 Configuration.idStringLength = 16
} }
@ -375,12 +422,16 @@ class QueryTest {
fun insertAutoRandomSQLite() { fun insertAutoRandomSQLite() {
Configuration.connectionString = lite Configuration.connectionString = lite
val query = Query.insert(tbl, AutoId.RANDOM_STRING) val query = Query.insert(tbl, AutoId.RANDOM_STRING)
assertTrue(query.startsWith("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '"), assertTrue(
"Query start not correct (actual: $query)") query.startsWith("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '"),
"Query start not correct (actual: $query)"
)
assertTrue(query.endsWith("'))"), "Query end not correct") assertTrue(query.endsWith("'))"), "Query end not correct")
assertEquals(Configuration.idStringLength, assertEquals(
Configuration.idStringLength,
query.replace("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '", "").replace("'))", "").length, query.replace("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '", "").replace("'))", "").length,
"Random string length incorrect") "Random string length incorrect"
)
} }
@Test @Test
@ -395,7 +446,8 @@ class QueryTest {
Configuration.connectionString = pg Configuration.connectionString = pg
assertEquals( assertEquals(
"INSERT INTO $tbl VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data", "INSERT INTO $tbl VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data",
Query.save(tbl), "INSERT ON CONFLICT UPDATE statement not constructed correctly") Query.save(tbl), "INSERT ON CONFLICT UPDATE statement not constructed correctly"
)
} }
@Test @Test
@ -407,26 +459,32 @@ class QueryTest {
@DisplayName("Count.byFields generates correctly (PostgreSQL)") @DisplayName("Count.byFields generates correctly (PostgreSQL)")
fun countByFieldsPostgres() { fun countByFieldsPostgres() {
Configuration.connectionString = pg Configuration.connectionString = pg
assertEquals("SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0", assertEquals(
"SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0",
Query.Count.byFields(tbl, listOf(Field.equal("test", "", ":field0"))), Query.Count.byFields(tbl, listOf(Field.equal("test", "", ":field0"))),
"Count query not constructed correctly") "Count query not constructed correctly"
)
} }
@Test @Test
@DisplayName("Count.byFields generates correctly (PostgreSQL)") @DisplayName("Count.byFields generates correctly (PostgreSQL)")
fun countByFieldsSQLite() { fun countByFieldsSQLite() {
Configuration.connectionString = lite Configuration.connectionString = lite
assertEquals("SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0", assertEquals(
"SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0",
Query.Count.byFields(tbl, listOf(Field.equal("test", "", ":field0"))), Query.Count.byFields(tbl, listOf(Field.equal("test", "", ":field0"))),
"Count query not constructed correctly") "Count query not constructed correctly"
)
} }
@Test @Test
@DisplayName("Count.byContains generates correctly (PostgreSQL)") @DisplayName("Count.byContains generates correctly (PostgreSQL)")
fun countByContainsPostgres() { fun countByContainsPostgres() {
Configuration.connectionString = pg Configuration.connectionString = pg
assertEquals("SELECT COUNT(*) AS it FROM $tbl WHERE data @> :criteria", Query.Count.byContains(tbl), assertEquals(
"Count query not constructed correctly") "SELECT COUNT(*) AS it FROM $tbl WHERE data @> :criteria", Query.Count.byContains(tbl),
"Count query not constructed correctly"
)
} }
@Test @Test
@ -440,8 +498,10 @@ class QueryTest {
@DisplayName("Count.byJsonPath generates correctly (PostgreSQL)") @DisplayName("Count.byJsonPath generates correctly (PostgreSQL)")
fun countByJsonPathPostgres() { fun countByJsonPathPostgres() {
Configuration.connectionString = pg Configuration.connectionString = pg
assertEquals("SELECT COUNT(*) AS it FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)", assertEquals(
Query.Count.byJsonPath(tbl), "Count query not constructed correctly") "SELECT COUNT(*) AS it FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)",
Query.Count.byJsonPath(tbl), "Count query not constructed correctly"
)
} }
@Test @Test
@ -451,11 +511,81 @@ class QueryTest {
assertThrows<DocumentException> { Query.Count.byJsonPath(tbl) } assertThrows<DocumentException> { Query.Count.byJsonPath(tbl) }
} }
// @Test @Test
// @DisplayName("exists generates correctly") @DisplayName("Exists.byId generates correctly (PostgreSQL)")
// fun exists() = fun existsByIdPostgres() {
// assertEquals("SELECT EXISTS (SELECT 1 FROM $tbl WHERE turkey) AS it", Query.exists(tbl, "turkey"), Configuration.connectionString = pg
// "Exists query not constructed correctly") assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'id' = :id) AS it",
Query.Exists.byId<String>(tbl), "Exists query not constructed correctly"
)
}
@Test
@DisplayName("Exists.byId generates correctly (SQLite)")
fun existsByIdSQLite() {
Configuration.connectionString = lite
assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'id' = :id) AS it",
Query.Exists.byId<String>(tbl), "Exists query not constructed correctly"
)
}
@Test
@DisplayName("Exists.byFields generates correctly (PostgreSQL)")
fun existsByFieldsPostgres() {
Configuration.connectionString = pg
assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE (data->>'it')::numeric = :test) AS it",
Query.Exists.byFields(tbl, listOf(Field.equal("it", 7, ":test"))),
"Exists query not constructed correctly"
)
}
@Test
@DisplayName("Exists.byFields generates correctly (SQLite)")
fun existsByFieldsSQLite() {
Configuration.connectionString = lite
assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'it' = :test) AS it",
Query.Exists.byFields(tbl, listOf(Field.equal("it", 7, ":test"))),
"Exists query not constructed correctly"
)
}
@Test
@DisplayName("Exists.byContains generates correctly (PostgreSQL)")
fun existsByContainsPostgres() {
Configuration.connectionString = pg
assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data @> :criteria) AS it", Query.Exists.byContains(tbl),
"Exists query not constructed correctly"
)
}
@Test
@DisplayName("Exists.byContains fails (SQLite)")
fun existsByContainsSQLite() {
Configuration.connectionString = lite
assertThrows<DocumentException> { Query.Exists.byContains(tbl) }
}
@Test
@DisplayName("Exists.byJsonPath generates correctly (PostgreSQL)")
fun existsByJsonPathPostgres() {
Configuration.connectionString = pg
assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)) AS it",
Query.Exists.byJsonPath(tbl), "Exists query not constructed correctly"
)
}
@Test
@DisplayName("Exists.byJsonPath fails (SQLite)")
fun existsByJsonPathSQLite() {
Configuration.connectionString = lite
assertThrows<DocumentException> { Query.Exists.byJsonPath(tbl) }
}
@Test @Test
@DisplayName("Find.all generates correctly") @DisplayName("Find.all generates correctly")
@ -482,56 +612,74 @@ class QueryTest {
@Test @Test
@DisplayName("orderBy generates single, no direction for PostgreSQL") @DisplayName("orderBy generates single, no direction for PostgreSQL")
fun orderBySinglePostgres() = fun orderBySinglePostgres() =
assertEquals(" ORDER BY data->>'TestField'", assertEquals(
Query.orderBy(listOf(Field.named("TestField")), Dialect.POSTGRESQL), "ORDER BY not constructed correctly") " ORDER BY data->>'TestField'",
Query.orderBy(listOf(Field.named("TestField")), Dialect.POSTGRESQL), "ORDER BY not constructed correctly"
)
@Test @Test
@DisplayName("orderBy generates single, no direction for SQLite") @DisplayName("orderBy generates single, no direction for SQLite")
fun orderBySingleSQLite() = fun orderBySingleSQLite() =
assertEquals(" ORDER BY data->>'TestField'", Query.orderBy(listOf(Field.named("TestField")), Dialect.SQLITE), assertEquals(
"ORDER BY not constructed correctly") " ORDER BY data->>'TestField'", Query.orderBy(listOf(Field.named("TestField")), Dialect.SQLITE),
"ORDER BY not constructed correctly"
)
@Test @Test
@DisplayName("orderBy generates multiple with direction for PostgreSQL") @DisplayName("orderBy generates multiple with direction for PostgreSQL")
fun orderByMultiplePostgres() = fun orderByMultiplePostgres() =
assertEquals(" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC", assertEquals(
" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC",
Query.orderBy( Query.orderBy(
listOf(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")), listOf(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")),
Dialect.POSTGRESQL), Dialect.POSTGRESQL
"ORDER BY not constructed correctly") ),
"ORDER BY not constructed correctly"
)
@Test @Test
@DisplayName("orderBy generates multiple with direction for SQLite") @DisplayName("orderBy generates multiple with direction for SQLite")
fun orderByMultipleSQLite() = fun orderByMultipleSQLite() =
assertEquals(" ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC", assertEquals(
" ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC",
Query.orderBy( Query.orderBy(
listOf(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")), listOf(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")),
Dialect.SQLITE), Dialect.SQLITE
"ORDER BY not constructed correctly") ),
"ORDER BY not constructed correctly"
)
@Test @Test
@DisplayName("orderBy generates numeric ordering PostgreSQL") @DisplayName("orderBy generates numeric ordering PostgreSQL")
fun orderByNumericPostgres() = fun orderByNumericPostgres() =
assertEquals(" ORDER BY (data->>'Test')::numeric", assertEquals(
Query.orderBy(listOf(Field.named("n:Test")), Dialect.POSTGRESQL), "ORDER BY not constructed correctly") " ORDER BY (data->>'Test')::numeric",
Query.orderBy(listOf(Field.named("n:Test")), Dialect.POSTGRESQL), "ORDER BY not constructed correctly"
)
@Test @Test
@DisplayName("orderBy generates numeric ordering for SQLite") @DisplayName("orderBy generates numeric ordering for SQLite")
fun orderByNumericSQLite() = fun orderByNumericSQLite() =
assertEquals(" ORDER BY data->>'Test'", Query.orderBy(listOf(Field.named("n:Test")), Dialect.SQLITE), assertEquals(
"ORDER BY not constructed correctly") " ORDER BY data->>'Test'", Query.orderBy(listOf(Field.named("n:Test")), Dialect.SQLITE),
"ORDER BY not constructed correctly"
)
@Test @Test
@DisplayName("orderBy generates case-insensitive ordering for PostgreSQL") @DisplayName("orderBy generates case-insensitive ordering for PostgreSQL")
fun orderByCIPostgres() = fun orderByCIPostgres() =
assertEquals(" ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST", assertEquals(
" ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST",
Query.orderBy(listOf(Field.named("i:Test.Field DESC NULLS FIRST")), Dialect.POSTGRESQL), Query.orderBy(listOf(Field.named("i:Test.Field DESC NULLS FIRST")), Dialect.POSTGRESQL),
"ORDER BY not constructed correctly") "ORDER BY not constructed correctly"
)
@Test @Test
@DisplayName("orderBy generates case-insensitive ordering for SQLite") @DisplayName("orderBy generates case-insensitive ordering for SQLite")
fun orderByCISQLite() = fun orderByCISQLite() =
assertEquals(" ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST", assertEquals(
" ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST",
Query.orderBy(listOf(Field.named("i:Test.Field ASC NULLS LAST")), Dialect.SQLITE), Query.orderBy(listOf(Field.named("i:Test.Field ASC NULLS LAST")), Dialect.SQLITE),
"ORDER BY not constructed correctly") "ORDER BY not constructed correctly"
)
} }