SQL generation
This service generates SQL statements for any supported database.
Instantiation
If the connection to the database is available, just call the new SQLGenerator(toolbox)
method.
If you don’t have any connection to the database, you can create a generator with the following snippet:
ISQLDialect dialect = switch (type) {
case SNOWFLAKE -> new SnowflakeSQLDialect();
case MSSQL -> new MSSQLDialect();
case CLICKHOUSE -> new ClickHouseSQLDialect();
};
var naming = (new DirectQueryNameMapper()).convertToRemote();
// databaseName is the name of the target database
// schemaName is the name of the target schema or null if the target database doesn't handle the schema feature
// All parameters except dialect could be setup later
var generator = new SQLGenerator(naming, dialect, "databaseName", "schemaName");
SQL Generator Parameters
The generation behavior can be tuned by changing the SQLGeneratorParameters
parameters. This object is available by calling its getter: generator.getParameters()
.
Parameter | Meaning | Sample On | Sample Off |
---|---|---|---|
generateDefaultValues | If true, generates the default value statement if provided and compatible. | "DOUBLE_KEY2" DOUBLE NOT NULL DEFAULT 0.0 |
"DOUBLE_KEY2" DOUBLE NOT NULL |
generateNotNullConstraints | If true, generates the non-null constraint on the fields if required. | "DOUBLE_KEY2" DOUBLE NOT NULL DEFAULT 0.0 |
"DOUBLE_KEY2" DOUBLE DEFAULT 0.0 |
generatePrimaryKeys | If true the primary key constraint will be generated. | PRIMARY KEY ("STRING_KEY1", "DOUBLE_KEY2") |
Not generated |
generateForeignKeys | If true the joins or the references will create a foreign key constraint. | foreign key statement | Not generated |
naming | The naming convention applied to the fields and table, also provided on the constructor. | "DOUBLE_KEY2" DOUBLE NOT NULL DEFAULT 0.0 |
"DoubleKey2" DOUBLE NOT NULL DEFAULT 0.0 |
database | The name of the database that can be used on the statements. | ||
schemaName | The name of the schema, if the feature is available, that can be used on the statements. |
Features
Table generation
From an IDataTable
or an IStoreDescription
object, the SQL generator is able to generate a CREATE TABLE
statement matching the provided table.
The table name and the table fields are converted according to the provided naming convention. The nullable constraint, the default value and the primary key may be generated according to the SQL parameters.
note
The SQL generator disregards whether the target database has vector support. Even if not supported, it will generate a valid vector field if requested.
For instance:
private static final IStoreDescription TABLE = StartBuilding.store().withStoreName("GeneratorSqlStoreName")
.withField("StringKey1").asKeyField()
.withField("DoubleKey2", ILiteralType.DOUBLE).asKeyField()
.withNullableField("IntValue", ILiteralType.INT).dictionarized()
.withField("LongValue", ILiteralType.LONG)
.withField("LocalDateValue", ILiteralType.LOCAL_DATE)
.withField("DateValue", ILiteralType.DATE)
.withField("BooleanValue", ILiteralType.BOOLEAN)
.withField("FloatValue", ILiteralType.FLOAT)
.withField("DateTimeValue", ILiteralType.ZONED_DATE_TIME)
.withField("Blob", ILiteralType.OBJECT)
.withVectorField("StringVector", ILiteralType.STRING)
.withVectorField("DoubleVector", ILiteralType.DOUBLE).withDefaultValue(new double[] {0.0, 1.0}) // Note that this field is not handled on MS SQL DirectQuery implementation
.withField("StringValue")
.build();
will generate:
CREATE TABLE TEST_SCHEMA.GENERATOR_SQL_STORE_NAME(
[STRING_KEY1] NVARCHAR(4000) NOT NULL DEFAULT 'N/A',
[DOUBLE_KEY2] FLOAT NOT NULL DEFAULT 0.0,
[INT_VALUE] INT,
[LONG_VALUE] BIGINT NOT NULL DEFAULT 0,
[LOCAL_DATE_VALUE] DATE NOT NULL,
[DATE_VALUE] DATE NOT NULL,
[BOOLEAN_VALUE] BIT NOT NULL DEFAULT 0,
[FLOAT_VALUE] REAL NOT NULL DEFAULT 0.0,
[DATE_TIME_VALUE] DATETIME2 NOT NULL,
[BLOB] NVARCHAR(MAX) NOT NULL DEFAULT 'N/A',
[STRING_VECTOR] NVARCHAR(MAX),
[DOUBLE_VECTOR] NVARCHAR(MAX) NOT NULL DEFAULT '[0.0, 1.0]',
[STRING_VALUE] NVARCHAR(4000) NOT NULL DEFAULT 'N/A',
PRIMARY KEY ([STRING_KEY1], [DOUBLE_KEY2])
);
Join generation
From an ITableJoin
or an IReferenceDescription
object, the SQL generator is able to generate a FOREIGN KEY
statement matching the provided join.
note
Atoti objects don’t ensure the order of the join fields, but the databases generally require that the order exactly matches the target table primary key. Therefore, you must also provide the primary key order or the table description.
For instance:
private static final IReferenceDescription TABLE_JOIN = StartBuilding.reference()
.fromStore("GeneratorSqlStoreName")
.toStore("StoreName2")
.withName("ReferenceName")
.withMapping("StringKey1", "StringKey")
.withMapping("DoubleKey2", "DoubleKey")
.build();
will generate:
ALTER TABLE TEST_SCHEMA.GENERATOR_SQL_STORE_NAME
ADD CONSTRAINT REFERENCE_NAME
FOREIGN KEY ([STRING_KEY1], [DOUBLE_KEY2])
REFERENCES TEST_SCHEMA.STORE_NAME2([STRING_KEY], [DOUBLE_KEY]);
Full schema generation
You can also directly generate a whole schema from an IDatabaseSchema
or an IDatastoreSchemaDescription
object.
For instance:
USE TEST_DATABASE;
GO
DROP SCHEMA IF EXISTS TEST_SCHEMA;
GO
CREATE SCHEMA TEST_SCHEMA;
GO
CREATE TABLE TEST_SCHEMA.GENERATOR_SQL_STORE_NAME(
[STRING_KEY1] NVARCHAR(4000) NOT NULL DEFAULT 'N/A',
[DOUBLE_KEY2] FLOAT NOT NULL DEFAULT 0.0,
[INT_VALUE] INT,
PRIMARY KEY ([STRING_KEY1], [DOUBLE_KEY2])
);
CREATE TABLE TEST_SCHEMA.STORE_NAME2(
[STRING_KEY] NVARCHAR(4000) NOT NULL DEFAULT 'N/A',
[DOUBLE_KEY] FLOAT NOT NULL DEFAULT 0.0,
[DOUBLE_VALUE] FLOAT,
PRIMARY KEY ([STRING_KEY], [DOUBLE_KEY])
);
ALTER TABLE TEST_SCHEMA.GENERATOR_SQL_STORE_NAME
ADD CONSTRAINT REFERENCE_NAME
FOREIGN KEY ([STRING_KEY1], [DOUBLE_KEY2])
REFERENCES TEST_SCHEMA.STORE_NAME2([STRING_KEY], [DOUBLE_KEY]);
GO
Insert statement
A collection of strong-typed tuples can be converted into SQL insert statements. You must provide the target table name.
note
These should only be used for testing.
They are not intended to be used in production nor to generate bulk load.
private static final Collection<Object[]> DATA = List.of(
new Object[] {
"String",
1.0,
4,
8L,
LocalDate.of(2023,4,5),
new Date(123, Calendar.MAY, 5),
true,
2.0F,
ZonedDateTime.of(LocalDate.of(2023,4,5), LocalTime.of(4, 25), ZoneId.of("GMT")),
MathFunctions.Metric.SUM,
new String[] {"A", "B", "C"},
new double[] { 6.0, 7.0, 8.0},
"A"
},
new Object[] {
"O' String",
5.0,
null,
12L,
LocalDate.of(2023,4,5),
new Date(123, Calendar.MAY, 5),
true,
1.0F,
ZonedDateTime.of(LocalDate.of(2023,4,5), LocalTime.of(4, 25), ZoneId.of("CET")),
MathFunctions.Metric.SUM,
new String[] {"A", "B", "C"},
new double[] { 6.0, 7.0, 8.0},
"B"
});
will generate:
INSERT INTO
TEST_SCHEMA.GENERATOR_SQL_STORE_NAME
VALUES
('String', 1.0, 4, 8, '2023-04-05', '2023-05-05', 1, 2.0, '2023-04-05 04:25:00', 'SUM', '[''A'', ''B'', ''C'']', '[6.0, 7.0, 8.0]', 'A'),
('O'' String', 5.0, null, 12, '2023-04-05', '2023-05-05', 1, 1.0, '2023-04-05 04:25:00', 'SUM', '[''A'', ''B'', ''C'']', '[6.0, 7.0, 8.0]', 'B');
Other features
- Drop table
- Insert statement (for debugging purposes only)
- Use database statement
- Create schema statement