diff --git a/runtimes/core/src/api/jsonschema/parse.rs b/runtimes/core/src/api/jsonschema/parse.rs index 690b6ac718..d4e9b1c7c3 100644 --- a/runtimes/core/src/api/jsonschema/parse.rs +++ b/runtimes/core/src/api/jsonschema/parse.rs @@ -409,6 +409,7 @@ fn describe_json(value: &PValue) -> &'static str { PValue::DateTime(_) => "a datetime", PValue::Array(_) => "an array", PValue::Object(_) => "an object", + PValue::BigInt(_) => "a bigint", } } diff --git a/runtimes/core/src/api/pvalue.rs b/runtimes/core/src/api/pvalue.rs index 5f5a33b55c..6abdcfd7c0 100644 --- a/runtimes/core/src/api/pvalue.rs +++ b/runtimes/core/src/api/pvalue.rs @@ -31,6 +31,9 @@ pub enum PValue { // Represents a datetime value. DateTime(DateTime), + + // Represents a BigInt value. + BigInt(String), } impl PValue { @@ -65,6 +68,7 @@ impl Serialize for PValue { PValue::Array(a) => a.serialize(serializer), PValue::Object(o) => o.serialize(serializer), PValue::DateTime(dt) => dt.serialize(serializer), + PValue::BigInt(bi) => serializer.serialize_str(bi), } } } @@ -76,6 +80,7 @@ impl Display for PValue { PValue::Bool(b) => write!(f, "{}", b), PValue::Number(n) => write!(f, "{}", n), PValue::String(s) => write!(f, "{}", s), + PValue::BigInt(bi) => write!(f, "{}", bi), PValue::DateTime(dt) => write!(f, "{}", dt.to_rfc3339()), PValue::Array(a) => { write!(f, "[")?; diff --git a/runtimes/core/src/api/schema/header.rs b/runtimes/core/src/api/schema/header.rs index bcc4ac362b..3c9be64588 100644 --- a/runtimes/core/src/api/schema/header.rs +++ b/runtimes/core/src/api/schema/header.rs @@ -291,6 +291,14 @@ fn to_reqwest_header_value(value: &PValue) -> APIResult { details: None, })?, + BigInt(bi) => reqwest::header::HeaderValue::from_str(bi).map_err(|e| api::Error { + code: api::ErrCode::InvalidArgument, + message: "unable to convert bigint to header value".to_string(), + internal_message: Some(format!("unable to convert bigint to header value: {}", e)), + stack: None, + details: None, + })?, + DateTime(dt) => { let s = dt.to_rfc3339(); reqwest::header::HeaderValue::from_str(&s).map_err(|e| api::Error { @@ -368,6 +376,14 @@ fn to_axum_header_value(value: &PValue) -> APIResult { details: None, })?, + BigInt(bi) => axum::http::HeaderValue::from_str(bi).map_err(|e| api::Error { + code: api::ErrCode::InvalidArgument, + message: "unable to convert bigint to header value".to_string(), + internal_message: Some(format!("unable to convert bigint to header value: {}", e)), + stack: None, + details: None, + })?, + DateTime(dt) => { let s = dt.to_rfc3339(); axum::http::HeaderValue::from_str(&s).map_err(|e| api::Error { diff --git a/runtimes/core/src/api/schema/path.rs b/runtimes/core/src/api/schema/path.rs index 1d4097a92d..ad6f7aac44 100644 --- a/runtimes/core/src/api/schema/path.rs +++ b/runtimes/core/src/api/schema/path.rs @@ -175,7 +175,7 @@ impl Path { }; match value { - PValue::String(str) => { + PValue::String(str) | PValue::BigInt(str) => { // URL-encode the string, so it doesn't get reinterpreted // as multiple path segments. let encoded = urlencoding::encode(str); diff --git a/runtimes/core/src/sqldb/val.rs b/runtimes/core/src/sqldb/val.rs index 303a50ec92..8c0a9581d4 100644 --- a/runtimes/core/src/sqldb/val.rs +++ b/runtimes/core/src/sqldb/val.rs @@ -70,6 +70,11 @@ impl ToSql for RowValue { _ => Err(format!("string not supported for column of type {}", ty).into()), }, + PValue::BigInt(bi) => match *ty { + Type::TEXT | Type::VARCHAR => bi.to_sql(ty, out), + _ => Err(format!("bigint not supported for column of type {}", ty).into()), + }, + PValue::Number(num) => match *ty { Type::INT2 => { let val: Result = if num.is_i64() { diff --git a/runtimes/js/encore.dev/storage/sqldb/database.ts b/runtimes/js/encore.dev/storage/sqldb/database.ts index 2b3f2223b6..1528b85f18 100644 --- a/runtimes/js/encore.dev/storage/sqldb/database.ts +++ b/runtimes/js/encore.dev/storage/sqldb/database.ts @@ -19,7 +19,14 @@ const driverName = "node-pg"; export type Row = Record; /** Represents a type that can be used in query template literals */ -export type Primitive = string | number | boolean | Buffer | Date | null; +export type Primitive = + | string + | number + | bigint + | boolean + | Buffer + | Date + | null; /** * Constructing a new database object will result in Encore provisioning a database with diff --git a/runtimes/js/src/pvalue.rs b/runtimes/js/src/pvalue.rs index 5f6220de4d..b21a695396 100644 --- a/runtimes/js/src/pvalue.rs +++ b/runtimes/js/src/pvalue.rs @@ -1,6 +1,6 @@ use chrono::TimeZone; use encore_runtime_core::api::{self, auth, PValue, PValues}; -use napi::{bindgen_prelude::*, sys, JsDate, JsObject, JsUnknown, NapiValue, Result}; +use napi::{bindgen_prelude::*, JsBigInt, JsDate, JsObject, JsUnknown, NapiValue, Result}; use serde_json::Number; #[allow(dead_code)] @@ -72,6 +72,16 @@ impl ToNapiValue for PVal { let dt = env2.create_date(ts as f64)?; JsDate::to_napi_value(env, dt) } + + PValue::BigInt(bi) => { + let env2 = Env::from_raw(env); + let global = env2.get_global()?; + let func: JsFunction = global.get_named_property_unchecked("BigInt")?; + let val = func.call(None, &[env2.create_string(&bi)?])?; + let bi = JsBigInt::from_unknown(val)?; + + JsBigInt::to_napi_value(env, bi) + } } } } @@ -106,6 +116,15 @@ impl ToNapiValue for &PVal { let dt = env2.create_date(ts as f64)?; JsDate::to_napi_value(env, dt) } + PValue::BigInt(bi) => { + let env2 = Env::from_raw(env); + let global = env2.get_global()?; + let func: JsFunction = global.get_named_property_unchecked("BigInt")?; + let val = func.call(None, &[env2.create_string(bi)?])?; + let bi = JsBigInt::from_unknown(val)?; + + JsBigInt::to_napi_value(env, bi) + } } } } @@ -148,7 +167,14 @@ impl FromNapiValue for PVal { } } } - ValueType::BigInt => todo!(), + ValueType::BigInt => { + let bi_string = JsBigInt::from_napi_value(env, napi_val)? + .coerce_to_string()? + .into_utf8()? + .try_into()?; + + PValue::BigInt(bi_string) + } ValueType::Null => PValue::Null, ValueType::Function => { return Err(Error::new( diff --git a/tsparser/src/legacymeta/schema.rs b/tsparser/src/legacymeta/schema.rs index 7b9b66148b..1fd6a7045d 100644 --- a/tsparser/src/legacymeta/schema.rs +++ b/tsparser/src/legacymeta/schema.rs @@ -216,12 +216,9 @@ impl BuilderCtx<'_, '_> { validation: None, }, - Basic::Void - | Basic::Object - | Basic::BigInt - | Basic::Symbol - | Basic::Undefined - | Basic::Never => { + Basic::BigInt => b(schema::Builtin::String), + + Basic::Void | Basic::Object | Basic::Symbol | Basic::Undefined | Basic::Never => { HANDLER.with(|h| h.err(&format!("unsupported basic type in schema: {:?}", typ))); b(schema::Builtin::Any) }