From e3175ac48fd5f7c16e32fcdd8588922b997c23c7 Mon Sep 17 00:00:00 2001
From: Sean Aye <sean.aye@agilebits.com>
Date: Thu, 9 Jan 2025 23:32:42 -0400
Subject: [PATCH] add test

---
 sqlx-macros-core/src/derives/attributes.rs |  7 ++++---
 sqlx-macros-core/src/derives/row.rs        |  2 +-
 tests/mysql/macros.rs                      | 24 ++++++++++++++++++++++
 3 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/sqlx-macros-core/src/derives/attributes.rs b/sqlx-macros-core/src/derives/attributes.rs
index 000e88462b..7b541f22b4 100644
--- a/sqlx-macros-core/src/derives/attributes.rs
+++ b/sqlx-macros-core/src/derives/attributes.rs
@@ -1,7 +1,7 @@
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::quote_spanned;
 use syn::{
-    parenthesized, parse::discouraged::AnyDelimiter, punctuated::Punctuated, token::{self, Comma}, Attribute, DeriveInput, Field, LitStr, Meta, Token, Type, Variant
+    parenthesized, punctuated::Punctuated, token::Comma, Attribute, DeriveInput, Field, LitStr, Meta, Token, Type, Variant
 };
 
 macro_rules! assert_attribute {
@@ -170,7 +170,8 @@ pub fn parse_child_attributes(input: &[Attribute]) -> syn::Result<SqlxChildAttri
                 if meta.input.peek(syn::token::Paren) {
                     let content;
                     parenthesized!(content in meta.input);
-                    let literal: Token![nullable] = content.parse()?;
+                    let literal: Ident = content.parse()?;
+                    assert_eq!(literal.to_string(), "nullable", "Unrecognized `json` attribute. Valid values are `json` or `json(nullable)`");
                     json = Some(JsonAttribute::Nullable);
                 } else {
                     json = Some(JsonAttribute::NonNullable);
@@ -180,7 +181,7 @@ pub fn parse_child_attributes(input: &[Attribute]) -> syn::Result<SqlxChildAttri
             Ok(())
         })?;
 
-        if json && flatten {
+        if json.is_some() && flatten {
             fail!(
                 attr,
                 "Cannot use `json` and `flatten` together on the same field"
diff --git a/sqlx-macros-core/src/derives/row.rs b/sqlx-macros-core/src/derives/row.rs
index 96abc8e2e0..83c4659c19 100644
--- a/sqlx-macros-core/src/derives/row.rs
+++ b/sqlx-macros-core/src/derives/row.rs
@@ -176,7 +176,7 @@ fn expand_derive_from_row_struct(
                     )
                 },
                 // Try from + Json nullable
-                (false, Some(try_from), Some(JsonAttribute::Nullable)) => {
+                (false, Some(_), Some(JsonAttribute::Nullable)) => {
                     panic!("Cannot use both try from and json nullable")
                 },
                 // Json
diff --git a/tests/mysql/macros.rs b/tests/mysql/macros.rs
index f6bc75955a..4dbdf07841 100644
--- a/tests/mysql/macros.rs
+++ b/tests/mysql/macros.rs
@@ -494,6 +494,30 @@ async fn test_from_row_json_attr() -> anyhow::Result<()> {
     Ok(())
 }
 
+#[sqlx_macros::test]
+async fn test_from_row_json_attr_nullable() -> anyhow::Result<()> {
+    #[derive(serde::Deserialize)]
+    struct J {
+        a: u32,
+        b: u32,
+    }
+
+    #[derive(sqlx::FromRow)]
+    struct Record {
+        #[sqlx(json(nullable))]
+        j: Option<J>,
+    }
+
+    let mut conn = new::<MySql>().await?;
+
+    let record = sqlx::query_as::<_, Record>("select null as j")
+        .fetch_one(&mut conn)
+        .await?;
+
+    assert_eq!(record.j, None);
+    Ok(())
+}
+
 #[sqlx_macros::test]
 async fn test_from_row_json_try_from_attr() -> anyhow::Result<()> {
     #[derive(serde::Deserialize)]