Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions basex-core/src/main/java/org/basex/query/QueryParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -271,13 +271,6 @@ private void finish(final MainModule mm) throws QueryException {
* @throws QueryException query exception
*/
private void check(final MainModule main) throws QueryException {
// add record constructor functions for built-in record types
for(final RecordType rt : Records.BUILT_IN.values()) {
if(qc.functions.get(sc, rt.name(), rt.minFields()) == null) {
declareRecordConstructor(rt, info());
}
}

// resolve function calls
qc.functions.resolve();
// resolve variable references
Expand Down
28 changes: 28 additions & 0 deletions basex-core/src/main/java/org/basex/query/func/Function.java
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,25 @@ public enum Function implements AFunction {
ZERO_OR_ONE(FnZeroOrOne::new, "zero-or-one(input)",
params(ITEM_ZM), ITEM_ZO),

// Predefined record constructor functions

/** XQuery function. */
DIVIDED_DECIMALS_RECORD(Records.DIVIDED_DECIMALS.get()),
/** XQuery function. */
INFER_ENCODING_RECORD(Records.INFER_ENCODING.get()),
/** XQuery function. */
LOAD_XQUERY_MODULE_RECORD(Records.LOAD_XQUERY_MODULE.get()),
/** XQuery function. */
MEMBER_RECORD(Records.MEMBER.get()),
/** XQuery function. */
PARSED_CSV_STRUCTURE_RECORD(Records.PARSED_CSV_STRUCTURE.get()),
/** XQuery function. */
RANDOM_NUMBER_GENERATOR_RECORD(Records.RANDOM_NUMBER_GENERATOR.get()),
/** XQuery function. */
SCHEMA_TYPE_RECORD(Records.SCHEMA_TYPE.get()),
/** XQuery function. */
URI_STRUCTURE_RECORD(Records.URI_STRUCTURE.get()),

// Map Module

/** XQuery function. */
Expand Down Expand Up @@ -2086,6 +2105,15 @@ EMPTY_SEQUENCE_Z, flag(UPD), USER_URI, Perm.ADMIN),
/** Function definition. */
private final FuncDefinition definition;

/**
* Constructs a function signature for a predefined record constructor from a RecordType.
* @param rt record type
*/
Function(final RecordType rt) {
this(rt.constructor(), rt.constructorDesc(), rt.constructorParams(), rt.seqType(),
EnumSet.noneOf(Flag.class), rt.name().uri());
}

/**
* Constructs a function signature; calls
* {@link #Function(Supplier, String, SeqType[], SeqType, EnumSet)}.
Expand Down
36 changes: 24 additions & 12 deletions basex-core/src/main/java/org/basex/query/func/Records.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.basex.query.func;

import org.basex.query.*;
import static org.basex.query.QueryText.*;

import org.basex.query.util.hash.*;
import org.basex.query.util.list.*;
import org.basex.query.value.item.*;
Expand All @@ -16,36 +17,46 @@
*/
public enum Records {
/** Record definition. */
DIVIDED_DECIMALS("divided-decimals",
DATETIME(FN_URI, "dateTime",
field("year", Types.INTEGER_O, true),
field("month", Types.INTEGER_O, true),
field("day", Types.INTEGER_O, true),
field("hours", Types.INTEGER_O, true),
field("minutes", Types.INTEGER_O, true),
field("seconds", Types.DECIMAL_O, true),
field("timezone", Types.DAY_TIME_DURATION_O, true)
),
/** Record definition. */
DIVIDED_DECIMALS(FN_URI, "divided-decimals",
field("quotient", Types.DECIMAL_O),
field("remainder", Types.DECIMAL_O)
),
/** Record definition. */
INFER_ENCODING("infer-encoding",
INFER_ENCODING(BIN_URI, "infer-encoding",
field("encoding", Types.STRING_O, false),
field("offset", Types.INTEGER_O, false)
),
/** Record definition. */
LOAD_XQUERY_MODULE("load-xquery-module",
field("variables", MapType.get(BasicType.QNAME, Types.ITEM_ZO).seqType()),
LOAD_XQUERY_MODULE(FN_URI, "load-xquery-module",
field("variables", MapType.get(BasicType.QNAME, Types.ITEM_ZM).seqType()),
field("functions", MapType.get(BasicType.QNAME,
MapType.get(BasicType.INTEGER, Types.FUNCTION_O).seqType()).seqType())),
/** Record definition. */
MEMBER("member",
MEMBER(ARRAY_URI, "member",
field("value", Types.ITEM_ZM)),
/** Record definition. */
PARSED_CSV_STRUCTURE("parsed-csv-structure",
PARSED_CSV_STRUCTURE(FN_URI, "parsed-csv-structure",
field("columns", Types.STRING_ZM),
field("column-index", MapType.get(BasicType.STRING, Types.INTEGER_O).seqType(Occ.ZERO_OR_ONE)),
field("rows", ArrayType.get(Types.STRING_O).seqType(Occ.ZERO_OR_MORE)),
field("get", FuncType.get(Types.STRING_O, Types.POSITIVE_INTEGER_O,
ChoiceItemType.get(Types.POSITIVE_INTEGER_O, Types.STRING_O).seqType()).seqType())),
/** Record definition. */
RANDOM_NUMBER_GENERATOR("random-number-generator"),
RANDOM_NUMBER_GENERATOR(FN_URI, "random-number-generator"),
/** Record definition. */
SCHEMA_TYPE("schema-type"),
SCHEMA_TYPE(FN_URI, "schema-type"),
/** Record definition. */
URI_STRUCTURE("uri-structure",
URI_STRUCTURE(FN_URI, "uri-structure",
field("uri", Types.STRING_ZO, true),
field("scheme", Types.STRING_ZO, true),
field("absolute", Types.BOOLEAN_ZO, true),
Expand Down Expand Up @@ -110,15 +121,16 @@ private record NamedRecordField(byte[] name, RecordField field) { }

/**
* Constructor.
* @param namespace namespace of record
* @param name name of record
* @param fields field declarations
*/
Records(final String name, final NamedRecordField... fields) {
Records(final byte[] namespace, final String name, final NamedRecordField... fields) {
final TokenObjectMap<RecordField> map = new TokenObjectMap<>(fields.length);
for(final NamedRecordField field : fields) {
map.put(field.name, field.field);
}
final QNm qnm = new QNm(name + "-record", QueryText.FN_URI);
final QNm qnm = new QNm(name + "-record", namespace);
type = new RecordType(map, qnm, AnnList.EMPTY);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,7 @@ public FNode parse(final IOContent source) throws QueryException {
if(doc != null) comment(doc, root);
for(final StaticVar sv : module.vars) root.add(variable(sv));
for(final StaticFunc sf : module.funcs) {
if(!NSGlobal.reserved(sf.name.uri())) {
root.add(function(sf.name, sf, sf.funcType(), sf.anns));
}
root.add(function(sf.name, sf, sf.funcType(), sf.anns));
}
options(module.options, root, true);
return root.finish();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ public FNode parse(final IOContent content) throws QueryException {
// functions
final FBuilder functions = element("functions");
for(final StaticFunc sf : module.funcs) {
if(NSGlobal.reserved(sf.name.uri())) continue;
final int al = sf.arity();
final byte[] name = sf.funcName().string();
final FuncType tp = sf.funcType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
import java.util.function.*;

import org.basex.query.*;
import org.basex.query.expr.*;
import org.basex.query.func.*;
import org.basex.query.util.hash.*;
import org.basex.query.util.list.*;
import org.basex.query.value.*;
import org.basex.query.value.item.*;
import org.basex.query.value.map.*;
import org.basex.util.*;
import org.basex.util.hash.*;

Expand Down Expand Up @@ -472,6 +475,83 @@ public RecordType getDeclaration(final QNmMap<RecordType> declaredRecordTypes)
return rt;
}

/**
* Returns constructor parameter types, one parameter per record field, in declaration order.
* @return parameter types
*/
public SeqType[] constructorParams() {
final int size = fields.size();
final SeqType[] params = new SeqType[size];
for(int i = 0; i < size; ++i) params[i] = fields.value(i + 1).seqType();
return params;
}

/**
* Returns the signature string of the constructor function, e.g. {@code name(a,b[,c])}, for use
* when registering it.
* @return signature string
*/
public String constructorDesc() {
if(name == null) throw Util.notExpected("only named record types have a constructor function");
final TokenBuilder tb = new TokenBuilder(name.local()).add('(');
final int max = fields.size();
final int min = minFields();
for(int i = 1; i <= max; ++i) {
if(i == min + 1) tb.add('[');
if(i > 1) tb.add(',');
tb.add(fields.key(i));
}
if(max > min) tb.add(']');
return tb.add(')').toString();
}

/**
* Returns a constructor function for this record type.
* @return constructor function
*/
public Supplier<? extends StandardFunc> constructor() {
return () -> new StandardFunc() {
@Override
public Expr opt(final CompileContext cc) throws QueryException {
return values(true, cc) ? cc.preEval(this) : this;
}

@Override
public Item item(final QueryContext qc, final InputInfo ii) throws QueryException {
final boolean opt = hasOptional();
final int fs = fields.size();
final Value[] values = opt ? null : new Value[fs];
final MapBuilder mb = opt ? new MapBuilder() : null;
final int el = exprs.length;
for(int i = 1; i <= fs; ++i) {
final RecordField rf = fields.value(i);
final Value value;
if(i <= el) {
value = arg(i - 1).value(qc);
final SeqType st = rf.seqType();
if(!st.instance(value)) throw typeError(value, st, ii);
} else {
final Expr expr = rf.expr();
value = expr == null ? null : expr.value(qc);
}
if(value != null) {
if (opt) mb.put(fields.key(i), value);
else values[i - 1] = value;
}
}
if(!opt) return new XQRecordMap(values, RecordType.this);
final XQMap map = mb.map();
map.type = RecordType.this;
return map;
}

@Override
public long structSize() {
return hasOptional() ? -1 : exprs.length;
}
};
}

/**
* An ordered pair of objects.
* @param o1 first object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private static void run(final FuncDefinition fd) {
if(t != 0) qu.append(", ");
if(in) {
// test arguments
if(fd.types[t].type == BasicType.STRING) {
if(fd.types[t].type == BasicType.STRING && t < 10) {
qu.append((char) (48 + t));
} else { // any type (skip test)
qu.append("'").append((char) (65 + t)).append("'");
Expand Down