Info for db and table with versioned tables and fields (#4692)
This commit is contained in:
parent
29af8f573d
commit
516bb977ac
24 changed files with 219 additions and 93 deletions
|
@ -34,7 +34,7 @@ pub async fn gc_ns(tx: &Transaction, ts: u64, ns: &str) -> Result<(), Error> {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
trace!("Performing garbage collection on {ns}:{db} for timestamp {ts}");
|
trace!("Performing garbage collection on {ns}:{db} for timestamp {ts}");
|
||||||
// Fetch all tables
|
// Fetch all tables
|
||||||
let tbs = tx.all_tb(ns, &db.name).await?;
|
let tbs = tx.all_tb(ns, &db.name, None).await?;
|
||||||
// Get the database changefeed expiration
|
// Get the database changefeed expiration
|
||||||
let db_cf_expiry = db.changefeed.map(|v| v.expiry.as_secs()).unwrap_or_default();
|
let db_cf_expiry = db.changefeed.map(|v| v.expiry.as_secs()).unwrap_or_default();
|
||||||
// Get the maximum table changefeed expiration
|
// Get the maximum table changefeed expiration
|
||||||
|
|
|
@ -254,7 +254,7 @@ impl Document {
|
||||||
// Get the record id
|
// Get the record id
|
||||||
let id = self.id.as_ref().unwrap();
|
let id = self.id.as_ref().unwrap();
|
||||||
// Get the field definitions
|
// Get the field definitions
|
||||||
ctx.tx().all_tb_fields(opt.ns()?, opt.db()?, &id.tb).await
|
ctx.tx().all_tb_fields(opt.ns()?, opt.db()?, &id.tb, None).await
|
||||||
}
|
}
|
||||||
/// Get the indexes for this document
|
/// Get the indexes for this document
|
||||||
pub async fn ix(
|
pub async fn ix(
|
||||||
|
|
|
@ -80,7 +80,7 @@ pub async fn generate_schema(
|
||||||
let tx = kvs.transaction(TransactionType::Read, LockType::Optimistic).await?;
|
let tx = kvs.transaction(TransactionType::Read, LockType::Optimistic).await?;
|
||||||
let ns = session.ns.as_ref().ok_or(GqlError::UnpecifiedNamespace)?;
|
let ns = session.ns.as_ref().ok_or(GqlError::UnpecifiedNamespace)?;
|
||||||
let db = session.db.as_ref().ok_or(GqlError::UnpecifiedDatabase)?;
|
let db = session.db.as_ref().ok_or(GqlError::UnpecifiedDatabase)?;
|
||||||
let tbs = tx.all_tb(ns, db).await?;
|
let tbs = tx.all_tb(ns, db, None).await?;
|
||||||
let mut query = Object::new("Query");
|
let mut query = Object::new("Query");
|
||||||
let mut types: Vec<Type> = Vec::new();
|
let mut types: Vec<Type> = Vec::new();
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ pub async fn generate_schema(
|
||||||
types.push(Type::InputObject(filter_id()));
|
types.push(Type::InputObject(filter_id()));
|
||||||
|
|
||||||
let sess1 = session.to_owned();
|
let sess1 = session.to_owned();
|
||||||
let fds = tx.all_tb_fields(ns, db, &tb.name.0).await?;
|
let fds = tx.all_tb_fields(ns, db, &tb.name.0, None).await?;
|
||||||
let fds1 = fds.clone();
|
let fds1 = fds.clone();
|
||||||
let kvs1 = datastore.clone();
|
let kvs1 = datastore.clone();
|
||||||
|
|
||||||
|
|
|
@ -594,7 +594,7 @@ struct SchemaCache {
|
||||||
impl SchemaCache {
|
impl SchemaCache {
|
||||||
async fn new(opt: &Options, table: &Table, tx: &Transaction) -> Result<Self, Error> {
|
async fn new(opt: &Options, table: &Table, tx: &Transaction) -> Result<Self, Error> {
|
||||||
let indexes = tx.all_tb_indexes(opt.ns()?, opt.db()?, table).await?;
|
let indexes = tx.all_tb_indexes(opt.ns()?, opt.db()?, table).await?;
|
||||||
let fields = tx.all_tb_fields(opt.ns()?, opt.db()?, table).await?;
|
let fields = tx.all_tb_fields(opt.ns()?, opt.db()?, table, None).await?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
indexes,
|
indexes,
|
||||||
fields,
|
fields,
|
||||||
|
|
|
@ -311,7 +311,7 @@ impl IndexStores {
|
||||||
ns: &str,
|
ns: &str,
|
||||||
db: &str,
|
db: &str,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
for tb in tx.all_tb(ns, db).await?.iter() {
|
for tb in tx.all_tb(ns, db, None).await?.iter() {
|
||||||
self.table_removed(tx, ns, db, &tb.name).await?;
|
self.table_removed(tx, ns, db, &tb.name).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -146,14 +146,18 @@ pub trait Transaction {
|
||||||
// Continue with function logic
|
// Continue with function logic
|
||||||
let beg: Key = key.into();
|
let beg: Key = key.into();
|
||||||
let end: Key = beg.clone().add(0xff);
|
let end: Key = beg.clone().add(0xff);
|
||||||
self.getr(beg..end).await
|
self.getr(beg..end, None).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve a range of keys from the datastore.
|
/// Retrieve a range of keys from the datastore.
|
||||||
///
|
///
|
||||||
/// This function fetches all matching key-value pairs from the underlying datastore in grouped batches.
|
/// This function fetches all matching key-value pairs from the underlying datastore in grouped batches.
|
||||||
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(rng = rng.sprint()))]
|
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(rng = rng.sprint()))]
|
||||||
async fn getr<K>(&mut self, rng: Range<K>) -> Result<Vec<(Key, Val)>, Error>
|
async fn getr<K>(
|
||||||
|
&mut self,
|
||||||
|
rng: Range<K>,
|
||||||
|
version: Option<u64>,
|
||||||
|
) -> Result<Vec<(Key, Val)>, Error>
|
||||||
where
|
where
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
{
|
{
|
||||||
|
@ -167,7 +171,7 @@ pub trait Transaction {
|
||||||
let end: Key = rng.end.into();
|
let end: Key = rng.end.into();
|
||||||
let mut next = Some(beg..end);
|
let mut next = Some(beg..end);
|
||||||
while let Some(rng) = next {
|
while let Some(rng) = next {
|
||||||
let res = self.batch(rng, *NORMAL_FETCH_SIZE, true).await?;
|
let res = self.batch(rng, *NORMAL_FETCH_SIZE, true, version).await?;
|
||||||
next = res.next;
|
next = res.next;
|
||||||
for v in res.values.into_iter() {
|
for v in res.values.into_iter() {
|
||||||
out.push(v);
|
out.push(v);
|
||||||
|
@ -219,7 +223,7 @@ pub trait Transaction {
|
||||||
let end: Key = rng.end.into();
|
let end: Key = rng.end.into();
|
||||||
let mut next = Some(beg..end);
|
let mut next = Some(beg..end);
|
||||||
while let Some(rng) = next {
|
while let Some(rng) = next {
|
||||||
let res = self.batch(rng, *NORMAL_FETCH_SIZE, false).await?;
|
let res = self.batch(rng, *NORMAL_FETCH_SIZE, false, None).await?;
|
||||||
next = res.next;
|
next = res.next;
|
||||||
for (k, _) in res.values.into_iter() {
|
for (k, _) in res.values.into_iter() {
|
||||||
self.del(k).await?;
|
self.del(k).await?;
|
||||||
|
@ -232,7 +236,13 @@ pub trait Transaction {
|
||||||
///
|
///
|
||||||
/// This function fetches keys or key-value pairs, in batches, with multiple requests to the underlying datastore.
|
/// This function fetches keys or key-value pairs, in batches, with multiple requests to the underlying datastore.
|
||||||
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(rng = rng.sprint()))]
|
#[instrument(level = "trace", target = "surrealdb::core::kvs::api", skip(self), fields(rng = rng.sprint()))]
|
||||||
async fn batch<K>(&mut self, rng: Range<K>, batch: u32, values: bool) -> Result<Batch, Error>
|
async fn batch<K>(
|
||||||
|
&mut self,
|
||||||
|
rng: Range<K>,
|
||||||
|
batch: u32,
|
||||||
|
values: bool,
|
||||||
|
version: Option<u64>,
|
||||||
|
) -> Result<Batch, Error>
|
||||||
where
|
where
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
{
|
{
|
||||||
|
@ -245,7 +255,7 @@ pub trait Transaction {
|
||||||
let end: Key = rng.end.into();
|
let end: Key = rng.end.into();
|
||||||
// Scan for the next batch
|
// Scan for the next batch
|
||||||
let res = if values {
|
let res = if values {
|
||||||
self.scan(beg..end.clone(), batch, None).await?
|
self.scan(beg..end.clone(), batch, version).await?
|
||||||
} else {
|
} else {
|
||||||
self.keys(beg..end.clone(), batch)
|
self.keys(beg..end.clone(), batch)
|
||||||
.await?
|
.await?
|
||||||
|
|
|
@ -91,7 +91,7 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
// Output TABLES
|
// Output TABLES
|
||||||
{
|
{
|
||||||
let tbs = self.all_tb(ns, db).await?;
|
let tbs = self.all_tb(ns, db, None).await?;
|
||||||
if !tbs.is_empty() {
|
if !tbs.is_empty() {
|
||||||
for tb in tbs.iter() {
|
for tb in tbs.iter() {
|
||||||
// Output TABLE
|
// Output TABLE
|
||||||
|
@ -102,7 +102,7 @@ impl Transaction {
|
||||||
chn.send(bytes!(format!("{tb};"))).await?;
|
chn.send(bytes!(format!("{tb};"))).await?;
|
||||||
chn.send(bytes!("")).await?;
|
chn.send(bytes!("")).await?;
|
||||||
// Output FIELDS
|
// Output FIELDS
|
||||||
let fds = self.all_tb_fields(ns, db, &tb.name).await?;
|
let fds = self.all_tb_fields(ns, db, &tb.name, None).await?;
|
||||||
if !fds.is_empty() {
|
if !fds.is_empty() {
|
||||||
for fd in fds.iter() {
|
for fd in fds.iter() {
|
||||||
chn.send(bytes!(format!("{fd};"))).await?;
|
chn.send(bytes!(format!("{fd};"))).await?;
|
||||||
|
@ -144,7 +144,7 @@ impl Transaction {
|
||||||
let mut next = Some(beg..end);
|
let mut next = Some(beg..end);
|
||||||
while let Some(rng) = next {
|
while let Some(rng) = next {
|
||||||
// Get the next batch of records
|
// Get the next batch of records
|
||||||
let batch = self.batch(rng, *EXPORT_BATCH_SIZE, true).await?;
|
let batch = self.batch(rng, *EXPORT_BATCH_SIZE, true, None).await?;
|
||||||
// Set the next scan range
|
// Set the next scan range
|
||||||
next = batch.next;
|
next = batch.next;
|
||||||
// Check there are records
|
// Check there are records
|
||||||
|
|
|
@ -308,7 +308,7 @@ impl super::api::Transaction for Transaction {
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
V: Into<Val> + Debug,
|
V: Into<Val> + Debug,
|
||||||
{
|
{
|
||||||
// FDB does not support verisoned queries.
|
// FDB does not support versioned queries.
|
||||||
if version.is_some() {
|
if version.is_some() {
|
||||||
return Err(Error::UnsupportedVersionedQueries);
|
return Err(Error::UnsupportedVersionedQueries);
|
||||||
}
|
}
|
||||||
|
@ -527,7 +527,7 @@ impl super::api::Transaction for Transaction {
|
||||||
where
|
where
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
{
|
{
|
||||||
// FDB does not support verisoned queries.
|
// FDB does not support versioned queries.
|
||||||
if version.is_some() {
|
if version.is_some() {
|
||||||
return Err(Error::UnsupportedVersionedQueries);
|
return Err(Error::UnsupportedVersionedQueries);
|
||||||
}
|
}
|
||||||
|
|
|
@ -303,7 +303,7 @@ impl Building {
|
||||||
while let Some(rng) = next {
|
while let Some(rng) = next {
|
||||||
// Get the next batch of records
|
// Get the next batch of records
|
||||||
let tx = self.new_read_tx().await?;
|
let tx = self.new_read_tx().await?;
|
||||||
let batch = catch!(tx, tx.batch(rng, *INDEXING_BATCH_SIZE, true).await);
|
let batch = catch!(tx, tx.batch(rng, *INDEXING_BATCH_SIZE, true, None).await);
|
||||||
// We can release the read transaction
|
// We can release the read transaction
|
||||||
drop(tx);
|
drop(tx);
|
||||||
// Set the next scan range
|
// Set the next scan range
|
||||||
|
|
|
@ -161,7 +161,7 @@ impl super::api::Transaction for Transaction {
|
||||||
where
|
where
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
{
|
{
|
||||||
// IndexDB does not support verisoned queries.
|
// IndexDB does not support versioned queries.
|
||||||
if version.is_some() {
|
if version.is_some() {
|
||||||
return Err(Error::UnsupportedVersionedQueries);
|
return Err(Error::UnsupportedVersionedQueries);
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ impl super::api::Transaction for Transaction {
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
V: Into<Val> + Debug,
|
V: Into<Val> + Debug,
|
||||||
{
|
{
|
||||||
// IndexDB does not support verisoned queries.
|
// IndexDB does not support versioned queries.
|
||||||
if version.is_some() {
|
if version.is_some() {
|
||||||
return Err(Error::UnsupportedVersionedQueries);
|
return Err(Error::UnsupportedVersionedQueries);
|
||||||
}
|
}
|
||||||
|
@ -209,7 +209,7 @@ impl super::api::Transaction for Transaction {
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
V: Into<Val> + Debug,
|
V: Into<Val> + Debug,
|
||||||
{
|
{
|
||||||
// IndexDB does not support verisoned queries.
|
// IndexDB does not support versioned queries.
|
||||||
if version.is_some() {
|
if version.is_some() {
|
||||||
return Err(Error::UnsupportedVersionedQueries);
|
return Err(Error::UnsupportedVersionedQueries);
|
||||||
}
|
}
|
||||||
|
@ -322,7 +322,7 @@ impl super::api::Transaction for Transaction {
|
||||||
where
|
where
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
{
|
{
|
||||||
// IndexDB does not support verisoned queries.
|
// IndexDB does not support versioned queries.
|
||||||
if version.is_some() {
|
if version.is_some() {
|
||||||
return Err(Error::UnsupportedVersionedQueries);
|
return Err(Error::UnsupportedVersionedQueries);
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,7 +158,7 @@ impl super::api::Transaction for Transaction {
|
||||||
where
|
where
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
{
|
{
|
||||||
// MemDB does not support verisoned queries.
|
// MemDB does not support versioned queries.
|
||||||
if version.is_some() {
|
if version.is_some() {
|
||||||
return Err(Error::UnsupportedVersionedQueries);
|
return Err(Error::UnsupportedVersionedQueries);
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ impl super::api::Transaction for Transaction {
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
V: Into<Val> + Debug,
|
V: Into<Val> + Debug,
|
||||||
{
|
{
|
||||||
// MemDB does not support verisoned queries.
|
// MemDB does not support versioned queries.
|
||||||
if version.is_some() {
|
if version.is_some() {
|
||||||
return Err(Error::UnsupportedVersionedQueries);
|
return Err(Error::UnsupportedVersionedQueries);
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@ impl Datastore {
|
||||||
let end = crate::key::node::lq::suffix(*id);
|
let end = crate::key::node::lq::suffix(*id);
|
||||||
let mut next = Some(beg..end);
|
let mut next = Some(beg..end);
|
||||||
while let Some(rng) = next {
|
while let Some(rng) = next {
|
||||||
let res = catch!(txn, txn.batch(rng, *NORMAL_FETCH_SIZE, true).await);
|
let res = catch!(txn, txn.batch(rng, *NORMAL_FETCH_SIZE, true, None).await);
|
||||||
next = res.next;
|
next = res.next;
|
||||||
for (k, v) in res.values.iter() {
|
for (k, v) in res.values.iter() {
|
||||||
// Decode the data for this live query
|
// Decode the data for this live query
|
||||||
|
@ -220,7 +220,7 @@ impl Datastore {
|
||||||
// Fetch all tables
|
// Fetch all tables
|
||||||
let tbs = {
|
let tbs = {
|
||||||
let txn = self.transaction(Read, Optimistic).await?;
|
let txn = self.transaction(Read, Optimistic).await?;
|
||||||
catch!(txn, txn.all_tb(&ns.name, &db.name).await)
|
catch!(txn, txn.all_tb(&ns.name, &db.name, None).await)
|
||||||
};
|
};
|
||||||
// Loop over all tables
|
// Loop over all tables
|
||||||
for tb in tbs.iter() {
|
for tb in tbs.iter() {
|
||||||
|
@ -232,7 +232,7 @@ impl Datastore {
|
||||||
let end = crate::key::table::lq::suffix(&ns.name, &db.name, &tb.name);
|
let end = crate::key::table::lq::suffix(&ns.name, &db.name, &tb.name);
|
||||||
let mut next = Some(beg..end);
|
let mut next = Some(beg..end);
|
||||||
while let Some(rng) = next {
|
while let Some(rng) = next {
|
||||||
let res = catch!(txn, txn.batch(rng, *NORMAL_FETCH_SIZE, true).await);
|
let res = catch!(txn, txn.batch(rng, *NORMAL_FETCH_SIZE, true, None).await);
|
||||||
next = res.next;
|
next = res.next;
|
||||||
for (k, v) in res.values.iter() {
|
for (k, v) in res.values.iter() {
|
||||||
// Decode the LIVE query statement
|
// Decode the LIVE query statement
|
||||||
|
|
|
@ -238,7 +238,7 @@ impl super::api::Transaction for Transaction {
|
||||||
where
|
where
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
{
|
{
|
||||||
// RocksDB does not support verisoned queries.
|
// RocksDB does not support versioned queries.
|
||||||
if version.is_some() {
|
if version.is_some() {
|
||||||
return Err(Error::UnsupportedVersionedQueries);
|
return Err(Error::UnsupportedVersionedQueries);
|
||||||
}
|
}
|
||||||
|
@ -286,7 +286,7 @@ impl super::api::Transaction for Transaction {
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
V: Into<Val> + Debug,
|
V: Into<Val> + Debug,
|
||||||
{
|
{
|
||||||
// RocksDB does not support verisoned queries.
|
// RocksDB does not support versioned queries.
|
||||||
if version.is_some() {
|
if version.is_some() {
|
||||||
return Err(Error::UnsupportedVersionedQueries);
|
return Err(Error::UnsupportedVersionedQueries);
|
||||||
}
|
}
|
||||||
|
@ -454,7 +454,7 @@ impl super::api::Transaction for Transaction {
|
||||||
where
|
where
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
{
|
{
|
||||||
// RocksDB does not support verisoned queries.
|
// RocksDB does not support versioned queries.
|
||||||
if version.is_some() {
|
if version.is_some() {
|
||||||
return Err(Error::UnsupportedVersionedQueries);
|
return Err(Error::UnsupportedVersionedQueries);
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,7 +297,7 @@ async fn batch() {
|
||||||
tx.commit().await.unwrap();
|
tx.commit().await.unwrap();
|
||||||
// Create a readonly transaction
|
// Create a readonly transaction
|
||||||
let mut tx = ds.transaction(Read, Optimistic).await.unwrap().inner();
|
let mut tx = ds.transaction(Read, Optimistic).await.unwrap().inner();
|
||||||
let res = tx.batch("test1".as_bytes().."test9".as_bytes(), u32::MAX, true).await.unwrap();
|
let res = tx.batch("test1".as_bytes().."test9".as_bytes(), u32::MAX, true, None).await.unwrap();
|
||||||
let val = res.values;
|
let val = res.values;
|
||||||
assert_eq!(val.len(), 5);
|
assert_eq!(val.len(), 5);
|
||||||
assert_eq!(val[0].0, b"test1");
|
assert_eq!(val[0].0, b"test1");
|
||||||
|
@ -313,7 +313,7 @@ async fn batch() {
|
||||||
tx.cancel().await.unwrap();
|
tx.cancel().await.unwrap();
|
||||||
// Create a readonly transaction
|
// Create a readonly transaction
|
||||||
let mut tx = ds.transaction(Read, Optimistic).await.unwrap().inner();
|
let mut tx = ds.transaction(Read, Optimistic).await.unwrap().inner();
|
||||||
let res = tx.batch("test2".as_bytes().."test4".as_bytes(), u32::MAX, true).await.unwrap();
|
let res = tx.batch("test2".as_bytes().."test4".as_bytes(), u32::MAX, true, None).await.unwrap();
|
||||||
let val = res.values;
|
let val = res.values;
|
||||||
assert_eq!(val.len(), 2);
|
assert_eq!(val.len(), 2);
|
||||||
assert_eq!(val[0].0, b"test2");
|
assert_eq!(val[0].0, b"test2");
|
||||||
|
@ -323,7 +323,7 @@ async fn batch() {
|
||||||
tx.cancel().await.unwrap();
|
tx.cancel().await.unwrap();
|
||||||
// Create a readonly transaction
|
// Create a readonly transaction
|
||||||
let mut tx = ds.transaction(Read, Optimistic).await.unwrap().inner();
|
let mut tx = ds.transaction(Read, Optimistic).await.unwrap().inner();
|
||||||
let res = tx.batch("test2".as_bytes().."test4".as_bytes(), u32::MAX, true).await.unwrap();
|
let res = tx.batch("test2".as_bytes().."test4".as_bytes(), u32::MAX, true, None).await.unwrap();
|
||||||
let val = res.values;
|
let val = res.values;
|
||||||
assert_eq!(val.len(), 2);
|
assert_eq!(val.len(), 2);
|
||||||
assert_eq!(val[0].0, b"test2");
|
assert_eq!(val[0].0, b"test2");
|
||||||
|
|
|
@ -256,7 +256,7 @@ impl super::api::Transaction for Transaction {
|
||||||
K: Into<Key> + Sprintable + Debug,
|
K: Into<Key> + Sprintable + Debug,
|
||||||
V: Into<Val> + Debug,
|
V: Into<Val> + Debug,
|
||||||
{
|
{
|
||||||
// TiKV does not support verisoned queries.
|
// TiKV does not support versioned queries.
|
||||||
if version.is_some() {
|
if version.is_some() {
|
||||||
return Err(Error::UnsupportedVersionedQueries);
|
return Err(Error::UnsupportedVersionedQueries);
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,13 +237,17 @@ impl Transactor {
|
||||||
///
|
///
|
||||||
/// This function fetches all matching key-value pairs from the underlying datastore in grouped batches.
|
/// This function fetches all matching key-value pairs from the underlying datastore in grouped batches.
|
||||||
#[instrument(level = "trace", target = "surrealdb::core::kvs::tr", skip_all)]
|
#[instrument(level = "trace", target = "surrealdb::core::kvs::tr", skip_all)]
|
||||||
pub async fn getr<K>(&mut self, rng: Range<K>) -> Result<Vec<(Key, Val)>, Error>
|
pub async fn getr<K>(
|
||||||
|
&mut self,
|
||||||
|
rng: Range<K>,
|
||||||
|
version: Option<u64>,
|
||||||
|
) -> Result<Vec<(Key, Val)>, Error>
|
||||||
where
|
where
|
||||||
K: Into<Key> + Debug,
|
K: Into<Key> + Debug,
|
||||||
{
|
{
|
||||||
let beg: Key = rng.start.into();
|
let beg: Key = rng.start.into();
|
||||||
let end: Key = rng.end.into();
|
let end: Key = rng.end.into();
|
||||||
expand_inner!(&mut self.inner, v => { v.getr(beg..end).await })
|
expand_inner!(&mut self.inner, v => { v.getr(beg..end, version).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve a specific prefixed range of keys from the datastore.
|
/// Retrieve a specific prefixed range of keys from the datastore.
|
||||||
|
@ -380,13 +384,14 @@ impl Transactor {
|
||||||
rng: Range<K>,
|
rng: Range<K>,
|
||||||
batch: u32,
|
batch: u32,
|
||||||
values: bool,
|
values: bool,
|
||||||
|
version: Option<u64>,
|
||||||
) -> Result<Batch, Error>
|
) -> Result<Batch, Error>
|
||||||
where
|
where
|
||||||
K: Into<Key> + Debug,
|
K: Into<Key> + Debug,
|
||||||
{
|
{
|
||||||
let beg: Key = rng.start.into();
|
let beg: Key = rng.start.into();
|
||||||
let end: Key = rng.end.into();
|
let end: Key = rng.end.into();
|
||||||
expand_inner!(&mut self.inner, v => { v.batch(beg..end, batch, values).await })
|
expand_inner!(&mut self.inner, v => { v.batch(beg..end, batch, values, version).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain a new change timestamp for a key
|
/// Obtain a new change timestamp for a key
|
||||||
|
@ -596,7 +601,7 @@ impl Transactor {
|
||||||
let ts_key = crate::key::database::ts::new(ns, db, ts);
|
let ts_key = crate::key::database::ts::new(ns, db, ts);
|
||||||
let begin = ts_key.encode()?;
|
let begin = ts_key.encode()?;
|
||||||
let end = crate::key::database::ts::suffix(ns, db);
|
let end = crate::key::database::ts::suffix(ns, db);
|
||||||
let ts_pairs: Vec<(Vec<u8>, Vec<u8>)> = self.getr(begin..end).await?;
|
let ts_pairs: Vec<(Vec<u8>, Vec<u8>)> = self.getr(begin..end, None).await?;
|
||||||
let latest_ts_pair = ts_pairs.last();
|
let latest_ts_pair = ts_pairs.last();
|
||||||
if let Some((k, _)) = latest_ts_pair {
|
if let Some((k, _)) = latest_ts_pair {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
|
@ -629,7 +634,7 @@ impl Transactor {
|
||||||
let start = crate::key::database::ts::prefix(ns, db);
|
let start = crate::key::database::ts::prefix(ns, db);
|
||||||
let ts_key = crate::key::database::ts::new(ns, db, ts + 1);
|
let ts_key = crate::key::database::ts::new(ns, db, ts + 1);
|
||||||
let end = ts_key.encode()?;
|
let end = ts_key.encode()?;
|
||||||
let ts_pairs = self.getr(start..end).await?;
|
let ts_pairs = self.getr(start..end, None).await?;
|
||||||
let latest_ts_pair = ts_pairs.last();
|
let latest_ts_pair = ts_pairs.last();
|
||||||
if let Some((_, v)) = latest_ts_pair {
|
if let Some((_, v)) = latest_ts_pair {
|
||||||
if v.len() == 10 {
|
if v.len() == 10 {
|
||||||
|
|
|
@ -141,11 +141,15 @@ impl Transaction {
|
||||||
///
|
///
|
||||||
/// This function fetches key-value pairs from the underlying datastore in grouped batches.
|
/// This function fetches key-value pairs from the underlying datastore in grouped batches.
|
||||||
#[instrument(level = "trace", target = "surrealdb::core::kvs::tx", skip_all)]
|
#[instrument(level = "trace", target = "surrealdb::core::kvs::tx", skip_all)]
|
||||||
pub async fn getr<K>(&self, rng: Range<K>) -> Result<Vec<(Key, Val)>, Error>
|
pub async fn getr<K>(
|
||||||
|
&self,
|
||||||
|
rng: Range<K>,
|
||||||
|
version: Option<u64>,
|
||||||
|
) -> Result<Vec<(Key, Val)>, Error>
|
||||||
where
|
where
|
||||||
K: Into<Key> + Debug,
|
K: Into<Key> + Debug,
|
||||||
{
|
{
|
||||||
self.lock().await.getr(rng).await
|
self.lock().await.getr(rng, version).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a key from the datastore.
|
/// Delete a key from the datastore.
|
||||||
|
@ -250,11 +254,17 @@ impl Transaction {
|
||||||
///
|
///
|
||||||
/// This function fetches the key-value pairs in batches, with multiple requests to the underlying datastore.
|
/// This function fetches the key-value pairs in batches, with multiple requests to the underlying datastore.
|
||||||
#[instrument(level = "trace", target = "surrealdb::core::kvs::tx", skip_all)]
|
#[instrument(level = "trace", target = "surrealdb::core::kvs::tx", skip_all)]
|
||||||
pub async fn batch<K>(&self, rng: Range<K>, batch: u32, values: bool) -> Result<Batch, Error>
|
pub async fn batch<K>(
|
||||||
|
&self,
|
||||||
|
rng: Range<K>,
|
||||||
|
batch: u32,
|
||||||
|
values: bool,
|
||||||
|
version: Option<u64>,
|
||||||
|
) -> Result<Batch, Error>
|
||||||
where
|
where
|
||||||
K: Into<Key> + Debug,
|
K: Into<Key> + Debug,
|
||||||
{
|
{
|
||||||
self.lock().await.batch(rng, batch, values).await
|
self.lock().await.batch(rng, batch, values, version).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve a stream over a specific range of keys in the datastore.
|
/// Retrieve a stream over a specific range of keys in the datastore.
|
||||||
|
@ -315,7 +325,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::root::nd::suffix();
|
let end = crate::key::root::nd::suffix();
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Nds(Arc::clone(&val));
|
let val = Entry::Nds(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -334,7 +344,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::root::us::suffix();
|
let end = crate::key::root::us::suffix();
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Rus(Arc::clone(&val));
|
let val = Entry::Rus(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -353,7 +363,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::root::ac::suffix();
|
let end = crate::key::root::ac::suffix();
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Ras(Arc::clone(&val));
|
let val = Entry::Ras(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -372,7 +382,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::root::access::gr::suffix(ra);
|
let end = crate::key::root::access::gr::suffix(ra);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Rag(Arc::clone(&val));
|
let val = Entry::Rag(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -391,7 +401,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::root::ns::suffix();
|
let end = crate::key::root::ns::suffix();
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Nss(Arc::clone(&val));
|
let val = Entry::Nss(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -410,7 +420,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::namespace::us::suffix(ns);
|
let end = crate::key::namespace::us::suffix(ns);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Nus(Arc::clone(&val));
|
let val = Entry::Nus(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -429,7 +439,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::namespace::ac::suffix(ns);
|
let end = crate::key::namespace::ac::suffix(ns);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Nas(Arc::clone(&val));
|
let val = Entry::Nas(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -452,7 +462,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::namespace::access::gr::suffix(ns, na);
|
let end = crate::key::namespace::access::gr::suffix(ns, na);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Nag(Arc::clone(&val));
|
let val = Entry::Nag(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -471,7 +481,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::namespace::db::suffix(ns);
|
let end = crate::key::namespace::db::suffix(ns);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Dbs(Arc::clone(&val));
|
let val = Entry::Dbs(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -494,7 +504,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::database::us::suffix(ns, db);
|
let end = crate::key::database::us::suffix(ns, db);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Dus(Arc::clone(&val));
|
let val = Entry::Dus(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -517,7 +527,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::database::ac::suffix(ns, db);
|
let end = crate::key::database::ac::suffix(ns, db);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Das(Arc::clone(&val));
|
let val = Entry::Das(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -541,7 +551,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::database::access::gr::suffix(ns, db, da);
|
let end = crate::key::database::access::gr::suffix(ns, db, da);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Dag(Arc::clone(&val));
|
let val = Entry::Dag(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -564,7 +574,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::database::az::suffix(ns, db);
|
let end = crate::key::database::az::suffix(ns, db);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Azs(Arc::clone(&val));
|
let val = Entry::Azs(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -587,7 +597,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::database::fc::suffix(ns, db);
|
let end = crate::key::database::fc::suffix(ns, db);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Fcs(Arc::clone(&val));
|
let val = Entry::Fcs(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -610,7 +620,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::database::pa::suffix(ns, db);
|
let end = crate::key::database::pa::suffix(ns, db);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Pas(Arc::clone(&val));
|
let val = Entry::Pas(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -633,7 +643,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::database::ml::suffix(ns, db);
|
let end = crate::key::database::ml::suffix(ns, db);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Mls(Arc::clone(&val));
|
let val = Entry::Mls(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -645,14 +655,19 @@ impl Transaction {
|
||||||
|
|
||||||
/// Retrieve all table definitions for a specific database.
|
/// Retrieve all table definitions for a specific database.
|
||||||
#[instrument(level = "trace", target = "surrealdb::core::kvs::tx", skip(self))]
|
#[instrument(level = "trace", target = "surrealdb::core::kvs::tx", skip(self))]
|
||||||
pub async fn all_tb(&self, ns: &str, db: &str) -> Result<Arc<[DefineTableStatement]>, Error> {
|
pub async fn all_tb(
|
||||||
|
&self,
|
||||||
|
ns: &str,
|
||||||
|
db: &str,
|
||||||
|
version: Option<u64>,
|
||||||
|
) -> Result<Arc<[DefineTableStatement]>, Error> {
|
||||||
let key = crate::key::database::tb::prefix(ns, db);
|
let key = crate::key::database::tb::prefix(ns, db);
|
||||||
let res = self.cache.get_value_or_guard_async(&key).await;
|
let res = self.cache.get_value_or_guard_async(&key).await;
|
||||||
match res {
|
match res {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::database::tb::suffix(ns, db);
|
let end = crate::key::database::tb::suffix(ns, db);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, version).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Tbs(Arc::clone(&val));
|
let val = Entry::Tbs(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -676,7 +691,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::table::ev::suffix(ns, db, tb);
|
let end = crate::key::table::ev::suffix(ns, db, tb);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Evs(Arc::clone(&val));
|
let val = Entry::Evs(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -693,6 +708,7 @@ impl Transaction {
|
||||||
ns: &str,
|
ns: &str,
|
||||||
db: &str,
|
db: &str,
|
||||||
tb: &str,
|
tb: &str,
|
||||||
|
version: Option<u64>,
|
||||||
) -> Result<Arc<[DefineFieldStatement]>, Error> {
|
) -> Result<Arc<[DefineFieldStatement]>, Error> {
|
||||||
let key = crate::key::table::fd::prefix(ns, db, tb);
|
let key = crate::key::table::fd::prefix(ns, db, tb);
|
||||||
let res = self.cache.get_value_or_guard_async(&key).await;
|
let res = self.cache.get_value_or_guard_async(&key).await;
|
||||||
|
@ -700,7 +716,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::table::fd::suffix(ns, db, tb);
|
let end = crate::key::table::fd::suffix(ns, db, tb);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, version).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Fds(Arc::clone(&val));
|
let val = Entry::Fds(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -724,7 +740,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::table::ix::suffix(ns, db, tb);
|
let end = crate::key::table::ix::suffix(ns, db, tb);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Ixs(Arc::clone(&val));
|
let val = Entry::Ixs(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -748,7 +764,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::table::ft::suffix(ns, db, tb);
|
let end = crate::key::table::ft::suffix(ns, db, tb);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Fts(Arc::clone(&val));
|
let val = Entry::Fts(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
@ -772,7 +788,7 @@ impl Transaction {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(cache) => {
|
Err(cache) => {
|
||||||
let end = crate::key::table::lq::suffix(ns, db, tb);
|
let end = crate::key::table::lq::suffix(ns, db, tb);
|
||||||
let val = self.getr(key..end).await?;
|
let val = self.getr(key..end, None).await?;
|
||||||
let val = val.convert().into();
|
let val = val.convert().into();
|
||||||
let val = Entry::Lvs(Arc::clone(&val));
|
let val = Entry::Lvs(Arc::clone(&val));
|
||||||
let _ = cache.insert(val.clone());
|
let _ = cache.insert(val.clone());
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub async fn v1_to_2_id_uuid(tx: Arc<Transaction>) -> Result<(), Error> {
|
||||||
let ns = ns.name.as_str();
|
let ns = ns.name.as_str();
|
||||||
for db in tx.all_db(ns).await?.iter() {
|
for db in tx.all_db(ns).await?.iter() {
|
||||||
let db = db.name.as_str();
|
let db = db.name.as_str();
|
||||||
for tb in tx.all_tb(ns, db).await?.iter() {
|
for tb in tx.all_tb(ns, db, None).await?.iter() {
|
||||||
let tb = tb.name.as_str();
|
let tb = tb.name.as_str();
|
||||||
migrate_tb_records(tx.clone(), ns, db, tb).await?;
|
migrate_tb_records(tx.clone(), ns, db, tb).await?;
|
||||||
migrate_tb_edges(tx.clone(), ns, db, tb).await?;
|
migrate_tb_edges(tx.clone(), ns, db, tb).await?;
|
||||||
|
|
|
@ -81,7 +81,7 @@ impl DefineFieldStatement {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// find existing field definitions.
|
// find existing field definitions.
|
||||||
let fields = txn.all_tb_fields(ns, db, &self.what).await.ok();
|
let fields = txn.all_tb_fields(ns, db, &self.what, None).await.ok();
|
||||||
|
|
||||||
// Process possible recursive_definitions.
|
// Process possible recursive_definitions.
|
||||||
if let Some(mut cur_kind) = self.kind.as_ref().and_then(|x| x.inner_kind()) {
|
if let Some(mut cur_kind) = self.kind.as_ref().and_then(|x| x.inner_kind()) {
|
||||||
|
|
|
@ -4,14 +4,14 @@ use crate::doc::CursorDoc;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::iam::Action;
|
use crate::iam::Action;
|
||||||
use crate::iam::ResourceKind;
|
use crate::iam::ResourceKind;
|
||||||
use crate::sql::{Base, Ident, Object, Value};
|
use crate::sql::{Base, Ident, Object, Value, Version};
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[revisioned(revision = 4)]
|
#[revisioned(revision = 5)]
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
||||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
@ -25,10 +25,10 @@ pub enum InfoStatement {
|
||||||
Ns(#[revision(start = 2)] bool),
|
Ns(#[revision(start = 2)] bool),
|
||||||
|
|
||||||
#[revision(override(revision = 2, discriminant = 5), override(revision = 3, discriminant = 5))]
|
#[revision(override(revision = 2, discriminant = 5), override(revision = 3, discriminant = 5))]
|
||||||
Db(#[revision(start = 2)] bool),
|
Db(#[revision(start = 2)] bool, #[revision(start = 5)] Option<Version>),
|
||||||
|
|
||||||
#[revision(override(revision = 2, discriminant = 7), override(revision = 3, discriminant = 7))]
|
#[revision(override(revision = 2, discriminant = 7), override(revision = 3, discriminant = 7))]
|
||||||
Tb(Ident, #[revision(start = 2)] bool),
|
Tb(Ident, #[revision(start = 2)] bool, #[revision(start = 5)] Option<Version>),
|
||||||
|
|
||||||
#[revision(override(revision = 2, discriminant = 9), override(revision = 3, discriminant = 9))]
|
#[revision(override(revision = 2, discriminant = 9), override(revision = 3, discriminant = 9))]
|
||||||
User(Ident, Option<Base>, #[revision(start = 2)] bool),
|
User(Ident, Option<Base>, #[revision(start = 2)] bool),
|
||||||
|
@ -131,12 +131,14 @@ impl InfoStatement {
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
InfoStatement::Db(structured) => {
|
InfoStatement::Db(structured, version) => {
|
||||||
// Allowed to run?
|
// Allowed to run?
|
||||||
opt.is_allowed(Action::View, ResourceKind::Any, &Base::Db)?;
|
opt.is_allowed(Action::View, ResourceKind::Any, &Base::Db)?;
|
||||||
// Get the NS and DB
|
// Get the NS and DB
|
||||||
let ns = opt.ns()?;
|
let ns = opt.ns()?;
|
||||||
let db = opt.db()?;
|
let db = opt.db()?;
|
||||||
|
// Convert the version to u64 if present
|
||||||
|
let version = version.as_ref().map(|v| v.to_u64());
|
||||||
// Get the transaction
|
// Get the transaction
|
||||||
let txn = ctx.tx();
|
let txn = ctx.tx();
|
||||||
// Create the result set
|
// Create the result set
|
||||||
|
@ -147,7 +149,7 @@ impl InfoStatement {
|
||||||
"functions".to_string() => process(txn.all_db_functions(ns, db).await?),
|
"functions".to_string() => process(txn.all_db_functions(ns, db).await?),
|
||||||
"models".to_string() => process(txn.all_db_models(ns, db).await?),
|
"models".to_string() => process(txn.all_db_models(ns, db).await?),
|
||||||
"params".to_string() => process(txn.all_db_params(ns, db).await?),
|
"params".to_string() => process(txn.all_db_params(ns, db).await?),
|
||||||
"tables".to_string() => process(txn.all_tb(ns, db).await?),
|
"tables".to_string() => process(txn.all_tb(ns, db, version).await?),
|
||||||
"users".to_string() => process(txn.all_db_users(ns, db).await?),
|
"users".to_string() => process(txn.all_db_users(ns, db).await?),
|
||||||
}),
|
}),
|
||||||
false => Value::from(map! {
|
false => Value::from(map! {
|
||||||
|
@ -188,7 +190,7 @@ impl InfoStatement {
|
||||||
},
|
},
|
||||||
"tables".to_string() => {
|
"tables".to_string() => {
|
||||||
let mut out = Object::default();
|
let mut out = Object::default();
|
||||||
for v in txn.all_tb(ns, db).await?.iter() {
|
for v in txn.all_tb(ns, db, version).await?.iter() {
|
||||||
out.insert(v.name.to_raw(), v.to_string().into());
|
out.insert(v.name.to_raw(), v.to_string().into());
|
||||||
}
|
}
|
||||||
out.into()
|
out.into()
|
||||||
|
@ -203,19 +205,21 @@ impl InfoStatement {
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
InfoStatement::Tb(tb, structured) => {
|
InfoStatement::Tb(tb, structured, version) => {
|
||||||
// Allowed to run?
|
// Allowed to run?
|
||||||
opt.is_allowed(Action::View, ResourceKind::Any, &Base::Db)?;
|
opt.is_allowed(Action::View, ResourceKind::Any, &Base::Db)?;
|
||||||
// Get the NS and DB
|
// Get the NS and DB
|
||||||
let ns = opt.ns()?;
|
let ns = opt.ns()?;
|
||||||
let db = opt.db()?;
|
let db = opt.db()?;
|
||||||
|
// Convert the version to u64 if present
|
||||||
|
let version = version.as_ref().map(|v| v.to_u64());
|
||||||
// Get the transaction
|
// Get the transaction
|
||||||
let txn = ctx.tx();
|
let txn = ctx.tx();
|
||||||
// Create the result set
|
// Create the result set
|
||||||
Ok(match structured {
|
Ok(match structured {
|
||||||
true => Value::from(map! {
|
true => Value::from(map! {
|
||||||
"events".to_string() => process(txn.all_tb_events(ns, db, tb).await?),
|
"events".to_string() => process(txn.all_tb_events(ns, db, tb).await?),
|
||||||
"fields".to_string() => process(txn.all_tb_fields(ns, db, tb).await?),
|
"fields".to_string() => process(txn.all_tb_fields(ns, db, tb, version).await?),
|
||||||
"indexes".to_string() => process(txn.all_tb_indexes(ns, db, tb).await?),
|
"indexes".to_string() => process(txn.all_tb_indexes(ns, db, tb).await?),
|
||||||
"lives".to_string() => process(txn.all_tb_lives(ns, db, tb).await?),
|
"lives".to_string() => process(txn.all_tb_lives(ns, db, tb).await?),
|
||||||
"tables".to_string() => process(txn.all_tb_views(ns, db, tb).await?),
|
"tables".to_string() => process(txn.all_tb_views(ns, db, tb).await?),
|
||||||
|
@ -230,7 +234,7 @@ impl InfoStatement {
|
||||||
},
|
},
|
||||||
"fields".to_string() => {
|
"fields".to_string() => {
|
||||||
let mut out = Object::default();
|
let mut out = Object::default();
|
||||||
for v in txn.all_tb_fields(ns, db, tb).await?.iter() {
|
for v in txn.all_tb_fields(ns, db, tb, version).await?.iter() {
|
||||||
out.insert(v.name.to_string(), v.to_string().into());
|
out.insert(v.name.to_string(), v.to_string().into());
|
||||||
}
|
}
|
||||||
out.into()
|
out.into()
|
||||||
|
@ -308,10 +312,23 @@ impl fmt::Display for InfoStatement {
|
||||||
Self::Root(true) => f.write_str("INFO FOR ROOT STRUCTURE"),
|
Self::Root(true) => f.write_str("INFO FOR ROOT STRUCTURE"),
|
||||||
Self::Ns(false) => f.write_str("INFO FOR NAMESPACE"),
|
Self::Ns(false) => f.write_str("INFO FOR NAMESPACE"),
|
||||||
Self::Ns(true) => f.write_str("INFO FOR NAMESPACE STRUCTURE"),
|
Self::Ns(true) => f.write_str("INFO FOR NAMESPACE STRUCTURE"),
|
||||||
Self::Db(false) => f.write_str("INFO FOR DATABASE"),
|
Self::Db(false, ref v) => match v {
|
||||||
Self::Db(true) => f.write_str("INFO FOR DATABASE STRUCTURE"),
|
Some(ref v) => write!(f, "INFO FOR DATABASE VERSION {v}"),
|
||||||
Self::Tb(ref t, false) => write!(f, "INFO FOR TABLE {t}"),
|
None => f.write_str("INFO FOR DATABASE"),
|
||||||
Self::Tb(ref t, true) => write!(f, "INFO FOR TABLE {t} STRUCTURE"),
|
},
|
||||||
|
Self::Db(true, ref v) => match v {
|
||||||
|
Some(ref v) => write!(f, "INFO FOR DATABASE VERSION {v} STRUCTURE"),
|
||||||
|
None => f.write_str("INFO FOR DATABASE STRUCTURE"),
|
||||||
|
},
|
||||||
|
Self::Tb(ref t, false, ref v) => match v {
|
||||||
|
Some(ref v) => write!(f, "INFO FOR TABLE {t} VERSION {v}"),
|
||||||
|
None => write!(f, "INFO FOR TABLE {t}"),
|
||||||
|
},
|
||||||
|
|
||||||
|
Self::Tb(ref t, true, ref v) => match v {
|
||||||
|
Some(ref v) => write!(f, "INFO FOR TABLE {t} VERSION {v} STRUCTURE"),
|
||||||
|
None => write!(f, "INFO FOR TABLE {t} STRUCTURE"),
|
||||||
|
},
|
||||||
Self::User(ref u, ref b, false) => match b {
|
Self::User(ref u, ref b, false) => match b {
|
||||||
Some(ref b) => write!(f, "INFO FOR USER {u} ON {b}"),
|
Some(ref b) => write!(f, "INFO FOR USER {u} ON {b}"),
|
||||||
None => write!(f, "INFO FOR USER {u}"),
|
None => write!(f, "INFO FOR USER {u}"),
|
||||||
|
@ -335,12 +352,20 @@ impl InfoStatement {
|
||||||
match self {
|
match self {
|
||||||
InfoStatement::Root(_) => InfoStatement::Root(true),
|
InfoStatement::Root(_) => InfoStatement::Root(true),
|
||||||
InfoStatement::Ns(_) => InfoStatement::Ns(true),
|
InfoStatement::Ns(_) => InfoStatement::Ns(true),
|
||||||
InfoStatement::Db(_) => InfoStatement::Db(true),
|
InfoStatement::Db(_, v) => InfoStatement::Db(true, v),
|
||||||
InfoStatement::Tb(t, _) => InfoStatement::Tb(t, true),
|
InfoStatement::Tb(t, _, v) => InfoStatement::Tb(t, true, v),
|
||||||
InfoStatement::User(u, b, _) => InfoStatement::User(u, b, true),
|
InfoStatement::User(u, b, _) => InfoStatement::User(u, b, true),
|
||||||
InfoStatement::Index(i, t, _) => InfoStatement::Index(i, t, true),
|
InfoStatement::Index(i, t, _) => InfoStatement::Index(i, t, true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn versionize(self, v: Version) -> Self {
|
||||||
|
match self {
|
||||||
|
InfoStatement::Db(s, _) => InfoStatement::Db(s, Some(v)),
|
||||||
|
InfoStatement::Tb(t, s, _) => InfoStatement::Tb(t, s, Some(v)),
|
||||||
|
_ => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process<T>(a: Arc<[T]>) -> Value
|
fn process<T>(a: Arc<[T]>) -> Value
|
||||||
|
|
|
@ -180,7 +180,7 @@ mod tests {
|
||||||
|
|
||||||
// Create a new transaction and verify that there are no tables defined.
|
// Create a new transaction and verify that there are no tables defined.
|
||||||
let tx = dbs.transaction(Write, Optimistic).await.unwrap();
|
let tx = dbs.transaction(Write, Optimistic).await.unwrap();
|
||||||
let table_occurrences = &*(tx.all_tb(ns, db).await.unwrap());
|
let table_occurrences = &*(tx.all_tb(ns, db, None).await.unwrap());
|
||||||
assert!(table_occurrences.is_empty());
|
assert!(table_occurrences.is_empty());
|
||||||
tx.cancel().await.unwrap();
|
tx.cancel().await.unwrap();
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ mod tests {
|
||||||
|
|
||||||
// Verify that the table definition has been created.
|
// Verify that the table definition has been created.
|
||||||
let tx = dbs.transaction(Write, Optimistic).await.unwrap();
|
let tx = dbs.transaction(Write, Optimistic).await.unwrap();
|
||||||
let table_occurrences = &*(tx.all_tb(ns, db).await.unwrap());
|
let table_occurrences = &*(tx.all_tb(ns, db, None).await.unwrap());
|
||||||
assert_eq!(table_occurrences.len(), 1);
|
assert_eq!(table_occurrences.len(), 1);
|
||||||
assert_eq!(table_occurrences[0].name.0, tb);
|
assert_eq!(table_occurrences[0].name.0, tb);
|
||||||
tx.cancel().await.unwrap();
|
tx.cancel().await.unwrap();
|
||||||
|
@ -218,7 +218,7 @@ mod tests {
|
||||||
|
|
||||||
// Create a new transaction to verify that the same table was used.
|
// Create a new transaction to verify that the same table was used.
|
||||||
let tx = dbs.transaction(Write, Optimistic).await.unwrap();
|
let tx = dbs.transaction(Write, Optimistic).await.unwrap();
|
||||||
let table_occurrences = &*(tx.all_tb(ns, db).await.unwrap());
|
let table_occurrences = &*(tx.all_tb(ns, db, None).await.unwrap());
|
||||||
assert_eq!(table_occurrences.len(), 1);
|
assert_eq!(table_occurrences.len(), 1);
|
||||||
assert_eq!(table_occurrences[0].name.0, tb);
|
assert_eq!(table_occurrences[0].name.0, tb);
|
||||||
tx.cancel().await.unwrap();
|
tx.cancel().await.unwrap();
|
||||||
|
@ -250,7 +250,7 @@ mod tests {
|
||||||
|
|
||||||
// Create a new transaction and verify that there are no tables defined.
|
// Create a new transaction and verify that there are no tables defined.
|
||||||
let tx = dbs.transaction(Write, Optimistic).await.unwrap();
|
let tx = dbs.transaction(Write, Optimistic).await.unwrap();
|
||||||
let table_occurrences = &*(tx.all_tb(ns, db).await.unwrap());
|
let table_occurrences = &*(tx.all_tb(ns, db, None).await.unwrap());
|
||||||
assert!(table_occurrences.is_empty());
|
assert!(table_occurrences.is_empty());
|
||||||
tx.cancel().await.unwrap();
|
tx.cancel().await.unwrap();
|
||||||
|
|
||||||
|
@ -260,7 +260,7 @@ mod tests {
|
||||||
|
|
||||||
// Create a new transaction and confirm that a new table is created.
|
// Create a new transaction and confirm that a new table is created.
|
||||||
let tx = dbs.transaction(Write, Optimistic).await.unwrap();
|
let tx = dbs.transaction(Write, Optimistic).await.unwrap();
|
||||||
let table_occurrences = &*(tx.all_tb(ns, db).await.unwrap());
|
let table_occurrences = &*(tx.all_tb(ns, db, None).await.unwrap());
|
||||||
assert_eq!(table_occurrences.len(), 1);
|
assert_eq!(table_occurrences.len(), 1);
|
||||||
assert_eq!(table_occurrences[0].name.0, tb);
|
assert_eq!(table_occurrences[0].name.0, tb);
|
||||||
tx.cancel().await.unwrap();
|
tx.cancel().await.unwrap();
|
||||||
|
@ -271,7 +271,7 @@ mod tests {
|
||||||
|
|
||||||
// Verify that the old table definition was used.
|
// Verify that the old table definition was used.
|
||||||
let tx = dbs.transaction(Write, Optimistic).await.unwrap();
|
let tx = dbs.transaction(Write, Optimistic).await.unwrap();
|
||||||
let table_occurrences = &*(tx.all_tb(ns, db).await.unwrap());
|
let table_occurrences = &*(tx.all_tb(ns, db, None).await.unwrap());
|
||||||
assert_eq!(table_occurrences.len(), 1);
|
assert_eq!(table_occurrences.len(), 1);
|
||||||
assert_eq!(table_occurrences[0].name.0, tb);
|
assert_eq!(table_occurrences[0].name.0, tb);
|
||||||
tx.cancel().await.unwrap();
|
tx.cancel().await.unwrap();
|
||||||
|
|
|
@ -526,10 +526,10 @@ impl Parser<'_> {
|
||||||
let mut stmt = match next.kind {
|
let mut stmt = match next.kind {
|
||||||
t!("ROOT") => InfoStatement::Root(false),
|
t!("ROOT") => InfoStatement::Root(false),
|
||||||
t!("NAMESPACE") | t!("ns") => InfoStatement::Ns(false),
|
t!("NAMESPACE") | t!("ns") => InfoStatement::Ns(false),
|
||||||
t!("DATABASE") => InfoStatement::Db(false),
|
t!("DATABASE") => InfoStatement::Db(false, None),
|
||||||
t!("TABLE") => {
|
t!("TABLE") => {
|
||||||
let ident = self.next_token_value()?;
|
let ident = self.next_token_value()?;
|
||||||
InfoStatement::Tb(ident, false)
|
InfoStatement::Tb(ident, false, None)
|
||||||
}
|
}
|
||||||
t!("USER") => {
|
t!("USER") => {
|
||||||
let ident = self.next_token_value()?;
|
let ident = self.next_token_value()?;
|
||||||
|
@ -546,6 +546,10 @@ impl Parser<'_> {
|
||||||
_ => unexpected!(self, next, "an info target"),
|
_ => unexpected!(self, next, "an info target"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(version) = self.try_parse_version()? {
|
||||||
|
stmt = stmt.versionize(version);
|
||||||
|
}
|
||||||
|
|
||||||
if self.peek_kind() == t!("STRUCTURE") {
|
if self.peek_kind() == t!("STRUCTURE") {
|
||||||
self.pop_peek();
|
self.pop_peek();
|
||||||
stmt = stmt.structurize();
|
stmt = stmt.structurize();
|
||||||
|
|
|
@ -1766,7 +1766,7 @@ fn parse_info() {
|
||||||
assert_eq!(res, Statement::Info(InfoStatement::Ns(false)));
|
assert_eq!(res, Statement::Info(InfoStatement::Ns(false)));
|
||||||
|
|
||||||
let res = test_parse!(parse_stmt, "INFO FOR TABLE table").unwrap();
|
let res = test_parse!(parse_stmt, "INFO FOR TABLE table").unwrap();
|
||||||
assert_eq!(res, Statement::Info(InfoStatement::Tb(Ident("table".to_owned()), false)));
|
assert_eq!(res, Statement::Info(InfoStatement::Tb(Ident("table".to_owned()), false, None)));
|
||||||
|
|
||||||
let res = test_parse!(parse_stmt, "INFO FOR USER user").unwrap();
|
let res = test_parse!(parse_stmt, "INFO FOR USER user").unwrap();
|
||||||
assert_eq!(res, Statement::Info(InfoStatement::User(Ident("user".to_owned()), None, false)));
|
assert_eq!(res, Statement::Info(InfoStatement::User(Ident("user".to_owned()), None, false)));
|
||||||
|
|
|
@ -581,6 +581,72 @@ mod api_integration {
|
||||||
assert!(response.is_none());
|
assert!(response.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_log::test(tokio::test)]
|
||||||
|
async fn info_for_db_with_versioned_tables() {
|
||||||
|
let (permit, db) = new_db().await;
|
||||||
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||||
|
drop(permit);
|
||||||
|
|
||||||
|
// Record the timestamp before creating a testing table.
|
||||||
|
let ts_before_create = chrono::Utc::now().to_rfc3339();
|
||||||
|
|
||||||
|
// Create the testing table.
|
||||||
|
let _ = db.query("DEFINE TABLE person").await.unwrap().check().unwrap();
|
||||||
|
|
||||||
|
// Record the timestamp after creating the testing table.
|
||||||
|
let ts_after_create = chrono::Utc::now().to_rfc3339();
|
||||||
|
|
||||||
|
// Check that historical query shows no table before it was created.
|
||||||
|
let q = format!("INFO FOR DB VERSION d'{}'", ts_before_create);
|
||||||
|
let mut response = db.query(q).await.unwrap().check().unwrap();
|
||||||
|
let info = response.take::<Value>(0).unwrap().to_string();
|
||||||
|
assert!(info.contains("tables: { }"));
|
||||||
|
|
||||||
|
// Now check that the table shows up later.
|
||||||
|
let q = format!("INFO FOR DB VERSION d'{}'", ts_after_create);
|
||||||
|
let mut response = db.query(q).await.unwrap().check().unwrap();
|
||||||
|
let info = response.take::<Value>(0).unwrap().to_string();
|
||||||
|
assert!(info.contains(
|
||||||
|
"tables: { person: 'DEFINE TABLE person TYPE ANY SCHEMALESS PERMISSIONS NONE' }"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test(tokio::test)]
|
||||||
|
async fn info_for_table_with_versioned_fields() {
|
||||||
|
let (permit, db) = new_db().await;
|
||||||
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||||
|
drop(permit);
|
||||||
|
|
||||||
|
// Create the testing table.
|
||||||
|
let _ = db.query("DEFINE TABLE person").await.unwrap().check().unwrap();
|
||||||
|
|
||||||
|
// Record the timestamp before creating a field.
|
||||||
|
let ts_before_field = chrono::Utc::now().to_rfc3339();
|
||||||
|
let _ = db
|
||||||
|
.query("DEFINE FIELD firstName ON TABLE person TYPE string")
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.check()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Record the timestamp after creating the field.
|
||||||
|
let ts_after_field = chrono::Utc::now().to_rfc3339();
|
||||||
|
|
||||||
|
// Check that historical query shows no field before it was created.
|
||||||
|
let q = format!("INFO FOR TABLE person VERSION d'{}'", ts_before_field);
|
||||||
|
let mut response = db.query(q).await.unwrap().check().unwrap();
|
||||||
|
let info = response.take::<Value>(0).unwrap().to_string();
|
||||||
|
assert!(info.contains("fields: { }"));
|
||||||
|
|
||||||
|
// Now check that the field shows up later.
|
||||||
|
let q = format!("INFO FOR TABLE person VERSION d'{}'", ts_after_field);
|
||||||
|
let mut response = db.query(q).await.unwrap().check().unwrap();
|
||||||
|
let info = response.take::<Value>(0).unwrap().to_string();
|
||||||
|
assert!(info.contains(
|
||||||
|
"fields: { firstName: 'DEFINE FIELD firstName ON person TYPE string PERMISSIONS FULL' }"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
include!("api/mod.rs");
|
include!("api/mod.rs");
|
||||||
include!("api/live.rs");
|
include!("api/live.rs");
|
||||||
include!("api/backup.rs");
|
include!("api/backup.rs");
|
||||||
|
|
Loading…
Reference in a new issue