OmniSciDB  16c4e035a1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
InjectFilterRule.java
Go to the documentation of this file.
1 /*
2  * Copyright 2021 OmniSci, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.apache.calcite.rel.rules;
18 
19 import org.apache.calcite.plan.RelOptRuleCall;
20 import org.apache.calcite.plan.RelOptTable;
21 import org.apache.calcite.plan.RelRule;
22 import org.apache.calcite.rel.RelNode;
23 import org.apache.calcite.rel.logical.LogicalTableScan;
24 import org.apache.calcite.rel.type.RelDataTypeField;
25 import org.apache.calcite.rex.RexBuilder;
26 import org.apache.calcite.rex.RexNode;
27 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
28 import org.apache.calcite.sql.type.SqlTypeName;
29 import org.apache.calcite.tools.RelBuilder;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32 
33 import java.util.ArrayList;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Set;
37 
38 public class InjectFilterRule extends RelRule<InjectFilterRule.Config> {
39  // goal: customer entitlements first swipe
40 
41  public static Set<String> visitedMemo = new HashSet<>();
42  final static Logger MAPDLOGGER = LoggerFactory.getLogger(InjectFilterRule.class);
43  final List<Restriction> restrictions;
44 
45  public InjectFilterRule(Config config, List<Restriction> restrictions) {
46  super(config);
47  this.restrictions = restrictions;
48  clearMemo();
49  }
50 
51  void clearMemo() {
52  visitedMemo.clear();
53  }
54 
55  @Override
56  public void onMatch(RelOptRuleCall call) {
57  LogicalTableScan parentNode = call.rel(0);
58  if (visitedMemo.contains(parentNode.toString())) {
59  return;
60  } else {
61  visitedMemo.add(parentNode.toString());
62  }
63  RelOptTable table = parentNode.getTable();
64  List<String> qname = table.getQualifiedName();
65 
66  String query_database = null;
67  String query_table = null;
68  if (qname.size() == 2) {
69  query_database = qname.get(0);
70  query_table = qname.get(1);
71  }
72  if (query_database == null || query_database.isEmpty() || query_table == null
73  || query_table.isEmpty()) {
74  throw new RuntimeException(
75  "Restrictions: Expected qualified name as [database, table] but got: "
76  + qname);
77  }
78 
79  ArrayList<RexNode> orList = new ArrayList<RexNode>();
80  RelBuilder builder = call.builder();
81  RexBuilder rBuilder = builder.getRexBuilder();
82  builder = builder.push(parentNode);
83  boolean found = false;
84  for (Restriction restriction : restrictions) {
85  // Match the database name.
86  String rest_database = restriction.getRestrictionDatabase();
87  if (rest_database != null && !rest_database.isEmpty()
88  && !rest_database.equals(query_database)) {
89  // TODO(sy): Maybe remove the isEmpty() wildcarding in OmniSciDB 6.0.
90  MAPDLOGGER.debug("RLS row-level security restriction for database "
91  + rest_database + " ignored because this query is on database "
92  + query_database);
93  continue;
94  }
95 
96  // Match the table name.
97  String rest_table = restriction.getRestrictionTable();
98  if (rest_table != null && !rest_table.isEmpty()
99  && !rest_table.equals(query_table)) {
100  // TODO(sy): Maybe remove the isEmpty() wildcarding in OmniSciDB 6.0.
101  MAPDLOGGER.debug("RLS row-level security restriction for table " + rest_table
102  + " ignored because this query is on table " + query_table);
103  continue;
104  }
105 
106  // Match the column name.
107  RelDataTypeField field = table.getRowType().getField(
108  restriction.getRestrictionColumn(), false, false);
109  if (field == null) {
110  MAPDLOGGER.debug("RLS row-level security restriction for column "
111  + restriction.getRestrictionColumn()
112  + " ignored because column not present in query table " + query_table);
113  continue;
114  }
115 
116  // Generate the RLS row-level security filter for one Restriction.
117  found = true;
118  MAPDLOGGER.debug(
119  "Scan is " + parentNode.toString() + " TABLE is " + table.toString());
120  MAPDLOGGER.debug("Column " + restriction.getRestrictionColumn()
121  + " exists in table " + table.getQualifiedName());
122 
123  for (String val : restriction.getRestrictionValues()) {
124  MAPDLOGGER.debug("Column is " + restriction.getRestrictionColumn()
125  + " literal is '" + val + "'");
126  RexNode lit;
127  if (SqlTypeName.NUMERIC_TYPES.indexOf(field.getType().getSqlTypeName()) == -1) {
128  if (val.length() < 2 || val.charAt(0) != '\''
129  || val.charAt(val.length() - 1) != '\'') {
130  throw new RuntimeException(
131  "Restrictions: Expected a CREATE POLICY VALUES string with single quotes.");
132  }
133  lit = rBuilder.makeLiteral(
134  val.substring(1, val.length() - 1), field.getType(), false);
135  } else {
136  lit = rBuilder.makeLiteral(Integer.parseInt(val), field.getType(), false);
137  }
138  RexNode rn = builder.call(SqlStdOperatorTable.EQUALS,
139  builder.field(restriction.getRestrictionColumn()),
140  lit);
141  orList.add(rn);
142  }
143  }
144 
145  if (found) {
146  RexNode relOr = builder.call(SqlStdOperatorTable.OR, orList);
147  final RelNode newNode = builder.filter(relOr).build();
148  call.transformTo(newNode);
149  }
150  };
151 
153  public interface Config extends RelRule.Config {
155  EMPTY.withOperandSupplier(b0 -> b0.operand(LogicalTableScan.class).noInputs())
156  .as(Config.class);
157 
158  @Override
160  return new InjectFilterRule(this, null);
161  }
162 
163  default InjectFilterRule toRule(List<Restriction> rests) {
164  return new InjectFilterRule(this, rests);
165  }
166  }
167 }
const rapidjson::Value & field(const rapidjson::Value &obj, const char field[]) noexcept
Definition: JsonAccessors.h:31
default InjectFilterRule toRule(List< Restriction > rests)
InjectFilterRule(Config config, List< Restriction > restrictions)
#define OR