@@ -70,7 +70,8 @@ if Code.ensure_loaded?(Postgrex) do
7070 case Postgrex . prepare_execute ( conn , name , sql , params , opts ) do
7171 { :error , % Postgrex.Error { postgres: % { pg_code: "22P02" , message: message } } = error } ->
7272 context = """
73- . If you are trying to query a JSON field, the parameter must be interpolated. Instead of
73+ . If you are trying to query a JSON field, the parameter may need to be interpolated. \
74+ Instead of
7475
7576 p.json["field"] == "value"
7677
@@ -684,16 +685,7 @@ if Code.ensure_loaded?(Postgrex) do
684685 end
685686
686687 defp expr ( { :json_extract_path , _ , [ expr , path ] } , sources , query ) do
687- path =
688- intersperse_map ( path , ?, , fn
689- binary when is_binary ( binary ) ->
690- [ ?" , escape_json_key ( binary ) , ?" ]
691-
692- integer when is_integer ( integer ) ->
693- Integer . to_string ( integer )
694- end )
695-
696- [ ?( , expr ( expr , sources , query ) , "#>'{" , path , "}')" ]
688+ json_extract_path ( expr , path , sources , query )
697689 end
698690
699691 defp expr ( { :filter , _ , [ agg , filter ] } , sources , query ) do
@@ -717,6 +709,18 @@ if Code.ensure_loaded?(Postgrex) do
717709
718710 defp expr ( { :count , _ , [ ] } , _sources , _query ) , do: "count(*)"
719711
712+ defp expr ( { :== , _ , [ { :json_extract_path , _ , [ expr , path ] } = left , right ] } , sources , query )
713+ when is_binary ( right ) or is_integer ( right ) do
714+ case Enum . split ( path , - 1 ) do
715+ { path , [ last ] } when is_binary ( last ) ->
716+ extracted = json_extract_path ( expr , path , sources , query )
717+ [ ?( , extracted , "@>'{" , escape_json ( last ) , ": " , escape_json ( right ) | "}')" ]
718+
719+ _ ->
720+ [ maybe_paren ( left , sources , query ) , " = " | maybe_paren ( right , sources , query ) ]
721+ end
722+ end
723+
720724 defp expr ( { fun , _ , args } , sources , query ) when is_atom ( fun ) and is_list ( args ) do
721725 { modifier , args } =
722726 case args do
@@ -770,6 +774,15 @@ if Code.ensure_loaded?(Postgrex) do
770774 error! ( query , "unsupported expression: #{ inspect ( expr ) } " )
771775 end
772776
777+ defp json_extract_path ( expr , [ ] , sources , query ) do
778+ expr ( expr , sources , query )
779+ end
780+
781+ defp json_extract_path ( expr , path , sources , query ) do
782+ path = intersperse_map ( path , ?, , & escape_json / 1 )
783+ [ ?( , expr ( expr , sources , query ) , "#>'{" , path , "}')" ]
784+ end
785+
773786 defp type_unless_typed ( % Ecto.Query.Tagged { } , _type ) , do: [ ]
774787 defp type_unless_typed ( _ , type ) , do: [ ?: , ?: | type ]
775788
@@ -1328,10 +1341,17 @@ if Code.ensure_loaded?(Postgrex) do
13281341 :binary . replace ( value , "'" , "''" , [ :global ] )
13291342 end
13301343
1331- defp escape_json_key ( value ) when is_binary ( value ) do
1332- value
1333- |> escape_string ( )
1334- |> :binary . replace ( "\" " , "\\ \" " , [ :global ] )
1344+ defp escape_json ( value ) when is_binary ( value ) do
1345+ escaped =
1346+ value
1347+ |> escape_string ( )
1348+ |> :binary . replace ( "\" " , "\\ \" " , [ :global ] )
1349+
1350+ [ ?" , escaped , ?" ]
1351+ end
1352+
1353+ defp escape_json ( value ) when is_integer ( value ) do
1354+ Integer . to_string ( value )
13351355 end
13361356
13371357 defp ecto_to_db ( { :array , t } ) , do: [ ecto_to_db ( t ) , ?[ , ?] ]
0 commit comments