Changeset: 639c163bd823 for MonetDB URL: https://dev.monetdb.org/hg/MonetDB/rev/639c163bd823 Added Files: monetdb5/modules/atoms/pg_jsonpath/jsonpath_exec.c monetdb5/modules/atoms/pg_jsonpath/yyjson.c monetdb5/modules/atoms/pg_jsonpath/yyjson.h Modified Files: monetdb5/modules/atoms/pg_jsonpath/CMakeLists.txt Branch: json-extend Log Message:
import yyjson jsonpath_exec as is diffs (truncated from 22878 to 300 lines): diff --git a/monetdb5/modules/atoms/pg_jsonpath/CMakeLists.txt b/monetdb5/modules/atoms/pg_jsonpath/CMakeLists.txt --- a/monetdb5/modules/atoms/pg_jsonpath/CMakeLists.txt +++ b/monetdb5/modules/atoms/pg_jsonpath/CMakeLists.txt @@ -26,7 +26,10 @@ BISON_TARGET(jsonpath_parser target_sources(pg_jsonpath PRIVATE ${FLEX_jsonpath_scanner_OUTPUTS} - ${BISON_jsonpath_parser_OUTPUT_SOURCE}) + ${BISON_jsonpath_parser_OUTPUT_SOURCE} + yyjson.c # for now just build yyjson directly into pg_jsonpath + jsonpath.c + jsonpath_exec.c) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/jsonpath_scan.l ${CMAKE_CURRENT_BINARY_DIR}/jsonpath_scan.l @@ -41,7 +44,8 @@ target_link_libraries(pg_jsonpath monetdb_config_header bat sqlinclude - sqlcommon) + sqlcommon + mal) target_include_directories(pg_jsonpath PRIVATE diff --git a/monetdb5/modules/atoms/pg_jsonpath/jsonpath_exec.c b/monetdb5/modules/atoms/pg_jsonpath/jsonpath_exec.c new file mode 100644 --- /dev/null +++ b/monetdb5/modules/atoms/pg_jsonpath/jsonpath_exec.c @@ -0,0 +1,4494 @@ +/*------------------------------------------------------------------------- + * + * jsonpath_exec.c + * Routines for SQL/JSON path execution. + * + * Jsonpath is executed in the global context stored in JsonPathExecContext, + * which is passed to almost every function involved into execution. Entry + * point for jsonpath execution is executeJsonPath() function, which + * initializes execution context including initial JsonPathItem and JsonbValue, + * flags, stack for calculation of @ in filters. + * + * The result of jsonpath query execution is enum JsonPathExecResult and + * if succeeded sequence of JsonbValue, written to JsonValueList *found, which + * is passed through the jsonpath items. When found == NULL, we're inside + * exists-query and we're interested only in whether result is empty. In this + * case execution is stopped once first result item is found, and the only + * execution result is JsonPathExecResult. The values of JsonPathExecResult + * are following: + * - jperOk -- result sequence is not empty + * - jperNotFound -- result sequence is empty + * - jperError -- error occurred during execution + * + * Jsonpath is executed recursively (see executeItem()) starting form the + * first path item (which in turn might be, for instance, an arithmetic + * expression evaluated separately). On each step single JsonbValue obtained + * from previous path item is processed. The result of processing is a + * sequence of JsonbValue (probably empty), which is passed to the next path + * item one by one. When there is no next path item, then JsonbValue is added + * to the 'found' list. When found == NULL, then execution functions just + * return jperOk (see executeNextItem()). + * + * Many of jsonpath operations require automatic unwrapping of arrays in lax + * mode. So, if input value is array, then corresponding operation is + * processed not on array itself, but on all of its members one by one. + * executeItemOptUnwrapTarget() function have 'unwrap' argument, which indicates + * whether unwrapping of array is needed. When unwrap == true, each of array + * members is passed to executeItemOptUnwrapTarget() again but with unwrap == false + * in order to avoid subsequent array unwrapping. + * + * All boolean expressions (predicates) are evaluated by executeBoolItem() + * function, which returns tri-state JsonPathBool. When error is occurred + * during predicate execution, it returns jpbUnknown. According to standard + * predicates can be only inside filters. But we support their usage as + * jsonpath expression. This helps us to implement @@ operator. In this case + * resulting JsonPathBool is transformed into jsonb bool or null. + * + * Arithmetic and boolean expression are evaluated recursively from expression + * tree top down to the leaves. Therefore, for binary arithmetic expressions + * we calculate operands first. Then we check that results are numeric + * singleton lists, calculate the result and pass it to the next path item. + * + * Copyright (c) 2019-2024, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/utils/adt/jsonpath_exec.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "catalog/pg_collation.h" +#include "catalog/pg_type.h" +#include "executor/execExpr.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/miscnodes.h" +#include "nodes/nodeFuncs.h" +#include "regex/regex.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datetime.h" +#include "utils/float.h" +#include "utils/formatting.h" +#include "utils/json.h" +#include "utils/jsonpath.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/timestamp.h" + +/* + * Represents "base object" and it's "id" for .keyvalue() evaluation. + */ +typedef struct JsonBaseObjectInfo +{ + JsonbContainer *jbc; + int id; +} JsonBaseObjectInfo; + +/* Callbacks for executeJsonPath() */ +typedef JsonbValue *(*JsonPathGetVarCallback) (void *vars, char *varName, int varNameLen, + JsonbValue *baseObject, int *baseObjectId); +typedef int (*JsonPathCountVarsCallback) (void *vars); + +/* + * Context of jsonpath execution. + */ +typedef struct JsonPathExecContext +{ + void *vars; /* variables to substitute into jsonpath */ + JsonPathGetVarCallback getVar; /* callback to extract a given variable + * from 'vars' */ + JsonbValue *root; /* for $ evaluation */ + JsonbValue *current; /* for @ evaluation */ + JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue() + * evaluation */ + int lastGeneratedObjectId; /* "id" counter for .keyvalue() + * evaluation */ + int innermostArraySize; /* for LAST array index evaluation */ + bool laxMode; /* true for "lax" mode, false for "strict" + * mode */ + bool ignoreStructuralErrors; /* with "true" structural errors such + * as absence of required json item or + * unexpected json item type are + * ignored */ + bool throwErrors; /* with "false" all suppressible errors are + * suppressed */ + bool useTz; +} JsonPathExecContext; + +/* Context for LIKE_REGEX execution. */ +typedef struct JsonLikeRegexContext +{ + text *regex; + int cflags; +} JsonLikeRegexContext; + +/* Result of jsonpath predicate evaluation */ +typedef enum JsonPathBool +{ + jpbFalse = 0, + jpbTrue = 1, + jpbUnknown = 2 +} JsonPathBool; + +/* Result of jsonpath expression evaluation */ +typedef enum JsonPathExecResult +{ + jperOk = 0, + jperNotFound = 1, + jperError = 2 +} JsonPathExecResult; + +#define jperIsError(jper) ((jper) == jperError) + +/* + * List of jsonb values with shortcut for single-value list. + */ +typedef struct JsonValueList +{ + JsonbValue *singleton; + List *list; +} JsonValueList; + +typedef struct JsonValueListIterator +{ + JsonbValue *value; + List *list; + ListCell *next; +} JsonValueListIterator; + +/* Structures for JSON_TABLE execution */ + +/* + * Struct holding the result of jsonpath evaluation, to be used as source row + * for JsonTableGetValue() which in turn computes the values of individual + * JSON_TABLE columns. + */ +typedef struct JsonTablePlanRowSource +{ + Datum value; + bool isnull; +} JsonTablePlanRowSource; + +/* + * State of evaluation of row pattern derived by applying jsonpath given in + * a JsonTablePlan to an input document given in the parent TableFunc. + */ +typedef struct JsonTablePlanState +{ + /* Original plan */ + JsonTablePlan *plan; + + /* The following fields are only valid for JsonTablePathScan plans */ + + /* jsonpath to evaluate against the input doc to get the row pattern */ + JsonPath *path; + + /* + * Memory context to use when evaluating the row pattern from the jsonpath + */ + MemoryContext mcxt; + + /* PASSING arguments passed to jsonpath executor */ + List *args; + + /* List and iterator of jsonpath result values */ + JsonValueList found; + JsonValueListIterator iter; + + /* Currently selected row for JsonTableGetValue() to use */ + JsonTablePlanRowSource current; + + /* Counter for ORDINAL columns */ + int ordinal; + + /* Nested plan, if any */ + struct JsonTablePlanState *nested; + + /* Left sibling, if any */ + struct JsonTablePlanState *left; + + /* Right sibling, if any */ + struct JsonTablePlanState *right; + + /* Parent plan, if this is a nested plan */ + struct JsonTablePlanState *parent; +} JsonTablePlanState; + +/* Random number to identify JsonTableExecContext for sanity checking */ +#define JSON_TABLE_EXEC_CONTEXT_MAGIC 418352867 + +typedef struct JsonTableExecContext +{ + int magic; + + /* State of the plan providing a row evaluated from "root" jsonpath */ + JsonTablePlanState *rootplanstate; + + /* + * Per-column JsonTablePlanStates for all columns including the nested + * ones. + */ + JsonTablePlanState **colplanstates; +} JsonTableExecContext; + +/* strict/lax flags is decomposed into four [un]wrap/error flags */ +#define jspStrictAbsenceOfErrors(cxt) (!(cxt)->laxMode) +#define jspAutoUnwrap(cxt) ((cxt)->laxMode) +#define jspAutoWrap(cxt) ((cxt)->laxMode) +#define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors) +#define jspThrowErrors(cxt) ((cxt)->throwErrors) + +/* Convenience macro: return or throw error depending on context */ +#define RETURN_ERROR(throw_error) \ +do { \ + if (jspThrowErrors(cxt)) \ + throw_error; \ + else \ + return jperError; \ +} while (0) + +typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp, + JsonbValue *larg, + JsonbValue *rarg, + void *param); +typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error); + +static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars, + JsonPathGetVarCallback getVar, + JsonPathCountVarsCallback countVars, + Jsonb *json, bool throwErrors, + JsonValueList *result, bool useTz); +static JsonPathExecResult executeItem(JsonPathExecContext *cxt, + JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found); +static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt, + JsonPathItem *jsp, JsonbValue *jb, + JsonValueList *found, bool unwrap); +static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt, + JsonPathItem *jsp, JsonbValue *jb, _______________________________________________ checkin-list mailing list -- checkin-list@monetdb.org To unsubscribe send an email to checkin-list-le...@monetdb.org