mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 10:30:33 +03:00 
			
		
		
		
	Unify JSON categorize type API and export for external use
This essentially removes the JsonbTypeCategory enum and jsonb_categorize_type() and integrates any jsonb-specific logic that was in jsonb_categorize_type() into json_categorize_type(), now moved to jsonfuncs.c. The remaining JsonTypeCategory enum and json_categorize_type() cover the needs of the callers in both json.c and jsonb.c. json_categorize_type() has grown a new parameter named is_jsonb for callers to engage the jsonb-specific behavior of json_categorize_type(). One notable change in the now exported API of json_categorize_type() is that it now always returns *outfuncoid even though a caller may have no need currently to see one. This is in preparation of later commits to implement additional SQL/JSON functions. Co-authored-by: Álvaro Herrera <alvherre@alvh.no-ip.org> Reviewed-by: Álvaro Herrera <alvherre@alvh.no-ip.org> Discussion: https://postgr.es/m/CA+HiwqE4XTdfb1nW=Ojoy_tQSRhYt-q_kb6i5d4xcKyrLC1Nbg@mail.gmail.com
This commit is contained in:
		| @@ -19,7 +19,6 @@ | ||||
| #include "funcapi.h" | ||||
| #include "libpq/pqformat.h" | ||||
| #include "miscadmin.h" | ||||
| #include "parser/parse_coerce.h" | ||||
| #include "utils/array.h" | ||||
| #include "utils/builtins.h" | ||||
| #include "utils/date.h" | ||||
| @@ -29,21 +28,6 @@ | ||||
| #include "utils/lsyscache.h" | ||||
| #include "utils/typcache.h" | ||||
|  | ||||
| typedef enum					/* type categories for datum_to_json */ | ||||
| { | ||||
| 	JSONTYPE_NULL,				/* null, so we didn't bother to identify */ | ||||
| 	JSONTYPE_BOOL,				/* boolean (built-in types only) */ | ||||
| 	JSONTYPE_NUMERIC,			/* numeric (ditto) */ | ||||
| 	JSONTYPE_DATE,				/* we use special formatting for datetimes */ | ||||
| 	JSONTYPE_TIMESTAMP, | ||||
| 	JSONTYPE_TIMESTAMPTZ, | ||||
| 	JSONTYPE_JSON,				/* JSON itself (and JSONB) */ | ||||
| 	JSONTYPE_ARRAY,				/* array */ | ||||
| 	JSONTYPE_COMPOSITE,			/* composite */ | ||||
| 	JSONTYPE_CAST,				/* something with an explicit cast to JSON */ | ||||
| 	JSONTYPE_OTHER				/* all else */ | ||||
| } JsonTypeCategory; | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Support for fast key uniqueness checking. | ||||
| @@ -107,9 +91,6 @@ static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, | ||||
| 							  bool use_line_feeds); | ||||
| static void array_to_json_internal(Datum array, StringInfo result, | ||||
| 								   bool use_line_feeds); | ||||
| static void json_categorize_type(Oid typoid, | ||||
| 								 JsonTypeCategory *tcategory, | ||||
| 								 Oid *outfuncoid); | ||||
| static void datum_to_json(Datum val, bool is_null, StringInfo result, | ||||
| 						  JsonTypeCategory tcategory, Oid outfuncoid, | ||||
| 						  bool key_scalar); | ||||
| @@ -182,106 +163,6 @@ json_recv(PG_FUNCTION_ARGS) | ||||
| 	PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes)); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Determine how we want to print values of a given type in datum_to_json. | ||||
|  * | ||||
|  * Given the datatype OID, return its JsonTypeCategory, as well as the type's | ||||
|  * output function OID.  If the returned category is JSONTYPE_CAST, we | ||||
|  * return the OID of the type->JSON cast function instead. | ||||
|  */ | ||||
| static void | ||||
| json_categorize_type(Oid typoid, | ||||
| 					 JsonTypeCategory *tcategory, | ||||
| 					 Oid *outfuncoid) | ||||
| { | ||||
| 	bool		typisvarlena; | ||||
|  | ||||
| 	/* Look through any domain */ | ||||
| 	typoid = getBaseType(typoid); | ||||
|  | ||||
| 	*outfuncoid = InvalidOid; | ||||
|  | ||||
| 	/* | ||||
| 	 * We need to get the output function for everything except date and | ||||
| 	 * timestamp types, array and composite types, booleans, and non-builtin | ||||
| 	 * types where there's a cast to json. | ||||
| 	 */ | ||||
|  | ||||
| 	switch (typoid) | ||||
| 	{ | ||||
| 		case BOOLOID: | ||||
| 			*tcategory = JSONTYPE_BOOL; | ||||
| 			break; | ||||
|  | ||||
| 		case INT2OID: | ||||
| 		case INT4OID: | ||||
| 		case INT8OID: | ||||
| 		case FLOAT4OID: | ||||
| 		case FLOAT8OID: | ||||
| 		case NUMERICOID: | ||||
| 			getTypeOutputInfo(typoid, outfuncoid, &typisvarlena); | ||||
| 			*tcategory = JSONTYPE_NUMERIC; | ||||
| 			break; | ||||
|  | ||||
| 		case DATEOID: | ||||
| 			*tcategory = JSONTYPE_DATE; | ||||
| 			break; | ||||
|  | ||||
| 		case TIMESTAMPOID: | ||||
| 			*tcategory = JSONTYPE_TIMESTAMP; | ||||
| 			break; | ||||
|  | ||||
| 		case TIMESTAMPTZOID: | ||||
| 			*tcategory = JSONTYPE_TIMESTAMPTZ; | ||||
| 			break; | ||||
|  | ||||
| 		case JSONOID: | ||||
| 		case JSONBOID: | ||||
| 			getTypeOutputInfo(typoid, outfuncoid, &typisvarlena); | ||||
| 			*tcategory = JSONTYPE_JSON; | ||||
| 			break; | ||||
|  | ||||
| 		default: | ||||
| 			/* Check for arrays and composites */ | ||||
| 			if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID | ||||
| 				|| typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID) | ||||
| 				*tcategory = JSONTYPE_ARRAY; | ||||
| 			else if (type_is_rowtype(typoid))	/* includes RECORDOID */ | ||||
| 				*tcategory = JSONTYPE_COMPOSITE; | ||||
| 			else | ||||
| 			{ | ||||
| 				/* It's probably the general case ... */ | ||||
| 				*tcategory = JSONTYPE_OTHER; | ||||
| 				/* but let's look for a cast to json, if it's not built-in */ | ||||
| 				if (typoid >= FirstNormalObjectId) | ||||
| 				{ | ||||
| 					Oid			castfunc; | ||||
| 					CoercionPathType ctype; | ||||
|  | ||||
| 					ctype = find_coercion_pathway(JSONOID, typoid, | ||||
| 												  COERCION_EXPLICIT, | ||||
| 												  &castfunc); | ||||
| 					if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc)) | ||||
| 					{ | ||||
| 						*tcategory = JSONTYPE_CAST; | ||||
| 						*outfuncoid = castfunc; | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						/* non builtin type with no cast */ | ||||
| 						getTypeOutputInfo(typoid, outfuncoid, &typisvarlena); | ||||
| 					} | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					/* any other builtin type */ | ||||
| 					getTypeOutputInfo(typoid, outfuncoid, &typisvarlena); | ||||
| 				} | ||||
| 			} | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Turn a Datum into JSON text, appending the string to "result". | ||||
|  * | ||||
| @@ -591,7 +472,7 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds) | ||||
| 	get_typlenbyvalalign(element_type, | ||||
| 						 &typlen, &typbyval, &typalign); | ||||
|  | ||||
| 	json_categorize_type(element_type, | ||||
| 	json_categorize_type(element_type, false, | ||||
| 						 &tcategory, &outfuncoid); | ||||
|  | ||||
| 	deconstruct_array(v, element_type, typlen, typbyval, | ||||
| @@ -665,7 +546,8 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds) | ||||
| 			outfuncoid = InvalidOid; | ||||
| 		} | ||||
| 		else | ||||
| 			json_categorize_type(att->atttypid, &tcategory, &outfuncoid); | ||||
| 			json_categorize_type(att->atttypid, false, &tcategory, | ||||
| 								 &outfuncoid); | ||||
|  | ||||
| 		datum_to_json(val, isnull, result, tcategory, outfuncoid, false); | ||||
| 	} | ||||
| @@ -699,7 +581,7 @@ add_json(Datum val, bool is_null, StringInfo result, | ||||
| 		outfuncoid = InvalidOid; | ||||
| 	} | ||||
| 	else | ||||
| 		json_categorize_type(val_type, | ||||
| 		json_categorize_type(val_type, false, | ||||
| 							 &tcategory, &outfuncoid); | ||||
|  | ||||
| 	datum_to_json(val, is_null, result, tcategory, outfuncoid, key_scalar); | ||||
| @@ -784,12 +666,13 @@ to_json_is_immutable(Oid typoid) | ||||
| 	JsonTypeCategory tcategory; | ||||
| 	Oid			outfuncoid; | ||||
|  | ||||
| 	json_categorize_type(typoid, &tcategory, &outfuncoid); | ||||
| 	json_categorize_type(typoid, false, &tcategory, &outfuncoid); | ||||
|  | ||||
| 	switch (tcategory) | ||||
| 	{ | ||||
| 		case JSONTYPE_BOOL: | ||||
| 		case JSONTYPE_JSON: | ||||
| 		case JSONTYPE_JSONB: | ||||
| 		case JSONTYPE_NULL: | ||||
| 			return true; | ||||
|  | ||||
| @@ -830,7 +713,7 @@ to_json(PG_FUNCTION_ARGS) | ||||
| 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 				 errmsg("could not determine input data type"))); | ||||
|  | ||||
| 	json_categorize_type(val_type, | ||||
| 	json_categorize_type(val_type, false, | ||||
| 						 &tcategory, &outfuncoid); | ||||
|  | ||||
| 	result = makeStringInfo(); | ||||
| @@ -880,7 +763,7 @@ json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null) | ||||
| 		MemoryContextSwitchTo(oldcontext); | ||||
|  | ||||
| 		appendStringInfoChar(state->str, '['); | ||||
| 		json_categorize_type(arg_type, &state->val_category, | ||||
| 		json_categorize_type(arg_type, false, &state->val_category, | ||||
| 							 &state->val_output_func); | ||||
| 	} | ||||
| 	else | ||||
| @@ -1112,7 +995,7 @@ json_object_agg_transfn_worker(FunctionCallInfo fcinfo, | ||||
| 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 					 errmsg("could not determine data type for argument %d", 1))); | ||||
|  | ||||
| 		json_categorize_type(arg_type, &state->key_category, | ||||
| 		json_categorize_type(arg_type, false, &state->key_category, | ||||
| 							 &state->key_output_func); | ||||
|  | ||||
| 		arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2); | ||||
| @@ -1122,7 +1005,7 @@ json_object_agg_transfn_worker(FunctionCallInfo fcinfo, | ||||
| 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 					 errmsg("could not determine data type for argument %d", 2))); | ||||
|  | ||||
| 		json_categorize_type(arg_type, &state->val_category, | ||||
| 		json_categorize_type(arg_type, false, &state->val_category, | ||||
| 							 &state->val_output_func); | ||||
|  | ||||
| 		appendStringInfoString(state->str, "{ "); | ||||
|   | ||||
| @@ -19,7 +19,6 @@ | ||||
| #include "funcapi.h" | ||||
| #include "libpq/pqformat.h" | ||||
| #include "miscadmin.h" | ||||
| #include "parser/parse_coerce.h" | ||||
| #include "utils/builtins.h" | ||||
| #include "utils/date.h" | ||||
| #include "utils/datetime.h" | ||||
| @@ -37,29 +36,12 @@ typedef struct JsonbInState | ||||
| 	Node	   *escontext; | ||||
| } JsonbInState; | ||||
|  | ||||
| /* unlike with json categories, we need to treat json and jsonb differently */ | ||||
| typedef enum					/* type categories for datum_to_jsonb */ | ||||
| { | ||||
| 	JSONBTYPE_NULL,				/* null, so we didn't bother to identify */ | ||||
| 	JSONBTYPE_BOOL,				/* boolean (built-in types only) */ | ||||
| 	JSONBTYPE_NUMERIC,			/* numeric (ditto) */ | ||||
| 	JSONBTYPE_DATE,				/* we use special formatting for datetimes */ | ||||
| 	JSONBTYPE_TIMESTAMP,		/* we use special formatting for timestamp */ | ||||
| 	JSONBTYPE_TIMESTAMPTZ,		/* ... and timestamptz */ | ||||
| 	JSONBTYPE_JSON,				/* JSON */ | ||||
| 	JSONBTYPE_JSONB,			/* JSONB */ | ||||
| 	JSONBTYPE_ARRAY,			/* array */ | ||||
| 	JSONBTYPE_COMPOSITE,		/* composite */ | ||||
| 	JSONBTYPE_JSONCAST,			/* something with an explicit cast to JSON */ | ||||
| 	JSONBTYPE_OTHER				/* all else */ | ||||
| } JsonbTypeCategory; | ||||
|  | ||||
| typedef struct JsonbAggState | ||||
| { | ||||
| 	JsonbInState *res; | ||||
| 	JsonbTypeCategory key_category; | ||||
| 	JsonTypeCategory key_category; | ||||
| 	Oid			key_output_func; | ||||
| 	JsonbTypeCategory val_category; | ||||
| 	JsonTypeCategory val_category; | ||||
| 	Oid			val_output_func; | ||||
| } JsonbAggState; | ||||
|  | ||||
| @@ -72,19 +54,13 @@ static JsonParseErrorType jsonb_in_array_end(void *pstate); | ||||
| static JsonParseErrorType jsonb_in_object_field_start(void *pstate, char *fname, bool isnull); | ||||
| static void jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal); | ||||
| static JsonParseErrorType jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype); | ||||
| static void jsonb_categorize_type(Oid typoid, | ||||
| 								  JsonbTypeCategory *tcategory, | ||||
| 								  Oid *outfuncoid); | ||||
| static void composite_to_jsonb(Datum composite, JsonbInState *result); | ||||
| static void array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, | ||||
| 							   Datum *vals, bool *nulls, int *valcount, | ||||
| 							   JsonbTypeCategory tcategory, Oid outfuncoid); | ||||
| 							   JsonTypeCategory tcategory, Oid outfuncoid); | ||||
| static void array_to_jsonb_internal(Datum array, JsonbInState *result); | ||||
| static void jsonb_categorize_type(Oid typoid, | ||||
| 								  JsonbTypeCategory *tcategory, | ||||
| 								  Oid *outfuncoid); | ||||
| static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
| 						   JsonbTypeCategory tcategory, Oid outfuncoid, | ||||
| 						   JsonTypeCategory tcategory, Oid outfuncoid, | ||||
| 						   bool key_scalar); | ||||
| static void add_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
| 					  Oid val_type, bool key_scalar); | ||||
| @@ -633,112 +609,6 @@ add_indent(StringInfo out, bool indent, int level) | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Determine how we want to render values of a given type in datum_to_jsonb. | ||||
|  * | ||||
|  * Given the datatype OID, return its JsonbTypeCategory, as well as the type's | ||||
|  * output function OID.  If the returned category is JSONBTYPE_JSONCAST, | ||||
|  * we return the OID of the relevant cast function instead. | ||||
|  */ | ||||
| static void | ||||
| jsonb_categorize_type(Oid typoid, | ||||
| 					  JsonbTypeCategory *tcategory, | ||||
| 					  Oid *outfuncoid) | ||||
| { | ||||
| 	bool		typisvarlena; | ||||
|  | ||||
| 	/* Look through any domain */ | ||||
| 	typoid = getBaseType(typoid); | ||||
|  | ||||
| 	*outfuncoid = InvalidOid; | ||||
|  | ||||
| 	/* | ||||
| 	 * We need to get the output function for everything except date and | ||||
| 	 * timestamp types, booleans, array and composite types, json and jsonb, | ||||
| 	 * and non-builtin types where there's a cast to json. In this last case | ||||
| 	 * we return the oid of the cast function instead. | ||||
| 	 */ | ||||
|  | ||||
| 	switch (typoid) | ||||
| 	{ | ||||
| 		case BOOLOID: | ||||
| 			*tcategory = JSONBTYPE_BOOL; | ||||
| 			break; | ||||
|  | ||||
| 		case INT2OID: | ||||
| 		case INT4OID: | ||||
| 		case INT8OID: | ||||
| 		case FLOAT4OID: | ||||
| 		case FLOAT8OID: | ||||
| 		case NUMERICOID: | ||||
| 			getTypeOutputInfo(typoid, outfuncoid, &typisvarlena); | ||||
| 			*tcategory = JSONBTYPE_NUMERIC; | ||||
| 			break; | ||||
|  | ||||
| 		case DATEOID: | ||||
| 			*tcategory = JSONBTYPE_DATE; | ||||
| 			break; | ||||
|  | ||||
| 		case TIMESTAMPOID: | ||||
| 			*tcategory = JSONBTYPE_TIMESTAMP; | ||||
| 			break; | ||||
|  | ||||
| 		case TIMESTAMPTZOID: | ||||
| 			*tcategory = JSONBTYPE_TIMESTAMPTZ; | ||||
| 			break; | ||||
|  | ||||
| 		case JSONBOID: | ||||
| 			*tcategory = JSONBTYPE_JSONB; | ||||
| 			break; | ||||
|  | ||||
| 		case JSONOID: | ||||
| 			*tcategory = JSONBTYPE_JSON; | ||||
| 			break; | ||||
|  | ||||
| 		default: | ||||
| 			/* Check for arrays and composites */ | ||||
| 			if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID | ||||
| 				|| typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID) | ||||
| 				*tcategory = JSONBTYPE_ARRAY; | ||||
| 			else if (type_is_rowtype(typoid))	/* includes RECORDOID */ | ||||
| 				*tcategory = JSONBTYPE_COMPOSITE; | ||||
| 			else | ||||
| 			{ | ||||
| 				/* It's probably the general case ... */ | ||||
| 				*tcategory = JSONBTYPE_OTHER; | ||||
|  | ||||
| 				/* | ||||
| 				 * but first let's look for a cast to json (note: not to | ||||
| 				 * jsonb) if it's not built-in. | ||||
| 				 */ | ||||
| 				if (typoid >= FirstNormalObjectId) | ||||
| 				{ | ||||
| 					Oid			castfunc; | ||||
| 					CoercionPathType ctype; | ||||
|  | ||||
| 					ctype = find_coercion_pathway(JSONOID, typoid, | ||||
| 												  COERCION_EXPLICIT, &castfunc); | ||||
| 					if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc)) | ||||
| 					{ | ||||
| 						*tcategory = JSONBTYPE_JSONCAST; | ||||
| 						*outfuncoid = castfunc; | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						/* not a cast type, so just get the usual output func */ | ||||
| 						getTypeOutputInfo(typoid, outfuncoid, &typisvarlena); | ||||
| 					} | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					/* any other builtin type */ | ||||
| 					getTypeOutputInfo(typoid, outfuncoid, &typisvarlena); | ||||
| 				} | ||||
| 				break; | ||||
| 			} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Turn a Datum into jsonb, adding it to the result JsonbInState. | ||||
|  * | ||||
| @@ -753,7 +623,7 @@ jsonb_categorize_type(Oid typoid, | ||||
|  */ | ||||
| static void | ||||
| datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
| 			   JsonbTypeCategory tcategory, Oid outfuncoid, | ||||
| 			   JsonTypeCategory tcategory, Oid outfuncoid, | ||||
| 			   bool key_scalar) | ||||
| { | ||||
| 	char	   *outputstr; | ||||
| @@ -770,11 +640,11 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
| 		jb.type = jbvNull; | ||||
| 	} | ||||
| 	else if (key_scalar && | ||||
| 			 (tcategory == JSONBTYPE_ARRAY || | ||||
| 			  tcategory == JSONBTYPE_COMPOSITE || | ||||
| 			  tcategory == JSONBTYPE_JSON || | ||||
| 			  tcategory == JSONBTYPE_JSONB || | ||||
| 			  tcategory == JSONBTYPE_JSONCAST)) | ||||
| 			 (tcategory == JSONTYPE_ARRAY || | ||||
| 			  tcategory == JSONTYPE_COMPOSITE || | ||||
| 			  tcategory == JSONTYPE_JSON || | ||||
| 			  tcategory == JSONTYPE_JSONB || | ||||
| 			  tcategory == JSONTYPE_JSON)) | ||||
| 	{ | ||||
| 		ereport(ERROR, | ||||
| 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| @@ -782,18 +652,18 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (tcategory == JSONBTYPE_JSONCAST) | ||||
| 		if (tcategory == JSONTYPE_CAST) | ||||
| 			val = OidFunctionCall1(outfuncoid, val); | ||||
|  | ||||
| 		switch (tcategory) | ||||
| 		{ | ||||
| 			case JSONBTYPE_ARRAY: | ||||
| 			case JSONTYPE_ARRAY: | ||||
| 				array_to_jsonb_internal(val, result); | ||||
| 				break; | ||||
| 			case JSONBTYPE_COMPOSITE: | ||||
| 			case JSONTYPE_COMPOSITE: | ||||
| 				composite_to_jsonb(val, result); | ||||
| 				break; | ||||
| 			case JSONBTYPE_BOOL: | ||||
| 			case JSONTYPE_BOOL: | ||||
| 				if (key_scalar) | ||||
| 				{ | ||||
| 					outputstr = DatumGetBool(val) ? "true" : "false"; | ||||
| @@ -807,7 +677,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
| 					jb.val.boolean = DatumGetBool(val); | ||||
| 				} | ||||
| 				break; | ||||
| 			case JSONBTYPE_NUMERIC: | ||||
| 			case JSONTYPE_NUMERIC: | ||||
| 				outputstr = OidOutputFunctionCall(outfuncoid, val); | ||||
| 				if (key_scalar) | ||||
| 				{ | ||||
| @@ -845,26 +715,26 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
| 					} | ||||
| 				} | ||||
| 				break; | ||||
| 			case JSONBTYPE_DATE: | ||||
| 			case JSONTYPE_DATE: | ||||
| 				jb.type = jbvString; | ||||
| 				jb.val.string.val = JsonEncodeDateTime(NULL, val, | ||||
| 													   DATEOID, NULL); | ||||
| 				jb.val.string.len = strlen(jb.val.string.val); | ||||
| 				break; | ||||
| 			case JSONBTYPE_TIMESTAMP: | ||||
| 			case JSONTYPE_TIMESTAMP: | ||||
| 				jb.type = jbvString; | ||||
| 				jb.val.string.val = JsonEncodeDateTime(NULL, val, | ||||
| 													   TIMESTAMPOID, NULL); | ||||
| 				jb.val.string.len = strlen(jb.val.string.val); | ||||
| 				break; | ||||
| 			case JSONBTYPE_TIMESTAMPTZ: | ||||
| 			case JSONTYPE_TIMESTAMPTZ: | ||||
| 				jb.type = jbvString; | ||||
| 				jb.val.string.val = JsonEncodeDateTime(NULL, val, | ||||
| 													   TIMESTAMPTZOID, NULL); | ||||
| 				jb.val.string.len = strlen(jb.val.string.val); | ||||
| 				break; | ||||
| 			case JSONBTYPE_JSONCAST: | ||||
| 			case JSONBTYPE_JSON: | ||||
| 			case JSONTYPE_CAST: | ||||
| 			case JSONTYPE_JSON: | ||||
| 				{ | ||||
| 					/* parse the json right into the existing result object */ | ||||
| 					JsonLexContext *lex; | ||||
| @@ -887,7 +757,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
| 					pg_parse_json_or_ereport(lex, &sem); | ||||
| 				} | ||||
| 				break; | ||||
| 			case JSONBTYPE_JSONB: | ||||
| 			case JSONTYPE_JSONB: | ||||
| 				{ | ||||
| 					Jsonb	   *jsonb = DatumGetJsonbP(val); | ||||
| 					JsonbIterator *it; | ||||
| @@ -931,7 +801,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
|  | ||||
| 	/* Now insert jb into result, unless we did it recursively */ | ||||
| 	if (!is_null && !scalar_jsonb && | ||||
| 		tcategory >= JSONBTYPE_JSON && tcategory <= JSONBTYPE_JSONCAST) | ||||
| 		tcategory >= JSONTYPE_JSON && tcategory <= JSONTYPE_CAST) | ||||
| 	{ | ||||
| 		/* work has been done recursively */ | ||||
| 		return; | ||||
| @@ -976,7 +846,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
|  */ | ||||
| static void | ||||
| array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, Datum *vals, | ||||
| 				   bool *nulls, int *valcount, JsonbTypeCategory tcategory, | ||||
| 				   bool *nulls, int *valcount, JsonTypeCategory tcategory, | ||||
| 				   Oid outfuncoid) | ||||
| { | ||||
| 	int			i; | ||||
| @@ -1020,7 +890,7 @@ array_to_jsonb_internal(Datum array, JsonbInState *result) | ||||
| 	int16		typlen; | ||||
| 	bool		typbyval; | ||||
| 	char		typalign; | ||||
| 	JsonbTypeCategory tcategory; | ||||
| 	JsonTypeCategory tcategory; | ||||
| 	Oid			outfuncoid; | ||||
|  | ||||
| 	ndim = ARR_NDIM(v); | ||||
| @@ -1037,8 +907,8 @@ array_to_jsonb_internal(Datum array, JsonbInState *result) | ||||
| 	get_typlenbyvalalign(element_type, | ||||
| 						 &typlen, &typbyval, &typalign); | ||||
|  | ||||
| 	jsonb_categorize_type(element_type, | ||||
| 						  &tcategory, &outfuncoid); | ||||
| 	json_categorize_type(element_type, true, | ||||
| 						 &tcategory, &outfuncoid); | ||||
|  | ||||
| 	deconstruct_array(v, element_type, typlen, typbyval, | ||||
| 					  typalign, &elements, &nulls, | ||||
| @@ -1084,7 +954,7 @@ composite_to_jsonb(Datum composite, JsonbInState *result) | ||||
| 		Datum		val; | ||||
| 		bool		isnull; | ||||
| 		char	   *attname; | ||||
| 		JsonbTypeCategory tcategory; | ||||
| 		JsonTypeCategory tcategory; | ||||
| 		Oid			outfuncoid; | ||||
| 		JsonbValue	v; | ||||
| 		Form_pg_attribute att = TupleDescAttr(tupdesc, i); | ||||
| @@ -1105,11 +975,12 @@ composite_to_jsonb(Datum composite, JsonbInState *result) | ||||
|  | ||||
| 		if (isnull) | ||||
| 		{ | ||||
| 			tcategory = JSONBTYPE_NULL; | ||||
| 			tcategory = JSONTYPE_NULL; | ||||
| 			outfuncoid = InvalidOid; | ||||
| 		} | ||||
| 		else | ||||
| 			jsonb_categorize_type(att->atttypid, &tcategory, &outfuncoid); | ||||
| 			json_categorize_type(att->atttypid, true, &tcategory, | ||||
| 								 &outfuncoid); | ||||
|  | ||||
| 		datum_to_jsonb(val, isnull, result, tcategory, outfuncoid, false); | ||||
| 	} | ||||
| @@ -1122,7 +993,7 @@ composite_to_jsonb(Datum composite, JsonbInState *result) | ||||
|  * Append JSON text for "val" to "result". | ||||
|  * | ||||
|  * This is just a thin wrapper around datum_to_jsonb.  If the same type will be | ||||
|  * printed many times, avoid using this; better to do the jsonb_categorize_type | ||||
|  * printed many times, avoid using this; better to do the json_categorize_type | ||||
|  * lookups only once. | ||||
|  */ | ||||
|  | ||||
| @@ -1130,7 +1001,7 @@ static void | ||||
| add_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
| 		  Oid val_type, bool key_scalar) | ||||
| { | ||||
| 	JsonbTypeCategory tcategory; | ||||
| 	JsonTypeCategory tcategory; | ||||
| 	Oid			outfuncoid; | ||||
|  | ||||
| 	if (val_type == InvalidOid) | ||||
| @@ -1140,12 +1011,12 @@ add_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
|  | ||||
| 	if (is_null) | ||||
| 	{ | ||||
| 		tcategory = JSONBTYPE_NULL; | ||||
| 		tcategory = JSONTYPE_NULL; | ||||
| 		outfuncoid = InvalidOid; | ||||
| 	} | ||||
| 	else | ||||
| 		jsonb_categorize_type(val_type, | ||||
| 							  &tcategory, &outfuncoid); | ||||
| 		json_categorize_type(val_type, true, | ||||
| 							 &tcategory, &outfuncoid); | ||||
|  | ||||
| 	datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar); | ||||
| } | ||||
| @@ -1160,33 +1031,33 @@ add_jsonb(Datum val, bool is_null, JsonbInState *result, | ||||
| bool | ||||
| to_jsonb_is_immutable(Oid typoid) | ||||
| { | ||||
| 	JsonbTypeCategory tcategory; | ||||
| 	JsonTypeCategory tcategory; | ||||
| 	Oid			outfuncoid; | ||||
|  | ||||
| 	jsonb_categorize_type(typoid, &tcategory, &outfuncoid); | ||||
| 	json_categorize_type(typoid, true, &tcategory, &outfuncoid); | ||||
|  | ||||
| 	switch (tcategory) | ||||
| 	{ | ||||
| 		case JSONBTYPE_NULL: | ||||
| 		case JSONBTYPE_BOOL: | ||||
| 		case JSONBTYPE_JSON: | ||||
| 		case JSONBTYPE_JSONB: | ||||
| 		case JSONTYPE_NULL: | ||||
| 		case JSONTYPE_BOOL: | ||||
| 		case JSONTYPE_JSON: | ||||
| 		case JSONTYPE_JSONB: | ||||
| 			return true; | ||||
|  | ||||
| 		case JSONBTYPE_DATE: | ||||
| 		case JSONBTYPE_TIMESTAMP: | ||||
| 		case JSONBTYPE_TIMESTAMPTZ: | ||||
| 		case JSONTYPE_DATE: | ||||
| 		case JSONTYPE_TIMESTAMP: | ||||
| 		case JSONTYPE_TIMESTAMPTZ: | ||||
| 			return false; | ||||
|  | ||||
| 		case JSONBTYPE_ARRAY: | ||||
| 		case JSONTYPE_ARRAY: | ||||
| 			return false;		/* TODO recurse into elements */ | ||||
|  | ||||
| 		case JSONBTYPE_COMPOSITE: | ||||
| 		case JSONTYPE_COMPOSITE: | ||||
| 			return false;		/* TODO recurse into fields */ | ||||
|  | ||||
| 		case JSONBTYPE_NUMERIC: | ||||
| 		case JSONBTYPE_JSONCAST: | ||||
| 		case JSONBTYPE_OTHER: | ||||
| 		case JSONTYPE_NUMERIC: | ||||
| 		case JSONTYPE_CAST: | ||||
| 		case JSONTYPE_OTHER: | ||||
| 			return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE; | ||||
| 	} | ||||
|  | ||||
| @@ -1202,7 +1073,7 @@ to_jsonb(PG_FUNCTION_ARGS) | ||||
| 	Datum		val = PG_GETARG_DATUM(0); | ||||
| 	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0); | ||||
| 	JsonbInState result; | ||||
| 	JsonbTypeCategory tcategory; | ||||
| 	JsonTypeCategory tcategory; | ||||
| 	Oid			outfuncoid; | ||||
|  | ||||
| 	if (val_type == InvalidOid) | ||||
| @@ -1210,8 +1081,8 @@ to_jsonb(PG_FUNCTION_ARGS) | ||||
| 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 				 errmsg("could not determine input data type"))); | ||||
|  | ||||
| 	jsonb_categorize_type(val_type, | ||||
| 						  &tcategory, &outfuncoid); | ||||
| 	json_categorize_type(val_type, true, | ||||
| 						 &tcategory, &outfuncoid); | ||||
|  | ||||
| 	memset(&result, 0, sizeof(JsonbInState)); | ||||
|  | ||||
| @@ -1636,8 +1507,8 @@ jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null) | ||||
| 									 WJB_BEGIN_ARRAY, NULL); | ||||
| 		MemoryContextSwitchTo(oldcontext); | ||||
|  | ||||
| 		jsonb_categorize_type(arg_type, &state->val_category, | ||||
| 							  &state->val_output_func); | ||||
| 		json_categorize_type(arg_type, true, &state->val_category, | ||||
| 							 &state->val_output_func); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| @@ -1816,8 +1687,8 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo, | ||||
| 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 					 errmsg("could not determine input data type"))); | ||||
|  | ||||
| 		jsonb_categorize_type(arg_type, &state->key_category, | ||||
| 							  &state->key_output_func); | ||||
| 		json_categorize_type(arg_type, true, &state->key_category, | ||||
| 							 &state->key_output_func); | ||||
|  | ||||
| 		arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2); | ||||
|  | ||||
| @@ -1826,8 +1697,8 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo, | ||||
| 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 					 errmsg("could not determine input data type"))); | ||||
|  | ||||
| 		jsonb_categorize_type(arg_type, &state->val_category, | ||||
| 							  &state->val_output_func); | ||||
| 		json_categorize_type(arg_type, true, &state->val_category, | ||||
| 							 &state->val_output_func); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
|   | ||||
| @@ -26,6 +26,7 @@ | ||||
| #include "mb/pg_wchar.h" | ||||
| #include "miscadmin.h" | ||||
| #include "nodes/miscnodes.h" | ||||
| #include "parser/parse_coerce.h" | ||||
| #include "utils/array.h" | ||||
| #include "utils/builtins.h" | ||||
| #include "utils/fmgroids.h" | ||||
| @@ -5685,3 +5686,113 @@ json_get_first_token(text *json, bool throw_error) | ||||
|  | ||||
| 	return JSON_TOKEN_INVALID;	/* invalid json */ | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Determine how we want to print values of a given type in datum_to_json(b). | ||||
|  * | ||||
|  * Given the datatype OID, return its JsonTypeCategory, as well as the type's | ||||
|  * output function OID.  If the returned category is JSONTYPE_CAST, we return | ||||
|  * the OID of the type->JSON cast function instead. | ||||
|  */ | ||||
| void | ||||
| json_categorize_type(Oid typoid, bool is_jsonb, | ||||
| 					 JsonTypeCategory *tcategory, Oid *outfuncoid) | ||||
| { | ||||
| 	bool		typisvarlena; | ||||
|  | ||||
| 	/* Look through any domain */ | ||||
| 	typoid = getBaseType(typoid); | ||||
|  | ||||
| 	*outfuncoid = InvalidOid; | ||||
|  | ||||
| 	switch (typoid) | ||||
| 	{ | ||||
| 		case BOOLOID: | ||||
| 			*outfuncoid = F_BOOLOUT; | ||||
| 			*tcategory = JSONTYPE_BOOL; | ||||
| 			break; | ||||
|  | ||||
| 		case INT2OID: | ||||
| 		case INT4OID: | ||||
| 		case INT8OID: | ||||
| 		case FLOAT4OID: | ||||
| 		case FLOAT8OID: | ||||
| 		case NUMERICOID: | ||||
| 			getTypeOutputInfo(typoid, outfuncoid, &typisvarlena); | ||||
| 			*tcategory = JSONTYPE_NUMERIC; | ||||
| 			break; | ||||
|  | ||||
| 		case DATEOID: | ||||
| 			*outfuncoid = F_DATE_OUT; | ||||
| 			*tcategory = JSONTYPE_DATE; | ||||
| 			break; | ||||
|  | ||||
| 		case TIMESTAMPOID: | ||||
| 			*outfuncoid = F_TIMESTAMP_OUT; | ||||
| 			*tcategory = JSONTYPE_TIMESTAMP; | ||||
| 			break; | ||||
|  | ||||
| 		case TIMESTAMPTZOID: | ||||
| 			*outfuncoid = F_TIMESTAMPTZ_OUT; | ||||
| 			*tcategory = JSONTYPE_TIMESTAMPTZ; | ||||
| 			break; | ||||
|  | ||||
| 		case JSONOID: | ||||
| 			getTypeOutputInfo(typoid, outfuncoid, &typisvarlena); | ||||
| 			*tcategory = JSONTYPE_JSON; | ||||
| 			break; | ||||
|  | ||||
| 		case JSONBOID: | ||||
| 			getTypeOutputInfo(typoid, outfuncoid, &typisvarlena); | ||||
| 			*tcategory = is_jsonb ? JSONTYPE_JSONB : JSONTYPE_JSON; | ||||
| 			break; | ||||
|  | ||||
| 		default: | ||||
| 			/* Check for arrays and composites */ | ||||
| 			if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID | ||||
| 				|| typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID) | ||||
| 			{ | ||||
| 				*outfuncoid = F_ARRAY_OUT; | ||||
| 				*tcategory = JSONTYPE_ARRAY; | ||||
| 			} | ||||
| 			else if (type_is_rowtype(typoid))	/* includes RECORDOID */ | ||||
| 			{ | ||||
| 				*outfuncoid = F_RECORD_OUT; | ||||
| 				*tcategory = JSONTYPE_COMPOSITE; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				/* | ||||
| 				 * It's probably the general case.  But let's look for a cast | ||||
| 				 * to json (note: not to jsonb even if is_jsonb is true), if | ||||
| 				 * it's not built-in. | ||||
| 				 */ | ||||
| 				*tcategory = JSONTYPE_OTHER; | ||||
| 				if (typoid >= FirstNormalObjectId) | ||||
| 				{ | ||||
| 					Oid			castfunc; | ||||
| 					CoercionPathType ctype; | ||||
|  | ||||
| 					ctype = find_coercion_pathway(JSONOID, typoid, | ||||
| 												  COERCION_EXPLICIT, | ||||
| 												  &castfunc); | ||||
| 					if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc)) | ||||
| 					{ | ||||
| 						*outfuncoid = castfunc; | ||||
| 						*tcategory = JSONTYPE_CAST; | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						/* non builtin type with no cast */ | ||||
| 						getTypeOutputInfo(typoid, outfuncoid, &typisvarlena); | ||||
| 					} | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					/* any other builtin type */ | ||||
| 					getTypeOutputInfo(typoid, outfuncoid, &typisvarlena); | ||||
| 				} | ||||
| 			} | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -63,4 +63,24 @@ extern Jsonb *transform_jsonb_string_values(Jsonb *jsonb, void *action_state, | ||||
| extern text *transform_json_string_values(text *json, void *action_state, | ||||
| 										  JsonTransformStringValuesAction transform_action); | ||||
|  | ||||
| /* Type categories returned by json_categorize_type */ | ||||
| typedef enum | ||||
| { | ||||
| 	JSONTYPE_NULL,				/* null, so we didn't bother to identify */ | ||||
| 	JSONTYPE_BOOL,				/* boolean (built-in types only) */ | ||||
| 	JSONTYPE_NUMERIC,			/* numeric (ditto) */ | ||||
| 	JSONTYPE_DATE,				/* we use special formatting for datetimes */ | ||||
| 	JSONTYPE_TIMESTAMP, | ||||
| 	JSONTYPE_TIMESTAMPTZ, | ||||
| 	JSONTYPE_JSON,				/* JSON (and JSONB, if not is_jsonb) */ | ||||
| 	JSONTYPE_JSONB,				/* JSONB (if is_jsonb) */ | ||||
| 	JSONTYPE_ARRAY,				/* array */ | ||||
| 	JSONTYPE_COMPOSITE,			/* composite */ | ||||
| 	JSONTYPE_CAST,				/* something with an explicit cast to JSON */ | ||||
| 	JSONTYPE_OTHER,				/* all else */ | ||||
| } JsonTypeCategory; | ||||
|  | ||||
| extern void json_categorize_type(Oid typoid, bool is_jsonb, | ||||
| 								 JsonTypeCategory *tcategory, Oid *outfuncoid); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -1318,7 +1318,6 @@ JsonbIteratorToken | ||||
| JsonbPair | ||||
| JsonbParseState | ||||
| JsonbSubWorkspace | ||||
| JsonbTypeCategory | ||||
| JsonbValue | ||||
| JumbleState | ||||
| JunkFilter | ||||
|   | ||||
		Reference in New Issue
	
	Block a user