diff --git a/README.md b/README.md index e4f545e0b..27584e922 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,52 @@ TODO: details ## Printing queries -TODO: details +### Select + +```scala +select(orderId, name) + .from(products.join(orders) + .on(productId === id)) + .limit(5) + .offset(10) + .show +// val res0: String = SELECT "order"."id", "products"."name" FROM "products" INNER JOIN "order" ON "order"."product_id" = "products"."id" LIMIT 5 OFFSET 10 +``` + +### Insert + +```scala +def insertProduct(uuid: UUID) = + insertInto(products)(id, name, price) + .values((uuid, "Zionomicon", 10.5)) + +insertProduct(UUID.fromString("dd5a7ae7-de19-446a-87a4-576d79de5c83")).show +// val res0: String = INSERT INTO "products" ("id", "name", "price") VALUES (?, ?, ?); +``` + +### Update + +```scala +def updateProduct(uuid: UUID) = + update(products) + .set(name, "foo") + .set(price, price * 1.1) + .where(id === uuid) + +updateProduct(UUID.fromString("f1e69839-964f-44b7-b90d-bd5f51700540")).show +// val res0: String = UPDATE "products" SET "name" = 'foo', "price" = "products"."price" * 1.1 WHERE "products"."id" = 'f1e69839-964f-44b7-b90d-bd5f51700540' +``` + +### Delete + +```scala +def deleteProduct(uuid: UUID) = + deleteFrom(products) + .where(id === uuid) + +deleteProduct(UUID.fromString("95625b37-e785-4b4f-86b1-69affaf5f848")).show +// val res0: String = DELETE FROM "products" WHERE "products"."id" = '95625b37-e785-4b4f-86b1-69affaf5f848' +``` ## Running queries diff --git a/core/jvm/src/main/scala/zio/sql/Sql.scala b/core/jvm/src/main/scala/zio/sql/Sql.scala index 7e1b9cd67..463003597 100644 --- a/core/jvm/src/main/scala/zio/sql/Sql.scala +++ b/core/jvm/src/main/scala/zio/sql/Sql.scala @@ -54,6 +54,22 @@ trait Sql { def renderInsert[A: Schema](insert: Insert[_, A]): SqlStatement + implicit class readOps(read: Read[_]) { + def show: String = renderRead(read) + } + + implicit class updateOps(update: Update[_]) { + def show: String = renderUpdate(update) + } + + implicit class insertOps[A: Schema](insert: Insert[_, A]) { + def show: String = renderInsert(insert).query + } + + implicit class deleteOps(delete: Delete[_]) { + def show: String = renderDelete(delete) + } + // TODO don't know where to put it now implicit def convertOptionToSome[A](implicit op: Schema[Option[A]]): Schema[Some[A]] = op.transformOrFail[Some[A]]( diff --git a/mysql/src/test/scala/zio/sql/mysql/MySqlQueryShowSpec.scala b/mysql/src/test/scala/zio/sql/mysql/MySqlQueryShowSpec.scala new file mode 100644 index 000000000..ae5f40504 --- /dev/null +++ b/mysql/src/test/scala/zio/sql/mysql/MySqlQueryShowSpec.scala @@ -0,0 +1,86 @@ +package zio.sql.mysql + +import zio.Scope +import zio.schema.DeriveSchema +import zio.sql.table.Table._ +import zio.test._ + +import java.time._ +import java.util.UUID + +object MySqlQueryShowSpec extends ZIOSpecDefault with MysqlRenderModule { + final case class Product(id: UUID, name: String, price: Double) + + object Product { + implicit val productSchema = DeriveSchema.gen[Product] + val products = defineTableSmart[Product] + val (id, name, price) = products.columns + } + + final case class Order(id: UUID, productId: UUID, quantity: Int, orderDate: LocalDate) + + object Order { + implicit val orderSchema = DeriveSchema.gen[Order] + val orders = defineTable[Order] + val (orderId, productId, quantity, date) = orders.columns + } + + override def spec: Spec[TestEnvironment with Scope, Any] = suite("MySqlQueryShow")( + test("rendering select") { + import Order._ + import Product._ + + val selectQueryRender = + select(orderId, name) + .from( + products + .join(orders) + .on(productId === id) + ) + .limit(5) + .offset(10) + .show + + val expectedQuery = + "SELECT order.id, products.name FROM products INNER JOIN order ON order.product_id = products.id LIMIT 5 OFFSET 10" + + assertTrue(selectQueryRender == expectedQuery) + }, + test("rendering insert") { + import Product._ + + def insertProduct(uuid: UUID) = + insertInto(products)(id, name, price) + .values((uuid, "Zionomicon", 10.5)) + + val expectedQuery = "INSERT INTO products (id, name, price) VALUES (?, ?, ?);" + + assertTrue(insertProduct(UUID.fromString("dd5a7ae7-de19-446a-87a4-576d79de5c83")).show == expectedQuery) + }, + test("rendering update") { + import Product._ + + def updateProduct(uuid: UUID) = + update(products) + .set(name, "foo") + .set(price, price * 1.1) + .where(id === uuid) + + val expectedQuery = + "UPDATE products SET products.name = 'foo', products.price = products.price * 1.1 WHERE products.id = 'f1e69839-964f-44b7-b90d-bd5f51700540'" + + assertTrue(updateProduct(UUID.fromString("f1e69839-964f-44b7-b90d-bd5f51700540")).show == expectedQuery) + }, + test("rendering delete") { + import Product._ + + def deleteProduct(uuid: UUID) = + deleteFrom(products) + .where(id === uuid) + + val expectedQuery = "DELETE FROM products WHERE products.id = '95625b37-e785-4b4f-86b1-69affaf5f848'" + + assertTrue(deleteProduct(UUID.fromString("95625b37-e785-4b4f-86b1-69affaf5f848")).show == expectedQuery) + } + ) +} diff --git a/oracle/src/test/scala/zio/sql/oracle/OracleSqlQueryShowSpec.scala b/oracle/src/test/scala/zio/sql/oracle/OracleSqlQueryShowSpec.scala new file mode 100644 index 000000000..7f2ee7951 --- /dev/null +++ b/oracle/src/test/scala/zio/sql/oracle/OracleSqlQueryShowSpec.scala @@ -0,0 +1,86 @@ +package zio.sql.oracle + +import zio.Scope +import zio.schema.DeriveSchema +import zio.sql.table.Table._ +import zio.test._ + +import java.time._ +import java.util.UUID + +object OracleSqlQueryShowSpec extends ZIOSpecDefault with OracleRenderModule { + final case class Product(id: UUID, name: String, price: Double) + + object Product { + implicit val productSchema = DeriveSchema.gen[Product] + val products = defineTableSmart[Product] + val (id, name, price) = products.columns + } + + final case class Order(id: UUID, productId: UUID, quantity: Int, orderDate: LocalDate) + + object Order { + implicit val orderSchema = DeriveSchema.gen[Order] + val orders = defineTable[Order] + val (orderId, productId, quantity, date) = orders.columns + } + + override def spec: Spec[TestEnvironment with Scope, Any] = suite("OracleSqlQueryShow")( + test("rendering select") { + import Order._ + import Product._ + + val selectQueryRender = + select(orderId, name) + .from( + products + .join(orders) + .on(productId === id) + ) + .limit(5) + .offset(10) + .show + + val expectedQuery = + "SELECT order.id, products.name FROM products INNER JOIN order ON order.product_id = products.id WHERE rownum <= 5" + + assertTrue(selectQueryRender == expectedQuery) + }, + test("rendering insert") { + import Product._ + + def insertProduct(uuid: UUID) = + insertInto(products)(id, name, price) + .values((uuid, "Zionomicon", 10.5)) + + val expectedQuery = "INSERT INTO products (id, name, price) VALUES (?, ?, ?)" + + assertTrue(insertProduct(UUID.fromString("dd5a7ae7-de19-446a-87a4-576d79de5c83")).show == expectedQuery) + }, + test("rendering update") { + import Product._ + + def updateProduct(uuid: UUID) = + update(products) + .set(name, "foo") + .set(price, price * 1.1) + .where(id === uuid) + + val expectedQuery = + "UPDATE products SET products.name = N'foo', products.price = products.price * 1.1 WHERE products.id = 'f1e69839-964f-44b7-b90d-bd5f51700540'" + + assertTrue(updateProduct(UUID.fromString("f1e69839-964f-44b7-b90d-bd5f51700540")).show == expectedQuery) + }, + test("rendering delete") { + import Product._ + + def deleteProduct(uuid: UUID) = + deleteFrom(products) + .where(id === uuid) + + val expectedQuery = "DELETE FROM products WHERE products.id = '95625b37-e785-4b4f-86b1-69affaf5f848'" + + assertTrue(deleteProduct(UUID.fromString("95625b37-e785-4b4f-86b1-69affaf5f848")).show == expectedQuery) + } + ) +} diff --git a/postgres/src/test/scala/zio/sql/postgresql/PostgresSqlQueryShowSpec.scala b/postgres/src/test/scala/zio/sql/postgresql/PostgresSqlQueryShowSpec.scala new file mode 100644 index 000000000..ee4faa1ab --- /dev/null +++ b/postgres/src/test/scala/zio/sql/postgresql/PostgresSqlQueryShowSpec.scala @@ -0,0 +1,86 @@ +package zio.sql.postgresql + +import zio.Scope +import zio.schema.DeriveSchema +import zio.sql.table.Table._ +import zio.test._ + +import java.time._ +import java.util.UUID + +object PostgresSqlQueryShowSpec extends ZIOSpecDefault with PostgresRenderModule { + final case class Product(id: UUID, name: String, price: Double) + + object Product { + implicit val productSchema = DeriveSchema.gen[Product] + val products = defineTableSmart[Product] + val (id, name, price) = products.columns + } + + final case class Order(id: UUID, productId: UUID, quantity: Int, orderDate: LocalDate) + + object Order { + implicit val orderSchema = DeriveSchema.gen[Order] + val orders = defineTable[Order] + val (orderId, productId, quantity, date) = orders.columns + } + + override def spec: Spec[TestEnvironment with Scope, Any] = suite("PostgresSqlQueryShow")( + test("rendering select") { + import Order._ + import Product._ + + val selectQueryRender = + select(orderId, name) + .from( + products + .join(orders) + .on(productId === id) + ) + .limit(5) + .offset(10) + .show + + val expectedQuery = + "SELECT \"order\".\"id\", \"products\".\"name\" FROM \"products\" INNER JOIN \"order\" ON \"order\".\"product_id\" = \"products\".\"id\" LIMIT 5 OFFSET 10" + + assertTrue(selectQueryRender == expectedQuery) + }, + test("rendering insert") { + import Product._ + + def insertProduct(uuid: UUID) = + insertInto(products)(id, name, price) + .values((uuid, "Zionomicon", 10.5)) + + val expectedQuery = "INSERT INTO \"products\" (\"id\", \"name\", \"price\") VALUES (?, ?, ?);" + + assertTrue(insertProduct(UUID.fromString("dd5a7ae7-de19-446a-87a4-576d79de5c83")).show == expectedQuery) + }, + test("rendering update") { + import Product._ + + def updateProduct(uuid: UUID) = + update(products) + .set(name, "foo") + .set(price, price * 1.1) + .where(id === uuid) + + val expectedQuery = + "UPDATE \"products\" SET \"name\" = 'foo', \"price\" = \"products\".\"price\" * 1.1 WHERE \"products\".\"id\" = 'f1e69839-964f-44b7-b90d-bd5f51700540'" + + assertTrue(updateProduct(UUID.fromString("f1e69839-964f-44b7-b90d-bd5f51700540")).show == expectedQuery) + }, + test("rendering delete") { + import Product._ + + def deleteProduct(uuid: UUID) = + deleteFrom(products) + .where(id === uuid) + + val expectedQuery = "DELETE FROM \"products\" WHERE \"products\".\"id\" = '95625b37-e785-4b4f-86b1-69affaf5f848'" + + assertTrue(deleteProduct(UUID.fromString("95625b37-e785-4b4f-86b1-69affaf5f848")).show == expectedQuery) + } + ) +} diff --git a/sqlserver/src/test/scala/zio/sql/sqlserver/SqlServerQueryShowSpec.scala b/sqlserver/src/test/scala/zio/sql/sqlserver/SqlServerQueryShowSpec.scala new file mode 100644 index 000000000..214a67858 --- /dev/null +++ b/sqlserver/src/test/scala/zio/sql/sqlserver/SqlServerQueryShowSpec.scala @@ -0,0 +1,86 @@ +package zio.sql.sqlserver + +import zio.Scope +import zio.schema.DeriveSchema +import zio.sql.table.Table._ +import zio.test._ + +import java.time._ +import java.util.UUID + +object SqlServerQueryShowSpec extends ZIOSpecDefault with SqlServerRenderModule { + final case class Product(id: UUID, name: String, price: Double) + + object Product { + implicit val productSchema = DeriveSchema.gen[Product] + val products = defineTableSmart[Product] + val (id, name, price) = products.columns + } + + final case class Order(id: UUID, productId: UUID, quantity: Int, orderDate: LocalDate) + + object Order { + implicit val orderSchema = DeriveSchema.gen[Order] + val orders = defineTable[Order] + val (orderId, productId, quantity, date) = orders.columns + } + + override def spec: Spec[TestEnvironment with Scope, Any] = suite("SqlServerQueryShow")( + test("rendering select") { + import Order._ + import Product._ + + val selectQueryRender = + select(orderId, name) + .from( + products + .join(orders) + .on(productId === id) + ) + .limit(5) + .offset(10) + .show + + val expectedQuery = + "SELECT TOP 5 order.id, products.name FROM products inner join order on order.product_id = products.id " + + assertTrue(selectQueryRender == expectedQuery) + }, + test("rendering insert") { + import Product._ + + def insertProduct(uuid: UUID) = + insertInto(products)(id, name, price) + .values((uuid, "Zionomicon", 10.5)) + + val expectedQuery = "INSERT INTO products (id, name, price) VALUES (?, ?, ?);" + + assertTrue(insertProduct(UUID.fromString("dd5a7ae7-de19-446a-87a4-576d79de5c83")).show == expectedQuery) + }, + test("rendering update") { + import Product._ + + def updateProduct(uuid: UUID) = + update(products) + .set(name, "foo") + .set(price, price * 1.1) + .where(id === uuid) + + val expectedQuery = + "UPDATE products SET products.name = N'foo', products.price = products.price * 1.1 WHERE products.id = 'f1e69839-964f-44b7-b90d-bd5f51700540'" + + assertTrue(updateProduct(UUID.fromString("f1e69839-964f-44b7-b90d-bd5f51700540")).show == expectedQuery) + }, + test("rendering delete") { + import Product._ + + def deleteProduct(uuid: UUID) = + deleteFrom(products) + .where(id === uuid) + + val expectedQuery = "DELETE FROM products WHERE products.id = '95625b37-e785-4b4f-86b1-69affaf5f848'" + + assertTrue(deleteProduct(UUID.fromString("95625b37-e785-4b4f-86b1-69affaf5f848")).show == expectedQuery) + } + ) +}