Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QuestDB Support #3656

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (C) Red Gate Software Ltd 2010-2022
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.flywaydb.community.database.questdb;

import java.util.concurrent.Callable;

import org.flywaydb.core.internal.database.base.Schema;
import org.flywaydb.core.internal.database.base.Table;
import org.flywaydb.core.internal.database.postgresql.PostgreSQLConnection;
import org.flywaydb.core.internal.jdbc.ExecutionTemplateFactory;

public class QuestDBConnection extends PostgreSQLConnection {

// QuestDB doesn't support schemas, everything is under qdb
private final QuestDBSchema schema = new QuestDBSchema(jdbcTemplate, (QuestDBDatabase) database, "qdb");

QuestDBConnection(QuestDBDatabase database, java.sql.Connection connection) {
super(database, connection);
}

@Override
public Schema getSchema(String name) {
return schema;
}

@Override
protected void doRestoreOriginalState() {
}

@Override
public Schema doGetCurrentSchema() {
return schema;
}

@Override
protected String getCurrentSchemaNameOrSearchPath() {
return "qdb";
}

@Override
public void changeCurrentSchemaTo(final Schema schema) {
// QuestDB's postgres implementation is not really schema-aware. You connect to "qdb" but "schemas" are just string-prefixed table names, nothing more
}

@Override
public void doChangeCurrentSchemaOrSearchPathTo(final String schema) {
}

@Override
public <T> T lock(final Table table, final Callable<T> callable) {
return ExecutionTemplateFactory
.createTableExclusiveExecutionTemplate(jdbcTemplate.getConnection(), table, database)
.execute(callable);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (C) Red Gate Software Ltd 2010-2022
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.flywaydb.community.database.questdb;

import java.sql.Connection;

import org.flywaydb.core.api.configuration.Configuration;
import org.flywaydb.core.internal.database.base.Table;
import org.flywaydb.core.internal.database.postgresql.PostgreSQLDatabase;
import org.flywaydb.core.internal.jdbc.JdbcConnectionFactory;
import org.flywaydb.core.internal.jdbc.StatementInterceptor;

public class QuestDBDatabase extends PostgreSQLDatabase {

public QuestDBDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) {
super(configuration, jdbcConnectionFactory, statementInterceptor);
}

@Override
protected QuestDBConnection doGetConnection(Connection connection) {
return new QuestDBConnection(this, connection);
}

@Override
public void ensureSupported() {
// Checks the Postgres version
ensureDatabaseIsRecentEnough("11.3");
}

@Override
public boolean supportsDdlTransactions() {
return false;
}

@Override
public String getRawCreateScript(Table table, boolean baseline) {
return "CREATE TABLE " + table.getName() + " (\n" +
" \"installed_rank\" INT,\n" +
" \"version\" STRING,\n" +
" \"description\" STRING,\n" +
" \"type\" STRING,\n" +
" \"script\" STRING,\n" +
" \"checksum\" INT,\n" +
" \"installed_by\" STRING,\n" +
" \"installed_on\" TIMESTAMP,\n" +
" \"execution_time\" INT,\n" +
" \"success\" BOOLEAN\n" +
") timestamp (installed_on);\n";
}

@Override
public String getInsertStatement(Table table) {
return "INSERT INTO " + table.getName()
+ " (" + quote("installed_rank")
+ ", " + quote("version")
+ ", " + quote("description")
+ ", " + quote("type")
+ ", " + quote("script")
+ ", " + quote("checksum")
+ ", " + quote("installed_by")
+ ", " + quote("installed_on")
+ ", " + quote("execution_time")
+ ", " + quote("success")
+ ")"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, now(), ?, ?)";
}

public String getSelectStatement(Table table) {
return "SELECT " + quote("installed_rank")
+ "," + quote("version")
+ "," + quote("description")
+ "," + quote("type")
+ "," + quote("script")
+ "," + quote("checksum")
+ "," + quote("installed_on")
+ "," + quote("installed_by")
+ "," + quote("execution_time")
+ "," + quote("success")
+ " FROM " + table.getName()
+ " WHERE " + quote("installed_rank") + " > ?"
+ " ORDER BY " + quote("installed_rank");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (C) Red Gate Software Ltd 2010-2022
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.flywaydb.community.database.questdb;

import java.sql.Connection;
import java.sql.SQLException;

import org.flywaydb.core.api.ResourceProvider;
import org.flywaydb.core.api.configuration.Configuration;
import org.flywaydb.core.internal.database.base.Database;
import org.flywaydb.core.internal.database.postgresql.PostgreSQLDatabaseType;
import org.flywaydb.core.internal.jdbc.JdbcConnectionFactory;
import org.flywaydb.core.internal.jdbc.StatementInterceptor;
import org.flywaydb.core.internal.parser.Parser;
import org.flywaydb.core.internal.parser.ParsingContext;

public class QuestDBDatabaseType extends PostgreSQLDatabaseType {
@Override
public String getName() {
return "QuestDB";
}

@Override
public boolean handlesJDBCUrl(String url) {
return url.startsWith("jdbc:postgresql:");
}

@Override
public int getPriority() {
// Should be checked before plain PostgreSQL
return 1;
}

@Override
public boolean handlesDatabaseProductNameAndVersion(String databaseProductName, String databaseProductVersion, Connection connection) {
return databaseProductName.startsWith("PostgreSQL") && databaseProductName.endsWith("QuestDB");
}

@Override
public Database createDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) {
return new QuestDBDatabase(configuration, jdbcConnectionFactory, statementInterceptor);
}

@Override
public Parser createParser(Configuration configuration, ResourceProvider resourceProvider, ParsingContext parsingContext) {
return new QuestDBParser(configuration, parsingContext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (C) Red Gate Software Ltd 2010-2022
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.flywaydb.community.database.questdb;

import org.flywaydb.core.api.configuration.Configuration;
import org.flywaydb.core.internal.database.postgresql.PostgreSQLParser;
import org.flywaydb.core.internal.parser.ParsingContext;

public class QuestDBParser extends PostgreSQLParser {
protected QuestDBParser(Configuration configuration, ParsingContext parsingContext) {
super(configuration, parsingContext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (C) Red Gate Software Ltd 2010-2022
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.flywaydb.community.database.questdb;

import java.sql.SQLException;
import java.util.List;
import java.util.Set;

import javax.naming.OperationNotSupportedException;

import org.flywaydb.core.internal.database.base.Table;
import org.flywaydb.core.internal.database.base.Type;
import org.flywaydb.core.internal.database.postgresql.PostgreSQLSchema;
import org.flywaydb.core.internal.database.postgresql.PostgreSQLTable;
import org.flywaydb.core.internal.jdbc.JdbcTemplate;

public class QuestDBSchema extends PostgreSQLSchema {

public static final Set<String> IGNORED_TABLES = Set.of("sys.telemetry_wal", "telemetry", "telemetry_config", "sys.column_versions_purge_log");

/**
* @param jdbcTemplate The Jdbc Template for communicating with the DB.
* @param database The database-specific support.
* @param name The name of the schema.
*/
public QuestDBSchema(JdbcTemplate jdbcTemplate, QuestDBDatabase database, String name) {
super(jdbcTemplate, database, name);
}

@Override
public Table getTable(String tableName) {
return new QuestDBTable(jdbcTemplate, (QuestDBDatabase) database, this, tableName);
}

@Override
protected boolean doExists() {
return true;
}

@Override
protected boolean doEmpty() throws SQLException {
return IGNORED_TABLES.containsAll(jdbcTemplate.queryForStringList("show tables"));
}

@Override
protected void doCreate() {

}

@Override
protected void doDrop() {

}

@Override
protected void doClean() {
for (Table table : allTables()) {
table.drop();
}
}

@Override
protected PostgreSQLTable[] doAllTables() throws SQLException {
List<String> tableNames =
jdbcTemplate.queryForStringList("SHOW TABLES").stream()
.filter(tbl -> !IGNORED_TABLES.contains(tbl))
.toList();
//Views and child tables are excluded as they are dropped with the parent table when using cascade.

PostgreSQLTable[] tables = new PostgreSQLTable[tableNames.size()];
for (int i = 0; i < tableNames.size(); i++) {
tables[i] = new QuestDBTable(jdbcTemplate, (QuestDBDatabase)database, this, tableNames.get(i));
}
return tables;
}

@Override
protected Type getType(final String typeName) {
throw new RuntimeException(new OperationNotSupportedException("This should not be called"));
}
}