Skip to content

Commit

Permalink
feat(psl): GA fullTextSearch for mysql, introduce `nativeFullText…
Browse files Browse the repository at this point in the history
…SearchPostgres` for `postgres` (#5055)

* feat(psl): [prep work] GA "fullTextSearch" preview feature for MySQL, rename it to "nativeFullTextSearchPostgres" for Postgres

* feat(psl): add support for renamed preview feature, add support for native preview features, add tests

* feat(psl): adjust "schema::can_full_text_search" via "Connector::native_full_text_search_preview_feature"

* chore(psl): "FeatureMap" is now private

* chore(psl): clippy

* chore: typo in MissingNativeFullTextSearchIndex

* chore(psl): update snapshots

* fix(connector-test-kit-rs): fix tests by enabling support for "FeatureMapWithProvider"

* fix(mongodb-schema-connector): fix tests by enabling support for "FeatureMapWithProvider"

* chore: rename "nativeFullTextSearchPostgres" preview feature to "fullTextSearchPostgres"

* chore: update pris.ly link

* chore(psl): address review comments

* chore(psl): address review comments

* chore(psl): address review comments
  • Loading branch information
jkomyno authored Nov 27, 2024
1 parent 4180c29 commit c1f5600
Show file tree
Hide file tree
Showing 30 changed files with 443 additions and 115 deletions.
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>,
}

#[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

0 comments on commit c1f5600

Please sign in to comment.