1use rupg_common::{Error, Result, SqlState};
4use rupg_pgcatalog::builtin::{self, TypeRow};
5use rupg_sql::nodes::{Node, RangeVar, TypeName};
6use rupg_types::typmod::{
7 INTERVAL_FULL_PRECISION, INTERVAL_FULL_RANGE, MAX_TIME_PRECISION, VARHDRSZ, interval_typmod,
8 numeric_typmod,
9};
10
11use crate::coerce::AtOpt;
12use crate::{Analyzer, Env, Params};
13
14const MAX_ATTR_SIZE: i32 = 10 * 1024 * 1024;
16const NUMERIC_MAX_PRECISION: i32 = 1000;
18const NUMERIC_SCALE: (i32, i32) = (-1000, 1000);
20
21const fn mask(bits: &[u8]) -> i32 {
23 let mut mask = 0;
24 let mut i = 0;
25 while i < bits.len() {
26 mask |= 1 << bits[i];
27 i += 1;
28 }
29 mask
30}
31
32const INTERVAL_RANGES: [i32; 14] = [
34 mask(&[2]),
35 mask(&[1]),
36 mask(&[3]),
37 mask(&[10]),
38 mask(&[11]),
39 mask(&[12]),
40 mask(&[2, 1]),
41 mask(&[3, 10]),
42 mask(&[3, 10, 11]),
43 mask(&[3, 10, 11, 12]),
44 mask(&[10, 11]),
45 mask(&[10, 11, 12]),
46 mask(&[11, 12]),
47 INTERVAL_FULL_RANGE,
48];
49
50pub(crate) fn type_name_text(name: &TypeName) -> String {
52 let mut text = names(&name.names).join(".");
53 if name.pct_type {
54 text.push_str("%TYPE");
55 }
56 if !name.arrayBounds.is_empty() {
57 text.push_str("[]");
58 }
59 text
60}
61
62pub(crate) fn names(list: &[Option<Node>]) -> Vec<&str> {
64 list.iter()
65 .filter_map(|n| match n {
66 Some(Node::String(s)) => Some(&**s),
67 _ => None,
68 })
69 .collect()
70}
71
72pub(crate) fn place(location: i32) -> Option<usize> {
74 usize::try_from(location).ok()
75}
76
77fn is_space(b: u8) -> bool {
79 matches!(b, b' ' | b'\t' | b'\n' | b'\r' | 0x0b | 0x0c)
80}
81
82pub fn parse_type(text: &str, env: &dyn Env) -> Result<Result<(u32, i32)>> {
88 let invalid = || Error::new(SqlState::SYNTAX_ERROR, format!("invalid type name \"{text}\""));
89 if text.bytes().all(is_space) {
90 return Err(invalid());
91 }
92 let name = rupg_sql::parse_type_name(text).map_err(|e| {
93 let at = e.location.map(|at| text[..at.min(text.len())].chars().count());
95 Error::from(e).with_position(at).with_context(format!("invalid type name \"{text}\""))
96 })?;
97 if name.setof {
98 return Err(invalid());
99 }
100 let mut analyzer = Analyzer::new(env, &Params::default());
101 match analyzer.type_name(&name) {
102 Ok(found) => Ok(Ok(found)),
103 Err(e) if e.state() == SqlState::UNDEFINED_SCHEMA => Ok(Err(e.with_position(None))),
104 Err(e) if e.state() == SqlState::UNDEFINED_OBJECT => Ok(Err(Error::new(
105 SqlState::UNDEFINED_OBJECT,
106 format!("type \"{}\" does not exist", type_name_text(&name)),
107 ))),
108 Err(e) => Err(e.with_position(None)),
109 }
110}
111
112struct Found {
114 oid: u32,
115 array: u32,
117 modin: u32,
119}
120
121impl Found {
122 fn builtin(row: &TypeRow) -> Found {
123 Found { oid: row.oid, array: row.array, modin: row.modin }
124 }
125
126 fn user(ty: &rupg_catalog::Type) -> Found {
128 Found { oid: ty.oid, array: ty.array, modin: 0 }
129 }
130}
131
132impl Analyzer<'_> {
133 fn column_type_of(&mut self, name: &TypeName) -> Result<(u32, i32)> {
135 let at = place(name.location);
136 let parts = names(&name.names);
137 let mut rv = RangeVar { location: name.location, ..RangeVar::default() };
138 let field = match parts[..] {
139 [_] => {
140 return Err(Error::new(
141 SqlState::SYNTAX_ERROR,
142 format!("improper %TYPE reference (too few dotted names): {}", parts.join(".")),
143 )
144 .at_opt(at));
145 }
146 [rel, field] => {
147 rv.relname = Some(rel.into());
148 field
149 }
150 [schema, rel, field] => {
151 rv.schemaname = Some(schema.into());
152 rv.relname = Some(rel.into());
153 field
154 }
155 [database, schema, rel, field] => {
156 rv.catalogname = Some(database.into());
157 rv.schemaname = Some(schema.into());
158 rv.relname = Some(rel.into());
159 field
160 }
161 _ => {
162 return Err(Error::new(
163 SqlState::SYNTAX_ERROR,
164 format!(
165 "improper %TYPE reference (too many dotted names): {}",
166 parts.join(".")
167 ),
168 )
169 .at_opt(at));
170 }
171 };
172 let relname = rv.relname.as_deref().unwrap_or_default().to_owned();
173 let Some(relid) = self.lookup_relation(&rv)? else {
174 let shown = match &rv.schemaname {
175 Some(schema) => format!("{schema}.{relname}"),
176 None => relname,
177 };
178 return Err(Error::new(
179 SqlState::UNDEFINED_TABLE,
180 format!("relation \"{shown}\" does not exist"),
181 ));
182 };
183 let columns = self.open_relation(relid, &relname, at)?;
184 let Some(column) = columns.iter().find(|c| c.name == field) else {
185 return Err(Error::new(
186 SqlState::UNDEFINED_COLUMN,
187 format!("column \"{field}\" of relation \"{relname}\" does not exist"),
188 )
189 .at_opt(at));
190 };
191 self.notices.push(
192 Error::new(
193 SqlState::SUCCESSFUL_COMPLETION,
194 format!(
195 "type reference {} converted to {}",
196 type_name_text(name),
197 crate::types::name(column.ty)
198 ),
199 )
200 .as_notice(),
201 );
202 Ok((column.ty, -1))
203 }
204
205 pub(crate) fn type_name(&mut self, name: &TypeName) -> Result<(u32, i32)> {
207 let at = place(name.location);
208 if name.pct_type {
209 return self.column_type_of(name);
210 }
211 let mut row = if name.typeOid != 0 {
212 self.type_by_oid(name.typeOid)
213 } else {
214 self.lookup_type(&names(&name.names), at)?
215 };
216 if !name.arrayBounds.is_empty() {
217 row = row.and_then(|element| self.type_by_oid(element.array));
219 }
220 let Some(row) = row else {
221 return Err(Error::new(
222 SqlState::UNDEFINED_OBJECT,
223 format!("type \"{}\" does not exist", type_name_text(name)),
224 )
225 .at_opt(at));
226 };
227 let first = self.notices.len();
228 let typmod = self.type_modifier(name, &row).map_err(|e| e.at_opt(at))?;
229 for notice in &mut self.notices[first..] {
230 *notice = notice.clone().at_opt(at);
231 }
232 Ok((row.oid, typmod))
233 }
234
235 pub(crate) fn lookup_type_name(&self, name: &TypeName) -> Result<Option<u32>> {
237 let mut row = self.lookup_type(&names(&name.names), None)?;
238 if !name.arrayBounds.is_empty() {
239 row = row.and_then(|element| self.type_by_oid(element.array));
240 }
241 Ok(row.map(|row| row.oid))
242 }
243
244 fn type_by_oid(&self, oid: u32) -> Option<Found> {
246 if oid == 0 {
247 return None;
248 }
249 if let Some(row) = builtin::type_by_oid(oid) {
250 return Some(Found::builtin(row));
251 }
252 self.env.catalog()?.type_by_oid(oid).map(Found::user)
253 }
254
255 fn type_in(&self, namespace: u32, name: &str) -> Option<Found> {
257 if let Some(row) = builtin::type_by_name(namespace, name) {
258 return Some(Found::builtin(row));
259 }
260 self.env.catalog()?.type_by_name(namespace, name).map(Found::user)
261 }
262
263 fn lookup_type(&self, names: &[&str], at: Option<usize>) -> Result<Option<Found>> {
265 match names {
266 [name] => Ok(self.temp_path.iter().find_map(|&ns| self.type_in(ns, name))),
267 [schema, name] => {
268 let ns = self.schema(schema).ok_or_else(|| {
269 Error::new(
270 SqlState::UNDEFINED_SCHEMA,
271 format!("schema \"{schema}\" does not exist"),
272 )
273 .at_opt(at)
274 })?;
275 Ok(self.type_in(ns, name))
276 }
277 [catalog, schema, name] => {
278 if *catalog != self.env.database() {
279 return Err(Error::new(
280 SqlState::FEATURE_NOT_SUPPORTED,
281 format!(
282 "cross-database references are not implemented: {}",
283 names.join(".")
284 ),
285 )
286 .at_opt(at));
287 }
288 self.lookup_type(&[schema, name], at)
289 }
290 _ => Err(Error::new(
291 SqlState::SYNTAX_ERROR,
292 format!("improper qualified name (too many dotted names): {}", names.join(".")),
293 )
294 .at_opt(at)),
295 }
296 }
297
298 fn type_modifier(&mut self, name: &TypeName, row: &Found) -> Result<i32> {
300 if name.typemod != -1 {
301 return Ok(name.typemod);
302 }
303 if name.typmods.is_empty() {
304 return Ok(-1);
305 }
306 let modin = builtin::proc_by_oid(row.modin).map(|p| p.src);
307 let Some(modin) = modin else {
308 return Err(Error::new(
309 SqlState::SYNTAX_ERROR,
310 format!("type modifier is not allowed for type \"{}\"", type_name_text(name)),
311 ));
312 };
313 let mut texts = Vec::new();
314 for modifier in &name.typmods {
315 let text = match modifier {
316 Some(Node::A_Const(c)) => match &c.val {
317 Some(Node::Integer(v)) => Some(v.to_string()),
318 Some(Node::Float(v) | Node::String(v)) => Some(v.to_string()),
319 _ => None,
320 },
321 Some(Node::ColumnRef(c)) => match c.fields.as_slice() {
322 [Some(Node::String(s))] => Some(s.to_string()),
323 _ => None,
324 },
325 _ => None,
326 };
327 let Some(text) = text else {
328 return Err(Error::new(
329 SqlState::SYNTAX_ERROR,
330 "type modifiers must be simple constants or identifiers",
331 ));
332 };
333 texts.push(text);
334 }
335 let mut values = Vec::with_capacity(texts.len());
336 for text in &texts {
337 let value = rupg_types::int4_in(text).map_err(Error::from)?;
339 values.push(value);
340 }
341 self.typmod_in(modin, &values)
342 }
343
344 fn typmod_in(&mut self, modin: &str, values: &[i32]) -> Result<i32> {
346 let invalid = |what: &str| {
347 Error::new(SqlState::INVALID_PARAMETER_VALUE, format!("invalid {what}type modifier"))
348 };
349 match modin {
350 "varchartypmodin" | "bpchartypmodin" => {
351 let name = if modin == "varchartypmodin" { "varchar" } else { "char" };
352 let &[n] = values else { return Err(invalid("")) };
353 if n < 1 {
354 return Err(Error::new(
355 SqlState::INVALID_PARAMETER_VALUE,
356 format!("length for type {name} must be at least 1"),
357 ));
358 }
359 if n > MAX_ATTR_SIZE {
360 return Err(Error::new(
361 SqlState::INVALID_PARAMETER_VALUE,
362 format!("length for type {name} cannot exceed {MAX_ATTR_SIZE}"),
363 ));
364 }
365 Ok(VARHDRSZ + n)
366 }
367 "bittypmodin" | "varbittypmodin" => {
368 let name = if modin == "bittypmodin" { "bit" } else { "varbit" };
369 let &[n] = values else { return Err(invalid("")) };
370 if n < 1 {
371 return Err(Error::new(
372 SqlState::INVALID_PARAMETER_VALUE,
373 format!("length for type {name} must be at least 1"),
374 ));
375 }
376 if n > MAX_ATTR_SIZE * 8 {
377 return Err(Error::new(
378 SqlState::INVALID_PARAMETER_VALUE,
379 format!("length for type {name} cannot exceed {}", MAX_ATTR_SIZE * 8),
380 ));
381 }
382 Ok(n)
383 }
384 "numerictypmodin" => {
385 let (precision, scale) = match *values {
386 [p] => (p, 0),
387 [p, s] => (p, s),
388 _ => return Err(invalid("NUMERIC ")),
389 };
390 if !(1..=NUMERIC_MAX_PRECISION).contains(&precision) {
391 return Err(Error::new(
392 SqlState::INVALID_PARAMETER_VALUE,
393 format!(
394 "NUMERIC precision {precision} must be between 1 and {NUMERIC_MAX_PRECISION}"
395 ),
396 ));
397 }
398 if !(NUMERIC_SCALE.0..=NUMERIC_SCALE.1).contains(&scale) {
399 return Err(Error::new(
400 SqlState::INVALID_PARAMETER_VALUE,
401 format!(
402 "NUMERIC scale {scale} must be between {} and {}",
403 NUMERIC_SCALE.0, NUMERIC_SCALE.1
404 ),
405 ));
406 }
407 Ok(numeric_typmod(precision, scale))
408 }
409 "timetypmodin" | "timetztypmodin" | "timestamptypmodin" | "timestamptztypmodin" => {
410 let &[n] = values else { return Err(invalid("")) };
411 let what = if modin.starts_with("timestamp") { "TIMESTAMP" } else { "TIME" };
412 let tz = if modin.contains("tz") { " WITH TIME ZONE" } else { "" };
413 self.precision(n, &format!("{what}({n}){tz}"))
414 }
415 "intervaltypmodin" => {
416 if let Some(range) = values.first()
417 && !INTERVAL_RANGES.contains(range)
418 {
419 return Err(invalid("INTERVAL "));
420 }
421 match *values {
422 [range] if range == INTERVAL_FULL_RANGE => Ok(-1),
423 [range] => Ok(interval_typmod(INTERVAL_FULL_PRECISION, range)),
424 [range, precision] => Ok(interval_typmod(
425 self.precision(precision, &format!("INTERVAL({precision})"))?,
426 range,
427 )),
428 _ => Err(invalid("INTERVAL ")),
429 }
430 }
431 _ => Err(Error::new(
432 SqlState::FEATURE_NOT_SUPPORTED,
433 format!("the type modifier function {modin} is not supported yet"),
434 )),
435 }
436 }
437
438 pub(crate) fn precision(&mut self, n: i32, what: &str) -> Result<i32> {
440 if n < 0 {
441 return Err(Error::new(
442 SqlState::INVALID_PARAMETER_VALUE,
443 format!("{what} precision must not be negative"),
444 ));
445 }
446 if n > MAX_TIME_PRECISION {
447 self.notices.push(Error::new(
448 SqlState::INVALID_PARAMETER_VALUE,
449 format!("{what} precision reduced to maximum allowed, {MAX_TIME_PRECISION}"),
450 ));
451 return Ok(MAX_TIME_PRECISION);
452 }
453 Ok(n)
454 }
455}