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

[DAT-18260] Add tblProperties to createView #184

Merged
merged 5 commits into from
Sep 25, 2024
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package liquibase.ext.databricks.change.createView;

import liquibase.change.DatabaseChange;
import liquibase.change.DatabaseChangeProperty;
import liquibase.change.core.CreateViewChange;
import liquibase.database.Database;
import liquibase.ext.databricks.database.DatabricksDatabase;
import liquibase.servicelocator.PrioritizedService;
import liquibase.statement.core.CreateViewStatement;
import lombok.Setter;


@DatabaseChange(name = "createView", description = "Create View", priority = PrioritizedService.PRIORITY_DATABASE)
@Setter
public class CreateViewChangeDatabricks extends CreateViewChange {
private String tblProperties;

@Override
public boolean supports(Database database) {
return database instanceof DatabricksDatabase;
}

@Override
protected CreateViewStatement createViewStatement(String catalogName, String schemaName, String viewName, String selectQuery, boolean replaceIfExists) {
CreateViewStatementDatabricks cvsd = new CreateViewStatementDatabricks(catalogName, schemaName, viewName, selectQuery, replaceIfExists);
cvsd.setTblProperties(this.getTblProperties());
return cvsd;
}


@DatabaseChangeProperty
public String getTblProperties() {
return tblProperties;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package liquibase.ext.databricks.change.createView;

import liquibase.statement.core.CreateViewStatement;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class CreateViewStatementDatabricks extends CreateViewStatement {

private String tblProperties;

public CreateViewStatementDatabricks(String catalogName, String schemaName, String viewName, String selectQuery, boolean replaceIfExists) {
super(catalogName, schemaName, viewName, selectQuery, replaceIfExists);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package liquibase.ext.databricks.sqlgenerator;

import liquibase.Scope;
import liquibase.database.Database;
import liquibase.ext.databricks.change.createView.CreateViewStatementDatabricks;
import liquibase.ext.databricks.database.DatabricksDatabase;
import liquibase.parser.LiquibaseSqlParser;
import liquibase.parser.SqlParserFactory;
import liquibase.sql.Sql;
import liquibase.sql.UnparsedSql;
import liquibase.sqlgenerator.SqlGeneratorChain;
import liquibase.sqlgenerator.core.CreateViewGenerator;
import liquibase.statement.core.CreateViewStatement;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;

public class CreateViewGeneratorDatabricks extends CreateViewGenerator {

@Override
public int getPriority() {
return PRIORITY_DATABASE;
}

@Override
public boolean supports(CreateViewStatement statement, Database database) {
return super.supports(statement, database) && (database instanceof DatabricksDatabase);
Copy link
Contributor

@SvampX SvampX Sep 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might check instanceof first, because boolean statements with double signs like '&&' are "fail fast" and if DB is inapropriate there won't be a call to super.
I'm not sure if my propose makes sence in readability perspective though.

}

@Override
public Sql[] generateSql(CreateViewStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) {
List<Sql> sql = new ArrayList<>();

SqlParserFactory sqlParserFactory = Scope.getCurrentScope().getSingleton(SqlParserFactory.class);
LiquibaseSqlParser sqlParser = sqlParserFactory.getSqlParser();
String viewDefinition = sqlParser.parse(statement.getSelectQuery(), true, true).toString();

if (!statement.isFullDefinition()) {
viewDefinition = "CREATE VIEW " +
database.escapeViewName(statement.getCatalogName(), statement.getSchemaName(), statement.getViewName()) +
addTblProperties(statement) +
" AS " + viewDefinition;
}

if (statement.isReplaceIfExists() && !statement.getSelectQuery().toUpperCase().contains("OR REPLACE")) {
viewDefinition = viewDefinition.replace("CREATE", "CREATE OR REPLACE");
}

sql.add(new UnparsedSql(viewDefinition, getAffectedView(statement)));
return sql.toArray(EMPTY_SQL);
}

private String addTblProperties(CreateViewStatement statement) {
if (statement instanceof CreateViewStatementDatabricks) {
CreateViewStatementDatabricks thisStatement = (CreateViewStatementDatabricks) statement;

if (StringUtils.isNotEmpty(thisStatement.getTblProperties()) && !statement.getSelectQuery().toUpperCase().contains("TBLPROPERTIES")) {
return " TBLPROPERTIES (" + thisStatement.getTblProperties() + ")";
}
}
return "";
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
liquibase.ext.databricks.change.createTable.CreateTableChangeDatabricks
liquibase.ext.databricks.change.createView.CreateViewChangeDatabricks
liquibase.ext.databricks.change.optimizeTable.OptimizeTableChange
liquibase.ext.databricks.change.analyzeTable.AnalyzeTableChange
liquibase.ext.databricks.change.vacuumTable.VacuumTableChange
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
liquibase.ext.databricks.sqlgenerator.CreateTableGeneratorDatabricks
liquibase.ext.databricks.sqlgenerator.CreateViewGeneratorDatabricks
liquibase.ext.databricks.change.optimizeTable.OptimizeTableGenerator
liquibase.ext.databricks.change.vacuumTable.VacuumTableGenerator
liquibase.ext.databricks.change.analyzeTable.AnalyzeTableGenerator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,55 @@
</xsd:complexType>
</xsd:element>

<!-- Attributes for changes -->
<xsd:attributeGroup name="changeAttributes">
<xsd:anyAttribute namespace="##other" processContents="lax"/>
</xsd:attributeGroup>

<xsd:simpleType name="propertyExpression" id="propertyExpression">
<xsd:restriction base="xsd:string">
<xsd:pattern value="$\{[\w\.]+\}"/>
</xsd:restriction>
</xsd:simpleType>

<xsd:simpleType name="booleanExp" id="booleanExp">
<xsd:annotation>
<xsd:appinfo>
<xsd:documentation>Extension to standard XSD boolean type to allow ${} parameters</xsd:documentation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union>
<xsd:simpleType>
<xsd:restriction base="xsd:boolean"/>
</xsd:simpleType>
<xsd:simpleType>
<xsd:restriction base="propertyExpression"/>
</xsd:simpleType>
</xsd:union>
</xsd:simpleType>

<!-- Children for createView -->
<xsd:element name="createView">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attributeGroup ref="changeAttributes"/>
<xsd:attribute name="catalogName" type="xsd:string"/>
<xsd:attribute name="schemaName" type="xsd:string"/>
<xsd:attribute name="viewName" type="xsd:string" use="required"/>
<xsd:attribute name="remarks" type="xsd:string"/>
<xsd:attribute name="replaceIfExists" type="booleanExp"/>
<xsd:attribute name="fullDefinition" type="booleanExp"/>
<xsd:attribute name="path" type="xsd:string"/>
<xsd:attribute name="encoding" type="xsd:string"/>
<xsd:attribute name="relativeToChangelogFile" type="booleanExp"/>
<xsd:attribute name="tblProperties" type="xsd:string" />
<xsd:anyAttribute namespace="##other" processContents="lax"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>

<xsd:element name="alterTableProperties">
<xsd:complexType>
<xsd:choice maxOccurs="1" minOccurs="1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,56 @@
</xsd:complexType>
</xsd:element>


<!-- Attributes for changes -->
<xsd:attributeGroup name="changeAttributes">
<xsd:anyAttribute namespace="##other" processContents="lax"/>
</xsd:attributeGroup>

<xsd:simpleType name="propertyExpression" id="propertyExpression">
<xsd:restriction base="xsd:string">
<xsd:pattern value="$\{[\w\.]+\}"/>
</xsd:restriction>
</xsd:simpleType>

<xsd:simpleType name="booleanExp" id="booleanExp">
<xsd:annotation>
<xsd:appinfo>
<xsd:documentation>Extension to standard XSD boolean type to allow ${} parameters</xsd:documentation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union>
<xsd:simpleType>
<xsd:restriction base="xsd:boolean"/>
</xsd:simpleType>
<xsd:simpleType>
<xsd:restriction base="propertyExpression"/>
</xsd:simpleType>
</xsd:union>
</xsd:simpleType>

<!-- Children for createView -->
<xsd:element name="createView">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attributeGroup ref="changeAttributes"/>
<xsd:attribute name="catalogName" type="xsd:string"/>
<xsd:attribute name="schemaName" type="xsd:string"/>
<xsd:attribute name="viewName" type="xsd:string" use="required"/>
<xsd:attribute name="remarks" type="xsd:string"/>
<xsd:attribute name="replaceIfExists" type="booleanExp"/>
<xsd:attribute name="fullDefinition" type="booleanExp"/>
<xsd:attribute name="path" type="xsd:string"/>
<xsd:attribute name="encoding" type="xsd:string"/>
<xsd:attribute name="relativeToChangelogFile" type="booleanExp"/>
<xsd:attribute name="tblProperties" type="xsd:string" />
<xsd:anyAttribute namespace="##other" processContents="lax"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>

<xsd:element name="alterTableProperties">
<xsd:complexType>
<xsd:choice maxOccurs="1" minOccurs="1">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package liquibase.ext.databricks.sqlgenerator

import liquibase.ext.databricks.change.createView.CreateViewStatementDatabricks
import liquibase.ext.databricks.database.DatabricksDatabase
import liquibase.sqlgenerator.SqlGeneratorFactory
import spock.lang.Specification

class CreateViewGeneratorDatabricksTest extends Specification {

def "creates a view from a sql"() {
when:
def selectQuery = "SELECT SYSDATE FROM DUAL"
def statement = new CreateViewStatementDatabricks("PUBLIC", "schema", "my_view", selectQuery, false)
def generators = SqlGeneratorFactory.instance.getGenerators(statement, new DatabricksDatabase())

then:
generators.size() > 0
generators[0] instanceof CreateViewGeneratorDatabricks

when:
def sql = SqlGeneratorFactory.instance.generateSql(statement, new DatabricksDatabase())

then:
sql.length == 1
sql[0].toString() == "CREATE VIEW PUBLIC.schema.my_view AS " + selectQuery + ";"
}

def "creates a view with tblProperties"() {
when:
def selectQuery = "SELECT * FROM mytable"
def tblProperties = "'external.location'='s3://mybucket/mytable','this.is.my.key'=12,'this.is.my.key2'=true"
def statement = new CreateViewStatementDatabricks("main", "schema", "my_view", selectQuery, false)
statement.tblProperties = tblProperties
def sqla = SqlGeneratorFactory.instance.generateSql(statement, new DatabricksDatabase())

then:
sqla.length == 1

when:
def sql = sqla[0].toString()

then:
sql == "CREATE VIEW main.schema.my_view TBLPROPERTIES (" + tblProperties + ") AS " + selectQuery + ";"
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:databricks="http://www.liquibase.org/xml/ns/databricks"

xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd
http://www.liquibase.org/xml/ns/databricks
http://www.liquibase.org/xml/ns/databricks/liquibase-databricks-latest.xsd">

<changeSet id="2" author="fl">
<databricks:createView viewName="test_view" tblProperties="'external.location'='s3://mybucket/myview','this.is.my.key'=12,'this.is.my.key2'=true" >
select id, first_name, last_name, email from authors
</databricks:createView>
</changeSet>
</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
CREATE VIEW main.liquibase_harness_test_ds.test_view AS select id, first_name, last_name, email from authors
CREATE VIEW main.liquibase_harness_test_ds.test_view TBLPROPERTIES ('external.location'='s3://mybucket/myview','this.is.my.key'=12,'this.is.my.key2'=true) AS select id, first_name, last_name, email from authors
Loading