|
| 1 | +SET SEARCH_PATH to pgstac, public; |
| 2 | +set check_function_bodies = off; |
| 3 | + |
| 4 | +INSERT INTO cql2_ops (op, template, types) VALUES |
| 5 | + ('eq', '%s = %s', NULL), |
| 6 | + ('lt', '%s < %s', NULL), |
| 7 | + ('lte', '%s <= %s', NULL), |
| 8 | + ('gt', '%s > %s', NULL), |
| 9 | + ('gte', '%s >= %s', NULL), |
| 10 | + ('le', '%s <= %s', NULL), |
| 11 | + ('ge', '%s >= %s', NULL), |
| 12 | + ('=', '%s = %s', NULL), |
| 13 | + ('<', '%s < %s', NULL), |
| 14 | + ('<=', '%s <= %s', NULL), |
| 15 | + ('>', '%s > %s', NULL), |
| 16 | + ('>=', '%s >= %s', NULL), |
| 17 | + ('like', '%s LIKE %s', NULL), |
| 18 | + ('ilike', '%s ILIKE %s', NULL), |
| 19 | + ('+', '%s + %s', NULL), |
| 20 | + ('-', '%s - %s', NULL), |
| 21 | + ('*', '%s * %s', NULL), |
| 22 | + ('/', '%s / %s', NULL), |
| 23 | + ('in', '%s = ANY (%s)', NULL), |
| 24 | + ('not', 'NOT (%s)', NULL), |
| 25 | + ('between', '%s BETWEEN %s AND %s', NULL), |
| 26 | + ('isnull', '%s IS NULL', NULL), |
| 27 | + ('upper', 'upper(%s)', NULL), |
| 28 | + ('lower', 'lower(%s)', NULL) |
| 29 | +ON CONFLICT (op) DO UPDATE |
| 30 | + SET |
| 31 | + template = EXCLUDED.template |
| 32 | +; |
| 33 | + |
| 34 | + |
| 35 | +CREATE OR REPLACE FUNCTION pgstac.cql2_query(j jsonb, wrapper text DEFAULT NULL::text) |
| 36 | + RETURNS text |
| 37 | + LANGUAGE plpgsql |
| 38 | + STABLE |
| 39 | +AS $function$ |
| 40 | +#variable_conflict use_variable |
| 41 | +DECLARE |
| 42 | + args jsonb := j->'args'; |
| 43 | + arg jsonb; |
| 44 | + op text := lower(j->>'op'); |
| 45 | + cql2op RECORD; |
| 46 | + literal text; |
| 47 | + _wrapper text; |
| 48 | +BEGIN |
| 49 | + IF j IS NULL OR (op IS NOT NULL AND args IS NULL) THEN |
| 50 | + RETURN NULL; |
| 51 | + END IF; |
| 52 | + RAISE NOTICE 'CQL2_QUERY: %', j; |
| 53 | + IF j ? 'filter' THEN |
| 54 | + RETURN cql2_query(j->'filter'); |
| 55 | + END IF; |
| 56 | + |
| 57 | + IF j ? 'upper' THEN |
| 58 | + RETURN cql2_query(jsonb_build_object('op', 'upper', 'args', j->'upper')); |
| 59 | + END IF; |
| 60 | + |
| 61 | + IF j ? 'lower' THEN |
| 62 | + RETURN cql2_query(jsonb_build_object('op', 'lower', 'args', j->'lower')); |
| 63 | + END IF; |
| 64 | + |
| 65 | + -- Temporal Query |
| 66 | + IF op ilike 't_%' or op = 'anyinteracts' THEN |
| 67 | + RETURN temporal_op_query(op, args); |
| 68 | + END IF; |
| 69 | + |
| 70 | + -- If property is a timestamp convert it to text to use with |
| 71 | + -- general operators |
| 72 | + IF j ? 'timestamp' THEN |
| 73 | + RETURN format('%L::timestamptz', to_tstz(j->'timestamp')); |
| 74 | + END IF; |
| 75 | + IF j ? 'interval' THEN |
| 76 | + RAISE EXCEPTION 'Please use temporal operators when using intervals.'; |
| 77 | + RETURN NONE; |
| 78 | + END IF; |
| 79 | + |
| 80 | + -- Spatial Query |
| 81 | + IF op ilike 's_%' or op = 'intersects' THEN |
| 82 | + RETURN spatial_op_query(op, args); |
| 83 | + END IF; |
| 84 | + |
| 85 | + |
| 86 | + IF op = 'in' THEN |
| 87 | + RETURN format( |
| 88 | + '%s = ANY (%L)', |
| 89 | + cql2_query(args->0), |
| 90 | + to_text_array(args->1) |
| 91 | + ); |
| 92 | + END IF; |
| 93 | + |
| 94 | + |
| 95 | + |
| 96 | + IF op = 'between' THEN |
| 97 | + args = jsonb_build_array( |
| 98 | + args->0, |
| 99 | + args->1->0, |
| 100 | + args->1->1 |
| 101 | + ); |
| 102 | + END IF; |
| 103 | + |
| 104 | + -- Make sure that args is an array and run cql2_query on |
| 105 | + -- each element of the array |
| 106 | + RAISE NOTICE 'ARGS PRE: %', args; |
| 107 | + IF j ? 'args' THEN |
| 108 | + IF jsonb_typeof(args) != 'array' THEN |
| 109 | + args := jsonb_build_array(args); |
| 110 | + END IF; |
| 111 | + |
| 112 | + IF jsonb_path_exists(args, '$[*] ? (@.property == "id" || @.property == "datetime" || @.property == "end_datetime" || @.property == "collection")') THEN |
| 113 | + wrapper := NULL; |
| 114 | + ELSE |
| 115 | + -- if any of the arguments are a property, try to get the property_wrapper |
| 116 | + FOR arg IN SELECT jsonb_path_query(args, '$[*] ? (@.property != null)') LOOP |
| 117 | + RAISE NOTICE 'Arg: %', arg; |
| 118 | + SELECT property_wrapper INTO wrapper |
| 119 | + FROM queryables |
| 120 | + WHERE name=(arg->>'property') |
| 121 | + LIMIT 1; |
| 122 | + RAISE NOTICE 'Property: %, Wrapper: %', arg, wrapper; |
| 123 | + IF wrapper IS NOT NULL THEN |
| 124 | + EXIT; |
| 125 | + END IF; |
| 126 | + END LOOP; |
| 127 | + |
| 128 | + -- if the property was not in queryables, see if any args were numbers |
| 129 | + IF |
| 130 | + wrapper IS NULL |
| 131 | + AND jsonb_path_exists(args, '$[*] ? (@.type()=="number")') |
| 132 | + THEN |
| 133 | + wrapper := 'to_float'; |
| 134 | + END IF; |
| 135 | + wrapper := coalesce(wrapper, 'to_text'); |
| 136 | + END IF; |
| 137 | + |
| 138 | + SELECT jsonb_agg(cql2_query(a, wrapper)) |
| 139 | + INTO args |
| 140 | + FROM jsonb_array_elements(args) a; |
| 141 | + END IF; |
| 142 | + RAISE NOTICE 'ARGS: %', args; |
| 143 | + |
| 144 | + IF op IN ('and', 'or') THEN |
| 145 | + RETURN |
| 146 | + format( |
| 147 | + '(%s)', |
| 148 | + array_to_string(to_text_array(args), format(' %s ', upper(op))) |
| 149 | + ); |
| 150 | + END IF; |
| 151 | + |
| 152 | + -- Look up template from cql2_ops |
| 153 | + IF j ? 'op' THEN |
| 154 | + SELECT * INTO cql2op FROM cql2_ops WHERE cql2_ops.op ilike op; |
| 155 | + IF FOUND THEN |
| 156 | + -- If specific index set in queryables for a property cast other arguments to that type |
| 157 | + RETURN format( |
| 158 | + cql2op.template, |
| 159 | + VARIADIC (to_text_array(args)) |
| 160 | + ); |
| 161 | + ELSE |
| 162 | + RAISE EXCEPTION 'Operator % Not Supported.', op; |
| 163 | + END IF; |
| 164 | + END IF; |
| 165 | + |
| 166 | + |
| 167 | + IF wrapper IS NOT NULL THEN |
| 168 | + RAISE NOTICE 'Wrapping % with %', j, wrapper; |
| 169 | + IF j ? 'property' THEN |
| 170 | + RETURN format('%I(%s)', wrapper, (queryable(j->>'property')).path); |
| 171 | + ELSE |
| 172 | + RETURN format('%I(%L)', wrapper, j); |
| 173 | + END IF; |
| 174 | + ELSIF j ? 'property' THEN |
| 175 | + RETURN quote_ident(j->>'property'); |
| 176 | + END IF; |
| 177 | + |
| 178 | + RETURN quote_literal(to_text(j)); |
| 179 | +END; |
| 180 | +$function$ |
| 181 | +; |
| 182 | + |
| 183 | + |
| 184 | + |
| 185 | +SELECT set_version('0.6.4'); |
0 commit comments