use std::borrow::Cow;
use crate::query::Select;
use crate::query::write_select;
use crate::writer::SqlWriter;
mod qualification;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Iden {
name: Cow<'static, str>,
escaped: bool,
}
impl Iden {
pub const fn new_static(name: &'static str) -> Self {
let escaped = is_escaped_iden(name);
Self {
name: Cow::Borrowed(name),
escaped,
}
}
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
let name = name.into();
let escaped = is_escaped_iden(&name);
Self { name, escaped }
}
pub fn is_escaped(&self) -> bool {
self.escaped
}
pub fn into_inner(self) -> Cow<'static, str> {
self.name
}
}
const fn is_escaped_iden(string: &str) -> bool {
let bytes = string.as_bytes();
if bytes.is_empty() {
return true;
}
if bytes[0] == b'_' || (bytes[0] as char).is_ascii_alphabetic() {
} else {
return false;
}
let mut i = 1;
while i < bytes.len() {
if bytes[i] == b'_' || (bytes[i] as char).is_ascii_alphanumeric() {
} else {
return false;
}
i += 1;
}
true
}
impl From<&'static str> for Iden {
fn from(name: &'static str) -> Self {
Iden::new(name)
}
}
impl From<String> for Iden {
fn from(name: String) -> Self {
Iden::new(name)
}
}
impl From<Cow<'static, str>> for Iden {
fn from(name: Cow<'static, str>) -> Self {
Iden::new(name)
}
}
pub trait IntoIden {
fn into_iden(self) -> Iden;
}
impl<T> IntoIden for T
where
T: Into<Iden>,
{
fn into_iden(self) -> Iden {
self.into()
}
}
#[derive(Default, Debug, Clone, Copy)]
pub struct Asterisk;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum TableRef {
Table(TableName, Option<Iden>),
SubQuery(Box<Select>, Iden),
}
impl TableRef {
pub fn alias<A>(self, alias: A) -> Self
where
A: IntoIden,
{
match self {
Self::Table(table, _) => Self::Table(table, Some(alias.into_iden())),
Self::SubQuery(statement, _) => Self::SubQuery(statement, alias.into_iden()),
}
}
}
impl<T> From<T> for TableRef
where
T: Into<TableName>,
{
fn from(value: T) -> Self {
TableRef::Table(value.into(), None)
}
}
pub trait IntoTableRef: Into<TableRef> {
fn into_table_ref(self) -> TableRef {
self.into()
}
}
impl<T> IntoTableRef for T where T: Into<TableRef> {}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum ColumnRef {
Column(ColumnName),
Asterisk(Option<TableName>),
}
impl From<Asterisk> for ColumnRef {
fn from(_: Asterisk) -> Self {
ColumnRef::Asterisk(None)
}
}
impl<Table> From<(Table, Asterisk)> for ColumnRef
where
Table: IntoIden,
{
fn from(table: (Table, Asterisk)) -> Self {
ColumnRef::Asterisk(Some(table.0.into()))
}
}
impl<Schema, Table> From<(Schema, Table, Asterisk)> for ColumnRef
where
Schema: IntoIden,
Table: IntoIden,
{
fn from(table: (Schema, Table, Asterisk)) -> Self {
ColumnRef::Asterisk(Some((table.0, table.1).into()))
}
}
impl<Database, Schema, Table> From<(Database, Schema, Table, Asterisk)> for ColumnRef
where
Database: IntoIden,
Schema: IntoIden,
Table: IntoIden,
{
fn from(table: (Database, Schema, Table, Asterisk)) -> Self {
ColumnRef::Asterisk(Some((table.0, table.1, table.2).into()))
}
}
impl<T> From<T> for ColumnRef
where
T: Into<ColumnName>,
{
fn from(value: T) -> Self {
ColumnRef::Column(value.into())
}
}
pub trait IntoColumnRef: Into<ColumnRef> {
fn into_column_ref(self) -> ColumnRef {
self.into()
}
}
impl<T> IntoColumnRef for T where T: Into<ColumnRef> {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DatabaseName(pub Iden);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SchemaName(pub Option<DatabaseName>, pub Iden);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TableName(pub Option<SchemaName>, pub Iden);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ColumnName(pub Option<TableName>, pub Iden);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
#[expect(missing_docs)]
pub enum JoinType {
LeftJoin,
InnerJoin,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DropBehavior {
Cascade,
Restrict,
}
pub(crate) fn write_iden<W: SqlWriter>(w: &mut W, iden: &Iden) {
const QUOTE: char = '"';
w.push_char(QUOTE);
if iden.escaped {
w.push_str(&iden.name);
} else {
for ch in iden.name.chars() {
if ch == QUOTE {
w.push_char(QUOTE);
}
w.push_char(ch);
}
}
w.push_char(QUOTE);
}
pub(crate) fn write_table_name<W: SqlWriter>(w: &mut W, table_name: &TableName) {
let TableName(schema_name, table) = table_name;
if let Some(schema_name) = schema_name {
write_schema_name(w, schema_name);
w.push_char('.');
}
write_iden(w, table);
}
pub(crate) fn write_schema_name<W: SqlWriter>(w: &mut W, schema_name: &SchemaName) {
let SchemaName(database_name, schema) = schema_name;
if let Some(DatabaseName(database)) = database_name {
write_iden(w, database);
w.push_char('.');
}
write_iden(w, schema);
}
pub(crate) fn write_table_ref<W: SqlWriter>(w: &mut W, table_ref: &TableRef) {
match table_ref {
TableRef::Table(table_name, alias) => {
write_table_name(w, table_name);
if let Some(alias) = alias {
w.push_str(" AS ");
write_iden(w, alias);
}
}
TableRef::SubQuery(query, alias) => {
w.push_char('(');
write_select(w, query);
w.push_char(')');
w.push_str(" AS ");
write_iden(w, alias);
}
}
}