diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractImportValidator.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractImportValidator.java
new file mode 100644
index 00000000..1a56627a
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractImportValidator.java
@@ -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
requiredByWhere = new HashSet<>();
+ Set 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) {}
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AssignValue.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AssignValue.java
new file mode 100644
index 00000000..8ded7e53
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AssignValue.java
@@ -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}.
+ *
+ * Results in a rendered assignment: {@code = } (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();
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Assignment.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Assignment.java
new file mode 100644
index 00000000..2a932bcf
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Assignment.java
@@ -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 {}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Assignments.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Assignments.java
new file mode 100644
index 00000000..968f25af
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Assignments.java
@@ -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() {}
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java
index 356a8e83..72f354a1 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java
@@ -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()
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDelete.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDelete.java
new file mode 100644
index 00000000..6d84c454
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDelete.java
@@ -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();
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDeleteBuilder.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDeleteBuilder.java
new file mode 100644
index 00000000..8d350ccc
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDeleteBuilder.java
@@ -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;
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsert.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsert.java
new file mode 100644
index 00000000..0f0715e7
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsert.java
@@ -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 columns;
+ private final Values values;
+
+ DefaultInsert(@Nullable Table into, List columns, List 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();
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsertBuilder.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsertBuilder.java
new file mode 100644
index 00000000..af7f96dc
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsertBuilder.java
@@ -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 columns = new ArrayList<>();
+ private List 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 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);
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSelect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSelect.java
index 6bd8f8d5..5b581625 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSelect.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSelect.java
@@ -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);
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdate.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdate.java
new file mode 100644
index 00000000..74ba30f2
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdate.java
@@ -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 assignments;
+ private final @Nullable Where where;
+
+ DefaultUpdate(Table table, List 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();
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdateBuilder.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdateBuilder.java
new file mode 100644
index 00000000..8418765c
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdateBuilder.java
@@ -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 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);
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Delete.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Delete.java
new file mode 100644
index 00000000..da85aa2b
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Delete.java
@@ -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:
+ *
+ * - Self
+ * - {@link Table FROM tables} clause
+ * - {@link Where WHERE} condition
+ *
+ *
+ * @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();
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DeleteBuilder.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DeleteBuilder.java
new file mode 100644
index 00000000..d98576fc
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DeleteBuilder.java
@@ -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();
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DeleteValidator.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DeleteValidator.java
new file mode 100644
index 00000000..bf07e0e2
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DeleteValidator.java
@@ -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.
+ *
+ * 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));
+ }
+ }
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Insert.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Insert.java
new file mode 100644
index 00000000..cf3e1c7d
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Insert.java
@@ -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:
+ *
+ * - Self
+ * - {@link Into INTO table} clause
+ * - {@link Column columns}
+ * - {@link Values VALUEs}
+ *
+ *
+ * @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();
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/InsertBuilder.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/InsertBuilder.java
new file mode 100644
index 00000000..f20c8e62
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/InsertBuilder.java
@@ -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 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();
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Into.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Into.java
new file mode 100644
index 00000000..9758a91e
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Into.java
@@ -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 tables;
+
+ Into(Table... tables) {
+ this(Arrays.asList(tables));
+ }
+
+ Into(List 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, ", ");
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Literal.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Literal.java
new file mode 100644
index 00000000..f874dc9f
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Literal.java
@@ -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 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();
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/NumericLiteral.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/NumericLiteral.java
new file mode 100644
index 00000000..d09d93d6
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/NumericLiteral.java
@@ -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 {
+
+ 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();
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SQL.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SQL.java
index 0f45a863..c01954f5 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SQL.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SQL.java
@@ -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 Literal literalOf(@Nullable T content) {
+ return new Literal<>(content);
+ }
+
+ /**
+ * Creates a new {@code NULL} {@link Literal}.
+ *
+ * @return a new {@link Literal}.
+ */
+ public static Literal nullLiteral() {
+ return new Literal<>(null);
+ }
+
// Utility constructor.
private SQL() {}
}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectBuilder.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectBuilder.java
index b1ea5ceb..f4ef3c8e 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectBuilder.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectBuilder.java
@@ -22,6 +22,7 @@ import java.util.Collection;
*
* @author Mark Paluch
* @since 1.1
+ * @see StatementBuilder
*/
public interface SelectBuilder {
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectValidator.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectValidator.java
index e1f92578..d8254c93 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectValidator.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectValidator.java
@@ -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 requiredBySelect = new HashSet<>();
- private Set requiredByWhere = new HashSet<>();
private Set requiredByOrderBy = new HashSet<>();
- private Set from = new HashSet<>();
private Set 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) {}
}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/StatementBuilder.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/StatementBuilder.java
index 91168860..bd209a46 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/StatementBuilder.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/StatementBuilder.java
@@ -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() {}
}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/StringLiteral.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/StringLiteral.java
new file mode 100644
index 00000000..ab2e05b1
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/StringLiteral.java
@@ -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 {
+
+ 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() + "'";
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Update.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Update.java
new file mode 100644
index 00000000..3289011b
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Update.java
@@ -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:
+ *
+ * - Self
+ * - {@link Table table}
+ * - {@link Assignments assignments}
+ * - {@link Where WHERE} condition
+ *
+ *
+ * @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();
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/UpdateBuilder.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/UpdateBuilder.java
new file mode 100644
index 00000000..55887418
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/UpdateBuilder.java
@@ -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();
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Values.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Values.java
new file mode 100644
index 00000000..f857aafc
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Values.java
@@ -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 tables;
+
+ Values(Expression... tables) {
+ this(Arrays.asList(tables));
+ }
+
+ Values(List 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, ", ") + ")";
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/AssignmentVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/AssignmentVisitor.java
new file mode 100644
index 00000000..01df5efc
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/AssignmentVisitor.java
@@ -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 {
+
+ 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);
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ColumnVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ColumnVisitor.java
new file mode 100644
index 00000000..814a38b1
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ColumnVisitor.java
@@ -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 {
+
+ 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);
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/DeleteStatementVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/DeleteStatementVisitor.java
new file mode 100644
index 00000000..dc492888
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/DeleteStatementVisitor.java
@@ -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;
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ExpressionVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ExpressionVisitor.java
index 604190e0..751e580c 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ExpressionVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ExpressionVisitor.java
@@ -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 implements PartR
} else {
value = segment.toString();
}
+ } else if (segment instanceof Literal) {
+ value = segment.toString();
}
return Delegation.retain();
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/InsertStatementVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/InsertStatementVisitor.java
new file mode 100644
index 00000000..8951b184
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/InsertStatementVisitor.java
@@ -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;
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/IntoClauseVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/IntoClauseVisitor.java
new file mode 100644
index 00000000..855c1825
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/IntoClauseVisitor.java
@@ -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 {
+
+ 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);
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/Renderer.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/Renderer.java
new file mode 100644
index 00000000..7a7466d7
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/Renderer.java
@@ -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);
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SqlRenderer.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SqlRenderer.java
index b114173c..e62d5512 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SqlRenderer.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SqlRenderer.java
@@ -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();
+ }
}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/UpdateStatementVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/UpdateStatementVisitor.java
new file mode 100644
index 00000000..125e4c1c
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/UpdateStatementVisitor.java
@@ -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;
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ValuesVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ValuesVisitor.java
new file mode 100644
index 00000000..501d17e4
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ValuesVisitor.java
@@ -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 {
+
+ 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);
+ }
+}
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/CapturingVisitor.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/CapturingVisitor.java
new file mode 100644
index 00000000..6d9abe8e
--- /dev/null
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/CapturingVisitor.java
@@ -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 enter = new ArrayList<>();
+
+ @Override
+ public void enter(Visitable segment) {
+ enter.add(segment);
+ }
+
+ @Override
+ public void leave(Visitable segment) {
+ leave.add(segment);
+ }
+
+ final List leave = new ArrayList<>();
+}
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/DeleteBuilderUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/DeleteBuilderUnitTests.java
new file mode 100644
index 00000000..4e13278f
--- /dev/null
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/DeleteBuilderUnitTests.java
@@ -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");
+ }
+}
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/DeleteValidatorUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/DeleteValidatorUnitTests.java
new file mode 100644
index 00000000..93413596
--- /dev/null
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/DeleteValidatorUnitTests.java
@@ -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]");
+ }
+}
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/InsertBuilderUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/InsertBuilderUnitTests.java
new file mode 100644
index 00000000..2ffc4bf8
--- /dev/null
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/InsertBuilderUnitTests.java
@@ -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(?)");
+ }
+}
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/SelectBuilderUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/SelectBuilderUnitTests.java
index 4a446310..bdf5f8a1 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/SelectBuilderUnitTests.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/SelectBuilderUnitTests.java
@@ -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 enter = new ArrayList<>();
-
- @Override
- public void enter(Visitable segment) {
- enter.add(segment);
- }
-
- @Override
- public void leave(Visitable segment) {
- leave.add(segment);
- }
-
- final List leave = new ArrayList<>();
- }
}
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/UpdateBuilderUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/UpdateBuilderUnitTests.java
new file mode 100644
index 00000000..36054eec
--- /dev/null
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/UpdateBuilderUnitTests.java
@@ -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");
+ }
+}
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/ConditionRendererUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/ConditionRendererUnitTests.java
index edf3dc80..21b82f84 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/ConditionRendererUnitTests.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/ConditionRendererUnitTests.java
@@ -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");
}
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/DeleteRendererUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/DeleteRendererUnitTests.java
new file mode 100644
index 00000000..ddaf1b09
--- /dev/null
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/DeleteRendererUnitTests.java
@@ -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");
+ }
+}
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/InsertRendererUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/InsertRendererUnitTests.java
new file mode 100644
index 00000000..1b7c14d7
--- /dev/null
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/InsertRendererUnitTests.java
@@ -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')");
+ }
+}
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/SqlRendererUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/SelectRendererUnitTests.java
similarity index 80%
rename from spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/SqlRendererUnitTests.java
rename to spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/SelectRendererUnitTests.java
index 42920236..c38fd260 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/SqlRendererUnitTests.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/SelectRendererUnitTests.java
@@ -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");
}
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/UpdateRendererUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/UpdateRendererUnitTests.java
new file mode 100644
index 00000000..3f054475
--- /dev/null
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/UpdateRendererUnitTests.java
@@ -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");
+ }
+}