Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,598 changes: 1,598 additions & 0 deletions crates/squawk_ide/src/builtins.sql

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions crates/squawk_ide/src/generated/keywords.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Generated via:
// cargo xtask codegen

pub(crate) const RESERVED_KEYWORDS: &[&str] = &[
"all",
"analyse",
Expand Down
12 changes: 7 additions & 5 deletions crates/squawk_ide/src/quote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,27 +87,29 @@ pub(crate) fn normalize_identifier(text: &str) -> String {

#[cfg(test)]
mod tests {
use insta::assert_snapshot;

use super::*;

#[test]
fn quote_column_alias_handles_embedded_quotes() {
assert_eq!(quote_column_alias(r#"foo"bar"#), r#""foo""bar""#);
assert_snapshot!(quote_column_alias(r#"foo"bar"#), @r#""foo""bar""#);
}

#[test]
fn quote_column_alias_doesnt_quote_reserved_words() {
// Keywords are allowed as column labels in AS clauses
assert_eq!(quote_column_alias("case"), "case");
assert_eq!(quote_column_alias("array"), "array");
assert_snapshot!(quote_column_alias("case"), @"case");
assert_snapshot!(quote_column_alias("array"), @"array");
}

#[test]
fn quote_column_alias_doesnt_quote_simple_identifiers() {
assert_eq!(quote_column_alias("col_name"), "col_name");
assert_snapshot!(quote_column_alias("col_name"), @"col_name");
}

#[test]
fn quote_column_alias_handles_special_column_name() {
assert_eq!(quote_column_alias("?column?"), r#""?column?""#);
assert_snapshot!(quote_column_alias("?column?"), @r#""?column?""#);
}
}
3 changes: 3 additions & 0 deletions crates/squawk_parser/src/generated/syntax_kind.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/squawk_parser/src/generated/token_sets.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/squawk_parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1653,7 +1653,7 @@ fn path_segment(p: &mut Parser<'_>, kind: SyntaxKind) {
// TODO: does this need to be flagged?
// Might want to disallow operators in some paths.
// Like `create table +()` doesn't make sense.
if !p.at(OPERATOR_KW) && current_operator(p).is_some() {
if p.at_ts(OPERATOR_FIRST) && !p.at(OPERATOR_KW) && current_operator(p).is_some() {
// check for operator kw so we can parse things like:
// create table operator();

Expand Down
6 changes: 6 additions & 0 deletions crates/squawk_parser/tests/data/ok/create_function.sql
Original file line number Diff line number Diff line change
Expand Up @@ -415,3 +415,9 @@ create function f (foo.bar.buzz.boo, t.u[], bool)
returns void
as '' language sql;

-- overlaps doesn't need quoting
create function overlaps() returns int8 as 'select 1' language sql;

-- like doesn't need quoting
create function like() returns int8 as 'select 1' language sql;

Original file line number Diff line number Diff line change
Expand Up @@ -3225,3 +3225,77 @@ SOURCE_FILE
SQL_KW "sql"
SEMICOLON ";"
WHITESPACE "\n\n"
COMMENT "-- overlaps doesn't need quoting"
WHITESPACE "\n"
CREATE_FUNCTION
CREATE_KW "create"
WHITESPACE " "
FUNCTION_KW "function"
WHITESPACE " "
PATH
PATH_SEGMENT
NAME
OVERLAPS_KW "overlaps"
PARAM_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
RET_TYPE
RETURNS_KW "returns"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "int8"
WHITESPACE " "
FUNC_OPTION_LIST
AS_FUNC_OPTION
AS_KW "as"
WHITESPACE " "
LITERAL
STRING "'select 1'"
WHITESPACE " "
LANGUAGE_FUNC_OPTION
LANGUAGE_KW "language"
WHITESPACE " "
SQL_KW "sql"
SEMICOLON ";"
WHITESPACE "\n\n"
COMMENT "-- like doesn't need quoting"
WHITESPACE "\n"
CREATE_FUNCTION
CREATE_KW "create"
WHITESPACE " "
FUNCTION_KW "function"
WHITESPACE " "
PATH
PATH_SEGMENT
NAME
LIKE_KW "like"
PARAM_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
RET_TYPE
RETURNS_KW "returns"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "int8"
WHITESPACE " "
FUNC_OPTION_LIST
AS_FUNC_OPTION
AS_KW "as"
WHITESPACE " "
LITERAL
STRING "'select 1'"
WHITESPACE " "
LANGUAGE_FUNC_OPTION
LANGUAGE_KW "language"
WHITESPACE " "
SQL_KW "sql"
SEMICOLON ";"
WHITESPACE "\n\n"
3 changes: 3 additions & 0 deletions crates/squawk_syntax/src/ast/generated/nodes.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/squawk_syntax/src/ast/generated/tokens.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 22 additions & 8 deletions crates/xtask/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,21 +217,29 @@ fn generate_kind_src(
}
}

const PRELUDE: &str = "\
// Generated via:
// cargo xtask codegen

";

fn generate_reserved_keywords_array(reserved_keywords: &[String]) -> Result<String> {
let mut reserved_keywords = reserved_keywords
.iter()
.map(|x| x.to_lowercase())
.collect::<Vec<_>>();
reserved_keywords.sort();

Ok(reformat(
let output = reformat(
quote! {
pub(crate) const RESERVED_KEYWORDS: &[&str] = &[
#(#reserved_keywords),*
];
}
.to_string(),
))
);

Ok(format!("{PRELUDE}{output}"))
}

fn generate_syntax_kinds(grammar: KindsSrc) -> Result<String> {
Expand Down Expand Up @@ -291,7 +299,7 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> Result<String> {
.map(|name| format_ident!("{}", name))
.collect::<Vec<_>>();

Ok(reformat(reformat(
let output = reformat(reformat(
quote! {
#![allow(bad_style, missing_docs, clippy::upper_case_acronyms)]

Expand Down Expand Up @@ -326,7 +334,9 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> Result<String> {
}
}
.to_string(),
).replace("#[space_hack]", "")))
).replace("#[space_hack]", ""));

Ok(format!("{PRELUDE}{output}"))
}

fn generate_token_sets(keyword_kinds: &KeywordKinds) -> Result<String> {
Expand Down Expand Up @@ -363,7 +373,7 @@ fn generate_token_sets(keyword_kinds: &KeywordKinds) -> Result<String> {
.map(|key| format_ident!("{}_KW", key.to_case(Case::UpperSnake)))
.collect::<Vec<_>>();

Ok(reformat(
let output = reformat(
quote! {
use crate::syntax_kind::SyntaxKind;
use crate::token_set::TokenSet;
Expand Down Expand Up @@ -394,7 +404,9 @@ fn generate_token_sets(keyword_kinds: &KeywordKinds) -> Result<String> {
}
.to_string(),
)
.replace("pub(crate)", "\npub(crate)"))
.replace("pub(crate)", "\npub(crate)");

Ok(format!("{PRELUDE}{output}"))
}

#[derive(Debug, Default)]
Expand Down Expand Up @@ -879,7 +891,8 @@ fn generate_nodes(nodes: &[AstNodeSrc], enums: &[AstEnumSrc]) -> String {
#(#node_boilerplate_impls)*
#(#enums_boilierplate_impls)*
};
reformat(file.to_string()).replace("#[derive", "\n#[derive")
let output = reformat(file.to_string()).replace("#[derive", "\n#[derive");
format!("{PRELUDE}{output}")
}

fn generate_tokens(tokens: &[(&'static str, &'static str)]) -> String {
Expand Down Expand Up @@ -920,5 +933,6 @@ fn generate_tokens(tokens: &[(&'static str, &'static str)]) -> String {
#(#tokens)*
};

reformat(file.to_string()).replace("#[derive", "\n#[derive")
let output = reformat(file.to_string()).replace("#[derive", "\n#[derive");
format!("{PRELUDE}{output}")
}
45 changes: 41 additions & 4 deletions crates/xtask/src/sync_builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ where n.nspname not like 'pg_temp%'
order by n.nspname, p.proname;
";

const BUILTIN_OPERATORS_QUERY: &str = r"
select n.nspname, o.oprname,
format_type(o.oprleft, null) as left_type,
format_type(o.oprright, null) as right_type
from pg_operator o
join pg_namespace n on n.oid = o.oprnamespace
where n.nspname not like 'pg_temp%'
and n.nspname not like 'pg_toast%'
and n.nspname <> 'public'
order by n.nspname, o.oprname;
";

const PG_VERSION_QUERY: &str = "show server_version;";

fn write_table(sql: &mut String, schema: &str, table_name: &str, columns: &[(String, String)]) {
Expand All @@ -68,7 +80,14 @@ fn write_table(sql: &mut String, schema: &str, table_name: &str, columns: &[(Str

fn run_sql(query: &str) -> Result<String> {
let output = Command::new("psql")
.args(["--tuples-only", "--no-align", "--command", query])
.args([
"--tuples-only",
"--no-align",
"--field-separator",
"\t",
"--command",
query,
])
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
Expand Down Expand Up @@ -112,7 +131,7 @@ pub(crate) fn sync_builtins() -> Result<()> {
.lines()
.filter(|line| !line.is_empty())
{
let mut parts = line.split('|');
let mut parts = line.split('\t');
let schema = parts.next().context("expected schema name")?;
let type_name = parts.next().context("expected type name")?;
let type_size = parts.next().context("expected type size")?;
Expand All @@ -132,7 +151,7 @@ pub(crate) fn sync_builtins() -> Result<()> {
.lines()
.filter(|line| !line.is_empty())
{
let mut parts = line.split('|');
let mut parts = line.split('\t');
let schema = parts.next().context("expected schema name")?;
let table_name = parts.next().context("expected table name")?;
let col_name = parts.next().context("expected column name")?;
Expand Down Expand Up @@ -161,7 +180,7 @@ pub(crate) fn sync_builtins() -> Result<()> {
.lines()
.filter(|line| !line.is_empty())
{
let mut parts = line.split('|');
let mut parts = line.split('\t');
let schema = parts.next().context("expected schema name")?;
let func_name = parts.next().context("expected function name")?;
let args = parts.next().context("expected function arguments")?;
Expand All @@ -171,6 +190,24 @@ pub(crate) fn sync_builtins() -> Result<()> {
));
}

for line in run_sql(BUILTIN_OPERATORS_QUERY)?
.lines()
.filter(|line| !line.is_empty())
{
let mut parts = line.split('\t');
let schema = parts.next().context("expected schema name")?;
let op_name = parts.next().context("expected operator name")?;
let left_type = parts.next().context("expected left type")?;
let right_type = parts.next().context("expected right type")?;

let args = match (left_type, right_type) {
("-", r) => format!("rightarg = {r}"),
(l, "-") => format!("leftarg = {l}"),
(l, r) => format!("leftarg = {l}, rightarg = {r}"),
};
sql.push_str(&format!("create operator {schema}.{op_name} ({args});\n\n"));
}

let builtins_path = project_root().join("crates/squawk_ide/src/builtins.sql");
std::fs::write(&builtins_path, sql).context("Failed to write builtins.sql")?;

Expand Down
Loading