aboutsummaryrefslogtreecommitdiff
path: root/development/libs/barrel/src/backend
diff options
context:
space:
mode:
authorMx Kookie <kookie@spacekookie.de>2020-10-31 20:17:22 +0100
committerMx Kookie <kookie@spacekookie.de>2020-12-21 05:19:21 +0100
commita9f4dd41c11bc4255823ebad7ceabf033cf32ecf (patch)
treed91064e96e60f444076c9ffc122a47afe3b22a72 /development/libs/barrel/src/backend
parent534807c7a3a3b96d219ec0ce9bb0b3029aa1bdd9 (diff)
Add 'development/libs/barrel/' from commit 'f3b8ab47d3a3ad8d43dc2b89a5eec1c4e87b033d'
git-subtree-dir: development/libs/barrel git-subtree-mainline: 518551bfa6ef7d6508b425afa4bfb3ddbd418141 git-subtree-split: f3b8ab47d3a3ad8d43dc2b89a5eec1c4e87b033d
Diffstat (limited to 'development/libs/barrel/src/backend')
-rw-r--r--development/libs/barrel/src/backend/mod.rs89
-rw-r--r--development/libs/barrel/src/backend/mysql.rs162
-rw-r--r--development/libs/barrel/src/backend/pg.rs164
-rw-r--r--development/libs/barrel/src/backend/sqlite3.rs155
4 files changed, 570 insertions, 0 deletions
diff --git a/development/libs/barrel/src/backend/mod.rs b/development/libs/barrel/src/backend/mod.rs
new file mode 100644
index 000000000000..107b718a7170
--- /dev/null
+++ b/development/libs/barrel/src/backend/mod.rs
@@ -0,0 +1,89 @@
+//! A backend module which provides a few generic traits
+//! to implement SQL generation for different databases.
+//!
+//! It also re-exports the generators for existing databases
+//! so they can be used more conveniently.
+
+#[cfg(feature = "mysql")]
+mod mysql;
+#[cfg(feature = "mysql")]
+pub use self::mysql::MySql;
+
+#[cfg(feature = "pg")]
+mod pg;
+#[cfg(feature = "pg")]
+pub use self::pg::Pg;
+
+#[cfg(feature = "sqlite3")]
+mod sqlite3;
+#[cfg(feature = "sqlite3")]
+pub use self::sqlite3::Sqlite;
+
+#[allow(unused_imports)]
+use crate::{types::Type, Migration};
+
+/// An enum describing all supported Sql flavours
+#[derive(Copy, Clone, Debug)]
+pub enum SqlVariant {
+ #[cfg(feature = "sqlite3")]
+ Sqlite,
+ #[cfg(feature = "pg")]
+ Pg,
+ #[cfg(feature = "mysql")]
+ Mysql,
+ #[doc(hidden)]
+ __Empty,
+}
+
+impl SqlVariant {
+ pub(crate) fn run_for(self, _migr: &Migration) -> String {
+ match self {
+ #[cfg(feature = "sqlite3")]
+ SqlVariant::Sqlite => _migr.make::<Sqlite>(),
+
+ #[cfg(feature = "pg")]
+ SqlVariant::Pg => _migr.make::<Pg>(),
+
+ #[cfg(feature = "mysql")]
+ SqlVariant::Mysql => _migr.make::<MySql>(),
+
+ _ => panic!("You need to select an Sql variant!"),
+ }
+ }
+}
+
+/// A generic SQL generator trait
+pub trait SqlGenerator {
+ /// Create a new table with a name
+ fn create_table(name: &str, schema: Option<&str>) -> String;
+
+ /// Create a new table with a name, only if it doesn't exist
+ fn create_table_if_not_exists(name: &str, schema: Option<&str>) -> String;
+
+ /// Drop a table with a name
+ fn drop_table(name: &str, schema: Option<&str>) -> String;
+
+ /// Drop a table with a name, only if it exists
+ fn drop_table_if_exists(name: &str, schema: Option<&str>) -> String;
+
+ /// Rename a table from <old> to <new>
+ fn rename_table(old: &str, new: &str, schema: Option<&str>) -> String;
+
+ /// Modify a table in some other way
+ fn alter_table(name: &str, schema: Option<&str>) -> String;
+
+ /// Create a new column with a type
+ fn add_column(ex: bool, schema: Option<&str>, name: &str, _type: &Type) -> String;
+
+ /// Drop an existing column from the table
+ fn drop_column(name: &str) -> String;
+
+ /// Rename an existing column
+ fn rename_column(old: &str, new: &str) -> String;
+
+ /// Create a multi-column index
+ fn create_index(table: &str, schema: Option<&str>, name: &str, _type: &Type) -> String;
+
+ /// Drop a multi-column index
+ fn drop_index(name: &str) -> String;
+}
diff --git a/development/libs/barrel/src/backend/mysql.rs b/development/libs/barrel/src/backend/mysql.rs
new file mode 100644
index 000000000000..5b27c69e334a
--- /dev/null
+++ b/development/libs/barrel/src/backend/mysql.rs
@@ -0,0 +1,162 @@
+//! MySQL implementation of a generator
+//!
+//! This module generates strings that are specific to MySQL
+//! databases. They should be thoroughly tested via unit testing
+
+use super::SqlGenerator;
+use crate::types::{BaseType, Type};
+
+/// A simple macro that will generate a schema prefix if it exists
+macro_rules! prefix {
+ ($schema:expr) => {
+ $schema
+ .map(|s| format!("`{}`.", s))
+ .unwrap_or_else(|| String::new())
+ };
+}
+
+/// MySQL generator backend
+pub struct MySql;
+impl SqlGenerator for MySql {
+ fn create_table(name: &str, schema: Option<&str>) -> String {
+ format!("CREATE TABLE {}`{}`", prefix!(schema), name)
+ }
+
+ fn create_table_if_not_exists(name: &str, schema: Option<&str>) -> String {
+ format!("CREATE TABLE {}`{}` IF NOT EXISTS", prefix!(schema), name)
+ }
+
+ fn drop_table(name: &str, schema: Option<&str>) -> String {
+ format!("DROP TABLE {}`{}`", prefix!(schema), name)
+ }
+
+ fn drop_table_if_exists(name: &str, schema: Option<&str>) -> String {
+ format!("DROP TABLE {}`{}` IF EXISTS", prefix!(schema), name)
+ }
+
+ fn rename_table(old: &str, new: &str, schema: Option<&str>) -> String {
+ let schema = prefix!(schema);
+ format!("RENAME TABLE {}`{}` TO {}`{}`", schema, old, schema, new)
+ }
+
+ fn alter_table(name: &str, schema: Option<&str>) -> String {
+ format!("ALTER TABLE {}`{}`", prefix!(schema), name)
+ }
+
+ fn add_column(ex: bool, schema: Option<&str>, name: &str, tt: &Type) -> String {
+ let bt: BaseType = tt.get_inner();
+ use self::BaseType::*;
+ let name = format!("`{}`", name);
+
+ #[cfg_attr(rustfmt, rustfmt_skip)] /* This shouldn't be formatted. It's too long */
+ format!(
+ "{}{}{}{}{}",
+ match bt {
+ Text => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
+ Varchar(_) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
+ Primary => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
+ Integer => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
+ Float => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
+ Double => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
+ UUID => unimplemented!(),
+ Json => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
+ Boolean => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
+ Date => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
+ Binary => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
+ Foreign(_, _, _) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
+ Custom(_) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
+ Array(it) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(Array(Box::new(*it)), schema)),
+ Index(_) => unreachable!(),
+ },
+ match tt.primary {
+ true => " PRIMARY KEY",
+ false => "",
+ },
+ match (&tt.default).as_ref() {
+ Some(ref m) => format!(" DEFAULT '{}'", m),
+ _ => format!(""),
+ },
+ match tt.nullable {
+ true => "",
+ false => " NOT NULL",
+ },
+ match tt.unique {
+ true => " UNIQUE",
+ false => "",
+ },
+ )
+ }
+
+ fn drop_column(name: &str) -> String {
+ format!("DROP COLUMN `{}`", name)
+ }
+
+ fn rename_column(old: &str, new: &str) -> String {
+ format!("CHANGE COLUMN `{}` `{}`", old, new)
+ }
+
+ fn create_index(table: &str, schema: Option<&str>, name: &str, _type: &Type) -> String {
+ // FIXME: Implement Mysql specific index builder here
+ format!(
+ "CREATE {} INDEX `{}` ON {}`{}` ({})",
+ match _type.unique {
+ true => "UNIQUE",
+ false => "",
+ },
+ name,
+ prefix!(schema),
+ table,
+ match _type.inner {
+ BaseType::Index(ref cols) => cols
+ .iter()
+ .map(|col| format!("`{}`", col))
+ .collect::<Vec<_>>()
+ .join(", "),
+ _ => unreachable!(),
+ }
+ )
+ }
+
+ fn drop_index(name: &str) -> String {
+ format!("DROP INDEX `{}`", name)
+ }
+}
+
+impl MySql {
+ fn prefix(ex: bool) -> String {
+ match ex {
+ true => format!("ADD COLUMN "),
+ false => format!(""),
+ }
+ }
+
+ fn print_type(t: BaseType, schema: Option<&str>) -> String {
+ use self::BaseType::*;
+ match t {
+ Text => format!("TEXT"),
+ Varchar(l) => match l {
+ 0 => format!("VARCHAR"), // For "0" remove the limit
+ _ => format!("VARCHAR({})", l),
+ },
+ /* "NOT NULL" is added here because normally primary keys are implicitly not-null */
+ Primary => format!("INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY"),
+ Integer => format!("INTEGER"),
+ Float => format!("FLOAT"),
+ Double => format!("DOUBLE"),
+ UUID => format!("CHAR(36)"),
+ Boolean => format!("BOOLEAN"),
+ Date => format!("DATE"),
+ Json => format!("JSON"),
+ Binary => format!("BYTEA"),
+ Foreign(s, t, refs) => format!(
+ "INTEGER REFERENCES {}{}({})",
+ prefix!(s),
+ t,
+ refs.0.join(",")
+ ),
+ Custom(t) => format!("{}", t),
+ Array(meh) => format!("{}[]", MySql::print_type(*meh, schema)),
+ Index(_) => unreachable!(),
+ }
+ }
+}
diff --git a/development/libs/barrel/src/backend/pg.rs b/development/libs/barrel/src/backend/pg.rs
new file mode 100644
index 000000000000..1bea666efa1a
--- /dev/null
+++ b/development/libs/barrel/src/backend/pg.rs
@@ -0,0 +1,164 @@
+//! Postgres implementation of a generator
+//!
+//! This module generates strings that are specific to Postgres
+//! databases. They should be thoroughly tested via unit testing
+
+use super::SqlGenerator;
+use crate::types::{BaseType, Type};
+
+/// A simple macro that will generate a schema prefix if it exists
+macro_rules! prefix {
+ ($schema:expr) => {
+ $schema
+ .map(|s| format!("\"{}\".", s))
+ .unwrap_or_else(|| String::new())
+ };
+}
+
+/// Postgres SQL generator backend
+pub struct Pg;
+impl SqlGenerator for Pg {
+ fn create_table(name: &str, schema: Option<&str>) -> String {
+ format!("CREATE TABLE {}\"{}\"", prefix!(schema), name)
+ }
+
+ fn create_table_if_not_exists(name: &str, schema: Option<&str>) -> String {
+ format!("CREATE TABLE IF NOT EXISTS {}\"{}\"", prefix!(schema), name)
+ }
+
+ fn drop_table(name: &str, schema: Option<&str>) -> String {
+ format!("DROP TABLE {}\"{}\"", prefix!(schema), name)
+ }
+
+ fn drop_table_if_exists(name: &str, schema: Option<&str>) -> String {
+ format!("DROP TABLE IF EXISTS {}\"{}\"", prefix!(schema), name)
+ }
+
+ fn rename_table(old: &str, new: &str, schema: Option<&str>) -> String {
+ let schema = prefix!(schema);
+ format!(
+ "ALTER TABLE {}\"{}\" RENAME TO {}\"{}\"",
+ schema, old, schema, new
+ )
+ }
+
+ fn alter_table(name: &str, schema: Option<&str>) -> String {
+ format!("ALTER TABLE {}\"{}\"", prefix!(schema), name)
+ }
+
+ fn add_column(ex: bool, schema: Option<&str>, name: &str, tt: &Type) -> String {
+ let bt: BaseType = tt.get_inner();
+ use self::BaseType::*;
+
+ #[cfg_attr(rustfmt, rustfmt_skip)] /* This shouldn't be formatted. It's too long */
+ format!(
+ "{}{}{}{}{}",
+ match bt {
+ Text => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ Varchar(_) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ Primary => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ Integer => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ Float => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ Double => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ UUID => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ Json => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ Boolean => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ Date => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ Binary => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ Foreign(_, _, _) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ Custom(_) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
+ Array(it) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(Array(Box::new(*it)), schema)),
+ Index(_) => unreachable!(), // Indices are handled via custom builder
+ },
+ match tt.primary {
+ true => " PRIMARY KEY",
+ false => "",
+ },
+ match (&tt.default).as_ref() {
+ Some(ref m) => format!(" DEFAULT '{}'", m),
+ _ => format!(""),
+ },
+ match tt.nullable {
+ true => "",
+ false => " NOT NULL",
+ },
+ match tt.unique {
+ true => " UNIQUE",
+ false => "",
+ },
+ )
+ }
+
+ fn drop_column(name: &str) -> String {
+ format!("DROP COLUMN \"{}\"", name)
+ }
+
+ fn rename_column(old: &str, new: &str) -> String {
+ format!("ALTER COLUMN \"{}\" RENAME TO \"{}\"", old, new)
+ }
+
+ fn create_index(table: &str, schema: Option<&str>, name: &str, _type: &Type) -> String {
+ // FIXME: Implement PG specific index builder here
+ format!(
+ "CREATE {} INDEX \"{}\" ON {}\"{}\" ({})",
+ match _type.unique {
+ true => "UNIQUE",
+ false => "",
+ },
+ name,
+ prefix!(schema),
+ table,
+ match _type.inner {
+ BaseType::Index(ref cols) => cols
+ .iter()
+ .map(|col| format!("\"{}\"", col))
+ .collect::<Vec<_>>()
+ .join(", "),
+ _ => unreachable!(),
+ }
+ )
+ }
+
+ fn drop_index(name: &str) -> String {
+ format!("DROP INDEX \"{}\"", name)
+ }
+}
+
+impl Pg {
+ fn prefix(ex: bool) -> String {
+ match ex {
+ true => format!("ADD COLUMN "),
+ false => format!(""),
+ }
+ }
+
+ fn print_type(t: BaseType, schema: Option<&str>) -> String {
+ use self::BaseType::*;
+ match t {
+ Text => format!("TEXT"),
+ Varchar(l) => match l {
+ 0 => format!("VARCHAR"), // For "0" remove the limit
+ _ => format!("VARCHAR({})", l),
+ },
+ /* "NOT NULL" is added here because normally primary keys are implicitly not-null */
+ Primary => format!("SERIAL PRIMARY KEY NOT NULL"),
+ Integer => format!("INTEGER"),
+ Float => format!("FLOAT"),
+ Double => format!("DOUBLE PRECISION"),
+ UUID => format!("UUID"),
+ Boolean => format!("BOOLEAN"),
+ Date => format!("DATE"),
+ Json => format!("JSON"),
+ Binary => format!("BYTEA"),
+ Foreign(s, t, refs) => format!(
+ "INTEGER REFERENCES {}\"{}\"({})",
+ prefix!(s.or(schema.map(|s| s.into()))),
+ t,
+ refs.0.join(",")
+ ),
+ Custom(t) => format!("{}", t),
+ Array(meh) => format!("{}[]", Pg::print_type(*meh, schema)),
+ Index(_) => unreachable!(), // Indices are handled via custom builder
+ }
+ }
+}
diff --git a/development/libs/barrel/src/backend/sqlite3.rs b/development/libs/barrel/src/backend/sqlite3.rs
new file mode 100644
index 000000000000..7c0d0438fa81
--- /dev/null
+++ b/development/libs/barrel/src/backend/sqlite3.rs
@@ -0,0 +1,155 @@
+//! Sqlite3 implementation of a generator
+
+use super::SqlGenerator;
+use crate::types::{BaseType, Type};
+
+/// A simple macro that will generate a schema prefix if it exists
+macro_rules! prefix {
+ ($schema:expr) => {
+ $schema
+ .map(|s| format!("\"{}\".", s))
+ .unwrap_or_else(|| String::new())
+ };
+}
+
+/// We call this struct Sqlite instead of Sqlite3 because we hope not
+/// to have to break the API further down the road
+pub struct Sqlite;
+impl SqlGenerator for Sqlite {
+ fn create_table(name: &str, schema: Option<&str>) -> String {
+ format!("CREATE TABLE {}\"{}\"", prefix!(schema), name)
+ }
+
+ fn create_table_if_not_exists(name: &str, schema: Option<&str>) -> String {
+ format!("CREATE TABLE IF NOT EXISTS {}\"{}\"", prefix!(schema), name)
+ }
+
+ fn drop_table(name: &str, schema: Option<&str>) -> String {
+ format!("DROP TABLE {}\"{}\"", prefix!(schema), name)
+ }
+
+ fn drop_table_if_exists(name: &str, schema: Option<&str>) -> String {
+ format!("DROP TABLE IF EXISTS {}\"{}\"", prefix!(schema), name)
+ }
+
+ fn rename_table(old: &str, new: &str, schema: Option<&str>) -> String {
+ let schema = prefix!(schema);
+ format!("ALTER TABLE {}\"{}\" RENAME TO \"{}\"", schema, old, new)
+ }
+
+ fn alter_table(name: &str, schema: Option<&str>) -> String {
+ format!("ALTER TABLE {}\"{}\"", prefix!(schema), name)
+ }
+
+ fn add_column(ex: bool, _: Option<&str>, name: &str, tt: &Type) -> String {
+ let bt: BaseType = tt.get_inner();
+ use self::BaseType::*;
+
+ #[cfg_attr(rustfmt, rustfmt_skip)] /* This shouldn't be formatted. It's too long */
+ format!(
+ // SQL base - default - nullable - unique
+ "{}{}{}{}{}",
+ match bt {
+ Text => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
+ Varchar(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
+ Primary => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
+ Integer => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
+ Float => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
+ Double => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
+ UUID => panic!("`UUID` not supported by Sqlite3. Use `Text` instead!"),
+ Json => panic!("`Json` not supported by Sqlite3. Use `Text` instead!"),
+ Boolean => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
+ Date => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
+ Binary => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
+ Foreign(_, _, _) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
+ Custom(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)),
+ Array(it) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(Array(Box::new(*it)))),
+ Index(_) => unreachable!(), // Indices are handled via custom builders
+ },
+ match tt.primary {
+ true => " PRIMARY KEY",
+ false => "",
+ },
+ match (&tt.default).as_ref() {
+ Some(ref m) => format!(" DEFAULT '{}'", m),
+ _ => format!(""),
+ },
+ match tt.nullable {
+ true => "",
+ false => " NOT NULL",
+ },
+ match tt.unique {
+ true => " UNIQUE",
+ false => "",
+ }
+ )
+ }
+
+ /// Create a multi-column index
+ fn create_index(table: &str, schema: Option<&str>, name: &str, _type: &Type) -> String {
+ format!(
+ "CREATE {} INDEX {}\"{}\" ON \"{}\" ({});",
+ match _type.unique {
+ true => "UNIQUE",
+ false => "",
+ },
+ prefix!(schema),
+ name,
+ table,
+ match _type.inner {
+ BaseType::Index(ref cols) => cols
+ .iter()
+ .map(|col| format!("\"{}\"", col))
+ .collect::<Vec<_>>()
+ .join(", "),
+ _ => unreachable!(),
+ }
+ )
+ }
+
+ /// Drop a multi-column index
+ fn drop_index(name: &str) -> String {
+ format!("DROP INDEX \"{}\"", name)
+ }
+
+ fn drop_column(_: &str) -> String {
+ panic!("Sqlite does not support dropping columns!")
+ }
+
+ fn rename_column(_: &str, _: &str) -> String {
+ panic!("Sqlite does not support renaming columns!")
+ }
+}
+
+impl Sqlite {
+ fn prefix(ex: bool) -> String {
+ match ex {
+ true => format!("ADD COLUMN "),
+ false => format!(""),
+ }
+ }
+
+ fn print_type(t: BaseType) -> String {
+ use self::BaseType::*;
+ match t {
+ Text => format!("TEXT"),
+ Varchar(l) => match l {
+ 0 => format!("VARCHAR"), // For "0" remove the limit
+ _ => format!("VARCHAR({})", l),
+ },
+ Primary => format!("INTEGER NOT NULL PRIMARY KEY"),
+ Integer => format!("INTEGER"),
+ Float => format!("REAL"),
+ Double => format!("DOUBLE"),
+ UUID => unimplemented!(),
+ Boolean => format!("BOOLEAN"),
+ Date => format!("DATE"),
+ Json => panic!("Json is not supported by Sqlite3"),
+ Binary => format!("BINARY"),
+ Foreign(_, t, refs) => format!("INTEGER REFERENCES {}({})", t, refs.0.join(",")),
+ Custom(t) => format!("{}", t),
+ Array(meh) => format!("{}[]", Sqlite::print_type(*meh)),
+ Index(_) => unimplemented!(),
+ }
+ }
+}