[NO ISSUE][API] Introduce 'readonly' request parameter
- user model changes: no
- storage format changes: no
- interface changes: yes
Details:
- Introduce 'readonly' request parameter which allows user
to specify whether DDL / DML statements must be rejected
(if set to 'true') or allowed ('false' - default)
- Add test cases and update documentation
- Fix category of WRITE and INSERT statements
Change-Id: Ia2555483f431f97c10d922d2a8832bace6a97610
Reviewed-on: https://asterix-gerrit.ics.uci.edu/3543
Reviewed-by: Murtadha Hubail <mhubail@apache.org>
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IRequestParameters.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IRequestParameters.java
index 84c53c9..e242258 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IRequestParameters.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IRequestParameters.java
@@ -53,4 +53,11 @@
* @return Statement parameters
*/
Map<String, IAObject> getStatementParameters();
+
+ /**
+ * @return a bitmask that restricts which statement
+ * {@link org.apache.asterix.lang.common.base.Statement.Category categories} are permitted for this request,
+ * {@code 0} if all categories are allowed
+ */
+ int getStatementCategoryRestrictionMask();
}
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
index b1361d9..27e685c 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
@@ -86,10 +86,13 @@
if (param.getTimeout() != null && !param.getTimeout().trim().isEmpty()) {
timeout = TimeUnit.NANOSECONDS.toMillis(Duration.parseDurationStringToNanos(param.getTimeout()));
}
- ExecuteStatementRequestMessage requestMsg = new ExecuteStatementRequestMessage(ncCtx.getNodeId(),
- responseFuture.getFutureId(), queryLanguage, statementsText, sessionOutput.config(),
- resultProperties.getNcToCcResultProperties(), param.getClientContextID(), handleUrl,
- optionalParameters, statementParameters, param.isMultiStatement(), requestReference);
+ int stmtCategoryRestrictionMask = org.apache.asterix.app.translator.RequestParameters
+ .getStatementCategoryRestrictionMask(param.isReadOnly());
+ ExecuteStatementRequestMessage requestMsg =
+ new ExecuteStatementRequestMessage(ncCtx.getNodeId(), responseFuture.getFutureId(), queryLanguage,
+ statementsText, sessionOutput.config(), resultProperties.getNcToCcResultProperties(),
+ param.getClientContextID(), handleUrl, optionalParameters, statementParameters,
+ param.isMultiStatement(), stmtCategoryRestrictionMask, requestReference);
execution.start();
ncMb.sendMessageToPrimaryCC(requestMsg);
try {
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
index df321bc..566133d 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
@@ -45,6 +45,7 @@
private Map<String, JsonNode> statementParams;
private boolean expressionTree;
private boolean parseOnly; //don't execute; simply check for syntax correctness and named parameters.
+ private boolean readOnly; // only allow statements that belong to QUERY category, fail for all other categories.
private boolean rewrittenExpressionTree;
private boolean logicalPlan;
private boolean optimizedLogicalPlan;
@@ -180,6 +181,14 @@
return parseOnly;
}
+ public void setReadOnly(boolean readOnly) {
+ this.readOnly = readOnly;
+ }
+
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
public boolean isJob() {
return job;
}
@@ -223,6 +232,8 @@
object.put("job", job);
object.put("signature", signature);
object.put("multiStatement", multiStatement);
+ object.put("parseOnly", parseOnly);
+ object.put("readOnly", readOnly);
if (statementParams != null) {
for (Map.Entry<String, JsonNode> statementParam : statementParams.entrySet()) {
object.set('$' + statementParam.getKey(), statementParam.getValue());
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
index db7075d..31f0f2b 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
@@ -58,6 +58,7 @@
import org.apache.asterix.app.result.fields.TypePrinter;
import org.apache.asterix.app.result.fields.WarningsPrinter;
import org.apache.asterix.app.translator.QueryTranslator;
+import org.apache.asterix.app.translator.RequestParameters;
import org.apache.asterix.common.api.IApplicationContext;
import org.apache.asterix.common.api.IClusterManagementWork;
import org.apache.asterix.common.api.ICodedMessage;
@@ -168,6 +169,7 @@
LOGICAL_PLAN("logical-plan"),
OPTIMIZED_LOGICAL_PLAN("optimized-logical-plan"),
PARSE_ONLY("parse-only"),
+ READ_ONLY("readonly"),
JOB("job"),
SIGNATURE("signature"),
MULTI_STATEMENT("multi-statement");
@@ -378,6 +380,7 @@
param.setRewrittenExpressionTree(getOptBoolean(jsonRequest, Parameter.REWRITTEN_EXPRESSION_TREE, false));
param.setLogicalPlan(getOptBoolean(jsonRequest, Parameter.LOGICAL_PLAN, false));
param.setParseOnly(getOptBoolean(jsonRequest, Parameter.PARSE_ONLY, false));
+ param.setReadOnly(getOptBoolean(jsonRequest, Parameter.READ_ONLY, false));
param.setOptimizedLogicalPlan(getOptBoolean(jsonRequest, Parameter.OPTIMIZED_LOGICAL_PLAN, false));
param.setJob(getOptBoolean(jsonRequest, Parameter.JOB, false));
param.setSignature(getOptBoolean(jsonRequest, Parameter.SIGNATURE, true));
@@ -403,6 +406,7 @@
param.setMaxResultReads(getParameter(request, Parameter.MAX_RESULT_READS));
param.setPlanFormat(getParameter(request, Parameter.PLAN_FORMAT));
param.setParseOnly(getOptBoolean(request, Parameter.PARSE_ONLY, false));
+ param.setReadOnly(getOptBoolean(request, Parameter.READ_ONLY, false));
param.setMultiStatement(getOptBoolean(request, Parameter.MULTI_STATEMENT, true));
try {
param.setStatementParams(getOptStatementParameters(request, request.getParameterNames().iterator(),
@@ -573,7 +577,7 @@
IParserFactory factory = compilationProvider.getParserFactory();
IParser parser = factory.createParser(statementsText);
List<Statement> stmts = parser.parse();
- QueryTranslator.validateStatements(stmts);
+ QueryTranslator.validateStatements(stmts, true, RequestParameters.NO_CATEGORY_RESTRICTION_MASK);
Query query = (Query) stmts.get(stmts.size() - 1);
Set<VariableExpr> extVars =
compilationProvider.getRewriterFactory().createQueryRewriter().getExternalVariables(query.getBody());
@@ -601,9 +605,11 @@
execution.start();
Map<String, IAObject> stmtParams =
org.apache.asterix.app.translator.RequestParameters.deserializeParameterValues(statementParameters);
+ int stmtCategoryRestriction = org.apache.asterix.app.translator.RequestParameters
+ .getStatementCategoryRestrictionMask(param.isReadOnly());
IRequestParameters requestParameters = new org.apache.asterix.app.translator.RequestParameters(requestReference,
statementsText, getResultSet(), resultProperties, stats, null, param.getClientContextID(),
- optionalParameters, stmtParams, param.isMultiStatement());
+ optionalParameters, stmtParams, param.isMultiStatement(), stmtCategoryRestriction);
translator.compileAndExecute(getHyracksClientConnection(), requestParameters);
execution.end();
translator.getWarnings(warnings);
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementRequestMessage.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementRequestMessage.java
index e394c7a..b666085 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementRequestMessage.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementRequestMessage.java
@@ -84,12 +84,14 @@
private final Map<String, String> optionalParameters;
private final Map<String, byte[]> statementParameters;
private final boolean multiStatement;
+ private final int statementCategoryRestrictionMask;
private final IRequestReference requestReference;
public ExecuteStatementRequestMessage(String requestNodeId, long requestMessageId, ILangExtension.Language lang,
String statementsText, SessionConfig sessionConfig, ResultProperties resultProperties,
String clientContextID, String handleUrl, Map<String, String> optionalParameters,
- Map<String, byte[]> statementParameters, boolean multiStatement, IRequestReference requestReference) {
+ Map<String, byte[]> statementParameters, boolean multiStatement, int statementCategoryRestrictionMask,
+ IRequestReference requestReference) {
this.requestNodeId = requestNodeId;
this.requestMessageId = requestMessageId;
this.lang = lang;
@@ -101,6 +103,7 @@
this.optionalParameters = optionalParameters;
this.statementParameters = statementParameters;
this.multiStatement = multiStatement;
+ this.statementCategoryRestrictionMask = statementCategoryRestrictionMask;
this.requestReference = requestReference;
}
@@ -139,9 +142,9 @@
compilationProvider, storageComponentProvider, new ResponsePrinter(sessionOutput));
final IStatementExecutor.Stats stats = new IStatementExecutor.Stats();
Map<String, IAObject> stmtParams = RequestParameters.deserializeParameterValues(statementParameters);
- final IRequestParameters requestParameters =
- new RequestParameters(requestReference, statementsText, null, resultProperties, stats, outMetadata,
- clientContextID, optionalParameters, stmtParams, multiStatement);
+ final IRequestParameters requestParameters = new RequestParameters(requestReference, statementsText, null,
+ resultProperties, stats, outMetadata, clientContextID, optionalParameters, stmtParams,
+ multiStatement, statementCategoryRestrictionMask);
translator.compileAndExecute(ccApp.getHcc(), requestParameters);
translator.getWarnings(warnings);
outPrinter.close();
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
index e1247bd..a59adb7 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
@@ -272,9 +272,8 @@
@Override
public void compileAndExecute(IHyracksClientConnection hcc, IRequestParameters requestParameters) throws Exception {
- if (!requestParameters.isMultiStatement()) {
- validateStatements(statements);
- }
+ validateStatements(statements, requestParameters.isMultiStatement(),
+ requestParameters.getStatementCategoryRestrictionMask());
trackRequest(requestParameters);
int resultSetIdCounter = 0;
FileSplit outputFile = null;
@@ -2977,9 +2976,20 @@
appCtx.getRequestTracker().track(clientRequest);
}
- public static void validateStatements(List<Statement> statements) throws CompilationException {
- if (statements.stream().filter(QueryTranslator::isNotAllowedMultiStatement).count() > 1) {
- throw new CompilationException(ErrorCode.UNSUPPORTED_MULTIPLE_STATEMENTS);
+ public static void validateStatements(List<Statement> statements, boolean allowMultiStatement,
+ int stmtCategoryRestrictionMask) throws CompilationException {
+ if (!allowMultiStatement) {
+ if (statements.stream().filter(QueryTranslator::isNotAllowedMultiStatement).count() > 1) {
+ throw new CompilationException(ErrorCode.UNSUPPORTED_MULTIPLE_STATEMENTS);
+ }
+ }
+ if (stmtCategoryRestrictionMask != RequestParameters.NO_CATEGORY_RESTRICTION_MASK) {
+ for (Statement stmt : statements) {
+ if (isNotAllowedStatementCategory(stmt, stmtCategoryRestrictionMask)) {
+ throw new CompilationException(ErrorCode.PROHIBITED_STATEMENT_CATEGORY, stmt.getSourceLocation(),
+ stmt.getKind());
+ }
+ }
}
}
@@ -2995,6 +3005,15 @@
}
}
+ private static boolean isNotAllowedStatementCategory(Statement statement, int categoryRestrictionMask) {
+ int category = statement.getCategory();
+ if (category <= 0) {
+ throw new IllegalArgumentException(String.valueOf(category));
+ }
+ int i = category & categoryRestrictionMask;
+ return i == 0;
+ }
+
private Map<VarIdentifier, IAObject> createExternalVariables(Map<String, IAObject> stmtParams,
IStatementRewriter stmtRewriter) {
if (stmtParams == null || stmtParams.isEmpty()) {
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/RequestParameters.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/RequestParameters.java
index eda8a4a..90602e7 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/RequestParameters.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/RequestParameters.java
@@ -26,6 +26,7 @@
import org.apache.asterix.common.api.IRequestReference;
import org.apache.asterix.external.parser.JSONDataParser;
import org.apache.asterix.formats.nontagged.SerializerDeserializerProvider;
+import org.apache.asterix.lang.common.base.Statement;
import org.apache.asterix.om.base.IAObject;
import org.apache.asterix.om.types.BuiltinType;
import org.apache.asterix.translator.IRequestParameters;
@@ -42,6 +43,8 @@
public class RequestParameters implements IRequestParameters {
+ public static final int NO_CATEGORY_RESTRICTION_MASK = 0;
+
private final IRequestReference requestReference;
private final IResultSet resultSet;
private final ResultProperties resultProperties;
@@ -51,12 +54,21 @@
private final String clientContextId;
private final Map<String, IAObject> statementParameters;
private final boolean multiStatement;
+ private final int statementCategoryRestrictionMask;
private final String statement;
public RequestParameters(IRequestReference requestReference, String statement, IResultSet resultSet,
ResultProperties resultProperties, Stats stats, IStatementExecutor.ResultMetadata outMetadata,
String clientContextId, Map<String, String> optionalParameters, Map<String, IAObject> statementParameters,
boolean multiStatement) {
+ this(requestReference, statement, resultSet, resultProperties, stats, outMetadata, clientContextId,
+ optionalParameters, statementParameters, multiStatement, NO_CATEGORY_RESTRICTION_MASK);
+ }
+
+ public RequestParameters(IRequestReference requestReference, String statement, IResultSet resultSet,
+ ResultProperties resultProperties, Stats stats, IStatementExecutor.ResultMetadata outMetadata,
+ String clientContextId, Map<String, String> optionalParameters, Map<String, IAObject> statementParameters,
+ boolean multiStatement, int statementCategoryRestrictionMask) {
this.requestReference = requestReference;
this.statement = statement;
this.resultSet = resultSet;
@@ -67,6 +79,7 @@
this.optionalParameters = optionalParameters;
this.statementParameters = statementParameters;
this.multiStatement = multiStatement;
+ this.statementCategoryRestrictionMask = statementCategoryRestrictionMask;
}
@Override
@@ -105,6 +118,11 @@
}
@Override
+ public int getStatementCategoryRestrictionMask() {
+ return statementCategoryRestrictionMask;
+ }
+
+ @Override
public Map<String, IAObject> getStatementParameters() {
return statementParameters;
}
@@ -159,4 +177,8 @@
}
return m;
}
+
+ public static int getStatementCategoryRestrictionMask(boolean readOnly) {
+ return readOnly ? Statement.Category.QUERY : NO_CATEGORY_RESTRICTION_MASK;
+ }
}
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.1.ddl.sqlpp
new file mode 100644
index 0000000..b469669
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.1.ddl.sqlpp
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+
+drop dataverse test2 if exists;
+create dataverse test2;
+
+use test;
+
+create type testType as {
+ id: bigint,
+ c1: bigint,
+ c2: bigint
+};
+
+create dataset t1(testType) primary key id;
+
+create dataset t2(testType) primary key id;
+
+create index idx_c1 on t2(c1);
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.10.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.10.update.sqlpp
new file mode 100644
index 0000000..cbc61f4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.10.update.sqlpp
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/*
+ * Description : Test that the following statement is prohibited in readonly request mode:
+ * : "insert"
+ * Expected Result : Failure
+ */
+
+// requesttype=application/json
+// param readonly:json=true
+
+use test;
+
+insert into t2
+([
+ {"id":1, "c1":100},
+ {"id":2, "c1":200},
+ {"id":3, "c1":300}
+]);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.11.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.11.update.sqlpp
new file mode 100644
index 0000000..000b5a9
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.11.update.sqlpp
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/*
+ * Description : Test that the following statement is prohibited in readonly request mode:
+ * : "upsert"
+ * Expected Result : Failure
+ */
+
+-- param readonly:string=true
+
+use test;
+
+upsert into t2
+([
+ {"id":1, "c1":100},
+ {"id":2, "c1":200},
+ {"id":3, "c1":300}
+]);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.12.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.12.update.sqlpp
new file mode 100644
index 0000000..c530572
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.12.update.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/*
+ * Description : Test that the following statement is prohibited in readonly request mode:
+ * : "delete"
+ * Expected Result : Failure
+ */
+
+// requesttype=application/json
+// param readonly:json=true
+
+use test;
+
+delete from t2
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.2.update.sqlpp
new file mode 100644
index 0000000..7306d85
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.2.update.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+use test;
+
+insert into t1
+([
+ {"id":1, "c1":100, "c2": 1000},
+ {"id":2, "c1":200, "c2": 2000},
+ {"id":3, "c1":300, "c2": 3000}
+]);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.3.query.sqlpp
new file mode 100644
index 0000000..da67eff
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.3.query.sqlpp
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/*
+ * Description : Test that the following statements are allowed in readonly request mode:
+ * : "set", "use", "select", "declare function"
+ * Expected Result : Success
+ */
+
+// requesttype=application/json
+// param readonly:json=true
+
+use test;
+
+declare function f1(x) {
+ x + 1
+};
+
+select t1.id, t1.c1, f1(t1.c1) f1
+from t1
+order by t1.c1
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.4.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.4.ddl.sqlpp
new file mode 100644
index 0000000..5426e6c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.4.ddl.sqlpp
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/*
+ * Description : Test that the following statement is prohibited in readonly request mode:
+ * : "create dataverse"
+ * Expected Result : Failure
+ */
+
+// requesttype=application/json
+// param readonly:json=true
+
+create dataverse test3;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.5.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.5.ddl.sqlpp
new file mode 100644
index 0000000..b6b3779
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.5.ddl.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/*
+ * Description : Test that the following statement is prohibited in readonly request mode:
+ * : "drop dataverse"
+ * Expected Result : Failure
+ */
+
+-- param readonly:string=true
+
+drop dataverse test2;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.6.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.6.ddl.sqlpp
new file mode 100644
index 0000000..1d96f33
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.6.ddl.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/*
+ * Description : Test that the following statement is prohibited in readonly request mode:
+ * : "create dataset"
+ * Expected Result : Failure
+ */
+
+// requesttype=application/json
+// param readonly:json=true
+
+use test;
+
+create dataset t3(testType) primary key id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.7.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.7.ddl.sqlpp
new file mode 100644
index 0000000..5dea1d6
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.7.ddl.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/*
+ * Description : Test that the following statement is prohibited in readonly request mode:
+ * : "drop dataset"
+ * Expected Result : Failure
+ */
+
+-- param readonly:string=true
+
+use test;
+
+drop dataset t2;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.8.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.8.ddl.sqlpp
new file mode 100644
index 0000000..a3f699b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.8.ddl.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/*
+ * Description : Test that the following statement is prohibited in readonly request mode:
+ * : "create index"
+ * Expected Result : Failure
+ */
+
+// requesttype=application/json
+// param readonly:json=true
+
+use test;
+
+create index idx_c2 on t2(c2);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.9.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.9.ddl.sqlpp
new file mode 100644
index 0000000..b3b09c9
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/readonly-request/readonly-request.9.ddl.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/*
+ * Description : Test that the following statement is prohibited in readonly request mode:
+ * : "drop index"
+ * Expected Result : Failure
+ */
+
+-- param readonly:string=true
+
+use test;
+
+drop index t2.idx_c1;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/readonly-request/readonly-request.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/readonly-request/readonly-request.3.adm
new file mode 100644
index 0000000..7f2b70a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/readonly-request/readonly-request.3.adm
@@ -0,0 +1,3 @@
+{ "id": 1, "c1": 100, "f1": 101 }
+{ "id": 2, "c1": 200, "f1": 201 }
+{ "id": 3, "c1": 300, "f1": 301 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index a3c9508..6227109 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -28,6 +28,22 @@
&GeoQueries;
&TemporalQueries;
<test-group name="flwor">
+ <test-case FilePath="api">
+ <compilation-unit name="readonly-request">
+ <output-dir compare="Text">readonly-request</output-dir>
+ <expected-error>ASX0044: CREATE_DATAVERSE statement is prohibited by this request</expected-error>
+ <expected-error>ASX0044: DATAVERSE_DROP statement is prohibited by this request</expected-error>
+ <expected-error>ASX0044: DATASET_DECL statement is prohibited by this request</expected-error>
+ <expected-error>ASX0044: DATASET_DROP statement is prohibited by this request</expected-error>
+ <expected-error>ASX0044: CREATE_INDEX statement is prohibited by this request</expected-error>
+ <expected-error>ASX0044: INDEX_DROP statement is prohibited by this request</expected-error>
+ <expected-error>ASX0044: INSERT statement is prohibited by this request</expected-error>
+ <expected-error>ASX0044: UPSERT statement is prohibited by this request</expected-error>
+ <expected-error>ASX0044: DELETE statement is prohibited by this request</expected-error>
+ </compilation-unit>
+ </test-case>
+ </test-group>
+ <test-group name="flwor">
<test-case FilePath="flwor">
<compilation-unit name="at00">
<output-dir compare="Text">at00</output-dir>
@@ -5867,8 +5883,8 @@
<test-case FilePath="misc">
<compilation-unit name="ensure_result_numeric_type">
<output-dir compare="Text">ensure_result_numeric_type</output-dir>
- <source-location>false</source-location>
<expected-error>expected < 3.0</expected-error>
+ <source-location>false</source-location>
</compilation-unit>
</test-case>
<test-case FilePath="misc">
@@ -10744,8 +10760,8 @@
</test-case>
<test-case FilePath="user-defined-functions">
<compilation-unit name="query-ASTERIXDB-1652">
- <expected-error>In function call "test.length(...)", the dataverse "test" cannot be found!</expected-error>
<output-dir compare="Text">query-ASTERIXDB-1652-2</output-dir>
+ <expected-error>In function call "test.length(...)", the dataverse "test" cannot be found!</expected-error>
</compilation-unit>
</test-case>
<test-case FilePath="user-defined-functions">
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
index ca8ee38..8803214 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
@@ -76,6 +76,7 @@
public static final int REQUEST_CANCELLED = 41;
public static final int TPCDS_INVALID_TABLE_NAME = 42;
public static final int VALUE_OUT_OF_RANGE = 43;
+ public static final int PROHIBITED_STATEMENT_CATEGORY = 44;
public static final int UNSUPPORTED_JRE = 100;
diff --git a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
index 362e506..73f87df 100644
--- a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
+++ b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
@@ -78,6 +78,7 @@
41 = Request %1$s has been cancelled
42 = %1$s: \"%2$s\" is not a TPC-DS table name
43 = Value out of range, function %1$s expects its %2$s input parameter value to be between %3$s and %4$s, received %5$s
+44 = %1$s statement is prohibited by this request
100 = Unsupported JRE: %1$s
diff --git a/asterixdb/asterix-doc/src/site/markdown/api.md b/asterixdb/asterix-doc/src/site/markdown/api.md
index 6853938..d39919f 100644
--- a/asterixdb/asterix-doc/src/site/markdown/api.md
+++ b/asterixdb/asterix-doc/src/site/markdown/api.md
@@ -43,6 +43,9 @@
If the delivery mode is `immediate` the query result is returned with the response.
If the delivery mode is `deferred` the response contains a handle to the <a href="#queryresult">result</a>.
If the delivery mode is `async` the response contains a handle to the query's <a href="#querystatus">status</a>.
+* `readonly` - Reject DDL and DML statements, only accept the following kinds:
+ [SELECT](sqlpp/manual.html#SELECT_statements), [USE](sqlpp/manual.html#Declarations),
+ [DECLARE FUNCTION](sqlpp/manual.html#Declarations), and [SET](sqlpp/manual.html#Performance_tuning)
* `args` - (SQL++ only) A JSON array where each item is a value of a [positional query parameter](sqlpp/manual.html#Parameter_references)
* `$parameter_name` - (SQL++ only) a JSON value of a [named query parameter](sqlpp/manual.html#Parameter_references).
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/InsertStatement.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/InsertStatement.java
index 7bb2cbe..efa58fc 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/InsertStatement.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/InsertStatement.java
@@ -141,10 +141,6 @@
@Override
public byte getCategory() {
- if (var == null) {
- return Category.UPDATE;
- }
- return Category.QUERY;
+ return Category.UPDATE;
}
-
}
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/WriteStatement.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/WriteStatement.java
index aabd999..d4c11e4 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/WriteStatement.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/WriteStatement.java
@@ -60,7 +60,7 @@
@Override
public byte getCategory() {
- return Category.QUERY;
+ return Category.PROCEDURE;
}
}