diff --git a/.gitignore b/.gitignore index 8e194bc..379cab7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ jsquery_gram.c jsquery_scan.c jsquery_gram.h +monq_gram.c +monq_gram.h +monq_scan.c regression.diffs regression.out results diff --git a/META.json b/META.json index 35ba35f..1eddfa1 100644 --- a/META.json +++ b/META.json @@ -1,8 +1,8 @@ -{ +{ "name": "JsQuery", "abstract": "JSON Query Language with GIN indexing support", "description": "JsQuery provides additional functionality for JSONB, such as a simple and effective way to search in nested objects and arrays, and more comparison operators with index support. It does this by implementing a specialized search syntax, the @@ operator, and the jsquery type for search strings.", - "version": "1.0.0", + "version": "1.0.1", "maintainer": [ "Teodor Sigaev ", "Alexander Korotkov ", @@ -42,7 +42,7 @@ }, "generated_by": "Josh Berkus", "meta-spec": { - "version": "1.0.0", + "version": "1.0.1", "url": "http://pgxn.org/meta/spec.txt" }, "tags": [ diff --git a/Makefile b/Makefile index 0892a24..63dc120 100644 --- a/Makefile +++ b/Makefile @@ -2,40 +2,39 @@ MODULE_big = jsquery OBJS = jsonb_gin_ops.o jsquery_constr.o jsquery_extract.o \ - jsquery_gram.o jsquery_io.o jsquery_op.o jsquery_support.o + jsquery_gram.o jsquery_io.o jsquery_op.o jsquery_support.o \ + monq_gram.o monq_scan.o monq_get_jsquery.o monq_create_query.o monq_delete_query.o EXTENSION = jsquery -DATA = jsquery--1.0.sql -INCLUDES = jsquery.h +DATA = jsquery--1.1.sql jsquery--1.0--1.1.sql +INCLUDES = jsquery.h monq.h REGRESS = jsquery # We need a UTF8 database ENCODING = UTF8 EXTRA_CLEAN = y.tab.c y.tab.h \ - jsquery_gram.c jsquery_scan.c jsquery_gram.h + jsquery_gram.c jsquery_scan.c jsquery_gram.h \ + monq_gram.c monq_scan.c monq_gram.h -ifdef USE_PGXS PG_CONFIG ?= pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) -else -subdir = contrib/jsquery -top_builddir = ../.. -include $(top_builddir)/src/Makefile.global -include $(top_srcdir)/contrib/contrib-global.mk -endif jsquery_gram.o: jsquery_scan.c jsquery_gram.c: BISONFLAGS += -d -distprep: jsquery_gram.c jsquery_scan.c +monq_gram.o: monq_scan.c + +monq_gram.c: BISONFLAGS += -d + +distprep: jsquery_gram.c jsquery_scan.c monq_gram.c monq_scan.c maintainer-clean: - rm -f jsquery_gram.c jsquery_scan.c jsquery_gram.h + rm -f jsquery_gram.c jsquery_scan.c jsquery_gram.h monq_gram.c monq_scan.c monq_gram.h install: installincludes installincludes: - $(INSTALL_DATA) $(addprefix $(srcdir)/, $(INCLUDES)) '$(DESTDIR)$(includedir_server)/' + $(INSTALL_DATA) $(addprefix $(srcdir)/, $(INCLUDES)) '$(DESTDIR)$(includedir_server)/' \ No newline at end of file diff --git a/README.md b/README.md index 4a019b4..b8efd67 100644 --- a/README.md +++ b/README.md @@ -341,6 +341,78 @@ correspondingly. ---------------------------- y > 0 , entry 0 + +### parse\_mquery +This function is transofrm MongoDB query to JsQuery query. Function get string +like argument and return jsquery object. +Example: +MongoDB query: +``` +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $lte : 1 } }'); +``` +Transformed to: +``` +select '{"a": {"b": 1 } }'::jsonb @@ 'a.b = 1'; +``` +And return: +``` +select '{"a": {"b": 1 } }'::jsonb @@ 'a.b = 1'; + ?column? +---------- + t +(1 row) +``` + +#### MongoDB operators supported by function + +Number of MongoDB query operators is limited by opportunities of +JsQuery language, but main part is supported. + +##### Comparison operators: +* `$eq` - supported; +* `$ne` - supported; +* `$lt` - supported; +* `$lte` - supported; +* `$gt` - supported; +* `$gte` - supported; +* `$in` - supported; +* `$nin` - supported. + +##### Logical operators: +* `$and` - supported; +* `$or` - supported; +* `$not` - supported; +* `$nor` - supported. + +##### Element operators: +* `$exists` - supported; +* `$type` - supported. + +##### Evaluation operators: +* `$mod` - not supported; +* `$regex` - not supported; +* `$text` - supported; +* `$where` - not supported. + +##### Bitwise operators: +* All operators are not supported. + +##### Array operators: +* `$all` - supported; +* `$elemMatch` - supported; +* `$size` - supported. + +##### Comment operators: +* All operators are not supported. + +##### Geospatial operators: +* All operators are not supported. + +##### Projextion operators: +* All operators are not supported. + +Examples of queries with all this operators you can find in the file +[sql/jsquery.sql](https://github.com/postgrespro/jsquery/blob/master/sql/jsquery.sql) + Contribution ------------ diff --git a/expected/jsquery.out b/expected/jsquery.out index 14b2730..dda85bb 100644 --- a/expected/jsquery.out +++ b/expected/jsquery.out @@ -2913,4 +2913,393 @@ select v from test_jsquery where v @@ 'array = [2,3]' order by v; {"array": [2, 3]} (1 row) +---MongoDB query translator tests +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : 1 }'); + ?column? +---------- + t +(1 row) + +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $eq : 1 } }'); + ?column? +---------- + t +(1 row) + +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $ne : 1 } }'); + ?column? +---------- + f +(1 row) + +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $lt : 1 } }'); + ?column? +---------- + f +(1 row) + +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $lte : 1 } }'); + ?column? +---------- + t +(1 row) + +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $gt : 1 } }'); + ?column? +---------- + f +(1 row) + +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $gte : 1 } }'); + ?column? +---------- + t +(1 row) + +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $in : [2,3] } }'); + ?column? +---------- + f +(1 row) + +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $nin : [2,3] } }'); + ?column? +---------- + t +(1 row) + +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ a : { $exists : false } }'); + ?column? +---------- + f +(1 row) + +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ a : { $exists : true } }'); + ?column? +---------- + t +(1 row) + +select parse_mquery('{ is : { $lt: 1 } }')::jsquery; + parse_mquery +-------------- + "is" < 1 +(1 row) + +select v from test_jsquery where v @@ parse_mquery('{ array : { $all: [2,3] } }') order by v; + v +---------------------- + {"array": [2, 3]} + {"array": [1, 2, 3]} + {"array": [2, 3, 4]} +(3 rows) + +select v from test_jsquery where v @@ parse_mquery('{ { $text: { $search: "Flew" } } }'); + v +--- +(0 rows) + +select '{ "a" : "ssl" }'::jsonb @@ parse_mquery('{ a: { $in: [ "ssl","security"] } }'); + ?column? +---------- + t +(1 row) + +select '{ "a" : 1 }'::jsonb @@ parse_mquery('{ a: { $in: [ "ssl","security"] } }'); + ?column? +---------- + f +(1 row) + +select '{ "a" : "ssl" }'::jsonb @@ parse_mquery('{ a: { $nin: [ "ssl","security"] } }'); + ?column? +---------- + f +(1 row) + +select '{ "a" : "sslqwerty" }'::jsonb @@ parse_mquery('{ a: { $nin: [ "ssl","security"] } }'); + ?column? +---------- + t +(1 row) + +select '{ "a" : [ "ssl","security"] }'::jsonb @@ parse_mquery('{ a: { $size: 2 } }'); + ?column? +---------- + t +(1 row) + +select '{ "a" : [ "ssl","security"] }'::jsonb @@ parse_mquery('{ a: { $size: 1 } }'); + ?column? +---------- + f +(1 row) + +select '{ "a" : [ "ssl","security", "pattern"] }'::jsonb @@ parse_mquery('{ a: { $all: [ "ssl","security"] } }'); + ?column? +---------- + t +(1 row) + +select '{ "a" : [ "ssl","pattern"] }'::jsonb @@ parse_mquery('{ a: { $all: [ "ssl","security"] } }'); + ?column? +---------- + f +(1 row) + +select '{ "a" : [ "ssl","security"] }'::jsonb @@ parse_mquery('{ a: { $all: [ "ssl","security"] } }'); + ?column? +---------- + t +(1 row) + +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ a : { $exists : false } }'); + ?column? +---------- + f +(1 row) + +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ a : { $exists : true } }'); + ?column? +---------- + t +(1 row) + +select '{ "b" : 2 }'::jsonb @@ parse_mquery('{ a : { $exists : false } }'); + ?column? +---------- + t +(1 row) + +select '{ "b" : 2 }'::jsonb @@ parse_mquery('{ a : { $exists : true } }'); + ?column? +---------- + f +(1 row) + +select '{ "b" : 2 }'::jsonb @@ parse_mquery('{ b: { $type: "int" } }'); + ?column? +---------- + t +(1 row) + +select '{ "b" : "qwerttyu" }'::jsonb @@ parse_mquery('{ b: { $type: "int" } }'); + ?column? +---------- + f +(1 row) + +select '{ "b" : 2 }'::jsonb @@ parse_mquery('{ b: { $type: "long" } }'); + ?column? +---------- + t +(1 row) + +select '{ "b" : "qwerttyu" }'::jsonb @@ parse_mquery('{ b: { $type: "long" } }'); + ?column? +---------- + f +(1 row) + +select '{ "b" : true }'::jsonb @@ parse_mquery('{ b: { $type: "bool" } }'); + ?column? +---------- + t +(1 row) + +select '{ "b" : "fklgjlksdfgsldflsgjslkrjekfjkl" }'::jsonb @@ parse_mquery('{ b: { $type: "bool" } }'); + ?column? +---------- + f +(1 row) + +select '{ "b" : "fklgjlksdfgsldflsgjslkrjekfjkl" }'::jsonb @@ parse_mquery('{ b: { $type: "array" } }'); + ?column? +---------- + f +(1 row) + +select '{ "b" : [1, 4] }'::jsonb @@ parse_mquery('{ b: { $type: "array" } }'); + ?column? +---------- + t +(1 row) + +select '{ "b" : "fklgjlksdfgsldflsgjslkrjekfjkl" }'::jsonb @@ parse_mquery('{ b: { $type: "string" } }'); + ?column? +---------- + t +(1 row) + +select '{ "b" : [1, 4] }'::jsonb @@ parse_mquery('{ b: { $type: "string" } }'); + ?column? +---------- + f +(1 row) + +select '{ "b" : 2.23432 }'::jsonb @@ parse_mquery('{ b: { $type: "double" } }'); + ?column? +---------- + t +(1 row) + +select '{ "b" : 2 }'::jsonb @@ parse_mquery('{ b: { $type: "double" } }'); + ?column? +---------- + t +(1 row) + +select '{ "b" : 2 }'::jsonb @@ parse_mquery('{ b: { $type: "decimal" } }'); + ?column? +---------- + t +(1 row) + +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "maxKey" } }'); +ERROR: Jsquery is not supported MongoDB "maxKey" value type +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "binData" } }'); +ERROR: Jsquery is not supported MongoDB "binData" value type +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "objectId" } }'); +ERROR: Jsquery is not supported MongoDB "objectId" value type +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "javascript" } }'); +ERROR: Jsquery is not supported MongoDB "javascript" value type +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "symbol" } }'); +ERROR: Jsquery is not supported MongoDB "symbol" value type +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "javascriptWithScope" } }'); +ERROR: Jsquery is not supported MongoDB "javascriptWithScope" value type +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "timestamp" } }'); +ERROR: Jsquery is not supported MongoDB "timestamp" value type +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "minKey" } }'); +ERROR: Jsquery is not supported MongoDB "minKey" value type +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "regex" } }'); +ERROR: Jsquery is not supported MongoDB "regex" value type +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "null" } }'); +ERROR: Jsquery is not supported MongoDB "null" value type +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "date" } }'); +ERROR: Jsquery is not supported MongoDB "date" value type +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "undefined" } }'); +ERROR: Jsquery is not supported MongoDB "undefined" value type +/* Or operator */ +select '{ "quantity" : 2, "price" : 10 }'::jsonb @@ parse_mquery('{ $or: [ { quantity: { $lt: 20 } }, { price: 10 } ] }'); + ?column? +---------- + t +(1 row) + +select '{ "quantity" : 200, "price" : 10 }'::jsonb @@ parse_mquery('{ $or: [ { quantity: { $lt: 20 } }, { price: 10 } ] }'); + ?column? +---------- + t +(1 row) + +select '{ "quantity" : 200, "price" : 10 }'::jsonb @@ parse_mquery('{ $or: [ { quantity: { $lt: 20 } }, { price: 100 } ] }'); + ?column? +---------- + f +(1 row) + +/* Nor operator */ +select '{ "quantity" : 2, "price" : 10 }'::jsonb @@ parse_mquery('{ $nor: [ { quantity: { $lt: 20 } }, { price: 10 } ] }'); + ?column? +---------- + f +(1 row) + +select '{ "quantity" : 200, "price" : 10 }'::jsonb @@ parse_mquery('{ $nor: [ { quantity: { $lt: 20 } }, { price: 10 } ] }'); + ?column? +---------- + t +(1 row) + +select '{ "quantity" : 200, "price" : 10 }'::jsonb @@ parse_mquery('{ $nor: [ { quantity: { $lt: 20 } }, { price: 100 } ] }'); + ?column? +---------- + t +(1 row) + +/* And operator */ +select '{ "quantity" : 200, "price" : 10 }'::jsonb @@ parse_mquery('{ $and: [ { quantity: { $lt: 20 } }, { price: 100 } ] }'); + ?column? +---------- + f +(1 row) + +select '{ "quantity" : 5, "price" : 100 }'::jsonb @@ parse_mquery('{ $and: [ { quantity: { $lt: 20 } }, { price: 100 } ] }'); + ?column? +---------- + t +(1 row) + +/* Not operator */ +select '{ "quantity" : 5, "price" : 100 }'::jsonb @@ parse_mquery('{ price: { $not: { $gt: 1.99 } } }'); + ?column? +---------- + f +(1 row) + +select '{ "quantity" : 5, "price" : 1 }'::jsonb @@ parse_mquery('{ price: { $not: { $gt: 1.99 } } }'); + ?column? +---------- + t +(1 row) + +/* Mod operator */ +select '{ "quantity" : 2, "price" : 10 }'::jsonb @@ parse_mquery('{ qty: { $mod: [ 4, 0 ] } } '); +ERROR: MongoDB module operator is not supported by jsquery +select '{"a": 5}'::jsonb @@ parse_mquery('{ a: { $eq: 5 } }'); + ?column? +---------- + t +(1 row) + +select '{"a": 5}'::jsonb @@ parse_mquery('{ a: { $eq: 6 } }'); + ?column? +---------- + f +(1 row) + +select '{ "quantity" : "qw", "price" : 10 }'::jsonb @@ parse_mquery('{ { $where: "qw"} }'); +ERROR: MongoDB where clause is not supported by jsquery +select '{ "quantity" : "qw", "price" : 10 }'::jsonb @@ parse_mquery('{ { $text: { $search: "qsddjkhjw" } } }'); + ?column? +---------- + f +(1 row) + +select '{ "quantity" : "qw", "price" : 10 }'::jsonb @@ parse_mquery('{ { $text: { $search: "qw" } } }'); + ?column? +---------- + t +(1 row) + +select '{"a": { "qwerty" : 5} }'::jsonb @@ parse_mquery('{ "a.qwerty" : { $eq: 6 } }'); + ?column? +---------- + f +(1 row) + +select '{"a": { "qwerty" : { "asdfgh" : { "fgfhg" : 5 } } } }'::jsonb @@ parse_mquery('{ "a.qwerty.asdfgh.fgfhg" : { $eq: 5 } }'); + ?column? +---------- + t +(1 row) + +select '{ "_id" : 3, "results" : [ { "product" : "abc", "score" : 7 }, { "product" : "abc", "score" : 8 } ] }' @@ parse_mquery('{ results: { $elemMatch: { product: "abc" } } }'); + ?column? +---------- + t +(1 row) + +select '{ "_id" : 3, "results" : [ 81, 84, 83] }' @@ parse_mquery('{ results: { $elemMatch: { $gte: 80, $lt: 85 } } }'); + ?column? +---------- + t +(1 row) + +select '{ "_id" : 3, "results" : [ 81, 86, 83] }' @@ parse_mquery('{ results: { $elemMatch: { $gte: 80, $lt: 85 } } }'); + ?column? +---------- + f +(1 row) + RESET enable_seqscan; diff --git a/jsquery--1.0--1.1.sql b/jsquery--1.0--1.1.sql new file mode 100644 index 0000000..f0d60b7 --- /dev/null +++ b/jsquery--1.0--1.1.sql @@ -0,0 +1,7 @@ +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION jsquery UPDATE TO '1.1'" to load this file. \quit + +CREATE OR REPLACE FUNCTION parse_mquery(cstring) + RETURNS jsquery + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; \ No newline at end of file diff --git a/jsquery--1.0.sql b/jsquery--1.1.sql similarity index 98% rename from jsquery--1.0.sql rename to jsquery--1.1.sql index 3bf1d9b..0141bb4 100644 --- a/jsquery--1.0.sql +++ b/jsquery--1.1.sql @@ -291,3 +291,8 @@ CREATE OR REPLACE FUNCTION gin_debug_query_path_value(jsquery) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; + +CREATE OR REPLACE FUNCTION parse_mquery(cstring) + RETURNS jsquery + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; diff --git a/jsquery.control b/jsquery.control index 94510c8..d95ba24 100644 --- a/jsquery.control +++ b/jsquery.control @@ -1,6 +1,6 @@ # jsquery extension comment = 'data type for jsonb inspection' -default_version = '1.0' +default_version = '1.1' module_pathname = '$libdir/jsquery' relocatable = true diff --git a/jsquery_io.c b/jsquery_io.c index 8a43e8e..734c8ca 100644 --- a/jsquery_io.c +++ b/jsquery_io.c @@ -19,6 +19,9 @@ #include "utils/json.h" #include "jsquery.h" +#include "monq.h" + +extern MQuery *parsemquery(char *str); PG_MODULE_MAGIC; @@ -168,6 +171,35 @@ jsquery_in(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } +PG_FUNCTION_INFO_V1(parse_mquery); +Datum +parse_mquery(PG_FUNCTION_ARGS) +{ + char *result = getJsquery(parse((char *) PG_GETARG_CSTRING(0))); + int32 len = strlen(result); + JsQueryParseItem *jsquery = parsejsquery(result, len); + JsQuery *res; + StringInfoData buf; + + initStringInfo(&buf); + enlargeStringInfo(&buf, 4 * len /* estimation */); + + appendStringInfoSpaces(&buf, VARHDRSZ); + + if (jsquery != NULL) + { + flattenJsQueryParseItem(&buf, jsquery, false); + + res = (JsQuery*)buf.data; + SET_VARSIZE(res, buf.len); + + PG_RETURN_JSQUERY(res); + } + + PG_RETURN_NULL(); +} + + static void printHint(StringInfo buf, JsQueryHint hint) { diff --git a/monq.h b/monq.h new file mode 100644 index 0000000..8dd7796 --- /dev/null +++ b/monq.h @@ -0,0 +1,264 @@ +#ifndef _MONQ_H_ +#define _MONQ_H_ + +#include "postgres.h" +#include "fmgr.h" +#include "miscadmin.h" +#include "nodes/pg_list.h" +#include "utils/builtins.h" +#include "utils/jsonb.h" + +typedef enum TypeOfLeafValue +{ + S, + I, + A, + D, + B +} TypeOfLeafValue; + +typedef enum TypeOfOperator +{ + NOP, + MOP, + AOP, + VOP, + EOP +} TypeOfOperator; + +typedef enum ArrayOperatorType +{ + _IN, + _NIN, + _ALL +} ArrayOperatorType; + +typedef enum ExpressionOperatorType +{ + _OR, + _NOR, + _AND +} ExpressionOperatorType; + +typedef enum ValueOperatorType +{ + _LESS, + _EQ, + _NOTEQ, + _LESSEQ, + _GREAT, + _GREATEQ, + _TYPE, + _SIZE, + _EXISTS +} ValueOperatorType; + + +typedef enum TypeOfClause +{ + LEAF, + COMMENT, + TEXT, + WHERE, + EXPRESSION +} TypeOfClause; + +typedef enum TypeOfValue +{ + LF_VALUE, + OP_OBJECT +} TypeOfValue; + +typedef enum TypeOfElemMatchValue +{ + E_EXPRESSION, + E_OP_OBJECT +} TypeOfElemMatchValue; + +typedef enum TypeOfWhereClauseValue +{ + STR +} TypeOfWhereClauseValue; + +typedef struct MArray +{ + List *arrayList; +} MArray; + +typedef struct LeafValue +{ + TypeOfLeafValue type; + union + { + char *str; + char *i; + MArray *ar; + bool b; + char *d; + }; +} LeafValue; + +/* Operators */ +typedef struct ValueOperator +{ + TypeOfOperator type; + + ValueOperatorType value_op; + LeafValue *value; +} ValueOperator; + +typedef struct NotOperator +{ + TypeOfOperator type; + + struct Operator *op; +} NotOperator; + +typedef struct ArrayOperator +{ + TypeOfOperator type; + + ArrayOperatorType array_op; + MArray *ar; +} ArrayOperator; + +typedef struct ModOperator +{ + TypeOfOperator type; + + LeafValue *divisor; + LeafValue *remainder; +} ModOperator; + +typedef struct Operator +{ + TypeOfOperator type; +} Operator; + +typedef struct OperatorObject +{ + List *operatorList; +} OperatorObject; + +typedef struct MValue +{ + TypeOfValue type; + union + { + LeafValue *lv; + OperatorObject *oob; + }; +} MValue; + +typedef struct LeafClause +{ + TypeOfClause type; + + char *key; + MValue *vl; +} LeafClause; + +typedef struct CommentClause +{ + TypeOfClause type; + + char *op; + char *str; +} CommentClause; + +typedef struct WhereClauseValue +{ + TypeOfClause type; + + TypeOfWhereClauseValue val_type; + char *str; +} WhereClauseValue; + +typedef struct WhereClause +{ + TypeOfClause type; + + WhereClauseValue *wcv; +} WhereClause; + +typedef struct TextClause +{ + TypeOfClause type; + + char *search_str; + bool lang_op; + char *lang_str; + bool case_sense; + bool diacr_sense; +} TextClause; + +typedef struct Clause +{ + TypeOfClause type; +} Clause; + +typedef struct Expression +{ + List *clauseList; +} Expression; + +typedef struct ElemMatchOperator +{ + TypeOfOperator type; + + TypeOfElemMatchValue typeOfValue; + + union + { + Expression *expression; + OperatorObject *operatorOpbject; + }; +} ElemMatchOperator; + +typedef struct ExpressionClause +{ + TypeOfClause type; + + ExpressionOperatorType op; + List *expressionList; +} ExpressionClause; + +typedef struct MQuery +{ + Expression *exp; + Datum jsQuery; +} MQuery; + + +extern MArray *createNewArray(List *arrayList); +extern List *addArrayElement(LeafValue * value, List *arrayList); +extern Operator *createNotOperator(Operator *op); +extern Operator *createModOperator(LeafValue *divisor, LeafValue *remainder); +extern Operator *createArrayOperator(ArrayOperatorType op, MArray *ar); +extern Operator *createValueOperator(ValueOperatorType op, LeafValue *value); +extern Operator *createElemMatchOperatorExpression(Expression *expression); +extern Operator *createElemMatchOperatorOpObject(OperatorObject *oob); +extern LeafValue *createStringValue(char *str); +extern LeafValue *createDoubleValue(char* d); +extern LeafValue *createIntegerValue(char* i); +extern LeafValue *createArrayValue(MArray *ar); +extern LeafValue *createBooleanValue(bool b); +extern List *addOperator(Operator *op, List *operatorList); +extern OperatorObject *createOperatorObject(List *operatorList); +extern MValue *createOperatorObjectValue(OperatorObject *oob); +extern MValue *createLeafValueValue(LeafValue *lv); +extern Clause *createLeafClause(char* key, MValue *vl); +extern Clause *createCommentClause(char *op, char *str); +extern WhereClauseValue *stringToWhereClauseValue(char *str); +extern Clause *createWhereClause(WhereClauseValue *wcv); +extern List *addClause(Clause *clause, List *clauseList); +extern Expression *createExpression(List *clauseList); +extern List *addExpression(Expression *exp, List *expressionList); +extern Clause *createExpressionTreeClause(ExpressionOperatorType op, List *expressionList); +extern Clause *createTextClause(char* search_str, bool lang_op, char* lang_str, bool case_sense, bool diacr_sense); +extern MQuery *createQuery(Expression *exp); +extern void deleteMquery(MQuery *qu); +extern char *getJsquery(MQuery *query); +extern MQuery *parse(char *str); + +#endif \ No newline at end of file diff --git a/monq_create_query.c b/monq_create_query.c new file mode 100644 index 0000000..d0a72ee --- /dev/null +++ b/monq_create_query.c @@ -0,0 +1,244 @@ +#include +#include +#include + +#include "monq.h" + + +MArray * +createNewArray(List *arrayList) +{ + MArray *new_ar = (MArray *) palloc(sizeof(MArray)); + new_ar->arrayList = arrayList; + return new_ar; +} + +List * +addArrayElement(LeafValue *value, List *arrayList) +{ + return lappend(arrayList, value); +} + +Operator * +createNotOperator(Operator *op) +{ + NotOperator *new_op = (NotOperator *) palloc(sizeof(NotOperator)); + new_op->type = NOP; + new_op->op = op; + return (Operator *) new_op; +} + +Operator * +createModOperator(LeafValue *divisor, LeafValue *remainder) +{ + ModOperator *new_op = (ModOperator *) palloc(sizeof(ModOperator)); + new_op->type = MOP; + new_op->divisor = divisor; + new_op->remainder = remainder; + return (Operator *) new_op; +} + +Operator * +createArrayOperator(ArrayOperatorType op, MArray *ar) +{ + ArrayOperator *new_op = (ArrayOperator *) palloc(sizeof(ArrayOperator)); + new_op->type = AOP; + new_op->array_op = op; + new_op->ar = ar; + return (Operator*) new_op; +} + +Operator * +createValueOperator(ValueOperatorType op, LeafValue * value) +{ + ValueOperator *new_op = (ValueOperator *) palloc(sizeof(ValueOperator)); + new_op->type = VOP; + new_op->value_op = op; + new_op->value = value; + return (Operator *) new_op; +} + +Operator * +createElemMatchOperatorOpObject(OperatorObject *oob) +{ + ElemMatchOperator *new_op = (ElemMatchOperator *) palloc(sizeof(ElemMatchOperator)); + new_op->type = EOP; + new_op->typeOfValue = E_OP_OBJECT; + new_op->operatorOpbject = oob; + return (Operator *) new_op; +} + +Operator * +createElemMatchOperatorExpression(Expression *expression) +{ + ElemMatchOperator *new_op = (ElemMatchOperator *) palloc(sizeof(ElemMatchOperator)); + new_op->type = EOP; + new_op->typeOfValue = E_EXPRESSION; + new_op->expression = expression; + return (Operator *) new_op; +} + +LeafValue * +createStringValue(char *str) +{ + LeafValue *lv = (LeafValue *) palloc(sizeof(LeafValue)); + lv->type = S; + lv->str = str; + return lv; +} + +LeafValue * +createDoubleValue(char* d) +{ + LeafValue *lv = (LeafValue *) palloc(sizeof(LeafValue)); + lv->type = D; + lv->d = d; + return lv; +} + +LeafValue * +createIntegerValue(char* i) +{ + LeafValue *lv = (LeafValue *) palloc(sizeof(LeafValue)); + lv->type = I; + lv->i = i; + return lv; +} + +LeafValue * +createArrayValue(MArray *ar) +{ + LeafValue *lv = (LeafValue *) palloc(sizeof(LeafValue)); + lv->type = A; + lv->ar = ar; + return lv; +} + +LeafValue * +createBooleanValue(bool b) +{ + LeafValue *lv = (LeafValue *) palloc(sizeof(LeafValue)); + lv->type = B; + lv->b = b; + return lv; +} + +List * +addOperator(Operator *op, List *operatorList) +{ + return lcons(op, operatorList); +} + +OperatorObject * +createOperatorObject(List *operatorList) +{ + OperatorObject *new_oob = (OperatorObject *) palloc(sizeof(OperatorObject)); + new_oob->operatorList = operatorList; + return new_oob; +} + +MValue * +createOperatorObjectValue(OperatorObject *oob) +{ + MValue *vl = (MValue *) palloc(sizeof(MValue)); + vl->type = OP_OBJECT; + vl->oob = oob; + return vl; +} + +MValue * +createLeafValueValue(LeafValue *lv) +{ + MValue *vl = (MValue *) palloc(sizeof(MValue)); + vl->type = LF_VALUE; + vl->lv = lv; + return vl; +} + +Clause * +createLeafClause(char* key, MValue *vl) +{ + LeafClause *new_lc = (LeafClause *) palloc(sizeof(LeafClause)); + new_lc->type = LEAF; + new_lc->key = key; + new_lc->vl = vl; + return ( Clause* ) new_lc; +} + +Clause * +createCommentClause(char *op, char *str) +{ + CommentClause *new_com_cl = (CommentClause *) palloc(sizeof(CommentClause)); + new_com_cl->type = COMMENT; + new_com_cl->op = op; + new_com_cl->str = str; + return ( Clause* ) new_com_cl; +} + +WhereClauseValue * +stringToWhereClauseValue(char *str) +{ + WhereClauseValue *wcv = (WhereClauseValue *) palloc(sizeof(WhereClauseValue)); + wcv->str = str; + return wcv; +} + +Clause * +createWhereClause(WhereClauseValue *wcv) +{ + WhereClause *wc = (WhereClause *) palloc(sizeof(WhereClause)); + wc->type = WHERE; + wc->wcv = wcv; + return (Clause *) wc; +} + +List * +addClause(Clause *clause, List *clauseList) +{ + return lappend(clauseList, clause); +} + +Expression * +createExpression(List *clauseList) +{ + Expression *exp = (Expression *) palloc(sizeof(Expression)); + exp->clauseList = clauseList; + return exp; +} + +List * +addExpression(Expression *exp, List *expressionList) +{ + return lcons(exp, expressionList); +} + +Clause * +createExpressionTreeClause(ExpressionOperatorType opType, List *expressionList) +{ + ExpressionClause *exp_cl = (ExpressionClause *) palloc(sizeof(ExpressionClause)); + exp_cl->type = EXPRESSION; + exp_cl->op = opType; + exp_cl->expressionList = expressionList; + return (Clause *) exp_cl; +} + +Clause * +createTextClause(char *search_str, bool lang_op, char *lang_str, bool case_sense, bool diacr_sense) +{ + TextClause *text_cl = (TextClause *) palloc(sizeof(TextClause)); + text_cl->type = TEXT; + text_cl->search_str = search_str; + text_cl->lang_op = lang_op; + text_cl->lang_str = lang_str; + text_cl->case_sense = case_sense; + text_cl->diacr_sense = diacr_sense; + return (Clause *) text_cl; +} + +MQuery * +createQuery(Expression *exp) +{ + MQuery *qu = (MQuery *) palloc(sizeof(MQuery)); + qu->exp = exp; + return qu; +} \ No newline at end of file diff --git a/monq_delete_query.c b/monq_delete_query.c new file mode 100644 index 0000000..05f1d75 --- /dev/null +++ b/monq_delete_query.c @@ -0,0 +1,192 @@ +#include +#include +#include + +#include "c.h" +#include "postgres.h" +#include "access/gin.h" +#include "utils/numeric.h" + +#include "monq.h" + + +static void deleteValueOperator(ValueOperator *vop); +static void deleteOperator(Operator *op); +static void deleteNotOperator(NotOperator *op); +static void deleteElemMatchOperator(ElemMatchOperator *elemMatchOperator); +static void deleteOperatorObject(OperatorObject *op_object); +static void deleteLeafClauseValue(MValue *val); +static void deleteLeafClause(LeafClause *lc); +static void deleteExpressionClause(ExpressionClause* expClause); +static void deleteTextClause(TextClause *tClause); +static void deleteClause(Clause *cl); +static void deleteExpression(Expression * ex); +static void deleteLeafValue(LeafValue *value); +static void deleteArraySequence(MArray *ar); +static void deleteArrayOperator(ArrayOperator *aop); + + +static void +deleteValueOperator(ValueOperator *vop) +{ + deleteLeafValue(vop->value); + pfree(vop); +} + +static void +deleteOperator(Operator *operator) +{ + switch(operator->type) + { + case NOP : + deleteNotOperator((NotOperator*) operator ); + break; + case AOP : + deleteArrayOperator((ArrayOperator*) operator ); + break; + case VOP : + deleteValueOperator((ValueOperator*) operator ); + break; + case EOP : + deleteElemMatchOperator((ElemMatchOperator*) operator); + break; + case MOP : + break; + } +} + +static void +deleteElemMatchOperator(ElemMatchOperator *elemMatchOperator) +{ + switch(elemMatchOperator->typeOfValue) + { + case E_EXPRESSION: + deleteExpression(elemMatchOperator->expression); + break; + case E_OP_OBJECT: + deleteOperatorObject(elemMatchOperator->operatorOpbject); + break; + } +} + +static void +deleteNotOperator(NotOperator *op) +{ + deleteOperator(op->op); + pfree(op); +} + +static void +deleteOperatorObject(OperatorObject *op_object) +{ + List *operatorList; + ListCell *cell; + + operatorList = op_object->operatorList; + + foreach(cell, operatorList) + deleteOperator((Operator *)lfirst(cell)); + + pfree(op_object->operatorList); + pfree(op_object); +} + +static void +deleteLeafClauseValue(MValue *value) +{ + value->type ? deleteOperatorObject(value->oob) : deleteLeafValue(value->lv); +} + +static void +deleteLeafClause(LeafClause *lc) +{ + deleteLeafClauseValue(lc->vl); + pfree(lc); +} + +static void +deleteExpressionClause(ExpressionClause* expClause) +{ + List *expressionList; + ListCell *cell; + + expressionList = expClause->expressionList; + + foreach(cell, expressionList) + deleteExpression((Expression *)lfirst(cell)); + + pfree(expressionList); + pfree(expClause); +} + +static void +deleteTextClause(TextClause *textClause) +{ + pfree(textClause); +} + +static void +deleteClause(Clause *clause) +{ + switch(clause->type) + { + case LEAF : + deleteLeafClause((LeafClause*) clause); + break; + case TEXT : + deleteTextClause((TextClause*) clause); + break; + case EXPRESSION : + deleteExpressionClause((ExpressionClause*) clause); + break; + default : + break; + } +} + +static void +deleteExpression(Expression * expression) +{ + List *clauseList = expression->clauseList; + ListCell *cell; + + foreach(cell, clauseList) + deleteClause((Clause *)lfirst(cell)); + + pfree(expression); +} + +static void +deleteLeafValue(LeafValue *value) +{ + if(value->type == A) + deleteArraySequence(value->ar); + pfree(value); +} + +static void +deleteArraySequence(MArray *marray) +{ + List *arrayList = marray->arrayList; + ListCell *cell; + + foreach(cell, arrayList) + deleteLeafValue((LeafValue *)lfirst(cell)); + + pfree(arrayList); + pfree(marray); +} + +static void +deleteArrayOperator(ArrayOperator *arrayOperator) +{ + deleteArraySequence(arrayOperator->ar); + pfree(arrayOperator); +} + +void +deleteMquery(MQuery *query) +{ + deleteExpression(query->exp); + pfree(query); +} \ No newline at end of file diff --git a/monq_get_jsquery.c b/monq_get_jsquery.c new file mode 100644 index 0000000..ec40ee5 --- /dev/null +++ b/monq_get_jsquery.c @@ -0,0 +1,388 @@ +#include +#include +#include + +#include "monq.h" + + +static void getExpression(StringInfo strInfo, Expression * expression); +static void getClause(StringInfo strInfo, Clause *clause); +static void getExpressionClause(StringInfo strInfo, ExpressionClause* expClause); +static void getTextClause(StringInfo strInfo, TextClause* textClause); +static void getLeafClause(StringInfo strInfo, LeafClause *leafClause); +static void getLeafClauseValue(StringInfo strInfo, char *key, MValue *value); +static void getLeafValueEq(StringInfo strInfo, char *key, LeafValue *leafValue); +static void getOperatorObject(StringInfo strInfo, char *key, OperatorObject *opObject); +static void getOperator(StringInfo strInfo, char *key, Operator *operator); +static void getNotOperator(StringInfo strInfo, char *key, NotOperator *notOperator); +static void getElemMatchOperator(StringInfo strInfo, char *key, ElemMatchOperator *elemMatchOperator); +static void getArrayOperator(StringInfo strInfo, char *key, ArrayOperator *arOperator); +static void getArraySequence(StringInfo strInfo, MArray *marray); +static void getLeafValue(StringInfo strInfo, LeafValue *value); +static void getValueOperator(StringInfo strInfo, char *key, ValueOperator *valOperator); +static void getValueOperatorType(StringInfo strInfo, ValueOperatorType type); +static void getValueType(StringInfo strInfo, char *type); + +static void +getValueOperatorType(StringInfo strInfo, ValueOperatorType type) +{ + switch(type) + { + case _LESS : + appendStringInfo(strInfo, "<"); + break; + case _EQ : + case _NOTEQ : + appendStringInfo(strInfo, "="); + break; + case _LESSEQ : + appendStringInfo(strInfo, "<="); + break; + case _GREAT : + appendStringInfo(strInfo, ">"); + break; + case _GREATEQ : + appendStringInfo(strInfo, ">="); + break; + case _TYPE : + break; + case _SIZE : + appendStringInfo(strInfo, ".@# ="); + break; + case _EXISTS : + appendStringInfo(strInfo, "= *"); + break; + default : + elog(ERROR,"This value operator is not supported"); + break; + } +} + +/* + * Return type in jsquery format + */ +static void +getValueType(StringInfo strInfo, char *type) +{ + if(strcmp(type,"\"string\"") == 0) appendStringInfo(strInfo, " IS STRING"); + else if( + strcmp(type, "\"double\"") == 0 || + strcmp(type, "\"int\"") == 0 || + strcmp(type, "\"long\"") == 0 || + strcmp(type, "\"decimal\"") == 0 + ) appendStringInfo(strInfo, " IS NUMERIC"); + + else if(strcmp(type, "\"array\"") == 0) appendStringInfo(strInfo, " IS ARRAY"); + else if(strcmp(type, "\"object\"") == 0) appendStringInfo(strInfo, " IS OBJECT"); + else if(strcmp(type, "\"bool\"") == 0) appendStringInfo(strInfo, " IS BOOLEAN"); + else + elog(ERROR, "Jsquery is not supported MongoDB %s value type", type); +} + +static void +getValueOperator(StringInfo strInfo, char *key, ValueOperator *valOperator) +{ + if(valOperator->value_op == _EXISTS) + { + if(valOperator->value->b) + { + appendStringInfo(strInfo, "%s", key); + appendStringInfo(strInfo, " "); + getValueOperatorType(strInfo, valOperator->value_op); + } + else + { + appendStringInfo(strInfo, "NOT (%s ", key); + getValueOperatorType(strInfo, valOperator->value_op); + appendStringInfo(strInfo, ")"); + } + } + else if(valOperator->value_op == _TYPE) + { + appendStringInfo(strInfo, "%s ", key); + getValueType(strInfo, valOperator->value->str); + } + else if(valOperator->value_op == _NOTEQ) + { + appendStringInfo(strInfo, "NOT (%s ", key); + getValueOperatorType(strInfo, valOperator->value_op); + appendStringInfo(strInfo, " "); + getLeafValue(strInfo, valOperator->value); + appendStringInfo(strInfo, ")"); + } + else + { + appendStringInfo(strInfo, "%s ", key); + getValueOperatorType(strInfo, valOperator->value_op); + appendStringInfo(strInfo, " "); + getLeafValue(strInfo, valOperator->value); + } +} + +static void +getElemMatchOperator(StringInfo strInfo, char *key, ElemMatchOperator *elemMatchOperator) +{ + appendStringInfo(strInfo, "%s.#:(", key); + switch(elemMatchOperator->typeOfValue) + { + case E_EXPRESSION: + getExpression(strInfo, elemMatchOperator->expression); + break; + case E_OP_OBJECT: + getOperatorObject(strInfo, "$",elemMatchOperator->operatorOpbject); + break; + } + appendStringInfo(strInfo, ")"); +} + +static void +getOperator(StringInfo strInfo, char *key, Operator *operator) +{ + switch(operator->type) + { + case NOP : + getNotOperator(strInfo, key, (NotOperator*) operator); + break; + case MOP : + elog(ERROR, "MongoDB module operator is not supported by jsquery"); + case AOP : + getArrayOperator(strInfo, key, (ArrayOperator*) operator); + break; + case VOP : + getValueOperator(strInfo, key, (ValueOperator*) operator); + break; + case EOP : + getElemMatchOperator(strInfo, key, (ElemMatchOperator*) operator); + break; + default : + elog(ERROR, "This mongoDB operator is not supported by jsquery"); + } +} + +static void +getNotOperator(StringInfo strInfo, char *key, NotOperator *notOperator) +{ + appendStringInfo(strInfo, "NOT ("); + getOperator(strInfo, key,notOperator->op); + appendStringInfo(strInfo, ")"); +} + +static void +getOperatorObject(StringInfo strInfo, char *key, OperatorObject *opObject) +{ + ListCell *cell; + bool first = true; + + foreach(cell, opObject->operatorList) + { + if(first) + { + appendStringInfo(strInfo, "("); + getOperator(strInfo, key, (Operator *)lfirst(cell)); + appendStringInfo(strInfo, ")"); + first = false; + } + else + { + appendStringInfo(strInfo, " AND ("); + getOperator(strInfo, key, ((Operator *)lfirst(cell))); + appendStringInfo(strInfo, ")"); + } + } +} + +static void +getLeafValueEq(StringInfo strInfo, char *key, LeafValue *leafValue) +{ + appendStringInfo(strInfo, "%s = ", key); + getLeafValue(strInfo, leafValue); +} + +static void +getLeafClauseValue(StringInfo strInfo, char *key, MValue *value) +{ + if(value->type) + getOperatorObject(strInfo, key, value->oob); + else + getLeafValueEq(strInfo, key, value->lv); +} + +static void +getLeafClause(StringInfo strInfo, LeafClause *leafClause) +{ + getLeafClauseValue(strInfo, leafClause->key, leafClause->vl); +} + +static void +getExpressionClause(StringInfo strInfo, ExpressionClause* expClause) +{ + ListCell *cell; + bool first = true; + char *expOperator = NULL; + + switch(expClause->op) + { + case _AND : + expOperator = "AND"; + break; + case _OR : + expOperator = "OR"; + break; + case _NOR : + expOperator = "OR NOT"; + break; + } + + foreach(cell, expClause->expressionList) + { + if(first) + { + if(expClause->op == _NOR) appendStringInfo(strInfo, "NOT "); + + appendStringInfo(strInfo, "("); + getExpression(strInfo, (Expression *)lfirst(cell)); + appendStringInfo(strInfo, ") "); + + first = false; + } + else + { + appendStringInfo(strInfo, "%s (", expOperator); + getExpression(strInfo, (Expression *)lfirst(cell)); + appendStringInfo(strInfo, ") "); + } + } +} + +static void +getTextClause(StringInfo strInfo, TextClause *textClause) +{ + appendStringInfo(strInfo, "* = %s", textClause->search_str); +} + +static void +getClause(StringInfo strInfo, Clause *clause) +{ + switch(clause->type) + { + case LEAF : + getLeafClause(strInfo, (LeafClause*) clause); + break; + case COMMENT : + elog(ERROR, "MongoDB comment clause is not supported by jsquery"); + case TEXT : + getTextClause(strInfo, (TextClause*) clause); + break; + case WHERE : + elog(ERROR, "MongoDB where clause is not supported by jsquery"); + case EXPRESSION : + getExpressionClause(strInfo, (ExpressionClause*) clause); + break; + default: + break; + } +} + +static void +getExpression(StringInfo strInfo, Expression *expression) +{ + ListCell *cell; + bool first = true; + + foreach(cell, expression->clauseList) + { + if (first) + { + getClause(strInfo, (Clause *)lfirst(cell)); + first = false; + } + else + { + appendStringInfo(strInfo, " AND "); + getClause(strInfo, (Clause *)lfirst(cell)); + } + } +} + +static void +getLeafValue(StringInfo strInfo, LeafValue *value) +{ + switch(value->type) + { + case S : + appendStringInfo(strInfo, "%s", value->str); + break; + case I : + appendStringInfo(strInfo, "%s", value->i); + break; + case A : + appendStringInfo(strInfo, "["); + getArraySequence(strInfo, value->ar); + appendStringInfo(strInfo, "]"); + break; + case B : + appendStringInfo(strInfo, "%s", (value->b ? "true" : "false")); + break; + case D : + appendStringInfo(strInfo, "%s", value->d); + break; + default : + break; + } +} + +static void +getArraySequence(StringInfo strInfo, MArray *marray) +{ + ListCell *cell; + bool first = true; + + foreach(cell, marray->arrayList) + { + if(first) + { + getLeafValue(strInfo, (LeafValue *)lfirst(cell)); + first = false; + } + else + { + appendStringInfo(strInfo, ", "); + getLeafValue(strInfo, (LeafValue *)lfirst(cell)); + } + } +} + +static void +getArrayOperator(StringInfo strInfo, char *key, ArrayOperator *arOperator) +{ + switch(arOperator->array_op) + { + case _IN : + appendStringInfo(strInfo, "%s IN (", key); + getArraySequence(strInfo, arOperator->ar); + appendStringInfo(strInfo, ")"); + break; + case _NIN: + appendStringInfo(strInfo, "NOT (%s IN (", key); + getArraySequence(strInfo, arOperator->ar); + appendStringInfo(strInfo, "))"); + break; + case _ALL: + appendStringInfo(strInfo, "%s @> [", key); + getArraySequence(strInfo, arOperator->ar); + appendStringInfo(strInfo, "]"); + break; + default : + break; + } +} + +char * +getJsquery(MQuery *qu) +{ + StringInfoData strInfo; + initStringInfo(&strInfo); + getExpression(&strInfo, qu->exp); + deleteMquery(qu); + return strInfo.data; +} \ No newline at end of file diff --git a/monq_gram.y b/monq_gram.y new file mode 100644 index 0000000..a1ca822 --- /dev/null +++ b/monq_gram.y @@ -0,0 +1,239 @@ +%{ + #include + #include + #include + + #include "monq.h" + + MQuery *RET; + + typedef struct yy_buffer_state *YY_BUFFER_STATE; + extern int yylex(); + extern int yyparse(); + extern void yyerror(char *s); + extern YY_BUFFER_STATE yy_scan_string(char * str); + extern void yy_delete_buffer(YY_BUFFER_STATE buffer); + + char *inputString; + int position; + MQuery* + parse(char *str) + { + YY_BUFFER_STATE buffer = yy_scan_string(str); + inputString = str; + yyparse(); + yy_delete_buffer(buffer); + position = 1; + return RET; + } +%} + +/* Types of query tree nodes and leafs */ +%union +{ + MQuery *mquery; + Expression *exp; + Clause *cl; + MValue *vl; + LeafValue *lv; + List *list; + WhereClauseValue *wcv; + char *strval; + int intval; + double dubval; + MArray *arrval; + bool boolval; + ArrayOperatorType aop_type; + ExpressionOperatorType exop_type; + ValueOperatorType valop_type; + OperatorObject *oob; + Operator *op; + ElemMatchOperator *elemMatchOp; +} + +%type QUERY +%type EXPRESSION +%type CLAUSE TEXT_CLAUSE EXPRESSION_TREE_CLAUSE LEAF_CLAUSE COMMENT_CLAUSE WHERE_CLAUSE +%type VALUE + +%type KEY KEY_STRING +%type OPERATOR +%type OPEARATOR_OBJECT + +%type LSCOPE RSCOPE COMMA + +%type OPERATOR_LIST LEAF_VALUE_LIST EXPRESSION_LIST CLAUSE_LIST + +%type WHERE_CLAUSE_VALUE +%type WHERE_OPERATOR +%token WHERE_OPERATOR + +/* OPERATORS */ + +/* Tree clause */ +%type TREE_OPERATOR OR NOR AND +%token OR NOR AND + +/* Leaf value operator */ +%type EQ LESS GREAT LESSEQ GREATEQ NOTEQ TYPE SIZE EXISTS NOT VALUE_OPERATOR +%token EQ NOTEQ LESS LESSEQ GREAT GREATEQ TYPE SIZE EXISTS NOT + +/* Array operator */ +%type IN NIN ALL ARRAY_OPERATOR +%token IN NIN ALL + +/* Mod operator */ +%type MOD_OPERATOR +%type DIVISOR REMAINDER +%token MOD_OPERATOR + +/* ElemMatch operator */ +%type ELEMMATCH_OPERATOR +%type ELEMMATCH +%token ELEMMATCH + +/* Comment clause */ +%type COMMENT_OPERATOR +%token COMMENT_OPERATOR + +/* Text clause */ +%type DIACRITIC_SENSITIVE_OPERATOR CASE_SENSITIVE_OPERATOR LANGUAGE_OPERATOR SEARCH_OPERATOR TEXT_OPERATOR +%token DIACRITIC_SENSITIVE_OPERATOR CASE_SENSITIVE_OPERATOR LANGUAGE_OPERATOR SEARCH_OPERATOR TEXT_OPERATOR + +/* Type of values */ +%type LEAF_VALUE +%type INT +%type STRING +%type DOUBLE +%type ARRAY +%type BOOLEAN +%token INT STRING DOUBLE BOOLEAN KEY_STRING + +/* Scope types */ +%token LSCOPE RSCOPE COMMA LSQBRACKET RSQBRACKET LRBRACKET RRBRACKET + +%start QUERY + +%% +QUERY : EXPRESSION {$$ = createQuery($1); RET=$$; } + ; + +EXPRESSION : LSCOPE CLAUSE_LIST RSCOPE { $$ = createExpression($2); } + ; + +CLAUSE_LIST : CLAUSE COMMA CLAUSE_LIST { $$ = addClause($1, $3); } + | CLAUSE { $$ = lappend(NULL, $1); } + ; + +CLAUSE : LEAF_CLAUSE + | COMMENT_CLAUSE + | WHERE_CLAUSE + | EXPRESSION_TREE_CLAUSE + | TEXT_CLAUSE + ; + + +/* TEXT CLAUSE SECTION */ + +TEXT_CLAUSE : LSCOPE TEXT_OPERATOR EQ LSCOPE SEARCH_OPERATOR EQ KEY + RSCOPE RSCOPE { $$ = createTextClause($7, false, "", false, false); } + | LSCOPE TEXT_OPERATOR EQ LSCOPE SEARCH_OPERATOR EQ KEY COMMA + LANGUAGE_OPERATOR EQ STRING COMMA + CASE_SENSITIVE_OPERATOR EQ BOOLEAN COMMA + DIACRITIC_SENSITIVE_OPERATOR EQ BOOLEAN RSCOPE RSCOPE { $$ = createTextClause($7, false, $11, $15, $19); } + ; + +/* END OF SECTION */ + +/*WHERE CLAUSE SECTION*/ + +WHERE_CLAUSE : LSCOPE WHERE_OPERATOR EQ WHERE_CLAUSE_VALUE RSCOPE { $$ = createWhereClause($4); } + ; + +WHERE_CLAUSE_VALUE : KEY { $$ = stringToWhereClauseValue($1); } + ; +/* END OF SECTION */ + +/*COMMENT CLAUSE SECTION*/ +COMMENT_CLAUSE : LSCOPE COMMENT_OPERATOR EQ STRING RSCOPE { $$ = createCommentClause($2, $4); } + ; +/* END OF SECTION */ + +/*TREE CLAUSE SECTION*/ + +EXPRESSION_TREE_CLAUSE : TREE_OPERATOR EQ LSQBRACKET EXPRESSION_LIST RSQBRACKET { $$ = createExpressionTreeClause($1, $4); } + | LSCOPE EXPRESSION_TREE_CLAUSE RSCOPE { $$ = $2; } + ; + +EXPRESSION_LIST : EXPRESSION { $$ = lcons($1, NULL); } + | EXPRESSION COMMA EXPRESSION_LIST { $$ = addExpression($1, $3); } + ; + +TREE_OPERATOR : OR | AND | NOR ; + +/* END OF SECTION */ + +/* LEAF CLAUSE SECTION */ +LEAF_CLAUSE : KEY EQ VALUE { $$ = createLeafClause($1, $3); } + ; + +KEY : STRING + | KEY_STRING + ; + +VALUE : LEAF_VALUE { $$ = createLeafValueValue($1); } + | OPEARATOR_OBJECT { $$ = createOperatorObjectValue($1); } + ; + +OPEARATOR_OBJECT : LSCOPE OPERATOR_LIST RSCOPE { $$ = createOperatorObject($2); } + ; + +OPERATOR_LIST : OPERATOR { $$ = lappend(NULL, $1); } + | OPERATOR COMMA OPERATOR_LIST { $$ = addOperator($1, $3); } + ; + +OPERATOR : VALUE_OPERATOR EQ LEAF_VALUE { $$ = createValueOperator($1, $3); } + | ARRAY_OPERATOR EQ ARRAY { $$ = createArrayOperator($1, $3); } + | MOD_OPERATOR EQ LSQBRACKET DIVISOR COMMA REMAINDER RSQBRACKET { $$ = createModOperator($4, $6); } + | NOT EQ LSCOPE OPERATOR RSCOPE { $$ = createNotOperator($4); } + | ELEMMATCH EQ ELEMMATCH_OPERATOR { $$ = $3; } + ; + +VALUE_OPERATOR : EQ | NOTEQ | LESS | LESSEQ | GREAT | GREATEQ | TYPE | SIZE | EXISTS + ; + +ELEMMATCH_OPERATOR : OPEARATOR_OBJECT { $$ = createElemMatchOperatorOpObject($1); } + | EXPRESSION { $$ = createElemMatchOperatorExpression($1); } + ; + +DIVISOR : LEAF_VALUE + ; + +REMAINDER : LEAF_VALUE + ; + +ARRAY : LSQBRACKET LEAF_VALUE_LIST RSQBRACKET {$$ = createNewArray($2); } + ; + +ARRAY_OPERATOR : IN | NIN | ALL + ; + +LEAF_VALUE_LIST : LEAF_VALUE { $$ = lcons($1, NULL); } + | LEAF_VALUE COMMA LEAF_VALUE_LIST { $$ = addArrayElement($1, $3); } + ; + +LEAF_VALUE : INT { $$ = createIntegerValue($1); } + | STRING { $$ = createStringValue($1); } + | KEY_STRING { + StringInfoData strf; + initStringInfo(&strf); + appendStringInfo(&strf, "\"%s\"", $1); + $$ = createStringValue(strf.data); + } + | DOUBLE { $$ = createDoubleValue($1); } + | ARRAY { $$ = createArrayValue($1); } + | BOOLEAN { $$ = createBooleanValue($1); } + ; + +/* END OF SECTION */ +%% \ No newline at end of file diff --git a/monq_scan.l b/monq_scan.l new file mode 100644 index 0000000..efc485f --- /dev/null +++ b/monq_scan.l @@ -0,0 +1,107 @@ +%{ + #include + #include + #include "monq.h" + #include "monq_gram.h" + #include + + extern int yylex(); + extern void yyerror(char *s); + + int position = 1; + char *inputString; + + void + yyerror(char *s) + { + int val; + position -= yyleng; + val = position; + position = 1; + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("bad MongoDB representation"), + /* translator: first %s is typically "syntax error" */ + errdetail("%s \"%s\" on line %d position %d\n%s\n%*c", s, yytext, yylineno, val,inputString,val,'^'))); + + exit(0); + } +%} + +%option yylineno +%option noyywrap +%option nounput +%option noinput + +%% + +[/][/].*\n ; // comment + +[0-9]+ { yylval.strval=strdup(yytext); position += yyleng; return INT; } +[0-9]+\.[0-9]+ { yylval.strval=strdup(yytext); position += yyleng; return DOUBLE; } +(true|false|TRUE|FALSE) { yylval.boolval=(strcmp(strdup(yytext),"true")==0); position += yyleng; return BOOLEAN; } + +\{ { position += yyleng; return LSCOPE; } +\} { position += yyleng; return RSCOPE; } +\[ { position += yyleng; return LSQBRACKET; } +\] { position += yyleng; return RSQBRACKET; } + +\, { position += yyleng; return COMMA; } + +\: { position += yyleng; yylval.valop_type=_EQ; return EQ; } +\$(eq|EQ) { position += yyleng; yylval.valop_type=_EQ; return EQ; } +\$(lt|LT) { position += yyleng; yylval.valop_type=_LESS; return LESS; } +\$(lte|LTE) { position += yyleng; yylval.valop_type=_LESSEQ; return LESSEQ; } +\$(gt|GT) { position += yyleng; yylval.valop_type=_GREAT; return GREAT; } +\$(gte|GTE) { position += yyleng; yylval.valop_type=_GREATEQ; return GREATEQ; } +\$(ne|NE) { position += yyleng; yylval.valop_type=_NOTEQ; return NOTEQ; } +\$type { position += yyleng; yylval.valop_type=_TYPE; return TYPE; } +\$size { position += yyleng; yylval.valop_type=_SIZE; return SIZE; } +\$exists { position += yyleng; yylval.valop_type=_EXISTS; return EXISTS; } + +\$in { position += yyleng; yylval.aop_type=_IN; return IN; } +\$nin { position += yyleng; yylval.aop_type=_NIN; return NIN; } +\$all { position += yyleng; yylval.aop_type=_ALL; return ALL; } + +\$not { position += yyleng; return NOT;} + +\$where { position += yyleng; return WHERE_OPERATOR; } + +\$elemMatch { position += yyleng; return ELEMMATCH; } + +\$or { position += yyleng; yylval.exop_type=_OR; return OR; } +\$nor { position += yyleng; yylval.exop_type=_NOR; return NOR; } +\$and { position += yyleng; yylval.exop_type=_AND; return AND; } + +\$search { position += yyleng; return SEARCH_OPERATOR; } +\$text { position += yyleng; return TEXT_OPERATOR; } +\$language { position += yyleng; return LANGUAGE_OPERATOR; } +\$caseSensitive { position += yyleng; return CASE_SENSITIVE_OPERATOR; } +\$diacriticSensitive { position += yyleng; return DIACRITIC_SENSITIVE_OPERATOR; } + +\$comment { position += yyleng; return COMMENT_OPERATOR; } + +\$mod { position += yyleng; return MOD_OPERATOR; } + +\"\" { position += yyleng; yylval.strval=strdup(yytext); return STRING; } + +[0-9a-zA-Z]+ { position += yyleng; yylval.strval=strdup(yytext); return KEY_STRING; } + +\"[\.0-9a-zA-Z]*\" { + char *str = strdup(yytext+1); + str[yyleng-2] = '\0'; + yylval.strval = str; + position += yyleng; + return KEY_STRING; + } + +\"[(\\\")(\\0)\;\!\@\#\$\%\^\&\*\(\)\.\, 0-9a-zA-Z]*\" { + position += yyleng; + yylval.strval=strdup(yytext); + return STRING; + } + +[ \t\r\n] ; { position += yyleng; } // whitespace + + +%% \ No newline at end of file diff --git a/sql/jsquery.sql b/sql/jsquery.sql index f4f461c..3e178c0 100644 --- a/sql/jsquery.sql +++ b/sql/jsquery.sql @@ -543,4 +543,89 @@ select v from test_jsquery where v @@ 'array && [2,3]' order by v; select v from test_jsquery where v @@ 'array @> [2,3]' order by v; select v from test_jsquery where v @@ 'array = [2,3]' order by v; +---MongoDB query translator tests +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : 1 }'); +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $eq : 1 } }'); +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $ne : 1 } }'); +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $lt : 1 } }'); +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $lte : 1 } }'); +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $gt : 1 } }'); +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $gte : 1 } }'); +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $in : [2,3] } }'); +select '{"a": {"b": 1 } }'::jsonb @@ parse_mquery('{ "a.b" : { $nin : [2,3] } }'); + +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ a : { $exists : false } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ a : { $exists : true } }'); + +select parse_mquery('{ is : { $lt: 1 } }')::jsquery; + +select v from test_jsquery where v @@ parse_mquery('{ array : { $all: [2,3] } }') order by v; + +select v from test_jsquery where v @@ parse_mquery('{ { $text: { $search: "Flew" } } }'); + +select '{ "a" : "ssl" }'::jsonb @@ parse_mquery('{ a: { $in: [ "ssl","security"] } }'); +select '{ "a" : 1 }'::jsonb @@ parse_mquery('{ a: { $in: [ "ssl","security"] } }'); +select '{ "a" : "ssl" }'::jsonb @@ parse_mquery('{ a: { $nin: [ "ssl","security"] } }'); +select '{ "a" : "sslqwerty" }'::jsonb @@ parse_mquery('{ a: { $nin: [ "ssl","security"] } }'); +select '{ "a" : [ "ssl","security"] }'::jsonb @@ parse_mquery('{ a: { $size: 2 } }'); +select '{ "a" : [ "ssl","security"] }'::jsonb @@ parse_mquery('{ a: { $size: 1 } }'); +select '{ "a" : [ "ssl","security", "pattern"] }'::jsonb @@ parse_mquery('{ a: { $all: [ "ssl","security"] } }'); +select '{ "a" : [ "ssl","pattern"] }'::jsonb @@ parse_mquery('{ a: { $all: [ "ssl","security"] } }'); +select '{ "a" : [ "ssl","security"] }'::jsonb @@ parse_mquery('{ a: { $all: [ "ssl","security"] } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ a : { $exists : false } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ a : { $exists : true } }'); +select '{ "b" : 2 }'::jsonb @@ parse_mquery('{ a : { $exists : false } }'); +select '{ "b" : 2 }'::jsonb @@ parse_mquery('{ a : { $exists : true } }'); +select '{ "b" : 2 }'::jsonb @@ parse_mquery('{ b: { $type: "int" } }'); +select '{ "b" : "qwerttyu" }'::jsonb @@ parse_mquery('{ b: { $type: "int" } }'); +select '{ "b" : 2 }'::jsonb @@ parse_mquery('{ b: { $type: "long" } }'); +select '{ "b" : "qwerttyu" }'::jsonb @@ parse_mquery('{ b: { $type: "long" } }'); +select '{ "b" : true }'::jsonb @@ parse_mquery('{ b: { $type: "bool" } }'); +select '{ "b" : "fklgjlksdfgsldflsgjslkrjekfjkl" }'::jsonb @@ parse_mquery('{ b: { $type: "bool" } }'); +select '{ "b" : "fklgjlksdfgsldflsgjslkrjekfjkl" }'::jsonb @@ parse_mquery('{ b: { $type: "array" } }'); +select '{ "b" : [1, 4] }'::jsonb @@ parse_mquery('{ b: { $type: "array" } }'); +select '{ "b" : "fklgjlksdfgsldflsgjslkrjekfjkl" }'::jsonb @@ parse_mquery('{ b: { $type: "string" } }'); +select '{ "b" : [1, 4] }'::jsonb @@ parse_mquery('{ b: { $type: "string" } }'); +select '{ "b" : 2.23432 }'::jsonb @@ parse_mquery('{ b: { $type: "double" } }'); +select '{ "b" : 2 }'::jsonb @@ parse_mquery('{ b: { $type: "double" } }'); +select '{ "b" : 2 }'::jsonb @@ parse_mquery('{ b: { $type: "decimal" } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "maxKey" } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "binData" } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "objectId" } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "javascript" } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "symbol" } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "javascriptWithScope" } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "timestamp" } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "minKey" } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "regex" } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "null" } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "date" } }'); +select '{ "a" : 2 }'::jsonb @@ parse_mquery('{ y: { $type: "undefined" } }'); +/* Or operator */ +select '{ "quantity" : 2, "price" : 10 }'::jsonb @@ parse_mquery('{ $or: [ { quantity: { $lt: 20 } }, { price: 10 } ] }'); +select '{ "quantity" : 200, "price" : 10 }'::jsonb @@ parse_mquery('{ $or: [ { quantity: { $lt: 20 } }, { price: 10 } ] }'); +select '{ "quantity" : 200, "price" : 10 }'::jsonb @@ parse_mquery('{ $or: [ { quantity: { $lt: 20 } }, { price: 100 } ] }'); +/* Nor operator */ +select '{ "quantity" : 2, "price" : 10 }'::jsonb @@ parse_mquery('{ $nor: [ { quantity: { $lt: 20 } }, { price: 10 } ] }'); +select '{ "quantity" : 200, "price" : 10 }'::jsonb @@ parse_mquery('{ $nor: [ { quantity: { $lt: 20 } }, { price: 10 } ] }'); +select '{ "quantity" : 200, "price" : 10 }'::jsonb @@ parse_mquery('{ $nor: [ { quantity: { $lt: 20 } }, { price: 100 } ] }'); +/* And operator */ +select '{ "quantity" : 200, "price" : 10 }'::jsonb @@ parse_mquery('{ $and: [ { quantity: { $lt: 20 } }, { price: 100 } ] }'); +select '{ "quantity" : 5, "price" : 100 }'::jsonb @@ parse_mquery('{ $and: [ { quantity: { $lt: 20 } }, { price: 100 } ] }'); +/* Not operator */ +select '{ "quantity" : 5, "price" : 100 }'::jsonb @@ parse_mquery('{ price: { $not: { $gt: 1.99 } } }'); +select '{ "quantity" : 5, "price" : 1 }'::jsonb @@ parse_mquery('{ price: { $not: { $gt: 1.99 } } }'); +/* Mod operator */ +select '{ "quantity" : 2, "price" : 10 }'::jsonb @@ parse_mquery('{ qty: { $mod: [ 4, 0 ] } } '); +select '{"a": 5}'::jsonb @@ parse_mquery('{ a: { $eq: 5 } }'); +select '{"a": 5}'::jsonb @@ parse_mquery('{ a: { $eq: 6 } }'); +select '{ "quantity" : "qw", "price" : 10 }'::jsonb @@ parse_mquery('{ { $where: "qw"} }'); +select '{ "quantity" : "qw", "price" : 10 }'::jsonb @@ parse_mquery('{ { $text: { $search: "qsddjkhjw" } } }'); +select '{ "quantity" : "qw", "price" : 10 }'::jsonb @@ parse_mquery('{ { $text: { $search: "qw" } } }'); +select '{"a": { "qwerty" : 5} }'::jsonb @@ parse_mquery('{ "a.qwerty" : { $eq: 6 } }'); +select '{"a": { "qwerty" : { "asdfgh" : { "fgfhg" : 5 } } } }'::jsonb @@ parse_mquery('{ "a.qwerty.asdfgh.fgfhg" : { $eq: 5 } }'); +select '{ "_id" : 3, "results" : [ { "product" : "abc", "score" : 7 }, { "product" : "abc", "score" : 8 } ] }' @@ parse_mquery('{ results: { $elemMatch: { product: "abc" } } }'); +select '{ "_id" : 3, "results" : [ 81, 84, 83] }' @@ parse_mquery('{ results: { $elemMatch: { $gte: 80, $lt: 85 } } }'); +select '{ "_id" : 3, "results" : [ 81, 86, 83] }' @@ parse_mquery('{ results: { $elemMatch: { $gte: 80, $lt: 85 } } }'); + RESET enable_seqscan; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy