Skip to content

Commit

Permalink
Merge pull request #67 from takapi327/enhancement/2023-10-Table-enhan…
Browse files Browse the repository at this point in the history
…cement

Enhancement/2023 10 table enhancement
  • Loading branch information
takapi327 authored Oct 11, 2023
2 parents f7ce5e4 + c942937 commit e556fe2
Show file tree
Hide file tree
Showing 9 changed files with 313 additions and 132 deletions.
53 changes: 46 additions & 7 deletions core/src/main/scala/ldbc/core/Table.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ private[ldbc] trait Table[P <: Product] extends Dynamic:
/** Table Key definitions */
private[ldbc] def keyDefinitions: Seq[Key]

/** Table comment */
private[ldbc] def comment: Option[String]

/** Table alias name */
private[ldbc] def alias: Option[String]

/** Additional table information */
private[ldbc] def options: Seq[TableOption]

/** Methods for statically accessing column information held by a Table.
*
* @param tag
Expand All @@ -50,12 +50,46 @@ private[ldbc] trait Table[P <: Product] extends Dynamic:
*/
private[ldbc] def all: List[Column[[A] => A => A]]

/** Method to retrieve all column information that a table has as a Tuple.
*
* @param mirror
* product isomorphism map
*/
def *(using mirror: Mirror.ProductOf[P]): Tuple.Map[mirror.MirroredElemTypes, Column]

/** Methods for setting key information for tables.
*
* @param func
* Function to construct an expression using the columns that Table has.
*/
def keySet(func: Table[P] => Key): Table[P]

def comment(str: String): Table[P]
/** Methods for setting multiple key information for a table.
*
* @param func
* Function to construct an expression using the columns that Table has.
*/
def keySets(func: Table[P] => Seq[Key]): Table[P]

/** Methods for setting additional information for the table.
*
* @param option
* Additional information to be given to the table.
*/
def setOption(option: TableOption): Table[P]

/** Methods for setting multiple additional information for a table.
*
* @param options
* Additional information to be given to the table.
*/
def setOptions(options: Seq[TableOption]): Table[P]

/** Methods for setting alias names for tables.
*
* @param name
* Alias name to be set for the table
*/
def as(name: String): Table[P]

object Table extends Dynamic:
Expand All @@ -64,7 +98,7 @@ object Table extends Dynamic:
_name: String,
columns: Tuple.Map[T, Column],
keyDefinitions: Seq[Key],
comment: Option[String],
options: Seq[TableOption],
alias: Option[String] = None
) extends Table[P]:

Expand All @@ -89,7 +123,12 @@ object Table extends Dynamic:

override def keySet(func: Table[P] => Key): Table[P] = this.copy(keyDefinitions = this.keyDefinitions :+ func(this))

override def comment(str: String): Table[P] = this.copy(comment = Some(str))
override def keySets(func: Table[P] => Seq[Key]): Table[P] =
this.copy(keyDefinitions = this.keyDefinitions ++ func(this))

override def setOption(option: TableOption): Table[P] = this.copy(options = options :+ option)

override def setOptions(options: Seq[TableOption]): Table[P] = this.copy(options = options)

override def as(name: String): Table[P] = this.copy(alias = Some(name))

Expand Down Expand Up @@ -130,4 +169,4 @@ object Table extends Dynamic:
)(
_name: String,
columns: Tuple.Map[mirror.MirroredElemTypes, Column]
): Table[P] = Impl[P, mirror.MirroredElemTypes](_name, columns, Seq.empty, None)
): Table[P] = Impl[P, mirror.MirroredElemTypes](_name, columns, Seq.empty, Seq.empty, None)
91 changes: 91 additions & 0 deletions core/src/main/scala/ldbc/core/TableOption.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/** This file is part of the ldbc. For the full copyright and license information, please view the LICENSE file that was
* distributed with this source code.
*/

package ldbc.core

trait TableOption:

def queryString: String

object TableOption:
case class AutoExtendSize(value: Int) extends TableOption:
override def queryString: String = s"AUTOEXTEND_SIZE=$value"

case class AutoIncrement(value: Int) extends TableOption:
override def queryString: String = s"AUTO_INCREMENT=$value"

case class AVGRowLength(value: Int) extends TableOption:
override def queryString: String = s"AVG_ROW_LENGTH=$value"

case class Character(value: String) extends TableOption:
override def queryString: String = s"DEFAULT CHARACTER SET=$value"

case class CheckSum(value: 0 | 1) extends TableOption:
override def queryString: String = s"CHECKSUM=$value"

case class Collate(value: String) extends TableOption:
override def queryString: String = s"DEFAULT COLLATE=$value"

case class Comment(value: String) extends TableOption:
override def queryString: String = s"COMMENT='$value'"

case class Compression(value: "ZLIB" | "LZ4" | "NONE") extends TableOption:
override def queryString: String = s"COMPRESSION='$value'"

case class Connection(value: String) extends TableOption:
override def queryString: String = s"CONNECTION='$value'"

case class Directory(`type`: "DATA" | "INDEX", value: String) extends TableOption:
override def queryString: String = s"${ `type` } DIRECTORY='$value'"

case class DelayKeyWrite(value: 0 | 1) extends TableOption:
override def queryString: String = s"DELAY_KEY_WRITE=$value"

case class Encryption(value: "Y" | "N") extends TableOption:
override def queryString: String = s"ENCRYPTION='$value'"

case class Engine(
value: "InnoDB" | "MyISAM" | "MEMORY" | "CSV" | "ARCHIVE" | "EXAMPLE" | "FEDERATED" | "HEAP" | "MERGE" | "NDB"
) extends TableOption:
override def queryString: String = s"ENGINE=$value"

case class EngineAttribute(value: String) extends TableOption:
override def queryString: String = s"ENGINE_ATTRIBUTE='$value'"

case class InsertMethod(value: "NO" | "FIRST" | "LAST") extends TableOption:
override def queryString: String = s"INSERT_METHOD=$value"

case class KeyBlockSize(value: 1 | 2 | 4 | 8 | 16) extends TableOption:
override def queryString: String = s"KEY_BLOCK_SIZE=$value"

case class MaxRows(value: Long) extends TableOption:
override def queryString: String = s"MAX_ROWS=$value"

case class MinRows(value: Long) extends TableOption:
override def queryString: String = s"MIN_ROWS=$value"

case class PackKeys(value: "0" | "1" | "DEFAULT") extends TableOption:
override def queryString: String = s"PACK_KEYS=$value"

case class RowFormat(value: "DEFAULT" | "DYNAMIC" | "FIXED" | "COMPRESSED" | "REDUNDANT" | "COMPACT")
extends TableOption:
override def queryString: String = s"ROW_FORMAT=$value"

case class SecondaryEngineAttribute(value: String) extends TableOption:
override def queryString: String = s"SECONDARY_ENGINE_ATTRIBUTE='$value'"

case class StatsAutoRecalc(value: "0" | "1" | "DEFAULT") extends TableOption:
override def queryString: String = s"STATS_AUTO_RECALC=$value"

case class StatsPersistent(value: "0" | "1" | "DEFAULT") extends TableOption:
override def queryString: String = s"STATS_PERSISTENT=$value"

case class StatsSamplePages(value: Int) extends TableOption:
override def queryString: String = s"STATS_SAMPLE_PAGES=$value"

case class Tablespace(name: String, storage: Option["DISK" | "MEMORY"]) extends TableOption:
override def queryString: String = storage.fold(s"TABLESPACE $name")(v => s"TABLESPACE $name STORAGE $v")

case class Union(tableNames: List[String]) extends TableOption:
override def queryString: String = s"UNION ${ tableNames.mkString(",") }"
8 changes: 5 additions & 3 deletions core/src/main/scala/ldbc/core/builder/TableQueryBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@ private[ldbc] case class TableQueryBuilder(table: Table[?]) extends TableValidat
private val columnDefinitions: Seq[String] =
table.all.map(_.queryString)

private val options: Seq[String] =
private val createDefinitions: Seq[String] =
columnDefinitions ++ table.keyDefinitions.map(_.queryString)

private val tableOptions: Seq[String] = table.options.map(_.queryString)

/** Variable that generates the Create statement that creates the Table.
*/
lazy val createStatement: String =
s"""
|CREATE TABLE `${ table._name }` (
| ${ options.mkString(",\n ") }
|);
| ${ createDefinitions.mkString(",\n ") }
|)${ if tableOptions.isEmpty then ";" else s" ${ tableOptions.mkString(" ") };" }
|""".stripMargin

/** Variable that generates the Drop statement that creates the Table.
Expand Down
21 changes: 14 additions & 7 deletions core/src/main/scala/ldbc/core/validator/TableValidator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ private[ldbc] trait TableValidator:

protected val autoInc = table.all.filter(_.attributes.contains(AutoInc()))

protected val primaryKey = table.all.filter(_.attributes.exists(_.isInstanceOf[PrimaryKey]))
protected val primaryKey = table.all.filter(_.attributes.exists {
case _: PrimaryKey => true
case _ => false
})

protected val keyPart = table.keyDefinitions.flatMap {
case key: PrimaryKey with Index => key.keyPart.toList
case key: UniqueKey with Index => key.keyPart.toList
case key: PrimaryKey with Index => key.keyPart
case key: UniqueKey with Index => key.keyPart
case _ => List.empty
}

Expand Down Expand Up @@ -48,13 +51,17 @@ private[ldbc] trait TableValidator:

require(
!(autoInc.nonEmpty &&
(autoInc.count(column =>
column.attributes.exists(_.isInstanceOf[PrimaryKey]) || column.attributes.exists(_.isInstanceOf[UniqueKey])
) >= 1 || keyPart.count(key =>
autoInc.count(column =>
column.attributes.exists {
case _: PrimaryKey => true
case _: UniqueKey => true
case _ => false
}
) == 0 && keyPart.count(key =>
autoInc
.map(_.label)
.contains(key.label)
) == 0)),
) == 0),
"The columns with AUTO_INCREMENT must have a Primary Key or Unique Key."
)

Expand Down
25 changes: 25 additions & 0 deletions core/src/test/scala/ldbc/core/builder/TableQueryBuilderTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,31 @@ object TableQueryBuilderTest extends Specification:
|""".stripMargin
}

"If the table option is set to Table, the query string will match the specified value." in {
case class Test(id: Long, name: String)

val table: Table[Test] = Table[Test]("test")(
column("id", BIGINT, AUTO_INCREMENT, PRIMARY_KEY),
column("name", VARCHAR(255))
)
.setOptions(
Seq(
TableOption.Engine("InnoDB"),
TableOption.Character("utf8mb4"),
TableOption.Collate("utf8mb4_unicode_ci"),
TableOption.Comment("test")
)
)

TableQueryBuilder(table).createStatement ===
"""
|CREATE TABLE `test` (
| `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
| `name` VARCHAR(255) NOT NULL
|) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci COMMENT='test';
|""".stripMargin
}

"IllegalArgumentException is raised if the type of the column set in FOREIGN KEY does not match." in {
case class Test(id: Long, subId: Long)
case class SubTest(id: Long, test: String)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ private[ldbc] object TableModelGenerator:
s".keySet(table => ${ key.toCode("table", classNameFormatter, propertyNameFormatter) })"
)

val tableOptions = statement.options.fold("")(
_.map(option => s".setOption(${ Table.buildTableOptionCode(option) })").mkString("\n ")
)

val builder = ColumnCodeBuilder(classNameFormatter)

val columns =
Expand All @@ -111,6 +115,7 @@ private[ldbc] object TableModelGenerator:
| ${ columns.mkString(",\n ") }
| )
| ${ keyDefinitions.mkString("\n ") }
| $tableOptions
|""".stripMargin

Files.write(outputFile.toPath, scalaSource.getBytes(summon[Codec].name))
Expand Down
67 changes: 32 additions & 35 deletions module/ldbc-codegen/src/main/scala/ldbc/codegen/model/Table.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,44 @@

package ldbc.codegen.model

object Table:

trait Options

object Options:
import ldbc.core.TableOption

case class AutoExtendSize(value: Int) extends Options
case class AutoIncrement(value: Int) extends Options
case class AVGRowLength(value: Int) extends Options
case class Character(value: String) extends Options
case class CheckSum(value: 0 | 1) extends Options
case class Collate(value: String) extends Options
case class Comment(value: String) extends Options
case class Compression(value: "ZLIB" | "LZ4" | "NONE") extends Options
case class Connection(value: String) extends Options
case class Directory(value: String) extends Options
case class DelayKeyWrite(value: 0 | 1) extends Options
case class Encryption(value: "Y" | "N") extends Options
case class Engine(
value: "InnoDB" | "MyISAM" | "MEMORY" | "CSV" | "ARCHIVE" | "EXAMPLE" | "FEDERATED" | "HEAP" | "MERGE" | "NDB"
) extends Options
case class EngineAttribute(value: Key.EngineAttribute) extends Options
case class InsertMethod(value: "NO" | "FIRST" | "LAST") extends Options
case class KeyBlockSize(value: Key.KeyBlockSize) extends Options
case class MaxRows(value: Long) extends Options
case class MinRows(value: Long) extends Options
case class PackKeys(value: "0" | "1" | "DEFAULT") extends Options
case class RowFormat(value: "DEFAULT" | "DYNAMIC" | "FIXED" | "COMPRESSED" | "REDUNDANT" | "COMPACT")
extends Options
case class SecondaryEngineAttribute(value: String) extends Options
case class StatsAutoRecalc(value: "0" | "1" | "DEFAULT") extends Options
case class StatsPersistent(value: "0" | "1" | "DEFAULT") extends Options
case class StatsSamplePages(value: Int) extends Options
case class Tablespace(name: String, storage: Option["DISK" | "MEMORY"]) extends Options
case class Union(tableNames: List[String]) extends Options
object Table:

case class CreateStatement(
tableName: String,
columnDefinitions: List[ColumnDefinition],
keyDefinitions: List[Key],
options: Option[List[Options]]
options: Option[List[TableOption]]
)

case class DropStatement(tableName: String)

def buildTableOptionCode(option: TableOption): String =
option match
case TableOption.AutoExtendSize(value) => s"TableOption.AutoExtendSize($value)"
case TableOption.AutoIncrement(value) => s"TableOption.AutoIncrement($value)"
case TableOption.AVGRowLength(value) => s"TableOption.AVGRowLength($value)"
case TableOption.Character(value) => s"TableOption.Character(\"$value\")"
case TableOption.CheckSum(value) => s"TableOption.CheckSum($value)"
case TableOption.Collate(value) => s"TableOption.Collate(\"$value\")"
case TableOption.Comment(value) => s"TableOption.Comment(\"$value\")"
case TableOption.Compression(value) => s"TableOption.Compression(\"$value\")"
case TableOption.Connection(value) => s"TableOption.Connection(\"$value\")"
case TableOption.Directory(str, value) => s"TableOption.Directory(\"$str\", \"$value\")"
case TableOption.DelayKeyWrite(value) => s"TableOption.DelayKeyWrite($value)"
case TableOption.Encryption(value) => s"TableOption.Encryption(\"$value\")"
case TableOption.Engine(value) => s"TableOption.Engine(\"$value\")"
case TableOption.EngineAttribute(value) => s"TableOption.EngineAttribute(\"$value\")"
case TableOption.InsertMethod(value) => s"TableOption.InsertMethod(\"$value\")"
case TableOption.KeyBlockSize(value) => s"TableOption.KeyBlockSize($value)"
case TableOption.MaxRows(value) => s"TableOption.MaxRows($value)"
case TableOption.MinRows(value) => s"TableOption.MinRows($value)"
case TableOption.PackKeys(value) => s"TableOption.PackKeys(\"$value\")"
case TableOption.RowFormat(value) => s"TableOption.RowFormat(\"$value\")"
case TableOption.SecondaryEngineAttribute(value) => s"TableOption.SecondaryEngineAttribute(\"$value\")"
case TableOption.StatsAutoRecalc(value) => s"TableOption.StatsAutoRecalc(\"$value\")"
case TableOption.StatsPersistent(value) => s"TableOption.StatsPersistent(\"$value\")"
case TableOption.StatsSamplePages(value) => s"TableOption.StatsSamplePages($value)"
case TableOption.Tablespace(name, value) => s"TableOption.Tablespace(\"$name\", \"$value\")"
case TableOption.Union(value) => s"TableOption.Union(List(${ value.map(str => s"\"$str\"").mkString(",") }))"
Loading

0 comments on commit e556fe2

Please sign in to comment.