Skip to content

Commit

Permalink
feat: exclude MySQL < 8.0.14 from joins (#4704)
Browse files Browse the repository at this point in the history
  • Loading branch information
Weakky authored Feb 16, 2024
1 parent 3d78035 commit ca42e86
Show file tree
Hide file tree
Showing 36 changed files with 431 additions and 129 deletions.
39 changes: 16 additions & 23 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,15 @@ dev-mysql8: start-mysql_8
start-mysql_mariadb:
docker compose -f docker-compose.yml up --wait -d --remove-orphans mariadb-10-0

start-mysql_mariadb_11:
docker compose -f docker-compose.yml up --wait -d --remove-orphans mariadb-11-0

dev-mariadb: start-mysql_mariadb
cp $(CONFIG_PATH)/mariadb $(CONFIG_FILE)

dev-mariadb11: start-mysql_mariadb_11
cp $(CONFIG_PATH)/mariadb $(CONFIG_FILE)

start-mssql_2019:
docker compose -f docker-compose.yml up --wait -d --remove-orphans mssql-2019

Expand Down
13 changes: 13 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,19 @@ services:
- databases
tmpfs: /var/lib/mariadb

mariadb-11-0:
image: mariadb:11
restart: unless-stopped
environment:
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: prisma
MYSQL_DATABASE: prisma
ports:
- '3308:3306'
networks:
- databases
tmpfs: /var/lib/mariadb

vitess-test-8_0:
image: vitess/vttestserver:mysql80@sha256:53a2d2f58ecf8e6cf984c725612f7651c4fc7ac9bc7d198dbd9964d50e28b9a2
restart: unless-stopped
Expand Down
13 changes: 11 additions & 2 deletions psl/psl-core/src/builtin_connectors/mysql_datamodel_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use prisma_value::{decode_bytes, PrismaValueResult};
use super::completions;
use crate::{
datamodel_connector::{
Connector, ConnectorCapabilities, ConnectorCapability, ConstraintScope, Flavour, NativeTypeConstructor,
NativeTypeInstance, RelationMode,
Connector, ConnectorCapabilities, ConnectorCapability, ConstraintScope, Flavour, JoinStrategySupport,
NativeTypeConstructor, NativeTypeInstance, RelationMode,
},
diagnostics::{DatamodelError, Diagnostics, Span},
parser_database::{walkers, ReferentialAction, ScalarType},
Expand Down Expand Up @@ -316,4 +316,13 @@ impl Connector for MySqlDatamodelConnector {
fn parse_json_bytes(&self, str: &str, _nt: Option<NativeTypeInstance>) -> PrismaValueResult<Vec<u8>> {
decode_bytes(str)
}

fn runtime_join_strategy_support(&self) -> JoinStrategySupport {
match self.static_join_strategy_support() {
// Prior to MySQL 8.0.14 and for MariaDB, a derived table cannot contain outer references.
// Source: https://dev.mysql.com/doc/refman/8.0/en/derived-tables.html.
true => JoinStrategySupport::UnknownYet,
false => JoinStrategySupport::No,
}
}
}
30 changes: 30 additions & 0 deletions psl/psl-core/src/datamodel_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,20 @@ pub trait Connector: Send + Sync {
) -> prisma_value::PrismaValueResult<Vec<u8>> {
unreachable!("This method is only implemented on connectors with lateral join support.")
}

fn static_join_strategy_support(&self) -> bool {
self.has_capability(ConnectorCapability::LateralJoin)
|| self.has_capability(ConnectorCapability::CorrelatedSubqueries)
}

/// Returns whether the connector supports the `RelationLoadStrategy::Join`.
/// On some connectors, this might return `UnknownYet`.
fn runtime_join_strategy_support(&self) -> JoinStrategySupport {
match self.static_join_strategy_support() {
true => JoinStrategySupport::Yes,
false => JoinStrategySupport::No,
}
}
}

#[derive(Copy, Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -406,3 +420,19 @@ impl ConstraintScope {
}
}
}

/// Describes whether a connector supports relation join strategy.
#[derive(Debug, Copy, Clone)]
pub enum JoinStrategySupport {
/// The connector supports it.
Yes,
/// The connector supports it but the specific database version does not.
/// This state can only be known at runtime by checking the actual database version.
UnsupportedDbVersion,
/// The connector does not support it.
No,
/// The connector may or may not support it. Additional runtime informations are required to determine the support.
/// This state is used when the connector does not have a static capability to determine the support.
/// For example, the MySQL connector supports relation join strategy, but only for versions >= 8.0.14.
UnknownYet,
}
20 changes: 20 additions & 0 deletions quaint/src/connector/connection_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,26 @@ impl ConnectionInfo {
ConnectionInfo::External(_) => "external".into(),
}
}

#[allow(unused_variables)]
pub fn set_version(&mut self, version: Option<String>) {
match self {
#[cfg(not(target_arch = "wasm32"))]
ConnectionInfo::Native(native) => native.set_version(version),
ConnectionInfo::External(_) => (),
}
}

pub fn version(&self) -> Option<&str> {
match self {
#[cfg(not(target_arch = "wasm32"))]
ConnectionInfo::Native(nt) => match nt {
NativeConnectionInfo::Mysql(m) => m.version(),
_ => None,
},
ConnectionInfo::External(_) => None,
}
}
}

/// One of the supported SQL variants.
Expand Down
12 changes: 5 additions & 7 deletions quaint/src/connector/mysql/native/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,12 @@ impl Queryable for Mysql {
}

async fn version(&self) -> crate::Result<Option<String>> {
let query = r#"SELECT @@GLOBAL.version version"#;
let rows = timeout::socket(self.socket_timeout, self.query_raw(query, &[])).await?;
let guard = self.conn.lock().await;
let (major, minor, patch) = guard.server_version();
let flavour = if guard.is_mariadb() { "MariaDB" } else { "MySQL" };
drop(guard);

let version_string = rows
.first()
.and_then(|row| row.get("version").and_then(|version| version.typed.to_string()));

Ok(version_string)
Ok(Some(format!("{major}.{minor}.{patch}-{flavour}")))
}

fn is_healthy(&self) -> bool {
Expand Down
15 changes: 14 additions & 1 deletion quaint/src/connector/mysql/url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use url::{Host, Url};
#[derive(Debug, Clone)]
pub struct MysqlUrl {
url: Url,
version: Option<String>,
pub(crate) query_params: MysqlUrlQueryParams,
}

Expand All @@ -22,7 +23,15 @@ impl MysqlUrl {
pub fn new(url: Url) -> Result<Self, Error> {
let query_params = Self::parse_query_params(&url)?;

Ok(Self { url, query_params })
Ok(Self {
url,
query_params,
version: None,
})
}

pub fn set_version(&mut self, version: Option<String>) {
self.version = version;
}

/// The bare `Url` to the database.
Expand Down Expand Up @@ -298,6 +307,10 @@ impl MysqlUrl {
pub(crate) fn connection_limit(&self) -> Option<usize> {
self.query_params.connection_limit
}

pub fn version(&self) -> Option<&str> {
self.version.as_deref()
}
}

#[derive(Debug, Clone)]
Expand Down
9 changes: 9 additions & 0 deletions quaint/src/connector/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,12 @@ pub enum NativeConnectionInfo {
#[cfg(feature = "sqlite")]
InMemorySqlite { db_name: String },
}

impl NativeConnectionInfo {
pub fn set_version(&mut self, version: Option<String>) {
#[cfg(feature = "mysql")]
if let NativeConnectionInfo::Mysql(c) = self {
c.set_version(version);
}
}
}
Loading

0 comments on commit ca42e86

Please sign in to comment.