jinggou commented on code in PR #1844:
URL: https://github.com/apache/phoenix/pull/1844#discussion_r1681917731


##########
phoenix-core-client/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java:
##########
@@ -209,6 +250,151 @@ public MutationPlan compile(CreateTableStatement create) 
throws SQLException {
             viewStatement, viewType, viewColumnConstants, 
isViewColumnReferenced, connection);
     }
 
+    /**
+     * Restrict view to be UPDATABLE if the view specification (view 
statement):
+     * 1. uses only the PK columns
+     * 2. starts from the first PK column if the parent table is not multi 
tenant; otherwise,
+     * starts from the second PK column (the first column will be TENANT_ID)
+     * 3. PK columns should be in the order they are defined
+     * 4. uses the same set of PK columns as its sibling views' specification
+     * Otherwise, mark the view as READ_ONLY.
+     *
+     * @param connection The client connection
+     * @param parentToBe To be parent for given view
+     * @param pkColumnsInWhere Set of primary key in where clause
+     * @param nonPkColumnsInWhere Set of non-primary key columns in where 
clause
+     * @throws IOException thrown if there is an error finding sibling views
+     * @throws SQLException
+     */
+    private ViewType setViewTypeToBe(final PhoenixConnection connection, final 
PTable parentToBe,
+                                     final Set<PColumn> pkColumnsInWhere,
+                                     final Set<PColumn> nonPkColumnsInWhere)
+            throws IOException, SQLException {
+        // 1. Check the view specification WHERE clause uses only the PK 
columns
+        if (!nonPkColumnsInWhere.isEmpty()) {
+            LOGGER.info("Setting the view type as READ_ONLY because the 
statement contains non-PK" +
+                    " columns");
+            return ViewType.READ_ONLY;
+        }
+        if (pkColumnsInWhere.isEmpty()) {
+            return ViewType.UPDATABLE;
+        }
+
+        List<Integer> tablePkPositions = new ArrayList<>();
+        List<Integer> pkPositions = new ArrayList<>();
+        parentToBe.getPKColumns().forEach(tablePkColumn ->
+                tablePkPositions.add(tablePkColumn.getPosition()));
+        pkColumnsInWhere.forEach(pkColumn -> 
pkPositions.add(pkColumn.getPosition()));
+        Collections.sort(pkPositions);
+
+        // 2. If not multi tenant, view specification WHERE clause should 
start from the first PK
+        // column; otherwise, start from the second PK column
+        boolean isMultiTenant = parentToBe.isMultiTenant();
+        int firstPkPosition = pkPositions.get(0);
+        if (!isMultiTenant && !Objects.equals(firstPkPosition, 
tablePkPositions.get(0))) {
+            LOGGER.info("Setting the view type as READ_ONLY because the 
statement WHERE clause " +
+                    "does not start from the first PK column");
+        }
+        if (isMultiTenant && !Objects.equals(firstPkPosition, 
tablePkPositions.get(1))) {
+            LOGGER.info("Setting the view type as READ_ONLY because the 
statement WHERE clause " +
+                    "does not start from the second PK column (the parent 
table is multi tenant " +
+                    "with the first column TENANT_ID");
+            return ViewType.READ_ONLY;
+        }
+
+        // 3. Otherwise, PK column(s) should be in the order they are defined
+        if (!isPkColumnsInOrder(pkPositions, tablePkPositions, isMultiTenant)) 
{
+            LOGGER.info("Setting the view type as READ_ONLY because the PK 
columns is not in the " +
+                    "order they are defined");
+            return ViewType.READ_ONLY;
+        }
+
+        // 4. Check it uses the same set of PK column(s) as its sibling views' 
specification
+        byte[] parentTenantIdInBytes = parentToBe.getTenantId() != null
+                ? parentToBe.getTenantId().getBytes() : null;
+        byte[] parentSchemaNameInBytes = parentToBe.getSchemaName() != null
+                ? parentToBe.getSchemaName().getBytes() : null;
+        ConnectionQueryServices queryServices = connection.getQueryServices();
+        Configuration config = queryServices.getConfiguration();
+        byte[] systemChildLinkTable = 
SchemaUtil.isNamespaceMappingEnabled(null, config)
+                ? SYSTEM_CHILD_LINK_NAMESPACE_BYTES
+                : SYSTEM_CHILD_LINK_NAME_BYTES;
+        try (Table childLinkTable = 
queryServices.getTable(systemChildLinkTable)) {
+            List<PTable> legitimateSiblingViewList =
+                    ViewUtil.findAllDescendantViews(childLinkTable, config, 
parentTenantIdInBytes,
+                            parentSchemaNameInBytes, 
parentToBe.getTableName().getBytes(),
+                            HConstants.LATEST_TIMESTAMP, true).getFirst();
+            if (legitimateSiblingViewList.size() > 0) {
+                PTable siblingView = legitimateSiblingViewList.get(0);
+                Expression siblingViewWhere = getWhereFromView(connection, 
siblingView);
+                Set<PColumn> siblingViewPkColsInWhere = new HashSet<>();
+                if (siblingViewWhere != null) {
+                    ViewWhereExpressionValidatorVisitor 
siblingViewValidatorVisitor =
+                            new ViewWhereExpressionValidatorVisitor(parentToBe,
+                                    siblingViewPkColsInWhere, new HashSet<>());
+                    siblingViewWhere.accept(siblingViewValidatorVisitor);
+                }
+                if (!pkColumnsInWhere.equals(siblingViewPkColsInWhere)) {
+                    LOGGER.info("Setting the view type as READ_ONLY because 
the set of PK columns" +
+                            " are different from its sibling views'");
+                    return ViewType.READ_ONLY;
+                }
+            }
+        }
+        return ViewType.UPDATABLE;
+    }
+
+    /**
+     * Get the where Expression of given view.
+     * @param connection The client connection
+     * @param view PTable of the view
+     * @return A where Expression
+     * @throws SQLException
+     */
+    private Expression getWhereFromView(final PhoenixConnection connection, 
final PTable view)
+            throws SQLException {
+        String viewStatement = view.getViewStatement();
+        if (viewStatement == null) {
+            return null;
+        }
+        SelectStatement select = new SQLParser(viewStatement).parseQuery();
+        ColumnResolver resolver = FromCompiler.getResolverForQuery(select, 
connection);
+        StatementContext context = new StatementContext(new 
PhoenixStatement(connection), resolver);
+        BitSet isViewColumnReferencedToBe = new 
BitSet(view.getColumns().size());
+        ExpressionCompiler expressionCompiler = new 
ColumnTrackingExpressionCompiler(context,
+                isViewColumnReferencedToBe);
+        ParseNode whereNode = select.getWhere();
+        return whereNode.accept(expressionCompiler);
+    }
+
+    /**
+     * Check if the primary key columns are in order (consecutive in position) 
as they are
+     * defined, providing their positions list.
+     * @param pkPositions A positions list of PK columns to be checked
+     * @param tablePkPositions The positions list of the table's PK columns
+     * @param isMultiTenant Whether the parent table is multi tenant
+     * @return
+     */
+    private boolean isPkColumnsInOrder(final List<Integer> pkPositions,

Review Comment:
   The column position will move one position back, just like the multi-tenant 
table



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to