Skip to content

Commit

Permalink
Merge pull request #2500 from HTGAzureX1212/nightly
Browse files Browse the repository at this point in the history
Database: Use `sqlparser` Instead
  • Loading branch information
HTGAzureX1212 authored Jan 29, 2025
2 parents 7a7fec3 + f841b28 commit eb57dd1
Show file tree
Hide file tree
Showing 11 changed files with 223 additions and 837 deletions.
343 changes: 49 additions & 294 deletions api-backend/Cargo.lock

Large diffs are not rendered by default.

318 changes: 48 additions & 270 deletions database/Cargo.lock

Large diffs are not rendered by default.

10 changes: 0 additions & 10 deletions database/hartex-database-queries/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,7 @@
* with HarTex. If not, see <https://www.gnu.org/licenses/>.
*/

use std::env;

use cornucopia::CodegenSettings;
use postgres::Client;
use postgres::NoTls;

pub fn main() {
if dotenvy::dotenv().is_err() {
return;
}

/*let api_backend_queries_path = "queries/api_backend";
println!("cargo::rerun-if-changed={api_backend_queries_path}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,5 @@ CREATE TABLE IF NOT EXISTS "Nightly"."CachedUsers" (

CREATE TABLE IF NOT EXISTS "Nightly"."GuildConfigurations" (
"guild_id" TEXT NOT NULL PRIMARY KEY,
"enabled_plugins" TEXT ARRAY NOT NULL
"enabled_plugins" TEXT[] NOT NULL
);
2 changes: 1 addition & 1 deletion database/hartex-database-typedsql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ rust-version = "1.86.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
pg_query = "6.0.0"
sqlparser = "0.54.0"
walkdir = "2.5.0"

[build-dependencies]
10 changes: 6 additions & 4 deletions database/hartex-database-typedsql/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@

use std::io;

use sqlparser::parser::ParserError;

#[derive(Debug)]
pub enum Error {
Io(io::Error),
QueryFile(&'static str),
SqlParse(pg_query::Error),
SqlError(ParserError)
}

impl From<io::Error> for Error {
Expand All @@ -35,9 +37,9 @@ impl From<io::Error> for Error {
}
}

impl From<pg_query::Error> for Error {
fn from(err: pg_query::Error) -> Error {
Self::SqlParse(err)
impl From<ParserError> for Error {
fn from(err: ParserError) -> Error {
Self::SqlError(err)
}
}

Expand Down
8 changes: 7 additions & 1 deletion database/hartex-database-typedsql/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,24 @@

#![deny(clippy::pedantic)]
#![deny(unsafe_code)]
#![deny(warnings)]
//#![deny(warnings)]
#![allow(dead_code)]
#![allow(incomplete_features)]
#![allow(unreachable_code)]
#![feature(deref_patterns)]
#![feature(let_chains)]

use std::collections::HashMap;
use std::path::Path;

use sqlparser::dialect::PostgreSqlDialect;

mod error;
mod query;
mod schema;

pub(crate) const POSTGRESQL_DIALECT: PostgreSqlDialect = PostgreSqlDialect {};

#[allow(clippy::missing_errors_doc)]
pub fn generate_queries_with_schemas<P>(schemas_dir: P, queries_dir: P, _: P) -> error::Result<()>
where
Expand Down
32 changes: 17 additions & 15 deletions database/hartex-database-typedsql/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@ use std::fs;
use std::path::Path;
use std::path::PathBuf;

use pg_query::protobuf::node::Node;
use sqlparser::ast::Query;
use sqlparser::ast::SetExpr;
use sqlparser::ast::Statement;
use sqlparser::parser::Parser;
use walkdir::WalkDir;

use crate::error::Error;
use crate::POSTGRESQL_DIALECT;
use crate::schema::SchemaInfo;

mod select;
Expand Down Expand Up @@ -71,21 +74,20 @@ pub(crate) fn parse_query(
query_info: &RawQueryInfo,
schema_map: HashMap<String, SchemaInfo>,
) -> crate::error::Result<QueryInfo> {
let result = pg_query::parse(query_info.contents.as_str())?;
let stmt = result
.protobuf
.stmts
let statement = Parser::parse_sql(&POSTGRESQL_DIALECT, &query_info.contents)?
.first()
.cloned()
.ok_or(Error::QueryFile("expected at least one query"))?
.stmt
.ok_or(Error::QueryFile("unexpected empty node"))?
.node
.ok_or(Error::QueryFile("unexpected empty inner node"))?;
.ok_or(crate::error::Error::QueryFile(
"no query found in query file",
))?;

match stmt {
// todo: add more branches
Node::SelectStmt(stmt) => select::parse_select_query(stmt.as_ref().clone(), schema_map),
_ => Err(Error::QueryFile("unexpected statement type")),
match statement {
Statement::Query(
deref!(Query {
body: deref!(SetExpr::Select(deref!(ref select))),
..
}),
) => select::parse_select_query(select.clone(), schema_map),
_ => Err(crate::error::Error::QueryFile("unsupported query type")),
}
}
61 changes: 4 additions & 57 deletions database/hartex-database-typedsql/src/query/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,69 +22,16 @@

use std::collections::HashMap;

use pg_query::protobuf::SelectStmt;
use pg_query::protobuf::node::Node;
use sqlparser::ast::Select;

use crate::schema::SchemaInfo;

#[derive(Clone, Debug)]
pub(crate) enum SelectTarget {
Everything,
}

#[derive(Clone, Debug)]
pub(crate) struct SelectQueryInfo {
pub(crate) target: SelectTarget,
}
pub(crate) struct SelectQueryInfo {}

pub(crate) fn parse_select_query(
stmt: SelectStmt,
_: Select,
_: HashMap<String, SchemaInfo>,
) -> crate::error::Result<super::QueryInfo> {
let target_column_fields = stmt
.target_list
.into_iter()
.filter_map(|node| node.node)
.filter_map(|node| {
if let Node::ResTarget(res_target) = node {
Some(res_target)
} else {
None
}
})
.filter_map(|res_target| res_target.val)
.filter_map(|node| node.node)
.filter_map(|node| {
if let Node::ColumnRef(column_ref) = node {
Some(column_ref)
} else {
None
}
})
.map(|node| {
node.fields
.into_iter()
.filter_map(|node| node.node)
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
let target = parse_select_target(target_column_fields)?;

Ok(super::QueryInfo::Select(SelectQueryInfo { target }))
}

fn parse_select_target(fields: Vec<Vec<Node>>) -> crate::error::Result<SelectTarget> {
let first = fields.first().ok_or(crate::error::Error::QueryFile(
"expected at least one node in select target",
))?;

// SELECT * FROM ...
// ^ everything
if let Some(Node::AStar(_)) = first.first() && first.len() == 1 {
return Ok(SelectTarget::Everything);
}

// todo: support selecting a collection of fields

Err(crate::error::Error::QueryFile("unsupported select target (yet)"))
Err(crate::error::Error::QueryFile("todo"))
}
85 changes: 36 additions & 49 deletions database/hartex-database-typedsql/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,31 @@ use std::fs;
use std::path::Path;
use std::path::PathBuf;

use pg_query::protobuf::node::Node;
use sqlparser::ast::ColumnDef;
use sqlparser::ast::ColumnOption;
use sqlparser::ast::CreateTable;
use sqlparser::ast::DataType;
use sqlparser::ast::Statement;
use sqlparser::parser::Parser;

use crate::POSTGRESQL_DIALECT;

#[allow(dead_code)]
#[derive(Clone, Debug)]
pub(crate) struct ColumnInfo {
pub(crate) name: String,
pub(crate) coltype: String,
pub(crate) coltype: DataType,
pub(crate) constraints: Vec<ColumnOption>,
}

impl From<ColumnDef> for ColumnInfo {
fn from(value: ColumnDef) -> Self {
Self {
name: value.name.to_string(),
coltype: value.data_type,
constraints: value.options.into_iter().map(|opt| opt.option).collect(),
}
}
}

#[allow(dead_code)]
Expand All @@ -47,6 +65,15 @@ pub(crate) struct TableInfo {
pub(crate) columns: Vec<ColumnInfo>,
}

impl From<CreateTable> for TableInfo {
fn from(value: CreateTable) -> Self {
Self {
name: value.name.to_string(),
columns: value.columns.into_iter().map(ColumnInfo::from).collect(),
}
}
}

#[allow(dead_code)]
#[derive(Debug)]
pub(crate) struct RawSchemaInfo {
Expand Down Expand Up @@ -89,56 +116,16 @@ pub(crate) fn read_schemas(
#[allow(clippy::missing_errors_doc)]
#[allow(clippy::needless_pass_by_value)]
pub(crate) fn parse_schema(schema_info: RawSchemaInfo) -> crate::error::Result<SchemaInfo> {
let result = pg_query::parse(schema_info.contents.as_str())?;
let statements = result.protobuf.stmts;
let tables = statements
let tables = Parser::parse_sql(&POSTGRESQL_DIALECT, schema_info.contents.as_str())?
.into_iter()
.filter_map(|statement| {
if let Some(Node::CreateStmt(create)) = statement.clone().stmt?.node {
Some(create)
} else {
None
}
})
.filter_map(|create| {
create
.relation
.map(|relation| (relation, create.table_elts))
})
.map(|(relation, nodes)| {
(
relation,
nodes
.into_iter()
.filter_map(|node| {
if let Some(Node::ColumnDef(def)) = node.node {
Some(def)
} else {
None
}
})
.filter(|def| def.type_name.is_some())
.map(|def| (def.colname, def.type_name.unwrap()))
.map(|(name, coltype)| {
let Node::String(string) =
coltype.clone().names.last().unwrap().clone().node.unwrap()
else {
unreachable!()
};
.filter_map(|st| {
let Statement::CreateTable(ct) = st else {
return None;
};

ColumnInfo {
name,
coltype: string.sval,
}
})
.collect::<Vec<_>>(),
)
})
.map(|(relation, columns)| TableInfo {
name: relation.relname,
columns,
Some(TableInfo::from(ct))
})
.collect::<Vec<_>>();
.collect();

Ok(SchemaInfo {
name: schema_info.name,
Expand Down
Loading

0 comments on commit eb57dd1

Please sign in to comment.