diff --git a/build.sc b/build.sc index c8990c9..b0de745 100644 --- a/build.sc +++ b/build.sc @@ -7,7 +7,6 @@ import io.kipp.mill.ci.release.CiReleaseModule import ba.sake.millhepek.MillHepekModule object squery extends CommonScalaModule with SqueryPublishModule { - def ivyDeps = Agg( ivy"com.typesafe.scala-logging::scala-logging:3.9.4", ivy"com.github.jsqlparser:jsqlparser:4.7", @@ -27,9 +26,8 @@ object squery extends CommonScalaModule with SqueryPublishModule { } object docs extends CommonScalaModule with MillHepekModule { - def ivyDeps = Agg( - ivy"ba.sake::hepek:0.22.0" + ivy"ba.sake::hepek:0.24.1" ) } @@ -38,9 +36,7 @@ trait CommonScalaModule extends ScalaModule with ScalafmtModule { } trait SqueryPublishModule extends CiReleaseModule { - def artifactName = "squery" - def pomSettings = PomSettings( organization = "ba.sake", url = "https://github.com/sake92/squery", diff --git a/docs/src/files/howtos/HowToPage.scala b/docs/src/files/howtos/HowToPage.scala index 5940333..d26fa52 100644 --- a/docs/src/files/howtos/HowToPage.scala +++ b/docs/src/files/howtos/HowToPage.scala @@ -18,9 +18,11 @@ trait HowToPage extends DocPage { InsertRetGenKeys, InsertRetValues, Update, + InterpolateLiteralStrings, InterpolateValues, InterpolateQueries, - DynamicQueries + DynamicQueries, + Transactions, ) override def pageCategory = Some("How-Tos") diff --git a/docs/src/files/howtos/InsertRetGenKeys.scala b/docs/src/files/howtos/InsertRetGenKeys.scala index f83f0cc..ce80593 100644 --- a/docs/src/files/howtos/InsertRetGenKeys.scala +++ b/docs/src/files/howtos/InsertRetGenKeys.scala @@ -26,8 +26,8 @@ object InsertRetGenKeys extends HowToPage { There are also variations that return a single result, depending if you want an `Option[T]` or `T` (throws if no row returned): ```scala - sql"SELECT ...".insertReturningGenKey[T]() : Option[T] - sql"SELECT ...".insertReturningGenKey[T]() : T + sql"SELECT ...".insertReturningGenKeyOpt[T]() : Option[T] // first result, if present + sql"SELECT ...".insertReturningGenKey[T]() : T // first result, or exception ``` """.md diff --git a/docs/src/files/howtos/InterpolateLiteralStrings.scala b/docs/src/files/howtos/InterpolateLiteralStrings.scala new file mode 100644 index 0000000..a618afc --- /dev/null +++ b/docs/src/files/howtos/InterpolateLiteralStrings.scala @@ -0,0 +1,36 @@ +package files.howtos + +import utils.* +import Bundle.*, Tags.* + +object InterpolateLiteralStrings extends HowToPage { + + override def pageSettings = + super.pageSettings.withTitle("Interpolate literal strings") + + override def blogSettings = + super.blogSettings.withSections(firstSection) + + val firstSection = Section( + "How To Interpolate Literal Strings?", + s""" + You can use `inline val` (literal type) string values in your queries: + ```scala + inline val columns = "id, name, age" + sql${Consts.tq} + SELECT $${columns} + FROM customers + ${Consts.tq}.readRows[Customer]() + ``` + + This will **not** make a `?` parameter, it will directly insert the literal string. + Same as if you wrote this: + ```scala + sql${Consts.tq} + SELECT id, name, age + FROM customers + ${Consts.tq}.readRows[Customer]() + ``` + """.md + ) +} diff --git a/docs/src/files/howtos/InterpolateQueries.scala b/docs/src/files/howtos/InterpolateQueries.scala index d0d9b04..21162bc 100644 --- a/docs/src/files/howtos/InterpolateQueries.scala +++ b/docs/src/files/howtos/InterpolateQueries.scala @@ -21,7 +21,7 @@ object InterpolateQueries extends HowToPage { SELECT customer_id, name FROM customers $${whereQuery} - ${Consts.tq}.insert() + ${Consts.tq}.readRow[Customer]() ``` """.md ) diff --git a/docs/src/files/howtos/InterpolateValues.scala b/docs/src/files/howtos/InterpolateValues.scala index b93ce5c..e752526 100644 --- a/docs/src/files/howtos/InterpolateValues.scala +++ b/docs/src/files/howtos/InterpolateValues.scala @@ -23,6 +23,13 @@ object InterpolateValues extends HowToPage { VALUES($$customerId, $$phoneNumber) ${Consts.tq}.insert() ``` + + The final query that gets executed will look like this: + ```sql + INSERT INTO phones(customer_id, number) + VALUES(?, ?) + ``` + so it is injection-safe. """.md ) } diff --git a/docs/src/files/howtos/ReadMultiColRows.scala b/docs/src/files/howtos/ReadMultiColRows.scala index 045b123..2226500 100644 --- a/docs/src/files/howtos/ReadMultiColRows.scala +++ b/docs/src/files/howtos/ReadMultiColRows.scala @@ -32,10 +32,10 @@ object ReadMultiColRows extends HowToPage { --- - There are also variations that return a single result, depending if you want an `Option[T]` or `T` (throws if no row returned): + There are also variations that return a single result, depending if you want an `Option[T]` or `T`: ```scala - sql"SELECT ...".readRowOpt[T]() : Option[T] - sql"SELECT ...".readRow[T]() : T + sql"SELECT ...".readRowOpt[T]() : Option[T] // first result, if present + sql"SELECT ...".readRow[T]() : T // first result, or exception ``` """.md diff --git a/docs/src/files/howtos/ReadOneColRows.scala b/docs/src/files/howtos/ReadOneColRows.scala index 6a1988f..de2cf0a 100644 --- a/docs/src/files/howtos/ReadOneColRows.scala +++ b/docs/src/files/howtos/ReadOneColRows.scala @@ -26,10 +26,10 @@ object ReadOneColRows extends HowToPage { --- - There are also variations that return a single result, depending if you want an `Option[T]` or `T` (throws if no row returned): + There are also variations that return a single result, depending if you want an `Option[T]` or `T`: ```scala - sql"SELECT ...".readValueOpt[T]() : Option[T] - sql"SELECT ...".readValue[T]() : T + sql"SELECT ...".readValueOpt[T]() : Option[T] // first result, if present + sql"SELECT ...".readValue[T]() : T // first result, or exception ``` """.md ) diff --git a/docs/src/files/howtos/Transactions.scala b/docs/src/files/howtos/Transactions.scala new file mode 100644 index 0000000..9b768bc --- /dev/null +++ b/docs/src/files/howtos/Transactions.scala @@ -0,0 +1,45 @@ +package files.howtos + +import utils.* +import Bundle.*, Tags.* + +object Transactions extends HowToPage { + + override def pageSettings = + super.pageSettings.withTitle("Transactions") + + override def blogSettings = + super.blogSettings.withSections(firstSection) + + val firstSection = Section( + "How To Run Transactions?", + frag( + s""" + We use the `runTransaction` to run queries inside of a transaction: + ```scala + ctx.runTransaction { + sql" + INSERT INTO customers(name) + VALUES (1, 'abc') + ".insert() + sql" + INSERT INTO customers(name) + VALUES (1, 'def') + ".insert() + } + ``` + If one of the queries fails, the transaction will be rolled back, nothing will happen. + + --- + The `runTransaction` uses the *default JDBC driver* transaction isolation (depends on db). + If you want to explicitly set the transaction isolation you can use the `runTransactionWithIsolation` function: + ```scala + ctx.runTransactionWithIsolation(TransactionIsolation.Serializable) { + // queries here + } + ``` + + """.md + ) + ) +} diff --git a/squery/test/src/ba/sake/squery/PostgresSuite.scala b/squery/test/src/ba/sake/squery/PostgresSuite.scala index 62bc646..8111a32 100644 --- a/squery/test/src/ba/sake/squery/PostgresSuite.scala +++ b/squery/test/src/ba/sake/squery/PostgresSuite.scala @@ -306,14 +306,14 @@ class PostgresSuite extends munit.FunSuite { intercept[Exception] { ctx.runTransaction { sql""" - INSERT INTO test_transactions(name) - VALUES ('abc') - """.insert() + INSERT INTO test_transactions(name) + VALUES ('abc') + """.insert() // fail coz unique name sql""" - INSERT INTO test_transactions(name) - VALUES ('abc') - """.insert() + INSERT INTO test_transactions(name) + VALUES ('abc') + """.insert() } } intercept[Exception] {