@@ -4117,7 +4117,20 @@ impl Codegen {
41174117 self.emit_ctor_as_function_value(builder, &name)
41184118 }
41194119
4120− ast::ExprKind::SourceRef(name) => {
4120+ ast::ExprKind::SourceRef { name, full } => {
4121+ // A bare `*rel` read that reaches here was not consumed by any
4122+ // SQL-pushdown matcher — it loads the whole relation into
4123+ // memory via `knot_source_read`. Require the user to opt in
4124+ // with `full *rel`.
4125+ if !full {
4126+ return self.push_codegen_error(
4127+ builder,
4128+ expr.span,
4129+ format!(
4130+ "reading `*{name}` loads the whole relation into memory (no SQL pushdown); write `full *{name}` to allow this",
4131+ ),
4132+ );
4133+ }
41214134 // Check if this is a view reference
41224135 let view_info = self.views.get(name).cloned();
41234136 if let Some(view) = view_info {
@@ -4672,7 +4685,7 @@ impl Codegen {
46724685 && fn_name == "match"
46734686 && !env.bindings.contains_key(fn_name)
46744687 && !(self.top_fn_names.contains(fn_name) && self.user_fns.contains_key(fn_name)) {
4675− if let ast::ExprKind::SourceRef(source_name) = &lhs.node
4688+ if let ast::ExprKind::SourceRef { name: source_name, .. } = &lhs.node
46764689 && let Some(schema) = self.source_schemas.get(source_name).cloned() {
46774690 let (name_ptr, name_len) =
46784691 self.string_ptr(builder, source_name);
@@ -4821,7 +4834,7 @@ impl Codegen {
48214834
48224835 ast::ExprKind::Set { target, value } => {
48234836 // target should be a SourceRef (source or view)
4824− if let ast::ExprKind::SourceRef(name) = &target.node {
4837+ if let ast::ExprKind::SourceRef { name, .. } = &target.node {
48254838 // Check if target is a view
48264839 let view_info = self.views.get(name).cloned();
48274840 if let Some(view) = view_info {
@@ -4998,7 +5011,7 @@ impl Codegen {
49985011 }
49995012
50005013 ast::ExprKind::FullSet { target, value } => {
5001− if let ast::ExprKind::SourceRef(name) = &target.node {
5014+ if let ast::ExprKind::SourceRef { name, .. } = &target.node {
50025015 // Check if target is a view
50035016 let view_info = self.views.get(name).cloned();
50045017 if let Some(view) = view_info {
@@ -5643,7 +5656,7 @@ impl Codegen {
56435656 match &expr.node {
56445657 Set { target, value } | FullSet { target, value } => {
56455658 let target_ok = match &target.node {
5646− SourceRef(name) if !self.views.contains_key(name) => {
5659+ SourceRef { name, .. } if !self.views.contains_key(name) => {
56475660 out.push(name.clone());
56485661 true
56495662 }
@@ -5729,7 +5742,7 @@ impl Codegen {
57295742 Serve { handlers, .. } => handlers
57305743 .iter()
57315744 .all(|h| self.collect_direct_write_targets(&h.body, out)),
5732− Lit(_) | Constructor(_) | SourceRef(_) | DerivedRef(_) => true,
5745+ Lit(_) | Constructor(_) | SourceRef { .. } | DerivedRef(_) => true,
57335746 TypeCtor { .. } | DataCtor { .. } | SourceDecl { .. } | SubsetConstraint { .. } => true,
57345747 ViewDecl { body, .. } | DerivedDecl { body, .. } => self.collect_direct_write_targets(body, out),
57355748 }
@@ -5762,7 +5775,7 @@ impl Codegen {
57625775 /// Handles both `*source` (SourceRef) and bound variables from `x <- *source`.
57635776 fn resolve_source(&self, expr: &ast::Expr) -> Option<String> {
57645777 match &expr.node {
5765− ast::ExprKind::SourceRef(name) => Some(name.clone()),
5778+ ast::ExprKind::SourceRef { name, .. } => Some(name.clone()),
57665779 ast::ExprKind::Var(name) => self.source_var_binds.get(name).cloned(),
57675780 _ => None,
57685781 }
@@ -6469,7 +6482,7 @@ impl Codegen {
64696482 if let ast::ExprKind::Var(name) = &func_expr.node
64706483 && name == "fold" && args.len() == 3 && !user_shadows_special {
64716484 // fold f init *source → SELECT cols FROM table; stream
6472− if let ast::ExprKind::SourceRef(source_name) = &args[2].node
6485+ if let ast::ExprKind::SourceRef { name: source_name, .. } = &args[2].node
64736486 && !self.views.contains_key(source_name)
64746487 && let Some(schema) = self.source_schemas.get(source_name).cloned()
64756488 && !schema.starts_with('#') && !schema.contains('[') {
@@ -6547,7 +6560,7 @@ impl Codegen {
65476560 if let ast::ExprKind::Var(name) = &func_expr.node
65486561 && name == "match" && args.len() == 2 && !user_shadows_special
65496562 && let ast::ExprKind::Constructor(ctor_name) = &args[0].node {
6550− if let ast::ExprKind::SourceRef(source_name) = &args[1].node
6563+ if let ast::ExprKind::SourceRef { name: source_name, .. } = &args[1].node
65516564 && let Some(schema) = self.source_schemas.get(source_name).cloned() {
65526565 let source_name = source_name.clone();
65536566 self.emit_stm_track_read(builder, &source_name);
@@ -6939,7 +6952,7 @@ impl Codegen {
69396952 && let Some((sort_bind, sort_body)) = extract_single_param_lambda(sort_lambda, &self.fun_bodies, &self.let_bindings) {
69406953 let sort_body: &ast::Expr = &sort_body;
69416954 // Case 1: sortBy f *source → SQL ORDER BY + LIMIT
6942− if let ast::ExprKind::SourceRef(source_name) = &sort_source.node
6955+ if let ast::ExprKind::SourceRef { name: source_name, .. } = &sort_source.node
69436956 && !self.views.contains_key(source_name)
69446957 && let Some(schema) = self.source_schemas.get(source_name).cloned()
69456958 && !schema.starts_with('#') && !schema.contains('[') {
@@ -8734,7 +8747,7 @@ impl Codegen {
87348747 fn expr_contains_io(expr: &ast::Expr, builtins: &HashSet<&str>, io_fns: &HashSet<String>) -> bool {
87358748 match &expr.node {
87368749 ast::ExprKind::Var(name) => builtins.contains(name.as_str()) || io_fns.contains(name),
8737− ast::ExprKind::SourceRef(_) | ast::ExprKind::DerivedRef(_) => true,
8750+ ast::ExprKind::SourceRef { .. } | ast::ExprKind::DerivedRef(_) => true,
87388751 ast::ExprKind::Set { .. } | ast::ExprKind::FullSet { .. } => true,
87398752 ast::ExprKind::Atomic(_) => true,
87408753 ast::ExprKind::App { func, arg } => {
@@ -9057,7 +9070,7 @@ impl Codegen {
90579070 ) || self.io_functions.contains(name)
90589071 || Self::io_scopes_lookup(scopes, name)
90599072 }
9060− ast::ExprKind::SourceRef(_) | ast::ExprKind::DerivedRef(_) => true,
9073+ ast::ExprKind::SourceRef { .. } | ast::ExprKind::DerivedRef(_) => true,
90619074 ast::ExprKind::Set { .. } | ast::ExprKind::FullSet { .. } => true,
90629075 ast::ExprKind::Atomic(_) => true,
90639076 ast::ExprKind::BinOp { lhs, rhs, .. } => {
@@ -9195,7 +9208,7 @@ impl Codegen {
91959208 }
91969209 // Relation reads are the "pure DB" IO that inference lets flow
91979210 // as the relation value itself.
9198− ast::ExprKind::SourceRef(_) | ast::ExprKind::DerivedRef(_) => false,
9211+ ast::ExprKind::SourceRef { .. } | ast::ExprKind::DerivedRef(_) => false,
91999212 // Writes and atomic blocks must not run at `let` time.
92009213 ast::ExprKind::Set { .. } | ast::ExprKind::FullSet { .. } => true,
92019214 ast::ExprKind::Atomic(_) => true,
@@ -9470,7 +9483,7 @@ impl Codegen {
94709483 );
94719484 let rhs_is_io_relation_source = matches!(
94729485 &expr.node,
9473− ast::ExprKind::SourceRef(_) | ast::ExprKind::DerivedRef(_)
9486+ ast::ExprKind::SourceRef { .. } | ast::ExprKind::DerivedRef(_)
94749487 );
94759488 // A comprehension TAIL inside a sequential IO block:
94769489 //
@@ -9569,7 +9582,7 @@ impl Codegen {
95699582 // `x <- *source` records x → source so inner do-blocks
95709583 // like `do { m <- x; where ...; yield m }` can compile to SQL.
95719584 if let ast::PatKind::Var(var_name) = &pat.node
9572− && let ast::ExprKind::SourceRef(source_name) = &expr.node {
9585+ && let ast::ExprKind::SourceRef { name: source_name, .. } = &expr.node {
95739586 self.source_var_binds.insert(var_name.clone(), source_name.clone());
95749587 }
95759588 // A comprehension over relation sources (`xs <- do { r <-
@@ -10251,7 +10264,7 @@ impl Codegen {
1025110264 }
1025210265 // Inner expr must be hoistable (source, derived, var, or list).
1025310266 let hoistable = match &inner_expr.node {
10254− ast::ExprKind::SourceRef(_)
10267+ ast::ExprKind::SourceRef { .. }
1025510268 | ast::ExprKind::DerivedRef(_)
1025610269 | ast::ExprKind::List(_) => true,
1025710270 // A `Var` is only hoistable when it resolves OUTSIDE this
@@ -10390,7 +10403,7 @@ impl Codegen {
1039010403 }
1039110404 primary_row_val = Some(row);
1039210405 match &expr.node {
10393− ast::ExprKind::SourceRef(name)
10406+ ast::ExprKind::SourceRef { name, .. }
1039410407 | ast::ExprKind::DerivedRef(name) => {
1039510408 primary_source = Some(name.clone());
1039610409 primary_schema = self.source_schemas.get(name).cloned();
@@ -10433,7 +10446,7 @@ impl Codegen {
1043310446
1043410447 // ── Filter pushdown: try to push Where clauses into SQL ──
1043510448 let use_filter_pushdown = if let ast::PatKind::Var(bind_var) = &pat.node {
10436− if let ast::ExprKind::SourceRef(source_name) = &expr.node {
10449+ if let ast::ExprKind::SourceRef { name: source_name, .. } = &expr.node {
1043710450 if !self.views.contains_key(source_name)
1043810451 && self.source_schemas.contains_key(source_name)
1043910452 {
@@ -10561,7 +10574,7 @@ impl Codegen {
1056110574 let rel = if matches!(&pat.node, ast::PatKind::Constructor { .. }) {
1056210575 let is_known_relation = matches!(
1056310576 &expr.node,
10564− ast::ExprKind::SourceRef(_)
10577+ ast::ExprKind::SourceRef { .. }
1056510578 | ast::ExprKind::DerivedRef(_)
1056610579 | ast::ExprKind::List(_)
1056710580 | ast::ExprKind::Do(_)
@@ -10613,7 +10626,7 @@ impl Codegen {
1061310626 }
1061410627 primary_row_val = Some(row);
1061510628 match &expr.node {
10616− ast::ExprKind::SourceRef(name)
10629+ ast::ExprKind::SourceRef { name, .. }
1061710630 | ast::ExprKind::DerivedRef(name) => {
1061810631 primary_source = Some(name.clone());
1061910632 primary_schema = self.source_schemas.get(name).cloned();
@@ -11613,7 +11626,7 @@ impl Codegen {
1161311626 /// Check whether an expression references `*<source_name>` anywhere.
1161411627 fn references_source(expr: &ast::Expr, source_name: &str) -> bool {
1161511628 match &expr.node {
11616− ast::ExprKind::SourceRef(name) => name == source_name,
11629+ ast::ExprKind::SourceRef { name, .. } => name == source_name,
1161711630 ast::ExprKind::ImplicitRef(_) => false,
1161811631 ast::ExprKind::TypeHole => false,
1161911632 ast::ExprKind::Lit(_)
@@ -11877,7 +11890,7 @@ impl Codegen {
1187711890
1187811891 // Source must be a SourceRef or a variable bound from a source read
1187911892 let source_name = match &source.node {
11880− ast::ExprKind::SourceRef(name) => name.clone(),
11893+ ast::ExprKind::SourceRef { name, .. } => name.clone(),
1188111894 ast::ExprKind::Var(name) => self.source_var_binds.get(name)?.clone(),
1188211895 _ => return None,
1188311896 };
@@ -12262,7 +12275,7 @@ impl Codegen {
1226212275 } else {
1226312276 return None;
1226412277 };
12265− let source_name = if let ast::ExprKind::SourceRef(name) = &expr.node {
12278+ let source_name = if let ast::ExprKind::SourceRef { name, .. } = &expr.node {
1226612279 name.clone()
1226712280 } else if let ast::ExprKind::Var(var_name) = &expr.node {
1226812281 // Variable bound from a source read (e.g. `allMessages <- *messages`)
@@ -13124,7 +13137,7 @@ impl Codegen {
1312413137 /// Check if an expression refers to a specific source (directly or via a bound variable).
1312513138 fn expr_is_source(node: &ast::ExprKind, source_name: &str, var_binds: &HashMap<String, String>) -> bool {
1312613139 match node {
13127− ast::ExprKind::SourceRef(name) => name == source_name,
13140+ ast::ExprKind::SourceRef { name, .. } => name == source_name,
1312813141 ast::ExprKind::Var(name) => var_binds.get(name).is_some_and(|s| s == source_name),
1312913142 _ => false,
1313013143 }
@@ -13180,7 +13193,7 @@ impl Codegen {
1318013193 if let ast::ExprKind::Do(stmts) = &value.node
1318113194 && stmts.len() == 2
1318213195 && let ast::StmtKind::Bind { expr, .. } = &stmts[0].node
13183− && let ast::ExprKind::SourceRef(name) = &expr.node
13196+ && let ast::ExprKind::SourceRef { name, .. } = &expr.node
1318413197 && name == source_name
1318513198 && let ast::StmtKind::Expr(e) = &stmts[1].node {
1318613199 return e.node.as_yield_arg().is_some();
@@ -13205,7 +13218,7 @@ impl Codegen {
1320513218
1320613219 // First: t <- *rel
1320713220 let bind_var = if let ast::StmtKind::Bind { pat, expr } = &stmts[0].node {
13208− if let ast::ExprKind::SourceRef(name) = &expr.node {
13221+ if let ast::ExprKind::SourceRef { name, .. } = &expr.node {
1320913222 if name == source_name {
1321013223 if let ast::PatKind::Var(v) = &pat.node {
1321113224 v.clone()
@@ -13787,7 +13800,7 @@ impl Codegen {
1378713800/// write rows that violate the filter), and we must not panic on valid input.
1378813801fn analyze_view(body: &ast::Expr) -> Result<Option<ViewInfo>, (ast::Span, String)> {
1378913802 // Case 1: simple alias — *view = *source
13790− if let ast::ExprKind::SourceRef(source_name) = &body.node {
13803+ if let ast::ExprKind::SourceRef { name: source_name, .. } = &body.node {
1379113804 return Ok(Some(ViewInfo {
1379213805 source_name: source_name.clone(),
1379313806 source_columns: vec![],
@@ -13801,7 +13814,7 @@ fn analyze_view(body: &ast::Expr) -> Result<Option<ViewInfo>, (ast::Span, String
1380113814 // Find the bind statement: t <- *source
1380213815 let bind_info = stmts.iter().find_map(|s| {
1380313816 if let ast::StmtKind::Bind { pat, expr } = &s.node
13804− && let ast::ExprKind::SourceRef(source_name) = &expr.node
13817+ && let ast::ExprKind::SourceRef { name: source_name, .. } = &expr.node
1380513818 && let ast::PatKind::Var(var_name) = &pat.node {
1380613819 return Some((var_name.clone(), source_name.clone()));
1380713820 }
@@ -14016,7 +14029,7 @@ fn expr_has_user_calls(expr: &ast::Expr, user_fns: &HashMap<String, (FuncId, usi
1401614029 ast::ExprKind::Lit(_)
1401714030 | ast::ExprKind::Var(_)
1401814031 | ast::ExprKind::Constructor(_)
14019− | ast::ExprKind::SourceRef(_)
14032+ | ast::ExprKind::SourceRef { .. }
1402014033 | ast::ExprKind::DerivedRef(_)
1402114034 | ast::ExprKind::List(_) => false,
1402214035 // Conservative: treat complex nodes as potentially having user calls
@@ -14029,7 +14042,7 @@ fn expr_references_var(expr: &ast::Expr, var_name: &str) -> bool {
1402914042 ast::ExprKind::Var(name) => name == var_name,
1403014043 ast::ExprKind::Lit(_)
1403114044 | ast::ExprKind::Constructor(_)
14032− | ast::ExprKind::SourceRef(_)
14045+ | ast::ExprKind::SourceRef { .. }
1403314046 | ast::ExprKind::DerivedRef(_) => false,
1403414047 ast::ExprKind::Record(fields) => fields
1403514048 .iter()
@@ -15119,7 +15132,7 @@ fn extract_filter_on_source(
1511915132 if let ast::ExprKind::App { func, arg: source_expr } = &reduced.node {
1512015133 let resolve_source = |se: &ast::Expr| -> Option<String> {
1512115134 match &se.node {
15122− ast::ExprKind::SourceRef(name) => Some(name.clone()),
15135+ ast::ExprKind::SourceRef { name, .. } => Some(name.clone()),
1512315136 ast::ExprKind::Var(name) => source_var_binds.get(name).cloned(),
1512415137 _ => None,
1512515138 }
@@ -15387,7 +15400,7 @@ fn beta_reduce_inner(
1538715400 // For constructs that bind names (Case arms, Do statements, Set, etc.)
1538815401 // we keep them unchanged: SQL pushdown never sees these inside the
1538915402 // expressions it analyzes (lambda bodies of filter/map/aggregate).
15390− Lit(_) | Constructor(_) | SourceRef(_) | DerivedRef(_) | Case { .. } | Do(_)
15403+ Lit(_) | Constructor(_) | SourceRef { .. } | DerivedRef(_) | Case { .. } | Do(_)
1539115404 | Set { .. } | FullSet { .. } | Atomic(_) | TimeUnitLit { .. }
1539215405 | Annot { .. } | Refine(_) | Serve { .. } | TypeCtor { .. } | DataCtor { .. } | SourceDecl { .. }
1539315406 | ViewDecl { .. } | DerivedDecl { .. } => return expr.clone(),
@@ -15413,7 +15426,7 @@ fn substitute_inner(
1541315426 let span = expr.span;
1541415427 let new_node = match &expr.node {
1541515428 Var(name) if name == var => return Some(value.clone()),
15416− Var(_) | Lit(_) | Constructor(_) | SourceRef(_) | DerivedRef(_) | ImplicitRef(_) | TypeHole | TypeCtor { .. }
15429+ Var(_) | Lit(_) | Constructor(_) | SourceRef { .. } | DerivedRef(_) | ImplicitRef(_) | TypeHole | TypeCtor { .. }
1541715430 | DataCtor { .. } | SourceDecl { .. } | SubsetConstraint { .. } | RouteDecl { .. } | RouteCompositeDecl { .. } => {
1541815431 return Some(expr.clone())
1541915432 }
@@ -15537,7 +15550,7 @@ fn expr_mentions_var(expr: &ast::Expr, var: &str) -> bool {
1553715550 };
1553815551 match &expr.node {
1553915552 Var(name) => name == var,
15540− Lit(_) | Constructor(_) | SourceRef(_) | DerivedRef(_) | ImplicitRef(_) | TypeHole | TypeCtor { .. }
15553+ Lit(_) | Constructor(_) | SourceRef { .. } | DerivedRef(_) | ImplicitRef(_) | TypeHole | TypeCtor { .. }
1554115554 | DataCtor { .. } | SourceDecl { .. } | SubsetConstraint { .. } | RouteDecl { .. } | RouteCompositeDecl { .. } => false,
1554215555 ViewDecl { body, .. } | DerivedDecl { body, .. } => expr_mentions_var(body, var),
1554315556 Record(fields) => fields.iter().any(|f| expr_mentions_var(&f.value, var)),
@@ -15590,7 +15603,7 @@ fn collect_free_vars_set(expr: &ast::Expr, bound: &HashSet<String>, free: &mut H
1559015603 free.insert(name.clone());
1559115604 }
1559215605 }
15593− Lit(_) | Constructor(_) | SourceRef(_) | DerivedRef(_) | ImplicitRef(_) | TypeHole | TypeCtor { .. }
15606+ Lit(_) | Constructor(_) | SourceRef { .. } | DerivedRef(_) | ImplicitRef(_) | TypeHole | TypeCtor { .. }
1559415607 | DataCtor { .. } | SourceDecl { .. } | SubsetConstraint { .. } | RouteDecl { .. } | RouteCompositeDecl { .. } => {}
1559515608 ViewDecl { body, .. } | DerivedDecl { body, .. } => collect_free_vars_set(body, bound, free),
1559615609 Lambda { params, body, .. } => {
@@ -16427,7 +16440,7 @@ fn expr_contains_derived_ref(expr: &ast::Expr, name: &str) -> bool {
1642716440 match &expr.node {
1642816441 ast::ExprKind::DerivedRef(n) => n == name,
1642916442 ast::ExprKind::Lit(_) | ast::ExprKind::Var(_) | ast::ExprKind::Constructor(_)
16430− | ast::ExprKind::SourceRef(_) | ast::ExprKind::ImplicitRef(_) | ast::ExprKind::TypeHole | ast::ExprKind::TypeCtor { .. }
16443+ | ast::ExprKind::SourceRef { .. } | ast::ExprKind::ImplicitRef(_) | ast::ExprKind::TypeHole | ast::ExprKind::TypeCtor { .. }
1643116444 | ast::ExprKind::DataCtor { .. } | ast::ExprKind::SourceDecl { .. } | ast::ExprKind::SubsetConstraint { .. } => false,
1643216445 ast::ExprKind::RouteDecl { .. } | ast::ExprKind::RouteCompositeDecl { .. } => false,
1643316446 ast::ExprKind::ViewDecl { body, .. } | ast::ExprKind::DerivedDecl { body, .. } => expr_contains_derived_ref(body, name),
@@ -16524,7 +16537,7 @@ fn collect_free_vars(expr: &ast::Expr, bound: &HashSet<&str>, free: &mut Vec<Str
1652416537 }
1652516538 }
1652616539 ast::ExprKind::Lit(_) | ast::ExprKind::Constructor(_) => {}
16527− ast::ExprKind::SourceRef(_) => {}
16540+ ast::ExprKind::SourceRef { .. } => {}
1652816541 ast::ExprKind::SourceDecl { .. } => {}
1652916542 ast::ExprKind::SubsetConstraint { .. } => {}
1653016543 ast::ExprKind::RouteDecl { .. } | ast::ExprKind::RouteCompositeDecl { .. } => {}
@@ -16698,7 +16711,7 @@ pub(crate) fn expr_refs_var(expr: &ast::Expr, var: &str) -> bool {
1669816711 ast::ExprKind::Var(name) => name == var,
1669916712 ast::ExprKind::Lit(_)
1670016713 | ast::ExprKind::Constructor(_)
16701− | ast::ExprKind::SourceRef(_)
16714+ | ast::ExprKind::SourceRef { .. }
1670216715 | ast::ExprKind::ImplicitRef(_)
1670316716 | ast::ExprKind::TypeHole
1670416717 | ast::ExprKind::DerivedRef(_) => false,
@@ -16791,7 +16804,7 @@ fn expr_uses_var_as_value(expr: &ast::Expr, var: &str) -> bool {
1679116804 ast::ExprKind::Var(name) => name == var,
1679216805 ast::ExprKind::Lit(_)
1679316806 | ast::ExprKind::Constructor(_)
16794− | ast::ExprKind::SourceRef(_)
16807+ | ast::ExprKind::SourceRef { .. }
1679516808 | ast::ExprKind::ImplicitRef(_)
1679616809 | ast::ExprKind::TypeHole
1679716810 | ast::ExprKind::DerivedRef(_) => false,
@@ -17142,7 +17155,7 @@ fn pretty_expr(expr: &ast::Expr) -> String {
1714217155 ast::ExprKind::RouteDecl { name, .. } | ast::ExprKind::RouteCompositeDecl { name, .. } => {
1714317156 format!("route {}", name)
1714417157 }
17145− ast::ExprKind::SourceRef(name) => format!("*{}", name),
17158+ ast::ExprKind::SourceRef { name, .. } => format!("*{}", name),
1714617159 ast::ExprKind::DerivedRef(name) => format!("&{}", name),
1714717160 ast::ExprKind::Record(fields) => {
1714817161 let fs: Vec<String> = fields