From 62a4eceab6cc1ef19ab3fd4aa42d0ae970e73fcb Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Fri, 16 Oct 2026 15:31:39 +0100 Subject: Generate SQL scripts for database schema --- .../compiler/output/generators/Generators.java | 2 + .../output/generators/sql/SqlGenerator.java | 60 ++++++++++ .../generators/sqlscripts/SqlScriptsGenerator.java | 121 +++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sql/SqlGenerator.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sqlscripts/SqlScriptsGenerator.java (limited to 'src/main/java') diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generators.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generators.java index 92b78b8..c1325dd 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generators.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generators.java @@ -5,6 +5,7 @@ import org.zwobble.hobgoblin.compiler.output.generators.javatransient0.JavaTrans import org.zwobble.hobgoblin.compiler.output.generators.javatypes.JavaTypesGenerator; import org.zwobble.hobgoblin.compiler.output.generators.rusttransient0.RustTransient0Generator; import org.zwobble.hobgoblin.compiler.output.generators.rusttypes.RustTypesGenerator; +import org.zwobble.hobgoblin.compiler.output.generators.sqlscripts.SqlScriptsGenerator; import org.zwobble.hobgoblin.compiler.output.generators.typescripttypes.TypescriptTypesGenerator; import java.util.List; @@ -21,6 +22,7 @@ public class Generators { JavaTransient0Generator.DEFINITION, RustTypesGenerator.DEFINITION, RustTransient0Generator.DEFINITION, + SqlScriptsGenerator.DEFINITION, TypescriptTypesGenerator.DEFINITION ); diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sql/SqlGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sql/SqlGenerator.java new file mode 100644 index 0000000..be847d5 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sql/SqlGenerator.java @@ -0,0 +1,60 @@ +package org.zwobble.hobgoblin.compiler.output.generators.sql; + +import org.zwobble.hobgoblin.compiler.ast.typed.TypedTableDefinitionNode; +import org.zwobble.hobgoblin.compiler.builtins.NativeTypes; +import org.zwobble.hobgoblin.compiler.output.lang.sql.ast.*; +import org.zwobble.hobgoblin.compiler.typechecker.TypesInfo; +import org.zwobble.hobgoblin.compiler.types.SimpleStructType; +import org.zwobble.hobgoblin.compiler.types.Type; +import org.zwobble.hobgoblin.compiler.util.Casing; + +import java.util.List; + +public class SqlGenerator { + public SqlStatement generateTableDefinition( + TypedTableDefinitionNode tableDefinition, + TypesInfo typesInfo + ) { + var sqlTableName = this.generateTableName(tableDefinition.name()); + + // TODO: constructed struct type. + var sqlColumns = typesInfo.fieldsOf((SimpleStructType) tableDefinition.rowType()) + .orElse(List.of()) + .stream() + .map(field -> new SqlColumnDefinition( + this.generateColumnName(field.name()), + this.generateSqlType(field.type()) + )) + .toList(); + + var sqlTableConstraints = List.of( + new SqlPrimaryKeyConstraint(List.of( + this.generateColumnName(tableDefinition.primaryKey().name()) + )) + ); + + return new SqlCreateTableStatement( + sqlTableName, + sqlColumns, + sqlTableConstraints + ); + } + + private SqlIdentifier generateSqlType(Type type) { + if (type.equals(NativeTypes.STRING)) { + return new SqlIdentifier("TEXT"); + } else if (type.equals(NativeTypes.INT_32)) { + return new SqlIdentifier("INT"); + } else { + throw new UnsupportedOperationException("TODO"); + } + } + + private SqlIdentifier generateColumnName(String fieldName) { + return new SqlIdentifier(Casing.lowerCamelCaseToSnakeCase(fieldName)); + } + + private SqlIdentifier generateTableName(String tableName) { + return new SqlIdentifier(Casing.lowerCamelCaseToSnakeCase(tableName)); + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sqlscripts/SqlScriptsGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sqlscripts/SqlScriptsGenerator.java new file mode 100644 index 0000000..60daf40 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sqlscripts/SqlScriptsGenerator.java @@ -0,0 +1,121 @@ +package org.zwobble.hobgoblin.compiler.output.generators.sqlscripts; + +import org.zwobble.hobgoblin.compiler.ast.typed.TypedDatabaseSchemaDefinitionNode; +import org.zwobble.hobgoblin.compiler.ast.typed.TypedDatabaseSchemaStatementNode; +import org.zwobble.hobgoblin.compiler.ast.typed.TypedNamespaceNode; +import org.zwobble.hobgoblin.compiler.ast.typed.TypedTableDefinitionNode; +import org.zwobble.hobgoblin.compiler.config.OutputConfig; +import org.zwobble.hobgoblin.compiler.output.generators.Generator; +import org.zwobble.hobgoblin.compiler.output.generators.GeneratorDefinition; +import org.zwobble.hobgoblin.compiler.output.generators.sql.SqlGenerator; +import org.zwobble.hobgoblin.compiler.output.lang.sql.SqlWriter; +import org.zwobble.hobgoblin.compiler.output.lang.sql.ast.SqlScript; +import org.zwobble.hobgoblin.compiler.output.lang.sql.ast.SqlStatement; +import org.zwobble.hobgoblin.compiler.typechecker.TypesInfo; +import org.zwobble.hobgoblin.compiler.types.NamespaceName; +import org.zwobble.json5.reader.Json5ObjectReader; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +public class SqlScriptsGenerator implements Generator { + private static final String NAME = "sql-scripts"; + + public static final GeneratorDefinition DEFINITION = new GeneratorDefinition() { + @Override + public String name() { + return NAME; + } + + @Override + public OutputConfig parseGeneratorConfig( + Path projectRoot, + Json5ObjectReader generatorJson, + Json5ObjectReader langsJson + ) { + return SqlScriptsGenerator.parseGeneratorConfig(projectRoot, generatorJson, langsJson); + } + }; + + private static OutputConfig parseGeneratorConfig( + Path projectRoot, + Json5ObjectReader generatorJson, + Json5ObjectReader langsJson + ) { + return () -> { + var relativeOutputPath = generatorJson.getString("path").value(); + var outputPath = projectRoot.resolve(relativeOutputPath); + + var sqlGenerator = new SqlGenerator(); + + return new SqlScriptsGenerator(outputPath, sqlGenerator); + }; + } + + private final Path outputPath; + private final SqlGenerator sqlGenerator; + + private SqlScriptsGenerator(Path outputPath, SqlGenerator sqlGenerator) { + this.outputPath = outputPath; + this.sqlGenerator = sqlGenerator; + } + + @Override + public void generate( + List namespaces, + TypesInfo typesInfo + ) throws IOException { + for (var namespace : namespaces) { + for (var namespaceStatement : namespace.body()) { + if (namespaceStatement instanceof TypedDatabaseSchemaDefinitionNode databaseSchemaDefinition) { + generateDatabaseSchemaDefinition( + databaseSchemaDefinition, + namespace.namespaceName(), + typesInfo + ); + } + } + } + } + + private void generateDatabaseSchemaDefinition( + TypedDatabaseSchemaDefinitionNode databaseSchemaDefinition, + NamespaceName namespaceName, + TypesInfo typesInfo + ) throws IOException { + var sqlStatements = databaseSchemaDefinition.body() + .stream() + .map(statement -> generateDatabaseSchemaStatement(statement, typesInfo)) + .toList(); + var sqlScript = new SqlScript(sqlStatements); + + write(namespaceName, databaseSchemaDefinition.name(), sqlScript); + } + + private SqlStatement generateDatabaseSchemaStatement( + TypedDatabaseSchemaStatementNode statement, + TypesInfo typesInfo + ) { + return switch (statement) { + case TypedTableDefinitionNode tableDefinition -> + this.sqlGenerator.generateTableDefinition(tableDefinition, typesInfo); + }; + } + + private void write( + NamespaceName namespaceName, + String schemaName, + SqlScript script + ) throws IOException { + var scriptPath = this.outputPath + .resolve(String.join(File.separator, namespaceName.parts())) + .resolve(schemaName + ".sql"); + + SqlWriter.writeTo( + scriptPath, + writer -> writer.writeScript(script) + ); + } +} -- cgit v1.2.3