Skip to content

Commit

Permalink
working but with bad tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bgaidioz committed Jan 17, 2024
1 parent 6dcc9b7 commit 39211c6
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 13 deletions.
2 changes: 1 addition & 1 deletion sql-client/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,6 @@ publishLocal := (publishLocal dependsOn Def.sequential(outputVersion, publishM2)
libraryDependencies ++= Seq(
rawClient % "compile->compile;test->test",
postgresqlDeps,
hikariCP) ++ truffleCompiler
hikariCP)

Compile / packageBin / packageOptions += Package.ManifestAttributes("Automatic-Module-Name" -> "raw.sql.client")
20 changes: 8 additions & 12 deletions sql-client/src/main/scala/raw/client/sql/SqlCompilerService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@
package raw.client.sql

import com.google.common.cache.{CacheBuilder, CacheLoader}
import org.graalvm.polyglot.{Context, HostAccess, Value}
import raw.client.api._
import raw.client.writers.{TypedPolyglotCsvWriter, TypedPolyglotJsonWriter}
import raw.client.sql.writers.{TypedResultSetCsvWriter, TypedResultSetJsonWriter}
import raw.utils.{AuthenticatedUser, RawSettings, RawUtils}

import java.io.{IOException, OutputStream}
import java.sql.{SQLException, SQLTimeoutException}
import java.sql.{ResultSet, SQLException, SQLTimeoutException}
import scala.collection.mutable

class SqlCompilerService(maybeClassLoader: Option[ClassLoader] = None)(implicit protected val settings: RawSettings)
Expand Down Expand Up @@ -106,12 +105,9 @@ class SqlCompilerService(maybeClassLoader: Option[ClassLoader] = None)(implicit
case Right(info) =>
try {
val tipe = info.outputType
val access = HostAccess.newBuilder().allowMapAccess(true).allowIteratorAccess(true).build()
val ctx = Context.newBuilder().allowHostAccess(access).build()
environment.maybeArguments.foreach(array => setParams(pstmt, array))
val r = pstmt.executeQuery()
val v = ctx.asValue(new ResultSetIterator(r, ctx))
render(environment, tipe, v, outputStream)
render(environment, tipe, r, outputStream)
} catch {
case e: SQLException => ExecutionRuntimeFailure(e.getMessage)
}
Expand All @@ -134,22 +130,22 @@ class SqlCompilerService(maybeClassLoader: Option[ClassLoader] = None)(implicit
private def render(
environment: ProgramEnvironment,
tipe: RawType,
v: Value,
v: ResultSet,
outputStream: OutputStream
): ExecutionResponse = {
environment.options
.get("output-format")
.map(_.toLowerCase) match {
case Some("csv") =>
if (!TypedPolyglotCsvWriter.outputWriteSupport(tipe)) {
if (!TypedResultSetCsvWriter.outputWriteSupport(tipe)) {
ExecutionRuntimeFailure("unsupported type")
}
val windowsLineEnding = environment.options.get("windows-line-ending") match {
case Some("true") => true
case _ => false //settings.config.getBoolean("raw.compiler.windows-line-ending")
}
val lineSeparator = if (windowsLineEnding) "\r\n" else "\n"
val csvWriter = new TypedPolyglotCsvWriter(outputStream, lineSeparator)
val csvWriter = new TypedResultSetCsvWriter(outputStream, lineSeparator)
try {
csvWriter.write(v, tipe)
ExecutionSuccess
Expand All @@ -159,10 +155,10 @@ class SqlCompilerService(maybeClassLoader: Option[ClassLoader] = None)(implicit
RawUtils.withSuppressNonFatalException(csvWriter.close())
}
case Some("json") =>
if (!TypedPolyglotJsonWriter.outputWriteSupport(tipe)) {
if (!TypedResultSetJsonWriter.outputWriteSupport(tipe)) {
ExecutionRuntimeFailure("unsupported type")
}
val w = new TypedPolyglotJsonWriter(outputStream)
val w = new TypedResultSetJsonWriter(outputStream)
try {
w.write(v, tipe)
ExecutionSuccess
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright 2023 RAW Labs S.A.
*
* Use of this software is governed by the Business Source License
* included in the file licenses/BSL.txt.
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0, included in the file
* licenses/APL.txt.
*/

package raw.client.sql.writers

import com.fasterxml.jackson.core.{JsonEncoding, JsonParser}
import com.fasterxml.jackson.dataformat.csv.CsvGenerator.Feature.STRICT_CHECK_FOR_QUOTING
import com.fasterxml.jackson.dataformat.csv.{CsvFactory, CsvSchema}
import raw.client.api._
import raw.utils.RecordFieldsNaming

import java.io.{IOException, OutputStream}
import java.sql.ResultSet
import java.time.format.DateTimeFormatter
import scala.annotation.tailrec

object TypedResultSetCsvWriter {

def outputWriteSupport(tipe: RawType): Boolean = tipe match {
case _: RawIterableType => true
case _: RawListType => true
case _ => false
}

}

class TypedResultSetCsvWriter(os: OutputStream, lineSeparator: String) {

final private val gen =
try {
val factory = new CsvFactory
factory.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE) // Don't close file descriptors automatically
factory.createGenerator(os, JsonEncoding.UTF8)
} catch {
case e: IOException => throw new RuntimeException(e)
}

private val schemaBuilder = CsvSchema.builder()
schemaBuilder.setColumnSeparator(',')
schemaBuilder.setUseHeader(true)
schemaBuilder.setLineSeparator(lineSeparator)
schemaBuilder.setQuoteChar('"')
schemaBuilder.setNullValue("")

final private val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
final private val timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS")
final private val timeFormatterNoMs = DateTimeFormatter.ofPattern("HH:mm:ss")
final private val timestampFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
final private val timestampFormatterNoMs = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")

@throws[IOException]
def write(resultSet: ResultSet, t: RawType): Unit = {
val RawIterableType(RawRecordType(atts, _, _), _, _) = t
val keys = new java.util.Vector[String]
atts.foreach(a => keys.add(a.idn))
val distincted = RecordFieldsNaming.makeDistinct(keys)
val columnNames = atts.map(_.idn)
for (colName <- columnNames) {
schemaBuilder.addColumn(colName)
}
gen.setSchema(schemaBuilder.build)
gen.enable(STRICT_CHECK_FOR_QUOTING)
while (resultSet.next()) {
for (i <- 0 until distincted.size()) {
writeValue(resultSet, i + 1, atts(i).tipe)
}
}
}

@throws[IOException]
@tailrec
private def writeValue(v: ResultSet, i: Int, t: RawType): Unit = {
if (t.nullable) {
if (v == null) gen.writeNull()
else writeValue(v, i, t.cloneNotNullable)
} else t match {
case _: RawBoolType => gen.writeBoolean(v.getBoolean(i))
case _: RawByteType => gen.writeNumber(v.getByte(i).toInt)
case _: RawShortType => gen.writeNumber(v.getShort(i).toInt)
case _: RawIntType => gen.writeNumber(v.getInt(i))
case _: RawLongType => gen.writeNumber(v.getLong(i))
case _: RawFloatType => gen.writeNumber(v.getFloat(i))
case _: RawDoubleType => gen.writeNumber(v.getDouble(i))
case _: RawDecimalType => gen.writeString(v.getBigDecimal(i).toString)
case _: RawStringType => gen.writeString(v.getString(i))
case _: RawDateType =>
val date = v.getDate(i).toLocalDate
gen.writeString(dateFormatter.format(date))
case _: RawTimeType =>
val time = v.getTime(i).toLocalTime
val formatter = if (time.getNano > 0) timeFormatter else timeFormatterNoMs
val formatted = formatter.format(time)
gen.writeString(formatted)
case _: RawTimestampType =>
val dateTime = v.getTimestamp(i).toLocalDateTime
val formatter = if (dateTime.getNano > 0) timestampFormatter else timestampFormatterNoMs
val formatted = formatter.format(dateTime)
gen.writeString(formatted)
case _ => throw new RuntimeException("unsupported type")
}
}

def close(): Unit = {
gen.close()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright 2023 RAW Labs S.A.
*
* Use of this software is governed by the Business Source License
* included in the file licenses/BSL.txt.
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0, included in the file
* licenses/APL.txt.
*/

package raw.client.sql.writers

import com.fasterxml.jackson.core.{JsonEncoding, JsonFactory, JsonParser}
import raw.client.api._
import raw.utils.RecordFieldsNaming

import java.io.{IOException, OutputStream}
import java.sql.ResultSet
import java.time.format.DateTimeFormatter
import scala.annotation.tailrec

object TypedResultSetJsonWriter {

def outputWriteSupport(tipe: RawType): Boolean = tipe match {
case _: RawIterableType => true
case _: RawListType => true
case _ => false
}

}

class TypedResultSetJsonWriter(os: OutputStream) {

final private val gen =
try {
val factory = new JsonFactory
factory.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE) // Don't close file descriptors automatically
factory.createGenerator(os, JsonEncoding.UTF8)
} catch {
case e: IOException => throw new RuntimeException(e)
}

final private val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
final private val timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS")
final private val timestampFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")

@throws[IOException]
def write(resultSet: ResultSet, t: RawType): Unit = {
val RawIterableType(RawRecordType(atts, _, _), _, _) = t
val keys = new java.util.Vector[String]
atts.foreach(a => keys.add(a.idn))
val distincted = RecordFieldsNaming.makeDistinct(keys)
gen.writeStartArray()
while (resultSet.next()) {
gen.writeStartObject()
for (i <- 0 until distincted.size()) {
val field = distincted.get(i)
val t = atts(i).tipe
gen.writeFieldName(field)
writeValue(resultSet, i + 1, t)
}
gen.writeEndObject()
}
gen.writeEndArray()
}

@throws[IOException]
@tailrec
private def writeValue(v: ResultSet, i: Int, t: RawType): Unit = {
if (t.nullable) {
if (v == null) gen.writeNull()
else writeValue(v, i, t.cloneNotNullable)
} else t match {
case _: RawBoolType => gen.writeBoolean(v.getBoolean(i))
case _: RawByteType => gen.writeNumber(v.getByte(i).toInt)
case _: RawShortType => gen.writeNumber(v.getShort(i).toInt)
case _: RawIntType => gen.writeNumber(v.getInt(i))
case _: RawLongType => gen.writeNumber(v.getLong(i))
case _: RawFloatType => gen.writeNumber(v.getFloat(i))
case _: RawDoubleType => gen.writeNumber(v.getDouble(i))
case _: RawDecimalType => gen.writeString(v.getBigDecimal(i).toString)
case _: RawStringType => gen.writeString(v.getString(i))
case _: RawDateType =>
val date = v.getDate(i).toLocalDate
gen.writeString(dateFormatter.format(date))
case _: RawTimeType =>
val time = v.getTime(i).toLocalTime
val formatted = timeFormatter.format(time)
gen.writeString(formatted)
case _: RawTimestampType =>
val dateTime = v.getTimestamp(i).toLocalDateTime
val formatted = timestampFormatter.format(dateTime)
gen.writeString(formatted)
case _ => throw new RuntimeException("unsupported type")
}
}

def close(): Unit = {
gen.close()
}
}

0 comments on commit 39211c6

Please sign in to comment.