Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/live-dialects.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,34 @@ jobs:
mvn -B -ntp -pl teaql-postgres,teaql-mysql,examples/business-id-runtime -am
-Dtest=PostgresIntegrationTest,MysqlIntegrationTest,JdbcBusinessIdLiveDialectIT
-Dsurefire.failIfNoSpecifiedTests=false test

sqlserver:
runs-on: ubuntu-latest
services:
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
env:
ACCEPT_EULA: "Y"
MSSQL_SA_PASSWORD: TeaqlSchemaLiveTest123!
ports:
- 1433:1433
options: >-
--health-cmd "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P TeaqlSchemaLiveTest123! -C -Q 'SELECT 1' -b"
--health-interval 10s --health-timeout 5s --health-retries 20
env:
TEAQL_REQUIRE_LIVE_DB: "true"
TEAQL_TEST_MSSQL_URL: "jdbc:sqlserver://127.0.0.1:1433;databaseName=master;encrypt=false;trustServerCertificate=true"
TEAQL_TEST_MSSQL_USER: sa
TEAQL_TEST_MSSQL_PASSWORD: TeaqlSchemaLiveTest123!
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
cache: maven
- name: Verify live SQL Server schema dialect (skips are forbidden)
run: >-
mvn -B -ntp -pl teaql-mssql -am
-Dtest=MssqlSchemaIntegrationTest
-Dsurefire.failIfNoSpecifiedTests=false test
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public List<Map<String, Object>> getTableColumns(String tableName) {

for (EntityDescriptor descriptor : descriptors) {
PortableSQLRepository repository = new PortableSQLRepository(descriptor, dbAdapter, null);
repository.setDialect(this.dialect);
repository.ensurePhysicalSchema(context);
}
}
Expand Down
101 changes: 101 additions & 0 deletions teaql-dm8/src/test/java/io/teaql/dm8/Dm8SchemaIntegrationTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package io.teaql.dm8;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import io.teaql.core.BaseEntity;
import io.teaql.core.UserContext;
import io.teaql.core.dm8.Dm8DataServiceExecutor;
import io.teaql.core.meta.SimpleEntityMetaFactory;
import io.teaql.core.sql.GenericSQLProperty;
import io.teaql.core.sql.SQLEntityDescriptor;
import io.teaql.provider.jdbc.JdbcSqlExecutor;
import io.teaql.runtime.DefaultUserContext;
import io.teaql.runtime.TeaQLRuntime;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.junit.Assume;
import org.junit.Test;

public class Dm8SchemaIntegrationTest {

private record TestDataSource(String url, String user, String password) implements DataSource {
@Override public Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
@Override public Connection getConnection(String username, String secret) throws SQLException {
return DriverManager.getConnection(url, username, secret);
}
@Override public PrintWriter getLogWriter() { return null; }
@Override public void setLogWriter(PrintWriter writer) {}
@Override public void setLoginTimeout(int seconds) {}
@Override public int getLoginTimeout() { return 0; }
@Override public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
@Override public <T> T unwrap(Class<T> type) throws SQLException {
throw new SQLException("Not a wrapper for " + type.getName());
}
@Override public boolean isWrapperFor(Class<?> type) { return false; }
}

@Test
public void ensureSchemaUsesSelectedDialectForLargeText() {
String url = System.getenv("TEAQL_TEST_DM8_URL");
String user = System.getenv("TEAQL_TEST_DM8_USER");
String password = System.getenv("TEAQL_TEST_DM8_PASSWORD");
if ("true".equalsIgnoreCase(System.getenv("TEAQL_REQUIRE_LIVE_DB"))) {
assertTrue("TEAQL_TEST_DM8_URL must be set", url != null && !url.isBlank());
assertTrue("TEAQL_TEST_DM8_USER must be set", user != null && !user.isBlank());
assertTrue("TEAQL_TEST_DM8_PASSWORD must be set", password != null && !password.isBlank());
}
Assume.assumeTrue("DM8 live test credentials are not configured",
url != null && user != null && password != null);

DataSource dataSource = new TestDataSource(url, user, password);
JdbcSqlExecutor jdbc = new JdbcSqlExecutor(dataSource);
String table = "dm8_long_text_" + UUID.randomUUID().toString().substring(0, 8);

SQLEntityDescriptor descriptor = new SQLEntityDescriptor();
descriptor.setType("Dm8LongTextProbe");
descriptor.setTargetType(BaseEntity.class);
descriptor.setEntitySupplier(BaseEntity::new);
descriptor.setDataService("dm8");
for (String name : List.of("id", "version")) {
GenericSQLProperty property = (GenericSQLProperty) descriptor.addSimpleProperty(name, Long.class);
property.setTableName(table);
property.setColumnName(name);
property.setColumnType("BIGINT");
}
GenericSQLProperty payload = (GenericSQLProperty) descriptor.addSimpleProperty("payload", String.class);
payload.setTableName(table);
payload.setColumnName("payload");
payload.setColumnType("LARGE_TEXT");
SimpleEntityMetaFactory metadata = new SimpleEntityMetaFactory();
metadata.register(descriptor);

TeaQLRuntime runtime = TeaQLRuntime.builder()
.metadata(metadata)
.dataService("dm8", new Dm8DataServiceExecutor("dm8", jdbc))
.build();
UserContext context = new DefaultUserContext(runtime);
context.ensureSchema();
context.ensureSchema();

List<Map<String, Object>> columns = jdbc.queryForList(
"SELECT data_type FROM all_tab_columns WHERE owner = USER "
+ "AND table_name = UPPER(?) AND column_name = 'PAYLOAD'",
new Object[] {table});
assertEquals(1, columns.size());
assertEquals("CLOB", String.valueOf(columns.get(0).values().iterator().next())
.toUpperCase(java.util.Locale.ROOT));
}
}
29 changes: 29 additions & 0 deletions teaql-mssql/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,34 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>13.4.0.jre11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.teaql</groupId>
<artifactId>teaql-provider-jdbc</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.teaql</groupId>
<artifactId>teaql-runtime</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>--add-reads io.teaql.mssql=io.teaql.runtime</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public MssqlDataServiceExecutor(String name, SqlExecutionAdapter executionAdapte
super(name, executionAdapter);
this.dialect = new MssqlDialect();
this.debugDatabaseKind = "mssql";
this.dialect = new MssqlDialect();
}

@Override
Expand Down Expand Up @@ -60,6 +59,7 @@ public List<Map<String, Object>> getTableColumns(String tableName) {

for (EntityDescriptor descriptor : descriptors) {
PortableSQLRepository repository = new PortableSQLRepository(descriptor, dbAdapter, null);
repository.setDialect(this.dialect);
repository.ensurePhysicalSchema(context);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package io.teaql.core.mssql;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import io.teaql.core.BaseEntity;
import io.teaql.core.UserContext;
import io.teaql.core.meta.SimpleEntityMetaFactory;
import io.teaql.core.sql.GenericSQLProperty;
import io.teaql.core.sql.SQLEntityDescriptor;
import io.teaql.provider.jdbc.JdbcSqlExecutor;
import io.teaql.runtime.DefaultUserContext;
import io.teaql.runtime.TeaQLRuntime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
import org.junit.Assume;
import org.junit.Test;

public class MssqlSchemaIntegrationTest {

private record TestDataSource(String url, String user, String password) implements DataSource {
@Override public Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
@Override public Connection getConnection(String username, String secret) throws SQLException {
return DriverManager.getConnection(url, username, secret);
}
@Override public PrintWriter getLogWriter() { return null; }
@Override public void setLogWriter(PrintWriter writer) {}
@Override public void setLoginTimeout(int seconds) {}
@Override public int getLoginTimeout() { return 0; }
@Override public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
@Override public <T> T unwrap(Class<T> type) throws SQLException {
throw new SQLException("Not a wrapper for " + type.getName());
}
@Override public boolean isWrapperFor(Class<?> type) { return false; }
}

@Test
public void ensureSchemaUsesSelectedDialectForLargeText() {
String url = System.getenv("TEAQL_TEST_MSSQL_URL");
String user = System.getenv("TEAQL_TEST_MSSQL_USER");
String password = System.getenv("TEAQL_TEST_MSSQL_PASSWORD");
if ("true".equalsIgnoreCase(System.getenv("TEAQL_REQUIRE_LIVE_DB"))) {
assertTrue("TEAQL_TEST_MSSQL_URL must be set", url != null && !url.isBlank());
assertTrue("TEAQL_TEST_MSSQL_USER must be set", user != null && !user.isBlank());
assertTrue("TEAQL_TEST_MSSQL_PASSWORD must be set", password != null && !password.isBlank());
}
Assume.assumeTrue("SQL Server live test credentials are not configured",
url != null && user != null && password != null);

DataSource dataSource = new TestDataSource(url, user, password);
JdbcSqlExecutor jdbc = new JdbcSqlExecutor(dataSource);
String table = "mssql_long_text_" + UUID.randomUUID().toString().substring(0, 8);

SQLEntityDescriptor descriptor = new SQLEntityDescriptor();
descriptor.setType("MssqlLongTextProbe");
descriptor.setTargetType(BaseEntity.class);
descriptor.setEntitySupplier(BaseEntity::new);
descriptor.setDataService("mssql");
for (String name : List.of("id", "version")) {
GenericSQLProperty property = (GenericSQLProperty) descriptor.addSimpleProperty(name, Long.class);
property.setTableName(table);
property.setColumnName(name);
property.setColumnType("BIGINT");
}
GenericSQLProperty payload = (GenericSQLProperty) descriptor.addSimpleProperty("payload", String.class);
payload.setTableName(table);
payload.setColumnName("payload");
payload.setColumnType("LARGE_TEXT");
SimpleEntityMetaFactory metadata = new SimpleEntityMetaFactory();
metadata.register(descriptor);

TeaQLRuntime runtime = TeaQLRuntime.builder()
.metadata(metadata)
.dataService("mssql", new MssqlDataServiceExecutor("mssql", jdbc))
.build();
UserContext context = new DefaultUserContext(runtime);
context.ensureSchema();
context.ensureSchema();

List<Map<String, Object>> columns = jdbc.queryForList(
"SELECT data_type, character_maximum_length FROM information_schema.columns "
+ "WHERE table_schema = SCHEMA_NAME() AND table_name = ? AND column_name = ?",
new Object[] {table, "payload"});
assertEquals(1, columns.size());
Map<String, Object> column = columns.get(0);
assertEquals("nvarchar", String.valueOf(column.get("data_type")).toLowerCase(java.util.Locale.ROOT));
assertEquals(-1, ((Number) column.get("character_maximum_length")).intValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public Optional<Boolean> indexExists(

for (EntityDescriptor descriptor : descriptors) {
PortableSQLRepository repository = new PortableSQLRepository(descriptor, dbAdapter, null);
repository.setDialect(this.dialect);
repository.ensurePhysicalSchema(context);
}
}
Expand Down
42 changes: 42 additions & 0 deletions teaql-mysql/src/test/java/io/teaql/mysql/MysqlIntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand Down Expand Up @@ -287,6 +288,47 @@ public void testSchemaUsesInvokingContextMetadata() {
jdbc.queryForList("SELECT id FROM context_probe_data WHERE 1 = 0", new Object[0]).isEmpty());
}

@Test
public void testEnsureSchemaUsesMysqlLongTextInsteadOfPostgresText() {
String table = "mysql_long_text_" + UUID.randomUUID().toString().substring(0, 8);
SimpleEntityMetaFactory isolated = new SimpleEntityMetaFactory();
SQLEntityDescriptor probe = new SQLEntityDescriptor();
probe.setType("MysqlLongTextProbe");
probe.setTargetType(Task.class);
probe.setEntitySupplier(Task::new);
probe.setDataService("mysql");
for (String name : List.of("id", "version")) {
io.teaql.core.sql.GenericSQLProperty field =
(io.teaql.core.sql.GenericSQLProperty) probe.addSimpleProperty(name, Long.class);
field.setTableName(table);
field.setColumnName(name);
field.setColumnType("BIGINT");
}
io.teaql.core.sql.GenericSQLProperty payload =
(io.teaql.core.sql.GenericSQLProperty) probe.addSimpleProperty("payload", String.class);
payload.setTableName(table);
payload.setColumnName("payload");
payload.setColumnType("LARGE_TEXT");
isolated.register(probe);

JdbcSqlExecutor jdbc = new JdbcSqlExecutor(dataSource);
TeaQLRuntime isolatedRuntime = TeaQLRuntime.builder()
.metadata(isolated)
.dataService("mysql", new MysqlDataServiceExecutor("mysql", jdbc, dataSource))
.build();
UserContext isolatedContext = new DefaultUserContext(isolatedRuntime);
isolatedContext.ensureSchema();
isolatedContext.ensureSchema();

List<java.util.Map<String, Object>> columns = jdbc.queryForList(
"SELECT data_type FROM information_schema.columns "
+ "WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ?",
new Object[] {table, "payload"});
assertEquals(1, columns.size());
assertEquals("longtext", String.valueOf(columns.get(0).values().iterator().next())
.toLowerCase(java.util.Locale.ROOT));
}

@Test
public void testCanonicalRelationIndexIsIdempotentOnLiveMysql() {
SimpleEntityMetaFactory isolated = relationMetadata("mysql");
Expand Down
Loading