DATAJDBC-335 - Add DSL and renderer for Insert, Update, and Delete.

We now provide Statement builders for Insert, Update, and Delete statements.

Table myTable = SQL.table("mytable");

Insert insert = StatementBuilder.insert().into(myTable).values(SQL.bindMarker()).build();

Update update = StatementBuilder.update(table).set(myTable.column("foo").set(SQL.bindMarker())).build();

Delete delete = StatementBuilder.delete().from(table).where(myTable.column("foo").isEqualTo(SQL.literal("bar"))).build();

Original pull request: #121.
This commit is contained in:
Mark Paluch
2019-02-28 14:31:21 +01:00
committed by Jens Schauder
parent 2aa44a38fd
commit a11c121407
49 changed files with 3067 additions and 101 deletions

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import java.util.HashSet;
import java.util.Set;
/**
* Validator for statements to import columns.
*
* @author Mark Paluch
* @since 1.1
*/
abstract class AbstractImportValidator implements Visitor {
Set<Table> requiredByWhere = new HashSet<>();
Set<Table> from = new HashSet<>();
Visitable parent;
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.Visitor#enter(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
public void enter(Visitable segment) {
if (segment instanceof Table && parent instanceof From) {
from.add((Table) segment);
}
if (segment instanceof Where) {
segment.visit(item -> {
if (item instanceof Table) {
requiredByWhere.add((Table) item);
}
});
}
if (segment instanceof Join || segment instanceof OrderByField || segment instanceof From
|| segment instanceof Select || segment instanceof Where || segment instanceof SimpleFunction) {
parent = segment;
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.Visitor#leave(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
public void leave(Visitable segment) {}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import org.springframework.util.Assert;
/**
* Assign a {@link Expression} to a {@link Column}.
* <p/>
* Results in a rendered assignment: {@code <column> = <value>} (e.g. {@code col = 'foo'}.
*
* @author Mark Paluch
* @since 1.1
*/
public class AssignValue extends AbstractSegment implements Assignment {
private final Column column;
private final Expression value;
private AssignValue(Column column, Expression value) {
super(column, value);
this.column = column;
this.value = value;
}
/**
* Creates a {@link AssignValue value} assignment to a {@link Column} given an {@link Expression}.
*
* @param target target column, must not be {@literal null}.
* @param value assignment value, must not be {@literal null}.
* @return the {@link AssignValue}.
*/
public static AssignValue create(Column target, Expression value) {
Assert.notNull(target, "Target column must not be null!");
Assert.notNull(value, "Value must not be null!");
return new AssignValue(target, value);
}
/**
* @return the target {@link Column}.
*/
public Column getColumn() {
return column;
}
/**
* @return the value to assign.
*/
public Expression getValue() {
return value;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder(32);
return builder.append(this.column).append(" = ").append(this.value).toString();
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
/**
* Update assignment to a {@link Column}.
*
* @author Mark Paluch
*/
public interface Assignment extends Segment {}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
/**
* Factory for common {@link Assignment}s.
*
* @author Mark Paluch
* @since 1.1
* @see SQL
* @see Expressions
* @see Functions
*/
public abstract class Assignments {
/**
* Creates a {@link AssignValue value} assignment to a {@link Column} given an {@link Expression}.
*
* @param target target column, must not be {@literal null}.
* @param value assignment value, must not be {@literal null}.
* @return the {@link AssignValue}.
*/
public static AssignValue value(Column target, Expression value) {
return AssignValue.create(target, value);
}
// Utility constructor.
private Assignments() {}
}

View File

@@ -200,6 +200,20 @@ public class Column extends AbstractSegment implements Expression, Named {
return isNull().not();
}
// -------------------------------------------------------------------------
// Methods for Assignment creation.
// -------------------------------------------------------------------------
/**
* Creates a value {@link AssignValue assignment}.
*
* @param value the value to assign.
* @return the {@link AssignValue} assignment.
*/
public AssignValue set(Expression value) {
return Assignments.value(this, value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.Named#getName()

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Default {@link Delete} implementation.
*
* @author Mark Paluch
* @since 1.1
*/
class DefaultDelete implements Delete {
private final From from;
private final @Nullable Where where;
DefaultDelete(Table table, @Nullable Condition where) {
this.from = new From(table);
this.where = where != null ? new Where(where) : null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.Visitable#visit(org.springframework.data.relational.core.sql.Visitor)
*/
@Override
public void visit(Visitor visitor) {
Assert.notNull(visitor, "Visitor must not be null!");
visitor.enter(this);
from.visit(visitor);
if (where != null) {
where.visit(visitor);
}
visitor.leave(this);
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("DELETE ").append(this.from);
if (this.where != null) {
builder.append(' ').append(this.where);
}
return builder.toString();
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Default {@link SelectBuilder} implementation.
*
* @author Mark Paluch
* @since 1.1
*/
class DefaultDeleteBuilder implements DeleteBuilder, DeleteBuilder.DeleteWhereAndOr, DeleteBuilder.DeleteWhere {
private Table from;
private @Nullable Condition where;
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.DeleteBuilder#from(org.springframework.data.relational.core.sql.Table)
*/
@Override
public DeleteWhere from(Table table) {
Assert.notNull(table, "Table must not be null!");
this.from = table;
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.DeleteBuilder.DeleteWhere#where(org.springframework.data.relational.core.sql.Condition)
*/
@Override
public DeleteWhereAndOr where(Condition condition) {
Assert.notNull(condition, "Where Condition must not be null!");
this.where = condition;
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.DeleteBuilder.DeleteWhereAndOr#and(org.springframework.data.relational.core.sql.Condition)
*/
@Override
public DeleteWhereAndOr and(Condition condition) {
Assert.notNull(condition, "Condition must not be null!");
this.where = this.where.and(condition);
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.DeleteBuilder.DeleteWhereAndOr#or(org.springframework.data.relational.core.sql.Condition)
*/
@Override
public DeleteWhereAndOr or(Condition condition) {
Assert.notNull(condition, "Condition must not be null!");
this.where = this.where.or(condition);
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.DeleteBuilder.BuildDelete#build()
*/
@Override
public Delete build() {
DefaultDelete delete = new DefaultDelete(this.from, this.where);
DeleteValidator.validate(delete);
return delete;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import java.util.ArrayList;
import java.util.List;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Default {@link Insert} implementation.
*
* @author Mark Paluch
* @since 1.1
*/
class DefaultInsert implements Insert {
private final Into into;
private final List<Column> columns;
private final Values values;
DefaultInsert(@Nullable Table into, List<Column> columns, List<Expression> values) {
this.into = new Into(into);
this.columns = new ArrayList<>(columns);
this.values = new Values(new ArrayList<>(values));
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.Visitable#visit(org.springframework.data.relational.core.sql.Visitor)
*/
@Override
public void visit(Visitor visitor) {
Assert.notNull(visitor, "Visitor must not be null!");
visitor.enter(this);
into.visit(visitor);
columns.forEach(it -> it.visit(visitor));
values.visit(visitor);
visitor.leave(this);
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("INSERT ").append(this.into);
if (!this.columns.isEmpty()) {
builder.append(" (").append(StringUtils.collectionToDelimitedString(this.columns, ", ")).append(")");
}
builder.append(" ").append(this.values);
return builder.toString();
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.springframework.util.Assert;
/**
* Default {@link InsertBuilder} implementation.
*
* @author Mark Paluch
* @since 1.1
*/
class DefaultInsertBuilder
implements InsertBuilder, InsertBuilder.InsertIntoColumnsAndValuesWithBuild, InsertBuilder.InsertValuesWithBuild {
private Table into;
private List<Column> columns = new ArrayList<>();
private List<Expression> values = new ArrayList<>();
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.InsertBuilder#into(org.springframework.data.relational.core.sql.Table)
*/
@Override
public InsertIntoColumnsAndValues into(Table table) {
Assert.notNull(table, "Insert Into Table must not be null!");
this.into = table;
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.InsertBuilder.InsertIntoColumnsAndValues#column(org.springframework.data.relational.core.sql.Column)
*/
@Override
public InsertIntoColumnsAndValuesWithBuild column(Column column) {
Assert.notNull(column, "Column must not be null!");
this.columns.add(column);
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.InsertBuilder.InsertIntoColumnsAndValues#columns(org.springframework.data.relational.core.sql.Column[])
*/
@Override
public InsertIntoColumnsAndValuesWithBuild columns(Column... columns) {
Assert.notNull(columns, "Columns must not be null!");
return columns(Arrays.asList(columns));
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.InsertBuilder.InsertIntoColumnsAndValues#columns(java.util.Collection)
*/
@Override
public InsertIntoColumnsAndValuesWithBuild columns(Collection<Column> columns) {
Assert.notNull(columns, "Columns must not be null!");
this.columns.addAll(columns);
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.InsertBuilder.InsertIntoColumnsAndValuesWithBuild#value(org.springframework.data.relational.core.sql.Expression)
*/
@Override
public InsertValuesWithBuild value(Expression value) {
Assert.notNull(value, "Value must not be null!");
this.values.add(value);
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.InsertBuilder.InsertIntoColumnsAndValuesWithBuild#values(org.springframework.data.relational.core.sql.Expression[])
*/
@Override
public InsertValuesWithBuild values(Expression... values) {
Assert.notNull(values, "Values must not be null!");
return values(Arrays.asList(values));
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.InsertBuilder.InsertIntoColumnsAndValuesWithBuild#values(java.util.Collection)
*/
@Override
public InsertValuesWithBuild values(Collection<? extends Expression> values) {
Assert.notNull(values, "Values must not be null!");
this.values.addAll(values);
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.InsertBuilder.BuildInsert#build()
*/
@Override
public Insert build() {
return new DefaultInsert(this.into, this.columns, this.values);
}
}

View File

@@ -44,7 +44,7 @@ class DefaultSelect implements Select {
this.distinct = distinct;
this.selectList = new SelectList(new ArrayList<>(selectList));
this.from = new From(from);
this.from = new From(new ArrayList<>(from));
this.limit = limit;
this.offset = offset;
this.joins = new ArrayList<>(joins);

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import java.util.ArrayList;
import java.util.List;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Default {@link Update} implementation.
*
* @author Mark Paluch
* @since 1.1
*/
class DefaultUpdate implements Update {
private final Table table;
private final List<Assignment> assignments;
private final @Nullable Where where;
DefaultUpdate(Table table, List<Assignment> assignments, @Nullable Condition where) {
this.table = table;
this.assignments = new ArrayList<>(assignments);
this.where = where != null ? new Where(where) : null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.Visitable#visit(org.springframework.data.relational.core.sql.Visitor)
*/
@Override
public void visit(Visitor visitor) {
Assert.notNull(visitor, "Visitor must not be null!");
visitor.enter(this);
this.table.visit(visitor);
this.assignments.forEach(it -> it.visit(visitor));
if (this.where != null) {
this.where.visit(visitor);
}
visitor.leave(this);
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder(32);
builder.append("UPDATE ").append(table);
if (!assignments.isEmpty()) {
builder.append(" SET ").append(StringUtils.collectionToDelimitedString(this.assignments, ", "));
}
if (this.where != null) {
builder.append(" WHERE ").append(this.where);
}
return builder.toString();
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.relational.core.sql.UpdateBuilder.UpdateAssign;
import org.springframework.data.relational.core.sql.UpdateBuilder.UpdateAssignAnd;
import org.springframework.data.relational.core.sql.UpdateBuilder.UpdateWhere;
import org.springframework.data.relational.core.sql.UpdateBuilder.UpdateWhereAndOr;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Default {@link UpdateBuilder} implementation.
*
* @author Mark Paluch
* @since 1.1
*/
class DefaultUpdateBuilder implements UpdateBuilder, UpdateWhere, UpdateWhereAndOr, UpdateAssign, UpdateAssignAnd {
private Table table;
private List<Assignment> assignments = new ArrayList<>();
private @Nullable Condition where;
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.UpdateBuilder#table(org.springframework.data.relational.core.sql.Table)
*/
@Override
public UpdateAssign table(Table table) {
Assert.notNull(table, "Table must not be null!");
this.table = table;
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.UpdateBuilder.UpdateAssign#set(org.springframework.data.relational.core.sql.Assignment)
*/
@Override
public DefaultUpdateBuilder set(Assignment assignment) {
Assert.notNull(assignment, "Assignment must not be null!");
this.assignments.add(assignment);
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.UpdateBuilder.UpdateAssignAnd#and(org.springframework.data.relational.core.sql.Assignment)
*/
@Override
public DefaultUpdateBuilder and(Assignment assignment) {
return set(assignment);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.UpdateBuilder.UpdateWhere#where(org.springframework.data.relational.core.sql.Condition)
*/
@Override
public UpdateWhereAndOr where(Condition condition) {
Assert.notNull(condition, "Condition must not be null!");
this.where = condition;
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.UpdateBuilder.UpdateWhereAndOr#and(org.springframework.data.relational.core.sql.Condition)
*/
@Override
public UpdateWhereAndOr and(Condition condition) {
Assert.notNull(condition, "Condition must not be null!");
this.where = this.where.and(condition);
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.UpdateBuilder.UpdateWhereAndOr#or(org.springframework.data.relational.core.sql.Condition)
*/
@Override
public UpdateWhereAndOr or(Condition condition) {
Assert.notNull(condition, "Condition must not be null!");
this.where = this.where.and(condition);
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.UpdateBuilder.BuildUpdate#build()
*/
@Override
public Update build() {
return new DefaultUpdate(this.table, this.assignments, this.where);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
/**
* AST for a {@code DELETE} statement. Visiting order:
* <ol>
* <li>Self</li>
* <li>{@link Table FROM tables} clause</li>
* <li>{@link Where WHERE} condition</li>
* </ol>
*
* @author Mark Paluch
* @since 1.1
* @see StatementBuilder
* @see DeleteBuilder
* @see SQL
*/
public interface Delete extends Segment, Visitable {
/**
* Creates a new {@link DeleteBuilder}.
*
* @return a new {@link DeleteBuilder}.
*/
static DeleteBuilder builder() {
return new DefaultDeleteBuilder();
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
/**
* Entry point to construct a {@link Delete} statement.
*
* @author Mark Paluch
* @since 1.1
* @see StatementBuilder
*/
public interface DeleteBuilder {
/**
* Declare a {@link Table} for {@code DELETE FROM}.
*
* @param table the table to {@code DELETE FROM} must not be {@literal null}.
* @return {@code this} builder.
* @see From
* @see SQL#table(String)
*/
DeleteWhere from(Table table);
/**
* Interface exposing {@code WHERE} methods.
*/
interface DeleteWhere extends BuildDelete {
/**
* Apply a {@code WHERE} clause.
*
* @param condition the {@code WHERE} condition.
* @return {@code this} builder.
* @see Where
* @see Condition
*/
DeleteWhereAndOr where(Condition condition);
}
/**
* Interface exposing {@code AND}/{@code OR} combinator methods for {@code WHERE} {@link Condition}s.
*/
interface DeleteWhereAndOr extends BuildDelete {
/**
* Combine the previous {@code WHERE} {@link Condition} using {@code AND}.
*
* @param condition the condition, must not be {@literal null}.
* @return {@code this} builder.
* @see Condition#and(Condition)
*/
DeleteWhereAndOr and(Condition condition);
/**
* Combine the previous {@code WHERE} {@link Condition} using {@code OR}.
*
* @param condition the condition, must not be {@literal null}.
* @return {@code this} builder.
* @see Condition#or(Condition)
*/
DeleteWhereAndOr or(Condition condition);
}
/**
* Interface exposing the {@link Delete} build method.
*/
interface BuildDelete {
/**
* Build the {@link Delete} statement and verify basic relationship constraints such as all referenced columns have
* a {@code FROM} table import.
*
* @return the build and immutable {@link Delete} statement.
*/
Delete build();
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
/**
* Validator for {@link Delete} statements.
* <p/>
* Validates that all {@link Column}s using a table qualifier have a table import from the {@code FROM} clause.
*
* @author Mark Paluch
* @since 1.1
*/
class DeleteValidator extends AbstractImportValidator {
public static void validate(Delete select) {
new DeleteValidator().doValidate(select);
}
private void doValidate(Delete select) {
select.visit(this);
for (Table table : requiredByWhere) {
if (!from.contains(table)) {
throw new IllegalStateException(
String.format("Required table [%s] by a WHERE predicate not imported by FROM %s", table, from));
}
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
/**
* AST for a {@code INSERT} statement. Visiting order:
* <ol>
* <li>Self</li>
* <li>{@link Into INTO table} clause</li>
* <li>{@link Column columns}</li>
* <li>{@link Values VALUEs}</li>
* </ol>
*
* @author Mark Paluch
* @since 1.1
* @see StatementBuilder
* @see InsertBuilder
* @see SQL
*/
public interface Insert extends Segment, Visitable {
/**
* Creates a new {@link InsertBuilder}.
*
* @return a new {@link InsertBuilder}.
*/
static InsertBuilder builder() {
return new DefaultInsertBuilder();
}
}

View File

@@ -0,0 +1,201 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import java.util.Collection;
/**
* Entry point to construct an {@link Insert} statement.
*
* @author Mark Paluch
* @since 1.1
* @see StatementBuilder
*/
public interface InsertBuilder {
/**
* Declare a {@link Table} to {@code INSERT INTO}.
*
* @param table the table to {@code INSERT INTO} must not be {@literal null}.
* @return {@code this} builder.
* @see Into
* @see SQL#table(String)
*/
InsertIntoColumnsAndValues into(Table table);
/**
* Interface exposing {@code WHERE} methods.
*/
interface InsertIntoColumnsAndValues extends InsertValues {
/**
* Add a {@link Column} to the {@code INTO} column list. Calling this method multiple times will add the
* {@link Column} multiple times.
*
* @param column the column.
* @return {@code this} builder.
* @see Column
*/
InsertIntoColumnsAndValuesWithBuild column(Column column);
/**
* Add a one or more {@link Column} to the {@code INTO} column list. Calling this method multiple times will add the
* {@link Column} multiple times.
*
* @param columns the columns.
* @return {@code this} builder.
* @see Column
*/
InsertIntoColumnsAndValuesWithBuild columns(Column... columns);
/**
* Add a one or more {@link Column} to the {@code INTO} column list. Calling this method multiple times will add the
* {@link Column} multiple times.
*
* @param columns the columns.
* @return {@code this} builder.
* @see Column
*/
InsertIntoColumnsAndValuesWithBuild columns(Collection<Column> columns);
}
/**
* Interface exposing {@code value} methods to add values to the {@code INSERT} statement and the build method.
*/
interface InsertIntoColumnsAndValuesWithBuild extends InsertIntoColumnsAndValues, InsertValues, BuildInsert {
/**
* Add a {@link Expression value} to the {@code VALUES} list. Calling this method multiple times will add a
* {@link Expression value} multiple times.
*
* @param value the value to use.
* @return {@code this} builder.
* @see Column
*/
@Override
InsertValuesWithBuild value(Expression value);
/**
* Add one or more {@link Expression values} to the {@code VALUES} list. Calling this method multiple times will add
* a {@link Expression values} multiple times.
*
* @param values the values.
* @return {@code this} builder.
* @see Column
*/
@Override
InsertValuesWithBuild values(Expression... values);
/**
* Add one or more {@link Expression values} to the {@code VALUES} list. Calling this method multiple times will add
* a {@link Expression values} multiple times.
*
* @param values the values.
* @return {@code this} builder.
* @see Column
*/
@Override
InsertValuesWithBuild values(Collection<? extends Expression> values);
}
/**
* Interface exposing {@code value} methods to add values to the {@code INSERT} statement and the build method.
*/
interface InsertValuesWithBuild extends InsertValues, BuildInsert {
/**
* Add a {@link Expression value} to the {@code VALUES} list. Calling this method multiple times will add a
* {@link Expression value} multiple times.
*
* @param value the value to use.
* @return {@code this} builder.
* @see Column
*/
@Override
InsertValuesWithBuild value(Expression value);
/**
* Add one or more {@link Expression values} to the {@code VALUES} list. Calling this method multiple times will add
* a {@link Expression values} multiple times.
*
* @param values the values.
* @return {@code this} builder.
* @see Column
*/
@Override
InsertValuesWithBuild values(Expression... values);
/**
* Add one or more {@link Expression values} to the {@code VALUES} list. Calling this method multiple times will add
* a {@link Expression values} multiple times.
*
* @param values the values.
* @return {@code this} builder.
* @see Column
*/
@Override
InsertValuesWithBuild values(Collection<? extends Expression> values);
}
/**
* Interface exposing {@code value} methods to add values to the {@code INSERT} statement.
*/
interface InsertValues {
/**
* Add a {@link Expression value} to the {@code VALUES} list. Calling this method multiple times will add a
* {@link Expression value} multiple times.
*
* @param value the value to use.
* @return {@code this} builder.
* @see Column
*/
InsertValuesWithBuild value(Expression value);
/**
* Add one or more {@link Expression values} to the {@code VALUES} list. Calling this method multiple times will add
* a {@link Expression values} multiple times.
*
* @param values the values.
* @return {@code this} builder.
* @see Column
*/
InsertValuesWithBuild values(Expression... values);
/**
* Add one or more {@link Expression values} to the {@code VALUES} list. Calling this method multiple times will add
* a {@link Expression values} multiple times.
*
* @param values the values.
* @return {@code this} builder.
* @see Column
*/
InsertValuesWithBuild values(Collection<? extends Expression> values);
}
/**
* Interface exposing the {@link Insert} build method.
*/
interface BuildInsert {
/**
* Build the {@link Insert} statement.
*
* @return the build and immutable {@link Insert} statement.
*/
Insert build();
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import java.util.Arrays;
import java.util.List;
import org.springframework.util.StringUtils;
/**
* {@code INTO} clause.
*
* @author Mark Paluch
* @since 1.1
*/
public class Into extends AbstractSegment {
private final List<Table> tables;
Into(Table... tables) {
this(Arrays.asList(tables));
}
Into(List<Table> tables) {
super(tables.toArray(new Table[] {}));
this.tables = tables;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "INTO " + StringUtils.collectionToDelimitedString(tables, ", ");
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import org.springframework.lang.Nullable;
/**
* Represents a literal.
*
* @author Mark Paluch
* @since 1.1
*/
public class Literal<T> extends AbstractSegment implements Expression {
private @Nullable T content;
Literal(@Nullable T content) {
this.content = content;
}
/**
* @return the content of the literal.
*/
@Nullable
public T getContent() {
return content;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
if (this.content == null) {
return "NULL";
}
return content.toString();
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import org.springframework.lang.Nullable;
/**
* Represents a {@link Number} literal.
*
* @author Mark Paluch
* @since 1.1
*/
public class NumericLiteral extends Literal<Number> {
NumericLiteral(@Nullable Number content) {
super(content);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.Literal#getContent()
*/
@Override
@Nullable
public Number getContent() {
return super.getContent();
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.relational.core.sql;
import org.springframework.data.relational.core.sql.BindMarker.NamedBindMarker;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -77,6 +78,45 @@ public abstract class SQL {
return new NamedBindMarker(name);
}
/**
* Creates a new {@link StringLiteral} from the {@code content}.
*
* @param content the literal content.
* @return a new {@link StringLiteral}.
*/
public static StringLiteral literalOf(@Nullable CharSequence content) {
return new StringLiteral(content);
}
/**
* Creates a new {@link NumericLiteral} from the {@code content}.
*
* @param content the literal content.
* @return a new {@link NumericLiteral}.
*/
public static NumericLiteral literalOf(@Nullable Number content) {
return new NumericLiteral(content);
}
/**
* Creates a new {@link Literal} from the {@code content}.
*
* @param content the literal content.
* @return a new {@link Literal}.
*/
public static <T> Literal<T> literalOf(@Nullable T content) {
return new Literal<>(content);
}
/**
* Creates a new {@code NULL} {@link Literal}.
*
* @return a new {@link Literal}.
*/
public static <T> Literal<T> nullLiteral() {
return new Literal<>(null);
}
// Utility constructor.
private SQL() {}
}

View File

@@ -22,6 +22,7 @@ import java.util.Collection;
*
* @author Mark Paluch
* @since 1.1
* @see StatementBuilder
*/
public interface SelectBuilder {

View File

@@ -27,18 +27,14 @@ import java.util.Set;
* @author Mark Paluch
* @since 1.1
*/
class SelectValidator implements Visitor {
class SelectValidator extends AbstractImportValidator {
private int selectFieldCount;
private Set<Table> requiredBySelect = new HashSet<>();
private Set<Table> requiredByWhere = new HashSet<>();
private Set<Table> requiredByOrderBy = new HashSet<>();
private Set<Table> from = new HashSet<>();
private Set<Table> join = new HashSet<>();
private Visitable parent;
public static void validate(Select select) {
new SelectValidator().doValidate(select);
}
@@ -80,6 +76,8 @@ class SelectValidator implements Visitor {
@Override
public void enter(Visitable segment) {
super.enter(segment);
if (segment instanceof AsteriskFromTable && parent instanceof Select) {
Table table = ((AsteriskFromTable) segment).getTable();
@@ -97,10 +95,6 @@ class SelectValidator implements Visitor {
}
}
if (segment instanceof Table && parent instanceof From) {
from.add((Table) segment);
}
if (segment instanceof Column && parent instanceof OrderByField) {
Table table = ((Column) segment).getTable();
@@ -123,17 +117,5 @@ class SelectValidator implements Visitor {
}
});
}
if (segment instanceof Join || segment instanceof OrderByField || segment instanceof From
|| segment instanceof Select || segment instanceof Where || segment instanceof SimpleFunction) {
parent = segment;
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.Visitor#leave(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
public void leave(Visitable segment) {}
}

View File

@@ -15,8 +15,12 @@
*/
package org.springframework.data.relational.core.sql;
import static org.springframework.data.relational.core.sql.UpdateBuilder.*;
import java.util.Collection;
import org.springframework.data.relational.core.sql.DeleteBuilder.DeleteWhere;
import org.springframework.data.relational.core.sql.InsertBuilder.InsertIntoColumnsAndValues;
import org.springframework.data.relational.core.sql.SelectBuilder.SelectAndFrom;
/**
@@ -32,7 +36,7 @@ import org.springframework.data.relational.core.sql.SelectBuilder.SelectAndFrom;
public abstract class StatementBuilder {
/**
* Creates a new {@link SelectBuilder} by specifying a {@code SELECT} column.
* Creates a new {@link SelectBuilder} and includes the {@code SELECT} columns.
*
* @param expression the select list expression.
* @return the {@link SelectBuilder} containing {@link Expression}.
@@ -43,7 +47,7 @@ public abstract class StatementBuilder {
}
/**
* Creates a new {@link SelectBuilder} by specifying one or more {@code SELECT} columns.
* Creates a new {@link SelectBuilder} and includes one or more {@code SELECT} columns.
*
* @param expressions the select list expressions.
* @return the {@link SelectBuilder} containing {@link Expression}s.
@@ -54,10 +58,10 @@ public abstract class StatementBuilder {
}
/**
* Include one or more {@link Expression}s in the select list.
* Creates a new {@link SelectBuilder} and includes one or more {@link Expression}s in the select list.
*
* @param expressions the expressions to include.
* @return {@code this} builder.
* @return the {@link SelectBuilder} containing {@link Expression}s.
* @see Table#columns(String...)
*/
public static SelectAndFrom select(Collection<? extends Expression> expressions) {
@@ -74,7 +78,68 @@ public abstract class StatementBuilder {
return Select.builder();
}
private StatementBuilder() {
/**
* Creates a new {@link InsertBuilder} and declare the {@link Table} to insert into.
*
* @param table the table to insert into.
* @return the new {@link InsertBuilder}.
* @see Table#create(String)
*/
public static InsertIntoColumnsAndValues insert(Table table) {
return insert().into(table);
}
/**
* Creates a new {@link InsertBuilder}.
*
* @return the new {@link InsertBuilder}.
* @see InsertBuilder
*/
public static InsertBuilder insert() {
return Insert.builder();
}
/**
* Creates a new {@link UpdateBuilder} and declare the {@link Table} for the update.
*
* @param table the table for the update.
* @return the new {@link UpdateBuilder}.
* @see Table#create(String)
*/
public static UpdateAssign update(Table table) {
return update().table(table);
}
/**
* Creates a new {@link UpdateBuilder}.
*
* @return the new {@link UpdateBuilder}.
* @see UpdateBuilder
*/
public static UpdateBuilder update() {
return Update.builder();
}
/**
* Creates a new {@link DeleteBuilder} and declares the {@link Table} to delete from.
*
* @param table the table to delete from.
* @return {@code this} builder.
* @see Table#columns(String...)
*/
public static DeleteWhere delete(Table table) {
return delete().from(table);
}
/**
* Creates a new {@link DeleteBuilder}.
*
* @return the new {@link DeleteBuilder}.
* @see DeleteBuilder
*/
public static DeleteBuilder delete() {
return Delete.builder();
}
private StatementBuilder() {}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import org.springframework.lang.Nullable;
/**
* Represents a {@link CharSequence} literal.
*
* @author Mark Paluch
* @since 1.1
*/
public class StringLiteral extends Literal<CharSequence> {
StringLiteral(@Nullable CharSequence content) {
super(content);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.Literal#getContent()
*/
@Override
@Nullable
public CharSequence getContent() {
return super.getContent();
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.Literal#toString()
*/
@Override
public String toString() {
return "'" + super.toString() + "'";
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
/**
* AST for aa {@code UPDATE} statement. Visiting order:
* <ol>
* <li>Self</li>
* <li>{@link Table table}</li>
* <li>{@link Assignments assignments}</li>
* <li>{@link Where WHERE} condition</li>
* </ol>
*
* @author Mark Paluch
* @since 1.1
* @see StatementBuilder
* @see SelectBuilder
* @see SQL
*/
public interface Update extends Segment, Visitable {
/**
* Creates a new {@link UpdateBuilder}.
*
* @return a new {@link UpdateBuilder}.
*/
static UpdateBuilder builder() {
return new DefaultUpdateBuilder();
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
/**
* Entry point to construct an {@link Update} statement.
*
* @author Mark Paluch
* @since 1.1
* @see StatementBuilder
*/
public interface UpdateBuilder {
/**
* Configure the {@link Table} to which the update is applied.
*
* @param count the top count.
* @return {@code this} {@link SelectBuilder}.
*/
UpdateAssign table(Table table);
/**
* Interface exposing {@code SET} methods.
*/
interface UpdateAssign {
/**
* Apply a {@link Assignment SET assignment}.
*
* @param assignment a single {@link Assignment column assignment}.
* @return {@code this} builder.
* @see Assignment
*/
UpdateAssignAnd set(Assignment assignment);
}
/**
* Interface exposing {@code SET} methods.
*/
interface UpdateAssignAnd extends UpdateWhere {
/**
* Apply a {@link Assignment SET assignment}.
*
* @param assignment a single {@link Assignment column assignment}.
* @return {@code this} builder.
* @see Assignment
*/
UpdateAssignAnd and(Assignment assignment);
}
/**
* Interface exposing {@code WHERE} methods.
*/
interface UpdateWhere extends BuildUpdate {
/**
* Apply a {@code WHERE} clause.
*
* @param condition the {@code WHERE} condition.
* @return {@code this} builder.
* @see Where
* @see Condition
*/
UpdateWhereAndOr where(Condition condition);
}
/**
* Interface exposing {@code AND}/{@code OR} combinator methods for {@code WHERE} {@link Condition}s.
*/
interface UpdateWhereAndOr extends BuildUpdate {
/**
* Combine the previous {@code WHERE} {@link Condition} using {@code AND}.
*
* @param condition the condition, must not be {@literal null}.
* @return {@code this} builder.
* @see Condition#and(Condition)
*/
UpdateWhereAndOr and(Condition condition);
/**
* Combine the previous {@code WHERE} {@link Condition} using {@code OR}.
*
* @param condition the condition, must not be {@literal null}.
* @return {@code this} builder.
* @see Condition#or(Condition)
*/
UpdateWhereAndOr or(Condition condition);
}
/**
* Interface exposing the {@link Update} build method.
*/
interface BuildUpdate {
/**
* Build the {@link Update}.
*
* @return the build and immutable {@link Update} statement.
*/
Update build();
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import java.util.Arrays;
import java.util.List;
import org.springframework.util.StringUtils;
/**
* {@code VALUES} clause.
*
* @author Mark Paluch
* @since 1.1
*/
public class Values extends AbstractSegment {
private final List<Expression> tables;
Values(Expression... tables) {
this(Arrays.asList(tables));
}
Values(List<Expression> expressions) {
super(expressions.toArray(new Expression[0]));
this.tables = expressions;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "VALUES(" + StringUtils.collectionToDelimitedString(tables, ", ") + ")";
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.sql.Assignment;
import org.springframework.data.relational.core.sql.Column;
import org.springframework.data.relational.core.sql.Expression;
import org.springframework.data.relational.core.sql.Visitable;
/**
* {@link org.springframework.data.relational.core.sql.Visitor} rendering {@link Assignment}. Uses a
* {@link RenderTarget} to call back for render results.
*
* @author Mark Paluch
* @since 1.1
* @see Assignment
*/
class AssignmentVisitor extends TypedSubtreeVisitor<Assignment> {
private final ColumnVisitor columnVisitor;
private final ExpressionVisitor expressionVisitor;
private final RenderTarget target;
private final StringBuilder part = new StringBuilder();
AssignmentVisitor(RenderContext context, RenderTarget target) {
this.columnVisitor = new ColumnVisitor(context, part::append);
this.expressionVisitor = new ExpressionVisitor(context);
this.target = target;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.FilteredSubtreeVisitor#enterNested(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
Delegation enterNested(Visitable segment) {
if (segment instanceof Column) {
return Delegation.delegateTo(columnVisitor);
}
if (segment instanceof Expression) {
return Delegation.delegateTo(expressionVisitor);
}
throw new IllegalStateException("Cannot provide visitor for " + segment);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.FilteredSubtreeVisitor#leaveNested(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
Delegation leaveNested(Visitable segment) {
if (segment instanceof Column) {
if (part.length() != 0) {
part.append(" = ");
}
return super.leaveNested(segment);
}
if (segment instanceof Expression) {
part.append(expressionVisitor.getRenderedPart());
}
return super.leaveNested(segment);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.FilteredSubtreeVisitor#leaveMatched(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
Delegation leaveMatched(Assignment segment) {
target.onRendered(new StringBuilder(part));
part.setLength(0);
return super.leaveMatched(segment);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.sql.Column;
import org.springframework.data.relational.core.sql.Table;
import org.springframework.data.relational.core.sql.Visitable;
import org.springframework.lang.Nullable;
/**
* Renderer for {@link Column}s.
*
* @author Mark Paluch
* @since 1.1
*/
class ColumnVisitor extends TypedSubtreeVisitor<Column> {
private final RenderContext context;
private final RenderTarget target;
private @Nullable String tableName;
ColumnVisitor(RenderContext context, RenderTarget target) {
this.context = context;
this.target = target;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.TypedSubtreeVisitor#leaveMatched(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
Delegation leaveMatched(Column segment) {
String column = context.getNamingStrategy().getName(segment);
StringBuilder builder = new StringBuilder(
tableName != null ? tableName.length() + column.length() : column.length());
if (tableName != null) {
builder.append(tableName);
}
builder.append(column);
target.onRendered(builder);
return super.leaveMatched(segment);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.TypedSubtreeVisitor#leaveNested(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
Delegation leaveNested(Visitable segment) {
if (segment instanceof Table) {
tableName = context.getNamingStrategy().getReferenceName((Table) segment) + '.';
}
return super.leaveNested(segment);
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.sql.Delete;
import org.springframework.data.relational.core.sql.From;
import org.springframework.data.relational.core.sql.Visitable;
import org.springframework.data.relational.core.sql.Where;
/**
* {@link PartRenderer} for {@link Delete} statements.
*
* @author Mark Paluch
* @since 1.1
*/
class DeleteStatementVisitor extends DelegatingVisitor implements PartRenderer {
private StringBuilder builder = new StringBuilder();
private StringBuilder from = new StringBuilder();
private StringBuilder where = new StringBuilder();
private FromClauseVisitor fromClauseVisitor;
private WhereClauseVisitor whereClauseVisitor;
DeleteStatementVisitor(RenderContext context) {
this.fromClauseVisitor = new FromClauseVisitor(context, it -> {
if (from.length() != 0) {
from.append(", ");
}
from.append(it);
});
this.whereClauseVisitor = new WhereClauseVisitor(context, where::append);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.DelegatingVisitor#doEnter(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
public Delegation doEnter(Visitable segment) {
if (segment instanceof From) {
return Delegation.delegateTo(fromClauseVisitor);
}
if (segment instanceof Where) {
return Delegation.delegateTo(whereClauseVisitor);
}
return Delegation.retain();
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.DelegatingVisitor#doLeave(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
public Delegation doLeave(Visitable segment) {
if (segment instanceof Delete) {
builder.append("DELETE ");
if (from.length() != 0) {
builder.append("FROM ").append(from);
}
if (where.length() != 0) {
builder.append(" WHERE ").append(where);
}
return Delegation.leave();
}
return Delegation.retain();
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.PartRenderer#getRenderedPart()
*/
@Override
public CharSequence getRenderedPart() {
return builder;
}
}

View File

@@ -19,6 +19,7 @@ import org.springframework.data.relational.core.sql.BindMarker;
import org.springframework.data.relational.core.sql.Column;
import org.springframework.data.relational.core.sql.Condition;
import org.springframework.data.relational.core.sql.Expression;
import org.springframework.data.relational.core.sql.Literal;
import org.springframework.data.relational.core.sql.Named;
import org.springframework.data.relational.core.sql.SubselectExpression;
import org.springframework.data.relational.core.sql.Visitable;
@@ -71,6 +72,8 @@ class ExpressionVisitor extends TypedSubtreeVisitor<Expression> implements PartR
} else {
value = segment.toString();
}
} else if (segment instanceof Literal) {
value = segment.toString();
}
return Delegation.retain();

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.sql.Column;
import org.springframework.data.relational.core.sql.Insert;
import org.springframework.data.relational.core.sql.Into;
import org.springframework.data.relational.core.sql.Values;
import org.springframework.data.relational.core.sql.Visitable;
/**
* {@link PartRenderer} for {@link Insert} statements.
*
* @author Mark Paluch
* @since 1.1
*/
class InsertStatementVisitor extends DelegatingVisitor implements PartRenderer {
private StringBuilder builder = new StringBuilder();
private StringBuilder into = new StringBuilder();
private StringBuilder columns = new StringBuilder();
private StringBuilder values = new StringBuilder();
private IntoClauseVisitor intoClauseVisitor;
private ColumnVisitor columnVisitor;
private ValuesVisitor valuesVisitor;
InsertStatementVisitor(RenderContext context) {
this.intoClauseVisitor = new IntoClauseVisitor(context, it -> {
if (into.length() != 0) {
into.append(", ");
}
into.append(it);
});
this.columnVisitor = new ColumnVisitor(context, it -> {
if (columns.length() != 0) {
columns.append(", ");
}
columns.append(it);
});
this.valuesVisitor = new ValuesVisitor(context, values::append);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.DelegatingVisitor#doEnter(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
public Delegation doEnter(Visitable segment) {
if (segment instanceof Into) {
return Delegation.delegateTo(this.intoClauseVisitor);
}
if (segment instanceof Column) {
return Delegation.delegateTo(this.columnVisitor);
}
if (segment instanceof Values) {
return Delegation.delegateTo(this.valuesVisitor);
}
return Delegation.retain();
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.DelegatingVisitor#doLeave(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
public Delegation doLeave(Visitable segment) {
if (segment instanceof Insert) {
builder.append("INSERT");
if (into.length() != 0) {
builder.append(" INTO ").append(into);
}
if (columns.length() != 0) {
builder.append(" (").append(columns).append(")");
}
if (values.length() != 0) {
builder.append(" VALUES(").append(values).append(")");
}
return Delegation.leave();
}
return Delegation.retain();
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.PartRenderer#getRenderedPart()
*/
@Override
public CharSequence getRenderedPart() {
return builder;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.sql.Into;
import org.springframework.data.relational.core.sql.Visitable;
/**
* Renderer for {@link Into}. Uses a {@link RenderTarget} to call back for render results.
*
* @author Mark Paluch
* @since 1.1
*/
class IntoClauseVisitor extends TypedSubtreeVisitor<Into> {
private final FromTableVisitor visitor;
private final RenderTarget parent;
private final StringBuilder builder = new StringBuilder();
private boolean first = true;
IntoClauseVisitor(RenderContext context, RenderTarget parent) {
this.visitor = new FromTableVisitor(context, it -> {
if (first) {
first = false;
} else {
builder.append(", ");
}
builder.append(it);
});
this.parent = parent;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.TypedSubtreeVisitor#enterNested(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
Delegation enterNested(Visitable segment) {
return Delegation.delegateTo(visitor);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.TypedSubtreeVisitor#leaveMatched(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
Delegation leaveMatched(Into segment) {
parent.onRendered(builder);
return super.leaveMatched(segment);
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.sql.Delete;
import org.springframework.data.relational.core.sql.Insert;
import org.springframework.data.relational.core.sql.Select;
import org.springframework.data.relational.core.sql.Update;
/**
* SQL renderer for {@link Select} and {@link Delete} statements.
*
* @author Mark Paluch
* @since 1.1
*/
public interface Renderer {
/**
* Render the {@link Select} AST into a SQL statement.
*
* @param select the statement to render, must not be {@literal null}.
* @return the rendered statement.
*/
String render(Select select);
/**
* Render the {@link Insert} AST into a SQL statement.
*
* @param insert the statement to render, must not be {@literal null}.
* @return the rendered statement.
*/
String render(Insert insert);
/**
* Render the {@link Update} AST into a SQL statement.
*
* @param update the statement to render, must not be {@literal null}.
* @return the rendered statement.
*/
String render(Update update);
/**
* Render the {@link Delete} AST into a SQL statement.
*
* @param delete the statement to render, must not be {@literal null}.
* @return the rendered statement.
*/
String render(Delete delete);
}

View File

@@ -15,49 +15,48 @@
*/
package org.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.sql.Delete;
import org.springframework.data.relational.core.sql.Insert;
import org.springframework.data.relational.core.sql.Select;
import org.springframework.data.relational.core.sql.Update;
import org.springframework.util.Assert;
/**
* Naive SQL renderer that does not consider dialect specifics. This class is to evaluate requirements of a SQL
* renderer.
* SQL renderer for {@link Select} and {@link Delete} statements.
*
* @author Mark Paluch
* @author Jens Schauder
* @since 1.1
* @see RenderContext
*/
public class SqlRenderer {
public class SqlRenderer implements Renderer {
private final Select select;
private final RenderContext context;
private SqlRenderer(Select select, RenderContext context) {
private SqlRenderer(RenderContext context) {
Assert.notNull(context, "RenderContext must not be null!");
this.context = context;
Assert.notNull(select, "Select must not be null!");
this.select = select;
}
/**
* Creates a new {@link SqlRenderer}.
*
* @param select must not be {@literal null}.
* @return the renderer.
*/
public static SqlRenderer create(Select select) {
return new SqlRenderer(select, new SimpleRenderContext(NamingStrategies.asIs()));
public static SqlRenderer create() {
return new SqlRenderer(new SimpleRenderContext(NamingStrategies.asIs()));
}
/**
* Creates a new {@link SqlRenderer} using a {@link RenderContext}.
*
* @param select must not be {@literal null}.
* @param context must not be {@literal null}.
* @return the renderer.
*/
public static SqlRenderer create(Select select, RenderContext context) {
return new SqlRenderer(select, context);
public static SqlRenderer create(RenderContext context) {
return new SqlRenderer(context);
}
/**
@@ -66,8 +65,38 @@ public class SqlRenderer {
* @param select must not be {@literal null}.
* @return the rendered statement.
*/
public static String render(Select select) {
return create(select).render();
public static String toString(Select select) {
return create().render(select);
}
/**
* Renders a {@link Insert} statement into its SQL representation.
*
* @param insert must not be {@literal null}.
* @return the rendered statement.
*/
public static String toString(Insert insert) {
return create().render(insert);
}
/**
* Renders a {@link Update} statement into its SQL representation.
*
* @param update must not be {@literal null}.
* @return the rendered statement.
*/
public static String toString(Update update) {
return create().render(update);
}
/**
* Renders a {@link Delete} statement into its SQL representation.
*
* @param delete must not be {@literal null}.
* @return the rendered statement.
*/
public static String toString(Delete delete) {
return create().render(delete);
}
/**
@@ -75,11 +104,54 @@ public class SqlRenderer {
*
* @return the rendered statement.
*/
public String render() {
@Override
public String render(Select select) {
SelectStatementVisitor visitor = new SelectStatementVisitor(context);
select.visit(visitor);
return visitor.getRenderedPart().toString();
}
/**
* Render the {@link Insert} AST into a SQL statement.
*
* @return the rendered statement.
*/
@Override
public String render(Insert insert) {
InsertStatementVisitor visitor = new InsertStatementVisitor(context);
insert.visit(visitor);
return visitor.getRenderedPart().toString();
}
/**
* Render the {@link Update} AST into a SQL statement.
*
* @return the rendered statement.
*/
@Override
public String render(Update update) {
UpdateStatementVisitor visitor = new UpdateStatementVisitor(context);
update.visit(visitor);
return visitor.getRenderedPart().toString();
}
/**
* Render the {@link Delete} AST into a SQL statement.
*
* @return the rendered statement.
*/
@Override
public String render(Delete delete) {
DeleteStatementVisitor visitor = new DeleteStatementVisitor(context);
delete.visit(visitor);
return visitor.getRenderedPart().toString();
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.sql.Assignment;
import org.springframework.data.relational.core.sql.Table;
import org.springframework.data.relational.core.sql.Update;
import org.springframework.data.relational.core.sql.Visitable;
import org.springframework.data.relational.core.sql.Where;
/**
* {@link PartRenderer} for {@link Update} statements.
*
* @author Mark Paluch
* @since 1.1
*/
class UpdateStatementVisitor extends DelegatingVisitor implements PartRenderer {
private StringBuilder builder = new StringBuilder();
private StringBuilder table = new StringBuilder();
private StringBuilder assignments = new StringBuilder();
private StringBuilder where = new StringBuilder();
private FromTableVisitor tableVisitor;
private AssignmentVisitor assignmentVisitor;
private WhereClauseVisitor whereClauseVisitor;
UpdateStatementVisitor(RenderContext context) {
this.tableVisitor = new FromTableVisitor(context, it -> {
if (table.length() != 0) {
table.append(", ");
}
table.append(it);
});
this.assignmentVisitor = new AssignmentVisitor(context, it -> {
if (assignments.length() != 0) {
assignments.append(", ");
}
assignments.append(it);
});
this.whereClauseVisitor = new WhereClauseVisitor(context, where::append);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.DelegatingVisitor#doEnter(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
public Delegation doEnter(Visitable segment) {
if (segment instanceof Table) {
return Delegation.delegateTo(this.tableVisitor);
}
if (segment instanceof Assignment) {
return Delegation.delegateTo(this.assignmentVisitor);
}
if (segment instanceof Where) {
return Delegation.delegateTo(this.whereClauseVisitor);
}
return Delegation.retain();
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.DelegatingVisitor#doLeave(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
public Delegation doLeave(Visitable segment) {
if (segment instanceof Update) {
builder.append("UPDATE");
if (table.length() != 0) {
builder.append(" ").append(table);
}
if (assignments.length() != 0) {
builder.append(" SET ").append(assignments);
}
if (where.length() != 0) {
builder.append(" WHERE ").append(where);
}
return Delegation.leave();
}
return Delegation.retain();
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.PartRenderer#getRenderedPart()
*/
@Override
public CharSequence getRenderedPart() {
return builder;
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.sql.Expression;
import org.springframework.data.relational.core.sql.Values;
import org.springframework.data.relational.core.sql.Visitable;
import org.springframework.lang.Nullable;
/**
* Renderer for {@link Values}. Uses a {@link RenderTarget} to call back for render results.
*
* @author Mark Paluch
* @since 1.1
*/
class ValuesVisitor extends TypedSubtreeVisitor<Values> {
private final RenderTarget parent;
private final StringBuilder builder = new StringBuilder();
private final RenderContext context;
private @Nullable ExpressionVisitor current;
private boolean first = true;
ValuesVisitor(RenderContext context, RenderTarget parent) {
this.context = context;
this.parent = parent;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.TypedSubtreeVisitor#enterNested(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
Delegation enterNested(Visitable segment) {
if (segment instanceof Expression) {
this.current = new ExpressionVisitor(context);
return Delegation.delegateTo(this.current);
}
return super.enterNested(segment);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.TypedSubtreeVisitor#leaveNested(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
Delegation leaveNested(Visitable segment) {
if (this.current != null) {
if (first) {
first = false;
} else {
builder.append(", ");
}
builder.append(this.current.getRenderedPart());
this.current = null;
}
return super.leaveNested(segment);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.TypedSubtreeVisitor#leaveMatched(org.springframework.data.relational.core.sql.Visitable)
*/
@Override
Delegation leaveMatched(Values segment) {
parent.onRendered(builder);
return super.leaveMatched(segment);
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import java.util.ArrayList;
import java.util.List;
/**
* @author Mark Paluch
*/
class CapturingVisitor implements Visitor {
final List<Visitable> enter = new ArrayList<>();
@Override
public void enter(Visitable segment) {
enter.add(segment);
}
@Override
public void leave(Visitable segment) {
leave.add(segment);
}
final List<Visitable> leave = new ArrayList<>();
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
/**
* Unit tests for {@link DeleteBuilder}.
*
* @author Mark Paluch
*/
public class DeleteBuilderUnitTests {
@Test // DATAJDBC-335
public void simpleDelete() {
DeleteBuilder builder = StatementBuilder.delete();
Table table = SQL.table("mytable");
Column foo = table.column("foo");
Column bar = table.column("bar");
Delete delete = builder.from(table).where(foo.isEqualTo(bar)).build();
CapturingVisitor visitor = new CapturingVisitor();
delete.visit(visitor);
assertThat(visitor.enter).containsSequence(new From(table), table, new Where(foo.isEqualTo(bar)),
foo.isEqualTo(bar), foo, table, bar, table);
assertThat(delete.toString()).isEqualTo("DELETE FROM mytable WHERE mytable.foo = mytable.bar");
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
/**
* Unit tests for {@link SelectValidator}.
*
* @author Mark Paluch
*/
public class DeleteValidatorUnitTests {
@Test // DATAJDBC-335
public void shouldReportMissingTableForDeleteViaWhere() {
Column column = SQL.table("table").column("foo");
Table bar = SQL.table("bar");
assertThatThrownBy(() -> {
StatementBuilder.delete() //
.from(bar) //
.where(new SimpleCondition(column, "=", "foo")) //
.build();
}).isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Required table [table] by a WHERE predicate not imported by FROM [bar]");
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
/**
* Unit tests for {@link InsertBuilder}.
*
* @author Mark Paluch
*/
public class InsertBuilderUnitTests {
@Test // DATAJDBC-335
public void shouldCreateSimpleInsert() {
Table table = SQL.table("mytable");
Column foo = table.column("foo");
Column bar = table.column("bar");
Insert insert = StatementBuilder.insert().into(table).column(foo).column(bar).value(SQL.bindMarker()).build();
CapturingVisitor visitor = new CapturingVisitor();
insert.visit(visitor);
assertThat(visitor.enter).containsSequence(insert, new Into(table), table, foo, table, bar, table,
new Values(SQL.bindMarker()));
assertThat(insert.toString()).isEqualTo("INSERT INTO mytable (mytable.foo, mytable.bar) VALUES(?)");
}
}

View File

@@ -42,7 +42,7 @@ public class SelectBuilderUnitTests {
Select select = builder.select(foo, bar).from(table).build();
CapturingSelectVisitor visitor = new CapturingSelectVisitor();
CapturingVisitor visitor = new CapturingVisitor();
select.visit(visitor);
assertThat(visitor.enter).containsSequence(foo, table, bar, table, new From(table), table);
@@ -58,7 +58,7 @@ public class SelectBuilderUnitTests {
Select select = builder.top(10).select(foo).from(table).build();
CapturingSelectVisitor visitor = new CapturingSelectVisitor();
CapturingVisitor visitor = new CapturingVisitor();
select.visit(visitor);
assertThat(visitor.enter).containsSequence(foo, table, new From(table), table);
@@ -78,7 +78,7 @@ public class SelectBuilderUnitTests {
Select select = builder.select(foo, bar).from(table1, table2).build();
CapturingSelectVisitor visitor = new CapturingSelectVisitor();
CapturingVisitor visitor = new CapturingVisitor();
select.visit(visitor);
assertThat(visitor.enter).containsSequence(foo, table1, bar, table2, new From(table1, table2), table1, table2);
@@ -96,7 +96,7 @@ public class SelectBuilderUnitTests {
OrderByField orderByField = OrderByField.from(foo).asc();
Select select = builder.select(foo).from(table).orderBy(orderByField).build();
CapturingSelectVisitor visitor = new CapturingSelectVisitor();
CapturingVisitor visitor = new CapturingVisitor();
select.visit(visitor);
assertThat(visitor.enter).containsSequence(foo, table, new From(table), table, orderByField, foo);
@@ -118,7 +118,7 @@ public class SelectBuilderUnitTests {
.and(SQL.column("tenant", employee)).equals(SQL.column("tenant", department))
.orderBy(OrderByField.from(name).asc()).build();
CapturingSelectVisitor visitor = new CapturingSelectVisitor();
CapturingVisitor visitor = new CapturingVisitor();
select.visit(visitor);
assertThat(visitor.enter).filteredOn(Join.class::isInstance).hasSize(1);
@@ -131,20 +131,4 @@ public class SelectBuilderUnitTests {
assertThat(join.getType()).isEqualTo(JoinType.JOIN);
}
static class CapturingSelectVisitor implements Visitor {
final List<Visitable> enter = new ArrayList<>();
@Override
public void enter(Visitable segment) {
enter.add(segment);
}
@Override
public void leave(Visitable segment) {
leave.add(segment);
}
final List<Visitable> leave = new ArrayList<>();
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
/**
* Unit tests for {@link UpdateBuilder}.
*
* @author Mark Paluch
*/
public class UpdateBuilderUnitTests {
@Test // DATAJDBC-335
public void shouldCreateSimpleUpdate() {
Table table = SQL.table("mytable");
Column column = table.column("foo");
Update update = StatementBuilder.update(table).set(column.set(SQL.bindMarker())).build();
CapturingVisitor visitor = new CapturingVisitor();
update.visit(visitor);
assertThat(visitor.enter).containsSequence(update, table, Assignments.value(column, SQL.bindMarker()), column,
table, SQL.bindMarker());
assertThat(update.toString()).isEqualTo("UPDATE mytable SET mytable.foo = ?");
}
@Test // DATAJDBC-335
public void shouldCreateUpdateWIthCondition() {
Table table = SQL.table("mytable");
Column column = table.column("foo");
Update update = StatementBuilder.update(table).set(column.set(SQL.bindMarker())).where(column.isNull()).build();
CapturingVisitor visitor = new CapturingVisitor();
update.visit(visitor);
assertThat(visitor.enter).containsSequence(update, table, Assignments.value(column, SQL.bindMarker()), column,
table, SQL.bindMarker(), column.isNull());
assertThat(update.toString()).isEqualTo("UPDATE mytable SET mytable.foo = ? WHERE mytable.foo IS NULL");
}
}

View File

@@ -38,7 +38,7 @@ public class ConditionRendererUnitTests {
public void shouldRenderEquals() {
String sql = SqlRenderer
.render(StatementBuilder.select(left).from(table).where(left.isEqualTo(right)).build());
.toString(StatementBuilder.select(left).from(table).where(left.isEqualTo(right)).build());
assertThat(sql).endsWith("WHERE my_table.left = my_table.right");
}
@@ -47,11 +47,11 @@ public class ConditionRendererUnitTests {
public void shouldRenderNotEquals() {
String sql = SqlRenderer
.render(StatementBuilder.select(left).from(table).where(left.isNotEqualTo(right)).build());
.toString(StatementBuilder.select(left).from(table).where(left.isNotEqualTo(right)).build());
assertThat(sql).endsWith("WHERE my_table.left != my_table.right");
sql = SqlRenderer.render(StatementBuilder.select(left).from(table).where(left.isEqualTo(right).not()).build());
sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.isEqualTo(right).not()).build());
assertThat(sql).endsWith("WHERE my_table.left != my_table.right");
}
@@ -59,7 +59,7 @@ public class ConditionRendererUnitTests {
@Test // DATAJDBC-309
public void shouldRenderIsLess() {
String sql = SqlRenderer.render(StatementBuilder.select(left).from(table).where(left.isLess(right)).build());
String sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.isLess(right)).build());
assertThat(sql).endsWith("WHERE my_table.left < my_table.right");
}
@@ -68,7 +68,7 @@ public class ConditionRendererUnitTests {
public void shouldRenderIsLessOrEqualTo() {
String sql = SqlRenderer
.render(StatementBuilder.select(left).from(table).where(left.isLessOrEqualTo(right)).build());
.toString(StatementBuilder.select(left).from(table).where(left.isLessOrEqualTo(right)).build());
assertThat(sql).endsWith("WHERE my_table.left <= my_table.right");
}
@@ -77,7 +77,7 @@ public class ConditionRendererUnitTests {
public void shouldRenderIsGreater() {
String sql = SqlRenderer
.render(StatementBuilder.select(left).from(table).where(left.isGreater(right)).build());
.toString(StatementBuilder.select(left).from(table).where(left.isGreater(right)).build());
assertThat(sql).endsWith("WHERE my_table.left > my_table.right");
}
@@ -86,7 +86,7 @@ public class ConditionRendererUnitTests {
public void shouldRenderIsGreaterOrEqualTo() {
String sql = SqlRenderer
.render(StatementBuilder.select(left).from(table).where(left.isGreaterOrEqualTo(right)).build());
.toString(StatementBuilder.select(left).from(table).where(left.isGreaterOrEqualTo(right)).build());
assertThat(sql).endsWith("WHERE my_table.left >= my_table.right");
}
@@ -94,7 +94,7 @@ public class ConditionRendererUnitTests {
@Test // DATAJDBC-309
public void shouldRenderIn() {
String sql = SqlRenderer.render(StatementBuilder.select(left).from(table).where(left.in(right)).build());
String sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.in(right)).build());
assertThat(sql).endsWith("WHERE my_table.left IN (my_table.right)");
}
@@ -102,7 +102,7 @@ public class ConditionRendererUnitTests {
@Test // DATAJDBC-309
public void shouldRenderLike() {
String sql = SqlRenderer.render(StatementBuilder.select(left).from(table).where(left.like(right)).build());
String sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.like(right)).build());
assertThat(sql).endsWith("WHERE my_table.left LIKE my_table.right");
}
@@ -110,7 +110,7 @@ public class ConditionRendererUnitTests {
@Test // DATAJDBC-309
public void shouldRenderIsNull() {
String sql = SqlRenderer.render(StatementBuilder.select(left).from(table).where(left.isNull()).build());
String sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.isNull()).build());
assertThat(sql).endsWith("WHERE my_table.left IS NULL");
}
@@ -118,11 +118,11 @@ public class ConditionRendererUnitTests {
@Test // DATAJDBC-309
public void shouldRenderIsNotNull() {
String sql = SqlRenderer.render(StatementBuilder.select(left).from(table).where(left.isNotNull()).build());
String sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.isNotNull()).build());
assertThat(sql).endsWith("WHERE my_table.left IS NOT NULL");
sql = SqlRenderer.render(StatementBuilder.select(left).from(table).where(left.isNull().not()).build());
sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.isNull().not()).build());
assertThat(sql).endsWith("WHERE my_table.left IS NOT NULL");
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql.render;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
import org.springframework.data.relational.core.sql.Delete;
import org.springframework.data.relational.core.sql.SQL;
import org.springframework.data.relational.core.sql.Table;
/**
* Unit tests for {@link SqlRenderer}.
*
* @author Mark Paluch
*/
public class DeleteRendererUnitTests {
@Test // DATAJDBC-335
public void shouldRenderWithoutWhere() {
Table bar = SQL.table("bar");
Delete delete = Delete.builder().from(bar).build();
assertThat(SqlRenderer.toString(delete)).isEqualTo("DELETE FROM bar");
}
@Test // DATAJDBC-335
public void shouldRenderWithCondition() {
Table table = Table.create("bar");
Delete delete = Delete.builder().from(table).where(table.column("foo").isEqualTo(table.column("baz")))
.and(table.column("doe").isNull()).build();
assertThat(SqlRenderer.toString(delete)).isEqualTo("DELETE FROM bar WHERE bar.foo = bar.baz AND bar.doe IS NULL");
}
@Test // DATAJDBC-335
public void shouldConsiderTableAlias() {
Table table = Table.create("bar").as("my_bar");
Delete delete = Delete.builder().from(table).where(table.column("foo").isEqualTo(table.column("baz"))).build();
assertThat(SqlRenderer.toString(delete)).isEqualTo("DELETE FROM bar AS my_bar WHERE my_bar.foo = my_bar.baz");
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql.render;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
import org.springframework.data.relational.core.sql.Insert;
import org.springframework.data.relational.core.sql.SQL;
import org.springframework.data.relational.core.sql.Table;
/**
* Unit tests for {@link SqlRenderer}.
*
* @author Mark Paluch
*/
public class InsertRendererUnitTests {
@Test // DATAJDBC-335
public void shouldRenderInsert() {
Table bar = SQL.table("bar");
Insert insert = Insert.builder().into(bar).values(SQL.bindMarker()).build();
assertThat(SqlRenderer.toString(insert)).isEqualTo("INSERT INTO bar VALUES(?)");
}
@Test // DATAJDBC-335
public void shouldRenderInsertColumn() {
Table bar = SQL.table("bar");
Insert insert = Insert.builder().into(bar).column(bar.column("foo")).values(SQL.bindMarker()).build();
assertThat(SqlRenderer.toString(insert)).isEqualTo("INSERT INTO bar (bar.foo) VALUES(?)");
}
@Test // DATAJDBC-335
public void shouldRenderInsertMultipleColumns() {
Table bar = SQL.table("bar");
Insert insert = Insert.builder().into(bar).columns(bar.columns("foo", "baz")).value(SQL.bindMarker())
.value(SQL.literalOf("foo")).build();
assertThat(SqlRenderer.toString(insert)).isEqualTo("INSERT INTO bar (bar.foo, bar.baz) VALUES(?, 'foo')");
}
}

View File

@@ -34,7 +34,7 @@ import org.springframework.util.StringUtils;
* @author Mark Paluch
* @author Jens Schauder
*/
public class SqlRendererUnitTests {
public class SelectRendererUnitTests {
@Test // DATAJDBC-309
public void shouldRenderSingleColumn() {
@@ -44,7 +44,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(foo).from(bar).build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT bar.foo FROM bar");
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT bar.foo FROM bar");
}
@Test // DATAJDBC-309
@@ -54,7 +54,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(table.column("foo").as("my_foo")).from(table).build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT my_bar.foo AS my_foo FROM bar AS my_bar");
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT my_bar.foo AS my_foo FROM bar AS my_bar");
}
@Test // DATAJDBC-309
@@ -66,7 +66,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(table1.column("col1")).select(table2.column("col2")).from(table1)
.from(table2).build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT table1.col1, table2.col2 FROM table1, table2");
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT table1.col1, table2.col2 FROM table1, table2");
}
@Test // DATAJDBC-309
@@ -78,7 +78,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().distinct().select(foo, bar).from(table).build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT DISTINCT bar.foo, bar.bar FROM bar");
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT DISTINCT bar.foo, bar.bar FROM bar");
}
@Test // DATAJDBC-309
@@ -90,7 +90,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(Functions.count(foo), bar).from(table).build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT COUNT(bar.foo), bar.bar FROM bar");
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT COUNT(bar.foo), bar.bar FROM bar");
}
@Test // DATAJDBC-309
@@ -103,7 +103,7 @@ public class SqlRendererUnitTests {
.join(department).on(employee.column("department_id")).equals(department.column("id")) //
.build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT employee.id, department.name FROM employee "
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT employee.id, department.name FROM employee "
+ "JOIN department ON employee.department_id = department.id");
}
@@ -118,7 +118,7 @@ public class SqlRendererUnitTests {
.and(employee.column("tenant")).equals(department.column("tenant")) //
.build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT employee.id, department.name FROM employee "
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT employee.id, department.name FROM employee "
+ "JOIN department ON employee.department_id = department.id " + "AND employee.tenant = department.tenant");
}
@@ -135,7 +135,7 @@ public class SqlRendererUnitTests {
.join(tenant).on(tenant.column("tenant_id")).equals(department.column("tenant")) //
.build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT employee.id, department.name FROM employee "
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT employee.id, department.name FROM employee "
+ "JOIN department ON employee.department_id = department.id " + "AND employee.tenant = department.tenant "
+ "JOIN tenant AS tenant_base ON tenant_base.tenant_id = department.tenant");
}
@@ -148,7 +148,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(column).from(employee).orderBy(OrderByField.from(column).asc()).build();
assertThat(SqlRenderer.render(select))
assertThat(SqlRenderer.toString(select))
.isEqualTo("SELECT emp.name AS emp_name FROM employee AS emp ORDER BY emp_name ASC");
}
@@ -160,7 +160,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(bar).from("foo").limitOffset(10, 20).build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT foo.bar FROM foo LIMIT 10 OFFSET 20");
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT foo.bar FROM foo LIMIT 10 OFFSET 20");
}
@Test // DATAJDBC-309
@@ -171,7 +171,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(bar).from(table).where(Conditions.isNull(bar)).build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar IS NULL");
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar IS NULL");
}
@Test // DATAJDBC-309
@@ -182,7 +182,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(bar).from(table).where(Conditions.isNull(bar).not()).build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar IS NOT NULL");
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar IS NOT NULL");
}
@Test // DATAJDBC-309
@@ -194,7 +194,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(bar).from(table).where(Conditions.isEqual(bar, SQL.bindMarker(":name")))
.build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar = :name");
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar = :name");
}
@Test // DATAJDBC-309
@@ -207,7 +207,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(bar).from(table).where(Conditions.isEqual(bar, SQL.bindMarker(":name"))
.or(Conditions.isEqual(bar, SQL.bindMarker(":name2"))).and(Conditions.isNull(baz))).build();
assertThat(SqlRenderer.render(select))
assertThat(SqlRenderer.toString(select))
.isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar = :name OR foo.bar = :name2 AND foo.baz IS NULL");
}
@@ -219,7 +219,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(bar).from(table).where(Conditions.in(bar, SQL.bindMarker(":name"))).build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar IN (:name)");
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar IN (:name)");
}
@Test // DATAJDBC-309
@@ -231,7 +231,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(bar).from(table)
.where(Conditions.in(bar, SQL.bindMarker(":name"), SQL.bindMarker(":name2"))).build();
assertThat(SqlRenderer.render(select)).isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar IN (:name, :name2)");
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar IN (:name, :name2)");
}
@Test // DATAJDBC-309
@@ -247,7 +247,7 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(bar).from(foo).where(Conditions.in(bar, subselect)).build();
assertThat(SqlRenderer.render(select))
assertThat(SqlRenderer.toString(select))
.isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar IN (SELECT floo.bah FROM floo)");
}
@@ -260,14 +260,14 @@ public class SqlRendererUnitTests {
Select select = Select.builder().select(bar).from(foo).where(bar.isEqualTo(baz)).build();
String upper = SqlRenderer.create(select, new SimpleRenderContext(NamingStrategies.toUpper())).render();
String upper = SqlRenderer.create(new SimpleRenderContext(NamingStrategies.toUpper())).render(select);
assertThat(upper).isEqualTo("SELECT FOO.BAR FROM FOO WHERE FOO.BAR = FOO.BAZ");
String lower = SqlRenderer.create(select, new SimpleRenderContext(NamingStrategies.toLower())).render();
String lower = SqlRenderer.create(new SimpleRenderContext(NamingStrategies.toLower())).render(select);
assertThat(lower).isEqualTo("SELECT foo.bar FROM foo WHERE foo.bar = foo.baz");
String mapped = SqlRenderer
.create(select, new SimpleRenderContext(NamingStrategies.mapWith(StringUtils::uncapitalize))).render();
String mapped = SqlRenderer.create(new SimpleRenderContext(NamingStrategies.mapWith(StringUtils::uncapitalize)))
.render(select);
assertThat(mapped).isEqualTo("SELECT foo.baR FROM foo WHERE foo.baR = foo.baZ");
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2019 the original author or authors.
*
* 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.springframework.data.relational.core.sql.render;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
import org.springframework.data.relational.core.sql.Column;
import org.springframework.data.relational.core.sql.SQL;
import org.springframework.data.relational.core.sql.StatementBuilder;
import org.springframework.data.relational.core.sql.Table;
import org.springframework.data.relational.core.sql.Update;
/**
* Unit tests for {@link SqlRenderer}.
*
* @author Mark Paluch
*/
public class UpdateRendererUnitTests {
@Test // DATAJDBC-335
public void shouldRenderSimpleUpdate() {
Table table = SQL.table("mytable");
Column column = table.column("foo");
Update update = StatementBuilder.update(table).set(column.set(SQL.bindMarker())).build();
assertThat(SqlRenderer.toString(update)).isEqualTo("UPDATE mytable SET mytable.foo = ?");
}
@Test // DATAJDBC-335
public void shouldRenderUpdateWithLiteral() {
Table table = SQL.table("mytable");
Column column = table.column("foo");
Update update = StatementBuilder.update(table).set(column.set(SQL.literalOf(20))).build();
assertThat(SqlRenderer.toString(update)).isEqualTo("UPDATE mytable SET mytable.foo = 20");
}
@Test // DATAJDBC-335
public void shouldCreateUpdateWIthCondition() {
Table table = SQL.table("mytable");
Column column = table.column("foo");
Update update = StatementBuilder.update(table).set(column.set(SQL.bindMarker())).where(column.isNull()).build();
assertThat(SqlRenderer.toString(update)).isEqualTo("UPDATE mytable SET mytable.foo = ? WHERE mytable.foo IS NULL");
}
}