Skip to content

Commit ae282ba

Browse files
committed
implement index hints
1 parent cb3bf77 commit ae282ba

8 files changed

+183
-32
lines changed

expected/jsquery.out

+48
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,54 @@ select '"xxx"' @@ '$ IS numeric'::jsquery;
10301030
f
10311031
(1 row)
10321032

1033+
--hint
1034+
select 'a /*-- noindex */ = 5'::jsquery;
1035+
jsquery
1036+
--------------------------
1037+
"a" /*-- noindex */ = 5
1038+
(1 row)
1039+
1040+
select 'a /*-- index */ = 5'::jsquery;
1041+
jsquery
1042+
------------------------
1043+
"a" /*-- index */ = 5
1044+
(1 row)
1045+
1046+
select 'asd.# = 3'::jsquery & 'zzz /*-- noindex */ = true' | 'xxx.# /*-- index */ = zero';
1047+
?column?
1048+
-----------------------------------------------------------------------------------
1049+
(("asd".# = 3 & "zzz" /*-- noindex */ = true) | "xxx".# /*-- index */ = "zero")
1050+
(1 row)
1051+
1052+
select 'a /*-- xxx */ = 5'::jsquery;
1053+
jsquery
1054+
---------
1055+
"a" = 5
1056+
(1 row)
1057+
1058+
select 'a /* index */ = 5'::jsquery;
1059+
jsquery
1060+
---------
1061+
"a" = 5
1062+
(1 row)
1063+
1064+
select 'a /* noindex */ = 5'::jsquery;
1065+
jsquery
1066+
---------
1067+
"a" = 5
1068+
(1 row)
1069+
1070+
select 'a = /*-- noindex */ 5'::jsquery;
1071+
ERROR: bad jsquery representation
1072+
LINE 1: select 'a = /*-- noindex */ 5'::jsquery;
1073+
^
1074+
DETAIL: syntax error, unexpected HINT_P at or near "*/"
1075+
select 'a = /* noindex */ 5'::jsquery;
1076+
jsquery
1077+
---------
1078+
"a" = 5
1079+
(1 row)
1080+
10331081
---table and index
10341082
select count(*) from test_jsquery where (v->>'review_helpful_votes')::int4 > 0;
10351083
count

jsquery.h

+32-18
Original file line numberDiff line numberDiff line change
@@ -35,26 +35,38 @@ typedef enum JsQueryItemType {
3535
jqiNumeric = jbvNumeric,
3636
jqiBool = jbvBool,
3737
jqiArray = jbvArray,
38-
jqiAnd = '&',
39-
jqiOr = '|',
40-
jqiNot = '!',
41-
jqiEqual = '=',
42-
jqiLess = '<',
43-
jqiGreater = '>',
44-
jqiLessOrEqual = '{',
45-
jqiGreaterOrEqual = '}',
46-
jqiContains = '@',
47-
jqiContained = '^',
48-
jqiOverlap = 'O',
49-
jqiAny = '*',
50-
jqiAnyArray = '#',
51-
jqiAnyKey = '%',
52-
jqiKey = 'K',
53-
jqiCurrent = '$',
54-
jqiIn = 'I',
55-
jqiIs = 'i'
38+
jqiAnd,
39+
jqiOr,
40+
jqiNot,
41+
jqiEqual,
42+
jqiLess,
43+
jqiGreater,
44+
jqiLessOrEqual,
45+
jqiGreaterOrEqual,
46+
jqiContains,
47+
jqiContained,
48+
jqiOverlap,
49+
jqiAny,
50+
jqiAnyArray,
51+
jqiAnyKey,
52+
jqiKey,
53+
jqiCurrent,
54+
jqiIn,
55+
jqiIs
5656
} JsQueryItemType;
5757

58+
/*
59+
* JsQueryHint stores in the same byte as JsQueryItemType so
60+
* JsQueryItemType should not use two high bits
61+
*/
62+
typedef enum JsQueryHint {
63+
jsqIndexDefault = 0x00,
64+
jsqForceIndex = 0x80,
65+
jsqNoIndex = 0x40
66+
} JsQueryHint;
67+
68+
#define JSQ_HINT_MASK (jsqIndexDefault | jsqForceIndex | jsqNoIndex)
69+
5870
/*
5971
* Support functions to parse/construct binary value.
6072
* Unlike many other representation of expression the first/main
@@ -66,6 +78,7 @@ typedef enum JsQueryItemType {
6678

6779
typedef struct JsQueryItem {
6880
JsQueryItemType type;
81+
JsQueryHint hint;
6982
int32 nextPos;
7083
char *base;
7184

@@ -113,6 +126,7 @@ typedef struct JsQueryParseItem JsQueryParseItem;
113126

114127
struct JsQueryParseItem {
115128
JsQueryItemType type;
129+
JsQueryHint hint;
116130
JsQueryParseItem *next; /* next in path */
117131

118132
union {

jsquery_constr.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ copyJsQuery(StringInfo buf, JsQueryItem *jsq)
2828

2929
check_stack_depth();
3030

31-
appendStringInfoChar(buf, (char)jsq->type);
31+
Assert((jsq->type & jsq->hint) == 0);
32+
Assert((jsq->type & JSQ_HINT_MASK) == 0);
33+
34+
appendStringInfoChar(buf, (char)(jsq->type | jsq->hint));
3235
alignStringInfoInt(buf);
3336

3437
next = (jsqGetNext(jsq, NULL)) ? buf->len : 0;

jsquery_gram.y

+15-4
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ makeItemType(int type)
6262
JsQueryParseItem* v = palloc(sizeof(*v));
6363

6464
v->type = type;
65+
v->hint = jsqIndexDefault;
6566
v->next = NULL;
6667

6768
return v;
@@ -192,10 +193,11 @@ makeItemList(List *list) {
192193
%error-verbose
193194

194195
%union {
195-
string str;
196-
List *elems; /* list of JsQueryParseItem */
196+
string str;
197+
List *elems; /* list of JsQueryParseItem */
197198

198-
JsQueryParseItem *value;
199+
JsQueryParseItem *value;
200+
JsQueryHint hint;
199201
}
200202

201203
%token <str> IN_P IS_P NULL_P TRUE_P ARRAY_T
@@ -211,6 +213,10 @@ makeItemList(List *list) {
211213

212214
%type <value> path_elem right_expr expr array
213215

216+
%token <hint> HINT_P
217+
218+
%type <hint> opt_hint
219+
214220
%left '|'
215221
%left '&'
216222
%left '!'
@@ -242,6 +248,11 @@ scalar_value:
242248
| NUMERIC_P { $$ = makeItemNumeric(&$1); }
243249
;
244250

251+
opt_hint:
252+
HINT_P { $$ = $1; }
253+
| /* EMPTY */ { $$ = jsqIndexDefault; }
254+
;
255+
245256
value_list:
246257
scalar_value { $$ = lappend(NIL, $1); }
247258
| value_list ',' scalar_value { $$ = lappend($1, $3); }
@@ -267,7 +278,7 @@ right_expr:
267278
;
268279

269280
expr:
270-
path right_expr { $$ = makeItemList(lappend($1, $2)); }
281+
path opt_hint right_expr { $3->hint = $2; $$ = makeItemList(lappend($1, $3)); }
271282
| path '(' expr ')' { $$ = makeItemList(lappend($1, $3)); }
272283
| '(' expr ')' { $$ = $2; }
273284
| '!' expr { $$ = makeItemUnary(jqiNot, $2); }

jsquery_io.c

+25-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ flattenJsQueryParseItem(StringInfo buf, JsQueryParseItem *item)
3030

3131
check_stack_depth();
3232

33-
appendStringInfoChar(buf, (char)item->type);
33+
Assert((item->type & item->hint) == 0);
34+
Assert((item->type & JSQ_HINT_MASK) == 0);
35+
36+
appendStringInfoChar(buf, (char)(item->type | item->hint));
3437
alignStringInfoInt(buf);
3538

3639
next = (item->next) ? buf->len : 0;
@@ -152,6 +155,24 @@ jsquery_in(PG_FUNCTION_ARGS)
152155
PG_RETURN_NULL();
153156
}
154157

158+
static void
159+
printHint(StringInfo buf, JsQueryHint hint)
160+
{
161+
switch(hint)
162+
{
163+
case jsqForceIndex:
164+
appendStringInfoString(buf, " /*-- index */ ");
165+
break;
166+
case jsqNoIndex:
167+
appendStringInfoString(buf, " /*-- noindex */ ");
168+
break;
169+
case jsqIndexDefault:
170+
break;
171+
default:
172+
elog(ERROR, "Unknown hint: %d", hint);
173+
}
174+
}
175+
155176
static void
156177
printOperation(StringInfo buf, JsQueryItemType type)
157178
{
@@ -186,10 +207,12 @@ static void
186207
printJsQueryItem(StringInfo buf, JsQueryItem *v, bool inKey, bool printBracketes)
187208
{
188209
JsQueryItem elem;
189-
bool first = true;
210+
bool first = true;
190211

191212
check_stack_depth();
192213

214+
printHint(buf, v->hint);
215+
193216
switch(v->type)
194217
{
195218
case jqiNull:

jsquery_scan.l

+39-5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ static int scanbuflen;
2828
static void addstring(bool init, char *s, int l);
2929
static void addchar(bool init, char s);
3030
static int checkSpecialVal(void); /* examine scanstring for the special value */
31+
static JsQueryHint checkHint(void);
3132

3233
static void parseUnicode(char *s, int l);
3334

@@ -58,7 +59,10 @@ unicode \\u[0-9A-Fa-f]{4}
5859
5960
<INITIAL>{blank}+ { /* ignore */ }
6061
61-
<INITIAL>\/\* { BEGIN xCOMMENT; }
62+
<INITIAL>\/\* {
63+
addchar(true, '\0');
64+
BEGIN xCOMMENT;
65+
}
6266
6367
<INITIAL>[+-]?[0-9]+(\.[0-9]+)?[eE][+-]?[0-9]+ /* float */ {
6468
addstring(true, yytext, yyleng);
@@ -101,7 +105,7 @@ unicode \\u[0-9A-Fa-f]{4}
101105
102106
<INITIAL>\\ {
103107
yyless(0);
104-
addstring(true, "", 0);
108+
addchar(true, '\0');
105109
BEGIN xNONQUOTED;
106110
}
107111
@@ -118,6 +122,7 @@ unicode \\u[0-9A-Fa-f]{4}
118122
119123
<xNONQUOTED>\/\* {
120124
yylval->str = scanstring;
125+
addchar(true, '\0');
121126
BEGIN xCOMMENT;
122127
return checkSpecialVal();
123128
}
@@ -170,11 +175,16 @@ unicode \\u[0-9A-Fa-f]{4}
170175
171176
<INITIAL><<EOF>> { yyterminate(); }
172177
173-
<xCOMMENT>\*\/ { BEGIN INITIAL; }
178+
<xCOMMENT>\*\/ {
179+
BEGIN INITIAL;
180+
181+
if ((yylval->hint = checkHint()) != jsqIndexDefault)
182+
return HINT_P;
183+
}
174184
175-
<xCOMMENT>[^\*]+ { /* ignore */ }
185+
<xCOMMENT>[^\*]+ { addstring(false, yytext, yyleng); }
176186
177-
<xCOMMENT>\* { /* ignore */ }
187+
<xCOMMENT>\* { addchar(false, '*'); }
178188
179189
<xCOMMENT><<EOF>> { yyerror("Unexpected end of comment"); }
180190
@@ -266,6 +276,30 @@ checkSpecialVal()
266276

267277
return res;
268278
}
279+
280+
static JsQueryHint
281+
checkHint()
282+
{
283+
if (scanstring.len <= 2 || strncmp(scanstring.val, "--", 2) != 0)
284+
return jsqIndexDefault;
285+
286+
scanstring.val += 2;
287+
scanstring.len -= 2;
288+
289+
while(scanstring.len > 0 && isspace(*scanstring.val))
290+
{
291+
scanstring.val++;
292+
scanstring.len--;
293+
}
294+
295+
if (scanstring.len >= 5 && pg_strncasecmp(scanstring.val, "index", 5) == 0)
296+
return jsqForceIndex;
297+
298+
if (scanstring.len >= 7 && pg_strncasecmp(scanstring.val, "noindex", 7) == 0)
299+
return jsqNoIndex;
300+
301+
return jsqIndexDefault;
302+
}
269303
/*
270304
* Called before any actual parsing is done
271305
*/

jsquery_support.c

+9-2
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717
#include "jsquery.h"
1818

1919
#define read_byte(v, b, p) do { \
20-
(v) = *(int8*)((b) + (p)); \
20+
(v) = *(uint8*)((b) + (p)); \
2121
(p) += 1; \
2222
} while(0) \
2323

2424
#define read_int32(v, b, p) do { \
25-
(v) = *(int32*)((b) + (p)); \
25+
(v) = *(uint32*)((b) + (p)); \
2626
(p) += sizeof(int32); \
2727
} while(0) \
2828

@@ -51,10 +51,16 @@ jsqInit(JsQueryItem *v, JsQuery *js)
5151
void
5252
jsqInitByBuffer(JsQueryItem *v, char *base, int32 pos)
5353
{
54+
int xxx;
55+
5456
v->base = base;
5557

5658
read_byte(v->type, base, pos);
5759

60+
xxx = v->type;
61+
v->hint = v->type & JSQ_HINT_MASK;
62+
v->type &= ~JSQ_HINT_MASK;
63+
5864
switch(INTALIGN(pos) - pos)
5965
{
6066
case 3: pos++;
@@ -105,6 +111,7 @@ jsqInitByBuffer(JsQueryItem *v, char *base, int32 pos)
105111
read_int32(v->arg, base, pos);
106112
break;
107113
default:
114+
abort();
108115
elog(ERROR, "Unknown type: %d", v->type);
109116
}
110117
}

sql/jsquery.sql

+11
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,17 @@ select '{"as": false}' @@ '$ IS object'::jsquery;
208208
select '"xxx"' @@ '$ IS string'::jsquery;
209209
select '"xxx"' @@ '$ IS numeric'::jsquery;
210210

211+
--hint
212+
213+
select 'a /*-- noindex */ = 5'::jsquery;
214+
select 'a /*-- index */ = 5'::jsquery;
215+
select 'asd.# = 3'::jsquery & 'zzz /*-- noindex */ = true' | 'xxx.# /*-- index */ = zero';
216+
select 'a /*-- xxx */ = 5'::jsquery;
217+
select 'a /* index */ = 5'::jsquery;
218+
select 'a /* noindex */ = 5'::jsquery;
219+
select 'a = /*-- noindex */ 5'::jsquery;
220+
select 'a = /* noindex */ 5'::jsquery;
221+
211222
---table and index
212223

213224
select count(*) from test_jsquery where (v->>'review_helpful_votes')::int4 > 0;

0 commit comments

Comments
 (0)