1mod aggregate;
4mod alter;
5mod alter_column;
6mod alter_constraint;
7mod alter_function;
8mod alter_inherit;
9mod alter_type;
10mod attmap;
11mod cluster;
12mod comment;
13mod copy;
14mod create_table_as;
15mod domain;
16mod drop;
17mod drop_constraint;
18mod drop_function;
19mod enum_label;
20mod enumtype;
21mod fkey;
22mod fkey_partition;
23mod function;
24mod generated;
25mod grant;
26mod identity;
27mod index;
28mod lock;
29mod matview;
30mod opclass;
31mod operator;
32mod owned;
33mod partition;
34mod partition_alter;
35mod partition_attach;
36mod partition_index;
37mod rangetype;
38mod reindex;
39mod relation_options;
40mod reloptions;
41mod rename;
42mod row_type;
43mod rule;
44mod schema_elements;
45mod sequence;
46mod set_schema;
47mod statistics;
48mod table;
49#[cfg(test)]
50mod tests;
51mod trigger;
52mod truncate;
53mod tsearch;
54mod type_change;
55mod using_index;
56mod vacuum;
57mod validate;
58mod view;
59
60pub use copy::{
61 CopyRelation, CopyTarget, attnums, copy_filter, copy_insert, copy_relation, copy_target,
62 rewrite_insert,
63};
64pub(crate) use drop::quote_identifier;
65pub use function::{
66 Body, REGRESS_SYMBOLS, Script, SqlBody, find_library, function_body, function_script,
67 function_sqlbody,
68};
69pub use grant::acl_roles;
70pub(crate) use lock::relkind;
71pub use lock::{Allowed, Take, lock_table};
72pub use owned::{acl_dependents, owned_objects};
73pub use reindex::{ReindexOptions, reindex_options};
74pub use reloptions::tablespace_options;
75pub use rule::{RuleTree, rule_tree};
76pub use schema_elements::SchemaElements;
77pub(crate) use table::check_attribute_type;
78pub use trigger::trigger_when;
79pub use vacuum::vacuum_relations;
80
81use std::cell::RefCell;
82
83use rupg_catalog::{Catalog, ObjRef, PG_CLASS, PG_NAMESPACE, PG_PROC, PG_TYPE, RelKind};
84use rupg_common::{Error, Result, SqlState};
85use rupg_pgcatalog::builtin::{self, ClassRow, Named, NamedRow, Owned};
86use rupg_sql::nodes::{CreateSchemaStmt, GrantTargetType, Node, ObjectType, RangeVar};
87use rupg_types::{Value, oid};
88
89use crate::coerce::AtOpt;
90use crate::expr::{Expr, ExprKind};
91use crate::typename::place;
92use crate::{Analyzer, Env, PG_CATALOG_NAMESPACE, Params};
93
94const PG_TOAST_NAMESPACE: u32 = 99;
96
97const BOOTSTRAP_SUPERUSER: u32 = 10;
99
100#[derive(Clone, Debug, PartialEq)]
102pub enum Message {
103 Warning(Error),
105 Notice(Error),
107}
108
109#[derive(Debug)]
111pub struct Defined {
112 pub messages: Vec<Message>,
114 pub result: Result<()>,
116 pub scans: Vec<u32>,
118 pub truncated: Vec<u32>,
120 pub transforms: Vec<Transform>,
122 pub fills: Vec<Fill>,
124 pub filled: Option<u32>,
126 pub views: Vec<u32>,
128 pub checks: Vec<PartitionCheck>,
130 pub detached: Vec<u32>,
132 pub grantors: Vec<u32>,
134 pub schema: Option<SchemaElements>,
136 pub top_level: Option<TopLevel>,
138 pub concurrent: Option<u32>,
140}
141
142#[derive(Clone, Debug, PartialEq, Eq)]
144pub struct TopLevel {
145 pub statement: &'static str,
147 pub context: String,
149}
150
151#[derive(Clone, Copy, Debug, PartialEq, Eq)]
153pub struct PartitionCheck {
154 pub table: u32,
156 pub default: bool,
158}
159
160#[derive(Clone, Debug)]
162pub struct Transform {
163 pub table: u32,
165 pub column: String,
167 pub expr: Node,
169}
170
171#[derive(Clone, Debug)]
173pub struct Fill {
174 pub table: u32,
176 pub column: i16,
178 pub default: Option<String>,
180}
181
182pub fn is_definition(stmt: &Node) -> bool {
184 matches!(
185 stmt,
186 Node::CreateStmt(_)
187 | Node::CreateTableAsStmt(_)
188 | Node::IndexStmt(_)
189 | Node::CreateSchemaStmt(_)
190 | Node::CreateDomainStmt(_)
191 | Node::CompositeTypeStmt(_)
192 | Node::CreateEnumStmt(_)
193 | Node::AlterEnumStmt(_)
194 | Node::CreateRangeStmt(_)
195 | Node::ViewStmt(_)
196 | Node::RuleStmt(_)
197 | Node::CreateTrigStmt(_)
198 | Node::CreateFunctionStmt(_)
199 | Node::AlterFunctionStmt(_)
200 | Node::CommentStmt(_)
201 | Node::CreateSeqStmt(_)
202 | Node::AlterSeqStmt(_)
203 | Node::AlterTSConfigurationStmt(_)
204 | Node::AlterOperatorStmt(_)
205 | Node::CreateOpFamilyStmt(_)
206 | Node::CreateOpClassStmt(_)
207 | Node::AlterOpFamilyStmt(_)
208 | Node::TruncateStmt(_)
209 | Node::RefreshMatViewStmt(_)
210 | Node::CreateStatsStmt(_)
211 | Node::AlterStatsStmt(_)
212 | Node::DropOwnedStmt(_)
213 | Node::ReassignOwnedStmt(_)
214 | Node::AlterDefaultPrivilegesStmt(_)
215 ) || matches!(stmt, Node::DefineStmt(define) if tsearch::is_ts_kind(define.kind) || matches!(define.kind, ObjectType::OBJECT_OPERATOR | ObjectType::OBJECT_AGGREGATE))
216 || matches!(stmt, Node::DropStmt(drop) if is_drop_kind(drop.removeType))
217 || matches!(stmt, Node::AlterTableStmt(alter) if is_alter_kind(alter.objtype) || alter.objtype == ObjectType::OBJECT_TYPE)
218 || matches!(stmt, Node::AlterOwnerStmt(alter) if matches!(alter.objectType, ObjectType::OBJECT_SCHEMA | ObjectType::OBJECT_OPERATOR | ObjectType::OBJECT_STATISTIC_EXT) || is_opcf_kind(alter.objectType) || type_change::is_type_kind(alter.objectType) || alter_function::is_function_kind(alter.objectType))
219 || matches!(stmt, Node::RenameStmt(rename) if rename::is_rename_kind(rename.renameType))
220 || matches!(stmt, Node::AlterObjectSchemaStmt(alter) if is_alter_kind(alter.objectType) || matches!(alter.objectType, ObjectType::OBJECT_OPERATOR | ObjectType::OBJECT_STATISTIC_EXT) || is_opcf_kind(alter.objectType) || type_change::is_type_kind(alter.objectType) || alter_function::is_function_kind(alter.objectType))
221 || matches!(stmt, Node::SelectStmt(select) if select.intoClause.is_some())
222 || matches!(stmt, Node::GrantStmt(grant) if grant.targtype != GrantTargetType::ACL_TARGET_DEFAULTS)
223}
224
225fn is_opcf_kind(kind: ObjectType) -> bool {
227 matches!(kind, ObjectType::OBJECT_OPCLASS | ObjectType::OBJECT_OPFAMILY)
228}
229
230fn is_alter_kind(kind: ObjectType) -> bool {
232 matches!(
233 kind,
234 ObjectType::OBJECT_TABLE
235 | ObjectType::OBJECT_VIEW
236 | ObjectType::OBJECT_INDEX
237 | ObjectType::OBJECT_SEQUENCE
238 | ObjectType::OBJECT_MATVIEW
239 | ObjectType::OBJECT_FOREIGN_TABLE
240 )
241}
242
243fn is_drop_kind(kind: ObjectType) -> bool {
245 matches!(
246 kind,
247 ObjectType::OBJECT_TABLE
248 | ObjectType::OBJECT_VIEW
249 | ObjectType::OBJECT_MATVIEW
250 | ObjectType::OBJECT_INDEX
251 | ObjectType::OBJECT_SEQUENCE
252 | ObjectType::OBJECT_SCHEMA
253 | ObjectType::OBJECT_TYPE
254 | ObjectType::OBJECT_DOMAIN
255 | ObjectType::OBJECT_RULE
256 | ObjectType::OBJECT_TRIGGER
257 | ObjectType::OBJECT_FUNCTION
258 | ObjectType::OBJECT_PROCEDURE
259 | ObjectType::OBJECT_ROUTINE
260 | ObjectType::OBJECT_AGGREGATE
261 | ObjectType::OBJECT_TSPARSER
262 | ObjectType::OBJECT_TSDICTIONARY
263 | ObjectType::OBJECT_TSTEMPLATE
264 | ObjectType::OBJECT_TSCONFIGURATION
265 | ObjectType::OBJECT_OPERATOR
266 | ObjectType::OBJECT_OPCLASS
267 | ObjectType::OBJECT_OPFAMILY
268 | ObjectType::OBJECT_STATISTIC_EXT
269 )
270}
271
272pub fn define(stmt: &Node, text: &str, env: &dyn Env, catalog: &mut Catalog, user: u32) -> Defined {
274 let schemas = SchemaEnv::new(env, catalog);
275 let mut definer = Definer {
276 an: Analyzer::new(&schemas, &Params::default()),
277 catalog,
278 user,
279 messages: Vec::new(),
280 scans: Vec::new(),
281 truncated: Vec::new(),
282 transforms: Vec::new(),
283 added: Vec::new(),
284 views: Vec::new(),
285 checks: Vec::new(),
286 detached: Vec::new(),
287 grantors: Vec::new(),
288 schema: None,
289 top_level: None,
290 concurrent: None,
291 };
292 let mut filled = None;
293 let result = match stmt {
294 Node::CreateStmt(create) => definer.create_table(create),
295 Node::CreateTableAsStmt(ctas) => {
296 definer.create_table_as(ctas, text).map(|table| filled = table)
297 }
298 Node::SelectStmt(select) => definer.select_into(select).map(|table| filled = table),
299 Node::RefreshMatViewStmt(stmt) => {
300 definer.refresh_matview(stmt).map(|matview| filled = matview)
301 }
302 Node::IndexStmt(index) => definer.create_index(index, false),
303 Node::CreateSchemaStmt(schema) => definer.create_schema(schema, text),
304 Node::ViewStmt(view) => definer.create_view(view, text),
305 Node::RuleStmt(rule) => definer.create_rule(rule, text),
306 Node::CreateTrigStmt(trigger) => definer.create_trigger(trigger),
307 Node::CreateDomainStmt(domain) => definer.create_domain(domain).map(|_| ()),
308 Node::CompositeTypeStmt(stmt) => definer.create_composite_type(stmt),
309 Node::CreateEnumStmt(stmt) => definer.create_enum(stmt),
310 Node::AlterEnumStmt(stmt) => definer.alter_enum(stmt),
311 Node::CreateRangeStmt(stmt) => definer.create_range(stmt),
312 Node::CreateFunctionStmt(function) => definer.create_function(function, text).map(|_| ()),
313 Node::AlterFunctionStmt(alter) => definer.alter_function(alter),
314 Node::CommentStmt(comment) => definer.comment(comment),
315 Node::DropStmt(drop) => definer.drop(drop),
316 Node::AlterTableStmt(alter) => definer.alter_table(alter),
317 Node::AlterOwnerStmt(alter) => definer.alter_owner(alter),
318 Node::RenameStmt(rename) => definer.rename(rename),
319 Node::AlterObjectSchemaStmt(alter) if type_change::is_type_kind(alter.objectType) => {
320 definer.alter_type_schema(alter)
321 }
322 Node::AlterObjectSchemaStmt(alter)
323 if alter_function::is_function_kind(alter.objectType) =>
324 {
325 definer.alter_function_schema(alter)
326 }
327 Node::AlterObjectSchemaStmt(alter) if alter.objectType == ObjectType::OBJECT_OPERATOR => {
328 definer.alter_operator_schema(alter)
329 }
330 Node::AlterObjectSchemaStmt(alter) if is_opcf_kind(alter.objectType) => {
331 definer.alter_opcf_schema(alter)
332 }
333 Node::AlterObjectSchemaStmt(alter)
334 if alter.objectType == ObjectType::OBJECT_STATISTIC_EXT =>
335 {
336 definer.alter_stats_schema(alter)
337 }
338 Node::AlterObjectSchemaStmt(alter) => definer.alter_relation_schema(alter),
339 Node::CreateStatsStmt(create) => definer.create_statistics(create),
340 Node::AlterStatsStmt(alter) => definer.alter_statistics(alter),
341 Node::CreateSeqStmt(create) => definer.create_sequence(create),
342 Node::AlterSeqStmt(alter) => definer.alter_sequence(alter),
343 Node::DefineStmt(define) if tsearch::is_ts_kind(define.kind) => definer.define_ts(define),
344 Node::DefineStmt(define) if define.kind == ObjectType::OBJECT_OPERATOR => {
345 definer.create_operator(define)
346 }
347 Node::DefineStmt(define) if define.kind == ObjectType::OBJECT_AGGREGATE => {
348 definer.create_aggregate(define)
349 }
350 Node::AlterOperatorStmt(alter) => definer.alter_operator(alter),
351 Node::CreateOpFamilyStmt(create) => definer.create_opfamily(create),
352 Node::CreateOpClassStmt(create) => definer.create_opclass(create),
353 Node::AlterOpFamilyStmt(alter) => definer.alter_opfamily(alter),
354 Node::AlterTSConfigurationStmt(alter) => definer.alter_ts_config(alter),
355 Node::TruncateStmt(truncate) => definer.truncate(truncate),
356 Node::GrantStmt(grant) => definer.grant(grant),
357 Node::DropOwnedStmt(stmt) => definer.drop_owned(stmt),
358 Node::AlterDefaultPrivilegesStmt(stmt) => definer.alter_default_privileges(stmt),
359 Node::ReassignOwnedStmt(stmt) => definer.reassign_owned(stmt),
360 Node::ReindexStmt(stmt) => definer.reindex(stmt),
361 _ => {
362 Err(Error::new(SqlState::FEATURE_NOT_SUPPORTED, "this statement is not supported yet"))
363 }
364 };
365 definer.flush();
366 let catalog = &*definer.catalog;
367 let fills = definer
368 .added
369 .into_iter()
370 .filter(|fill| {
371 let column = catalog.relation(fill.table).and_then(|r| r.column(fill.column));
372 let live = column.is_some_and(|c| !c.dropped);
373 let generated = column.is_some_and(|c| c.generated != 0);
375 let now = catalog.default_of(fill.table, fill.column).map(|d| &d.expr);
376 live && (generated || now != fill.default.as_ref())
377 })
378 .collect();
379 Defined {
380 messages: definer.messages,
381 result,
382 scans: definer.scans,
383 truncated: definer.truncated,
384 transforms: definer.transforms,
385 fills,
386 filled,
387 views: definer.views,
388 checks: definer.checks,
389 detached: definer.detached,
390 grantors: definer.grantors,
391 schema: definer.schema,
392 top_level: definer.top_level,
393 concurrent: definer.concurrent,
394 }
395}
396
397struct SchemaEnv<'e> {
399 env: &'e dyn Env,
400 schemas: Vec<(String, u32)>,
401 relations: RefCell<Vec<(u32, String, u32)>>,
403}
404
405impl<'e> SchemaEnv<'e> {
406 fn new(env: &'e dyn Env, catalog: &Catalog) -> SchemaEnv<'e> {
407 let schemas = catalog.schemas().map(|s| (s.name.clone(), s.oid)).collect();
408 SchemaEnv { env, schemas, relations: RefCell::new(Vec::new()) }
409 }
410
411 fn new_class(&self, text: &str) -> Option<u32> {
413 let names = rupg_types::qualified_name_list(text).ok()?;
414 let (spaces, name) = match names.as_slice() {
415 [name] => (self.search_path(), name),
416 [schema, name] => (vec![self.namespace(schema)?], name),
417 _ => return None,
418 };
419 let relations = self.relations.borrow();
420 spaces.iter().find_map(|&ns| {
421 relations.iter().find(|(n, rel, _)| *n == ns && rel == name).map(|(_, _, oid)| *oid)
422 })
423 }
424}
425
426impl Env for SchemaEnv<'_> {
427 fn search_path(&self) -> Vec<u32> {
428 self.env.search_path()
429 }
430
431 fn namespace(&self, name: &str) -> Option<u32> {
432 self.schemas
433 .iter()
434 .find(|(n, _)| n == name)
435 .map(|(_, oid)| *oid)
436 .or_else(|| self.env.namespace(name))
437 }
438
439 fn database(&self) -> String {
440 self.env.database()
441 }
442
443 fn input(&self, ty: u32, text: &str, typmod: i32) -> Result<Value> {
444 match self.env.input(ty, text, typmod) {
445 Err(error) if ty == oid::REGCLASS && error.state() == SqlState::UNDEFINED_TABLE => {
446 self.new_class(text).map(Value::Oid).ok_or(error)
447 }
448 value => value,
449 }
450 }
451
452 fn catalog(&self) -> Option<&Catalog> {
453 self.env.catalog()
454 }
455
456 fn allow_system_table_mods(&self) -> bool {
457 self.env.allow_system_table_mods()
458 }
459
460 fn check_function_bodies(&self) -> bool {
461 self.env.check_function_bodies()
462 }
463
464 fn sequence_row(&self, file: u32) -> Option<rupg_catalog::SequenceData> {
465 self.env.sequence_row(file)
466 }
467
468 fn deparse(&self, text: &str, relation: u32) -> Result<String> {
469 self.env.deparse(text, relation)
470 }
471
472 fn temp_number(&self) -> Option<i32> {
473 self.env.temp_number()
474 }
475
476 fn check_function(&self, function: &rupg_catalog::Function) -> Result<()> {
477 self.env.check_function(function)
478 }
479
480 fn function_setting(
481 &self,
482 set: &rupg_sql::nodes::VariableSetStmt,
483 ) -> Result<(String, Option<String>)> {
484 self.env.function_setting(set)
485 }
486
487 fn push_function_settings(&self, config: &[String]) -> Result<()> {
488 self.env.push_function_settings(config)
489 }
490
491 fn pop_function_settings(&self) {
492 self.env.pop_function_settings();
493 }
494
495 fn command_counter_increment(&self, catalog: &Catalog) {
496 let session = self.env.catalog();
497 *self.relations.borrow_mut() = catalog
498 .relations()
499 .filter(|r| session.is_none_or(|s| s.relation(r.oid).is_none()))
500 .map(|r| (r.namespace, r.name.clone(), r.oid))
501 .collect();
502 self.env.command_counter_increment(catalog);
503 }
504
505 fn evaluate(&self, expr: &Expr) -> Result<Value> {
506 self.env.evaluate(expr)
507 }
508
509 fn compare(&self, ty: u32, a: &Value, b: &Value) -> Result<std::cmp::Ordering> {
510 self.env.compare(ty, a, b)
511 }
512
513 fn deparse_constant(&self, text: &str) -> Result<String> {
514 self.env.deparse_constant(text)
515 }
516}
517
518#[derive(Clone, Copy, Debug)]
520enum Found {
521 User(u32),
523 Builtin(&'static ClassRow),
525}
526
527pub(crate) struct Definer<'a, 'c> {
529 an: Analyzer<'a>,
530 catalog: &'c mut Catalog,
531 user: u32,
532 messages: Vec<Message>,
533 scans: Vec<u32>,
535 truncated: Vec<u32>,
537 transforms: Vec<Transform>,
539 added: Vec<Fill>,
541 views: Vec<u32>,
543 checks: Vec<PartitionCheck>,
545 detached: Vec<u32>,
547 grantors: Vec<u32>,
549 schema: Option<SchemaElements>,
551 top_level: Option<TopLevel>,
553 concurrent: Option<u32>,
555}
556
557fn not_yet(what: &str, at: Option<usize>) -> Error {
559 Error::new(SqlState::FEATURE_NOT_SUPPORTED, format!("{what} is not supported yet")).at_opt(at)
560}
561
562fn qualified(rv: &RangeVar) -> String {
564 [rv.catalogname.as_deref(), rv.schemaname.as_deref(), rv.relname.as_deref()]
565 .into_iter()
566 .flatten()
567 .collect::<Vec<_>>()
568 .join(".")
569}
570
571fn relname(rv: &RangeVar) -> &str {
573 rv.relname.as_deref().unwrap_or_default()
574}
575
576fn is_mutable(catalog: &Catalog, expr: &Expr) -> bool {
578 let mutable_proc = |oid: u32| match builtin::proc_by_oid(oid) {
579 Some(proc) => proc.volatile != b'i',
580 None => catalog.function(oid).is_none_or(|f| f.volatile != b'i'),
581 };
582 expr.find(0, &mut |e, _| {
583 let mutable = match &e.kind {
584 ExprKind::Func(f) if matches!(f.oid, 2003 | 2004) => {
586 let other = f.args.get(if f.oid == 2003 { 1 } else { 0 });
587 other.is_none_or(|arg| {
588 mutable_proc(builtin::type_by_oid(arg.ty).map_or(0, |t| t.output))
589 })
590 }
591 ExprKind::Func(f) => mutable_proc(f.oid),
592 ExprKind::NullIf { equal, .. } | ExprKind::Distinct { equal, .. } => {
593 mutable_proc(*equal)
594 }
595 ExprKind::ScalarArrayOp { func, .. } => mutable_proc(*func),
596 ExprKind::CoerceViaIo(arg, _) => {
597 let output = builtin::type_by_oid(arg.ty).map_or(0, |t| t.output);
598 let input = builtin::type_by_oid(e.ty).map_or(0, |t| t.input);
599 mutable_proc(output) || mutable_proc(input)
600 }
601 ExprKind::SqlValue(_) => true,
602 _ => false,
603 };
604 mutable.then_some(())
605 })
606 .is_some()
607}
608
609fn column_numbers(exprs: &[&Expr]) -> Vec<i16> {
611 let mut numbers = Vec::new();
612 for expr in exprs {
613 expr.find(0, &mut |e, depth| {
614 if let ExprKind::Var(var) = e.kind
615 && var.levels_up == depth
616 && !numbers.contains(&var.attnum)
617 {
618 numbers.push(var.attnum);
619 }
620 None::<()>
621 });
622 }
623 numbers
624}
625
626fn references(catalog: &Catalog, relation: u32, exprs: &[&Expr]) -> Result<Vec<ObjRef>> {
628 shared_constants(exprs)?;
629 let mut refs: Vec<ObjRef> =
630 column_numbers(exprs).into_iter().map(|n| ObjRef::column(relation, n)).collect();
631 for expr in exprs {
632 expr.find(0, &mut |e, _| {
633 let found = match (&e.kind, e.ty) {
634 (ExprKind::Const(Value::Oid(oid)), oid::REGCLASS) => {
635 Some(ObjRef::new(PG_CLASS, *oid))
636 }
637 (ExprKind::Const(Value::Oid(oid)), oid::REGTYPE) => {
638 Some(ObjRef::new(PG_TYPE, *oid))
639 }
640 (ExprKind::Func(f), _) => Some(ObjRef::new(PG_PROC, f.oid)),
641 _ => field_reference(catalog, e),
642 };
643 for found in found.into_iter().chain(type_reference(e)) {
644 if !refs.contains(&found) {
645 refs.push(found);
646 }
647 }
648 None::<()>
649 });
650 }
651 Ok(refs)
652}
653
654fn shared_constants(exprs: &[&Expr]) -> Result<()> {
656 for expr in exprs {
657 let shared = expr.find(0, &mut |e, _| match (&e.kind, e.ty) {
658 (ExprKind::Const(Value::Oid(_)), oid::REGROLE) => Some("regrole"),
659 (ExprKind::Const(Value::Oid(_)), oid::REGDATABASE) => Some("regdatabase"),
660 _ => None,
661 });
662 if let Some(name) = shared {
663 return Err(Error::new(
664 SqlState::FEATURE_NOT_SUPPORTED,
665 format!("constant of the type {name} cannot be used here"),
666 ));
667 }
668 }
669 Ok(())
670}
671
672pub(crate) fn field_reference(catalog: &Catalog, expr: &Expr) -> Option<ObjRef> {
674 let ExprKind::FieldSelect(arg, field) = &expr.kind else { return None };
675 let rel = row_type::row_relation(catalog, arg.ty)?;
676 let (at, _) = rel.columns.iter().enumerate().filter(|(_, c)| !c.dropped).nth(*field)?;
677 Some(ObjRef::column(rel.oid, i16::try_from(at + 1).ok()?))
678}
679
680pub(crate) fn type_reference(expr: &Expr) -> Option<ObjRef> {
682 match expr.kind {
683 ExprKind::Const(_)
684 | ExprKind::Relabel(..)
685 | ExprKind::CoerceViaIo(..)
686 | ExprKind::ArrayCoerce { .. }
687 | ExprKind::CoerceToDomain(..)
688 | ExprKind::SubColumn(_) => Some(ObjRef::new(PG_TYPE, expr.ty)),
689 _ => None,
690 }
691}
692
693impl Definer<'_, '_> {
694 fn flush(&mut self) {
696 self.messages.extend(self.an.notices.drain(..).map(|error| {
697 if error.is_notice() { Message::Notice(error) } else { Message::Warning(error) }
698 }));
699 }
700
701 fn notice(&mut self, state: SqlState, message: String) {
703 self.flush();
704 self.messages.push(Message::Notice(Error::new(state, message)));
705 }
706
707 fn warning(&mut self, state: SqlState, message: String) {
709 self.flush();
710 self.messages.push(Message::Warning(Error::new(state, message)));
711 }
712
713 fn is_superuser(&self) -> bool {
715 rupg_pgcatalog::roles::current().superuser(self.user)
716 }
717
718 fn builtin_schema(&self, name: &str) -> Option<&'static NamedRow> {
720 builtin::named(Named::Namespace)
721 .iter()
722 .find(|n| n.name == name && !self.catalog.schema_dropped(n.oid))
723 }
724
725 fn schema_owner(&self, namespace: u32) -> Option<u32> {
727 match self.catalog.schema(namespace) {
728 Some(schema) => Some(schema.owner),
729 None if self.catalog.schema_dropped(namespace) => None,
730 None => builtin::owned_by_oid(Owned::Namespace, namespace).map(|n| n.owner),
731 }
732 }
733
734 fn schema_name(&self, namespace: u32) -> String {
736 match self.catalog.schema(namespace) {
737 Some(schema) => schema.name.clone(),
738 None => builtin::named_by_oid(Named::Namespace, namespace)
739 .filter(|n| !self.catalog.schema_dropped(n.oid))
740 .map_or_else(String::new, |n| n.name.to_string()),
741 }
742 }
743
744 fn may_create_in(&self, namespace: u32, role: u32) -> bool {
746 rupg_pgcatalog::roles::current().superuser(role)
747 || self.schema_owner(namespace) == Some(role)
748 || self.schema_create_allowed(namespace, role)
749 }
750
751 fn check_create_in(&self, namespace: u32, at: Option<usize>) -> Result<()> {
753 if self.may_create_in(namespace, self.user) {
754 return Ok(());
755 }
756 Err(Error::new(
757 SqlState::INSUFFICIENT_PRIVILEGE,
758 format!("permission denied for schema {}", self.schema_name(namespace)),
759 )
760 .at_opt(at))
761 }
762
763 fn creation_namespace(&mut self, rv: &RangeVar) -> Result<u32> {
765 let at = place(rv.location);
766 if let Some(catalog) = rv.catalogname.as_deref()
767 && catalog != self.an.env.database()
768 {
769 return Err(Error::new(
770 SqlState::FEATURE_NOT_SUPPORTED,
771 format!("cross-database references are not implemented: \"{}\"", qualified(rv)),
772 )
773 .at_opt(at));
774 }
775 let unlogged = rv.relpersistence == b'u';
776 let only_temporary = || {
778 Error::new(
779 SqlState::INVALID_TABLE_DEFINITION,
780 "only temporary relations may be created in temporary schemas",
781 )
782 .at_opt(at)
783 };
784 let temp = rv.relpersistence == b't';
785 if rv.schemaname.as_deref() == Some("pg_temp") || (temp && rv.schemaname.is_none()) {
786 let namespace = self.temp_namespace()?;
787 return if unlogged { Err(only_temporary()) } else { Ok(namespace) };
788 }
789 let namespace = match rv.schemaname.as_deref() {
790 Some(schema) => self.an.schema(schema).ok_or_else(|| {
791 Error::new(
792 SqlState::UNDEFINED_SCHEMA,
793 format!("schema \"{schema}\" does not exist"),
794 )
795 .at_opt(at)
796 })?,
797 None => self.an.env.search_path().first().copied().ok_or_else(|| {
798 Error::new(SqlState::UNDEFINED_SCHEMA, "no schema has been selected to create in")
799 .at_opt(at)
800 })?,
801 };
802 if unlogged && self.catalog.is_temp_schema(namespace) {
803 return Err(only_temporary());
804 }
805 let own = namespace == self.an.temp || self.is_own_toast_temp(namespace);
806 if !own && self.catalog.is_temp_schema(namespace) {
807 return Err(Error::new(
808 SqlState::INVALID_TABLE_DEFINITION,
809 "cannot create relations in temporary schemas of other sessions",
810 ));
811 }
812 if temp && !own {
813 return Err(Error::new(
814 SqlState::INVALID_TABLE_DEFINITION,
815 "cannot create temporary relation in non-temporary schema",
816 )
817 .at_opt(at));
818 }
819 if !own {
820 self.check_create_in(namespace, at)?;
821 }
822 Ok(namespace)
823 }
824
825 fn is_own_toast_temp(&self, namespace: u32) -> bool {
827 let Some(n) = self.an.env.temp_number() else { return false };
828 self.catalog.schema(namespace).is_some_and(|s| s.name == format!("pg_toast_temp_{n}"))
829 }
830
831 fn temp_namespace(&mut self) -> Result<u32> {
833 if self.an.temp != 0 {
834 return Ok(self.an.temp);
835 }
836 let Some(n) = self.an.env.temp_number() else {
837 return Err(not_yet("a temporary object in a session with no server", None));
838 };
839 let name = format!("pg_temp_{n}");
840 let oid = match self.catalog.schema_by_name(&name) {
841 Some(schema) => schema.oid,
842 None => {
843 let oid = self.catalog.create_schema(&name, BOOTSTRAP_SUPERUSER)?;
844 self.catalog.create_schema(&format!("pg_toast_temp_{n}"), BOOTSTRAP_SUPERUSER)?;
845 oid
846 }
847 };
848 self.an.temp = oid;
849 self.an.path.retain(|&ns| ns != oid);
850 if !self.an.temp_path.contains(&oid) {
851 self.an.temp_path.insert(0, oid);
852 }
853 Ok(oid)
854 }
855
856 fn relation_in(&self, namespace: u32, name: &str) -> Option<Found> {
858 if let Some(rel) = self.catalog.relation_by_name(namespace, name) {
859 return Some(Found::User(rel.oid));
860 }
861 builtin::named(Named::Class)
862 .iter()
863 .find(|r| r.namespace == namespace && r.name == name)
864 .and_then(|r| builtin::class_by_oid(r.oid))
865 .map(Found::Builtin)
866 }
867
868 fn lookup_relation(&self, rv: &RangeVar) -> Result<Found> {
870 let name = relname(rv);
871 if let Some(catalog) = rv.catalogname.as_deref()
872 && catalog != self.an.env.database()
873 {
874 return Err(Error::new(
875 SqlState::FEATURE_NOT_SUPPORTED,
876 format!("cross-database references are not implemented: \"{}\"", qualified(rv)),
877 ));
878 }
879 let found = match rv.schemaname.as_deref() {
880 Some(schema) => {
881 let namespace = self.an.schema(schema).ok_or_else(|| {
882 Error::new(
883 SqlState::UNDEFINED_SCHEMA,
884 format!("schema \"{schema}\" does not exist"),
885 )
886 })?;
887 self.relation_in(namespace, name)
888 }
889 None => self.an.temp_path.iter().find_map(|&ns| self.relation_in(ns, name)),
890 };
891 found.ok_or_else(|| {
892 let shown = match rv.schemaname.as_deref() {
893 Some(schema) => format!("{schema}.{name}"),
894 None => name.to_string(),
895 };
896 Error::new(SqlState::UNDEFINED_TABLE, format!("relation \"{shown}\" does not exist"))
897 })
898 }
899
900 fn is_system(&self, found: Found) -> bool {
902 match found {
903 Found::Builtin(row) => row.oid < rupg_catalog::FIRST_UNPINNED_OID,
904 Found::User(oid) => {
905 self.catalog.relation(oid).is_some_and(|r| r.namespace == PG_TOAST_NAMESPACE)
906 }
907 }
908 }
909
910 fn kind_of(&self, found: Found) -> u8 {
912 match found {
913 Found::User(oid) => match self.catalog.relation(oid).map(|r| r.kind) {
914 Some(RelKind::Index) => b'i',
915 Some(RelKind::Sequence) => b'S',
916 Some(RelKind::Toast) => b't',
917 Some(RelKind::View) => b'v',
918 Some(RelKind::Composite) => b'c',
919 Some(RelKind::Partitioned) => b'p',
920 Some(RelKind::PartitionedIndex) => b'I',
921 Some(RelKind::MatView) => b'm',
922 _ => b'r',
923 },
924 Found::Builtin(row) => row.kind,
925 }
926 }
927
928 fn create_schema(&mut self, stmt: &CreateSchemaStmt, text: &str) -> Result<()> {
930 let owner = match &stmt.authrole {
931 None => self.user,
932 Some(role) => self.role_oid(role)?,
933 };
934 let roles = rupg_pgcatalog::roles::current();
935 let owner_name = roles.name(owner);
936 let name = match stmt.schemaname.as_deref() {
937 Some(name) => name.to_string(),
938 None => owner_name.unwrap_or_default().to_string(),
939 };
940 if !self.is_superuser() {
941 return Err(Error::new(
942 SqlState::INSUFFICIENT_PRIVILEGE,
943 format!("permission denied for database {}", self.an.env.database()),
944 ));
945 }
946 if name.starts_with("pg_") {
947 return Err(Error::new(
948 SqlState::RESERVED_NAME,
949 format!("unacceptable schema name \"{name}\""),
950 )
951 .with_detail("The prefix \"pg_\" is reserved for system schemas."));
952 }
953 let exists =
954 self.catalog.schema_by_name(&name).is_some() || self.builtin_schema(&name).is_some();
955 if exists && stmt.if_not_exists {
956 self.notice(
957 SqlState::DUPLICATE_SCHEMA,
958 format!("schema \"{name}\" already exists, skipping"),
959 );
960 return Ok(());
961 }
962 if exists {
963 return Err(Error::new(
964 SqlState::DUPLICATE_SCHEMA,
965 format!("schema \"{name}\" already exists"),
966 ));
967 }
968 let schema = self.catalog.create_schema(&name, owner)?;
969 self.apply_default_acl(ObjRef::new(PG_NAMESPACE, schema))?;
970 if !stmt.schemaElts.is_empty() {
971 let elements: Vec<Node> = stmt.schemaElts.iter().flatten().cloned().collect();
972 let elements = schema_elements::schema_elements(&elements, &name, text)?;
973 self.schema = Some(SchemaElements { owner, schema: name, elements });
974 }
975 Ok(())
976 }
977}
978
979fn relkind_detail(kind: u8) -> &'static str {
981 match kind {
982 b'i' => "This operation is not supported for indexes.",
983 b'I' => "This operation is not supported for partitioned indexes.",
984 b'p' => "This operation is not supported for partitioned tables.",
985 b'S' => "This operation is not supported for sequences.",
986 b'v' => "This operation is not supported for views.",
987 b'm' => "This operation is not supported for materialized views.",
988 b'c' => "This operation is not supported for composite types.",
989 b'f' => "This operation is not supported for foreign tables.",
990 b't' => "This operation is not supported for TOAST tables.",
991 _ => "This operation is not supported for tables.",
992 }
993}
994
995fn is_system_namespace(namespace: u32) -> bool {
997 namespace == PG_CATALOG_NAMESPACE || namespace == PG_TOAST_NAMESPACE
998}