mirror of https://github.com/redis/redis.git
				
				
				
			Expr filtering: conversions and other fixes.
This commit is contained in:
		
							parent
							
								
									a90d2ea290
								
							
						
					
					
						commit
						af6fa6f732
					
				
							
								
								
									
										76
									
								
								expr.c
								
								
								
								
							
							
						
						
									
										76
									
								
								expr.c
								
								
								
								
							|  | @ -10,6 +10,7 @@ | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| #include <math.h> | #include <math.h> | ||||||
|  | #include "cJSON.h" | ||||||
| 
 | 
 | ||||||
| #define EXPR_TOKEN_EOF 0 | #define EXPR_TOKEN_EOF 0 | ||||||
| #define EXPR_TOKEN_NUM 1 | #define EXPR_TOKEN_NUM 1 | ||||||
|  | @ -173,6 +174,14 @@ void exprStackFree(exprstack *stack) { | ||||||
|     free(stack->items); |     free(stack->items); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Just reset the stack removing all the items, but leaving it in a state
 | ||||||
|  |  * that makes it still usable for new elements. */ | ||||||
|  | void exprStackReset(exprstack *stack) { | ||||||
|  |     for (int j = 0; j < stack->numitems; j++) | ||||||
|  |         exprFreeToken(stack->items[j]); | ||||||
|  |     stack->numitems = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* =========================== Expression compilation ======================= */ | /* =========================== Expression compilation ======================= */ | ||||||
| 
 | 
 | ||||||
| void exprConsumeSpaces(exprstate *es) { | void exprConsumeSpaces(exprstate *es) { | ||||||
|  | @ -553,21 +562,31 @@ exprstate *exprCompile(char *expr, int *errpos) { | ||||||
| /* Convert a token to its numeric value. For strings we attempt to parse them
 | /* Convert a token to its numeric value. For strings we attempt to parse them
 | ||||||
|  * as numbers, returning 0 if conversion fails. */ |  * as numbers, returning 0 if conversion fails. */ | ||||||
| double exprTokenToNum(exprtoken *t) { | double exprTokenToNum(exprtoken *t) { | ||||||
|  |     char buf[128]; | ||||||
|     if (t->token_type == EXPR_TOKEN_NUM) { |     if (t->token_type == EXPR_TOKEN_NUM) { | ||||||
|         return t->num; |         return t->num; | ||||||
|     } else if (t->token_type == EXPR_TOKEN_STR) { |     } else if (t->token_type == EXPR_TOKEN_STR && t->str.len < sizeof(buf)) { | ||||||
|         char *str = malloc(t->str.len + 1); |         memcpy(buf, t->str.start, t->str.len); | ||||||
|         memcpy(str, t->str.start, t->str.len); |         buf[t->str.len] = '\0'; | ||||||
|         str[t->str.len] = '\0'; |  | ||||||
|         char *endptr; |         char *endptr; | ||||||
|         double val = strtod(str, &endptr); |         double val = strtod(buf, &endptr); | ||||||
|         free(str); |  | ||||||
|         return *endptr == '\0' ? val : 0; |         return *endptr == '\0' ? val : 0; | ||||||
|     } else { |     } else { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Conver obejct to true/false (0 or 1) */ | ||||||
|  | double exprTokenToBool(exprtoken *t) { | ||||||
|  |     if (t->token_type == EXPR_TOKEN_NUM) { | ||||||
|  |         return t->num != 0; | ||||||
|  |     } else if (t->token_type == EXPR_TOKEN_STR && t->str.len == 0) { | ||||||
|  |         return 0; // Empty string are false, like in Javascript.
 | ||||||
|  |     } else { | ||||||
|  |         return 1; // Every non numerical type is true.
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Compare two tokens. Returns true if they are equal. */ | /* Compare two tokens. Returns true if they are equal. */ | ||||||
| int exprTokensEqual(exprtoken *a, exprtoken *b) { | int exprTokensEqual(exprtoken *a, exprtoken *b) { | ||||||
|     // If both are strings, do string comparison.
 |     // If both are strings, do string comparison.
 | ||||||
|  | @ -587,9 +606,9 @@ int exprTokensEqual(exprtoken *a, exprtoken *b) { | ||||||
| 
 | 
 | ||||||
| /* Execute the compiled expression program. Returns 1 if the final stack value
 | /* Execute the compiled expression program. Returns 1 if the final stack value
 | ||||||
|  * evaluates to true, 0 otherwise. Also returns 0 if any selector callback fails. */ |  * evaluates to true, 0 otherwise. Also returns 0 if any selector callback fails. */ | ||||||
| int exprRun(exprstate *es, int (*get_attrib_callback)(void *privdata, char *attrname, size_t attrnamelen, exprtoken *token), void *privdata) { | int exprRun(exprstate *es, char *json, size_t json_len) { | ||||||
|     // Reset the values stack.
 |     exprStackReset(&es->values_stack); | ||||||
|     es->values_stack.numitems = 0; |     cJSON *parsed_json = NULL; | ||||||
| 
 | 
 | ||||||
|     // Execute each instruction in the program.
 |     // Execute each instruction in the program.
 | ||||||
|     for (int i = 0; i < es->program.numitems; i++) { |     for (int i = 0; i < es->program.numitems; i++) { | ||||||
|  | @ -598,14 +617,17 @@ int exprRun(exprstate *es, int (*get_attrib_callback)(void *privdata, char *attr | ||||||
|         // Handle selectors by calling the callback.
 |         // Handle selectors by calling the callback.
 | ||||||
|         if (t->token_type == EXPR_TOKEN_SELECTOR) { |         if (t->token_type == EXPR_TOKEN_SELECTOR) { | ||||||
|             exprtoken *result = malloc(sizeof(exprtoken)); |             exprtoken *result = malloc(sizeof(exprtoken)); | ||||||
|             if (result != NULL && |             if (result != NULL && json != NULL) { | ||||||
|                 get_attrib_callback(privdata, t->str.start, t->str.len, result)) |                 if (parsed_json == NULL) | ||||||
|             { |                     parsed_json = cJSON_ParseWithLength(json,json_len); | ||||||
|                 exprStackPush(&es->values_stack, result); |                 if (parsed_json) { | ||||||
|                 continue; |                     exprStackPush(&es->values_stack, result); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             exprFreeToken(result); |             exprFreeToken(result); | ||||||
|             return 0;  // Selector not found, expression fails.
 |             if (parsed_json) cJSON_Delete(parsed_json); | ||||||
|  |             return 0;  // Selector not found or OOM: expression fails.
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Push non-operator values directly onto the stack.
 |         // Push non-operator values directly onto the stack.
 | ||||||
|  | @ -629,7 +651,7 @@ int exprRun(exprstate *es, int (*get_attrib_callback)(void *privdata, char *attr | ||||||
| 
 | 
 | ||||||
|         switch(t->opcode) { |         switch(t->opcode) { | ||||||
|         case EXPR_OP_NOT: |         case EXPR_OP_NOT: | ||||||
|             result->num = exprTokenToNum(b) == 0 ? 1 : 0; |             result->num = exprTokenToBool(b) == 0 ? 1 : 0; | ||||||
|             break; |             break; | ||||||
|         case EXPR_OP_POW: { |         case EXPR_OP_POW: { | ||||||
|             double base = exprTokenToNum(a); |             double base = exprTokenToNum(a); | ||||||
|  | @ -688,11 +710,11 @@ int exprRun(exprstate *es, int (*get_attrib_callback)(void *privdata, char *attr | ||||||
|         } |         } | ||||||
|         case EXPR_OP_AND: |         case EXPR_OP_AND: | ||||||
|             result->num = |             result->num = | ||||||
|                 exprTokenToNum(a) != 0 && exprTokenToNum(b) != 0 ? 1 : 0; |                 exprTokenToBool(a) != 0 && exprTokenToBool(b) != 0 ? 1 : 0; | ||||||
|             break; |             break; | ||||||
|         case EXPR_OP_OR: |         case EXPR_OP_OR: | ||||||
|             result->num = |             result->num = | ||||||
|                 exprTokenToNum(a) != 0 || exprTokenToNum(b) != 0 ? 1 : 0; |                 exprTokenToBool(a) != 0 || exprTokenToBool(b) != 0 ? 1 : 0; | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             // Do nothing: we don't want runtime errors.
 |             // Do nothing: we don't want runtime errors.
 | ||||||
|  | @ -705,19 +727,14 @@ int exprRun(exprstate *es, int (*get_attrib_callback)(void *privdata, char *attr | ||||||
|         exprStackPush(&es->values_stack, result); |         exprStackPush(&es->values_stack, result); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (parsed_json) cJSON_Delete(parsed_json); | ||||||
|  | 
 | ||||||
|     // Get final result from stack.
 |     // Get final result from stack.
 | ||||||
|     exprtoken *final = exprStackPop(&es->values_stack); |     exprtoken *final = exprStackPop(&es->values_stack); | ||||||
|     if (final == NULL) return 0; |     if (final == NULL) return 0; | ||||||
| 
 | 
 | ||||||
|     // Convert result to boolean.
 |     // Convert result to boolean.
 | ||||||
|     int retval; |     int retval = exprTokenToBool(final); | ||||||
|     if (final->token_type == EXPR_TOKEN_NUM) { |  | ||||||
|         printf("FINAL %f\n", final->num); |  | ||||||
|         retval = final->num != 0; |  | ||||||
|     } else { |  | ||||||
|         retval = 1;  // Non-numeric types evaluate to true.
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     exprFreeToken(final); |     exprFreeToken(final); | ||||||
|     return retval; |     return retval; | ||||||
| } | } | ||||||
|  | @ -765,7 +782,9 @@ void exprPrintStack(exprstack *stack, const char *name) { | ||||||
| 
 | 
 | ||||||
| int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||||||
|     char *testexpr = "(5+2)*3 and 'foo'"; |     char *testexpr = "(5+2)*3 and 'foo'"; | ||||||
|  |     char *testjson = "{\"year\": 1984, \"name\": \"The Matrix\"}"; | ||||||
|     if (argc >= 2) testexpr = argv[1]; |     if (argc >= 2) testexpr = argv[1]; | ||||||
|  |     if (argc >= 3) testjson = argv[2]; | ||||||
| 
 | 
 | ||||||
|     printf("Compiling expression: %s\n", testexpr); |     printf("Compiling expression: %s\n", testexpr); | ||||||
| 
 | 
 | ||||||
|  | @ -778,8 +797,9 @@ int main(int argc, char **argv) { | ||||||
| 
 | 
 | ||||||
|     exprPrintStack(&es->tokens, "Tokens"); |     exprPrintStack(&es->tokens, "Tokens"); | ||||||
|     exprPrintStack(&es->program, "Program"); |     exprPrintStack(&es->program, "Program"); | ||||||
|     int result = exprRun(es,NULL,NULL); |     printf("Running against object: %s\n", testjson); | ||||||
|     printf("Result: %d\n", result); |     int result = exprRun(es,testjson,strlen(testjson)); | ||||||
|  |     printf("Result: %s\n", result ? "True" : "False"); | ||||||
|     exprFree(es); |     exprFree(es); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue