Skip to content

Commit

Permalink
Mainframe Connector: user-specified charset (#901)
Browse files Browse the repository at this point in the history
* Mainframe Connector: increment version

* Mainframe Connector: accept user-specified charset from envvar and command-line arg
  • Loading branch information
Jason Mar authored Oct 7, 2022
1 parent 30fdbf3 commit 2c2d13e
Show file tree
Hide file tree
Showing 15 changed files with 60 additions and 10 deletions.
2 changes: 1 addition & 1 deletion tools/bigquery-zos-mainframe-connector/gszutil/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
organization := "com.google.cloud.imf"
name := "mainframe-connector"
version := "5.7.1"
version := "5.7.2"

scalaVersion := "2.13.8"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ case class ExportConfig(
timeoutMinutes: Int = 90,
vartext: Boolean = false,
runMode: String = "parallel",
encoding: Option[String] = None,
picTCharset: Option[String] = None,

bucket: String = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ object ExportOptionParser
.action((x,c) => c.copy(sync = x))

// Custom Options
opt[String]("encoding")
.optional()
.text("(optional) charset used for encoding and decoding character fields. Overrides default set by ENCODING environment variable.")
.action((x, c) => c.copy(encoding = Option(x)))

opt[String]("stats_table")
.optional()
.text("tablespec of table to insert stats")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ case class GsUtilConfig(source: String = "INFILE",
// Custom
schemaProvider: Option[SchemaProvider] = None,
picTCharset: Option[String] = None,
encoding: Option[String] = None,
statsTable: String = "",
remote: Boolean = false,
remoteHost: String = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ object GsUtilOptionParser extends OptionParser[GsUtilConfig]("gsutil") with ArgP
.action { (x, c) =>
c.copy(tfGCS = x)
},
opt[String]("encoding")
.optional()
.text("(optional) charset used for encoding and decoding character fields. Overrides default set by ENCODING environment variable.")
.action((x,c) => c.copy(encoding = Option(x))),
opt[String]("pic_t_charset")
.optional()
.text("(optional) charset used for encoding and decoding international strings, used with PIC T copybook type, default is EBCDIC")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ case class GsZUtilConfig(
projectId: String = "",
datasetId: String = "",
location: String = "US",
encoding: Option[String] = None,
picTCharset: Option[String] = None,
timeOutMinutes: Option[Int] = None,
keepAliveTimeInSeconds: Option[Int] = None)
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ with ArgParser[GsZUtilConfig]{
.text("port of GRecv transcoding service (default: 52701 or SRVPORT environment variable)")
.action((x,c) => c.copy(remoteHost = x))

opt[String]("encoding")
.optional()
.text("(optional) charset used for encoding and decoding character fields. Overrides default set by ENCODING environment variable.")
.action((x, c) => c.copy(encoding = Option(x)))

opt[String]("pic_t_charset")
.optional()
.text("(optional) charset used for encoding and decoding international strings, used with PIC T copybook type, default is EBCDIC")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ object Cp extends Command[GsUtilConfig] with Logging {
case Some(x) => {
logger.info("Merging copybook with provided transformations ...")

val sch = c.schemaProvider.getOrElse(zos.loadCopyBook(c.copyBook, c.picTCharset))
val sch = c.schemaProvider.getOrElse(zos.loadCopyBook(c.copyBook, c.encoding, c.picTCharset))
logger.info(s"Current Schema: ${sch.toString}")

val newSchema = merge(sch, x)
Expand All @@ -63,7 +63,7 @@ object Cp extends Command[GsUtilConfig] with Logging {
}
case None => {
logger.info("Use original copybook")
c.schemaProvider.getOrElse(zos.loadCopyBook(c.copyBook, c.picTCharset))
c.schemaProvider.getOrElse(zos.loadCopyBook(c.copyBook, c.encoding, c.picTCharset))
}
}
val in: ZRecordReaderT = c.testInput.getOrElse(zos.readCloudDD(c.source))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ object Export extends Command[ExportConfig] with Logging {
CopyBook(zos.readDSNLines(MVSStorage.parseDSN(cfg.cobDsn)).mkString("\n"), picTCharset = cfg.picTCharset)
} else {
logger.info(s"reading copybook from DD:COPYBOOK")
zos.loadCopyBook("COPYBOOK", cfg.picTCharset)
zos.loadCopyBook("COPYBOOK", cfg.encoding, cfg.picTCharset)
}

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ object GsZUtil extends Command[GsZUtilConfig] with Logging {
CopyBook(zos.readDSNLines(MVSStorage.parseDSN(c.cobDsn)).mkString("\n"), picTCharset = c.picTCharset)
} else {
logger.info(s"reading copybook from DD:COPYBOOK")
zos.loadCopyBook("COPYBOOK", c.picTCharset)
zos.loadCopyBook("COPYBOOK", c.encoding, c.picTCharset)
}
//TODO read FLDINFO DD and merge field info

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.google.cloud.imf.gzos

import com.google.cloud.gszutil.Transcoder

import java.nio.charset.Charset

/** Transcoder with user-specified character set
* @param encoding name of character set
*/
case class CharsetTranscoder(encoding: String) extends Transcoder {
override val charset: Charset = Charset.forName(encoding)
override val SP: Byte = {
val bytes = charset.encode(" ").array()
require(bytes.length == 1, "multi-byte space character not supported")
bytes.head
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,19 @@ import java.nio.charset.Charset

import com.google.cloud.gszutil.Transcoder

/** Default Transcoder
* Uses EBCDIC US charset by default.
* Obtains charset name from ENCODING environment variable.
*/
case object Ebcdic extends Transcoder {
override final val charset: Charset = new EBCDIC1()
override final val charset: Charset = {
sys.env.get("ENCODING") match {
case Some(charset) =>
System.out.println(s"Using Charset '$charset'")
Charset.forName(charset)
case None =>
new EBCDIC1()
}
}
override val SP: Byte = 0x40
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,15 @@ object IBM extends MVS with Logging {
}
}

override def loadCopyBook(dd: String, picTCharset: Option[String] = None) : CopyBook = {
override def loadCopyBook(dd: String, encoding: Option[String] = None, picTCharset: Option[String] = None) : CopyBook = {
val raw = readDDString(dd, "\n")
logger.info(s"Parsing copy book:\n$raw")
try {
val copyBook = CopyBook(raw, Ebcdic, picTCharset = picTCharset)
val transcoder = encoding match {
case Some(charsetName) => CharsetTranscoder(charsetName)
case None => Ebcdic
}
val copyBook = CopyBook(raw, transcoder, picTCharset = picTCharset)
logger.info(s"Loaded copy book with LRECL=${copyBook.LRECL}")
copyBook
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ object Linux extends MVS with Logging {

override def readKeyfile(): Array[Byte] = Array.empty

override def loadCopyBook(dd: String, picTCharset: Option[String] = Some("UTF-8")): CopyBook = {
override def loadCopyBook(dd: String, encoding: Option[String] = None, picTCharset: Option[String] = Some("UTF-8")): CopyBook = {
val ddValue = System.getenv(dd)
require(ddValue != null, s"$dd environment variable not defined")
val ddPath = Paths.get(ddValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ trait MVS {
def getCredentialProvider(): CredentialProvider
def readKeyfile(): Array[Byte]
def listPDS(dsn: DSN): Iterator[PDSMemberInfo]
def loadCopyBook(dd: String, picTCharset: Option[String] = None): CopyBook
def loadCopyBook(dd: String, encoding: Option[String] = None, picTCharset: Option[String] = None): CopyBook
def jobName: String
def jobDate: String
def jobTime: String
Expand Down

0 comments on commit 2c2d13e

Please sign in to comment.