Skip to content

Commit fc28466

Browse files
Refactored introducing link_symbol
1 parent 8b16415 commit fc28466

File tree

5 files changed

+90
-59
lines changed

5 files changed

+90
-59
lines changed

src/ast/ddl.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3099,15 +3099,11 @@ impl fmt::Display for CreateFunction {
30993099
if let Some(remote_connection) = &self.remote_connection {
31003100
write!(f, " REMOTE WITH CONNECTION {remote_connection}")?;
31013101
}
3102-
if let Some(CreateFunctionBody::AsBeforeOptions(function_body)) = &self.function_body {
3103-
write!(f, " AS ")?;
3104-
// Special handling for tuple expressions to format without parentheses
3105-
// PostgreSQL C functions use: AS 'obj_file', 'link_symbol'
3106-
// rather than: AS ('obj_file', 'link_symbol')
3107-
if let Expr::Tuple(exprs) = function_body {
3108-
write!(f, "{}", display_comma_separated(exprs))?;
3109-
} else {
3110-
write!(f, "{function_body}")?;
3102+
if let Some(CreateFunctionBody::AsBeforeOptions { body, link_symbol }) = &self.function_body
3103+
{
3104+
write!(f, " AS {body}")?;
3105+
if let Some(link_symbol) = link_symbol {
3106+
write!(f, ", {link_symbol}")?;
31113107
}
31123108
}
31133109
if let Some(CreateFunctionBody::Return(function_body)) = &self.function_body {

src/ast/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9099,8 +9099,19 @@ pub enum CreateFunctionBody {
90999099
/// OPTIONS(description="desc");
91009100
/// ```
91019101
///
9102+
/// For PostgreSQL C functions with object file and link symbol:
9103+
/// ```sql
9104+
/// CREATE FUNCTION cas_in(input cstring) RETURNS cas
9105+
/// AS 'MODULE_PATHNAME', 'cas_in_wrapper'
9106+
/// ```
9107+
///
91029108
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11
9103-
AsBeforeOptions(Expr),
9109+
AsBeforeOptions {
9110+
/// The primary expression (object file path for C functions, or regular expression)
9111+
body: Expr,
9112+
/// Optional link symbol for C language functions
9113+
link_symbol: Option<Expr>,
9114+
},
91049115
/// A function body expression using the 'AS' keyword and shows up
91059116
/// after any `OPTIONS` clause.
91069117
///

src/parser/mod.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5204,9 +5204,7 @@ impl<'a> Parser<'a> {
52045204
}
52055205
if self.parse_keyword(Keyword::AS) {
52065206
ensure_not_set(&body.function_body, "AS")?;
5207-
body.function_body = Some(CreateFunctionBody::AsBeforeOptions(
5208-
self.parse_create_function_body_string()?,
5209-
));
5207+
body.function_body = Some(self.parse_create_function_body_string()?);
52105208
} else if self.parse_keyword(Keyword::LANGUAGE) {
52115209
ensure_not_set(&body.language, "LANGUAGE")?;
52125210
body.language = Some(self.parse_identifier()?);
@@ -5298,15 +5296,15 @@ impl<'a> Parser<'a> {
52985296
let name = self.parse_object_name(false)?;
52995297
self.expect_keyword_is(Keyword::AS)?;
53005298

5301-
let as_ = self.parse_create_function_body_string()?;
5299+
let body = self.parse_create_function_body_string()?;
53025300
let using = self.parse_optional_create_function_using()?;
53035301

53045302
Ok(Statement::CreateFunction(CreateFunction {
53055303
or_alter: false,
53065304
or_replace,
53075305
temporary,
53085306
name,
5309-
function_body: Some(CreateFunctionBody::AsBeforeOptions(as_)),
5307+
function_body: Some(body),
53105308
using,
53115309
if_not_exists: false,
53125310
args: None,
@@ -5368,7 +5366,10 @@ impl<'a> Parser<'a> {
53685366
let expr = self.parse_expr()?;
53695367
if options.is_none() {
53705368
options = self.maybe_parse_options(Keyword::OPTIONS)?;
5371-
Some(CreateFunctionBody::AsBeforeOptions(expr))
5369+
Some(CreateFunctionBody::AsBeforeOptions {
5370+
body: expr,
5371+
link_symbol: None,
5372+
})
53725373
} else {
53735374
Some(CreateFunctionBody::AsAfterOptions(expr))
53745375
}
@@ -10521,7 +10522,7 @@ impl<'a> Parser<'a> {
1052110522

1052210523
/// Parse the body of a `CREATE FUNCTION` specified as a string.
1052310524
/// e.g. `CREATE FUNCTION ... AS $$ body $$`.
10524-
fn parse_create_function_body_string(&mut self) -> Result<Expr, ParserError> {
10525+
fn parse_create_function_body_string(&mut self) -> Result<CreateFunctionBody, ParserError> {
1052510526
// Helper closure to parse a single string value (quoted or dollar-quoted)
1052610527
let parse_string_expr = |parser: &mut Parser| -> Result<Expr, ParserError> {
1052710528
let peek_token = parser.peek_token();
@@ -10538,17 +10539,16 @@ impl<'a> Parser<'a> {
1053810539
}
1053910540
};
1054010541

10541-
let first_expr = parse_string_expr(self)?;
10542-
1054310542
// Check if there's a comma, indicating multiple strings (e.g., AS 'obj_file', 'link_symbol')
1054410543
// This is used for C language functions: AS 'MODULE_PATHNAME', 'link_symbol'
10545-
if self.consume_token(&Token::Comma) {
10546-
let mut exprs = vec![first_expr];
10547-
exprs.extend(self.parse_comma_separated(parse_string_expr)?);
10548-
Ok(Expr::Tuple(exprs))
10549-
} else {
10550-
Ok(first_expr)
10551-
}
10544+
Ok(CreateFunctionBody::AsBeforeOptions {
10545+
body: parse_string_expr(self)?,
10546+
link_symbol: if self.consume_token(&Token::Comma) {
10547+
Some(parse_string_expr(self)?)
10548+
} else {
10549+
None
10550+
},
10551+
})
1055210552
}
1055310553

1055410554
/// Parse a literal string

tests/sqlparser_hive.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -408,10 +408,13 @@ fn parse_create_function() {
408408
assert_eq!(name.to_string(), "mydb.myfunc");
409409
assert_eq!(
410410
function_body,
411-
Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
412-
(Value::SingleQuotedString("org.random.class.Name".to_string()))
413-
.with_empty_span()
414-
)))
411+
Some(CreateFunctionBody::AsBeforeOptions {
412+
body: Expr::Value(
413+
(Value::SingleQuotedString("org.random.class.Name".to_string()))
414+
.with_empty_span()
415+
),
416+
link_symbol: None,
417+
})
415418
);
416419
assert_eq!(
417420
using,

tests/sqlparser_postgres.rs

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4259,9 +4259,12 @@ $$"#;
42594259
behavior: None,
42604260
called_on_null: None,
42614261
parallel: None,
4262-
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
4263-
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF str1 <> str2 THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
4264-
))),
4262+
function_body: Some(CreateFunctionBody::AsBeforeOptions {
4263+
body: Expr::Value(
4264+
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF str1 <> str2 THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
4265+
),
4266+
link_symbol: None,
4267+
}),
42654268
if_not_exists: false,
42664269
using: None,
42674270
determinism_specifier: None,
@@ -4297,9 +4300,12 @@ $$"#;
42974300
behavior: None,
42984301
called_on_null: None,
42994302
parallel: None,
4300-
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
4301-
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF int1 <> 0 THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
4302-
))),
4303+
function_body: Some(CreateFunctionBody::AsBeforeOptions {
4304+
body: Expr::Value(
4305+
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF int1 <> 0 THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
4306+
),
4307+
link_symbol: None,
4308+
}),
43034309
if_not_exists: false,
43044310
using: None,
43054311
determinism_specifier: None,
@@ -4339,9 +4345,12 @@ $$"#;
43394345
behavior: None,
43404346
called_on_null: None,
43414347
parallel: None,
4342-
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
4343-
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF a <> b THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
4344-
))),
4348+
function_body: Some(CreateFunctionBody::AsBeforeOptions {
4349+
body: Expr::Value(
4350+
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF a <> b THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
4351+
),
4352+
link_symbol: None,
4353+
}),
43454354
if_not_exists: false,
43464355
using: None,
43474356
determinism_specifier: None,
@@ -4381,9 +4390,12 @@ $$"#;
43814390
behavior: None,
43824391
called_on_null: None,
43834392
parallel: None,
4384-
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
4385-
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF int1 <> int2 THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
4386-
))),
4393+
function_body: Some(CreateFunctionBody::AsBeforeOptions {
4394+
body: Expr::Value(
4395+
(Value::DollarQuotedString(DollarQuotedString {value: "\nBEGIN\n IF int1 <> int2 THEN\n RETURN TRUE;\n ELSE\n RETURN FALSE;\n END IF;\nEND;\n".to_owned(), tag: None})).with_empty_span()
4396+
),
4397+
link_symbol: None,
4398+
}),
43874399
if_not_exists: false,
43884400
using: None,
43894401
determinism_specifier: None,
@@ -4416,13 +4428,16 @@ $$"#;
44164428
behavior: None,
44174429
called_on_null: None,
44184430
parallel: None,
4419-
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
4420-
(Value::DollarQuotedString(DollarQuotedString {
4421-
value: "\n BEGIN\n RETURN TRUE;\n END;\n ".to_owned(),
4422-
tag: None
4423-
}))
4424-
.with_empty_span()
4425-
))),
4431+
function_body: Some(CreateFunctionBody::AsBeforeOptions {
4432+
body: Expr::Value(
4433+
(Value::DollarQuotedString(DollarQuotedString {
4434+
value: "\n BEGIN\n RETURN TRUE;\n END;\n ".to_owned(),
4435+
tag: None
4436+
}))
4437+
.with_empty_span()
4438+
),
4439+
link_symbol: None,
4440+
}),
44264441
if_not_exists: false,
44274442
using: None,
44284443
determinism_specifier: None,
@@ -4454,9 +4469,12 @@ fn parse_create_function() {
44544469
behavior: Some(FunctionBehavior::Immutable),
44554470
called_on_null: Some(FunctionCalledOnNull::Strict),
44564471
parallel: Some(FunctionParallel::Safe),
4457-
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Value(
4458-
(Value::SingleQuotedString("select $1 + $2;".into())).with_empty_span()
4459-
))),
4472+
function_body: Some(CreateFunctionBody::AsBeforeOptions {
4473+
body: Expr::Value(
4474+
(Value::SingleQuotedString("select $1 + $2;".into())).with_empty_span()
4475+
),
4476+
link_symbol: None,
4477+
}),
44604478
if_not_exists: false,
44614479
using: None,
44624480
determinism_specifier: None,
@@ -4509,12 +4527,14 @@ fn parse_create_function_c_with_module_pathname() {
45094527
behavior: Some(FunctionBehavior::Immutable),
45104528
called_on_null: None,
45114529
parallel: Some(FunctionParallel::Safe),
4512-
function_body: Some(CreateFunctionBody::AsBeforeOptions(Expr::Tuple(vec![
4513-
Expr::Value(
4530+
function_body: Some(CreateFunctionBody::AsBeforeOptions {
4531+
body: Expr::Value(
45144532
(Value::SingleQuotedString("MODULE_PATHNAME".into())).with_empty_span()
45154533
),
4516-
Expr::Value((Value::SingleQuotedString("cas_in_wrapper".into())).with_empty_span()),
4517-
]))),
4534+
link_symbol: Some(Expr::Value(
4535+
(Value::SingleQuotedString("cas_in_wrapper".into())).with_empty_span()
4536+
)),
4537+
}),
45184538
if_not_exists: false,
45194539
using: None,
45204540
determinism_specifier: None,
@@ -6118,8 +6138,8 @@ fn parse_trigger_related_functions() {
61186138
args: Some(vec![]),
61196139
return_type: Some(DataType::Trigger),
61206140
function_body: Some(
6121-
CreateFunctionBody::AsBeforeOptions(
6122-
Expr::Value((
6141+
CreateFunctionBody::AsBeforeOptions {
6142+
body: Expr::Value((
61236143
Value::DollarQuotedString(
61246144
DollarQuotedString {
61256145
value: "\n BEGIN\n -- Check that empname and salary are given\n IF NEW.empname IS NULL THEN\n RAISE EXCEPTION 'empname cannot be null';\n END IF;\n IF NEW.salary IS NULL THEN\n RAISE EXCEPTION '% cannot have null salary', NEW.empname;\n END IF;\n\n -- Who works for us when they must pay for it?\n IF NEW.salary < 0 THEN\n RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;\n END IF;\n\n -- Remember who changed the payroll when\n NEW.last_date := current_timestamp;\n NEW.last_user := current_user;\n RETURN NEW;\n END;\n ".to_owned(),
@@ -6129,7 +6149,8 @@ fn parse_trigger_related_functions() {
61296149
},
61306150
)
61316151
).with_empty_span()),
6132-
),
6152+
link_symbol: None,
6153+
},
61336154
),
61346155
behavior: None,
61356156
called_on_null: None,

0 commit comments

Comments
 (0)