2017-03-15 11:37:17 +00:00
|
|
|
// Copyright 2016 Mozilla
|
|
|
|
//
|
|
|
|
// Licensed 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.
|
|
|
|
|
2017-04-07 23:24:57 +00:00
|
|
|
use std::collections::BTreeSet;
|
2020-01-14 15:46:21 +00:00
|
|
|
use std::fmt::{Debug, Formatter};
|
|
|
|
|
|
|
|
use core_traits::{Entid, TypedValue, ValueType, ValueTypeSet};
|
|
|
|
|
|
|
|
use mentat_core::ValueRc;
|
|
|
|
|
|
|
|
use edn::query::{Direction, FindSpec, Keyword, Limit, Order, SrcVar, Variable, WhereClause};
|
2017-03-28 02:34:02 +00:00
|
|
|
|
2017-03-15 11:37:17 +00:00
|
|
|
/// This enum models the fixed set of default tables we have -- two
|
2017-04-07 23:24:57 +00:00
|
|
|
/// tables and two views -- and computed tables defined in the enclosing CC.
|
2017-03-15 11:37:17 +00:00
|
|
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
|
|
|
pub enum DatomsTable {
|
2020-01-14 15:46:21 +00:00
|
|
|
Datoms, // The non-fulltext datoms table.
|
|
|
|
FulltextValues, // The virtual table mapping IDs to strings.
|
|
|
|
FulltextDatoms, // The fulltext-datoms view.
|
|
|
|
AllDatoms, // Fulltext and non-fulltext datoms.
|
|
|
|
Computed(usize), // A computed table, tracked elsewhere in the query.
|
|
|
|
Transactions, // The transactions table, which makes the tx-data log API efficient.
|
2017-04-07 23:24:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A source of rows that isn't a named table -- typically a subquery or union.
|
2017-04-28 09:44:11 +00:00
|
|
|
#[derive(PartialEq, Eq, Debug)]
|
2017-04-07 23:24:57 +00:00
|
|
|
pub enum ComputedTable {
|
2017-04-28 09:44:11 +00:00
|
|
|
Subquery(::clauses::ConjoiningClauses),
|
2017-04-07 23:24:57 +00:00
|
|
|
Union {
|
|
|
|
projection: BTreeSet<Variable>,
|
|
|
|
type_extraction: BTreeSet<Variable>,
|
|
|
|
arms: Vec<::clauses::ConjoiningClauses>,
|
|
|
|
},
|
2017-04-26 22:50:17 +00:00
|
|
|
NamedValues {
|
|
|
|
names: Vec<Variable>,
|
|
|
|
values: Vec<TypedValue>,
|
|
|
|
},
|
2017-03-15 11:37:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DatomsTable {
|
|
|
|
pub fn name(&self) -> &'static str {
|
|
|
|
match *self {
|
|
|
|
DatomsTable::Datoms => "datoms",
|
|
|
|
DatomsTable::FulltextValues => "fulltext_values",
|
|
|
|
DatomsTable::FulltextDatoms => "fulltext_datoms",
|
|
|
|
DatomsTable::AllDatoms => "all_datoms",
|
2017-04-07 23:24:57 +00:00
|
|
|
DatomsTable::Computed(_) => "c",
|
2018-04-16 21:08:00 +00:00
|
|
|
DatomsTable::Transactions => "transactions",
|
2017-03-15 11:37:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-08 00:23:41 +00:00
|
|
|
pub trait ColumnName {
|
|
|
|
fn column_name(&self) -> String;
|
|
|
|
}
|
|
|
|
|
2017-03-15 11:37:17 +00:00
|
|
|
/// One of the named columns of our tables.
|
2017-04-08 00:23:41 +00:00
|
|
|
#[derive(PartialEq, Eq, Clone)]
|
2017-03-15 11:37:17 +00:00
|
|
|
pub enum DatomsColumn {
|
|
|
|
Entity,
|
|
|
|
Attribute,
|
|
|
|
Value,
|
|
|
|
Tx,
|
|
|
|
ValueTypeTag,
|
|
|
|
}
|
|
|
|
|
2017-06-12 21:24:56 +00:00
|
|
|
/// One of the named columns of our fulltext values table.
|
|
|
|
#[derive(PartialEq, Eq, Clone)]
|
|
|
|
pub enum FulltextColumn {
|
|
|
|
Rowid,
|
|
|
|
Text,
|
|
|
|
}
|
|
|
|
|
2018-04-16 21:08:00 +00:00
|
|
|
/// One of the named columns of our transactions table.
|
|
|
|
#[derive(PartialEq, Eq, Clone)]
|
|
|
|
pub enum TransactionsColumn {
|
|
|
|
Entity,
|
|
|
|
Attribute,
|
|
|
|
Value,
|
|
|
|
Tx,
|
|
|
|
Added,
|
|
|
|
ValueTypeTag,
|
|
|
|
}
|
|
|
|
|
2017-04-08 00:23:41 +00:00
|
|
|
#[derive(PartialEq, Eq, Clone)]
|
|
|
|
pub enum VariableColumn {
|
|
|
|
Variable(Variable),
|
|
|
|
VariableTypeTag(Variable),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Clone)]
|
|
|
|
pub enum Column {
|
|
|
|
Fixed(DatomsColumn),
|
2017-06-12 21:24:56 +00:00
|
|
|
Fulltext(FulltextColumn),
|
2017-04-08 00:23:41 +00:00
|
|
|
Variable(VariableColumn),
|
2018-04-16 21:08:00 +00:00
|
|
|
Transactions(TransactionsColumn),
|
2017-04-08 00:23:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<DatomsColumn> for Column {
|
|
|
|
fn from(from: DatomsColumn) -> Column {
|
|
|
|
Column::Fixed(from)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<VariableColumn> for Column {
|
|
|
|
fn from(from: VariableColumn) -> Column {
|
|
|
|
Column::Variable(from)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-16 21:08:00 +00:00
|
|
|
impl From<TransactionsColumn> for Column {
|
|
|
|
fn from(from: TransactionsColumn) -> Column {
|
|
|
|
Column::Transactions(from)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-15 11:37:17 +00:00
|
|
|
impl DatomsColumn {
|
|
|
|
pub fn as_str(&self) -> &'static str {
|
|
|
|
use self::DatomsColumn::*;
|
|
|
|
match *self {
|
|
|
|
Entity => "e",
|
|
|
|
Attribute => "a",
|
|
|
|
Value => "v",
|
|
|
|
Tx => "tx",
|
|
|
|
ValueTypeTag => "value_type_tag",
|
|
|
|
}
|
|
|
|
}
|
2018-04-18 15:38:46 +00:00
|
|
|
|
|
|
|
/// The type of the `v` column is determined by the `value_type_tag` column. Return the
|
|
|
|
/// associated column determining the type of this column, if there is one.
|
|
|
|
pub fn associated_type_tag_column(&self) -> Option<DatomsColumn> {
|
|
|
|
use self::DatomsColumn::*;
|
|
|
|
match *self {
|
|
|
|
Value => Some(ValueTypeTag),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
2017-03-15 11:37:17 +00:00
|
|
|
}
|
|
|
|
|
2017-04-08 00:23:41 +00:00
|
|
|
impl ColumnName for DatomsColumn {
|
|
|
|
fn column_name(&self) -> String {
|
|
|
|
self.as_str().to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ColumnName for VariableColumn {
|
|
|
|
fn column_name(&self) -> String {
|
|
|
|
match self {
|
2020-01-23 18:16:19 +00:00
|
|
|
VariableColumn::Variable(ref v) => v.to_string(),
|
|
|
|
VariableColumn::VariableTypeTag(ref v) => format!("{}_value_type_tag", v.as_str()),
|
2017-04-08 00:23:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for VariableColumn {
|
2018-05-31 21:10:49 +00:00
|
|
|
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
|
2017-04-08 00:23:41 +00:00
|
|
|
match self {
|
|
|
|
// These should agree with VariableColumn::column_name.
|
2020-01-23 18:16:19 +00:00
|
|
|
VariableColumn::Variable(ref v) => write!(f, "{}", v.as_str()),
|
|
|
|
VariableColumn::VariableTypeTag(ref v) => write!(f, "{}_value_type_tag", v.as_str()),
|
2017-04-08 00:23:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for DatomsColumn {
|
2018-05-31 21:10:49 +00:00
|
|
|
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
|
2017-04-08 00:23:41 +00:00
|
|
|
write!(f, "{}", self.as_str())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for Column {
|
2018-05-31 21:10:49 +00:00
|
|
|
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
|
2017-04-08 00:23:41 +00:00
|
|
|
match self {
|
2020-01-23 18:16:19 +00:00
|
|
|
Column::Fixed(ref c) => c.fmt(f),
|
|
|
|
Column::Fulltext(ref c) => c.fmt(f),
|
|
|
|
Column::Variable(ref v) => v.fmt(f),
|
|
|
|
Column::Transactions(ref t) => t.fmt(f),
|
2017-04-08 00:23:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-12 21:24:56 +00:00
|
|
|
impl FulltextColumn {
|
|
|
|
pub fn as_str(&self) -> &'static str {
|
|
|
|
use self::FulltextColumn::*;
|
|
|
|
match *self {
|
|
|
|
Rowid => "rowid",
|
|
|
|
Text => "text",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ColumnName for FulltextColumn {
|
|
|
|
fn column_name(&self) -> String {
|
|
|
|
self.as_str().to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for FulltextColumn {
|
2018-05-31 21:10:49 +00:00
|
|
|
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
|
2017-06-12 21:24:56 +00:00
|
|
|
write!(f, "{}", self.as_str())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-16 21:08:00 +00:00
|
|
|
impl TransactionsColumn {
|
|
|
|
pub fn as_str(&self) -> &'static str {
|
|
|
|
use self::TransactionsColumn::*;
|
|
|
|
match *self {
|
|
|
|
Entity => "e",
|
|
|
|
Attribute => "a",
|
|
|
|
Value => "v",
|
|
|
|
Tx => "tx",
|
|
|
|
Added => "added",
|
|
|
|
ValueTypeTag => "value_type_tag",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn associated_type_tag_column(&self) -> Option<TransactionsColumn> {
|
|
|
|
use self::TransactionsColumn::*;
|
|
|
|
match *self {
|
|
|
|
Value => Some(ValueTypeTag),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ColumnName for TransactionsColumn {
|
|
|
|
fn column_name(&self) -> String {
|
|
|
|
self.as_str().to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for TransactionsColumn {
|
2018-05-31 21:10:49 +00:00
|
|
|
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
|
2018-04-16 21:08:00 +00:00
|
|
|
write!(f, "{}", self.as_str())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-15 11:37:17 +00:00
|
|
|
/// A specific instance of a table within a query. E.g., "datoms123".
|
|
|
|
pub type TableAlias = String;
|
|
|
|
|
|
|
|
/// The association between a table and its alias. E.g., AllDatoms, "all_datoms123".
|
|
|
|
#[derive(PartialEq, Eq, Clone)]
|
|
|
|
pub struct SourceAlias(pub DatomsTable, pub TableAlias);
|
|
|
|
|
|
|
|
impl Debug for SourceAlias {
|
2018-05-31 21:10:49 +00:00
|
|
|
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
|
2017-03-15 11:37:17 +00:00
|
|
|
write!(f, "SourceAlias({:?}, {})", self.0, self.1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A particular column of a particular aliased table. E.g., "datoms123", Attribute.
|
|
|
|
#[derive(PartialEq, Eq, Clone)]
|
2017-04-08 00:23:41 +00:00
|
|
|
pub struct QualifiedAlias(pub TableAlias, pub Column);
|
2017-03-15 11:37:17 +00:00
|
|
|
|
|
|
|
impl Debug for QualifiedAlias {
|
2018-05-31 21:10:49 +00:00
|
|
|
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
|
2017-04-08 00:23:41 +00:00
|
|
|
write!(f, "{}.{:?}", self.0, self.1)
|
2017-03-15 11:37:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl QualifiedAlias {
|
2017-04-08 00:23:41 +00:00
|
|
|
pub fn new<C: Into<Column>>(table: TableAlias, column: C) -> Self {
|
|
|
|
QualifiedAlias(table, column.into())
|
|
|
|
}
|
|
|
|
|
2018-04-18 15:38:46 +00:00
|
|
|
pub fn for_associated_type_tag(&self) -> Option<QualifiedAlias> {
|
|
|
|
match self.1 {
|
|
|
|
Column::Fixed(ref c) => c.associated_type_tag_column().map(Column::Fixed),
|
|
|
|
Column::Fulltext(_) => None,
|
|
|
|
Column::Variable(_) => None,
|
2018-04-16 21:08:00 +00:00
|
|
|
Column::Transactions(ref c) => c.associated_type_tag_column().map(Column::Transactions),
|
2020-01-14 15:46:21 +00:00
|
|
|
}
|
|
|
|
.map(|d| QualifiedAlias(self.0.clone(), d))
|
2017-03-15 11:37:17 +00:00
|
|
|
}
|
2017-03-16 19:23:48 +00:00
|
|
|
}
|
|
|
|
|
2017-04-04 21:54:08 +00:00
|
|
|
#[derive(PartialEq, Eq, Clone)]
|
2017-03-16 19:23:48 +00:00
|
|
|
pub enum QueryValue {
|
|
|
|
Column(QualifiedAlias),
|
|
|
|
Entid(Entid),
|
|
|
|
TypedValue(TypedValue),
|
|
|
|
|
|
|
|
// This is different: a numeric value can only apply to the 'v' column, and it implicitly
|
|
|
|
// constrains the `value_type_tag` column. For instance, a primitive long on `datoms00` of `5`
|
|
|
|
// cannot be a boolean, so `datoms00.value_type_tag` must be in the set `#{0, 4, 5}`.
|
|
|
|
// Note that `5 = 5.0` in SQLite, and we preserve that here.
|
|
|
|
PrimitiveLong(i64),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for QueryValue {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
|
|
|
|
use self::QueryValue::*;
|
|
|
|
match self {
|
2020-01-23 18:16:19 +00:00
|
|
|
Column(ref qa) => write!(f, "{:?}", qa),
|
|
|
|
Entid(ref entid) => write!(f, "entity({:?})", entid),
|
|
|
|
TypedValue(ref typed_value) => write!(f, "value({:?})", typed_value),
|
|
|
|
PrimitiveLong(value) => write!(f, "primitive({:?})", value),
|
2017-03-16 19:23:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Implement :order. (#415) (#416) r=nalexander
This adds an `:order` keyword to `:find`.
If present, the results of the query will be an ordered set, rather than
an unordered set; rows will appear in an ordered defined by each
`:order` entry.
Each can be one of three things:
- A var, `?x`, meaning "order by ?x ascending".
- A pair, `(asc ?x)`, meaning "order by ?x ascending".
- A pair, `(desc ?x)`, meaning "order by ?x descending".
Values will be ordered in this sequence for asc, and in reverse for desc:
1. Entity IDs, in ascending numerical order.
2. Booleans, false then true.
3. Timestamps, in ascending numerical order.
4. Longs and doubles, intermixed, in ascending numerical order.
5. Strings, in ascending lexicographic order.
6. Keywords, in ascending lexicographic order, considering the entire
ns/name pair as a single string separated by '/'.
Subcommits:
Pre: make bound_value public.
Pre: generalize ErrorKind::UnboundVariable for use in order.
Part 1: parse (direction, var) pairs.
Part 2: parse :order clause into FindQuery.
Part 3: include order variables in algebrized query.
We add order variables to :with, so we can reuse its type tag projection
logic, and so that we can phrase ordering in terms of variables rather
than datoms columns.
Part 4: produce SQL for order clauses.
2017-04-14 23:10:56 +00:00
|
|
|
/// Represents an entry in the ORDER BY list: a variable or a variable's type tag.
|
|
|
|
/// (We require order vars to be projected, so we can simply use a variable here.)
|
2018-06-01 21:17:31 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
Implement :order. (#415) (#416) r=nalexander
This adds an `:order` keyword to `:find`.
If present, the results of the query will be an ordered set, rather than
an unordered set; rows will appear in an ordered defined by each
`:order` entry.
Each can be one of three things:
- A var, `?x`, meaning "order by ?x ascending".
- A pair, `(asc ?x)`, meaning "order by ?x ascending".
- A pair, `(desc ?x)`, meaning "order by ?x descending".
Values will be ordered in this sequence for asc, and in reverse for desc:
1. Entity IDs, in ascending numerical order.
2. Booleans, false then true.
3. Timestamps, in ascending numerical order.
4. Longs and doubles, intermixed, in ascending numerical order.
5. Strings, in ascending lexicographic order.
6. Keywords, in ascending lexicographic order, considering the entire
ns/name pair as a single string separated by '/'.
Subcommits:
Pre: make bound_value public.
Pre: generalize ErrorKind::UnboundVariable for use in order.
Part 1: parse (direction, var) pairs.
Part 2: parse :order clause into FindQuery.
Part 3: include order variables in algebrized query.
We add order variables to :with, so we can reuse its type tag projection
logic, and so that we can phrase ordering in terms of variables rather
than datoms columns.
Part 4: produce SQL for order clauses.
2017-04-14 23:10:56 +00:00
|
|
|
pub struct OrderBy(pub Direction, pub VariableColumn);
|
|
|
|
|
|
|
|
impl From<Order> for OrderBy {
|
|
|
|
fn from(item: Order) -> OrderBy {
|
|
|
|
let Order(direction, variable) = item;
|
|
|
|
OrderBy(direction, VariableColumn::Variable(variable))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-16 19:23:48 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
2017-06-14 23:17:25 +00:00
|
|
|
/// Define the different inequality operators that we support.
|
2017-03-16 19:23:48 +00:00
|
|
|
/// Note that we deliberately don't just use "<=" and friends as strings:
|
|
|
|
/// Datalog and SQL don't use the same operators (e.g., `<>` and `!=`).
|
2017-06-14 23:17:25 +00:00
|
|
|
/// These are applicable to numbers and instants.
|
|
|
|
pub enum Inequality {
|
2017-03-16 19:23:48 +00:00
|
|
|
LessThan,
|
|
|
|
LessThanOrEquals,
|
|
|
|
GreaterThan,
|
|
|
|
GreaterThanOrEquals,
|
|
|
|
NotEquals,
|
2018-03-12 22:18:50 +00:00
|
|
|
|
|
|
|
// Ref operators.
|
|
|
|
Unpermute,
|
|
|
|
Differ,
|
2018-04-05 17:49:06 +00:00
|
|
|
|
|
|
|
// Tx operators.
|
|
|
|
TxAfter,
|
|
|
|
TxBefore,
|
2017-03-16 19:23:48 +00:00
|
|
|
}
|
|
|
|
|
2017-06-14 23:17:25 +00:00
|
|
|
impl Inequality {
|
2017-03-16 19:23:48 +00:00
|
|
|
pub fn to_sql_operator(self) -> &'static str {
|
2017-06-14 23:17:25 +00:00
|
|
|
use self::Inequality::*;
|
2017-03-16 19:23:48 +00:00
|
|
|
match self {
|
2020-01-14 15:46:21 +00:00
|
|
|
LessThan => "<",
|
|
|
|
LessThanOrEquals => "<=",
|
|
|
|
GreaterThan => ">",
|
2017-03-16 19:23:48 +00:00
|
|
|
GreaterThanOrEquals => ">=",
|
2020-01-14 15:46:21 +00:00
|
|
|
NotEquals => "<>",
|
2018-03-12 22:18:50 +00:00
|
|
|
|
2020-01-14 15:46:21 +00:00
|
|
|
Unpermute => "<",
|
|
|
|
Differ => "<>",
|
2018-04-05 17:49:06 +00:00
|
|
|
|
2020-01-14 15:46:21 +00:00
|
|
|
TxAfter => ">",
|
|
|
|
TxBefore => "<",
|
2017-03-16 19:23:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-14 23:17:25 +00:00
|
|
|
pub fn from_datalog_operator(s: &str) -> Option<Inequality> {
|
2017-03-16 19:23:48 +00:00
|
|
|
match s {
|
2020-01-14 15:46:21 +00:00
|
|
|
"<" => Some(Inequality::LessThan),
|
2017-06-14 23:17:25 +00:00
|
|
|
"<=" => Some(Inequality::LessThanOrEquals),
|
2020-01-14 15:46:21 +00:00
|
|
|
">" => Some(Inequality::GreaterThan),
|
2017-06-14 23:17:25 +00:00
|
|
|
">=" => Some(Inequality::GreaterThanOrEquals),
|
|
|
|
"!=" => Some(Inequality::NotEquals),
|
2018-03-12 22:18:50 +00:00
|
|
|
|
|
|
|
"unpermute" => Some(Inequality::Unpermute),
|
|
|
|
"differ" => Some(Inequality::Differ),
|
2018-04-05 17:49:06 +00:00
|
|
|
|
|
|
|
"tx-after" => Some(Inequality::TxAfter),
|
|
|
|
"tx-before" => Some(Inequality::TxBefore),
|
2018-03-12 22:18:50 +00:00
|
|
|
_ => None,
|
2017-03-16 19:23:48 +00:00
|
|
|
}
|
|
|
|
}
|
2017-06-14 23:17:25 +00:00
|
|
|
|
|
|
|
// The built-in inequality operators apply to Long, Double, and Instant.
|
2020-01-23 18:16:19 +00:00
|
|
|
pub fn supported_types(self) -> ValueTypeSet {
|
2018-03-12 22:18:50 +00:00
|
|
|
use self::Inequality::*;
|
|
|
|
match self {
|
2020-01-23 18:16:19 +00:00
|
|
|
LessThan | LessThanOrEquals | GreaterThan | GreaterThanOrEquals | NotEquals => {
|
2018-03-12 22:18:50 +00:00
|
|
|
let mut ts = ValueTypeSet::of_numeric_types();
|
|
|
|
ts.insert(ValueType::Instant);
|
|
|
|
ts
|
2020-01-14 15:46:21 +00:00
|
|
|
}
|
2020-01-23 18:16:19 +00:00
|
|
|
Unpermute | Differ | TxAfter | TxBefore => ValueTypeSet::of_one(ValueType::Ref),
|
2018-03-12 22:18:50 +00:00
|
|
|
}
|
2017-06-14 23:17:25 +00:00
|
|
|
}
|
2017-03-16 19:23:48 +00:00
|
|
|
}
|
|
|
|
|
2017-06-14 23:17:25 +00:00
|
|
|
impl Debug for Inequality {
|
2017-03-16 19:23:48 +00:00
|
|
|
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
|
2017-06-14 23:17:25 +00:00
|
|
|
use self::Inequality::*;
|
2017-03-16 19:23:48 +00:00
|
|
|
f.write_str(match self {
|
2020-01-23 18:16:19 +00:00
|
|
|
LessThan => "<",
|
|
|
|
LessThanOrEquals => "<=",
|
|
|
|
GreaterThan => ">",
|
|
|
|
GreaterThanOrEquals => ">=",
|
|
|
|
NotEquals => "!=", // Datalog uses !=. SQL uses <>.
|
2018-03-12 22:18:50 +00:00
|
|
|
|
2020-01-23 18:16:19 +00:00
|
|
|
Unpermute => "<",
|
|
|
|
Differ => "<>",
|
2018-04-05 17:49:06 +00:00
|
|
|
|
2020-01-23 18:16:19 +00:00
|
|
|
TxAfter => ">",
|
|
|
|
TxBefore => "<",
|
2017-03-16 19:23:48 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq)]
|
|
|
|
pub enum ColumnConstraint {
|
|
|
|
Equals(QualifiedAlias, QueryValue),
|
2017-06-14 23:17:25 +00:00
|
|
|
Inequality {
|
|
|
|
operator: Inequality,
|
2017-03-16 19:23:48 +00:00
|
|
|
left: QueryValue,
|
|
|
|
right: QueryValue,
|
|
|
|
},
|
2018-01-29 22:29:16 +00:00
|
|
|
HasTypes {
|
|
|
|
value: TableAlias,
|
|
|
|
value_types: ValueTypeSet,
|
|
|
|
check_value: bool,
|
|
|
|
},
|
2017-04-28 09:44:11 +00:00
|
|
|
NotExists(ComputedTable),
|
2017-06-12 21:24:56 +00:00
|
|
|
Matches(QualifiedAlias, QueryValue),
|
2017-03-16 19:23:48 +00:00
|
|
|
}
|
|
|
|
|
2018-01-29 22:29:16 +00:00
|
|
|
impl ColumnConstraint {
|
|
|
|
pub fn has_unit_type(value: TableAlias, value_type: ValueType) -> ColumnConstraint {
|
|
|
|
ColumnConstraint::HasTypes {
|
|
|
|
value,
|
|
|
|
value_types: ValueTypeSet::of_one(value_type),
|
|
|
|
check_value: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-28 02:35:39 +00:00
|
|
|
#[derive(PartialEq, Eq, Debug)]
|
|
|
|
pub enum ColumnConstraintOrAlternation {
|
|
|
|
Constraint(ColumnConstraint),
|
|
|
|
Alternation(ColumnAlternation),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ColumnConstraint> for ColumnConstraintOrAlternation {
|
|
|
|
fn from(thing: ColumnConstraint) -> Self {
|
|
|
|
ColumnConstraintOrAlternation::Constraint(thing)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A `ColumnIntersection` constraint is satisfied if all of its inner constraints are satisfied.
|
|
|
|
/// An empty intersection is always satisfied.
|
|
|
|
#[derive(PartialEq, Eq)]
|
|
|
|
pub struct ColumnIntersection(pub Vec<ColumnConstraintOrAlternation>);
|
|
|
|
|
|
|
|
impl From<Vec<ColumnConstraint>> for ColumnIntersection {
|
|
|
|
fn from(thing: Vec<ColumnConstraint>) -> Self {
|
|
|
|
ColumnIntersection(thing.into_iter().map(|x| x.into()).collect())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ColumnIntersection {
|
|
|
|
fn default() -> Self {
|
|
|
|
ColumnIntersection(vec![])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IntoIterator for ColumnIntersection {
|
|
|
|
type Item = ColumnConstraintOrAlternation;
|
|
|
|
type IntoIter = ::std::vec::IntoIter<ColumnConstraintOrAlternation>;
|
|
|
|
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
|
|
self.0.into_iter()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ColumnIntersection {
|
2017-04-04 21:54:08 +00:00
|
|
|
#[inline]
|
2017-03-28 02:35:39 +00:00
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.0.len()
|
|
|
|
}
|
|
|
|
|
2017-04-04 21:54:08 +00:00
|
|
|
#[inline]
|
2017-03-28 02:35:39 +00:00
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.0.is_empty()
|
|
|
|
}
|
|
|
|
|
2017-04-04 21:54:08 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn add(&mut self, constraint: ColumnConstraintOrAlternation) {
|
|
|
|
self.0.push(constraint);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2017-03-31 01:16:04 +00:00
|
|
|
pub fn add_intersection(&mut self, constraint: ColumnConstraint) {
|
2017-04-04 21:54:08 +00:00
|
|
|
self.add(ColumnConstraintOrAlternation::Constraint(constraint));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn append(&mut self, other: &mut Self) {
|
|
|
|
self.0.append(&mut other.0)
|
2017-03-28 02:35:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A `ColumnAlternation` constraint is satisfied if at least one of its inner constraints is
|
|
|
|
/// satisfied. An empty `ColumnAlternation` is never satisfied.
|
|
|
|
#[derive(PartialEq, Eq, Debug)]
|
|
|
|
pub struct ColumnAlternation(pub Vec<ColumnIntersection>);
|
|
|
|
|
|
|
|
impl Default for ColumnAlternation {
|
|
|
|
fn default() -> Self {
|
|
|
|
ColumnAlternation(vec![])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IntoIterator for ColumnAlternation {
|
|
|
|
type Item = ColumnIntersection;
|
|
|
|
type IntoIter = ::std::vec::IntoIter<ColumnIntersection>;
|
|
|
|
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
|
|
self.0.into_iter()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ColumnAlternation {
|
2017-03-31 01:16:04 +00:00
|
|
|
pub fn add_alternate(&mut self, intersection: ColumnIntersection) {
|
2017-03-28 02:35:39 +00:00
|
|
|
self.0.push(intersection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for ColumnIntersection {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
|
|
|
|
write!(f, "{:?}", self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-16 19:23:48 +00:00
|
|
|
impl Debug for ColumnConstraint {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
|
|
|
|
use self::ColumnConstraint::*;
|
|
|
|
match self {
|
2020-01-23 18:16:19 +00:00
|
|
|
Equals(ref qa1, ref thing) => write!(f, "{:?} = {:?}", qa1, thing),
|
2017-03-16 19:23:48 +00:00
|
|
|
|
2020-01-23 18:16:19 +00:00
|
|
|
Inequality {
|
2020-01-14 15:46:21 +00:00
|
|
|
operator,
|
|
|
|
ref left,
|
|
|
|
ref right,
|
|
|
|
} => write!(f, "{:?} {:?} {:?}", left, operator, right),
|
2017-03-16 19:23:48 +00:00
|
|
|
|
2020-01-23 18:16:19 +00:00
|
|
|
Matches(ref qa, ref thing) => write!(f, "{:?} MATCHES {:?}", qa, thing),
|
2017-06-12 21:24:56 +00:00
|
|
|
|
2020-01-23 18:16:19 +00:00
|
|
|
HasTypes {
|
2020-01-14 15:46:21 +00:00
|
|
|
ref value,
|
|
|
|
ref value_types,
|
|
|
|
check_value,
|
|
|
|
} => {
|
2018-01-29 22:29:16 +00:00
|
|
|
// This is cludgey, but it's debug code.
|
|
|
|
write!(f, "(")?;
|
|
|
|
for value_type in value_types.iter() {
|
|
|
|
write!(f, "({:?}.value_type_tag = {:?}", value, value_type)?;
|
2020-01-23 18:16:19 +00:00
|
|
|
if *check_value && value_type == ValueType::Double
|
2020-01-14 15:46:21 +00:00
|
|
|
|| value_type == ValueType::Long
|
|
|
|
{
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
" AND typeof({:?}) = '{:?}')",
|
|
|
|
value,
|
|
|
|
if value_type == ValueType::Double {
|
|
|
|
"real"
|
|
|
|
} else {
|
|
|
|
"integer"
|
|
|
|
}
|
|
|
|
)?;
|
2018-01-29 22:29:16 +00:00
|
|
|
} else {
|
|
|
|
write!(f, ")")?;
|
|
|
|
}
|
|
|
|
write!(f, " OR ")?;
|
|
|
|
}
|
|
|
|
write!(f, "1)")
|
2020-01-14 15:46:21 +00:00
|
|
|
}
|
2020-01-23 18:16:19 +00:00
|
|
|
NotExists(ref ct) => write!(f, "NOT EXISTS {:?}", ct),
|
2017-03-16 19:23:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-03-28 02:34:02 +00:00
|
|
|
|
2017-03-28 03:36:03 +00:00
|
|
|
#[derive(PartialEq, Clone)]
|
2017-03-28 02:34:02 +00:00
|
|
|
pub enum EmptyBecause {
|
2020-01-14 15:46:21 +00:00
|
|
|
CachedAttributeHasNoValues {
|
|
|
|
entity: Entid,
|
|
|
|
attr: Entid,
|
|
|
|
},
|
|
|
|
CachedAttributeHasNoEntity {
|
|
|
|
value: TypedValue,
|
|
|
|
attr: Entid,
|
|
|
|
},
|
|
|
|
ConflictingBindings {
|
|
|
|
var: Variable,
|
|
|
|
existing: TypedValue,
|
|
|
|
desired: TypedValue,
|
|
|
|
},
|
2017-06-14 23:17:25 +00:00
|
|
|
|
|
|
|
// A variable is known to be of two conflicting sets of types.
|
2020-01-14 15:46:21 +00:00
|
|
|
TypeMismatch {
|
|
|
|
var: Variable,
|
|
|
|
existing: ValueTypeSet,
|
|
|
|
desired: ValueTypeSet,
|
|
|
|
},
|
2017-06-14 23:17:25 +00:00
|
|
|
|
|
|
|
// The same, but for non-variables.
|
2020-01-14 15:46:21 +00:00
|
|
|
KnownTypeMismatch {
|
|
|
|
left: ValueTypeSet,
|
|
|
|
right: ValueTypeSet,
|
|
|
|
},
|
2017-04-04 21:54:08 +00:00
|
|
|
NoValidTypes(Variable),
|
2017-06-12 21:24:56 +00:00
|
|
|
NonAttributeArgument,
|
2017-06-14 23:17:25 +00:00
|
|
|
NonInstantArgument,
|
2017-03-28 02:34:02 +00:00
|
|
|
NonNumericArgument,
|
2018-03-12 22:18:50 +00:00
|
|
|
NonEntityArgument,
|
2017-03-28 03:36:03 +00:00
|
|
|
NonStringFulltextValue,
|
2018-02-14 00:51:21 +00:00
|
|
|
NonFulltextAttribute(Entid),
|
2018-05-11 16:52:17 +00:00
|
|
|
UnresolvedIdent(Keyword),
|
|
|
|
InvalidAttributeIdent(Keyword),
|
2017-03-28 02:34:02 +00:00
|
|
|
InvalidAttributeEntid(Entid),
|
2017-04-08 00:23:41 +00:00
|
|
|
InvalidBinding(Column, TypedValue),
|
2017-03-28 02:34:02 +00:00
|
|
|
ValueTypeMismatch(ValueType, TypedValue),
|
2020-01-14 15:46:21 +00:00
|
|
|
AttributeLookupFailed, // Catch-all, because the table lookup code is lazy. TODO
|
2017-03-28 03:34:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for EmptyBecause {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
|
|
|
|
use self::EmptyBecause::*;
|
|
|
|
match self {
|
2020-01-23 18:16:19 +00:00
|
|
|
CachedAttributeHasNoEntity {
|
2020-01-14 15:46:21 +00:00
|
|
|
ref value,
|
|
|
|
ref attr,
|
|
|
|
} => write!(f, "(?e, {}, {:?}, _) not present in store", attr, value),
|
2020-01-23 18:16:19 +00:00
|
|
|
CachedAttributeHasNoValues {
|
2020-01-14 15:46:21 +00:00
|
|
|
ref entity,
|
|
|
|
ref attr,
|
|
|
|
} => write!(f, "({}, {}, ?v, _) not present in store", entity, attr),
|
2020-01-23 18:16:19 +00:00
|
|
|
ConflictingBindings {
|
2020-01-14 15:46:21 +00:00
|
|
|
ref var,
|
|
|
|
ref existing,
|
|
|
|
ref desired,
|
|
|
|
} => write!(
|
|
|
|
f,
|
|
|
|
"Var {:?} can't be {:?} because it's already bound to {:?}",
|
|
|
|
var, desired, existing
|
|
|
|
),
|
2020-01-23 18:16:19 +00:00
|
|
|
TypeMismatch {
|
2020-01-14 15:46:21 +00:00
|
|
|
ref var,
|
|
|
|
ref existing,
|
|
|
|
ref desired,
|
|
|
|
} => write!(
|
|
|
|
f,
|
|
|
|
"Type mismatch: {:?} can't be {:?}, because it's already {:?}",
|
|
|
|
var, desired, existing
|
|
|
|
),
|
2020-01-23 18:16:19 +00:00
|
|
|
KnownTypeMismatch {
|
2020-01-14 15:46:21 +00:00
|
|
|
ref left,
|
|
|
|
ref right,
|
|
|
|
} => write!(
|
|
|
|
f,
|
|
|
|
"Type mismatch: {:?} can't be compared to {:?}",
|
|
|
|
left, right
|
|
|
|
),
|
2020-01-23 18:16:19 +00:00
|
|
|
NoValidTypes(ref var) => write!(f, "Type mismatch: {:?} has no valid types", var),
|
|
|
|
NonAttributeArgument => write!(f, "Non-attribute argument in attribute place"),
|
|
|
|
NonInstantArgument => write!(f, "Non-instant argument in instant place"),
|
|
|
|
NonEntityArgument => write!(f, "Non-entity argument in entity place"),
|
|
|
|
NonNumericArgument => write!(f, "Non-numeric argument in numeric place"),
|
|
|
|
NonStringFulltextValue => write!(f, "Non-string argument for fulltext attribute"),
|
|
|
|
UnresolvedIdent(ref kw) => write!(f, "Couldn't resolve keyword {}", kw),
|
|
|
|
InvalidAttributeIdent(ref kw) => write!(f, "{} does not name an attribute", kw),
|
|
|
|
InvalidAttributeEntid(entid) => write!(f, "{} is not an attribute", entid),
|
|
|
|
NonFulltextAttribute(entid) => write!(f, "{} is not a fulltext attribute", entid),
|
|
|
|
InvalidBinding(ref column, ref tv) => {
|
2017-03-28 03:34:56 +00:00
|
|
|
write!(f, "{:?} cannot name column {:?}", tv, column)
|
2020-01-14 15:46:21 +00:00
|
|
|
}
|
2020-01-23 18:16:19 +00:00
|
|
|
ValueTypeMismatch(value_type, ref typed_value) => write!(
|
2020-01-14 15:46:21 +00:00
|
|
|
f,
|
|
|
|
"Type mismatch: {:?} doesn't match attribute type {:?}",
|
|
|
|
typed_value, value_type
|
|
|
|
),
|
2020-01-23 18:16:19 +00:00
|
|
|
AttributeLookupFailed => write!(f, "Attribute lookup failed"),
|
2017-03-28 03:34:56 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-24 21:15:26 +00:00
|
|
|
}
|
2018-02-14 00:51:21 +00:00
|
|
|
|
2018-05-31 21:10:49 +00:00
|
|
|
/// A `FindQuery` represents a valid query to the query algebrizer.
|
2018-06-04 22:21:27 +00:00
|
|
|
///
|
|
|
|
/// We split `FindQuery` from `ParsedQuery` because it's not easy to generalize over containers
|
|
|
|
/// (here, `Vec` and `BTreeSet`) in Rust.
|
2018-05-31 21:10:49 +00:00
|
|
|
#[allow(dead_code)]
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
pub struct FindQuery {
|
|
|
|
pub find_spec: FindSpec,
|
|
|
|
pub default_source: SrcVar,
|
|
|
|
pub with: BTreeSet<Variable>,
|
|
|
|
pub in_vars: BTreeSet<Variable>,
|
|
|
|
pub in_sources: BTreeSet<SrcVar>,
|
|
|
|
pub limit: Limit,
|
|
|
|
pub where_clauses: Vec<WhereClause>,
|
|
|
|
pub order: Option<Vec<Order>>,
|
|
|
|
}
|
|
|
|
|
2018-02-14 00:51:21 +00:00
|
|
|
// Intermediate data structures for resolving patterns.
|
|
|
|
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
pub enum EvolvedNonValuePlace {
|
|
|
|
Placeholder,
|
|
|
|
Variable(Variable),
|
2020-01-14 15:46:21 +00:00
|
|
|
Entid(Entid), // Will always be +ve. See #190.
|
2018-02-14 00:51:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: some of these aren't necessary?
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
pub enum EvolvedValuePlace {
|
|
|
|
Placeholder,
|
|
|
|
Variable(Variable),
|
|
|
|
Entid(Entid),
|
|
|
|
Value(TypedValue),
|
|
|
|
EntidOrInteger(i64),
|
2018-05-11 16:52:17 +00:00
|
|
|
IdentOrKeyword(ValueRc<Keyword>),
|
2018-02-14 00:51:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum PlaceOrEmpty<T> {
|
|
|
|
Place(T),
|
|
|
|
Empty(EmptyBecause),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> PlaceOrEmpty<T> {
|
|
|
|
pub fn and_then<U, F: FnOnce(T) -> PlaceOrEmpty<U>>(self, f: F) -> PlaceOrEmpty<U> {
|
|
|
|
match self {
|
|
|
|
PlaceOrEmpty::Place(x) => f(x),
|
|
|
|
PlaceOrEmpty::Empty(e) => PlaceOrEmpty::Empty(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct EvolvedPattern {
|
|
|
|
pub source: SrcVar,
|
|
|
|
pub entity: EvolvedNonValuePlace,
|
|
|
|
pub attribute: EvolvedNonValuePlace,
|
|
|
|
pub value: EvolvedValuePlace,
|
|
|
|
pub tx: EvolvedNonValuePlace,
|
|
|
|
}
|