JSON Data Guide Nuance 1

The JSON Data Guide provides a parseable list of elements (including name, datatype and length) contained JSON data. This makes parsing much faster. by eliminating the need to interrogate for structure and then get the desired data from it.

But Data Guide has a few nuances to be aware of. And the first is that data types may change in summary entries. Allow me to demonstrate:

I’ll create two Oracle views showing Data Guide info for table named JSON_DATA. The table has two columns – one containing JSON data and another containin a numeric primary key.

The first view (ALL_DG) ignores the primary key field to show the Data Guide for all records in the table.

CREATE OR REPLACE VIEW all_dg AS
WITH dataguide_view AS
  ( SELECT JSON_DATAGUIDE(json_col) dataguide_rows
      FROM json_data )
SELECT edetails.*
  FROM dataguide_view,
       JSON_TABLE(dataguide_rows,'$[*]'
  COLUMNS epath VARCHAR2(100) PATH '$."o:path"',
          etype VARCHAR2(10) PATH '$."type"',
          elength NUMBER PATH '$."o:length"') edetails;
View created.

The next view (DG_PER_ID) includes the primary key. This means the JSON_DATAGUIDE call includes ALL JSON keys for the specific record denoted by the primary key.

CREATE OR REPLACE VIEW dg_per_id AS
WITH dataguide_view AS
  ( SELECT json_id,
           JSON_DATAGUIDE(json_col) dataguide_rows
      FROM json_data
    GROUP BY json_id)
SELECT json_id,
       edetails.*
  FROM dataguide_view,
       JSON_TABLE(dataguide_rows,'$[*]'
 COLUMNS epath VARCHAR2(100) PATH '$."o:path"',
         etype VARCHAR2(10) PATH '$."type"',
         elength NUMBER PATH '$."o:length"') edetails;
 View created.

Before I query from the views lets take a look at the data in the table. It contains 2 distinct keys – RAPTORS and CAVALIERS. The value for CAVALIERS is the number 4 which is the number of games it took them to dispatch the Raptors in this years NBA playoffs. The RAPTORS key has two values – the number 0 which denotes the number of games they won in the series and the phrase ‘LOST AGAIN’ which summarizes their playoff run…again…this year.

SELECT *
 FROM json_data;
 JSON_ID    JSON_COL
 ---------- -----------------------
          1 [{"RAPTORS":0},{"CAVALIERS":4}]
          2 {"RAPTORS":"LOST AGAIN"}

First a query from the detailed view including the primary keys. No surprises here.

SELECT *
  FROM dg_per_id;
JSON_ID EPATH        ETYPE  ELENGTH
------- ------------ ------ -------
 1      $.RAPTORS    number       1
 1      $.CAVALIERS  number       1
 2      $.RAPTORS    string      16

Now lets query from the summary view. Notice the type of data for the RAPTORS key changed.

SELECT *
  FROM all_dg;
EPATH       ETYPE      ELENGTH
----------- ---------- -------
$.RAPTORS   string          16
$.CAVALIERS number           1

This is happens because this query gets ALL keys. And since one value for RAPTORS is a number and the other is a string Data Guide chooses a datatype of string that will safely process both values.

Be aware of this when using Data Guide because it may require more datatype checks in your code. And remember to not make any assumptions.

Thanks for reading!

JSON Schema 2

Dataguides provide a map of JSON data and facilitate quick access to the elements contained within. I’ve demonstrated that in several other posts. This post expands on those concepts to show how Dataguides can be used to validate JSON as well.

What do I mean by validate? Good question – I mean ensuring that JSON data contains a set of specific elements in a specific order before being allowed into the database.

I’ll use a PL/SQL package to hold the “correct” Data Guide. Then I’ll use a database trigger to validate the inserted JSON against what is in the package. If the Data Guides don’t match then the insert is refused.

--Create a table with a JSON column
CREATE TABLE json_data
( json_id NUMBER NOT NULL PRIMARY KEY,
  json_col CLOB,
CONSTRAINT must_be_json
CHECK ( json_col IS JSON ));

-- Insert a row of JSON data
INSERT INTO json_data
VALUES(1,'{"KeyOne":"ValueOne"}');

-- View the Data Guide of the row inserted
SELECT JSON_DATAGUIDE(json_col)
  FROM json_data;
JSON_DATAGUIDE(JSON_COL)
----------------------------------------------------
[{"o:path":"$.KeyOne","type":"string","o:length":8}]

-- Create a package containing the Dataguide to
-- validate against. For the sake of simplicity I will
-- use the one shown above
CREATE OR REPLACE PACKAGE json_schema AS

  v_json_data VARCHAR2(100) := 
   '[{"o:path":"$.KeyOne","type":"string","o:length":8}]';

END;
/

-- And now the trigger that fires every time a record
-- is inserted
CREATE OR REPLACE TRIGGER json_schema
BEFORE INSERT OR UPDATE ON json_data
FOR EACH ROW
DECLARE
  v_dg CLOB;
BEGIN
  -- get the Data Guide of the new row
  SELECT JSON_DATAGUIDE(:NEW.json_col)
    INTO v_dg
    FROM DUAL;
  -- if the Data Guide is not a match then fail
  IF v_dg <> json_schema.v_json_data THEN
    RAISE_APPLICATION_ERROR(-20000,
                     'Incorrect JSON Format');
  END IF;
END;
/

-- Insert a record with the correct Data Guide
INSERT INTO json_data
VALUES(2,'{"KeyOne":"ValueOne"}');

-- Insert a record with an incorrect Data Guide
-- this will fail
INSERT INTO json_data
VALUES(3,'{"NotKeyOne":"NotValueOne"}');
INSERT INTO json_data
*
ERROR at line 1:
ORA-20000: Incorrect JSON Format
ORA-06512: at "D.JSON_SCHEMA", line 8
ORA-04088: error during execution of trigger
           'D.JSON_SCHEMA'

This is a simple way to validate JSON data before it is allowed into the database.

Thanks for reading!

Oracle Prequel

April 1, 2018

Oracle Spokesperson April Fulz released the following brief statement today:

The Oracle database has undergone many changes to it’s release numbering scheme over the years that caused an undue amount of confusion. To alleviate the confusion we have decided to go right back to the beginning with Release 1.0.

You may know that we skipped release 1.0 in the early days for fear that nobody would trust the 1.0 release of any software. This was just the first rule of our release numbering scheme. Other highlights include:

  • Using incremental whole numbers for each release to denote forward movement and added features
  • Offset the intimidation of whole number increments by including decimal points
  • Append decrimental lowercase letters (i, g, c) to further offset the perceived complexity (thankfully industry trends such as Internet [i] and Cloud [c] arose to make the chosen letters more relevant)
  • The years between 13 and 17 were awkward for a lot of us so we planned to skip those release numbers and go directly to 18 (thankfully the year 2018 arrived just in time for this)
  • Just like the great movie franchises we always planned a prequel release of our flagship Database that we are now proudly branding Oracle RDBMS 1.0

More information will be made available closer to the final release of…the first release.