Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(psl): GA fullTextSearch for mysql, introduce nativeFullTextSearchPostgres for postgres #5055

Merged
merged 15 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions libs/user-facing-errors/src/query_engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,9 @@ pub struct QueryParameterLimitExceeded {
#[derive(Debug, UserFacingError, Serialize)]
#[user_facing(
code = "P2030",
message = "Cannot find a fulltext index to use for the search, try adding a @@fulltext([Fields...]) to your schema"
message = "Cannot find a fulltext index to use for the native search, try adding a @@fulltext([Fields...]) to your schema"
)]
pub struct MissingFullTextSearchIndex {}
pub struct MissingNativeFullTextSearchIndex {}

#[derive(Debug, UserFacingError, Serialize)]
#[user_facing(
Expand Down
28 changes: 27 additions & 1 deletion psl/diagnostics/src/warning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
};
use colored::{ColoredString, Colorize};
use indoc::indoc;
use std::fmt::Display;

/// A non-fatal warning emitted by the schema parser.
/// For fancy printing, please use the `pretty_print_error` function.
Expand All @@ -20,13 +21,38 @@ impl DatamodelWarning {
DatamodelWarning { message, span }
}

pub fn new_feature_deprecated(feature: &str, span: Span) -> DatamodelWarning {
pub fn new_preview_feature_deprecated(feature: &str, span: Span) -> DatamodelWarning {
let message = format!(
"Preview feature \"{feature}\" is deprecated. The functionality can be used without specifying it as a preview feature."
);
Self::new(message, span)
}

pub fn new_preview_feature_renamed(
deprecated_feature: &str,
renamed_feature: impl Display,
prisly_link_endpoint: &str,
span: Span,
) -> DatamodelWarning {
let message = format!(
"Preview feature \"{deprecated_feature}\" has been renamed to \"{renamed_feature}\". Learn more at https://pris.ly/d/{prisly_link_endpoint}."
);
Self::new(message, span)
}

pub fn new_preview_feature_renamed_for_provider(
provider: &str,
deprecated_feature: &str,
renamed_feature: impl Display,
prisly_link_endpoint: &str,
span: Span,
) -> DatamodelWarning {
let message = format!(
"On `provider = \"{provider}\"`, preview feature \"{deprecated_feature}\" has been renamed to \"{renamed_feature}\". Learn more at https://pris.ly/d/{prisly_link_endpoint}."
);
Self::new(message, span)
}

pub fn new_referential_integrity_attr_deprecation_warning(span: Span) -> DatamodelWarning {
let message = "The `referentialIntegrity` attribute is deprecated. Please use `relationMode` instead. Learn more at https://pris.ly/d/relation-mode";
Self::new(message.to_string(), span)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ pub const CAPABILITIES: ConnectorCapabilities = enumflags2::make_bitflags!(Conne
AdvancedJsonNullability |
IndexColumnLengthPrefixing |
FullTextIndex |
FullTextSearch |
FullTextSearchWithIndex |
NativeFullTextSearch |
NativeFullTextSearchWithIndex |
MultipleFullTextAttributesPerModel |
ImplicitManyToManyRelation |
DecimalType |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ pub const CAPABILITIES: ConnectorCapabilities = enumflags2::make_bitflags!(Conne
CreateSkipDuplicates |
Enums |
EnumArrayPush |
FullTextSearch |
FullTextSearchWithoutIndex |
NativeFullTextSearch |
NativeFullTextSearchWithoutIndex |
InsensitiveFilters |
Json |
JsonFiltering |
Expand Down Expand Up @@ -266,6 +266,11 @@ impl Connector for PostgresDatamodelConnector {
CAPABILITIES
}

/// The connector-specific name of the `fullTextSearch` preview feature.
fn native_full_text_search_preview_feature(&self) -> Option<PreviewFeature> {
Some(PreviewFeature::FullTextSearchPostgres)
}

/// The maximum length of postgres identifiers, in bytes.
///
/// Reference: <https://www.postgresql.org/docs/12/limits.html>
Expand Down
3 changes: 2 additions & 1 deletion psl/psl-core/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

mod preview_features;

pub use self::preview_features::{FeatureMap, PreviewFeature, PreviewFeatures, ALL_PREVIEW_FEATURES};
pub(crate) use self::preview_features::RenamedFeature;
pub use self::preview_features::{FeatureMapWithProvider, PreviewFeature, PreviewFeatures, ALL_PREVIEW_FEATURES};
234 changes: 171 additions & 63 deletions psl/psl-core/src/common/preview_features.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use serde::{Serialize, Serializer};
use std::collections::HashMap;
use std::fmt;
use std::sync::LazyLock;

/// A set of preview features.
pub type PreviewFeatures = enumflags2::BitFlags<PreviewFeature>;
Expand All @@ -8,7 +10,7 @@ macro_rules! features {
($( $variant:ident $(,)? ),*) => {
#[enumflags2::bitflags]
#[repr(u64)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum PreviewFeature {
$( $variant,)*
}
Expand Down Expand Up @@ -53,6 +55,7 @@ features!(
FilterJson,
FullTextIndex,
FullTextSearch,
FullTextSearchPostgres,
GroupBy,
ImprovedQueryRaw,
InteractiveTransactions,
Expand Down Expand Up @@ -85,88 +88,193 @@ features!(
StrictUndefinedChecks
);

/// Generator preview features (alphabetically sorted)
pub const ALL_PREVIEW_FEATURES: FeatureMap = FeatureMap {
active: enumflags2::make_bitflags!(PreviewFeature::{
Deno
| DriverAdapters
| FullTextSearch
| Metrics
| MultiSchema
| NativeDistinct
| PostgresqlExtensions
| Tracing
| Views
| RelationJoins
| OmitApi
| PrismaSchemaFolder
| StrictUndefinedChecks
}),
deprecated: enumflags2::make_bitflags!(PreviewFeature::{
AtomicNumberOperations
| AggregateApi
| ClientExtensions
| Cockroachdb
| ConnectOrCreate
| CreateMany
| DataProxy
| Distinct
| ExtendedIndexes
| ExtendedWhereUnique
| FieldReference
| FilteredRelationCount
| FilterJson
| FullTextIndex
| GroupBy
| ImprovedQueryRaw
| InteractiveTransactions
| JsonProtocol
| MicrosoftSqlServer
| Middlewares
| MongoDb
| NamedConstraints
| NApi
| NativeTypes
| OrderByAggregateGroup
| OrderByNulls
| OrderByRelation
| ReferentialActions
| ReferentialIntegrity
| SelectRelationCount
| TransactionApi
| UncheckedScalarInputs
}),
hidden: enumflags2::make_bitflags!(PreviewFeature::{ReactNative | TypedSql}),
};

#[derive(Debug)]
pub struct FeatureMap {
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
struct RenamedFeatureKey<'a> {
/// The old, deprecated preview feature that was renamed.
pub from: PreviewFeature,

/// The provider that the feature was renamed for.
pub provider: Option<&'a str>,
jkomyno marked this conversation as resolved.
Show resolved Hide resolved
}

#[derive(Debug, Copy, Clone)]
pub(crate) struct RenamedFeatureValue {
/// The new preview feature.
pub to: PreviewFeature,

/// The Pris.ly link endpoint for the feature, i.e., what comes after `https://pris.ly/d/`.
pub prisly_link_endpoint: &'static str,
}

#[derive(Debug, Clone)]
pub(crate) enum RenamedFeature<'a> {
/// The preview feature was renamed for a specific provider.
ForProvider((&'a str, RenamedFeatureValue)),

/// The preview feature was renamed for all providers.
AllProviders(RenamedFeatureValue),
}

#[derive(Debug, Clone)]
struct FeatureMap {
/// Valid, visible features.
active: PreviewFeatures,

/// Valid, but connector-specific features that are only visible on matching provider key.
native: HashMap<&'static str, PreviewFeatures>,

/// Deprecated features.
deprecated: PreviewFeatures,

/// History of renamed deprecated features.
renamed: HashMap<RenamedFeatureKey<'static>, RenamedFeatureValue>,

/// Hidden preview features are valid features, but are not propagated into the tooling
/// (as autocomplete or similar) or into error messages (eg. showing a list of valid features).
hidden: PreviewFeatures,
}

impl FeatureMap {
pub const fn active_features(&self) -> PreviewFeatures {
self.active
#[derive(Debug, Clone)]
pub struct FeatureMapWithProvider<'a> {
provider: Option<&'a str>,
feature_map: FeatureMap,
}

/// The default feature map with an unknown provider.
/// This is used for convenience in `prisma/language-tools`, which needs the list of all available preview features
/// before a provider is necessarily known.
pub static ALL_PREVIEW_FEATURES: LazyLock<FeatureMapWithProvider<'static>> =
LazyLock::new(|| FeatureMapWithProvider::new(None));

impl<'a> FeatureMapWithProvider<'a> {
pub fn new(connector_provider: Option<&'a str>) -> FeatureMapWithProvider<'a> {
// Generator preview features (alphabetically sorted)
let feature_map: FeatureMap = FeatureMap {
active: enumflags2::make_bitflags!(PreviewFeature::{
Deno
| DriverAdapters
| Metrics
| MultiSchema
| NativeDistinct
| OmitApi
| PostgresqlExtensions
| PrismaSchemaFolder
| RelationJoins
| StrictUndefinedChecks
| Tracing
| Views
}),
native: HashMap::from([
#[cfg(feature = "postgresql")]
(
"postgresql",
enumflags2::make_bitflags!(PreviewFeature::{
FullTextSearchPostgres
}),
),
]),
renamed: HashMap::from([
#[cfg(feature = "postgresql")]
(
RenamedFeatureKey {
from: PreviewFeature::FullTextSearch,
provider: Some("postgresql"),
},
RenamedFeatureValue {
to: PreviewFeature::FullTextSearchPostgres,
prisly_link_endpoint: "fts-postgres",
},
),
]),
deprecated: enumflags2::make_bitflags!(PreviewFeature::{
AtomicNumberOperations
| AggregateApi
| ClientExtensions
| Cockroachdb
| ConnectOrCreate
| CreateMany
| DataProxy
| Distinct
| ExtendedIndexes
| ExtendedWhereUnique
| FieldReference
| FilteredRelationCount
| FilterJson
| FullTextIndex
| FullTextSearch
| GroupBy
| ImprovedQueryRaw
| InteractiveTransactions
| JsonProtocol
| MicrosoftSqlServer
| Middlewares
| MongoDb
| NamedConstraints
| NApi
| NativeTypes
| OrderByAggregateGroup
| OrderByNulls
| OrderByRelation
| ReferentialActions
| ReferentialIntegrity
| SelectRelationCount
| TransactionApi
| UncheckedScalarInputs
}),
hidden: enumflags2::make_bitflags!(PreviewFeature::{ReactNative | TypedSql}),
};

Self {
provider: connector_provider,
feature_map,
}
}

pub fn native_features(&self) -> PreviewFeatures {
self.provider
.and_then(|provider| self.feature_map.native.get(provider).copied())
.unwrap_or_default()
}

pub fn active_features(&self) -> PreviewFeatures {
self.feature_map.active | self.native_features()
}

pub const fn hidden_features(&self) -> PreviewFeatures {
self.hidden
self.feature_map.hidden
}

pub(crate) fn is_valid(&self, flag: PreviewFeature) -> bool {
(self.active | self.hidden).contains(flag)
(self.active_features() | self.feature_map.hidden).contains(flag)
}

pub(crate) fn is_deprecated(&self, flag: PreviewFeature) -> bool {
self.deprecated.contains(flag)
self.feature_map.deprecated.contains(flag)
}

/// Was the given preview feature deprecated and renamed?
pub(crate) fn is_renamed(&self, flag: PreviewFeature) -> Option<RenamedFeature<'a>> {
// Check for a renamed feature specific to the provider. This is only possible if a provider is not None.
let provider_specific = self.provider.and_then(|provider| {
self.feature_map
.renamed
.get(&RenamedFeatureKey {
from: flag,
provider: Some(provider),
})
.map(|renamed| RenamedFeature::ForProvider((provider, *renamed)))
});

// Fallback to provider-independent renamed feature
provider_specific.or_else(|| {
self.feature_map
.renamed
.get(&RenamedFeatureKey {
from: flag,
provider: None,
})
.map(|renamed| RenamedFeature::AllProviders(*renamed))
})
}
}

Expand Down
5 changes: 5 additions & 0 deletions psl/psl-core/src/datamodel_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ pub trait Connector: Send + Sync {
/// The static list of capabilities for the connector.
fn capabilities(&self) -> ConnectorCapabilities;

/// The connector-specific name of the `fullTextSearch` preview feature.
fn native_full_text_search_preview_feature(&self) -> Option<PreviewFeature> {
None
}

/// The maximum length of constraint names in bytes. Connectors without a
/// limit should return usize::MAX.
fn max_identifier_length(&self) -> usize;
Expand Down
Loading
Loading