1818 */
1919package oracle .json .parser ;
2020
21+ import java .io .ByteArrayOutputStream ;
2122import java .io .InputStream ;
2223import java .math .BigDecimal ;
2324import java .sql .PreparedStatement ;
4748import oracle .json .common .JsonFactoryProvider ;
4849import oracle .json .parser .Evaluator .EvaluatorCode ;
4950import oracle .sql .json .OracleJsonValue ;
51+ import oracle .sql .json .OracleJsonFactory ;
52+ import oracle .sql .json .OracleJsonGenerator ;
5053import oracle .sql .json .OracleJsonString ;
5154import oracle .sql .json .OracleJsonTimestampTZ ;
5255
@@ -867,12 +870,22 @@ public void appendJsonExists(StringBuilder output, boolean isTreatAsAvailable)
867870 appendJsonExists (output , null , 0 , isTreatAsAvailable );
868871 }
869872
870- public int appendJsonExists (StringBuilder output , String tokenFormat , int startingTokenIndex , boolean isTreatAsAvailable )
873+ public int appendJsonExists (StringBuilder output , String tokenFormat , int startingTokenIndex ,
874+ boolean isTreatAsAvailable )
875+ {
876+ return appendJsonExists (output , tokenFormat , startingTokenIndex , isTreatAsAvailable , true , null );
877+ }
878+
879+ public int appendJsonExists (StringBuilder output , String tokenFormat , int startingTokenIndex ,
880+ boolean isTreatAsAvailable , boolean allowBinds , OracleJsonFactory factory )
871881 {
872882 // Assumes that this method is invoked only if hasJsonExists()
873883 // returns true. So jsonExists cannot be null here.
874884 if (jsonExists == null )
875885 throw new IllegalStateException ();
886+
887+ if ((tokenFormat != null || startingTokenIndex != 0 ) && (!allowBinds ))
888+ throw new IllegalStateException ();
876889
877890 int tokenCount = 0 ;
878891
@@ -883,64 +896,139 @@ public int appendJsonExists(StringBuilder output, String tokenFormat, int starti
883896 output .append ("'" );
884897
885898 int numBinds = getNumVals ();
886- if (numBinds > 0 )
887- {
899+ if (numBinds > 0 ) {
888900 output .append (" passing " );
889901
890- for (int varNum = 0 ; varNum < numBinds ; ++varNum )
891- {
902+ for (int varNum = 0 ; varNum < numBinds ; ++varNum ) {
892903 if (tokenFormat != null )
893904 token = String .format (tokenFormat , startingTokenIndex );
894905 ValueTypePair vpair = valueArray .get (varNum );
895906
896907 if (varNum > 0 )
897908 output .append (", " );
898909
899- if (vpair .isTimestamp ())
900- {
901- // This format can consume trailing timezones including a "Z"
902- // but only if it's used with TO_TIMESTAMP_TZ.
903- output .append ("TO_TIMESTAMP_TZ(" );
904- output .append (token );
905- output .append (",'SYYYY-MM-DD\" T\" HH24:MI:SS.FFTZH:TZM')" );
910+ if (allowBinds ) {
911+ appendBindValue (vpair , output , token , isTreatAsAvailable );
912+ tokenCount ++;
913+ } else {
914+ appendInlineBindValueExpression (vpair .value , output , factory );
906915 }
907- else if (vpair .isDate ())
908- {
909- // This format includes the time component to reliably consume
910- // Oracle date+time values, but should also work if the time is
911- // not present in the bind variable.
912- output .append ("TO_DATE(" );
913- output .append (token );
914- output .append ("?,'SYYYY-MM-DD\" T\" HH24:MI:SS')" );
915- }
916- else if (isTreatAsAvailable && vpair .isObject ())
917- {
918- output .append ("treat(" );
919- output .append (token );
920- output .append (" as json(object))" );
921- }
922- else if (isTreatAsAvailable && vpair .isArray ())
923- {
924- output .append ("treat(" );
925- output .append (token );
926- output .append (" as json(array))" );
927- }
928- else
929- output .append (token );
930-
931- tokenCount ++;
932916
933917 output .append (" as \" B" );
934918 output .append (Integer .toString (varNum ));
935919 output .append ("\" " );
936-
937- startingTokenIndex ++;
920+
921+ if (allowBinds )
922+ startingTokenIndex ++;
938923 }
939924 }
940925
941926 return tokenCount ;
942927 }
943928
929+ private String getOsonHex (OracleJsonValue bind , OracleJsonFactory factory ) {
930+ ByteArrayOutputStream baos = new ByteArrayOutputStream ();
931+ OracleJsonGenerator gen = factory .createJsonBinaryGenerator (baos );
932+ gen .write (bind );
933+ gen .close ();
934+ byte [] oson = baos .toByteArray ();
935+ String hex = ByteArray .rawToHex (oson );
936+ return hex ;
937+ }
938+
939+ private void appendValueAsSQLType (String returnType , StringBuilder sql , String hex ) {
940+ if (returnType != null )
941+ sql .append ("json_value(json(hextoraw('" ).append (hex ).append ("')), '$' returning " ).append (returnType ).append (")" );
942+ else
943+ sql .append ("json(hextoraw('" ).append (hex ).append ("'))" );
944+ }
945+
946+ private void appendInlineBindValueExpression (JsonValue value , StringBuilder sql , OracleJsonFactory factory ) {
947+ OracleJsonValue ovalue = null ;
948+ try {
949+ Wrapper wrapper ;
950+ if (!(value instanceof Wrapper ) || !((wrapper = (Wrapper ) value )).isWrapperFor (OracleJsonValue .class )) {
951+ // infeasible as allowBinds is only false when input filter is oson
952+ throw new UnsupportedOperationException ();
953+ }
954+ ovalue = wrapper .unwrap (OracleJsonValue .class );
955+ } catch (SQLException e ) {
956+ // infeasible
957+ throw new IllegalStateException (e );
958+ }
959+ String hex = getOsonHex (ovalue , factory );
960+ switch (ovalue .getOracleJsonType ()) {
961+ case ARRAY :
962+ case NULL :
963+ case OBJECT :
964+ case TRUE :
965+ case FALSE :
966+ appendValueAsSQLType (null , sql , hex );
967+ break ;
968+ case BINARY :
969+ if (ovalue .asJsonBinary ().isId ())
970+ appendValueAsSQLType (null , sql , hex );
971+ else
972+ appendValueAsSQLType ("raw" , sql , hex );
973+ break ;
974+ case DATE :
975+ appendValueAsSQLType ("date" , sql , hex );
976+ break ;
977+ case DECIMAL :
978+ appendValueAsSQLType ("number" , sql , hex );
979+ break ;
980+ case DOUBLE :
981+ appendValueAsSQLType ("double precision" , sql , hex );
982+ break ;
983+ case FLOAT :
984+ appendValueAsSQLType ("float" , sql , hex );
985+ break ;
986+ case INTERVALDS :
987+ appendValueAsSQLType ("interval day to second" , sql , hex );
988+ break ;
989+ case INTERVALYM :
990+ appendValueAsSQLType ("interval year to month" , sql , hex );
991+ break ;
992+ case STRING :
993+ appendValueAsSQLType ("varchar2" , sql , hex );
994+ break ;
995+ case TIMESTAMP :
996+ appendValueAsSQLType ("timestamp" , sql , hex );
997+ break ;
998+ case TIMESTAMPTZ :
999+ appendValueAsSQLType ("timestamp with time zone" , sql , hex );
1000+ break ;
1001+ default :
1002+ throw new IllegalStateException ();
1003+ }
1004+ }
1005+
1006+ private void appendBindValue (ValueTypePair vpair , StringBuilder output , String token , boolean isTreatAsAvailable ) {
1007+ if (vpair .isTimestamp ()) {
1008+ // This format can consume trailing timezones including a "Z"
1009+ // but only if it's used with TO_TIMESTAMP_TZ.
1010+ output .append ("TO_TIMESTAMP_TZ(" );
1011+ output .append (token );
1012+ output .append (",'SYYYY-MM-DD\" T\" HH24:MI:SS.FFTZH:TZM')" );
1013+ } else if (vpair .isDate ()) {
1014+ // This format includes the time component to reliably consume
1015+ // Oracle date+time values, but should also work if the time is
1016+ // not present in the bind variable.
1017+ output .append ("TO_DATE(" );
1018+ output .append (token );
1019+ output .append ("?,'SYYYY-MM-DD\" T\" HH24:MI:SS')" );
1020+ } else if (isTreatAsAvailable && vpair .isObject ()) {
1021+ output .append ("treat(" );
1022+ output .append (token );
1023+ output .append (" as json(object))" );
1024+ } else if (isTreatAsAvailable && vpair .isArray ()) {
1025+ output .append ("treat(" );
1026+ output .append (token );
1027+ output .append (" as json(array))" );
1028+ } else
1029+ output .append (token );
1030+ }
1031+
9441032 /*
9451033 * Append a bind variable for a SQL JSON operator,
9461034 * with an optional format wrapper
@@ -1082,13 +1170,15 @@ public boolean hasFilterSpec()
10821170 hasSqlJsonClause ();
10831171 }
10841172
1085- public boolean appendFilterSpec (StringBuilder sb , boolean append ,
1086- String columnName , String inputFormatClause ,
1087- boolean isTreatAsAvailable )
1088- throws QueryException
1089- {
1173+ public boolean appendFilterSpec (StringBuilder sb , boolean append , String columnName , String inputFormatClause ,
1174+ boolean isTreatAsAvailable ) throws QueryException {
1175+ return appendFilterSpec (sb , append , columnName , inputFormatClause , isTreatAsAvailable , true , null );
1176+ }
1177+
1178+ public boolean appendFilterSpec (StringBuilder sb , boolean append , String columnName , String inputFormatClause ,
1179+ boolean isTreatAsAvailable , boolean allowBinds , OracleJsonFactory factory ) throws QueryException {
10901180 int length = sb .length ();
1091- appendFilterSpec (sb , append , columnName , inputFormatClause , null , 0 , isTreatAsAvailable );
1181+ appendFilterSpec (sb , append , columnName , inputFormatClause , null , 0 , isTreatAsAvailable , allowBinds , factory );
10921182
10931183 if (sb .length () == length ) {
10941184 if (append == true )
@@ -1098,12 +1188,20 @@ public boolean appendFilterSpec(StringBuilder sb, boolean append,
10981188 }
10991189 return true ;
11001190 }
1191+
1192+
1193+ //Not part of a public API. Needs to be public for use from REST layer.
1194+ public int appendFilterSpec (StringBuilder sb , boolean append , String columnName , String inputFormatClause ,
1195+ String format , int startingIndex , boolean isTreatAsAvailable ) throws QueryException {
1196+ return appendFilterSpec (sb , append , columnName , inputFormatClause , format , startingIndex , isTreatAsAvailable , true , null );
1197+ }
11011198
1102- // Not part of a public API. Needs to be public for use from REST layer.
1199+ //Not part of a public API
11031200 public int appendFilterSpec (StringBuilder sb , boolean append ,
11041201 String columnName , String inputFormatClause ,
11051202 String format , int startingIndex ,
1106- boolean isTreatAsAvailable ) throws QueryException
1203+ boolean isTreatAsAvailable , boolean allowBinds ,
1204+ OracleJsonFactory factory ) throws QueryException
11071205 {
11081206 int tokenCount = 0 ;
11091207
@@ -1116,7 +1214,7 @@ public int appendFilterSpec(StringBuilder sb, boolean append,
11161214 if (hasJsonExists ())
11171215 {
11181216 appendAnd (sb , append );
1119- tokenCount = appendFilterSpecJsonExists (sb , columnName , inputFormatClause , format , startingIndex , isTreatAsAvailable );
1217+ tokenCount = appendFilterSpecJsonExists (sb , columnName , inputFormatClause , format , startingIndex , isTreatAsAvailable , allowBinds , factory );
11201218 append = true ;
11211219 }
11221220
@@ -1159,7 +1257,8 @@ public int appendFilterSpec(StringBuilder sb, boolean append,
11591257
11601258 private int appendFilterSpecJsonExists (StringBuilder sb , String columnName ,
11611259 String inputFormatClause , String tokenFormat ,
1162- int startingTokenIndex , boolean isTreatAsAvailable )
1260+ int startingTokenIndex , boolean isTreatAsAvailable ,
1261+ boolean allowBinds , OracleJsonFactory factory )
11631262 {
11641263 int tokenCount = 0 ;
11651264 sb .append ("JSON_EXISTS(" );
@@ -1169,7 +1268,7 @@ private int appendFilterSpecJsonExists(StringBuilder sb, String columnName,
11691268 }
11701269
11711270 sb .append ("," );
1172- tokenCount = appendJsonExists (sb , tokenFormat , startingTokenIndex , isTreatAsAvailable );
1271+ tokenCount = appendJsonExists (sb , tokenFormat , startingTokenIndex , isTreatAsAvailable , allowBinds , factory );
11731272 if (this .strictTypeMode ) {
11741273 sb .append (" type(strict)" );
11751274 }
0 commit comments