Skip to main content

reifydb_rql/
nodes.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::{collections, fmt, time};
5
6use reifydb_catalog::catalog::{
7	ringbuffer::RingBufferColumnToCreate, series::SeriesColumnToCreate, subscription::SubscriptionColumnToCreate,
8	table::TableColumnToCreate, view::ViewColumnToCreate,
9};
10use reifydb_core::{
11	common::{JoinType, WindowSize, WindowSlide, WindowType},
12	interface::{
13		catalog::{
14			id::{NamespaceId, RingBufferId, SeriesId, TableId, ViewId},
15			namespace::Namespace,
16			procedure::{ProcedureParamDef, ProcedureTrigger},
17			property::ColumnPropertyKind,
18			series::TimestampPrecision,
19		},
20		resolved::{
21			ResolvedColumn, ResolvedDictionary, ResolvedNamespace, ResolvedPrimitive, ResolvedRingBuffer,
22			ResolvedSequence, ResolvedSeries, ResolvedTable, ResolvedTableVirtual, ResolvedView,
23		},
24	},
25	sort::{SortDirection, SortKey},
26};
27use reifydb_type::{
28	fragment::Fragment,
29	value::{constraint::TypeConstraint, dictionary::DictionaryId, sumtype::SumTypeId, r#type::Type},
30};
31
32use crate::{
33	expression::{AliasExpression, Expression, VariableExpression},
34	query::QueryPlan,
35};
36
37/// Owned primary key definition for physical plan nodes (materialized from bump-allocated logical plan)
38#[derive(Debug, Clone)]
39pub struct PrimaryKeyDef {
40	pub columns: Vec<PrimaryKeyColumn>,
41}
42
43/// Owned primary key column for physical plan nodes
44#[derive(Debug, Clone)]
45pub struct PrimaryKeyColumn {
46	pub column: Fragment,
47	pub order: Option<SortDirection>,
48}
49
50#[derive(Debug, Clone)]
51pub enum PhysicalPlan {
52	CreateDeferredView(CreateDeferredViewNode),
53	CreateTransactionalView(CreateTransactionalViewNode),
54	CreateNamespace(CreateNamespaceNode),
55	CreateRemoteNamespace(CreateRemoteNamespaceNode),
56	CreateTable(CreateTableNode),
57	CreateRingBuffer(CreateRingBufferNode),
58	CreateDictionary(CreateDictionaryNode),
59	CreateSumType(CreateSumTypeNode),
60	CreateSubscription(CreateSubscriptionNode),
61	CreatePrimaryKey(CreatePrimaryKeyNode),
62	CreateColumnProperty(CreateColumnPropertyNode),
63	CreateProcedure(CreateProcedureNode),
64	CreateSeries(CreateSeriesNode),
65	CreateEvent(CreateEventNode),
66	CreateTag(CreateTagNode),
67	CreateTest(CreateTestNode),
68	RunTests(RunTestsNode),
69
70	CreateMigration(CreateMigrationNode),
71	Migrate(MigrateNode),
72	RollbackMigration(RollbackMigrationNode),
73	Dispatch(DispatchNode),
74	// Alter
75	AlterSequence(AlterSequenceNode),
76	AlterTable(AlterTableNode),
77	AlterRemoteNamespace(AlterRemoteNamespaceNode),
78	// Mutate
79	Delete(DeleteTableNode),
80	DeleteRingBuffer(DeleteRingBufferNode),
81	InsertTable(InsertTableNode),
82	InsertRingBuffer(InsertRingBufferNode),
83	InsertDictionary(InsertDictionaryNode),
84	Update(UpdateTableNode),
85	UpdateRingBuffer(UpdateRingBufferNode),
86	UpdateSeries(UpdateSeriesNode),
87	// Variable assignment
88	Declare(DeclareNode),
89	Assign(AssignNode),
90	Append(AppendPhysicalNode),
91	// Variable resolution
92	Variable(VariableNode),
93	Environment(EnvironmentNode),
94	// Control flow
95	Conditional(ConditionalNode),
96	Loop(LoopPhysicalNode),
97	While(WhilePhysicalNode),
98	For(ForPhysicalNode),
99	Break,
100	Continue,
101	// User-defined functions
102	DefineFunction(DefineFunctionNode),
103	Return(ReturnNode),
104	CallFunction(CallFunctionNode),
105
106	// Query
107	Aggregate(AggregateNode),
108	Distinct(DistinctNode),
109	Filter(FilterNode),
110	IndexScan(IndexScanNode),
111	// Row-number optimized access
112	RowPointLookup(RowPointLookupNode),
113	RowListLookup(RowListLookupNode),
114	RowRangeScan(RowRangeScanNode),
115	JoinInner(JoinInnerNode),
116	JoinLeft(JoinLeftNode),
117	JoinNatural(JoinNaturalNode),
118	Take(TakeNode),
119	Sort(SortNode),
120	Map(MapNode),
121	Extend(ExtendNode),
122	Patch(PatchNode),
123	Apply(ApplyNode),
124	InlineData(InlineDataNode),
125	RemoteScan(RemoteScanNode),
126	TableScan(TableScanNode),
127	TableVirtualScan(TableVirtualScanNode),
128	ViewScan(ViewScanNode),
129	RingBufferScan(RingBufferScanNode),
130	DictionaryScan(DictionaryScanNode),
131	SeriesScan(SeriesScanNode),
132	// Series DML
133	InsertSeries(InsertSeriesNode),
134	DeleteSeries(DeleteSeriesNode),
135	Generator(GeneratorNode),
136	Window(WindowNode),
137	// Auto-scalarization for 1x1 frames
138	Scalarize(ScalarizeNode),
139	// Auth/Permissions
140	CreateUser(CreateUserNode),
141	CreateRole(CreateRoleNode),
142	Grant(GrantNode),
143	Revoke(RevokeNode),
144	DropUser(DropUserNode),
145	DropRole(DropRoleNode),
146	CreateAuthentication(CreateAuthenticationNode),
147	DropAuthentication(DropAuthenticationNode),
148	CreatePolicy(CreatePolicyNode),
149	AlterPolicy(AlterPolicyNode),
150	DropPolicy(DropPolicyNode),
151}
152
153#[derive(Debug, Clone)]
154pub struct CreateDeferredViewNode {
155	pub namespace: Namespace, // FIXME REsolvedNamespace
156	pub view: Fragment,
157	pub if_not_exists: bool,
158	pub columns: Vec<ViewColumnToCreate>,
159	pub as_clause: Box<QueryPlan>,
160}
161
162#[derive(Debug, Clone)]
163pub struct CreateTransactionalViewNode {
164	pub namespace: Namespace, // FIXME REsolvedNamespace
165	pub view: Fragment,
166	pub if_not_exists: bool,
167	pub columns: Vec<ViewColumnToCreate>,
168	pub as_clause: Box<QueryPlan>,
169}
170
171#[derive(Debug, Clone)]
172pub struct CreateNamespaceNode {
173	pub segments: Vec<Fragment>,
174	pub if_not_exists: bool,
175}
176
177#[derive(Debug, Clone)]
178pub struct CreateRemoteNamespaceNode {
179	pub segments: Vec<Fragment>,
180	pub if_not_exists: bool,
181	pub grpc: Fragment,
182}
183
184#[derive(Debug, Clone)]
185pub struct AlterRemoteNamespaceNode {
186	pub namespace: Fragment,
187	pub grpc: Fragment,
188}
189
190#[derive(Debug, Clone)]
191pub struct CreateTableNode {
192	pub namespace: ResolvedNamespace,
193	pub table: Fragment,
194	pub if_not_exists: bool,
195	pub columns: Vec<TableColumnToCreate>,
196}
197
198#[derive(Debug, Clone)]
199pub struct CreateRingBufferNode {
200	pub namespace: ResolvedNamespace,
201	pub ringbuffer: Fragment,
202	pub if_not_exists: bool,
203	pub columns: Vec<RingBufferColumnToCreate>,
204	pub capacity: u64,
205}
206
207#[derive(Debug, Clone)]
208pub struct CreateDictionaryNode {
209	pub namespace: Namespace,
210	pub dictionary: Fragment,
211	pub if_not_exists: bool,
212	pub value_type: Type,
213	pub id_type: Type,
214}
215
216#[derive(Debug, Clone)]
217pub struct CreateSumTypeNode {
218	pub namespace: Namespace,
219	pub name: Fragment,
220	pub if_not_exists: bool,
221	pub variants: Vec<CreateSumTypeVariant>,
222}
223
224#[derive(Debug, Clone)]
225pub struct CreateSumTypeVariant {
226	pub name: String,
227	pub columns: Vec<CreateSumTypeColumn>,
228}
229
230#[derive(Debug, Clone)]
231pub struct CreateSumTypeColumn {
232	pub name: String,
233	pub column_type: TypeConstraint,
234}
235
236#[derive(Debug, Clone)]
237pub struct CreateSubscriptionNode {
238	pub columns: Vec<SubscriptionColumnToCreate>,
239	pub as_clause: Option<Box<QueryPlan>>,
240}
241
242#[derive(Debug, Clone)]
243pub struct AlterSequenceNode {
244	pub sequence: ResolvedSequence,
245	pub column: ResolvedColumn,
246	pub value: Expression,
247}
248
249#[derive(Debug, Clone)]
250pub struct AlterTableNode {
251	pub namespace: ResolvedNamespace,
252	pub table: Fragment,
253	pub action: AlterTableAction,
254}
255
256#[derive(Debug, Clone)]
257pub enum AlterTableAction {
258	AddColumn {
259		column: TableColumnToCreate,
260	},
261	DropColumn {
262		column: Fragment,
263	},
264	RenameColumn {
265		old_name: Fragment,
266		new_name: Fragment,
267	},
268}
269
270// Create Primary Key node
271#[derive(Debug, Clone)]
272pub struct CreatePrimaryKeyNode {
273	pub namespace: ResolvedNamespace,
274	pub table: Fragment,
275	pub columns: Vec<PrimaryKeyColumn>,
276}
277
278// Create Procedure node
279#[derive(Debug, Clone)]
280pub struct CreateProcedureNode {
281	pub namespace: Namespace,
282	pub name: Fragment,
283	pub params: Vec<ProcedureParamDef>,
284	pub body_source: String,
285	pub trigger: ProcedureTrigger,
286	pub is_test: bool,
287}
288
289/// Physical node for CREATE SERIES
290#[derive(Debug, Clone)]
291pub struct CreateSeriesNode {
292	pub namespace: ResolvedNamespace,
293	pub series: Fragment,
294	pub columns: Vec<SeriesColumnToCreate>,
295	pub tag: Option<SumTypeId>,
296	pub precision: TimestampPrecision,
297}
298
299/// Physical node for CREATE EVENT
300#[derive(Debug, Clone)]
301pub struct CreateEventNode {
302	pub namespace: Namespace,
303	pub name: Fragment,
304	pub variants: Vec<CreateSumTypeVariant>,
305}
306
307/// Physical node for CREATE TAG
308#[derive(Debug, Clone)]
309pub struct CreateTagNode {
310	pub namespace: Namespace,
311	pub name: Fragment,
312	pub variants: Vec<CreateSumTypeVariant>,
313}
314
315// Assert Block node (multi-statement ASSERT or ASSERT ERROR)
316#[derive(Debug, Clone)]
317pub struct AssertBlockNode {
318	pub rql: String,
319	pub expect_error: bool,
320	pub message: Option<String>,
321}
322
323// Create Test node
324#[derive(Debug, Clone)]
325pub struct CreateTestNode {
326	pub namespace: Namespace,
327	pub name: Fragment,
328	pub cases: Option<String>,
329	pub body_source: String,
330}
331
332// Run Tests node
333#[derive(Debug, Clone)]
334pub struct RunTestsNode {
335	pub scope: RunTestsScope,
336}
337
338#[derive(Debug, Clone)]
339pub enum RunTestsScope {
340	All,
341	Namespace(ResolvedNamespace),
342	Single(ResolvedNamespace, String),
343}
344
345/// Physical node for CREATE MIGRATION
346#[derive(Debug, Clone)]
347pub struct CreateMigrationNode {
348	pub name: String,
349	pub body_source: String,
350	pub rollback_body_source: Option<String>,
351}
352
353/// Physical node for MIGRATE
354#[derive(Debug, Clone)]
355pub struct MigrateNode {
356	pub target: Option<String>,
357}
358
359/// Physical node for ROLLBACK MIGRATION
360#[derive(Debug, Clone)]
361pub struct RollbackMigrationNode {
362	pub target: Option<String>,
363}
364
365/// Physical node for DISPATCH
366#[derive(Debug, Clone)]
367pub struct DispatchNode {
368	pub namespace: Namespace,
369	pub on_sumtype_id: SumTypeId,
370	pub variant_name: String,
371	pub fields: Vec<(String, Expression)>,
372}
373
374// Create Policy node
375#[derive(Debug, Clone)]
376pub struct CreateColumnPropertyNode {
377	pub namespace: ResolvedNamespace,
378	pub table: Fragment,
379	pub column: Fragment,
380	pub properties: Vec<ColumnPropertyKind>,
381}
382
383#[derive(Debug, Clone)]
384pub enum LetValue {
385	Expression(Expression),
386	Statement(QueryPlan),
387	EmptyFrame,
388}
389
390impl fmt::Display for LetValue {
391	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
392		match self {
393			LetValue::Expression(expr) => write!(f, "{}", expr),
394			LetValue::Statement(query) => write!(f, "Statement({:?})", query),
395			LetValue::EmptyFrame => write!(f, "EmptyFrame"),
396		}
397	}
398}
399
400#[derive(Debug, Clone)]
401pub struct DeclareNode {
402	pub name: Fragment,
403	pub value: LetValue,
404}
405
406#[derive(Debug, Clone)]
407pub enum AssignValue {
408	Expression(Expression),
409	Statement(QueryPlan),
410}
411
412impl fmt::Display for AssignValue {
413	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414		match self {
415			AssignValue::Expression(expr) => write!(f, "{}", expr),
416			AssignValue::Statement(query) => write!(f, "Statement({:?})", query),
417		}
418	}
419}
420
421#[derive(Debug, Clone)]
422pub struct AssignNode {
423	pub name: Fragment,
424	pub value: AssignValue,
425}
426
427#[derive(Debug, Clone)]
428pub struct VariableNode {
429	pub variable_expr: VariableExpression,
430}
431
432#[derive(Debug, Clone)]
433pub struct EnvironmentNode {}
434
435/// A function parameter in the physical plan
436#[derive(Debug, Clone)]
437pub struct FunctionParameter {
438	/// Parameter name (includes $)
439	pub name: Fragment,
440	/// Optional type constraint
441	pub type_constraint: Option<TypeConstraint>,
442}
443
444#[derive(Debug, Clone)]
445pub struct ScalarizeNode {
446	pub input: Box<QueryPlan>,
447	pub fragment: Fragment,
448}
449
450#[derive(Debug, Clone)]
451pub struct AggregateNode {
452	pub input: Box<QueryPlan>,
453	pub by: Vec<Expression>,
454	pub map: Vec<Expression>,
455}
456
457#[derive(Debug, Clone)]
458pub struct DistinctNode {
459	pub input: Box<QueryPlan>,
460	pub columns: Vec<ResolvedColumn>,
461}
462
463#[derive(Debug, Clone)]
464pub struct AssertNode {
465	pub input: Option<Box<QueryPlan>>,
466	pub conditions: Vec<Expression>,
467	pub message: Option<String>,
468}
469
470#[derive(Debug, Clone)]
471pub struct FilterNode {
472	pub input: Box<QueryPlan>,
473	pub conditions: Vec<Expression>,
474}
475
476#[derive(Debug, Clone)]
477pub struct GateNode {
478	pub input: Box<QueryPlan>,
479	pub conditions: Vec<Expression>,
480}
481
482#[derive(Debug, Clone)]
483pub struct DeleteTableNode {
484	pub input: Option<Box<QueryPlan>>,
485	pub target: Option<ResolvedTable>,
486}
487
488#[derive(Debug, Clone)]
489pub struct InsertTableNode {
490	pub input: Box<QueryPlan>,
491	pub target: ResolvedTable,
492}
493
494#[derive(Debug, Clone)]
495pub struct InsertRingBufferNode {
496	pub input: Box<QueryPlan>,
497	pub target: ResolvedRingBuffer,
498}
499
500#[derive(Debug, Clone)]
501pub struct InsertDictionaryNode {
502	pub input: Box<QueryPlan>,
503	pub target: ResolvedDictionary,
504}
505
506#[derive(Debug, Clone)]
507pub struct UpdateTableNode {
508	pub input: Box<QueryPlan>,
509	pub target: Option<ResolvedTable>,
510}
511
512#[derive(Debug, Clone)]
513pub struct DeleteRingBufferNode {
514	pub input: Option<Box<QueryPlan>>,
515	pub target: ResolvedRingBuffer,
516}
517
518#[derive(Debug, Clone)]
519pub struct UpdateRingBufferNode {
520	pub input: Box<QueryPlan>,
521	pub target: ResolvedRingBuffer,
522}
523
524#[derive(Debug, Clone)]
525pub struct UpdateSeriesNode {
526	pub input: Box<QueryPlan>,
527	pub target: ResolvedSeries,
528}
529
530#[derive(Debug, Clone)]
531pub struct JoinInnerNode {
532	pub left: Box<QueryPlan>,
533	pub right: Box<QueryPlan>,
534	pub on: Vec<Expression>,
535	pub alias: Option<Fragment>,
536}
537
538#[derive(Debug, Clone)]
539pub struct JoinLeftNode {
540	pub left: Box<QueryPlan>,
541	pub right: Box<QueryPlan>,
542	pub on: Vec<Expression>,
543	pub alias: Option<Fragment>,
544}
545
546#[derive(Debug, Clone)]
547pub struct JoinNaturalNode {
548	pub left: Box<QueryPlan>,
549	pub right: Box<QueryPlan>,
550	pub join_type: JoinType,
551	pub alias: Option<Fragment>,
552}
553
554#[derive(Debug, Clone)]
555pub struct AppendQueryNode {
556	pub left: Box<QueryPlan>,
557	pub right: Box<QueryPlan>,
558}
559
560#[derive(Debug, Clone)]
561pub struct SortNode {
562	pub input: Box<QueryPlan>,
563	pub by: Vec<SortKey>,
564}
565
566#[derive(Debug, Clone)]
567pub struct MapNode {
568	pub input: Option<Box<QueryPlan>>,
569	pub map: Vec<Expression>,
570}
571
572#[derive(Debug, Clone)]
573pub struct ExtendNode {
574	pub input: Option<Box<QueryPlan>>,
575	pub extend: Vec<Expression>,
576}
577
578#[derive(Debug, Clone)]
579pub struct PatchNode {
580	pub input: Option<Box<QueryPlan>>,
581	pub assignments: Vec<Expression>,
582}
583
584#[derive(Debug, Clone)]
585pub struct ApplyNode {
586	pub input: Option<Box<QueryPlan>>,
587	pub operator: Fragment, // FIXME becomes OperatorIdentifier
588	pub expressions: Vec<Expression>,
589}
590
591#[derive(Debug, Clone)]
592pub struct InlineDataNode {
593	pub rows: Vec<Vec<AliasExpression>>,
594}
595
596#[derive(Debug, Clone)]
597pub struct IndexScanNode {
598	pub source: ResolvedTable,
599	pub index_name: String,
600}
601
602#[derive(Debug, Clone)]
603pub struct RemoteScanNode {
604	pub address: String,
605	pub remote_rql: String,
606	pub local_namespace: String,
607	pub remote_name: String,
608	pub variables: Vec<String>,
609}
610
611#[derive(Debug, Clone)]
612pub struct TableScanNode {
613	pub source: ResolvedTable,
614}
615
616#[derive(Debug, Clone)]
617pub struct ViewScanNode {
618	pub source: ResolvedView,
619}
620
621#[derive(Debug, Clone)]
622pub struct RingBufferScanNode {
623	pub source: ResolvedRingBuffer,
624}
625
626#[derive(Debug, Clone)]
627pub struct DictionaryScanNode {
628	pub source: ResolvedDictionary,
629}
630
631#[derive(Debug, Clone)]
632pub struct SeriesScanNode {
633	pub source: ResolvedSeries,
634	pub time_range_start: Option<i64>,
635	pub time_range_end: Option<i64>,
636	pub variant_tag: Option<u8>,
637}
638
639#[derive(Debug, Clone)]
640pub struct InsertSeriesNode {
641	pub input: Box<QueryPlan>,
642	pub target: ResolvedSeries,
643}
644
645#[derive(Debug, Clone)]
646pub struct DeleteSeriesNode {
647	pub input: Option<Box<QueryPlan>>,
648	pub target: ResolvedSeries,
649}
650
651#[derive(Debug, Clone)]
652pub struct GeneratorNode {
653	pub name: Fragment,
654	pub expressions: Vec<Expression>,
655}
656
657#[derive(Debug, Clone)]
658pub struct TableVirtualScanNode {
659	pub source: ResolvedTableVirtual,
660	pub pushdown_context: Option<TableVirtualPushdownContext>,
661}
662
663#[derive(Debug, Clone)]
664pub struct TableVirtualPushdownContext {
665	pub filters: Vec<Expression>,
666	pub projections: Vec<Expression>,
667	pub order_by: Vec<SortKey>,
668	pub limit: Option<usize>,
669}
670
671#[derive(Debug, Clone)]
672pub enum TakeLimit {
673	Literal(usize),
674	Variable(String),
675}
676
677impl fmt::Display for TakeLimit {
678	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
679		match self {
680			TakeLimit::Literal(n) => write!(f, "{}", n),
681			TakeLimit::Variable(name) => write!(f, "${}", name),
682		}
683	}
684}
685
686#[derive(Debug, Clone)]
687pub struct TakeNode {
688	pub input: Box<QueryPlan>,
689	pub take: TakeLimit,
690}
691
692#[derive(Debug, Clone)]
693pub struct WindowNode {
694	pub input: Option<Box<QueryPlan>>,
695	pub window_type: WindowType,
696	pub size: WindowSize,
697	pub slide: Option<WindowSlide>,
698	pub group_by: Vec<Expression>,
699	pub aggregations: Vec<Expression>,
700	pub min_events: usize,
701	pub max_window_count: Option<usize>,
702	pub max_window_age: Option<time::Duration>,
703}
704
705/// O(1) point lookup by row number: `filter rownum == N`
706#[derive(Debug, Clone)]
707pub struct RowPointLookupNode {
708	/// The source to look up in (table, ring buffer, etc.)
709	pub source: ResolvedPrimitive,
710	/// The row number to fetch
711	pub row_number: u64,
712}
713
714/// O(k) list lookup by row numbers: `filter rownum in [a, b, c]`
715#[derive(Debug, Clone)]
716pub struct RowListLookupNode {
717	/// The source to look up in
718	pub source: ResolvedPrimitive,
719	/// The row numbers to fetch
720	pub row_numbers: Vec<u64>,
721}
722
723/// Range scan by row numbers: `filter rownum between X and Y`
724#[derive(Debug, Clone)]
725pub struct RowRangeScanNode {
726	/// The source to scan
727	pub source: ResolvedPrimitive,
728	/// Start of the range (inclusive)
729	pub start: u64,
730	/// End of the range (inclusive)
731	pub end: u64,
732}
733
734/// APPEND statement physical plan node
735#[derive(Debug, Clone)]
736pub enum AppendPhysicalNode {
737	IntoVariable {
738		target: Fragment,
739		source: AppendPhysicalSource,
740	},
741	Query {
742		left: Box<QueryPlan>,
743		right: Box<QueryPlan>,
744	},
745}
746
747/// Source for an APPEND physical plan
748#[derive(Debug, Clone)]
749pub enum AppendPhysicalSource {
750	Statement(Vec<PhysicalPlan>),
751	Inline(InlineDataNode),
752}
753
754// --- Control flow and function nodes (owned, for PhysicalPlan enum) ---
755
756#[derive(Debug, Clone)]
757pub struct ConditionalNode {
758	pub condition: Expression,
759	pub then_branch: Box<PhysicalPlan>,
760	pub else_ifs: Vec<ElseIfBranch>,
761	pub else_branch: Option<Box<PhysicalPlan>>,
762}
763
764#[derive(Debug, Clone)]
765pub struct ElseIfBranch {
766	pub condition: Expression,
767	pub then_branch: Box<PhysicalPlan>,
768}
769
770#[derive(Debug, Clone)]
771pub struct LoopPhysicalNode {
772	pub body: Vec<PhysicalPlan>,
773}
774
775#[derive(Debug, Clone)]
776pub struct WhilePhysicalNode {
777	pub condition: Expression,
778	pub body: Vec<PhysicalPlan>,
779}
780
781#[derive(Debug, Clone)]
782pub struct ForPhysicalNode {
783	pub variable_name: Fragment,
784	pub iterable: Box<PhysicalPlan>,
785	pub body: Vec<PhysicalPlan>,
786}
787
788#[derive(Debug, Clone)]
789pub struct DefineFunctionNode {
790	pub name: Fragment,
791	pub parameters: Vec<FunctionParameter>,
792	pub return_type: Option<TypeConstraint>,
793	pub body: Vec<PhysicalPlan>,
794}
795
796#[derive(Debug, Clone)]
797pub struct ReturnNode {
798	pub value: Option<Expression>,
799}
800
801#[derive(Debug, Clone)]
802pub struct CallFunctionNode {
803	pub name: Fragment,
804	pub arguments: Vec<Expression>,
805	pub is_procedure_call: bool,
806}
807
808// === Drop nodes ===
809
810#[derive(Debug, Clone)]
811pub struct DropNamespaceNode {
812	pub namespace_name: Fragment,
813	pub namespace_id: Option<NamespaceId>,
814	pub if_exists: bool,
815	pub cascade: bool,
816}
817
818#[derive(Debug, Clone)]
819pub struct DropTableNode {
820	pub namespace_name: Fragment,
821	pub table_name: Fragment,
822	pub table_id: Option<TableId>,
823	pub if_exists: bool,
824	pub cascade: bool,
825}
826
827#[derive(Debug, Clone)]
828pub struct DropViewNode {
829	pub namespace_name: Fragment,
830	pub view_name: Fragment,
831	pub view_id: Option<ViewId>,
832	pub if_exists: bool,
833	pub cascade: bool,
834}
835
836#[derive(Debug, Clone)]
837pub struct DropRingBufferNode {
838	pub namespace_name: Fragment,
839	pub ringbuffer_name: Fragment,
840	pub ringbuffer_id: Option<RingBufferId>,
841	pub if_exists: bool,
842	pub cascade: bool,
843}
844
845#[derive(Debug, Clone)]
846pub struct DropDictionaryNode {
847	pub namespace_name: Fragment,
848	pub dictionary_name: Fragment,
849	pub dictionary_id: Option<DictionaryId>,
850	pub if_exists: bool,
851	pub cascade: bool,
852}
853
854#[derive(Debug, Clone)]
855pub struct DropSumTypeNode {
856	pub namespace_name: Fragment,
857	pub sumtype_name: Fragment,
858	pub sumtype_id: Option<SumTypeId>,
859	pub if_exists: bool,
860	pub cascade: bool,
861}
862
863#[derive(Debug, Clone)]
864pub struct DropSubscriptionNode {
865	pub subscription_name: Fragment,
866	pub if_exists: bool,
867	pub cascade: bool,
868}
869
870#[derive(Debug, Clone)]
871pub struct DropSeriesNode {
872	pub namespace_name: Fragment,
873	pub series_name: Fragment,
874	pub series_id: Option<SeriesId>,
875	pub if_exists: bool,
876	pub cascade: bool,
877}
878
879// === Auth/Permissions physical plan nodes ===
880
881#[derive(Debug, Clone)]
882pub struct CreateUserNode {
883	pub name: Fragment,
884}
885
886#[derive(Debug, Clone)]
887pub struct CreateRoleNode {
888	pub name: Fragment,
889}
890
891#[derive(Debug, Clone)]
892pub struct GrantNode {
893	pub role: Fragment,
894	pub user: Fragment,
895}
896
897#[derive(Debug, Clone)]
898pub struct RevokeNode {
899	pub role: Fragment,
900	pub user: Fragment,
901}
902
903#[derive(Debug, Clone)]
904pub struct DropUserNode {
905	pub name: Fragment,
906	pub if_exists: bool,
907}
908
909#[derive(Debug, Clone)]
910pub struct DropRoleNode {
911	pub name: Fragment,
912	pub if_exists: bool,
913}
914
915#[derive(Debug, Clone)]
916pub struct CreateAuthenticationNode {
917	pub user: Fragment,
918	pub method: Fragment,
919	pub config: collections::HashMap<String, String>,
920}
921
922#[derive(Debug, Clone)]
923pub struct DropAuthenticationNode {
924	pub user: Fragment,
925	pub method: Fragment,
926	pub if_exists: bool,
927}
928
929#[derive(Debug, Clone)]
930pub struct CreatePolicyNode {
931	pub name: Option<Fragment>,
932	pub target_type: String,
933	pub scope_namespace: Option<Fragment>,
934	pub scope_object: Option<Fragment>,
935	pub operations: Vec<PolicyOperationNode>,
936}
937
938#[derive(Debug, Clone)]
939pub struct PolicyOperationNode {
940	pub operation: String,
941	pub body_source: String,
942}
943
944#[derive(Debug, Clone)]
945pub struct AlterPolicyNode {
946	pub target_type: String,
947	pub name: Fragment,
948	pub enable: bool,
949}
950
951#[derive(Debug, Clone)]
952pub struct DropPolicyNode {
953	pub target_type: String,
954	pub name: Fragment,
955	pub if_exists: bool,
956}