You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2020/10/30 13:53:25 UTC

[cayenne] branch master updated: CAY-2686 SQL translator incorrectly quotes fully qualified tables' names

This is an automated email from the ASF dual-hosted git repository.

ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/master by this push:
     new b3ae66d  CAY-2686 SQL translator incorrectly quotes fully qualified tables' names
b3ae66d is described below

commit b3ae66d55e54dcd01b93397e14cafeae032483a9
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Fri Oct 30 16:50:40 2020 +0300

    CAY-2686 SQL translator incorrectly quotes fully qualified tables' names
---
 RELEASE-NOTES.txt                                  |  1 +
 .../cayenne/access/sqlbuilder/DeleteBuilder.java   |  6 +++++
 .../cayenne/access/sqlbuilder/InsertBuilder.java   |  6 +++++
 .../cayenne/access/sqlbuilder/SQLBuilder.java      | 17 ++++++++++++
 .../access/sqlbuilder/TableNodeBuilder.java        | 15 ++++++++++-
 .../cayenne/access/sqlbuilder/UpdateBuilder.java   |  6 +++++
 .../access/sqlbuilder/sqltree/TableNode.java       | 28 +++++++++++++++++--
 .../translator/batch/DeleteBatchTranslator.java    |  2 +-
 .../translator/batch/InsertBatchTranslator.java    |  2 +-
 .../batch/SoftDeleteBatchTranslator.java           |  2 +-
 .../translator/batch/UpdateBatchTranslator.java    |  2 +-
 .../access/translator/select/TableTreeStage.java   |  2 +-
 .../access/sqlbuilder/BaseSqlBuilderTest.java      | 30 +++++++++++++++++++++
 .../access/sqlbuilder/DeleteBuilderTest.java       | 29 ++++++++++++++++----
 .../access/sqlbuilder/InsertBuilderTest.java       | 31 +++++++++++++++++-----
 .../access/sqlbuilder/SelectBuilderTest.java       | 22 ++++++++++-----
 .../access/sqlbuilder/UpdateBuilderTest.java       | 31 +++++++++++++++++-----
 17 files changed, 201 insertions(+), 31 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index f8d051c..3560673 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -16,6 +16,7 @@ Changes/New Features:
 Bug Fixes:
 
 CAY-2683 Don't use DISTINCT for joins on to-one related tables
+CAY-2686 SQL translator incorrectly quotes fully qualified tables' names
 
 ----------------------------------
 Release: 4.2.M2
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/DeleteBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/DeleteBuilder.java
index 9d00f2e..739ebcf 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/DeleteBuilder.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/DeleteBuilder.java
@@ -22,6 +22,7 @@ package org.apache.cayenne.access.sqlbuilder;
 import org.apache.cayenne.access.sqlbuilder.sqltree.DeleteNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.TableNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.WhereNode;
+import org.apache.cayenne.map.DbEntity;
 
 /**
  * @since 4.2
@@ -36,6 +37,11 @@ public class DeleteBuilder extends BaseBuilder {
         node(TABLE_NODE, () -> new TableNode(table, null));
     }
 
+    public DeleteBuilder(DbEntity table) {
+        super(new DeleteNode(), WHERE_NODE + 1);
+        node(TABLE_NODE, () -> new TableNode(table, null));
+    }
+
     public DeleteBuilder where(NodeBuilder expression) {
         if(expression != null) {
             node(WHERE_NODE, WhereNode::new).addChild(expression.build());
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/InsertBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/InsertBuilder.java
index fa1e3c7..0ed5aca 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/InsertBuilder.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/InsertBuilder.java
@@ -23,6 +23,7 @@ import org.apache.cayenne.access.sqlbuilder.sqltree.InsertColumnsNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.InsertNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.TableNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.InsertValuesNode;
+import org.apache.cayenne.map.DbEntity;
 
 /**
  * @since 4.2
@@ -42,6 +43,11 @@ public class InsertBuilder extends BaseBuilder {
         node(TABLE_NODE, () -> new TableNode(table, null));
     }
 
+    public InsertBuilder(DbEntity table) {
+        super(new InsertNode(), VALUES_NODE + 1);
+        node(TABLE_NODE, () -> new TableNode(table, null));
+    }
+
     public InsertBuilder column(ColumnNodeBuilder columnNode) {
         node(COLUMNS_NODE, InsertColumnsNode::new).addChild(columnNode.build());
         return this;
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLBuilder.java
index b5b92b3..091ea4d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLBuilder.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLBuilder.java
@@ -26,6 +26,7 @@ import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
 import org.apache.cayenne.access.sqlbuilder.sqltree.NodeType;
 import org.apache.cayenne.access.sqlbuilder.sqltree.SimpleNodeTreeVisitor;
 import org.apache.cayenne.access.sqlbuilder.sqltree.TextNode;
+import org.apache.cayenne.map.DbEntity;
 
 /**
  * @since 4.2
@@ -40,18 +41,34 @@ public final class SQLBuilder {
         return new InsertBuilder(table);
     }
 
+    public static InsertBuilder insert(DbEntity table) {
+        return new InsertBuilder(table);
+    }
+
     public static UpdateBuilder update(String table) {
         return new UpdateBuilder(table);
     }
 
+    public static UpdateBuilder update(DbEntity table) {
+        return new UpdateBuilder(table);
+    }
+
     public static DeleteBuilder delete(String table) {
         return new DeleteBuilder(table);
     }
 
+    public static DeleteBuilder delete(DbEntity table) {
+        return new DeleteBuilder(table);
+    }
+
     public static TableNodeBuilder table(String table) {
         return new TableNodeBuilder(table);
     }
 
+    public static TableNodeBuilder table(DbEntity table) {
+        return new TableNodeBuilder(table);
+    }
+
     public static ColumnNodeBuilder column(String column) {
         return new ColumnNodeBuilder(null, column);
     }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/TableNodeBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/TableNodeBuilder.java
index 982d88f..91a5f21 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/TableNodeBuilder.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/TableNodeBuilder.java
@@ -19,9 +19,12 @@
 
 package org.apache.cayenne.access.sqlbuilder;
 
+import java.util.Objects;
+
 import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
 import org.apache.cayenne.access.sqlbuilder.sqltree.TableNode;
 import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
 
 /**
  * @since 4.2
@@ -29,11 +32,18 @@ import org.apache.cayenne.map.DbAttribute;
 public class TableNodeBuilder implements NodeBuilder {
 
     private final String tableName;
+    private final DbEntity dbEntity;
 
     private String alias;
 
     TableNodeBuilder(String tableName) {
-        this.tableName = tableName;
+        this.tableName = Objects.requireNonNull(tableName);
+        this.dbEntity = null;
+    }
+
+    TableNodeBuilder(DbEntity dbEntity) {
+        this.dbEntity = Objects.requireNonNull(dbEntity);
+        this.tableName = dbEntity.getName();
     }
 
     public TableNodeBuilder as(String alias) {
@@ -59,6 +69,9 @@ public class TableNodeBuilder implements NodeBuilder {
 
     @Override
     public Node build() {
+        if(dbEntity != null) {
+            return new TableNode(dbEntity, alias);
+        }
         return new TableNode(tableName, alias);
     }
 
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/UpdateBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/UpdateBuilder.java
index 4dad52a..cc0d7f3 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/UpdateBuilder.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/UpdateBuilder.java
@@ -23,6 +23,7 @@ import org.apache.cayenne.access.sqlbuilder.sqltree.UpdateSetNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.TableNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.UpdateNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.WhereNode;
+import org.apache.cayenne.map.DbEntity;
 
 /**
  * @since 4.2
@@ -38,6 +39,11 @@ public class UpdateBuilder extends BaseBuilder {
         node(TABLE_NODE, () -> new TableNode(table, null));
     }
 
+    public UpdateBuilder(DbEntity table) {
+        super(new UpdateNode(), WHERE_NODE + 1);
+        node(TABLE_NODE, () -> new TableNode(table, null));
+    }
+
     public UpdateBuilder set(NodeBuilder setExpression) {
         node(SET_NODE, UpdateSetNode::new).addChild(setExpression.build());
         return this;
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/TableNode.java b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/TableNode.java
index 04b3931..56d2c88 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/TableNode.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/TableNode.java
@@ -19,24 +19,48 @@
 
 package org.apache.cayenne.access.sqlbuilder.sqltree;
 
+import java.util.Objects;
+
 import org.apache.cayenne.access.sqlbuilder.QuotingAppendable;
+import org.apache.cayenne.map.DbEntity;
 
 /**
  * @since 4.2
  */
 public class TableNode extends Node {
 
+    private final DbEntity dbEntity;
     private final String tableName;
     private final String alias;
 
     public TableNode(String tableName, String alias) {
-        this.tableName = tableName;
+        this.dbEntity = null;
+        this.tableName = Objects.requireNonNull(tableName);
+        this.alias = alias;
+    }
+
+    public TableNode(DbEntity dbEntity, String alias) {
+        this.dbEntity = Objects.requireNonNull(dbEntity);
+        this.tableName = null;
         this.alias = alias;
     }
 
     @Override
     public QuotingAppendable append(QuotingAppendable buffer) {
-        buffer.append(' ').appendQuoted(tableName);
+        buffer.append(' ');
+
+        if(dbEntity != null) {
+            if(dbEntity.getCatalog() != null) {
+                buffer.appendQuoted(dbEntity.getCatalog()).append('.');
+            }
+            if(dbEntity.getSchema() != null) {
+                buffer.appendQuoted(dbEntity.getSchema()).append('.');
+            }
+            buffer.appendQuoted(dbEntity.getName());
+        } else {
+            buffer.appendQuoted(tableName);
+        }
+
         if (alias != null) {
             buffer.append(' ').appendQuoted(alias);
         }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java
index 87441ac..57675d1 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java
@@ -40,7 +40,7 @@ public class DeleteBatchTranslator extends BaseBatchTranslator<DeleteBatchQuery>
     @Override
     public String getSql() {
         DeleteBuilder deleteBuilder = SQLBuilder
-                .delete(context.getRootDbEntity().getFullyQualifiedName())
+                .delete(context.getRootDbEntity())
                 .where(buildQualifier(context.getQuery().getDbAttributes()));
         return doTranslate(deleteBuilder);
     }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java
index aff48d7..03e6e41 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java
@@ -40,7 +40,7 @@ public class InsertBatchTranslator extends BaseBatchTranslator<InsertBatchQuery>
     @Override
     public String getSql() {
         InsertBatchQuery query = context.getQuery();
-        InsertBuilder insertBuilder = SQLBuilder.insert(context.getRootDbEntity().getFullyQualifiedName());
+        InsertBuilder insertBuilder = SQLBuilder.insert(context.getRootDbEntity());
 
         for(DbAttribute attribute : query.getDbAttributes()) {
             // skip generated attributes, if needed
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java
index 5c1bc2a..9f2850c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java
@@ -48,7 +48,7 @@ public class SoftDeleteBatchTranslator extends DeleteBatchTranslator {
         DeleteBatchQuery query = context.getQuery();
         DbAttribute deleteAttribute = query.getDbEntity().getAttribute(deletedFieldName);
 
-        UpdateBuilder updateBuilder = update(context.getRootDbEntity().getFullyQualifiedName())
+        UpdateBuilder updateBuilder = update(context.getRootDbEntity())
                 .set(column(deletedFieldName).attribute(deleteAttribute)
                         .eq(SQLBuilder.value(true).attribute(deleteAttribute)))
                 .where(buildQualifier(query.getDbAttributes()));
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java
index 0f4e385..6b973b5 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java
@@ -41,7 +41,7 @@ public class UpdateBatchTranslator extends BaseBatchTranslator<UpdateBatchQuery>
     public String getSql() {
         UpdateBatchQuery query = context.getQuery();
 
-        UpdateBuilder updateBuilder = SQLBuilder.update(context.getRootDbEntity().getFullyQualifiedName());
+        UpdateBuilder updateBuilder = SQLBuilder.update(context.getRootDbEntity());
         for (DbAttribute attr : query.getUpdatedAttributes()) {
             updateBuilder.set(SQLBuilder
                     .column(attr.getName()).attribute(attr)
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java
index 46a10cf..25511f3 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java
@@ -37,7 +37,7 @@ class TableTreeStage implements TranslationStage {
     @Override
     public void perform(TranslatorContext context) {
         context.getTableTree().visit(node -> {
-            NodeBuilder tableNode = table(node.getEntity().getFullyQualifiedName()).as(node.getTableAlias());
+            NodeBuilder tableNode = table(node.getEntity()).as(node.getTableAlias());
             if(node.getRelationship() != null) {
                 tableNode = getJoin(node, tableNode).on(getJoinExpression(context, node));
             }
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/BaseSqlBuilderTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/BaseSqlBuilderTest.java
new file mode 100644
index 0000000..07b18a4
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/BaseSqlBuilderTest.java
@@ -0,0 +1,30 @@
+package org.apache.cayenne.access.sqlbuilder;
+
+import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
+
+import static org.junit.Assert.assertEquals;
+
+class BaseSqlBuilderTest {
+
+    void assertSQL(String expected, Node node) {
+        assertSQL(expected, node, new StringBuilderAppendable());
+    }
+
+    void assertQuotedSQL(String expected, Node node) {
+        assertSQL(expected, node, new MockQuotedStringBuilderAppendable());
+    }
+
+    void assertSQL(String expected, Node node, QuotingAppendable appendable) {
+        SQLGenerationVisitor visitor = new SQLGenerationVisitor(appendable);
+        node.visit(visitor);
+        assertEquals(expected, visitor.getSQLString());
+    }
+
+    static class MockQuotedStringBuilderAppendable extends StringBuilderAppendable {
+        @Override
+        public QuotingAppendable appendQuoted(CharSequence csq) {
+            builder.append('`').append(csq).append('`');
+            return this;
+        }
+    }
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/DeleteBuilderTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/DeleteBuilderTest.java
index be6aaba..f6bd129 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/DeleteBuilderTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/DeleteBuilderTest.java
@@ -21,13 +21,14 @@ package org.apache.cayenne.access.sqlbuilder;
 
 import org.apache.cayenne.access.sqlbuilder.sqltree.DeleteNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
+import org.apache.cayenne.map.DbEntity;
 import org.junit.Test;
 
 import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.*;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.junit.Assert.*;
 
-public class DeleteBuilderTest {
+public class DeleteBuilderTest extends BaseSqlBuilderTest {
 
     @Test
     public void testDelete() {
@@ -49,9 +50,27 @@ public class DeleteBuilderTest {
         assertSQL("DELETE FROM test WHERE ( ( col1 = 1 ) AND ( col2 = 'test' ) ) AND ( col3 IS NULL )", node);
     }
 
-    private void assertSQL(String expected, Node node) {
-        SQLGenerationVisitor visitor = new SQLGenerationVisitor(new StringBuilderAppendable());
-        node.visit(visitor);
-        assertEquals(expected, visitor.getSQLString());
+    @Test
+    public void testDeleteDbEntityCatalog() {
+        DbEntity entity = new DbEntity("test");
+        entity.setCatalog("catalog");
+        DeleteBuilder builder = new DeleteBuilder(entity);
+        Node node = builder.build();
+        assertThat(node, instanceOf(DeleteNode.class));
+        assertSQL("DELETE FROM catalog.test", node);
+        assertQuotedSQL("DELETE FROM `catalog`.`test`", node);
     }
+
+    @Test
+    public void testDeleteDbEntityCatalogAndSchema() {
+        DbEntity entity = new DbEntity("test");
+        entity.setSchema("schema");
+        entity.setCatalog("catalog");
+        DeleteBuilder builder = new DeleteBuilder(entity);
+        Node node = builder.build();
+        assertThat(node, instanceOf(DeleteNode.class));
+        assertSQL("DELETE FROM catalog.schema.test", node);
+        assertQuotedSQL("DELETE FROM `catalog`.`schema`.`test`", node);
+    }
+
 }
\ No newline at end of file
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/InsertBuilderTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/InsertBuilderTest.java
index 229d6a1..501525d 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/InsertBuilderTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/InsertBuilderTest.java
@@ -21,13 +21,14 @@ package org.apache.cayenne.access.sqlbuilder;
 
 import org.apache.cayenne.access.sqlbuilder.sqltree.InsertNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
+import org.apache.cayenne.map.DbEntity;
 import org.junit.Test;
 
 import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.*;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.junit.Assert.*;
 
-public class InsertBuilderTest {
+public class InsertBuilderTest extends BaseSqlBuilderTest  {
 
     @Test
     public void testInsert() {
@@ -38,6 +39,29 @@ public class InsertBuilderTest {
     }
 
     @Test
+    public void testInsertDbEntityCatalog() {
+        DbEntity entity = new DbEntity("test");
+        entity.setCatalog("catalog");
+        InsertBuilder builder = new InsertBuilder(entity);
+        Node node = builder.build();
+        assertThat(node, instanceOf(InsertNode.class));
+        assertSQL("INSERT INTO catalog.test", node);
+        assertQuotedSQL("INSERT INTO `catalog`.`test`", node);
+    }
+
+    @Test
+    public void testInsertDbEntityCatalogAndSchema() {
+        DbEntity entity = new DbEntity("test");
+        entity.setSchema("schema");
+        entity.setCatalog("catalog");
+        InsertBuilder builder = new InsertBuilder(entity);
+        Node node = builder.build();
+        assertThat(node, instanceOf(InsertNode.class));
+        assertSQL("INSERT INTO catalog.schema.test", node);
+        assertQuotedSQL("INSERT INTO `catalog`.`schema`.`test`", node);
+    }
+
+    @Test
     public void testInsertWithColumns() {
         InsertBuilder builder = new InsertBuilder("test");
         builder
@@ -79,9 +103,4 @@ public class InsertBuilderTest {
         assertSQL("INSERT INTO test( col1, col2, col3) VALUES( 1, 'test', NULL)", node);
     }
 
-    private void assertSQL(String expected, Node node) {
-        SQLGenerationVisitor visitor = new SQLGenerationVisitor(new StringBuilderAppendable());
-        node.visit(visitor);
-        assertEquals(expected, visitor.getSQLString());
-    }
 }
\ No newline at end of file
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/SelectBuilderTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/SelectBuilderTest.java
index 48e2035..2631944 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/SelectBuilderTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/SelectBuilderTest.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.access.sqlbuilder;
 
 import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
 import org.apache.cayenne.access.sqlbuilder.sqltree.SelectNode;
+import org.apache.cayenne.map.DbEntity;
 import org.junit.Test;
 
 import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.*;
@@ -30,7 +31,7 @@ import static org.junit.Assert.*;
 /**
  * @since 4.2
  */
-public class SelectBuilderTest {
+public class SelectBuilderTest extends BaseSqlBuilderTest  {
 
     @Test
     public void testSelect() {
@@ -46,6 +47,7 @@ public class SelectBuilderTest {
         Node node = builder.build();
         assertThat(node, instanceOf(SelectNode.class));
         assertSQL("SELECT a, c.b", node);
+        assertQuotedSQL("SELECT `a`, `c`.`b`", node);
     }
 
     @Test
@@ -54,6 +56,19 @@ public class SelectBuilderTest {
         Node node = builder.build();
         assertThat(node, instanceOf(SelectNode.class));
         assertSQL("SELECT a FROM b", node);
+        assertQuotedSQL("SELECT `a` FROM `b`", node);
+    }
+
+    @Test
+    public void testSelectFromDbEntity() {
+        DbEntity entity = new DbEntity("b");
+        entity.setSchema("d");
+        entity.setCatalog("c");
+        SelectBuilder builder = new SelectBuilder(column("a")).from(table(entity));
+        Node node = builder.build();
+        assertThat(node, instanceOf(SelectNode.class));
+        assertSQL("SELECT a FROM c.d.b", node);
+        assertQuotedSQL("SELECT `a` FROM `c`.`d`.`b`", node);
     }
 
     @Test
@@ -123,9 +138,4 @@ public class SelectBuilderTest {
                 " ORDER BY p_count DESC, a_id", node);
     }
 
-    private void assertSQL(String expected, Node node) {
-        SQLGenerationVisitor visitor = new SQLGenerationVisitor(new StringBuilderAppendable());
-        node.visit(visitor);
-        assertEquals(expected, visitor.getSQLString());
-    }
 }
\ No newline at end of file
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/UpdateBuilderTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/UpdateBuilderTest.java
index d0c1133..4c627f4 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/UpdateBuilderTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/sqlbuilder/UpdateBuilderTest.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.access.sqlbuilder;
 
 import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
 import org.apache.cayenne.access.sqlbuilder.sqltree.UpdateNode;
+import org.apache.cayenne.map.DbEntity;
 import org.junit.Test;
 
 import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.*;
@@ -30,7 +31,7 @@ import static org.junit.Assert.*;
 /**
  * @since 4.2
  */
-public class UpdateBuilderTest {
+public class UpdateBuilderTest extends BaseSqlBuilderTest {
 
     @Test
     public void testUpdate() {
@@ -41,6 +42,29 @@ public class UpdateBuilderTest {
     }
 
     @Test
+    public void testUpdateDbEntityCatalog() {
+        DbEntity entity = new DbEntity("test");
+        entity.setCatalog("catalog");
+        UpdateBuilder builder = new UpdateBuilder(entity);
+        Node node = builder.build();
+        assertThat(node, instanceOf(UpdateNode.class));
+        assertSQL("UPDATE catalog.test", node);
+        assertQuotedSQL("UPDATE `catalog`.`test`", node);
+    }
+
+    @Test
+    public void testUpdateDbEntityCatalogAndSchema() {
+        DbEntity entity = new DbEntity("test");
+        entity.setSchema("schema");
+        entity.setCatalog("catalog");
+        UpdateBuilder builder = new UpdateBuilder(entity);
+        Node node = builder.build();
+        assertThat(node, instanceOf(UpdateNode.class));
+        assertSQL("UPDATE catalog.schema.test", node);
+        assertQuotedSQL("UPDATE `catalog`.`schema`.`test`", node);
+    }
+
+    @Test
     public void testUpdateWithFields() {
         UpdateBuilder builder = new UpdateBuilder("test");
         builder
@@ -65,9 +89,4 @@ public class UpdateBuilderTest {
         assertSQL("UPDATE test SET col1 = 1 WHERE id = 123", node);
     }
 
-    private void assertSQL(String expected, Node node) {
-        SQLGenerationVisitor visitor = new SQLGenerationVisitor(new StringBuilderAppendable());
-        node.visit(visitor);
-        assertEquals(expected, visitor.getSQLString());
-    }
 }
\ No newline at end of file