11use crate :: db:: MetricsRecorderQueue ;
2- use crate :: error:: { DBError , DatabaseError , RestoreSnapshotError } ;
2+ use crate :: error:: { DBError , DatabaseError , RestoreSnapshotError , ViewError } ;
3+ use crate :: host:: ArgsTuple ;
34use crate :: messages:: control_db:: HostType ;
45use crate :: subscription:: ExecutionCounters ;
56use crate :: util:: { asyncify, spawn_rayon} ;
67use crate :: worker_metrics:: WORKER_METRICS ;
78use anyhow:: { anyhow, Context } ;
9+ use bytes:: Bytes ;
810use enum_map:: EnumMap ;
911use fs2:: FileExt ;
12+ use log:: trace;
1013use spacetimedb_commitlog as commitlog;
1114use spacetimedb_commitlog:: repo:: OnNewSegmentFn ;
1215use spacetimedb_data_structures:: map:: IntSet ;
@@ -20,6 +23,7 @@ use spacetimedb_datastore::locking_tx_datastore::state_view::{
2023use spacetimedb_datastore:: locking_tx_datastore:: { MutTxId , TxId } ;
2124use spacetimedb_datastore:: system_tables:: { system_tables, StModuleRow , StViewRow , ST_VIEW_ID } ;
2225use spacetimedb_datastore:: system_tables:: { StFields , StVarFields , StVarName , StVarRow , ST_MODULE_ID , ST_VAR_ID } ;
26+ use spacetimedb_datastore:: system_tables:: { StViewArgRow , ST_VIEW_ARG_ID } ;
2327use spacetimedb_datastore:: traits:: {
2428 InsertFlags , IsolationLevel , Metadata , MutTx as _, MutTxDatastore , Program , RowTypeForTable , Tx as _, TxDatastore ,
2529 UpdateFlags ,
@@ -32,16 +36,18 @@ use spacetimedb_datastore::{
3236 traits:: TxData ,
3337} ;
3438use spacetimedb_durability as durability;
39+ use spacetimedb_lib:: bsatn:: ToBsatn ;
3540use spacetimedb_lib:: db:: auth:: StAccess ;
3641use spacetimedb_lib:: db:: raw_def:: v9:: { btree, RawModuleDefV9Builder , RawSql } ;
42+ use spacetimedb_lib:: de:: DeserializeSeed as _;
3743use spacetimedb_lib:: st_var:: StVarValue ;
38- use spacetimedb_lib:: ConnectionId ;
3944use spacetimedb_lib:: Identity ;
45+ use spacetimedb_lib:: { bsatn, ConnectionId } ;
4046use spacetimedb_paths:: server:: { CommitLogDir , ReplicaDir , SnapshotsPath } ;
4147use spacetimedb_primitives:: * ;
4248use spacetimedb_sats:: algebraic_type:: fmt:: fmt_algebraic_type;
4349use spacetimedb_sats:: memory_usage:: MemoryUsage ;
44- use spacetimedb_sats:: { AlgebraicType , AlgebraicValue , ProductType , ProductValue } ;
50+ use spacetimedb_sats:: { product , AlgebraicType , AlgebraicValue , ProductType , ProductValue , Typespace } ;
4551use spacetimedb_schema:: def:: { ModuleDef , TableDef , ViewDef } ;
4652use spacetimedb_schema:: schema:: {
4753 ColumnSchema , IndexSchema , RowLevelSecuritySchema , Schema , SequenceSchema , TableSchema ,
@@ -1512,6 +1518,117 @@ impl RelationalDB {
15121518 . into ( )
15131519 } )
15141520 }
1521+
1522+ /// Get or insert view argument into `ST_VIEW_ARG_ID`.
1523+ pub fn get_or_insert_st_view_arg ( & self , tx : & mut MutTxId , args : & Bytes ) -> Result < u64 , DBError > {
1524+ let bytes_av = AlgebraicValue :: Bytes ( args. to_vec ( ) . into ( ) ) ;
1525+ let mut rows = self . iter_by_col_eq_mut ( tx, ST_VIEW_ARG_ID , col_list ! [ 1 ] , & bytes_av) ?;
1526+
1527+ // Extract the first matching `arg_id`, if any.
1528+ if let Some ( res) = rows. next ( ) {
1529+ let row = StViewArgRow :: try_from ( res) . expect ( "valid StViewArgRow" ) ;
1530+ return Ok ( row. id ) ;
1531+ }
1532+
1533+ let view_arg_bytes = product ! [ 0u64 , bytes_av]
1534+ . to_bsatn_vec ( )
1535+ . map_err ( |_| ViewError :: SerializeArgs ) ?;
1536+
1537+ let ( _, view_arg_row, _) = self . insert ( tx, ST_VIEW_ARG_ID , & view_arg_bytes) ?;
1538+ let StViewArgRow { id : arg_id, .. } = view_arg_row. try_into ( ) . expect ( "valid StViewArgRow" ) ;
1539+
1540+ Ok ( arg_id)
1541+ }
1542+
1543+ /// Evaluate and update View.
1544+ /// This involves:
1545+ /// 1. Serializing the view arguments into `ST_VIEW_ARG_ID`
1546+ /// 2. Deleting all rows in the view table matching the view arguments
1547+ /// 3. Deserializing the return value from the view execution
1548+ /// 4. Inserting all rows from the return value into the view table, with the arg_id
1549+ /// set to the inserted view argument's id.
1550+ /// The `typespace` is needed for deserializing the return value.
1551+ pub fn evaluate_view (
1552+ & self ,
1553+ tx : & mut MutTxId ,
1554+ // Name of the view to update
1555+ view : & str ,
1556+ // Arguments passed to the view call
1557+ args : ArgsTuple ,
1558+ // Return type of the view call
1559+ return_type : AlgebraicType ,
1560+ // Serialized bytes of the return value from the view call
1561+ //TODO: pass arg_id; do the insertion during starting of invoking view
1562+ bytes : Bytes ,
1563+ typespace : & Typespace ,
1564+ // Identity of the caller (for non-anonymous views)
1565+ caller_identity : Identity ,
1566+ ) -> Result < ( ) , DBError > {
1567+ let st_view_row = tx. lookup_st_view_by_name ( view) ?;
1568+
1569+ let ( table_id, is_anonymous) = (
1570+ st_view_row
1571+ . table_id
1572+ . ok_or_else ( || ViewError :: ViewNotFound ( view. to_string ( ) ) ) ?,
1573+ st_view_row. is_anonymous ,
1574+ ) ;
1575+
1576+ // Insert the view arguments into ST_VIEW_ARG_ID
1577+ let arg_id = self . get_or_insert_st_view_arg ( tx, & args. get_bsatn ( ) ) ?;
1578+
1579+ // Delete all existing rows in the view table matching the view arguments
1580+ let av: AlgebraicValue = args. tuple . into ( ) ;
1581+ let rows_to_delete: Vec < _ > = self
1582+ . iter_by_col_eq_mut ( tx, table_id, col_list ! [ 0 ] , & av) ?
1583+ . map ( |res| res. pointer ( ) )
1584+ . collect ( ) ;
1585+ let count = self . delete ( tx, table_id, rows_to_delete) ;
1586+ trace ! ( "Deleted {count} rows from view table {table_id} for arg_id {arg_id}" ) ;
1587+
1588+ // Deserialize the return value
1589+ let seed = spacetimedb_sats:: WithTypespace :: new ( typespace, & return_type) ;
1590+ let return_val = seed
1591+ . deserialize ( bsatn:: Deserializer :: new ( & mut & bytes[ ..] ) )
1592+ . map_err ( |e| ViewError :: DeserializeReturn ( e. to_string ( ) ) ) ?;
1593+ let products = Self :: extract_products ( return_val, & return_type) ?;
1594+
1595+ // Insert all rows from the return value into the view table
1596+ for product in products {
1597+ let row = {
1598+ let mut elements = Vec :: with_capacity ( 2 + product. elements . len ( ) ) ;
1599+ elements. push ( if is_anonymous {
1600+ AlgebraicValue :: OptionNone ( )
1601+ } else {
1602+ AlgebraicValue :: OptionSome ( caller_identity. into ( ) )
1603+ } ) ;
1604+ elements. push ( AlgebraicValue :: U64 ( arg_id) ) ;
1605+ elements. extend_from_slice ( & product. elements ) ;
1606+
1607+ ProductValue {
1608+ elements : elements. into_boxed_slice ( ) ,
1609+ }
1610+ } ;
1611+ let row_bytes = row. to_bsatn_vec ( ) . map_err ( |_| ViewError :: SerializeRow ) ?;
1612+ self . insert ( tx, table_id, & row_bytes) ?;
1613+ }
1614+
1615+ Ok ( ( ) )
1616+ }
1617+
1618+ fn extract_products (
1619+ return_val : AlgebraicValue ,
1620+ return_type : & AlgebraicType ,
1621+ ) -> Result < Vec < ProductValue > , ViewError > {
1622+ if return_type. is_array ( ) {
1623+ let arr = return_val. into_array ( ) . expect ( "return type is array" ) ;
1624+ Ok ( arr. into_iter ( ) . map ( |v| v. into_product ( ) . unwrap ( ) ) . collect ( ) )
1625+ } else if return_type. is_option ( ) {
1626+ let opt = return_val. into_option ( ) . expect ( "return type is option" ) ;
1627+ Ok ( opt. into_iter ( ) . map ( |v| v. into_product ( ) . unwrap ( ) ) . collect ( ) )
1628+ } else {
1629+ Err ( ViewError :: InvalidReturnType ( return_type. clone ( ) ) )
1630+ }
1631+ }
15151632}
15161633
15171634#[ allow( unused) ]
0 commit comments