Skip to content

Commit 47de924

Browse files
committed
feat(qc): add support for SQL NOW() function
1 parent 85179d7 commit 47de924

File tree

10 files changed

+63
-9
lines changed

10 files changed

+63
-9
lines changed

quaint/src/ast/function.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod json_unquote;
1111
mod lower;
1212
mod maximum;
1313
mod minimum;
14+
mod now;
1415
mod row_number;
1516
mod row_to_json;
1617
mod search;
@@ -32,6 +33,7 @@ pub use json_unquote::*;
3233
pub use lower::*;
3334
pub use maximum::*;
3435
pub use minimum::*;
36+
pub use now::*;
3537
pub use row_number::*;
3638
pub use row_to_json::*;
3739
pub use search::*;
@@ -90,6 +92,7 @@ pub(crate) enum FunctionType<'a> {
9092
UuidToBin,
9193
UuidToBinSwapped,
9294
Uuid,
95+
Now,
9396
}
9497

9598
impl<'a> Aliasable<'a> for Function<'a> {

quaint/src/ast/function/now.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use super::{Function, FunctionType};
2+
use crate::ast::Expression;
3+
4+
/// Generates the SQL function NOW() returning the current timestamp in MySQL/PostgreSQL.
5+
/// ```rust
6+
/// # use quaint::{ast::*, visitor::{Visitor, Mysql, Postgres}};
7+
/// # fn main() -> Result<(), quaint::error::Error> {
8+
///
9+
/// let query = Select::default().value(now());
10+
///
11+
/// let (sql, _) = Mysql::build(query)?;
12+
/// assert_eq!("SELECT NOW()", sql);
13+
///
14+
/// let (sql, _) = Postgres::build(query)?;
15+
/// assert_eq!("SELECT NOW()", sql);
16+
/// # Ok(())
17+
/// # }
18+
/// ```
19+
pub fn native_now() -> Expression<'static> {
20+
let func = Function {
21+
typ_: FunctionType::Now,
22+
alias: None,
23+
};
24+
25+
func.into()
26+
}

quaint/src/visitor.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,7 @@ pub trait Visitor<'a> {
12051205
self.write("uuid_to_bin(uuid(), 1)")?;
12061206
}
12071207
FunctionType::Uuid => self.write("uuid()")?,
1208+
FunctionType::Now => self.write("NOW()")?,
12081209
FunctionType::Concat(concat) => {
12091210
self.visit_concat(concat)?;
12101211
}

query-compiler/query-compiler/src/expression.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ pub enum FieldOperation {
246246
Subtract(PrismaValue),
247247
Multiply(PrismaValue),
248248
Divide(PrismaValue),
249+
Now,
249250
}
250251

251252
impl TryFrom<ScalarWriteOperation> for FieldOperation {
@@ -258,6 +259,7 @@ impl TryFrom<ScalarWriteOperation> for FieldOperation {
258259
ScalarWriteOperation::Subtract(val) => Ok(Self::Subtract(val)),
259260
ScalarWriteOperation::Multiply(val) => Ok(Self::Multiply(val)),
260261
ScalarWriteOperation::Divide(val) => Ok(Self::Divide(val)),
262+
ScalarWriteOperation::Now => Ok(Self::Now),
261263
ScalarWriteOperation::Field(_) | ScalarWriteOperation::Unset(_) => Err(UnsupportedScalarWriteOperation(op)),
262264
}
263265
}

query-compiler/query-compiler/src/expression/format.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ where
453453
self.keyword("mul").append(self.space()).append(self.value(val))
454454
}
455455
FieldOperation::Divide(val) => self.keyword("div").append(self.space()).append(self.value(val)),
456+
FieldOperation::Now => self.keyword("now"),
456457
},
457458
)
458459
})))

query-engine/connectors/mongodb-query-connector/src/root_queries/update/into_operation.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ impl IntoUpdateOperation for ScalarWriteOperation {
4444
field_path,
4545
doc! { "$divide": [dollar_field_path, (field, rhs).into_bson()?] },
4646
)),
47+
ScalarWriteOperation::Now => Some(UpdateOperation::generic(
48+
field_path,
49+
doc! { "$currentDate": true },
50+
)),
4751
ScalarWriteOperation::Unset(true) => Some(UpdateOperation::unset(field_path)),
4852
ScalarWriteOperation::Unset(false) => None,
4953
ScalarWriteOperation::Field(_) => unimplemented!(),

query-engine/query-builders/sql-query-builder/src/write.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ pub fn build_update_and_set_query(
191191
e / field.value(rhs, ctx).into()
192192
}
193193

194+
ScalarWriteOperation::Now => {
195+
native_now()
196+
},
197+
194198
ScalarWriteOperation::Unset(_) => unreachable!("Unset is not supported on SQL connectors"),
195199
};
196200

query-engine/query-structure/src/write_args.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ pub enum ScalarWriteOperation {
150150

151151
/// Divide field by value.
152152
Divide(PrismaValue),
153+
154+
/// Set field to the current date-time
155+
Now,
153156
}
154157

155158
#[derive(Debug, PartialEq, Clone)]
@@ -487,5 +490,6 @@ pub fn apply_expression(val: PrismaValue, scalar_write: ScalarWriteOperation) ->
487490
ScalarWriteOperation::Multiply(rhs) => val * rhs,
488491
ScalarWriteOperation::Divide(rhs) => val / rhs,
489492
ScalarWriteOperation::Unset(_) => unimplemented!(),
493+
ScalarWriteOperation::Now => PrismaValue::generator_now(),
490494
}
491495
}

query-engine/schema/src/build/input_types/fields/data_input_mapper/update.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,32 @@ impl UpdateDataInputFieldMapper {
1919
impl DataInputFieldMapper for UpdateDataInputFieldMapper {
2020
fn map_scalar<'a>(&self, ctx: &'a QuerySchema, sf: ScalarFieldRef) -> InputField<'a> {
2121
let base_update_type = match sf.type_identifier() {
22-
TypeIdentifier::Float => InputType::object(update_operations_object_type(ctx, "Float", sf.clone(), true)),
22+
TypeIdentifier::Float => InputType::object(update_operations_object_type(ctx, "Float", sf.clone(), true, false)),
2323
TypeIdentifier::Decimal => {
24-
InputType::object(update_operations_object_type(ctx, "Decimal", sf.clone(), true))
24+
InputType::object(update_operations_object_type(ctx, "Decimal", sf.clone(), true, false))
2525
}
26-
TypeIdentifier::Int => InputType::object(update_operations_object_type(ctx, "Int", sf.clone(), true)),
27-
TypeIdentifier::BigInt => InputType::object(update_operations_object_type(ctx, "BigInt", sf.clone(), true)),
26+
TypeIdentifier::Int => InputType::object(update_operations_object_type(ctx, "Int", sf.clone(), true, false)),
27+
TypeIdentifier::BigInt => InputType::object(update_operations_object_type(ctx, "BigInt", sf.clone(), true, false)),
2828
TypeIdentifier::String => {
29-
InputType::object(update_operations_object_type(ctx, "String", sf.clone(), false))
29+
InputType::object(update_operations_object_type(ctx, "String", sf.clone(), false, false))
3030
}
31-
TypeIdentifier::Boolean => InputType::object(update_operations_object_type(ctx, "Bool", sf.clone(), false)),
31+
TypeIdentifier::Boolean => InputType::object(update_operations_object_type(ctx, "Bool", sf.clone(), false, false)),
3232
TypeIdentifier::Enum(enum_id) => {
3333
let enum_name = ctx.internal_data_model.walk(enum_id).name();
3434
InputType::object(update_operations_object_type(
3535
ctx,
3636
&format!("Enum{enum_name}"),
3737
sf.clone(),
3838
false,
39+
false,
3940
))
4041
}
4142
TypeIdentifier::Json => map_scalar_input_type_for_field(ctx, &sf),
4243
TypeIdentifier::DateTime => {
43-
InputType::object(update_operations_object_type(ctx, "DateTime", sf.clone(), false))
44+
InputType::object(update_operations_object_type(ctx, "DateTime", sf.clone(), false, true))
4445
}
45-
TypeIdentifier::UUID => InputType::object(update_operations_object_type(ctx, "Uuid", sf.clone(), false)),
46-
TypeIdentifier::Bytes => InputType::object(update_operations_object_type(ctx, "Bytes", sf.clone(), false)),
46+
TypeIdentifier::UUID => InputType::object(update_operations_object_type(ctx, "Uuid", sf.clone(), false, false)),
47+
TypeIdentifier::Bytes => InputType::object(update_operations_object_type(ctx, "Bytes", sf.clone(), false, false)),
4748

4849
TypeIdentifier::Unsupported => unreachable!("No unsupported field should reach that path"),
4950
};
@@ -160,6 +161,7 @@ fn update_operations_object_type<'a>(
160161
prefix: &str,
161162
sf: ScalarField,
162163
with_number_operators: bool,
164+
with_datetime_operators: bool,
163165
) -> InputObjectType<'a> {
164166
let ident = Identifier::new_prisma(IdentifierType::FieldUpdateOperationsInput(
165167
!sf.is_required(),
@@ -184,6 +186,10 @@ fn update_operations_object_type<'a>(
184186
fields.push(simple_input_field(operations::DIVIDE, typ, None).optional());
185187
}
186188

189+
if with_datetime_operators {
190+
fields.push(InputField::new(operations::NOW.into(), vec![], None, false));
191+
}
192+
187193
if ctx.has_capability(ConnectorCapability::UndefinedType) && !sf.is_required() {
188194
fields.push(simple_input_field(operations::UNSET, InputType::boolean(), None).optional());
189195
}

query-engine/schema/src/constants.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ pub mod operations {
5151
pub const DECREMENT: &str = "decrement";
5252
pub const MULTIPLY: &str = "multiply";
5353
pub const DIVIDE: &str = "divide";
54+
55+
// date-time
56+
pub const NOW: &str = "now";
5457
}
5558

5659
pub mod filters {

0 commit comments

Comments
 (0)