feat: find element in jsonb by path is works!!!
This commit is contained in:
@@ -2,31 +2,14 @@
|
|||||||
\echo Use "CREATE EXTENSION hemar" to load this file. \quit
|
\echo Use "CREATE EXTENSION hemar" to load this file. \quit
|
||||||
|
|
||||||
CREATE SCHEMA hemar;
|
CREATE SCHEMA hemar;
|
||||||
|
|
||||||
-- Define the template rendering functions using hectic library
|
|
||||||
-- Expected usage:
|
|
||||||
-- ```sql
|
|
||||||
-- SELECT "hemar"."render"(
|
|
||||||
-- "declare" :=
|
|
||||||
-- jsonb_build_object(
|
|
||||||
-- 'name', 'test',
|
|
||||||
-- 'config', jsonb_build_object(
|
|
||||||
-- 'debug', true,
|
|
||||||
-- 'limit', 100
|
|
||||||
-- )
|
|
||||||
-- ),
|
|
||||||
-- "template" := $hemar$
|
|
||||||
-- {{ name }} {{ config.limit }}
|
|
||||||
-- $hemar$
|
|
||||||
-- );
|
|
||||||
-- ```
|
|
||||||
CREATE FUNCTION "hemar"."render"("declare" jsonb, "template" text)
|
|
||||||
RETURNS text
|
|
||||||
LANGUAGE C STRICT
|
|
||||||
AS 'hemar', 'pg_render';
|
|
||||||
|
|
||||||
-- Parse function returns the structure of a template for debugging
|
-- Parse function returns the structure of a template for debugging
|
||||||
CREATE FUNCTION "hemar"."parse"("template" text)
|
CREATE FUNCTION "hemar"."parse"("template" text)
|
||||||
RETURNS text
|
RETURNS text
|
||||||
LANGUAGE C STRICT
|
LANGUAGE C STRICT
|
||||||
AS 'hemar', 'pg_template_parse';
|
AS 'hemar', 'pg_template_parse';
|
||||||
|
|
||||||
|
-- JSONB path access function
|
||||||
|
CREATE FUNCTION "hemar"."jsonb_get_by_path"("json" jsonb, "path" text)
|
||||||
|
RETURNS jsonb
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'hemar', 'pg_jsonb_get_by_path';
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,115 +1,115 @@
|
|||||||
/*
|
/*
|
||||||
* hemar.h
|
* hemar.h
|
||||||
* Template parser for Hemar
|
* Template parser for Hemar
|
||||||
*/
|
*/
|
||||||
#ifndef HEMAR_TEMPLATE_H
|
#ifndef HEMAR_TEMPLATE_H
|
||||||
#define HEMAR_TEMPLATE_H
|
#define HEMAR_TEMPLATE_H
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
|
|
||||||
/* Maximum length for template syntax elements */
|
/* Maximum length for template syntax elements */
|
||||||
#define TEMPLATE_MAX_PREFIX_LEN 32
|
#define TEMPLATE_MAX_PREFIX_LEN 32
|
||||||
|
|
||||||
/* Template error codes */
|
/* Template error codes */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TEMPLATE_ERROR_NONE = 0,
|
TEMPLATE_ERROR_NONE = 0,
|
||||||
TEMPLATE_ERROR_UNKNOWN_TAG,
|
TEMPLATE_ERROR_UNKNOWN_TAG,
|
||||||
TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_CONTROLE,
|
TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_CONTROLE,
|
||||||
TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_SOURCE,
|
TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_SOURCE,
|
||||||
TEMPLATE_ERROR_UNEXPECTED_INTERPOLATION_END,
|
TEMPLATE_ERROR_UNEXPECTED_INTERPOLATION_END,
|
||||||
TEMPLATE_ERROR_NO_SOURSE_IN_SECTION,
|
TEMPLATE_ERROR_NO_SOURSE_IN_SECTION,
|
||||||
TEMPLATE_ERROR_NESTED_INTERPOLATION,
|
TEMPLATE_ERROR_NESTED_INTERPOLATION,
|
||||||
TEMPLATE_ERROR_UNEXPECTED_SECTION_END,
|
TEMPLATE_ERROR_UNEXPECTED_SECTION_END,
|
||||||
TEMPLATE_ERROR_NO_BEGIN_IN_SECTION,
|
TEMPLATE_ERROR_NO_BEGIN_IN_SECTION,
|
||||||
TEMPLATE_ERROR_NESTED_INCLUDE,
|
TEMPLATE_ERROR_NESTED_INCLUDE,
|
||||||
TEMPLATE_ERROR_NESTED_EXECUTE,
|
TEMPLATE_ERROR_NESTED_EXECUTE,
|
||||||
TEMPLATE_ERROR_INVALID_CONFIG,
|
TEMPLATE_ERROR_INVALID_CONFIG,
|
||||||
TEMPLATE_ERROR_OUT_OF_MEMORY,
|
TEMPLATE_ERROR_OUT_OF_MEMORY,
|
||||||
TEMPLATE_ERROR_UNEXPECTED_INCLUDE_END,
|
TEMPLATE_ERROR_UNEXPECTED_INCLUDE_END,
|
||||||
TEMPLATE_ERROR_UNEXPECTED_EXECUTE_END
|
TEMPLATE_ERROR_UNEXPECTED_EXECUTE_END
|
||||||
} TemplateErrorCode;
|
} TemplateErrorCode;
|
||||||
|
|
||||||
/* Template node types */
|
/* Template node types */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TEMPLATE_NODE_TEXT,
|
TEMPLATE_NODE_TEXT,
|
||||||
TEMPLATE_NODE_INTERPOLATE,
|
TEMPLATE_NODE_INTERPOLATE,
|
||||||
TEMPLATE_NODE_SECTION,
|
TEMPLATE_NODE_SECTION,
|
||||||
TEMPLATE_NODE_EXECUTE,
|
TEMPLATE_NODE_EXECUTE,
|
||||||
TEMPLATE_NODE_INCLUDE
|
TEMPLATE_NODE_INCLUDE
|
||||||
} TemplateNodeType;
|
} TemplateNodeType;
|
||||||
|
|
||||||
/* Template configuration structure */
|
/* Template configuration structure */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct {
|
struct {
|
||||||
struct {
|
struct {
|
||||||
const char *open; /* Default: "{%" */
|
const char *open; /* Default: "{%" */
|
||||||
const char *close; /* Default: "%}" */
|
const char *close; /* Default: "%}" */
|
||||||
} Braces;
|
} Braces;
|
||||||
struct {
|
struct {
|
||||||
const char *control; /* default: "for " */
|
const char *control; /* default: "for " */
|
||||||
const char *source; /* default: "in " */
|
const char *source; /* default: "in " */
|
||||||
const char *begin; /* default: "do " */
|
const char *begin; /* default: "do " */
|
||||||
} Section;
|
} Section;
|
||||||
struct {
|
struct {
|
||||||
const char *invoke; /* default: "" */
|
const char *invoke; /* default: "" */
|
||||||
} Interpolate;
|
} Interpolate;
|
||||||
struct {
|
struct {
|
||||||
const char *invoke; /* default: "include " */
|
const char *invoke; /* default: "include " */
|
||||||
} Include;
|
} Include;
|
||||||
struct {
|
struct {
|
||||||
const char *invoke; /* default: "exec " */
|
const char *invoke; /* default: "exec " */
|
||||||
} Execute;
|
} Execute;
|
||||||
const char *nesting; /* default: "->" */
|
const char *nesting; /* default: "->" */
|
||||||
} Syntax;
|
} Syntax;
|
||||||
} TemplateConfig;
|
} TemplateConfig;
|
||||||
|
|
||||||
/* Forward declaration */
|
/* Forward declaration */
|
||||||
typedef struct TemplateNode TemplateNode;
|
typedef struct TemplateNode TemplateNode;
|
||||||
|
|
||||||
/* Template value structures */
|
/* Template value structures */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *iterator;
|
char *iterator;
|
||||||
char *collection;
|
char *collection;
|
||||||
TemplateNode *body;
|
TemplateNode *body;
|
||||||
} TemplateSectionValue;
|
} TemplateSectionValue;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *key;
|
char *key;
|
||||||
} TemplateInterpolateValue;
|
} TemplateInterpolateValue;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *code;
|
char *code;
|
||||||
} TemplateExecuteValue;
|
} TemplateExecuteValue;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *key;
|
char *key;
|
||||||
} TemplateIncludeValue;
|
} TemplateIncludeValue;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *content;
|
char *content;
|
||||||
} TemplateTextValue;
|
} TemplateTextValue;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
TemplateSectionValue section;
|
TemplateSectionValue section;
|
||||||
TemplateInterpolateValue interpolate;
|
TemplateInterpolateValue interpolate;
|
||||||
TemplateExecuteValue execute;
|
TemplateExecuteValue execute;
|
||||||
TemplateIncludeValue include;
|
TemplateIncludeValue include;
|
||||||
TemplateTextValue text;
|
TemplateTextValue text;
|
||||||
} TemplateValue;
|
} TemplateValue;
|
||||||
|
|
||||||
/* Template node structure */
|
/* Template node structure */
|
||||||
struct TemplateNode {
|
struct TemplateNode {
|
||||||
TemplateNodeType type;
|
TemplateNodeType type;
|
||||||
TemplateValue *value;
|
TemplateValue *value;
|
||||||
TemplateNode *next;
|
TemplateNode *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Function declarations */
|
/* Function declarations */
|
||||||
TemplateConfig template_default_config(MemoryContext context);
|
TemplateConfig template_default_config(MemoryContext context);
|
||||||
bool template_validate_config(const TemplateConfig *config, TemplateErrorCode *error_code);
|
bool template_validate_config(const TemplateConfig *config, TemplateErrorCode *error_code);
|
||||||
TemplateNode *template_parse(MemoryContext context, const char **s, const TemplateConfig *config, bool inner_parse, TemplateErrorCode *error_code);
|
TemplateNode *template_parse(MemoryContext context, const char **s, const TemplateConfig *config, bool inner_parse, TemplateErrorCode *error_code);
|
||||||
void template_free_node(TemplateNode *node);
|
void template_free_node(TemplateNode *node);
|
||||||
const char *template_error_to_string(TemplateErrorCode code, TemplateConfig *config);
|
const char *template_error_to_string(TemplateErrorCode code, TemplateConfig *config);
|
||||||
|
|
||||||
#endif /* HEMAR_TEMPLATE_H */
|
#endif /* HEMAR_TEMPLATE_H */
|
||||||
@@ -1,333 +1,267 @@
|
|||||||
-- Test file for hemar.jsonb_get_by_path function
|
|
||||||
|
|
||||||
|
-- Test file for hemar.jsonb_get_by_path function
|
||||||
-- Run with: psql -f test_jsonb_path.sql
|
-- Run with: psql -f test_jsonb_path.sql
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Load extension if not already loaded
|
-- Load extension if not already loaded
|
||||||
|
|
||||||
-- CREATE EXTENSION IF NOT EXISTS hemar;
|
-- CREATE EXTENSION IF NOT EXISTS hemar;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Create sample test data
|
-- Create sample test data
|
||||||
|
|
||||||
DO $$
|
DO $$
|
||||||
|
|
||||||
DECLARE
|
DECLARE
|
||||||
|
|
||||||
test_json jsonb;
|
test_json jsonb;
|
||||||
result jsonb;
|
result jsonb;
|
||||||
passed boolean;
|
passed boolean;
|
||||||
total_tests integer := 0;
|
total_tests integer := 0;
|
||||||
passed_tests integer := 0;
|
passed_tests integer := 0;
|
||||||
|
current_path text;
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
test_json := jsonb_build_object(
|
test_json := jsonb_build_object(
|
||||||
|
|
||||||
'name', 'John Doe',
|
'name', 'John Doe',
|
||||||
|
|
||||||
'age', 30,
|
'age', 30,
|
||||||
|
|
||||||
'is_active', true,
|
'is_active', true,
|
||||||
|
|
||||||
'tags', jsonb_build_array('developer', 'postgresql', 'jsonb'),
|
'tags', jsonb_build_array('developer', 'postgresql', 'jsonb'),
|
||||||
|
|
||||||
'address', jsonb_build_object(
|
'address', jsonb_build_object(
|
||||||
|
|
||||||
'street', '123 Main St',
|
'street', '123 Main St',
|
||||||
|
|
||||||
'city', 'New York',
|
'city', 'New York',
|
||||||
|
|
||||||
'zip', '10001'
|
'zip', '10001'
|
||||||
|
|
||||||
),
|
),
|
||||||
|
|
||||||
'contacts', jsonb_build_array(
|
'contacts', jsonb_build_array(
|
||||||
|
|
||||||
jsonb_build_object(
|
jsonb_build_object(
|
||||||
|
|
||||||
'type', 'email',
|
'type', 'email',
|
||||||
|
|
||||||
'value', 'john@example.com'
|
'value', 'john@example.com'
|
||||||
|
|
||||||
),
|
),
|
||||||
|
|
||||||
jsonb_build_object(
|
jsonb_build_object(
|
||||||
|
|
||||||
'type', 'phone',
|
'type', 'phone',
|
||||||
|
|
||||||
'value', '555-1234',
|
'value', '555-1234',
|
||||||
|
|
||||||
'verified', true
|
'verified', true
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
),
|
),
|
||||||
|
|
||||||
'skills', jsonb_build_array(
|
'skills', jsonb_build_array(
|
||||||
|
|
||||||
jsonb_build_array('PostgreSQL', 5),
|
jsonb_build_array('PostgreSQL', 5),
|
||||||
|
|
||||||
jsonb_build_array('Python', 4),
|
jsonb_build_array('Python', 4),
|
||||||
|
|
||||||
jsonb_build_array('JavaScript', 3)
|
jsonb_build_array('JavaScript', 3)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Test basic field access
|
-- Test basic field access
|
||||||
|
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'name');
|
current_path := 'name';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result = '"John Doe"'::jsonb;
|
passed := result = '"John Doe"'::jsonb;
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
RAISE NOTICE 'Test %: Simple field access (string): % | PASSED: %',
|
RAISE NOTICE 'Test %: Simple field access (path="%"): % | PASSED: %',
|
||||||
|
total_tests, current_path, result, passed;
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Simple field access (string): % | PASSED: % (expected: "John Doe")',
|
RAISE WARNING 'Test %: Simple field access (path="%"): % | PASSED: % (expected: "John Doe")',
|
||||||
total_tests, result, passed;
|
total_tests, current_path, result, passed;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'age');
|
current_path := 'age';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result = '30'::jsonb;
|
passed := result = '30'::jsonb;
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
|
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
|
RAISE NOTICE 'Test %: Numeric field access (path="%"): % | PASSED: %',
|
||||||
RAISE NOTICE 'Test %: Numeric field access: % | PASSED: %',
|
total_tests, current_path, result, passed;
|
||||||
|
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Numeric field access: % | PASSED: % (expected: 30)',
|
RAISE WARNING 'Test %: Numeric field access (path="%"): % | PASSED: % (expected: 30)',
|
||||||
total_tests, result, passed;
|
total_tests, current_path, result, passed;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'is_active');
|
current_path := 'is_active';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result = 'true'::jsonb;
|
passed := result = 'true'::jsonb;
|
||||||
|
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
RAISE NOTICE 'Test %: Boolean field access: % | PASSED: %',
|
RAISE NOTICE 'Test %: Boolean field access (path="%"): % | PASSED: %',
|
||||||
|
total_tests, current_path, result, passed;
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Boolean field access: % | PASSED: % (expected: true)',
|
RAISE WARNING 'Test %: Boolean field access (path="%"): % | PASSED: % (expected: true)',
|
||||||
total_tests, result, passed;
|
total_tests, current_path, result, passed;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Test nested field access
|
-- Test nested field access
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'address.city');
|
current_path := 'address.city';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result = '"New York"'::jsonb;
|
passed := result = '"New York"'::jsonb;
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
|
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
|
RAISE NOTICE 'Test %: Nested object field access (path="%"): % | PASSED: %',
|
||||||
RAISE NOTICE 'Test %: Nested object field access: % | PASSED: %',
|
total_tests, current_path, result, passed;
|
||||||
|
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Nested object field access: % | PASSED: % (expected: "New York")',
|
RAISE WARNING 'Test %: Nested object field access (path="%"): % | PASSED: % (expected: "New York")',
|
||||||
|
total_tests, current_path, result, passed;
|
||||||
total_tests, result, passed;
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Test array access
|
-- Test array access
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'tags[1]');
|
current_path := 'tags[1]';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result = '"postgresql"'::jsonb;
|
passed := result = '"postgresql"'::jsonb;
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
|
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
|
RAISE NOTICE 'Test %: Simple array access (path="%"): % | PASSED: %',
|
||||||
RAISE NOTICE 'Test %: Simple array access: % | PASSED: %',
|
total_tests, current_path, result, passed;
|
||||||
|
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Simple array access: % | PASSED: % (expected: "postgresql")',
|
|
||||||
|
|
||||||
total_tests, result, passed;
|
RAISE WARNING 'Test %: Simple array access (path="%"): % | PASSED: % (expected: "postgresql")',
|
||||||
|
total_tests, current_path, result, passed;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'contacts[0].type');
|
current_path := 'contacts[0].type';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result = '"email"'::jsonb;
|
passed := result = '"email"'::jsonb;
|
||||||
|
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
RAISE NOTICE 'Test %: Object in array access: % | PASSED: %',
|
RAISE NOTICE 'Test %: Object in array access (path="%"): % | PASSED: %',
|
||||||
|
total_tests, current_path, result, passed;
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Object in array access: % | PASSED: % (expected: "email")',
|
RAISE WARNING 'Test %: Object in array access (path="%"): % | PASSED: % (expected: "email")',
|
||||||
total_tests, result, passed;
|
total_tests, current_path, result, passed;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'skills[1][0]');
|
current_path := 'skills[1][0]';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result = '"Python"'::jsonb;
|
passed := result = '"Python"'::jsonb;
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
|
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
|
RAISE NOTICE 'Test %: Nested array access (path="%"): % | PASSED: %',
|
||||||
RAISE NOTICE 'Test %: Nested array access: % | PASSED: %',
|
total_tests, current_path, result, passed;
|
||||||
|
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Nested array access: % | PASSED: % (expected: "Python")',
|
RAISE WARNING 'Test %: Nested array access (path="%"): % | PASSED: % (expected: "Python")',
|
||||||
total_tests, result, passed;
|
total_tests, current_path, result, passed;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'contacts[1].value');
|
current_path := 'contacts[1].value';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result = '"555-1234"'::jsonb;
|
passed := result = '"555-1234"'::jsonb;
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
|
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
|
RAISE NOTICE 'Test %: Complex path with multiple array indices (path="%"): % | PASSED: %',
|
||||||
RAISE NOTICE 'Test %: Complex path with multiple array indices: % | PASSED: %',
|
total_tests, current_path, result, passed;
|
||||||
|
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Complex path with multiple array indices: % | PASSED: % (expected: "555-1234")',
|
RAISE WARNING 'Test %: Complex path with multiple array indices (path="%"): % | PASSED: % (expected: "555-1234")',
|
||||||
total_tests, result, passed;
|
total_tests, current_path, result, passed;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Test object and array returns
|
-- Test object and array returns
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'address');
|
current_path := 'address';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := jsonb_typeof(result) = 'object';
|
passed := jsonb_typeof(result) = 'object';
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
|
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
|
RAISE NOTICE 'Test %: Path to object (path="%"): % | PASSED: %',
|
||||||
RAISE NOTICE 'Test %: Path to object: % | PASSED: %',
|
total_tests, current_path, result, passed;
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Path to object: % | PASSED: % (expected type: object, got: %)',
|
RAISE WARNING 'Test %: Path to object (path="%"): % | PASSED: % (expected type: object, got: %)',
|
||||||
|
total_tests, current_path, result, passed, jsonb_typeof(result);
|
||||||
total_tests, result, passed, jsonb_typeof(result);
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'contacts');
|
current_path := 'contacts';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := jsonb_typeof(result) = 'array';
|
passed := jsonb_typeof(result) = 'array';
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
|
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
|
RAISE NOTICE 'Test %: Path to array (path="%"): % | PASSED: %',
|
||||||
RAISE NOTICE 'Test %: Path to array: % | PASSED: %',
|
total_tests, current_path, result, passed;
|
||||||
|
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Path to array: % | PASSED: % (expected type: array, got: %)',
|
RAISE WARNING 'Test %: Path to array (path="%"): % | PASSED: % (expected type: array, got: %)',
|
||||||
total_tests, result, passed, jsonb_typeof(result);
|
total_tests, current_path, result, passed, jsonb_typeof(result);
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Test error cases
|
-- Test error cases
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'unknown_field');
|
current_path := 'unknown_field';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result IS NULL;
|
passed := result IS NULL;
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
|
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
|
RAISE NOTICE 'Test %: Non-existent field (path="%"): % | PASSED: %',
|
||||||
RAISE NOTICE 'Test %: Non-existent field: % | PASSED: %',
|
total_tests, current_path, result, passed;
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Non-existent field: % | PASSED: % (expected: NULL)',
|
RAISE WARNING 'Test %: Non-existent field (path="%"): % | PASSED: % (expected: NULL)',
|
||||||
|
total_tests, current_path, result, passed;
|
||||||
total_tests, result, passed;
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'address.country');
|
current_path := 'address.country';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result IS NULL;
|
passed := result IS NULL;
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
|
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
RAISE NOTICE 'Test %: Non-existent nested field: % | PASSED: %',
|
RAISE NOTICE 'Test %: Non-existent nested field (path="%"): % | PASSED: %',
|
||||||
total_tests, result, passed;
|
total_tests, current_path, result, passed;
|
||||||
|
|
||||||
ELSE
|
ELSE
|
||||||
|
RAISE WARNING 'Test %: Non-existent nested field (path="%"): % | PASSED: % (expected: NULL)',
|
||||||
RAISE WARNING 'Test %: Non-existent nested field: % | PASSED: % (expected: NULL)',
|
total_tests, current_path, result, passed;
|
||||||
|
|
||||||
total_tests, result, passed;
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'tags[10]');
|
current_path := 'tags[10]';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result IS NULL;
|
passed := result IS NULL;
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
|
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
|
RAISE NOTICE 'Test %: Array index out of bounds (path="%"): % | PASSED: %',
|
||||||
RAISE NOTICE 'Test %: Array index out of bounds: % | PASSED: %',
|
total_tests, current_path, result, passed;
|
||||||
|
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Array index out of bounds: % | PASSED: % (expected: NULL)',
|
RAISE WARNING 'Test %: Array index out of bounds (path="%"): % | PASSED: % (expected: NULL)',
|
||||||
total_tests, result, passed;
|
total_tests, current_path, result, passed;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Test edge cases
|
-- Test edge cases
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, '');
|
current_path := '';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result IS NULL;
|
passed := result IS NULL;
|
||||||
|
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
|
RAISE NOTICE 'Test %: Empty path (path="%"): % | PASSED: %',
|
||||||
RAISE NOTICE 'Test %: Empty path: % | PASSED: %',
|
total_tests, current_path, result, passed;
|
||||||
|
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Empty path: % | PASSED: % (expected: NULL)',
|
RAISE WARNING 'Test %: Empty path (path="%"): % | PASSED: % (expected: NULL)',
|
||||||
total_tests, result, passed;
|
total_tests, current_path, result, passed;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
total_tests := total_tests + 1;
|
total_tests := total_tests + 1;
|
||||||
result := hemar.jsonb_get_by_path(test_json, 'skills[0][1]');
|
current_path := 'skills[0][1]';
|
||||||
|
result := hemar.jsonb_get_by_path(test_json, current_path);
|
||||||
passed := result = '5'::jsonb;
|
passed := result = '5'::jsonb;
|
||||||
|
|
||||||
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
passed_tests := passed_tests + (CASE WHEN passed THEN 1 ELSE 0 END);
|
||||||
IF passed THEN
|
IF passed THEN
|
||||||
RAISE NOTICE 'Test %: Multiple array indices: % | PASSED: %',
|
RAISE NOTICE 'Test %: Multiple array indices (path="%"): % | PASSED: %',
|
||||||
|
total_tests, current_path, result, passed;
|
||||||
total_tests, result, passed;
|
|
||||||
ELSE
|
ELSE
|
||||||
RAISE WARNING 'Test %: Multiple array indices: % | PASSED: % (expected: 5)',
|
RAISE WARNING 'Test %: Multiple array indices (path="%"): % | PASSED: % (expected: 5)',
|
||||||
total_tests, result, passed;
|
total_tests, current_path, result, passed;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Print summary
|
-- Print summary
|
||||||
RAISE NOTICE '------------------------------------';
|
|
||||||
IF passed_tests = total_tests THEN
|
IF passed_tests = total_tests THEN
|
||||||
|
RAISE NOTICE '------------------------------------';
|
||||||
RAISE NOTICE 'SUMMARY: % of % tests passed (100%%)',
|
RAISE NOTICE 'SUMMARY: % of % tests passed (100%%)',
|
||||||
passed_tests, total_tests;
|
passed_tests, total_tests;
|
||||||
|
RAISE NOTICE '------------------------------------';
|
||||||
ELSE
|
ELSE
|
||||||
|
RAISE WARNING '------------------------------------';
|
||||||
RAISE WARNING 'SUMMARY: % of % tests passed (%)',
|
RAISE WARNING 'SUMMARY: % of % tests passed (%)',
|
||||||
passed_tests,
|
passed_tests,
|
||||||
total_tests,
|
total_tests,
|
||||||
round((passed_tests::numeric / total_tests::numeric) * 100, 2) || '%';
|
round((passed_tests::numeric / total_tests::numeric) * 100, 2) || '%';
|
||||||
|
RAISE WARNING '------------------------------------';
|
||||||
END IF;
|
END IF;
|
||||||
RAISE NOTICE '------------------------------------';
|
|
||||||
|
|
||||||
IF passed_tests != total_tests THEN
|
IF passed_tests != total_tests THEN
|
||||||
RAISE EXCEPTION 'Tests failed: % of % tests did not pass', (total_tests - passed_tests), total_tests;
|
RAISE EXCEPTION 'Tests failed: % of % tests did not pass', (total_tests - passed_tests), total_tests;
|
||||||
END IF;
|
END IF;
|
||||||
|
END $$;
|
||||||
END $$;
|
|
||||||
|
|||||||
Reference in New Issue
Block a user