pg_query 0.7.0

PostgreSQL parser that uses the actual PostgreSQL server source to parse SQL queries and return the internal PostgreSQL parse tree.
Documentation
#![allow(non_snake_case)]
#![cfg(test)]

use pg_query::parse;

#[cfg(test)]
use regex::Regex;

fn assert_deparse(input: &str, output: &str) {
    let result = parse(input).unwrap();
    assert_eq!(result.deparse().unwrap(), output);
}

fn oneline(query: &str) -> String {
    let query = Regex::new(r"\s+").unwrap().replace_all(&query, " ");
    let query = Regex::new(r"\( ").unwrap().replace_all(&query, "(");
    let query = Regex::new(r" \)").unwrap().replace_all(&query, ")");
    query.trim().trim_end_matches(";").to_string()
}

#[test]
fn it_deparses_SELECT() {
    let query = "SELECT a AS b FROM x WHERE y = 5 AND z = y";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_with_empty_target_list() {
    let query = "SELECT FROM x WHERE y = 5 AND z = y";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_with_schema() {
    let query = "SELECT a AS b FROM public.x WHERE y = 5 AND z = y";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_with_DISTINCT() {
    let query = "SELECT DISTINCT a, b, * FROM c WHERE d = e";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_with_DISTINCT_ON() {
    let query = "SELECT DISTINCT ON (a) a, b FROM c";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_with_INTO() {
    let query = "SELECT * INTO films_recent FROM films WHERE date_prod >= '2002-01-01'";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_value_function() {
    let query = "SELECT current_timestamp";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_value_function_with_precision() {
    let query = "SELECT current_time(2)";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_complex() {
    let query = "SELECT memory_total_bytes, memory_swap_total_bytes - memory_swap_free_bytes AS swap, date_part(?, s.collected_at) AS collected_at FROM snapshots s JOIN system_snapshots ON snapshot_id = s.id WHERE s.database_id = ? AND s.collected_at >= ? AND s.collected_at <= ? ORDER BY collected_at ASC";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_ORDER_BY_with_NULLS_FIRST() {
    let query = "SELECT * FROM a ORDER BY x ASC NULLS FIRST";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_ORDER_BY_with_NULLS_LAST() {
    let query = "SELECT * FROM a ORDER BY x ASC NULLS LAST";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_ORDER_BY_with_COLLATE() {
    let query = r#"SELECT * FROM a ORDER BY x COLLATE "tr_TR" DESC NULLS LAST"#;
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_text_with_COLLATE() {
    let query = r#"SELECT 'foo' COLLATE "tr_TR""#;
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_UNION_or_UNION_ALL() {
    let query = "WITH kodsis AS (SELECT * FROM application), kodsis2 AS (SELECT * FROM application) SELECT * FROM kodsis UNION SELECT * FROM kodsis ORDER BY id DESC";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_UNION_with_ORDER() {
    let query = "SELECT id, name FROM table1 UNION (SELECT id, name FROM table2 ORDER BY name) ORDER BY id ASC";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_EXCEPT() {
    let query = "SELECT a FROM kodsis EXCEPT SELECT a FROM application";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_INTERSECT() {
    let query = "SELECT 'a' INTERSECT SELECT 'b'";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_with_specific_column_alias() {
    let query = "SELECT * FROM (VALUES ('anne', 'smith'), ('bob', 'jones'), ('joe', 'blow')) names(first, last)";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_with_LIKE_filter() {
    let query = "SELECT * FROM users WHERE name LIKE 'postgresql:%';";
    assert_deparse(query, &oneline(query));
}

#[test]
fn it_deparses_SELECT_with_NOT_LIKE_filter() {
    let query = "SELECT * FROM users WHERE name NOT LIKE 'postgresql:%';";
    assert_deparse(query, &oneline(query));
}

#[test]
fn it_deparses_SELECT_with_ILIKE_filter() {
    let query = "SELECT * FROM users WHERE name ILIKE 'postgresql:%';";
    assert_deparse(query, &oneline(query));
}

#[test]
fn it_deparses_SELECT_with_NOT_ILIKE_filter() {
    let query = "SELECT * FROM users WHERE name NOT ILIKE 'postgresql:%';";
    assert_deparse(query, &oneline(query));
}

#[test]
fn it_deparses_SELECT_simple_WITH() {
    let query = "WITH t AS (SELECT random() AS x FROM generate_series(1, 3)) SELECT * FROM t";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_complex_WITH() {
    // Taken from http://www.postgresql.org/docs/9.1/static/queries-with.html
    let query = "
        WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (
          SELECT g.id, g.link, g.data, 1,
            ARRAY[ROW(g.f1, g.f2)],
            false
          FROM graph g
        UNION ALL
          SELECT g.id, g.link, g.data, sg.depth + 1,
            path || ROW(g.f1, g.f2),
            ROW(g.f1, g.f2) = ANY(path)
          FROM graph g, search_graph sg
          WHERE g.id = sg.link AND NOT cycle
        )
        SELECT id, data, link FROM search_graph;
    ";
    assert_deparse(query, &oneline(query));
}

#[test]
fn it_deparses_SELECT_OVERLAY() {
    let query = "SELECT OVERLAY(m.name PLACING \'******\' FROM 3 FOR 6) AS tc_kimlik FROM tb_test m";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_SUM() {
    let query = "SELECT sum(price_cents) FROM products";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_ARRAY() {
    let query = "SELECT ARRAY(SELECT id FROM products)::bigint[]";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_LATERAL() {
    let query = "SELECT m.name AS mname, pname FROM manufacturers m, LATERAL get_product_names(m.id) pname";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_LATERAL_JOIN() {
    let query = "
        SELECT m.name AS mname, pname
        FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true
    ";
    assert_deparse(query, &oneline(query));
}

#[test]
fn it_deparses_SELECT_LATERAL_JOIN_with_nested_query() {
    let query = "
        SELECT *
        FROM tb_test_main mh
        JOIN LATERAL (
            SELECT ftnrm.* FROM test ftnrm WHERE ftnrm.hizmet_id = mh.id
            UNION ALL
            SELECT ftarc.* FROM test.test2 ftarc WHERE ftarc.hizmet_id = mh.id
        ) ft ON true
    ";
    assert_deparse(query, &oneline(query));
}

#[test]
fn it_deparses_SELECT_CROSS_JOIN() {
    let query = "SELECT x, y FROM a CROSS JOIN b";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_NATURAL_JOIN() {
    let query = "SELECT x, y FROM a NATURAL JOIN b";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_LEFT_JOIN() {
    let query = "SELECT x, y FROM a LEFT JOIN b ON 1 > 0";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_RIGHT_JOIN() {
    let query = "SELECT x, y FROM a RIGHT JOIN b ON 1 > 0";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_FULL_JOIN() {
    let query = "SELECT x, y FROM a FULL JOIN b ON 1 > 0";
    assert_deparse(query, query);
}

#[test]
fn it_deparses_SELECT_JOIN_with_USING() {
    let query = "SELECT x, y FROM a JOIN b USING (z)";
    assert_deparse(query, query);
}

// There are many more Ruby tests but we probably don't need to implement
// them here since we're just passing the protobuf back to C
// https://github.com/pganalyze/pg_query/tree/main/spec/lib/pg_query/deparse_spec.rb