Skip to content
Open
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
10 changes: 10 additions & 0 deletions crates/pgls_pretty_print/src/nodes/alter_table_stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ fn emit_alter_table_cmd_impl(e: &mut EventEmitter, cmd: &AlterTableCmd, for_type
} else {
e.token(TokenKind::COLUMN_KW);
}
// Dropping the flag turns an idempotent DDL into one that fails on a second run, so
// the AST round trip guard refuses the whole statement rather than let it through.
if cmd.missing_ok {
e.space();
e.token(TokenKind::IF_KW);
e.space();
e.token(TokenKind::NOT_KW);
e.space();
e.token(TokenKind::EXISTS_KW);
}
if let Some(ref def) = cmd.def {
e.space();
e.indent_start();
Expand Down
13 changes: 13 additions & 0 deletions crates/pgls_pretty_print/src/nodes/boolean_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,20 @@ pub(super) fn emit_boolean_test(e: &mut EventEmitter, n: &BooleanTest) {

// Emit the argument
if let Some(ref arg) = n.arg {
// AND, OR and NOT bind more loosely than the postfix IS test, so the grouping is lost
// unless it is spelled out: `(a OR b) IS TRUE` would come back as `a OR b IS TRUE`, which
// parses as `a OR (b IS TRUE)`. Every other argument kind binds tighter and needs nothing.
let needs_parens = matches!(arg.node.as_ref(), Some(pgls_query::NodeEnum::BoolExpr(_)));

if needs_parens {
e.token(TokenKind::L_PAREN);
}

super::emit_node(arg, e);

if needs_parens {
e.token(TokenKind::R_PAREN);
}
}

e.line(LineType::SoftOrSpace);
Expand Down
12 changes: 12 additions & 0 deletions crates/pgls_pretty_print/src/nodes/constraint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,18 @@ pub(super) fn emit_constraint(e: &mut EventEmitter, n: &Constraint) {

e.token(TokenKind::UNIQUE_KW);

// For a table constraint the qualifier sits between the keyword and the column list,
// unlike CREATE INDEX where it follows the list. Dropping it would let two NULLs
// coexist where the schema forbids it.
if n.nulls_not_distinct {
e.space();
e.token(TokenKind::NULLS_KW);
e.space();
e.token(TokenKind::NOT_KW);
e.space();
e.token(TokenKind::DISTINCT_KW);
}

if !n.keys.is_empty() {
e.space();
e.token(TokenKind::L_PAREN);
Expand Down
9 changes: 9 additions & 0 deletions crates/pgls_pretty_print/src/nodes/select_stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ fn emit_distinct_clause(e: &mut EventEmitter, clause: &[Node]) {
return;
}

// The clause has its own group so that it can stay on one line while the target list breaks,
// which is the usual shape of a DISTINCT ON query.
e.group_start(GroupKind::SelectStmt);

e.space();
e.token(TokenKind::ON_KW);
e.space();
Expand All @@ -364,7 +368,12 @@ fn emit_distinct_clause(e: &mut EventEmitter, clause: &[Node]) {
}

e.indent_end();
// Symmetric with the Soft after the opening parenthesis: without it the closing parenthesis
// stays glued to the last expression when the list breaks.
e.line(LineType::Soft);
e.token(TokenKind::R_PAREN);

e.group_end();
}

/// Determines if we need parentheses around a set operation operand.
Expand Down
48 changes: 48 additions & 0 deletions crates/pgls_pretty_print/src/nodes/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,22 @@ pub(super) fn emit_identifier_maybe_quoted(e: &mut EventEmitter, value: &str) {
}
}

/// Emits a type name part, honouring `type_case` when the name can be written unquoted.
///
/// A name that needs quotes keeps its own spelling: `"MyType"` and `"MYTYPE"` are two different
/// types in PostgreSQL, so casing it would change which type is referenced.
pub(super) fn emit_type_identifier_maybe_quoted(e: &mut EventEmitter, value: &str) {
if value.is_empty() {
return;
}

if needs_quoting(value) {
emit_identifier(e, value);
} else {
e.token(TokenKind::TYPE_IDENT(value.to_string()));
}
}

pub(super) fn emit_keyword(e: &mut EventEmitter, keyword: &str) {
if let Some(token) = TokenKind::from_keyword(keyword) {
e.token(token);
Expand Down Expand Up @@ -254,3 +270,35 @@ fn pick_dollar_delimiter(body: &str, hint: DollarQuoteHint) -> String {
counter += 1;
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::renderer::{KeywordCase, RenderConfig, Renderer};

fn render(value: &str, type_case: KeywordCase) -> String {
let mut emitter = EventEmitter::new();
emit_type_identifier_maybe_quoted(&mut emitter, value);

let mut output = String::new();
let config = RenderConfig {
type_case,
..Default::default()
};
let mut renderer = Renderer::new(&mut output, config);
renderer.render(emitter.events).expect("render");
output
}

#[test]
fn an_unquoted_type_name_follows_the_type_case() {
assert_eq!(render("object_id", KeywordCase::Upper), "OBJECT_ID");
assert_eq!(render("object_id", KeywordCase::Lower), "object_id");
}

#[test]
fn a_type_name_needing_quotes_keeps_its_own_case() {
assert_eq!(render("MyType", KeywordCase::Upper), "\"MyType\"");
assert_eq!(render("OBJECT_ID", KeywordCase::Lower), "\"OBJECT_ID\"");
}
}
13 changes: 11 additions & 2 deletions crates/pgls_pretty_print/src/nodes/type_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
};
use pgls_query::protobuf::{self, TypeName};

use super::string::emit_identifier_maybe_quoted;
use super::string::{emit_identifier_maybe_quoted, emit_type_identifier_maybe_quoted};

const INTERVAL_MASK_MONTH: i32 = 1 << 1;
const INTERVAL_MASK_YEAR: i32 = 1 << 2;
Expand Down Expand Up @@ -69,7 +69,7 @@ fn emit_normalized_type_name(e: &mut EventEmitter, name_parts: &[String]) {
if let Some(words) = builtin_type_keywords(name_parts) {
emit_keyword_sequence(e, words);
} else if !name_parts.is_empty() {
emit_dot_separated_name(e, name_parts);
emit_dot_separated_type_name(e, name_parts);
} else {
e.token(TokenKind::IDENT("<?>".to_string()));
}
Expand All @@ -93,6 +93,15 @@ fn emit_dot_separated_name(e: &mut EventEmitter, name_parts: &[String]) {
}
}

fn emit_dot_separated_type_name(e: &mut EventEmitter, name_parts: &[String]) {
for (index, part) in name_parts.iter().enumerate() {
if index > 0 {
e.token(TokenKind::DOT);
}
emit_type_identifier_maybe_quoted(e, part);
}
}

fn builtin_type_keywords(name_parts: &[String]) -> Option<&'static [&'static str]> {
if name_parts.is_empty() {
return None;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE s.t
ADD COLUMN IF NOT EXISTS row_id TEXT,
ADD COLUMN IF NOT EXISTS designation_source TEXT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SELECT id
FROM s.units
WHERE (units.has_history OR units.has_shares OR units.has_calls) IS TRUE
AND (units.is_active AND units.is_visible) IS NOT FALSE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SELECT DISTINCT ON (addresses.hash) addresses.hash AS origin_hash, addresses.entity_type, addresses.entity_fk
FROM normalization.addresses
ORDER BY addresses.hash, addresses.entity_type;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SELECT
CAST(t.id AS public.object_id),
CAST(t.ids AS public.object_id[]),
CAST(t.n AS bigint)
FROM s.t;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE s.expense_types (
expense_type_legacy_id TEXT,
legacy_branch_code TEXT,
UNIQUE NULLS NOT DISTINCT (expense_type_legacy_id, legacy_branch_code)
);
Original file line number Diff line number Diff line change
Expand Up @@ -1472,9 +1472,7 @@ select not a from (values (true)) as t (a) group by rollup (not a) having not no

select not a from (values (true)) as t (a) group by rollup (not a) having not not a;

select distinct on (
a,
b)
select distinct on (a, b)
a,
b
from
Expand All @@ -1485,9 +1483,7 @@ group by grouping sets ((a, b), a)
order by a,
b;

select distinct on (
a,
b)
select distinct on (a, b)
a,
b
from
Expand All @@ -1498,9 +1494,7 @@ group by grouping sets ((a, b), a)
order by a,
b;

select distinct on (
a,
b + 1)
select distinct on (a, b + 1)
a,
b + 1
from
Expand All @@ -1511,9 +1505,7 @@ group by grouping sets ((a, b + 1), a)
order by a,
b + 1;

select distinct on (
a,
b + 1)
select distinct on (a, b + 1)
a,
b + 1
from
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1933,9 +1933,7 @@ group by rollup (not a)
having
not not a;

select distinct on (
a,
b)
select distinct on (a, b)
a,
b
from
Expand All @@ -1946,9 +1944,7 @@ group by grouping sets ((a, b), a)
order by a,
b;

select distinct on (
a,
b)
select distinct on (a, b)
a,
b
from
Expand All @@ -1959,9 +1955,7 @@ group by grouping sets ((a, b), a)
order by a,
b;

select distinct on (
a,
b + 1)
select distinct on (a, b + 1)
a,
b + 1
from
Expand All @@ -1972,9 +1966,7 @@ group by grouping sets ((a, b + 1), a)
order by a,
b + 1;

select distinct on (
a,
b + 1)
select distinct on (a, b + 1)
a,
b + 1
from
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: crates/pgls_pretty_print/tests/tests.rs
input_file: crates/pgls_pretty_print/tests/data/multi/memoize.sql
snapshot_kind: text
---
create function explain_memoize(query text, hide_hitmiss boolean)
returns setof text
Expand Down Expand Up @@ -431,8 +430,7 @@ from
tab_anti as t1
left outer join
lateral (
select distinct on (
a)
select distinct on (a)
a,
b,
t1.a as x
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: crates/pgls_pretty_print/tests/tests.rs
input_file: crates/pgls_pretty_print/tests/data/multi/memoize.sql
snapshot_kind: text
---
create function explain_memoize(query text, hide_hitmiss boolean)
returns setof text
Expand Down Expand Up @@ -457,8 +456,7 @@ from
tab_anti as t1
left outer join
lateral (
select distinct on (
a)
select distinct on (a)
a,
b,
t1.a as x
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
---
source: crates/pgls_pretty_print/tests/tests.rs
input_file: crates/pgls_pretty_print/tests/data/multi/select_distinct_on.sql
snapshot_kind: text
---
select distinct on (
string4)
select distinct on (string4)
string4,
two,
ten
Expand All @@ -14,9 +12,7 @@ order by string4 using <,
two using >,
ten using <;

select distinct on (
string4,
ten)
select distinct on (string4, ten)
string4,
two,
ten
Expand All @@ -26,9 +22,7 @@ order by string4 using <,
two using <,
ten using <;

select distinct on (
string4,
ten)
select distinct on (string4, ten)
string4,
ten,
two
Expand Down Expand Up @@ -74,9 +68,7 @@ select distinct on (y, x) x, y from distinct_on_tbl order by y;

select distinct on (y, x) x, y from distinct_on_tbl order by y;

select distinct on (
y,
x)
select distinct on (y, x)
x,
y
from
Expand All @@ -88,9 +80,7 @@ order by y,
x,
z;

select distinct on (
y,
x)
select distinct on (y, x)
x,
y
from
Expand Down
Loading