>From Peeyush Gupta <[email protected]>: Peeyush Gupta has uploaded this change for review. ( https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/20800?usp=email )
Change subject: DO NOT MERGE: curl function (AI written) ...................................................................... DO NOT MERGE: curl function (AI written) Change-Id: I346e01f1e05a2bb6ce7aef2c2f6393844eed3b2f --- M asterixdb/asterix-app/src/test/resources/runtimets/only_sqlpp.xml A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.1.query.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.2.query.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.3.query.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.4.query.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.1.regex A asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.2.regex A asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.3.regex A asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.4.regex M asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml M asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java A asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CurlDescriptor.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java 13 files changed, 466 insertions(+), 4 deletions(-) git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb refs/changes/00/20800/1 diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/only_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/only_sqlpp.xml index 334dd52..740b298 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/only_sqlpp.xml +++ b/asterixdb/asterix-app/src/test/resources/runtimets/only_sqlpp.xml @@ -19,5 +19,10 @@ !--> <test-suite xmlns="urn:xml.testframework.asterix.apache.org" ResultOffsetPath="results" QueryOffsetPath="queries_sqlpp" QueryFileExtension=".sqlpp"> <test-group name="failed"> + <test-case FilePath="misc"> + <compilation-unit name="curl"> + <output-dir compare="Text">curl</output-dir> + </compilation-unit> + </test-case> </test-group> </test-suite> diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.1.query.sqlpp new file mode 100644 index 0000000..bc5c555 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.1.query.sqlpp @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Description: Test curl function - Basic GET request + * Expected Res: Success - returns cluster info + */ + +curl("http://localhost:19002/admin/cluster"); diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.2.query.sqlpp new file mode 100644 index 0000000..bdfc59b --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.2.query.sqlpp @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Description: Test curl function with explicit GET request option + * Expected Res: Success - returns cluster info + */ + +curl("http://localhost:19002/admin/cluster", {"request": "GET"}); diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.3.query.sqlpp new file mode 100644 index 0000000..527abbf --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.3.query.sqlpp @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Description: Test curl function with custom headers + * Expected Res: Success - returns cluster info with custom header set + */ + +curl("http://localhost:19002/admin/cluster", { + "request": "GET", + "header": "Accept: application/json" +}); diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.4.query.sqlpp new file mode 100644 index 0000000..963c5d5 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/curl/curl.4.query.sqlpp @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Description: Test curl function with timeout settings + * Expected Res: Success - returns cluster info with custom timeout + */ + +curl("http://localhost:19002/admin/cluster", { + "request": "GET", + "connect-timeout": 3000, + "max-time": 5000 +}); diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.1.regex b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.1.regex new file mode 100644 index 0000000..ab6a155 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.1.regex @@ -0,0 +1 @@ +/.*/ \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.2.regex b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.2.regex new file mode 100644 index 0000000..5c30942 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.2.regex @@ -0,0 +1 @@ +/.*/ diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.3.regex b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.3.regex new file mode 100644 index 0000000..5c30942 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.3.regex @@ -0,0 +1 @@ +/.*/ diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.4.regex b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.4.regex new file mode 100644 index 0000000..5c30942 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/curl/curl.4.regex @@ -0,0 +1 @@ +/.*/ diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml index 5e6c654..966f3f6 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml +++ b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml @@ -7190,6 +7190,11 @@ </test-group> <test-group name="misc"> <test-case FilePath="misc"> + <compilation-unit name="curl"> + <output-dir compare="Text">curl</output-dir> + </compilation-unit> + </test-case> + <test-case FilePath="misc"> <compilation-unit name="big_in_list/000"> <output-dir compare="Text">big_in_list/000</output-dir> </compilation-unit> diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java index 51ad36b..295479a 100644 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java @@ -407,6 +407,8 @@ public static final FunctionIdentifier STRING_LPAD = FunctionConstants.newAsterix("lpad", 3); public static final FunctionIdentifier STRING_RPAD = FunctionConstants.newAsterix("rpad", 3); + public static final FunctionIdentifier CURL = FunctionConstants.newAsterix("curl", FunctionIdentifier.VARARGS); + public static final FunctionIdentifier DATASET = FunctionConstants.newAsterix("dataset", FunctionIdentifier.VARARGS); // 1, 2 or 3 public static final FunctionIdentifier FEED_COLLECT = FunctionConstants.newAsterix("feed-collect", 7); @@ -1047,7 +1049,7 @@ public static final FunctionIdentifier RANDOM = FunctionConstants.newAsterix("random", 0); public static final FunctionIdentifier RANDOM_WITH_SEED = FunctionConstants.newAsterix("random", 1); - //Geo + // Geo public static final FunctionIdentifier ST_AREA = FunctionConstants.newAsterix("st-area", 1); public static final FunctionIdentifier ST_MAKE_POINT = FunctionConstants.newAsterix("st-make-point", 2); public static final FunctionIdentifier ST_MAKE_POINT3D = FunctionConstants.newAsterix("st-make-point", 3); @@ -1556,6 +1558,8 @@ addFunction(STRING_LPAD, AStringTypeComputer.INSTANCE_NULLABLE, true); addFunction(STRING_RPAD, AStringTypeComputer.INSTANCE_NULLABLE, true); + addFunction(CURL, AStringTypeComputer.INSTANCE_NULLABLE, true); + addPrivateFunction(ORDERED_LIST_CONSTRUCTOR, OrderedListConstructorTypeComputer.INSTANCE, true); addFunction(POINT_CONSTRUCTOR, APointTypeComputer.INSTANCE, true); addFunction(POINT3D_CONSTRUCTOR, APoint3DTypeComputer.INSTANCE, true); @@ -1937,7 +1941,7 @@ addPrivateFunction(REFERENCE_TILE, AInt32TypeComputer.INSTANCE, true); addPrivateFunction(GET_INTERSECTION, ARectangleTypeComputer.INSTANCE, true); - //geo functions + // geo functions addFunction(ST_AREA, ADoubleTypeComputer.INSTANCE, true); addFunction(ST_MAKE_POINT, AGeometryTypeComputer.INSTANCE, true); addFunction(ST_MAKE_POINT3D, AGeometryTypeComputer.INSTANCE, true); @@ -2213,7 +2217,7 @@ } static { - // Aggregate functions + // Aggregate functions // AVG @@ -3015,7 +3019,7 @@ } public static BuiltinFunctionInfo resolveBuiltinFunction(String name, int arity) { - //TODO:optimize + // TODO:optimize BuiltinFunctionInfo finfo; finfo = getBuiltinFunctionInfo(FunctionConstants.newAsterix(name, arity)); if (finfo != null) { diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CurlDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CurlDescriptor.java new file mode 100644 index 0000000..64d0c84 --- /dev/null +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CurlDescriptor.java @@ -0,0 +1,333 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.asterix.runtime.evaluators.functions; + +import java.io.BufferedReader; +import java.io.DataOutput; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; + +import org.apache.asterix.formats.nontagged.SerializerDeserializerProvider; +import org.apache.asterix.om.base.AMissing; +import org.apache.asterix.om.base.AString; +import org.apache.asterix.om.functions.BuiltinFunctions; +import org.apache.asterix.om.functions.IFunctionDescriptor; +import org.apache.asterix.om.functions.IFunctionDescriptorFactory; +import org.apache.asterix.om.pointables.ARecordVisitablePointable; +import org.apache.asterix.om.pointables.base.DefaultOpenFieldType; +import org.apache.asterix.om.pointables.base.IVisitablePointable; +import org.apache.asterix.om.types.ATypeTag; +import org.apache.asterix.om.types.BuiltinType; +import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory; +import org.apache.hyracks.api.context.IEvaluatorContext; +import org.apache.hyracks.api.dataflow.value.ISerializerDeserializer; +import org.apache.hyracks.api.exceptions.HyracksDataException; +import org.apache.hyracks.data.std.api.IPointable; +import org.apache.hyracks.data.std.primitive.UTF8StringPointable; +import org.apache.hyracks.data.std.primitive.VoidPointable; +import org.apache.hyracks.data.std.util.ArrayBackedValueStorage; +import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; + +/** + * CURL function implementation for AsterixDB. + * Syntax: curl(url [, options]) + * Options (record): + * - request: HTTP method (GET, POST, PUT, DELETE, etc.) + * - header: Header string (e.g., "Content-Type: application/json") + * - data: Request body (string) + * - user: Basic auth credentials ("username:password") + * - connect-timeout: Connection timeout in milliseconds + * - max-time: Max total time in milliseconds + */ +public class CurlDescriptor extends AbstractScalarFunctionDynamicDescriptor { + private static final long serialVersionUID = 1L; + + public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() { + @Override + public IFunctionDescriptor createFunctionDescriptor() { + return new CurlDescriptor(); + } + }; + + @Override + public FunctionIdentifier getIdentifier() { + return BuiltinFunctions.CURL; + } + + @Override + public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) { + return new IScalarEvaluatorFactory() { + private static final long serialVersionUID = 1L; + + @Override + public IScalarEvaluator createScalarEvaluator(final IEvaluatorContext ctx) throws HyracksDataException { + return new CurlEvaluator(args, ctx); + } + }; + } + + private class CurlEvaluator implements IScalarEvaluator { + private final ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage(); + private final DataOutput out = resultStorage.getDataOutput(); + private final IScalarEvaluator urlEval; + private final IScalarEvaluator optionsEval; + private final IPointable urlArg = new VoidPointable(); + private final IPointable optionsArg = new VoidPointable(); + private final UTF8StringPointable stringPointable = new UTF8StringPointable(); + private final ARecordVisitablePointable optionsRecord = + new ARecordVisitablePointable(DefaultOpenFieldType.NESTED_OPEN_RECORD_TYPE); + + @SuppressWarnings("rawtypes") + private final ISerializerDeserializer stringSerde = + SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ASTRING); + @SuppressWarnings("rawtypes") + private final ISerializerDeserializer missingSerde = + SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.AMISSING); + + // Default configuration + private static final int DEFAULT_CONNECT_TIMEOUT = 5000; + private static final int DEFAULT_READ_TIMEOUT = 10000; + + public CurlEvaluator(IScalarEvaluatorFactory[] args, IEvaluatorContext ctx) throws HyracksDataException { + this.urlEval = args[0].createScalarEvaluator(ctx); + if (args.length > 1) { + this.optionsEval = args[1].createScalarEvaluator(ctx); + } else { + this.optionsEval = null; + } + } + + @SuppressWarnings("unchecked") + @Override + public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException { + resultStorage.reset(); + urlEval.evaluate(tuple, urlArg); + + byte[] bytes = urlArg.getByteArray(); + int offset = urlArg.getStartOffset(); + int len = urlArg.getLength(); + + if (bytes[offset] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) { + missingSerde.serialize(AMissing.MISSING, out); + result.set(resultStorage); + return; + } + + stringPointable.set(bytes, offset + 1, len - 1); + String urlString = stringPointable.toString(); + + // Parse options - defaults + String method = "GET"; + String data = null; + String user = null; + String header = null; + int connectTimeout = DEFAULT_CONNECT_TIMEOUT; + int readTimeout = DEFAULT_READ_TIMEOUT; + + if (optionsEval != null) { + optionsEval.evaluate(tuple, optionsArg); + byte[] optBytes = optionsArg.getByteArray(); + int optOffset = optionsArg.getStartOffset(); + + if (optBytes[optOffset] == ATypeTag.SERIALIZED_RECORD_TYPE_TAG) { + try { + optionsRecord.set(optionsArg); + List<IVisitablePointable> fieldNames = optionsRecord.getFieldNames(); + List<IVisitablePointable> fieldValues = optionsRecord.getFieldValues(); + + for (int i = 0; i < fieldNames.size(); i++) { + String fieldName = extractString(fieldNames.get(i)); + if (fieldName == null) { + continue; + } + + IVisitablePointable fieldValue = fieldValues.get(i); + String strValue = extractString(fieldValue); + + switch (fieldName) { + case "request": + if (strValue != null) { + method = strValue; + } + break; + case "data": + data = strValue; + break; + case "user": + user = strValue; + break; + case "header": + header = strValue; + break; + case "connect-timeout": + connectTimeout = extractInt(fieldValue, DEFAULT_CONNECT_TIMEOUT); + break; + case "max-time": + readTimeout = extractInt(fieldValue, DEFAULT_READ_TIMEOUT); + break; + } + } + } catch (Exception e) { + // If options parsing fails, continue with defaults + } + } + } + + try { + URL url = new URL(urlString); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod(method.toUpperCase()); + con.setConnectTimeout(connectTimeout); + con.setReadTimeout(readTimeout); + + // Set Basic Auth header if user is provided + if (user != null && !user.isEmpty()) { + String encoded = Base64.getEncoder().encodeToString(user.getBytes(StandardCharsets.UTF_8)); + con.setRequestProperty("Authorization", "Basic " + encoded); + } + + // Set custom header + if (header != null && !header.isEmpty()) { + int colonIdx = header.indexOf(':'); + if (colonIdx > 0) { + String headerName = header.substring(0, colonIdx).trim(); + String headerValue = header.substring(colonIdx + 1).trim(); + con.setRequestProperty(headerName, headerValue); + } + } + + // Send data for POST/PUT + if (data != null && !data.isEmpty()) { + con.setDoOutput(true); + try (OutputStream os = con.getOutputStream()) { + byte[] input = data.getBytes(StandardCharsets.UTF_8); + os.write(input, 0, input.length); + } + } + + int status = con.getResponseCode(); + + // Read response + InputStream is = (status >= 200 && status < 300) ? con.getInputStream() : con.getErrorStream(); + if (is == null) { + is = con.getInputStream(); + } + + StringBuilder content = new StringBuilder(); + if (is != null) { + try (BufferedReader in = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + String inputLine; + while ((inputLine = in.readLine()) != null) { + content.append(inputLine); + } + } + } + con.disconnect(); + + stringSerde.serialize(new AString(content.toString()), out); + + } catch (Exception e) { + missingSerde.serialize(AMissing.MISSING, out); + } + + result.set(resultStorage); + } + + /** + * Extract a UTF8 string from an IVisitablePointable. + * The pointable contains a type tag byte followed by UTF8 string data. + */ + private String extractString(IVisitablePointable pointable) { + byte[] bytes = pointable.getByteArray(); + int offset = pointable.getStartOffset(); + int len = pointable.getLength(); + + if (len <= 0) { + return null; + } + + // Check if it's a string type + byte typeTag = bytes[offset]; + if (typeTag == ATypeTag.SERIALIZED_STRING_TYPE_TAG) { + // Skip type tag, parse UTF8 string + if (len > 1) { + UTF8StringPointable sp = new UTF8StringPointable(); + sp.set(bytes, offset + 1, len - 1); + return sp.toString(); + } + return ""; + } + + // For field names, they don't have a type tag - they're raw UTF8 + // Check if first byte looks like UTF8 length marker + int firstByte = bytes[offset] & 0xFF; + if (firstByte < 128 || (firstByte >= 0xC0 && firstByte <= 0xFD)) { + // Looks like UTF8 string without type tag + UTF8StringPointable sp = new UTF8StringPointable(); + sp.set(bytes, offset, len); + return sp.toString(); + } + + return null; + } + + /** + * Extract an integer from an IVisitablePointable. + */ + private int extractInt(IVisitablePointable pointable, int defaultValue) { + byte[] bytes = pointable.getByteArray(); + int offset = pointable.getStartOffset(); + int len = pointable.getLength(); + + if (len <= 0) { + return defaultValue; + } + + byte typeTag = bytes[offset]; + try { + if (typeTag == ATypeTag.SERIALIZED_INT32_TYPE_TAG && len >= 5) { + return ((bytes[offset + 1] & 0xFF) << 24) | ((bytes[offset + 2] & 0xFF) << 16) + | ((bytes[offset + 3] & 0xFF) << 8) | (bytes[offset + 4] & 0xFF); + } else if (typeTag == ATypeTag.SERIALIZED_INT64_TYPE_TAG && len >= 9) { + long val = 0; + for (int i = 0; i < 8; i++) { + val = (val << 8) | (bytes[offset + 1 + i] & 0xFF); + } + return (int) val; + } else if (typeTag == ATypeTag.SERIALIZED_INT8_TYPE_TAG && len >= 2) { + return bytes[offset + 1]; + } else if (typeTag == ATypeTag.SERIALIZED_INT16_TYPE_TAG && len >= 3) { + return ((bytes[offset + 1] & 0xFF) << 8) | (bytes[offset + 2] & 0xFF); + } + } catch (Exception e) { + // Fall through to default + } + return defaultValue; + } + } +} diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java index 106b165..cd21b6d 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java @@ -382,6 +382,7 @@ import org.apache.asterix.runtime.evaluators.functions.CreateQueryUIDDescriptor; import org.apache.asterix.runtime.evaluators.functions.CreateRectangleDescriptor; import org.apache.asterix.runtime.evaluators.functions.CreateUUIDDescriptor; +import org.apache.asterix.runtime.evaluators.functions.CurlDescriptor; import org.apache.asterix.runtime.evaluators.functions.DecodeDataverseNameDescriptor; import org.apache.asterix.runtime.evaluators.functions.DeepEqualityDescriptor; import org.apache.asterix.runtime.evaluators.functions.FromBaseDescriptor; @@ -1390,6 +1391,9 @@ fc.add(CheckListDescriptor.FACTORY); fc.add(CheckIntegerDescriptor.FACTORY); + // Network functions + fc.add(CurlDescriptor.FACTORY); + ServiceLoader.load(IFunctionRegistrant.class).iterator().forEachRemaining(c -> c.register(fc)); return fc; } -- To view, visit https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/20800?usp=email To unsubscribe, or for help writing mail filters, visit https://asterix-gerrit.ics.uci.edu/settings?usp=email Gerrit-MessageType: newchange Gerrit-Project: asterixdb Gerrit-Branch: master Gerrit-Change-Id: I346e01f1e05a2bb6ce7aef2c2f6393844eed3b2f Gerrit-Change-Number: 20800 Gerrit-PatchSet: 1 Gerrit-Owner: Peeyush Gupta <[email protected]>
