[ASTERIXDB-2954][COMP] Support PRIMARY KEY declaration in CREATE VIEW
- user model changes: no
- storage format changes: no
- interface changes: no
Details:
- Allow not-enforced primary key to be specified in
CREATE VIEW for typed views
- Primary key fields must be declared as "not unknown"
in the type definition
- If a type cast operation produces NULL for a primary key
field then the whole tuple is excluded from the view
- Add testcases
Change-Id: I7ad08dcb0e1437c1e791daab4ee8eadd9c8135e1
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/12963
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
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 bf6e3b5..abef74e 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
@@ -2499,6 +2499,7 @@
}
}
+ List<String> primaryKeyFields = cvs.getPrimaryKeyFields();
Datatype itemTypeEntity = null;
boolean itemTypeIsInline = false;
if (cvs.hasItemType()) {
@@ -2506,6 +2507,13 @@
itemTypeDataverseName, itemTypeName, cvs.getItemType(), false, metadataProvider, sourceLoc);
itemTypeEntity = itemTypePair.first;
itemTypeIsInline = itemTypePair.second;
+ if (primaryKeyFields != null) {
+ ValidateUtil.validatePartitioningExpressions((ARecordType) itemTypeEntity.getDatatype(), null,
+ primaryKeyFields.stream().map(Collections::singletonList).collect(Collectors.toList()),
+ Collections.nCopies(primaryKeyFields.size(), Index.RECORD_INDICATOR), false, sourceLoc);
+ }
+ } else if (primaryKeyFields != null) {
+ throw new CompilationException(ErrorCode.INVALID_PRIMARY_KEY_DEFINITION, sourceLoc);
}
// Check whether the view is usable:
@@ -2523,10 +2531,8 @@
List<List<Triple<DataverseName, String, String>>> dependencies =
ViewUtil.getViewDependencies(viewDecl, queryRewriter);
- ViewDetails viewDetails = cvs.hasItemType()
- ? new ViewDetails(cvs.getViewBody(), dependencies, cvs.getDefaultNull(), cvs.getDatetimeFormat(),
- cvs.getDateFormat(), cvs.getTimeFormat())
- : new ViewDetails(cvs.getViewBody(), dependencies, null, null, null, null);
+ ViewDetails viewDetails = new ViewDetails(cvs.getViewBody(), dependencies, cvs.getDefaultNull(),
+ primaryKeyFields, cvs.getDatetimeFormat(), cvs.getDateFormat(), cvs.getTimeFormat());
Dataset view = new Dataset(dataverseName, viewName, itemTypeDataverseName, itemTypeName,
MetadataConstants.METADATA_NODEGROUP_NAME, "", Collections.emptyMap(), viewDetails,
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-2-negative/create-view-2-negative.12.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-2-negative/create-view-2-negative.12.ddl.sqlpp
new file mode 100644
index 0000000..633af81
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-2-negative/create-view-2-negative.12.ddl.sqlpp
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+--- Negative: cannot declare primary key
+
+drop dataverse test if exists;
+create dataverse test;
+
+create view test.v1 primary key (r) not enforced as
+ select r from range(1,2) r;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.1.ddl.sqlpp
index 2e1f472..3576628 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.1.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.1.ddl.sqlpp
@@ -70,3 +70,55 @@
) default null
date 'MM/DD/YYYY'
as t2;
+
+/* primary key (not enforced) */
+
+create view v5_pk(
+ c_id int32 not unknown,
+ c_datetime datetime
+) default null
+ datetime 'MM/DD/YYYY hh:mm:ss.nnna'
+ primary key (c_id) not enforced
+as t2;
+
+/* primary key (not enforced), check that invalid tuples are eliminated */
+
+create view v6_pk_no_nulls(
+ c_i64 int64 not unknown,
+ c_id int32
+) default null
+ primary key (c_i64) not enforced
+as t1;
+
+/* no primary key, check that invalid tuples are eliminated if target field type is declared as not unknown */
+
+create view v7_no_nulls(
+ c_i64 int64 not unknown,
+ c_id int32
+) default null
+as t1;
+
+/* no primary key, check that invalid tuples are eliminated if target field type is declared as not unknown */
+
+create view v8_no_nulls_multi(
+ c_id int32,
+ c_x int64 not unknown,
+ c_y int64 not unknown
+) default null
+as
+ select
+ c_id,
+ case when to_bigint(c_i32) >= 0 then to_bigint(c_i32) when to_bigint(c_i32) < 0 then null else 0 end as c_x,
+ case when to_bigint(c_i64) >= 0 then null when to_bigint(c_i64) < 0 then to_bigint(c_i64) else 0 end as c_y
+ from t1;
+
+/* composite pk */
+
+create view v9_pk_composite(
+ c_id1 int32 not unknown,
+ c_id2 int32 not unknown
+) default null
+ primary key (c_id1, c_id2) not enforced
+as
+ select c_id as c_id1, -c_id as c_id2
+ from t1;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.10.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.10.query.sqlpp
new file mode 100644
index 0000000..62dddff
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.10.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * 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 test1;
+
+select c_id, c_x, c_y
+from v8_no_nulls_multi
+order by c_id;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.11.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.11.query.sqlpp
new file mode 100644
index 0000000..71bb255
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.11.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * 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 test1;
+
+select c_id1, c_id2
+from v9_pk_composite
+order by c_id1, c_id2;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.12.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.12.query.sqlpp
new file mode 100644
index 0000000..8c5d268
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.12.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+select DataverseName, DatasetName, ViewDetails
+from Metadata.`Dataset` d
+where DatasetType='VIEW'
+order by DataverseName, DatasetName;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.7.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.7.query.sqlpp
index 8c5d268..6513352 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.7.query.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.7.query.sqlpp
@@ -17,7 +17,8 @@
* under the License.
*/
-select DataverseName, DatasetName, ViewDetails
-from Metadata.`Dataset` d
-where DatasetType='VIEW'
-order by DataverseName, DatasetName;
+use test1;
+
+select c_id, c_date
+from v5_pk
+order by c_id;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.8.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.8.query.sqlpp
new file mode 100644
index 0000000..35a7799
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.8.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * 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 test1;
+
+select c_id, c_i64
+from v6_pk_no_nulls
+order by c_id;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.9.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.9.query.sqlpp
new file mode 100644
index 0000000..6f14510
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-3-typed/create-view-3-typed.9.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * 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 test1;
+
+select c_id, c_i64
+from v7_no_nulls
+order by c_id;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.10.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.10.ddl.sqlpp
new file mode 100644
index 0000000..f3c2076
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.10.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.
+ */
+
+--- Negative: primary key declaration requires "not enforced" modifier
+
+drop dataverse test if exists;
+create dataverse test;
+
+create view test.v1(r bigint)
+ default null
+ primary key (r)
+ as select r from range(1,2) r;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.11.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.11.ddl.sqlpp
new file mode 100644
index 0000000..fcb6727
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.11.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.
+ */
+
+--- Negative: primary key field must be declared as "not unknown"
+
+drop dataverse test if exists;
+create dataverse test;
+
+create view test.v1(r bigint)
+ default null
+ primary key (r) not enforced
+ as select r from range(1,2) r;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.12.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.12.ddl.sqlpp
new file mode 100644
index 0000000..343967a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.12.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.
+ */
+
+--- Negative: primary key field must be declared as "not unknown"
+
+drop dataverse test if exists;
+create dataverse test;
+
+create view test.v1(r bigint not unknown, r2 bigint)
+ default null
+ primary key (r, r2) not enforced
+ as select r, -r as r2 from range(1,2) r;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.13.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.13.ddl.sqlpp
new file mode 100644
index 0000000..101ed8f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.13.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.
+ */
+
+--- Negative: primary key declaration refers to a non-existent nested field
+
+drop dataverse test if exists;
+create dataverse test;
+
+create view test.v1(r bigint not unknown)
+ default null
+ primary key (r.unknown_field) not enforced
+ as select r from range(1,2) r;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.14.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.14.ddl.sqlpp
new file mode 100644
index 0000000..26a9668
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.14.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.
+ */
+
+--- Negative: primary key declaration uses
+--- meta() reference
+
+drop dataverse test if exists;
+create dataverse test;
+
+create view test.v1(r bigint not unknown)
+ default null
+ primary key (meta().unknown_field) not enforced
+ as select r from range(1,2) r;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.6.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.6.ddl.sqlpp
index dc344c3..2e01c03 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.6.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.6.ddl.sqlpp
@@ -17,14 +17,13 @@
* under the License.
*/
---- Negative: view type has non-optional fields
+--- Negative: invalid view parameter clause
drop dataverse test if exists;
create dataverse test;
-create type test.t1 as closed {
- r:int64
-};
-
-create view test.v1(t1) default null as
- select r from range(1,2) r;
+create view test.v1(cd date) default null
+ date 'YYYY-MM-DD'
+ date_illegal_property_name 'YYYY-MM-DD'
+as
+ select string(current_date()) cd from range(1,2) r;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.7.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.7.ddl.sqlpp
index 2e01c03..adb53a8 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.7.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.7.ddl.sqlpp
@@ -17,13 +17,10 @@
* under the License.
*/
---- Negative: invalid view parameter clause
+--- Negative: default null is required
drop dataverse test if exists;
create dataverse test;
-create view test.v1(cd date) default null
- date 'YYYY-MM-DD'
- date_illegal_property_name 'YYYY-MM-DD'
-as
- select string(current_date()) cd from range(1,2) r;
+create view test.v1(r bigint) as
+ select r from range(1,2) r;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.8.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.8.ddl.sqlpp
index adb53a8..dd7e995 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.8.ddl.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.8.ddl.sqlpp
@@ -17,10 +17,12 @@
* under the License.
*/
---- Negative: default null is required
+--- Negative: primary key declaration refers to a non-existent field
drop dataverse test if exists;
create dataverse test;
-create view test.v1(r bigint) as
- select r from range(1,2) r;
+create view test.v1(r bigint)
+ default null
+ primary key (unknown_field) not enforced
+ as select r from range(1,2) r;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.9.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.9.ddl.sqlpp
new file mode 100644
index 0000000..8aba1c8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/view/create-view-6-typed-negative/create-view-6-typed-negative.9.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.
+ */
+
+--- Negative: primary key declaration refers to a non-existent field
+
+drop dataverse test if exists;
+create dataverse test;
+
+create view test.v1(r bigint not unknown)
+ default null
+ primary key (r, unknown_field_2) not enforced
+ as select r from range(1,2) r;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.10.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.10.adm
new file mode 100644
index 0000000..af0378d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.10.adm
@@ -0,0 +1,3 @@
+{ "c_id": 2, "c_x": 0, "c_y": 0 }
+{ "c_id": 3, "c_x": 0, "c_y": 0 }
+{ "c_id": 4, "c_x": 0, "c_y": 0 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.11.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.11.adm
new file mode 100644
index 0000000..c32dff1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.11.adm
@@ -0,0 +1,5 @@
+{ "c_id1": 0, "c_id2": 0 }
+{ "c_id1": 1, "c_id2": -1 }
+{ "c_id1": 2, "c_id2": -2 }
+{ "c_id1": 3, "c_id2": -3 }
+{ "c_id1": 4, "c_id2": -4 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.12.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.12.adm
new file mode 100644
index 0000000..e561054
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.12.adm
@@ -0,0 +1,9 @@
+{ "DataverseName": "test1", "DatasetName": "v1", "ViewDetails": { "Definition": "t1", "Dependencies": [ [ [ "test1", "t1" ] ], [ ], [ ] ], "Default": null } }
+{ "DataverseName": "test1", "DatasetName": "v2_ref_type", "ViewDetails": { "Definition": "select c_id,\n c_i8, c_i16, c_i32, c_i64, c_f, c_d,\n c_b, c_s,\n c_datetime, c_date, c_time,\n c_dur, c_ymdur, c_dtdur\n from t1", "Dependencies": [ [ [ "test1", "t1" ] ], [ ], [ ] ], "Default": null } }
+{ "DataverseName": "test1", "DatasetName": "v3_datetime_format", "ViewDetails": { "Definition": "t2", "Dependencies": [ [ [ "test1", "t2" ] ], [ ], [ ] ], "Default": null, "DataFormat": [ "MM/DD/YYYY hh:mm:ss.nnna", "MM/DD/YYYY", "hh:mm:ss.nnna" ] } }
+{ "DataverseName": "test1", "DatasetName": "v4_date_format_only", "ViewDetails": { "Definition": "t2", "Dependencies": [ [ [ "test1", "t2" ] ], [ ], [ ] ], "Default": null, "DataFormat": [ null, "MM/DD/YYYY", null ] } }
+{ "DataverseName": "test1", "DatasetName": "v5_pk", "ViewDetails": { "Definition": "t2", "Dependencies": [ [ [ "test1", "t2" ] ], [ ], [ ] ], "Default": null, "PrimaryKey": [ [ "c_id" ] ], "PrimaryKeyEnforced": false, "DataFormat": [ "MM/DD/YYYY hh:mm:ss.nnna", null, null ] } }
+{ "DataverseName": "test1", "DatasetName": "v6_pk_no_nulls", "ViewDetails": { "Definition": "t1", "Dependencies": [ [ [ "test1", "t1" ] ], [ ], [ ] ], "Default": null, "PrimaryKey": [ [ "c_i64" ] ], "PrimaryKeyEnforced": false } }
+{ "DataverseName": "test1", "DatasetName": "v7_no_nulls", "ViewDetails": { "Definition": "t1", "Dependencies": [ [ [ "test1", "t1" ] ], [ ], [ ] ], "Default": null } }
+{ "DataverseName": "test1", "DatasetName": "v8_no_nulls_multi", "ViewDetails": { "Definition": "select\n c_id,\n case when to_bigint(c_i32) >= 0 then to_bigint(c_i32) when to_bigint(c_i32) < 0 then null else 0 end as c_x,\n case when to_bigint(c_i64) >= 0 then null when to_bigint(c_i64) < 0 then to_bigint(c_i64) else 0 end as c_y\n from t1", "Dependencies": [ [ [ "test1", "t1" ] ], [ ], [ ] ], "Default": null } }
+{ "DataverseName": "test1", "DatasetName": "v9_pk_composite", "ViewDetails": { "Definition": "select c_id as c_id1, -c_id as c_id2\n from t1", "Dependencies": [ [ [ "test1", "t1" ] ], [ ], [ ] ], "Default": null, "PrimaryKey": [ [ "c_id1" ], [ "c_id2" ] ], "PrimaryKeyEnforced": false } }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.7.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.7.adm
index adcf41f..83cff58 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.7.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.7.adm
@@ -1,4 +1,3 @@
-{ "DataverseName": "test1", "DatasetName": "v1", "ViewDetails": { "Definition": "t1", "Dependencies": [ [ [ "test1", "t1" ] ], [ ], [ ] ], "Default": null } }
-{ "DataverseName": "test1", "DatasetName": "v2_ref_type", "ViewDetails": { "Definition": "select c_id,\n c_i8, c_i16, c_i32, c_i64, c_f, c_d,\n c_b, c_s,\n c_datetime, c_date, c_time,\n c_dur, c_ymdur, c_dtdur\n from t1", "Dependencies": [ [ [ "test1", "t1" ] ], [ ], [ ] ], "Default": null } }
-{ "DataverseName": "test1", "DatasetName": "v3_datetime_format", "ViewDetails": { "Definition": "t2", "Dependencies": [ [ [ "test1", "t2" ] ], [ ], [ ] ], "Default": null, "DataFormat": [ "MM/DD/YYYY hh:mm:ss.nnna", "MM/DD/YYYY", "hh:mm:ss.nnna" ] } }
-{ "DataverseName": "test1", "DatasetName": "v4_date_format_only", "ViewDetails": { "Definition": "t2", "Dependencies": [ [ [ "test1", "t2" ] ], [ ], [ ] ], "Default": null, "DataFormat": [ null, "MM/DD/YYYY", null ] } }
\ No newline at end of file
+{ "c_id": 0 }
+{ "c_id": 1 }
+{ "c_id": 2 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.8.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.8.adm
new file mode 100644
index 0000000..6136866
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.8.adm
@@ -0,0 +1,2 @@
+{ "c_id": 0, "c_i64": 64 }
+{ "c_id": 1, "c_i64": -64 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.9.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.9.adm
new file mode 100644
index 0000000..6136866
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/view/create-view-3-typed/create-view-3-typed.9.adm
@@ -0,0 +1,2 @@
+{ "c_id": 0, "c_i64": 64 }
+{ "c_id": 1, "c_i64": -64 }
\ 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 94c43ed..29526f3 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -13106,6 +13106,7 @@
<expected-error>ASX1149: Illegal function or view recursion (in line 31, at column 1)</expected-error>
<expected-error>ASX1149: Illegal function or view recursion (in line 32, at column 1)</expected-error>
<expected-error>ASX1149: Illegal function or view recursion (in line 33, at column 1)</expected-error>
+ <expected-error><![CDATA[ASX1001: Syntax error: In line 25 >>create view test.v1 primary key (r) not enforced as<< Encountered "primary" at column 21]]></expected-error>
</compilation-unit>
</test-case>
<test-case FilePath="view">
@@ -13159,9 +13160,15 @@
<expected-error>ASX1079: Compilation error: view type cannot have open fields (in line 29, at column 1)</expected-error>
<expected-error>ASX1004: Unsupported type: view cannot process input type t1_a (in line 30, at column 1)</expected-error>
<expected-error><![CDATA[ASX1001: Syntax error: In line 25 >>create view test.v1(r bigint, a [bigint]) default null as<< Encountered "[" at column 33]]></expected-error>
- <expected-error>ASX1079: Compilation error: Invalid type for field r. The type must allow MISSING and NULL (in line 29, at column 1)</expected-error>
<expected-error>ASX1001: Syntax error: ASX1092: Parameter date_illegal_property_name cannot be set (in line 25, at column 1)</expected-error>
<expected-error><![CDATA[ASX1001: Syntax error: In line 25 >>create view test.v1(r bigint) as<< Encountered "as" at column 31]]></expected-error>
+ <expected-error><![CDATA[ASX1014: Field "unknown_field" is not found (in line 25, at column 1)]]></expected-error>
+ <expected-error><![CDATA[ASX1014: Field "unknown_field_2" is not found (in line 25, at column 1)]]></expected-error>
+ <expected-error><![CDATA[ASX1001: Syntax error: In line 28 >> as select r from range(1,2) r;<< Encountered "as" at column 3]]></expected-error>
+ <expected-error><![CDATA[ASX1021: The primary key field "r" cannot be nullable (in line 25, at column 1)]]></expected-error>
+ <expected-error><![CDATA[ASX1021: The primary key field "r2" cannot be nullable (in line 25, at column 1)]]></expected-error>
+ <expected-error><![CDATA[ASX1001: Syntax error: ASX1162: Invalid primary key definition (in line 25, at column 1)]]></expected-error>
+ <expected-error><![CDATA[ASX1001: Syntax error: ASX1162: Invalid primary key definition (in line 26, at column 1)]]></expected-error>
<source-location>false</source-location>
</compilation-unit>
</test-case>
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 79d663e..ecec240 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
@@ -246,6 +246,7 @@
UNKNOWN_VIEW(1159),
VIEW_EXISTS(1160),
UNSUPPORTED_TYPE_FOR_PARQUET(1161),
+ INVALID_PRIMARY_KEY_DEFINITION(1162),
// Feed errors
DATAFLOW_ILLEGAL_STATE(3001),
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 159e0ef..e964531 100644
--- a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
+++ b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
@@ -248,6 +248,7 @@
1159 = Cannot find view with name %1$s
1160 = A view with this name %1$s already exists
1161 = Type '%1$s' contains declared fields, which is not supported for 'parquet' format
+1162 = Invalid primary key definition
# Feed Errors
3001 = Illegal state.
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CreateViewStatement.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CreateViewStatement.java
index ae3c50e..74586eb 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CreateViewStatement.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/CreateViewStatement.java
@@ -19,6 +19,7 @@
package org.apache.asterix.lang.common.statement;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -30,6 +31,7 @@
import org.apache.asterix.lang.common.expression.TypeExpression;
import org.apache.asterix.lang.common.util.ViewUtil;
import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
+import org.apache.hyracks.algebricks.common.utils.Pair;
public final class CreateViewStatement extends AbstractStatement {
@@ -45,6 +47,8 @@
private final Map<String, String> viewConfig;
+ private final List<String> primaryKeyFields;
+
private final Boolean defaultNull;
private final boolean replaceIfExists;
@@ -52,15 +56,18 @@
private final boolean ifNotExists;
public CreateViewStatement(DataverseName dataverseName, String viewName, TypeExpression itemType, String viewBody,
- Expression viewBodyExpression, Boolean defaultNull, Map<String, String> viewConfig, boolean replaceIfExists,
- boolean ifNotExists) throws CompilationException {
+ Expression viewBodyExpression, Boolean defaultNull, Map<String, String> viewConfig,
+ Pair<List<Integer>, List<List<String>>> primaryKeyFields, boolean replaceIfExists, boolean ifNotExists)
+ throws CompilationException {
this.dataverseName = dataverseName;
this.viewName = Objects.requireNonNull(viewName);
this.itemType = itemType;
+ boolean hasItemType = itemType != null;
this.viewBody = Objects.requireNonNull(viewBody);
this.viewBodyExpression = Objects.requireNonNull(viewBodyExpression);
this.defaultNull = defaultNull;
- this.viewConfig = ViewUtil.validateViewConfiguration(viewConfig, itemType != null);
+ this.viewConfig = ViewUtil.validateViewConfiguration(viewConfig, hasItemType);
+ this.primaryKeyFields = ViewUtil.validateViewPrimaryKey(primaryKeyFields, hasItemType);
this.replaceIfExists = replaceIfExists;
this.ifNotExists = ifNotExists;
}
@@ -113,6 +120,10 @@
return defaultNull;
}
+ public List<String> getPrimaryKeyFields() {
+ return primaryKeyFields;
+ }
+
public String getDatetimeFormat() {
return viewConfig.get(ViewUtil.DATETIME_PARAMETER_NAME);
}
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/ViewUtil.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/ViewUtil.java
index 3dc08ee..7b87ffe 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/ViewUtil.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/ViewUtil.java
@@ -43,6 +43,7 @@
import org.apache.asterix.lang.common.statement.ViewDecl;
import org.apache.asterix.lang.common.struct.Identifier;
import org.apache.asterix.lang.common.struct.VarIdentifier;
+import org.apache.asterix.metadata.entities.Index;
import org.apache.asterix.metadata.entities.ViewDetails;
import org.apache.asterix.om.functions.BuiltinFunctions;
import org.apache.asterix.om.types.ARecordType;
@@ -50,6 +51,7 @@
import org.apache.asterix.om.types.AUnionType;
import org.apache.asterix.om.types.BuiltinType;
import org.apache.asterix.om.types.IAType;
+import org.apache.hyracks.algebricks.common.utils.Pair;
import org.apache.hyracks.algebricks.common.utils.Triple;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
import org.apache.hyracks.api.exceptions.IWarningCollector;
@@ -110,16 +112,17 @@
IAType[] fieldTypes = recordType.getFieldTypes();
for (int i = 0, n = fieldNames.length; i < n; i++) {
IAType fieldType = fieldTypes[i];
- if (fieldType.getTypeTag() != ATypeTag.UNION) {
- throw new CompilationException(ErrorCode.COMPILATION_ERROR, sourceLoc, String
- .format("Invalid type for field %s. The type must allow MISSING and NULL", fieldNames[i]));
+ IAType primeType;
+ if (fieldType.getTypeTag() == ATypeTag.UNION) {
+ AUnionType unionType = (AUnionType) fieldType;
+ if (!unionType.isNullableType()) {
+ throw new CompilationException(ErrorCode.COMPILATION_ERROR, sourceLoc,
+ String.format("Invalid type for field %s. Optional type must allow NULL", fieldNames[i]));
+ }
+ primeType = unionType.getActualType();
+ } else {
+ primeType = fieldType;
}
- AUnionType unionType = (AUnionType) fieldType;
- if (!unionType.isMissableType() || !unionType.isNullableType()) {
- throw new CompilationException(ErrorCode.COMPILATION_ERROR, sourceLoc, String
- .format("Invalid type for field %s. The type must allow MISSING and NULL", fieldNames[i]));
- }
- IAType primeType = unionType.getActualType();
if (getTypeConstructor(primeType) == null) {
throw new CompilationException(ErrorCode.COMPILATION_TYPE_UNSUPPORTED, sourceLoc, "view",
primeType.getTypeName());
@@ -151,6 +154,31 @@
return viewConfig;
}
+ public static List<String> validateViewPrimaryKey(Pair<List<Integer>, List<List<String>>> primaryKeyFieldsPair,
+ boolean hasItemType) throws CompilationException {
+ if (primaryKeyFieldsPair == null || primaryKeyFieldsPair.second.isEmpty()) {
+ return null;
+ }
+ if (!hasItemType) {
+ throw new CompilationException(ErrorCode.INVALID_PRIMARY_KEY_DEFINITION);
+ }
+ List<Integer> sourceIndicators = primaryKeyFieldsPair.first;
+ List<List<String>> primaryKeyFields = primaryKeyFieldsPair.second;
+ int n = primaryKeyFields.size();
+ List<String> resultFields = new ArrayList<>(n);
+ for (int i = 0; i < n; i++) {
+ if (sourceIndicators.get(i) != Index.RECORD_INDICATOR) {
+ throw new CompilationException(ErrorCode.INVALID_PRIMARY_KEY_DEFINITION);
+ }
+ List<String> nestedField = primaryKeyFields.get(i);
+ if (nestedField.size() != 1) {
+ throw new CompilationException(ErrorCode.INVALID_PRIMARY_KEY_DEFINITION);
+ }
+ resultFields.add(nestedField.get(0));
+ }
+ return resultFields;
+ }
+
public static Expression createTypeConvertExpression(Expression inExpr, IAType targetType,
Triple<String, String, String> temporalDataFormat, DatasetFullyQualifiedName viewName,
SourceLocation sourceLoc) throws CompilationException {
@@ -183,6 +211,18 @@
return missing2NullExpr;
}
+ public static Expression createNotIsNullExpression(Expression inExpr, SourceLocation sourceLoc) {
+ List<Expression> isNullArgs = new ArrayList<>(1);
+ isNullArgs.add(inExpr);
+ CallExpr isNullExpr = new CallExpr(new FunctionSignature(BuiltinFunctions.IS_NULL), isNullArgs);
+ isNullExpr.setSourceLocation(sourceLoc);
+ List<Expression> notExprArgs = new ArrayList<>(1);
+ notExprArgs.add(isNullExpr);
+ CallExpr notExpr = new CallExpr(new FunctionSignature(BuiltinFunctions.NOT), notExprArgs);
+ notExpr.setSourceLocation(sourceLoc);
+ return notExpr;
+ }
+
public static Expression createFieldAccessExpression(VarIdentifier inVar, String fieldName,
SourceLocation sourceLoc) {
VariableExpr inVarRef = new VariableExpr(inVar);
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java
index 5ac1e2d..868469b 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java
@@ -25,26 +25,35 @@
import org.apache.asterix.common.exceptions.CompilationException;
import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.functions.FunctionSignature;
import org.apache.asterix.common.metadata.DatasetFullyQualifiedName;
+import org.apache.asterix.lang.common.base.AbstractClause;
import org.apache.asterix.lang.common.base.Expression;
import org.apache.asterix.lang.common.base.IParserFactory;
import org.apache.asterix.lang.common.base.IReturningStatement;
+import org.apache.asterix.lang.common.clause.LetClause;
+import org.apache.asterix.lang.common.clause.WhereClause;
+import org.apache.asterix.lang.common.expression.CallExpr;
+import org.apache.asterix.lang.common.expression.FieldBinding;
+import org.apache.asterix.lang.common.expression.LiteralExpr;
+import org.apache.asterix.lang.common.expression.RecordConstructor;
import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.literal.StringLiteral;
import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
import org.apache.asterix.lang.common.struct.VarIdentifier;
import org.apache.asterix.lang.common.util.ViewUtil;
import org.apache.asterix.lang.sqlpp.clause.FromClause;
import org.apache.asterix.lang.sqlpp.clause.FromTerm;
-import org.apache.asterix.lang.sqlpp.clause.Projection;
import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
import org.apache.asterix.lang.sqlpp.clause.SelectClause;
-import org.apache.asterix.lang.sqlpp.clause.SelectRegular;
+import org.apache.asterix.lang.sqlpp.clause.SelectElement;
import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation;
import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
import org.apache.asterix.lang.sqlpp.struct.SetOperationInput;
-import org.apache.asterix.om.typecomputer.impl.TypeComputeUtils;
+import org.apache.asterix.om.functions.BuiltinFunctions;
import org.apache.asterix.om.types.ARecordType;
import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.AUnionType;
import org.apache.asterix.om.types.IAType;
import org.apache.hyracks.algebricks.common.utils.Triple;
import org.apache.hyracks.api.exceptions.SourceLocation;
@@ -142,16 +151,49 @@
throw new CompilationException(ErrorCode.COMPILATION_TYPE_UNSUPPORTED, sourceLoc, viewName,
itemType.getTypeName());
}
- List<Projection> projections = new ArrayList<>(n);
+ List<FieldBinding> projections = new ArrayList<>(n);
+ List<AbstractClause> letWhereClauseList = new ArrayList<>(n + 1);
+ List<Expression> filters = null;
VarIdentifier fromVar = context.newVariable();
for (int i = 0; i < n; i++) {
String fieldName = fieldNames[i];
- IAType targetType = TypeComputeUtils.getActualType(fieldTypes[i]);
+ IAType fieldType = fieldTypes[i];
+ IAType primeType;
+ boolean fieldTypeNullable;
+ if (fieldType.getTypeTag() == ATypeTag.UNION) {
+ AUnionType unionType = (AUnionType) fieldType;
+ fieldTypeNullable = unionType.isNullableType();
+ if (!fieldTypeNullable) {
+ throw new CompilationException(ErrorCode.COMPILATION_TYPE_UNSUPPORTED, sourceLoc, viewName,
+ unionType.toString());
+ }
+ primeType = unionType.getActualType();
+ } else {
+ fieldTypeNullable = false;
+ primeType = fieldType;
+ }
Expression expr = ViewUtil.createFieldAccessExpression(fromVar, fieldName, sourceLoc);
expr = ViewUtil.createMissingToNullExpression(expr, sourceLoc); // Default Null handling
Expression projectExpr =
- ViewUtil.createTypeConvertExpression(expr, targetType, temporalDataFormat, viewName, sourceLoc);
- projections.add(new Projection(projectExpr, fieldName, false, false));
+ ViewUtil.createTypeConvertExpression(expr, primeType, temporalDataFormat, viewName, sourceLoc);
+ VarIdentifier projectVar = context.newVariable();
+ VariableExpr projectVarRef1 = new VariableExpr(projectVar);
+ projectVarRef1.setSourceLocation(sourceLoc);
+ LetClause letClause = new LetClause(projectVarRef1, projectExpr);
+ letWhereClauseList.add(letClause);
+ VariableExpr projectVarRef2 = new VariableExpr(projectVar);
+ projectVarRef2.setSourceLocation(sourceLoc);
+ projections.add(new FieldBinding(new LiteralExpr(new StringLiteral(fieldName)), projectVarRef2));
+
+ if (!fieldTypeNullable) {
+ VariableExpr projectVarRef3 = new VariableExpr(projectVar);
+ projectVarRef3.setSourceLocation(sourceLoc);
+ Expression notIsNullExpr = ViewUtil.createNotIsNullExpression(projectVarRef3, sourceLoc);
+ if (filters == null) {
+ filters = new ArrayList<>();
+ }
+ filters.add(notIsNullExpr);
+ }
}
VariableExpr fromVarRef = new VariableExpr(fromVar);
@@ -159,9 +201,27 @@
FromClause fromClause =
new FromClause(Collections.singletonList(new FromTerm(bodyExpr, fromVarRef, null, null)));
fromClause.setSourceLocation(sourceLoc);
- SelectClause selectClause = new SelectClause(null, new SelectRegular(projections), false);
+
+ if (filters != null && !filters.isEmpty()) {
+ Expression whereExpr;
+ if (filters.size() == 1) {
+ whereExpr = filters.get(0);
+ } else {
+ CallExpr andExpr = new CallExpr(new FunctionSignature(BuiltinFunctions.AND), filters);
+ andExpr.setSourceLocation(sourceLoc);
+ whereExpr = andExpr;
+ }
+ WhereClause whereClause = new WhereClause(whereExpr);
+ whereClause.setSourceLocation(sourceLoc);
+ letWhereClauseList.add(whereClause);
+ }
+
+ RecordConstructor recordConstr = new RecordConstructor(projections);
+ recordConstr.setSourceLocation(sourceLoc);
+
+ SelectClause selectClause = new SelectClause(new SelectElement(recordConstr), null, false);
selectClause.setSourceLocation(sourceLoc);
- SelectBlock selectBlock = new SelectBlock(selectClause, fromClause, null, null, null);
+ SelectBlock selectBlock = new SelectBlock(selectClause, fromClause, letWhereClauseList, null, null);
selectBlock.setSourceLocation(sourceLoc);
SelectSetOperation selectSetOperation = new SelectSetOperation(new SetOperationInput(selectBlock, null), null);
selectSetOperation.setSourceLocation(sourceLoc);
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index 0e6bb5e..f3e19a3 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -1438,6 +1438,7 @@
Expression viewBodyExpr = null;
Boolean defaultNull = null;
Map<String, String> viewConfig = null;
+ Pair<List<Integer>, List<List<String>>> primaryKeyFields = null;
DataverseName currentDataverse = defaultDataverse;
}
{
@@ -1448,6 +1449,7 @@
ifNotExists = IfNotExists()
<IDENTIFIER> { expectToken(DEFAULT); } <NULL> { defaultNull = true; }
viewConfig = ViewConfiguration()
+ ( <PRIMARY> <KEY> <LEFTPAREN> primaryKeyFields = PrimaryKeyFields() <RIGHTPAREN> <NOT> <ENFORCED> )?
)
|
( ifNotExists = IfNotExists() )
@@ -1474,7 +1476,7 @@
defaultDataverse = currentDataverse;
try {
CreateViewStatement stmt = new CreateViewStatement(nameComponents.first, nameComponents.second.getValue(),
- typeExpr, viewBody, viewBodyExpr, defaultNull, viewConfig, orReplace, ifNotExists);
+ typeExpr, viewBody, viewBodyExpr, defaultNull, viewConfig, primaryKeyFields, orReplace, ifNotExists);
return addSourceLocation(stmt, startStmtToken);
} catch (CompilationException e) {
throw new SqlppParseException(getSourceLocation(startStmtToken), e.getMessage());
@@ -1950,12 +1952,23 @@
Pair<List<Integer>, List<List<String>>> PrimaryKey() throws ParseException:
{
+ Pair<List<Integer>, List<List<String>>> primaryKeyFields = null;
+}
+{
+ <PRIMARY> <KEY> primaryKeyFields = PrimaryKeyFields()
+ {
+ return primaryKeyFields;
+ }
+}
+
+Pair<List<Integer>, List<List<String>>> PrimaryKeyFields() throws ParseException:
+{
Pair<Integer, List<String>> tmp = null;
List<Integer> keyFieldSourceIndicators = new ArrayList<Integer>();
List<List<String>> primaryKeyFields = new ArrayList<List<String>>();
}
{
- <PRIMARY> <KEY> tmp = NestedField()
+ tmp = NestedField()
{
keyFieldSourceIndicators.add(tmp.first);
primaryKeyFields.add(tmp.second);
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java
index 567568d..a7af77e 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java
@@ -95,6 +95,7 @@
public static final String FIELD_NAME_PENDING_OP = "PendingOp";
public static final String FIELD_NAME_POLICY_NAME = "PolicyName";
public static final String FIELD_NAME_PRIMARY_KEY = "PrimaryKey";
+ public static final String FIELD_NAME_PRIMARY_KEY_ENFORCED = "PrimaryKeyEnforced";
public static final String FIELD_NAME_PROPERTIES = "Properties";
public static final String FIELD_NAME_RECORD = "Record";
public static final String FIELD_NAME_RETURN_TYPE = "ReturnType";
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/ViewDetails.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/ViewDetails.java
index 4e6f96d..cd98f32 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/ViewDetails.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/ViewDetails.java
@@ -36,6 +36,7 @@
import org.apache.asterix.metadata.IDatasetDetails;
import org.apache.asterix.metadata.bootstrap.MetadataRecordTypes;
import org.apache.asterix.metadata.entitytupletranslators.AbstractTupleTranslator;
+import org.apache.asterix.om.base.ABoolean;
import org.apache.asterix.om.base.AMutableString;
import org.apache.asterix.om.base.ANull;
import org.apache.asterix.om.base.AString;
@@ -67,14 +68,18 @@
private final String timeFormat;
+ private final List<String> primaryKeyFields;
+
public ViewDetails(String viewBody, List<List<Triple<DataverseName, String, String>>> dependencies,
- Boolean defaultNull, String datetimeFormat, String dateFormat, String timeFormat) {
+ Boolean defaultNull, List<String> primaryKeyFields, String datetimeFormat, String dateFormat,
+ String timeFormat) {
this.viewBody = Objects.requireNonNull(viewBody);
this.dependencies = Objects.requireNonNull(dependencies);
this.defaultNull = defaultNull;
this.datetimeFormat = datetimeFormat;
this.dateFormat = dateFormat;
this.timeFormat = timeFormat;
+ this.primaryKeyFields = primaryKeyFields;
}
@Override
@@ -96,6 +101,10 @@
return defaultNull;
}
+ public List<String> getPrimaryKeyFields() {
+ return primaryKeyFields;
+ }
+
public String getDatetimeFormat() {
return datetimeFormat;
}
@@ -120,7 +129,8 @@
AMutableString aString = new AMutableString("");
ISerializerDeserializer<AString> stringSerde =
SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ASTRING);
-
+ ISerializerDeserializer<ABoolean> booleanSerde =
+ SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ABOOLEAN);
ISerializerDeserializer<ANull> nullSerde =
SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ANULL);
@@ -180,6 +190,40 @@
viewRecordBuilder.addField(fieldName, fieldValue);
}
+ // write field 'PrimaryKey'
+ if (primaryKeyFields != null && !primaryKeyFields.isEmpty()) {
+ fieldName.reset();
+ aString.setValue(MetadataRecordTypes.FIELD_NAME_PRIMARY_KEY);
+ stringSerde.serialize(aString, fieldName.getDataOutput());
+
+ // write as list of lists to be consistent with how InternalDatasetDetails writes its primary key
+ OrderedListBuilder primaryKeyListBuilder = new OrderedListBuilder();
+ OrderedListBuilder listBuilder = new OrderedListBuilder();
+
+ primaryKeyListBuilder.reset(FULL_OPEN_ORDEREDLIST_TYPE);
+ for (String field : primaryKeyFields) {
+ listBuilder.reset(FULL_OPEN_ORDEREDLIST_TYPE);
+ itemValue.reset();
+ aString.setValue(field);
+ stringSerde.serialize(aString, itemValue.getDataOutput());
+ listBuilder.addItem(itemValue);
+ itemValue.reset();
+ listBuilder.write(itemValue.getDataOutput(), true);
+ primaryKeyListBuilder.addItem(itemValue);
+ }
+ fieldValue.reset();
+ primaryKeyListBuilder.write(fieldValue.getDataOutput(), true);
+ viewRecordBuilder.addField(fieldName, fieldValue);
+
+ // write field 'PrimaryKeyEnforced'
+ fieldName.reset();
+ aString.setValue(MetadataRecordTypes.FIELD_NAME_PRIMARY_KEY_ENFORCED);
+ stringSerde.serialize(aString, fieldName.getDataOutput());
+ fieldValue.reset();
+ booleanSerde.serialize(ABoolean.FALSE, fieldValue.getDataOutput());
+ viewRecordBuilder.addField(fieldName, fieldValue);
+ }
+
// write field 'Format'
if (datetimeFormat != null || dateFormat != null || timeFormat != null) {
fieldName.reset();
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/DatasetTupleTranslator.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/DatasetTupleTranslator.java
index 1fe51b3..6caf649 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/DatasetTupleTranslator.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/DatasetTupleTranslator.java
@@ -35,6 +35,8 @@
import org.apache.asterix.builders.UnorderedListBuilder;
import org.apache.asterix.common.config.DatasetConfig.DatasetType;
import org.apache.asterix.common.config.DatasetConfig.TransactionState;
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.common.exceptions.ErrorCode;
import org.apache.asterix.common.metadata.DataverseName;
import org.apache.asterix.metadata.IDatasetDetails;
import org.apache.asterix.metadata.bootstrap.MetadataPrimaryIndexes;
@@ -274,6 +276,25 @@
defaultNull = defaultValue.getType().getTypeTag() == ATypeTag.NULL;
}
+ // Primary Key
+ List<String> primaryKeyFields = null;
+ int primaryKeyFieldPos =
+ datasetDetailsRecord.getType().getFieldIndex(MetadataRecordTypes.FIELD_NAME_PRIMARY_KEY);
+ if (primaryKeyFieldPos >= 0) {
+ AOrderedList primaryKeyFieldList =
+ ((AOrderedList) datasetDetailsRecord.getValueByPos(primaryKeyFieldPos));
+ int n = primaryKeyFieldList.size();
+ primaryKeyFields = new ArrayList<>(n);
+ for (int i = 0; i < n; i++) {
+ AOrderedList list = (AOrderedList) primaryKeyFieldList.getItem(i);
+ if (list.size() != 1) {
+ throw new AsterixException(ErrorCode.METADATA_ERROR, list.toJSON());
+ }
+ AString str = (AString) list.getItem(0);
+ primaryKeyFields.add(str.getStringValue());
+ }
+ }
+
// Format fields
String datetimeFormat = null, dateFormat = null, timeFormat = null;
int formatFieldPos =
@@ -292,8 +313,8 @@
}
}
- datasetDetails =
- new ViewDetails(definition, dependencies, defaultNull, datetimeFormat, dateFormat, timeFormat);
+ datasetDetails = new ViewDetails(definition, dependencies, defaultNull, primaryKeyFields,
+ datetimeFormat, dateFormat, timeFormat);
break;
}
}