Skip to content

Commit

Permalink
Merge pull request #1443 from johnedquinn/kow-reader
Browse files Browse the repository at this point in the history
Initializes PartiQLData and PartiQLValueLoader
  • Loading branch information
johnedquinn authored Apr 30, 2024
2 parents a0282cf + 676bf4b commit a9888b8
Show file tree
Hide file tree
Showing 46 changed files with 1,014 additions and 1,031 deletions.
21 changes: 13 additions & 8 deletions partiql-cli/src/main/kotlin/org/partiql/cli/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
package org.partiql.cli

import com.amazon.ion.system.IonReaderBuilder
import com.amazon.ion.system.IonTextWriterBuilder
import com.amazon.ionelement.api.ionListOf
import com.amazon.ionelement.api.ionNull
import com.amazon.ionelement.api.loadAllElements
import org.partiql.cli.io.Format
import org.partiql.cli.io.PartiQLCursorWriter
import org.partiql.cli.pipeline.Pipeline
import org.partiql.cli.shell.Shell
import org.partiql.eval.PartiQLEngine
Expand All @@ -32,7 +32,6 @@ import org.partiql.spi.connector.Connector
import org.partiql.spi.connector.sql.info.InfoSchema
import org.partiql.types.StaticType
import org.partiql.value.PartiQLValueExperimental
import org.partiql.value.toIon
import picocli.CommandLine
import java.io.File
import java.io.InputStream
Expand Down Expand Up @@ -77,7 +76,8 @@ internal class Version : CommandLine.IVersionProvider {
],
showDefaultValues = true
)
internal class MainCommand() : Runnable {
internal class MainCommand : Runnable {
// TODO: Need to add tests to CLI. All tests were removed in the same commit as this TODO. See Git blame.

internal companion object {
private const val SHEBANG_PREFIX = "#!"
Expand All @@ -95,6 +95,13 @@ internal class MainCommand() : Runnable {
)
var strict: Boolean = false

@CommandLine.Option(
names = ["--debug"],
description = ["THIS IS FOR INTERNAL DEVELOPMENT USE ONLY. Shows typing information in the output."],
hidden = true
)
var debug: Boolean = false

@CommandLine.Option(
names = ["-f", "--format"],
description = ["The data format, using the form <input>[:<output>]."],
Expand Down Expand Up @@ -150,7 +157,7 @@ internal class MainCommand() : Runnable {
true -> Pipeline.strict()
else -> Pipeline.default()
}
Shell(pipeline, session()).start()
Shell(pipeline, session(), debug).start()
}

@OptIn(PartiQLValueExperimental::class)
Expand All @@ -167,10 +174,8 @@ internal class MainCommand() : Runnable {
error(result.cause.stackTrace)
}
is PartiQLResult.Value -> {
// TODO handle output format
val ion = result.value.toIon()
val writer = IonTextWriterBuilder.pretty().build(System.out as Appendable)
ion.writeTo(writer)
val writer = PartiQLCursorWriter(System.out, debug)
writer.append(result.value)
println()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

package org.partiql.cli.format


// internal object ExplainFormatter {
//
// internal fun format(result: PartiQLResult.Explain.Domain): String {
Expand Down
213 changes: 213 additions & 0 deletions partiql-cli/src/main/kotlin/org/partiql/cli/io/PartiQLCursorWriter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package org.partiql.cli.io

import org.partiql.value.PartiQLCursor
import org.partiql.value.PartiQLValueExperimental
import org.partiql.value.PartiQLValueType
import org.partiql.value.datetime.Date
import org.partiql.value.datetime.Time
import org.partiql.value.datetime.TimeZone
import org.partiql.value.datetime.Timestamp
import kotlin.math.abs

/**
* Aids in appending [PartiQLCursor] to [out].
*
* Prints in a human-readable fashion. Indents appropriately. Example output:
* ```
* partiql ▶ SELECT VALUE { 'a': { 'b': t } } FROM <<1, 2>> AS t
* |
* <<
* {
* 'a': {
* 'b': 1
* }
* },
* {
* 'a': {
* 'b': 2
* }
* }
* >>
* ```
*
* @param debug specifies whether to also output typing information; if set to true, values will have their types prefixed
* to the output; for example: `int32::512`, `string::'hello, world!'`, and `null.int64`; if set to false, values will
* be printed as-is; for example: `512`, `'hello, world!'`, and `null`.
*/
class PartiQLCursorWriter(
private val out: Appendable,
private val debug: Boolean = false
) {

/**
* Determines how much to indent
*/
private var indent = 0

@OptIn(PartiQLValueExperimental::class)
fun append(data: PartiQLCursor) {
for (element in data) {
writeValue(data, element)
}
}

@OptIn(PartiQLValueExperimental::class)
private fun writeValue(data: PartiQLCursor, element: PartiQLValueType) {
when (element) {
PartiQLValueType.ANY -> error("This shouldn't have happened.")
PartiQLValueType.BOOL -> writeScalar(data, "bool", PartiQLCursor::getBoolValue)
PartiQLValueType.INT8 -> writeScalar(data, "int8", PartiQLCursor::getInt8Value)
PartiQLValueType.INT16 -> writeScalar(data, "int16", PartiQLCursor::getInt16Value)
PartiQLValueType.INT32 -> writeScalar(data, "int32", PartiQLCursor::getInt32Value)
PartiQLValueType.INT64 -> writeScalar(data, "int64", PartiQLCursor::getInt64Value)
PartiQLValueType.INT -> writeScalar(data, "int", PartiQLCursor::getIntValue)
PartiQLValueType.DECIMAL -> writeScalar(data, "decimal", PartiQLCursor::getDecimalValue)
PartiQLValueType.DECIMAL_ARBITRARY -> writeScalar(data, "decimal_arbitrary", PartiQLCursor::getDecimalArbitraryValue)
PartiQLValueType.FLOAT32 -> writeScalar(data, "float32", PartiQLCursor::getFloat32Value)
PartiQLValueType.FLOAT64 -> writeScalar(data, "float64", PartiQLCursor::getFloat64Value)
PartiQLValueType.CHAR -> writeScalar(data, "char", PartiQLCursor::getCharValue)
PartiQLValueType.STRING -> writeScalar(data, "string", PartiQLCursor::getStringValue)
PartiQLValueType.SYMBOL -> writeScalar(data, "symbol", PartiQLCursor::getSymbolValue)
PartiQLValueType.BINARY -> writeScalar(data, "binary", PartiQLCursor::getBinaryValue)
PartiQLValueType.BYTE -> writeScalar(data, "byte", PartiQLCursor::getByteValue)
PartiQLValueType.BLOB -> writeScalar(data, "blob", PartiQLCursor::getBlobValue)
PartiQLValueType.CLOB -> writeScalar(data, "clob", PartiQLCursor::getClobValue)
PartiQLValueType.DATE -> writeScalar(data, "date") { it.dateValue.getLiteralString() }
PartiQLValueType.TIME -> writeScalar(data, "time") { it.timeValue.getLiteralString() }
PartiQLValueType.TIMESTAMP -> writeScalar(data, "timestamp") { it.timestampValue.getLiteralString() }
PartiQLValueType.INTERVAL -> writeScalar(data, "interval", PartiQLCursor::getIntervalValue)
PartiQLValueType.BAG -> writeCollection(data, "bag", "<<", ">>", named = false)
PartiQLValueType.LIST -> writeCollection(data, "list", "[", "]", named = false)
PartiQLValueType.SEXP -> writeCollection(data, "sexp", "(", ")", named = false)
PartiQLValueType.STRUCT -> writeCollection(data, "struct", "{", "}", named = true)
PartiQLValueType.NULL -> writeScalar(data, "null") { d -> "null".also { assert(d.isNullValue) } }
PartiQLValueType.MISSING -> writeScalar(data, "missing") { d -> "missing".also { assert(d.isMissingValue) } }
}
}

@OptIn(PartiQLValueExperimental::class)
private fun writeCollection(data: PartiQLCursor, type: String, prefix: String, postfix: String, named: Boolean) {
if (appendPotentialNullValue(data, type)) {
return
}
// Print value prefix (AKA: << for bag, [ for list, or ( for s-exp)
appendTypePrefixIfDebugEnabled(type)
out.appendLine(prefix)

// Print children
stepIn(data)
for (child in data) {
out.append(buildIndent())
if (named) {
val fieldName = data.fieldName
out.append('\'')
out.append(fieldName)
out.append('\'')
out.append(": ")
}
writeValue(data, child)
when (data.hasNext()) {
true -> out.appendLine(",")
false -> out.appendLine()
}
}
stepOut(data)

// Print value postfix
out.append(buildIndent())
out.append(postfix)
}

private fun writeScalar(data: PartiQLCursor, type: String, transform: (PartiQLCursor) -> Any) {
if (appendPotentialNullValue(data, type)) {
return
}
appendTypePrefixIfDebugEnabled(type)
out.append(transform(data).toString())
}

private fun appendTypePrefixIfDebugEnabled(type: String) {
if (debug) {
out.append(type)
out.append("::")
}
}

/**
* @return true if the value was null and [out] was appended to; false if the value was not null and [out] was
* not appended to.
*/
private fun appendPotentialNullValue(data: PartiQLCursor, type: String): Boolean {
if (data.isNullValue) {
out.append("null")
// Print out the type of the null. AKA: null.bag
if (debug) {
out.append('.')
out.append(type)
}
return true
}
return false
}

private fun stepIn(data: PartiQLCursor) {
data.stepIn()
indent++
}

private fun stepOut(data: PartiQLCursor) {
data.stepOut()
indent--
}

private fun buildIndent(): String {
var prefix = ""
for (i in 1..indent) {
prefix += " "
}
return prefix
}

private fun Time.getLiteralString(): String {
val tz = this.timeZone.getTypeString()
return "TIME $tz '${this.hour}:${this.minute}:${this.decimalSecond}'"
}

private fun Date.getLiteralString(): String {
val dateString = "${this.year.pad(4)}-${this.month.pad()}-${this.day.pad()}"
return "DATE '$dateString'"
}

private fun Timestamp.getLiteralString(): String {
val tz = this.timeZone.getTypeString()
val dateString = "${this.year.pad(4)}-${this.month.pad()}-${this.day.pad()}"
val timeString = "${this.hour.pad()}:${this.minute.pad()}:${this.decimalSecond}"
val tzLiteral = this.timeZone.getLiteralTimeZoneString()
return "TIMESTAMP $tz '$dateString $timeString$tzLiteral'"
}

private fun TimeZone?.getTypeString() = when (this) {
null -> "WITHOUT TIME ZONE"
is TimeZone.UnknownTimeZone -> "WITH UNKNOWN TIME ZONE"
is TimeZone.UtcOffset -> "WITH TIME ZONE"
}

private fun TimeZone?.getLiteralTimeZoneString() = when (this) {
null -> ""
is TimeZone.UnknownTimeZone -> "-00:00"
is TimeZone.UtcOffset -> {
val sign = when (this.totalOffsetMinutes >= 0) {
true -> "+"
false -> "-"
}
val offset = abs(this.totalOffsetMinutes)
val hours = offset.div(60)
val minutes = offset - (hours * 60)
"$sign${hours.pad()}:${minutes.pad()}"
}
}

private fun Int.pad(length: Int = 2): String {
return this.toString().padStart(length, '0')
}
}
10 changes: 6 additions & 4 deletions partiql-cli/src/main/kotlin/org/partiql/cli/shell/Shell.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package org.partiql.cli.shell

import com.amazon.ion.system.IonSystemBuilder
import com.amazon.ion.system.IonTextWriterBuilder
import com.amazon.ionelement.api.toIonValue
import com.google.common.util.concurrent.Uninterruptibles
import org.fusesource.jansi.AnsiConsole
Expand All @@ -32,6 +31,7 @@ import org.jline.utils.AttributedStyle
import org.jline.utils.AttributedStyle.BOLD
import org.jline.utils.InfoCmp
import org.joda.time.Duration
import org.partiql.cli.io.PartiQLCursorWriter
import org.partiql.cli.pipeline.Pipeline
import org.partiql.eval.PartiQLResult
import org.partiql.plugins.fs.toIon
Expand All @@ -40,8 +40,6 @@ import org.partiql.spi.BindingName
import org.partiql.spi.BindingPath
import org.partiql.spi.connector.ConnectorHandle
import org.partiql.value.PartiQLValueExperimental
import org.partiql.value.io.PartiQLValueTextWriter
import software.amazon.ion.IonSystem
import java.io.Closeable
import java.io.PrintStream
import java.nio.file.Path
Expand Down Expand Up @@ -119,9 +117,13 @@ val exiting = AtomicBoolean(false)
val doneCompiling = AtomicBoolean(true)
val donePrinting = AtomicBoolean(true)

/**
* @param debug specifies whether to print typing information or not.
*/
internal class Shell(
private val pipeline: Pipeline,
private val session: Pipeline.Session,
private val debug: Boolean
) {

private var state: State = State(false)
Expand Down Expand Up @@ -308,7 +310,7 @@ internal class Shell(
when (result) {
is PartiQLResult.Error -> throw result.cause
is PartiQLResult.Value -> {
val writer = PartiQLValueTextWriter(out)
val writer = PartiQLCursorWriter(out, debug)
writer.append(result.value)
out.appendLine()
out.appendLine()
Expand Down
Loading

0 comments on commit a9888b8

Please sign in to comment.