pub mod capabilities;
pub mod constraint_names;
pub mod walker_ext_traits;
pub mod completions;
mod empty_connector;
mod filters;
mod native_types;
mod relation_mode;
pub use self::{
capabilities::{ConnectorCapabilities, ConnectorCapability},
completions::format_completion_docs,
empty_connector::EmptyDatamodelConnector,
filters::*,
native_types::{NativeTypeArguments, NativeTypeConstructor, NativeTypeInstance},
relation_mode::RelationMode,
};
use crate::{configuration::DatasourceConnectorData, Configuration, Datasource, PreviewFeature};
use chrono::{DateTime, FixedOffset};
use diagnostics::{DatamodelError, Diagnostics, NativeTypeErrorFactory, Span};
use enumflags2::BitFlags;
use lsp_types::CompletionList;
use parser_database::{
ast::{self, SchemaPosition},
walkers, IndexAlgorithm, ParserDatabase, ReferentialAction, ScalarType,
};
use std::{borrow::Cow, collections::HashMap, str::FromStr};
pub const EXTENSIONS_KEY: &str = "extensions";
pub trait Connector: Send + Sync {
fn provider_name(&self) -> &'static str;
fn is_provider(&self, name: &str) -> bool {
name == self.provider_name()
}
fn flavour(&self) -> Flavour;
fn name(&self) -> &str;
fn capabilities(&self) -> ConnectorCapabilities;
fn has_capability(&self, capability: ConnectorCapability) -> bool {
self.capabilities().contains(capability)
}
fn max_identifier_length(&self) -> usize;
fn allowed_relation_mode_settings(&self) -> BitFlags<RelationMode> {
use RelationMode::*;
ForeignKeys | Prisma
}
fn default_relation_mode(&self) -> RelationMode {
RelationMode::ForeignKeys
}
fn referential_actions(&self) -> BitFlags<ReferentialAction>;
fn emulated_referential_actions(&self) -> BitFlags<ReferentialAction> {
RelationMode::allowed_emulated_referential_actions_default()
}
fn allows_set_null_referential_action_on_non_nullable_fields(&self, _relation_mode: RelationMode) -> bool {
false
}
fn supports_composite_types(&self) -> bool {
self.has_capability(ConnectorCapability::CompositeTypes)
}
fn supports_named_primary_keys(&self) -> bool {
self.has_capability(ConnectorCapability::NamedPrimaryKeys)
}
fn supports_named_foreign_keys(&self) -> bool {
self.has_capability(ConnectorCapability::NamedForeignKeys)
}
fn supports_named_default_values(&self) -> bool {
self.has_capability(ConnectorCapability::NamedDefaultValues)
}
fn supports_referential_action(&self, relation_mode: &RelationMode, action: ReferentialAction) -> bool {
match relation_mode {
RelationMode::ForeignKeys => self.referential_actions().contains(action),
RelationMode::Prisma => self.emulated_referential_actions().contains(action),
}
}
fn scalar_filter_name(&self, scalar_type_name: String, _native_type_name: Option<&str>) -> Cow<'_, str> {
Cow::Owned(scalar_type_name)
}
fn string_filters(&self, input_object_name: &str) -> BitFlags<StringFilter> {
match input_object_name {
"String" => BitFlags::all(), _ => panic!("Unexpected scalar input object name for string filters: `{input_object_name}`"),
}
}
fn validate_native_type_arguments(
&self,
_native_type: &NativeTypeInstance,
_scalar_type: &ScalarType,
_span: Span,
_: &mut Diagnostics,
) {
}
fn validate_enum(&self, _enum: walkers::EnumWalker<'_>, _: &mut Diagnostics) {}
fn validate_model(&self, _model: walkers::ModelWalker<'_>, _: RelationMode, _: &mut Diagnostics) {}
fn validate_relation_field(&self, _field: walkers::RelationFieldWalker<'_>, _: &mut Diagnostics) {}
fn validate_datasource(&self, _: BitFlags<PreviewFeature>, _: &Datasource, _: &mut Diagnostics) {}
fn validate_scalar_field_unknown_default_functions(
&self,
db: &parser_database::ParserDatabase,
diagnostics: &mut Diagnostics,
) {
for d in db.walk_scalar_field_defaults_with_unknown_function() {
let (func_name, _, span) = d.value().as_function().unwrap();
diagnostics.push_error(DatamodelError::new_default_unknown_function(func_name, span));
}
}
fn constraint_violation_scopes(&self) -> &'static [ConstraintScope] {
&[]
}
fn available_native_type_constructors(&self) -> &'static [NativeTypeConstructor];
fn scalar_type_for_native_type(&self, native_type: &NativeTypeInstance) -> ScalarType;
fn default_native_type_for_scalar_type(&self, scalar_type: &ScalarType) -> Option<NativeTypeInstance>;
fn native_type_is_default_for_scalar_type(
&self,
native_type: &NativeTypeInstance,
scalar_type: &ScalarType,
) -> bool;
fn native_type_to_parts(&self, native_type: &NativeTypeInstance) -> (&'static str, Vec<String>);
fn find_native_type_constructor(&self, name: &str) -> Option<&NativeTypeConstructor> {
self.available_native_type_constructors()
.iter()
.find(|constructor| constructor.name == name)
}
fn parse_native_type(
&self,
name: &str,
args: &[String],
span: Span,
diagnostics: &mut Diagnostics,
) -> Option<NativeTypeInstance>;
fn supports_scalar_lists(&self) -> bool {
self.has_capability(ConnectorCapability::ScalarLists)
}
fn supports_enums(&self) -> bool {
self.has_capability(ConnectorCapability::Enums)
}
fn supports_json(&self) -> bool {
self.has_capability(ConnectorCapability::Json)
}
fn supports_json_lists(&self) -> bool {
self.has_capability(ConnectorCapability::JsonLists)
}
fn supports_auto_increment(&self) -> bool {
self.has_capability(ConnectorCapability::AutoIncrement)
}
fn supports_non_id_auto_increment(&self) -> bool {
self.has_capability(ConnectorCapability::AutoIncrementAllowedOnNonId)
}
fn supports_multiple_auto_increment(&self) -> bool {
self.has_capability(ConnectorCapability::AutoIncrementMultipleAllowed)
}
fn supports_non_indexed_auto_increment(&self) -> bool {
self.has_capability(ConnectorCapability::AutoIncrementNonIndexedAllowed)
}
fn supports_compound_ids(&self) -> bool {
self.has_capability(ConnectorCapability::CompoundIds)
}
fn supports_decimal(&self) -> bool {
self.has_capability(ConnectorCapability::DecimalType)
}
fn supported_index_types(&self) -> BitFlags<IndexAlgorithm> {
IndexAlgorithm::BTree.into()
}
fn supports_index_type(&self, algo: IndexAlgorithm) -> bool {
self.supported_index_types().contains(algo)
}
fn allows_relation_fields_in_arbitrary_order(&self) -> bool {
self.has_capability(ConnectorCapability::RelationFieldsInArbitraryOrder)
}
fn should_suggest_missing_referencing_fields_indexes(&self) -> bool {
true
}
fn native_type_to_string(&self, instance: &NativeTypeInstance) -> String {
let (name, args) = self.native_type_to_parts(instance);
let args = if args.is_empty() {
String::new()
} else {
format!("({})", args.join(","))
};
format!("{name}{args}")
}
fn native_instance_error(&self, instance: &NativeTypeInstance) -> NativeTypeErrorFactory {
NativeTypeErrorFactory::new(self.native_type_to_string(instance), self.name().to_owned())
}
fn validate_url(&self, url: &str) -> Result<(), String>;
fn datamodel_completions(
&self,
_db: &ParserDatabase,
_position: SchemaPosition<'_>,
_completions: &mut CompletionList,
) {
}
fn datasource_completions(&self, _config: &Configuration, _completion_list: &mut CompletionList) {}
fn parse_datasource_properties(
&self,
_args: &mut HashMap<&str, (Span, &ast::Expression)>,
_diagnostics: &mut Diagnostics,
) -> DatasourceConnectorData {
Default::default()
}
fn parse_json_datetime(
&self,
_str: &str,
_nt: Option<NativeTypeInstance>,
) -> chrono::ParseResult<DateTime<FixedOffset>> {
unreachable!("This method is only implemented on connectors with lateral join support.")
}
fn parse_json_bytes(
&self,
_str: &str,
_nt: Option<NativeTypeInstance>,
) -> prisma_value::PrismaValueResult<Vec<u8>> {
unreachable!("This method is only implemented on connectors with lateral join support.")
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Flavour {
Cockroach,
Mongo,
Sqlserver,
Mysql,
Postgres,
Sqlite,
}
impl FromStr for Flavour {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"mysql" => Ok(Self::Mysql),
"postgres" => Ok(Self::Postgres),
"cockroachdb" => Ok(Self::Cockroach),
"mssql" => Ok(Self::Sqlserver),
"sqlite" => Ok(Self::Sqlite),
_ => Err(format!("Unknown flavour: {}", s)),
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub enum ConstraintType {
PrimaryKey,
ForeignKey,
KeyOrIdx,
Default,
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum ConstraintScope {
GlobalKeyIndex,
GlobalForeignKey,
GlobalPrimaryKeyKeyIndex,
GlobalPrimaryKeyForeignKeyDefault,
ModelKeyIndex,
ModelPrimaryKeyKeyIndex,
ModelPrimaryKeyKeyIndexForeignKey,
}
impl ConstraintScope {
pub fn description(self, model_name: &str) -> Cow<'static, str> {
match self {
ConstraintScope::GlobalKeyIndex => Cow::from("global for indexes and unique constraints"),
ConstraintScope::GlobalForeignKey => Cow::from("global for foreign keys"),
ConstraintScope::GlobalPrimaryKeyKeyIndex => {
Cow::from("global for primary key, indexes and unique constraints")
}
ConstraintScope::GlobalPrimaryKeyForeignKeyDefault => {
Cow::from("global for primary keys, foreign keys and default constraints")
}
ConstraintScope::ModelKeyIndex => {
Cow::from(format!("on model `{model_name}` for indexes and unique constraints"))
}
ConstraintScope::ModelPrimaryKeyKeyIndex => Cow::from(format!(
"on model `{model_name}` for primary key, indexes and unique constraints"
)),
ConstraintScope::ModelPrimaryKeyKeyIndexForeignKey => Cow::from(format!(
"on model `{model_name}` for primary key, indexes, unique constraints and foreign keys"
)),
}
}
}