From e9adca4defd32bc5036040820d32682ab6981e67 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 23 Aug 2023 22:24:08 +0200 Subject: [PATCH 001/298] webview --- .../org/usf/jquery/web/view/CalendarView.java | 68 +++++ .../org/usf/jquery/web/view/Chart2DView.java | 164 ++++++++++++ .../org/usf/jquery/web/view/PieChartView.java | 73 ++++++ .../usf/jquery/web/view/ResultWebView.java | 234 ++++++++++++++++++ .../org/usf/jquery/web/view/TableView.java | 73 ++++++ .../usf/jquery/web/view/TreemapChartView.java | 72 ++++++ .../usf/jquery/web/view/calendar.google.html | 7 + .../org/usf/jquery/web/view/chart.google.html | 7 + .../org/usf/jquery/web/view/pie.google.html | 5 + .../org/usf/jquery/web/view/table.google.html | 7 + .../usf/jquery/web/view/treemap.google.html | 6 + 11 files changed, 716 insertions(+) create mode 100644 src/main/java/org/usf/jquery/web/view/CalendarView.java create mode 100644 src/main/java/org/usf/jquery/web/view/Chart2DView.java create mode 100644 src/main/java/org/usf/jquery/web/view/PieChartView.java create mode 100644 src/main/java/org/usf/jquery/web/view/ResultWebView.java create mode 100644 src/main/java/org/usf/jquery/web/view/TableView.java create mode 100644 src/main/java/org/usf/jquery/web/view/TreemapChartView.java create mode 100644 src/main/java/org/usf/jquery/web/view/calendar.google.html create mode 100644 src/main/java/org/usf/jquery/web/view/chart.google.html create mode 100644 src/main/java/org/usf/jquery/web/view/pie.google.html create mode 100644 src/main/java/org/usf/jquery/web/view/table.google.html create mode 100644 src/main/java/org/usf/jquery/web/view/treemap.google.html diff --git a/src/main/java/org/usf/jquery/web/view/CalendarView.java b/src/main/java/org/usf/jquery/web/view/CalendarView.java new file mode 100644 index 00000000..5c287825 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/view/CalendarView.java @@ -0,0 +1,68 @@ +package org.usf.jquery.web.view; + +import static java.lang.System.currentTimeMillis; +import static java.lang.System.lineSeparator; +import static java.nio.file.Files.readString; +import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.web.view.ResultWebView.requireDateColumn; +import static org.usf.jquery.web.view.ResultWebView.requireNumberColumn; + +import java.io.IOException; +import java.io.Writer; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.sql.ResultSet; +import java.sql.SQLException; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * + * @author u$f + * + * @see calendar + * + */ +@Slf4j +@RequiredArgsConstructor +public final class CalendarView implements ResultWebView { + + private static final String COLS = "$columns"; + private static final String DATA = "$data"; + + private final Writer writer; + + public Void map(ResultSet rs) throws SQLException { + log.debug("mapping results..."); + var bg = currentTimeMillis(); + var rw = 0; + var xCol = requireDateColumn(rs.getMetaData()); + var yCol = requireNumberColumn(rs.getMetaData()); + var sb1 = new StringBuilder(100) + .append("[") + .append(quote(xCol.getValue().typeName())).append(",") + .append(quote(xCol.getKey())).append("],") + .append("[") + .append(quote(yCol.getValue().typeName())).append(",") + .append(quote(yCol.getKey())).append("]"); + var sb2 = new StringBuilder(1000); + while(rs.next()) { + sb2.append("[") + .append(xCol.getValue().format(rs.getObject(xCol.getKey()))).append(",") + .append(yCol.getValue().format(rs.getObject(yCol.getKey()))).append("],"); + } + sb2.deleteCharAt(sb2.length()-1); //dirty but less code + try { + writer.write(readString(Paths.get(getClass().getResource("./calendar.google.html").toURI())) + .replace(COLS, sb1.toString()) //TD optim this + .replace(DATA, sb2.toString()) + .replace(lineSeparator(), "")); + } catch (IOException | URISyntaxException e) { + throw new RuntimeException("error while mapping results", e); + } + log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); + return null; + } + +} diff --git a/src/main/java/org/usf/jquery/web/view/Chart2DView.java b/src/main/java/org/usf/jquery/web/view/Chart2DView.java new file mode 100644 index 00000000..e406bf67 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/view/Chart2DView.java @@ -0,0 +1,164 @@ +package org.usf.jquery.web.view; + +import static java.lang.System.currentTimeMillis; +import static java.lang.System.lineSeparator; +import static java.nio.file.Files.readString; +import static java.util.Comparator.comparing; +import static java.util.Map.entry; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; +import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; +import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; +import static org.usf.jquery.web.view.ResultWebView.WebType.STRING; +import static org.usf.jquery.web.view.ResultWebView.WebType.typeOf; + +import java.io.IOException; +import java.io.Writer; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedList; +import java.util.Map.Entry; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * + * @author u$f + * + * @see columnchart + * @see barchart + * @see linechart + * @see areachart + * + */ +@Slf4j +@RequiredArgsConstructor +public final class Chart2DView implements ResultWebView { + + private static final String COLS = "$columns"; + private static final String DATA = "$data"; + private static final String TYPE = "$chart"; + + private final String type; + private final Writer writer; + + public Void map(ResultSet rs) throws SQLException { + log.debug("mapping results..."); + var bg = currentTimeMillis(); + var rw = 0; + var sb = rs.getMetaData().getColumnCount() > 2 ? pivotAndMap(rs) : simpleMap(rs); + try { + writer.write(readString(Paths.get(getClass().getResource("./chart.google.html").toURI())) + .replace(TYPE, type) + .replace(COLS, sb[0].toString()) //TD optim this + .replace(DATA, sb[1].toString() + .replace(lineSeparator(), ""))); + } catch (IOException | URISyntaxException e) { + throw new RuntimeException("error while mapping results", e); + } + log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); + return null; + } + + private StringBuilder[] simpleMap(ResultSet rs) throws SQLException { //2d + var rsm = rs.getMetaData(); + int cols = rsm.getColumnCount(); + var yType = typeOf(rsm.getColumnType(cols)); + if(yType != NUMBER) { + throw new IllegalArgumentException("require numeric column as last column"); + } + var xType = cols > 1 ? typeOf(rsm.getColumnType(1)) : STRING; //can be null + var sb1 = new StringBuilder(); + sb1.append("[") + .append(doubleQuote(xType.typeName())).append(",") + .append(doubleQuote(cols > 1 ? rsm.getColumnName(1) : "")) + .append("],[") + .append(doubleQuote(yType.typeName())).append(",") + .append(doubleQuote(rsm.getColumnName(cols))) + .append("]"); + var sb2 = new StringBuilder(); + var data = new LinkedList>(); + while(rs.next()) { + data.add(entry(rs.getObject(1), rs.getObject(cols))); + } + data.stream().sorted(comparing(e-> (Comparable)e.getKey())).forEach(e-> + sb2.append("[") + .append(xType.format(e.getKey())).append(",") + .append(NUMBER.format(e.getValue())).append("],")); + if(!sb2.isEmpty()) { + sb2.deleteCharAt(sb2.length()-1); //dirty but less code + } + return new StringBuilder[]{sb1, sb2}; + } + + private StringBuilder[] pivotAndMap(ResultSet rs) throws SQLException { //xAxis,dim1,...,yAxis + + var rsm = rs.getMetaData(); + int cols = rsm.getColumnCount(); + var xType = typeOf(rsm.getColumnType(1)); //can be null + var yType = typeOf(rsm.getColumnType(cols)); + if(yType != NUMBER) { + throw new IllegalArgumentException("require numeric column as last column"); + } + var rows = new LinkedList(); + var join = new LinkedList<>(); + while(rs.next()) { + for(int i=2; i sb1.append(",[").append(doubleQuote(NUMBER.typeName())).append(",").append(doubleQuote(s)).append("]")); + var sb2= new StringBuilder(); + rows.stream().collect(groupingBy(DataHolder::getXValue)) + .entrySet().stream() + .sorted(comparing(e-> (Comparable)e.getKey())) + .forEach(e->{ + sb2.append("[").append(xType.format(e.getKey())); + var map = e.getValue().stream().collect(toMap(DataHolder::getKey, o-> NUMBER.format(o.getYValue()))); + headers.forEach(s-> sb2.append(",").append(map.getOrDefault(s, "0"))); + sb2.append("],"); + }); + sb2.deleteCharAt(sb2.length()-1); //dirty but less code + return new StringBuilder[]{sb1, sb2}; + } + + public static final Chart2DView areaChart(Writer w) { + return new Chart2DView("AreaChart", w); + } + + public static final Chart2DView barChart(Writer w) { + return new Chart2DView("BarChart", w); + } + + public static final Chart2DView columnChart(Writer w) { + return new Chart2DView("ColumnChart", w); + } + + public static final Chart2DView lineChart(Writer w) { + return new Chart2DView("LineChart", w); + } + + @Getter + @RequiredArgsConstructor + static class DataHolder { + + private final String key; + private final Object xValue; + private final Object yValue; + } + +} diff --git a/src/main/java/org/usf/jquery/web/view/PieChartView.java b/src/main/java/org/usf/jquery/web/view/PieChartView.java new file mode 100644 index 00000000..588e988e --- /dev/null +++ b/src/main/java/org/usf/jquery/web/view/PieChartView.java @@ -0,0 +1,73 @@ +package org.usf.jquery.web.view; + +import static java.lang.String.join; +import static java.lang.System.currentTimeMillis; +import static java.lang.System.lineSeparator; +import static java.nio.file.Files.readString; +import static java.util.Collections.singleton; +import static java.util.function.Predicate.not; +import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.web.view.ResultWebView.columns; +import static org.usf.jquery.web.view.ResultWebView.requireNumberColumn; +import static org.usf.jquery.web.view.ResultWebView.Formatter.formatCollection; + +import java.io.IOException; +import java.io.Writer; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedList; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * + * @author u$f + * + * @see piechart + * + */ +@Slf4j +@RequiredArgsConstructor +public final class PieChartView implements ResultWebView { + + private static final String DATA = "$data"; + private static final String TYPE = "$chart"; + + private final Writer writer; + + public Void map(ResultSet rs) throws SQLException { + log.debug("mapping results..."); + var bg = currentTimeMillis(); + var rw = 0; + var yCols = requireNumberColumn(rs.getMetaData()); + var xCols = rs.getMetaData().getColumnCount() == 1 ? singleton(yCols.getKey()) : columns(rs.getMetaData(), not(yCols.getKey()::equals)); + var xType = formatCollection("_"); //join empty + var sb = new StringBuilder("[") + .append(quote(join("_", xCols))).append(",") + .append(quote(yCols.getKey())).append("]"); + while(rs.next()) { + sb.append(",["); + var xVals= new LinkedList(); + for(var c : xCols) { + xVals.add(rs.getObject(c)); + } + sb.append(xType.format(xVals)).append(",") + .append(yCols.getValue().format(rs.getObject(yCols.getKey()))) + .append("]"); + } + try { + writer.write(readString(Paths.get(getClass().getResource("./pie.google.html").toURI())) + .replace(TYPE, "PieChart") + .replace(DATA, sb.toString()) + .replace(lineSeparator(), "")); + } catch (IOException | URISyntaxException e) { + throw new RuntimeException("error while mapping results", e); + } + log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); + return null; + } + +} diff --git a/src/main/java/org/usf/jquery/web/view/ResultWebView.java b/src/main/java/org/usf/jquery/web/view/ResultWebView.java new file mode 100644 index 00000000..9b5dd41c --- /dev/null +++ b/src/main/java/org/usf/jquery/web/view/ResultWebView.java @@ -0,0 +1,234 @@ +package org.usf.jquery.web.view; + +import static java.lang.Math.max; +import static java.lang.String.join; +import static java.time.ZoneId.systemDefault; +import static java.util.Map.entry; +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toList; +import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; +import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; +import static org.usf.jquery.web.view.ResultWebView.WebType.STRING; +import static org.usf.jquery.web.view.ResultWebView.WebType.typeOf; + +import java.sql.Date; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.IntFunction; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.usf.jquery.core.ResultMapper; +import org.usf.jquery.core.SqlStringBuilder; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * + * @author u$f + * + * + */ +public interface ResultWebView extends ResultMapper { + + static Entry requireNumberColumn(ResultSetMetaData rsm) throws SQLException { + var idx = rsm.getColumnCount(); + var yCol = entry(rsm.getColumnName(idx), typeOf(rsm.getColumnType(idx))); //y column + return yCol.getValue() == NUMBER ? yCol : requireColumn(rsm, NUMBER::equals); + } + + static Entry requireDateColumn(ResultSetMetaData rsm) throws SQLException { + var yCol = entry(rsm.getColumnName(1), typeOf(rsm.getColumnType(1))); //x column + return yCol.getValue().isDate() ? yCol : requireColumn(rsm, WebType::isDate); + } + + static Entry requireColumn(ResultSetMetaData rsm, Predicate type) throws SQLException { + var cols = columns(rsm).entrySet().stream() + .filter(e-> type.test(e.getValue())) + .collect(toList()); + if(cols.size() == 1) { + return cols.get(0); + } + throw new IllegalArgumentException("require one ?? column"); //TODO + } + + static Map columns(ResultSetMetaData rsm) throws SQLException { + var map = new LinkedHashMap(); + for(var i=0; i columns(ResultSetMetaData rsm, Predicate test) throws SQLException { + List columns = new LinkedList<>(); + for(var i=0; i { + + BOOLEAN(o-> ofNullable(o) + .map(Object::toString) + .orElse(null)), + + NUMBER(o-> ofNullable(o) + .map(Object::toString) + .orElse(null)), + + DATETIME(o-> ofNullable((Timestamp) o) + .map(Timestamp::toInstant) + .map(t-> "new Date('" + t + "')") //ISO + .orElse(null)), + + DATE(o-> ofNullable((Date) o) + .map(Date::toLocalDate) + .map(d-> d.atStartOfDay().atZone(systemDefault()).toInstant()) + .map(t-> "new Date('" + t + "')") //ISO + .orElse(null)), + + STRING(o-> ofNullable(o) + .map(Object::toString) + .map(SqlStringBuilder::doubleQuote) //escape ' character + .orElseGet(()-> doubleQuote(""))); + + //TD: google types timeOfDate, Date + + private final Formatter formatter; + + @Override + public String format(Object o) { + return formatter.format(o); + } + + public boolean isDate() { + return this == DATE || this == DATETIME; + } + + public String typeName() { + return name().toLowerCase(); + } + + static WebType typeOf(int type) { + switch (type) { + case Types.BOOLEAN: return BOOLEAN; + case Types.BIT: + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.REAL: + case Types.FLOAT: + case Types.DOUBLE: + case Types.NUMERIC: + case Types.DECIMAL: return NUMBER; + case Types.DATE: return DATE; + case Types.TIMESTAMP: return DATETIME; + //case Types.TIME: //need explicit cast format !? + default: return STRING; + } + } + } + + @FunctionalInterface + interface Formatter { + + String format(T o); + + public static Formatter> formatCollection(String delimiter) { + return c-> STRING.format(c.stream().map(Object::toString).collect(joining(delimiter))); + } + } + + + + + + + + + + + + + + + + + + + @Getter + @RequiredArgsConstructor + static final class DataTable { + + private final List columns; //indexed + private final List> rows; //indexed + + public void append(ResultSet rs) throws SQLException { + var arr = new ArrayList(max(2, columns.size())); + for(var c : columns) { + arr.add(c.format(0, rs)); + } + rows.add(arr); + } + + public static DataTable init(ResultSetMetaData rsm) throws SQLException { + var yAxis = new LinkedList(); + var xAxis = new LinkedList(); + for(var i=0; i(yAxis); + if(xAxis.isEmpty()) { + arr.add(0, new TableColumn("", STRING, idx-> yAxis.stream().map(c-> c.getName()).collect(joining(",")))); //join yAxis + } + else if(xAxis.size() > 1) { + arr.add(0, new TableColumn(join(",", ""), STRING, idx-> xAxis.stream().map(c-> c.getName()).collect(joining(",")))); //join xAxis + } + else { + arr.add(0, xAxis.get(0)); + } + return new DataTable(arr, new LinkedList<>()); + } + } + + @Getter + @RequiredArgsConstructor + static final class TableColumn { + + private final String name; + private final WebType type; + private final IntFunction supplier; + + public TableColumn(String name, WebType type) { + this.name = name; + this.type = type; + this.supplier = null; + } + + public String format(int index, ResultSet rs) throws SQLException { + return type.format(supplier == null ? rs.getObject(name) : supplier.apply(index)); + } + } + +} diff --git a/src/main/java/org/usf/jquery/web/view/TableView.java b/src/main/java/org/usf/jquery/web/view/TableView.java new file mode 100644 index 00000000..0ae3bd8b --- /dev/null +++ b/src/main/java/org/usf/jquery/web/view/TableView.java @@ -0,0 +1,73 @@ +package org.usf.jquery.web.view; + +import static java.lang.System.currentTimeMillis; +import static java.lang.System.lineSeparator; +import static java.nio.file.Files.readString; +import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.web.view.ResultWebView.WebType.typeOf; + +import java.io.IOException; +import java.io.Writer; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.sql.ResultSet; +import java.sql.SQLException; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * + * @author u$f + * + * @see table + * + */ +@Slf4j +@RequiredArgsConstructor +public final class TableView implements ResultWebView { + + private static final String COLS = "$columns"; + private static final String DATA = "$data"; + + private final Writer writer; + + public Void map(ResultSet rs) throws SQLException { + log.debug("mapping results..."); + var bg = currentTimeMillis(); + var rw = 0; + var nc = rs.getMetaData().getColumnCount(); + var sb1 = new StringBuilder(nc * 20); + var types = new WebType[nc]; + for(var i=0; itreemap + * + */ +@Slf4j +@RequiredArgsConstructor +public final class TreemapChartView implements ResultWebView { + + private static final String DATA = "$data"; + private static final String TYPE = "$chart"; + + private final Writer writer; + + public Void map(ResultSet rs) throws SQLException { + log.debug("mapping results..."); + var bg = currentTimeMillis(); + var rw = 0; + + var yCols = requireNumberColumn(rs.getMetaData()); + var xCols = columns(rs.getMetaData(), not(yCols.getKey()::equals)); //TODO 2 required columns + var xType = formatCollection("_"); //join empty + var sb = new StringBuilder("[") + .append(quote(join("_", xCols))).append(",") + .append(quote(yCols.getKey())).append("]"); + while(rs.next()) { + sb.append(",["); + var xVals= new LinkedList(); + for(var c : xCols) { + xVals.add(rs.getObject(c)); + } + sb.append(xType.format(xVals)).append(",") + .append(yCols.getValue().format(rs.getObject(yCols.getKey()))) + .append("]"); + } + try { + writer.write(readString(Paths.get(getClass().getResource("./treemap.google.html").toURI())) + .replace(TYPE, "PieChart") + .replace(DATA, sb.toString()) + .replace(lineSeparator(), "")); + } catch (IOException | URISyntaxException e) { + throw new RuntimeException("error while mapping results", e); + } + log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); + return null; + } +} diff --git a/src/main/java/org/usf/jquery/web/view/calendar.google.html b/src/main/java/org/usf/jquery/web/view/calendar.google.html new file mode 100644 index 00000000..8ef62a1f --- /dev/null +++ b/src/main/java/org/usf/jquery/web/view/calendar.google.html @@ -0,0 +1,7 @@ +JQuery
\ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/chart.google.html b/src/main/java/org/usf/jquery/web/view/chart.google.html new file mode 100644 index 00000000..b1cfc020 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/view/chart.google.html @@ -0,0 +1,7 @@ +JQuery
\ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/pie.google.html b/src/main/java/org/usf/jquery/web/view/pie.google.html new file mode 100644 index 00000000..b11c7a73 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/view/pie.google.html @@ -0,0 +1,5 @@ +JQuery
\ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/table.google.html b/src/main/java/org/usf/jquery/web/view/table.google.html new file mode 100644 index 00000000..6a1e152e --- /dev/null +++ b/src/main/java/org/usf/jquery/web/view/table.google.html @@ -0,0 +1,7 @@ +JQuery
\ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/treemap.google.html b/src/main/java/org/usf/jquery/web/view/treemap.google.html new file mode 100644 index 00000000..650a5a20 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/view/treemap.google.html @@ -0,0 +1,6 @@ +JQuery
\ No newline at end of file From 2f75f072c43e53aec33990e4c2c41e6d159bbb1a Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 24 Aug 2023 00:19:11 +0200 Subject: [PATCH 002/298] edit --- .../usf/jquery/web/view/ResultWebView.java | 89 ++++++++++++++----- 1 file changed, 65 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/view/ResultWebView.java b/src/main/java/org/usf/jquery/web/view/ResultWebView.java index 9b5dd41c..4592348c 100644 --- a/src/main/java/org/usf/jquery/web/view/ResultWebView.java +++ b/src/main/java/org/usf/jquery/web/view/ResultWebView.java @@ -2,12 +2,15 @@ import static java.lang.Math.max; import static java.lang.String.join; +import static java.lang.String.valueOf; import static java.time.ZoneId.systemDefault; import static java.util.Map.entry; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; +import static org.usf.jquery.core.Utils.isBlank; +import static org.usf.jquery.web.view.ResultWebView.TableColumnBind.bindColumns; import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; import static org.usf.jquery.web.view.ResultWebView.WebType.STRING; import static org.usf.jquery.web.view.ResultWebView.WebType.typeOf; @@ -32,9 +35,11 @@ import org.usf.jquery.core.ResultMapper; import org.usf.jquery.core.SqlStringBuilder; +import org.usf.jquery.core.Utils; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.ToString; /** * @@ -180,55 +185,91 @@ public static Formatter> formatCollection(String delimiter) { @RequiredArgsConstructor static final class DataTable { - private final List columns; //indexed - private final List> rows; //indexed + private final TableColumn xAxis; + private final List yAxis; + private final TableColumn over; + private final List> rows; public void append(ResultSet rs) throws SQLException { - var arr = new ArrayList(max(2, columns.size())); - for(var c : columns) { - arr.add(c.format(0, rs)); + var arr = new ArrayList(yAxis.size()+ 1); + arr.add(xAxis.format(rs)); + for(var c : yAxis) { + arr.add(c.format(rs)); } + arr.add(over.format(rs)); rows.add(arr); } public static DataTable init(ResultSetMetaData rsm) throws SQLException { + var xAxis = new TableColumn(rsm.getColumnName(1), typeOf(rsm.getColumnType(1))); var yAxis = new LinkedList(); - var xAxis = new LinkedList(); - for(var i=0; i(); + if(rsm.getColumnCount() == 1) { + if(xAxis.getType() == NUMBER) { + yAxis.add(xAxis); + } + } + else { + for(var i=1; i(yAxis); - if(xAxis.isEmpty()) { - arr.add(0, new TableColumn("", STRING, idx-> yAxis.stream().map(c-> c.getName()).collect(joining(",")))); //join yAxis + if(yAxis.isEmpty()) { + throw new IllegalArgumentException("require number column"); } - else if(xAxis.size() > 1) { - arr.add(0, new TableColumn(join(",", ""), STRING, idx-> xAxis.stream().map(c-> c.getName()).collect(joining(",")))); //join xAxis + TableColumn over; + if(dimen.isEmpty()) { + var v = yAxis.size() == 1 ? yAxis.get(0).getName() : ""; + over = new TableColumn("", null) { + @Override + public String format(ResultSet rs) throws SQLException { return v; } + }; } else { - arr.add(0, xAxis.get(0)); + over = dimen.size() == 1 ? dimen.get(0) : bindColumns(dimen); //join xAxis } - return new DataTable(arr, new LinkedList<>()); + return new DataTable(xAxis, yAxis, over, new LinkedList<>()); } } @Getter + @ToString @RequiredArgsConstructor - static final class TableColumn { + static class TableColumn { private final String name; private final WebType type; - private final IntFunction supplier; - public TableColumn(String name, WebType type) { - this.name = name; - this.type = type; - this.supplier = null; + public String format(ResultSet rs) throws SQLException { + return type.format(rs.getObject(name)); + } + } + + @Getter + static final class TableColumnBind extends TableColumn { + + private final List columns; + + private TableColumnBind(String name, WebType type, List columns) { + super(name, type); + this.columns = columns; } - public String format(int index, ResultSet rs) throws SQLException { - return type.format(supplier == null ? rs.getObject(name) : supplier.apply(index)); + @Override + public String format(ResultSet rs) throws SQLException { + var arr = new ArrayList(columns.size()); + for(var c : columns) { + arr.add(valueOf(rs.getObject(c))); + } + return getType().format(join("_", arr)); } + + public static TableColumnBind bindColumns(List childs) { + var cols = childs.stream().map(TableColumn::getName).collect(toList()); + var name = String.join("_", cols); + return new TableColumnBind(name, STRING, cols); + } } } From f57b92a2314e041acb62ab0608b90deffee5c2bf Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 24 Aug 2023 02:11:56 +0200 Subject: [PATCH 003/298] edit --- .../org/usf/jquery/web/view/Chart2DView.java | 119 ++++-------------- .../usf/jquery/web/view/ResultWebView.java | 83 +++++------- 2 files changed, 60 insertions(+), 142 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/view/Chart2DView.java b/src/main/java/org/usf/jquery/web/view/Chart2DView.java index e406bf67..4e6ae1e7 100644 --- a/src/main/java/org/usf/jquery/web/view/Chart2DView.java +++ b/src/main/java/org/usf/jquery/web/view/Chart2DView.java @@ -3,16 +3,10 @@ import static java.lang.System.currentTimeMillis; import static java.lang.System.lineSeparator; import static java.nio.file.Files.readString; -import static java.util.Comparator.comparing; -import static java.util.Map.entry; -import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.joining; +import static java.util.Map.ofEntries; import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toMap; -import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; +import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; -import static org.usf.jquery.web.view.ResultWebView.WebType.STRING; -import static org.usf.jquery.web.view.ResultWebView.WebType.typeOf; import java.io.IOException; import java.io.Writer; @@ -20,10 +14,9 @@ import java.nio.file.Paths; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.LinkedList; +import java.util.Map; import java.util.Map.Entry; -import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -52,12 +45,33 @@ public Void map(ResultSet rs) throws SQLException { log.debug("mapping results..."); var bg = currentTimeMillis(); var rw = 0; - var sb = rs.getMetaData().getColumnCount() > 2 ? pivotAndMap(rs) : simpleMap(rs); + var dt = DataTable.init(rs.getMetaData()); + while(rs.next()) { + dt.append(rs); + rw++; + } + var sb1 = new StringBuilder(); + var xAxis = dt.getXAxis(); + sb1.append("[").append(quote(xAxis.getType().typeName())).append(",").append(quote(xAxis.getName())).append("]"); + var cols = dt.getRows().stream().flatMap(c-> c.stream().skip(1)).map(Entry::getKey).distinct().sorted().collect(toList()); + for(var c : cols) { + sb1.append(",[").append(quote(NUMBER.typeName())).append(",").append(quote(c)).append("]"); + } + var sb2 = new StringBuilder(); + for(var r : dt.getRows()) { + var map = (Map) ofEntries(r.toArray(Entry[]::new)); + sb2.append("[").append(map.get(xAxis.getName())); + for(var c : cols) { + sb2.append(",").append(map.getOrDefault(c, "0")); + } + sb2.append("],"); //dirty but less code + } + sb2.deleteCharAt(sb2.length()-1); try { writer.write(readString(Paths.get(getClass().getResource("./chart.google.html").toURI())) .replace(TYPE, type) - .replace(COLS, sb[0].toString()) //TD optim this - .replace(DATA, sb[1].toString() + .replace(COLS, sb1.toString()) //TD optim this + .replace(DATA, sb2.toString() .replace(lineSeparator(), ""))); } catch (IOException | URISyntaxException e) { throw new RuntimeException("error while mapping results", e); @@ -66,76 +80,6 @@ public Void map(ResultSet rs) throws SQLException { return null; } - private StringBuilder[] simpleMap(ResultSet rs) throws SQLException { //2d - var rsm = rs.getMetaData(); - int cols = rsm.getColumnCount(); - var yType = typeOf(rsm.getColumnType(cols)); - if(yType != NUMBER) { - throw new IllegalArgumentException("require numeric column as last column"); - } - var xType = cols > 1 ? typeOf(rsm.getColumnType(1)) : STRING; //can be null - var sb1 = new StringBuilder(); - sb1.append("[") - .append(doubleQuote(xType.typeName())).append(",") - .append(doubleQuote(cols > 1 ? rsm.getColumnName(1) : "")) - .append("],[") - .append(doubleQuote(yType.typeName())).append(",") - .append(doubleQuote(rsm.getColumnName(cols))) - .append("]"); - var sb2 = new StringBuilder(); - var data = new LinkedList>(); - while(rs.next()) { - data.add(entry(rs.getObject(1), rs.getObject(cols))); - } - data.stream().sorted(comparing(e-> (Comparable)e.getKey())).forEach(e-> - sb2.append("[") - .append(xType.format(e.getKey())).append(",") - .append(NUMBER.format(e.getValue())).append("],")); - if(!sb2.isEmpty()) { - sb2.deleteCharAt(sb2.length()-1); //dirty but less code - } - return new StringBuilder[]{sb1, sb2}; - } - - private StringBuilder[] pivotAndMap(ResultSet rs) throws SQLException { //xAxis,dim1,...,yAxis - - var rsm = rs.getMetaData(); - int cols = rsm.getColumnCount(); - var xType = typeOf(rsm.getColumnType(1)); //can be null - var yType = typeOf(rsm.getColumnType(cols)); - if(yType != NUMBER) { - throw new IllegalArgumentException("require numeric column as last column"); - } - var rows = new LinkedList(); - var join = new LinkedList<>(); - while(rs.next()) { - for(int i=2; i sb1.append(",[").append(doubleQuote(NUMBER.typeName())).append(",").append(doubleQuote(s)).append("]")); - var sb2= new StringBuilder(); - rows.stream().collect(groupingBy(DataHolder::getXValue)) - .entrySet().stream() - .sorted(comparing(e-> (Comparable)e.getKey())) - .forEach(e->{ - sb2.append("[").append(xType.format(e.getKey())); - var map = e.getValue().stream().collect(toMap(DataHolder::getKey, o-> NUMBER.format(o.getYValue()))); - headers.forEach(s-> sb2.append(",").append(map.getOrDefault(s, "0"))); - sb2.append("],"); - }); - sb2.deleteCharAt(sb2.length()-1); //dirty but less code - return new StringBuilder[]{sb1, sb2}; - } - public static final Chart2DView areaChart(Writer w) { return new Chart2DView("AreaChart", w); } @@ -152,13 +96,4 @@ public static final Chart2DView lineChart(Writer w) { return new Chart2DView("LineChart", w); } - @Getter - @RequiredArgsConstructor - static class DataHolder { - - private final String key; - private final Object xValue; - private final Object yValue; - } - } diff --git a/src/main/java/org/usf/jquery/web/view/ResultWebView.java b/src/main/java/org/usf/jquery/web/view/ResultWebView.java index 4592348c..3d3fd02e 100644 --- a/src/main/java/org/usf/jquery/web/view/ResultWebView.java +++ b/src/main/java/org/usf/jquery/web/view/ResultWebView.java @@ -1,16 +1,15 @@ package org.usf.jquery.web.view; -import static java.lang.Math.max; import static java.lang.String.join; import static java.lang.String.valueOf; import static java.time.ZoneId.systemDefault; +import static java.util.Collections.emptyList; import static java.util.Map.entry; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.Utils.isBlank; -import static org.usf.jquery.web.view.ResultWebView.TableColumnBind.bindColumns; import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; import static org.usf.jquery.web.view.ResultWebView.WebType.STRING; import static org.usf.jquery.web.view.ResultWebView.WebType.typeOf; @@ -28,17 +27,14 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.function.IntFunction; import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Collectors; import org.usf.jquery.core.ResultMapper; import org.usf.jquery.core.SqlStringBuilder; -import org.usf.jquery.core.Utils; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.Setter; import lombok.ToString; /** @@ -187,16 +183,14 @@ static final class DataTable { private final TableColumn xAxis; private final List yAxis; - private final TableColumn over; - private final List> rows; + private final List>> rows = new LinkedList<>(); public void append(ResultSet rs) throws SQLException { - var arr = new ArrayList(yAxis.size()+ 1); + var arr = new ArrayList>(yAxis.size()+ 1); arr.add(xAxis.format(rs)); for(var c : yAxis) { arr.add(c.format(rs)); } - arr.add(over.format(rs)); rows.add(arr); } @@ -206,33 +200,31 @@ public static DataTable init(ResultSetMetaData rsm) throws SQLException { var dimen = new LinkedList(); if(rsm.getColumnCount() == 1) { if(xAxis.getType() == NUMBER) { - yAxis.add(xAxis); + yAxis.add(new TableColumn(xAxis.getName(), xAxis.getType())); } + xAxis.prefixed("x-"+xAxis.getName()); //avoid same yAxis name } else { for(var i=1; i 1 || dimen.isEmpty()) { + yAxis.forEach(TableColumn::prefixed); } - else { - over = dimen.size() == 1 ? dimen.get(0) : bindColumns(dimen); //join xAxis + if(!dimen.isEmpty()) { + var over = dimen.stream().map(TableColumn::getName).collect(toList()); + yAxis.forEach(c-> c.setOver(over)); } - return new DataTable(xAxis, yAxis, over, new LinkedList<>()); + return new DataTable(xAxis, yAxis); } } - + @Getter @ToString @RequiredArgsConstructor @@ -240,36 +232,27 @@ static class TableColumn { private final String name; private final WebType type; + @Setter + private List over = emptyList(); + private String prefixed = ""; - public String format(ResultSet rs) throws SQLException { - return type.format(rs.getObject(name)); + public Entry format(ResultSet rs) throws SQLException { + var arr = new LinkedList(); + for(var s : over) { + arr.add(valueOf(rs.getObject(s))); + } + if(!isBlank(prefixed)) { //optim this + arr.add(0, prefixed); + } + return entry(join("_", arr), type.format(rs.getObject(name))); } - } - - @Getter - static final class TableColumnBind extends TableColumn { - - private final List columns; - - private TableColumnBind(String name, WebType type, List columns) { - super(name, type); - this.columns = columns; - } - - @Override - public String format(ResultSet rs) throws SQLException { - var arr = new ArrayList(columns.size()); - for(var c : columns) { - arr.add(valueOf(rs.getObject(c))); - } - return getType().format(join("_", arr)); + + public void prefixed() { + prefixed(name); + } + + public void prefixed(String v) { + this.prefixed = v; } - - public static TableColumnBind bindColumns(List childs) { - var cols = childs.stream().map(TableColumn::getName).collect(toList()); - var name = String.join("_", cols); - return new TableColumnBind(name, STRING, cols); - } } - } From 9a23c3cab1bc24df764e8f22e6ad742648b0c24d Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 24 Aug 2023 02:19:36 +0200 Subject: [PATCH 004/298] edit --- src/main/java/org/usf/jquery/web/view/Chart2DView.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/view/Chart2DView.java b/src/main/java/org/usf/jquery/web/view/Chart2DView.java index 4e6ae1e7..570cf1e8 100644 --- a/src/main/java/org/usf/jquery/web/view/Chart2DView.java +++ b/src/main/java/org/usf/jquery/web/view/Chart2DView.java @@ -5,7 +5,7 @@ import static java.nio.file.Files.readString; import static java.util.Map.ofEntries; import static java.util.stream.Collectors.toList; -import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; import java.io.IOException; @@ -52,10 +52,10 @@ public Void map(ResultSet rs) throws SQLException { } var sb1 = new StringBuilder(); var xAxis = dt.getXAxis(); - sb1.append("[").append(quote(xAxis.getType().typeName())).append(",").append(quote(xAxis.getName())).append("]"); + sb1.append("[").append(doubleQuote(xAxis.getType().typeName())).append(",").append(doubleQuote(xAxis.getName())).append("]"); var cols = dt.getRows().stream().flatMap(c-> c.stream().skip(1)).map(Entry::getKey).distinct().sorted().collect(toList()); for(var c : cols) { - sb1.append(",[").append(quote(NUMBER.typeName())).append(",").append(quote(c)).append("]"); + sb1.append(",[").append(doubleQuote(NUMBER.typeName())).append(",").append(doubleQuote(c)).append("]"); } var sb2 = new StringBuilder(); for(var r : dt.getRows()) { From 301033b6fe84977eac0fd34c70c78f6c39566844 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 25 Aug 2023 12:45:34 +0200 Subject: [PATCH 005/298] edit --- .../org/usf/jquery/core/RequestQuery.java | 5 + .../org/usf/jquery/web/view/CalendarView.java | 31 +++--- .../org/usf/jquery/web/view/Chart2DView.java | 36 ++++--- .../org/usf/jquery/web/view/PieChartView.java | 74 +++++++++----- .../usf/jquery/web/view/ResultWebView.java | 72 +++----------- .../org/usf/jquery/web/view/TableView.java | 3 +- .../jquery/web/view/TimelineChartView.java | 99 +++++++++++++++++++ .../usf/jquery/web/view/TreemapChartView.java | 72 -------------- .../usf/jquery/web/view/calendar.google.html | 6 +- .../org/usf/jquery/web/view/chart.google.html | 3 +- .../org/usf/jquery/web/view/pie.google.html | 3 +- .../org/usf/jquery/web/view/table.google.html | 6 +- .../usf/jquery/web/view/timeline.google.html | 9 ++ .../usf/jquery/web/view/treemap.google.html | 6 -- 14 files changed, 233 insertions(+), 192 deletions(-) create mode 100644 src/main/java/org/usf/jquery/web/view/TimelineChartView.java delete mode 100644 src/main/java/org/usf/jquery/web/view/TreemapChartView.java create mode 100644 src/main/java/org/usf/jquery/web/view/timeline.google.html delete mode 100644 src/main/java/org/usf/jquery/web/view/treemap.google.html diff --git a/src/main/java/org/usf/jquery/core/RequestQuery.java b/src/main/java/org/usf/jquery/core/RequestQuery.java index 0aeb4fca..09e7ecb8 100644 --- a/src/main/java/org/usf/jquery/core/RequestQuery.java +++ b/src/main/java/org/usf/jquery/core/RequestQuery.java @@ -5,6 +5,7 @@ import static org.usf.jquery.web.view.Chart2DView.areaChart; import static org.usf.jquery.web.view.Chart2DView.barChart; import static org.usf.jquery.web.view.Chart2DView.columnChart; +import static org.usf.jquery.web.view.Chart2DView.comboChart; import static org.usf.jquery.web.view.Chart2DView.lineChart; import java.io.Writer; @@ -16,9 +17,11 @@ import org.usf.jquery.core.ResultMapper.DataWriter; import org.usf.jquery.web.view.CalendarView; +import org.usf.jquery.web.view.Chart2DView; import org.usf.jquery.web.view.PieChartView; import org.usf.jquery.web.view.ResultWebView; import org.usf.jquery.web.view.TableView; +import org.usf.jquery.web.view.TimelineChartView; import lombok.Getter; import lombok.NonNull; @@ -99,7 +102,9 @@ public ResultWebView chart(String view, Writer w) { case "column" : return columnChart(w); case "bar" : return barChart(w); case "area" : return areaChart(w); + case "combo" : return comboChart(w); case "line" : return lineChart(w); + case "timeline" : return new TimelineChartView(w); case "calendar" : return new CalendarView(w); default: throw new IllegalArgumentException(view); } diff --git a/src/main/java/org/usf/jquery/web/view/CalendarView.java b/src/main/java/org/usf/jquery/web/view/CalendarView.java index 5c287825..7a63b2fe 100644 --- a/src/main/java/org/usf/jquery/web/view/CalendarView.java +++ b/src/main/java/org/usf/jquery/web/view/CalendarView.java @@ -4,8 +4,8 @@ import static java.lang.System.lineSeparator; import static java.nio.file.Files.readString; import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.web.view.ResultWebView.requireDateColumn; -import static org.usf.jquery.web.view.ResultWebView.requireNumberColumn; +import static org.usf.jquery.web.view.ResultWebView.TableColumn.columns; +import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; import java.io.IOException; import java.io.Writer; @@ -13,6 +13,7 @@ import java.nio.file.Paths; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -34,25 +35,33 @@ public final class CalendarView implements ResultWebView { private final Writer writer; public Void map(ResultSet rs) throws SQLException { + if(rs.getMetaData().getColumnCount() != 2) { + throw new IllegalArgumentException("require 2 columns [DATE, NUMBER]"); + } log.debug("mapping results..."); var bg = currentTimeMillis(); var rw = 0; - var xCol = requireDateColumn(rs.getMetaData()); - var yCol = requireNumberColumn(rs.getMetaData()); + var cols = columns(rs.getMetaData()); + var xCol = Stream.of(cols).filter(c-> c.getType().isDate()).findAny().orElseThrow(()-> new IllegalArgumentException("require date column")); + var yCol = Stream.of(cols).filter(c-> c.getType() == NUMBER).findAny().orElseThrow(()-> new IllegalArgumentException("require number column")); + var sb1 = new StringBuilder(100) .append("[") - .append(quote(xCol.getValue().typeName())).append(",") - .append(quote(xCol.getKey())).append("],") + .append(quote(xCol.getType().typeName())).append(",") + .append(quote(xCol.getName())).append("],") .append("[") - .append(quote(yCol.getValue().typeName())).append(",") - .append(quote(yCol.getKey())).append("]"); + .append(quote(yCol.getType().typeName())).append(",") + .append(quote(yCol.getName())).append("]"); var sb2 = new StringBuilder(1000); while(rs.next()) { sb2.append("[") - .append(xCol.getValue().format(rs.getObject(xCol.getKey()))).append(",") - .append(yCol.getValue().format(rs.getObject(yCol.getKey()))).append("],"); + .append(xCol.getType().format(rs.getObject(xCol.getName()))).append(",") + .append(yCol.getType().format(rs.getObject(yCol.getName()))).append("],"); + rw++; + } + if(!sb2.isEmpty()) { //no data + sb2.deleteCharAt(sb2.length()-1); //dirty but less code } - sb2.deleteCharAt(sb2.length()-1); //dirty but less code try { writer.write(readString(Paths.get(getClass().getResource("./calendar.google.html").toURI())) .replace(COLS, sb1.toString()) //TD optim this diff --git a/src/main/java/org/usf/jquery/web/view/Chart2DView.java b/src/main/java/org/usf/jquery/web/view/Chart2DView.java index 570cf1e8..a3fbd785 100644 --- a/src/main/java/org/usf/jquery/web/view/Chart2DView.java +++ b/src/main/java/org/usf/jquery/web/view/Chart2DView.java @@ -6,6 +6,7 @@ import static java.util.Map.ofEntries; import static java.util.stream.Collectors.toList; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; +import static org.usf.jquery.web.view.ResultWebView.DataTable.fromMetaData; import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; import java.io.IOException; @@ -28,6 +29,7 @@ * @see barchart * @see linechart * @see areachart + * @see areachart * */ @Slf4j @@ -44,29 +46,35 @@ public final class Chart2DView implements ResultWebView { public Void map(ResultSet rs) throws SQLException { log.debug("mapping results..."); var bg = currentTimeMillis(); - var rw = 0; - var dt = DataTable.init(rs.getMetaData()); + var dt = fromMetaData(rs.getMetaData()); while(rs.next()) { - dt.append(rs); - rw++; + dt.fetchRow(rs); } var sb1 = new StringBuilder(); var xAxis = dt.getXAxis(); sb1.append("[").append(doubleQuote(xAxis.getType().typeName())).append(",").append(doubleQuote(xAxis.getName())).append("]"); var cols = dt.getRows().stream().flatMap(c-> c.stream().skip(1)).map(Entry::getKey).distinct().sorted().collect(toList()); - for(var c : cols) { - sb1.append(",[").append(doubleQuote(NUMBER.typeName())).append(",").append(doubleQuote(c)).append("]"); + if(cols.isEmpty()) { //no data + for(var c : dt.getYAxis()) { + sb1.append(",[").append(doubleQuote(NUMBER.typeName())).append(",").append(doubleQuote(c.getName())).append("]"); + } + } + else { + for(var c : cols) { + sb1.append(",[").append(doubleQuote(NUMBER.typeName())).append(",").append(doubleQuote(c)).append("]"); + } } var sb2 = new StringBuilder(); for(var r : dt.getRows()) { + @SuppressWarnings("unchecked") var map = (Map) ofEntries(r.toArray(Entry[]::new)); sb2.append("[").append(map.get(xAxis.getName())); - for(var c : cols) { - sb2.append(",").append(map.getOrDefault(c, "0")); - } - sb2.append("],"); //dirty but less code + cols.forEach(c-> sb2.append(",").append(map.getOrDefault(c, "0"))); + sb2.append("],"); + } + if(!sb2.isEmpty()) { //no data + sb2.deleteCharAt(sb2.length()-1); //dirty but less code } - sb2.deleteCharAt(sb2.length()-1); try { writer.write(readString(Paths.get(getClass().getResource("./chart.google.html").toURI())) .replace(TYPE, type) @@ -76,7 +84,7 @@ public Void map(ResultSet rs) throws SQLException { } catch (IOException | URISyntaxException e) { throw new RuntimeException("error while mapping results", e); } - log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); + log.info("{} rows mapped in {} ms", dt.getRows().size(), currentTimeMillis() - bg); return null; } @@ -96,4 +104,8 @@ public static final Chart2DView lineChart(Writer w) { return new Chart2DView("LineChart", w); } + public static final Chart2DView comboChart(Writer w) { + return new Chart2DView("ComboChart", w); + } + } diff --git a/src/main/java/org/usf/jquery/web/view/PieChartView.java b/src/main/java/org/usf/jquery/web/view/PieChartView.java index 588e988e..b6f5b0f2 100644 --- a/src/main/java/org/usf/jquery/web/view/PieChartView.java +++ b/src/main/java/org/usf/jquery/web/view/PieChartView.java @@ -1,15 +1,13 @@ package org.usf.jquery.web.view; import static java.lang.String.join; +import static java.lang.String.valueOf; import static java.lang.System.currentTimeMillis; import static java.lang.System.lineSeparator; import static java.nio.file.Files.readString; -import static java.util.Collections.singleton; -import static java.util.function.Predicate.not; -import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.web.view.ResultWebView.columns; -import static org.usf.jquery.web.view.ResultWebView.requireNumberColumn; -import static org.usf.jquery.web.view.ResultWebView.Formatter.formatCollection; +import static java.util.stream.Collectors.toList; +import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; +import static org.usf.jquery.web.view.ResultWebView.WebType.STRING; import java.io.IOException; import java.io.Writer; @@ -18,6 +16,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.LinkedList; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -38,26 +37,55 @@ public final class PieChartView implements ResultWebView { private final Writer writer; - public Void map(ResultSet rs) throws SQLException { + + public Void map(ResultSet rs) throws SQLException { //scroll. log.debug("mapping results..."); var bg = currentTimeMillis(); var rw = 0; - var yCols = requireNumberColumn(rs.getMetaData()); - var xCols = rs.getMetaData().getColumnCount() == 1 ? singleton(yCols.getKey()) : columns(rs.getMetaData(), not(yCols.getKey()::equals)); - var xType = formatCollection("_"); //join empty - var sb = new StringBuilder("[") - .append(quote(join("_", xCols))).append(",") - .append(quote(yCols.getKey())).append("]"); - while(rs.next()) { - sb.append(",["); - var xVals= new LinkedList(); - for(var c : xCols) { - xVals.add(rs.getObject(c)); - } - sb.append(xType.format(xVals)).append(",") - .append(yCols.getValue().format(rs.getObject(yCols.getKey()))) - .append("]"); - } + var cols = TableColumn.columns(rs.getMetaData()); + var numb = Stream.of(cols).filter(c-> c.getType() == NUMBER).collect(toList()); + if(numb.isEmpty()) { + throw new IllegalArgumentException("require number column"); + } + var yAxis = numb.get(numb.size()-1); //last one + var xAxis = Stream.of(cols).filter(c-> !c.getName().equals(yAxis.getName())).map(TableColumn::getName).collect(toList()); + if(xAxis.isEmpty()) { + xAxis.add(yAxis.getName()); + } + var sb1 = new StringBuilder(); + var sb2 = new StringBuilder(); + if(rs.next()) { + for(var c : numb) { + sb1.append(",[") + .append(STRING.format(c.getName())).append(",") + .append(NUMBER.format(rs.getObject(c.getName()))).append("]"); + } + do { + sb2.append(",["); + var xVals= new LinkedList(); + for(var c : xAxis) { + xVals.add(valueOf(rs.getObject(c))); + } + sb2.append(STRING.format(join("_", xVals))).append(",") + .append(NUMBER.format(rs.getObject(yAxis.getName()))) + .append("]"); + rw++; + } + while(rs.next()); + } + StringBuilder sb = new StringBuilder(); + if(rw == 1 && numb.size() == cols.length) { //one row, only number columns + sb.append("[") + .append(STRING.format("column")).append(",") + .append(STRING.format("value")).append("]") + .append(sb1); + } + else { + sb.append("[") + .append(STRING.format(join("_", xAxis))).append(",") + .append(STRING.format(yAxis.getName())).append("]") + .append(sb2); + } try { writer.write(readString(Paths.get(getClass().getResource("./pie.google.html").toURI())) .replace(TYPE, "PieChart") diff --git a/src/main/java/org/usf/jquery/web/view/ResultWebView.java b/src/main/java/org/usf/jquery/web/view/ResultWebView.java index 3d3fd02e..2a6a70d9 100644 --- a/src/main/java/org/usf/jquery/web/view/ResultWebView.java +++ b/src/main/java/org/usf/jquery/web/view/ResultWebView.java @@ -22,12 +22,9 @@ import java.sql.Types; import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Map.Entry; -import java.util.function.Predicate; import org.usf.jquery.core.ResultMapper; import org.usf.jquery.core.SqlStringBuilder; @@ -45,46 +42,6 @@ */ public interface ResultWebView extends ResultMapper { - static Entry requireNumberColumn(ResultSetMetaData rsm) throws SQLException { - var idx = rsm.getColumnCount(); - var yCol = entry(rsm.getColumnName(idx), typeOf(rsm.getColumnType(idx))); //y column - return yCol.getValue() == NUMBER ? yCol : requireColumn(rsm, NUMBER::equals); - } - - static Entry requireDateColumn(ResultSetMetaData rsm) throws SQLException { - var yCol = entry(rsm.getColumnName(1), typeOf(rsm.getColumnType(1))); //x column - return yCol.getValue().isDate() ? yCol : requireColumn(rsm, WebType::isDate); - } - - static Entry requireColumn(ResultSetMetaData rsm, Predicate type) throws SQLException { - var cols = columns(rsm).entrySet().stream() - .filter(e-> type.test(e.getValue())) - .collect(toList()); - if(cols.size() == 1) { - return cols.get(0); - } - throw new IllegalArgumentException("require one ?? column"); //TODO - } - - static Map columns(ResultSetMetaData rsm) throws SQLException { - var map = new LinkedHashMap(); - for(var i=0; i columns(ResultSetMetaData rsm, Predicate test) throws SQLException { - List columns = new LinkedList<>(); - for(var i=0; i { @@ -161,22 +118,6 @@ public static Formatter> formatCollection(String delimiter) { } - - - - - - - - - - - - - - - - @Getter @RequiredArgsConstructor static final class DataTable { @@ -185,7 +126,7 @@ static final class DataTable { private final List yAxis; private final List>> rows = new LinkedList<>(); - public void append(ResultSet rs) throws SQLException { + public void fetchRow(ResultSet rs) throws SQLException { var arr = new ArrayList>(yAxis.size()+ 1); arr.add(xAxis.format(rs)); for(var c : yAxis) { @@ -194,7 +135,7 @@ public void append(ResultSet rs) throws SQLException { rows.add(arr); } - public static DataTable init(ResultSetMetaData rsm) throws SQLException { + public static DataTable fromMetaData(ResultSetMetaData rsm) throws SQLException { var xAxis = new TableColumn(rsm.getColumnName(1), typeOf(rsm.getColumnType(1))); var yAxis = new LinkedList(); var dimen = new LinkedList(); @@ -254,5 +195,14 @@ public void prefixed() { public void prefixed(String v) { this.prefixed = v; } + + static TableColumn[] columns(ResultSetMetaData rsm) throws SQLException { + var arr = new TableColumn[rsm.getColumnCount()]; + for(var i=0; itreemap + * + */ +@Slf4j +@RequiredArgsConstructor +public final class TimelineChartView implements ResultWebView { + + private static final String COLS = "$columns"; + private static final String DATA = "$data"; + + private final Writer writer; + + public Void map(ResultSet rs) throws SQLException { + if(rs.getMetaData().getColumnCount() < 2) { + throw new IllegalArgumentException("require 2 columns [DATE, NUMBER]"); + } + log.debug("mapping results..."); + var bg = currentTimeMillis(); + var rw = 0; + var cols = columns(rs.getMetaData()); + var numb = Stream.of(cols).filter(c-> c.getType() == NUMBER).collect(toList()); + if(numb.isEmpty()) { + numb = Stream.of(cols).filter(c-> c.getType().isDate()).collect(toList()); + } + if(numb.isEmpty() || numb.size() != 2) { + throw new IllegalArgumentException("require NUMBER or DATE columns"); + } + var yAxis = numb; + var xAxis = Stream.of(cols).filter(c-> yAxis.stream().noneMatch(v-> v.getName().equals(c.getName()))).collect(toList()); + if(xAxis.isEmpty()) { + + } + if(numb.size() > 2) { + throw new IllegalArgumentException("too many columns"); + } + var sb1 = new StringBuilder(100); + for(var c : xAxis) { + sb1.append("[") + .append(STRING.format(c.getType().typeName())).append(",") + .append(STRING.format(c.getName())).append("],"); + } + for(var c : yAxis) { + sb1.append("[") + .append(STRING.format(c.getType().typeName())).append(",") + .append(STRING.format(c.getName())).append("],"); + } + sb1.deleteCharAt(sb1.length()-1); //dirty but less code + var sb2 = new StringBuilder(100); + while(rs.next()) { + sb2.append("["); + for(var c : xAxis) { + sb2.append(c.getType().format(rs.getObject(c.getName()))).append(","); + } + for(var c : yAxis) { + sb2.append(c.getType().format(rs.getObject(c.getName()))).append(","); + } + sb2.deleteCharAt(sb2.length()-1); //dirty but less code + sb2.append("],"); + } + if(!sb2.isEmpty()) { //no data + sb2.deleteCharAt(sb2.length()-1); //dirty but less code + } + try { + writer.write(readString(Paths.get(getClass().getResource("./timeline.google.html").toURI())) + .replace(COLS, sb1.toString()) //TD optim this + .replace(DATA, sb2.toString()) + .replace(lineSeparator(), "")); + } catch (IOException | URISyntaxException e) { + throw new RuntimeException("error while mapping results", e); + } + log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); + return null; + } +} diff --git a/src/main/java/org/usf/jquery/web/view/TreemapChartView.java b/src/main/java/org/usf/jquery/web/view/TreemapChartView.java deleted file mode 100644 index 40f708f2..00000000 --- a/src/main/java/org/usf/jquery/web/view/TreemapChartView.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.usf.jquery.web.view; - -import static java.lang.String.join; -import static java.lang.System.currentTimeMillis; -import static java.lang.System.lineSeparator; -import static java.nio.file.Files.readString; -import static java.util.function.Predicate.not; -import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.web.view.ResultWebView.columns; -import static org.usf.jquery.web.view.ResultWebView.requireNumberColumn; -import static org.usf.jquery.web.view.ResultWebView.Formatter.formatCollection; - -import java.io.IOException; -import java.io.Writer; -import java.net.URISyntaxException; -import java.nio.file.Paths; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.LinkedList; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -/** - * - * @author u$f - * - * @see treemap - * - */ -@Slf4j -@RequiredArgsConstructor -public final class TreemapChartView implements ResultWebView { - - private static final String DATA = "$data"; - private static final String TYPE = "$chart"; - - private final Writer writer; - - public Void map(ResultSet rs) throws SQLException { - log.debug("mapping results..."); - var bg = currentTimeMillis(); - var rw = 0; - - var yCols = requireNumberColumn(rs.getMetaData()); - var xCols = columns(rs.getMetaData(), not(yCols.getKey()::equals)); //TODO 2 required columns - var xType = formatCollection("_"); //join empty - var sb = new StringBuilder("[") - .append(quote(join("_", xCols))).append(",") - .append(quote(yCols.getKey())).append("]"); - while(rs.next()) { - sb.append(",["); - var xVals= new LinkedList(); - for(var c : xCols) { - xVals.add(rs.getObject(c)); - } - sb.append(xType.format(xVals)).append(",") - .append(yCols.getValue().format(rs.getObject(yCols.getKey()))) - .append("]"); - } - try { - writer.write(readString(Paths.get(getClass().getResource("./treemap.google.html").toURI())) - .replace(TYPE, "PieChart") - .replace(DATA, sb.toString()) - .replace(lineSeparator(), "")); - } catch (IOException | URISyntaxException e) { - throw new RuntimeException("error while mapping results", e); - } - log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); - return null; - } -} diff --git a/src/main/java/org/usf/jquery/web/view/calendar.google.html b/src/main/java/org/usf/jquery/web/view/calendar.google.html index 8ef62a1f..90b7d680 100644 --- a/src/main/java/org/usf/jquery/web/view/calendar.google.html +++ b/src/main/java/org/usf/jquery/web/view/calendar.google.html @@ -3,5 +3,7 @@ function draw() { var data = new google.visualization.DataTable(); [$columns].forEach(a=>data.addColumn(a[0], a[1]));data.addRows([$data]); -new google.visualization.Calendar(document.getElementById('calendar')).draw(data, {});} -
\ No newline at end of file +new google.visualization.Calendar(document.getElementById('chart')).draw(data, {});} +window.onresize = draw +
+ \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/chart.google.html b/src/main/java/org/usf/jquery/web/view/chart.google.html index b1cfc020..1554ab79 100644 --- a/src/main/java/org/usf/jquery/web/view/chart.google.html +++ b/src/main/java/org/usf/jquery/web/view/chart.google.html @@ -4,4 +4,5 @@ var data = new google.visualization.DataTable();[$columns].forEach(a=>data.addColumn(a[0], a[1]));data.addRows([$data]); new google.visualization.$chart(document.getElementById('chart')).draw(data);} window.onresize = draw -
\ No newline at end of file +
+ \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/pie.google.html b/src/main/java/org/usf/jquery/web/view/pie.google.html index b11c7a73..df0b0ed3 100644 --- a/src/main/java/org/usf/jquery/web/view/pie.google.html +++ b/src/main/java/org/usf/jquery/web/view/pie.google.html @@ -2,4 +2,5 @@ google.charts.load('current',{callback: 'draw','packages':['corechart'],'language': window.navigator.language.substring(0,2).toLowerCase()}); function draw() {new google.visualization.$chart(document.getElementById('chart')).draw(google.visualization.arrayToDataTable([$data]));} window.onresize = draw -
\ No newline at end of file +
+ \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/table.google.html b/src/main/java/org/usf/jquery/web/view/table.google.html index 6a1e152e..25d16894 100644 --- a/src/main/java/org/usf/jquery/web/view/table.google.html +++ b/src/main/java/org/usf/jquery/web/view/table.google.html @@ -2,6 +2,8 @@ google.charts.load('current',{callback:'draw','packages':['table'],'language': window.navigator.language.substring(0,2).toLowerCase()}); function draw() { var data = new google.visualization.DataTable();[$columns].forEach(a=>data.addColumn(a[0], a[1]));data.addRows([$data]); -new google.visualization.Table(document.getElementById('table')).draw(data, {showRowNumber:true, width:'100%'}); +new google.visualization.Table(document.getElementById('chart')).draw(data, {showRowNumber:true, width:'100%'}); } -
\ No newline at end of file +window.onresize = draw +
+ \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/timeline.google.html b/src/main/java/org/usf/jquery/web/view/timeline.google.html new file mode 100644 index 00000000..09735da1 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/view/timeline.google.html @@ -0,0 +1,9 @@ +JQuery
+ \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/treemap.google.html b/src/main/java/org/usf/jquery/web/view/treemap.google.html deleted file mode 100644 index 650a5a20..00000000 --- a/src/main/java/org/usf/jquery/web/view/treemap.google.html +++ /dev/null @@ -1,6 +0,0 @@ -JQuery
\ No newline at end of file From c8e7813f937d74eba0e4e4c405b393707be1b6f4 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 29 Aug 2023 09:38:28 +0200 Subject: [PATCH 006/298] edit --- .../java/org/usf/jquery/web/view/chart.google.html | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/view/chart.google.html b/src/main/java/org/usf/jquery/web/view/chart.google.html index 1554ab79..6e0cd296 100644 --- a/src/main/java/org/usf/jquery/web/view/chart.google.html +++ b/src/main/java/org/usf/jquery/web/view/chart.google.html @@ -1,7 +1,15 @@ JQuery
From 558ac34adefa5167492ba469e9eaff358ce54c9b Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 4 Sep 2023 09:52:21 +0200 Subject: [PATCH 007/298] edit --- .../org/usf/jquery/core/RequestQuery.java | 3 +- .../org/usf/jquery/web/view/SankeyView.java | 86 +++++++++++++++++++ .../org/usf/jquery/web/view/chart.google.html | 6 +- .../usf/jquery/web/view/sankey.google.html | 9 ++ 4 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/usf/jquery/web/view/SankeyView.java create mode 100644 src/main/java/org/usf/jquery/web/view/sankey.google.html diff --git a/src/main/java/org/usf/jquery/core/RequestQuery.java b/src/main/java/org/usf/jquery/core/RequestQuery.java index 09e7ecb8..a0ccd208 100644 --- a/src/main/java/org/usf/jquery/core/RequestQuery.java +++ b/src/main/java/org/usf/jquery/core/RequestQuery.java @@ -17,9 +17,9 @@ import org.usf.jquery.core.ResultMapper.DataWriter; import org.usf.jquery.web.view.CalendarView; -import org.usf.jquery.web.view.Chart2DView; import org.usf.jquery.web.view.PieChartView; import org.usf.jquery.web.view.ResultWebView; +import org.usf.jquery.web.view.SankeyView; import org.usf.jquery.web.view.TableView; import org.usf.jquery.web.view.TimelineChartView; @@ -106,6 +106,7 @@ public ResultWebView chart(String view, Writer w) { case "line" : return lineChart(w); case "timeline" : return new TimelineChartView(w); case "calendar" : return new CalendarView(w); + case "sankey" : return new SankeyView(w); default: throw new IllegalArgumentException(view); } } diff --git a/src/main/java/org/usf/jquery/web/view/SankeyView.java b/src/main/java/org/usf/jquery/web/view/SankeyView.java new file mode 100644 index 00000000..0d808980 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/view/SankeyView.java @@ -0,0 +1,86 @@ +package org.usf.jquery.web.view; + +import static java.lang.System.currentTimeMillis; +import static java.lang.System.lineSeparator; +import static java.nio.file.Files.readString; +import static java.util.stream.Collectors.toList; +import static org.usf.jquery.web.view.ResultWebView.TableColumn.columns; +import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; +import static org.usf.jquery.web.view.ResultWebView.WebType.STRING; + +import java.io.IOException; +import java.io.Writer; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * + * @author u$f + * + * @see treemap + * + */ +@Slf4j +@RequiredArgsConstructor +public class SankeyView implements ResultWebView { + + private static final String COLS = "$columns"; + private static final String DATA = "$data"; + + private final Writer writer; + + @Override + public Void map(ResultSet rs) throws SQLException { + if(rs.getMetaData().getColumnCount() != 3) { + throw new IllegalArgumentException("require [STRING, STRING, NUMBER] columns"); + } + log.debug("mapping results..."); + var bg = currentTimeMillis(); + var rw = 0; + var cols = columns(rs.getMetaData()); + var xAxis = Stream.of(cols).filter(c-> c.getType() == STRING).collect(toList()); + if(xAxis.size() != 2) { + throw new IllegalArgumentException("require [STRING, STRING, NUMBER] columns"); + } + var yAxis = Stream.of(cols).filter(c-> c.getType() == NUMBER).findAny().orElseThrow(()-> new IllegalArgumentException("require number column")); + var sb1 = new StringBuilder(); + for(var c : xAxis) { + sb1.append("[") + .append(STRING.format(c.getType().typeName())).append(",") + .append(STRING.format(c.getName())).append("],"); + } + sb1.append("[") + .append(STRING.format(yAxis.getType().typeName())).append(",") + .append(STRING.format(yAxis.getName())).append("]"); + + var sb2 = new StringBuilder(); + while(rs.next()) { + sb2.append("["); + for(var c : xAxis) { + sb2.append(c.getType().format(rs.getObject(c.getName()))).append(","); + } + sb2.append(yAxis.getType().format(rs.getObject(yAxis.getName()))).append("],"); + } + if(!sb2.isEmpty()) { //no data + sb2.deleteCharAt(sb2.length()-1); //dirty but less code + } + try { + writer.write(readString(Paths.get(getClass().getResource("./sankey.google.html").toURI())) + .replace(COLS, sb1.toString()) //TD optim this + .replace(DATA, sb2.toString()) + .replace(lineSeparator(), "")); + } catch (IOException | URISyntaxException e) { + throw new RuntimeException("error while mapping results", e); + } + log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); + return null; + } + +} diff --git a/src/main/java/org/usf/jquery/web/view/chart.google.html b/src/main/java/org/usf/jquery/web/view/chart.google.html index 6e0cd296..214a3b0b 100644 --- a/src/main/java/org/usf/jquery/web/view/chart.google.html +++ b/src/main/java/org/usf/jquery/web/view/chart.google.html @@ -1,8 +1,8 @@ JQuery
+ \ No newline at end of file From c9dfc370cfad9c2c682c96266ab64473a19f9414 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 8 Sep 2023 14:32:16 +0200 Subject: [PATCH 008/298] edit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7c78124b..5296837f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 io.github.oneteme jquery - 3.0.0 + 4.0.0-SNAPSHOT jar java-query java-query From facbdf6c73eb15732280a5236d1f32d9e56d0bf5 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 28 Nov 2023 17:16:23 +0100 Subject: [PATCH 009/298] edit --- .../core/ComparisonSingleExpression.java | 1 - .../java/org/usf/jquery/core/DBFunction.java | 22 ++++++++++++++++++- .../org/usf/jquery/core/DynamicModel.java | 4 +++- src/main/java/org/usf/jquery/core/Utils.java | 14 +++++++----- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index cdacdca4..bc301072 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -4,7 +4,6 @@ import static org.usf.jquery.core.NestedSql.aggregation; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.QueryParameterBuilder.streamArray; -import static org.usf.jquery.core.SqlStringBuilder.EMPTY; import java.util.LinkedList; diff --git a/src/main/java/org/usf/jquery/core/DBFunction.java b/src/main/java/org/usf/jquery/core/DBFunction.java index 521ad9a0..670d71ef 100644 --- a/src/main/java/org/usf/jquery/core/DBFunction.java +++ b/src/main/java/org/usf/jquery/core/DBFunction.java @@ -98,6 +98,14 @@ static TypedFunction length() { static TypedFunction trim() { return new TypedFunction(VARCHAR, function("TRIM"), VARCHAR); } + + static TypedFunction ltrim() { + return new TypedFunction(VARCHAR, function("LTRIM"), VARCHAR); + } + + static TypedFunction rtrim() { + return new TypedFunction(VARCHAR, function("RTRIM"), VARCHAR); + } static TypedFunction upper() { return new TypedFunction(VARCHAR, function("UPPER"), VARCHAR); @@ -111,12 +119,24 @@ static TypedFunction initcap() { return new TypedFunction(VARCHAR, function("INITCAP"), VARCHAR); } + static TypedFunction reverse() { + return new TypedFunction(VARCHAR, function("REVERSE"), VARCHAR); + } + + static TypedFunction left() { + return new TypedFunction(VARCHAR, function("LEFT"), VARCHAR, INTEGER); + } + + static TypedFunction right() { + return new TypedFunction(VARCHAR, function("RIGHT"), VARCHAR, INTEGER); + } + static TypedFunction replace() { //int start, int length return new TypedFunction(VARCHAR, function("REPLACE"), VARCHAR, VARCHAR, VARCHAR); //!teradata } static TypedFunction oreplace() { //int start, int length - return new TypedFunction(VARCHAR, function("OREPLACE"), VARCHAR, VARCHAR, VARCHAR); + return new TypedFunction(VARCHAR, function("OREPLACE"), VARCHAR, VARCHAR, VARCHAR); //teradata } static TypedFunction substring() { //int start, int length diff --git a/src/main/java/org/usf/jquery/core/DynamicModel.java b/src/main/java/org/usf/jquery/core/DynamicModel.java index aa9af773..a15b6434 100644 --- a/src/main/java/org/usf/jquery/core/DynamicModel.java +++ b/src/main/java/org/usf/jquery/core/DynamicModel.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static java.util.Objects.isNull; + import java.util.LinkedHashMap; /** @@ -18,6 +20,6 @@ public T getField(String name) { public T getField(String name, T defaultValue) { T v = getField(name); - return v == null ? defaultValue : v; + return isNull(v) ? defaultValue : v; } } diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 12845cbb..6cfe7ecd 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static java.util.Objects.isNull; + import java.util.Collection; import java.util.Map; @@ -25,18 +27,18 @@ public static boolean isPresent(T[] a) { } public static boolean isEmpty(T[] a) { - return a == null || a.length == 0; + return isNull(a) || a.length == 0; } public static boolean isEmpty(Collection c) { - return c == null || c.isEmpty(); + return isNull(c) || c.isEmpty(); } - public static boolean isEmpty(Map c) { - return c == null || c.isEmpty(); + public static boolean isEmpty(Map map) { + return isNull(map) || map.isEmpty(); } - public static boolean isBlank(String str) { - return str == null || str.isBlank(); + public static boolean isBlank(String s) { + return isNull(s) || s.isBlank(); } } From 78a690b6109367a6932b2b471819a7d20fe613a8 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 30 Nov 2023 12:19:51 +0100 Subject: [PATCH 010/298] fix sonar issues --- .../usf/jquery/core/AsciiResultMapper.java | 4 +- .../org/usf/jquery/core/CsvResultMapper.java | 6 +- .../org/usf/jquery/core/DynamicModel.java | 2 +- ...eResultMapper.java => KeyValueMapper.java} | 2 +- .../java/org/usf/jquery/core/Mappers.java | 68 +++++++++++++++++++ .../org/usf/jquery/core/MappingException.java | 13 ++++ .../org/usf/jquery/core/RequestQuery.java | 67 +----------------- ...ResultMapper.java => ResultSetMapper.java} | 6 +- .../org/usf/jquery/web/view/CalendarView.java | 10 +-- .../org/usf/jquery/web/view/Chart2DView.java | 18 ++--- .../org/usf/jquery/web/view/PieChartView.java | 10 +-- .../org/usf/jquery/web/view/SankeyView.java | 13 ++-- .../org/usf/jquery/web/view/TableView.java | 8 ++- .../jquery/web/view/TimelineChartView.java | 12 ++-- ...{ResultWebView.java => WebViewMapper.java} | 10 +-- .../usf/jquery/web/view/calendar.google.html | 9 ++- .../org/usf/jquery/web/view/chart.google.html | 4 +- .../org/usf/jquery/web/view/pie.google.html | 4 +- .../usf/jquery/web/view/sankey.google.html | 4 +- .../org/usf/jquery/web/view/table.google.html | 4 +- .../usf/jquery/web/view/timeline.google.html | 4 +- 21 files changed, 154 insertions(+), 124 deletions(-) rename src/main/java/org/usf/jquery/core/{SimpleResultMapper.java => KeyValueMapper.java} (91%) create mode 100644 src/main/java/org/usf/jquery/core/Mappers.java create mode 100644 src/main/java/org/usf/jquery/core/MappingException.java rename src/main/java/org/usf/jquery/core/{ResultMapper.java => ResultSetMapper.java} (92%) rename src/main/java/org/usf/jquery/web/view/{ResultWebView.java => WebViewMapper.java} (94%) diff --git a/src/main/java/org/usf/jquery/core/AsciiResultMapper.java b/src/main/java/org/usf/jquery/core/AsciiResultMapper.java index 8e2063a9..2f251293 100644 --- a/src/main/java/org/usf/jquery/core/AsciiResultMapper.java +++ b/src/main/java/org/usf/jquery/core/AsciiResultMapper.java @@ -34,7 +34,7 @@ */ @Slf4j @RequiredArgsConstructor -public final class AsciiResultMapper implements ResultMapper { +public final class AsciiResultMapper implements ResultSetMapper { private static final int MAX_LENGTH = 50; @@ -87,7 +87,7 @@ public Void map(ResultSet rs) throws SQLException { } writer.writeLine(div); } catch (IOException e) { - throw new RuntimeException("error while mapping results", e); + throw new MappingException("error writing results", e); } log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); return null; diff --git a/src/main/java/org/usf/jquery/core/CsvResultMapper.java b/src/main/java/org/usf/jquery/core/CsvResultMapper.java index fbbfbb25..53304352 100644 --- a/src/main/java/org/usf/jquery/core/CsvResultMapper.java +++ b/src/main/java/org/usf/jquery/core/CsvResultMapper.java @@ -16,7 +16,7 @@ */ @Slf4j @RequiredArgsConstructor -public final class CsvResultMapper implements ResultMapper { +public final class CsvResultMapper implements ResultSetMapper { private static final String SEMIC = ";"; private final DataWriter writer; @@ -43,9 +43,9 @@ public Void map(ResultSet rs) throws SQLException { } } catch(IOException e) { - throw new RuntimeException("error while mapping results", e); + throw new MappingException("error writing results", e); } - log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); + log.info("{} rows written in {} ms", rw, currentTimeMillis() - bg); return null; } diff --git a/src/main/java/org/usf/jquery/core/DynamicModel.java b/src/main/java/org/usf/jquery/core/DynamicModel.java index a15b6434..f40aa369 100644 --- a/src/main/java/org/usf/jquery/core/DynamicModel.java +++ b/src/main/java/org/usf/jquery/core/DynamicModel.java @@ -9,7 +9,7 @@ * @author u$f * */ -//TODO change to List> +//change to List> @SuppressWarnings("serial") public final class DynamicModel extends LinkedHashMap { diff --git a/src/main/java/org/usf/jquery/core/SimpleResultMapper.java b/src/main/java/org/usf/jquery/core/KeyValueMapper.java similarity index 91% rename from src/main/java/org/usf/jquery/core/SimpleResultMapper.java rename to src/main/java/org/usf/jquery/core/KeyValueMapper.java index ec8ae047..e26dae80 100644 --- a/src/main/java/org/usf/jquery/core/SimpleResultMapper.java +++ b/src/main/java/org/usf/jquery/core/KeyValueMapper.java @@ -15,7 +15,7 @@ * */ @Slf4j -public final class SimpleResultMapper implements ResultMapper> { +public final class KeyValueMapper implements ResultSetMapper> { @Override public List map(ResultSet rs) throws SQLException { diff --git a/src/main/java/org/usf/jquery/core/Mappers.java b/src/main/java/org/usf/jquery/core/Mappers.java new file mode 100644 index 00000000..87941f72 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/Mappers.java @@ -0,0 +1,68 @@ +package org.usf.jquery.core; + +import static org.usf.jquery.core.ResultSetMapper.DataWriter.usingRowWriter; +import static org.usf.jquery.web.view.Chart2DView.areaChart; +import static org.usf.jquery.web.view.Chart2DView.barChart; +import static org.usf.jquery.web.view.Chart2DView.columnChart; +import static org.usf.jquery.web.view.Chart2DView.comboChart; +import static org.usf.jquery.web.view.Chart2DView.lineChart; + +import java.io.Writer; + +import org.usf.jquery.core.ResultSetMapper.DataWriter; +import org.usf.jquery.web.view.CalendarView; +import org.usf.jquery.web.view.PieChartView; +import org.usf.jquery.web.view.SankeyView; +import org.usf.jquery.web.view.TableView; +import org.usf.jquery.web.view.TimelineChartView; +import org.usf.jquery.web.view.WebViewMapper; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class Mappers { + + public KeyValueMapper keyValue() { + return new KeyValueMapper(); + } + + public static AsciiResultMapper log() { + return new AsciiResultMapper(usingRowWriter(log::debug)); + } + + public static AsciiResultMapper ascii(Writer w) { + return ascii(w::write); + } + + public static AsciiResultMapper ascii(DataWriter out) { + return new AsciiResultMapper(out); + } + + public static CsvResultMapper csv(Writer w) { + return csv(w::write); + } + + public static CsvResultMapper csv(DataWriter out) { + return new CsvResultMapper(out); + } + + public static WebViewMapper webChart(String view, Writer w) { + switch (view) { + case "table" : return new TableView(w); + case "pie" : return new PieChartView(w); + case "column" : return columnChart(w); + case "bar" : return barChart(w); + case "area" : return areaChart(w); + case "combo" : return comboChart(w); + case "line" : return lineChart(w); + case "timeline" : return new TimelineChartView(w); + case "calendar" : return new CalendarView(w); + case "sankey" : return new SankeyView(w); + default: throw new IllegalArgumentException(view); + } + } + +} diff --git a/src/main/java/org/usf/jquery/core/MappingException.java b/src/main/java/org/usf/jquery/core/MappingException.java new file mode 100644 index 00000000..de5bbec9 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/MappingException.java @@ -0,0 +1,13 @@ +package org.usf.jquery.core; + +@SuppressWarnings("serial") +public final class MappingException extends RuntimeException { + + public MappingException(Throwable cause) { + super(cause); + } + + public MappingException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/usf/jquery/core/RequestQuery.java b/src/main/java/org/usf/jquery/core/RequestQuery.java index a0ccd208..bd66ff37 100644 --- a/src/main/java/org/usf/jquery/core/RequestQuery.java +++ b/src/main/java/org/usf/jquery/core/RequestQuery.java @@ -1,28 +1,13 @@ package org.usf.jquery.core; import static java.lang.System.currentTimeMillis; -import static org.usf.jquery.core.ResultMapper.DataWriter.usingRowWriter; -import static org.usf.jquery.web.view.Chart2DView.areaChart; -import static org.usf.jquery.web.view.Chart2DView.barChart; -import static org.usf.jquery.web.view.Chart2DView.columnChart; -import static org.usf.jquery.web.view.Chart2DView.comboChart; -import static org.usf.jquery.web.view.Chart2DView.lineChart; -import java.io.Writer; import java.sql.SQLException; import java.util.Arrays; import java.util.List; import javax.sql.DataSource; -import org.usf.jquery.core.ResultMapper.DataWriter; -import org.usf.jquery.web.view.CalendarView; -import org.usf.jquery.web.view.PieChartView; -import org.usf.jquery.web.view.ResultWebView; -import org.usf.jquery.web.view.SankeyView; -import org.usf.jquery.web.view.TableView; -import org.usf.jquery.web.view.TimelineChartView; - import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -43,10 +28,10 @@ public final class RequestQuery { private final Object[] params; public List execute(DataSource ds) { - return execute(ds, new SimpleResultMapper()); + return execute(ds, new KeyValueMapper()); } - public T execute(DataSource ds, ResultMapper mapper) { + public T execute(DataSource ds, ResultSetMapper mapper) { // overload with sql types try(var cn = ds.getConnection()){ log.debug("preparing statement : {}", query); try(var ps = cn.prepareStatement(query)){ @@ -65,53 +50,7 @@ public T execute(DataSource ds, ResultMapper mapper) { } } catch(SQLException e) { - throw new RuntimeException(e); + throw new MappingException("error while mapping results", e); } } - - /* experimental */ - - public void toCsv(DataSource ds, Writer w) { - toCsv(ds, w::write); - } - - public void toCsv(DataSource ds, DataWriter out) { - execute(ds, new CsvResultMapper(out)); - } - - public void toAscii(DataSource ds, Writer w) { - toAscii(ds, w::write); - } - - public void toAscii(DataSource ds, DataWriter out) { - execute(ds, new AsciiResultMapper(out)); - } - - public void toChart(DataSource ds, Writer w, String view) { - execute(ds, chart(view, w)); - } - - public void logResult(DataSource ds) { - execute(ds, new AsciiResultMapper(usingRowWriter(log::debug))); - } - - public ResultWebView chart(String view, Writer w) { - switch (view) { - case "table" : return new TableView(w); - case "pie" : return new PieChartView(w); - case "column" : return columnChart(w); - case "bar" : return barChart(w); - case "area" : return areaChart(w); - case "combo" : return comboChart(w); - case "line" : return lineChart(w); - case "timeline" : return new TimelineChartView(w); - case "calendar" : return new CalendarView(w); - case "sankey" : return new SankeyView(w); - default: throw new IllegalArgumentException(view); - } - } - - public SimpleResultMapper defaultMapper() { - return new SimpleResultMapper(); - } } diff --git a/src/main/java/org/usf/jquery/core/ResultMapper.java b/src/main/java/org/usf/jquery/core/ResultSetMapper.java similarity index 92% rename from src/main/java/org/usf/jquery/core/ResultMapper.java rename to src/main/java/org/usf/jquery/core/ResultSetMapper.java index 3d10406b..eb7b15bb 100644 --- a/src/main/java/org/usf/jquery/core/ResultMapper.java +++ b/src/main/java/org/usf/jquery/core/ResultSetMapper.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; - import static java.lang.System.lineSeparator; +import static org.usf.jquery.core.SqlStringBuilder.EMPTY; import java.io.IOException; import java.sql.ResultSet; @@ -16,7 +16,7 @@ * */ @FunctionalInterface -public interface ResultMapper { +public interface ResultSetMapper { T map(ResultSet rs) throws SQLException; //SQLException only @@ -60,7 +60,7 @@ public void write(String s) throws IOException { @Override public void writeLine() throws IOException { - writeLine(""); + writeLine(EMPTY); } @Override diff --git a/src/main/java/org/usf/jquery/web/view/CalendarView.java b/src/main/java/org/usf/jquery/web/view/CalendarView.java index 7a63b2fe..2abe9d5e 100644 --- a/src/main/java/org/usf/jquery/web/view/CalendarView.java +++ b/src/main/java/org/usf/jquery/web/view/CalendarView.java @@ -4,8 +4,8 @@ import static java.lang.System.lineSeparator; import static java.nio.file.Files.readString; import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.web.view.ResultWebView.TableColumn.columns; -import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; +import static org.usf.jquery.web.view.WebViewMapper.TableColumn.columns; +import static org.usf.jquery.web.view.WebViewMapper.WebType.NUMBER; import java.io.IOException; import java.io.Writer; @@ -15,6 +15,8 @@ import java.sql.SQLException; import java.util.stream.Stream; +import org.usf.jquery.core.MappingException; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -27,7 +29,7 @@ */ @Slf4j @RequiredArgsConstructor -public final class CalendarView implements ResultWebView { +public final class CalendarView implements WebViewMapper { private static final String COLS = "$columns"; private static final String DATA = "$data"; @@ -68,7 +70,7 @@ public Void map(ResultSet rs) throws SQLException { .replace(DATA, sb2.toString()) .replace(lineSeparator(), "")); } catch (IOException | URISyntaxException e) { - throw new RuntimeException("error while mapping results", e); + throw new MappingException("error mapping results", e); } log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); return null; diff --git a/src/main/java/org/usf/jquery/web/view/Chart2DView.java b/src/main/java/org/usf/jquery/web/view/Chart2DView.java index a3fbd785..233cdacb 100644 --- a/src/main/java/org/usf/jquery/web/view/Chart2DView.java +++ b/src/main/java/org/usf/jquery/web/view/Chart2DView.java @@ -6,8 +6,8 @@ import static java.util.Map.ofEntries; import static java.util.stream.Collectors.toList; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; -import static org.usf.jquery.web.view.ResultWebView.DataTable.fromMetaData; -import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; +import static org.usf.jquery.web.view.WebViewMapper.DataTable.fromMetaData; +import static org.usf.jquery.web.view.WebViewMapper.WebType.NUMBER; import java.io.IOException; import java.io.Writer; @@ -18,6 +18,8 @@ import java.util.Map; import java.util.Map.Entry; +import org.usf.jquery.core.MappingException; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -34,11 +36,11 @@ */ @Slf4j @RequiredArgsConstructor -public final class Chart2DView implements ResultWebView { +public final class Chart2DView implements WebViewMapper { - private static final String COLS = "$columns"; - private static final String DATA = "$data"; - private static final String TYPE = "$chart"; + private static final String COLS = "$columns"; + private static final String DATA = "$data"; + private static final String CHART = "$chart"; private final String type; private final Writer writer; @@ -77,12 +79,12 @@ public Void map(ResultSet rs) throws SQLException { } try { writer.write(readString(Paths.get(getClass().getResource("./chart.google.html").toURI())) - .replace(TYPE, type) + .replace(CHART, type) .replace(COLS, sb1.toString()) //TD optim this .replace(DATA, sb2.toString() .replace(lineSeparator(), ""))); } catch (IOException | URISyntaxException e) { - throw new RuntimeException("error while mapping results", e); + throw new MappingException("error mapping results", e); } log.info("{} rows mapped in {} ms", dt.getRows().size(), currentTimeMillis() - bg); return null; diff --git a/src/main/java/org/usf/jquery/web/view/PieChartView.java b/src/main/java/org/usf/jquery/web/view/PieChartView.java index b6f5b0f2..3c5726e1 100644 --- a/src/main/java/org/usf/jquery/web/view/PieChartView.java +++ b/src/main/java/org/usf/jquery/web/view/PieChartView.java @@ -6,8 +6,8 @@ import static java.lang.System.lineSeparator; import static java.nio.file.Files.readString; import static java.util.stream.Collectors.toList; -import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; -import static org.usf.jquery.web.view.ResultWebView.WebType.STRING; +import static org.usf.jquery.web.view.WebViewMapper.WebType.NUMBER; +import static org.usf.jquery.web.view.WebViewMapper.WebType.STRING; import java.io.IOException; import java.io.Writer; @@ -18,6 +18,8 @@ import java.util.LinkedList; import java.util.stream.Stream; +import org.usf.jquery.core.MappingException; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -30,7 +32,7 @@ */ @Slf4j @RequiredArgsConstructor -public final class PieChartView implements ResultWebView { +public final class PieChartView implements WebViewMapper { private static final String DATA = "$data"; private static final String TYPE = "$chart"; @@ -92,7 +94,7 @@ public Void map(ResultSet rs) throws SQLException { //scroll. .replace(DATA, sb.toString()) .replace(lineSeparator(), "")); } catch (IOException | URISyntaxException e) { - throw new RuntimeException("error while mapping results", e); + throw new MappingException("error mapping results", e); } log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); return null; diff --git a/src/main/java/org/usf/jquery/web/view/SankeyView.java b/src/main/java/org/usf/jquery/web/view/SankeyView.java index 0d808980..563fa829 100644 --- a/src/main/java/org/usf/jquery/web/view/SankeyView.java +++ b/src/main/java/org/usf/jquery/web/view/SankeyView.java @@ -4,9 +4,9 @@ import static java.lang.System.lineSeparator; import static java.nio.file.Files.readString; import static java.util.stream.Collectors.toList; -import static org.usf.jquery.web.view.ResultWebView.TableColumn.columns; -import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; -import static org.usf.jquery.web.view.ResultWebView.WebType.STRING; +import static org.usf.jquery.web.view.WebViewMapper.TableColumn.columns; +import static org.usf.jquery.web.view.WebViewMapper.WebType.NUMBER; +import static org.usf.jquery.web.view.WebViewMapper.WebType.STRING; import java.io.IOException; import java.io.Writer; @@ -14,9 +14,10 @@ import java.nio.file.Paths; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.stream.Collectors; import java.util.stream.Stream; +import org.usf.jquery.core.MappingException; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -29,7 +30,7 @@ */ @Slf4j @RequiredArgsConstructor -public class SankeyView implements ResultWebView { +public class SankeyView implements WebViewMapper { private static final String COLS = "$columns"; private static final String DATA = "$data"; @@ -77,7 +78,7 @@ public Void map(ResultSet rs) throws SQLException { .replace(DATA, sb2.toString()) .replace(lineSeparator(), "")); } catch (IOException | URISyntaxException e) { - throw new RuntimeException("error while mapping results", e); + throw new MappingException("error mapping results", e); } log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); return null; diff --git a/src/main/java/org/usf/jquery/web/view/TableView.java b/src/main/java/org/usf/jquery/web/view/TableView.java index b54cd327..c429e327 100644 --- a/src/main/java/org/usf/jquery/web/view/TableView.java +++ b/src/main/java/org/usf/jquery/web/view/TableView.java @@ -4,7 +4,7 @@ import static java.lang.System.lineSeparator; import static java.nio.file.Files.readString; import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.web.view.ResultWebView.WebType.typeOf; +import static org.usf.jquery.web.view.WebViewMapper.WebType.typeOf; import java.io.IOException; import java.io.Writer; @@ -13,6 +13,8 @@ import java.sql.ResultSet; import java.sql.SQLException; +import org.usf.jquery.core.MappingException; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -25,7 +27,7 @@ */ @Slf4j @RequiredArgsConstructor -public final class TableView implements ResultWebView { +public final class TableView implements WebViewMapper { private static final String COLS = "$columns"; private static final String DATA = "$data"; @@ -65,7 +67,7 @@ public Void map(ResultSet rs) throws SQLException { .replace(DATA, sb2.toString()) .replace(lineSeparator(), "")); } catch (IOException | URISyntaxException e) { - throw new RuntimeException("error while mapping results", e); + throw new MappingException("error mapping results", e); } log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); return null; diff --git a/src/main/java/org/usf/jquery/web/view/TimelineChartView.java b/src/main/java/org/usf/jquery/web/view/TimelineChartView.java index e835abd5..2c0f01ea 100644 --- a/src/main/java/org/usf/jquery/web/view/TimelineChartView.java +++ b/src/main/java/org/usf/jquery/web/view/TimelineChartView.java @@ -4,9 +4,9 @@ import static java.lang.System.lineSeparator; import static java.nio.file.Files.readString; import static java.util.stream.Collectors.toList; -import static org.usf.jquery.web.view.ResultWebView.TableColumn.columns; -import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; -import static org.usf.jquery.web.view.ResultWebView.WebType.STRING; +import static org.usf.jquery.web.view.WebViewMapper.TableColumn.columns; +import static org.usf.jquery.web.view.WebViewMapper.WebType.NUMBER; +import static org.usf.jquery.web.view.WebViewMapper.WebType.STRING; import java.io.IOException; import java.io.Writer; @@ -16,6 +16,8 @@ import java.sql.SQLException; import java.util.stream.Stream; +import org.usf.jquery.core.MappingException; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -28,7 +30,7 @@ */ @Slf4j @RequiredArgsConstructor -public final class TimelineChartView implements ResultWebView { +public final class TimelineChartView implements WebViewMapper { private static final String COLS = "$columns"; private static final String DATA = "$data"; @@ -91,7 +93,7 @@ public Void map(ResultSet rs) throws SQLException { .replace(DATA, sb2.toString()) .replace(lineSeparator(), "")); } catch (IOException | URISyntaxException e) { - throw new RuntimeException("error while mapping results", e); + throw new MappingException("error mapping results", e); } log.info("{} rows mapped in {} ms", rw, currentTimeMillis() - bg); return null; diff --git a/src/main/java/org/usf/jquery/web/view/ResultWebView.java b/src/main/java/org/usf/jquery/web/view/WebViewMapper.java similarity index 94% rename from src/main/java/org/usf/jquery/web/view/ResultWebView.java rename to src/main/java/org/usf/jquery/web/view/WebViewMapper.java index 2a6a70d9..060df2d7 100644 --- a/src/main/java/org/usf/jquery/web/view/ResultWebView.java +++ b/src/main/java/org/usf/jquery/web/view/WebViewMapper.java @@ -10,9 +10,9 @@ import static java.util.stream.Collectors.toList; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.Utils.isBlank; -import static org.usf.jquery.web.view.ResultWebView.WebType.NUMBER; -import static org.usf.jquery.web.view.ResultWebView.WebType.STRING; -import static org.usf.jquery.web.view.ResultWebView.WebType.typeOf; +import static org.usf.jquery.web.view.WebViewMapper.WebType.NUMBER; +import static org.usf.jquery.web.view.WebViewMapper.WebType.STRING; +import static org.usf.jquery.web.view.WebViewMapper.WebType.typeOf; import java.sql.Date; import java.sql.ResultSet; @@ -26,7 +26,7 @@ import java.util.List; import java.util.Map.Entry; -import org.usf.jquery.core.ResultMapper; +import org.usf.jquery.core.ResultSetMapper; import org.usf.jquery.core.SqlStringBuilder; import lombok.Getter; @@ -40,7 +40,7 @@ * * */ -public interface ResultWebView extends ResultMapper { +public interface WebViewMapper extends ResultSetMapper { @RequiredArgsConstructor enum WebType implements Formatter { diff --git a/src/main/java/org/usf/jquery/web/view/calendar.google.html b/src/main/java/org/usf/jquery/web/view/calendar.google.html index 90b7d680..8ee0fbf9 100644 --- a/src/main/java/org/usf/jquery/web/view/calendar.google.html +++ b/src/main/java/org/usf/jquery/web/view/calendar.google.html @@ -1,9 +1,8 @@ -JQuery
- \ No newline at end of file +JARVIS \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/chart.google.html b/src/main/java/org/usf/jquery/web/view/chart.google.html index 214a3b0b..48a06587 100644 --- a/src/main/java/org/usf/jquery/web/view/chart.google.html +++ b/src/main/java/org/usf/jquery/web/view/chart.google.html @@ -1,4 +1,4 @@ -JQuery
- \ No newline at end of file +JARVIS \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/pie.google.html b/src/main/java/org/usf/jquery/web/view/pie.google.html index df0b0ed3..0138b2bc 100644 --- a/src/main/java/org/usf/jquery/web/view/pie.google.html +++ b/src/main/java/org/usf/jquery/web/view/pie.google.html @@ -1,6 +1,6 @@ -JQuery
- \ No newline at end of file +JARVIS \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/sankey.google.html b/src/main/java/org/usf/jquery/web/view/sankey.google.html index 5a80327e..ca03f305 100644 --- a/src/main/java/org/usf/jquery/web/view/sankey.google.html +++ b/src/main/java/org/usf/jquery/web/view/sankey.google.html @@ -1,4 +1,4 @@ -JQuery
- \ No newline at end of file +JARVIS \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/table.google.html b/src/main/java/org/usf/jquery/web/view/table.google.html index 25d16894..90c763fa 100644 --- a/src/main/java/org/usf/jquery/web/view/table.google.html +++ b/src/main/java/org/usf/jquery/web/view/table.google.html @@ -1,4 +1,4 @@ -JQuery
- \ No newline at end of file +JARVIS \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/view/timeline.google.html b/src/main/java/org/usf/jquery/web/view/timeline.google.html index 09735da1..b6408aac 100644 --- a/src/main/java/org/usf/jquery/web/view/timeline.google.html +++ b/src/main/java/org/usf/jquery/web/view/timeline.google.html @@ -1,4 +1,4 @@ -JQuery
- \ No newline at end of file +JARVIS \ No newline at end of file From cd2d1cf40cd83a78b8ff619af6933f2994394a29 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 30 Nov 2023 12:23:04 +0100 Subject: [PATCH 011/298] edit --- src/main/java/org/usf/jquery/web/view/Chart2DView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/view/Chart2DView.java b/src/main/java/org/usf/jquery/web/view/Chart2DView.java index 233cdacb..df634cda 100644 --- a/src/main/java/org/usf/jquery/web/view/Chart2DView.java +++ b/src/main/java/org/usf/jquery/web/view/Chart2DView.java @@ -31,7 +31,7 @@ * @see barchart * @see linechart * @see areachart - * @see areachart + * @see combochart * */ @Slf4j From 572bd1fc36c23e545fa241a9b81ea397a6bf1004 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 30 Nov 2023 12:25:32 +0100 Subject: [PATCH 012/298] fix sonar issues --- src/main/java/org/usf/jquery/web/view/Chart2DView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/view/Chart2DView.java b/src/main/java/org/usf/jquery/web/view/Chart2DView.java index df634cda..f26dc1d9 100644 --- a/src/main/java/org/usf/jquery/web/view/Chart2DView.java +++ b/src/main/java/org/usf/jquery/web/view/Chart2DView.java @@ -69,7 +69,7 @@ public Void map(ResultSet rs) throws SQLException { var sb2 = new StringBuilder(); for(var r : dt.getRows()) { @SuppressWarnings("unchecked") - var map = (Map) ofEntries(r.toArray(Entry[]::new)); + var map = ofEntries((Entry[])r.toArray(Entry[]::new)); sb2.append("[").append(map.get(xAxis.getName())); cols.forEach(c-> sb2.append(",").append(map.getOrDefault(c, "0"))); sb2.append("],"); From e385f9e00de6935d8001031f045e4f629d3fe43b Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 14 Dec 2023 01:26:58 +0100 Subject: [PATCH 013/298] edit --- ...onFunction.java => AggregateFunction.java} | 6 +- .../usf/jquery/core/ArithmeticOperator.java | 18 + .../org/usf/jquery/core/BasicComparator.java | 2 +- .../java/org/usf/jquery/core/CaseColumn.java | 23 +- .../org/usf/jquery/core/CastFunction.java | 35 +- .../{DBComparator.java => Comparator.java} | 2 +- .../usf/jquery/core/ComparisonExpression.java | 28 +- .../core/ComparisonSingleExpression.java | 2 +- .../org/usf/jquery/core/DBArithmetic.java | 52 --- .../java/org/usf/jquery/core/DBCallable.java | 12 - .../java/org/usf/jquery/core/DBColumn.java | 53 ++- .../java/org/usf/jquery/core/DBFunction.java | 286 ---------------- .../{DBOperation.java => DBProcessor.java} | 2 +- .../org/usf/jquery/core/ExtractFunction.java | 18 +- .../org/usf/jquery/core/FunctionOperator.java | 27 ++ .../java/org/usf/jquery/core/InCompartor.java | 2 +- .../java/org/usf/jquery/core/JDBCType.java | 126 +++++-- .../java/org/usf/jquery/core/JavaType.java | 36 ++ .../java/org/usf/jquery/core/NamedColumn.java | 11 +- .../org/usf/jquery/core/NullComparator.java | 2 +- .../org/usf/jquery/core/OperationColumn.java | 36 +- .../java/org/usf/jquery/core/Operator.java | 312 ++++++++++++++++++ .../java/org/usf/jquery/core/OverColumn.java | 19 -- .../java/org/usf/jquery/core/Parameter.java | 61 ++++ .../org/usf/jquery/core/PipeFunction.java | 21 ++ .../jquery/core/QueryParameterBuilder.java | 56 ++-- .../java/org/usf/jquery/core/SQLType.java | 18 - .../org/usf/jquery/core/StringComparator.java | 2 +- .../java/org/usf/jquery/core/TableColumn.java | 37 --- .../org/usf/jquery/core/TaggableColumn.java | 2 +- src/main/java/org/usf/jquery/core/Typed.java | 7 + .../org/usf/jquery/core/TypedFunction.java | 74 ----- .../org/usf/jquery/core/TypedOperator.java | 59 ++++ src/main/java/org/usf/jquery/core/Utils.java | 3 +- .../java/org/usf/jquery/core/Validation.java | 23 +- .../java/org/usf/jquery/core/ViewColumn.java | 46 +++ .../org/usf/jquery/core/WindowFunction.java | 6 +- .../java/org/usf/jquery/core/WindowView.java | 20 +- .../org/usf/jquery/web/ArgumentParser.java | 36 ++ .../org/usf/jquery/web/ColumnDecorator.java | 41 ++- .../org/usf/jquery/web/ColumnMetadata.java | 8 +- .../java/org/usf/jquery/web/Constants.java | 2 +- .../org/usf/jquery/web/CriteriaBuilder.java | 4 +- .../usf/jquery/web/LinkedRequestEntry.java | 41 ++- .../org/usf/jquery/web/ParsableJDBCType.java | 6 +- .../org/usf/jquery/web/ParsableSQLType.java | 8 +- .../org/usf/jquery/web/RequestColumn.java | 16 +- .../org/usf/jquery/web/RequestParser.java | 166 ++++++++++ .../org/usf/jquery/web/TableDecorator.java | 25 +- .../org/usf/jquery/web/TableMetadata.java | 4 +- .../org/usf/jquery/web/view/Chart2DView.java | 1 - .../org/usf/jquery/web/RequestParserTest.java | 56 ++++ 52 files changed, 1189 insertions(+), 770 deletions(-) rename src/main/java/org/usf/jquery/core/{AggregationFunction.java => AggregateFunction.java} (50%) create mode 100644 src/main/java/org/usf/jquery/core/ArithmeticOperator.java rename src/main/java/org/usf/jquery/core/{DBComparator.java => Comparator.java} (96%) delete mode 100644 src/main/java/org/usf/jquery/core/DBArithmetic.java delete mode 100644 src/main/java/org/usf/jquery/core/DBCallable.java delete mode 100644 src/main/java/org/usf/jquery/core/DBFunction.java rename src/main/java/org/usf/jquery/core/{DBOperation.java => DBProcessor.java} (57%) create mode 100644 src/main/java/org/usf/jquery/core/FunctionOperator.java create mode 100644 src/main/java/org/usf/jquery/core/JavaType.java create mode 100644 src/main/java/org/usf/jquery/core/Operator.java delete mode 100644 src/main/java/org/usf/jquery/core/OverColumn.java create mode 100644 src/main/java/org/usf/jquery/core/Parameter.java create mode 100644 src/main/java/org/usf/jquery/core/PipeFunction.java delete mode 100644 src/main/java/org/usf/jquery/core/SQLType.java delete mode 100644 src/main/java/org/usf/jquery/core/TableColumn.java create mode 100644 src/main/java/org/usf/jquery/core/Typed.java delete mode 100644 src/main/java/org/usf/jquery/core/TypedFunction.java create mode 100644 src/main/java/org/usf/jquery/core/TypedOperator.java create mode 100644 src/main/java/org/usf/jquery/core/ViewColumn.java create mode 100644 src/main/java/org/usf/jquery/web/RequestParser.java create mode 100644 src/test/java/org/usf/jquery/web/RequestParserTest.java diff --git a/src/main/java/org/usf/jquery/core/AggregationFunction.java b/src/main/java/org/usf/jquery/core/AggregateFunction.java similarity index 50% rename from src/main/java/org/usf/jquery/core/AggregationFunction.java rename to src/main/java/org/usf/jquery/core/AggregateFunction.java index 210f2c04..76fa9192 100644 --- a/src/main/java/org/usf/jquery/core/AggregationFunction.java +++ b/src/main/java/org/usf/jquery/core/AggregateFunction.java @@ -6,15 +6,11 @@ * */ @FunctionalInterface -public interface AggregationFunction extends DBFunction { +public interface AggregateFunction extends WindowFunction { @Override default boolean isAggregation() { return true; } - static AggregationFunction aggregationFunction(String name) { - return ()-> name; - } - } diff --git a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java new file mode 100644 index 00000000..bfc49868 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java @@ -0,0 +1,18 @@ +package org.usf.jquery.core; + +import static org.usf.jquery.core.Validation.requireNArgs; + +/** + * + * @author u$f + * + */ +@FunctionalInterface +interface ArithmeticOperator extends Operator { + + @Override + default String sql(QueryParameterBuilder builder, Object[] args) { + requireNArgs(2, args, ArithmeticOperator.class::getSimpleName); + return builder.appendLitteral(args[0]) + id() + builder.appendLitteral(args[1]); + } +} diff --git a/src/main/java/org/usf/jquery/core/BasicComparator.java b/src/main/java/org/usf/jquery/core/BasicComparator.java index 31872dfc..a8271393 100644 --- a/src/main/java/org/usf/jquery/core/BasicComparator.java +++ b/src/main/java/org/usf/jquery/core/BasicComparator.java @@ -8,7 +8,7 @@ * */ @FunctionalInterface -public interface BasicComparator extends DBComparator { +public interface BasicComparator extends Comparator { String symbol(); diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index ba22e8fb..35528486 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -1,11 +1,14 @@ package org.usf.jquery.core; +import static java.util.function.Predicate.not; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import java.util.Collection; import java.util.LinkedList; +import java.util.function.Predicate; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -22,15 +25,17 @@ public final class CaseColumn implements DBColumn { @Override public String sql(QueryParameterBuilder builder) { - builder.forceValue(true); //add filters as value - try { - return expressions.stream() - .map(o-> o.sql(builder)) - .collect(joining(SPACE, "CASE ", " END")); - } - finally { - builder.forceValue(false); - } + return expressions.stream() + .map(o-> o.sql(addWithValue(builder))) + .collect(joining(SPACE, "CASE ", " END")); + } + + @Override + public JavaType javaType() { + return expressions.stream() + .map(JDBCType::typeOf) + .filter(not(AUTO::equals)) // should have same type + .findAny().orElse(AUTO); } public CaseColumn append(WhenExpression we) { diff --git a/src/main/java/org/usf/jquery/core/CastFunction.java b/src/main/java/org/usf/jquery/core/CastFunction.java index 80fb169f..3ca361db 100644 --- a/src/main/java/org/usf/jquery/core/CastFunction.java +++ b/src/main/java/org/usf/jquery/core/CastFunction.java @@ -1,8 +1,7 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Validation.requireNArgs; - -import java.util.function.IntFunction; +import static org.usf.jquery.core.SqlStringBuilder.COMA; +import static org.usf.jquery.core.Validation.requireAtLeastNArgs; /** * @@ -10,28 +9,28 @@ * */ @FunctionalInterface -public interface CastFunction extends DBFunction { +public interface CastFunction extends FunctionOperator { String asType(); @Override - default String name() { + default String id() { return "CAST"; } @Override - default String sql(QueryParameterBuilder builder, Object[] args, IntFunction indexedType) { - var n = "VARCHAR".equals(asType()) ? 2 : 1; //require length - requireNArgs(n, args, ()-> name() + "." + asType()); - return new SqlStringBuilder(name()) - .append("(").append(builder.appendLitteral(args[0], indexedType.apply(0))).append(" AS ").append(asType()) - .appendIf(n == 2, ()-> "(" + builder.appendLitteral(args[1], indexedType.apply(1))+ ")") - .append(")") - .toString(); - } - - static CastFunction castFunction(String type) { - return ()-> type; + default String sql(QueryParameterBuilder builder, Object[] args) { + requireAtLeastNArgs(1, args, ()-> id() + "_AS_" + asType()); + var sb = new SqlStringBuilder(id()).append("(") + .append(builder.appendLitteral(args[0])).append(" AS ").append(asType()); + if(args.length > 1) { + sb.append("(") + .append(builder.appendLitteral(args[1])); + for(int i=2; i ComparisonExpression in(@NonNull T... right) { - return DBComparator.in().expression(right); + return Comparator.in().expression(right); } @SuppressWarnings("unchecked") static ComparisonExpression notIn(@NonNull T... right) { - return DBComparator.notIn().expression(right); + return Comparator.notIn().expression(right); } } diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index bc301072..fd083fca 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -18,7 +18,7 @@ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class ComparisonSingleExpression implements ComparisonExpression { - private final DBComparator comparator; + private final Comparator comparator; private final Object right; //null|array|any @Override diff --git a/src/main/java/org/usf/jquery/core/DBArithmetic.java b/src/main/java/org/usf/jquery/core/DBArithmetic.java deleted file mode 100644 index 134ccca9..00000000 --- a/src/main/java/org/usf/jquery/core/DBArithmetic.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.usf.jquery.core; - -import static org.usf.jquery.core.Validation.requireNArgs; - -/** - * - * @author u$f - * - */ -@FunctionalInterface -interface DBArithmetic extends DBOperation { - - String symbol(); - - @Override - default String sql(QueryParameterBuilder builder, Object[] args) { - requireNArgs(2, args, DBArithmetic.class::getSimpleName); - return builder.appendNumber(args[0]) + symbol() + builder.appendNumber(args[1]); - } - - default OperationColumn args(Object left, Object right) { - return new OperationColumn(this, new Object[] {left, right}); - } - - static DBArithmetic plus() { - return operator("+"); - } - - static DBArithmetic minus() { - return operator("-"); - } - - static DBArithmetic multiply() { - return operator("*"); - } - - static DBArithmetic divise() { - return operator("/"); - } - - static DBArithmetic mode() { - return operator("%"); - } - - static DBArithmetic pow() { - return operator("^"); - } - - static DBArithmetic operator(final String symbol) { - return ()-> symbol; - } -} diff --git a/src/main/java/org/usf/jquery/core/DBCallable.java b/src/main/java/org/usf/jquery/core/DBCallable.java deleted file mode 100644 index 9a6101a1..00000000 --- a/src/main/java/org/usf/jquery/core/DBCallable.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.usf.jquery.core; - -/** - * https://learnsql.com/blog/standard-sql-functions-cheat-sheet/ - * - * @author u$f - * - */ -@FunctionalInterface -public interface DBCallable extends DBObject { - -} diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index fb9de341..5ddbf217 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -15,7 +16,7 @@ * */ @FunctionalInterface -public interface DBColumn extends DBObject, NestedSql { +public interface DBColumn extends DBObject, Typed, NestedSql { @Override default String sql(QueryParameterBuilder builder, Object[] args) { @@ -33,6 +34,10 @@ default boolean isAggregation() { default boolean isConstant() { return false; } + + default JavaType javaType() { + return AUTO; + } default NamedColumn as(String name) { return new NamedColumn(this, requireLegalVariable(name)); @@ -113,29 +118,21 @@ default ColumnSingleFilter filter(ComparisonExpression exp) { // operations default OperationColumn plus(Object o) { - return DBArithmetic.plus().args(this, o); + return Operator.plus().args(this, o); } default OperationColumn minus(Object o) { - return DBArithmetic.minus().args(this, o); + return Operator.minus().args(this, o); } default OperationColumn multiply(Object o) { - return DBArithmetic.multiply().args(this, o); + return Operator.multiply().args(this, o); } default OperationColumn divise(Object o) { - return DBArithmetic.divise().args(this, o); + return Operator.divise().args(this, o); } - default OperationColumn mode(Object o) { - return DBArithmetic.mode().args(this, o); - } - - default OperationColumn pow(Object o) { - return DBArithmetic.pow().args(this, o); - } - default WhenFilterBridge when(ComparisonExpression ex) { return new CaseSingleColumnBuilder(this).when(ex); } @@ -173,65 +170,65 @@ static OperationColumn count() { } static OperationColumn count(Object arg) { - return DBFunction.count().args(arg); + return Operator.count().args(arg); } static OperationColumn min(Object arg) { - return DBFunction.min().args(arg); + return Operator.min().args(arg); } static OperationColumn max(Object arg) { - return DBFunction.max().args(arg); + return Operator.max().args(arg); } static OperationColumn sum(Object arg) { - return DBFunction.sum().args(arg); + return Operator.sum().args(arg); } static OperationColumn avg(Object arg) { - return DBFunction.avg().args(arg); + return Operator.avg().args(arg); } //numeric static OperationColumn abs(Object arg) { - return DBFunction.abs().args(arg); + return Operator.abs().args(arg); } static OperationColumn sqrt(Object arg) { - return DBFunction.sqrt().args(arg); + return Operator.sqrt().args(arg); } static OperationColumn trunc(Object arg) { - return DBFunction.trunc().args(arg); + return Operator.trunc().args(arg); } static OperationColumn ceil(Object arg) { - return DBFunction.ceil().args(arg); + return Operator.ceil().args(arg); } static OperationColumn floor(Object arg) { - return DBFunction.floor().args(arg); + return Operator.floor().args(arg); } //string static OperationColumn trim(Object arg) { - return DBFunction.trim().args(arg); + return Operator.trim().args(arg); } static OperationColumn length(Object arg) { - return DBFunction.length().args(arg); + return Operator.length().args(arg); } static OperationColumn upper(Object arg) { - return DBFunction.upper().args(arg); + return Operator.upper().args(arg); } static OperationColumn lower(Object arg) { - return DBFunction.lower().args(arg); + return Operator.lower().args(arg); } static OperationColumn substring(Object arg, int start, int length) { - return DBFunction.substring().args(arg, start, length); + return Operator.substring().args(arg, start, length); } } diff --git a/src/main/java/org/usf/jquery/core/DBFunction.java b/src/main/java/org/usf/jquery/core/DBFunction.java deleted file mode 100644 index 670d71ef..00000000 --- a/src/main/java/org/usf/jquery/core/DBFunction.java +++ /dev/null @@ -1,286 +0,0 @@ -package org.usf.jquery.core; - -import static java.lang.reflect.Modifier.isStatic; -import static java.util.Optional.empty; -import static java.util.stream.Collectors.joining; -import static java.util.stream.IntStream.range; -import static org.usf.jquery.core.AggregationFunction.aggregationFunction; -import static org.usf.jquery.core.CastFunction.castFunction; -import static org.usf.jquery.core.ExtractFunction.extractFunction; -import static org.usf.jquery.core.JDBCType.AUTO_TYPE; -import static org.usf.jquery.core.JDBCType.BIGINT; -import static org.usf.jquery.core.JDBCType.DATE; -import static org.usf.jquery.core.JDBCType.DECIMAL; -import static org.usf.jquery.core.JDBCType.INTEGER; -import static org.usf.jquery.core.JDBCType.TIMESTAMP; -import static org.usf.jquery.core.JDBCType.VARCHAR; -import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.SqlStringBuilder.SPACE; -import static org.usf.jquery.core.SqlStringBuilder.parenthese; -import static org.usf.jquery.core.TypedFunction.autoTypeReturn; -import static org.usf.jquery.core.Utils.isPresent; -import static org.usf.jquery.core.Validation.illegalArgumentIf; -import static org.usf.jquery.core.Validation.requireNArgs; -import static org.usf.jquery.core.WindowFunction.windowFunction; - -import java.util.Optional; -import java.util.function.IntFunction; - -/** - * - * @author u$f - * - */ -@FunctionalInterface -public interface DBFunction extends DBOperation { - - String name(); - - @Override - default String sql(QueryParameterBuilder builder, Object[] args) { - return sql(builder, args, i-> AUTO_TYPE); - } - - default String sql(QueryParameterBuilder builder, Object[] args, IntFunction indexedType) { //arg type inject - return new SqlStringBuilder(name()) - .append("(") - .appendIf(isPresent(args), ()-> range(0, args.length) - .mapToObj(i-> builder.appendLitteral(args[i], indexedType.apply(i))) - .collect(joining(SCOMA))) //accept any - .append(")") - .toString(); - } - - default OperationColumn args(Object... args) { - return new OperationColumn(this, args); - } - - //numeric funct. - - static TypedFunction trunc() { - return new TypedFunction(INTEGER, function("TRUNC"), DECIMAL); - } - - static TypedFunction ceil() { - return new TypedFunction(INTEGER, function("CEIL"), DECIMAL); - } - - static TypedFunction floor() { - return new TypedFunction(INTEGER, function("FLOOR"), DECIMAL); - } - - static TypedFunction sqrt() { - return new TypedFunction(DECIMAL, function("SQRT"), DECIMAL); - } - - static TypedFunction exp() { - return new TypedFunction(DECIMAL, function("EXP"), DECIMAL); - } - - static TypedFunction log() { - return new TypedFunction(DECIMAL, function("LOG"), DECIMAL); - } - - static TypedFunction abs() { - return autoTypeReturn(function("ABS"), DECIMAL); //INTEGER | DECIMAL - } - - static TypedFunction mod() { - return autoTypeReturn(function("MOD"), DECIMAL, DECIMAL); //INTEGER | DECIMAL - } - - //string funct. - - static TypedFunction length() { - return new TypedFunction(INTEGER, function("LENGTH"), VARCHAR); - } - - static TypedFunction trim() { - return new TypedFunction(VARCHAR, function("TRIM"), VARCHAR); - } - - static TypedFunction ltrim() { - return new TypedFunction(VARCHAR, function("LTRIM"), VARCHAR); - } - - static TypedFunction rtrim() { - return new TypedFunction(VARCHAR, function("RTRIM"), VARCHAR); - } - - static TypedFunction upper() { - return new TypedFunction(VARCHAR, function("UPPER"), VARCHAR); - } - - static TypedFunction lower() { - return new TypedFunction(VARCHAR, function("LOWER"), VARCHAR); - } - - static TypedFunction initcap() { - return new TypedFunction(VARCHAR, function("INITCAP"), VARCHAR); - } - - static TypedFunction reverse() { - return new TypedFunction(VARCHAR, function("REVERSE"), VARCHAR); - } - - static TypedFunction left() { - return new TypedFunction(VARCHAR, function("LEFT"), VARCHAR, INTEGER); - } - - static TypedFunction right() { - return new TypedFunction(VARCHAR, function("RIGHT"), VARCHAR, INTEGER); - } - - static TypedFunction replace() { //int start, int length - return new TypedFunction(VARCHAR, function("REPLACE"), VARCHAR, VARCHAR, VARCHAR); //!teradata - } - - static TypedFunction oreplace() { //int start, int length - return new TypedFunction(VARCHAR, function("OREPLACE"), VARCHAR, VARCHAR, VARCHAR); //teradata - } - - static TypedFunction substring() { //int start, int length - return new TypedFunction(VARCHAR, function("SUBSTRING"), VARCHAR, INTEGER, INTEGER); - } - - //temporal funct. - - static TypedFunction year() { - return new TypedFunction(INTEGER, extractFunction("YEAR"), AUTO_TYPE); //DATE & TIMESTAMP - } - - static TypedFunction month() { - return new TypedFunction(INTEGER, extractFunction("MONTH"), AUTO_TYPE); - } - - static TypedFunction week() { - return new TypedFunction(INTEGER, extractFunction("WEEK"), AUTO_TYPE); - } - - static TypedFunction day() { - return new TypedFunction(INTEGER, extractFunction("DAY"), AUTO_TYPE); - } - - static TypedFunction dow() { - return new TypedFunction(INTEGER, extractFunction("DOW"), AUTO_TYPE); //!Teradata - } - - static TypedFunction doy() { - return new TypedFunction(INTEGER, extractFunction("DOY"), AUTO_TYPE); //!Teradata - } - - static TypedFunction hour() { - return new TypedFunction(INTEGER, extractFunction("HOUR"), AUTO_TYPE); - } - - static TypedFunction minute() { - return new TypedFunction(INTEGER, extractFunction("MINUTE"), AUTO_TYPE); - } - - static TypedFunction second() { - return new TypedFunction(INTEGER, extractFunction("SECOND"), AUTO_TYPE); - } - - static TypedFunction epoch() { - return new TypedFunction(INTEGER, extractFunction("EPOCH"), AUTO_TYPE); //!Teradata - } - - //cast funct. - - static TypedFunction varchar() { - return new TypedFunction(VARCHAR, castFunction("VARCHAR"), AUTO_TYPE, INTEGER); //any - } - - static TypedFunction date() { - return new TypedFunction(DATE, castFunction("DATE"), TIMESTAMP); // + string !? - } - - static TypedFunction integer() { - return new TypedFunction(INTEGER, castFunction("INTEGER"), VARCHAR); // + string !? - } - - static TypedFunction bigint() { - return new TypedFunction(BIGINT, castFunction("BIGINT"), VARCHAR); // + string !? - } - - //other funct - - static TypedFunction coalesce() { - return autoTypeReturn(function("COALESCE"), AUTO_TYPE, AUTO_TYPE); //takes 1 or 2 param - } - - //aggregate funct. - - - static TypedFunction count() { - return new TypedFunction(BIGINT, aggregationFunction("COUNT"), AUTO_TYPE); - } - - static TypedFunction min() { - return autoTypeReturn(aggregationFunction("MIN"), AUTO_TYPE); - } - - static TypedFunction max() { - return autoTypeReturn(aggregationFunction("MAX"), AUTO_TYPE); - } - - static TypedFunction sum() { - return autoTypeReturn(aggregationFunction("SUM"), DECIMAL); //INTEGER | DECIMAL - } - - static TypedFunction avg() { - return autoTypeReturn(aggregationFunction("AVG"), DECIMAL); //INTEGER | DECIMAL - } - - //window funct. - - static TypedFunction rank() { - return new TypedFunction(INTEGER, windowFunction("RANK")); - } - - static TypedFunction rowNumber() { - return new TypedFunction(INTEGER, windowFunction("ROW_NUMBER")); - } - - static TypedFunction denseRank() { - return new TypedFunction(INTEGER, windowFunction("DENSE_RANK")); - } - - static TypedFunction over() { - var fn = new DBFunction() { - - @Override - public String name() { - return "OVER"; - } - - @Override - public String sql(QueryParameterBuilder builder, Object[] args, IntFunction indexedType) { - requireNArgs(2, args, ()-> "over function"); //NamedColumn | OperationColumn - illegalArgumentIf(!(args[0] instanceof DBColumn), "over function require DBColumn @1st parameter"); - illegalArgumentIf(!(args[1] instanceof OverClause), "over function require OverClause @2nd parameter"); - return builder.appendParameter(args[0]) + SPACE + name() + parenthese(((OverClause)args[1]).sql(builder)); - } - }; - return new TypedFunction(AUTO_TYPE, fn, AUTO_TYPE, AUTO_TYPE) { - @Override - public OperationColumn args(Object... args) { - return new OverColumn(this, args); - } - }; - } - - static DBFunction function(final String name) { - return ()-> name; - } - - static Optional lookupFunction(String fn) { - try { - var m = DBFunction.class.getMethod(fn); - if(isStatic(m.getModifiers()) && m.getReturnType() == TypedFunction.class) { // no private static - return Optional.of((TypedFunction) m.invoke(null)); - } - } catch (Exception e) {/*do not throw exception*/} - return empty(); - } -} diff --git a/src/main/java/org/usf/jquery/core/DBOperation.java b/src/main/java/org/usf/jquery/core/DBProcessor.java similarity index 57% rename from src/main/java/org/usf/jquery/core/DBOperation.java rename to src/main/java/org/usf/jquery/core/DBProcessor.java index a256b981..68fd8a43 100644 --- a/src/main/java/org/usf/jquery/core/DBOperation.java +++ b/src/main/java/org/usf/jquery/core/DBProcessor.java @@ -6,6 +6,6 @@ * */ @FunctionalInterface -public interface DBOperation extends DBCallable, NestedSql { +public interface DBProcessor extends DBObject { } diff --git a/src/main/java/org/usf/jquery/core/ExtractFunction.java b/src/main/java/org/usf/jquery/core/ExtractFunction.java index e9fd2566..5b4d14d1 100644 --- a/src/main/java/org/usf/jquery/core/ExtractFunction.java +++ b/src/main/java/org/usf/jquery/core/ExtractFunction.java @@ -2,32 +2,24 @@ import static org.usf.jquery.core.Validation.requireNArgs; -import java.util.function.IntFunction; - /** * * @author u$f * */ @FunctionalInterface -public interface ExtractFunction extends DBFunction { +public interface ExtractFunction extends FunctionOperator { String field(); @Override - default String name() { + default String id() { return "EXTRACT"; } @Override - default String sql(QueryParameterBuilder builder, Object[] args, IntFunction indexedType) { - requireNArgs(1, args, this::name); - return name() + "(" + field() + " FROM " + - builder.appendLitteral(args[0], indexedType.apply(0)) + ")"; - } - - static ExtractFunction extractFunction(String type) { - return ()-> type; + default String sql(QueryParameterBuilder builder, Object[] args) { + requireNArgs(1, args, this::id); + return id() + "(" + field() + " FROM " + builder.appendLitteral(args[0]) + ")"; } - } diff --git a/src/main/java/org/usf/jquery/core/FunctionOperator.java b/src/main/java/org/usf/jquery/core/FunctionOperator.java new file mode 100644 index 00000000..09be5e83 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/FunctionOperator.java @@ -0,0 +1,27 @@ +package org.usf.jquery.core; + +import static java.util.stream.Collectors.joining; +import static java.util.stream.IntStream.range; +import static org.usf.jquery.core.SqlStringBuilder.SCOMA; +import static org.usf.jquery.core.Utils.isPresent; + +/** + * https://learnsql.com/blog/standard-sql-functions-cheat-sheet/ + * + * @author u$f + * + */ +@FunctionalInterface +public interface FunctionOperator extends Operator { + + @Override + default String sql(QueryParameterBuilder builder, Object[] args) { + return new SqlStringBuilder(id()) + .append("(") + .appendIf(isPresent(args), ()-> range(0, args.length) + .mapToObj(i-> builder.appendLitteral(args[i])) + .collect(joining(SCOMA))) //accept any + .append(")") + .toString(); + } +} diff --git a/src/main/java/org/usf/jquery/core/InCompartor.java b/src/main/java/org/usf/jquery/core/InCompartor.java index 1b2ee850..32ebf256 100644 --- a/src/main/java/org/usf/jquery/core/InCompartor.java +++ b/src/main/java/org/usf/jquery/core/InCompartor.java @@ -11,7 +11,7 @@ * */ @FunctionalInterface -public interface InCompartor extends DBComparator { +public interface InCompartor extends Comparator { String name(); diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index 8714e122..23a91d6d 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -1,14 +1,17 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.JavaType.declare; + import java.math.BigDecimal; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; +import java.util.function.Function; +import java.util.function.Predicate; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.ToString; /** * @@ -18,36 +21,105 @@ * */ @Getter -@ToString @RequiredArgsConstructor -public enum JDBCType implements SQLType { - - AUTO_TYPE(Types.NULL, Object.class), //replace by array type - BOOLEAN(Types.BOOLEAN, Boolean.class), - BIT(Types.BIT, Boolean.class), - TINYINT(Types.TINYINT, Byte.class), - SMALLINT(Types.SMALLINT, Short.class), - INTEGER(Types.INTEGER, Integer.class), - BIGINT(Types.BIGINT, Long.class), - REAL(Types.REAL, Float.class), - FLOAT(Types.FLOAT, Double.class), - DOUBLE(Types.DOUBLE, Double.class), - NUMERIC(Types.NUMERIC, BigDecimal.class), - DECIMAL(Types.DECIMAL, BigDecimal.class), - CHAR(Types.CHAR, String.class), //teradata !char - VARCHAR(Types.VARCHAR, String.class), - NVARCHAR(Types.NVARCHAR, String.class), - LONGNVARCHAR(Types.LONGNVARCHAR, String.class), - DATE(Types.DATE, Date.class), - TIME(Types.TIME, Time.class), - TIMESTAMP(Types.TIMESTAMP, Timestamp.class), - TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class); +public enum JDBCType implements JavaType { + + //do not change enum order + BOOLEAN(Types.BOOLEAN, Boolean.class, JDBCType::isBoolean), + BIT(Types.BIT, Boolean.class, JDBCType::isBoolean), + TINYINT(Types.TINYINT, Byte.class, o-> isNumber(o, Byte.MIN_VALUE, Byte.MAX_VALUE, false)), + SMALLINT(Types.SMALLINT, Short.class, o-> isNumber(o, Short.MIN_VALUE, Short.MAX_VALUE, false)), + INTEGER(Types.INTEGER, Integer.class, o-> isNumber(o, Integer.MIN_VALUE, Integer.MAX_VALUE, false)), + BIGINT(Types.BIGINT, Long.class, o-> isNumber(o, Long.MIN_VALUE, Long.MAX_VALUE, false)), + REAL(Types.REAL, Float.class, o-> isNumber(o, Float.MIN_VALUE, Float.MAX_VALUE, true)), + FLOAT(Types.FLOAT, Double.class, o-> isNumber(o, Double.MIN_VALUE, Double.MAX_VALUE, true)), + DOUBLE(Types.DOUBLE, Double.class, o-> isNumber(o, Double.MIN_VALUE, Double.MAX_VALUE, true)), + NUMERIC(Types.NUMERIC, BigDecimal.class, JDBCType::isNumber), + DECIMAL(Types.DECIMAL, BigDecimal.class, JDBCType::isNumber), + CHAR(Types.CHAR, Character.class, JDBCType::isChar), //teradata !char + VARCHAR(Types.VARCHAR, String.class, JDBCType::isString), + NVARCHAR(Types.NVARCHAR, String.class, JDBCType::isString), + LONGNVARCHAR(Types.LONGNVARCHAR, String.class, JDBCType::isString), + DATE(Types.DATE, Date.class, Date.class::isInstance), + TIME(Types.TIME, Time.class, Time.class::isInstance), + TIMESTAMP(Types.TIMESTAMP, Timestamp.class, Timestamp.class::isInstance), + TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class, Timestamp.class::isInstance); + + public static final JavaType AUTO = declare(Object.class, o-> true); + public static final JavaType OTHER = declare(Object.class, o-> true); private final int value; - private final Class javaType; + private final Class type; + private final Function matcher; @Override - public boolean isAutoType() { - return this == AUTO_TYPE; + public boolean accept(Object o) { + if(o instanceof Typed) { + var t = ((Typed) o).javaType(); + return t == null + || this == t + || getType() == t.getType() + || (subType(this, Number.class) && subType(t, Number.class)); //other types compatibility + } + return acceptValue(o); + } + + static boolean subType(JavaType type, Class c) { + return c.isAssignableFrom(type.getType()); + } + + private boolean acceptValue(Object o) { + return o == null || matcher.apply(o); + } + + private static boolean isNumber(Object o, double min, double max, boolean decimal) { + if(isNumber(o)) { + var n = (Number) o; + var v = n.doubleValue(); + return (v >= min && v <= max) && (decimal || v == n.longValue()); + } + return false; + } + + private static boolean isNumber(Object o) { + return o instanceof Number; + } + + private static boolean isBoolean(Object o) { + return o.getClass() == Boolean.class + || isNumber(o, 0, 1, false) + || (o.getClass() == String.class && o.toString().matches("[yYnN]")); + } + + private static boolean isChar(Object o) { + return o.getClass() == Character.class + || (o.getClass() == String.class && o.toString().length() == 1); + } + + private static boolean isString(Object o) { + return o.getClass() == Character.class + || o.getClass() == String.class; + } + + public static JavaType typeOf(Object o) { + if(o instanceof Typed) { + var t = ((Typed) o).javaType(); + return t == null ? AUTO : findType(t::equals); + } + return o == null ? AUTO : findType(e-> e.getType().isInstance(o)); + } + + public static JavaType fromDataType(int value) { + return findType(t-> t.value == value); } + + public static JavaType findType(Predicate predicate) { + for(var t : values()) { + if(predicate.test(t)) { + return t; + } + } + return OTHER; + } + } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/JavaType.java b/src/main/java/org/usf/jquery/core/JavaType.java new file mode 100644 index 00000000..b0e47fda --- /dev/null +++ b/src/main/java/org/usf/jquery/core/JavaType.java @@ -0,0 +1,36 @@ +package org.usf.jquery.core; + +import java.util.function.Predicate; + +import lombok.NonNull; + +/** + * + * @author u$f + * + */ +public interface JavaType { + + boolean accept(Object o); + + Class getType(); + + static JavaType instance(Class type) { + return declare(type, type::isInstance); + } + + static JavaType declare(@NonNull Class type, @NonNull Predicate predicate) { + return new JavaType() { + @Override + public Class getType() { + return type; + } + + @Override + public boolean accept(Object o) { + return predicate.test(o); + } + }; + } + +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index 8fbc9a3e..7c3ed0de 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -1,7 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; - import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.Delegate; @@ -16,11 +14,11 @@ public final class NamedColumn implements TaggableColumn { @Delegate private final DBColumn column; - private final String reference; + private final String tag; @Override public String tagname() { - return reference; + return tag; } @Override @@ -31,9 +29,4 @@ public NamedColumn as(String name) { // map public DBColumn unwrap() { return column; } - - @Override - public String toString() { - return sql(addWithValue()) + " AS " + reference; - } } diff --git a/src/main/java/org/usf/jquery/core/NullComparator.java b/src/main/java/org/usf/jquery/core/NullComparator.java index 2f669e67..273d3857 100644 --- a/src/main/java/org/usf/jquery/core/NullComparator.java +++ b/src/main/java/org/usf/jquery/core/NullComparator.java @@ -9,7 +9,7 @@ * */ @FunctionalInterface -public interface NullComparator extends DBComparator { +public interface NullComparator extends Comparator { String name(); diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index f138a957..d2922968 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -1,11 +1,11 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import java.util.stream.Stream; import lombok.AccessLevel; -import lombok.NonNull; import lombok.RequiredArgsConstructor; /** @@ -14,21 +14,40 @@ * */ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public class OperationColumn implements DBColumn { +public final class OperationColumn implements DBColumn { - @NonNull - private final DBOperation operation; - private final Object[] args; // + private final Operator operator; + private final Object[] args; + private final JavaType type; + private Boolean aggregation; + public OperationColumn(Operator operation, Object[] args) { + this(operation, args, AUTO); + } + @Override public String sql(QueryParameterBuilder builder) { - return operation.sql(builder, args); + return operator.sql(builder, args); } + @Override + public JavaType javaType() { + return type; + } + @Override public boolean isAggregation() { - return operation.isAggregation() - || Stream.of(args).anyMatch(NestedSql::aggregation); + if(aggregation == null) { + return operator.isAggregation() + || Stream.of(args).anyMatch(NestedSql::aggregation); + } + return aggregation; + } + + //see Operator::over + OperationColumn aggregation(boolean aggregation) { + this.aggregation = aggregation; + return this; } @Override @@ -40,4 +59,5 @@ public boolean isConstant() { public String toString() { return sql(addWithValue()); } + } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java new file mode 100644 index 00000000..57d2cb1e --- /dev/null +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -0,0 +1,312 @@ +package org.usf.jquery.core; + +import static java.lang.reflect.Modifier.isStatic; +import static java.util.Optional.empty; +import static org.usf.jquery.core.JDBCType.BIGINT; +import static org.usf.jquery.core.JDBCType.DATE; +import static org.usf.jquery.core.JDBCType.DOUBLE; +import static org.usf.jquery.core.JDBCType.INTEGER; +import static org.usf.jquery.core.JDBCType.TIMESTAMP; +import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; +import static org.usf.jquery.core.JDBCType.VARCHAR; +import static org.usf.jquery.core.JDBCType.typeOf; +import static org.usf.jquery.core.JavaType.instance; +import static org.usf.jquery.core.Parameter.optional; +import static org.usf.jquery.core.Parameter.required; +import static org.usf.jquery.core.Parameter.varargs; +import static org.usf.jquery.core.Validation.requireAtLeastNArgs; + +import java.util.Optional; +import java.util.function.Function; + +/** + * + * @author u$f + * + */ +public interface Operator extends DBProcessor, NestedSql { + + String id(); + + default OperationColumn args(Object... args) { + return new OperationColumn(this, args); // no type + } + + //Arithmetic operations + + static TypedOperator plus() { + return new TypedOperator(DOUBLE, operator("+"), required(DOUBLE), required(DOUBLE)); + } + + static TypedOperator minus() { + return new TypedOperator(DOUBLE, operator("-"), required(DOUBLE), required(DOUBLE)); // date|datetime + } + + static TypedOperator multiply() { + return new TypedOperator(DOUBLE, operator("*"), required(DOUBLE), required(DOUBLE)); + } + + static TypedOperator divise() { + return new TypedOperator(DOUBLE, operator("/"), required(DOUBLE), required(DOUBLE)); + } + + //numeric functions + + static TypedOperator trunc() { + return new TypedOperator(BIGINT, function("TRUNC"), required(DOUBLE)); + } + + static TypedOperator ceil() { + return new TypedOperator(BIGINT, function("CEIL"), required(DOUBLE)); + } + + static TypedOperator floor() { + return new TypedOperator(BIGINT, function("FLOOR"), required(DOUBLE)); + } + + static TypedOperator sqrt() { + return new TypedOperator(DOUBLE, function("SQRT"), required(DOUBLE)); + } + + static TypedOperator exp() { + return new TypedOperator(DOUBLE, function("EXP"), required(DOUBLE)); + } + + static TypedOperator log() { + return new TypedOperator(DOUBLE, function("LOG"), required(DOUBLE)); + } + + static TypedOperator abs() { + return new TypedOperator(DOUBLE, function("ABS"), required(DOUBLE)); + } + + static TypedOperator mod() { + return new TypedOperator(BIGINT, function("MOD"), required(DOUBLE), required(DOUBLE)); + } + + static TypedOperator pow() { + return new TypedOperator(DOUBLE, function("POW"), required(DOUBLE), required(DOUBLE)); + } + + static TypedOperator round() { + return new TypedOperator(DOUBLE, function("ROUND"), required(DOUBLE), optional(INTEGER)); + } + + //string functions + + static TypedOperator length() { + return new TypedOperator(INTEGER, function("LENGTH"), required(VARCHAR)); + } + + static TypedOperator trim() { + return new TypedOperator(VARCHAR, function("TRIM"), required(VARCHAR)); + } + + static TypedOperator ltrim() { + return new TypedOperator(VARCHAR, function("LTRIM"), required(VARCHAR)); + } + + static TypedOperator rtrim() { + return new TypedOperator(VARCHAR, function("RTRIM"), required(VARCHAR)); + } + + static TypedOperator upper() { + return new TypedOperator(VARCHAR, function("UPPER"), required(VARCHAR)); + } + + static TypedOperator lower() { + return new TypedOperator(VARCHAR, function("LOWER"), required(VARCHAR)); + } + + static TypedOperator initcap() { + return new TypedOperator(VARCHAR, function("INITCAP"), required(VARCHAR)); + } + + static TypedOperator reverse() { + return new TypedOperator(VARCHAR, function("REVERSE"), required(VARCHAR)); + } + + static TypedOperator left() { + return new TypedOperator(VARCHAR, function("LEFT"), required(VARCHAR), required(INTEGER)); + } + + static TypedOperator right() { + return new TypedOperator(VARCHAR, function("RIGHT"), required(VARCHAR), required(INTEGER)); + } + + static TypedOperator replace() { + return new TypedOperator(VARCHAR, function("REPLACE"), required(VARCHAR), required(VARCHAR), required(VARCHAR)); //!teradata + } + + static TypedOperator oreplace() { + return new TypedOperator(VARCHAR, function("OREPLACE"), required(VARCHAR), required(VARCHAR), required(VARCHAR)); //teradata + } + + static TypedOperator substring() { //int start, int length + return new TypedOperator(VARCHAR, function("SUBSTRING"), required(VARCHAR), required(INTEGER), required(INTEGER)); + } + + static TypedOperator concat() { + return new TypedOperator(VARCHAR, function("CONCAT"), required(VARCHAR), required(VARCHAR), varargs(VARCHAR)); + } + + //temporal functions + + static TypedOperator year() { + return new TypedOperator(INTEGER, extract("YEAR"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } + + static TypedOperator month() { + return new TypedOperator(INTEGER, extract("MONTH"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } + + static TypedOperator week() { + return new TypedOperator(INTEGER, extract("WEEK"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } + + static TypedOperator day() { + return new TypedOperator(INTEGER, extract("DAY"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } + + static TypedOperator dow() { + return new TypedOperator(INTEGER, extract("DOW"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata + } + + static TypedOperator doy() { + return new TypedOperator(INTEGER, extract("DOY"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata + } + + static TypedOperator hour() { + return new TypedOperator(INTEGER, extract("HOUR"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } + + static TypedOperator minute() { + return new TypedOperator(INTEGER, extract("MINUTE"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } + + static TypedOperator second() { + return new TypedOperator(INTEGER, extract("SECOND"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } + + static TypedOperator epoch() { + return new TypedOperator(BIGINT, extract("EPOCH"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata + } + + + //cast functions + + static TypedOperator varchar() { + return new TypedOperator(VARCHAR, cast("VARCHAR"), required(), required(INTEGER)); //any + } + + static TypedOperator date() { + return new TypedOperator(DATE, cast("DATE"), required(VARCHAR, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } + + static TypedOperator integer() { + return new TypedOperator(INTEGER, cast("INTEGER"), required(VARCHAR, DOUBLE)); + } + + static TypedOperator bigint() { + return new TypedOperator(BIGINT, cast("BIGINT"), required(VARCHAR, DOUBLE)); // any number + } + + static TypedOperator decimal() { + return new TypedOperator(DOUBLE, cast("DECIMAL"), required(VARCHAR, DOUBLE), optional(INTEGER), optional(INTEGER)); + } + + //other functions + + static TypedOperator coalesce() { + return new TypedOperator(firstArgType(), function("COALESCE"), required(), required()); + } + + //aggregate functions + + static TypedOperator count() { + return new TypedOperator(BIGINT, aggregation("COUNT"), required()); + } + + static TypedOperator min() { + return new TypedOperator(firstArgType(), aggregation("MIN"), required()); + } + + static TypedOperator max() { + return new TypedOperator(firstArgType(), aggregation("MAX"), required()); + } + + static TypedOperator sum() { + return new TypedOperator(DOUBLE, aggregation("SUM"), required(DOUBLE)); + } + + static TypedOperator avg() { + return new TypedOperator(DOUBLE, aggregation("AVG"), required(DOUBLE)); + } + + //window functions + + static TypedOperator rank() { + return new TypedOperator(INTEGER, window("RANK")); // takes no args + } + + static TypedOperator rowNumber() { + return new TypedOperator(INTEGER, window("ROW_NUMBER")); // takes no args + } + + static TypedOperator denseRank() { + return new TypedOperator(INTEGER, window("DENSE_RANK")); // takes no args + } + + static TypedOperator over() { + return new TypedOperator(firstArgType(), pipe("OVER"), + required(instance(OperationColumn.class)), + required(instance(OverClause.class))) { + @Override + public OperationColumn args(Object... args) { + return super.args(args).aggregation(false); //!aggregation + } + }; + } + + static ArithmeticOperator operator(String symbol) { + return ()-> symbol; + } + + static FunctionOperator function(String name) { + return ()-> name; + } + + static ExtractFunction extract(String field) { + return ()-> field; + } + + static CastFunction cast(String type) { + return ()-> type; + } + + static WindowFunction window(String name) { + return ()-> name; + } + + static AggregateFunction aggregation(String name) { + return ()-> name; + } + + static PipeFunction pipe(String name) { + return ()-> name; + } + + static Optional lookupOperator(String op) { + try { + var m = Operator.class.getMethod(op); + if(isStatic(m.getModifiers()) && m.getReturnType() == TypedOperator.class) { // no private static + return Optional.of((TypedOperator) m.invoke(null)); + } + } catch (Exception e) {/*do not throw exception*/} + return empty(); + } + + private static Function firstArgType() { + return arr-> typeOf(requireAtLeastNArgs(1, arr, ()-> "firstArgType function")[0]); + } +} diff --git a/src/main/java/org/usf/jquery/core/OverColumn.java b/src/main/java/org/usf/jquery/core/OverColumn.java deleted file mode 100644 index 4d96014b..00000000 --- a/src/main/java/org/usf/jquery/core/OverColumn.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.usf.jquery.core; - -/** - * - * @author u$f - * - */ -public class OverColumn extends OperationColumn { - - public OverColumn(DBOperation operation, Object[] args) { - super(operation, args); - } - - @Override //!important - public boolean isAggregation() { - return false; - } - -} diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java new file mode 100644 index 00000000..8750c9f4 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -0,0 +1,61 @@ +package org.usf.jquery.core; + +import static java.util.Objects.nonNull; +import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.Utils.isEmpty; + +import java.util.stream.Stream; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * + * @author u$f + * + */ +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class Parameter { + + private final JavaType[] types; + private final boolean required; + private final boolean varargs; + + public boolean accept(Object o) { + return isEmpty(types) || Stream.of(types).anyMatch(t-> t.accept(o)); + } + + @Override + public String toString() { + return Stream.of(types) + .map(Object::toString) + .collect(joining("|")); + } + + public static Parameter required(JavaType... type) { + return new Parameter(type, true, false); + } + + public static Parameter optional(JavaType... type) { + return new Parameter(type, false, false); + } + + public static Parameter varargs(JavaType... type) { + return new Parameter(type, false, true); + } + + public static Parameter[] checkArgs(Parameter... parameters) { + if(nonNull(parameters)) { + var i = parameters.length; + while(--i>=0 && parameters[i].isRequired()); + for(; i>=0; i--) { + if(parameters[i].isRequired()) { + throw new IllegalArgumentException("optional argument"); + } + } + } + return parameters; + } +} diff --git a/src/main/java/org/usf/jquery/core/PipeFunction.java b/src/main/java/org/usf/jquery/core/PipeFunction.java new file mode 100644 index 00000000..c673820f --- /dev/null +++ b/src/main/java/org/usf/jquery/core/PipeFunction.java @@ -0,0 +1,21 @@ +package org.usf.jquery.core; + +import static java.util.Arrays.copyOfRange; +import static org.usf.jquery.core.SqlStringBuilder.SPACE; +import static org.usf.jquery.core.Validation.requireAtLeastNArgs; + +/** + * + * @author u$f + * + */ +@FunctionalInterface +public interface PipeFunction extends FunctionOperator { + + @Override + default String sql(QueryParameterBuilder builder, Object[] args) { + requireAtLeastNArgs(1, args, ()-> "Pipe function"); + return builder.appendParameter(args[0]) + SPACE + + FunctionOperator.super.sql(builder, copyOfRange(args, 1, args.length)); + } +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index b02608c9..6443224f 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -4,19 +4,17 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; import static java.util.stream.IntStream.range; import static org.usf.jquery.core.SqlStringBuilder.COMA; import static org.usf.jquery.core.SqlStringBuilder.EMPTY; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.SqlStringBuilder.member; import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.core.Utils.isPresent; import static org.usf.jquery.core.Validation.illegalArgumentIf; import java.lang.reflect.Array; import java.sql.Date; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; @@ -33,29 +31,21 @@ */ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class QueryParameterBuilder { - + + private static final String ALIAS = "t"; private static final String ARG = "?"; private final Collection args; - private List tables = new LinkedList<>(); - private boolean forceValue = false; + private final List views; //indexed - public QueryParameterBuilder tables(String... tablenames) { - if(isPresent(tablenames)){ - tables = Stream.of(tablenames).distinct().collect(toList()); + public String view(TaggableView view) { + for(int i=0; i 0 ? "t"+idx : tablename; + views.add(view); + return ALIAS + views.size(); //always add alias } public String appendParameter(Object o) { @@ -78,8 +68,13 @@ public String appendTimestamp(Object o) { return appendParameter(o, Timestamp.class, false); } - public String appendLitteral(Object o, SQLType type) { - return appendParameter(o, type.getJavaType(), true); + public String appendLitteral(Object o, JavaType type) { + return appendParameter(o, type.getType(), true); + } + + //TODO + public String appendLitteral(Object o) { + return null; } private String appendParameter(Object o, Class type, boolean addWithValue) { @@ -121,13 +116,8 @@ String formatValue(Object o) { : formatString(o); } - @Deprecated - public void forceValue(boolean forceValue) { - this.forceValue = forceValue; - } - boolean dynamic() { - return nonNull(args) && !forceValue; + return nonNull(args); } public Object[] args() { @@ -154,10 +144,14 @@ static String formatNumber(Object o) { } public static QueryParameterBuilder addWithValue() { - return new QueryParameterBuilder(null); //no args + return new QueryParameterBuilder(null, new ArrayList<>()); //no args + } + + public static QueryParameterBuilder addWithValue(QueryParameterBuilder builder) { + return new QueryParameterBuilder(null, builder.views); } public static QueryParameterBuilder parametrized() { - return new QueryParameterBuilder(new LinkedList<>()); + return new QueryParameterBuilder(new LinkedList<>(), new ArrayList<>()); } } diff --git a/src/main/java/org/usf/jquery/core/SQLType.java b/src/main/java/org/usf/jquery/core/SQLType.java deleted file mode 100644 index 20da9f53..00000000 --- a/src/main/java/org/usf/jquery/core/SQLType.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.usf.jquery.core; - -/** - * - * @author u$f - * - */ -public interface SQLType { - - int getValue(); - - Class getJavaType(); - - default boolean isAutoType() { - return false; - } - -} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/StringComparator.java b/src/main/java/org/usf/jquery/core/StringComparator.java index a16b3e7e..a9911a90 100644 --- a/src/main/java/org/usf/jquery/core/StringComparator.java +++ b/src/main/java/org/usf/jquery/core/StringComparator.java @@ -9,7 +9,7 @@ * */ @FunctionalInterface -public interface StringComparator extends DBComparator { +public interface StringComparator extends Comparator { String name(); diff --git a/src/main/java/org/usf/jquery/core/TableColumn.java b/src/main/java/org/usf/jquery/core/TableColumn.java deleted file mode 100644 index 20c0d8b4..00000000 --- a/src/main/java/org/usf/jquery/core/TableColumn.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.usf.jquery.core; - -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -/** - * - * @author u$f - * - */ -@RequiredArgsConstructor -public final class TableColumn implements TaggableColumn { - - @NonNull - private final String columnName; - @NonNull - private final String reference; - private final String tablename; - - @Override - public String sql(QueryParameterBuilder arg) { - return arg.columnFullReference(tablename, columnName); - } - - @Override - public String tagname() { - return reference; - } - - @Override - public String toString() { - return sql(addWithValue()); - } - -} diff --git a/src/main/java/org/usf/jquery/core/TaggableColumn.java b/src/main/java/org/usf/jquery/core/TaggableColumn.java index 6c3ff199..6c50a6e1 100644 --- a/src/main/java/org/usf/jquery/core/TaggableColumn.java +++ b/src/main/java/org/usf/jquery/core/TaggableColumn.java @@ -8,5 +8,5 @@ public interface TaggableColumn extends DBColumn { String tagname(); //JSON & TAG - + } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/Typed.java b/src/main/java/org/usf/jquery/core/Typed.java new file mode 100644 index 00000000..cc45bcb5 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/Typed.java @@ -0,0 +1,7 @@ +package org.usf.jquery.core; + +public interface Typed { + + JavaType javaType(); + +} diff --git a/src/main/java/org/usf/jquery/core/TypedFunction.java b/src/main/java/org/usf/jquery/core/TypedFunction.java deleted file mode 100644 index f98c4259..00000000 --- a/src/main/java/org/usf/jquery/core/TypedFunction.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.usf.jquery.core; - -import static java.util.stream.Stream.concat; -import static org.usf.jquery.core.JDBCType.AUTO_TYPE; -import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Validation.requireNArgs; -import static org.usf.jquery.core.Validation.requireNoArgs; - -import java.util.stream.Stream; - -import lombok.Getter; -import lombok.experimental.Delegate; - -/** - * - * @author u$f - * - */ -@Getter -public class TypedFunction implements DBFunction { - - @Delegate - private final DBFunction fn; - private final SQLType[] argTypes; - private final SQLType returnedType; - - private Object[] additionalArgs; - - public TypedFunction(SQLType returnedType, DBFunction fn, SQLType... argTypes) { - this.returnedType = returnedType; - this.fn = fn; - this.argTypes = argTypes; - } - - public TypedFunction additionalArgs(Object... args) { - this.additionalArgs = args; - return this; - } - - @Override //do not delegate this - public OperationColumn args(Object... args) { - return DBFunction.super.args(args); - } - - @Override - public String sql(QueryParameterBuilder builder, Object[] args) { - args = mergeArrays(args, additionalArgs); - return fn.sql(builder, isEmpty(argTypes) - ? requireNoArgs(args, fn::name) - : requireNArgs(argTypes.length, args, fn::name), i-> argTypes[i]); - } - - public int argumentCount() { - return isEmpty(argTypes) ? 0 : argTypes.length; - } - - public boolean isWindowFunction() { - return fn instanceof WindowFunction; - } - - public static TypedFunction autoTypeReturn(DBFunction fn, SQLType... argTypes) { - return new TypedFunction(AUTO_TYPE, fn, argTypes); - } - - private static Object[] mergeArrays(Object[] a1, Object[] a2) { - if(isEmpty(a1)) { - return a2; - } - if(isEmpty(a2)) { - return a1; - } - return concat(Stream.of(a1), Stream.of(a2)).toArray(); - } -} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java new file mode 100644 index 00000000..285ef49b --- /dev/null +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -0,0 +1,59 @@ +package org.usf.jquery.core; + +import static org.usf.jquery.core.Parameter.checkArgs; +import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.core.Validation.requireNoArgs; + +import java.util.function.Function; + +import lombok.Getter; +import lombok.experimental.Delegate; + +/** + * + * @author u$f + * + */ +@Getter +public class TypedOperator implements Operator { + + @Delegate + private final Operator operator; + private final Function typeFn; + private final Parameter[] parameters; + + public TypedOperator(JavaType type, Operator function, Parameter... args) { + this(o-> type, function, args); + } + + public TypedOperator(Function typeFn, Operator function, Parameter... parameter) { + this.typeFn = typeFn; + this.operator = function; + this.parameters = checkArgs(parameter); + } + + public Operator unwrap() { + return operator; + } + + @Override + public OperationColumn args(Object... args) { + // TODO check arg types + if(isEmpty(parameters)) { + requireNoArgs(args, operator::id); + } + else { + if(isEmpty(args)) { + if(parameters[0].isRequired()) { + + } + //require args + } + else { + + } + } + return new OperationColumn(operator, args, typeFn.apply(args)); + } + +} diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 6cfe7ecd..500d97f2 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -1,6 +1,7 @@ package org.usf.jquery.core; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import java.util.Collection; import java.util.Map; @@ -23,7 +24,7 @@ public static boolean isEmpty(int[] a) { } public static boolean isPresent(T[] a) { - return !isEmpty(a); + return nonNull(a) && a.length > 0; } public static boolean isEmpty(T[] a) { diff --git a/src/main/java/org/usf/jquery/core/Validation.java b/src/main/java/org/usf/jquery/core/Validation.java index 75b43012..470e6774 100644 --- a/src/main/java/org/usf/jquery/core/Validation.java +++ b/src/main/java/org/usf/jquery/core/Validation.java @@ -5,6 +5,7 @@ import java.util.Collection; import java.util.function.Supplier; +import java.util.function.UnaryOperator; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -16,15 +17,19 @@ */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class Validation { - - public static final String VARIABLE_PATTERN = "[a-zA-Z]\\w*"; - - public static String requireLegalVariable(String s) { - illegalArgumentIf(isNull(s) || !s.matches(VARIABLE_PATTERN), ()-> "illegal variable name : " + s); + + public static final String VAR_PATTERN = "[a-zA-Z]\\w*"; + + public static String requireLegalVariable(String s) { + return requireLegalVariable(s, v-> "illegal variable name : " + v); + } + + public static String requireLegalVariable(String s, UnaryOperator msg) { + illegalArgumentIf(isNull(s) || !s.matches(VAR_PATTERN), ()-> msg.apply(s)); return s; } - - public static String requireNonBlank(String s) { + + public static String requireNonBlank(String s) { illegalArgumentIf(isNull(s) || s.isBlank(), "empty string"); return s; } @@ -33,7 +38,7 @@ public static T[] requireNonEmpty(T[] arr){ illegalArgumentIf(isNull(arr) || arr.length == 0, "empty array"); return arr; } - + public static Collection requireNonEmpty(Collection c){ illegalArgumentIf(isNull(c) || c.isEmpty(), "empty collection"); return c; @@ -53,7 +58,7 @@ public static T[] requireAtLeastNArgs(int n, T[] args, Supplier name illegalArgumentIf(isNull(args) || args.length < n, ()-> name.get() + " takes at least " + n + " parameters"); return args; } - + public static T[] requireAtMostNArgs(int n, T[] args, Supplier name) { illegalArgumentIf(nonNull(args) && args.length > n, ()-> name.get() + " takes at most" + n + " parameters"); return args; diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java new file mode 100644 index 00000000..acbd0e1e --- /dev/null +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -0,0 +1,46 @@ +package org.usf.jquery.core; + +import static org.usf.jquery.core.JDBCType.AUTO; +import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.SqlStringBuilder.member; + +import lombok.RequiredArgsConstructor; + +/** + * + * @author u$f + * + */ +@RequiredArgsConstructor +public final class ViewColumn implements TaggableColumn { + + private final TaggableView view; + private final String name; + private final String tag; + private final JavaType type; + + public ViewColumn(TaggableView view, String name, String tag) { + this(view, name, tag, AUTO); + } + + @Override + public String sql(QueryParameterBuilder arg) { + return member(arg.view(view), name); + } + + @Override + public JavaType javaType() { + return type; + } + + @Override + public String tagname() { + return tag; + } + + @Override + public String toString() { + return sql(addWithValue()); + } + +} diff --git a/src/main/java/org/usf/jquery/core/WindowFunction.java b/src/main/java/org/usf/jquery/core/WindowFunction.java index 1715048e..67dd9c7d 100644 --- a/src/main/java/org/usf/jquery/core/WindowFunction.java +++ b/src/main/java/org/usf/jquery/core/WindowFunction.java @@ -6,10 +6,6 @@ * */ @FunctionalInterface -public interface WindowFunction extends DBFunction { - - static WindowFunction windowFunction(String name) { - return ()-> name; - } +public interface WindowFunction extends FunctionOperator { } diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index 0ebf42c6..1d6be39c 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -2,7 +2,9 @@ import static org.usf.jquery.core.DBColumn.column; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; +import static org.usf.jquery.core.SqlStringBuilder.member; import lombok.RequiredArgsConstructor; @@ -14,29 +16,27 @@ @RequiredArgsConstructor public final class WindowView implements TaggableView { - private final DBTable table; - private final OverColumn column; - private final String alias; + private final TaggableView view; + private final TaggableColumn column; //named operation column private final ComparisonExpression expression; @Override public String sql(QueryParameterBuilder builder, String schema) { - var tn = table.sql(builder, schema); //build tablename + var va = "v0"; return new SqlStringBuilder(100) - .append("(SELECT ").append(tn).append(".*, ") - .append(column.sql(addWithValue())) // - .append(" AS ").append(doubleQuote(alias)) - .append(" FROM ").append(tn).append(")") + .append("(SELECT ").append(va).append(".*, ") + .append(column.sql(addWithValue())).append(" AS ").append(doubleQuote(column.tagname())) + .append(" FROM ").append(view.sql(builder, schema)).append(SPACE).append(va).append(")") .toString(); } public DBFilter filter() { - return b-> expression.sql(b, column(b.columnFullReference(table.tagname(), doubleQuote(alias)))); + return b-> expression.sql(b, column(member(b.view(view), doubleQuote(column.tagname())))); } @Override public String tagname() { - return table.tagname(); + return view.tagname(); //inherits tagname } @Override diff --git a/src/main/java/org/usf/jquery/web/ArgumentParser.java b/src/main/java/org/usf/jquery/web/ArgumentParser.java index ff66e79c..20147494 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParser.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParser.java @@ -3,8 +3,18 @@ import static java.util.Objects.isNull; import static org.usf.jquery.web.ParseException.cannotParseException; +import java.math.BigDecimal; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; import java.util.stream.Stream; +import org.usf.jquery.core.JDBCType; + /** * * @author u$f @@ -27,4 +37,30 @@ default Object parse(String v) { default Object[] parseAll(String... args) { return isNull(args) ? null : Stream.of(args).map(this::parse).toArray(); } + + static ArgumentParser parserFor(JDBCType type) { + switch (type) { + case BOOLEAN: return Boolean::parseBoolean; + case BIT: return Boolean::parseBoolean; + case TINYINT: return Byte::parseByte; + case SMALLINT: return Short::parseShort; + case INTEGER: return Integer::parseInt; + case BIGINT: return Long::parseLong; + case REAL: return Float::parseFloat; + case FLOAT: return Double::parseDouble; + case DOUBLE: return Double::parseDouble; + case NUMERIC: return BigDecimal::new; + case DECIMAL: return BigDecimal::new; + case CHAR: return v-> v; + case VARCHAR: return v-> v; + case NVARCHAR: return v-> v; + case LONGNVARCHAR: return v-> v; + case DATE: return v-> Date.valueOf(LocalDate.parse(v)); + case TIME: return v-> Time.valueOf(LocalTime.parse(v)); + case TIMESTAMP: return v-> Timestamp.from(Instant.parse(v)); + case TIMESTAMP_WITH_TIMEZONE: return v-> Timestamp.from(ZonedDateTime.parse(v).toInstant()); + default: throw new UnsupportedOperationException("unsupported type " + type); + } + } + } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 30ac47ea..eaa3540e 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -2,23 +2,22 @@ import static java.util.Objects.isNull; import static org.usf.jquery.core.DBColumn.count; -import static org.usf.jquery.core.DBComparator.equal; -import static org.usf.jquery.core.DBComparator.greaterOrEqual; -import static org.usf.jquery.core.DBComparator.greaterThan; -import static org.usf.jquery.core.DBComparator.iLike; -import static org.usf.jquery.core.DBComparator.in; -import static org.usf.jquery.core.DBComparator.lessOrEqual; -import static org.usf.jquery.core.DBComparator.lessThan; -import static org.usf.jquery.core.DBComparator.like; -import static org.usf.jquery.core.DBComparator.notEqual; -import static org.usf.jquery.core.DBComparator.notIn; -import static org.usf.jquery.core.DBComparator.notLike; -import static org.usf.jquery.core.JDBCType.AUTO_TYPE; +import static org.usf.jquery.core.Comparator.equal; +import static org.usf.jquery.core.Comparator.greaterOrEqual; +import static org.usf.jquery.core.Comparator.greaterThan; +import static org.usf.jquery.core.Comparator.iLike; +import static org.usf.jquery.core.Comparator.in; +import static org.usf.jquery.core.Comparator.lessOrEqual; +import static org.usf.jquery.core.Comparator.lessThan; +import static org.usf.jquery.core.Comparator.like; +import static org.usf.jquery.core.Comparator.notEqual; +import static org.usf.jquery.core.Comparator.notIn; +import static org.usf.jquery.core.Comparator.notLike; import static org.usf.jquery.web.ParsableJDBCType.typeOf; -import org.usf.jquery.core.DBComparator; -import org.usf.jquery.core.DBFunction; -import org.usf.jquery.core.SQLType; +import org.usf.jquery.core.Comparator; +import org.usf.jquery.core.JavaType; +import org.usf.jquery.core.Operator; import org.usf.jquery.core.StringComparator; /** @@ -33,8 +32,8 @@ public interface ColumnDecorator { String reference(); //JSON - default SQLType dataType() { - return AUTO_TYPE; + default JavaType dataType() { + return null; } default String pattern() { @@ -57,7 +56,7 @@ default CriteriaBuilder criteria(String name) { return null; // no criteria by default } - default DBComparator comparator(String comparator, int nArg) { + default Comparator comparator(String comparator, int nArg) { if(isNull(comparator)) { return nArg == 1 ? equal() : in(); } @@ -78,13 +77,13 @@ default DBComparator comparator(String comparator, int nArg) { /** * override parser | format | local */ - default ArgumentParser parser(SQLType type){ + default ArgumentParser parser(JavaType type){ return type instanceof ParsableSQLType ? (ParsableSQLType) type //improve parser search : typeOf(type); } - private static DBComparator containsArgPartten(StringComparator fn) { + private static Comparator containsArgPartten(StringComparator fn) { return (b, args)-> { args[1] = "%" + args[1] + "%"; return fn.sql(b, args); @@ -92,7 +91,7 @@ private static DBComparator containsArgPartten(StringComparator fn) { } static ColumnDecorator countColumn() { - return ofColumn(DBFunction.count().name(), t-> count()); + return ofColumn(Operator.count().id(), t-> count()); } static ColumnDecorator ofColumn(String ref, ColumnBuilder cb) { diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index 1d4e7dec..edd6975a 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -1,9 +1,9 @@ package org.usf.jquery.web; +import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.Utils.UNLIMITED; -import static org.usf.jquery.web.ParsableJDBCType.AUTO_TYPE; -import org.usf.jquery.core.SQLType; +import org.usf.jquery.core.JavaType; import lombok.AccessLevel; import lombok.Getter; @@ -23,11 +23,11 @@ public final class ColumnMetadata { private final String columnName; - private SQLType dataType = AUTO_TYPE; + private JavaType dataType = AUTO; private int dataSize = UNLIMITED; ColumnMetadata reset() { - this.dataType = AUTO_TYPE; + this.dataType = AUTO; this.dataSize = UNLIMITED; return this; } diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index 9de47ded..60a6a1ec 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -22,7 +22,7 @@ public final class Constants { public static final String PARTITION = "partition"; //not res static final Set RESERVED_WORDS = - Set.of(COLUMN, COLUMN_DISTINCT, ORDER, REVISION, REVISION_MODE); + Set.of(COLUMN, COLUMN_DISTINCT, ORDER, REVISION, REVISION_MODE); //metadata ? static final YearMonth[] EMPTY_REVISION = new YearMonth[0]; //not standard diff --git a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java index fa557e48..ad82ad38 100644 --- a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java +++ b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java @@ -8,7 +8,7 @@ import java.util.stream.Stream; import org.usf.jquery.core.ComparisonExpression; -import org.usf.jquery.core.DBComparator; +import org.usf.jquery.core.Comparator; import org.usf.jquery.core.LogicalOperator; /** @@ -34,7 +34,7 @@ default ComparisonExpression build(T... args) { .orElseThrow(); } - public static CriteriaBuilder ofComparator(DBComparator cmp) { + public static CriteriaBuilder ofComparator(Comparator cmp) { return cmp::expression; } } diff --git a/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java b/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java index d82c5da9..3685704c 100644 --- a/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java +++ b/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java @@ -7,7 +7,7 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.regex.Pattern.compile; -import static org.usf.jquery.core.DBFunction.lookupFunction; +import static org.usf.jquery.core.Operator.lookupOperator; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.isBlank; import static org.usf.jquery.core.Utils.isEmpty; @@ -29,8 +29,11 @@ import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBOrder; +import org.usf.jquery.core.OperationColumn; +import org.usf.jquery.core.Operator; import org.usf.jquery.core.OverClause; import org.usf.jquery.core.TypedFunction; +import org.usf.jquery.core.TypedOperator; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -113,7 +116,7 @@ private static ColumnDecorator requireColumn(RequestEntry re) { if("count".equals(re.requireNoArgFunction())) { // not arguments return countColumn(); } - var fn = lookupFunction(snakeToCamelCase(re.getName())); + var fn = lookupOperator(snakeToCamelCase(re.getName())); if(fn.filter(TypedFunction::isWindowFunction).isPresent()) { return ofColumn(re.requireNoArgFunction().toLowerCase(), t-> fn.get().args()); } @@ -126,28 +129,26 @@ private static ColumnDecorator requireColumn(RequestEntry re) { *
  • over function
  • * */ - private static TypedFunction requireFunction(RequestEntry re, TableDecorator td) { - var fn = lookupFunction(re.getName()) + private static OperationColumn requireFunction(RequestEntry re, TableDecorator td) { + var fn = lookupOperator(re.getName()) .orElseThrow(()-> cannotEvaluateException("function", re.getName())); - if("OVER".equals(fn.name())) { + if("OVER".equals(fn.id())) { //map arg function var args = re.getArgs(); if(isNull(args) || !args.containsKey(ARRAY_ARGS_KEY)) { //named arguments function - return fn.additionalArgs(overClause(td, isNull(args) ? emptyMap() : args)); + return fn.args(overClause(td, isNull(args) ? emptyMap() : args)); } throw new UnsupportedOperationException("over function require named args"); } - //array arguments functions - parseEntry(fn, re, td); - return fn; + return parseEntry(fn, re, td); } - private static void parseEntry(TypedFunction fn, RequestEntry re, TableDecorator td){ + private static OperationColumn parseEntry(Operator fn, RequestEntry re, TableDecorator td){ if(!isEmpty(re.getArgs()) && !re.getArgs().containsKey(ARRAY_ARGS_KEY)) { throw new UnsupportedOperationException("functions does not support named args"); } if(fn.argumentCount() <= 1) { //1st argument ignored if(!isEmpty(re.getArgs())) { - throw new IllegalArgumentException(fn.name() + " takes no arguments"); + throw new IllegalArgumentException(fn.id() + " takes no arguments"); } } else { @@ -171,10 +172,10 @@ private static void parseEntry(TypedFunction fn, RequestEntry re, TableDecorator } } } - fn.additionalArgs(args.toArray()); + return fn.args(args.toArray()); } else { - throw new IllegalArgumentException(fn.name() + " takes " + n + " argument(s)"); + throw new IllegalArgumentException(fn.id() + " takes " + n + " argument(s)"); } } } @@ -221,7 +222,7 @@ private static List parseLinkedEntries(String s, boolean mul if(to == s.length()) { break; } - if(c == '(') { + if(c == '(') { // operator var jmp = s.indexOf(')', ++to); if(jmp > -1) { var nest = s.indexOf('(', to); @@ -238,7 +239,7 @@ private static List parseLinkedEntries(String s, boolean mul } c = s.charAt(to); } - if(c == ':') { + if(c == ':') { // tag var jmp = s.indexOf(',', ++to); if(jmp == -1) { jmp = s.length(); @@ -247,21 +248,22 @@ private static List parseLinkedEntries(String s, boolean mul if((to = jmp) == s.length()) { break; } - c = s.charAt(to); + c = s.charAt(to); //else throw exception } if(c == ',' && multiple) { res.add(new LinkedRequestEntry()); } - else if(c != '.') { + else if(c != '.') { //not accessor throw new IllegalArgumentException("'" + s.charAt(to) + "' not valid at index=" + to); } - if((from = ++to) == s.length()) { + if((from = ++to) == s.length()) { //ends with dot throw new IllegalArgumentException("'" + s.charAt(to-1) + "' not allowed at the end"); } } return res; } + private static final String ARG_PATTERN = "\\w+(\\.\\w*)*"; private static Map> parseArgs(String s) { @@ -306,6 +308,9 @@ else if(c == ',') { } return map; } + + + private static boolean legalArgChar(char c) { //later next version return legalVariableChar(c) || c == '.'; diff --git a/src/main/java/org/usf/jquery/web/ParsableJDBCType.java b/src/main/java/org/usf/jquery/web/ParsableJDBCType.java index cb8a5d45..4f3af67a 100644 --- a/src/main/java/org/usf/jquery/web/ParsableJDBCType.java +++ b/src/main/java/org/usf/jquery/web/ParsableJDBCType.java @@ -14,7 +14,7 @@ import java.util.List; import org.usf.jquery.core.JDBCType; -import org.usf.jquery.core.SQLType; +import org.usf.jquery.core.JavaType; import lombok.RequiredArgsConstructor; import lombok.experimental.Delegate; @@ -54,7 +54,7 @@ public enum ParsableJDBCType implements ParsableSQLType { DATE, TIME, TIMESTAMP); //else string @Delegate - private final SQLType type; + private final JavaType type; @Delegate private final ArgumentParser parser; //isAutoType delegated @@ -67,7 +67,7 @@ public static ParsableSQLType typeOf(int type) { return unparsableType(type); } - public static ParsableSQLType typeOf(SQLType type) { + public static ParsableSQLType typeOf(JavaType type) { for(var t : values()) { if(t.type == type) { return t; diff --git a/src/main/java/org/usf/jquery/web/ParsableSQLType.java b/src/main/java/org/usf/jquery/web/ParsableSQLType.java index b8757157..874d49f2 100644 --- a/src/main/java/org/usf/jquery/web/ParsableSQLType.java +++ b/src/main/java/org/usf/jquery/web/ParsableSQLType.java @@ -1,6 +1,6 @@ package org.usf.jquery.web; -import org.usf.jquery.core.SQLType; +import org.usf.jquery.core.JavaType; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -10,14 +10,14 @@ * @author u$f * */ -public interface ParsableSQLType extends SQLType, ArgumentParser { +public interface ParsableSQLType extends JavaType, ArgumentParser { public static ParsableSQLType unparsableType(int type) { return new UnparsableJDBCType(type, Object.class); } - public static ParsableSQLType unparsableType(SQLType type) { - return new UnparsableJDBCType(type.getValue(), type.getJavaType()); + public static ParsableSQLType unparsableType(JavaType type) { + return new UnparsableJDBCType(type.getValue(), type.getType()); } @Getter diff --git a/src/main/java/org/usf/jquery/web/RequestColumn.java b/src/main/java/org/usf/jquery/web/RequestColumn.java index a38b1f7e..ffe9a7a2 100644 --- a/src/main/java/org/usf/jquery/web/RequestColumn.java +++ b/src/main/java/org/usf/jquery/web/RequestColumn.java @@ -15,10 +15,10 @@ import org.usf.jquery.core.BasicComparator; import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.DBColumn; -import org.usf.jquery.core.DBComparator; +import org.usf.jquery.core.Comparator; import org.usf.jquery.core.DBOrder; import org.usf.jquery.core.InCompartor; -import org.usf.jquery.core.SQLType; +import org.usf.jquery.core.JavaType; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedFunction; @@ -54,9 +54,9 @@ public String reference() { } @Override - public SQLType dataType() { + public JavaType dataType() { var i = fns.size(); - while(--i>=0 && fns.get(i).getReturnedType().isAutoType()); + while(--i>=0 && fns.get(i).getReturnedType() == null); return i<0 ? cd.dataType() : fns.get(i).getReturnedType(); } @@ -64,12 +64,12 @@ public SQLType dataType() { public ColumnBuilder builder() { return t-> fns.stream().reduce( (DBColumn) t.column(cd), - (c, fn)-> fn.args(c), + (c, fn)-> fn.function(c), (c1,c2)-> c1); //combiner -> sequentially collect } @Override - public DBComparator comparator(String comparator, int nArg) { + public Comparator comparator(String comparator, int nArg) { return fns.isEmpty() //cannot apply column comparator on function ? cd.comparator(comparator, nArg) : ColumnDecorator.super.comparator(comparator, nArg); @@ -83,7 +83,7 @@ public CriteriaBuilder criteria(String name) { } @Override - public ArgumentParser parser(SQLType type) { + public ArgumentParser parser(JavaType type) { return fns.isEmpty() //cannot apply column parser on function ? cd.parser(type) : ColumnDecorator.super.parser(type); @@ -98,7 +98,7 @@ ComparisonExpression expression(String... values) { var cmp = comparator(exp, values.length); if(nonNull(cmp)) { var type = dataType(); - if(type.isAutoType()) { // logical column type can be set in table + if(type == null) { // logical column type can be set in table type = td.columnType(cd).orElse(type); } //else : overridden var pars = requireNonNull(parser(type)); diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java new file mode 100644 index 00000000..a7665832 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -0,0 +1,166 @@ +package org.usf.jquery.web; + +import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.Validation.VAR_PATTERN; +import static org.usf.jquery.core.Validation.requireLegalVariable; + +import java.util.LinkedList; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * + * @author u$f + * + */ +public final class RequestParser { + + private final String s; + private int idx; + private int size; + private char c; + + private RequestParser(String s) { + this.s = s; + size = s.length(); + } + + public static RequestEntry parse(String s) { + return new RequestParser(s).parseEntry(false, true); + } + + private RequestEntry parseEntry(boolean argument, boolean tag) { + var entry = new RequestEntry(argument + ? jmpVal() //null value + : requireLegalVariable(jmpVar(), v-> "illegal variable name : '" + v + "'")); + if(c == '(') { //operation + shift(); + if(idx < size) { + entry.initArgs(); + if(c != ')') { + do { + entry.getArgs().add(parseEntry(true, tag)); + } while(idx < size && (c=s.charAt(idx)) == ',' && ++idx < size); + } + if(c == ')') { // !else + shift(); + } + else { + throw new IllegalArgumentException("')' expected"); + } + } + else { + throw new IllegalArgumentException("')' expected"); + } + } + if(c == '.') { + shift(); + entry.setNext(parseEntry(argument, tag)); + } + if(c == ':' && tag) { + shift(); + entry.setTag(requireLegalVariable(jmpVar(), v-> "illegal variable name : '" + v + "'")); + } + if(c == ',' || (idx == size && c == 0) || (c == ')' && argument)) { + return entry; + } + throw new IllegalArgumentException("unexpected character '" + c + "' at index=" + idx); + } + + private String jmpVal() { + var from = idx; + ChartPredicate pr; + if(s.charAt(from) == '"') { + shift(); + pr = RequestParser::legalAnyChar; //accept any character + } + else { + var v = jmpVar(); + if((idx == size || s.charAt(idx) == '.' || !legalVarChar(c)) && v.matches(VAR_PATTERN)) { + return v; + } + pr = RequestParser::legalValChar; //auto switch to value + } + jmp(pr); + if(s.charAt(from) != '"') { + return s.substring(from, idx); + } + if(c == '"') { + shift(); + return s.substring(from+1, idx-1); + } + else { + throw new IllegalArgumentException("'\"' expected before '" + c + "'" ); //end + } + } + + private String jmpVar() { + var from = idx; + jmp(RequestParser::legalVarChar); + return s.substring(from, idx); + } + + private void jmp(ChartPredicate pr) { + while(idx= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z' ) || (c >= '0' && c <= '9') || c == '_'; + } + + private static boolean legalAnyChar(char c) { + return c != '"' && c != '\'' && c != '&' && c != '?' && c != '='; //avoid sql injection & http reserved symbol + } + + @Setter + @Getter + @NoArgsConstructor + static final class RequestEntry { + + private String name; + private RequestEntry next; + private LinkedList args; + private String tag; + + public RequestEntry(String name) { + this.name = name; + } + + public void initArgs() { + args = new LinkedList<>(); + } + + @Override + public String toString() { + var s = name; + if(args != null){ + s += args.stream().map(RequestEntry::toString).collect(joining(",", "(", ")")); + } + if(next != null) { + s += "." + next.toString(); + } + return tag == null ? s : s + ":" + tag; + } + } + + @FunctionalInterface + interface ChartPredicate { + boolean test(char c); + } +} diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index ab427d1e..cc23e206 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -29,9 +29,10 @@ import org.usf.jquery.core.NamedColumn; import org.usf.jquery.core.OverColumn; import org.usf.jquery.core.RequestQueryBuilder; -import org.usf.jquery.core.SQLType; -import org.usf.jquery.core.TableColumn; +import org.usf.jquery.core.JavaType; import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.TaggableView; +import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.WindowView; /** @@ -47,24 +48,24 @@ public interface TableDecorator { Optional columnName(ColumnDecorator cd); - default DBTable table() { + default TaggableView table() { return new DBTable(tableName(), identity()); } - default Optional columnType(ColumnDecorator cd) { + default Optional columnType(ColumnDecorator cd) { return metadata().columnMetada(cd) .map(ColumnMetadata::getDataType); //else not binded } - default TaggableColumn column(ColumnDecorator column) { - if(nonNull(column.builder())) { - return column.builder().column(this).as(column.reference()); + default TaggableColumn column(ColumnDecorator cd) { + if(nonNull(cd.builder())) { + return cd.builder().column(this).as(cd.reference()); } - var cn = columnName(column); + var cn = columnName(cd); if(cn.isPresent()) { - return new TableColumn(requireLegalVariable(cn.get()), column.reference(), identity()); + return new ViewColumn(table(), requireLegalVariable(cn.get()), cd.reference(), columnType(cd).orElse(null)); } - throw undeclaredResouceException(identity(), column.identity()); + throw undeclaredResouceException(identity(), cd.identity()); } default RequestQueryBuilder query(Map parameterMap) { @@ -92,10 +93,8 @@ default void parseViews(RequestQueryBuilder query, Map paramet var entry = c.get(0); var rc = decodeSingleColumn(entry.getKey(), this, true); //allow comparator var nc = (NamedColumn) rc.toColumn(); - var oc = nc.unwrap(); if(oc instanceof OverColumn) { - var wv = new WindowView(rc.tableDecorator().table(), (OverColumn) oc, - nc.tagname(), rc.expression(entry.getValue())); + var wv = new WindowView(rc.tableDecorator().table(), nc, rc.expression(entry.getValue())); query.tables(wv).filters(wv.filter()); parameters.remove(entry.getKey()); } diff --git a/src/main/java/org/usf/jquery/web/TableMetadata.java b/src/main/java/org/usf/jquery/web/TableMetadata.java index 7279dfaa..a816d698 100644 --- a/src/main/java/org/usf/jquery/web/TableMetadata.java +++ b/src/main/java/org/usf/jquery/web/TableMetadata.java @@ -7,9 +7,9 @@ import static java.util.Optional.empty; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; +import static org.usf.jquery.core.JDBCType.fromDataType; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.web.JQueryContext.database; -import static org.usf.jquery.web.ParsableJDBCType.typeOf; import java.sql.DatabaseMetaData; import java.sql.SQLException; @@ -61,7 +61,7 @@ void fetch(DatabaseMetaData metadata) throws SQLException { do { var meta = dbMap.remove(rs.getString("COLUMN_NAME")); if(nonNull(meta)) { - meta.setDataType(typeOf(rs.getInt("DATA_TYPE"))); + meta.setDataType(fromDataType(rs.getInt("DATA_TYPE"))); meta.setDataSize(rs.getInt("COLUMN_SIZE")); }// else undeclared column } while(rs.next()); diff --git a/src/main/java/org/usf/jquery/web/view/Chart2DView.java b/src/main/java/org/usf/jquery/web/view/Chart2DView.java index f26dc1d9..a4a3c3f7 100644 --- a/src/main/java/org/usf/jquery/web/view/Chart2DView.java +++ b/src/main/java/org/usf/jquery/web/view/Chart2DView.java @@ -15,7 +15,6 @@ import java.nio.file.Paths; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.Map; import java.util.Map.Entry; import org.usf.jquery.core.MappingException; diff --git a/src/test/java/org/usf/jquery/web/RequestParserTest.java b/src/test/java/org/usf/jquery/web/RequestParserTest.java new file mode 100644 index 00000000..c0519224 --- /dev/null +++ b/src/test/java/org/usf/jquery/web/RequestParserTest.java @@ -0,0 +1,56 @@ +package org.usf.jquery.web; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.usf.jquery.web.RequestParser.parse; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +/** + * + * @author u$f + * + */ +class RequestParserTest { + + @ParameterizedTest + @ValueSource(strings = { + "col123", + "c.n", + "noArgFn()", + "fn(a,b,c,d)", + "trunc(3.55,44)", + "mod(abs(trunc(exp())))", + "co1.mod(6.acc).trunc(3).plus(100)", //6.acc as value + "co1.concat(co1.trunc(2).string(10), 1234)", //6.acc as value + "co1.concat(,123)", //null value + "aa.fn(3.3,b,c,d)", + "aa.fn(2020-01-01,b,c,d)", +// "aa.fn(\"a:3'\",b,c,d)", //pass + }) + void testParse(String s) { + assertEquals(s, parse(s).toString()); + assertEquals(s+=":tag", parse(s).toString()); + } + + @ParameterizedTest + @ValueSource(strings = { + "column)", + "column(", + "column(1,2,3", + "column(1,2,3))", + "column:", + "column.", + "1ab", + "123", + "123(a,b,c)", + "123.abc", + "aa.fn(a:3,b,c,d)", + "aa.fn(\"a:3,b,c,d)", + "aa.fn(\"a:3,b,c,d&\")", + }) + void testParse2(String s) { + assertThrows(IllegalArgumentException.class, ()-> parse(s)); + } +} From edd4a6dc5ddaa6982bb089686f47b1cbcb4e4061 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 14 Dec 2023 01:28:45 +0100 Subject: [PATCH 014/298] edit --- src/main/java/org/usf/jquery/web/RequestParser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index a7665832..c3112693 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -24,7 +24,7 @@ public final class RequestParser { private RequestParser(String s) { this.s = s; - size = s.length(); + this.size = s.length(); } public static RequestEntry parse(String s) { @@ -39,7 +39,7 @@ private RequestEntry parseEntry(boolean argument, boolean tag) { shift(); if(idx < size) { entry.initArgs(); - if(c != ')') { + if(c != ')') { //TODO parse map do { entry.getArgs().add(parseEntry(true, tag)); } while(idx < size && (c=s.charAt(idx)) == ',' && ++idx < size); From e5eb2df61137bde44c24d71335295d2039ee14bb Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 14 Dec 2023 09:45:40 +0100 Subject: [PATCH 015/298] edit --- .../org/usf/jquery/core/BasicComparator.java | 4 ---- .../java/org/usf/jquery/core/Comparator.java | 22 ++++++++++++++----- .../java/org/usf/jquery/core/InCompartor.java | 4 ---- .../org/usf/jquery/core/NullComparator.java | 4 ---- .../org/usf/jquery/core/StringComparator.java | 5 ----- .../org/usf/jquery/web/RequestParser.java | 4 +++- 6 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/BasicComparator.java b/src/main/java/org/usf/jquery/core/BasicComparator.java index a8271393..8674183b 100644 --- a/src/main/java/org/usf/jquery/core/BasicComparator.java +++ b/src/main/java/org/usf/jquery/core/BasicComparator.java @@ -17,8 +17,4 @@ default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, BasicComparator.class::getSimpleName); return builder.appendParameter(args[0]) + symbol() + builder.appendParameter(args[1]); } - - static BasicComparator basicComparator(final String name) { - return ()-> name; - } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index cf76f262..4cc4e4d7 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -1,10 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.BasicComparator.basicComparator; -import static org.usf.jquery.core.InCompartor.inComparator; -import static org.usf.jquery.core.NullComparator.nullComparator; -import static org.usf.jquery.core.StringComparator.stringComparator; - /** * * @author u$f @@ -72,5 +67,20 @@ static InCompartor in() { static InCompartor notIn() { return inComparator("NOT IN"); } - + + static BasicComparator basicComparator(final String name) { + return ()-> name; + } + + static StringComparator stringComparator(final String name) { + return ()-> name; + } + + static NullComparator nullComparator(final String name) { + return ()-> name; + } + + static InCompartor inComparator(final String name) { + return ()-> name; + } } diff --git a/src/main/java/org/usf/jquery/core/InCompartor.java b/src/main/java/org/usf/jquery/core/InCompartor.java index 32ebf256..2a2de6b5 100644 --- a/src/main/java/org/usf/jquery/core/InCompartor.java +++ b/src/main/java/org/usf/jquery/core/InCompartor.java @@ -21,8 +21,4 @@ default String sql(QueryParameterBuilder builder, Object[] args) { var params = copyOfRange(args, 1, args.length); return builder.appendParameter(args[0]) + SPACE + name() + parenthese(builder.appendArray(params)); } - - static InCompartor inComparator(final String name) { - return ()-> name; - } } diff --git a/src/main/java/org/usf/jquery/core/NullComparator.java b/src/main/java/org/usf/jquery/core/NullComparator.java index 273d3857..87f547ee 100644 --- a/src/main/java/org/usf/jquery/core/NullComparator.java +++ b/src/main/java/org/usf/jquery/core/NullComparator.java @@ -18,8 +18,4 @@ default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(1, args, NullComparator.class::getSimpleName); return builder.appendParameter(args[0]) + SPACE + name(); } - - static NullComparator nullComparator(final String name) { - return ()-> name; - } } diff --git a/src/main/java/org/usf/jquery/core/StringComparator.java b/src/main/java/org/usf/jquery/core/StringComparator.java index a9911a90..731ecb19 100644 --- a/src/main/java/org/usf/jquery/core/StringComparator.java +++ b/src/main/java/org/usf/jquery/core/StringComparator.java @@ -18,9 +18,4 @@ default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, String.class::getSimpleName); return builder.appendString(args[0]) + space(name()) + builder.appendString(args[1]); } - - static StringComparator stringComparator(final String name) { - return ()-> name; - } - } diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index c3112693..7f896d3c 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -5,6 +5,7 @@ import static org.usf.jquery.core.Validation.requireLegalVariable; import java.util.LinkedList; +import java.util.List; import lombok.Getter; import lombok.NoArgsConstructor; @@ -21,6 +22,7 @@ public final class RequestParser { private int idx; private int size; private char c; + private List entries; private RequestParser(String s) { this.s = s; @@ -39,7 +41,7 @@ private RequestEntry parseEntry(boolean argument, boolean tag) { shift(); if(idx < size) { entry.initArgs(); - if(c != ')') { //TODO parse map + if(c != ')') { //TD parse map do { entry.getArgs().add(parseEntry(true, tag)); } while(idx < size && (c=s.charAt(idx)) == ',' && ++idx < size); From 825fa956b1007fb20bc8a7a74e9da77fcdb8b8aa Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 19 Dec 2023 20:56:30 +0100 Subject: [PATCH 016/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 6 + .../java/org/usf/jquery/core/Operator.java | 2 +- .../java/org/usf/jquery/core/Parameter.java | 17 +- .../org/usf/jquery/core/TypedOperator.java | 13 +- .../org/usf/jquery/web/ArgumentParser.java | 46 +---- .../org/usf/jquery/web/ArgumentParsers.java | 83 +++++++++ .../java/org/usf/jquery/web/RequestEntry.java | 138 ++++++++++++++ .../org/usf/jquery/web/RequestParser.java | 171 ++++++++---------- .../org/usf/jquery/web/RequestParserTest.java | 46 +++-- 9 files changed, 371 insertions(+), 151 deletions(-) create mode 100644 src/main/java/org/usf/jquery/web/ArgumentParsers.java create mode 100644 src/main/java/org/usf/jquery/web/RequestEntry.java diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 5ddbf217..f07cf9c2 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -1,9 +1,11 @@ package org.usf.jquery.core; +import static java.util.Optional.empty; import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; +import java.util.Optional; import java.util.function.Supplier; import org.usf.jquery.core.CaseSingleColumnBuilder.WhenFilterBridge; @@ -231,4 +233,8 @@ static OperationColumn lower(Object arg) { static OperationColumn substring(Object arg, int start, int length) { return Operator.substring().args(arg, start, length); } + + static Optional lookupColumnFunction() { + return empty(); + } } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 57d2cb1e..2db33d4b 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -259,7 +259,7 @@ static TypedOperator denseRank() { static TypedOperator over() { return new TypedOperator(firstArgType(), pipe("OVER"), - required(instance(OperationColumn.class)), + required(instance(OperationColumn.class)), // TODO wrap => aggreagation || window required(instance(OverClause.class))) { @Override public OperationColumn args(Object... args) { diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 8750c9f4..ab915f5e 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -2,6 +2,7 @@ import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.Utils.isEmpty; import java.util.stream.Stream; @@ -22,6 +23,10 @@ public final class Parameter { private final JavaType[] types; private final boolean required; private final boolean varargs; + + public JavaType[] getTypes() { + return isEmpty(types) ? new JavaType[] {AUTO} : types; + } public boolean accept(Object o) { return isEmpty(types) || Stream.of(types).anyMatch(t-> t.accept(o)); @@ -34,16 +39,16 @@ public String toString() { .collect(joining("|")); } - public static Parameter required(JavaType... type) { - return new Parameter(type, true, false); + public static Parameter required(JavaType... types) { + return new Parameter(types, true, false); } - public static Parameter optional(JavaType... type) { - return new Parameter(type, false, false); + public static Parameter optional(JavaType... types) { + return new Parameter(types, false, false); } - public static Parameter varargs(JavaType... type) { - return new Parameter(type, false, true); + public static Parameter varargs(JavaType... types) { + return new Parameter(types, false, true); } public static Parameter[] checkArgs(Parameter... parameters) { diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 285ef49b..3e340edf 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -4,6 +4,7 @@ import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireNoArgs; +import java.awt.image.renderable.ParameterBlock; import java.util.function.Function; import lombok.Getter; @@ -23,7 +24,7 @@ public class TypedOperator implements Operator { private final Parameter[] parameters; public TypedOperator(JavaType type, Operator function, Parameter... args) { - this(o-> type, function, args); + this(o-> type, function, args == null ? new Parameter[0] : args); } public TypedOperator(Function typeFn, Operator function, Parameter... parameter) { @@ -56,4 +57,14 @@ public OperationColumn args(Object... args) { return new OperationColumn(operator, args, typeFn.apply(args)); } + public int requireArgCount() { + var i=0; + while(i 0 && parameters[parameters.length-1].isVarargs(); + } + } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParser.java b/src/main/java/org/usf/jquery/web/ArgumentParser.java index 20147494..3b7d4c11 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParser.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParser.java @@ -3,18 +3,8 @@ import static java.util.Objects.isNull; import static org.usf.jquery.web.ParseException.cannotParseException; -import java.math.BigDecimal; -import java.sql.Date; -import java.sql.Time; -import java.sql.Timestamp; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.ZonedDateTime; import java.util.stream.Stream; -import org.usf.jquery.core.JDBCType; - /** * * @author u$f @@ -25,6 +15,10 @@ public interface ArgumentParser { Object nativeParse(String v); + default Object[] parseAll(String... args) { + return isNull(args) ? null : Stream.of(args).map(this::parse).toArray(); + } + default Object parse(String v) { try { return nativeParse(v); @@ -33,33 +27,13 @@ default Object parse(String v) { throw cannotParseException("parameter value", v, e); } } - - default Object[] parseAll(String... args) { - return isNull(args) ? null : Stream.of(args).map(this::parse).toArray(); - } - static ArgumentParser parserFor(JDBCType type) { - switch (type) { - case BOOLEAN: return Boolean::parseBoolean; - case BIT: return Boolean::parseBoolean; - case TINYINT: return Byte::parseByte; - case SMALLINT: return Short::parseShort; - case INTEGER: return Integer::parseInt; - case BIGINT: return Long::parseLong; - case REAL: return Float::parseFloat; - case FLOAT: return Double::parseDouble; - case DOUBLE: return Double::parseDouble; - case NUMERIC: return BigDecimal::new; - case DECIMAL: return BigDecimal::new; - case CHAR: return v-> v; - case VARCHAR: return v-> v; - case NVARCHAR: return v-> v; - case LONGNVARCHAR: return v-> v; - case DATE: return v-> Date.valueOf(LocalDate.parse(v)); - case TIME: return v-> Time.valueOf(LocalTime.parse(v)); - case TIMESTAMP: return v-> Timestamp.from(Instant.parse(v)); - case TIMESTAMP_WITH_TIMEZONE: return v-> Timestamp.from(ZonedDateTime.parse(v).toInstant()); - default: throw new UnsupportedOperationException("unsupported type " + type); + default Object tryParse(String v) { + try { + return nativeParse(v); + } + catch(Exception e) { + return null; } } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java new file mode 100644 index 00000000..5f42514a --- /dev/null +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -0,0 +1,83 @@ +package org.usf.jquery.web; + +import static org.usf.jquery.core.JDBCType.AUTO; +import static org.usf.jquery.core.JDBCType.BIGINT; +import static org.usf.jquery.core.JDBCType.DATE; +import static org.usf.jquery.core.JDBCType.DOUBLE; +import static org.usf.jquery.core.JDBCType.TIME; +import static org.usf.jquery.core.JDBCType.TIMESTAMP; +import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; + +import java.math.BigDecimal; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; + +import org.usf.jquery.core.JDBCType; +import org.usf.jquery.core.JavaType; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * + * + * @author u$f + * + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ArgumentParsers { + + private static final ArgumentParser[] DEFAULT = { + jdbcTypeParser(BIGINT), jdbcTypeParser(DOUBLE), + jdbcTypeParser(DATE), jdbcTypeParser(TIMESTAMP), + jdbcTypeParser(TIME), jdbcTypeParser(TIMESTAMP_WITH_TIMEZONE)}; + + public static ArgumentParser javaTypeParser(JavaType type) { + if(type instanceof JDBCType) { + return ArgumentParsers.jdbcTypeParser((JDBCType) type); + } + if(type == AUTO) { + return v-> { + for(var p : DEFAULT) { + var o = p.tryParse(v); + if(o != null) { + return o; + } + } + return v; + }; + } + throw new UnsupportedOperationException("unsupported type " + type); + } + + public static ArgumentParser jdbcTypeParser(JDBCType type) { + switch (type) { + case BOOLEAN: return Boolean::parseBoolean; + case BIT: return Boolean::parseBoolean; + case TINYINT: return Byte::parseByte; + case SMALLINT: return Short::parseShort; + case INTEGER: return Integer::parseInt; + case BIGINT: return Long::parseLong; + case REAL: return Float::parseFloat; + case FLOAT: return Double::parseDouble; + case DOUBLE: return Double::parseDouble; + case NUMERIC: return BigDecimal::new; + case DECIMAL: return BigDecimal::new; + case CHAR: return v-> v; + case VARCHAR: return v-> v; + case NVARCHAR: return v-> v; + case LONGNVARCHAR: return v-> v; + case DATE: return v-> Date.valueOf(LocalDate.parse(v)); + case TIME: return v-> Time.valueOf(LocalTime.parse(v)); + case TIMESTAMP: return v-> Timestamp.from(Instant.parse(v)); + case TIMESTAMP_WITH_TIMEZONE: return v-> Timestamp.from(ZonedDateTime.parse(v).toInstant()); + default: throw new UnsupportedOperationException("unsupported type " + type); + } + } + +} diff --git a/src/main/java/org/usf/jquery/web/RequestEntry.java b/src/main/java/org/usf/jquery/web/RequestEntry.java new file mode 100644 index 00000000..66df9451 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/RequestEntry.java @@ -0,0 +1,138 @@ +package org.usf.jquery.web; + +import static java.lang.Math.min; +import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.DBColumn.lookupColumnFunction; +import static org.usf.jquery.core.Operator.lookupOperator; +import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; +import static org.usf.jquery.core.Validation.VAR_PATTERN; +import static org.usf.jquery.web.ArgumentParsers.javaTypeParser; +import static org.usf.jquery.web.JQueryContext.context; + +import java.util.ArrayList; +import java.util.List; + +import org.usf.jquery.core.DBColumn; +import org.usf.jquery.core.JavaType; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@RequiredArgsConstructor +final class RequestEntry { + + private final String value; + private final boolean unparsable; //"string" + private RequestEntry next; + private List args; + private String tag; + + public RequestEntry(String value) { + this(value, false); + } + + public DBColumn toColumn(TableDecorator td) { //columnName == viewName + DBColumn c = null; + RequestEntry nxt = requireNoArgs(); + if(next != null && context().isDeclaredTable(value) && context().isDeclaredColumn(next.value)) { + nxt = next.requireNoArgs(); + c = context().getTable(value).column(context().getColumn(nxt.value)); + } + else if(context().isDeclaredColumn(value)) { + c = td.column(context().getColumn(value)); + } + else { + c = lookupColumnFunction().orElse(null); + } + return c == null ? null : nxt.next(td, c); + } + + private Object toArg(TableDecorator td, JavaType... types) { + if(value == null) { + return requireNoArgs().value; + } + if(value.matches(VAR_PATTERN)) { + var c = toColumn(td); + if(c != null) { + return c; // type will be checked later + } + } + if(!unparsable) { + requireNoArgs(); + for(var t : types) { + var o = javaTypeParser(t).tryParse(value); + if(o != null) { + return o; + } + } + throw new ParseException("cannot parse value : " + value); + } + for(var t : types) { + if(t.accept(value)) { + return value; + } + } + throw new ParseException("illegal value : " + value); + } + + DBColumn toOperation(TableDecorator td, DBColumn prev) { + var res = lookupOperator(value); + if(res.isPresent()) { + var op = res.get(); + var min = op.requireArgCount(); + var np = args.size()+1; + if(np < min || (!op.isVarags() && np > op.getParameters().length)) { + throw new IllegalArgumentException(); + } + var params = new ArrayList(np); + params.add(prev); + var i=1; + for(; i entries; private RequestParser(String s) { this.s = s; @@ -30,94 +24,110 @@ private RequestParser(String s) { } public static RequestEntry parse(String s) { - return new RequestParser(s).parseEntry(false, true); + return new RequestParser(s).parseEntry(false, false); + } + + public static List parseEntries(String s) { + return new RequestParser(s).parseEntries(true, false); + } + + private List parseEntries(boolean multiple, boolean argument) { + var entries = new LinkedList(); + entries.add(parseEntry(multiple, argument)); + while(c == ',') { + nextChar(true); + entries.add(parseEntry(multiple, argument)); + } + return entries; } - private RequestEntry parseEntry(boolean argument, boolean tag) { - var entry = new RequestEntry(argument - ? jmpVal() //null value - : requireLegalVariable(jmpVar(), v-> "illegal variable name : '" + v + "'")); - if(c == '(') { //operation - shift(); - if(idx < size) { - entry.initArgs(); - if(c != ')') { //TD parse map - do { - entry.getArgs().add(parseEntry(true, tag)); - } while(idx < size && (c=s.charAt(idx)) == ',' && ++idx < size); - } - if(c == ')') { // !else - shift(); - } - else { - throw new IllegalArgumentException("')' expected"); - } - } - else { - throw new IllegalArgumentException("')' expected"); + private RequestEntry parseEntry(boolean multiple, boolean argument) { + var entry = argument + ? nextEntry() + : new RequestEntry(requireLegalVariable(jmpVar())); + if(c == '(') { //operator + nextChar(true); + entry.setArgs(parseEntries(true, true)); // no args | null + if(c != ')') { + throw somethingExpectedException(); } + nextChar(false); } if(c == '.') { - shift(); - entry.setNext(parseEntry(argument, tag)); + nextChar(true); + entry.setNext(parseEntry(multiple, argument)); } - if(c == ':' && tag) { - shift(); - entry.setTag(requireLegalVariable(jmpVar(), v-> "illegal variable name : '" + v + "'")); + if(c == ':' && !argument) { + nextChar(true); + entry.setTag(requireLegalVariable(jmpVar())); } - if(c == ',' || (idx == size && c == 0) || (c == ')' && argument)) { + if((idx == size && c == 0) || (c == ',' && multiple) || (c == ')' && argument)) { return entry; } - throw new IllegalArgumentException("unexpected character '" + c + "' at index=" + idx); + throw unexpectedCharException(); } - private String jmpVal() { + private RequestEntry nextEntry() { var from = idx; - ChartPredicate pr; - if(s.charAt(from) == '"') { - shift(); - pr = RequestParser::legalAnyChar; //accept any character + if(c == '"') { + nextChar(true); + jmp(RequestParser::legalAnyChar); //accept any character + if(c == '"') { + nextChar(false); + return new RequestEntry(s.substring(from+1, idx-1), true); + } + throw new IllegalArgumentException("'\"' expected"); } else { var v = jmpVar(); - if((idx == size || s.charAt(idx) == '.' || !legalVarChar(c)) && v.matches(VAR_PATTERN)) { - return v; + if((idx == size || s.charAt(idx) == '.' || !legalValChar(c)) && v.matches(VAR_PATTERN)) { + return new RequestEntry(v); } - pr = RequestParser::legalValChar; //auto switch to value - } - jmp(pr); - if(s.charAt(from) != '"') { - return s.substring(from, idx); - } - if(c == '"') { - shift(); - return s.substring(from+1, idx-1); - } - else { - throw new IllegalArgumentException("'\"' expected before '" + c + "'" ); //end } + jmp(RequestParser::legalValChar); + return new RequestEntry(from == idx ? null : s.substring(from, idx)); // empty => null } - + private String jmpVar() { var from = idx; jmp(RequestParser::legalVarChar); return s.substring(from, idx); } - private void jmp(ChartPredicate pr) { - while(idx args; - private String tag; - - public RequestEntry(String name) { - this.name = name; - } - - public void initArgs() { - args = new LinkedList<>(); - } - - @Override - public String toString() { - var s = name; - if(args != null){ - s += args.stream().map(RequestEntry::toString).collect(joining(",", "(", ")")); - } - if(next != null) { - s += "." + next.toString(); - } - return tag == null ? s : s + ":" + tag; - } + return c != '"' && c != '\'' && c != '&' && c != '?' && c != '='; //avoid SQL injection & HTTP reserved symbol } @FunctionalInterface - interface ChartPredicate { + interface CharPredicate { boolean test(char c); } } diff --git a/src/test/java/org/usf/jquery/web/RequestParserTest.java b/src/test/java/org/usf/jquery/web/RequestParserTest.java index c0519224..ae622a5d 100644 --- a/src/test/java/org/usf/jquery/web/RequestParserTest.java +++ b/src/test/java/org/usf/jquery/web/RequestParserTest.java @@ -16,18 +16,28 @@ class RequestParserTest { @ParameterizedTest @ValueSource(strings = { - "col123", - "c.n", + "dymmy", + "camelCaseCol0123", + "snake_column_123", + "view.column", + "view.column.fn1.fn2.exp", "noArgFn()", - "fn(a,b,c,d)", - "trunc(3.55,44)", + "oneArgFn(c1)", + "concat(a,b,c,d)", + "trunc(3.55,2)", + "convert(2020-01-01)", + "extract(2020-01-01T00:00:00Z,epoch)", + "timestamp_diff(2022-01-01T00:00:00+01:00,2020-01-01T00:00:00+07:30)", + "concat(v.c,\"abc\",\"123\")", "mod(abs(trunc(exp())))", "co1.mod(6.acc).trunc(3).plus(100)", //6.acc as value "co1.concat(co1.trunc(2).string(10), 1234)", //6.acc as value "co1.concat(,123)", //null value + "co1.concat(123,)", //null value + "co1.concat(, ,123,)", //null value "aa.fn(3.3,b,c,d)", + "aa.fn(,3.3,b,c,d,)", "aa.fn(2020-01-01,b,c,d)", -// "aa.fn(\"a:3'\",b,c,d)", //pass }) void testParse(String s) { assertEquals(s, parse(s).toString()); @@ -36,17 +46,31 @@ void testParse(String s) { @ParameterizedTest @ValueSource(strings = { - "column)", + "", + "12345", + "1name", + "_column", + "(column", + ")column", + "\"column", + ",column", + ":column", + ".column", "column(", - "column(1,2,3", - "column(1,2,3))", + "column)", + "column\"", + "column,", "column:", "column.", - "1ab", - "123", + "\"column\"", + "function(arg", + "function(arg))", "123(a,b,c)", "123.abc", - "aa.fn(a:3,b,c,d)", + "fn(3@c)", + "fn(\"", + "fn(\"arg", + "fn(\"arg,", "aa.fn(\"a:3,b,c,d)", "aa.fn(\"a:3,b,c,d&\")", }) From 766127083d0b9b766c8a0b58eebb82a561f6b708 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 27 Dec 2023 01:14:45 +0100 Subject: [PATCH 017/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 7 +- .../java/org/usf/jquery/core/DBOrder.java | 11 +- .../java/org/usf/jquery/core/JDBCType.java | 17 +- .../java/org/usf/jquery/core/JavaType.java | 20 +- .../java/org/usf/jquery/core/Mappers.java | 27 --- .../java/org/usf/jquery/core/Operator.java | 8 +- src/main/java/org/usf/jquery/core/Order.java | 7 + .../jquery/core/QueryParameterBuilder.java | 2 +- src/main/java/org/usf/jquery/core/Utils.java | 2 +- .../org/usf/jquery/web/ArgumentParsers.java | 23 ++- .../java/org/usf/jquery/web/ChartMappers.java | 40 ++++ .../usf/jquery/web/LinkedRequestEntry.java | 2 +- .../org/usf/jquery/web/ParsableSQLType.java | 2 +- .../java/org/usf/jquery/web/RequestEntry.java | 186 ++++++++++++------ .../org/usf/jquery/web/RequestParser.java | 59 +++--- .../org/usf/jquery/web/TableDecorator.java | 5 +- .../org/usf/jquery/web/TableMetadata.java | 2 +- .../org/usf/jquery/web/RequestParserTest.java | 8 +- 18 files changed, 264 insertions(+), 164 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/Order.java create mode 100644 src/main/java/org/usf/jquery/web/ChartMappers.java diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index f07cf9c2..9728f1dc 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -46,11 +46,10 @@ default NamedColumn as(String name) { } default DBOrder order() { - return order(null); + return new DBOrder(this); } - @Deprecated(forRemoval = false) //unsafe value - default DBOrder order(String order) { + default DBOrder order(Order order) { return new DBOrder(this, order); } @@ -235,6 +234,6 @@ static OperationColumn substring(Object arg, int start, int length) { } static Optional lookupColumnFunction() { - return empty(); + return empty(); //CURRENT_DATE, CURRENT_DATETIME } } diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index d9e39867..1bceceb1 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -4,6 +4,7 @@ import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; +import lombok.AccessLevel; import lombok.RequiredArgsConstructor; /** @@ -11,11 +12,15 @@ * @author u$f * */ -@RequiredArgsConstructor +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class DBOrder implements DBObject { private final DBColumn column; - private final String order; + private final Order order; + + public DBOrder(DBColumn column) { + this(column, null); + } @Override public String sql(QueryParameterBuilder builder, Object[] args) { @@ -26,7 +31,7 @@ public String sql(QueryParameterBuilder builder, Object[] args) { public String sql(QueryParameterBuilder builder) { return isNull(order) ? column.sql(builder) - : column.sql(builder) + SPACE + order; + : column.sql(builder) + SPACE + order.name(); } } diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index 23a91d6d..121b4358 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -10,7 +10,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import lombok.Getter; import lombok.RequiredArgsConstructor; /** @@ -20,7 +19,6 @@ * @author u$f * */ -@Getter @RequiredArgsConstructor public enum JDBCType implements JavaType { @@ -45,27 +43,32 @@ public enum JDBCType implements JavaType { TIMESTAMP(Types.TIMESTAMP, Timestamp.class, Timestamp.class::isInstance), TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class, Timestamp.class::isInstance); - public static final JavaType AUTO = declare(Object.class, o-> true); - public static final JavaType OTHER = declare(Object.class, o-> true); + public static final JavaType AUTO = declare("AUTO", Object.class, o-> true); + public static final JavaType OTHER = declare("OTHER", Object.class, o-> {throw new UnsupportedOperationException("Unsupported SQL type");}); private final int value; private final Class type; private final Function matcher; + @Override + public Class type() { + return type; + } + @Override public boolean accept(Object o) { if(o instanceof Typed) { var t = ((Typed) o).javaType(); return t == null || this == t - || getType() == t.getType() + || type() == t.type() || (subType(this, Number.class) && subType(t, Number.class)); //other types compatibility } return acceptValue(o); } static boolean subType(JavaType type, Class c) { - return c.isAssignableFrom(type.getType()); + return c.isAssignableFrom(type.type()); } private boolean acceptValue(Object o) { @@ -106,7 +109,7 @@ public static JavaType typeOf(Object o) { var t = ((Typed) o).javaType(); return t == null ? AUTO : findType(t::equals); } - return o == null ? AUTO : findType(e-> e.getType().isInstance(o)); + return o == null ? AUTO : findType(e-> e.type().isInstance(o)); } public static JavaType fromDataType(int value) { diff --git a/src/main/java/org/usf/jquery/core/JavaType.java b/src/main/java/org/usf/jquery/core/JavaType.java index b0e47fda..89d76a89 100644 --- a/src/main/java/org/usf/jquery/core/JavaType.java +++ b/src/main/java/org/usf/jquery/core/JavaType.java @@ -11,18 +11,26 @@ */ public interface JavaType { - boolean accept(Object o); + String name(); + + Class type(); - Class getType(); + boolean accept(Object o); - static JavaType instance(Class type) { - return declare(type, type::isInstance); + static JavaType instance(String name, Class type) { + return declare(name, type, type::isInstance); } - static JavaType declare(@NonNull Class type, @NonNull Predicate predicate) { + static JavaType declare(@NonNull String name, @NonNull Class type, @NonNull Predicate predicate) { return new JavaType() { + + @Override + public String name() { + return name; + } + @Override - public Class getType() { + public Class type() { return type; } diff --git a/src/main/java/org/usf/jquery/core/Mappers.java b/src/main/java/org/usf/jquery/core/Mappers.java index 87941f72..3e8d393e 100644 --- a/src/main/java/org/usf/jquery/core/Mappers.java +++ b/src/main/java/org/usf/jquery/core/Mappers.java @@ -1,21 +1,10 @@ package org.usf.jquery.core; import static org.usf.jquery.core.ResultSetMapper.DataWriter.usingRowWriter; -import static org.usf.jquery.web.view.Chart2DView.areaChart; -import static org.usf.jquery.web.view.Chart2DView.barChart; -import static org.usf.jquery.web.view.Chart2DView.columnChart; -import static org.usf.jquery.web.view.Chart2DView.comboChart; -import static org.usf.jquery.web.view.Chart2DView.lineChart; import java.io.Writer; import org.usf.jquery.core.ResultSetMapper.DataWriter; -import org.usf.jquery.web.view.CalendarView; -import org.usf.jquery.web.view.PieChartView; -import org.usf.jquery.web.view.SankeyView; -import org.usf.jquery.web.view.TableView; -import org.usf.jquery.web.view.TimelineChartView; -import org.usf.jquery.web.view.WebViewMapper; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -49,20 +38,4 @@ public static CsvResultMapper csv(DataWriter out) { return new CsvResultMapper(out); } - public static WebViewMapper webChart(String view, Writer w) { - switch (view) { - case "table" : return new TableView(w); - case "pie" : return new PieChartView(w); - case "column" : return columnChart(w); - case "bar" : return barChart(w); - case "area" : return areaChart(w); - case "combo" : return comboChart(w); - case "line" : return lineChart(w); - case "timeline" : return new TimelineChartView(w); - case "calendar" : return new CalendarView(w); - case "sankey" : return new SankeyView(w); - default: throw new IllegalArgumentException(view); - } - } - } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 2db33d4b..089e2d6c 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -259,8 +259,8 @@ static TypedOperator denseRank() { static TypedOperator over() { return new TypedOperator(firstArgType(), pipe("OVER"), - required(instance(OperationColumn.class)), // TODO wrap => aggreagation || window - required(instance(OverClause.class))) { + required(instance("??", OperationColumn.class)), // TODO wrap => aggreagation || window + required(instance("??", OverClause.class))) { @Override public OperationColumn args(Object... args) { return super.args(args).aggregation(false); //!aggregation @@ -295,6 +295,10 @@ static AggregateFunction aggregation(String name) { static PipeFunction pipe(String name) { return ()-> name; } + + static Optional lookupNoArgFunction(String op) { + return lookupOperator(op).filter(fn-> fn.requireArgCount() == 0); + } static Optional lookupOperator(String op) { try { diff --git a/src/main/java/org/usf/jquery/core/Order.java b/src/main/java/org/usf/jquery/core/Order.java new file mode 100644 index 00000000..33518d42 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/Order.java @@ -0,0 +1,7 @@ +package org.usf.jquery.core; + +public enum Order { + + ASC, DESC; + +} diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 6443224f..b76ba6b6 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -69,7 +69,7 @@ public String appendTimestamp(Object o) { } public String appendLitteral(Object o, JavaType type) { - return appendParameter(o, type.getType(), true); + return appendParameter(o, type.type(), true); } //TODO diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 500d97f2..fc95a8ca 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -20,7 +20,7 @@ public final class Utils { public static final int UNLIMITED = -1; public static boolean isEmpty(int[] a) { - return a == null || a.length == 0; + return isNull(a) || a.length == 0; } public static boolean isPresent(T[] a) { diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 5f42514a..e1c0cf8a 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -37,23 +37,26 @@ public class ArgumentParsers { jdbcTypeParser(DATE), jdbcTypeParser(TIMESTAMP), jdbcTypeParser(TIME), jdbcTypeParser(TIMESTAMP_WITH_TIMEZONE)}; + public static ArgumentParser javaTypeParser(JavaType type) { if(type instanceof JDBCType) { - return ArgumentParsers.jdbcTypeParser((JDBCType) type); + return jdbcTypeParser((JDBCType) type); } if(type == AUTO) { - return v-> { - for(var p : DEFAULT) { - var o = p.tryParse(v); - if(o != null) { - return o; - } - } - return v; - }; + return ArgumentParsers::autoTypeParse; } throw new UnsupportedOperationException("unsupported type " + type); } + + public static Object autoTypeParse(String v) { + for(var p : DEFAULT) { + var o = p.tryParse(v); + if(o != null) { + return o; + } + } + return v; //string value + } public static ArgumentParser jdbcTypeParser(JDBCType type) { switch (type) { diff --git a/src/main/java/org/usf/jquery/web/ChartMappers.java b/src/main/java/org/usf/jquery/web/ChartMappers.java new file mode 100644 index 00000000..f7896d39 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/ChartMappers.java @@ -0,0 +1,40 @@ +package org.usf.jquery.web; + +import static org.usf.jquery.web.view.Chart2DView.areaChart; +import static org.usf.jquery.web.view.Chart2DView.barChart; +import static org.usf.jquery.web.view.Chart2DView.columnChart; +import static org.usf.jquery.web.view.Chart2DView.comboChart; +import static org.usf.jquery.web.view.Chart2DView.lineChart; + +import java.io.Writer; + +import org.usf.jquery.web.view.CalendarView; +import org.usf.jquery.web.view.PieChartView; +import org.usf.jquery.web.view.SankeyView; +import org.usf.jquery.web.view.TableView; +import org.usf.jquery.web.view.TimelineChartView; +import org.usf.jquery.web.view.WebViewMapper; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class ChartMappers { + + public static WebViewMapper webChart(String view, Writer w) { + switch (view) { + case "table" : return new TableView(w); + case "pie" : return new PieChartView(w); + case "column" : return columnChart(w); + case "bar" : return barChart(w); + case "area" : return areaChart(w); + case "combo" : return comboChart(w); + case "line" : return lineChart(w); + case "timeline" : return new TimelineChartView(w); + case "calendar" : return new CalendarView(w); + case "sankey" : return new SankeyView(w); + default : throw new IllegalArgumentException(view); + } + } + +} diff --git a/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java b/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java index 3685704c..2ebd9380 100644 --- a/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java +++ b/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java @@ -161,7 +161,7 @@ private static OperationColumn parseEntry(Operator fn, RequestEntry re, TableDec for(int i=1; i args; private String tag; @@ -34,84 +43,124 @@ public RequestEntry(String value) { this(value, false); } - public DBColumn toColumn(TableDecorator td) { //columnName == viewName - DBColumn c = null; - RequestEntry nxt = requireNoArgs(); - if(next != null && context().isDeclaredTable(value) && context().isDeclaredColumn(next.value)) { - nxt = next.requireNoArgs(); - c = context().getTable(value).column(context().getColumn(nxt.value)); + public DBOrder asOrder(TableDecorator td) { + var t = toColumn(td); + var c = t.td.column(t.tc); + return isNull(t.entry.next) ? c.order() : t.entry.next.chainOrder(td, c); + } + + private DBOrder chainOrder(TableDecorator td, DBColumn col) { + var c = toOperation(td, col); + if(nonNull(c)) { + return isNull(next) ? c.order() : next.chainOrder(td, c); } - else if(context().isDeclaredColumn(value)) { - c = td.column(context().getColumn(value)); + if(isNull(next)) { //last entry + var upVal = value.toUpperCase(); + var order = Stream.of(Order.values()).filter(o-> o.name().equals(upVal)).findAny(); + if(order.isPresent()) { + return col.order(order.get()); + } } - else { - c = lookupColumnFunction().orElse(null); + throw cannotEvaluateException("entry", value); //column expected + } + + public TaggableColumn asFilter(TableDecorator td, List values) { + + return null; + } + + public TaggableColumn asColumn(TableDecorator td) { //columnName == viewName + var t = toColumn(td); + var c = t.td.column(t.tc); + return isNull(t.entry.next) ? c : t.entry.next.chainColumn(td, c, c.tagname()); + } + + private TaggableColumn chainColumn(TableDecorator td, DBColumn col, String alias) { + var c = toOperation(td, col); + if(nonNull(c)) { + return nonNull(next) + ? next.chainColumn(td, c, alias) + : c.as(isNull(tag) ? alias : tag); } - return c == null ? null : nxt.next(td, c); + throw cannotEvaluateException("entry", value); //column expected } - private Object toArg(TableDecorator td, JavaType... types) { - if(value == null) { - return requireNoArgs().value; + private Triple toColumn(TableDecorator td) { //columnName == viewName + requireNoArgs(); + if(next != null && context().isDeclaredTable(value) && context().isDeclaredColumn(next.value)) { + return new Triple( + context().getTable(value), + context().getColumn(next.requireNoArgs().value), + next); } - if(value.matches(VAR_PATTERN)) { - var c = toColumn(td); - if(c != null) { - return c; // type will be checked later - } + if(context().isDeclaredColumn(value)) { + return new Triple(td, context().getColumn(value), this); } - if(!unparsable) { - requireNoArgs(); - for(var t : types) { - var o = javaTypeParser(t).tryParse(value); - if(o != null) { - return o; - } - } - throw new ParseException("cannot parse value : " + value); + throw cannotEvaluateException("column expression", value); + } + + private DBOrder chainExpression(TableDecorator td, DBColumn col) { + var c = toOperation(td, col); + if(nonNull(c)) { + return isNull(next) ? c.order() : next.chainOrder(td, c); } - for(var t : types) { - if(t.accept(value)) { - return value; + if(isNull(next)) { + var order = Stream.of(Order.values()).filter(o-> o.name().equalsIgnoreCase(value)).findAny(); + if(order.isPresent()) { + return col.order(order.get()); } } - throw new ParseException("illegal value : " + value); + throw cannotEvaluateException("entry", value); //column expected + } - DBColumn toOperation(TableDecorator td, DBColumn prev) { + private DBColumn toOperation(TableDecorator td, DBColumn prev) { var res = lookupOperator(value); - if(res.isPresent()) { - var op = res.get(); - var min = op.requireArgCount(); - var np = args.size()+1; - if(np < min || (!op.isVarags() && np > op.getParameters().length)) { - throw new IllegalArgumentException(); - } - var params = new ArrayList(np); - params.add(prev); - var i=1; - for(; i op.getParameters().length)) { + throw new IllegalArgumentException(); + } + var params = new ArrayList(np); + params.add(prev); + var i=1; + for(; i parseEntries(boolean multiple, boolean argument) { private RequestEntry parseEntry(boolean multiple, boolean argument) { var entry = argument ? nextEntry() - : new RequestEntry(requireLegalVariable(jmpVar())); + : new RequestEntry(requireLegalVariable(nextVar())); if(c == '(') { //operator nextChar(true); entry.setArgs(parseEntries(true, true)); // no args | null - if(c != ')') { - throw somethingExpectedException(); - } + requireChar(')'); //nextChar nextChar(false); } if(c == '.') { @@ -59,7 +57,7 @@ private RequestEntry parseEntry(boolean multiple, boolean argument) { } if(c == ':' && !argument) { nextChar(true); - entry.setTag(requireLegalVariable(jmpVar())); + entry.setTag(requireLegalVariable(nextVar())); } if((idx == size && c == 0) || (c == ',' && multiple) || (c == ')' && argument)) { return entry; @@ -71,34 +69,29 @@ private RequestEntry nextEntry() { var from = idx; if(c == '"') { nextChar(true); - jmp(RequestParser::legalAnyChar); //accept any character - if(c == '"') { - nextChar(false); - return new RequestEntry(s.substring(from+1, idx-1), true); - } - throw new IllegalArgumentException("'\"' expected"); + nextWhile(RequestParser::legalTxtChar); //accept any + requireChar('"'); //nextChar + nextChar(false); + return new RequestEntry(s.substring(from+1, idx-1), true); } - else { - var v = jmpVar(); - if((idx == size || s.charAt(idx) == '.' || !legalValChar(c)) && v.matches(VAR_PATTERN)) { - return new RequestEntry(v); - } + var v = nextVar(); //to optim + if((idx == size || s.charAt(idx) == '.' || !legalValChar(c)) && v.matches(VAR_PATTERN)) { + return new RequestEntry(v); } - jmp(RequestParser::legalValChar); + nextWhile(RequestParser::legalValChar); return new RequestEntry(from == idx ? null : s.substring(from, idx)); // empty => null } - private String jmpVar() { + private String nextVar() { var from = idx; - jmp(RequestParser::legalVarChar); + nextWhile(RequestParser::legalVarChar); return s.substring(from, idx); } - private void jmp(CharPredicate cp) { + private void nextWhile(CharPredicate cp) { while(idx= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z' ) || (c >= '0' && c <= '9') || c == '_'; } - private static boolean legalAnyChar(char c) { - return c != '"' && c != '\'' && c != '&' && c != '?' && c != '='; //avoid SQL injection & HTTP reserved symbol - } - @FunctionalInterface interface CharPredicate { boolean test(char c); diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index cc23e206..a5a5a062 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -17,6 +17,7 @@ import static org.usf.jquery.web.RequestColumn.decodeColumns; import static org.usf.jquery.web.RequestColumn.decodeSingleColumn; import static org.usf.jquery.web.RequestFilter.decodeFilter; +import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.TableMetadata.emptyMetadata; import static org.usf.jquery.web.TableMetadata.tableMetadata; @@ -123,8 +124,8 @@ default void parseColumns(RequestQueryBuilder query, Map param query.distinct(); } Stream.of(cols) - .flatMap(c-> decodeColumns(c, this, false)) - .forEach(rc-> query.tablesIfAbsent(rc.tableDecorator().table()).columns(rc.toColumn())); + .flatMap(v-> parseEntries(v).stream()) + .forEach(e-> query.columns(e.asColumn(this))); } default void parseFilters(RequestQueryBuilder query, Map parameters) { diff --git a/src/main/java/org/usf/jquery/web/TableMetadata.java b/src/main/java/org/usf/jquery/web/TableMetadata.java index a816d698..f947ab52 100644 --- a/src/main/java/org/usf/jquery/web/TableMetadata.java +++ b/src/main/java/org/usf/jquery/web/TableMetadata.java @@ -42,7 +42,7 @@ public class TableMetadata { @Setter(AccessLevel.PACKAGE) private Instant lastUpdate; - public Optional columnMetada(ColumnDecorator cd){ + public Optional columnMetada(ColumnDecorator cd) { return columns.containsKey(cd.identity()) ? Optional.of(columns.get(cd.identity())) : empty(); } diff --git a/src/test/java/org/usf/jquery/web/RequestParserTest.java b/src/test/java/org/usf/jquery/web/RequestParserTest.java index ae622a5d..ca5d9933 100644 --- a/src/test/java/org/usf/jquery/web/RequestParserTest.java +++ b/src/test/java/org/usf/jquery/web/RequestParserTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.usf.jquery.web.RequestParser.parse; +import static org.usf.jquery.web.RequestParser.parseEntry; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -40,8 +40,8 @@ class RequestParserTest { "aa.fn(2020-01-01,b,c,d)", }) void testParse(String s) { - assertEquals(s, parse(s).toString()); - assertEquals(s+=":tag", parse(s).toString()); + assertEquals(s, parseEntry(s).toString()); + assertEquals(s+=":tag", parseEntry(s).toString()); } @ParameterizedTest @@ -75,6 +75,6 @@ void testParse(String s) { "aa.fn(\"a:3,b,c,d&\")", }) void testParse2(String s) { - assertThrows(IllegalArgumentException.class, ()-> parse(s)); + assertThrows(ParseException.class, ()-> parseEntry(s)); } } From c52704849185ae3f7459aa7d450f2ce5c427a69f Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 28 Dec 2023 22:08:15 +0100 Subject: [PATCH 018/298] edit --- .../org/usf/jquery/core/TypedOperator.java | 1 - .../org/usf/jquery/web/ColumnDecorator.java | 11 +- .../java/org/usf/jquery/web/RequestEntry.java | 161 +++++++++++------- 3 files changed, 107 insertions(+), 66 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 3e340edf..ff587cbb 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -66,5 +66,4 @@ public int requireArgCount() { public boolean isVarags() { return parameters.length > 0 && parameters[parameters.length-1].isVarargs(); } - } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index eaa3540e..689c1c45 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -2,6 +2,7 @@ import static java.util.Objects.isNull; import static org.usf.jquery.core.DBColumn.count; +import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.Comparator.equal; import static org.usf.jquery.core.Comparator.greaterOrEqual; import static org.usf.jquery.core.Comparator.greaterThan; @@ -16,6 +17,7 @@ import static org.usf.jquery.web.ParsableJDBCType.typeOf; import org.usf.jquery.core.Comparator; +import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.JavaType; import org.usf.jquery.core.Operator; import org.usf.jquery.core.StringComparator; @@ -26,14 +28,17 @@ * * */ +@FunctionalInterface public interface ColumnDecorator { String identity(); //URL - String reference(); //JSON + default String reference() { //JSON + return identity(); + } default JavaType dataType() { - return null; + return AUTO; } default String pattern() { @@ -90,6 +95,8 @@ private static Comparator containsArgPartten(StringComparator fn) { }; } + default ComparisonExpression expression(String exp, String... values) { return null; }; + static ColumnDecorator countColumn() { return ofColumn(Operator.count().id(), t-> count()); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntry.java b/src/main/java/org/usf/jquery/web/RequestEntry.java index a241a31e..9bbf8a76 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntry.java +++ b/src/main/java/org/usf/jquery/web/RequestEntry.java @@ -17,9 +17,10 @@ import java.util.stream.Stream; import org.usf.jquery.core.DBColumn; +import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBOrder; import org.usf.jquery.core.JavaType; -import org.usf.jquery.core.Operator; +import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; import org.usf.jquery.core.TaggableColumn; @@ -32,6 +33,8 @@ @Setter(value = AccessLevel.PACKAGE) @RequiredArgsConstructor final class RequestEntry { + + private static final ColumnDecorator DEFAUL_COLUMN = ()-> null; //unused identity private final String value; private final boolean text; //"string" @@ -42,91 +45,88 @@ final class RequestEntry { public RequestEntry(String value) { this(value, false); } - - public DBOrder asOrder(TableDecorator td) { - var t = toColumn(td); - var c = t.td.column(t.tc); - return isNull(t.entry.next) ? c.order() : t.entry.next.chainOrder(td, c); - } - private DBOrder chainOrder(TableDecorator td, DBColumn col) { - var c = toOperation(td, col); - if(nonNull(c)) { - return isNull(next) ? c.order() : next.chainOrder(td, c); - } - if(isNull(next)) { //last entry - var upVal = value.toUpperCase(); - var order = Stream.of(Order.values()).filter(o-> o.name().equals(upVal)).findAny(); - if(order.isPresent()) { - return col.order(order.get()); + public TaggableColumn asColumn(TableDecorator td) { //columnName == viewName + var t = lookup(td, true); + var c = t.buildColumn(); + var e = t.entry; + DBColumn oc = c; + while(e.next()) { + e = e.next; + oc = e.toOperation(td, oc); + if(isNull(oc)) { + throw cannotEvaluateException("entry", e.value); //column expected } } - throw cannotEvaluateException("entry", value); //column expected - } - - public TaggableColumn asFilter(TableDecorator td, List values) { - - return null; + return oc.as(isNull(e.tag) ? c.tagname() : e.tag); } - public TaggableColumn asColumn(TableDecorator td) { //columnName == viewName - var t = toColumn(td); - var c = t.td.column(t.tc); - return isNull(t.entry.next) ? c : t.entry.next.chainColumn(td, c, c.tagname()); - } - - private TaggableColumn chainColumn(TableDecorator td, DBColumn col, String alias) { - var c = toOperation(td, col); - if(nonNull(c)) { - return nonNull(next) - ? next.chainColumn(td, c, alias) - : c.as(isNull(tag) ? alias : tag); - } - throw cannotEvaluateException("entry", value); //column expected - } - - private Triple toColumn(TableDecorator td) { //columnName == viewName - requireNoArgs(); - if(next != null && context().isDeclaredTable(value) && context().isDeclaredColumn(next.value)) { - return new Triple( - context().getTable(value), - context().getColumn(next.requireNoArgs().value), - next); + public DBFilter asFilter(TableDecorator td, String[] values) { + var t = lookup(td, false); + var c = t.buildColumn(); + var e = t.entry.next; + DBColumn oc = c; + while(nonNull(e)) { + var op = e.toOperation(td, oc); + if(isNull(oc)) { + break; + } + else { + oc = op; //preserve last non value + } + e = e.next; } - if(context().isDeclaredColumn(value)) { - return new Triple(td, context().getColumn(value), this); + var cd = c == oc ? t.cd : DEFAUL_COLUMN; //no operation + if(isNull(e)) { // no expression + return oc.filter(cd.expression(null, values)); } - throw cannotEvaluateException("column expression", value); + else if(e.isLast()) { + return oc.filter(cd.expression(e.value, values)); + } + throw cannotEvaluateException("entry", e.toString()); //more detail } - private DBOrder chainExpression(TableDecorator td, DBColumn col) { - var c = toOperation(td, col); - if(nonNull(c)) { - return isNull(next) ? c.order() : next.chainOrder(td, c); + public DBOrder asOrder(TableDecorator td) { + var t = lookup(td, false); + var c = t.buildColumn(); + var e = t.entry.next; + DBColumn oc = c; + while(nonNull(e)) { + var op = e.toOperation(td, c); + if(isNull(op)) { + break; + } + else { + oc = op; //preserve last non value + } + e = e.next; + } + if(isNull(e)) { // no expression + return c.order(); } - if(isNull(next)) { - var order = Stream.of(Order.values()).filter(o-> o.name().equalsIgnoreCase(value)).findAny(); + else if(e.isLast()) { //last entry + var upVal = e.value.toUpperCase(); + var order = Stream.of(Order.values()).filter(o-> o.name().equals(upVal)).findAny(); if(order.isPresent()) { - return col.order(order.get()); + return oc.order(order.get()); } } - throw cannotEvaluateException("entry", value); //column expected - + throw cannotEvaluateException("entry", e.toString()); //column expected } - private DBColumn toOperation(TableDecorator td, DBColumn prev) { + private OperationColumn toOperation(TableDecorator td, DBColumn col) { var res = lookupOperator(value); if(res.isEmpty()) { return null; } var op = res.get(); var min = op.requireArgCount(); - var np = args.size()+1; + var np = args.size()+1; // col if(np < min || (!op.isVarags() && np > op.getParameters().length)) { throw new IllegalArgumentException(); } var params = new ArrayList(np); - params.add(prev); + params.add(col); var i=1; for(; i Date: Fri, 29 Dec 2023 01:09:05 +0100 Subject: [PATCH 019/298] edit --- .../org/usf/jquery/core/TypedOperator.java | 1 - .../org/usf/jquery/web/ColumnDecorator.java | 30 +++---- .../org/usf/jquery/web/ParseException.java | 6 +- ...questEntry.java => RequestEntryChain.java} | 83 +++++++++++++------ .../org/usf/jquery/web/RequestParser.java | 24 +++--- .../org/usf/jquery/web/TableDecorator.java | 19 ++--- .../java/org/usf/jquery/web/WebException.java | 22 +++++ 7 files changed, 121 insertions(+), 64 deletions(-) rename src/main/java/org/usf/jquery/web/{RequestEntry.java => RequestEntryChain.java} (70%) create mode 100644 src/main/java/org/usf/jquery/web/WebException.java diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index ff587cbb..57b55db7 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -4,7 +4,6 @@ import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireNoArgs; -import java.awt.image.renderable.ParameterBlock; import java.util.function.Function; import lombok.Getter; diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 689c1c45..b3e921fa 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -14,7 +14,7 @@ import static org.usf.jquery.core.Comparator.notEqual; import static org.usf.jquery.core.Comparator.notIn; import static org.usf.jquery.core.Comparator.notLike; -import static org.usf.jquery.web.ParsableJDBCType.typeOf; +import static org.usf.jquery.web.ArgumentParsers.javaTypeParser; import org.usf.jquery.core.Comparator; import org.usf.jquery.core.ComparisonExpression; @@ -37,19 +37,28 @@ default String reference() { //JSON return identity(); } - default JavaType dataType() { - return AUTO; + default JavaType dataType(TableDecorator td) { + return td.metadata().columnMetada(this) + .map(ColumnMetadata::getDataType) + .orElse(AUTO); } - default String pattern() { + /** + * override parser | format | local + */ + default ArgumentParser parser(TableDecorator td){ + return javaTypeParser(dataType(td)); + } + + default String pattern(TableDecorator td) { throw new UnsupportedOperationException(); //improve API security and performance } - default boolean canSelect() { + default boolean canSelect(TableDecorator td) { throw new UnsupportedOperationException(); //authorization inject } - default boolean canFilter() { + default boolean canFilter(TableDecorator td) { throw new UnsupportedOperationException(); //authorization inject } @@ -79,15 +88,6 @@ default Comparator comparator(String comparator, int nArg) { } } - /** - * override parser | format | local - */ - default ArgumentParser parser(JavaType type){ - return type instanceof ParsableSQLType - ? (ParsableSQLType) type //improve parser search - : typeOf(type); - } - private static Comparator containsArgPartten(StringComparator fn) { return (b, args)-> { args[1] = "%" + args[1] + "%"; diff --git a/src/main/java/org/usf/jquery/web/ParseException.java b/src/main/java/org/usf/jquery/web/ParseException.java index 59946de9..bf445965 100644 --- a/src/main/java/org/usf/jquery/web/ParseException.java +++ b/src/main/java/org/usf/jquery/web/ParseException.java @@ -8,7 +8,7 @@ * */ @SuppressWarnings("serial") -public final class ParseException extends IllegalArgumentException { +public final class ParseException extends WebException { public ParseException(String message) { super(message); @@ -18,10 +18,12 @@ public ParseException(String message, Throwable cause) { super(message, cause); } + @Deprecated static ParseException cannotEvaluateException(String type, String expression) { return new ParseException("cannot evaluate " + type + " " + quote(expression)); } - + + @Deprecated static ParseException cannotParseException(String type, String value, Throwable cause) { return new ParseException("cannot parse " + type + " " + quote(value), cause); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntry.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java similarity index 70% rename from src/main/java/org/usf/jquery/web/RequestEntry.java rename to src/main/java/org/usf/jquery/web/RequestEntryChain.java index 9bbf8a76..c8b238c3 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntry.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -3,22 +3,26 @@ import static java.lang.Math.min; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Operator.lookupNoArgFunction; import static org.usf.jquery.core.Operator.lookupOperator; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; +import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Validation.VAR_PATTERN; import static org.usf.jquery.web.ArgumentParsers.javaTypeParser; +import static org.usf.jquery.web.CriteriaBuilder.ofComparator; import static org.usf.jquery.web.JQueryContext.context; -import static org.usf.jquery.web.ParseException.cannotEvaluateException; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; +import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBOrder; +import org.usf.jquery.core.InCompartor; import org.usf.jquery.core.JavaType; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; @@ -32,36 +36,38 @@ @Getter @Setter(value = AccessLevel.PACKAGE) @RequiredArgsConstructor -final class RequestEntry { +final class RequestEntryChain { private static final ColumnDecorator DEFAUL_COLUMN = ()-> null; //unused identity private final String value; private final boolean text; //"string" - private RequestEntry next; - private List args; + private RequestEntryChain next; + private List args; private String tag; - public RequestEntry(String value) { + public RequestEntryChain(String value) { this(value, false); } - public TaggableColumn asColumn(TableDecorator td) { //columnName == viewName + public TaggableColumn asColumn(TableDecorator td) { var t = lookup(td, true); var c = t.buildColumn(); var e = t.entry; DBColumn oc = c; - while(e.next()) { - e = e.next; - oc = e.toOperation(td, oc); - if(isNull(oc)) { - throw cannotEvaluateException("entry", e.value); //column expected - } + if(e.next()) { + do { + e = e.next; + oc = e.toOperation(td, oc); + if(isNull(oc)) { + throw cannotEvaluateException(e); + } + } while(e.next()); //preserve last non null entry } return oc.as(isNull(e.tag) ? c.tagname() : e.tag); } - public DBFilter asFilter(TableDecorator td, String[] values) { + public DBFilter asFilter(TableDecorator td, String expression) { var t = lookup(td, false); var c = t.buildColumn(); var e = t.entry.next; @@ -72,18 +78,18 @@ public DBFilter asFilter(TableDecorator td, String[] values) { break; } else { - oc = op; //preserve last non value + oc = op; //preserve last non null column } e = e.next; } var cd = c == oc ? t.cd : DEFAUL_COLUMN; //no operation if(isNull(e)) { // no expression - return oc.filter(cd.expression(null, values)); + return oc.filter(toExpression(td, cd, null, expression)); } else if(e.isLast()) { - return oc.filter(cd.expression(e.value, values)); + return oc.filter(toExpression(td, cd, e.value, expression)); } - throw cannotEvaluateException("entry", e.toString()); //more detail + throw cannotEvaluateException(e); //more detail } public DBOrder asOrder(TableDecorator td) { @@ -97,7 +103,7 @@ public DBOrder asOrder(TableDecorator td) { break; } else { - oc = op; //preserve last non value + oc = op; //preserve last non null column } e = e.next; } @@ -111,7 +117,7 @@ else if(e.isLast()) { //last entry return oc.order(order.get()); } } - throw cannotEvaluateException("entry", e.toString()); //column expected + throw cannotEvaluateException(e); //column expected } private OperationColumn toOperation(TableDecorator td, DBColumn col) { @@ -143,6 +149,31 @@ private OperationColumn toOperation(TableDecorator td, DBColumn col) { return op.args(params.toArray()); } + + static ComparisonExpression toExpression(TableDecorator td, ColumnDecorator cd, String exp, String... values) { + if(nonNull(exp)) { + var criteria = cd.criteria(exp); + if(nonNull(criteria)) { + return criteria.build(values); + } + } + var cmp = cd.comparator(exp, values.length); + if(nonNull(cmp)) { + var prs = requireNonNull(cd.parser(td)); + if(values.length == 0) { + return cmp.expression(null); + } + if(values.length == 1) { + return cmp.expression(prs.parse(values[0])); + } + var args = prs.parseAll(values); + return cmp instanceof InCompartor + ? cmp.expression(args) + : ofComparator(cmp).build(args); + } + throw ParseException.cannotEvaluateException("expression", null); + } + private Object toArg(TableDecorator td, JavaType... types) { if(isNull(value) || text) { return requireNoArgs().value; @@ -183,11 +214,11 @@ private Triple lookup(TableDecorator td, boolean noArgFn) { //columnName == view throw new IllegalArgumentException(op.id() + " takes no arguments"); } } - throw cannotEvaluateException("column expression", value); + throw cannotEvaluateException(this); } - private RequestEntry requireNoArgs() { - if(args == null) { + private RequestEntryChain requireNoArgs() { + if(isNull(args)) { return this; } throw new IllegalArgumentException(value + " takes no args"); @@ -208,7 +239,7 @@ public String toString() { s += text ? doubleQuote(value) : value; } if(args != null){ - s += args.stream().map(RequestEntry::toString).collect(joining(",", "(", ")")); + s += args.stream().map(RequestEntryChain::toString).collect(joining(",", "(", ")")); } if(next != null) { s += "." + next.toString(); @@ -216,12 +247,16 @@ public String toString() { return tag == null ? s : s + ":" + tag; } + static ParseException cannotEvaluateException(RequestEntryChain entry) { + return new ParseException("cannot evaluate entry : " + quote(entry.toString())); + } + @RequiredArgsConstructor static class Triple { private final TableDecorator td; private final ColumnDecorator cd; - private final RequestEntry entry; + private final RequestEntryChain entry; private final TaggableColumn column; TaggableColumn buildColumn() { diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 470246e0..462f1e7b 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -23,16 +23,16 @@ private RequestParser(String s) { this.size = s.length(); } - public static RequestEntry parseEntry(String s) { + public static RequestEntryChain parseEntry(String s) { return new RequestParser(s).parseEntry(false, false); } - public static List parseEntries(String s) { + public static List parseEntries(String s) { return new RequestParser(s).parseEntries(true, false); } - private List parseEntries(boolean multiple, boolean argument) { - var entries = new LinkedList(); + private List parseEntries(boolean multiple, boolean argument) { + var entries = new LinkedList(); entries.add(parseEntry(multiple, argument)); while(c == ',') { nextChar(true); @@ -41,10 +41,10 @@ private List parseEntries(boolean multiple, boolean argument) { return entries; } - private RequestEntry parseEntry(boolean multiple, boolean argument) { + private RequestEntryChain parseEntry(boolean multiple, boolean argument) { var entry = argument ? nextEntry() - : new RequestEntry(requireLegalVariable(nextVar())); + : new RequestEntryChain(requireLegalVariable(nextVar())); if(c == '(') { //operator nextChar(true); entry.setArgs(parseEntries(true, true)); // no args | null @@ -65,21 +65,21 @@ private RequestEntry parseEntry(boolean multiple, boolean argument) { throw unexpectedCharException(); } - private RequestEntry nextEntry() { + private RequestEntryChain nextEntry() { var from = idx; if(c == '"') { nextChar(true); nextWhile(RequestParser::legalTxtChar); //accept any requireChar('"'); //nextChar nextChar(false); - return new RequestEntry(s.substring(from+1, idx-1), true); + return new RequestEntryChain(s.substring(from+1, idx-1), true); } var v = nextVar(); //to optim if((idx == size || s.charAt(idx) == '.' || !legalValChar(c)) && v.matches(VAR_PATTERN)) { - return new RequestEntry(v); + return new RequestEntryChain(v); } nextWhile(RequestParser::legalValChar); - return new RequestEntry(from == idx ? null : s.substring(from, idx)); // empty => null + return new RequestEntryChain(from == idx ? null : s.substring(from, idx)); // empty => null } private String nextVar() { @@ -119,11 +119,11 @@ private String requireLegalVariable(String s) { : new ParseException("illegal variable name : " + quote(s)); } - private IllegalArgumentException unexpectedCharException() { + private ParseException unexpectedCharException() { return new ParseException("unexpected character '" + c + "' at index=" + idx); //end } - private IllegalArgumentException somethingExpectedException() { + private ParseException somethingExpectedException() { return new ParseException("something expected after '" + s.charAt(size-1) + "'"); } diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index a5a5a062..bd0f377c 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -18,6 +18,7 @@ import static org.usf.jquery.web.RequestColumn.decodeSingleColumn; import static org.usf.jquery.web.RequestFilter.decodeFilter; import static org.usf.jquery.web.RequestParser.parseEntries; +import static org.usf.jquery.web.RequestParser.parseEntry; import static org.usf.jquery.web.TableMetadata.emptyMetadata; import static org.usf.jquery.web.TableMetadata.tableMetadata; @@ -53,18 +54,13 @@ default TaggableView table() { return new DBTable(tableName(), identity()); } - default Optional columnType(ColumnDecorator cd) { - return metadata().columnMetada(cd) - .map(ColumnMetadata::getDataType); //else not binded - } - default TaggableColumn column(ColumnDecorator cd) { if(nonNull(cd.builder())) { return cd.builder().column(this).as(cd.reference()); } var cn = columnName(cd); if(cn.isPresent()) { - return new ViewColumn(table(), requireLegalVariable(cn.get()), cd.reference(), columnType(cd).orElse(null)); + return new ViewColumn(table(), requireLegalVariable(cn.get()), cd.reference(), cd.dataType(this)); } throw undeclaredResouceException(identity(), cd.identity()); } @@ -131,15 +127,18 @@ default void parseColumns(RequestQueryBuilder query, Map param default void parseFilters(RequestQueryBuilder query, Map parameters) { parameters.entrySet().stream() .filter(e-> !RESERVED_WORDS.contains(e.getKey())) - .map(e-> decodeFilter(e, this)) - .forEach(rf-> query.tablesIfAbsent(rf.tables()).filters(rf.filters())); + .flatMap(e-> { + var re = parseEntry(e.getKey()); + return Stream.of(e.getValue()).map(v-> re.asFilter(this, v)); + }) + .forEach(query::filters); } default void parseOrders(RequestQueryBuilder query, Map parameters) { if(parameters.containsKey(ORDER)) { Stream.of(parameters.get(ORDER)) - .flatMap(c-> decodeColumns(c, this, true)) - .forEach(rc-> query.tablesIfAbsent(rc.tableDecorator().table()).orders(rc.toOrder())); + .flatMap(c-> parseEntries(c).stream()) + .forEach(e-> query.orders(e.asOrder(this))); } } diff --git a/src/main/java/org/usf/jquery/web/WebException.java b/src/main/java/org/usf/jquery/web/WebException.java new file mode 100644 index 00000000..9b1587aa --- /dev/null +++ b/src/main/java/org/usf/jquery/web/WebException.java @@ -0,0 +1,22 @@ +package org.usf.jquery.web; + +/** + * + * @author u$f + * + */ +@SuppressWarnings("serial") +public class WebException extends RuntimeException { + + public WebException(String message) { + super(message); + } + + public WebException(Throwable cause) { + super(cause); + } + + public WebException(String message, Throwable cause) { + super(message, cause); + } +} From 34df567e127cd68eaa76c1a2bf911fd2897f26a0 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 29 Dec 2023 01:52:53 +0100 Subject: [PATCH 020/298] edit --- .../org/usf/jquery/web/RequestEntryChain.java | 42 ++++++++++++------- .../org/usf/jquery/web/RequestParser.java | 4 ++ .../org/usf/jquery/web/TableDecorator.java | 3 +- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index c8b238c3..d9b2575f 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -13,8 +13,10 @@ import static org.usf.jquery.web.ArgumentParsers.javaTypeParser; import static org.usf.jquery.web.CriteriaBuilder.ofComparator; import static org.usf.jquery.web.JQueryContext.context; +import static org.usf.jquery.web.ParseException.cannotEvaluateException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.stream.Stream; @@ -67,7 +69,7 @@ public TaggableColumn asColumn(TableDecorator td) { return oc.as(isNull(e.tag) ? c.tagname() : e.tag); } - public DBFilter asFilter(TableDecorator td, String expression) { + public DBFilter asFilter(TableDecorator td, List values) { var t = lookup(td, false); var c = t.buildColumn(); var e = t.entry.next; @@ -84,10 +86,10 @@ public DBFilter asFilter(TableDecorator td, String expression) { } var cd = c == oc ? t.cd : DEFAUL_COLUMN; //no operation if(isNull(e)) { // no expression - return oc.filter(toExpression(td, cd, null, expression)); + return oc.filter(toComparison(td, cd, null, values)); } else if(e.isLast()) { - return oc.filter(toExpression(td, cd, e.value, expression)); + return oc.filter(toComparison(td, cd, e.value, values)); } throw cannotEvaluateException(e); //more detail } @@ -149,29 +151,31 @@ private OperationColumn toOperation(TableDecorator td, DBColumn col) { return op.args(params.toArray()); } - - static ComparisonExpression toExpression(TableDecorator td, ColumnDecorator cd, String exp, String... values) { + static ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, String exp, List values) { if(nonNull(exp)) { var criteria = cd.criteria(exp); if(nonNull(criteria)) { - return criteria.build(values); + return criteria.build(toStringArray(values)); } } - var cmp = cd.comparator(exp, values.length); + var cmp = cd.comparator(exp, values.size()); if(nonNull(cmp)) { - var prs = requireNonNull(cd.parser(td)); - if(values.length == 0) { - return cmp.expression(null); - } - if(values.length == 1) { - return cmp.expression(prs.parse(values[0])); + if(values.size() == 1) { + try { + return cmp.expression(values.get(0).asColumn(td)); // try parse column + } + catch (Exception e) { + var prs = requireNonNull(cd.parser(td)); + return cmp.expression(prs.parse(values.get(0).toString())); + } } - var args = prs.parseAll(values); + var prs = requireNonNull(cd.parser(td)); + var args = prs.parseAll(toStringArray(values)); return cmp instanceof InCompartor ? cmp.expression(args) : ofComparator(cmp).build(args); } - throw ParseException.cannotEvaluateException("expression", null); + throw ParseException.cannotEvaluateException("exp", exp); } private Object toArg(TableDecorator td, JavaType... types) { @@ -251,6 +255,10 @@ static ParseException cannotEvaluateException(RequestEntryChain entry) { return new ParseException("cannot evaluate entry : " + quote(entry.toString())); } + static String[] toStringArray(List entries) { + return entries.stream().map(RequestEntryChain::toString).toArray(String[]::new); + } + @RequiredArgsConstructor static class Triple { @@ -263,4 +271,8 @@ TaggableColumn buildColumn() { return isNull(column) ? td.column(cd) : column; } } + + public static void main(String[] args) { + System.out.println(Arrays.toString(",2,3,".split(","))); + } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 462f1e7b..86b56c31 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -31,6 +31,10 @@ public static List parseEntries(String s) { return new RequestParser(s).parseEntries(true, false); } + public static List parseArgs(String s) { + return new RequestParser(s).parseEntries(true, true); + } + private List parseEntries(boolean multiple, boolean argument) { var entries = new LinkedList(); entries.add(parseEntry(multiple, argument)); diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index bd0f377c..e0980106 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -17,6 +17,7 @@ import static org.usf.jquery.web.RequestColumn.decodeColumns; import static org.usf.jquery.web.RequestColumn.decodeSingleColumn; import static org.usf.jquery.web.RequestFilter.decodeFilter; +import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.RequestParser.parseEntry; import static org.usf.jquery.web.TableMetadata.emptyMetadata; @@ -129,7 +130,7 @@ default void parseFilters(RequestQueryBuilder query, Map param .filter(e-> !RESERVED_WORDS.contains(e.getKey())) .flatMap(e-> { var re = parseEntry(e.getKey()); - return Stream.of(e.getValue()).map(v-> re.asFilter(this, v)); + return Stream.of(e.getValue()).map(v-> re.asFilter(this, parseArgs(v))); }) .forEach(query::filters); } From 48c03983a5ed0b9df0c06415cd9a705b31687c1e Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 29 Dec 2023 02:00:16 +0100 Subject: [PATCH 021/298] edit --- .../org/usf/jquery/web/RequestEntryChain.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index d9b2575f..81f72fcc 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -13,10 +13,8 @@ import static org.usf.jquery.web.ArgumentParsers.javaTypeParser; import static org.usf.jquery.web.CriteriaBuilder.ofComparator; import static org.usf.jquery.web.JQueryContext.context; -import static org.usf.jquery.web.ParseException.cannotEvaluateException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.stream.Stream; @@ -175,7 +173,7 @@ static ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, ? cmp.expression(args) : ofComparator(cmp).build(args); } - throw ParseException.cannotEvaluateException("exp", exp); + throw cannotEvaluateException(exp); } private Object toArg(TableDecorator td, JavaType... types) { @@ -250,9 +248,13 @@ public String toString() { } return tag == null ? s : s + ":" + tag; } - + static ParseException cannotEvaluateException(RequestEntryChain entry) { - return new ParseException("cannot evaluate entry : " + quote(entry.toString())); + return cannotEvaluateException(entry.toString()); + } + + static ParseException cannotEvaluateException(String entry) { + return new ParseException("cannot evaluate entry : " + quote(entry)); } static String[] toStringArray(List entries) { @@ -271,8 +273,4 @@ TaggableColumn buildColumn() { return isNull(column) ? td.column(cd) : column; } } - - public static void main(String[] args) { - System.out.println(Arrays.toString(",2,3,".split(","))); - } } \ No newline at end of file From dc51b2439c6504cbdb1753a30138e3c6d8952e88 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 2 Jan 2024 01:38:38 +0100 Subject: [PATCH 022/298] edit --- .../java/org/usf/jquery/core/CaseColumn.java | 1 - .../org/usf/jquery/core/ClauseFunction.java | 26 +++++ .../usf/jquery/core/ColumnSingleFilter.java | 3 +- .../java/org/usf/jquery/core/DBTable.java | 4 +- .../java/org/usf/jquery/core/JDBCType.java | 14 ++- .../java/org/usf/jquery/core/JavaType.java | 7 +- .../org/usf/jquery/core/OperationColumn.java | 2 + .../java/org/usf/jquery/core/Operator.java | 31 +++-- .../java/org/usf/jquery/core/OverClause.java | 61 ++++++---- .../jquery/core/QueryParameterBuilder.java | 20 +++- .../org/usf/jquery/core/TaggableColumn.java | 6 + .../org/usf/jquery/core/TaggableView.java | 2 +- .../org/usf/jquery/core/TypedOperator.java | 1 + .../java/org/usf/jquery/core/WindowView.java | 18 +-- .../org/usf/jquery/web/ArgumentParser.java | 4 +- .../org/usf/jquery/web/ArgumentParsers.java | 18 +-- .../java/org/usf/jquery/web/ChartMappers.java | 5 + .../org/usf/jquery/web/ColumnBuilder.java | 2 +- .../org/usf/jquery/web/ColumnDecorator.java | 9 +- .../org/usf/jquery/web/ColumnMetadata.java | 7 +- .../org/usf/jquery/web/CriteriaBuilder.java | 2 +- .../org/usf/jquery/web/DatabaseMetadata.java | 2 +- .../usf/jquery/web/LinkedRequestEntry.java | 1 - .../org/usf/jquery/web/RequestColumn.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 107 +++++++++++------- .../org/usf/jquery/web/TableDecorator.java | 3 - 26 files changed, 237 insertions(+), 121 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/ClauseFunction.java diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 35528486..6196d710 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -8,7 +8,6 @@ import java.util.Collection; import java.util.LinkedList; -import java.util.function.Predicate; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/org/usf/jquery/core/ClauseFunction.java b/src/main/java/org/usf/jquery/core/ClauseFunction.java new file mode 100644 index 00000000..29641ce3 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/ClauseFunction.java @@ -0,0 +1,26 @@ +package org.usf.jquery.core; + +import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.SqlStringBuilder.COMA; +import static org.usf.jquery.core.SqlStringBuilder.EMPTY; +import static org.usf.jquery.core.SqlStringBuilder.SPACE; +import static org.usf.jquery.core.Utils.isEmpty; + +import java.util.stream.Stream; + +/** + * + * @author u$f + * + */ +@FunctionalInterface +public interface ClauseFunction extends FunctionOperator { + + @Override + default String sql(QueryParameterBuilder builder, Object[] args) { + return isEmpty(args) + ? EMPTY + : id() + SPACE + Stream.of(args).map(builder::appendParameter).collect(joining(COMA)); + } + +} diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index fb947c1d..b88b2b69 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -29,8 +29,7 @@ public String sql(QueryParameterBuilder ph) { @Override public boolean isAggregation() { - return column.isAggregation() - || aggregation(expression); + return column.isAggregation() || aggregation(expression); } @Override diff --git a/src/main/java/org/usf/jquery/core/DBTable.java b/src/main/java/org/usf/jquery/core/DBTable.java index eb109ec4..2ae8df21 100644 --- a/src/main/java/org/usf/jquery/core/DBTable.java +++ b/src/main/java/org/usf/jquery/core/DBTable.java @@ -29,6 +29,6 @@ public String tagname() { @Override public String toString() { - return sql(addWithValue(), ""); - } + return sql(addWithValue(), ""); + } } diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index 121b4358..9d005efe 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -41,10 +41,12 @@ public enum JDBCType implements JavaType { DATE(Types.DATE, Date.class, Date.class::isInstance), TIME(Types.TIME, Time.class, Time.class::isInstance), TIMESTAMP(Types.TIMESTAMP, Timestamp.class, Timestamp.class::isInstance), - TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class, Timestamp.class::isInstance); + TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class, Timestamp.class::isInstance), + OTHER(Types.OTHER, null, o-> false); //isnull !? public static final JavaType AUTO = declare("AUTO", Object.class, o-> true); - public static final JavaType OTHER = declare("OTHER", Object.class, o-> {throw new UnsupportedOperationException("Unsupported SQL type");}); + + static final JavaType OVER_ARG = declare("over.arg", Object.class, o-> true); private final int value; private final Class type; @@ -107,16 +109,16 @@ private static boolean isString(Object o) { public static JavaType typeOf(Object o) { if(o instanceof Typed) { var t = ((Typed) o).javaType(); - return t == null ? AUTO : findType(t::equals); + return t == null ? null : findType(t::equals); } - return o == null ? AUTO : findType(e-> e.type().isInstance(o)); + return o == null ? null : findType(e-> e.type().isInstance(o)); } - public static JavaType fromDataType(int value) { + public static JDBCType fromDataType(int value) { return findType(t-> t.value == value); } - public static JavaType findType(Predicate predicate) { + public static JDBCType findType(Predicate predicate) { for(var t : values()) { if(predicate.test(t)) { return t; diff --git a/src/main/java/org/usf/jquery/core/JavaType.java b/src/main/java/org/usf/jquery/core/JavaType.java index 89d76a89..3b8b605f 100644 --- a/src/main/java/org/usf/jquery/core/JavaType.java +++ b/src/main/java/org/usf/jquery/core/JavaType.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static java.util.Objects.isNull; + import java.util.function.Predicate; import lombok.NonNull; @@ -17,8 +19,8 @@ public interface JavaType { boolean accept(Object o); - static JavaType instance(String name, Class type) { - return declare(name, type, type::isInstance); + static JavaType instance(Class type) { + return declare(type.getSimpleName(), type, o-> isNull(o) || type.isInstance(o)); } static JavaType declare(@NonNull String name, @NonNull Class type, @NonNull Predicate predicate) { @@ -40,5 +42,4 @@ public boolean accept(Object o) { } }; } - } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index d2922968..baad4c38 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -6,6 +6,7 @@ import java.util.stream.Stream; import lombok.AccessLevel; +import lombok.Getter; import lombok.RequiredArgsConstructor; /** @@ -13,6 +14,7 @@ * @author u$f * */ +@Getter(AccessLevel.PACKAGE) @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class OperationColumn implements DBColumn { diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 089e2d6c..57f3775b 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -39,7 +39,7 @@ static TypedOperator plus() { } static TypedOperator minus() { - return new TypedOperator(DOUBLE, operator("-"), required(DOUBLE), required(DOUBLE)); // date|datetime + return new TypedOperator(DOUBLE, operator("-"), required(DOUBLE), required(DOUBLE)); //date|datetime } static TypedOperator multiply() { @@ -192,7 +192,6 @@ static TypedOperator epoch() { return new TypedOperator(BIGINT, extract("EPOCH"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata } - //cast functions static TypedOperator varchar() { @@ -256,11 +255,13 @@ static TypedOperator rowNumber() { static TypedOperator denseRank() { return new TypedOperator(INTEGER, window("DENSE_RANK")); // takes no args } + + //pipe functions static TypedOperator over() { return new TypedOperator(firstArgType(), pipe("OVER"), - required(instance("??", OperationColumn.class)), // TODO wrap => aggreagation || window - required(instance("??", OverClause.class))) { + required(instance(OperationColumn.class)), + required(instance(OverClause.class))) { @Override public OperationColumn args(Object... args) { return super.args(args).aggregation(false); //!aggregation @@ -268,6 +269,18 @@ public OperationColumn args(Object... args) { }; } + //clause functions + + static TypedOperator partition() { + var ct = instance(DBColumn.class); // require at least one column + return new TypedOperator(instance(DBColumn[].class), clause("PARTITION BY"), required(ct), varargs(ct)); + } + + static TypedOperator order() { + var ot = instance(DBOrder.class); // require at least one order + return new TypedOperator(instance(DBOrder[].class), clause("ORDER BY"), required(ot), varargs(ot)); + } + static ArithmeticOperator operator(String symbol) { return ()-> symbol; } @@ -296,8 +309,12 @@ static PipeFunction pipe(String name) { return ()-> name; } - static Optional lookupNoArgFunction(String op) { - return lookupOperator(op).filter(fn-> fn.requireArgCount() == 0); + static ClauseFunction clause(String name) { + return ()-> name; + } + + static Optional lookupWindowFunction(String op) { + return lookupOperator(op).filter(WindowFunction.class::isInstance); } static Optional lookupOperator(String op) { @@ -306,7 +323,7 @@ static Optional lookupOperator(String op) { if(isStatic(m.getModifiers()) && m.getReturnType() == TypedOperator.class) { // no private static return Optional.of((TypedOperator) m.invoke(null)); } - } catch (Exception e) {/*do not throw exception*/} + } catch (Exception e) {/* do not throw exception */} return empty(); } diff --git a/src/main/java/org/usf/jquery/core/OverClause.java b/src/main/java/org/usf/jquery/core/OverClause.java index 39546b70..e02ed505 100644 --- a/src/main/java/org/usf/jquery/core/OverClause.java +++ b/src/main/java/org/usf/jquery/core/OverClause.java @@ -1,52 +1,73 @@ package org.usf.jquery.core; +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; +import static java.util.stream.Collectors.groupingBy; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; -import static org.usf.jquery.core.SqlStringBuilder.COMA; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; -import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.stream.Stream; -import lombok.NonNull; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; /** * * @author u$f * */ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class OverClause implements DBObject { - private final List partitions = new LinkedList<>(); - private final List orders = new LinkedList<>(); + private final OperationColumn partition; + private final OperationColumn order; - public OverClause partitions(@NonNull DBColumn... columns) { - Stream.of(columns).forEach(this.partitions::add); - return this; - } - - public OverClause orders(@NonNull DBOrder... orders) { - Stream.of(orders).forEach(this.orders::add); - return this; + public OverClause() { + this(null, null); } @Override public String sql(QueryParameterBuilder builder, Object[] args) { - requireNoArgs(args, getClass()::getSimpleName); + requireNoArgs(args, OverClause.class::getSimpleName); return sql(builder); } - public String sql(QueryParameterBuilder builder) { + String sql(QueryParameterBuilder builder) { var qp = addWithValue(); //no alias var sb = new SqlStringBuilder(100); - if(!partitions.isEmpty()) { - sb.append("PARTITION BY ").appendEach(partitions, COMA, o-> o.sql(qp)); + if(nonNull(partition)) { + sb.append(partition.sql(qp)); } - if(!orders.isEmpty()) { //require orders - sb.appendIf(!partitions.isEmpty(), SPACE) - .append("ORDER BY ").appendEach(orders, COMA, o-> o.sql(qp)); + if(nonNull(order)) { //require orders + sb.appendIf(nonNull(partition), SPACE).append(order.sql(qp)); } return sb.toString(); } + + public static OverClause clauses(OperationColumn... args) { //partition, order, ... + if(args == null) { + return new OverClause(); + } + var map = Stream.of(args).collect(groupingBy(o-> o.getOperator().id())); + var prt = requireOneArg(map, "PARTITION BY"); + var ord = requireOneArg(map, "ORDER BY"); + if(map.isEmpty()) { + return new OverClause(prt, ord); + } + throw new IllegalArgumentException("illegal over function arguments : " + map.keySet()); + } + + private static OperationColumn requireOneArg(Map> map, String key) { + var args = map.remove(key); + if(isNull(args)) { + return null; + } + if(args.size() == 1) { + return args.get(0); + } + throw new IllegalArgumentException("duplicated arg values " + key); + } } diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index b76ba6b6..55da5919 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -19,6 +19,7 @@ import java.util.LinkedList; import java.util.List; import java.util.function.Function; +import java.util.function.IntConsumer; import java.util.stream.Stream; import lombok.AccessLevel; @@ -31,7 +32,7 @@ */ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class QueryParameterBuilder { - + private static final String ALIAS = "t"; private static final String ARG = "?"; @@ -39,14 +40,25 @@ public final class QueryParameterBuilder { private final List views; //indexed public String view(TaggableView view) { - for(int i=0; i {}); + } + + public String overwriteView(TaggableView view) { + return view(view, i-> views.set(i, view)); + } + + private String view(TaggableView view, IntConsumer consumer) { + var i=0; + for(; i type, boolean addWithValue) { diff --git a/src/main/java/org/usf/jquery/core/TaggableColumn.java b/src/main/java/org/usf/jquery/core/TaggableColumn.java index 6c50a6e1..e4f4b08f 100644 --- a/src/main/java/org/usf/jquery/core/TaggableColumn.java +++ b/src/main/java/org/usf/jquery/core/TaggableColumn.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; + /** * * @author u$f @@ -9,4 +11,8 @@ public interface TaggableColumn extends DBColumn { String tagname(); //JSON & TAG + default String sql(QueryParameterBuilder builder, boolean as) { + var s = this.sql(builder); + return as ? s + " AS " + doubleQuote(tagname()) : s; + } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/TaggableView.java b/src/main/java/org/usf/jquery/core/TaggableView.java index d3a06a1d..caf3f4cb 100644 --- a/src/main/java/org/usf/jquery/core/TaggableView.java +++ b/src/main/java/org/usf/jquery/core/TaggableView.java @@ -8,5 +8,5 @@ public interface TaggableView extends DBView { String tagname(); - + } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 57b55db7..3f4a8185 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -55,6 +55,7 @@ public OperationColumn args(Object... args) { } return new OperationColumn(operator, args, typeFn.apply(args)); } + public int requireArgCount() { var i=0; diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index 1d6be39c..f7fc5efc 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.DBColumn.column; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; @@ -18,29 +17,30 @@ public final class WindowView implements TaggableView { private final TaggableView view; private final TaggableColumn column; //named operation column - private final ComparisonExpression expression; @Override - public String sql(QueryParameterBuilder builder, String schema) { + public String sql(QueryParameterBuilder builder, String schema) { //sub query should not use main builder var va = "v0"; return new SqlStringBuilder(100) .append("(SELECT ").append(va).append(".*, ") .append(column.sql(addWithValue())).append(" AS ").append(doubleQuote(column.tagname())) - .append(" FROM ").append(view.sql(builder, schema)).append(SPACE).append(va).append(")") + .append(" FROM ").append(view.sql(addWithValue(), schema)).append(SPACE).append(va).append(")") .toString(); } - - public DBFilter filter() { - return b-> expression.sql(b, column(member(b.view(view), doubleQuote(column.tagname())))); + + public DBFilter filter(ComparisonExpression expression) { + DBColumn col = b-> member(b.overwriteView(this), column.tagname()); + return col.filter(expression); } @Override - public String tagname() { - return view.tagname(); //inherits tagname + public String tagname() { //inherits tagname + return view.tagname(); } @Override public String toString() { return sql(addWithValue(), ""); } + } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParser.java b/src/main/java/org/usf/jquery/web/ArgumentParser.java index 3b7d4c11..c18a4133 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParser.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParser.java @@ -1,7 +1,7 @@ package org.usf.jquery.web; +import static java.lang.String.format; import static java.util.Objects.isNull; -import static org.usf.jquery.web.ParseException.cannotParseException; import java.util.stream.Stream; @@ -24,7 +24,7 @@ default Object parse(String v) { return nativeParse(v); } catch(Exception e) { - throw cannotParseException("parameter value", v, e); + throw new ParseException(format("cannot parse %s value '%s'", toString(), v), e); } } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index e1c0cf8a..e268cb64 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -1,6 +1,7 @@ package org.usf.jquery.web; -import static org.usf.jquery.core.JDBCType.AUTO; +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.JDBCType.BIGINT; import static org.usf.jquery.core.JDBCType.DATE; import static org.usf.jquery.core.JDBCType.DOUBLE; @@ -24,38 +25,36 @@ import lombok.NoArgsConstructor; /** - * * * @author u$f - * + * */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ArgumentParsers { - private static final ArgumentParser[] DEFAULT = { + private static final ArgumentParser[] STD_TYPES = { jdbcTypeParser(BIGINT), jdbcTypeParser(DOUBLE), jdbcTypeParser(DATE), jdbcTypeParser(TIMESTAMP), jdbcTypeParser(TIME), jdbcTypeParser(TIMESTAMP_WITH_TIMEZONE)}; - public static ArgumentParser javaTypeParser(JavaType type) { if(type instanceof JDBCType) { return jdbcTypeParser((JDBCType) type); } - if(type == AUTO) { + if(isNull(type)) { return ArgumentParsers::autoTypeParse; } throw new UnsupportedOperationException("unsupported type " + type); } public static Object autoTypeParse(String v) { - for(var p : DEFAULT) { + for(var p : STD_TYPES) { var o = p.tryParse(v); - if(o != null) { + if(nonNull(o)) { return o; } } - return v; //string value + return v; //string value by default } public static ArgumentParser jdbcTypeParser(JDBCType type) { @@ -79,6 +78,7 @@ public static ArgumentParser jdbcTypeParser(JDBCType type) { case TIME: return v-> Time.valueOf(LocalTime.parse(v)); case TIMESTAMP: return v-> Timestamp.from(Instant.parse(v)); case TIMESTAMP_WITH_TIMEZONE: return v-> Timestamp.from(ZonedDateTime.parse(v).toInstant()); + case OTHER: default: throw new UnsupportedOperationException("unsupported type " + type); } } diff --git a/src/main/java/org/usf/jquery/web/ChartMappers.java b/src/main/java/org/usf/jquery/web/ChartMappers.java index f7896d39..fc50db65 100644 --- a/src/main/java/org/usf/jquery/web/ChartMappers.java +++ b/src/main/java/org/usf/jquery/web/ChartMappers.java @@ -18,6 +18,11 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; +/** + * + * @author u$f + * + */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class ChartMappers { diff --git a/src/main/java/org/usf/jquery/web/ColumnBuilder.java b/src/main/java/org/usf/jquery/web/ColumnBuilder.java index 86bba369..7da88bf6 100644 --- a/src/main/java/org/usf/jquery/web/ColumnBuilder.java +++ b/src/main/java/org/usf/jquery/web/ColumnBuilder.java @@ -5,7 +5,7 @@ /** * * @author u$f - * + * */ @FunctionalInterface public interface ColumnBuilder { diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index b3e921fa..dadad9ac 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -1,8 +1,6 @@ package org.usf.jquery.web; import static java.util.Objects.isNull; -import static org.usf.jquery.core.DBColumn.count; -import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.Comparator.equal; import static org.usf.jquery.core.Comparator.greaterOrEqual; import static org.usf.jquery.core.Comparator.greaterThan; @@ -14,11 +12,12 @@ import static org.usf.jquery.core.Comparator.notEqual; import static org.usf.jquery.core.Comparator.notIn; import static org.usf.jquery.core.Comparator.notLike; +import static org.usf.jquery.core.DBColumn.count; import static org.usf.jquery.web.ArgumentParsers.javaTypeParser; import org.usf.jquery.core.Comparator; import org.usf.jquery.core.ComparisonExpression; -import org.usf.jquery.core.JavaType; +import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.Operator; import org.usf.jquery.core.StringComparator; @@ -37,10 +36,10 @@ default String reference() { //JSON return identity(); } - default JavaType dataType(TableDecorator td) { + default JDBCType dataType(TableDecorator td) { return td.metadata().columnMetada(this) .map(ColumnMetadata::getDataType) - .orElse(AUTO); + .orElse(null); } /** diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index edd6975a..212320d8 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -1,9 +1,8 @@ package org.usf.jquery.web; -import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.Utils.UNLIMITED; -import org.usf.jquery.core.JavaType; +import org.usf.jquery.core.JDBCType; import lombok.AccessLevel; import lombok.Getter; @@ -23,11 +22,11 @@ public final class ColumnMetadata { private final String columnName; - private JavaType dataType = AUTO; + private JDBCType dataType = null; private int dataSize = UNLIMITED; ColumnMetadata reset() { - this.dataType = AUTO; + this.dataType = null; this.dataSize = UNLIMITED; return this; } diff --git a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java index ad82ad38..97e8075e 100644 --- a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java +++ b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java @@ -7,8 +7,8 @@ import java.util.stream.Stream; -import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.Comparator; +import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.LogicalOperator; /** diff --git a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java index 247bae39..128b0926 100644 --- a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java +++ b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java @@ -87,7 +87,7 @@ static void logTableColumns(Map map) { log.info(format(pattern, "TAGNAME", "NAME", "TYPE", "LENGTH")); log.info(bar); map.entrySet().forEach(e-> - log.info(format(pattern, e.getKey(), e.getValue().getColumnName(), e.getValue().getDataType().getValue(), e.getValue().getDataSize()))); + log.info(format(pattern, e.getKey(), e.getValue().getColumnName(), e.getValue().getDataType(), e.getValue().getDataSize()))); log.info(bar); } } diff --git a/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java b/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java index 2ebd9380..d537432c 100644 --- a/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java +++ b/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java @@ -33,7 +33,6 @@ import org.usf.jquery.core.Operator; import org.usf.jquery.core.OverClause; import org.usf.jquery.core.TypedFunction; -import org.usf.jquery.core.TypedOperator; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/org/usf/jquery/web/RequestColumn.java b/src/main/java/org/usf/jquery/web/RequestColumn.java index ffe9a7a2..7804e358 100644 --- a/src/main/java/org/usf/jquery/web/RequestColumn.java +++ b/src/main/java/org/usf/jquery/web/RequestColumn.java @@ -13,9 +13,9 @@ import java.util.stream.Stream; import org.usf.jquery.core.BasicComparator; +import org.usf.jquery.core.Comparator; import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.DBColumn; -import org.usf.jquery.core.Comparator; import org.usf.jquery.core.DBOrder; import org.usf.jquery.core.InCompartor; import org.usf.jquery.core.JavaType; diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 81f72fcc..1d1e7199 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -7,6 +7,7 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Operator.lookupNoArgFunction; import static org.usf.jquery.core.Operator.lookupOperator; +import static org.usf.jquery.core.OverClause.clauses; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Validation.VAR_PATTERN; @@ -27,6 +28,7 @@ import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.WindowView; import lombok.AccessLevel; import lombok.Getter; @@ -39,6 +41,8 @@ final class RequestEntryChain { private static final ColumnDecorator DEFAUL_COLUMN = ()-> null; //unused identity + + private static final String OVER_FN = "OVER"; private final String value; private final boolean text; //"string" @@ -83,13 +87,19 @@ public DBFilter asFilter(TableDecorator td, List values) { e = e.next; } var cd = c == oc ? t.cd : DEFAUL_COLUMN; //no operation + ComparisonExpression exp = null; if(isNull(e)) { // no expression - return oc.filter(toComparison(td, cd, null, values)); + exp = new RequestEntryChain(null).toComparison(td, cd, values); } else if(e.isLast()) { - return oc.filter(toComparison(td, cd, e.value, values)); + exp = e.toComparison(td, cd, values); + } + else { + throw cannotEvaluateException(e); //more detail } - throw cannotEvaluateException(e); //more detail + return OVER_FN.equals(e.value) + ? new WindowView(td.table(), oc.as(c.tagname())).filter(exp) + : oc.filter(exp); } public DBOrder asOrder(TableDecorator td) { @@ -120,16 +130,55 @@ else if(e.isLast()) { //last entry throw cannotEvaluateException(e); //column expected } - private OperationColumn toOperation(TableDecorator td, DBColumn col) { + ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List values) { + if(nonNull(value)) { + var criteria = cd.criteria(value); + if(nonNull(criteria)) { + return criteria.build(toStringArray(values)); + } + } + var cmp = cd.comparator(value, values.size()); + if(nonNull(cmp)) { + if(values.size() == 1) { + try { + return cmp.expression(values.get(0).asColumn(td)); // try parse column + } + catch (Exception e) { + var prs = requireNonNull(cd.parser(td)); + return cmp.expression(prs.parse(values.get(0).toString())); + } + } + var prs = requireNonNull(cd.parser(td)); + var arr = prs.parseAll(toStringArray(values)); + return cmp instanceof InCompartor + ? cmp.expression(arr) + : ofComparator(cmp).build(arr); + } + throw cannotEvaluateException(value); + } + + OperationColumn requireOperation(TableDecorator td, DBColumn col) { + var op = toOperation(td, col); + if(nonNull(op)) { + return op; + } + throw cannotEvaluateException(value); // as function + } + + OperationColumn toOperation(TableDecorator td, DBColumn col) { var res = lookupOperator(value); if(res.isEmpty()) { return null; } var op = res.get(); + if("OVER".equals(res.get().id())) { + var oc = args.stream().map(o-> o.requireOperation(td, col)).toArray(OperationColumn[]::new); + return op.args(clauses(oc)); + } var min = op.requireArgCount(); var np = args.size()+1; // col if(np < min || (!op.isVarags() && np > op.getParameters().length)) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("msg"); } var params = new ArrayList(np); params.add(col); @@ -149,34 +198,7 @@ private OperationColumn toOperation(TableDecorator td, DBColumn col) { return op.args(params.toArray()); } - static ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, String exp, List values) { - if(nonNull(exp)) { - var criteria = cd.criteria(exp); - if(nonNull(criteria)) { - return criteria.build(toStringArray(values)); - } - } - var cmp = cd.comparator(exp, values.size()); - if(nonNull(cmp)) { - if(values.size() == 1) { - try { - return cmp.expression(values.get(0).asColumn(td)); // try parse column - } - catch (Exception e) { - var prs = requireNonNull(cd.parser(td)); - return cmp.expression(prs.parse(values.get(0).toString())); - } - } - var prs = requireNonNull(cd.parser(td)); - var args = prs.parseAll(toStringArray(values)); - return cmp instanceof InCompartor - ? cmp.expression(args) - : ofComparator(cmp).build(args); - } - throw cannotEvaluateException(exp); - } - - private Object toArg(TableDecorator td, JavaType... types) { + Object toArg(TableDecorator td, JavaType... types) { if(isNull(value) || text) { return requireNoArgs().value; } @@ -207,7 +229,7 @@ private Triple lookup(TableDecorator td, boolean noArgFn) { //columnName == view return new Triple(td, context().getColumn(requireNoArgs().value), this, null); } if(noArgFn) { - var res = lookupNoArgFunction(value); + var res = lookupNoArgFunction(value); //window function if(res.isPresent()) { var op = res.get(); if(isNull(args) || args.isEmpty()) { @@ -219,6 +241,14 @@ private Triple lookup(TableDecorator td, boolean noArgFn) { //columnName == view throw cannotEvaluateException(this); } + + private Object lookupColumn(TableDecorator td, boolean noArgFn) { + + if(context().isDeclaredColumn(value)) { + + } + } + private RequestEntryChain requireNoArgs() { if(isNull(args)) { return this; @@ -237,16 +267,16 @@ public boolean next() { @Override public String toString() { var s = ""; - if(value != null) { + if(nonNull(value)) { s += text ? doubleQuote(value) : value; } - if(args != null){ + if(nonNull(args)){ s += args.stream().map(RequestEntryChain::toString).collect(joining(",", "(", ")")); } - if(next != null) { + if(nonNull(next)) { s += "." + next.toString(); } - return tag == null ? s : s + ":" + tag; + return isNull(tag) ? s : s + ":" + tag; } static ParseException cannotEvaluateException(RequestEntryChain entry) { @@ -273,4 +303,5 @@ TaggableColumn buildColumn() { return isNull(column) ? td.column(cd) : column; } } + } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index e0980106..c6c0897c 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -14,9 +14,7 @@ import static org.usf.jquery.web.JQueryContext.database; import static org.usf.jquery.web.MissingParameterException.missingParameterException; import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; -import static org.usf.jquery.web.RequestColumn.decodeColumns; import static org.usf.jquery.web.RequestColumn.decodeSingleColumn; -import static org.usf.jquery.web.RequestFilter.decodeFilter; import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.RequestParser.parseEntry; @@ -32,7 +30,6 @@ import org.usf.jquery.core.NamedColumn; import org.usf.jquery.core.OverColumn; import org.usf.jquery.core.RequestQueryBuilder; -import org.usf.jquery.core.JavaType; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TaggableView; import org.usf.jquery.core.ViewColumn; From 10e6f107b25412fe1e0f8b3779b624598c3ee5da Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 2 Jan 2024 22:14:29 +0100 Subject: [PATCH 023/298] edit --- .../java/org/usf/jquery/core/CaseColumn.java | 7 +- .../java/org/usf/jquery/core/DBColumn.java | 4 +- .../java/org/usf/jquery/core/JDBCType.java | 6 +- .../org/usf/jquery/core/OperationColumn.java | 3 +- .../java/org/usf/jquery/core/Operator.java | 48 ++- .../java/org/usf/jquery/core/OverClause.java | 4 +- .../java/org/usf/jquery/core/Parameter.java | 17 +- .../jquery/core/QueryParameterBuilder.java | 7 +- .../usf/jquery/core/RequestQueryBuilder.java | 7 +- .../org/usf/jquery/core/TaggableView.java | 6 + .../org/usf/jquery/core/TypedOperator.java | 49 ++- .../java/org/usf/jquery/core/ViewColumn.java | 3 +- .../org/usf/jquery/web/ArgumentParser.java | 1 - .../org/usf/jquery/web/ColumnDecorator.java | 6 - .../usf/jquery/web/LinkedRequestEntry.java | 368 ------------------ .../org/usf/jquery/web/ParsableJDBCType.java | 90 ----- .../org/usf/jquery/web/ParsableSQLType.java | 35 -- .../org/usf/jquery/web/RequestColumn.java | 147 ------- .../org/usf/jquery/web/RequestEntryChain.java | 127 +++--- .../org/usf/jquery/web/RequestFilter.java | 68 ---- .../org/usf/jquery/web/RequestParser.java | 6 +- .../org/usf/jquery/web/TableDecorator.java | 40 -- .../org/usf/jquery/web/YearTableMetadata.java | 5 +- .../jquery/web/{ => view}/ChartMappers.java | 9 +- 24 files changed, 172 insertions(+), 891 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/web/LinkedRequestEntry.java delete mode 100644 src/main/java/org/usf/jquery/web/ParsableJDBCType.java delete mode 100644 src/main/java/org/usf/jquery/web/ParsableSQLType.java delete mode 100644 src/main/java/org/usf/jquery/web/RequestColumn.java delete mode 100644 src/main/java/org/usf/jquery/web/RequestFilter.java rename src/main/java/org/usf/jquery/web/{ => view}/ChartMappers.java (78%) diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 6196d710..2079fa63 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -1,13 +1,12 @@ package org.usf.jquery.core; -import static java.util.function.Predicate.not; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import java.util.Collection; import java.util.LinkedList; +import java.util.Objects; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -33,8 +32,8 @@ public String sql(QueryParameterBuilder builder) { public JavaType javaType() { return expressions.stream() .map(JDBCType::typeOf) - .filter(not(AUTO::equals)) // should have same type - .findAny().orElse(AUTO); + .filter(Objects::nonNull) // should have same type + .findAny().orElse(null); } public CaseColumn append(WhenExpression we) { diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 9728f1dc..b7cddaf3 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -1,7 +1,6 @@ package org.usf.jquery.core; import static java.util.Optional.empty; -import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -38,7 +37,7 @@ default boolean isConstant() { } default JavaType javaType() { - return AUTO; + return null; } default NamedColumn as(String name) { @@ -165,7 +164,6 @@ static boolean isColumnConstant(Object o) { return !(o instanceof DBColumn) || ((DBColumn)o).isConstant(); } - static OperationColumn count() { return count(column("*")); } diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index 9d005efe..1d75857d 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -10,6 +10,7 @@ import java.util.function.Function; import java.util.function.Predicate; +import lombok.Getter; import lombok.RequiredArgsConstructor; /** @@ -19,6 +20,7 @@ * @author u$f * */ +@Getter @RequiredArgsConstructor public enum JDBCType implements JavaType { @@ -44,8 +46,6 @@ public enum JDBCType implements JavaType { TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class, Timestamp.class::isInstance), OTHER(Types.OTHER, null, o-> false); //isnull !? - public static final JavaType AUTO = declare("AUTO", Object.class, o-> true); - static final JavaType OVER_ARG = declare("over.arg", Object.class, o-> true); private final int value; @@ -106,7 +106,7 @@ private static boolean isString(Object o) { || o.getClass() == String.class; } - public static JavaType typeOf(Object o) { + public static JDBCType typeOf(Object o) { if(o instanceof Typed) { var t = ((Typed) o).javaType(); return t == null ? null : findType(t::equals); diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index baad4c38..5cbd717b 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import java.util.stream.Stream; @@ -24,7 +23,7 @@ public final class OperationColumn implements DBColumn { private Boolean aggregation; public OperationColumn(Operator operation, Object[] args) { - this(operation, args, AUTO); + this(operation, args, null); } @Override diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 57f3775b..3028de32 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -51,18 +51,6 @@ static TypedOperator divise() { } //numeric functions - - static TypedOperator trunc() { - return new TypedOperator(BIGINT, function("TRUNC"), required(DOUBLE)); - } - - static TypedOperator ceil() { - return new TypedOperator(BIGINT, function("CEIL"), required(DOUBLE)); - } - - static TypedOperator floor() { - return new TypedOperator(BIGINT, function("FLOOR"), required(DOUBLE)); - } static TypedOperator sqrt() { return new TypedOperator(DOUBLE, function("SQRT"), required(DOUBLE)); @@ -73,12 +61,28 @@ static TypedOperator exp() { } static TypedOperator log() { - return new TypedOperator(DOUBLE, function("LOG"), required(DOUBLE)); + return new TypedOperator(DOUBLE, function("LOG"), required(DOUBLE), optional(INTEGER)); } static TypedOperator abs() { return new TypedOperator(DOUBLE, function("ABS"), required(DOUBLE)); } + + static TypedOperator ceil() { + return new TypedOperator(BIGINT, function("CEIL"), required(DOUBLE)); + } + + static TypedOperator floor() { + return new TypedOperator(BIGINT, function("FLOOR"), required(DOUBLE)); + } + + static TypedOperator trunc() { + return new TypedOperator(BIGINT, function("TRUNC"), required(DOUBLE), optional(INTEGER)); + } + + static TypedOperator round() { + return new TypedOperator(DOUBLE, function("ROUND"), required(DOUBLE), optional(INTEGER)); + } static TypedOperator mod() { return new TypedOperator(BIGINT, function("MOD"), required(DOUBLE), required(DOUBLE)); @@ -88,10 +92,6 @@ static TypedOperator pow() { return new TypedOperator(DOUBLE, function("POW"), required(DOUBLE), required(DOUBLE)); } - static TypedOperator round() { - return new TypedOperator(DOUBLE, function("ROUND"), required(DOUBLE), optional(INTEGER)); - } - //string functions static TypedOperator length() { @@ -260,11 +260,11 @@ static TypedOperator denseRank() { static TypedOperator over() { return new TypedOperator(firstArgType(), pipe("OVER"), - required(instance(OperationColumn.class)), + required(instance(DBColumn.class)), //NamedColumn, OperationColumn required(instance(OverClause.class))) { @Override public OperationColumn args(Object... args) { - return super.args(args).aggregation(false); //!aggregation + return super.args(args).aggregation(false); //over aggregation functions } }; } @@ -313,8 +313,16 @@ static ClauseFunction clause(String name) { return ()-> name; } + static StandaloneFunction constant(String name) { + return ()-> name; + } + + static Optional lookupStandaloneFunction(String op) { + return lookupOperator(op).filter(fn-> fn.unwrap() instanceof StandaloneFunction); + } + static Optional lookupWindowFunction(String op) { - return lookupOperator(op).filter(WindowFunction.class::isInstance); + return lookupOperator(op).filter(fn-> fn.unwrap() instanceof WindowFunction); } static Optional lookupOperator(String op) { diff --git a/src/main/java/org/usf/jquery/core/OverClause.java b/src/main/java/org/usf/jquery/core/OverClause.java index e02ed505..095f87bc 100644 --- a/src/main/java/org/usf/jquery/core/OverClause.java +++ b/src/main/java/org/usf/jquery/core/OverClause.java @@ -32,10 +32,10 @@ public OverClause() { @Override public String sql(QueryParameterBuilder builder, Object[] args) { requireNoArgs(args, OverClause.class::getSimpleName); - return sql(builder); + return sql(); } - String sql(QueryParameterBuilder builder) { + String sql() { var qp = addWithValue(); //no alias var sb = new SqlStringBuilder(100); if(nonNull(partition)) { diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index ab915f5e..db43bb62 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -2,7 +2,6 @@ import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.JDBCType.AUTO; import static org.usf.jquery.core.Utils.isEmpty; import java.util.stream.Stream; @@ -20,13 +19,9 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class Parameter { - private final JavaType[] types; + private final JavaType[] types; //null => accept all private final boolean required; private final boolean varargs; - - public JavaType[] getTypes() { - return isEmpty(types) ? new JavaType[] {AUTO} : types; - } public boolean accept(Object o) { return isEmpty(types) || Stream.of(types).anyMatch(t-> t.accept(o)); @@ -51,13 +46,13 @@ public static Parameter varargs(JavaType... types) { return new Parameter(types, false, true); } - public static Parameter[] checkArgs(Parameter... parameters) { + public static Parameter[] checkParams(Parameter... parameters) { if(nonNull(parameters)) { - var i = parameters.length; - while(--i>=0 && parameters[i].isRequired()); - for(; i>=0; i--) { + var i=0; + while(i views(){ + return views; + } public String appendParameter(Object o) { return appendParameter(o, Object.class, false); @@ -93,8 +96,8 @@ private String appendParameter(Object o, Class type, boolean addWithValue) { if(isNull(o)) { return dynamic() && !addWithValue ? appendArg(null) : "null"; } - if(o instanceof DBColumn) { //check type !? - return ((DBColumn)o).sql(this); + if(o instanceof DBObject) { //check type !? + return ((DBObject)o).sql(this, null); } if(type.isInstance(o)) { if(dynamic() && !addWithValue) { diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index dde1adf7..2917d665 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -8,7 +8,6 @@ import static org.usf.jquery.core.LogicalOperator.AND; import static org.usf.jquery.core.QueryParameterBuilder.parametrized; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.Validation.requireNonEmpty; @@ -84,12 +83,12 @@ public RequestQuery build(){ public RequestQuery build(String schema) { log.debug("building query..."); - requireNonEmpty(tables); +// requireNonEmpty(tables); requireNonEmpty(columns); var bg = currentTimeMillis(); var pb = parametrized(); var sb = new SqlStringBuilder(1000); //avg - pb.tables(tables.stream().map(TaggableView::tagname).toArray(String[]::new)); +// pb.tables(tables.stream().map(TaggableView::tagname).toArray(String[]::new)); if(isNull(it)) { build(sb, pb, schema); } @@ -113,7 +112,7 @@ void select(SqlStringBuilder sb, QueryParameterBuilder pb, String schema){ .appendIf(distinct, ()-> "DISTINCT ") .appendEach(columns, SCOMA, o-> o.sql(pb) + " AS " + doubleQuote(o.tagname())) .append(" FROM ") - .appendEach(tables, SCOMA, o-> o.sql(pb, schema) + SPACE + pb.tableAlias(o.tagname())); + .appendEach(pb.views(), SCOMA, o-> o.sql(pb, schema, true)); } void where(SqlStringBuilder sb, QueryParameterBuilder pb){ diff --git a/src/main/java/org/usf/jquery/core/TaggableView.java b/src/main/java/org/usf/jquery/core/TaggableView.java index caf3f4cb..ca964c3a 100644 --- a/src/main/java/org/usf/jquery/core/TaggableView.java +++ b/src/main/java/org/usf/jquery/core/TaggableView.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.SqlStringBuilder.SPACE; + /** * * @author u$f @@ -9,4 +11,8 @@ public interface TaggableView extends DBView { String tagname(); + default String sql(QueryParameterBuilder builder, String schema, boolean as) { + var s = this.sql(builder, schema); + return as ? s + SPACE + builder.view(this) : s; + } } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 3f4a8185..81ee32da 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -1,9 +1,10 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Parameter.checkArgs; -import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Validation.requireNoArgs; +import static java.lang.Math.min; +import static java.util.Objects.isNull; +import static org.usf.jquery.core.Parameter.checkParams; +import java.util.function.BiConsumer; import java.util.function.Function; import lombok.Getter; @@ -16,6 +17,8 @@ */ @Getter public class TypedOperator implements Operator { + + private static final Parameter[] NO_PARAM = new Parameter[0]; @Delegate private final Operator operator; @@ -23,13 +26,13 @@ public class TypedOperator implements Operator { private final Parameter[] parameters; public TypedOperator(JavaType type, Operator function, Parameter... args) { - this(o-> type, function, args == null ? new Parameter[0] : args); + this(o-> type, function, args == null ? NO_PARAM : args); } public TypedOperator(Function typeFn, Operator function, Parameter... parameter) { this.typeFn = typeFn; this.operator = function; - this.parameters = checkArgs(parameter); + this.parameters = checkParams(parameter); } public Operator unwrap() { @@ -38,28 +41,36 @@ public Operator unwrap() { @Override public OperationColumn args(Object... args) { - // TODO check arg types - if(isEmpty(parameters)) { - requireNoArgs(args, operator::id); + return args(args, (p, o)->{ + if(!p.accept(o)) { + throw new IllegalArgumentException("mismatch arg type"); + } + }); + } + + OperationColumn args(Object[] args, BiConsumer fn) { + if(isNull(args)) { + args = new Object[0]; } - else { - if(isEmpty(args)) { - if(parameters[0].isRequired()) { - - } - //require args + var na = args.length; + var rq = requireArgCount(); + if(na >= rq && (na <= parameters.length || isVarags())) { + var i=0; + for(; i count()); - } - static ColumnDecorator ofColumn(String ref, ColumnBuilder cb) { return new ColumnDecorator() { @Override diff --git a/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java b/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java deleted file mode 100644 index d537432c..00000000 --- a/src/main/java/org/usf/jquery/web/LinkedRequestEntry.java +++ /dev/null @@ -1,368 +0,0 @@ -package org.usf.jquery.web; - -import static java.lang.String.join; -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; -import static java.util.regex.Pattern.compile; -import static org.usf.jquery.core.Operator.lookupOperator; -import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.core.Utils.isBlank; -import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Validation.requireLegalVariable; -import static org.usf.jquery.web.ColumnDecorator.countColumn; -import static org.usf.jquery.web.ColumnDecorator.ofColumn; -import static org.usf.jquery.web.Constants.ORDER; -import static org.usf.jquery.web.Constants.PARTITION; -import static org.usf.jquery.web.JQueryContext.context; -import static org.usf.jquery.web.ParsableJDBCType.typeOf; -import static org.usf.jquery.web.ParseException.cannotEvaluateException; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; - -import org.usf.jquery.core.DBColumn; -import org.usf.jquery.core.DBOrder; -import org.usf.jquery.core.OperationColumn; -import org.usf.jquery.core.Operator; -import org.usf.jquery.core.OverClause; -import org.usf.jquery.core.TypedFunction; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -/** - * - * [table.]column[.function([arg1][,arg2]*)]*[.comparator|criteria|order][:alias] - * - * @author u$f - * - * - */ -public final class LinkedRequestEntry { - - private static final String ARRAY_ARGS_KEY = "$args"; - - private final LinkedList entries = new LinkedList<>(); - private String tag; //optional - - public void appendEntry(String name) { - entries.add(new RequestEntry(name)); - } - - public void appendEntryArgs(Map> args) { - entries.getLast().args = args; - } - - public RequestColumn toRequestColumn(TableDecorator defaultTable, boolean allowedExp) { //predicate - if(entries.isEmpty()) { - throw new IllegalArgumentException("empty"); - } - else if(entries.size() == 1) { - var cd = requireColumn(entries.getFirst()); - return new RequestColumn(defaultTable, cd, emptyList(), null, tag); - } - else { - var arr = new ArrayList<>(entries); //improve indexing - ColumnDecorator cd = null; - String exp = null; - int idx = 1; - try { - cd = requireColumn(arr.get(idx)); - defaultTable = context().getTable(arr.get(idx-1).requireFiedName()); //parent - } - catch (Exception e) { - cd = requireColumn(arr.get(--idx)); - } - var limit = allowedExp ? arr.size() - 1 : arr.size(); - var fn = new LinkedList(); - for(++idx; idx - *
  • column decorator
  • - *
  • count function
  • - *
  • window functions
  • - * - */ - private static ColumnDecorator requireColumn(RequestEntry re) { - if(context().isDeclaredColumn(re.getName())) { - return context().getColumn(re.requireFiedName()); - } - if("count".equals(re.requireNoArgFunction())) { // not arguments - return countColumn(); - } - var fn = lookupOperator(snakeToCamelCase(re.getName())); - if(fn.filter(TypedFunction::isWindowFunction).isPresent()) { - return ofColumn(re.requireNoArgFunction().toLowerCase(), t-> fn.get().args()); - } - throw cannotEvaluateException("column expression", re.toString()); //column expected - } - - /** - *
      - *
    1. std functions
    2. - *
    3. over function
    4. - *
    - */ - private static OperationColumn requireFunction(RequestEntry re, TableDecorator td) { - var fn = lookupOperator(re.getName()) - .orElseThrow(()-> cannotEvaluateException("function", re.getName())); - if("OVER".equals(fn.id())) { //map arg function - var args = re.getArgs(); - if(isNull(args) || !args.containsKey(ARRAY_ARGS_KEY)) { //named arguments function - return fn.args(overClause(td, isNull(args) ? emptyMap() : args)); - } - throw new UnsupportedOperationException("over function require named args"); - } - return parseEntry(fn, re, td); - } - - private static OperationColumn parseEntry(Operator fn, RequestEntry re, TableDecorator td){ - if(!isEmpty(re.getArgs()) && !re.getArgs().containsKey(ARRAY_ARGS_KEY)) { - throw new UnsupportedOperationException("functions does not support named args"); - } - if(fn.argumentCount() <= 1) { //1st argument ignored - if(!isEmpty(re.getArgs())) { - throw new IllegalArgumentException(fn.id() + " takes no arguments"); - } - } - else { - var n = fn.argumentCount()-1; - if(nonNull(re.getArgs()) - && re.getArgs().containsKey(ARRAY_ARGS_KEY) - && re.getArgs().get(ARRAY_ARGS_KEY).size() == n) { - var params = re.getArgs().get(ARRAY_ARGS_KEY); - var args = new LinkedList(); - for(int i=1; i> args) { - var clause = new OverClause(); - if(args.containsKey(PARTITION)) { - clause.partitions(args.get(PARTITION).stream() - .map(LinkedRequestEntry::parseSingleLinkedEntry) - .map(o-> o.toRequestColumn(td, false)) - .map(RequestColumn::toColumn) - .toArray(DBColumn[]::new)); - } - if(args.containsKey(ORDER)) { - clause.orders(args.get(ORDER).stream() - .map(LinkedRequestEntry::parseSingleLinkedEntry) - .map(o-> o.toRequestColumn(td, true)) //ASC | DESC - .map(RequestColumn::toOrder) - .toArray(DBOrder[]::new)); - } - return clause; - } - - static LinkedRequestEntry parseSingleLinkedEntry(String s) { - return parseLinkedEntries(s, false).get(0); - } - - static List parseLinkedEntries(String s) { - return parseLinkedEntries(s, true); - } - - private static List parseLinkedEntries(String s, boolean multiple) { - if(isBlank(s)) { - throw new IllegalArgumentException("empty"); - } - var res = new LinkedList(); - res.add(new LinkedRequestEntry()); - int from = 0; - int to = 0; - char c = 0; - for(;;) { - while(to < s.length() && legalVariableChar(c = s.charAt(to))) to++; - res.getLast().appendEntry(requireLegalVariable(s.substring(from, to))); - if(to == s.length()) { - break; - } - if(c == '(') { // operator - var jmp = s.indexOf(')', ++to); - if(jmp > -1) { - var nest = s.indexOf('(', to); - if(nest > -1 && nest < jmp) { //avoid (..(..) - throw new IllegalArgumentException("'(' not allowed at index=" + nest); - } - res.getLast().appendEntryArgs(parseArgs(s.substring(to, jmp))); - } - else { - throw new IllegalArgumentException("')' expected after index=" + --to); - } - if((to = ++jmp) == s.length()) { - break; - } - c = s.charAt(to); - } - if(c == ':') { // tag - var jmp = s.indexOf(',', ++to); - if(jmp == -1) { - jmp = s.length(); - } - res.getLast().tag = requireLegalVariable(s.substring(to, jmp)); - if((to = jmp) == s.length()) { - break; - } - c = s.charAt(to); //else throw exception - } - if(c == ',' && multiple) { - res.add(new LinkedRequestEntry()); - } - else if(c != '.') { //not accessor - throw new IllegalArgumentException("'" + s.charAt(to) + "' not valid at index=" + to); - } - if((from = ++to) == s.length()) { //ends with dot - throw new IllegalArgumentException("'" + s.charAt(to-1) + "' not allowed at the end"); - } - } - return res; - } - - - private static final String ARG_PATTERN = "\\w+(\\.\\w*)*"; - - private static Map> parseArgs(String s) { - if(isBlank(s)) { - return emptyMap(); - } - if(!s.contains(":")) { - if(s.matches(wholeWord(ARG_PATTERN + "(," + ARG_PATTERN + ")*"))) { - return Map.of(ARRAY_ARGS_KEY, asList(s.split(","))); - } - throw new IllegalArgumentException(quote(s) + " args format not valid"); - } - int from = 0; - int to = 0; - char c = 0; - var map = new LinkedHashMap>(); - List last = null; - String key = null; - for(;;) { - while(to < s.length() && legalArgChar(c = s.charAt(to))) to++; - var sub = s.substring(from, to); - if(!sub.matches(wholeWord(ARG_PATTERN))) { - throw new IllegalArgumentException("illegal arg value : " + sub); - } - if(to == s.length()) { - last.add(sub); //sonar dummy sequential analyze !? - break; - } - if(c == ':') { //!!!!! timestamp argument - last = map.computeIfAbsent((key = sub), k-> new LinkedList<>()); - } - else if(c == ',') { - if(key == null) { - throw new IllegalArgumentException("arg name expected at index=" + to); - } - last.add(sub); - } - else { - throw new IllegalArgumentException("'" + s.charAt(to) + "' not valid at index=" + to); - } - from = ++to; - } - return map; - } - - - - - private static boolean legalArgChar(char c) { //later next version - return legalVariableChar(c) || c == '.'; - } - - private static boolean legalVariableChar(char c) { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z' ) || (c >= '0' && c <= '9') || c == '_'; - } - - private static String wholeWord(String rex) { - return "^" + rex + "$"; - } - - @Getter - @RequiredArgsConstructor - static final class RequestEntry { - - private final String name; - private Map> args; - - public String requireFiedName() { - if(isNull(args)) { //no parentheses - return name; - } - throw new IllegalArgumentException(quote(name) + " field takes no args"); - } - - public String requireNoArgFunction() { - if(isNull(args) || args.isEmpty()) { //no arguments - return name; - } - throw new IllegalArgumentException(quote(name) + " function takes no args"); - } - - @Override - public String toString() { - if(isNull(args)) { - return name; - } - var s = name + "("; - if(!args.isEmpty()) { - s+= args.containsKey(ARRAY_ARGS_KEY) ? join(", ", args.get(ARRAY_ARGS_KEY)) : args; - } - return s + ")"; - } - } - - static String snakeToCamelCase(String fn) { - StringBuffer sb = new StringBuffer(fn.length()); - Matcher m = compile("_[a-z]").matcher(fn); - while (m.find()) { - m.appendReplacement(sb, m.group().substring(1).toUpperCase()); - } - return m.appendTail(sb).toString(); - } -} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/ParsableJDBCType.java b/src/main/java/org/usf/jquery/web/ParsableJDBCType.java deleted file mode 100644 index 4f3af67a..00000000 --- a/src/main/java/org/usf/jquery/web/ParsableJDBCType.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.usf.jquery.web; - -import static java.util.Arrays.asList; -import static java.util.Objects.nonNull; -import static org.usf.jquery.web.ParsableSQLType.unparsableType; - -import java.math.BigDecimal; -import java.sql.Date; -import java.sql.Time; -import java.sql.Timestamp; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; - -import org.usf.jquery.core.JDBCType; -import org.usf.jquery.core.JavaType; - -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; - -/** - * - * @author u$f - * - */ -@RequiredArgsConstructor -public enum ParsableJDBCType implements ParsableSQLType { - - AUTO_TYPE(JDBCType.AUTO_TYPE, ParsableJDBCType::tryParse), //replace by array type - BOOLEAN(JDBCType.BOOLEAN, Boolean::parseBoolean), - BIT(JDBCType.BIT, BOOLEAN::parse), - TINYINT(JDBCType.TINYINT, Byte::parseByte), - SMALLINT(JDBCType.SMALLINT, Short::parseShort), - INTEGER(JDBCType.INTEGER, Integer::parseInt), - BIGINT(JDBCType.BIGINT, Long::parseLong), - REAL(JDBCType.REAL, Float::parseFloat), - FLOAT(JDBCType.FLOAT, Double::parseDouble), - DOUBLE(JDBCType.DOUBLE, Double::parseDouble), - NUMERIC(JDBCType.NUMERIC, BigDecimal::new), - DECIMAL(JDBCType.DECIMAL, BigDecimal::new), - CHAR(JDBCType.CHAR, v-> v), //teradata !char - VARCHAR(JDBCType.VARCHAR, v-> v), - NVARCHAR(JDBCType.NVARCHAR, v-> v), - LONGNVARCHAR(JDBCType.LONGNVARCHAR, v-> v), - DATE(JDBCType.DATE, v-> Date.valueOf(LocalDate.parse(v))), - TIME(JDBCType.TIME, v-> Time.valueOf(LocalTime.parse(v))), - TIMESTAMP(JDBCType.TIMESTAMP,v-> Timestamp.from(Instant.parse(v))), - TIMESTAMP_WITH_TIMEZONE(JDBCType.TIMESTAMP_WITH_TIMEZONE, TIMESTAMP::parse); - - private static final List COMMON_PARSERS = asList( -// Boolean, not throw exception - BIGINT, DOUBLE, // byte, short, integer, float - DATE, TIME, TIMESTAMP); //else string - - @Delegate - private final JavaType type; - @Delegate - private final ArgumentParser parser; //isAutoType delegated - - public static ParsableSQLType typeOf(int type) { - for(var t : values()) { - if(t.type.getValue() == type) { - return t; - } - } - return unparsableType(type); - } - - public static ParsableSQLType typeOf(JavaType type) { - for(var t : values()) { - if(t.type == type) { - return t; - } - } - return unparsableType(type); - } - - public static Object tryParse(String value) { - if(nonNull(value)) { - for(var p : COMMON_PARSERS) { - try { - return p.nativeParse(value); - } - catch (Exception e) {/* do not handle exception */} - } - } - return value; //default type String - } -} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/ParsableSQLType.java b/src/main/java/org/usf/jquery/web/ParsableSQLType.java deleted file mode 100644 index ade9f197..00000000 --- a/src/main/java/org/usf/jquery/web/ParsableSQLType.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.usf.jquery.web; - -import org.usf.jquery.core.JavaType; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * - * @author u$f - * - */ -public interface ParsableSQLType extends JavaType, ArgumentParser { - - public static ParsableSQLType unparsableType(int type) { - return new UnparsableJDBCType(type, Object.class); - } - - public static ParsableSQLType unparsableType(JavaType type) { - return new UnparsableJDBCType(type.getValue(), type.type()); - } - - @Getter - @RequiredArgsConstructor - class UnparsableJDBCType implements ParsableSQLType { - - private final int value; - private final Class javaType; - - @Override - public Object nativeParse(String v) { - throw new UnsupportedOperationException("unsupported SQLType=" + value); - } - } -} diff --git a/src/main/java/org/usf/jquery/web/RequestColumn.java b/src/main/java/org/usf/jquery/web/RequestColumn.java deleted file mode 100644 index 7804e358..00000000 --- a/src/main/java/org/usf/jquery/web/RequestColumn.java +++ /dev/null @@ -1,147 +0,0 @@ -package org.usf.jquery.web; - -import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; -import static java.util.Objects.requireNonNull; -import static org.usf.jquery.web.Constants.ORDER; -import static org.usf.jquery.web.CriteriaBuilder.ofComparator; -import static org.usf.jquery.web.LinkedRequestEntry.parseLinkedEntries; -import static org.usf.jquery.web.LinkedRequestEntry.parseSingleLinkedEntry; -import static org.usf.jquery.web.ParseException.cannotEvaluateException; - -import java.util.List; -import java.util.stream.Stream; - -import org.usf.jquery.core.BasicComparator; -import org.usf.jquery.core.Comparator; -import org.usf.jquery.core.ComparisonExpression; -import org.usf.jquery.core.DBColumn; -import org.usf.jquery.core.DBOrder; -import org.usf.jquery.core.InCompartor; -import org.usf.jquery.core.JavaType; -import org.usf.jquery.core.TaggableColumn; -import org.usf.jquery.core.TypedFunction; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; - -/** - * - * @author u$f - * - */ -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public final class RequestColumn implements ColumnDecorator { - - private final TableDecorator td; - private final ColumnDecorator cd; //conditional delegate on fns - private final List fns; - private final String exp; - private final String tag; - - public TableDecorator tableDecorator() { - return td; - } - - @Override - public String identity() { - return cd.identity(); - } - - @Override - public String reference() { - return isNull(tag) ? cd.reference() : tag; //join function !? - } - - @Override - public JavaType dataType() { - var i = fns.size(); - while(--i>=0 && fns.get(i).getReturnedType() == null); - return i<0 ? cd.dataType() : fns.get(i).getReturnedType(); - } - - @Override - public ColumnBuilder builder() { - return t-> fns.stream().reduce( - (DBColumn) t.column(cd), - (c, fn)-> fn.function(c), - (c1,c2)-> c1); //combiner -> sequentially collect - } - - @Override - public Comparator comparator(String comparator, int nArg) { - return fns.isEmpty() //cannot apply column comparator on function - ? cd.comparator(comparator, nArg) - : ColumnDecorator.super.comparator(comparator, nArg); - } - - @Override - public CriteriaBuilder criteria(String name) { - return fns.isEmpty() //cannot apply column criteria on function - ? cd.criteria(name) - : ColumnDecorator.super.criteria(name); - } - - @Override - public ArgumentParser parser(JavaType type) { - return fns.isEmpty() //cannot apply column parser on function - ? cd.parser(type) - : ColumnDecorator.super.parser(type); - } - - //expression => criteria | comparator - ComparisonExpression expression(String... values) { - var criteria = criteria(exp); - if(nonNull(criteria)) { - return criteria.build(values); - } - var cmp = comparator(exp, values.length); - if(nonNull(cmp)) { - var type = dataType(); - if(type == null) { // logical column type can be set in table - type = td.columnType(cd).orElse(type); - } //else : overridden - var pars = requireNonNull(parser(type)); - if(values.length == 1) { - return cmp.expression(pars.parse(values[0])); - } - return cmp instanceof InCompartor - ? cmp.expression(pars.parseAll(values)) - : ofComparator(cmp).build(pars.parseAll(values)); - } - throw cannotEvaluateException("expression", exp); - } - - Stream expression(List columns) { - var cmp = comparator(exp, 1); - if(cmp instanceof BasicComparator) { - return columns.stream().map(RequestColumn::toColumn).map(cmp::expression); - } - throw isNull(cmp) - ? cannotEvaluateException("expression", exp) - : new IllegalArgumentException("illegal column comparator " + exp); - } - - public DBOrder toOrder(){ - if(isNull(exp)) { - return toColumn().order(); - } - if(exp.matches("asc|desc")) { - return toColumn().order(exp.toUpperCase()); - } - throw cannotEvaluateException(ORDER, exp); - } - - public TaggableColumn toColumn(){ - return tableDecorator().column(this); - } - - static RequestColumn decodeSingleColumn(String value, TableDecorator defaultTable, boolean allowedExp) { - return parseSingleLinkedEntry(value).toRequestColumn(defaultTable, allowedExp); - } - - static Stream decodeColumns(String value, TableDecorator defaultTable, boolean allowedExp) { - return parseLinkedEntries(value).stream() - .map(e-> e.toRequestColumn(defaultTable, allowedExp)); - } -} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 1d1e7199..e0c04b6b 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -5,13 +5,18 @@ import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.Operator.lookupNoArgFunction; +import static org.usf.jquery.core.DBColumn.column; +import static org.usf.jquery.core.Operator.count; import static org.usf.jquery.core.Operator.lookupOperator; +import static org.usf.jquery.core.Operator.lookupStandaloneFunction; +import static org.usf.jquery.core.Operator.lookupWindowFunction; import static org.usf.jquery.core.OverClause.clauses; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.VAR_PATTERN; import static org.usf.jquery.web.ArgumentParsers.javaTypeParser; +import static org.usf.jquery.web.ColumnDecorator.ofColumn; import static org.usf.jquery.web.CriteriaBuilder.ofComparator; import static org.usf.jquery.web.JQueryContext.context; @@ -24,10 +29,11 @@ import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBOrder; import org.usf.jquery.core.InCompartor; -import org.usf.jquery.core.JavaType; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; +import org.usf.jquery.core.Parameter; import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.TypedOperator; import org.usf.jquery.core.WindowView; import lombok.AccessLevel; @@ -41,8 +47,7 @@ final class RequestEntryChain { private static final ColumnDecorator DEFAUL_COLUMN = ()-> null; //unused identity - - private static final String OVER_FN = "OVER"; + private static final String OVER_FN = "OVER"; private final String value; private final boolean text; //"string" @@ -55,7 +60,7 @@ public RequestEntryChain(String value) { } public TaggableColumn asColumn(TableDecorator td) { - var t = lookup(td, true); + var t = lookup(td); var c = t.buildColumn(); var e = t.entry; DBColumn oc = c; @@ -68,11 +73,14 @@ public TaggableColumn asColumn(TableDecorator td) { } } while(e.next()); //preserve last non null entry } + if(oc == c) { + return c; + } return oc.as(isNull(e.tag) ? c.tagname() : e.tag); } public DBFilter asFilter(TableDecorator td, List values) { - var t = lookup(td, false); + var t = lookup(td); var c = t.buildColumn(); var e = t.entry.next; DBColumn oc = c; @@ -103,7 +111,7 @@ else if(e.isLast()) { } public DBOrder asOrder(TableDecorator td) { - var t = lookup(td, false); + var t = lookup(td); var c = t.buildColumn(); var e = t.entry.next; DBColumn oc = c; @@ -167,49 +175,66 @@ OperationColumn requireOperation(TableDecorator td, DBColumn col) { OperationColumn toOperation(TableDecorator td, DBColumn col) { var res = lookupOperator(value); - if(res.isEmpty()) { - return null; - } - var op = res.get(); - if("OVER".equals(res.get().id())) { - var oc = args.stream().map(o-> o.requireOperation(td, col)).toArray(OperationColumn[]::new); - return op.args(clauses(oc)); + return res.isEmpty() ? null : fillArgs(td, col, res.get()); + } + + OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator op) { + if(OVER_FN.equals(op.id())) { + var oc = args.stream().map(o-> o.requireOperation(td, null)).toArray(OperationColumn[]::new); + return op.args(col, clauses(oc)); } var min = op.requireArgCount(); - var np = args.size()+1; // col + var np = isNull(args) ? 0 : args.size(); + if(nonNull(col)) { + np++; + } if(np < min || (!op.isVarags() && np > op.getParameters().length)) { throw new IllegalArgumentException("msg"); } var params = new ArrayList(np); - params.add(col); - var i=1; - for(; i fillArgs(td, column("*"), count())), this); + } + var res = lookupWindowFunction(value) //rank, rowNumber, .. + .or(()-> lookupStandaloneFunction(value)); //current_date, .. + if(res.isPresent()) { + var fn = res.get(); + return new Triple(td, ofColumn(value, b-> fn.args()), this); //args //?? } + return null; } private RequestEntryChain requireNoArgs() { @@ -297,10 +321,9 @@ static class Triple { private final TableDecorator td; private final ColumnDecorator cd; private final RequestEntryChain entry; - private final TaggableColumn column; TaggableColumn buildColumn() { - return isNull(column) ? td.column(cd) : column; + return td.column(cd); } } diff --git a/src/main/java/org/usf/jquery/web/RequestFilter.java b/src/main/java/org/usf/jquery/web/RequestFilter.java deleted file mode 100644 index 12ac47af..00000000 --- a/src/main/java/org/usf/jquery/web/RequestFilter.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.usf.jquery.web; - -import static org.usf.jquery.web.RequestColumn.decodeSingleColumn; - -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; -import java.util.stream.Stream; - -import org.usf.jquery.core.DBFilter; -import org.usf.jquery.core.DBTable; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * - * RequestColumn=val1[,val2]* - * RequestColumn=AnOtherRequestColumn - * - * @author u$f - * - */ -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public final class RequestFilter { - - private final RequestColumn rc; - private final List constants; - private final List columns; - - public DBTable[] tables() { - Set tables = new LinkedHashSet<>(); - tables.add(rc.tableDecorator().table()); - columns.forEach(c-> tables.add(c.tableDecorator().table())); - return tables.toArray(DBTable[]::new); - } - - public DBFilter[] filters() { - var col = rc.toColumn(); - var filters = new LinkedList<>(); - if(!columns.isEmpty()) { - rc.expression(columns).map(col::filter).forEach(filters::add); - } - if(!constants.isEmpty()) { - constants.stream().map(rc::expression).map(col::filter).forEach(filters::add); - } - return filters.toArray(DBFilter[]::new);// do not join filters (WHERE + HAVING) - } - - static RequestFilter decodeFilter(Entry entry, TableDecorator defaultTable) { - var col = decodeSingleColumn(entry.getKey(), defaultTable, true); //allow comparator - var columns = new LinkedList(); - var constants = new LinkedList(); - Stream.of(entry.getValue()).forEach(v->{ - try {//TODO pattern - columns.add(decodeSingleColumn(v, defaultTable, false)); //deny expression - } - catch (Exception e) { //TODO - constants.add(v.contains(",") ? v.split(",") : new String[] {v}); //check values - } - }); - return new RequestFilter(col, constants, columns); - } -} diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 86b56c31..f4132676 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -1,5 +1,7 @@ package org.usf.jquery.web; +import static java.util.Collections.emptyList; +import static java.util.Objects.isNull; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Validation.VAR_PATTERN; @@ -42,7 +44,9 @@ private List parseEntries(boolean multiple, boolean argument) nextChar(true); entries.add(parseEntry(multiple, argument)); } - return entries; + return entries.size() == 1 && isNull(entries.get(0).getValue()) //avoid () => (null) + ? emptyList() + : entries; } private RequestEntryChain parseEntry(boolean multiple, boolean argument) { diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index c6c0897c..cb593dbf 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -1,8 +1,6 @@ package org.usf.jquery.web; -import static java.lang.String.join; import static java.util.Objects.nonNull; -import static java.util.stream.Collectors.toList; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireLegalVariable; @@ -10,11 +8,9 @@ import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.RESERVED_WORDS; -import static org.usf.jquery.web.JQueryContext.context; import static org.usf.jquery.web.JQueryContext.database; import static org.usf.jquery.web.MissingParameterException.missingParameterException; import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; -import static org.usf.jquery.web.RequestColumn.decodeSingleColumn; import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.RequestParser.parseEntry; @@ -27,13 +23,10 @@ import java.util.stream.Stream; import org.usf.jquery.core.DBTable; -import org.usf.jquery.core.NamedColumn; -import org.usf.jquery.core.OverColumn; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TaggableView; import org.usf.jquery.core.ViewColumn; -import org.usf.jquery.core.WindowView; /** * @@ -65,45 +58,12 @@ default TaggableColumn column(ColumnDecorator cd) { default RequestQueryBuilder query(Map parameterMap) { var query = new RequestQueryBuilder(); - parseViews (query, parameterMap); parseColumns(query, parameterMap); parseFilters(query, parameterMap); parseOrders (query, parameterMap); return query; } - default void parseViews(RequestQueryBuilder query, Map parameters) { - var exp = "(" + join("|", "rank", "row_number", "dense_rank") + ")" + "(\\(\\))?\\.over.*"; //almost - for(var t : context().tables()) { - var pr = "^(" + t.identity() + "\\.)"; - if(t == this) { - pr += "?"; - } - var pattern = pr + exp; - var c = parameters.entrySet().stream() - .filter(e-> e.getKey().matches(pattern)) - .collect(toList()); - if(!c.isEmpty()) { - if(c.size() == 1 && c.get(0).getValue().length == 1) { - var entry = c.get(0); - var rc = decodeSingleColumn(entry.getKey(), this, true); //allow comparator - var nc = (NamedColumn) rc.toColumn(); - if(oc instanceof OverColumn) { - var wv = new WindowView(rc.tableDecorator().table(), nc, rc.expression(entry.getValue())); - query.tables(wv).filters(wv.filter()); - parameters.remove(entry.getKey()); - } - else { - throw new IllegalStateException("OverColumn expected"); - } - } - else { - throw new UnsupportedOperationException("multiple window function"); - } - } - } - } - default void parseColumns(RequestQueryBuilder query, Map parameters) { if(parameters.containsKey(COLUMN_DISTINCT) && parameters.containsKey(COLUMN)) { throw new IllegalArgumentException("cannot use both parameters " + quote(COLUMN_DISTINCT) + " and " + quote(COLUMN)); diff --git a/src/main/java/org/usf/jquery/web/YearTableMetadata.java b/src/main/java/org/usf/jquery/web/YearTableMetadata.java index 60d23465..968dccca 100644 --- a/src/main/java/org/usf/jquery/web/YearTableMetadata.java +++ b/src/main/java/org/usf/jquery/web/YearTableMetadata.java @@ -12,11 +12,10 @@ import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; +import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.web.Constants.EMPTY_REVISION; import static org.usf.jquery.web.JQueryContext.database; -import static org.usf.jquery.web.ParsableJDBCType.AUTO_TYPE; -import static org.usf.jquery.web.ParsableJDBCType.typeOf; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -87,7 +86,7 @@ void fetch(DatabaseMetaData metadata) throws SQLException { columnTables.computeIfAbsent(cm.getColumnName(), k-> new LinkedHashSet<>()).add(tn); var type = rs.getInt("DATA_TYPE"); var size = rs.getInt("COLUMN_SIZE"); - if(cm.getDataType() == AUTO_TYPE) { //first time + if(isNull(cm.getDataType())) { //first time cm.setDataType(typeOf(type)); cm.setDataSize(size); } diff --git a/src/main/java/org/usf/jquery/web/ChartMappers.java b/src/main/java/org/usf/jquery/web/view/ChartMappers.java similarity index 78% rename from src/main/java/org/usf/jquery/web/ChartMappers.java rename to src/main/java/org/usf/jquery/web/view/ChartMappers.java index fc50db65..f09c38dc 100644 --- a/src/main/java/org/usf/jquery/web/ChartMappers.java +++ b/src/main/java/org/usf/jquery/web/view/ChartMappers.java @@ -1,4 +1,4 @@ -package org.usf.jquery.web; +package org.usf.jquery.web.view; import static org.usf.jquery.web.view.Chart2DView.areaChart; import static org.usf.jquery.web.view.Chart2DView.barChart; @@ -8,13 +8,6 @@ import java.io.Writer; -import org.usf.jquery.web.view.CalendarView; -import org.usf.jquery.web.view.PieChartView; -import org.usf.jquery.web.view.SankeyView; -import org.usf.jquery.web.view.TableView; -import org.usf.jquery.web.view.TimelineChartView; -import org.usf.jquery.web.view.WebViewMapper; - import lombok.AccessLevel; import lombok.NoArgsConstructor; From 8e5bed5f923e2d698d27d1b044eda66d1e4c2bef Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 2 Jan 2024 22:43:43 +0100 Subject: [PATCH 024/298] edit --- .../usf/jquery/core/StandaloneFunction.java | 10 ++++++ .../org/usf/jquery/web/RequestEntryChain.java | 31 +++++++++------- .../usf/jquery/web/RequestEntryChainTest.java | 36 +++++++++++++++++++ 3 files changed, 64 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/StandaloneFunction.java create mode 100644 src/test/java/org/usf/jquery/web/RequestEntryChainTest.java diff --git a/src/main/java/org/usf/jquery/core/StandaloneFunction.java b/src/main/java/org/usf/jquery/core/StandaloneFunction.java new file mode 100644 index 00000000..802eb942 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/StandaloneFunction.java @@ -0,0 +1,10 @@ +package org.usf.jquery.core; + +/** + * + * @author u$f + * + */ +public interface StandaloneFunction extends FunctionOperator { + +} diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index e0c04b6b..0cc3e5f8 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -195,8 +195,8 @@ OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator op) { if(nonNull(col)) { params.add(col); } - var s=nonNull(col)?1:0; - var i=s; + var s = nonNull(col) ? 1 : 0; + var i = s; for(; i fillArgs(td, column("*"), count())), this); } var res = lookupWindowFunction(value) //rank, rowNumber, .. diff --git a/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java b/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java new file mode 100644 index 00000000..b159f626 --- /dev/null +++ b/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java @@ -0,0 +1,36 @@ +package org.usf.jquery.web; + +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.*; +import static org.usf.jquery.web.RequestParser.*; + +import java.util.Collections; +import java.util.Optional; + +import org.junit.jupiter.api.Test; + +class RequestEntryChainTest { + + @Test + void test() { + JQueryContext.register(emptyList(), emptyList()); + parseEntry("count()").asColumn(new TableDecorator() { + + @Override + public String tableName() { + return null; + } + + @Override + public String identity() { + return null; + } + + @Override + public Optional columnName(ColumnDecorator cd) { + return Optional.empty(); + } + }); + } + +} From 5655d2c4073fbece61a9f25745e115a738bcb632 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 3 Jan 2024 23:19:58 +0100 Subject: [PATCH 025/298] edit --- .../java/org/usf/jquery/core/JDBCType.java | 9 +-- .../java/org/usf/jquery/core/JavaType.java | 35 ++------- .../java/org/usf/jquery/core/JqueryType.java | 25 +++++++ .../java/org/usf/jquery/core/Operator.java | 27 ++++--- .../java/org/usf/jquery/core/OverClause.java | 1 + .../usf/jquery/core/RequestQueryBuilder.java | 17 ----- .../org/usf/jquery/core/TypedOperator.java | 29 ++++---- .../org/usf/jquery/web/ArgumentParsers.java | 73 +++++++++++++------ .../org/usf/jquery/web/ColumnDecorator.java | 6 +- ...entParser.java => JDBCArgumentParser.java} | 16 ++-- .../usf/jquery/web/JavaArgumentParser.java | 13 ++++ .../org/usf/jquery/web/RequestEntryChain.java | 68 ++++------------- 12 files changed, 155 insertions(+), 164 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/JqueryType.java rename src/main/java/org/usf/jquery/web/{ArgumentParser.java => JDBCArgumentParser.java} (73%) create mode 100644 src/main/java/org/usf/jquery/web/JavaArgumentParser.java diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index 1d75857d..d40d0218 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -1,13 +1,10 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.JavaType.declare; - import java.math.BigDecimal; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; -import java.util.function.Function; import java.util.function.Predicate; import lombok.Getter; @@ -46,11 +43,9 @@ public enum JDBCType implements JavaType { TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class, Timestamp.class::isInstance), OTHER(Types.OTHER, null, o-> false); //isnull !? - static final JavaType OVER_ARG = declare("over.arg", Object.class, o-> true); - private final int value; private final Class type; - private final Function matcher; + private final Predicate matcher; @Override public Class type() { @@ -74,7 +69,7 @@ static boolean subType(JavaType type, Class c) { } private boolean acceptValue(Object o) { - return o == null || matcher.apply(o); + return o == null || matcher.test(o); } private static boolean isNumber(Object o, double min, double max, boolean decimal) { diff --git a/src/main/java/org/usf/jquery/core/JavaType.java b/src/main/java/org/usf/jquery/core/JavaType.java index 3b8b605f..bb985691 100644 --- a/src/main/java/org/usf/jquery/core/JavaType.java +++ b/src/main/java/org/usf/jquery/core/JavaType.java @@ -1,45 +1,20 @@ package org.usf.jquery.core; -import static java.util.Objects.isNull; - -import java.util.function.Predicate; - -import lombok.NonNull; - /** * * @author u$f * */ +@FunctionalInterface public interface JavaType { - String name(); - Class type(); - boolean accept(Object o); - - static JavaType instance(Class type) { - return declare(type.getSimpleName(), type, o-> isNull(o) || type.isInstance(o)); + default String name() { + return type().getSimpleName(); } - static JavaType declare(@NonNull String name, @NonNull Class type, @NonNull Predicate predicate) { - return new JavaType() { - - @Override - public String name() { - return name; - } - - @Override - public Class type() { - return type; - } - - @Override - public boolean accept(Object o) { - return predicate.test(o); - } - }; + default boolean accept(Object o) { + return o == null || type().isInstance(o); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/JqueryType.java b/src/main/java/org/usf/jquery/core/JqueryType.java new file mode 100644 index 00000000..6b677249 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/JqueryType.java @@ -0,0 +1,25 @@ +package org.usf.jquery.core; + +import lombok.RequiredArgsConstructor; + +/** + * + * @author u$f + * + */ +@RequiredArgsConstructor +public enum JqueryType implements JavaType { + + COLUMN(DBColumn.class), + ORDER(DBOrder.class), + CLAUSE(OperationColumn.class); + //expression, WHEN_THEN, ... + + private final Class type; + + @Override + public Class type() { + return type; + } + +} diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 3028de32..3b1f56e1 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -10,7 +10,10 @@ import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.JDBCType.typeOf; -import static org.usf.jquery.core.JavaType.instance; +import static org.usf.jquery.core.JqueryType.CLAUSE; +import static org.usf.jquery.core.JqueryType.COLUMN; +import static org.usf.jquery.core.JqueryType.ORDER; +import static org.usf.jquery.core.OverClause.clauses; import static org.usf.jquery.core.Parameter.optional; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; @@ -18,6 +21,7 @@ import java.util.Optional; import java.util.function.Function; +import java.util.stream.Stream; /** * @@ -131,7 +135,7 @@ static TypedOperator left() { } static TypedOperator right() { - return new TypedOperator(VARCHAR, function("RIGHT"), required(VARCHAR), required(INTEGER)); + return new TypedOperator(VARCHAR, function("RIGHT"), required(VARCHAR), required(INTEGER)); } static TypedOperator replace() { @@ -211,7 +215,7 @@ static TypedOperator bigint() { } static TypedOperator decimal() { - return new TypedOperator(DOUBLE, cast("DECIMAL"), required(VARCHAR, DOUBLE), optional(INTEGER), optional(INTEGER)); + return new TypedOperator(DOUBLE, cast("DECIMAL"), required(VARCHAR, BIGINT), optional(INTEGER), optional(INTEGER)); } //other functions @@ -259,26 +263,29 @@ static TypedOperator denseRank() { //pipe functions static TypedOperator over() { - return new TypedOperator(firstArgType(), pipe("OVER"), - required(instance(DBColumn.class)), //NamedColumn, OperationColumn - required(instance(OverClause.class))) { + return new TypedOperator(firstArgType(), pipe("OVER"), required(COLUMN), optional(CLAUSE), optional(CLAUSE)) { + @Override public OperationColumn args(Object... args) { return super.args(args).aggregation(false); //over aggregation functions } + + @Override + Object[] mapArgs(Object... args) { + var c = Stream.of(args).skip(1).map(OperationColumn.class::cast).toArray(OperationColumn[]::new); + return super.mapArgs(args[0], clauses(c)); + } }; } //clause functions static TypedOperator partition() { - var ct = instance(DBColumn.class); // require at least one column - return new TypedOperator(instance(DBColumn[].class), clause("PARTITION BY"), required(ct), varargs(ct)); + return new TypedOperator(CLAUSE, clause("PARTITION BY"), required(COLUMN), varargs(COLUMN)); } static TypedOperator order() { - var ot = instance(DBOrder.class); // require at least one order - return new TypedOperator(instance(DBOrder[].class), clause("ORDER BY"), required(ot), varargs(ot)); + return new TypedOperator(CLAUSE, clause("ORDER BY"), required(ORDER), varargs(ORDER)); } static ArithmeticOperator operator(String symbol) { diff --git a/src/main/java/org/usf/jquery/core/OverClause.java b/src/main/java/org/usf/jquery/core/OverClause.java index 095f87bc..d9efe2cc 100644 --- a/src/main/java/org/usf/jquery/core/OverClause.java +++ b/src/main/java/org/usf/jquery/core/OverClause.java @@ -66,6 +66,7 @@ private static OperationColumn requireOneArg(Map> return null; } if(args.size() == 1) { + //instance of ClauseFunction ? return args.get(0); } throw new IllegalArgumentException("duplicated arg values " + key); diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 2917d665..75f9b2b2 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -30,33 +30,16 @@ public class RequestQueryBuilder { private final List columns = new LinkedList<>(); - private final List tables = new LinkedList<>(); private final List filters = new LinkedList<>(); //WERE & HAVING private final List orders = new LinkedList<>(); private Iterator it; private boolean distinct; - public RequestQueryBuilder select(TaggableView table, TaggableColumn... columns) { - return tables(table).columns(columns); - } - public RequestQueryBuilder distinct() { distinct = true; return this; } - public RequestQueryBuilder tables(@NonNull TaggableView... tables) { - Stream.of(tables).forEach(this.tables::add); - return this; - } - - public RequestQueryBuilder tablesIfAbsent(@NonNull TaggableView... tables) { - Stream.of(tables) - .filter(v-> this.tables.stream().noneMatch(t-> t.tagname().equals(v.tagname()))) - .forEach(this.tables::add); - return this; - } - public RequestQueryBuilder columns(@NonNull TaggableColumn... columns) { Stream.of(columns).forEach(this.columns::add); return this; diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 81ee32da..46da338d 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -4,7 +4,6 @@ import static java.util.Objects.isNull; import static org.usf.jquery.core.Parameter.checkParams; -import java.util.function.BiConsumer; import java.util.function.Function; import lombok.Getter; @@ -41,14 +40,6 @@ public Operator unwrap() { @Override public OperationColumn args(Object... args) { - return args(args, (p, o)->{ - if(!p.accept(o)) { - throw new IllegalArgumentException("mismatch arg type"); - } - }); - } - - OperationColumn args(Object[] args, BiConsumer fn) { if(isNull(args)) { args = new Object[0]; } @@ -57,15 +48,27 @@ OperationColumn args(Object[] args, BiConsumer fn) { if(na >= rq && (na <= parameters.length || isVarags())) { var i=0; for(; i Timestamp.from(Instant.parse(v)); case TIMESTAMP_WITH_TIMEZONE: return v-> Timestamp.from(ZonedDateTime.parse(v).toInstant()); case OTHER: - default: throw new UnsupportedOperationException("unsupported type " + type); + default: throw unsupportedTypeException(requireNonNull(type)); } } + public static JavaArgumentParser jqueryArgParser(JqueryType type) { + switch (type) { + case COLUMN: return RequestEntryChain::asColumn; + case ORDER: return RequestEntryChain::asOrder; + case CLAUSE: return RequestEntryChain::asOperation; + default: throw unsupportedTypeException(requireNonNull(type)); + } + } + + private static Optional tryParse(RequestEntryChain entry, TableDecorator td, JavaArgumentParser p) { + try { + return Optional.of(p.parse(entry, td)); + } + catch(Exception e) { + return empty(); + } + } + + private static UnsupportedOperationException unsupportedTypeException(JavaType type) { + return new UnsupportedOperationException("unsupported type " + type.name()); + } } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index ee03d18e..3030ff5d 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -12,7 +12,7 @@ import static org.usf.jquery.core.Comparator.notEqual; import static org.usf.jquery.core.Comparator.notIn; import static org.usf.jquery.core.Comparator.notLike; -import static org.usf.jquery.web.ArgumentParsers.javaTypeParser; +import static org.usf.jquery.web.ArgumentParsers.jdbcArgParser; import org.usf.jquery.core.Comparator; import org.usf.jquery.core.ComparisonExpression; @@ -43,8 +43,8 @@ default JDBCType dataType(TableDecorator td) { /** * override parser | format | local */ - default ArgumentParser parser(TableDecorator td){ - return javaTypeParser(dataType(td)); + default JDBCArgumentParser parser(TableDecorator td){ + return jdbcArgParser(dataType(td)); } default String pattern(TableDecorator td) { diff --git a/src/main/java/org/usf/jquery/web/ArgumentParser.java b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java similarity index 73% rename from src/main/java/org/usf/jquery/web/ArgumentParser.java rename to src/main/java/org/usf/jquery/web/JDBCArgumentParser.java index ea5d98ec..e9bced6d 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParser.java +++ b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java @@ -11,9 +11,14 @@ * */ @FunctionalInterface -public interface ArgumentParser { +public interface JDBCArgumentParser extends JavaArgumentParser { Object nativeParse(String v); + + @Override + default Object parse(RequestEntryChain entry, TableDecorator td) { + return parse(entry.requireNoArgs().getValue()); + } default Object[] parseAll(String... args) { return isNull(args) ? null : Stream.of(args).map(this::parse).toArray(); @@ -27,13 +32,4 @@ default Object parse(String v) { throw new ParseException(format("cannot parse %s value '%s'", toString(), v), e); } } - - default Object tryParse(String v) { - try { - return nativeParse(v); - } - catch(Exception e) { - return null; - } - } } diff --git a/src/main/java/org/usf/jquery/web/JavaArgumentParser.java b/src/main/java/org/usf/jquery/web/JavaArgumentParser.java new file mode 100644 index 00000000..47aeac0c --- /dev/null +++ b/src/main/java/org/usf/jquery/web/JavaArgumentParser.java @@ -0,0 +1,13 @@ +package org.usf.jquery.web; + +/** + * + * @author u$f + * + */ +@FunctionalInterface +public interface JavaArgumentParser { + + Object parse(RequestEntryChain entry, TableDecorator td); + +} diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 0cc3e5f8..ddaa9e7e 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -10,12 +10,9 @@ import static org.usf.jquery.core.Operator.lookupOperator; import static org.usf.jquery.core.Operator.lookupStandaloneFunction; import static org.usf.jquery.core.Operator.lookupWindowFunction; -import static org.usf.jquery.core.OverClause.clauses; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Validation.VAR_PATTERN; -import static org.usf.jquery.web.ArgumentParsers.javaTypeParser; +import static org.usf.jquery.web.ArgumentParsers.parse; import static org.usf.jquery.web.ColumnDecorator.ofColumn; import static org.usf.jquery.web.CriteriaBuilder.ofComparator; import static org.usf.jquery.web.JQueryContext.context; @@ -165,8 +162,8 @@ ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List o.requireOperation(td, null)).toArray(OperationColumn[]::new); - return op.args(col, clauses(oc)); - } var min = op.requireArgCount(); var np = isNull(args) ? 0 : args.size(); if(nonNull(col)) { @@ -210,42 +203,9 @@ OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator op) { } Object toArg(TableDecorator td, Parameter parameter) { - if(isNull(value) || text) { - return requireNoArgs().value; - } - if(isEmpty(parameter.getTypes())) { - var c = tryParseColumn(td); - return isNull(c) ? value : c; - } - if(parameter.getTypes().length == 1) { - var type = parameter.getTypes()[0].type(); - if(type.isAssignableFrom(DBColumn.class)) { - return asColumn(td); - } - if(type.isAssignableFrom(DBOrder.class)) { - return asOrder(td); - } - } - var c = tryParseColumn(td); - if(isNull(c)) { - return c; - } - for(var t : parameter.getTypes()) { - var o = javaTypeParser(t).tryParse(value); - if(nonNull(o)) { - return o; - } - } - throw new ParseException("cannot parse value : " + value); - } - - private DBColumn tryParseColumn(TableDecorator td) { - try { - return asColumn(td); - } - catch (Exception e) {/* do not throw exception */ - return null; - } + return isNull(value) || text + ? requireNoArgs().value + : parse(this, td, parameter.getTypes()); } private Triple lookup(TableDecorator td) { @@ -259,6 +219,14 @@ private Triple lookup(TableDecorator td) { if(nonNull(tp)) { return tp; } + if("count".equals(value)) { //table !? + return new Triple(td, ofColumn("count", b-> fillArgs(td, column("*"), count())), this); + } + var res = lookupStandaloneFunction(value); //rank, rowNumber, .. + if(res.isPresent()) { + var fn = res.get(); + return new Triple(td, ofColumn(value, b-> fn.args()), this); //args //?? + } throw cannotEvaluateException(this); } @@ -266,11 +234,7 @@ private Triple lookupColumn(TableDecorator td) { if(context().isDeclaredColumn(value)) { return new Triple(td, context().getColumn(requireNoArgs().value), this); } - if("count".equals(value)) { //table !? - return new Triple(td, ofColumn("count", b-> fillArgs(td, column("*"), count())), this); - } - var res = lookupWindowFunction(value) //rank, rowNumber, .. - .or(()-> lookupStandaloneFunction(value)); //current_date, .. + var res = lookupWindowFunction(value); //rank, rowNumber, .. if(res.isPresent()) { var fn = res.get(); return new Triple(td, ofColumn(value, b-> fn.args()), this); //args //?? @@ -278,7 +242,7 @@ private Triple lookupColumn(TableDecorator td) { return null; } - private RequestEntryChain requireNoArgs() { + RequestEntryChain requireNoArgs() { if(isNull(args)) { return this; } From b1f5e76a1328c187d24358f7c383bd49ecbcea8e Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 4 Jan 2024 11:27:35 +0100 Subject: [PATCH 026/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 2 +- .../java/org/usf/jquery/core/Operator.java | 10 +- .../org/usf/jquery/core/TypedOperator.java | 4 +- .../org/usf/jquery/web/ArgumentParsers.java | 8 +- .../org/usf/jquery/web/RequestEntryChain.java | 140 +++++++++--------- 5 files changed, 82 insertions(+), 82 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index b7cddaf3..ded2c54e 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -140,7 +140,7 @@ default WhenFilterBridge when(ComparisonExpression ex) { static DBColumn column(@NonNull String value) { return p-> value; } - + static DBColumn constant(Object value) { return constant(()-> value); } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 3b1f56e1..4ce26288 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -271,9 +271,9 @@ public OperationColumn args(Object... args) { } @Override - Object[] mapArgs(Object... args) { - var c = Stream.of(args).skip(1).map(OperationColumn.class::cast).toArray(OperationColumn[]::new); - return super.mapArgs(args[0], clauses(c)); + Object[] mapArg(Object... args) { //map args after check + var c = Stream.of(args).skip(1).toArray(OperationColumn[]::new); + return super.mapArg(args[0], clauses(c)); } }; } @@ -325,11 +325,11 @@ static StandaloneFunction constant(String name) { } static Optional lookupStandaloneFunction(String op) { - return lookupOperator(op).filter(fn-> fn.unwrap() instanceof StandaloneFunction); + return lookupOperator(op).filter(fn-> fn.unwrap().getClass() == StandaloneFunction.class); } static Optional lookupWindowFunction(String op) { - return lookupOperator(op).filter(fn-> fn.unwrap() instanceof WindowFunction); + return lookupOperator(op).filter(fn-> fn.unwrap().getClass() == WindowFunction.class); //!aggregation } static Optional lookupOperator(String op) { diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 46da338d..7c454124 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -58,12 +58,12 @@ public OperationColumn args(Object... args) { throw illegalArgumentException(); } } - return new OperationColumn(operator, mapArgs(args), typeFn.apply(args)); + return new OperationColumn(operator, mapArg(args), typeFn.apply(args)); } throw new IllegalArgumentException("mismatch parameters"); } - Object[] mapArgs(Object... args) { + Object[] mapArg(Object... args) { return args; } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 6db68516..87a5333f 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -93,10 +93,10 @@ public static JDBCArgumentParser jdbcArgParser(JDBCType type) { public static JavaArgumentParser jqueryArgParser(JqueryType type) { switch (type) { - case COLUMN: return RequestEntryChain::asColumn; - case ORDER: return RequestEntryChain::asOrder; - case CLAUSE: return RequestEntryChain::asOperation; - default: throw unsupportedTypeException(requireNonNull(type)); + case COLUMN: return RequestEntryChain::asColumn; + case ORDER : return RequestEntryChain::asOrder; + case CLAUSE: return RequestEntryChain::asOperation; + default: throw unsupportedTypeException(requireNonNull(type)); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index ddaa9e7e..284cfcf7 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; import java.util.stream.Stream; import org.usf.jquery.core.ComparisonExpression; @@ -27,8 +28,10 @@ import org.usf.jquery.core.DBOrder; import org.usf.jquery.core.InCompartor; import org.usf.jquery.core.OperationColumn; +import org.usf.jquery.core.Operator; import org.usf.jquery.core.Order; import org.usf.jquery.core.Parameter; +import org.usf.jquery.core.SqlStringBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedOperator; import org.usf.jquery.core.WindowView; @@ -44,7 +47,6 @@ final class RequestEntryChain { private static final ColumnDecorator DEFAUL_COLUMN = ()-> null; //unused identity - private static final String OVER_FN = "OVER"; private final String value; private final boolean text; //"string" @@ -57,7 +59,7 @@ public RequestEntryChain(String value) { } public TaggableColumn asColumn(TableDecorator td) { - var t = lookup(td); + var t = lookupResource(td); var c = t.buildColumn(); var e = t.entry; DBColumn oc = c; @@ -66,18 +68,18 @@ public TaggableColumn asColumn(TableDecorator td) { e = e.next; oc = e.toOperation(td, oc); if(isNull(oc)) { - throw cannotEvaluateException(e); + throw cannotEvaluateException("column", e); } - } while(e.next()); //preserve last non null entry + } while(e.next()); } - if(oc == c) { + else { return c; } return oc.as(isNull(e.tag) ? c.tagname() : e.tag); } public DBFilter asFilter(TableDecorator td, List values) { - var t = lookup(td); + var t = lookupResource(td); var c = t.buildColumn(); var e = t.entry.next; DBColumn oc = c; @@ -100,15 +102,15 @@ else if(e.isLast()) { exp = e.toComparison(td, cd, values); } else { - throw cannotEvaluateException(e); //more detail + throw cannotEvaluateException("filter", e); //more detail } - return OVER_FN.equals(e.value) + return "OVER".equals(e.value) ? new WindowView(td.table(), oc.as(c.tagname())).filter(exp) : oc.filter(exp); } public DBOrder asOrder(TableDecorator td) { - var t = lookup(td); + var t = lookupResource(td); var c = t.buildColumn(); var e = t.entry.next; DBColumn oc = c; @@ -132,7 +134,15 @@ else if(e.isLast()) { //last entry return oc.order(order.get()); } } - throw cannotEvaluateException(e); //column expected + throw cannotEvaluateException("order", e); //column expected + } + + OperationColumn asOperation(TableDecorator td) { + var op = toOperation(td, null); + if(nonNull(op)) { + return op; + } + throw cannotEvaluateException("operation", this); // as function } ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List values) { @@ -159,87 +169,77 @@ ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List op.getParameters().length)) { - throw new IllegalArgumentException("msg"); - } - var params = new ArrayList(np); - if(nonNull(col)) { - params.add(col); - } - var s = nonNull(col) ? 1 : 0; - var i = s; - for(; i fillArgs(td, column("*"), count())), this); + DBColumn col = b-> { + b.view(td.table()); + return "*"; + }; + return new Triple(td, ofColumn(value, b-> fillArgs(td, col, count())), this); } - var res = lookupStandaloneFunction(value); //rank, rowNumber, .. - if(res.isPresent()) { - var fn = res.get(); - return new Triple(td, ofColumn(value, b-> fn.args()), this); //args //?? - } - throw cannotEvaluateException(this); + return lookupStandaloneFunction(value) + .map(fn-> new Triple(td, ofColumn(value, b-> fillArgs(td, null, fn)), this)) + .orElseThrow(()-> cannotEvaluateException("resource", this)); } - private Triple lookupColumn(TableDecorator td) { - if(context().isDeclaredColumn(value)) { - return new Triple(td, context().getColumn(requireNoArgs().value), this); - } - var res = lookupWindowFunction(value); //rank, rowNumber, .. - if(res.isPresent()) { - var fn = res.get(); - return new Triple(td, ofColumn(value, b-> fn.args()), this); //args //?? + private Triple lookupPrefixedResource(TableDecorator td) { + return context().isDeclaredColumn(value) + ? new Triple(td, context().getColumn(requireNoArgs().value), this) + : lookupWindowFunction(value) //rank, rowNumber, .. + .map(fn-> new Triple(td, ofColumn(value, b-> fillArgs(td, null, fn)), this)) //reuse fn + .orElse(null); + } + + private OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator op) { + var np = isNull(args) ? 0 : args.size(); + if(nonNull(col)) { + np++; + } + var min = op.requireArgCount(); + var max = op.getParameters().length; + if(np >= min && (op.isVarags() || np <= max)) { + var params = new ArrayList(np); + if(nonNull(col)) { + params.add(col); + } + var s = nonNull(col) ? 1 : 0; + var i = s; + for(; i entries) { From f1b2f37cec45f6f4c588d6f999aa2e1154c49525 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 5 Jan 2024 00:55:28 +0100 Subject: [PATCH 027/298] edit --- .../java/org/usf/jquery/core/Operator.java | 4 - .../usf/jquery/core/RequestQueryBuilder.java | 2 +- .../org/usf/jquery/core/SqlStringBuilder.java | 6 +- .../org/usf/jquery/web/ArgumentParsers.java | 60 ++++++------ .../org/usf/jquery/web/ParseException.java | 7 +- .../org/usf/jquery/web/RequestEntryChain.java | 97 ++++++++----------- .../usf/jquery/web/RequestEntryChainTest.java | 4 +- 7 files changed, 83 insertions(+), 97 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 4ce26288..af70e911 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -323,10 +323,6 @@ static ClauseFunction clause(String name) { static StandaloneFunction constant(String name) { return ()-> name; } - - static Optional lookupStandaloneFunction(String op) { - return lookupOperator(op).filter(fn-> fn.unwrap().getClass() == StandaloneFunction.class); - } static Optional lookupWindowFunction(String op) { return lookupOperator(op).filter(fn-> fn.unwrap().getClass() == WindowFunction.class); //!aggregation diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 75f9b2b2..f3effc8a 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -94,7 +94,7 @@ void select(SqlStringBuilder sb, QueryParameterBuilder pb, String schema){ sb.append("SELECT ") .appendIf(distinct, ()-> "DISTINCT ") .appendEach(columns, SCOMA, o-> o.sql(pb) + " AS " + doubleQuote(o.tagname())) - .append(" FROM ") + .appendIf(!pb.views().isEmpty(), " FROM ") //TODO finish this .appendEach(pb.views(), SCOMA, o-> o.sql(pb, schema, true)); } diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index 925c46ca..e641b239 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -22,7 +22,7 @@ public final class SqlStringBuilder { static final String DQUOT = "\""; static final String SCOMA = COMA + SPACE; - private final StringBuilder sb; + final StringBuilder sb; public SqlStringBuilder(int capacity) { this.sb = new StringBuilder(capacity); @@ -80,6 +80,10 @@ public SqlStringBuilder append(String s) { return this; } + public int length(){ + return sb.length(); + } + @Override public String toString() { return sb.toString(); diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 87a5333f..f68a7e82 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -1,7 +1,7 @@ package org.usf.jquery.web; -import static java.util.Objects.requireNonNull; -import static java.util.Optional.empty; +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.JDBCType.BIGINT; import static org.usf.jquery.core.JDBCType.DATE; import static org.usf.jquery.core.JDBCType.DOUBLE; @@ -11,6 +11,7 @@ import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.JqueryType.COLUMN; import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.web.ParseException.cannotParseException; import java.math.BigDecimal; import java.sql.Date; @@ -20,7 +21,6 @@ import java.time.LocalDate; import java.time.LocalTime; import java.time.ZonedDateTime; -import java.util.Optional; import java.util.stream.Stream; import org.usf.jquery.core.JDBCType; @@ -29,6 +29,7 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; +import lombok.NonNull; /** * @@ -38,34 +39,39 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ArgumentParsers { - private static final JavaType[] STD_TYPES = {BIGINT, DOUBLE, DATE, TIMESTAMP, TIME, TIMESTAMP_WITH_TIMEZONE, VARCHAR}; + private static final JDBCArgumentParser[] STD_PRS = { + jdbcArgParser(BIGINT), jdbcArgParser(DOUBLE), + jdbcArgParser(DATE), jdbcArgParser(TIMESTAMP), + jdbcArgParser(TIME), jdbcArgParser(TIMESTAMP_WITH_TIMEZONE), + jdbcArgParser(VARCHAR)}; public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType... types) { - if(isEmpty(types)) { - types = STD_TYPES; - } - else { - if(types.length == 1 && types[0] instanceof JqueryType) { // support single JQuery type + if(nonNull(types)) { + if(types.length == 1 && types[0] instanceof JqueryType) { // only one type return jqueryArgParser((JqueryType)types[0]).parse(entry, td); } if(!Stream.of(types).allMatch(JDBCType.class::isInstance)) { throw new IllegalArgumentException("different types"); } } - var res = tryParse(entry, td, jqueryArgParser(COLUMN)); - if(res.isPresent()) { - return res.get(); + try { + return jqueryArgParser(COLUMN).parse(entry, td); + } catch (Exception e) {/*do not throw exception*/} + if(isEmpty(types)) { + types = new JavaType[] {null}; } for(var type : types) { - res = tryParse(entry, td, jdbcArgParser((JDBCType) type)); - if(res.isPresent()) { - return res.get(); - } + try { + return jdbcArgParser((JDBCType) type).parse(entry, td); + } catch (Exception e) {/*do not throw exception*/} } - throw new ParseException("cannot parse value : " + entry); + throw cannotParseException("value", entry.toString()); } public static JDBCArgumentParser jdbcArgParser(JDBCType type) { + if(isNull(type)) { + return ArgumentParsers::parseUnknown; + } switch (type) { case BOOLEAN: return Boolean::parseBoolean; case BIT: return Boolean::parseBoolean; @@ -87,26 +93,26 @@ public static JDBCArgumentParser jdbcArgParser(JDBCType type) { case TIMESTAMP: return v-> Timestamp.from(Instant.parse(v)); case TIMESTAMP_WITH_TIMEZONE: return v-> Timestamp.from(ZonedDateTime.parse(v).toInstant()); case OTHER: - default: throw unsupportedTypeException(requireNonNull(type)); + default: throw unsupportedTypeException(type); } } - public static JavaArgumentParser jqueryArgParser(JqueryType type) { + public static JavaArgumentParser jqueryArgParser(@NonNull JqueryType type) { switch (type) { case COLUMN: return RequestEntryChain::asColumn; case ORDER : return RequestEntryChain::asOrder; case CLAUSE: return RequestEntryChain::asOperation; - default: throw unsupportedTypeException(requireNonNull(type)); + default: throw unsupportedTypeException(type); } } - - private static Optional tryParse(RequestEntryChain entry, TableDecorator td, JavaArgumentParser p) { - try { - return Optional.of(p.parse(entry, td)); - } - catch(Exception e) { - return empty(); + + private static Object parseUnknown(String s) { + for(var p : STD_PRS) { + try { + return p.parse(s); + } catch (Exception e) {/*do not throw exception*/} } + return s; } private static UnsupportedOperationException unsupportedTypeException(JavaType type) { diff --git a/src/main/java/org/usf/jquery/web/ParseException.java b/src/main/java/org/usf/jquery/web/ParseException.java index bf445965..0de0ecd3 100644 --- a/src/main/java/org/usf/jquery/web/ParseException.java +++ b/src/main/java/org/usf/jquery/web/ParseException.java @@ -18,14 +18,15 @@ public ParseException(String message, Throwable cause) { super(message, cause); } - @Deprecated static ParseException cannotEvaluateException(String type, String expression) { return new ParseException("cannot evaluate " + type + " " + quote(expression)); } - @Deprecated + static ParseException cannotParseException(String type, String value) { + return cannotParseException(type, value, null); + } + static ParseException cannotParseException(String type, String value, Throwable cause) { return new ParseException("cannot parse " + type + " " + quote(value), cause); } - } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 284cfcf7..50f7e818 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -5,13 +5,10 @@ import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.DBColumn.column; -import static org.usf.jquery.core.Operator.count; import static org.usf.jquery.core.Operator.lookupOperator; -import static org.usf.jquery.core.Operator.lookupStandaloneFunction; import static org.usf.jquery.core.Operator.lookupWindowFunction; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; -import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.web.ArgumentParsers.parse; import static org.usf.jquery.web.ColumnDecorator.ofColumn; import static org.usf.jquery.web.CriteriaBuilder.ofComparator; @@ -19,7 +16,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.function.Predicate; import java.util.stream.Stream; import org.usf.jquery.core.ComparisonExpression; @@ -28,10 +24,8 @@ import org.usf.jquery.core.DBOrder; import org.usf.jquery.core.InCompartor; import org.usf.jquery.core.OperationColumn; -import org.usf.jquery.core.Operator; import org.usf.jquery.core.Order; import org.usf.jquery.core.Parameter; -import org.usf.jquery.core.SqlStringBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedOperator; import org.usf.jquery.core.WindowView; @@ -72,39 +66,30 @@ public TaggableColumn asColumn(TableDecorator td) { } } while(e.next()); } - else { - return c; + if(nonNull(e.tag)) { + return oc.as(e.tag); } - return oc.as(isNull(e.tag) ? c.tagname() : e.tag); + return oc == c ? c : oc.as(c.tagname()); } public DBFilter asFilter(TableDecorator td, List values) { var t = lookupResource(td); var c = t.buildColumn(); var e = t.entry.next; + var p = t.entry; DBColumn oc = c; while(nonNull(e)) { var op = e.toOperation(td, oc); - if(isNull(oc)) { + if(isNull(op)) { break; } - else { - oc = op; //preserve last non null column - } + oc = op; //preserve last non null column + p = e; //preserve last operator entry e = e.next; } - var cd = c == oc ? t.cd : DEFAUL_COLUMN; //no operation - ComparisonExpression exp = null; - if(isNull(e)) { // no expression - exp = new RequestEntryChain(null).toComparison(td, cd, values); - } - else if(e.isLast()) { - exp = e.toComparison(td, cd, values); - } - else { - throw cannotEvaluateException("filter", e); //more detail - } - return "OVER".equals(e.value) + var exp = (isNull(e) ? new RequestEntryChain(null) : e) + .toComparison(td, c == oc ? t.cd : DEFAUL_COLUMN, values); + return "over".equals(p.value) ? new WindowView(td.table(), oc.as(c.tagname())).filter(exp) : oc.filter(exp); } @@ -137,13 +122,24 @@ else if(e.isLast()) { //last entry throw cannotEvaluateException("order", e); //column expected } - OperationColumn asOperation(TableDecorator td) { + public OperationColumn asOperation(TableDecorator td) { //predicate var op = toOperation(td, null); if(nonNull(op)) { return op; } throw cannotEvaluateException("operation", this); // as function } + + OperationColumn toOperation(TableDecorator td, DBColumn col) { + var res = lookupOperator(value); + if(isNull(col) && "count".equals(value) && isEmpty(args)) { + col = b-> { //no column & no args + b.view(td.table()); + return "*"; + }; + } + return res.isEmpty() ? null : fillArgs(td, col, res.get()); + } ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List values) { if(nonNull(value)) { @@ -169,48 +165,32 @@ ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List { - b.view(td.table()); - return "*"; - }; - return new Triple(td, ofColumn(value, b-> fillArgs(td, col, count())), this); + var op = toOperation(td, null); + if(nonNull(op)) { + return new Triple(td, ofColumn(value, b-> op), this); } - return lookupStandaloneFunction(value) - .map(fn-> new Triple(td, ofColumn(value, b-> fillArgs(td, null, fn)), this)) - .orElseThrow(()-> cannotEvaluateException("resource", this)); + throw cannotEvaluateException("resource", this); } - private Triple lookupPrefixedResource(TableDecorator td) { + private Triple lookupViewResource(TableDecorator td) { return context().isDeclaredColumn(value) ? new Triple(td, context().getColumn(requireNoArgs().value), this) : lookupWindowFunction(value) //rank, rowNumber, .. - .map(fn-> new Triple(td, ofColumn(value, b-> fillArgs(td, null, fn)), this)) //reuse fn + .map(fn-> new Triple(td, ofColumn(value, b-> fillArgs(td, null, fn)), this)) .orElse(null); } @@ -241,6 +221,12 @@ private OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator } throw new IllegalArgumentException("msg"); } + + private Object toArg(TableDecorator td, Parameter parameter) { + return isNull(value) || text + ? requireNoArgs().value + : parse(this, td, parameter.getTypes()); + } RequestEntryChain requireNoArgs() { if(isNull(args)) { @@ -273,11 +259,7 @@ public String toString() { } static ParseException cannotEvaluateException(String type, RequestEntryChain entry) { - return cannotEvaluateException(type, entry.toString()); - } - - static ParseException cannotEvaluateException(String type, String entry) { - return new ParseException("cannot evaluate " + type + " " + quote(entry)); + return ParseException.cannotEvaluateException(type, entry.toString()); } static String[] toStringArray(List entries) { @@ -295,5 +277,4 @@ TaggableColumn buildColumn() { return td.column(cd); } } - } \ No newline at end of file diff --git a/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java b/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java index b159f626..cf55c89d 100644 --- a/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java +++ b/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java @@ -1,10 +1,8 @@ package org.usf.jquery.web; import static java.util.Collections.emptyList; -import static org.junit.jupiter.api.Assertions.*; -import static org.usf.jquery.web.RequestParser.*; +import static org.usf.jquery.web.RequestParser.parseEntry; -import java.util.Collections; import java.util.Optional; import org.junit.jupiter.api.Test; From 9d833f329eb0a6394097696574b4313ea1d91d23 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 5 Jan 2024 01:29:37 +0100 Subject: [PATCH 028/298] edit --- .../org/usf/jquery/core/QueryParameterBuilder.java | 12 ++++++------ .../org/usf/jquery/core/RequestQueryBuilder.java | 9 +++++---- src/main/java/org/usf/jquery/core/WindowView.java | 11 ++++++----- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index b83ce3c1..f05797bb 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -33,9 +33,9 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class QueryParameterBuilder { - private static final String ALIAS = "t"; private static final String ARG = "?"; + private final String viewAlias; private final Collection args; private final List views; //indexed @@ -52,11 +52,11 @@ private String view(TaggableView view, IntConsumer consumer) { for(; i views(){ @@ -159,14 +159,14 @@ static String formatNumber(Object o) { } public static QueryParameterBuilder addWithValue() { - return new QueryParameterBuilder(null, new ArrayList<>()); //no args + return new QueryParameterBuilder("s", null, new ArrayList<>()); //no args } public static QueryParameterBuilder addWithValue(QueryParameterBuilder builder) { - return new QueryParameterBuilder(null, builder.views); + return new QueryParameterBuilder(builder.viewAlias, null, builder.views); } public static QueryParameterBuilder parametrized() { - return new QueryParameterBuilder(new LinkedList<>(), new ArrayList<>()); + return new QueryParameterBuilder("v", new LinkedList<>(), new ArrayList<>()); } } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index f3effc8a..e4166c24 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -83,19 +83,20 @@ public RequestQuery build(String schema) { } public final void build(SqlStringBuilder sb, QueryParameterBuilder pb, String schema){ - select(sb, pb, schema); where(sb, pb); groupBy(sb); having(sb, pb); orderBy(sb, pb); + sb.sb.insert(0, select(pb, schema)); //declare all view before FROM } - void select(SqlStringBuilder sb, QueryParameterBuilder pb, String schema){ - sb.append("SELECT ") + @Deprecated + String select(QueryParameterBuilder pb, String schema){ + return new SqlStringBuilder(100).append("SELECT ") .appendIf(distinct, ()-> "DISTINCT ") .appendEach(columns, SCOMA, o-> o.sql(pb) + " AS " + doubleQuote(o.tagname())) .appendIf(!pb.views().isEmpty(), " FROM ") //TODO finish this - .appendEach(pb.views(), SCOMA, o-> o.sql(pb, schema, true)); + .appendEach(pb.views(), SCOMA, o-> o.sql(pb, schema, true)).toString(); } void where(SqlStringBuilder sb, QueryParameterBuilder pb){ diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index f7fc5efc..08768b5e 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -20,16 +20,17 @@ public final class WindowView implements TaggableView { @Override public String sql(QueryParameterBuilder builder, String schema) { //sub query should not use main builder - var va = "v0"; + var b = addWithValue(); + var v = b.view(this); return new SqlStringBuilder(100) - .append("(SELECT ").append(va).append(".*, ") - .append(column.sql(addWithValue())).append(" AS ").append(doubleQuote(column.tagname())) - .append(" FROM ").append(view.sql(addWithValue(), schema)).append(SPACE).append(va).append(")") + .append("(SELECT ").append(member(v, "*")).append(", ") + .append(column.sql(b)).append(" AS ").append(doubleQuote(column.tagname())) + .append(" FROM ").append(view.sql(b, schema)).append(SPACE).append(v).append(")") .toString(); } public DBFilter filter(ComparisonExpression expression) { - DBColumn col = b-> member(b.overwriteView(this), column.tagname()); + DBColumn col = b-> member(b.overwriteView(this), doubleQuote(column.tagname())); return col.filter(expression); } From 658c2ebe9aed23d201e95d7ec84d6797584e8fe7 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 5 Jan 2024 01:58:56 +0100 Subject: [PATCH 029/298] edit --- src/main/java/org/usf/jquery/core/OverClause.java | 9 ++++----- .../java/org/usf/jquery/core/QueryParameterBuilder.java | 3 +-- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 9 +++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/OverClause.java b/src/main/java/org/usf/jquery/core/OverClause.java index d9efe2cc..c29a5b69 100644 --- a/src/main/java/org/usf/jquery/core/OverClause.java +++ b/src/main/java/org/usf/jquery/core/OverClause.java @@ -32,17 +32,16 @@ public OverClause() { @Override public String sql(QueryParameterBuilder builder, Object[] args) { requireNoArgs(args, OverClause.class::getSimpleName); - return sql(); + return sql(builder); } - String sql() { - var qp = addWithValue(); //no alias + String sql(QueryParameterBuilder builder) { var sb = new SqlStringBuilder(100); if(nonNull(partition)) { - sb.append(partition.sql(qp)); + sb.append(partition.sql(builder)); } if(nonNull(order)) { //require orders - sb.appendIf(nonNull(partition), SPACE).append(order.sql(qp)); + sb.appendIf(nonNull(partition), SPACE).append(order.sql(builder)); } return sb.toString(); } diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index f05797bb..cce4078b 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -15,7 +15,6 @@ import java.sql.Date; import java.sql.Timestamp; import java.util.ArrayList; -import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.function.Function; @@ -36,7 +35,7 @@ public final class QueryParameterBuilder { private static final String ARG = "?"; private final String viewAlias; - private final Collection args; + private final List args; private final List views; //indexed public String view(TaggableView view) { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 50f7e818..c847d251 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -104,13 +104,11 @@ public DBOrder asOrder(TableDecorator td) { if(isNull(op)) { break; } - else { - oc = op; //preserve last non null column - } + oc = op; //preserve last non null column e = e.next; } if(isNull(e)) { // no expression - return c.order(); + return oc.order(); } else if(e.isLast()) { //last entry var upVal = e.value.toUpperCase(); @@ -142,6 +140,9 @@ OperationColumn toOperation(TableDecorator td, DBColumn col) { } ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List values) { + if(next()) { + throw cannotEvaluateException("expression", next); // as function + } if(nonNull(value)) { var criteria = cd.criteria(value); if(nonNull(criteria)) { From 85eb66be5be7f43c88b8d68dd417adf16663937b Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 5 Jan 2024 02:06:14 +0100 Subject: [PATCH 030/298] edit --- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index c847d251..d8f79802 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -141,7 +141,7 @@ OperationColumn toOperation(TableDecorator td, DBColumn col) { ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List values) { if(next()) { - throw cannotEvaluateException("expression", next); // as function + throw cannotEvaluateException("expression", this); // as function } if(nonNull(value)) { var criteria = cd.criteria(value); From 56259eacddceb68eafba0895ebc89f860095a111 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 5 Jan 2024 13:42:51 +0100 Subject: [PATCH 031/298] edit --- .../java/org/usf/jquery/core/Operator.java | 20 ++++++++++++++ .../java/org/usf/jquery/core/OverClause.java | 1 - .../org/usf/jquery/web/ArgumentParsers.java | 27 ++++++++++--------- .../org/usf/jquery/web/RequestEntryChain.java | 2 +- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index af70e911..1ee9c8d8 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -17,7 +17,9 @@ import static org.usf.jquery.core.Parameter.optional; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; +import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; +import static org.usf.jquery.core.Validation.requireNArgs; import java.util.Optional; import java.util.function.Function; @@ -29,6 +31,20 @@ * */ public interface Operator extends DBProcessor, NestedSql { + + static final Operator VALUE_RETURN = new Operator() { + + @Override + public String sql(QueryParameterBuilder builder, Object[] args) { + requireNArgs(1, args, this::id); + return args[0] instanceof Number ? args[0].toString() : quote(args[0].toString()); + } + + @Override + public String id() { + return "value"; + } + }; String id(); @@ -287,6 +303,10 @@ static TypedOperator partition() { static TypedOperator order() { return new TypedOperator(CLAUSE, clause("ORDER BY"), required(ORDER), varargs(ORDER)); } + + static TypedOperator value() { + return new TypedOperator(firstArgType(), VALUE_RETURN, required()); + } static ArithmeticOperator operator(String symbol) { return ()-> symbol; diff --git a/src/main/java/org/usf/jquery/core/OverClause.java b/src/main/java/org/usf/jquery/core/OverClause.java index c29a5b69..7d3e244f 100644 --- a/src/main/java/org/usf/jquery/core/OverClause.java +++ b/src/main/java/org/usf/jquery/core/OverClause.java @@ -3,7 +3,6 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.stream.Collectors.groupingBy; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index f68a7e82..5796e2b9 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -1,7 +1,6 @@ package org.usf.jquery.web; import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; import static org.usf.jquery.core.JDBCType.BIGINT; import static org.usf.jquery.core.JDBCType.DATE; import static org.usf.jquery.core.JDBCType.DOUBLE; @@ -46,23 +45,25 @@ public class ArgumentParsers { jdbcArgParser(VARCHAR)}; public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType... types) { - if(nonNull(types)) { - if(types.length == 1 && types[0] instanceof JqueryType) { // only one type - return jqueryArgParser((JqueryType)types[0]).parse(entry, td); - } - if(!Stream.of(types).allMatch(JDBCType.class::isInstance)) { - throw new IllegalArgumentException("different types"); - } + if(isEmpty(types) || Stream.of(types).allMatch(JDBCType.class::isInstance)) { + try { + return jqueryArgParser(COLUMN).parse(entry, td); //only JDBC types + } catch (Exception e) {/*do not throw exception*/} } - try { - return jqueryArgParser(COLUMN).parse(entry, td); - } catch (Exception e) {/*do not throw exception*/} if(isEmpty(types)) { - types = new JavaType[] {null}; + return jdbcArgParser(null).parse(entry, td); } for(var type : types) { try { - return jdbcArgParser((JDBCType) type).parse(entry, td); + if(type instanceof JqueryType) { + return jqueryArgParser((JqueryType) type).parse(entry, td); + } + else if(type instanceof JDBCType) { + return jdbcArgParser((JDBCType) type).parse(entry, td); + } + else { + throw new UnsupportedOperationException("unsupported " + type); + } } catch (Exception e) {/*do not throw exception*/} } throw cannotParseException("value", entry.toString()); diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index d8f79802..a5be3f1a 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -141,7 +141,7 @@ OperationColumn toOperation(TableDecorator td, DBColumn col) { ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List values) { if(next()) { - throw cannotEvaluateException("expression", this); // as function + throw cannotEvaluateException("expression", this); } if(nonNull(value)) { var criteria = cd.criteria(value); From f67d7a47b2c871badb9099e2d35ec669e4b116fe Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 5 Jan 2024 17:35:44 +0100 Subject: [PATCH 032/298] edit --- src/main/java/org/usf/jquery/core/Operator.java | 4 ++-- src/main/java/org/usf/jquery/core/TypedOperator.java | 4 ++-- src/main/java/org/usf/jquery/web/ArgumentParsers.java | 10 ++++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 1ee9c8d8..b369ed07 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -287,9 +287,9 @@ public OperationColumn args(Object... args) { } @Override - Object[] mapArg(Object... args) { //map args after check + Object[] afterCheck(Object... args) { //map args after check var c = Stream.of(args).skip(1).toArray(OperationColumn[]::new); - return super.mapArg(args[0], clauses(c)); + return super.afterCheck(args[0], clauses(c)); } }; } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 7c454124..cebc890d 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -58,12 +58,12 @@ public OperationColumn args(Object... args) { throw illegalArgumentException(); } } - return new OperationColumn(operator, mapArg(args), typeFn.apply(args)); + return new OperationColumn(operator, afterCheck(args), typeFn.apply(args)); } throw new IllegalArgumentException("mismatch parameters"); } - Object[] mapArg(Object... args) { + Object[] afterCheck(Object... args) { return args; } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 5796e2b9..ea95a368 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -7,7 +7,6 @@ import static org.usf.jquery.core.JDBCType.TIME; import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; -import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.JqueryType.COLUMN; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.web.ParseException.cannotParseException; @@ -41,17 +40,16 @@ public class ArgumentParsers { private static final JDBCArgumentParser[] STD_PRS = { jdbcArgParser(BIGINT), jdbcArgParser(DOUBLE), jdbcArgParser(DATE), jdbcArgParser(TIMESTAMP), - jdbcArgParser(TIME), jdbcArgParser(TIMESTAMP_WITH_TIMEZONE), - jdbcArgParser(VARCHAR)}; + jdbcArgParser(TIME), jdbcArgParser(TIMESTAMP_WITH_TIMEZONE)}; public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType... types) { if(isEmpty(types) || Stream.of(types).allMatch(JDBCType.class::isInstance)) { try { return jqueryArgParser(COLUMN).parse(entry, td); //only JDBC types } catch (Exception e) {/*do not throw exception*/} - } - if(isEmpty(types)) { - return jdbcArgParser(null).parse(entry, td); + if(isEmpty(types)) { + return jdbcArgParser(null).parse(entry, td); + } } for(var type : types) { try { From bb5eb57fc99fa8d861413206a1342558f0b403f6 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 6 Jan 2024 21:34:32 +0100 Subject: [PATCH 033/298] edit --- .../org/usf/jquery/core/BasicComparator.java | 2 +- .../java/org/usf/jquery/core/CaseColumn.java | 2 +- .../org/usf/jquery/core/ClauseFunction.java | 2 +- .../java/org/usf/jquery/core/DBColumn.java | 3 +- .../java/org/usf/jquery/core/InCompartor.java | 2 +- .../org/usf/jquery/core/NullComparator.java | 2 +- .../org/usf/jquery/core/PipeFunction.java | 2 +- .../jquery/core/QueryParameterBuilder.java | 119 ++++++------------ .../org/usf/jquery/core/StringComparator.java | 2 +- .../org/usf/jquery/core/WhenExpression.java | 2 +- 10 files changed, 51 insertions(+), 87 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/BasicComparator.java b/src/main/java/org/usf/jquery/core/BasicComparator.java index 8674183b..668e6c1b 100644 --- a/src/main/java/org/usf/jquery/core/BasicComparator.java +++ b/src/main/java/org/usf/jquery/core/BasicComparator.java @@ -15,6 +15,6 @@ public interface BasicComparator extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, BasicComparator.class::getSimpleName); - return builder.appendParameter(args[0]) + symbol() + builder.appendParameter(args[1]); + return builder.appendLitteral(args[0]) + symbol() + builder.appendParameter(args[1]); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 2079fa63..a8d0ac44 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -24,7 +24,7 @@ public final class CaseColumn implements DBColumn { @Override public String sql(QueryParameterBuilder builder) { return expressions.stream() - .map(o-> o.sql(addWithValue(builder))) + .map(o-> o.sql(builder.withValue())) .collect(joining(SPACE, "CASE ", " END")); } diff --git a/src/main/java/org/usf/jquery/core/ClauseFunction.java b/src/main/java/org/usf/jquery/core/ClauseFunction.java index 29641ce3..e37da7a1 100644 --- a/src/main/java/org/usf/jquery/core/ClauseFunction.java +++ b/src/main/java/org/usf/jquery/core/ClauseFunction.java @@ -20,7 +20,7 @@ public interface ClauseFunction extends FunctionOperator { default String sql(QueryParameterBuilder builder, Object[] args) { return isEmpty(args) ? EMPTY - : id() + SPACE + Stream.of(args).map(builder::appendParameter).collect(joining(COMA)); + : id() + SPACE + Stream.of(args).map(builder::appendLitteral).collect(joining(COMA)); } } diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index ded2c54e..4aa04b7c 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -1,6 +1,7 @@ package org.usf.jquery.core; import static java.util.Optional.empty; +import static org.usf.jquery.core.QueryParameterBuilder.formatValue; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -150,7 +151,7 @@ static DBColumn constant(Supplier value) { @Override public String sql(QueryParameterBuilder arg) { - return arg.formatValue(value.get()); + return formatValue(value.get()); } @Override diff --git a/src/main/java/org/usf/jquery/core/InCompartor.java b/src/main/java/org/usf/jquery/core/InCompartor.java index 2a2de6b5..fcbd8680 100644 --- a/src/main/java/org/usf/jquery/core/InCompartor.java +++ b/src/main/java/org/usf/jquery/core/InCompartor.java @@ -19,6 +19,6 @@ public interface InCompartor extends Comparator { default String sql(QueryParameterBuilder builder, Object[] args) { requireAtLeastNArgs(2, args, InCompartor.class::getSimpleName); var params = copyOfRange(args, 1, args.length); - return builder.appendParameter(args[0]) + SPACE + name() + parenthese(builder.appendArray(params)); + return builder.appendLitteral(args[0]) + SPACE + name() + parenthese(builder.appendArrayParameter(params)); } } diff --git a/src/main/java/org/usf/jquery/core/NullComparator.java b/src/main/java/org/usf/jquery/core/NullComparator.java index 87f547ee..d65f4d72 100644 --- a/src/main/java/org/usf/jquery/core/NullComparator.java +++ b/src/main/java/org/usf/jquery/core/NullComparator.java @@ -16,6 +16,6 @@ public interface NullComparator extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(1, args, NullComparator.class::getSimpleName); - return builder.appendParameter(args[0]) + SPACE + name(); + return builder.appendLitteral(args[0]) + SPACE + name(); } } diff --git a/src/main/java/org/usf/jquery/core/PipeFunction.java b/src/main/java/org/usf/jquery/core/PipeFunction.java index c673820f..ad2918eb 100644 --- a/src/main/java/org/usf/jquery/core/PipeFunction.java +++ b/src/main/java/org/usf/jquery/core/PipeFunction.java @@ -15,7 +15,7 @@ public interface PipeFunction extends FunctionOperator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireAtLeastNArgs(1, args, ()-> "Pipe function"); - return builder.appendParameter(args[0]) + SPACE + return builder.appendLitteral(args[0]) + SPACE + FunctionOperator.super.sql(builder, copyOfRange(args, 1, args.length)); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index cce4078b..ddd42cb1 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -1,19 +1,13 @@ package org.usf.jquery.core; -import static java.lang.reflect.Array.getLength; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; -import static java.util.stream.IntStream.range; import static org.usf.jquery.core.SqlStringBuilder.COMA; import static org.usf.jquery.core.SqlStringBuilder.EMPTY; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.core.Validation.illegalArgumentIf; -import java.lang.reflect.Array; -import java.sql.Date; -import java.sql.Timestamp; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -22,6 +16,7 @@ import java.util.stream.Stream; import lombok.AccessLevel; +import lombok.NonNull; import lombok.RequiredArgsConstructor; /** @@ -38,6 +33,10 @@ public final class QueryParameterBuilder { private final List args; private final List views; //indexed + public List views(){ + return views; + } + public String view(TaggableView view) { return view(view, i-> {}); } @@ -47,8 +46,7 @@ public String overwriteView(TaggableView view) { } private String view(TaggableView view, IntConsumer consumer) { - var i=0; - for(; i views(){ - return views; - } public String appendParameter(Object o) { - return appendParameter(o, Object.class, false); - } - - public String appendNumber(Object o) { - return appendParameter(o, Number.class, false); - } - - public String appendString(Object o) { - return appendParameter(o, String.class, false); - } - - public String appendDate(Object o) { - return appendParameter(o, Date.class, false); - } - - public String appendTimestamp(Object o) { - return appendParameter(o, Timestamp.class, false); + if(dynamic()) { + return o instanceof DBObject + ? ((DBObject)o).sql(this, null) + : appendArg(o); + } + return appendLitteral(o); } - public String appendLitteral(Object o, JavaType type) { - return appendParameter(o, type.type(), true); - } - - //TODO public String appendLitteral(Object o) { - return appendParameter(o, Object.class, true); - } - - private String appendParameter(Object o, Class type, boolean addWithValue) { - if(isNull(o)) { - return dynamic() && !addWithValue ? appendArg(null) : "null"; - } - if(o instanceof DBObject) { //check type !? - return ((DBObject)o).sql(this, null); - } - if(type.isInstance(o)) { - if(dynamic() && !addWithValue) { - return appendArg(o); - } - return formatValue(o); - } - throw new IllegalArgumentException("require " + type.getSimpleName().toLowerCase() + " parameter"); + return o instanceof DBObject + ? ((DBObject)o).sql(this, null) + : formatValue(o); } - - public String appendArray(Object o) { - illegalArgumentIf(o == null || !o.getClass().isArray(), ()-> "require array parameter"); + + public String appendArrayParameter(@NonNull Object[] arr) { if(dynamic()) { - streamArray(o).forEach(args::add); - return nParameter(getLength(o)); + Stream.of(arr).forEach(args::add); + return nParameter(arr.length); } - Function fn = o.getClass().getComponentType().isAssignableFrom(Number.class) + return appendArrayParameter(arr); + } + + public String appendLitteralArray(@NonNull Object[] arr) { + Function fn = arr.getClass().getComponentType().isAssignableFrom(Number.class) ? QueryParameterBuilder::formatNumber : QueryParameterBuilder::formatString; - return streamArray(o).map(fn).collect(joining(SCOMA)); + return Stream.of(arr).map(fn).collect(joining(SCOMA)); } private String appendArg(Object o) { args.add(o); return ARG; } - - String formatValue(Object o) { - return o instanceof Number - ? formatNumber(o) - : formatString(o); - } - - boolean dynamic() { - return nonNull(args); - } public Object[] args() { return dynamic() ? args.toArray() : new Object[0]; } - - static Stream streamArray(Object o) { - return range(0, getLength(o)) - .mapToObj(i-> Array.get(o, i)); + + private boolean dynamic() { + return nonNull(args); } static String nParameter(int n){ @@ -149,23 +105,30 @@ static String nParameter(int n){ } return n == 1 ? ARG : ARG + (COMA + ARG).repeat(n-1); } + + static String formatValue(Object o) { + return o instanceof Number + ? formatNumber(o) + : formatString(o); + } static String formatString(Object o) { - return quote(o.toString()); + return isNull(o) ? "null" : quote(o.toString()); } static String formatNumber(Object o) { - return o.toString(); + return isNull(o) ? "null" : o.toString(); } - public static QueryParameterBuilder addWithValue() { - return new QueryParameterBuilder("s", null, new ArrayList<>()); //no args + public QueryParameterBuilder withValue() { + return new QueryParameterBuilder(viewAlias, null, views); } - public static QueryParameterBuilder addWithValue(QueryParameterBuilder builder) { - return new QueryParameterBuilder(builder.viewAlias, null, builder.views); + public static QueryParameterBuilder addWithValue() { + return new QueryParameterBuilder("s", null, new ArrayList<>()); //no args } public static QueryParameterBuilder parametrized() { return new QueryParameterBuilder("v", new LinkedList<>(), new ArrayList<>()); } + } diff --git a/src/main/java/org/usf/jquery/core/StringComparator.java b/src/main/java/org/usf/jquery/core/StringComparator.java index 731ecb19..d30984fd 100644 --- a/src/main/java/org/usf/jquery/core/StringComparator.java +++ b/src/main/java/org/usf/jquery/core/StringComparator.java @@ -16,6 +16,6 @@ public interface StringComparator extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, String.class::getSimpleName); - return builder.appendString(args[0]) + space(name()) + builder.appendString(args[1]); + return builder.appendLitteral(args[0]) + space(name()) + builder.appendParameter(args[1]); } } diff --git a/src/main/java/org/usf/jquery/core/WhenExpression.java b/src/main/java/org/usf/jquery/core/WhenExpression.java index 15eee7e6..ba6e4de4 100644 --- a/src/main/java/org/usf/jquery/core/WhenExpression.java +++ b/src/main/java/org/usf/jquery/core/WhenExpression.java @@ -29,7 +29,7 @@ public String sql(QueryParameterBuilder arg) { : sb.append("WHEN ") .append(filter.sql(arg)) .append(" THEN "); - return sb.append(arg.appendParameter(value)).toString(); + return sb.append(arg.appendLitteral(value)).toString(); } @Override From 2c523e7d86c21b68753c836111a9447caf8636e4 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 8 Jan 2024 11:16:36 +0100 Subject: [PATCH 034/298] edit --- .../java/org/usf/jquery/core/CaseColumn.java | 2 +- .../core/ComparisonSingleExpression.java | 6 +-- .../java/org/usf/jquery/core/DBColumn.java | 6 --- .../java/org/usf/jquery/core/DBTable.java | 3 +- .../org/usf/jquery/core/FunctionOperator.java | 9 +--- .../jquery/core/QueryParameterBuilder.java | 52 ++++++++++--------- .../org/usf/jquery/core/SqlStringBuilder.java | 3 +- .../org/usf/jquery/core/TypedOperator.java | 2 +- .../java/org/usf/jquery/core/WindowView.java | 2 +- 9 files changed, 38 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index a8d0ac44..f083c4c1 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -23,7 +23,7 @@ public final class CaseColumn implements DBColumn { @Override public String sql(QueryParameterBuilder builder) { - return expressions.stream() + return expressions.stream() //empty !? .map(o-> o.sql(builder.withValue())) .collect(joining(SPACE, "CASE ", " END")); } diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index fd083fca..e61aaf4d 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -3,9 +3,9 @@ import static org.usf.jquery.core.DBColumn.column; import static org.usf.jquery.core.NestedSql.aggregation; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; -import static org.usf.jquery.core.QueryParameterBuilder.streamArray; import java.util.LinkedList; +import java.util.stream.Stream; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -27,7 +27,7 @@ public String sql(QueryParameterBuilder builder, Object left) { param.add(left); if(right != null) { if(right.getClass().isArray()) { - streamArray(right).forEach(param::add); + Stream.of((Object[])right).forEach(param::add); } else { param.add(right); @@ -49,5 +49,5 @@ public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) @Override public String toString() { return sql(addWithValue(), column("")); - } + } } diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 4aa04b7c..7618c6fe 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -1,11 +1,9 @@ package org.usf.jquery.core; -import static java.util.Optional.empty; import static org.usf.jquery.core.QueryParameterBuilder.formatValue; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; -import java.util.Optional; import java.util.function.Supplier; import org.usf.jquery.core.CaseSingleColumnBuilder.WhenFilterBridge; @@ -231,8 +229,4 @@ static OperationColumn lower(Object arg) { static OperationColumn substring(Object arg, int start, int length) { return Operator.substring().args(arg, start, length); } - - static Optional lookupColumnFunction() { - return empty(); //CURRENT_DATE, CURRENT_DATETIME - } } diff --git a/src/main/java/org/usf/jquery/core/DBTable.java b/src/main/java/org/usf/jquery/core/DBTable.java index 2ae8df21..88a7c1b3 100644 --- a/src/main/java/org/usf/jquery/core/DBTable.java +++ b/src/main/java/org/usf/jquery/core/DBTable.java @@ -2,7 +2,6 @@ import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.member; -import static org.usf.jquery.core.Utils.isBlank; import lombok.RequiredArgsConstructor; @@ -19,7 +18,7 @@ public class DBTable implements TaggableView { @Override public String sql(QueryParameterBuilder builder, String schema) { - return isBlank(schema) ? name : member(schema, name); + return member(schema, name); } @Override diff --git a/src/main/java/org/usf/jquery/core/FunctionOperator.java b/src/main/java/org/usf/jquery/core/FunctionOperator.java index 09be5e83..0e263c97 100644 --- a/src/main/java/org/usf/jquery/core/FunctionOperator.java +++ b/src/main/java/org/usf/jquery/core/FunctionOperator.java @@ -1,10 +1,5 @@ package org.usf.jquery.core; -import static java.util.stream.Collectors.joining; -import static java.util.stream.IntStream.range; -import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.Utils.isPresent; - /** * https://learnsql.com/blog/standard-sql-functions-cheat-sheet/ * @@ -18,9 +13,7 @@ public interface FunctionOperator extends Operator { default String sql(QueryParameterBuilder builder, Object[] args) { return new SqlStringBuilder(id()) .append("(") - .appendIf(isPresent(args), ()-> range(0, args.length) - .mapToObj(i-> builder.appendLitteral(args[i])) - .collect(joining(SCOMA))) //accept any + .append(builder.appendLitteralArray(args)) //accept any .append(")") .toString(); } diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index ddd42cb1..eb68e739 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -7,11 +7,11 @@ import static org.usf.jquery.core.SqlStringBuilder.EMPTY; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.core.Utils.isEmpty; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -import java.util.function.Function; import java.util.function.IntConsumer; import java.util.stream.Stream; @@ -29,7 +29,7 @@ public final class QueryParameterBuilder { private static final String ARG = "?"; - private final String viewAlias; + private final String vPrefix; private final List args; private final List views; //indexed @@ -46,14 +46,17 @@ public String overwriteView(TaggableView view) { } private String view(TaggableView view, IntConsumer consumer) { + if(isNull(vPrefix)) { + return null; + } for(var i=0; i fn = arr.getClass().getComponentType().isAssignableFrom(Number.class) - ? QueryParameterBuilder::formatNumber - : QueryParameterBuilder::formatString; - return Stream.of(arr).map(fn).collect(joining(SCOMA)); + return isEmpty(arr) ? EMPTY : Stream.of(arr) + .map(this::appendLitteral) + .collect(joining(SCOMA)); } private String appendArg(Object o) { @@ -107,28 +112,27 @@ static String nParameter(int n){ } static String formatValue(Object o) { - return o instanceof Number - ? formatNumber(o) - : formatString(o); - } - - static String formatString(Object o) { - return isNull(o) ? "null" : quote(o.toString()); - } - static String formatNumber(Object o) { - return isNull(o) ? "null" : o.toString(); + if(nonNull(o)){ + return o instanceof Number + ? o.toString() + : quote(o.toString()); + } + return "null"; } public QueryParameterBuilder withValue() { - return new QueryParameterBuilder(viewAlias, null, views); + return new QueryParameterBuilder(vPrefix, null, views); } - + public static QueryParameterBuilder addWithValue() { - return new QueryParameterBuilder("s", null, new ArrayList<>()); //no args + return new QueryParameterBuilder(null, null, null); //no args + } + + public static QueryParameterBuilder addWithValue(String prefix) { + return new QueryParameterBuilder(prefix, null, new LinkedList<>()); } public static QueryParameterBuilder parametrized() { return new QueryParameterBuilder("v", new LinkedList<>(), new ArrayList<>()); } - } diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index e641b239..23f7fe6c 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static java.util.Objects.isNull; import static java.util.function.Function.identity; import java.util.Collection; @@ -106,6 +107,6 @@ public static String parenthese(String op) { } public static String member(String parent, String child) { - return parent + "." + child; + return isNull(parent) ? child : parent + "." + child; } } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index cebc890d..76c31993 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -25,7 +25,7 @@ public class TypedOperator implements Operator { private final Parameter[] parameters; public TypedOperator(JavaType type, Operator function, Parameter... args) { - this(o-> type, function, args == null ? NO_PARAM : args); + this(o-> type, function, isNull(args) ? NO_PARAM : args); } public TypedOperator(Function typeFn, Operator function, Parameter... parameter) { diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index 08768b5e..74aa7b2a 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -20,7 +20,7 @@ public final class WindowView implements TaggableView { @Override public String sql(QueryParameterBuilder builder, String schema) { //sub query should not use main builder - var b = addWithValue(); + var b = addWithValue("v"); var v = b.view(this); return new SqlStringBuilder(100) .append("(SELECT ").append(member(v, "*")).append(", ") From 3d0860533d2c21d4eb5d0a6960db202619dc8485 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 8 Jan 2024 12:18:57 +0100 Subject: [PATCH 035/298] edit --- .../org/usf/jquery/core/ComparisonSingleExpression.java | 3 ++- src/main/java/org/usf/jquery/core/OverClause.java | 2 +- src/main/java/org/usf/jquery/core/TypedOperator.java | 8 ++++---- src/main/java/org/usf/jquery/core/Utils.java | 9 --------- src/main/java/org/usf/jquery/core/WindowView.java | 6 +++--- 5 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index e61aaf4d..d5b38af5 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.DBColumn.column; import static org.usf.jquery.core.NestedSql.aggregation; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; @@ -25,7 +26,7 @@ public final class ComparisonSingleExpression implements ComparisonExpression { public String sql(QueryParameterBuilder builder, Object left) { var param = new LinkedList<>(); param.add(left); - if(right != null) { + if(nonNull(right)) { if(right.getClass().isArray()) { Stream.of((Object[])right).forEach(param::add); } diff --git a/src/main/java/org/usf/jquery/core/OverClause.java b/src/main/java/org/usf/jquery/core/OverClause.java index 7d3e244f..1a7fc14c 100644 --- a/src/main/java/org/usf/jquery/core/OverClause.java +++ b/src/main/java/org/usf/jquery/core/OverClause.java @@ -46,7 +46,7 @@ String sql(QueryParameterBuilder builder) { } public static OverClause clauses(OperationColumn... args) { //partition, order, ... - if(args == null) { + if(isNull(args)) { return new OverClause(); } var map = Stream.of(args).collect(groupingBy(o-> o.getOperator().id())); diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 76c31993..8515c090 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -67,10 +67,6 @@ Object[] afterCheck(Object... args) { return args; } - private static IllegalArgumentException illegalArgumentException() { - return new IllegalArgumentException("mismatch arg type"); - } - public int requireArgCount() { var i=0; while(i 0 && parameters[parameters.length-1].isVarargs(); } + + private static IllegalArgumentException illegalArgumentException() { + return new IllegalArgumentException("mismatch arg type"); + } } diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index fc95a8ca..78cd03f9 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -4,7 +4,6 @@ import static java.util.Objects.nonNull; import java.util.Collection; -import java.util.Map; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -19,10 +18,6 @@ public final class Utils { public static final int UNLIMITED = -1; - public static boolean isEmpty(int[] a) { - return isNull(a) || a.length == 0; - } - public static boolean isPresent(T[] a) { return nonNull(a) && a.length > 0; } @@ -35,10 +30,6 @@ public static boolean isEmpty(Collection c) { return isNull(c) || c.isEmpty(); } - public static boolean isEmpty(Map map) { - return isNull(map) || map.isEmpty(); - } - public static boolean isBlank(String s) { return isNull(s) || s.isBlank(); } diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index 74aa7b2a..d1c949b6 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -21,11 +21,11 @@ public final class WindowView implements TaggableView { @Override public String sql(QueryParameterBuilder builder, String schema) { //sub query should not use main builder var b = addWithValue("v"); - var v = b.view(this); + var alias = b.view(view); return new SqlStringBuilder(100) - .append("(SELECT ").append(member(v, "*")).append(", ") + .append("(SELECT ").append(member(alias, "*")).append(", ") .append(column.sql(b)).append(" AS ").append(doubleQuote(column.tagname())) - .append(" FROM ").append(view.sql(b, schema)).append(SPACE).append(v).append(")") + .append(" FROM ").append(view.sql(b, schema)).append(SPACE).append(alias).append(")") .toString(); } From 5a142b879ee0c22ff2495f406673d3a6c7b74ba6 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 8 Jan 2024 12:35:56 +0100 Subject: [PATCH 036/298] edit --- src/main/java/org/usf/jquery/core/RequestQueryBuilder.java | 5 ++--- src/main/java/org/usf/jquery/core/TaggableColumn.java | 5 ++--- src/main/java/org/usf/jquery/core/TaggableView.java | 7 +++---- src/main/java/org/usf/jquery/core/WindowView.java | 5 ++--- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index e4166c24..b4fedfd2 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -8,7 +8,6 @@ import static org.usf.jquery.core.LogicalOperator.AND; import static org.usf.jquery.core.QueryParameterBuilder.parametrized; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.Validation.requireNonEmpty; import java.util.Iterator; @@ -94,9 +93,9 @@ public final void build(SqlStringBuilder sb, QueryParameterBuilder pb, String sc String select(QueryParameterBuilder pb, String schema){ return new SqlStringBuilder(100).append("SELECT ") .appendIf(distinct, ()-> "DISTINCT ") - .appendEach(columns, SCOMA, o-> o.sql(pb) + " AS " + doubleQuote(o.tagname())) + .appendEach(columns, SCOMA, o-> o.sqlWithTag(pb)) .appendIf(!pb.views().isEmpty(), " FROM ") //TODO finish this - .appendEach(pb.views(), SCOMA, o-> o.sql(pb, schema, true)).toString(); + .appendEach(pb.views(), SCOMA, o-> o.sqlWithTag(pb, schema)).toString(); } void where(SqlStringBuilder sb, QueryParameterBuilder pb){ diff --git a/src/main/java/org/usf/jquery/core/TaggableColumn.java b/src/main/java/org/usf/jquery/core/TaggableColumn.java index e4f4b08f..1e38a943 100644 --- a/src/main/java/org/usf/jquery/core/TaggableColumn.java +++ b/src/main/java/org/usf/jquery/core/TaggableColumn.java @@ -11,8 +11,7 @@ public interface TaggableColumn extends DBColumn { String tagname(); //JSON & TAG - default String sql(QueryParameterBuilder builder, boolean as) { - var s = this.sql(builder); - return as ? s + " AS " + doubleQuote(tagname()) : s; + default String sqlWithTag(QueryParameterBuilder builder) { + return sql(builder) + " AS " + doubleQuote(tagname()); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/TaggableView.java b/src/main/java/org/usf/jquery/core/TaggableView.java index ca964c3a..4194bed7 100644 --- a/src/main/java/org/usf/jquery/core/TaggableView.java +++ b/src/main/java/org/usf/jquery/core/TaggableView.java @@ -10,9 +10,8 @@ public interface TaggableView extends DBView { String tagname(); - - default String sql(QueryParameterBuilder builder, String schema, boolean as) { - var s = this.sql(builder, schema); - return as ? s + SPACE + builder.view(this) : s; + + default String sqlWithTag(QueryParameterBuilder builder, String schema) { + return sql(builder, schema) + SPACE + builder.view(this); } } diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index d1c949b6..6a4f708a 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -20,11 +20,10 @@ public final class WindowView implements TaggableView { @Override public String sql(QueryParameterBuilder builder, String schema) { //sub query should not use main builder - var b = addWithValue("v"); + var b = addWithValue("w"); var alias = b.view(view); return new SqlStringBuilder(100) - .append("(SELECT ").append(member(alias, "*")).append(", ") - .append(column.sql(b)).append(" AS ").append(doubleQuote(column.tagname())) + .append("(SELECT ").append(member(alias, "*")).append(", ").append(column.sqlWithTag(b)) .append(" FROM ").append(view.sql(b, schema)).append(SPACE).append(alias).append(")") .toString(); } From 6d038c2d2699f4218ce716a83e3b10838bdd035f Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 8 Jan 2024 22:53:15 +0100 Subject: [PATCH 037/298] edit --- .../java/org/usf/jquery/core/JDBCType.java | 4 +-- .../java/org/usf/jquery/core/WindowView.java | 8 ++--- .../org/usf/jquery/web/ColumnDecorator.java | 2 +- .../org/usf/jquery/web/ColumnMetadata.java | 32 +++++++++++++++++-- .../org/usf/jquery/web/DatabaseMetadata.java | 7 ++-- .../org/usf/jquery/web/RequestEntryChain.java | 5 +++ .../org/usf/jquery/web/TableMetadata.java | 3 +- 7 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index d40d0218..2d1f9e69 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -41,7 +41,7 @@ public enum JDBCType implements JavaType { TIME(Types.TIME, Time.class, Time.class::isInstance), TIMESTAMP(Types.TIMESTAMP, Timestamp.class, Timestamp.class::isInstance), TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class, Timestamp.class::isInstance), - OTHER(Types.OTHER, null, o-> false); //isnull !? + OTHER(Types.OTHER, Object.class, o-> false); //isnull !? private final int value; private final Class type; @@ -84,7 +84,7 @@ private static boolean isNumber(Object o, double min, double max, boolean decima private static boolean isNumber(Object o) { return o instanceof Number; } - + private static boolean isBoolean(Object o) { return o.getClass() == Boolean.class || isNumber(o, 0, 1, false) diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index 6a4f708a..63224bad 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -1,7 +1,6 @@ package org.usf.jquery.core; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; -import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.SqlStringBuilder.member; @@ -21,10 +20,10 @@ public final class WindowView implements TaggableView { @Override public String sql(QueryParameterBuilder builder, String schema) { //sub query should not use main builder var b = addWithValue("w"); - var alias = b.view(view); return new SqlStringBuilder(100) - .append("(SELECT ").append(member(alias, "*")).append(", ").append(column.sqlWithTag(b)) - .append(" FROM ").append(view.sql(b, schema)).append(SPACE).append(alias).append(")") + .append("(SELECT ").append(member(b.view(view), "*")) + .append(", ").append(column.sqlWithTag(b)) + .append(" FROM ").append(view.sqlWithTag(b, schema)).append(")") .toString(); } @@ -42,5 +41,4 @@ public String tagname() { //inherits tagname public String toString() { return sql(addWithValue(), ""); } - } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 3030ff5d..50c46c54 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -92,7 +92,7 @@ private static Comparator containsArgPartten(StringComparator fn) { }; } - default ComparisonExpression expression(String exp, String... values) { return null; }; + default ComparisonExpression expression(String exp, String... values) { return null; } static ColumnDecorator ofColumn(String ref, ColumnBuilder cb) { return new ColumnDecorator() { diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index 212320d8..114d90be 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -1,7 +1,15 @@ package org.usf.jquery.web; +import static java.lang.Integer.MAX_VALUE; +import static org.usf.jquery.core.JDBCType.DECIMAL; +import static org.usf.jquery.core.JDBCType.DOUBLE; +import static org.usf.jquery.core.JDBCType.FLOAT; +import static org.usf.jquery.core.JDBCType.NUMERIC; +import static org.usf.jquery.core.JDBCType.REAL; import static org.usf.jquery.core.Utils.UNLIMITED; +import java.sql.Timestamp; + import org.usf.jquery.core.JDBCType; import lombok.AccessLevel; @@ -24,10 +32,30 @@ public final class ColumnMetadata { private final String columnName; private JDBCType dataType = null; private int dataSize = UNLIMITED; + private Integer precision = UNLIMITED; ColumnMetadata reset() { - this.dataType = null; - this.dataSize = UNLIMITED; + this.dataType = null; + this.dataSize = UNLIMITED; + this.precision = UNLIMITED; return this; } + + public String toJavaType(){ + return dataType.type().getSimpleName(); + } + + public String toSqlType(){ + var s = dataType.name(); + if(dataType.type() == String.class) { + s+= "(" + (dataSize == MAX_VALUE ? "" : dataSize) + ")"; + } + if(dataType.type() == Timestamp.class) { + s+= "(" + precision + ")"; + } + if(dataType == REAL || dataType == NUMERIC || dataType == DECIMAL || dataType == FLOAT || dataType == DOUBLE) { + s+= "(" + dataSize + "," + precision + ")"; + } + return s; + } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java index 128b0926..bf860823 100644 --- a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java +++ b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java @@ -81,13 +81,14 @@ public void fetch() { static void logTableColumns(Map map) { if(!map.isEmpty()) { - var pattern = "|%-20s|%-40s|%-6s|%-12s|"; + var pattern = "|%-20s|%-15s|%-25s|%-20s|"; var bar = format(pattern, "", "", "", "").replace("|", "+").replace(" ", "-"); log.info(bar); - log.info(format(pattern, "TAGNAME", "NAME", "TYPE", "LENGTH")); + log.info(format(pattern, "ID", "CLASS", "COLUMN", "TYPE")); log.info(bar); map.entrySet().forEach(e-> - log.info(format(pattern, e.getKey(), e.getValue().getColumnName(), e.getValue().getDataType(), e.getValue().getDataSize()))); + log.info(format(pattern, e.getKey(), e.getValue().toJavaType(), + e.getValue().getColumnName(), e.getValue().toSqlType()))); log.info(bar); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index a5be3f1a..42948589 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -277,5 +277,10 @@ static class Triple { TaggableColumn buildColumn() { return td.column(cd); } + + @Override + public String toString() { + return td + "." + cd + " => " + entry.toString(); + } } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/TableMetadata.java b/src/main/java/org/usf/jquery/web/TableMetadata.java index f947ab52..330c182f 100644 --- a/src/main/java/org/usf/jquery/web/TableMetadata.java +++ b/src/main/java/org/usf/jquery/web/TableMetadata.java @@ -63,7 +63,8 @@ void fetch(DatabaseMetaData metadata) throws SQLException { if(nonNull(meta)) { meta.setDataType(fromDataType(rs.getInt("DATA_TYPE"))); meta.setDataSize(rs.getInt("COLUMN_SIZE")); - }// else undeclared column + meta.setPrecision(rs.getInt("DECIMAL_DIGITS")); + } // else undeclared column } while(rs.next()); } if(!dbMap.isEmpty()) { From 34c4781355c2a9a57a0666af74cd2fc0108f486c Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 9 Jan 2024 10:26:23 +0100 Subject: [PATCH 038/298] edit --- src/main/java/org/usf/jquery/web/ColumnMetadata.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index 114d90be..3c9597a9 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -47,8 +47,8 @@ public String toJavaType(){ public String toSqlType(){ var s = dataType.name(); - if(dataType.type() == String.class) { - s+= "(" + (dataSize == MAX_VALUE ? "" : dataSize) + ")"; + if(dataType.type() == String.class && dataSize < MAX_VALUE) { + s+= "(" + dataSize + ")"; } if(dataType.type() == Timestamp.class) { s+= "(" + precision + ")"; From 9eb82520b409dac7c0d6bbf179aff0bf91a058cb Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 9 Jan 2024 23:20:31 +0100 Subject: [PATCH 039/298] edit --- .../java/org/usf/jquery/core/Comparator.java | 18 +++++++++++++- .../java/org/usf/jquery/core/DBProcessor.java | 5 ++-- .../java/org/usf/jquery/core/Database.java | 22 +++++++++++++++++ .../org/usf/jquery/core/FunctionOperator.java | 4 +--- .../java/org/usf/jquery/core/JavaType.java | 4 +++- .../java/org/usf/jquery/core/Operator.java | 22 +++++++++-------- .../jquery/core/QueryParameterBuilder.java | 2 +- .../org/usf/jquery/core/RequestQuery.java | 2 +- .../usf/jquery/core/RequestQueryBuilder.java | 21 ++++++++++++++++ src/main/java/org/usf/jquery/core/Utils.java | 6 +++++ .../org/usf/jquery/web/ColumnMetadata.java | 4 ++-- .../java/org/usf/jquery/web/Constants.java | 5 ++-- .../org/usf/jquery/web/DatabaseMetadata.java | 7 +++++- .../org/usf/jquery/web/TableDecorator.java | 24 +++++++++++++++++++ 14 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/Database.java diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 4cc4e4d7..b1cdb12e 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -1,13 +1,29 @@ package org.usf.jquery.core; +import static java.util.Objects.nonNull; + /** * * @author u$f * */ @FunctionalInterface -public interface Comparator extends DBProcessor { +public interface Comparator extends DBProcessor { + @Override + default ColumnSingleFilter args(Object... args) { + if(nonNull(args) && args.length == 2) { + if(args[0] instanceof DBColumn) { + return new ColumnSingleFilter((DBColumn)args[0], + this.expression(args.length > 1 ? args[1] : null)); // no type + } + else { + throw new IllegalArgumentException(); //TODO msg + } + } + throw new IllegalArgumentException(); //TODO msg + } + default ComparisonExpression expression(Object right) { return new ComparisonSingleExpression(this, right); } diff --git a/src/main/java/org/usf/jquery/core/DBProcessor.java b/src/main/java/org/usf/jquery/core/DBProcessor.java index 68fd8a43..f48ab325 100644 --- a/src/main/java/org/usf/jquery/core/DBProcessor.java +++ b/src/main/java/org/usf/jquery/core/DBProcessor.java @@ -5,7 +5,8 @@ * @author u$f * */ -@FunctionalInterface -public interface DBProcessor extends DBObject { +public interface DBProcessor extends DBObject { + + T args(Object... args); } diff --git a/src/main/java/org/usf/jquery/core/Database.java b/src/main/java/org/usf/jquery/core/Database.java new file mode 100644 index 00000000..a75dfccc --- /dev/null +++ b/src/main/java/org/usf/jquery/core/Database.java @@ -0,0 +1,22 @@ +package org.usf.jquery.core; + +import java.util.Optional; +import java.util.stream.Stream; + +/** + * + * @author u$f + * + */ +public enum Database { + + MYSQL, POSTGRESQL, ORACLE, SQLSERVER, TERADATA; + + public static Optional of(String name) { + var v = name.toUpperCase(); + return Stream.of(values()) + .filter(d-> v.contains(d.name())) + .findAny(); + } + +} diff --git a/src/main/java/org/usf/jquery/core/FunctionOperator.java b/src/main/java/org/usf/jquery/core/FunctionOperator.java index 0e263c97..15e29cc5 100644 --- a/src/main/java/org/usf/jquery/core/FunctionOperator.java +++ b/src/main/java/org/usf/jquery/core/FunctionOperator.java @@ -12,9 +12,7 @@ public interface FunctionOperator extends Operator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { return new SqlStringBuilder(id()) - .append("(") - .append(builder.appendLitteralArray(args)) //accept any - .append(")") + .append("(").append(builder.appendLitteralArray(args)).append(")")//accept any .toString(); } } diff --git a/src/main/java/org/usf/jquery/core/JavaType.java b/src/main/java/org/usf/jquery/core/JavaType.java index bb985691..561873e2 100644 --- a/src/main/java/org/usf/jquery/core/JavaType.java +++ b/src/main/java/org/usf/jquery/core/JavaType.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static java.util.Objects.isNull; + /** * * @author u$f @@ -15,6 +17,6 @@ default String name() { } default boolean accept(Object o) { - return o == null || type().isInstance(o); + return isNull(o) || type().isInstance(o); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index b369ed07..3588c4cf 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -2,6 +2,7 @@ import static java.lang.reflect.Modifier.isStatic; import static java.util.Optional.empty; +import static org.usf.jquery.core.Database.TERADATA; import static org.usf.jquery.core.JDBCType.BIGINT; import static org.usf.jquery.core.JDBCType.DATE; import static org.usf.jquery.core.JDBCType.DOUBLE; @@ -17,7 +18,8 @@ import static org.usf.jquery.core.Parameter.optional; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; -import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.core.QueryParameterBuilder.formatValue; +import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; import static org.usf.jquery.core.Validation.requireNArgs; @@ -30,14 +32,14 @@ * @author u$f * */ -public interface Operator extends DBProcessor, NestedSql { +public interface Operator extends DBProcessor, NestedSql { static final Operator VALUE_RETURN = new Operator() { @Override public String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(1, args, this::id); - return args[0] instanceof Number ? args[0].toString() : quote(args[0].toString()); + return formatValue(args[0]); } @Override @@ -155,11 +157,8 @@ static TypedOperator right() { } static TypedOperator replace() { - return new TypedOperator(VARCHAR, function("REPLACE"), required(VARCHAR), required(VARCHAR), required(VARCHAR)); //!teradata - } - - static TypedOperator oreplace() { - return new TypedOperator(VARCHAR, function("OREPLACE"), required(VARCHAR), required(VARCHAR), required(VARCHAR)); //teradata + var id = currentDatabase() == TERADATA ? "OREPLACE" : "REPLACE"; + return new TypedOperator(VARCHAR, function(id), required(VARCHAR), required(VARCHAR), required(VARCHAR)); //!teradata } static TypedOperator substring() { //int start, int length @@ -181,6 +180,7 @@ static TypedOperator month() { } static TypedOperator week() { + //teradata return new TypedOperator(INTEGER, extract("WEEK"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } @@ -189,11 +189,13 @@ static TypedOperator day() { } static TypedOperator dow() { - return new TypedOperator(INTEGER, extract("DOW"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata + var fn = currentDatabase() == TERADATA ? function("td_day_of_week") : extract("DOW"); + return new TypedOperator(INTEGER, fn, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata } static TypedOperator doy() { - return new TypedOperator(INTEGER, extract("DOY"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata + var fn = currentDatabase() == TERADATA ? function("td_day_of_year") : extract("DOY"); + return new TypedOperator(INTEGER, fn, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata } static TypedOperator hour() { diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index eb68e739..634e26cb 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -111,7 +111,7 @@ static String nParameter(int n){ return n == 1 ? ARG : ARG + (COMA + ARG).repeat(n-1); } - static String formatValue(Object o) { + public static String formatValue(Object o) { if(nonNull(o)){ return o instanceof Number ? o.toString() diff --git a/src/main/java/org/usf/jquery/core/RequestQuery.java b/src/main/java/org/usf/jquery/core/RequestQuery.java index bd66ff37..f70b56c0 100644 --- a/src/main/java/org/usf/jquery/core/RequestQuery.java +++ b/src/main/java/org/usf/jquery/core/RequestQuery.java @@ -49,7 +49,7 @@ public T execute(DataSource ds, ResultSetMapper mapper) { // overload wit } } } - catch(SQLException e) { + catch(SQLException e) { // rethrow exception throw new MappingException("error while mapping results", e); } } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index b4fedfd2..d0e7f2bc 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -2,6 +2,7 @@ import static java.lang.System.currentTimeMillis; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static java.util.function.Predicate.not; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; @@ -33,6 +34,8 @@ public class RequestQueryBuilder { private final List orders = new LinkedList<>(); private Iterator it; private boolean distinct; + private Integer fetch; + private Integer offset; public RequestQueryBuilder distinct() { distinct = true; @@ -53,6 +56,14 @@ public RequestQueryBuilder orders(@NonNull DBOrder... orders) { Stream.of(orders).forEach(this.orders::add); return this; } + + // Use LIMIT & OFFSET clauses to limit the number of rows returned by a query. + // LIMIT & OFFSET is not SQL standard. + public RequestQueryBuilder fetch(Integer offset, Integer fetch) { + this.offset = offset; + this.fetch = fetch; + return this; + } public RequestQueryBuilder repeat(@NonNull Iterator it) { this.it = it; @@ -86,6 +97,7 @@ public final void build(SqlStringBuilder sb, QueryParameterBuilder pb, String sc groupBy(sb); having(sb, pb); orderBy(sb, pb); + fetch(sb); sb.sb.insert(0, select(pb, schema)); //declare all view before FROM } @@ -141,6 +153,15 @@ void orderBy(SqlStringBuilder sb, QueryParameterBuilder pb) { } } + void fetch(SqlStringBuilder sb) { + if(nonNull(offset)) { + sb.append(" OFFSET ").append(offset.toString()).append(" ROWS"); + } + if(nonNull(fetch)) { + sb.append(" FETCH NEXT ").append(fetch.toString()).append(" ROWS ONLY"); + } + } + public boolean isAggregation() { return columns.stream().anyMatch(DBColumn::isAggregation) || filters.stream().anyMatch(DBFilter::isAggregation); diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 78cd03f9..43a887c8 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -15,6 +15,8 @@ */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class Utils { + + static ThreadLocal context = new ThreadLocal(); public static final int UNLIMITED = -1; @@ -33,4 +35,8 @@ public static boolean isEmpty(Collection c) { public static boolean isBlank(String s) { return isNull(s) || s.isBlank(); } + + public static Database currentDatabase() { + return context.get(); + } } diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index 3c9597a9..87ec4203 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -23,16 +23,16 @@ * @author u$f * */ -@ToString @Getter @Setter(value = AccessLevel.PACKAGE) +@ToString @RequiredArgsConstructor public final class ColumnMetadata { private final String columnName; private JDBCType dataType = null; private int dataSize = UNLIMITED; - private Integer precision = UNLIMITED; + private int precision = UNLIMITED; ColumnMetadata reset() { this.dataType = null; diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index 60a6a1ec..7fa68823 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -17,12 +17,13 @@ public final class Constants { public static final String COLUMN = "column"; public static final String COLUMN_DISTINCT = "column.distinct"; public static final String ORDER = "order"; + public static final String FETCH = "fetch"; + public static final String OFFSET = "offset"; public static final String REVISION = "revision"; //not standard public static final String REVISION_MODE = "revision.mode"; //not standard - public static final String PARTITION = "partition"; //not res static final Set RESERVED_WORDS = - Set.of(COLUMN, COLUMN_DISTINCT, ORDER, REVISION, REVISION_MODE); //metadata ? + Set.of(COLUMN, COLUMN_DISTINCT, ORDER, OFFSET, FETCH, REVISION, REVISION_MODE); //metadata ? static final YearMonth[] EMPTY_REVISION = new YearMonth[0]; //not standard diff --git a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java index bf860823..f34469f0 100644 --- a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java +++ b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java @@ -23,6 +23,8 @@ import javax.sql.DataSource; +import org.usf.jquery.core.Database; + import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -39,11 +41,13 @@ public final class DatabaseMetadata { private final Object mutex = new Object(); - @Getter(AccessLevel.PACKAGE) + @Getter(AccessLevel.PACKAGE) private final DataSource dataSource; //nullable if no sync private final Map tables; //empty if no sync @Getter private Instant lastUpdate; + @Getter + private Database type; public Optional tableMetada(TableDecorator td){ return ofNullable(tables.get(td.identity())); @@ -59,6 +63,7 @@ public void fetch() { log.info("Scanning database metadata..."); try(var cn = dataSource.getConnection()){ var metadata = cn.getMetaData(); + type = Database.of(metadata.getDatabaseProductName()).orElse(null); for(var t : tables.values()) { log.info("Scanning table '{}' metadata...", t.getTablename()); t.fetch(metadata); diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index cb593dbf..698c0dcc 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -1,11 +1,14 @@ package org.usf.jquery.web; +import static java.lang.Integer.parseInt; import static java.util.Objects.nonNull; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; +import static org.usf.jquery.web.Constants.FETCH; +import static org.usf.jquery.web.Constants.OFFSET; import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.RESERVED_WORDS; import static org.usf.jquery.web.JQueryContext.database; @@ -61,6 +64,7 @@ default RequestQueryBuilder query(Map parameterMap) { parseColumns(query, parameterMap); parseFilters(query, parameterMap); parseOrders (query, parameterMap); + parseFetch(query, parameterMap); return query; } @@ -99,6 +103,26 @@ default void parseOrders(RequestQueryBuilder query, Map parame .forEach(e-> query.orders(e.asOrder(this))); } } + + default void parseFetch(RequestQueryBuilder query, Map parameters) { + query.fetch(requirePositiveInt(OFFSET, parameters), + requirePositiveInt(FETCH, parameters)); + } + + private static Integer requirePositiveInt(String key, Map parameters) { + if(parameters.containsKey(key)) { + var values = parameters.get(key); + if(values.length == 1) { + var v = parseInt(values[0]); + if(v >= 0) { + return v; + } + throw new IllegalArgumentException("negative"); + } + throw new IllegalArgumentException("too many value"); + } + return null; + } default TableMetadata metadata() { return database().tableMetada(this) From 2e75d2b3fb7da0eb52d22c49f4a2c9ed64c7642f Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 10 Jan 2024 10:18:55 +0100 Subject: [PATCH 040/298] edit --- .../org/usf/jquery/core/RequestQueryBuilder.java | 15 +++++++++++---- src/main/java/org/usf/jquery/core/Utils.java | 8 ++++++-- .../java/org/usf/jquery/web/TableDecorator.java | 3 +++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index d0e7f2bc..ba0fb804 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -6,9 +6,11 @@ import static java.util.function.Predicate.not; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; +import static org.usf.jquery.core.Database.TERADATA; import static org.usf.jquery.core.LogicalOperator.AND; import static org.usf.jquery.core.QueryParameterBuilder.parametrized; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; +import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Validation.requireNonEmpty; import java.util.Iterator; @@ -154,11 +156,16 @@ void orderBy(SqlStringBuilder sb, QueryParameterBuilder pb) { } void fetch(SqlStringBuilder sb) { - if(nonNull(offset)) { - sb.append(" OFFSET ").append(offset.toString()).append(" ROWS"); + if(currentDatabase() == TERADATA) { + } - if(nonNull(fetch)) { - sb.append(" FETCH NEXT ").append(fetch.toString()).append(" ROWS ONLY"); + else { + if(nonNull(offset)) { + sb.append(" OFFSET ").append(offset.toString()).append(" ROWS"); + } + if(nonNull(fetch)) { + sb.append(" FETCH NEXT ").append(fetch.toString()).append(" ROWS ONLY"); + } } } diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 43a887c8..73e5572d 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -15,8 +15,8 @@ */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class Utils { - - static ThreadLocal context = new ThreadLocal(); + //move this + static ThreadLocal context = new ThreadLocal<>(); public static final int UNLIMITED = -1; @@ -39,4 +39,8 @@ public static boolean isBlank(String s) { public static Database currentDatabase() { return context.get(); } + + public static void currentDatabase(Database db) { + context.set(db); + } } diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index 698c0dcc..02d44e83 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -3,6 +3,7 @@ import static java.lang.Integer.parseInt; import static java.util.Objects.nonNull; import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.web.Constants.COLUMN; @@ -29,6 +30,7 @@ import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TaggableView; +import org.usf.jquery.core.Utils; import org.usf.jquery.core.ViewColumn; /** @@ -60,6 +62,7 @@ default TaggableColumn column(ColumnDecorator cd) { } default RequestQueryBuilder query(Map parameterMap) { + currentDatabase(database().getType()); //table database var query = new RequestQueryBuilder(); parseColumns(query, parameterMap); parseFilters(query, parameterMap); From ec5c947578e5da2f8ee56a79f21e883b7d773e52 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 11 Jan 2024 15:28:31 +0100 Subject: [PATCH 041/298] edit --- .../org/usf/jquery/core/OperationColumn.java | 3 +- .../java/org/usf/jquery/core/Operator.java | 33 ++++++++++++++----- .../org/usf/jquery/core/RequestQuery.java | 14 ++++++-- .../usf/jquery/core/RequestQueryBuilder.java | 24 +++++++++----- .../usf/jquery/core/StandaloneFunction.java | 10 ------ .../org/usf/jquery/core/StringComparator.java | 2 +- .../org/usf/jquery/core/TypedOperator.java | 8 ++--- .../org/usf/jquery/web/ColumnDecorator.java | 8 ++--- .../org/usf/jquery/web/RequestEntryChain.java | 2 +- .../org/usf/jquery/web/RequestParser.java | 2 +- .../org/usf/jquery/web/TableDecorator.java | 2 +- 11 files changed, 66 insertions(+), 42 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/core/StandaloneFunction.java diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 5cbd717b..e0914ac1 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -2,6 +2,7 @@ import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import java.util.Objects; import java.util.stream.Stream; import lombok.AccessLevel; @@ -38,7 +39,7 @@ public JavaType javaType() { @Override public boolean isAggregation() { - if(aggregation == null) { + if(Objects.isNull(aggregation)) { return operator.isAggregation() || Stream.of(args).anyMatch(NestedSql::aggregation); } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 3588c4cf..8cddb1d5 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -7,6 +7,7 @@ import static org.usf.jquery.core.JDBCType.DATE; import static org.usf.jquery.core.JDBCType.DOUBLE; import static org.usf.jquery.core.JDBCType.INTEGER; +import static org.usf.jquery.core.JDBCType.TIME; import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; import static org.usf.jquery.core.JDBCType.VARCHAR; @@ -34,6 +35,7 @@ */ public interface Operator extends DBProcessor, NestedSql { + @Deprecated(forRemoval = true) static final Operator VALUE_RETURN = new Operator() { @Override @@ -180,8 +182,8 @@ static TypedOperator month() { } static TypedOperator week() { - //teradata - return new TypedOperator(INTEGER, extract("WEEK"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + var fn = currentDatabase() == TERADATA ? function("td_week_of_year") : extract("WEEK");//teradata 1st week index = 0 + return new TypedOperator(INTEGER, fn, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } static TypedOperator day() { @@ -190,24 +192,24 @@ static TypedOperator day() { static TypedOperator dow() { var fn = currentDatabase() == TERADATA ? function("td_day_of_week") : extract("DOW"); - return new TypedOperator(INTEGER, fn, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata + return new TypedOperator(INTEGER, fn, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } static TypedOperator doy() { var fn = currentDatabase() == TERADATA ? function("td_day_of_year") : extract("DOY"); - return new TypedOperator(INTEGER, fn, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata + return new TypedOperator(INTEGER, fn, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } static TypedOperator hour() { - return new TypedOperator(INTEGER, extract("HOUR"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + return new TypedOperator(INTEGER, extract("HOUR"), required(TIME, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } static TypedOperator minute() { - return new TypedOperator(INTEGER, extract("MINUTE"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + return new TypedOperator(INTEGER, extract("MINUTE"), required(TIME, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } static TypedOperator second() { - return new TypedOperator(INTEGER, extract("SECOND"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + return new TypedOperator(INTEGER, extract("SECOND"), required(TIME, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } static TypedOperator epoch() { @@ -305,7 +307,22 @@ static TypedOperator partition() { static TypedOperator order() { return new TypedOperator(CLAUSE, clause("ORDER BY"), required(ORDER), varargs(ORDER)); } + + // constant operators + + static TypedOperator cdate() { + return new TypedOperator(DATE, constant("CURRENT_DATE")); + } + + static TypedOperator ctime() { + return new TypedOperator(TIME, constant("CURRENT_TIME")); + } + + static TypedOperator ctimestamp() { + return new TypedOperator(TIMESTAMP_WITH_TIMEZONE, constant("CURRENT_TIMESTAMP")); + } + @Deprecated(forRemoval = true) static TypedOperator value() { return new TypedOperator(firstArgType(), VALUE_RETURN, required()); } @@ -342,7 +359,7 @@ static ClauseFunction clause(String name) { return ()-> name; } - static StandaloneFunction constant(String name) { + static ConstantOperator constant(String name) { return ()-> name; } diff --git a/src/main/java/org/usf/jquery/core/RequestQuery.java b/src/main/java/org/usf/jquery/core/RequestQuery.java index f70b56c0..5affd58d 100644 --- a/src/main/java/org/usf/jquery/core/RequestQuery.java +++ b/src/main/java/org/usf/jquery/core/RequestQuery.java @@ -1,10 +1,13 @@ package org.usf.jquery.core; import static java.lang.System.currentTimeMillis; +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import java.sql.SQLException; import java.util.Arrays; import java.util.List; +import java.util.Objects; import javax.sql.DataSource; @@ -35,9 +38,14 @@ public T execute(DataSource ds, ResultSetMapper mapper) { // overload wit try(var cn = ds.getConnection()){ log.debug("preparing statement : {}", query); try(var ps = cn.prepareStatement(query)){ - if(params != null) { + if(nonNull(params)) { for(var i=0; i T execute(DataSource ds, ResultSetMapper mapper) { // overload wit } } } - catch(SQLException e) { // rethrow exception + catch(SQLException e) { // re-throw SQLException throw new MappingException("error while mapping results", e); } } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index ba0fb804..d35a2345 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -10,12 +10,14 @@ import static org.usf.jquery.core.LogicalOperator.AND; import static org.usf.jquery.core.QueryParameterBuilder.parametrized; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; +import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Validation.requireNonEmpty; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.stream.Stream; import lombok.Getter; @@ -59,8 +61,7 @@ public RequestQueryBuilder orders(@NonNull DBOrder... orders) { return this; } - // Use LIMIT & OFFSET clauses to limit the number of rows returned by a query. - // LIMIT & OFFSET is not SQL standard. + // the LIMIT clause is not in SQL standard. public RequestQueryBuilder fetch(Integer offset, Integer fetch) { this.offset = offset; this.fetch = fetch; @@ -105,8 +106,18 @@ public final void build(SqlStringBuilder sb, QueryParameterBuilder pb, String sc @Deprecated String select(QueryParameterBuilder pb, String schema){ - return new SqlStringBuilder(100).append("SELECT ") - .appendIf(distinct, ()-> "DISTINCT ") + if(currentDatabase() == TERADATA) { + if(nonNull(offset)) { + throw new UnsupportedOperationException(""); + } + if(distinct && nonNull(fetch)) { + throw new UnsupportedOperationException("Top N option is not supported with DISTINCT option."); + } + } + return new SqlStringBuilder(100).append("SELECT") + .appendIf(distinct, ()-> " DISTINCT") + .appendIf(nonNull(fetch), ()-> " TOP " + fetch) + .append(SPACE) .appendEach(columns, SCOMA, o-> o.sqlWithTag(pb)) .appendIf(!pb.views().isEmpty(), " FROM ") //TODO finish this .appendEach(pb.views(), SCOMA, o-> o.sqlWithTag(pb, schema)).toString(); @@ -156,10 +167,7 @@ void orderBy(SqlStringBuilder sb, QueryParameterBuilder pb) { } void fetch(SqlStringBuilder sb) { - if(currentDatabase() == TERADATA) { - - } - else { + if(currentDatabase() != TERADATA) { // TOP n if(nonNull(offset)) { sb.append(" OFFSET ").append(offset.toString()).append(" ROWS"); } diff --git a/src/main/java/org/usf/jquery/core/StandaloneFunction.java b/src/main/java/org/usf/jquery/core/StandaloneFunction.java deleted file mode 100644 index 802eb942..00000000 --- a/src/main/java/org/usf/jquery/core/StandaloneFunction.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.usf.jquery.core; - -/** - * - * @author u$f - * - */ -public interface StandaloneFunction extends FunctionOperator { - -} diff --git a/src/main/java/org/usf/jquery/core/StringComparator.java b/src/main/java/org/usf/jquery/core/StringComparator.java index d30984fd..279f53ce 100644 --- a/src/main/java/org/usf/jquery/core/StringComparator.java +++ b/src/main/java/org/usf/jquery/core/StringComparator.java @@ -15,7 +15,7 @@ public interface StringComparator extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { - requireNArgs(2, args, String.class::getSimpleName); + requireNArgs(2, args, StringComparator.class::getSimpleName); return builder.appendLitteral(args[0]) + space(name()) + builder.appendParameter(args[1]); } } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 8515c090..c6f7a87f 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -49,13 +49,13 @@ public OperationColumn args(Object... args) { var i=0; for(; i 0 && parameters[parameters.length-1].isVarargs(); } - private static IllegalArgumentException illegalArgumentException() { - return new IllegalArgumentException("mismatch arg type"); + private static IllegalArgumentException argumentTypeMismatch() { + return new IllegalArgumentException("argument type mismatch"); } } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 50c46c54..943cfb13 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -77,15 +77,15 @@ default Comparator comparator(String comparator, int nArg) { case "lt" : return lessThan(); case "le" : return lessOrEqual(); case "not" : return nArg == 1 ? notEqual() : notIn(); - case "like" : return containsArgPartten(like()); - case "ilike" : return containsArgPartten(iLike()); - case "unlike" : return containsArgPartten(notLike()); + case "like" : return wildcards(like()); + case "ilike" : return wildcards(iLike()); + case "unlike" : return wildcards(notLike()); default : return null; //isnull } } - private static Comparator containsArgPartten(StringComparator fn) { + private static Comparator wildcards(StringComparator fn) { return (b, args)-> { args[1] = "%" + args[1] + "%"; return fn.sql(b, args); diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 42948589..bfb35c21 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -264,7 +264,7 @@ static ParseException cannotEvaluateException(String type, RequestEntryChain ent } static String[] toStringArray(List entries) { - return entries.stream().map(RequestEntryChain::toString).toArray(String[]::new); + return entries.stream().map(e-> isNull(e.value) ? null : e.toString()).toArray(String[]::new); } @RequiredArgsConstructor diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index f4132676..1c4716a6 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -41,7 +41,7 @@ private List parseEntries(boolean multiple, boolean argument) var entries = new LinkedList(); entries.add(parseEntry(multiple, argument)); while(c == ',') { - nextChar(true); + nextChar(!argument); entries.add(parseEntry(multiple, argument)); } return entries.size() == 1 && isNull(entries.get(0).getValue()) //avoid () => (null) diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index 02d44e83..611a3147 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -120,7 +120,7 @@ private static Integer requirePositiveInt(String key, Map para if(v >= 0) { return v; } - throw new IllegalArgumentException("negative"); + throw new IllegalArgumentException(key + " cannot be negative"); } throw new IllegalArgumentException("too many value"); } From 85beb01b0ee0b5c683a9bfb26701e0501b2efb7c Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 11 Jan 2024 15:30:02 +0100 Subject: [PATCH 042/298] edit --- .../org/usf/jquery/core/ConstantOperator.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/org/usf/jquery/core/ConstantOperator.java diff --git a/src/main/java/org/usf/jquery/core/ConstantOperator.java b/src/main/java/org/usf/jquery/core/ConstantOperator.java new file mode 100644 index 00000000..1a0f56a6 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/ConstantOperator.java @@ -0,0 +1,19 @@ +package org.usf.jquery.core; + +import static org.usf.jquery.core.Validation.requireNoArgs; + +/** + * + * @author u$f + * + */ +@FunctionalInterface +public interface ConstantOperator extends Operator { + + @Override + default String sql(QueryParameterBuilder builder, Object[] args) { + requireNoArgs(args, ConstantOperator.class::getSimpleName); + return id(); + } + +} From dac7d76e5936c641f2d990670f866653c51340e1 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 12 Jan 2024 13:59:28 +0100 Subject: [PATCH 043/298] edit --- .../java/org/usf/jquery/core/Comparator.java | 58 ++++++------- .../java/org/usf/jquery/core/Parameter.java | 3 + .../org/usf/jquery/core/ParameterSet.java | 83 +++++++++++++++++++ .../org/usf/jquery/core/TypedComparator.java | 38 +++++++++ .../org/usf/jquery/core/TypedOperator.java | 51 ++---------- 5 files changed, 161 insertions(+), 72 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/ParameterSet.java create mode 100644 src/main/java/org/usf/jquery/core/TypedComparator.java diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index b1cdb12e..fa8b507b 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -1,6 +1,8 @@ package org.usf.jquery.core; import static java.util.Objects.nonNull; +import static org.usf.jquery.core.Parameter.required; +import static org.usf.jquery.core.Parameter.varargs; /** * @@ -28,60 +30,60 @@ default ComparisonExpression expression(Object right) { return new ComparisonSingleExpression(this, right); } - static BasicComparator equal() { - return basicComparator("="); + static TypedComparator equal() { //eq + return new TypedComparator(basicComparator("="), required(), required()); } - static BasicComparator notEqual() { - return basicComparator("<>"); + static TypedComparator notEqual() { //ne + return new TypedComparator(basicComparator("<>"), required(), required()); } - static BasicComparator lessThan() { - return basicComparator("<"); + static TypedComparator lessThan() { //lt + return new TypedComparator(basicComparator("<"), required(), required()); } - static BasicComparator lessOrEqual() { - return basicComparator("<="); + static TypedComparator lessOrEqual() { //le + return new TypedComparator(basicComparator("<="), required(), required()); } - static BasicComparator greaterThan() { - return basicComparator(">"); + static TypedComparator greaterThan() { //gt + return new TypedComparator(basicComparator(">"), required(), required()); } - static BasicComparator greaterOrEqual() { - return basicComparator(">="); + static TypedComparator greaterOrEqual() { //ge + return new TypedComparator(basicComparator(">="), required(), required()); } - static StringComparator like() { - return stringComparator("LIKE"); + static TypedComparator like() { + return new TypedComparator(stringComparator("LIKE"), required(), required()); } - static StringComparator iLike() { - return stringComparator("ILIKE"); + static TypedComparator iLike() { + return new TypedComparator(stringComparator("ILIKE"), required(), required()); } - static StringComparator notLike() { - return stringComparator("NOT LIKE"); + static TypedComparator notLike() { + return new TypedComparator(stringComparator("NOT LIKE"), required(), required()); } - static StringComparator notILike() { - return stringComparator("NOT ILIKE"); + static TypedComparator notILike() { + return new TypedComparator(stringComparator("NOT ILIKE"), required(), required()); } - static NullComparator isNull() { - return nullComparator("IS NULL"); + static TypedComparator isNull() { + return new TypedComparator(nullComparator("IS NULL")); } - static NullComparator isNotNull() { - return nullComparator("IS NOT NULL"); + static TypedComparator isNotNull() { + return new TypedComparator(nullComparator("IS NOT NULL")); } - static InCompartor in() { - return inComparator("IN"); + static TypedComparator in() { + return new TypedComparator(inComparator("IN"), required(), varargs()); } - static InCompartor notIn() { - return inComparator("NOT IN"); + static TypedComparator notIn() { + return new TypedComparator(inComparator("NOT IN"), required(), varargs()); } static BasicComparator basicComparator(final String name) { diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index db43bb62..9b66a4b3 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static java.lang.Math.min; +import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Utils.isEmpty; @@ -58,4 +60,5 @@ public static Parameter[] checkParams(Parameter... parameters) { } return parameters; } + } diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java new file mode 100644 index 00000000..097abbae --- /dev/null +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -0,0 +1,83 @@ +package org.usf.jquery.core; + +import static java.lang.Math.min; +import static java.util.Objects.isNull; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + +/** + * + * @author u$f + * + */ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class ParameterSet { + + private static final Parameter[] NO_PARAM = new Parameter[0]; + + private final Parameter[] parameters; + + public Object[] match(Object... args) { + if(isNull(args)) { + args = new Object[0]; + } + var na = args.length; + var rq = requireArgCount(); + if(na >= rq && (na <= parameters.length || isVarags())) { + var i=0; + for(; i 0 && parameters[parameters.length-1].isVarargs(); + } + + public static ParameterSet ofParameters(Parameter... parameters) { + if(isNull(parameters)) { + parameters = NO_PARAM; + } + else { + var i=0; + while(i typeFn; - private final Parameter[] parameters; + private final ParameterSet parameterSet; public TypedOperator(JavaType type, Operator function, Parameter... args) { - this(o-> type, function, isNull(args) ? NO_PARAM : args); + this(o-> type, function, args); } public TypedOperator(Function typeFn, Operator function, Parameter... parameter) { - this.typeFn = typeFn; this.operator = function; - this.parameters = checkParams(parameter); + this.typeFn = typeFn; + this.parameterSet = ofParameters(parameter); } public Operator unwrap() { @@ -40,44 +36,11 @@ public Operator unwrap() { @Override public OperationColumn args(Object... args) { - if(isNull(args)) { - args = new Object[0]; - } - var na = args.length; - var rq = requireArgCount(); - if(na >= rq && (na <= parameters.length || isVarags())) { - var i=0; - for(; i 0 && parameters[parameters.length-1].isVarargs(); - } - - private static IllegalArgumentException argumentTypeMismatch() { - return new IllegalArgumentException("argument type mismatch"); - } } From ae13965f828f99e7dbaf83827f895d16518a0d4a Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 15 Jan 2024 14:54:51 +0100 Subject: [PATCH 044/298] edit --- .../org/usf/jquery/core/BasicComparator.java | 8 ++- .../java/org/usf/jquery/core/CaseColumn.java | 8 ++- .../java/org/usf/jquery/core/Comparator.java | 65 ++++++++++++++----- .../usf/jquery/core/ComparisonExpression.java | 14 ++-- .../java/org/usf/jquery/core/InCompartor.java | 13 ++-- .../java/org/usf/jquery/core/JDBCType.java | 22 +++++-- .../org/usf/jquery/core/NullComparator.java | 4 +- .../java/org/usf/jquery/core/Operator.java | 5 +- .../java/org/usf/jquery/core/Parameter.java | 2 - .../org/usf/jquery/core/ParameterSet.java | 21 +++--- .../jquery/core/QueryParameterBuilder.java | 53 ++++++++------- .../org/usf/jquery/core/RequestQuery.java | 18 ++--- .../usf/jquery/core/RequestQueryBuilder.java | 3 +- .../org/usf/jquery/core/StringComparator.java | 7 +- .../org/usf/jquery/core/TypedComparator.java | 11 +++- .../org/usf/jquery/core/TypedOperator.java | 8 +-- .../org/usf/jquery/web/ColumnDecorator.java | 32 ++++----- .../org/usf/jquery/web/RequestEntryChain.java | 17 ++--- .../org/usf/jquery/web/TableDecorator.java | 1 - .../org/usf/jquery/web/TableMetadata.java | 3 +- .../org/usf/jquery/web/YearTableMetadata.java | 3 +- 21 files changed, 193 insertions(+), 125 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/BasicComparator.java b/src/main/java/org/usf/jquery/core/BasicComparator.java index 668e6c1b..88dbc32d 100644 --- a/src/main/java/org/usf/jquery/core/BasicComparator.java +++ b/src/main/java/org/usf/jquery/core/BasicComparator.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.Validation.requireNArgs; /** @@ -9,12 +10,13 @@ */ @FunctionalInterface public interface BasicComparator extends Comparator { - - String symbol(); @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, BasicComparator.class::getSimpleName); - return builder.appendLitteral(args[0]) + symbol() + builder.appendParameter(args[1]); + var type = typeOf(args[0]) + .or(()-> typeOf(args[1])) + .orElseThrow(Comparator::typeCannotBeNullException); // null 'cmp' null + return builder.appendLitteral(args[0]) + id() + builder.appendParameter(type, args[1]); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index f083c4c1..e41ad9ff 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -6,7 +6,7 @@ import java.util.Collection; import java.util.LinkedList; -import java.util.Objects; +import java.util.Optional; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -32,8 +32,10 @@ public String sql(QueryParameterBuilder builder) { public JavaType javaType() { return expressions.stream() .map(JDBCType::typeOf) - .filter(Objects::nonNull) // should have same type - .findAny().orElse(null); + .filter(Optional::isPresent) // should have same type + .findAny() + .orElseGet(Optional::empty) + .orElse(null); } public CaseColumn append(WhenExpression we) { diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index fa8b507b..2b6ada50 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -1,20 +1,28 @@ package org.usf.jquery.core; -import static java.util.Objects.nonNull; +import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; +import java.util.Objects; +import java.util.function.UnaryOperator; + /** * * @author u$f * */ -@FunctionalInterface public interface Comparator extends DBProcessor { + String id(); + + default boolean isVarargs() { + return false; + } + @Override default ColumnSingleFilter args(Object... args) { - if(nonNull(args) && args.length == 2) { + if(Objects.nonNull(args) && args.length >= 1 && args.length <= 2) { if(args[0] instanceof DBColumn) { return new ColumnSingleFilter((DBColumn)args[0], this.expression(args.length > 1 ? args[1] : null)); // no type @@ -30,52 +38,64 @@ default ComparisonExpression expression(Object right) { return new ComparisonSingleExpression(this, right); } - static TypedComparator equal() { //eq + static TypedComparator eq() { return new TypedComparator(basicComparator("="), required(), required()); } - static TypedComparator notEqual() { //ne + static TypedComparator ne() { return new TypedComparator(basicComparator("<>"), required(), required()); } - static TypedComparator lessThan() { //lt + static TypedComparator lt() { return new TypedComparator(basicComparator("<"), required(), required()); } - static TypedComparator lessOrEqual() { //le + static TypedComparator le() { return new TypedComparator(basicComparator("<="), required(), required()); } - static TypedComparator greaterThan() { //gt + static TypedComparator gt() { return new TypedComparator(basicComparator(">"), required(), required()); } - static TypedComparator greaterOrEqual() { //ge + static TypedComparator ge() { return new TypedComparator(basicComparator(">="), required(), required()); } + static TypedComparator startsLike() { + return like().argsMapper(wildcardSecondArg(o-> o + "%")); + } + + static TypedComparator endsLike() { + return like().argsMapper(wildcardSecondArg(o-> "%" + o)); + } + + static TypedComparator contentLike() { + return like().argsMapper(wildcardSecondArg(o-> "%" + o + "%")); + } + static TypedComparator like() { - return new TypedComparator(stringComparator("LIKE"), required(), required()); + return new TypedComparator(stringComparator("LIKE"), required(VARCHAR), required(VARCHAR)); } static TypedComparator iLike() { - return new TypedComparator(stringComparator("ILIKE"), required(), required()); + return new TypedComparator(stringComparator("ILIKE"), required(VARCHAR), required(VARCHAR)); } static TypedComparator notLike() { - return new TypedComparator(stringComparator("NOT LIKE"), required(), required()); + return new TypedComparator(stringComparator("NOT LIKE"), required(VARCHAR), required(VARCHAR)); } static TypedComparator notILike() { - return new TypedComparator(stringComparator("NOT ILIKE"), required(), required()); + return new TypedComparator(stringComparator("NOT ILIKE"), required(VARCHAR), required(VARCHAR)); } - + static TypedComparator isNull() { - return new TypedComparator(nullComparator("IS NULL")); + return new TypedComparator(nullComparator("IS NULL")); // takes no args } - static TypedComparator isNotNull() { - return new TypedComparator(nullComparator("IS NOT NULL")); + static TypedComparator nonNull() { + return new TypedComparator(nullComparator("IS NOT NULL")); // takes no args } static TypedComparator in() { @@ -101,4 +121,15 @@ static NullComparator nullComparator(final String name) { static InCompartor inComparator(final String name) { return ()-> name; } + + private static UnaryOperator wildcardSecondArg(UnaryOperator fn) { + return args->{ + args[1] = fn.apply(args[1]); + return args; + }; + } + + static IllegalArgumentException typeCannotBeNullException() { + return new IllegalArgumentException("type cannot be null"); + } } diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpression.java b/src/main/java/org/usf/jquery/core/ComparisonExpression.java index 094a5f26..a2230fd6 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpression.java @@ -32,27 +32,27 @@ default ComparisonExpression or(ComparisonExpression exp) { } static ComparisonExpression equal(Object right) { - return Comparator.equal().expression(right); + return Comparator.eq().expression(right); } static ComparisonExpression notEqual(Object right) { - return Comparator.notEqual().expression(right); + return Comparator.ne().expression(right); } static ComparisonExpression lessThan(Object right) { - return Comparator.lessThan().expression(right); + return Comparator.lt().expression(right); } static ComparisonExpression lessOrEqual(Object right) { - return Comparator.lessOrEqual().expression(right); + return Comparator.le().expression(right); } static ComparisonExpression greaterThan(Object right) { - return Comparator.greaterThan().expression(right); + return Comparator.gt().expression(right); } static ComparisonExpression greaterOrEqual(Object right) { - return Comparator.greaterOrEqual().expression(right); + return Comparator.ge().expression(right); } static ComparisonExpression like(Object right) { @@ -76,7 +76,7 @@ static ComparisonExpression isNull() { } static ComparisonExpression isNotNull() { - return Comparator.isNotNull().expression(null); + return Comparator.nonNull().expression(null); } @SuppressWarnings("unchecked") diff --git a/src/main/java/org/usf/jquery/core/InCompartor.java b/src/main/java/org/usf/jquery/core/InCompartor.java index fcbd8680..e2b7bab1 100644 --- a/src/main/java/org/usf/jquery/core/InCompartor.java +++ b/src/main/java/org/usf/jquery/core/InCompartor.java @@ -1,6 +1,7 @@ package org.usf.jquery.core; import static java.util.Arrays.copyOfRange; +import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.SqlStringBuilder.parenthese; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; @@ -13,12 +14,16 @@ @FunctionalInterface public interface InCompartor extends Comparator { - String name(); - @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireAtLeastNArgs(2, args, InCompartor.class::getSimpleName); - var params = copyOfRange(args, 1, args.length); - return builder.appendLitteral(args[0]) + SPACE + name() + parenthese(builder.appendArrayParameter(params)); + var type = typeOf(args[0]).orElseThrow(Comparator::typeCannotBeNullException); + var varg = copyOfRange(args, 1, args.length); + return builder.appendLitteral(args[0]) + SPACE + id() + parenthese(builder.appendArrayParameter(type, varg)); + } + + @Override + default boolean isVarargs() { + return true; } } diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index 2d1f9e69..7ab4b6dc 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -1,10 +1,18 @@ package org.usf.jquery.core; +import static java.util.Optional.empty; + +/** + * + * @author u$f + * + */ import java.math.BigDecimal; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; +import java.util.Optional; import java.util.function.Predicate; import lombok.Getter; @@ -101,25 +109,25 @@ private static boolean isString(Object o) { || o.getClass() == String.class; } - public static JDBCType typeOf(Object o) { + public static Optional typeOf(Object o) { if(o instanceof Typed) { var t = ((Typed) o).javaType(); - return t == null ? null : findType(t::equals); + return Optional.of(t).flatMap(v-> findType(v::equals)); } - return o == null ? null : findType(e-> e.type().isInstance(o)); + return Optional.of(o).flatMap(v-> findType(e-> e.type().isInstance(o))); } - public static JDBCType fromDataType(int value) { + public static Optional fromDataType(int value) { return findType(t-> t.value == value); } - public static JDBCType findType(Predicate predicate) { + public static Optional findType(Predicate predicate) { for(var t : values()) { if(predicate.test(t)) { - return t; + return Optional.of(t); } } - return OTHER; + return empty(); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/NullComparator.java b/src/main/java/org/usf/jquery/core/NullComparator.java index d65f4d72..bb7b8171 100644 --- a/src/main/java/org/usf/jquery/core/NullComparator.java +++ b/src/main/java/org/usf/jquery/core/NullComparator.java @@ -11,11 +11,9 @@ @FunctionalInterface public interface NullComparator extends Comparator { - String name(); - @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(1, args, NullComparator.class::getSimpleName); - return builder.appendLitteral(args[0]) + SPACE + name(); + return builder.appendLitteral(args[0]) + SPACE + id(); } } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 8cddb1d5..c64790f6 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -293,7 +293,7 @@ public OperationColumn args(Object... args) { @Override Object[] afterCheck(Object... args) { //map args after check var c = Stream.of(args).skip(1).toArray(OperationColumn[]::new); - return super.afterCheck(args[0], clauses(c)); + return new Object[] {args[0], clauses(c)}; } }; } @@ -378,6 +378,7 @@ static Optional lookupOperator(String op) { } private static Function firstArgType() { - return arr-> typeOf(requireAtLeastNArgs(1, arr, ()-> "firstArgType function")[0]); + return arr-> typeOf(requireAtLeastNArgs(1, arr, ()-> "firstArgType function")[0]) + .orElse(null); // not sure } } diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 9b66a4b3..abb231ce 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -1,7 +1,5 @@ package org.usf.jquery.core; -import static java.lang.Math.min; -import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Utils.isEmpty; diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index 097abbae..4d3f66e7 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -4,6 +4,7 @@ import static java.util.Objects.isNull; import lombok.AccessLevel; +import lombok.Getter; import lombok.RequiredArgsConstructor; /** @@ -11,6 +12,7 @@ * @author u$f * */ +@Getter @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class ParameterSet { @@ -23,7 +25,7 @@ public Object[] match(Object... args) { args = new Object[0]; } var na = args.length; - var rq = requireArgCount(); + var rq = requireParameterCount(); if(na >= rq && (na <= parameters.length || isVarags())) { var i=0; for(; i args; + private final List argTypes; private final List views; //indexed public List views(){ @@ -58,28 +59,16 @@ private String view(TaggableView view, IntConsumer consumer) { views.add(view); return vPrefix + views.size(); } - - public String appendParameter(Object o) { - if(dynamic()) { - return o instanceof DBObject - ? ((DBObject)o).sql(this, null) - : appendArg(o); - } - return appendLitteral(o); - } - - public String appendLitteral(Object o) { - return o instanceof DBObject - ? ((DBObject)o).sql(this, null) - : formatValue(o); - } - public String appendArrayParameter(@NonNull Object[] arr) { + public String appendArrayParameter(JDBCType type, @NonNull Object[] arr) { if(dynamic()) { if(isEmpty(arr)) { return EMPTY; } - Stream.of(arr).forEach(args::add); + for(var o : arr){ + argTypes.add(type); + args.add(o); + } return nParameter(arr.length); } return appendLitteralArray(arr); @@ -90,6 +79,22 @@ public String appendLitteralArray(@NonNull Object[] arr) { .map(this::appendLitteral) .collect(joining(SCOMA)); } + + public String appendParameter(JDBCType type, Object o) { + if(dynamic()) { + argTypes.add(type); + return o instanceof DBObject + ? ((DBObject)o).sql(this, null) + : appendArg(o); + } + return appendLitteral(o); + } + + public String appendLitteral(Object o) { //TD : stringify value using db default pattern + return o instanceof DBObject + ? ((DBObject)o).sql(this, null) + : formatValue(o); + } private String appendArg(Object o) { args.add(o); @@ -97,7 +102,11 @@ private String appendArg(Object o) { } public Object[] args() { - return dynamic() ? args.toArray() : new Object[0]; + return dynamic() ? args.toArray() : null; + } + + public int[] argTypes() { + return dynamic() ? argTypes.stream().mapToInt(JDBCType::getValue).toArray() : null; } private boolean dynamic() { @@ -121,18 +130,18 @@ public static String formatValue(Object o) { } public QueryParameterBuilder withValue() { - return new QueryParameterBuilder(vPrefix, null, views); + return new QueryParameterBuilder(vPrefix, null, null, views); } public static QueryParameterBuilder addWithValue() { - return new QueryParameterBuilder(null, null, null); //no args + return new QueryParameterBuilder(null, null, null, null); //no args } public static QueryParameterBuilder addWithValue(String prefix) { - return new QueryParameterBuilder(prefix, null, new LinkedList<>()); + return new QueryParameterBuilder(prefix, null, null, new LinkedList<>()); } public static QueryParameterBuilder parametrized() { - return new QueryParameterBuilder("v", new LinkedList<>(), new ArrayList<>()); + return new QueryParameterBuilder("v", new LinkedList<>(), new LinkedList<>(), new ArrayList<>()); } } diff --git a/src/main/java/org/usf/jquery/core/RequestQuery.java b/src/main/java/org/usf/jquery/core/RequestQuery.java index 5affd58d..2764f90d 100644 --- a/src/main/java/org/usf/jquery/core/RequestQuery.java +++ b/src/main/java/org/usf/jquery/core/RequestQuery.java @@ -2,12 +2,11 @@ import static java.lang.System.currentTimeMillis; import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; +import static org.usf.jquery.core.Utils.isEmpty; import java.sql.SQLException; import java.util.Arrays; import java.util.List; -import java.util.Objects; import javax.sql.DataSource; @@ -28,7 +27,8 @@ public final class RequestQuery { @NonNull private final String query; - private final Object[] params; + private final Object[] args; + private final int[] argTypes; public List execute(DataSource ds) { return execute(ds, new KeyValueMapper()); @@ -38,17 +38,17 @@ public T execute(DataSource ds, ResultSetMapper mapper) { // overload wit try(var cn = ds.getConnection()){ log.debug("preparing statement : {}", query); try(var ps = cn.prepareStatement(query)){ - if(nonNull(params)) { - for(var i=0; i build(sb, pb, schema)); } log.debug("query built in {} ms", currentTimeMillis() - bg); - return new RequestQuery(sb.toString(), pb.args()); + return new RequestQuery(sb.toString(), pb.args(), pb.argTypes()); } public final void build(SqlStringBuilder sb, QueryParameterBuilder pb, String schema){ diff --git a/src/main/java/org/usf/jquery/core/StringComparator.java b/src/main/java/org/usf/jquery/core/StringComparator.java index 279f53ce..7fbaa551 100644 --- a/src/main/java/org/usf/jquery/core/StringComparator.java +++ b/src/main/java/org/usf/jquery/core/StringComparator.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.SqlStringBuilder.space; import static org.usf.jquery.core.Validation.requireNArgs; @@ -11,11 +12,11 @@ @FunctionalInterface public interface StringComparator extends Comparator { - String name(); - @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, StringComparator.class::getSimpleName); - return builder.appendLitteral(args[0]) + space(name()) + builder.appendParameter(args[1]); + var type = typeOf(args[0]) + .orElseThrow(Comparator::typeCannotBeNullException); // null 'cmp' + return builder.appendLitteral(args[0]) + space(id()) + builder.appendParameter(type, args[1]); } } diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index f06a2557..79a5aca1 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -1,7 +1,10 @@ package org.usf.jquery.core; +import static java.util.function.UnaryOperator.identity; import static org.usf.jquery.core.ParameterSet.ofParameters; +import java.util.function.UnaryOperator; + import lombok.Getter; import lombok.experimental.Delegate; @@ -16,6 +19,7 @@ public final class TypedComparator implements Comparator { @Delegate private final Comparator comparator; private final ParameterSet parameterSet; + private UnaryOperator argMapper = identity(); public TypedComparator(Comparator comparator, Parameter... parameters) { this.comparator = comparator; @@ -25,14 +29,15 @@ public TypedComparator(Comparator comparator, Parameter... parameters) { @Override public ColumnSingleFilter args(Object... args) { args = parameterSet.match(args); - return comparator.args(afterCheck(args)); + return comparator.args(argMapper.apply(args)); } public Comparator unwrap() { return comparator; } - Object[] afterCheck(Object... args) { - return args; + TypedComparator argsMapper(UnaryOperator argMapper) { + this.argMapper = argMapper; + return this; } } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 92ee476d..6ea8d355 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -30,16 +30,16 @@ public TypedOperator(Function typeFn, Operator function, Par this.parameterSet = ofParameters(parameter); } - public Operator unwrap() { - return operator; - } - @Override public OperationColumn args(Object... args) { args = parameterSet.match(args); return new OperationColumn(operator, afterCheck(args), typeFn.apply(args)); } + public Operator unwrap() { + return operator; + } + Object[] afterCheck(Object... args) { return args; } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 943cfb13..fd7b7f12 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -1,15 +1,15 @@ package org.usf.jquery.web; import static java.util.Objects.isNull; -import static org.usf.jquery.core.Comparator.equal; -import static org.usf.jquery.core.Comparator.greaterOrEqual; -import static org.usf.jquery.core.Comparator.greaterThan; +import static org.usf.jquery.core.Comparator.eq; +import static org.usf.jquery.core.Comparator.ge; +import static org.usf.jquery.core.Comparator.gt; import static org.usf.jquery.core.Comparator.iLike; import static org.usf.jquery.core.Comparator.in; -import static org.usf.jquery.core.Comparator.lessOrEqual; -import static org.usf.jquery.core.Comparator.lessThan; +import static org.usf.jquery.core.Comparator.le; +import static org.usf.jquery.core.Comparator.lt; import static org.usf.jquery.core.Comparator.like; -import static org.usf.jquery.core.Comparator.notEqual; +import static org.usf.jquery.core.Comparator.ne; import static org.usf.jquery.core.Comparator.notIn; import static org.usf.jquery.core.Comparator.notLike; import static org.usf.jquery.web.ArgumentParsers.jdbcArgParser; @@ -69,28 +69,30 @@ default CriteriaBuilder criteria(String name) { default Comparator comparator(String comparator, int nArg) { if(isNull(comparator)) { - return nArg == 1 ? equal() : in(); + return nArg == 1 ? eq() : in(); } switch(comparator) { - case "gt" : return greaterThan(); - case "ge" : return greaterOrEqual(); - case "lt" : return lessThan(); - case "le" : return lessOrEqual(); - case "not" : return nArg == 1 ? notEqual() : notIn(); - case "like" : return wildcards(like()); - case "ilike" : return wildcards(iLike()); - case "unlike" : return wildcards(notLike()); + case "gt" : return gt(); + case "ge" : return ge(); + case "lt" : return lt(); + case "le" : return le(); + case "not" : return nArg == 1 ? ne() : notIn(); + case "like" : return like(); + case "ilike" : return iLike(); + case "unlike" : return notLike(); default : return null; //isnull } } + /* private static Comparator wildcards(StringComparator fn) { return (b, args)-> { args[1] = "%" + args[1] + "%"; return fn.sql(b, args); }; } + */ default ComparisonExpression expression(String exp, String... values) { return null; } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index bfb35c21..3bc32ad0 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -162,7 +162,7 @@ ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List= min && (op.isVarags() || np <= max)) { + } + var ps = op.getParameterSet(); + var min = ps.requireParameterCount(); + var max = ps.parameterCount(); + if(np >= min && (ps.isVarags() || np <= max)) { var params = new ArrayList(np); if(nonNull(col)) { params.add(col); @@ -210,10 +211,10 @@ private OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator var s = nonNull(col) ? 1 : 0; var i = s; for(; i Date: Mon, 15 Jan 2024 15:24:07 +0100 Subject: [PATCH 045/298] edit --- src/main/java/org/usf/jquery/core/JDBCType.java | 2 +- src/main/java/org/usf/jquery/core/WindowView.java | 15 ++++++++++++++- .../java/org/usf/jquery/web/ColumnDecorator.java | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index 7ab4b6dc..eaf5d271 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -112,7 +112,7 @@ private static boolean isString(Object o) { public static Optional typeOf(Object o) { if(o instanceof Typed) { var t = ((Typed) o).javaType(); - return Optional.of(t).flatMap(v-> findType(v::equals)); + return t instanceof JDBCType ? Optional.of((JDBCType) t) : empty(); } return Optional.of(o).flatMap(v-> findType(e-> e.type().isInstance(o))); } diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index 63224bad..fa784b2b 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -28,7 +28,20 @@ public String sql(QueryParameterBuilder builder, String schema) { //sub query sh } public DBFilter filter(ComparisonExpression expression) { - DBColumn col = b-> member(b.overwriteView(this), doubleQuote(column.tagname())); + var col = new DBColumn() { + @Override + public String sql(QueryParameterBuilder builder) { + return member(builder.overwriteView(WindowView.this), doubleQuote(column.tagname())); + } + @Override + public JavaType javaType() { + return column.javaType(); + } + @Override + public String toString() { + return sql(addWithValue()); + } + }; return col.filter(expression); } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index fd7b7f12..970ecf32 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -67,6 +67,7 @@ default CriteriaBuilder criteria(String name) { return null; // no criteria by default } + @Deprecated(forRemoval = true) default Comparator comparator(String comparator, int nArg) { if(isNull(comparator)) { return nArg == 1 ? eq() : in(); From 35054fba5c9fdeaa82fe0f2e7f974dafc866e442 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 15 Jan 2024 15:28:53 +0100 Subject: [PATCH 046/298] edit --- src/main/java/org/usf/jquery/core/NamedColumn.java | 5 +++++ src/main/java/org/usf/jquery/web/RequestEntryChain.java | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index 7c3ed0de..3a47634b 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -29,4 +29,9 @@ public NamedColumn as(String name) { // map public DBColumn unwrap() { return column; } + + @Override + public String toString() { + return column.toString(); + } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 3bc32ad0..222f1d80 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -22,7 +22,6 @@ import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBOrder; -import org.usf.jquery.core.InCompartor; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; import org.usf.jquery.core.Parameter; From 41715253d3b812f3265d58d505c975a48a3acab5 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 15 Jan 2024 15:55:01 +0100 Subject: [PATCH 047/298] edit --- src/main/java/org/usf/jquery/core/WindowView.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index fa784b2b..3ca2cb28 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -21,16 +21,15 @@ public final class WindowView implements TaggableView { public String sql(QueryParameterBuilder builder, String schema) { //sub query should not use main builder var b = addWithValue("w"); return new SqlStringBuilder(100) - .append("(SELECT ").append(member(b.view(view), "*")) - .append(", ").append(column.sqlWithTag(b)) + .append("(SELECT ").append(member(b.view(view), "*")).append(", ").append(column.sqlWithTag(b)) .append(" FROM ").append(view.sqlWithTag(b, schema)).append(")") .toString(); } public DBFilter filter(ComparisonExpression expression) { - var col = new DBColumn() { + return new DBColumn() { @Override - public String sql(QueryParameterBuilder builder) { + public String sql(QueryParameterBuilder builder) { //overwrite view return member(builder.overwriteView(WindowView.this), doubleQuote(column.tagname())); } @Override @@ -41,8 +40,7 @@ public JavaType javaType() { public String toString() { return sql(addWithValue()); } - }; - return col.filter(expression); + }.filter(expression); } @Override From 6f3b64a9d433e273a20674add7ecffcd6ab5a6b7 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 16 Jan 2024 00:57:14 +0100 Subject: [PATCH 048/298] edit --- .../java/org/usf/jquery/core/Comparator.java | 48 ++++-- .../java/org/usf/jquery/core/DBProcessor.java | 1 - .../java/org/usf/jquery/core/JqueryType.java | 3 +- .../java/org/usf/jquery/core/Operator.java | 14 +- .../org/usf/jquery/core/StringComparator.java | 3 +- .../org/usf/jquery/core/TypedComparator.java | 2 +- .../org/usf/jquery/core/TypedOperator.java | 12 +- .../java/org/usf/jquery/core/WindowView.java | 31 ++-- .../org/usf/jquery/web/ArgumentParsers.java | 1 + .../org/usf/jquery/web/ColumnDecorator.java | 66 ++------- .../org/usf/jquery/web/RequestEntryChain.java | 137 ++++++++++++++---- 11 files changed, 188 insertions(+), 130 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 2b6ada50..63234db7 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -1,10 +1,16 @@ package org.usf.jquery.core; +import static java.lang.reflect.Modifier.isStatic; +import static java.util.Arrays.copyOfRange; +import static java.util.Optional.empty; import static org.usf.jquery.core.JDBCType.VARCHAR; +import static org.usf.jquery.core.JqueryType.FILTER; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; +import java.util.Arrays; import java.util.Objects; +import java.util.Optional; import java.util.function.UnaryOperator; /** @@ -12,7 +18,7 @@ * @author u$f * */ -public interface Comparator extends DBProcessor { +public interface Comparator extends DBProcessor { String id(); @@ -21,17 +27,9 @@ default boolean isVarargs() { } @Override - default ColumnSingleFilter args(Object... args) { - if(Objects.nonNull(args) && args.length >= 1 && args.length <= 2) { - if(args[0] instanceof DBColumn) { - return new ColumnSingleFilter((DBColumn)args[0], - this.expression(args.length > 1 ? args[1] : null)); // no type - } - else { - throw new IllegalArgumentException(); //TODO msg - } - } - throw new IllegalArgumentException(); //TODO msg + default DBFilter args(Object... args) { + return new ColumnSingleFilter((DBColumn)args[0], + this.expression(copyOfRange(args, 1, args.length))); // no type } default ComparisonExpression expression(Object right) { @@ -106,6 +104,16 @@ static TypedComparator notIn() { return new TypedComparator(inComparator("NOT IN"), required(), varargs()); } + //pipe + + static TypedComparator and() { + return new TypedComparator(pipe("AND"), required(FILTER), required(FILTER)); + } + + static TypedComparator or() { + return new TypedComparator(pipe("OR"), required(FILTER), required(FILTER)); + } + static BasicComparator basicComparator(final String name) { return ()-> name; } @@ -122,8 +130,22 @@ static InCompartor inComparator(final String name) { return ()-> name; } + static ComparatorPipe pipe(final String name) { + return ()-> name; + } + + static Optional lookupComparator(String op) { + try { + var m = Comparator.class.getMethod(op); + if(isStatic(m.getModifiers()) && m.getReturnType() == TypedComparator.class && m.getParameterCount() == 0) { // no private static + return Optional.of((TypedComparator) m.invoke(null)); + } + } catch (Exception e) {/* do not throw exception */} + return empty(); + } + private static UnaryOperator wildcardSecondArg(UnaryOperator fn) { - return args->{ + return args-> { args[1] = fn.apply(args[1]); return args; }; diff --git a/src/main/java/org/usf/jquery/core/DBProcessor.java b/src/main/java/org/usf/jquery/core/DBProcessor.java index f48ab325..b8edf638 100644 --- a/src/main/java/org/usf/jquery/core/DBProcessor.java +++ b/src/main/java/org/usf/jquery/core/DBProcessor.java @@ -8,5 +8,4 @@ public interface DBProcessor extends DBObject { T args(Object... args); - } diff --git a/src/main/java/org/usf/jquery/core/JqueryType.java b/src/main/java/org/usf/jquery/core/JqueryType.java index 6b677249..bc75ea64 100644 --- a/src/main/java/org/usf/jquery/core/JqueryType.java +++ b/src/main/java/org/usf/jquery/core/JqueryType.java @@ -12,7 +12,8 @@ public enum JqueryType implements JavaType { COLUMN(DBColumn.class), ORDER(DBOrder.class), - CLAUSE(OperationColumn.class); + CLAUSE(OperationColumn.class), + FILTER(DBFilter.class); //expression, WHEN_THEN, ... private final Class type; diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index c64790f6..90789093 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -284,18 +284,14 @@ static TypedOperator denseRank() { static TypedOperator over() { return new TypedOperator(firstArgType(), pipe("OVER"), required(COLUMN), optional(CLAUSE), optional(CLAUSE)) { - @Override public OperationColumn args(Object... args) { return super.args(args).aggregation(false); //over aggregation functions } - - @Override - Object[] afterCheck(Object... args) { //map args after check - var c = Stream.of(args).skip(1).toArray(OperationColumn[]::new); - return new Object[] {args[0], clauses(c)}; - } - }; + }.argsMapper(args->{ //map args after check + var c = Stream.of(args).skip(1).toArray(OperationColumn[]::new); + return new Object[] {args[0], clauses(c)}; + }); } //clause functions @@ -370,7 +366,7 @@ static Optional lookupWindowFunction(String op) { static Optional lookupOperator(String op) { try { var m = Operator.class.getMethod(op); - if(isStatic(m.getModifiers()) && m.getReturnType() == TypedOperator.class) { // no private static + if(isStatic(m.getModifiers()) && m.getReturnType() == TypedOperator.class && m.getParameterCount() == 0) { // no private static return Optional.of((TypedOperator) m.invoke(null)); } } catch (Exception e) {/* do not throw exception */} diff --git a/src/main/java/org/usf/jquery/core/StringComparator.java b/src/main/java/org/usf/jquery/core/StringComparator.java index 7fbaa551..18c456f8 100644 --- a/src/main/java/org/usf/jquery/core/StringComparator.java +++ b/src/main/java/org/usf/jquery/core/StringComparator.java @@ -15,8 +15,7 @@ public interface StringComparator extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, StringComparator.class::getSimpleName); - var type = typeOf(args[0]) - .orElseThrow(Comparator::typeCannotBeNullException); // null 'cmp' + var type = typeOf(args[0]).orElseThrow(Comparator::typeCannotBeNullException); // null 'cmp' return builder.appendLitteral(args[0]) + space(id()) + builder.appendParameter(type, args[1]); } } diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 79a5aca1..3097f12d 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -27,7 +27,7 @@ public TypedComparator(Comparator comparator, Parameter... parameters) { } @Override - public ColumnSingleFilter args(Object... args) { + public DBFilter args(Object... args) { args = parameterSet.match(args); return comparator.args(argMapper.apply(args)); } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 6ea8d355..01a34eb6 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -1,8 +1,10 @@ package org.usf.jquery.core; +import static java.util.function.UnaryOperator.identity; import static org.usf.jquery.core.ParameterSet.ofParameters; import java.util.function.Function; +import java.util.function.UnaryOperator; import lombok.Getter; import lombok.experimental.Delegate; @@ -19,6 +21,7 @@ public class TypedOperator implements Operator { private final Operator operator; private final Function typeFn; private final ParameterSet parameterSet; + private UnaryOperator argMapper = identity(); public TypedOperator(JavaType type, Operator function, Parameter... args) { this(o-> type, function, args); @@ -33,14 +36,15 @@ public TypedOperator(Function typeFn, Operator function, Par @Override public OperationColumn args(Object... args) { args = parameterSet.match(args); - return new OperationColumn(operator, afterCheck(args), typeFn.apply(args)); + return new OperationColumn(operator, argMapper.apply(args), typeFn.apply(args)); } public Operator unwrap() { return operator; } - - Object[] afterCheck(Object... args) { - return args; + + TypedOperator argsMapper(UnaryOperator argMapper) { + this.argMapper = argMapper; + return this; } } diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index 3ca2cb28..afcc3d07 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -4,6 +4,7 @@ import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.SqlStringBuilder.member; +import lombok.AccessLevel; import lombok.RequiredArgsConstructor; /** @@ -11,7 +12,7 @@ * @author u$f * */ -@RequiredArgsConstructor +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class WindowView implements TaggableView { private final TaggableView view; @@ -26,11 +27,23 @@ public String sql(QueryParameterBuilder builder, String schema) { //sub query sh .toString(); } - public DBFilter filter(ComparisonExpression expression) { + @Override + public String tagname() { //inherits tagname + return view.tagname(); + } + + @Override + public String toString() { + return sql(addWithValue(), ""); + } + + + public static DBColumn windowColumn(TaggableView view, TaggableColumn column) { + var wv = new WindowView(view, column); return new DBColumn() { @Override public String sql(QueryParameterBuilder builder) { //overwrite view - return member(builder.overwriteView(WindowView.this), doubleQuote(column.tagname())); + return member(builder.overwriteView(wv), doubleQuote(column.tagname())); } @Override public JavaType javaType() { @@ -40,16 +53,6 @@ public JavaType javaType() { public String toString() { return sql(addWithValue()); } - }.filter(expression); - } - - @Override - public String tagname() { //inherits tagname - return view.tagname(); - } - - @Override - public String toString() { - return sql(addWithValue(), ""); + }; } } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index ea95a368..0d8e52bd 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -100,6 +100,7 @@ public static JavaArgumentParser jqueryArgParser(@NonNull JqueryType type) { switch (type) { case COLUMN: return RequestEntryChain::asColumn; case ORDER : return RequestEntryChain::asOrder; + case FILTER: return RequestEntryChain::asFilter; case CLAUSE: return RequestEntryChain::asOperation; default: throw unsupportedTypeException(type); } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 970ecf32..4fb1fbb1 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -1,23 +1,8 @@ package org.usf.jquery.web; -import static java.util.Objects.isNull; -import static org.usf.jquery.core.Comparator.eq; -import static org.usf.jquery.core.Comparator.ge; -import static org.usf.jquery.core.Comparator.gt; -import static org.usf.jquery.core.Comparator.iLike; -import static org.usf.jquery.core.Comparator.in; -import static org.usf.jquery.core.Comparator.le; -import static org.usf.jquery.core.Comparator.lt; -import static org.usf.jquery.core.Comparator.like; -import static org.usf.jquery.core.Comparator.ne; -import static org.usf.jquery.core.Comparator.notIn; -import static org.usf.jquery.core.Comparator.notLike; import static org.usf.jquery.web.ArgumentParsers.jdbcArgParser; -import org.usf.jquery.core.Comparator; -import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.JDBCType; -import org.usf.jquery.core.StringComparator; /** * @@ -40,13 +25,18 @@ default JDBCType dataType(TableDecorator td) { .orElse(null); } - /** - * override parser | format | local - */ - default JDBCArgumentParser parser(TableDecorator td){ + default JDBCArgumentParser parser(TableDecorator td){ // override parser | format | local return jdbcArgParser(dataType(td)); } + default ColumnBuilder builder() { + return null; // physical column by default + } + + default CriteriaBuilder criteria(String name) { + return null; // no criteria by default + } + default String pattern(TableDecorator td) { throw new UnsupportedOperationException(); //improve API security and performance } @@ -59,44 +49,6 @@ default boolean canFilter(TableDecorator td) { throw new UnsupportedOperationException(); //authorization inject } - default ColumnBuilder builder() { - return null; // physical column by default - } - - default CriteriaBuilder criteria(String name) { - return null; // no criteria by default - } - - @Deprecated(forRemoval = true) - default Comparator comparator(String comparator, int nArg) { - if(isNull(comparator)) { - return nArg == 1 ? eq() : in(); - } - switch(comparator) { - case "gt" : return gt(); - case "ge" : return ge(); - case "lt" : return lt(); - case "le" : return le(); - case "not" : return nArg == 1 ? ne() : notIn(); - case "like" : return like(); - case "ilike" : return iLike(); - case "unlike" : return notLike(); - default : return null; - //isnull - } - } - - /* - private static Comparator wildcards(StringComparator fn) { - return (b, args)-> { - args[1] = "%" + args[1] + "%"; - return fn.sql(b, args); - }; - } - */ - - default ComparisonExpression expression(String exp, String... values) { return null; } - static ColumnDecorator ofColumn(String ref, ColumnBuilder cb) { return new ColumnDecorator() { @Override diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 222f1d80..600615fc 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -5,34 +5,45 @@ import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.Comparator.eq; +import static org.usf.jquery.core.Comparator.in; +import static org.usf.jquery.core.Comparator.isNull; +import static org.usf.jquery.core.Comparator.lookupComparator; import static org.usf.jquery.core.Operator.lookupOperator; import static org.usf.jquery.core.Operator.lookupWindowFunction; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.core.WindowView.windowColumn; import static org.usf.jquery.web.ArgumentParsers.parse; import static org.usf.jquery.web.ColumnDecorator.ofColumn; -import static org.usf.jquery.web.CriteriaBuilder.ofComparator; import static org.usf.jquery.web.JQueryContext.context; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.stream.Stream; +import org.usf.jquery.core.Comparator; import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; +import org.usf.jquery.core.DBObject; import org.usf.jquery.core.DBOrder; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; import org.usf.jquery.core.Parameter; +import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.TypedComparator; import org.usf.jquery.core.TypedOperator; +import org.usf.jquery.core.Utils; import org.usf.jquery.core.WindowView; import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; +import lombok.val; @Getter @Setter(value = AccessLevel.PACKAGE) @@ -71,26 +82,75 @@ public TaggableColumn asColumn(TableDecorator td) { return oc == c ? c : oc.as(c.tagname()); } + public DBFilter asFilter(TableDecorator td) { + return asFilter(td, null); + } + public DBFilter asFilter(TableDecorator td, List values) { var t = lookupResource(td); var c = t.buildColumn(); var e = t.entry.next; - var p = t.entry; DBColumn oc = c; while(nonNull(e)) { var op = e.toOperation(td, oc); if(isNull(op)) { break; } - oc = op; //preserve last non null column - p = e; //preserve last operator entry + oc = "over".equals(e.value) + ? windowColumn(td.table(), op.as(c.tagname())) + : op; //preserve last non null column e = e.next; } - var exp = (isNull(e) ? new RequestEntryChain(null) : e) - .toComparison(td, c == oc ? t.cd : DEFAUL_COLUMN, values); - return "over".equals(p.value) - ? new WindowView(td.table(), oc.as(c.tagname())).filter(exp) - : oc.filter(exp); + if(isNull(e)) { + return isEmpty(values) + ? oc.isNull() + : defaultFilter(t.td, oc, requireNonNull(t.cd.parser(t.td)), values); + } + if(!isEmpty(values) && (e.next() || !isEmpty(e.args))) { + throw new IllegalArgumentException("illegal"); + } + if(isNull(e.args) && !isEmpty(values)) { + e.args = values; + } + var ftr = e.toComparison(td, oc); + if(nonNull(ftr)) { + if(e.next()) { + //values isEmpty + e = e.next; + while(nonNull(e)) { + ftr = e.toComparison(td, ftr); + e = e.next; + } + return ftr; + } + if(isNull(e.args)) { + var prs = requireNonNull(t.cd.parser(td)); + var arr = prs.parseAll(toStringArray(values)); + return oc.filter(in().expression(arr)); + } + //values isEmpty + return ftr; + } + else if(oc == c) { + return c.filter(e.toComparison(td, t.cd, values)); + } + else { + throw new IllegalArgumentException(); + } + } + + static DBFilter defaultFilter(TableDecorator td, DBColumn oc, JDBCArgumentParser parser, List values) { + if(values.size() > 1) { + return oc.in(parser.parseAll(toStringArray(values))); + } + Object o; + try { + o = values.get(0).asColumn(td); + } + catch(Exception e) { + o = parser.parse(values.get(0).toString()); + } + return oc.equal(o); } public DBOrder asOrder(TableDecorator td) { @@ -138,6 +198,19 @@ OperationColumn toOperation(TableDecorator td, DBColumn col) { return res.isEmpty() ? null : fillArgs(td, col, res.get()); } + + DBFilter toComparison2(TableDecorator td, DBColumn col) { + var f = toComparison(td, col); + if(nonNull(f)) { + } + return null; + } + + DBFilter toComparison(TableDecorator td, DBObject col) { + var res = lookupComparator(value); + return res.isEmpty() ? null : fillArgs(td, col, res.get()); + } + ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List values) { if(next()) { throw cannotEvaluateException("expression", this); @@ -148,23 +221,23 @@ ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List= min && (ps.isVarags() || np <= max)) { @@ -218,7 +299,7 @@ private OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator params.add(args.get(i-s).toArg(td, types)); } } - return op.args(params.toArray()); + return params.toArray(); } throw new IllegalArgumentException("msg"); } From ef437c31d028a5f0d8fd35677793582f488476eb Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 16 Jan 2024 00:57:32 +0100 Subject: [PATCH 049/298] edit --- .../java/org/usf/jquery/core/ComparatorPipe.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/org/usf/jquery/core/ComparatorPipe.java diff --git a/src/main/java/org/usf/jquery/core/ComparatorPipe.java b/src/main/java/org/usf/jquery/core/ComparatorPipe.java new file mode 100644 index 00000000..9952febf --- /dev/null +++ b/src/main/java/org/usf/jquery/core/ComparatorPipe.java @@ -0,0 +1,15 @@ +package org.usf.jquery.core; + +public interface ComparatorPipe extends Comparator { + + @Override + default String sql(QueryParameterBuilder builder, Object[] args) { + throw new UnsupportedOperationException("no sql"); + } + + @Override + default DBFilter args(Object... args) { + return ((DBFilter)args[0]).append(LogicalOperator.valueOf(id()), ((DBFilter)args[1])); + } + +} From ec5cf29c55d86e7bbe677cdd68d49b96c1842b13 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 16 Jan 2024 01:11:15 +0100 Subject: [PATCH 050/298] edit --- src/main/java/org/usf/jquery/core/Comparator.java | 2 -- src/main/java/org/usf/jquery/core/Parameter.java | 14 -------------- .../java/org/usf/jquery/web/RequestEntryChain.java | 7 ------- 3 files changed, 23 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 63234db7..533da468 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -8,8 +8,6 @@ import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; -import java.util.Arrays; -import java.util.Objects; import java.util.Optional; import java.util.function.UnaryOperator; diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index abb231ce..9983ef53 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Utils.isEmpty; @@ -46,17 +45,4 @@ public static Parameter varargs(JavaType... types) { return new Parameter(types, false, true); } - public static Parameter[] checkParams(Parameter... parameters) { - if(nonNull(parameters)) { - var i=0; - while(i Date: Tue, 16 Jan 2024 17:16:07 +0100 Subject: [PATCH 051/298] edit --- src/main/java/org/usf/jquery/core/Comparator.java | 2 +- .../core/{ComparatorPipe.java => ComparatorChain.java} | 2 +- src/main/java/org/usf/jquery/core/Parameter.java | 1 - .../java/org/usf/jquery/web/RequestEntryChain.java | 10 ---------- 4 files changed, 2 insertions(+), 13 deletions(-) rename src/main/java/org/usf/jquery/core/{ComparatorPipe.java => ComparatorChain.java} (85%) diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 533da468..3bd19fae 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -128,7 +128,7 @@ static InCompartor inComparator(final String name) { return ()-> name; } - static ComparatorPipe pipe(final String name) { + static ComparatorChain pipe(final String name) { return ()-> name; } diff --git a/src/main/java/org/usf/jquery/core/ComparatorPipe.java b/src/main/java/org/usf/jquery/core/ComparatorChain.java similarity index 85% rename from src/main/java/org/usf/jquery/core/ComparatorPipe.java rename to src/main/java/org/usf/jquery/core/ComparatorChain.java index 9952febf..300bfa5b 100644 --- a/src/main/java/org/usf/jquery/core/ComparatorPipe.java +++ b/src/main/java/org/usf/jquery/core/ComparatorChain.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -public interface ComparatorPipe extends Comparator { +public interface ComparatorChain extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 9983ef53..f823051f 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -44,5 +44,4 @@ public static Parameter optional(JavaType... types) { public static Parameter varargs(JavaType... types) { return new Parameter(types, false, true); } - } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 1b73082f..f2f14a24 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -43,8 +43,6 @@ @RequiredArgsConstructor final class RequestEntryChain { - private static final ColumnDecorator DEFAUL_COLUMN = ()-> null; //unused identity - private final String value; private final boolean text; //"string" private RequestEntryChain next; @@ -191,14 +189,6 @@ OperationColumn toOperation(TableDecorator td, DBColumn col) { return res.isEmpty() ? null : fillArgs(td, col, res.get()); } - - DBFilter toComparison2(TableDecorator td, DBColumn col) { - var f = toComparison(td, col); - if(nonNull(f)) { - } - return null; - } - DBFilter toComparison(TableDecorator td, DBObject col) { var res = lookupComparator(value); return res.isEmpty() ? null : fillArgs(td, col, res.get()); From a3c61c09546e477acc4414c021d967ef7cf48e20 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 17 Jan 2024 23:54:28 +0100 Subject: [PATCH 052/298] edit --- .../java/org/usf/jquery/core/ArgTypeRef.java | 19 +++++ .../java/org/usf/jquery/core/Comparator.java | 11 +-- .../java/org/usf/jquery/core/InCompartor.java | 5 -- .../java/org/usf/jquery/core/Operator.java | 9 +-- .../java/org/usf/jquery/core/Parameter.java | 31 ++++++-- .../org/usf/jquery/core/ParameterSet.java | 77 +++++++++++-------- .../org/usf/jquery/core/TypedComparator.java | 2 +- .../org/usf/jquery/core/TypedOperator.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 38 +++------ 9 files changed, 106 insertions(+), 88 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/ArgTypeRef.java diff --git a/src/main/java/org/usf/jquery/core/ArgTypeRef.java b/src/main/java/org/usf/jquery/core/ArgTypeRef.java new file mode 100644 index 00000000..d045658f --- /dev/null +++ b/src/main/java/org/usf/jquery/core/ArgTypeRef.java @@ -0,0 +1,19 @@ +package org.usf.jquery.core; + +import static org.usf.jquery.core.JDBCType.typeOf; +import static org.usf.jquery.core.Validation.requireAtLeastNArgs; + +import java.util.function.Function; + +/** + * + * @author u$f + * + */ +interface ArgTypeRef extends Function { + + static ArgTypeRef firstArgType() { + return arr-> typeOf(requireAtLeastNArgs(1, arr, + ()-> "ArgTypeRef function")[0]).orElse(null); // not sure + } +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 3bd19fae..7b411d8a 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -3,6 +3,7 @@ import static java.lang.reflect.Modifier.isStatic; import static java.util.Arrays.copyOfRange; import static java.util.Optional.empty; +import static org.usf.jquery.core.ArgTypeRef.firstArgType; import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.JqueryType.FILTER; import static org.usf.jquery.core.Parameter.required; @@ -19,11 +20,7 @@ public interface Comparator extends DBProcessor { String id(); - - default boolean isVarargs() { - return false; - } - + @Override default DBFilter args(Object... args) { return new ColumnSingleFilter((DBColumn)args[0], @@ -95,11 +92,11 @@ static TypedComparator nonNull() { } static TypedComparator in() { - return new TypedComparator(inComparator("IN"), required(), varargs()); + return new TypedComparator(inComparator("IN"), required(), varargs(firstArgType())); } static TypedComparator notIn() { - return new TypedComparator(inComparator("NOT IN"), required(), varargs()); + return new TypedComparator(inComparator("NOT IN"), required(), varargs(firstArgType())); } //pipe diff --git a/src/main/java/org/usf/jquery/core/InCompartor.java b/src/main/java/org/usf/jquery/core/InCompartor.java index e2b7bab1..d2d618b1 100644 --- a/src/main/java/org/usf/jquery/core/InCompartor.java +++ b/src/main/java/org/usf/jquery/core/InCompartor.java @@ -21,9 +21,4 @@ default String sql(QueryParameterBuilder builder, Object[] args) { var varg = copyOfRange(args, 1, args.length); return builder.appendLitteral(args[0]) + SPACE + id() + parenthese(builder.appendArrayParameter(type, varg)); } - - @Override - default boolean isVarargs() { - return true; - } } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 90789093..413432d0 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -2,6 +2,7 @@ import static java.lang.reflect.Modifier.isStatic; import static java.util.Optional.empty; +import static org.usf.jquery.core.ArgTypeRef.firstArgType; import static org.usf.jquery.core.Database.TERADATA; import static org.usf.jquery.core.JDBCType.BIGINT; import static org.usf.jquery.core.JDBCType.DATE; @@ -11,7 +12,6 @@ import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; import static org.usf.jquery.core.JDBCType.VARCHAR; -import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.JqueryType.CLAUSE; import static org.usf.jquery.core.JqueryType.COLUMN; import static org.usf.jquery.core.JqueryType.ORDER; @@ -21,11 +21,9 @@ import static org.usf.jquery.core.Parameter.varargs; import static org.usf.jquery.core.QueryParameterBuilder.formatValue; import static org.usf.jquery.core.Utils.currentDatabase; -import static org.usf.jquery.core.Validation.requireAtLeastNArgs; import static org.usf.jquery.core.Validation.requireNArgs; import java.util.Optional; -import java.util.function.Function; import java.util.stream.Stream; /** @@ -372,9 +370,4 @@ static Optional lookupOperator(String op) { } catch (Exception e) {/* do not throw exception */} return empty(); } - - private static Function firstArgType() { - return arr-> typeOf(requireAtLeastNArgs(1, arr, ()-> "firstArgType function")[0]) - .orElse(null); // not sure - } } diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index f823051f..7e9e7ea6 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static java.util.Objects.isNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Utils.isEmpty; @@ -19,11 +20,19 @@ public final class Parameter { private final JavaType[] types; //null => accept all + private final ArgTypeRef typeRef; private final boolean required; private final boolean varargs; - public boolean accept(Object o) { - return isEmpty(types) || Stream.of(types).anyMatch(t-> t.accept(o)); + public boolean accept(int idx, Object[] args) { + return isNull(typeRef) + ? isEmpty(types) || Stream.of(types).anyMatch(t-> t.accept(args[idx])) + : typeRef.apply(args).accept(args[idx]); + } + + + public JavaType[] types(Object[] args) { + return isNull(typeRef) ? types : new JavaType[] {typeRef.apply(args)}; } @Override @@ -34,14 +43,26 @@ public String toString() { } public static Parameter required(JavaType... types) { - return new Parameter(types, true, false); + return new Parameter(types, null, true, false); } public static Parameter optional(JavaType... types) { - return new Parameter(types, false, false); + return new Parameter(types, null, false, false); } public static Parameter varargs(JavaType... types) { - return new Parameter(types, false, true); + return new Parameter(types, null, false, true); + } + + public static Parameter required(ArgTypeRef typeRef) { + return new Parameter(null, typeRef, true, false); + } + + public static Parameter optional(ArgTypeRef typeRef) { + return new Parameter(null, typeRef, false, false); + } + + public static Parameter varargs(ArgTypeRef typeRef) { + return new Parameter(null, typeRef, false, true); } } diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index 4d3f66e7..f5d449b4 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -3,6 +3,8 @@ import static java.lang.Math.min; import static java.util.Objects.isNull; +import java.util.function.ObjIntConsumer; + import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -20,35 +22,40 @@ public final class ParameterSet { private final Parameter[] parameters; - public Object[] match(Object... args) { - if(isNull(args)) { - args = new Object[0]; - } - var na = args.length; - var rq = requireParameterCount(); - if(na >= rq && (na <= parameters.length || isVarags())) { + public Object[] args(Object... args) { + var arr = isNull(args) ? new Object[0] : args; + args(arr.length, (p,i)-> { + if(!p.accept(i, arr)) { + throw badArgumentTypeException(); + } + }); + return arr; + } + + public void args(int nArgs, ObjIntConsumer cons) { + var rq = requiredParameterCount(); + if(nArgs >= rq && (nArgs <= parameters.length || isVarags())) { var i=0; - for(; i typeFn, Operator function, Par @Override public OperationColumn args(Object... args) { - args = parameterSet.match(args); + args = parameterSet.args(args); return new OperationColumn(operator, argMapper.apply(args), typeFn.apply(args)); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index f2f14a24..2730b95c 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -1,6 +1,5 @@ package org.usf.jquery.web; -import static java.lang.Math.min; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; @@ -16,7 +15,6 @@ import static org.usf.jquery.web.ColumnDecorator.ofColumn; import static org.usf.jquery.web.JQueryContext.context; -import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; @@ -25,9 +23,9 @@ import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; import org.usf.jquery.core.DBOrder; +import org.usf.jquery.core.JavaType; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; -import org.usf.jquery.core.Parameter; import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedComparator; @@ -260,37 +258,23 @@ private OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator } private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps) { - var np = isNull(args) ? 0 : args.size(); + int inc = nonNull(col) ? 1 : 0; + var arr = new Object[isNull(args) ? inc : args.size() + inc]; if(nonNull(col)) { - np++; + arr[0] = col; } - var min = ps.requireParameterCount(); - var max = ps.parameterCount(); - if(np >= min && (ps.isVarags() || np <= max)) { - var params = new ArrayList(np); - if(nonNull(col)) { - params.add(col); + ps.args(arr.length, (p,i)-> { + if(i>0 || inc==0) { + arr[i] = args.get(i-inc).toArg(td, p.types(arr)); } - var s = nonNull(col) ? 1 : 0; - var i = s; - for(; i Date: Thu, 18 Jan 2024 00:23:13 +0100 Subject: [PATCH 053/298] edit --- .../org/usf/jquery/core/ClauseFunction.java | 4 +-- .../java/org/usf/jquery/core/Parameter.java | 5 +-- .../org/usf/jquery/core/ParameterSet.java | 32 ++++++++----------- .../org/usf/jquery/web/RequestEntryChain.java | 7 ++-- 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ClauseFunction.java b/src/main/java/org/usf/jquery/core/ClauseFunction.java index e37da7a1..85f7b79e 100644 --- a/src/main/java/org/usf/jquery/core/ClauseFunction.java +++ b/src/main/java/org/usf/jquery/core/ClauseFunction.java @@ -1,8 +1,8 @@ package org.usf.jquery.core; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.SqlStringBuilder.COMA; import static org.usf.jquery.core.SqlStringBuilder.EMPTY; +import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Utils.isEmpty; @@ -20,7 +20,7 @@ public interface ClauseFunction extends FunctionOperator { default String sql(QueryParameterBuilder builder, Object[] args) { return isEmpty(args) ? EMPTY - : id() + SPACE + Stream.of(args).map(builder::appendLitteral).collect(joining(COMA)); + : id() + SPACE + Stream.of(args).map(builder::appendLitteral).collect(joining(SCOMA)); } } diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 7e9e7ea6..8e6ca566 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -30,9 +30,10 @@ public boolean accept(int idx, Object[] args) { : typeRef.apply(args).accept(args[idx]); } - public JavaType[] types(Object[] args) { - return isNull(typeRef) ? types : new JavaType[] {typeRef.apply(args)}; + return isNull(typeRef) + ? types + : new JavaType[] {typeRef.apply(args)}; } @Override diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index f5d449b4..311ff9e9 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -24,7 +24,7 @@ public final class ParameterSet { public Object[] args(Object... args) { var arr = isNull(args) ? new Object[0] : args; - args(arr.length, (p,i)-> { + forEach(arr.length, (p,i)-> { if(!p.accept(i, arr)) { throw badArgumentTypeException(); } @@ -32,27 +32,21 @@ public Object[] args(Object... args) { return arr; } - public void args(int nArgs, ObjIntConsumer cons) { + public void forEach(int nArgs, ObjIntConsumer cons) { var rq = requiredParameterCount(); - if(nArgs >= rq && (nArgs <= parameters.length || isVarags())) { - var i=0; - for(; i parameters.length && !isVarags())) { throw badArgumentCountException(); } - } - - public int parameterCount() { - return parameters.length; + var i=0; + for(; i new Triple(td, ofColumn(value, b-> fillArgs(td, null, fn)), this)) .orElse(null); } - private DBFilter fillArgs(TableDecorator td, DBObject col, TypedComparator cmp) { return cmp.args(toArgs(td, col, cmp.getParameterSet())); @@ -263,15 +262,15 @@ private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps) { if(nonNull(col)) { arr[0] = col; } - ps.args(arr.length, (p,i)-> { + ps.forEach(arr.length, (p,i)-> { if(i>0 || inc==0) { - arr[i] = args.get(i-inc).toArg(td, p.types(arr)); + arr[i] = args.get(i-inc).parseValue(td, p.types(arr)); } }); return arr; } - private Object toArg(TableDecorator td, JavaType[] types) { + private Object parseValue(TableDecorator td, JavaType[] types) { return isNull(value) || text ? requireNoArgs().value : parse(this, td, types); From 96fdef24ac72975451311d3dd5dd503c0d00e658 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 18 Jan 2024 01:41:03 +0100 Subject: [PATCH 054/298] edit --- .../java/org/usf/jquery/core/Comparator.java | 32 +++++++++++++------ .../usf/jquery/core/ComparisonExpression.java | 2 +- .../java/org/usf/jquery/core/DBProcessor.java | 15 +++++++++ .../java/org/usf/jquery/core/Operator.java | 10 +----- .../java/org/usf/jquery/core/Parameter.java | 6 ++-- .../org/usf/jquery/core/ParameterSet.java | 1 - .../org/usf/jquery/web/RequestEntryChain.java | 2 +- 7 files changed, 43 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 7b411d8a..b77f9482 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -1,8 +1,6 @@ package org.usf.jquery.core; -import static java.lang.reflect.Modifier.isStatic; import static java.util.Arrays.copyOfRange; -import static java.util.Optional.empty; import static org.usf.jquery.core.ArgTypeRef.firstArgType; import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.JqueryType.FILTER; @@ -27,6 +25,8 @@ default DBFilter args(Object... args) { this.expression(copyOfRange(args, 1, args.length))); // no type } + //basic comparator + default ComparisonExpression expression(Object right) { return new ComparisonSingleExpression(this, right); } @@ -55,6 +55,8 @@ static TypedComparator ge() { return new TypedComparator(basicComparator(">="), required(), required()); } + //string comparator + static TypedComparator startsLike() { return like().argsMapper(wildcardSecondArg(o-> o + "%")); } @@ -67,6 +69,18 @@ static TypedComparator contentLike() { return like().argsMapper(wildcardSecondArg(o-> "%" + o + "%")); } + static TypedComparator startsNotLike() { + return notLike().argsMapper(wildcardSecondArg(o-> o + "%")); + } + + static TypedComparator endsNotLike() { + return notLike().argsMapper(wildcardSecondArg(o-> "%" + o)); + } + + static TypedComparator contentNotLike() { + return notLike().argsMapper(wildcardSecondArg(o-> "%" + o + "%")); + } + static TypedComparator like() { return new TypedComparator(stringComparator("LIKE"), required(VARCHAR), required(VARCHAR)); } @@ -83,13 +97,17 @@ static TypedComparator notILike() { return new TypedComparator(stringComparator("NOT ILIKE"), required(VARCHAR), required(VARCHAR)); } + //null comparator + static TypedComparator isNull() { return new TypedComparator(nullComparator("IS NULL")); // takes no args } - static TypedComparator nonNull() { + static TypedComparator notNull() { return new TypedComparator(nullComparator("IS NOT NULL")); // takes no args } + + //in comparator static TypedComparator in() { return new TypedComparator(inComparator("IN"), required(), varargs(firstArgType())); @@ -130,13 +148,7 @@ static ComparatorChain pipe(final String name) { } static Optional lookupComparator(String op) { - try { - var m = Comparator.class.getMethod(op); - if(isStatic(m.getModifiers()) && m.getReturnType() == TypedComparator.class && m.getParameterCount() == 0) { // no private static - return Optional.of((TypedComparator) m.invoke(null)); - } - } catch (Exception e) {/* do not throw exception */} - return empty(); + return DBProcessor.lookup(Comparator.class, TypedComparator.class, op); } private static UnaryOperator wildcardSecondArg(UnaryOperator fn) { diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpression.java b/src/main/java/org/usf/jquery/core/ComparisonExpression.java index a2230fd6..cd25e579 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpression.java @@ -76,7 +76,7 @@ static ComparisonExpression isNull() { } static ComparisonExpression isNotNull() { - return Comparator.nonNull().expression(null); + return Comparator.notNull().expression(null); } @SuppressWarnings("unchecked") diff --git a/src/main/java/org/usf/jquery/core/DBProcessor.java b/src/main/java/org/usf/jquery/core/DBProcessor.java index b8edf638..121b7fd8 100644 --- a/src/main/java/org/usf/jquery/core/DBProcessor.java +++ b/src/main/java/org/usf/jquery/core/DBProcessor.java @@ -1,5 +1,10 @@ package org.usf.jquery.core; +import static java.lang.reflect.Modifier.isStatic; +import static java.util.Optional.empty; + +import java.util.Optional; + /** * * @author u$f @@ -8,4 +13,14 @@ public interface DBProcessor extends DBObject { T args(Object... args); + + static Optional lookup(Class clazz, Class ext, String op) { + try { + var m = clazz.getMethod(op); + if(m.getReturnType() == ext && isStatic(m.getModifiers()) && m.getParameterCount() == 0) { // no private static + return Optional.of(ext.cast(m.invoke(null))); + } + } catch (Exception e) {/* do not throw exception */} + return empty(); + } } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 413432d0..0da55b95 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -1,7 +1,5 @@ package org.usf.jquery.core; -import static java.lang.reflect.Modifier.isStatic; -import static java.util.Optional.empty; import static org.usf.jquery.core.ArgTypeRef.firstArgType; import static org.usf.jquery.core.Database.TERADATA; import static org.usf.jquery.core.JDBCType.BIGINT; @@ -362,12 +360,6 @@ static Optional lookupWindowFunction(String op) { } static Optional lookupOperator(String op) { - try { - var m = Operator.class.getMethod(op); - if(isStatic(m.getModifiers()) && m.getReturnType() == TypedOperator.class && m.getParameterCount() == 0) { // no private static - return Optional.of((TypedOperator) m.invoke(null)); - } - } catch (Exception e) {/* do not throw exception */} - return empty(); + return DBProcessor.lookup(Operator.class, TypedOperator.class, op); } } diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 8e6ca566..080e6f19 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -38,9 +38,9 @@ public JavaType[] types(Object[] args) { @Override public String toString() { - return Stream.of(types) - .map(Object::toString) - .collect(joining("|")); + return isNull(typeRef) + ? Stream.of(types).map(Object::toString).collect(joining("|")) + : typeRef.toString(); } public static Parameter required(JavaType... types) { diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index 311ff9e9..6921aa3e 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -87,5 +87,4 @@ private static IllegalArgumentException badArgumentTypeException() { private static IllegalArgumentException badArgumentCountException() { return new IllegalArgumentException("bad argument count"); } - } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 9e806f06..41158bc0 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -110,7 +110,7 @@ public DBFilter asFilter(TableDecorator td, List values) { ftr = e.toComparison(td, ftr); e = e.next; } - return ftr; + return ftr; //throw if null } if(isNull(e.args)) { var prs = requireNonNull(t.cd.parser(td)); From 5e6ba9f9bb94812605189b3ac1e5899513192b37 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 18 Jan 2024 10:18:13 +0100 Subject: [PATCH 055/298] edit --- src/main/java/org/usf/jquery/core/Comparator.java | 8 ++++---- src/main/java/org/usf/jquery/core/JDBCType.java | 2 +- src/main/java/org/usf/jquery/core/TypedOperator.java | 4 ++-- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 6 ++++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index b77f9482..a13f60b0 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -117,14 +117,14 @@ static TypedComparator notIn() { return new TypedComparator(inComparator("NOT IN"), required(), varargs(firstArgType())); } - //pipe + //comparator chain static TypedComparator and() { - return new TypedComparator(pipe("AND"), required(FILTER), required(FILTER)); + return new TypedComparator(chain("AND"), required(FILTER), required(FILTER)); } static TypedComparator or() { - return new TypedComparator(pipe("OR"), required(FILTER), required(FILTER)); + return new TypedComparator(chain("OR"), required(FILTER), required(FILTER)); } static BasicComparator basicComparator(final String name) { @@ -143,7 +143,7 @@ static InCompartor inComparator(final String name) { return ()-> name; } - static ComparatorChain pipe(final String name) { + static ComparatorChain chain(final String name) { return ()-> name; } diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index eaf5d271..06b0d42e 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -49,7 +49,7 @@ public enum JDBCType implements JavaType { TIME(Types.TIME, Time.class, Time.class::isInstance), TIMESTAMP(Types.TIMESTAMP, Timestamp.class, Timestamp.class::isInstance), TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class, Timestamp.class::isInstance), - OTHER(Types.OTHER, Object.class, o-> false); //isnull !? + OTHER(Types.OTHER, Object.class, o-> false); private final int value; private final Class type; diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 6b1537dd..4cbaa9d3 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -19,7 +19,7 @@ public class TypedOperator implements Operator { @Delegate private final Operator operator; - private final Function typeFn; + private final ArgTypeRef typeFn; private final ParameterSet parameterSet; private UnaryOperator argMapper = identity(); @@ -27,7 +27,7 @@ public TypedOperator(JavaType type, Operator function, Parameter... args) { this(o-> type, function, args); } - public TypedOperator(Function typeFn, Operator function, Parameter... parameter) { + public TypedOperator(ArgTypeRef typeFn, Operator function, Parameter... parameter) { this.operator = function; this.typeFn = typeFn; this.parameterSet = ofParameters(parameter); diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 41158bc0..9871fbc0 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -263,7 +263,7 @@ private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps) { arr[0] = col; } ps.forEach(arr.length, (p,i)-> { - if(i>0 || inc==0) { + if(i>0 || inc==0) { //arg0 already parsed arr[i] = args.get(i-inc).parseValue(td, p.types(arr)); } }); @@ -311,7 +311,9 @@ static ParseException cannotEvaluateException(String type, RequestEntryChain ent } static String[] toStringArray(List entries) { - return entries.stream().map(e-> isNull(e.value) ? null : e.toString()).toArray(String[]::new); + return entries.stream() + .map(e-> isNull(e.value) ? null : e.toString()) + .toArray(String[]::new); } @RequiredArgsConstructor From db574b85953cbcb1b0680f54d429d02c122fd763 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 18 Jan 2024 21:56:12 +0100 Subject: [PATCH 056/298] edit --- .../org/usf/jquery/core/BasicComparator.java | 4 +- .../java/org/usf/jquery/core/Chainable.java | 22 +++++++ .../usf/jquery/core/ComparisonExpression.java | 14 +---- .../java/org/usf/jquery/core/DBFilter.java | 14 +---- src/main/java/org/usf/jquery/core/DBView.java | 4 ++ .../java/org/usf/jquery/core/JDBCType.java | 59 +++++++------------ .../java/org/usf/jquery/core/JavaType.java | 6 +- .../java/org/usf/jquery/core/NamedView.java | 37 ++++++++++++ .../java/org/usf/jquery/core/Operator.java | 4 +- .../org/usf/jquery/core/StringComparator.java | 5 +- src/main/java/org/usf/jquery/core/Typed.java | 5 ++ .../org/usf/jquery/core/TypedOperator.java | 1 - .../org/usf/jquery/web/ArgumentParsers.java | 2 +- .../org/usf/jquery/web/ColumnBuilder.java | 2 +- .../org/usf/jquery/web/ColumnDecorator.java | 13 ++-- .../org/usf/jquery/web/CriteriaBuilder.java | 22 +++---- .../org/usf/jquery/web/RequestEntryChain.java | 13 ++-- .../org/usf/jquery/web/TableDecorator.java | 30 ++++++---- .../java/org/usf/jquery/web/ViewBuilder.java | 14 +++++ 19 files changed, 158 insertions(+), 113 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/Chainable.java create mode 100644 src/main/java/org/usf/jquery/core/NamedView.java create mode 100644 src/main/java/org/usf/jquery/web/ViewBuilder.java diff --git a/src/main/java/org/usf/jquery/core/BasicComparator.java b/src/main/java/org/usf/jquery/core/BasicComparator.java index 88dbc32d..024d112d 100644 --- a/src/main/java/org/usf/jquery/core/BasicComparator.java +++ b/src/main/java/org/usf/jquery/core/BasicComparator.java @@ -14,9 +14,7 @@ public interface BasicComparator extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, BasicComparator.class::getSimpleName); - var type = typeOf(args[0]) - .or(()-> typeOf(args[1])) - .orElseThrow(Comparator::typeCannotBeNullException); // null 'cmp' null + var type = typeOf(args[1]).orElseThrow(Comparator::typeCannotBeNullException); // null 'cmp' null return builder.appendLitteral(args[0]) + id() + builder.appendParameter(type, args[1]); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/Chainable.java b/src/main/java/org/usf/jquery/core/Chainable.java new file mode 100644 index 00000000..f8ea3c5c --- /dev/null +++ b/src/main/java/org/usf/jquery/core/Chainable.java @@ -0,0 +1,22 @@ +package org.usf.jquery.core; + +import static org.usf.jquery.core.LogicalOperator.AND; +import static org.usf.jquery.core.LogicalOperator.OR; + +/** + * + * @author u$f + * + */ +public interface Chainable { + + T append(LogicalOperator op, T exp); + + default T and(T exp) { + return append(AND, exp); + } + + default T or(T exp) { + return append(OR, exp); + } +} diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpression.java b/src/main/java/org/usf/jquery/core/ComparisonExpression.java index cd25e579..2b000e85 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpression.java @@ -1,7 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.LogicalOperator.AND; -import static org.usf.jquery.core.LogicalOperator.OR; import static org.usf.jquery.core.Validation.requireNArgs; import lombok.NonNull; @@ -11,7 +9,7 @@ * @author u$f * */ -public interface ComparisonExpression extends DBExpression, NestedSql { +public interface ComparisonExpression extends DBExpression, NestedSql, Chainable { @Override default String sql(QueryParameterBuilder builder, Object[] args) { @@ -21,16 +19,6 @@ default String sql(QueryParameterBuilder builder, Object[] args) { String sql(QueryParameterBuilder builder, Object left); // do change method order - ComparisonExpression append(LogicalOperator op, ComparisonExpression exp); - - default ComparisonExpression and(ComparisonExpression exp) { - return append(AND, exp); - } - - default ComparisonExpression or(ComparisonExpression exp) { - return append(OR, exp); - } - static ComparisonExpression equal(Object right) { return Comparator.eq().expression(right); } diff --git a/src/main/java/org/usf/jquery/core/DBFilter.java b/src/main/java/org/usf/jquery/core/DBFilter.java index 08ad2abc..63a5718c 100644 --- a/src/main/java/org/usf/jquery/core/DBFilter.java +++ b/src/main/java/org/usf/jquery/core/DBFilter.java @@ -1,7 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.LogicalOperator.AND; -import static org.usf.jquery.core.LogicalOperator.OR; import static org.usf.jquery.core.Validation.requireNoArgs; /** @@ -10,7 +8,7 @@ * */ @FunctionalInterface -public interface DBFilter extends DBObject, NestedSql { +public interface DBFilter extends DBObject, NestedSql, Chainable { String sql(QueryParameterBuilder builder); @@ -23,12 +21,4 @@ default String sql(QueryParameterBuilder builder, Object[] args) { default DBFilter append(LogicalOperator op, DBFilter filter) { throw new UnsupportedOperationException(); //explicitly overridden } - - default DBFilter and(DBFilter filter) { - return append(AND, filter); - } - - default DBFilter or(DBFilter filter) { - return append(OR, filter); - } -} +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 7815e43f..90c6aaa3 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -19,4 +19,8 @@ default String sql(QueryParameterBuilder builder, Object[] args) { String sql(QueryParameterBuilder builder, String schema); + default TaggableView as(String tagname) { + return new NamedView(this, tagname); + } + } diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index 06b0d42e..2774ea7c 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static java.util.Optional.empty; /** @@ -32,15 +34,15 @@ public enum JDBCType implements JavaType { //do not change enum order BOOLEAN(Types.BOOLEAN, Boolean.class, JDBCType::isBoolean), BIT(Types.BIT, Boolean.class, JDBCType::isBoolean), - TINYINT(Types.TINYINT, Byte.class, o-> isNumber(o, Byte.MIN_VALUE, Byte.MAX_VALUE, false)), - SMALLINT(Types.SMALLINT, Short.class, o-> isNumber(o, Short.MIN_VALUE, Short.MAX_VALUE, false)), - INTEGER(Types.INTEGER, Integer.class, o-> isNumber(o, Integer.MIN_VALUE, Integer.MAX_VALUE, false)), - BIGINT(Types.BIGINT, Long.class, o-> isNumber(o, Long.MIN_VALUE, Long.MAX_VALUE, false)), - REAL(Types.REAL, Float.class, o-> isNumber(o, Float.MIN_VALUE, Float.MAX_VALUE, true)), - FLOAT(Types.FLOAT, Double.class, o-> isNumber(o, Double.MIN_VALUE, Double.MAX_VALUE, true)), - DOUBLE(Types.DOUBLE, Double.class, o-> isNumber(o, Double.MIN_VALUE, Double.MAX_VALUE, true)), - NUMERIC(Types.NUMERIC, BigDecimal.class, JDBCType::isNumber), - DECIMAL(Types.DECIMAL, BigDecimal.class, JDBCType::isNumber), + TINYINT(Types.TINYINT, Byte.class, Number.class, Number.class::isInstance), + SMALLINT(Types.SMALLINT, Short.class, Number.class, Number.class::isInstance), + INTEGER(Types.INTEGER, Integer.class, Number.class, Number.class::isInstance), + BIGINT(Types.BIGINT, Long.class, Number.class, Number.class::isInstance), + REAL(Types.REAL, Float.class, Number.class, Number.class::isInstance), + FLOAT(Types.FLOAT, Double.class, Number.class, Number.class::isInstance), + DOUBLE(Types.DOUBLE, Double.class, Number.class, Number.class::isInstance), + NUMERIC(Types.NUMERIC, BigDecimal.class, Number.class, Number.class::isInstance), + DECIMAL(Types.DECIMAL, BigDecimal.class, Number.class, Number.class::isInstance), CHAR(Types.CHAR, Character.class, JDBCType::isChar), //teradata !char VARCHAR(Types.VARCHAR, String.class, JDBCType::isString), NVARCHAR(Types.NVARCHAR, String.class, JDBCType::isString), @@ -49,12 +51,17 @@ public enum JDBCType implements JavaType { TIME(Types.TIME, Time.class, Time.class::isInstance), TIMESTAMP(Types.TIMESTAMP, Timestamp.class, Timestamp.class::isInstance), TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class, Timestamp.class::isInstance), - OTHER(Types.OTHER, Object.class, o-> false); + OTHER(Types.OTHER, Object.class, null, o-> false); private final int value; private final Class type; + private final Class superType; private final Predicate matcher; + private JDBCType(int value, Class type, Predicate matcher) { + this(value, type, type, matcher); + } + @Override public Class type() { return type; @@ -64,38 +71,15 @@ public Class type() { public boolean accept(Object o) { if(o instanceof Typed) { var t = ((Typed) o).javaType(); - return t == null - || this == t - || type() == t.type() - || (subType(this, Number.class) && subType(t, Number.class)); //other types compatibility - } - return acceptValue(o); - } - - static boolean subType(JavaType type, Class c) { - return c.isAssignableFrom(type.type()); - } - - private boolean acceptValue(Object o) { - return o == null || matcher.test(o); - } - - private static boolean isNumber(Object o, double min, double max, boolean decimal) { - if(isNumber(o)) { - var n = (Number) o; - var v = n.doubleValue(); - return (v >= min && v <= max) && (decimal || v == n.longValue()); + return t == null || t == this + || (nonNull(superType) && superType.isAssignableFrom(t.type())); } - return false; - } - - private static boolean isNumber(Object o) { - return o instanceof Number; + return isNull(o) || matcher.test(o); } private static boolean isBoolean(Object o) { return o.getClass() == Boolean.class - || isNumber(o, 0, 1, false) + || o.equals(0) || o.equals(1) || (o.getClass() == String.class && o.toString().matches("[yYnN]")); } @@ -129,5 +113,4 @@ public static Optional findType(Predicate predicate) { } return empty(); } - } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/JavaType.java b/src/main/java/org/usf/jquery/core/JavaType.java index 561873e2..856d3518 100644 --- a/src/main/java/org/usf/jquery/core/JavaType.java +++ b/src/main/java/org/usf/jquery/core/JavaType.java @@ -12,11 +12,7 @@ public interface JavaType { Class type(); - default String name() { - return type().getSimpleName(); - } - - default boolean accept(Object o) { + default boolean accept(Object o) { // nullable by default return isNull(o) || type().isInstance(o); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/NamedView.java b/src/main/java/org/usf/jquery/core/NamedView.java new file mode 100644 index 00000000..86164408 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/NamedView.java @@ -0,0 +1,37 @@ +package org.usf.jquery.core; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; + +/** + * + * @author u$f + * + */ +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public final class NamedView implements TaggableView { + + @Delegate + private final DBView view; + private final String tag; + + @Override + public String tagname() { + return tag; + } + + @Override + public NamedView as(String name) { // map + return new NamedView(unwrap(), name); + } + + public DBView unwrap() { + return view; + } + + @Override + public String toString() { + return view.toString(); + } +} diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 0da55b95..1623e640 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -30,6 +30,8 @@ * */ public interface Operator extends DBProcessor, NestedSql { + + String id(); @Deprecated(forRemoval = true) static final Operator VALUE_RETURN = new Operator() { @@ -46,8 +48,6 @@ public String id() { } }; - String id(); - default OperationColumn args(Object... args) { return new OperationColumn(this, args); // no type } diff --git a/src/main/java/org/usf/jquery/core/StringComparator.java b/src/main/java/org/usf/jquery/core/StringComparator.java index 18c456f8..7538862a 100644 --- a/src/main/java/org/usf/jquery/core/StringComparator.java +++ b/src/main/java/org/usf/jquery/core/StringComparator.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.JDBCType.typeOf; +import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.SqlStringBuilder.space; import static org.usf.jquery.core.Validation.requireNArgs; @@ -15,7 +15,6 @@ public interface StringComparator extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, StringComparator.class::getSimpleName); - var type = typeOf(args[0]).orElseThrow(Comparator::typeCannotBeNullException); // null 'cmp' - return builder.appendLitteral(args[0]) + space(id()) + builder.appendParameter(type, args[1]); + return builder.appendLitteral(args[0]) + space(id()) + builder.appendParameter(VARCHAR, args[1]); } } diff --git a/src/main/java/org/usf/jquery/core/Typed.java b/src/main/java/org/usf/jquery/core/Typed.java index cc45bcb5..6f9c4c39 100644 --- a/src/main/java/org/usf/jquery/core/Typed.java +++ b/src/main/java/org/usf/jquery/core/Typed.java @@ -1,5 +1,10 @@ package org.usf.jquery.core; +/** + * + * @author u$f + * + */ public interface Typed { JavaType javaType(); diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 4cbaa9d3..aa99db72 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -3,7 +3,6 @@ import static java.util.function.UnaryOperator.identity; import static org.usf.jquery.core.ParameterSet.ofParameters; -import java.util.function.Function; import java.util.function.UnaryOperator; import lombok.Getter; diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 0d8e52bd..ed0585bc 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -116,6 +116,6 @@ private static Object parseUnknown(String s) { } private static UnsupportedOperationException unsupportedTypeException(JavaType type) { - return new UnsupportedOperationException("unsupported type " + type.name()); + return new UnsupportedOperationException("unsupported type " + type.toString()); } } diff --git a/src/main/java/org/usf/jquery/web/ColumnBuilder.java b/src/main/java/org/usf/jquery/web/ColumnBuilder.java index 7da88bf6..12c9245d 100644 --- a/src/main/java/org/usf/jquery/web/ColumnBuilder.java +++ b/src/main/java/org/usf/jquery/web/ColumnBuilder.java @@ -10,5 +10,5 @@ @FunctionalInterface public interface ColumnBuilder { - DBColumn column(TableDecorator table); + DBColumn build(TableDecorator table); } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 4fb1fbb1..809890a0 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -2,6 +2,7 @@ import static org.usf.jquery.web.ArgumentParsers.jdbcArgParser; +import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.JDBCType; /** @@ -19,21 +20,21 @@ default String reference() { //JSON return identity(); } + default JDBCArgumentParser parser(TableDecorator td){ // override parser | format | local + return jdbcArgParser(dataType(td)); + } + default JDBCType dataType(TableDecorator td) { return td.metadata().columnMetada(this) .map(ColumnMetadata::getDataType) .orElse(null); } - default JDBCArgumentParser parser(TableDecorator td){ // override parser | format | local - return jdbcArgParser(dataType(td)); - } - default ColumnBuilder builder() { - return null; // physical column by default + return null; // no builder by default } - default CriteriaBuilder criteria(String name) { + default CriteriaBuilder criteria(String name) { return null; // no criteria by default } diff --git a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java index 97e8075e..442adef7 100644 --- a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java +++ b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java @@ -7,8 +7,7 @@ import java.util.stream.Stream; -import org.usf.jquery.core.Comparator; -import org.usf.jquery.core.ComparisonExpression; +import org.usf.jquery.core.Chainable; import org.usf.jquery.core.LogicalOperator; /** @@ -17,24 +16,19 @@ * */ @FunctionalInterface -public interface CriteriaBuilder { +public interface CriteriaBuilder> { - ComparisonExpression criteria(T arg); + T criteria(String arg); - default LogicalOperator combiner() { - return OR; - } - - @SuppressWarnings("unchecked") - default ComparisonExpression build(T... args) { + default T build(String... args) { return Stream.of(requireAtLeastNArgs(1, args, CriteriaBuilder.class::getSimpleName)) .map(v-> ofNullable(criteria(v)) - .orElseThrow(()-> cannotEvaluateException("criteria value", v.toString()))) - .reduce(ComparisonExpression::or) + .orElseThrow(()-> cannotEvaluateException("criteria value", v))) + .reduce((e1, e2)-> e1.append(combiner(), e2)) .orElseThrow(); } - public static CriteriaBuilder ofComparator(Comparator cmp) { - return cmp::expression; + default LogicalOperator combiner() { + return OR; } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 9871fbc0..f79bc225 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -36,6 +36,11 @@ import lombok.RequiredArgsConstructor; import lombok.Setter; +/** + * + * @author u$f + * + */ @Getter @Setter(value = AccessLevel.PACKAGE) @RequiredArgsConstructor @@ -305,16 +310,16 @@ public String toString() { } return isNull(tag) ? s : s + ":" + tag; } - - static ParseException cannotEvaluateException(String type, RequestEntryChain entry) { - return ParseException.cannotEvaluateException(type, entry.toString()); - } static String[] toStringArray(List entries) { return entries.stream() .map(e-> isNull(e.value) ? null : e.toString()) .toArray(String[]::new); } + + static ParseException cannotEvaluateException(String type, RequestEntryChain entry) { + return ParseException.cannotEvaluateException(type, entry.toString()); + } @RequiredArgsConstructor static class Triple { diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index 99045e1e..b0931f52 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -1,7 +1,7 @@ package org.usf.jquery.web; import static java.lang.Integer.parseInt; -import static java.util.Objects.nonNull; +import static java.util.Objects.isNull; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Utils.isEmpty; @@ -26,6 +26,7 @@ import java.util.Optional; import java.util.stream.Stream; +import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBTable; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; @@ -46,18 +47,27 @@ public interface TableDecorator { Optional columnName(ColumnDecorator cd); default TaggableView table() { - return new DBTable(tableName(), identity()); + var b = builder(); + return isNull(b) + ? new DBTable(tableName(), identity()) + : b.build().as(identity()); } default TaggableColumn column(ColumnDecorator cd) { - if(nonNull(cd.builder())) { - return cd.builder().column(this).as(cd.reference()); - } - var cn = columnName(cd); - if(cn.isPresent()) { - return new ViewColumn(table(), requireLegalVariable(cn.get()), cd.reference(), cd.dataType(this)); - } - throw undeclaredResouceException(identity(), cd.identity()); + var b = cd.builder(); + return isNull(b) + ? columnName(cd) + .map(cn-> new ViewColumn(table(), requireLegalVariable(cn), cd.reference(), cd.dataType(this))) + .orElseThrow(()-> undeclaredResouceException(identity(), cd.identity())) + : b.build(this).as(cd.reference()); + } + + default ViewBuilder builder() { + return null; // no builder by default + } + + default CriteriaBuilder criteria(String name) { //!aggregation + return null; // no criteria by default } default RequestQueryBuilder query(Map parameterMap) { diff --git a/src/main/java/org/usf/jquery/web/ViewBuilder.java b/src/main/java/org/usf/jquery/web/ViewBuilder.java new file mode 100644 index 00000000..280c91ed --- /dev/null +++ b/src/main/java/org/usf/jquery/web/ViewBuilder.java @@ -0,0 +1,14 @@ +package org.usf.jquery.web; + +import org.usf.jquery.core.DBView; + +/** + * + * @author u$f + * + */ +@FunctionalInterface +public interface ViewBuilder { + + DBView build(); +} From 07ca3af66ffe29ec9550280ab9784ad5cf9c09df Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 19 Jan 2024 16:33:51 +0100 Subject: [PATCH 057/298] edit --- src/main/java/org/usf/jquery/core/Comparator.java | 2 +- src/main/java/org/usf/jquery/core/JqueryType.java | 2 +- src/main/java/org/usf/jquery/core/ParameterSet.java | 2 +- src/main/java/org/usf/jquery/core/WindowView.java | 5 ++--- src/main/java/org/usf/jquery/web/ArgumentParsers.java | 2 +- .../java/org/usf/jquery/web/RequestEntryChain.java | 11 ++++------- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index a13f60b0..7afb5724 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -110,7 +110,7 @@ static TypedComparator notNull() { //in comparator static TypedComparator in() { - return new TypedComparator(inComparator("IN"), required(), varargs(firstArgType())); + return new TypedComparator(inComparator("IN"), required(), varargs(firstArgType())); // in query } static TypedComparator notIn() { diff --git a/src/main/java/org/usf/jquery/core/JqueryType.java b/src/main/java/org/usf/jquery/core/JqueryType.java index bc75ea64..be2a062c 100644 --- a/src/main/java/org/usf/jquery/core/JqueryType.java +++ b/src/main/java/org/usf/jquery/core/JqueryType.java @@ -12,7 +12,7 @@ public enum JqueryType implements JavaType { COLUMN(DBColumn.class), ORDER(DBOrder.class), - CLAUSE(OperationColumn.class), + CLAUSE(OperationColumn.class), //+predicate FILTER(DBFilter.class); //expression, WHEN_THEN, ... diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index 6921aa3e..65ada508 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -23,7 +23,7 @@ public final class ParameterSet { private final Parameter[] parameters; public Object[] args(Object... args) { - var arr = isNull(args) ? new Object[0] : args; + var arr = isNull(args) ? NO_PARAM : args; forEach(arr.length, (p,i)-> { if(!p.accept(i, arr)) { throw badArgumentTypeException(); diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index afcc3d07..3ee14eba 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -19,8 +19,8 @@ public final class WindowView implements TaggableView { private final TaggableColumn column; //named operation column @Override - public String sql(QueryParameterBuilder builder, String schema) { //sub query should not use main builder - var b = addWithValue("w"); + public String sql(QueryParameterBuilder builder, String schema) { + var b = addWithValue("w"); //sub query should not use main builder return new SqlStringBuilder(100) .append("(SELECT ").append(member(b.view(view), "*")).append(", ").append(column.sqlWithTag(b)) .append(" FROM ").append(view.sqlWithTag(b, schema)).append(")") @@ -36,7 +36,6 @@ public String tagname() { //inherits tagname public String toString() { return sql(addWithValue(), ""); } - public static DBColumn windowColumn(TaggableView view, TaggableColumn column) { var wv = new WindowView(view, column); diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index ed0585bc..ddcaa402 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -60,7 +60,7 @@ else if(type instanceof JDBCType) { return jdbcArgParser((JDBCType) type).parse(entry, td); } else { - throw new UnsupportedOperationException("unsupported " + type); + throw unsupportedTypeException(type); } } catch (Exception e) {/*do not throw exception*/} } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index f79bc225..d7683376 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -269,17 +269,14 @@ private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps) { } ps.forEach(arr.length, (p,i)-> { if(i>0 || inc==0) { //arg0 already parsed - arr[i] = args.get(i-inc).parseValue(td, p.types(arr)); + var e = args.get(i-inc); + arr[i] = isNull(e.value) || e.text + ? e.requireNoArgs().value + : parse(e, td, p.types(arr)); } }); return arr; } - - private Object parseValue(TableDecorator td, JavaType[] types) { - return isNull(value) || text - ? requireNoArgs().value - : parse(this, td, types); - } RequestEntryChain requireNoArgs() { if(isNull(args)) { From 23b24f7386d4bc030ecb1874f38ba818e953eae9 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 30 Jan 2024 21:01:40 +0100 Subject: [PATCH 058/298] edit --- .../java/org/usf/jquery/core/ArgTypeRef.java | 2 +- .../java/org/usf/jquery/core/CaseColumn.java | 2 +- .../java/org/usf/jquery/core/Comparator.java | 12 +++++-- .../java/org/usf/jquery/core/DBColumn.java | 3 +- .../java/org/usf/jquery/core/JDBCType.java | 22 ++++++------ .../java/org/usf/jquery/core/JavaType.java | 11 ++++-- .../java/org/usf/jquery/core/JqueryType.java | 7 ++-- .../org/usf/jquery/core/OperationColumn.java | 2 +- .../java/org/usf/jquery/core/Operator.java | 12 +++---- .../java/org/usf/jquery/core/Parameter.java | 2 +- src/main/java/org/usf/jquery/core/Typed.java | 12 ------- .../org/usf/jquery/core/TypedComparator.java | 35 +++++++++++++++---- .../org/usf/jquery/core/TypedOperator.java | 8 ++--- .../java/org/usf/jquery/core/ViewColumn.java | 2 +- .../java/org/usf/jquery/core/WindowView.java | 4 +-- .../org/usf/jquery/web/ArgumentParsers.java | 3 +- .../org/usf/jquery/web/ColumnMetadata.java | 6 ++-- .../org/usf/jquery/web/RequestEntryChain.java | 5 ++- 18 files changed, 87 insertions(+), 63 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/core/Typed.java diff --git a/src/main/java/org/usf/jquery/core/ArgTypeRef.java b/src/main/java/org/usf/jquery/core/ArgTypeRef.java index d045658f..7b78aa84 100644 --- a/src/main/java/org/usf/jquery/core/ArgTypeRef.java +++ b/src/main/java/org/usf/jquery/core/ArgTypeRef.java @@ -12,7 +12,7 @@ */ interface ArgTypeRef extends Function { - static ArgTypeRef firstArgType() { + static ArgTypeRef firstArgJdbcType() { return arr-> typeOf(requireAtLeastNArgs(1, arr, ()-> "ArgTypeRef function")[0]).orElse(null); // not sure } diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index e41ad9ff..64ac191b 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -29,7 +29,7 @@ public String sql(QueryParameterBuilder builder) { } @Override - public JavaType javaType() { + public JavaType getType() { return expressions.stream() .map(JDBCType::typeOf) .filter(Optional::isPresent) // should have same type diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 7afb5724..6ea50fc0 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -1,11 +1,13 @@ package org.usf.jquery.core; import static java.util.Arrays.copyOfRange; -import static org.usf.jquery.core.ArgTypeRef.firstArgType; +import static org.usf.jquery.core.ArgTypeRef.firstArgJdbcType; import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.JqueryType.FILTER; +import static org.usf.jquery.core.JqueryType.QUERY; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; +import static org.usf.jquery.core.ParameterSet.ofParameters; import java.util.Optional; import java.util.function.UnaryOperator; @@ -110,11 +112,15 @@ static TypedComparator notNull() { //in comparator static TypedComparator in() { - return new TypedComparator(inComparator("IN"), required(), varargs(firstArgType())); // in query + return new TypedComparator(inComparator("IN"), + ofParameters(required(), varargs(firstArgJdbcType())), + ofParameters(required(), required(QUERY))); } static TypedComparator notIn() { - return new TypedComparator(inComparator("NOT IN"), required(), varargs(firstArgType())); + return new TypedComparator(inComparator("NOT IN"), + ofParameters(required(), varargs(firstArgJdbcType())), + ofParameters(required(), required(QUERY))); } //comparator chain diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 7618c6fe..1cb30f45 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -7,6 +7,7 @@ import java.util.function.Supplier; import org.usf.jquery.core.CaseSingleColumnBuilder.WhenFilterBridge; +import org.usf.jquery.core.JavaType.Typed; import lombok.NonNull; @@ -35,7 +36,7 @@ default boolean isConstant() { return false; } - default JavaType javaType() { + default JavaType getType() { return null; } diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index 2774ea7c..44021d8f 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -1,7 +1,6 @@ package org.usf.jquery.core; import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; import static java.util.Optional.empty; /** @@ -30,8 +29,7 @@ @Getter @RequiredArgsConstructor public enum JDBCType implements JavaType { - - //do not change enum order + BOOLEAN(Types.BOOLEAN, Boolean.class, JDBCType::isBoolean), BIT(Types.BIT, Boolean.class, JDBCType::isBoolean), TINYINT(Types.TINYINT, Byte.class, Number.class, Number.class::isInstance), @@ -51,7 +49,12 @@ public enum JDBCType implements JavaType { TIME(Types.TIME, Time.class, Time.class::isInstance), TIMESTAMP(Types.TIMESTAMP, Timestamp.class, Timestamp.class::isInstance), TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class, Timestamp.class::isInstance), - OTHER(Types.OTHER, Object.class, null, o-> false); + OTHER(Types.OTHER, Object.class, null) { //readonly + @Override + public boolean accept(Object o) { + return false; + } + }; private final int value; private final Class type; @@ -63,16 +66,15 @@ private JDBCType(int value, Class type, Predicate matcher) { } @Override - public Class type() { + public Class typeClass() { return type; } @Override public boolean accept(Object o) { if(o instanceof Typed) { - var t = ((Typed) o).javaType(); - return t == null || t == this - || (nonNull(superType) && superType.isAssignableFrom(t.type())); + var t = ((Typed) o).getType(); + return t == this || isNull(t) || superType.isAssignableFrom(t.typeClass()); } return isNull(o) || matcher.test(o); } @@ -95,10 +97,10 @@ private static boolean isString(Object o) { public static Optional typeOf(Object o) { if(o instanceof Typed) { - var t = ((Typed) o).javaType(); + var t = ((Typed) o).getType(); return t instanceof JDBCType ? Optional.of((JDBCType) t) : empty(); } - return Optional.of(o).flatMap(v-> findType(e-> e.type().isInstance(o))); + return Optional.of(o).flatMap(v-> findType(e-> e.typeClass().isInstance(o))); } public static Optional fromDataType(int value) { diff --git a/src/main/java/org/usf/jquery/core/JavaType.java b/src/main/java/org/usf/jquery/core/JavaType.java index 856d3518..c0db1ce8 100644 --- a/src/main/java/org/usf/jquery/core/JavaType.java +++ b/src/main/java/org/usf/jquery/core/JavaType.java @@ -10,9 +10,14 @@ @FunctionalInterface public interface JavaType { - Class type(); + Class typeClass(); - default boolean accept(Object o) { // nullable by default - return isNull(o) || type().isInstance(o); + default boolean accept(Object o) { + return isNull(o) || typeClass().isInstance(o); + } + + public interface Typed { + + JavaType getType(); // return null by default } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/JqueryType.java b/src/main/java/org/usf/jquery/core/JqueryType.java index be2a062c..520f7838 100644 --- a/src/main/java/org/usf/jquery/core/JqueryType.java +++ b/src/main/java/org/usf/jquery/core/JqueryType.java @@ -12,14 +12,15 @@ public enum JqueryType implements JavaType { COLUMN(DBColumn.class), ORDER(DBOrder.class), - CLAUSE(OperationColumn.class), //+predicate - FILTER(DBFilter.class); + CLAUSE(OperationColumn.class), //SELECT, WHERE, ORDER, PARTITION + FILTER(DBFilter.class), + QUERY(RequestQueryBuilder.class); //SELECT CLAUSE //expression, WHEN_THEN, ... private final Class type; @Override - public Class type() { + public Class typeClass() { return type; } diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index e0914ac1..5ce173e4 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -33,7 +33,7 @@ public String sql(QueryParameterBuilder builder) { } @Override - public JavaType javaType() { + public JavaType getType() { return type; } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 1623e640..ad07f5f4 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.ArgTypeRef.firstArgType; +import static org.usf.jquery.core.ArgTypeRef.firstArgJdbcType; import static org.usf.jquery.core.Database.TERADATA; import static org.usf.jquery.core.JDBCType.BIGINT; import static org.usf.jquery.core.JDBCType.DATE; @@ -237,7 +237,7 @@ static TypedOperator decimal() { //other functions static TypedOperator coalesce() { - return new TypedOperator(firstArgType(), function("COALESCE"), required(), required()); + return new TypedOperator(firstArgJdbcType(), function("COALESCE"), required(), required()); } //aggregate functions @@ -247,11 +247,11 @@ static TypedOperator count() { } static TypedOperator min() { - return new TypedOperator(firstArgType(), aggregation("MIN"), required()); + return new TypedOperator(firstArgJdbcType(), aggregation("MIN"), required()); } static TypedOperator max() { - return new TypedOperator(firstArgType(), aggregation("MAX"), required()); + return new TypedOperator(firstArgJdbcType(), aggregation("MAX"), required()); } static TypedOperator sum() { @@ -279,7 +279,7 @@ static TypedOperator denseRank() { //pipe functions static TypedOperator over() { - return new TypedOperator(firstArgType(), pipe("OVER"), required(COLUMN), optional(CLAUSE), optional(CLAUSE)) { + return new TypedOperator(firstArgJdbcType(), pipe("OVER"), required(COLUMN), optional(CLAUSE), optional(CLAUSE)) { @Override public OperationColumn args(Object... args) { return super.args(args).aggregation(false); //over aggregation functions @@ -316,7 +316,7 @@ static TypedOperator ctimestamp() { @Deprecated(forRemoval = true) static TypedOperator value() { - return new TypedOperator(firstArgType(), VALUE_RETURN, required()); + return new TypedOperator(firstArgJdbcType(), VALUE_RETURN, required()); } static ArithmeticOperator operator(String symbol) { diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 080e6f19..8226a76f 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -19,7 +19,7 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class Parameter { - private final JavaType[] types; //null => accept all + private final JavaType[] types; //empty => accept all types private final ArgTypeRef typeRef; private final boolean required; private final boolean varargs; diff --git a/src/main/java/org/usf/jquery/core/Typed.java b/src/main/java/org/usf/jquery/core/Typed.java deleted file mode 100644 index 6f9c4c39..00000000 --- a/src/main/java/org/usf/jquery/core/Typed.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.usf.jquery.core; - -/** - * - * @author u$f - * - */ -public interface Typed { - - JavaType javaType(); - -} diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 31720eb4..a9abf0f2 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -1,7 +1,8 @@ package org.usf.jquery.core; -import static java.util.function.UnaryOperator.identity; +import static java.util.Objects.isNull; import static org.usf.jquery.core.ParameterSet.ofParameters; +import static org.usf.jquery.core.Utils.isEmpty; import java.util.function.UnaryOperator; @@ -19,24 +20,44 @@ public final class TypedComparator implements Comparator { @Delegate private final Comparator comparator; private final ParameterSet parameterSet; - private UnaryOperator argMapper = identity(); + private final ParameterSet[] overloads; + private UnaryOperator argMapper; public TypedComparator(Comparator comparator, Parameter... parameters) { + this(comparator, ofParameters(parameters)); + } + + public TypedComparator(Comparator comparator, ParameterSet parameterSet, ParameterSet... overloads) { this.comparator = comparator; - this.parameterSet = ofParameters(parameters); + this.parameterSet = parameterSet; + this.overloads = overloads; } - @Override public DBFilter args(Object... args) { - args = parameterSet.args(args); - return comparator.args(argMapper.apply(args)); + try { + return internalArgs(parameterSet, args); + } catch (RuntimeException e) { + if(!isEmpty(overloads)) { + for(var ps : overloads) { + try { + return internalArgs(ps, args); + } catch (RuntimeException e1) { /* do not throw exception */ } + } + } + throw e; //wrap exception + } + } + + private DBFilter internalArgs(ParameterSet ps, Object... args) { + args = ps.args(args); + return comparator.args(isNull(argMapper) ? args : argMapper.apply(args)); } public Comparator unwrap() { return comparator; } - TypedComparator argsMapper(UnaryOperator argMapper) { + public TypedComparator argsMapper(UnaryOperator argMapper) { this.argMapper = argMapper; return this; } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index aa99db72..dc384dc7 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static java.util.function.UnaryOperator.identity; +import static java.util.Objects.isNull; import static org.usf.jquery.core.ParameterSet.ofParameters; import java.util.function.UnaryOperator; @@ -20,7 +20,7 @@ public class TypedOperator implements Operator { private final Operator operator; private final ArgTypeRef typeFn; private final ParameterSet parameterSet; - private UnaryOperator argMapper = identity(); + private UnaryOperator argMapper; public TypedOperator(JavaType type, Operator function, Parameter... args) { this(o-> type, function, args); @@ -35,14 +35,14 @@ public TypedOperator(ArgTypeRef typeFn, Operator function, Parameter... paramete @Override public OperationColumn args(Object... args) { args = parameterSet.args(args); - return new OperationColumn(operator, argMapper.apply(args), typeFn.apply(args)); + return new OperationColumn(operator, isNull(argMapper) ? args : argMapper.apply(args), typeFn.apply(args)); } public Operator unwrap() { return operator; } - TypedOperator argsMapper(UnaryOperator argMapper) { + public TypedOperator argsMapper(UnaryOperator argMapper) { this.argMapper = argMapper; return this; } diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index f32d4113..f6907ee9 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -28,7 +28,7 @@ public String sql(QueryParameterBuilder arg) { } @Override - public JavaType javaType() { + public JavaType getType() { return type; } diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index 3ee14eba..db0e2c8f 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -45,8 +45,8 @@ public String sql(QueryParameterBuilder builder) { //overwrite view return member(builder.overwriteView(wv), doubleQuote(column.tagname())); } @Override - public JavaType javaType() { - return column.javaType(); + public JavaType getType() { + return column.getType(); } @Override public String toString() { diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index ddcaa402..2400fa95 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -101,7 +101,8 @@ public static JavaArgumentParser jqueryArgParser(@NonNull JqueryType type) { case COLUMN: return RequestEntryChain::asColumn; case ORDER : return RequestEntryChain::asOrder; case FILTER: return RequestEntryChain::asFilter; - case CLAUSE: return RequestEntryChain::asOperation; + case CLAUSE: return RequestEntryChain::asOperation; //filter + case QUERY : //TODO view(column(..), filter(..)) || column.view(filters..) default: throw unsupportedTypeException(type); } } diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index 87ec4203..1c533acd 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -42,15 +42,15 @@ ColumnMetadata reset() { } public String toJavaType(){ - return dataType.type().getSimpleName(); + return dataType.typeClass().getSimpleName(); } public String toSqlType(){ var s = dataType.name(); - if(dataType.type() == String.class && dataSize < MAX_VALUE) { + if(dataType.typeClass() == String.class && dataSize < MAX_VALUE) { s+= "(" + dataSize + ")"; } - if(dataType.type() == Timestamp.class) { + if(dataType.typeClass() == Timestamp.class) { s+= "(" + precision + ")"; } if(dataType == REAL || dataType == NUMERIC || dataType == DECIMAL || dataType == FLOAT || dataType == DOUBLE) { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index d7683376..17b3a820 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -23,7 +23,6 @@ import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; import org.usf.jquery.core.DBOrder; -import org.usf.jquery.core.JavaType; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; import org.usf.jquery.core.ParameterSet; @@ -257,8 +256,8 @@ private DBFilter fillArgs(TableDecorator td, DBObject col, TypedComparator cmp) return cmp.args(toArgs(td, col, cmp.getParameterSet())); } - private OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator op) { - return op.args(toArgs(td, col, op.getParameterSet())); + private OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator opr) { + return opr.args(toArgs(td, col, opr.getParameterSet())); } private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps) { From ffdf6c88adad28b108fe4e1166aed61a5975572b Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 31 Jan 2024 01:24:49 +0100 Subject: [PATCH 059/298] edit --- .../java/org/usf/jquery/core/InCompartor.java | 2 +- .../org/usf/jquery/core/InternalQuery.java | 21 ++++++++ .../java/org/usf/jquery/core/JqueryType.java | 11 +++-- .../java/org/usf/jquery/core/Operator.java | 25 +++------- .../java/org/usf/jquery/core/OverClause.java | 47 ++++-------------- .../org/usf/jquery/core/TypedOperator.java | 7 ++- .../org/usf/jquery/web/ArgumentParsers.java | 34 ++++++++++--- .../org/usf/jquery/web/RequestEntryChain.java | 49 +++++++++++++++++-- .../org/usf/jquery/web/TableDecorator.java | 2 + 9 files changed, 124 insertions(+), 74 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/InternalQuery.java diff --git a/src/main/java/org/usf/jquery/core/InCompartor.java b/src/main/java/org/usf/jquery/core/InCompartor.java index d2d618b1..93c8ad8f 100644 --- a/src/main/java/org/usf/jquery/core/InCompartor.java +++ b/src/main/java/org/usf/jquery/core/InCompartor.java @@ -19,6 +19,6 @@ default String sql(QueryParameterBuilder builder, Object[] args) { requireAtLeastNArgs(2, args, InCompartor.class::getSimpleName); var type = typeOf(args[0]).orElseThrow(Comparator::typeCannotBeNullException); var varg = copyOfRange(args, 1, args.length); - return builder.appendLitteral(args[0]) + SPACE + id() + parenthese(builder.appendArrayParameter(type, varg)); + return builder.appendLitteral(args[0]) + SPACE + id() + parenthese(args.length == 2 && args[1] instanceof InternalQuery ? builder.appendLitteral(args[1]) : builder.appendArrayParameter(type, varg)); } } diff --git a/src/main/java/org/usf/jquery/core/InternalQuery.java b/src/main/java/org/usf/jquery/core/InternalQuery.java new file mode 100644 index 00000000..2cd91d3c --- /dev/null +++ b/src/main/java/org/usf/jquery/core/InternalQuery.java @@ -0,0 +1,21 @@ +package org.usf.jquery.core; + +import static java.util.stream.Collectors.joining; + +import java.util.stream.Collectors; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class InternalQuery implements DBObject { + + private final DBColumn[] columns; + private final DBFilter[] filters; + + @Override + public String sql(QueryParameterBuilder builder, Object[] args) { + var b = QueryParameterBuilder.addWithValue("r"); + return "SELECT " + b.appendLitteralArray(columns) + " FROM " + b.views().stream().map(v-> v.sqlWithTag(b, null)).collect(joining(",")); + } + +} diff --git a/src/main/java/org/usf/jquery/core/JqueryType.java b/src/main/java/org/usf/jquery/core/JqueryType.java index 520f7838..18f7bfc3 100644 --- a/src/main/java/org/usf/jquery/core/JqueryType.java +++ b/src/main/java/org/usf/jquery/core/JqueryType.java @@ -10,11 +10,14 @@ @RequiredArgsConstructor public enum JqueryType implements JavaType { - COLUMN(DBColumn.class), - ORDER(DBOrder.class), - CLAUSE(OperationColumn.class), //SELECT, WHERE, ORDER, PARTITION + COLUMN(DBColumn.class), FILTER(DBFilter.class), - QUERY(RequestQueryBuilder.class); //SELECT CLAUSE + ORDER(DBOrder.class), + PARTITIONS(DBColumn[].class), + COLUMNS(DBColumn[].class), + FILTERS(DBFilter[].class), + ORDERS(DBOrder[].class), + QUERY(InternalQuery.class); //SELECT CLAUSE //expression, WHEN_THEN, ... private final Class type; diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index ad07f5f4..96180496 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -10,10 +10,9 @@ import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; import static org.usf.jquery.core.JDBCType.VARCHAR; -import static org.usf.jquery.core.JqueryType.CLAUSE; import static org.usf.jquery.core.JqueryType.COLUMN; -import static org.usf.jquery.core.JqueryType.ORDER; -import static org.usf.jquery.core.OverClause.clauses; +import static org.usf.jquery.core.JqueryType.ORDERS; +import static org.usf.jquery.core.JqueryType.PARTITIONS; import static org.usf.jquery.core.Parameter.optional; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; @@ -22,7 +21,6 @@ import static org.usf.jquery.core.Validation.requireNArgs; import java.util.Optional; -import java.util.stream.Stream; /** * @@ -279,25 +277,14 @@ static TypedOperator denseRank() { //pipe functions static TypedOperator over() { - return new TypedOperator(firstArgJdbcType(), pipe("OVER"), required(COLUMN), optional(CLAUSE), optional(CLAUSE)) { + return new TypedOperator(firstArgJdbcType(), pipe("OVER"), required(COLUMN), optional(PARTITIONS), optional(ORDERS)) { @Override public OperationColumn args(Object... args) { return super.args(args).aggregation(false); //over aggregation functions } - }.argsMapper(args->{ //map args after check - var c = Stream.of(args).skip(1).toArray(OperationColumn[]::new); - return new Object[] {args[0], clauses(c)}; - }); - } - - //clause functions - - static TypedOperator partition() { - return new TypedOperator(CLAUSE, clause("PARTITION BY"), required(COLUMN), varargs(COLUMN)); - } - - static TypedOperator order() { - return new TypedOperator(CLAUSE, clause("ORDER BY"), required(ORDER), varargs(ORDER)); + }.argsMapper(args-> new Object[] {args[0], new OverClause( + args.length > 1 ? (DBColumn[])args[1] : null, + args.length > 2 ? (DBOrder[]) args[2] : null)}); } // constant operators diff --git a/src/main/java/org/usf/jquery/core/OverClause.java b/src/main/java/org/usf/jquery/core/OverClause.java index 1a7fc14c..cf3147a5 100644 --- a/src/main/java/org/usf/jquery/core/OverClause.java +++ b/src/main/java/org/usf/jquery/core/OverClause.java @@ -1,15 +1,10 @@ package org.usf.jquery.core; -import static java.util.Objects.isNull; import static java.util.Objects.nonNull; -import static java.util.stream.Collectors.groupingBy; import static org.usf.jquery.core.SqlStringBuilder.SPACE; +import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireNoArgs; -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; - import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -18,11 +13,11 @@ * @author u$f * */ -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class OverClause implements DBObject { - private final OperationColumn partition; - private final OperationColumn order; + private final DBColumn[] partitions; + private final DBOrder[] orders; public OverClause() { this(null, null); @@ -36,37 +31,13 @@ public String sql(QueryParameterBuilder builder, Object[] args) { String sql(QueryParameterBuilder builder) { var sb = new SqlStringBuilder(100); - if(nonNull(partition)) { - sb.append(partition.sql(builder)); + if(!isEmpty(partitions)) { + sb.append("PARTITION BY ").append(builder.appendLitteralArray(partitions)); } - if(nonNull(order)) { //require orders - sb.appendIf(nonNull(partition), SPACE).append(order.sql(builder)); + if(!isEmpty(orders)) { //require orders + sb.appendIf(nonNull(partitions), SPACE) + .append("ORDER BY ").append(builder.appendLitteralArray(orders)); } return sb.toString(); } - - public static OverClause clauses(OperationColumn... args) { //partition, order, ... - if(isNull(args)) { - return new OverClause(); - } - var map = Stream.of(args).collect(groupingBy(o-> o.getOperator().id())); - var prt = requireOneArg(map, "PARTITION BY"); - var ord = requireOneArg(map, "ORDER BY"); - if(map.isEmpty()) { - return new OverClause(prt, ord); - } - throw new IllegalArgumentException("illegal over function arguments : " + map.keySet()); - } - - private static OperationColumn requireOneArg(Map> map, String key) { - var args = map.remove(key); - if(isNull(args)) { - return null; - } - if(args.size() == 1) { - //instance of ClauseFunction ? - return args.get(0); - } - throw new IllegalArgumentException("duplicated arg values " + key); - } } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index dc384dc7..d06f9863 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.ParameterSet.ofParameters; import java.util.function.UnaryOperator; @@ -35,7 +35,10 @@ public TypedOperator(ArgTypeRef typeFn, Operator function, Parameter... paramete @Override public OperationColumn args(Object... args) { args = parameterSet.args(args); - return new OperationColumn(operator, isNull(argMapper) ? args : argMapper.apply(args), typeFn.apply(args)); + if(nonNull(argMapper)) { + args = argMapper.apply(args); + } + return new OperationColumn(operator, args, typeFn.apply(args)); } public Operator unwrap() { diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 2400fa95..5334ba77 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -8,6 +8,13 @@ import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; import static org.usf.jquery.core.JqueryType.COLUMN; +import static org.usf.jquery.core.JqueryType.COLUMNS; +import static org.usf.jquery.core.JqueryType.FILTER; +import static org.usf.jquery.core.JqueryType.FILTERS; +import static org.usf.jquery.core.JqueryType.ORDER; +import static org.usf.jquery.core.Parameter.optional; +import static org.usf.jquery.core.Parameter.required; +import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.web.ParseException.cannotParseException; @@ -21,9 +28,14 @@ import java.time.ZonedDateTime; import java.util.stream.Stream; +import org.usf.jquery.core.DBColumn; +import org.usf.jquery.core.DBFilter; +import org.usf.jquery.core.InternalQuery; import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.JavaType; import org.usf.jquery.core.JqueryType; +import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.TaggableColumn; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -98,14 +110,24 @@ public static JDBCArgumentParser jdbcArgParser(JDBCType type) { public static JavaArgumentParser jqueryArgParser(@NonNull JqueryType type) { switch (type) { - case COLUMN: return RequestEntryChain::asColumn; - case ORDER : return RequestEntryChain::asOrder; - case FILTER: return RequestEntryChain::asFilter; - case CLAUSE: return RequestEntryChain::asOperation; //filter - case QUERY : //TODO view(column(..), filter(..)) || column.view(filters..) - default: throw unsupportedTypeException(type); + case COLUMN: return RequestEntryChain::asColumn; + case FILTER: return RequestEntryChain::asFilter; + case ORDER: return RequestEntryChain::asOrder; + case PARTITIONS: return (re, td)-> re.evalArray(td, "partition", COLUMN); + case COLUMNS: return (re, td)-> re.evalArray(td, Constants.COLUMN, COLUMN); + case FILTERS: return (re, td)-> re.evalArray(td, "filter", FILTER); + case ORDERS: return (re, td)-> re.evalArray(td, Constants.ORDER, ORDER); + case QUERY: return ArgumentParsers::parseQuery; + default: throw unsupportedTypeException(type); } } + + private static InternalQuery parseQuery(RequestEntryChain re, TableDecorator td) { + var args = re.evalFunction(td, "query", ofParameters(required(COLUMNS), optional(FILTERS))); + var cols = (DBColumn[]) args[0]; + var flts = args.length > 1 ? (DBFilter[]) args[1] : null; + return new InternalQuery(cols, flts); + } private static Object parseUnknown(String s) { for(var p : STD_PRS) { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 17b3a820..c06a4ab8 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -1,13 +1,18 @@ package org.usf.jquery.web; +import static java.lang.reflect.Array.newInstance; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Comparator.in; import static org.usf.jquery.core.Comparator.lookupComparator; +import static org.usf.jquery.core.JqueryType.COLUMN; import static org.usf.jquery.core.Operator.lookupOperator; import static org.usf.jquery.core.Operator.lookupWindowFunction; +import static org.usf.jquery.core.Parameter.required; +import static org.usf.jquery.core.Parameter.varargs; +import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.WindowView.windowColumn; @@ -15,7 +20,9 @@ import static org.usf.jquery.web.ColumnDecorator.ofColumn; import static org.usf.jquery.web.JQueryContext.context; +import java.lang.reflect.Array; import java.util.List; +import java.util.function.IntFunction; import java.util.stream.Stream; import org.usf.jquery.core.ComparisonExpression; @@ -23,9 +30,12 @@ import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; import org.usf.jquery.core.DBOrder; +import org.usf.jquery.core.JqueryType; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; +import org.usf.jquery.core.Parameter; import org.usf.jquery.core.ParameterSet; +import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedComparator; import org.usf.jquery.core.TypedOperator; @@ -54,6 +64,24 @@ final class RequestEntryChain { public RequestEntryChain(String value) { this(value, false); } + + public Object[] evalFunction(TableDecorator td, String fnName, ParameterSet ps) { + if(fnName.equals(value)) { + return toArgs(td, null, ps, Object[]::new); + } + throw new IllegalArgumentException(); + } + + public Object[] evalArray(TableDecorator td, String fnName, JqueryType type) { + if(fnName.equals(value)) { + var c = type.typeClass(); + if(c.isArray()) { + throw new UnsupportedOperationException(); + } + return toArgs(td, null, ofParameters(required(type), varargs(type)), s-> (Object[]) newInstance(c, s)); + } + throw new IllegalArgumentException(); + } public TaggableColumn asColumn(TableDecorator td) { var t = lookupResource(td); @@ -253,16 +281,29 @@ private Triple lookupViewResource(TableDecorator td) { } private DBFilter fillArgs(TableDecorator td, DBObject col, TypedComparator cmp) { - return cmp.args(toArgs(td, col, cmp.getParameterSet())); + try { + return cmp.args(toArgs(td, col, cmp.getParameterSet(), Object[]::new)); + } + catch(Exception e) { + for(var ps : cmp.getOverloads()) { + try { + return cmp.args(toArgs(td, col, ps, Object[]::new)); + } + catch(Exception e1) { + System.out.println(e1); + } + } + throw e; + } } private OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator opr) { - return opr.args(toArgs(td, col, opr.getParameterSet())); + return opr.args(toArgs(td, col, opr.getParameterSet(), Object[]::new)); } - private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps) { + private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps, IntFunction arrFn) { int inc = nonNull(col) ? 1 : 0; - var arr = new Object[isNull(args) ? inc : args.size() + inc]; + var arr = arrFn.apply(isNull(args) ? inc : args.size() + inc); if(nonNull(col)) { arr[0] = col; } diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index b0931f52..2250d043 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -2,6 +2,7 @@ import static java.lang.Integer.parseInt; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Utils.isEmpty; @@ -23,6 +24,7 @@ import java.util.Collection; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; From 9699cfd96900bd116111394a3c98dc6bd88ca7c3 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 31 Jan 2024 23:24:31 +0100 Subject: [PATCH 060/298] edit --- .../usf/jquery/core/AggregateFunction.java | 1 - .../org/usf/jquery/core/ClauseFunction.java | 26 ----------- .../java/org/usf/jquery/core/Comparator.java | 4 +- .../java/org/usf/jquery/core/DBTable.java | 6 +-- src/main/java/org/usf/jquery/core/DBView.java | 10 ++--- .../org/usf/jquery/core/InternalQuery.java | 44 ++++++++++++++++--- .../java/org/usf/jquery/core/JqueryType.java | 4 +- .../java/org/usf/jquery/core/Operator.java | 4 -- src/main/java/org/usf/jquery/core/Query.java | 11 +++++ .../jquery/core/QueryParameterBuilder.java | 21 ++++++--- .../usf/jquery/core/RequestQueryBuilder.java | 14 +++--- .../org/usf/jquery/core/TaggableView.java | 4 +- src/main/java/org/usf/jquery/core/Utils.java | 2 +- .../java/org/usf/jquery/core/WindowView.java | 20 ++++----- .../org/usf/jquery/web/ArgumentParsers.java | 8 ++-- .../org/usf/jquery/web/RequestEntryChain.java | 7 +-- .../org/usf/jquery/web/RevisionIterator.java | 4 +- .../org/usf/jquery/web/TableDecorator.java | 4 +- 18 files changed, 101 insertions(+), 93 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/core/ClauseFunction.java create mode 100644 src/main/java/org/usf/jquery/core/Query.java diff --git a/src/main/java/org/usf/jquery/core/AggregateFunction.java b/src/main/java/org/usf/jquery/core/AggregateFunction.java index 76fa9192..cbe7b1a8 100644 --- a/src/main/java/org/usf/jquery/core/AggregateFunction.java +++ b/src/main/java/org/usf/jquery/core/AggregateFunction.java @@ -12,5 +12,4 @@ public interface AggregateFunction extends WindowFunction { default boolean isAggregation() { return true; } - } diff --git a/src/main/java/org/usf/jquery/core/ClauseFunction.java b/src/main/java/org/usf/jquery/core/ClauseFunction.java deleted file mode 100644 index 85f7b79e..00000000 --- a/src/main/java/org/usf/jquery/core/ClauseFunction.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.usf.jquery.core; - -import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.SqlStringBuilder.EMPTY; -import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.SqlStringBuilder.SPACE; -import static org.usf.jquery.core.Utils.isEmpty; - -import java.util.stream.Stream; - -/** - * - * @author u$f - * - */ -@FunctionalInterface -public interface ClauseFunction extends FunctionOperator { - - @Override - default String sql(QueryParameterBuilder builder, Object[] args) { - return isEmpty(args) - ? EMPTY - : id() + SPACE + Stream.of(args).map(builder::appendLitteral).collect(joining(SCOMA)); - } - -} diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 6ea50fc0..e63efc10 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -102,11 +102,11 @@ static TypedComparator notILike() { //null comparator static TypedComparator isNull() { - return new TypedComparator(nullComparator("IS NULL")); // takes no args + return new TypedComparator(nullComparator("IS NULL"), required()); } static TypedComparator notNull() { - return new TypedComparator(nullComparator("IS NOT NULL")); // takes no args + return new TypedComparator(nullComparator("IS NOT NULL"), required()); } //in comparator diff --git a/src/main/java/org/usf/jquery/core/DBTable.java b/src/main/java/org/usf/jquery/core/DBTable.java index 88a7c1b3..74dd5166 100644 --- a/src/main/java/org/usf/jquery/core/DBTable.java +++ b/src/main/java/org/usf/jquery/core/DBTable.java @@ -17,8 +17,8 @@ public class DBTable implements TaggableView { private final String tag; @Override - public String sql(QueryParameterBuilder builder, String schema) { - return member(schema, name); + public String sql(QueryParameterBuilder builder) { + return member(builder.getSchema(), name); } @Override @@ -28,6 +28,6 @@ public String tagname() { @Override public String toString() { - return sql(addWithValue(), ""); + return sql(addWithValue()); } } diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 90c6aaa3..0095979d 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -1,7 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Validation.requireAtMostNArgs; +import static org.usf.jquery.core.Validation.requireNoArgs; /** * @@ -13,14 +12,13 @@ public interface DBView extends DBObject { @Override default String sql(QueryParameterBuilder builder, Object[] args) { - requireAtMostNArgs(1, args, DBView.class::getSimpleName); - return sql(builder, isEmpty(args) ? null : args[0].toString()); + requireNoArgs(args, DBColumn.class::getSimpleName); + return sql(builder); } - String sql(QueryParameterBuilder builder, String schema); + String sql(QueryParameterBuilder builder); default TaggableView as(String tagname) { return new NamedView(this, tagname); } - } diff --git a/src/main/java/org/usf/jquery/core/InternalQuery.java b/src/main/java/org/usf/jquery/core/InternalQuery.java index 2cd91d3c..2ba03e17 100644 --- a/src/main/java/org/usf/jquery/core/InternalQuery.java +++ b/src/main/java/org/usf/jquery/core/InternalQuery.java @@ -1,21 +1,55 @@ package org.usf.jquery.core; +import static java.util.function.Predicate.not; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.LogicalOperator.AND; +import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.SqlStringBuilder.SCOMA; +import static org.usf.jquery.core.SqlStringBuilder.space; +import static org.usf.jquery.core.Utils.isEmpty; -import java.util.stream.Collectors; +import java.util.function.Predicate; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; +/** + * + * @author u$f + * + */ @RequiredArgsConstructor -public class InternalQuery implements DBObject { +public class InternalQuery implements Query {//see RequestQueryBuilder::build private final DBColumn[] columns; private final DBFilter[] filters; @Override - public String sql(QueryParameterBuilder builder, Object[] args) { - var b = QueryParameterBuilder.addWithValue("r"); - return "SELECT " + b.appendLitteralArray(columns) + " FROM " + b.views().stream().map(v-> v.sqlWithTag(b, null)).collect(joining(",")); + public String sql(QueryParameterBuilder builder) { + var sub = builder.subQuery(); + var sb = new SqlStringBuilder(100) + .append("SELECT ").append(sub.appendLitteralArray(columns)) + .append(" FROM ").appendEach(sub.views(), SCOMA, v-> v.sqlWithTag(sub)); + filter(sub, sb, "WHERE", not(DBFilter::isAggregation)); +// filter(sub, sb, "HAVING", DBFilter::isAggregation); + return sb.toString(); + } + + void filter(QueryParameterBuilder pb, SqlStringBuilder sb, String caluse, Predicate pre){ + if(!isEmpty(filters)) { + var ex = Stream.of(filters) + .filter(pre) + .map(f-> f.sql(pb)) + .collect(joining(AND.sql())); + if(!ex.isEmpty()) { + sb.append(space(caluse)).append(ex); + } + } + } + + @Override + public String toString() { + return sql(addWithValue()); } } diff --git a/src/main/java/org/usf/jquery/core/JqueryType.java b/src/main/java/org/usf/jquery/core/JqueryType.java index 18f7bfc3..5c482f96 100644 --- a/src/main/java/org/usf/jquery/core/JqueryType.java +++ b/src/main/java/org/usf/jquery/core/JqueryType.java @@ -13,11 +13,11 @@ public enum JqueryType implements JavaType { COLUMN(DBColumn.class), FILTER(DBFilter.class), ORDER(DBOrder.class), + QUERY(Query.class), PARTITIONS(DBColumn[].class), COLUMNS(DBColumn[].class), FILTERS(DBFilter[].class), - ORDERS(DBOrder[].class), - QUERY(InternalQuery.class); //SELECT CLAUSE + ORDERS(DBOrder[].class); //expression, WHEN_THEN, ... private final Class type; diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 96180496..f76fe8f9 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -334,10 +334,6 @@ static PipeFunction pipe(String name) { return ()-> name; } - static ClauseFunction clause(String name) { - return ()-> name; - } - static ConstantOperator constant(String name) { return ()-> name; } diff --git a/src/main/java/org/usf/jquery/core/Query.java b/src/main/java/org/usf/jquery/core/Query.java new file mode 100644 index 00000000..a827a2bb --- /dev/null +++ b/src/main/java/org/usf/jquery/core/Query.java @@ -0,0 +1,11 @@ +package org.usf.jquery.core; + +/** + * + * @author u$f + * + */ +@FunctionalInterface +public interface Query extends DBView { + +} diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 36188b46..b01ad69b 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -16,6 +16,7 @@ import java.util.stream.Stream; import lombok.AccessLevel; +import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -29,6 +30,8 @@ public final class QueryParameterBuilder { private static final String ARG = "?"; + @Getter + private final String schema; private final String vPrefix; private final List args; private final List argTypes; @@ -130,18 +133,22 @@ public static String formatValue(Object o) { } public QueryParameterBuilder withValue() { - return new QueryParameterBuilder(vPrefix, null, null, views); + return new QueryParameterBuilder(schema, vPrefix, null, null, views); + } + + public QueryParameterBuilder subQuery() { + return new QueryParameterBuilder(schema, vPrefix + "_s", args, argTypes, new LinkedList<>()); } public static QueryParameterBuilder addWithValue() { - return new QueryParameterBuilder(null, null, null, null); //no args + return new QueryParameterBuilder(null, null, null, null, null); //no args } - - public static QueryParameterBuilder addWithValue(String prefix) { - return new QueryParameterBuilder(prefix, null, null, new LinkedList<>()); + + public static QueryParameterBuilder parametrized() { + return parametrized(null); } - public static QueryParameterBuilder parametrized() { - return new QueryParameterBuilder("v", new LinkedList<>(), new LinkedList<>(), new ArrayList<>()); + public static QueryParameterBuilder parametrized(String schema) { + return new QueryParameterBuilder(schema, "v", new LinkedList<>(), new LinkedList<>(), new ArrayList<>()); } } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index af309d8a..07ee2c9f 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -81,30 +81,30 @@ public RequestQuery build(String schema) { // requireNonEmpty(tables); requireNonEmpty(columns); var bg = currentTimeMillis(); - var pb = parametrized(); + var pb = parametrized(schema); var sb = new SqlStringBuilder(1000); //avg // pb.tables(tables.stream().map(TaggableView::tagname).toArray(String[]::new)); if(isNull(it)) { - build(sb, pb, schema); + build(sb, pb); } else { - sb.forEach(it, " UNION ALL ", o-> build(sb, pb, schema)); + sb.forEach(it, " UNION ALL ", o-> build(sb, pb)); } log.debug("query built in {} ms", currentTimeMillis() - bg); return new RequestQuery(sb.toString(), pb.args(), pb.argTypes()); } - public final void build(SqlStringBuilder sb, QueryParameterBuilder pb, String schema){ + public final void build(SqlStringBuilder sb, QueryParameterBuilder pb){ where(sb, pb); groupBy(sb); having(sb, pb); orderBy(sb, pb); fetch(sb); - sb.sb.insert(0, select(pb, schema)); //declare all view before FROM + sb.sb.insert(0, select(pb)); //declare all view before FROM } @Deprecated - String select(QueryParameterBuilder pb, String schema){ + String select(QueryParameterBuilder pb){ if(currentDatabase() == TERADATA) { if(nonNull(offset)) { throw new UnsupportedOperationException(""); @@ -119,7 +119,7 @@ String select(QueryParameterBuilder pb, String schema){ .append(SPACE) .appendEach(columns, SCOMA, o-> o.sqlWithTag(pb)) .appendIf(!pb.views().isEmpty(), " FROM ") //TODO finish this - .appendEach(pb.views(), SCOMA, o-> o.sqlWithTag(pb, schema)).toString(); + .appendEach(pb.views(), SCOMA, o-> o.sqlWithTag(pb)).toString(); } void where(SqlStringBuilder sb, QueryParameterBuilder pb){ diff --git a/src/main/java/org/usf/jquery/core/TaggableView.java b/src/main/java/org/usf/jquery/core/TaggableView.java index 4194bed7..ba095aee 100644 --- a/src/main/java/org/usf/jquery/core/TaggableView.java +++ b/src/main/java/org/usf/jquery/core/TaggableView.java @@ -11,7 +11,7 @@ public interface TaggableView extends DBView { String tagname(); - default String sqlWithTag(QueryParameterBuilder builder, String schema) { - return sql(builder, schema) + SPACE + builder.view(this); + default String sqlWithTag(QueryParameterBuilder builder) { + return sql(builder) + SPACE + builder.view(this); } } diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 73e5572d..d6b58ae0 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -16,7 +16,7 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class Utils { //move this - static ThreadLocal context = new ThreadLocal<>(); + static ThreadLocal context = new ThreadLocal<>(); // change it public static final int UNLIMITED = -1; diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java index db0e2c8f..e312581b 100644 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ b/src/main/java/org/usf/jquery/core/WindowView.java @@ -1,6 +1,7 @@ package org.usf.jquery.core; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.SqlStringBuilder.member; @@ -13,32 +14,27 @@ * */ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public final class WindowView implements TaggableView { +public final class WindowView implements Query { private final TaggableView view; private final TaggableColumn column; //named operation column @Override - public String sql(QueryParameterBuilder builder, String schema) { - var b = addWithValue("w"); //sub query should not use main builder + public String sql(QueryParameterBuilder builder) { + var b = builder.subQuery(); //sub query should not use main builder return new SqlStringBuilder(100) - .append("(SELECT ").append(member(b.view(view), "*")).append(", ").append(column.sqlWithTag(b)) - .append(" FROM ").append(view.sqlWithTag(b, schema)).append(")") + .append("(SELECT ").append(member(b.view(view), "*")).append(SCOMA).append(column.sqlWithTag(b)) + .append(" FROM ").append(view.sqlWithTag(b)).append(")") .toString(); } - @Override - public String tagname() { //inherits tagname - return view.tagname(); - } - @Override public String toString() { - return sql(addWithValue(), ""); + return sql(addWithValue()); } public static DBColumn windowColumn(TaggableView view, TaggableColumn column) { - var wv = new WindowView(view, column); + var wv = new WindowView(view, column).as(view.tagname()); return new DBColumn() { @Override public String sql(QueryParameterBuilder builder) { //overwrite view diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 5334ba77..6c295d2e 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -34,8 +34,6 @@ import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.JavaType; import org.usf.jquery.core.JqueryType; -import org.usf.jquery.core.RequestQueryBuilder; -import org.usf.jquery.core.TaggableColumn; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -74,7 +72,7 @@ else if(type instanceof JDBCType) { else { throw unsupportedTypeException(type); } - } catch (Exception e) {/*do not throw exception*/} + } catch (Exception e) {/*do not throw exception*/} // only parseException } throw cannotParseException("value", entry.toString()); } @@ -117,12 +115,12 @@ public static JavaArgumentParser jqueryArgParser(@NonNull JqueryType type) { case COLUMNS: return (re, td)-> re.evalArray(td, Constants.COLUMN, COLUMN); case FILTERS: return (re, td)-> re.evalArray(td, "filter", FILTER); case ORDERS: return (re, td)-> re.evalArray(td, Constants.ORDER, ORDER); - case QUERY: return ArgumentParsers::parseQuery; + case QUERY: return ArgumentParsers::evalQuery; default: throw unsupportedTypeException(type); } } - private static InternalQuery parseQuery(RequestEntryChain re, TableDecorator td) { + private static InternalQuery evalQuery(RequestEntryChain re, TableDecorator td) { var args = re.evalFunction(td, "query", ofParameters(required(COLUMNS), optional(FILTERS))); var cols = (DBColumn[]) args[0]; var flts = args.length > 1 ? (DBFilter[]) args[1] : null; diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index c06a4ab8..9f79530b 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -7,7 +7,6 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Comparator.in; import static org.usf.jquery.core.Comparator.lookupComparator; -import static org.usf.jquery.core.JqueryType.COLUMN; import static org.usf.jquery.core.Operator.lookupOperator; import static org.usf.jquery.core.Operator.lookupWindowFunction; import static org.usf.jquery.core.Parameter.required; @@ -20,8 +19,8 @@ import static org.usf.jquery.web.ColumnDecorator.ofColumn; import static org.usf.jquery.web.JQueryContext.context; -import java.lang.reflect.Array; import java.util.List; +import java.util.Objects; import java.util.function.IntFunction; import java.util.stream.Stream; @@ -33,9 +32,7 @@ import org.usf.jquery.core.JqueryType; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; -import org.usf.jquery.core.Parameter; import org.usf.jquery.core.ParameterSet; -import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedComparator; import org.usf.jquery.core.TypedOperator; @@ -146,7 +143,7 @@ public DBFilter asFilter(TableDecorator td, List values) { } if(isNull(e.args)) { var prs = requireNonNull(t.cd.parser(td)); - var arr = prs.parseAll(toStringArray(values)); + var arr = isNull(values) ? null : prs.parseAll(toStringArray(values)); return oc.filter(in().expression(arr)); } //values isEmpty diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index 54ad5d3a..fe4884e6 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -56,8 +56,8 @@ public static RevisionIterator iterator(YearMonth[] revisions){ static DBTable yearTable(String name, String tagname) { return new DBTable(name, tagname) { @Override - public String sql(QueryParameterBuilder builder, String schema) { - return super.sql(builder, schema) + "_" + currentRev.get().getKey(); + public String sql(QueryParameterBuilder builder) { + return super.sql(builder) + "_" + currentRev.get().getKey(); } }; } diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index 2250d043..532ec45c 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -2,7 +2,6 @@ import static java.lang.Integer.parseInt; import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Utils.isEmpty; @@ -24,7 +23,6 @@ import java.util.Collection; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; @@ -44,7 +42,7 @@ public interface TableDecorator { String identity(); //URL - String tableName(); //SQL check schema.table + String tableName(); //SQL check schema.table Optional columnName(ColumnDecorator cd); From 0e172f71750c6c3725bd85b719c129c903d580ec Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 4 Feb 2024 23:52:33 +0100 Subject: [PATCH 061/298] edit --- .../usf/jquery/core/BadArgumentException.java | 28 ++ .../java/org/usf/jquery/core/CaseColumn.java | 3 +- .../org/usf/jquery/core/ComparatorChain.java | 3 + .../org/usf/jquery/core/InternalQuery.java | 1 - .../org/usf/jquery/core/JqueryException.java | 22 ++ .../org/usf/jquery/core/ParameterSet.java | 29 +- src/main/java/org/usf/jquery/core/Utils.java | 8 + .../java/org/usf/jquery/core/ViewColumn.java | 1 - .../org/usf/jquery/web/ArgumentParsers.java | 21 +- .../org/usf/jquery/web/ColumnDecorator.java | 2 +- .../java/org/usf/jquery/web/Constants.java | 4 +- .../org/usf/jquery/web/CriteriaBuilder.java | 3 +- .../org/usf/jquery/web/EvalException.java | 21 ++ .../org/usf/jquery/web/ParseException.java | 4 - .../org/usf/jquery/web/RequestEntryChain.java | 307 ++++++++---------- .../org/usf/jquery/web/TableDecorator.java | 26 +- .../java/org/usf/jquery/web/WebException.java | 4 +- .../usf/jquery/web/YearTableDecorator.java | 5 +- .../usf/jquery/web/RequestEntryChainTest.java | 2 +- 19 files changed, 265 insertions(+), 229 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/BadArgumentException.java create mode 100644 src/main/java/org/usf/jquery/core/JqueryException.java create mode 100644 src/main/java/org/usf/jquery/web/EvalException.java diff --git a/src/main/java/org/usf/jquery/core/BadArgumentException.java b/src/main/java/org/usf/jquery/core/BadArgumentException.java new file mode 100644 index 00000000..5d59d861 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/BadArgumentException.java @@ -0,0 +1,28 @@ +package org.usf.jquery.core; + +import static java.util.stream.Collectors.joining; + +import java.util.stream.Stream; + +/** + * + * @author u$f + * + */ +@SuppressWarnings("serial") +public final class BadArgumentException extends JqueryException { + + public BadArgumentException(String message) { + super(message); + } + + public static BadArgumentException badArgumentTypeException(JavaType[] types, Object actual) { + return new BadArgumentException("bad argument type : " + + Stream.of(types).map(Object::toString).collect(joining("|")) + " # " + actual); + } + + public static BadArgumentException badArgumentCountException(int count, int actual) { + return new BadArgumentException("bad argument count : " + count + " # " + actual); + } + +} diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 64ac191b..cb3369d5 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -23,8 +23,9 @@ public final class CaseColumn implements DBColumn { @Override public String sql(QueryParameterBuilder builder) { + var b = builder.withValue(); return expressions.stream() //empty !? - .map(o-> o.sql(builder.withValue())) + .map(o-> o.sql(b)) .collect(joining(SPACE, "CASE ", " END")); } diff --git a/src/main/java/org/usf/jquery/core/ComparatorChain.java b/src/main/java/org/usf/jquery/core/ComparatorChain.java index 300bfa5b..7d28d459 100644 --- a/src/main/java/org/usf/jquery/core/ComparatorChain.java +++ b/src/main/java/org/usf/jquery/core/ComparatorChain.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.Validation.requireNArgs; + public interface ComparatorChain extends Comparator { @Override @@ -9,6 +11,7 @@ default String sql(QueryParameterBuilder builder, Object[] args) { @Override default DBFilter args(Object... args) { + requireNArgs(2, args, this::id); return ((DBFilter)args[0]).append(LogicalOperator.valueOf(id()), ((DBFilter)args[1])); } diff --git a/src/main/java/org/usf/jquery/core/InternalQuery.java b/src/main/java/org/usf/jquery/core/InternalQuery.java index 2ba03e17..9d916887 100644 --- a/src/main/java/org/usf/jquery/core/InternalQuery.java +++ b/src/main/java/org/usf/jquery/core/InternalQuery.java @@ -51,5 +51,4 @@ void filter(QueryParameterBuilder pb, SqlStringBuilder sb, String caluse, Predic public String toString() { return sql(addWithValue()); } - } diff --git a/src/main/java/org/usf/jquery/core/JqueryException.java b/src/main/java/org/usf/jquery/core/JqueryException.java new file mode 100644 index 00000000..f20c562c --- /dev/null +++ b/src/main/java/org/usf/jquery/core/JqueryException.java @@ -0,0 +1,22 @@ +package org.usf.jquery.core; + +/** + * + * @author u$f + * + */ +@SuppressWarnings("serial") +public class JqueryException extends RuntimeException { + + public JqueryException(String message) { + super(message); + } + + public JqueryException(Throwable cause) { + super(cause); + } + + public JqueryException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index 65ada508..a461e940 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -2,6 +2,8 @@ import static java.lang.Math.min; import static java.util.Objects.isNull; +import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; +import static org.usf.jquery.core.BadArgumentException.badArgumentTypeException; import java.util.function.ObjIntConsumer; @@ -20,22 +22,22 @@ public final class ParameterSet { private static final Parameter[] NO_PARAM = new Parameter[0]; + private final int nReqArgs; private final Parameter[] parameters; public Object[] args(Object... args) { var arr = isNull(args) ? NO_PARAM : args; forEach(arr.length, (p,i)-> { if(!p.accept(i, arr)) { - throw badArgumentTypeException(); + throw badArgumentTypeException(p.types(args), arr[i]); } }); return arr; } public void forEach(int nArgs, ObjIntConsumer cons) { - var rq = requiredParameterCount(); - if(nArgs < rq || (nArgs > parameters.length && !isVarags())) { - throw badArgumentCountException(); + if(nArgs < nReqArgs || (nArgs > parameters.length && !isVarags())) { + throw badArgumentCountException(nReqArgs, nArgs); } var i=0; for(; i cons) { } } - public int requiredParameterCount() { - var i=0; - while(i 0 && parameters[parameters.length-1].isVarargs(); } public static ParameterSet ofParameters(Parameter... parameters) { if(isNull(parameters)) { - return new ParameterSet(NO_PARAM); + return new ParameterSet(0, NO_PARAM); } var i=0; for(; i> Optional findEnum(String v, Class clazz){ + return Stream.of(clazz.getEnumConstants()) + .filter(e-> e.name().equals(v)) + .findAny(); + } + public static Database currentDatabase() { return context.get(); } diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index f6907ee9..383e3b2d 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -41,5 +41,4 @@ public String tagname() { public String toString() { return sql(addWithValue()); } - } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 6c295d2e..1893be46 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -55,7 +55,7 @@ public class ArgumentParsers { public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType... types) { if(isEmpty(types) || Stream.of(types).allMatch(JDBCType.class::isInstance)) { try { - return jqueryArgParser(COLUMN).parse(entry, td); //only JDBC types + return jqueryArgParser(COLUMN).parse(entry, td); //only with JDBC types } catch (Exception e) {/*do not throw exception*/} if(isEmpty(types)) { return jdbcArgParser(null).parse(entry, td); @@ -108,20 +108,21 @@ public static JDBCArgumentParser jdbcArgParser(JDBCType type) { public static JavaArgumentParser jqueryArgParser(@NonNull JqueryType type) { switch (type) { - case COLUMN: return RequestEntryChain::asColumn; - case FILTER: return RequestEntryChain::asFilter; - case ORDER: return RequestEntryChain::asOrder; - case PARTITIONS: return (re, td)-> re.evalArray(td, "partition", COLUMN); - case COLUMNS: return (re, td)-> re.evalArray(td, Constants.COLUMN, COLUMN); - case FILTERS: return (re, td)-> re.evalArray(td, "filter", FILTER); - case ORDERS: return (re, td)-> re.evalArray(td, Constants.ORDER, ORDER); + case COLUMN: return RequestEntryChain::evalColumn; + case FILTER: return RequestEntryChain::evalFilter; + case ORDER: return RequestEntryChain::evalOrder; case QUERY: return ArgumentParsers::evalQuery; + case PARTITIONS: return (re, td)-> re.evalArrayFunction(td, Constants.PARTITION, COLUMN); + case COLUMNS: return (re, td)-> re.evalArrayFunction(td, Constants.COLUMN, COLUMN); + case FILTERS: return (re, td)-> re.evalArrayFunction(td, Constants.FILTER, FILTER); + case ORDERS: return (re, td)-> re.evalArrayFunction(td, Constants.ORDER, ORDER); default: throw unsupportedTypeException(type); } } - private static InternalQuery evalQuery(RequestEntryChain re, TableDecorator td) { - var args = re.evalFunction(td, "query", ofParameters(required(COLUMNS), optional(FILTERS))); + //move it => RequestEntryChain + private static InternalQuery evalQuery(RequestEntryChain re, TableDecorator td) {//move it + var args = re.evalFunction(td, "query", ofParameters(required(COLUMNS), optional(FILTERS))); //.distinct var cols = (DBColumn[]) args[0]; var flts = args.length > 1 ? (DBFilter[]) args[1] : null; return new InternalQuery(cols, flts); diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 809890a0..b4df1a98 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -54,7 +54,7 @@ static ColumnDecorator ofColumn(String ref, ColumnBuilder cb) { return new ColumnDecorator() { @Override public String identity() { - return null; //unused + return ref; } @Override public String reference() { diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index 7fa68823..7c10066f 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -16,14 +16,16 @@ public final class Constants { public static final String COLUMN = "column"; public static final String COLUMN_DISTINCT = "column.distinct"; + public static final String FILTER = "filter"; public static final String ORDER = "order"; public static final String FETCH = "fetch"; public static final String OFFSET = "offset"; + public static final String PARTITION = "partition"; public static final String REVISION = "revision"; //not standard public static final String REVISION_MODE = "revision.mode"; //not standard static final Set RESERVED_WORDS = - Set.of(COLUMN, COLUMN_DISTINCT, ORDER, OFFSET, FETCH, REVISION, REVISION_MODE); //metadata ? + Set.of(COLUMN, COLUMN_DISTINCT, FILTER, ORDER, OFFSET, FETCH, PARTITION, REVISION, REVISION_MODE); //metadata ? static final YearMonth[] EMPTY_REVISION = new YearMonth[0]; //not standard diff --git a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java index 442adef7..0966f752 100644 --- a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java +++ b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java @@ -3,7 +3,6 @@ import static java.util.Optional.ofNullable; import static org.usf.jquery.core.LogicalOperator.OR; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; -import static org.usf.jquery.web.ParseException.cannotEvaluateException; import java.util.stream.Stream; @@ -23,7 +22,7 @@ public interface CriteriaBuilder> { default T build(String... args) { return Stream.of(requireAtLeastNArgs(1, args, CriteriaBuilder.class::getSimpleName)) .map(v-> ofNullable(criteria(v)) - .orElseThrow(()-> cannotEvaluateException("criteria value", v))) + .orElseThrow(()-> EvalException.cannotEvaluateException("criteria value", v))) .reduce((e1, e2)-> e1.append(combiner(), e2)) .orElseThrow(); } diff --git a/src/main/java/org/usf/jquery/web/EvalException.java b/src/main/java/org/usf/jquery/web/EvalException.java new file mode 100644 index 00000000..07d0449e --- /dev/null +++ b/src/main/java/org/usf/jquery/web/EvalException.java @@ -0,0 +1,21 @@ +package org.usf.jquery.web; + +import static org.usf.jquery.core.SqlStringBuilder.quote; + +/** + * + * @author u$f + * + */ +@SuppressWarnings("serial") + +public final class EvalException extends WebException { + + public EvalException(String message) { + super(message); + } + + static EvalException cannotEvaluateException(String type, String expression) { + return new EvalException("cannot evaluate " + type + " " + quote(expression)); + } +} diff --git a/src/main/java/org/usf/jquery/web/ParseException.java b/src/main/java/org/usf/jquery/web/ParseException.java index 0de0ecd3..a761955f 100644 --- a/src/main/java/org/usf/jquery/web/ParseException.java +++ b/src/main/java/org/usf/jquery/web/ParseException.java @@ -18,10 +18,6 @@ public ParseException(String message, Throwable cause) { super(message, cause); } - static ParseException cannotEvaluateException(String type, String expression) { - return new ParseException("cannot evaluate " + type + " " + quote(expression)); - } - static ParseException cannotParseException(String type, String value) { return cannotParseException(type, value, null); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 9f79530b..b6e69ec7 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -3,9 +3,8 @@ import static java.lang.reflect.Array.newInstance; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; -import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.Comparator.in; +import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.Comparator.lookupComparator; import static org.usf.jquery.core.Operator.lookupOperator; import static org.usf.jquery.core.Operator.lookupWindowFunction; @@ -13,23 +12,23 @@ import static org.usf.jquery.core.Parameter.varargs; import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; +import static org.usf.jquery.core.Utils.findEnum; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.WindowView.windowColumn; import static org.usf.jquery.web.ArgumentParsers.parse; import static org.usf.jquery.web.ColumnDecorator.ofColumn; import static org.usf.jquery.web.JQueryContext.context; import java.util.List; -import java.util.Objects; +import java.util.Optional; import java.util.function.IntFunction; -import java.util.stream.Stream; -import org.usf.jquery.core.ComparisonExpression; +import org.usf.jquery.core.BadArgumentException; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; import org.usf.jquery.core.DBOrder; import org.usf.jquery.core.JqueryType; +import org.usf.jquery.core.LogicalOperator; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; import org.usf.jquery.core.ParameterSet; @@ -62,197 +61,154 @@ public RequestEntryChain(String value) { this(value, false); } - public Object[] evalFunction(TableDecorator td, String fnName, ParameterSet ps) { - if(fnName.equals(value)) { - return toArgs(td, null, ps, Object[]::new); + public TaggableColumn evalColumn(TableDecorator td) { + var r = chainResourceOperations(td); + if(r.entry.isLast()) { + if(r.col instanceof TaggableColumn) { //no operation + return (TaggableColumn) r.col; + } + return r.col.as(isNull(r.entry.tag) ? r.cd.identity() : r.entry.tag); } - throw new IllegalArgumentException(); + throw cannotEvaluateException("operation", r.entry.next); } - public Object[] evalArray(TableDecorator td, String fnName, JqueryType type) { - if(fnName.equals(value)) { - var c = type.typeClass(); - if(c.isArray()) { - throw new UnsupportedOperationException(); + public DBOrder evalOrder(TableDecorator td) { + var r = chainResourceOperations(td); + if(r.entry.isLast()) { // no order + return r.col.order(); + } + var e = r.entry.next; + if(e.isLast()) { // next must be last + var o = findEnum(e.value, Order.class); + if(o.isPresent()) { + e.requireNoArgs(); //throw exception on valid order + return r.col.order(o.get()); } - return toArgs(td, null, ofParameters(required(type), varargs(type)), s-> (Object[]) newInstance(c, s)); - } - throw new IllegalArgumentException(); - } - - public TaggableColumn asColumn(TableDecorator td) { - var t = lookupResource(td); - var c = t.buildColumn(); - var e = t.entry; - DBColumn oc = c; - if(e.next()) { - do { - e = e.next; - oc = e.toOperation(td, oc); - if(isNull(oc)) { - throw cannotEvaluateException("column", e); - } - } while(e.next()); } - if(nonNull(e.tag)) { - return oc.as(e.tag); - } - return oc == c ? c : oc.as(c.tagname()); + throw cannotEvaluateException("order", e); } - public DBFilter asFilter(TableDecorator td) { - return asFilter(td, null); + public DBFilter evalFilter(TableDecorator td) { + return evalFilter(td, null); } - public DBFilter asFilter(TableDecorator td, List values) { - var t = lookupResource(td); - var c = t.buildColumn(); - var e = t.entry.next; - DBColumn oc = c; - while(nonNull(e)) { - var op = e.toOperation(td, oc); - if(isNull(op)) { - break; + public DBFilter evalFilter(TableDecorator td, List values) { + var r = chainResourceOperations(td); + if(r.entry.isLast()) { + return defaultFilter(r, values); + } + var e = r.entry.next; + if(e.isLast()) { + if(nonNull(e.args) && !isEmpty(values)) { + throw new IllegalArgumentException("both"); } - oc = "over".equals(e.value) - ? windowColumn(td.table(), op.as(c.tagname())) - : op; //preserve last non null column - e = e.next; - } - if(isNull(e)) { - return isEmpty(values) - ? oc.isNull() - : defaultFilter(t.td, oc, requireNonNull(t.cd.parser(t.td)), values); - } - if(!isEmpty(values) && (e.next() || !isEmpty(e.args))) { - throw new IllegalArgumentException("illegal"); - } - if(isNull(e.args) && !isEmpty(values)) { - e.args = values; - } - var ftr = e.toComparison(td, oc); - if(nonNull(ftr)) { - if(e.next()) { - //values isEmpty - e = e.next; - while(nonNull(e)) { - ftr = e.toComparison(td, ftr); - e = e.next; - } - return ftr; //throw if null + else if(isNull(e.args)) { + e.args = values; } - if(isNull(e.args)) { - var prs = requireNonNull(t.cd.parser(td)); - var arr = isNull(values) ? null : prs.parseAll(toStringArray(values)); - return oc.filter(in().expression(arr)); + var o = e.toComparison(td, r.col); + if(o.isPresent()) { + return o.get(); + } //eval comparator first to avoid overriding default comparators + if(r.col instanceof TaggableColumn) { + var c = r.cd.criteria(e.value); + if(nonNull(c)) { + return r.col.filter(c.build(toStringArray(e.args))); + } } - //values isEmpty - return ftr; + throw cannotEvaluateException("comparator|criteria", e); } - else if(oc == c) { - return c.filter(e.toComparison(td, t.cd, values)); + if(isEmpty(values)) { + return chainFilters(td, r.col); } - else { - throw new IllegalArgumentException(); + throw new IllegalArgumentException(); + } + + public Object[] evalFunction(TableDecorator td, String fnName, ParameterSet ps) { + if(fnName.equals(value)) { + return requireNoNext().toArgs(td, null, ps, Object[]::new); } + throw cannotEvaluateException(fnName, this); } - static DBFilter defaultFilter(TableDecorator td, DBColumn oc, JDBCArgumentParser parser, List values) { + public Object[] evalArrayFunction(TableDecorator td, String fnName, JqueryType type) { + if(fnName.equals(value)) { + var c = type.typeClass(); + if(!c.isArray()) { //basic type + return toArgs(td, null, ofParameters(required(type), varargs(type)), s-> (Object[]) newInstance(c, s)); + } + throw new UnsupportedOperationException(); + } + throw cannotEvaluateException(fnName, this); + } + + static DBFilter defaultFilter(ResourceCursor r, List values) { + if(isEmpty(values)) { + return r.col.isNull(); + } if(values.size() > 1) { - return oc.in(parser.parseAll(toStringArray(values))); + return r.col.in(r.cd.parser(r.td).parseAll(toStringArray(values))); } Object o; try { - o = values.get(0).asColumn(td); + o = values.get(0).evalColumn(r.td); } catch(Exception e) { - o = parser.parse(values.get(0).toString()); + o = r.cd.parser(r.td).parse(values.get(0).toString()); } - return oc.equal(o); + return r.col.equal(o); } - public DBOrder asOrder(TableDecorator td) { - var t = lookupResource(td); - var c = t.buildColumn(); - var e = t.entry.next; - DBColumn oc = c; + ResourceCursor chainResourceOperations(TableDecorator td) { + var r = lookupResource(td); + var e = r.entry.next; while(nonNull(e)) { - var op = e.toOperation(td, c); - if(isNull(op)) { + var c = e.toOperation(td, r.col); + if(c.isEmpty()) { break; } - oc = op; //preserve last non null column + r.col = c.get(); // preserve last non null column + r.entry = e; e = e.next; } - if(isNull(e)) { // no expression - return oc.order(); - } - else if(e.isLast()) { //last entry - var upVal = e.value.toUpperCase(); - var order = Stream.of(Order.values()).filter(o-> o.name().equals(upVal)).findAny(); - if(order.isPresent()) { - return oc.order(order.get()); - } - } - throw cannotEvaluateException("order", e); //column expected - } - - public OperationColumn asOperation(TableDecorator td) { //predicate - var op = toOperation(td, null); - if(nonNull(op)) { - return op; - } - throw cannotEvaluateException("operation", this); // as function + return r; } - OperationColumn toOperation(TableDecorator td, DBColumn col) { + Optional toOperation(TableDecorator td, DBColumn col) { var res = lookupOperator(value); - if(isNull(col) && "count".equals(value) && isEmpty(args)) { - col = b-> { //no column & no args - b.view(td.table()); - return "*"; - }; - } - return res.isEmpty() ? null : fillArgs(td, col, res.get()); - } - - DBFilter toComparison(TableDecorator td, DBObject col) { - var res = lookupComparator(value); - return res.isEmpty() ? null : fillArgs(td, col, res.get()); + return res.map(fn-> { + var c = col; + if(isNull(c) && isEmpty(args) && "count".equals(fn.id())) { + c = b-> { + b.view(td.table()); // important! register view + return "*"; + }; + } + return fillArgs(td, c, fn); + }); } - ComparisonExpression toComparison(TableDecorator td, ColumnDecorator cd, List values) { - if(next()) { - throw cannotEvaluateException("expression", this); - } - if(nonNull(value)) { - var criteria = cd.criteria(value); - if(nonNull(criteria)) { - return criteria.build(toStringArray(values)); + DBFilter chainFilters(TableDecorator td, DBColumn col) { + var f = toComparison(td, col).orElseThrow(()-> cannotEvaluateException("comparator", this)); + var e = this.next; + while(nonNull(e)) { + var op = findEnum(e.value, LogicalOperator.class).orElseThrow(()-> cannotEvaluateException("logical operator", this)); + if(isEmpty(e.args)) { + throw badArgumentCountException(1, 0); + } + if(e.args.size() > 1) { + throw badArgumentCountException(1, e.args.size()); } + f = f.append(op, e.args.get(0).evalFilter(td)); } -// var cmp = cd.comparator(value, values.size()); -// if(nonNull(cmp)) { -// if(values.size() == 1) { -// try { -// return cmp.expression(values.get(0).asColumn(td)); // try parse column -// } -// catch (Exception e) { -// var prs = requireNonNull(cd.parser(td)); -// return cmp.expression(prs.parse(values.get(0).toString())); -// } -// } -// var prs = requireNonNull(cd.parser(td)); -// var arr = prs.parseAll(toStringArray(values)); -// return cmp.isVarargs() -// ? cmp.expression(arr) -// : ofComparator(cmp).build(arr); -// } - throw cannotEvaluateException("expression", this); + return f; + } + + Optional toComparison(TableDecorator td, DBObject col) { + return lookupComparator(value).map(c-> fillArgs(td, col, c)); } - private Triple lookupResource(TableDecorator td) { - if(next() && context().isDeclaredTable(value)) { //columnName == viewName + private ResourceCursor lookupResource(TableDecorator td) { + if(next() && context().isDeclaredTable(value)) { //sometimes td.id == cd.id var tr = next.lookupViewResource(context().getTable(value)); if(nonNull(tr)) { return tr; @@ -263,17 +219,17 @@ private Triple lookupResource(TableDecorator td) { return tp; } var op = toOperation(td, null); - if(nonNull(op)) { - return new Triple(td, ofColumn(value, b-> op), this); + if(op.isPresent()) { + return new ResourceCursor(td, ofColumn(value, b-> op.get()), this); } throw cannotEvaluateException("resource", this); } - private Triple lookupViewResource(TableDecorator td) { + private ResourceCursor lookupViewResource(TableDecorator td) { return context().isDeclaredColumn(value) - ? new Triple(td, context().getColumn(requireNoArgs().value), this) + ? new ResourceCursor(td, context().getColumn(requireNoArgs().value), this) : lookupWindowFunction(value) //rank, rowNumber, .. - .map(fn-> new Triple(td, ofColumn(value, b-> fillArgs(td, null, fn)), this)) + .map(fn-> new ResourceCursor(td, ofColumn(value, b-> fillArgs(td, null, fn)), this)) .orElse(null); } @@ -305,7 +261,7 @@ private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps, IntFun arr[0] = col; } ps.forEach(arr.length, (p,i)-> { - if(i>0 || inc==0) { //arg0 already parsed + if(i>=inc) { //arg0 already parsed var e = args.get(i-inc); arr[i] = isNull(e.value) || e.text ? e.requireNoArgs().value @@ -322,6 +278,13 @@ RequestEntryChain requireNoArgs() { throw new IllegalArgumentException(value + " takes no args"); } + RequestEntryChain requireNoNext(){ + if(isLast()) { + return this; + } + throw new IllegalArgumentException(value + " must be the last entry"); + } + public boolean isLast() { return isNull(next); } @@ -332,7 +295,7 @@ public boolean next() { @Override public String toString() { - var s = ""; + var s = ""; // null == EMPTY if(nonNull(value)) { s += text ? doubleQuote(value) : value; } @@ -351,19 +314,23 @@ static String[] toStringArray(List entries) { .toArray(String[]::new); } - static ParseException cannotEvaluateException(String type, RequestEntryChain entry) { - return ParseException.cannotEvaluateException(type, entry.toString()); + static EvalException cannotEvaluateException(String type, RequestEntryChain entry) { + return EvalException.cannotEvaluateException(type, entry.toString()); } @RequiredArgsConstructor - static class Triple { + static final class ResourceCursor { private final TableDecorator td; private final ColumnDecorator cd; - private final RequestEntryChain entry; + private RequestEntryChain entry; + private DBColumn col; - TaggableColumn buildColumn() { - return td.column(cd); + public ResourceCursor(TableDecorator td, ColumnDecorator cd, RequestEntryChain entry) { + this.td = td; + this.cd = cd; + this.entry = entry; + this.col = td.column(cd); } @Override diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index 532ec45c..e67719f5 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -1,7 +1,7 @@ package org.usf.jquery.web; import static java.lang.Integer.parseInt; -import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Utils.isEmpty; @@ -46,20 +46,20 @@ public interface TableDecorator { Optional columnName(ColumnDecorator cd); - default TaggableView table() { + default TaggableView table() { //optim var b = builder(); - return isNull(b) - ? new DBTable(tableName(), identity()) - : b.build().as(identity()); + return nonNull(b) + ? b.build().as(identity()) + : new DBTable(tableName(), identity()); } default TaggableColumn column(ColumnDecorator cd) { var b = cd.builder(); - return isNull(b) - ? columnName(cd) - .map(cn-> new ViewColumn(table(), requireLegalVariable(cn), cd.reference(), cd.dataType(this))) - .orElseThrow(()-> undeclaredResouceException(identity(), cd.identity())) - : b.build(this).as(cd.reference()); + if(nonNull(b)) { + return b.build(this).as(cd.reference()); + } + var cn = columnName(cd).orElseThrow(()-> undeclaredResouceException(identity(), cd.identity())); + return new ViewColumn(table(), requireLegalVariable(cn), cd.reference(), cd.dataType(this)); } default ViewBuilder builder() { @@ -95,7 +95,7 @@ default void parseColumns(RequestQueryBuilder query, Map param } Stream.of(cols) .flatMap(v-> parseEntries(v).stream()) - .forEach(e-> query.columns(e.asColumn(this))); + .forEach(e-> query.columns(e.evalColumn(this))); } default void parseFilters(RequestQueryBuilder query, Map parameters) { @@ -103,7 +103,7 @@ default void parseFilters(RequestQueryBuilder query, Map param .filter(e-> !RESERVED_WORDS.contains(e.getKey())) .flatMap(e-> { var re = parseEntry(e.getKey()); - return Stream.of(e.getValue()).map(v-> re.asFilter(this, parseArgs(v))); + return Stream.of(e.getValue()).map(v-> re.evalFilter(this, parseArgs(v))); }) .forEach(query::filters); } @@ -112,7 +112,7 @@ default void parseOrders(RequestQueryBuilder query, Map parame if(parameters.containsKey(ORDER)) { Stream.of(parameters.get(ORDER)) .flatMap(c-> parseEntries(c).stream()) - .forEach(e-> query.orders(e.asOrder(this))); + .forEach(e-> query.orders(e.evalOrder(this))); } } diff --git a/src/main/java/org/usf/jquery/web/WebException.java b/src/main/java/org/usf/jquery/web/WebException.java index 9b1587aa..ca81b733 100644 --- a/src/main/java/org/usf/jquery/web/WebException.java +++ b/src/main/java/org/usf/jquery/web/WebException.java @@ -1,12 +1,14 @@ package org.usf.jquery.web; +import org.usf.jquery.core.JqueryException; + /** * * @author u$f * */ @SuppressWarnings("serial") -public class WebException extends RuntimeException { +public class WebException extends JqueryException { public WebException(String message) { super(message); diff --git a/src/main/java/org/usf/jquery/web/YearTableDecorator.java b/src/main/java/org/usf/jquery/web/YearTableDecorator.java index 46cf55c6..30b8772a 100644 --- a/src/main/java/org/usf/jquery/web/YearTableDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearTableDecorator.java @@ -12,7 +12,6 @@ import static org.usf.jquery.web.Constants.REVISION_MODE; import static org.usf.jquery.web.JQueryContext.database; import static org.usf.jquery.web.NoSuchResourceException.noSuchResouceException; -import static org.usf.jquery.web.ParseException.cannotEvaluateException; import static org.usf.jquery.web.ParseException.cannotParseException; import static org.usf.jquery.web.RevisionIterator.iterator; import static org.usf.jquery.web.RevisionIterator.monthFilter; @@ -71,7 +70,7 @@ default RequestQueryBuilder query(Map parameterMap) { default YearMonth[] parseRevisions(Map parameterMap) { var arr = parameterMap.get(REVISION_MODE); if(nonNull(arr) && arr.length > 1) { - throw cannotEvaluateException(REVISION, join(", ", arr)); //multiple values + throw EvalException.cannotEvaluateException(REVISION, join(", ", arr)); //multiple values } var mod = revisionMode(isEmpty(arr) || isBlank(arr[0]) ? defaultRevisionMode() : arr[0]); var values = parameterMap.containsKey(REVISION) @@ -99,7 +98,7 @@ default UnaryOperator revisionMode(String mode) { case "strict" : return this::strictRevisions; case "preceding" : return this::precedingRevisions; case "succeeding" : return this::succeedingRevisions; - default : throw cannotEvaluateException(REVISION_MODE, mode); + default : throw EvalException.cannotEvaluateException(REVISION_MODE, mode); } } diff --git a/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java b/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java index cf55c89d..d7199cf0 100644 --- a/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java +++ b/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java @@ -12,7 +12,7 @@ class RequestEntryChainTest { @Test void test() { JQueryContext.register(emptyList(), emptyList()); - parseEntry("count()").asColumn(new TableDecorator() { + parseEntry("count()").evalColumn(new TableDecorator() { @Override public String tableName() { From ae525dc503308244f8bb6cee47fc73253994adc8 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 5 Feb 2024 13:28:22 +0100 Subject: [PATCH 062/298] edit --- .../org/usf/jquery/core/ParameterSet.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 33 ++++++++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index a461e940..62c0e283 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -65,7 +65,7 @@ public static ParameterSet ofParameters(Parameter... parameters) { throw new IllegalArgumentException("varargs should be the last parameter"); } } - var nReqArgs = i; + var nReqArgs = i-1; for(; i values) { - var r = chainResourceOperations(td); + var r = chainResourceOperations(td, true); if(r.entry.isLast()) { return defaultFilter(r, values); } @@ -118,7 +118,7 @@ else if(isNull(e.args)) { throw cannotEvaluateException("comparator|criteria", e); } if(isEmpty(values)) { - return chainFilters(td, r.col); + return e.chainFilters(td, r.col); } throw new IllegalArgumentException(); } @@ -158,7 +158,7 @@ static DBFilter defaultFilter(ResourceCursor r, List values) return r.col.equal(o); } - ResourceCursor chainResourceOperations(TableDecorator td) { + ResourceCursor chainResourceOperations(TableDecorator td, boolean filter) { var r = lookupResource(td); var e = r.entry.next; while(nonNull(e)) { @@ -166,7 +166,9 @@ ResourceCursor chainResourceOperations(TableDecorator td) { if(c.isEmpty()) { break; } - r.col = c.get(); // preserve last non null column + r.col = filter && "over".equals(e.value) + ? windowColumn(r.td.table(), c.get().as(r.cd.identity())) + : c.get(); // preserve last non null column r.entry = e; e = e.next; } @@ -177,7 +179,7 @@ Optional toOperation(TableDecorator td, DBColumn col) { var res = lookupOperator(value); return res.map(fn-> { var c = col; - if(isNull(c) && isEmpty(args) && "count".equals(fn.id())) { + if(isNull(c) && isEmpty(args) && "count".equals(value)) { // id is MAJ c = b-> { b.view(td.table()); // important! register view return "*"; @@ -191,14 +193,13 @@ DBFilter chainFilters(TableDecorator td, DBColumn col) { var f = toComparison(td, col).orElseThrow(()-> cannotEvaluateException("comparator", this)); var e = this.next; while(nonNull(e)) { - var op = findEnum(e.value, LogicalOperator.class).orElseThrow(()-> cannotEvaluateException("logical operator", this)); - if(isEmpty(e.args)) { - throw badArgumentCountException(1, 0); + var op = findEnum(e.value.toUpperCase(), LogicalOperator.class).orElseThrow(()-> cannotEvaluateException("logical operator", this)); + var n = isEmpty(e.args) ? 0 : e.args.size(); + if(n != 1) { + throw badArgumentCountException(1, n); } - if(e.args.size() > 1) { - throw badArgumentCountException(1, e.args.size()); - } - f = f.append(op, e.args.get(0).evalFilter(td)); + f = f.append(op, e.args.get(0).evalFilter(td)); + e = e.next; } return f; } From dc0499b4e7de3eef2b8dc471a64dd007a9228d90 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 5 Feb 2024 22:00:43 +0100 Subject: [PATCH 063/298] edit --- .../usf/jquery/web/JavaArgumentParser.java | 1 - .../org/usf/jquery/web/RequestEntryChain.java | 28 +++++++++++-------- .../org/usf/jquery/web/RequestParser.java | 1 + 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/JavaArgumentParser.java b/src/main/java/org/usf/jquery/web/JavaArgumentParser.java index 47aeac0c..038c0775 100644 --- a/src/main/java/org/usf/jquery/web/JavaArgumentParser.java +++ b/src/main/java/org/usf/jquery/web/JavaArgumentParser.java @@ -9,5 +9,4 @@ public interface JavaArgumentParser { Object parse(RequestEntryChain entry, TableDecorator td); - } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 78c865d5..1e0a7043 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -64,10 +64,12 @@ public RequestEntryChain(String value) { public TaggableColumn evalColumn(TableDecorator td) { var r = chainResourceOperations(td, false); if(r.entry.isLast()) { - if(r.col instanceof TaggableColumn) { //no operation - return (TaggableColumn) r.col; + if(nonNull(r.entry.tag)) { + return r.col.as(r.entry.tag); } - return r.col.as(isNull(r.entry.tag) ? r.cd.identity() : r.entry.tag); + return r.col instanceof TaggableColumn + ? (TaggableColumn) r.col + : r.col.as(r.cd.identity()); } throw cannotEvaluateException("operation", r.entry.next); } @@ -78,12 +80,9 @@ public DBOrder evalOrder(TableDecorator td) { return r.col.order(); } var e = r.entry.next; - if(e.isLast()) { // next must be last - var o = findEnum(e.value.toUpperCase(), Order.class); - if(o.isPresent()) { - e.requireNoArgs(); //throw exception on valid order - return r.col.order(o.get()); - } + if(e.isLast() && e.value.matches("asc|desc")) { // next must be last + var o = Order.valueOf(e.requireNoArgs().value.toUpperCase()); // noArgs on valid order + return r.col.order(o); } throw cannotEvaluateException("order", e); } @@ -149,10 +148,15 @@ static DBFilter defaultFilter(ResourceCursor r, List values) return r.col.in(r.cd.parser(r.td).parseAll(toStringArray(values))); } Object o; - try { - o = values.get(0).evalColumn(r.td); + if(!r.entry.text) { + try { + o = values.get(0).evalColumn(r.td); + } + catch(Exception e) { + o = r.cd.parser(r.td).parse(values.get(0).toString()); + } } - catch(Exception e) { + else { o = r.cd.parser(r.td).parse(values.get(0).toString()); } return r.col.equal(o); diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 1c4716a6..082fc523 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -23,6 +23,7 @@ public final class RequestParser { private RequestParser(String s) { this.s = s; this.size = s.length(); + this.c = size == 0 ? 0 : s.charAt(idx); } public static RequestEntryChain parseEntry(String s) { From 06a3d9147c0f5cf7818a5731a277e5713aa75762 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 5 Feb 2024 23:04:06 +0100 Subject: [PATCH 064/298] edit --- .../java/org/usf/jquery/core/Operator.java | 4 --- .../org/usf/jquery/web/ColumnDecorator.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 33 +++++++++---------- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index f76fe8f9..cf3bad42 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -338,10 +338,6 @@ static ConstantOperator constant(String name) { return ()-> name; } - static Optional lookupWindowFunction(String op) { - return lookupOperator(op).filter(fn-> fn.unwrap().getClass() == WindowFunction.class); //!aggregation - } - static Optional lookupOperator(String op) { return DBProcessor.lookup(Operator.class, TypedOperator.class, op); } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index b4df1a98..133723e2 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -54,7 +54,7 @@ static ColumnDecorator ofColumn(String ref, ColumnBuilder cb) { return new ColumnDecorator() { @Override public String identity() { - return ref; + return ref; // default column tag } @Override public String reference() { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 1e0a7043..9b59737d 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -7,7 +7,6 @@ import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.Comparator.lookupComparator; import static org.usf.jquery.core.Operator.lookupOperator; -import static org.usf.jquery.core.Operator.lookupWindowFunction; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; import static org.usf.jquery.core.ParameterSet.ofParameters; @@ -22,6 +21,7 @@ import java.util.List; import java.util.Optional; import java.util.function.IntFunction; +import java.util.function.Predicate; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; @@ -35,6 +35,7 @@ import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedComparator; import org.usf.jquery.core.TypedOperator; +import org.usf.jquery.core.WindowFunction; import lombok.AccessLevel; import lombok.Getter; @@ -166,7 +167,7 @@ ResourceCursor chainResourceOperations(TableDecorator td, boolean filter) { var r = lookupResource(td); var e = r.entry.next; while(nonNull(e)) { - var c = e.toOperation(td, r.col); + var c = e.toOperation(td, r.col, fn-> true); if(c.isEmpty()) { break; } @@ -179,9 +180,8 @@ ResourceCursor chainResourceOperations(TableDecorator td, boolean filter) { return r; } - Optional toOperation(TableDecorator td, DBColumn col) { - var res = lookupOperator(value); - return res.map(fn-> { + Optional toOperation(TableDecorator td, DBColumn col, Predicate pre) { + return lookupOperator(value).filter(pre).map(fn-> { var c = col; if(isNull(c) && isEmpty(args) && "count".equals(value)) { // id is MAJ c = b-> { @@ -214,27 +214,24 @@ Optional toComparison(TableDecorator td, DBObject col) { private ResourceCursor lookupResource(TableDecorator td) { if(next() && context().isDeclaredTable(value)) { //sometimes td.id == cd.id - var tr = next.lookupViewResource(context().getTable(value)); - if(nonNull(tr)) { - return tr; + var rc = next.lookupViewResource(context().getTable(value), + fn-> fn.unwrap().getClass() == WindowFunction.class); // only window function + if(nonNull(rc)) { + return rc; } } - var tp = lookupViewResource(td); - if(nonNull(tp)) { - return tp; - } - var op = toOperation(td, null); - if(op.isPresent()) { - return new ResourceCursor(td, ofColumn(value, b-> op.get()), this); + var rc = lookupViewResource(td, fn-> true); // all operations + if(nonNull(rc)) { + return rc; } throw cannotEvaluateException("resource", this); } - private ResourceCursor lookupViewResource(TableDecorator td) { + private ResourceCursor lookupViewResource(TableDecorator td, Predicate pre) { return context().isDeclaredColumn(value) ? new ResourceCursor(td, context().getColumn(requireNoArgs().value), this) - : lookupWindowFunction(value) //rank, rowNumber, .. - .map(fn-> new ResourceCursor(td, ofColumn(value, b-> fillArgs(td, null, fn)), this)) + : toOperation(td, null, pre) + .map(op-> new ResourceCursor(td, ofColumn(value, b-> op), this)) .orElse(null); } From bafad37e1b7286097ff9e0d9cb503825eb73f242 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 5 Feb 2024 23:42:51 +0100 Subject: [PATCH 065/298] edit --- src/main/java/org/usf/jquery/core/DBColumn.java | 4 ++-- src/main/java/org/usf/jquery/core/Operator.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 1cb30f45..930998d4 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -129,8 +129,8 @@ default OperationColumn multiply(Object o) { return Operator.multiply().args(this, o); } - default OperationColumn divise(Object o) { - return Operator.divise().args(this, o); + default OperationColumn divide(Object o) { + return Operator.divide().args(this, o); } default WhenFilterBridge when(ComparisonExpression ex) { diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index cf3bad42..93bbb1d5 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -64,7 +64,7 @@ static TypedOperator multiply() { return new TypedOperator(DOUBLE, operator("*"), required(DOUBLE), required(DOUBLE)); } - static TypedOperator divise() { + static TypedOperator divide() { return new TypedOperator(DOUBLE, operator("/"), required(DOUBLE), required(DOUBLE)); } From e10bc41157ed24a9b6c460aec081c81715ded3ee Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 6 Feb 2024 01:12:04 +0100 Subject: [PATCH 066/298] edit --- .../org/usf/jquery/web/RequestEntryChain.java | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 9b59737d..1c7699d3 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -95,7 +95,8 @@ public DBFilter evalFilter(TableDecorator td) { public DBFilter evalFilter(TableDecorator td, List values) { var r = chainResourceOperations(td, true); if(r.entry.isLast()) { - return defaultFilter(r, values); + return defaultComparatorEntry(values).toComparison(r.td, r.col) + .orElseThrow(); } var e = r.entry.next; if(e.isLast()) { @@ -122,6 +123,19 @@ else if(isNull(e.args)) { } throw new IllegalArgumentException(); } + + static RequestEntryChain defaultComparatorEntry(List values) { + String cmp; + if(isEmpty(values)) { + cmp = "isNull"; + } + else { + cmp = values.size() > 1 ? "in" : "eq"; + } + var e = new RequestEntryChain(cmp); + e.args = values; + return e; + } public Object[] evalFunction(TableDecorator td, String fnName, ParameterSet ps) { if(fnName.equals(value)) { @@ -140,29 +154,6 @@ public Object[] evalArrayFunction(TableDecorator td, String fnName, JqueryType t } throw cannotEvaluateException(fnName, this); } - - static DBFilter defaultFilter(ResourceCursor r, List values) { - if(isEmpty(values)) { - return r.col.isNull(); - } - if(values.size() > 1) { - return r.col.in(r.cd.parser(r.td).parseAll(toStringArray(values))); - } - Object o; - if(!r.entry.text) { - try { - o = values.get(0).evalColumn(r.td); - } - catch(Exception e) { - o = r.cd.parser(r.td).parse(values.get(0).toString()); - } - } - else { - o = r.cd.parser(r.td).parse(values.get(0).toString()); - } - return r.col.equal(o); - } - ResourceCursor chainResourceOperations(TableDecorator td, boolean filter) { var r = lookupResource(td); var e = r.entry.next; From 7654a57333d19ca9c2c35c38e179234cfca25a6a Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 6 Feb 2024 11:41:17 +0100 Subject: [PATCH 067/298] edit --- src/main/java/org/usf/jquery/core/Utils.java | 8 +- .../org/usf/jquery/web/RequestEntryChain.java | 78 +++++++++---------- .../jquery/web/RequestSyntaxException.java | 15 ++++ 3 files changed, 54 insertions(+), 47 deletions(-) create mode 100644 src/main/java/org/usf/jquery/web/RequestSyntaxException.java diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index aa9ca3fe..d73951f2 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -2,9 +2,9 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; +import static java.util.stream.Collectors.joining; import java.util.Collection; -import java.util.Optional; import java.util.stream.Stream; import lombok.AccessLevel; @@ -38,10 +38,8 @@ public static boolean isBlank(String s) { return isNull(s) || s.isBlank(); } - public static > Optional findEnum(String v, Class clazz){ - return Stream.of(clazz.getEnumConstants()) - .filter(e-> e.name().equals(v)) - .findAny(); + public static String toString(Object[] a) { + return Stream.of(a).map(Object::toString).collect(joining(",")); } public static Database currentDatabase() { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 1c7699d3..5c837a03 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -11,7 +11,6 @@ import static org.usf.jquery.core.Parameter.varargs; import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; -import static org.usf.jquery.core.Utils.findEnum; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.WindowView.windowColumn; import static org.usf.jquery.web.ArgumentParsers.parse; @@ -35,6 +34,7 @@ import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedComparator; import org.usf.jquery.core.TypedOperator; +import org.usf.jquery.core.Utils; import org.usf.jquery.core.WindowFunction; import lombok.AccessLevel; @@ -65,7 +65,7 @@ public RequestEntryChain(String value) { public TaggableColumn evalColumn(TableDecorator td) { var r = chainResourceOperations(td, false); if(r.entry.isLast()) { - if(nonNull(r.entry.tag)) { + if(nonNull(r.entry.tag)) { //TD required if operation return r.col.as(r.entry.tag); } return r.col instanceof TaggableColumn @@ -95,33 +95,41 @@ public DBFilter evalFilter(TableDecorator td) { public DBFilter evalFilter(TableDecorator td, List values) { var r = chainResourceOperations(td, true); if(r.entry.isLast()) { - return defaultComparatorEntry(values).toComparison(r.td, r.col) - .orElseThrow(); + return defaultComparatorEntry(values) + .toComparison(td, r.col) + .orElseThrow(); //cannot be empty } var e = r.entry.next; - if(e.isLast()) { - if(nonNull(e.args) && !isEmpty(values)) { - throw new IllegalArgumentException("both"); + if(!isEmpty(values)) { + if(!e.isLast()) { + throw new RequestSyntaxException(e + "=" + Utils.toString(values.toArray())); } - else if(isNull(e.args)) { - e.args = values; + if(isNull(e.args)) { + e.setArgs(values); } - var o = e.toComparison(td, r.col); - if(o.isPresent()) { - return o.get(); - } //eval comparator first to avoid overriding default comparators - if(r.col instanceof TaggableColumn) { - var c = r.cd.criteria(e.value); - if(nonNull(c)) { - return r.col.filter(c.build(toStringArray(e.args))); - } + } + var o = e.toComparison(td, r.col); //eval comparator first => avoid overriding + if(o.isEmpty() && r.col instanceof TaggableColumn) { //no operation + var c = r.cd.criteria(e.value); + if(nonNull(c)) { + return r.col.filter(c.build(toStringArray(e.args))); } - throw cannotEvaluateException("comparator|criteria", e); } - if(isEmpty(values)) { - return e.chainFilters(td, r.col); + if(o.isEmpty()) { + cannotEvaluateException("comparison|criteria", e); } - throw new IllegalArgumentException(); + if(e.isLast()) { + return o.get(); + } + e = e.next; + if(e.value.matches("and|or")) { + var op = LogicalOperator.valueOf(e.value.toUpperCase()); + if(!isEmpty(e.args) && e.args.size() == 1) { + return o.get().append(op, e.args.get(0).evalFilter(td)); + } + throw badArgumentCountException(1, isEmpty(e.args) ? 0 : e.args.size()); + } + throw new RequestSyntaxException("logical operator" + e); } static RequestEntryChain defaultComparatorEntry(List values) { @@ -133,7 +141,7 @@ static RequestEntryChain defaultComparatorEntry(List values) cmp = values.size() > 1 ? "in" : "eq"; } var e = new RequestEntryChain(cmp); - e.args = values; + e.setArgs(values); return e; } @@ -154,6 +162,7 @@ public Object[] evalArrayFunction(TableDecorator td, String fnName, JqueryType t } throw cannotEvaluateException(fnName, this); } + ResourceCursor chainResourceOperations(TableDecorator td, boolean filter) { var r = lookupResource(td); var e = r.entry.next; @@ -161,11 +170,11 @@ ResourceCursor chainResourceOperations(TableDecorator td, boolean filter) { var c = e.toOperation(td, r.col, fn-> true); if(c.isEmpty()) { break; - } - r.col = filter && "over".equals(e.value) - ? windowColumn(r.td.table(), c.get().as(r.cd.identity())) - : c.get(); // preserve last non null column + } // preserve last non null column r.entry = e; + r.col = filter && "over".equals(e.value) + ? windowColumn(r.td.table(), c.get().as(r.cd.identity())) + : c.get(); e = e.next; } return r; @@ -184,21 +193,6 @@ Optional toOperation(TableDecorator td, DBColumn col, Predicate }); } - DBFilter chainFilters(TableDecorator td, DBColumn col) { - var f = toComparison(td, col).orElseThrow(()-> cannotEvaluateException("comparator", this)); - var e = this.next; - while(nonNull(e)) { - var op = findEnum(e.value.toUpperCase(), LogicalOperator.class).orElseThrow(()-> cannotEvaluateException("logical operator", this)); - var n = isEmpty(e.args) ? 0 : e.args.size(); - if(n != 1) { - throw badArgumentCountException(1, n); - } - f = f.append(op, e.args.get(0).evalFilter(td)); - e = e.next; - } - return f; - } - Optional toComparison(TableDecorator td, DBObject col) { return lookupComparator(value).map(c-> fillArgs(td, col, c)); } diff --git a/src/main/java/org/usf/jquery/web/RequestSyntaxException.java b/src/main/java/org/usf/jquery/web/RequestSyntaxException.java new file mode 100644 index 00000000..416ec1e2 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/RequestSyntaxException.java @@ -0,0 +1,15 @@ +package org.usf.jquery.web; + +/** + * + * @author u$f + * + */ +@SuppressWarnings("serial") +public class RequestSyntaxException extends WebException { + + public RequestSyntaxException(String message) { + super(message); + } + +} From 0d4e9765ab58b0923e02b1395ac4bedc137da807 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 6 Feb 2024 11:55:05 +0100 Subject: [PATCH 068/298] edit --- .../org/usf/jquery/web/RequestEntryChain.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 5c837a03..f0b1a532 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -118,18 +118,23 @@ public DBFilter evalFilter(TableDecorator td, List values) { if(o.isEmpty()) { cannotEvaluateException("comparison|criteria", e); } - if(e.isLast()) { - return o.get(); - } - e = e.next; - if(e.value.matches("and|or")) { - var op = LogicalOperator.valueOf(e.value.toUpperCase()); - if(!isEmpty(e.args) && e.args.size() == 1) { - return o.get().append(op, e.args.get(0).evalFilter(td)); + var f = o.get(); + while(e.next()) { + e = e.next; + if(e.value.matches("and|or")) { + var op = LogicalOperator.valueOf(e.value.toUpperCase()); + if(!isEmpty(e.args) && e.args.size() == 1) { + f = f.append(op, e.args.get(0).evalFilter(td)); + } + else { + throw badArgumentCountException(1, isEmpty(e.args) ? 0 : e.args.size()); + } + } + else { + throw cannotEvaluateException("logical operator", e); } - throw badArgumentCountException(1, isEmpty(e.args) ? 0 : e.args.size()); } - throw new RequestSyntaxException("logical operator" + e); + return f; } static RequestEntryChain defaultComparatorEntry(List values) { From 90cc1645861291b8c5e94232e250079ee81875f8 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 6 Feb 2024 15:10:11 +0100 Subject: [PATCH 069/298] edit --- .../java/org/usf/jquery/core/Comparator.java | 15 ---- .../org/usf/jquery/core/ComparatorChain.java | 18 ---- .../org/usf/jquery/web/RequestEntryChain.java | 87 ++++++++++--------- 3 files changed, 46 insertions(+), 74 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/core/ComparatorChain.java diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index e63efc10..2f9928c1 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -3,7 +3,6 @@ import static java.util.Arrays.copyOfRange; import static org.usf.jquery.core.ArgTypeRef.firstArgJdbcType; import static org.usf.jquery.core.JDBCType.VARCHAR; -import static org.usf.jquery.core.JqueryType.FILTER; import static org.usf.jquery.core.JqueryType.QUERY; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; @@ -123,16 +122,6 @@ static TypedComparator notIn() { ofParameters(required(), required(QUERY))); } - //comparator chain - - static TypedComparator and() { - return new TypedComparator(chain("AND"), required(FILTER), required(FILTER)); - } - - static TypedComparator or() { - return new TypedComparator(chain("OR"), required(FILTER), required(FILTER)); - } - static BasicComparator basicComparator(final String name) { return ()-> name; } @@ -149,10 +138,6 @@ static InCompartor inComparator(final String name) { return ()-> name; } - static ComparatorChain chain(final String name) { - return ()-> name; - } - static Optional lookupComparator(String op) { return DBProcessor.lookup(Comparator.class, TypedComparator.class, op); } diff --git a/src/main/java/org/usf/jquery/core/ComparatorChain.java b/src/main/java/org/usf/jquery/core/ComparatorChain.java deleted file mode 100644 index 7d28d459..00000000 --- a/src/main/java/org/usf/jquery/core/ComparatorChain.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.usf.jquery.core; - -import static org.usf.jquery.core.Validation.requireNArgs; - -public interface ComparatorChain extends Comparator { - - @Override - default String sql(QueryParameterBuilder builder, Object[] args) { - throw new UnsupportedOperationException("no sql"); - } - - @Override - default DBFilter args(Object... args) { - requireNArgs(2, args, this::id); - return ((DBFilter)args[0]).append(LogicalOperator.valueOf(id()), ((DBFilter)args[1])); - } - -} diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index f0b1a532..1c5550fb 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -94,7 +94,7 @@ public DBFilter evalFilter(TableDecorator td) { public DBFilter evalFilter(TableDecorator td, List values) { var r = chainResourceOperations(td, true); - if(r.entry.isLast()) { + if(r.entry.isLast()) { // no comparator return defaultComparatorEntry(values) .toComparison(td, r.col) .orElseThrow(); //cannot be empty @@ -108,48 +108,9 @@ public DBFilter evalFilter(TableDecorator td, List values) { e.setArgs(values); } } - var o = e.toComparison(td, r.col); //eval comparator first => avoid overriding - if(o.isEmpty() && r.col instanceof TaggableColumn) { //no operation - var c = r.cd.criteria(e.value); - if(nonNull(c)) { - return r.col.filter(c.build(toStringArray(e.args))); - } - } - if(o.isEmpty()) { - cannotEvaluateException("comparison|criteria", e); - } - var f = o.get(); - while(e.next()) { - e = e.next; - if(e.value.matches("and|or")) { - var op = LogicalOperator.valueOf(e.value.toUpperCase()); - if(!isEmpty(e.args) && e.args.size() == 1) { - f = f.append(op, e.args.get(0).evalFilter(td)); - } - else { - throw badArgumentCountException(1, isEmpty(e.args) ? 0 : e.args.size()); - } - } - else { - throw cannotEvaluateException("logical operator", e); - } - } - return f; + return e.chainComparator(td, r.cd, r.col); } - static RequestEntryChain defaultComparatorEntry(List values) { - String cmp; - if(isEmpty(values)) { - cmp = "isNull"; - } - else { - cmp = values.size() > 1 ? "in" : "eq"; - } - var e = new RequestEntryChain(cmp); - e.setArgs(values); - return e; - } - public Object[] evalFunction(TableDecorator td, String fnName, ParameterSet ps) { if(fnName.equals(value)) { return requireNoNext().toArgs(td, null, ps, Object[]::new); @@ -197,6 +158,50 @@ Optional toOperation(TableDecorator td, DBColumn col, Predicate return fillArgs(td, c, fn); }); } + + static RequestEntryChain defaultComparatorEntry(List values) { + String cmp; + if(isEmpty(values)) { + cmp = "isNull"; + } + else { + cmp = values.size() > 1 ? "in" : "eq"; + } + var e = new RequestEntryChain(cmp); + e.setArgs(values); + return e; + } + + DBFilter chainComparator(TableDecorator td, ColumnDecorator cd, DBColumn col){ + var f = toComparison(td, col).orElse(null); //eval comparator first => avoid overriding + if(isNull(f) && col instanceof TaggableColumn) { //no operation + var c = cd.criteria(value); + if(nonNull(c)) { + f = col.filter(c.build(toStringArray(args))); + } + } + if(isNull(f)) { + throw cannotEvaluateException("comparison|criteria", this); + } + var e = this; + while(e.next()) { + e = e.next; + if(e.value.matches("and|or")) { + var op = LogicalOperator.valueOf(e.value.toUpperCase()); + if(!isEmpty(e.args) && e.args.size() == 1) { + f = f.append(op, e.args.get(0).evalFilter(td)); + } + else { + throw badArgumentCountException(1, isEmpty(e.args) ? 0 : e.args.size()); + } + } + else { + throw cannotEvaluateException("logical operator", e); + } + } + return f; + } + Optional toComparison(TableDecorator td, DBObject col) { return lookupComparator(value).map(c-> fillArgs(td, col, c)); From 0e57e5f62221651bbc9af86b1a31ace774414d31 Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 11 Feb 2024 02:05:29 +0100 Subject: [PATCH 070/298] edit --- .../usf/jquery/core/BadArgumentException.java | 2 +- .../java/org/usf/jquery/core/Comparator.java | 2 +- .../java/org/usf/jquery/core/DBColumn.java | 14 +- .../java/org/usf/jquery/core/DBOrder.java | 6 + .../java/org/usf/jquery/core/DBTable.java | 15 +- src/main/java/org/usf/jquery/core/DBView.java | 14 +- .../java/org/usf/jquery/core/InCompartor.java | 2 +- .../org/usf/jquery/core/InternalQuery.java | 54 ----- .../org/usf/jquery/core/JQueryException.java | 22 ++ .../core/{JqueryType.java => JQueryType.java} | 4 +- .../org/usf/jquery/core/JqueryException.java | 22 -- .../java/org/usf/jquery/core/NamedColumn.java | 2 +- .../java/org/usf/jquery/core/NamedView.java | 37 ---- .../java/org/usf/jquery/core/Operator.java | 6 +- src/main/java/org/usf/jquery/core/Query.java | 11 - .../jquery/core/QueryParameterBuilder.java | 29 +-- .../usf/jquery/core/RequestQueryBuilder.java | 17 +- .../org/usf/jquery/core/TaggableColumn.java | 5 +- .../org/usf/jquery/core/TaggableView.java | 17 -- .../java/org/usf/jquery/core/ViewColumn.java | 4 +- .../java/org/usf/jquery/core/ViewQuery.java | 43 ++++ .../java/org/usf/jquery/core/WindowView.java | 53 ----- .../org/usf/jquery/web/ArgumentParsers.java | 29 +-- .../org/usf/jquery/web/ColumnDecorator.java | 5 +- .../usf/jquery/web/CompletableViewQuery.java | 17 ++ .../org/usf/jquery/web/EvalException.java | 1 - .../org/usf/jquery/web/RequestContext.java | 46 +++++ .../org/usf/jquery/web/RequestEntryChain.java | 188 +++++++++++------- .../org/usf/jquery/web/TableDecorator.java | 13 +- .../java/org/usf/jquery/web/ViewBuilder.java | 2 +- .../java/org/usf/jquery/web/WebException.java | 4 +- 31 files changed, 339 insertions(+), 347 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/core/InternalQuery.java create mode 100644 src/main/java/org/usf/jquery/core/JQueryException.java rename src/main/java/org/usf/jquery/core/{JqueryType.java => JQueryType.java} (86%) delete mode 100644 src/main/java/org/usf/jquery/core/JqueryException.java delete mode 100644 src/main/java/org/usf/jquery/core/NamedView.java delete mode 100644 src/main/java/org/usf/jquery/core/Query.java delete mode 100644 src/main/java/org/usf/jquery/core/TaggableView.java create mode 100644 src/main/java/org/usf/jquery/core/ViewQuery.java delete mode 100644 src/main/java/org/usf/jquery/core/WindowView.java create mode 100644 src/main/java/org/usf/jquery/web/CompletableViewQuery.java create mode 100644 src/main/java/org/usf/jquery/web/RequestContext.java diff --git a/src/main/java/org/usf/jquery/core/BadArgumentException.java b/src/main/java/org/usf/jquery/core/BadArgumentException.java index 5d59d861..81dfdab2 100644 --- a/src/main/java/org/usf/jquery/core/BadArgumentException.java +++ b/src/main/java/org/usf/jquery/core/BadArgumentException.java @@ -10,7 +10,7 @@ * */ @SuppressWarnings("serial") -public final class BadArgumentException extends JqueryException { +public final class BadArgumentException extends JQueryException { public BadArgumentException(String message) { super(message); diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 2f9928c1..fff1b254 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -3,7 +3,7 @@ import static java.util.Arrays.copyOfRange; import static org.usf.jquery.core.ArgTypeRef.firstArgJdbcType; import static org.usf.jquery.core.JDBCType.VARCHAR; -import static org.usf.jquery.core.JqueryType.QUERY; +import static org.usf.jquery.core.JQueryType.QUERY; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; import static org.usf.jquery.core.ParameterSet.ofParameters; diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 930998d4..5c5af726 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -1,9 +1,11 @@ package org.usf.jquery.core; import static org.usf.jquery.core.QueryParameterBuilder.formatValue; +import static org.usf.jquery.core.SqlStringBuilder.member; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; +import java.util.Objects; import java.util.function.Supplier; import org.usf.jquery.core.CaseSingleColumnBuilder.WhenFilterBridge; @@ -41,7 +43,7 @@ default JavaType getType() { } default NamedColumn as(String name) { - return new NamedColumn(this, requireLegalVariable(name)); + return new NamedColumn(this, Objects.isNull(name) ? null : requireLegalVariable(name)); } default DBOrder order() { @@ -138,7 +140,15 @@ default WhenFilterBridge when(ComparisonExpression ex) { } static DBColumn column(@NonNull String value) { - return p-> value; + return b-> value; + } + + static DBColumn allColumns(@NonNull DBView view) { + return column(view, "*"); + } + + static DBColumn column(@NonNull DBView view, @NonNull String value) { + return b-> member(b.view(view), value); } static DBColumn constant(Object value) { diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index 1bceceb1..c62caaa7 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -1,6 +1,7 @@ package org.usf.jquery.core; import static java.util.Objects.isNull; +import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -34,4 +35,9 @@ public String sql(QueryParameterBuilder builder) { : column.sql(builder) + SPACE + order.name(); } + @Override + public String toString() { + return sql(addWithValue()); + } + } diff --git a/src/main/java/org/usf/jquery/core/DBTable.java b/src/main/java/org/usf/jquery/core/DBTable.java index 74dd5166..dc01644a 100644 --- a/src/main/java/org/usf/jquery/core/DBTable.java +++ b/src/main/java/org/usf/jquery/core/DBTable.java @@ -3,6 +3,7 @@ import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.member; +import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; /** @@ -10,22 +11,22 @@ * @author u$f * */ +@EqualsAndHashCode(of = "tag") @RequiredArgsConstructor -public class DBTable implements TaggableView { +public class DBTable implements DBView { private final String name; - private final String tag; + private final String tag; //only for equals + + public DBTable(String name) { + this(name, null); + } @Override public String sql(QueryParameterBuilder builder) { return member(builder.getSchema(), name); } - @Override - public String tagname() { - return tag; - } - @Override public String toString() { return sql(addWithValue()); diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 0095979d..e25256f7 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.DBColumn.allColumns; +import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; /** @@ -16,9 +18,17 @@ default String sql(QueryParameterBuilder builder, Object[] args) { return sql(builder); } + default String sqlWithTag(QueryParameterBuilder builder) { + return sql(builder) + SPACE + builder.view(this); + } + String sql(QueryParameterBuilder builder); + + default ViewQuery select(String tag, TaggableColumn... columns) { + return new ViewQuery(tag, columns); + } - default TaggableView as(String tagname) { - return new NamedView(this, tagname); + default ViewQuery window(String tag, TaggableColumn column) { + return new ViewQuery(tag, allColumns(this).as(null), column); } } diff --git a/src/main/java/org/usf/jquery/core/InCompartor.java b/src/main/java/org/usf/jquery/core/InCompartor.java index 93c8ad8f..eb1874ea 100644 --- a/src/main/java/org/usf/jquery/core/InCompartor.java +++ b/src/main/java/org/usf/jquery/core/InCompartor.java @@ -19,6 +19,6 @@ default String sql(QueryParameterBuilder builder, Object[] args) { requireAtLeastNArgs(2, args, InCompartor.class::getSimpleName); var type = typeOf(args[0]).orElseThrow(Comparator::typeCannotBeNullException); var varg = copyOfRange(args, 1, args.length); - return builder.appendLitteral(args[0]) + SPACE + id() + parenthese(args.length == 2 && args[1] instanceof InternalQuery ? builder.appendLitteral(args[1]) : builder.appendArrayParameter(type, varg)); + return builder.appendLitteral(args[0]) + SPACE + id() + parenthese(args.length == 2 && args[1] instanceof ViewQuery ? builder.appendLitteral(args[1]) : builder.appendArrayParameter(type, varg)); } } diff --git a/src/main/java/org/usf/jquery/core/InternalQuery.java b/src/main/java/org/usf/jquery/core/InternalQuery.java deleted file mode 100644 index 9d916887..00000000 --- a/src/main/java/org/usf/jquery/core/InternalQuery.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.usf.jquery.core; - -import static java.util.function.Predicate.not; -import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.LogicalOperator.AND; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; -import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.SqlStringBuilder.space; -import static org.usf.jquery.core.Utils.isEmpty; - -import java.util.function.Predicate; -import java.util.stream.Stream; - -import lombok.RequiredArgsConstructor; - -/** - * - * @author u$f - * - */ -@RequiredArgsConstructor -public class InternalQuery implements Query {//see RequestQueryBuilder::build - - private final DBColumn[] columns; - private final DBFilter[] filters; - - @Override - public String sql(QueryParameterBuilder builder) { - var sub = builder.subQuery(); - var sb = new SqlStringBuilder(100) - .append("SELECT ").append(sub.appendLitteralArray(columns)) - .append(" FROM ").appendEach(sub.views(), SCOMA, v-> v.sqlWithTag(sub)); - filter(sub, sb, "WHERE", not(DBFilter::isAggregation)); -// filter(sub, sb, "HAVING", DBFilter::isAggregation); - return sb.toString(); - } - - void filter(QueryParameterBuilder pb, SqlStringBuilder sb, String caluse, Predicate pre){ - if(!isEmpty(filters)) { - var ex = Stream.of(filters) - .filter(pre) - .map(f-> f.sql(pb)) - .collect(joining(AND.sql())); - if(!ex.isEmpty()) { - sb.append(space(caluse)).append(ex); - } - } - } - - @Override - public String toString() { - return sql(addWithValue()); - } -} diff --git a/src/main/java/org/usf/jquery/core/JQueryException.java b/src/main/java/org/usf/jquery/core/JQueryException.java new file mode 100644 index 00000000..c585bf58 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/JQueryException.java @@ -0,0 +1,22 @@ +package org.usf.jquery.core; + +/** + * + * @author u$f + * + */ +@SuppressWarnings("serial") +public class JQueryException extends RuntimeException { + + public JQueryException(String message) { + super(message); + } + + public JQueryException(Throwable cause) { + super(cause); + } + + public JQueryException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/usf/jquery/core/JqueryType.java b/src/main/java/org/usf/jquery/core/JQueryType.java similarity index 86% rename from src/main/java/org/usf/jquery/core/JqueryType.java rename to src/main/java/org/usf/jquery/core/JQueryType.java index 5c482f96..fa77920f 100644 --- a/src/main/java/org/usf/jquery/core/JqueryType.java +++ b/src/main/java/org/usf/jquery/core/JQueryType.java @@ -8,12 +8,12 @@ * */ @RequiredArgsConstructor -public enum JqueryType implements JavaType { +public enum JQueryType implements JavaType { COLUMN(DBColumn.class), FILTER(DBFilter.class), ORDER(DBOrder.class), - QUERY(Query.class), + QUERY(ViewQuery.class), PARTITIONS(DBColumn[].class), COLUMNS(DBColumn[].class), FILTERS(DBFilter[].class), diff --git a/src/main/java/org/usf/jquery/core/JqueryException.java b/src/main/java/org/usf/jquery/core/JqueryException.java deleted file mode 100644 index f20c562c..00000000 --- a/src/main/java/org/usf/jquery/core/JqueryException.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.usf.jquery.core; - -/** - * - * @author u$f - * - */ -@SuppressWarnings("serial") -public class JqueryException extends RuntimeException { - - public JqueryException(String message) { - super(message); - } - - public JqueryException(Throwable cause) { - super(cause); - } - - public JqueryException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index 3a47634b..8c9c58da 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -23,7 +23,7 @@ public String tagname() { @Override public NamedColumn as(String name) { // map - return new NamedColumn(unwrap(), name); + return tag.equals(name) ? this : new NamedColumn(unwrap(), name); } public DBColumn unwrap() { diff --git a/src/main/java/org/usf/jquery/core/NamedView.java b/src/main/java/org/usf/jquery/core/NamedView.java deleted file mode 100644 index 86164408..00000000 --- a/src/main/java/org/usf/jquery/core/NamedView.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.usf.jquery.core; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; - -/** - * - * @author u$f - * - */ -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public final class NamedView implements TaggableView { - - @Delegate - private final DBView view; - private final String tag; - - @Override - public String tagname() { - return tag; - } - - @Override - public NamedView as(String name) { // map - return new NamedView(unwrap(), name); - } - - public DBView unwrap() { - return view; - } - - @Override - public String toString() { - return view.toString(); - } -} diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 93bbb1d5..7d87ee35 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -10,9 +10,9 @@ import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; import static org.usf.jquery.core.JDBCType.VARCHAR; -import static org.usf.jquery.core.JqueryType.COLUMN; -import static org.usf.jquery.core.JqueryType.ORDERS; -import static org.usf.jquery.core.JqueryType.PARTITIONS; +import static org.usf.jquery.core.JQueryType.COLUMN; +import static org.usf.jquery.core.JQueryType.ORDERS; +import static org.usf.jquery.core.JQueryType.PARTITIONS; import static org.usf.jquery.core.Parameter.optional; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; diff --git a/src/main/java/org/usf/jquery/core/Query.java b/src/main/java/org/usf/jquery/core/Query.java deleted file mode 100644 index a827a2bb..00000000 --- a/src/main/java/org/usf/jquery/core/Query.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.usf.jquery.core; - -/** - * - * @author u$f - * - */ -@FunctionalInterface -public interface Query extends DBView { - -} diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index b01ad69b..5ac1cca0 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -9,10 +9,8 @@ import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.isEmpty; -import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -import java.util.function.IntConsumer; import java.util.stream.Stream; import lombok.AccessLevel; @@ -35,27 +33,18 @@ public final class QueryParameterBuilder { private final String vPrefix; private final List args; private final List argTypes; - private final List views; //indexed + private final List views; //indexed - public List views(){ + public List views(){ return views; } - public String view(TaggableView view) { - return view(view, i-> {}); - } - - public String overwriteView(TaggableView view) { - return view(view, i-> views.set(i, view)); - } - - private String view(TaggableView view, IntConsumer consumer) { + public String view(@NonNull DBView view) { if(isNull(vPrefix)) { return null; } for(var i=0; i()); + return new QueryParameterBuilder(schema, isNull(vPrefix) ? null : vPrefix + "_s", args, argTypes, new LinkedList<>()); } public static QueryParameterBuilder addWithValue() { return new QueryParameterBuilder(null, null, null, null, null); //no args } - public static QueryParameterBuilder parametrized() { - return parametrized(null); + public static QueryParameterBuilder parametrized(List views) { + return parametrized(null, views); } - public static QueryParameterBuilder parametrized(String schema) { - return new QueryParameterBuilder(schema, "v", new LinkedList<>(), new LinkedList<>(), new ArrayList<>()); + public static QueryParameterBuilder parametrized(String schema, List views) { + return new QueryParameterBuilder(schema, "v", new LinkedList<>(), new LinkedList<>(), views); } } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 07ee2c9f..552f0d9b 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -34,6 +34,7 @@ public class RequestQueryBuilder { private final List columns = new LinkedList<>(); private final List filters = new LinkedList<>(); //WERE & HAVING + private final List views = new LinkedList<>(); private final List orders = new LinkedList<>(); private Iterator it; private boolean distinct; @@ -44,6 +45,11 @@ public RequestQueryBuilder distinct() { distinct = true; return this; } + + public RequestQueryBuilder views(@NonNull DBView... views) { + Stream.of(views).forEach(this.views::add); + return this; + } public RequestQueryBuilder columns(@NonNull TaggableColumn... columns) { Stream.of(columns).forEach(this.columns::add); @@ -81,7 +87,7 @@ public RequestQuery build(String schema) { // requireNonEmpty(tables); requireNonEmpty(columns); var bg = currentTimeMillis(); - var pb = parametrized(schema); + var pb = parametrized(schema, views); var sb = new SqlStringBuilder(1000); //avg // pb.tables(tables.stream().map(TaggableView::tagname).toArray(String[]::new)); if(isNull(it)) { @@ -95,16 +101,15 @@ public RequestQuery build(String schema) { } public final void build(SqlStringBuilder sb, QueryParameterBuilder pb){ + select(sb, pb); //declare all view before FROM where(sb, pb); groupBy(sb); having(sb, pb); orderBy(sb, pb); fetch(sb); - sb.sb.insert(0, select(pb)); //declare all view before FROM } - @Deprecated - String select(QueryParameterBuilder pb){ + void select(SqlStringBuilder sb, QueryParameterBuilder pb){ if(currentDatabase() == TERADATA) { if(nonNull(offset)) { throw new UnsupportedOperationException(""); @@ -113,13 +118,13 @@ String select(QueryParameterBuilder pb){ throw new UnsupportedOperationException("Top N option is not supported with DISTINCT option."); } } - return new SqlStringBuilder(100).append("SELECT") + sb.append("SELECT") .appendIf(distinct, ()-> " DISTINCT") .appendIf(nonNull(fetch), ()-> " TOP " + fetch) .append(SPACE) .appendEach(columns, SCOMA, o-> o.sqlWithTag(pb)) .appendIf(!pb.views().isEmpty(), " FROM ") //TODO finish this - .appendEach(pb.views(), SCOMA, o-> o.sqlWithTag(pb)).toString(); + .appendEach(pb.views(), SCOMA, o-> o.sqlWithTag(pb)); } void where(SqlStringBuilder sb, QueryParameterBuilder pb){ diff --git a/src/main/java/org/usf/jquery/core/TaggableColumn.java b/src/main/java/org/usf/jquery/core/TaggableColumn.java index 1e38a943..702c250d 100644 --- a/src/main/java/org/usf/jquery/core/TaggableColumn.java +++ b/src/main/java/org/usf/jquery/core/TaggableColumn.java @@ -2,6 +2,8 @@ import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; +import java.util.Objects; + /** * * @author u$f @@ -12,6 +14,7 @@ public interface TaggableColumn extends DBColumn { String tagname(); //JSON & TAG default String sqlWithTag(QueryParameterBuilder builder) { - return sql(builder) + " AS " + doubleQuote(tagname()); + var s = sql(builder); + return Objects.isNull(tagname()) ? s : s + " AS " + doubleQuote(tagname()); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/TaggableView.java b/src/main/java/org/usf/jquery/core/TaggableView.java deleted file mode 100644 index ba095aee..00000000 --- a/src/main/java/org/usf/jquery/core/TaggableView.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.usf.jquery.core; - -import static org.usf.jquery.core.SqlStringBuilder.SPACE; - -/** - * - * @author u$f - * - */ -public interface TaggableView extends DBView { - - String tagname(); - - default String sqlWithTag(QueryParameterBuilder builder) { - return sql(builder) + SPACE + builder.view(this); - } -} diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index 383e3b2d..7f319e76 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -13,12 +13,12 @@ @RequiredArgsConstructor public final class ViewColumn implements TaggableColumn { - private final TaggableView view; + private final DBView view; private final String name; private final String tag; private final JavaType type; - public ViewColumn(TaggableView view, String name, String tag) { + public ViewColumn(DBView view, String name, String tag) { this(view, name, tag, null); } diff --git a/src/main/java/org/usf/jquery/core/ViewQuery.java b/src/main/java/org/usf/jquery/core/ViewQuery.java new file mode 100644 index 00000000..76ba6aca --- /dev/null +++ b/src/main/java/org/usf/jquery/core/ViewQuery.java @@ -0,0 +1,43 @@ +package org.usf.jquery.core; + +import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; + +import lombok.NonNull; + +/** + * + * @author u$f + * + */ +//@EqualsAndHashCode(callSuper = true) //doesn't works +public class ViewQuery extends DBTable { //! important extends DBTable + + private final RequestQueryBuilder query; //named operation column + + public ViewQuery(String tag, @NonNull TaggableColumn... columns) { + super(null, tag); + this.query = new RequestQueryBuilder().columns(columns); + } + + @Override + public String sql(QueryParameterBuilder builder) { + var s = new SqlStringBuilder(100).append("("); + query.build(s, builder.subQuery()); + return s.append(")").toString(); + } + + public ViewQuery columns(TaggableColumn... columns) { + query.columns(columns); + return this; + } + + public ViewQuery filters(DBFilter... filters){ + query.filters(filters); + return this; + } + + @Override + public String toString() { + return sql(addWithValue()); + } +} diff --git a/src/main/java/org/usf/jquery/core/WindowView.java b/src/main/java/org/usf/jquery/core/WindowView.java deleted file mode 100644 index e312581b..00000000 --- a/src/main/java/org/usf/jquery/core/WindowView.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.usf.jquery.core; - -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; -import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; -import static org.usf.jquery.core.SqlStringBuilder.member; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; - -/** - * - * @author u$f - * - */ -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public final class WindowView implements Query { - - private final TaggableView view; - private final TaggableColumn column; //named operation column - - @Override - public String sql(QueryParameterBuilder builder) { - var b = builder.subQuery(); //sub query should not use main builder - return new SqlStringBuilder(100) - .append("(SELECT ").append(member(b.view(view), "*")).append(SCOMA).append(column.sqlWithTag(b)) - .append(" FROM ").append(view.sqlWithTag(b)).append(")") - .toString(); - } - - @Override - public String toString() { - return sql(addWithValue()); - } - - public static DBColumn windowColumn(TaggableView view, TaggableColumn column) { - var wv = new WindowView(view, column).as(view.tagname()); - return new DBColumn() { - @Override - public String sql(QueryParameterBuilder builder) { //overwrite view - return member(builder.overwriteView(wv), doubleQuote(column.tagname())); - } - @Override - public JavaType getType() { - return column.getType(); - } - @Override - public String toString() { - return sql(addWithValue()); - } - }; - } -} diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 1893be46..fbad03c6 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -7,11 +7,11 @@ import static org.usf.jquery.core.JDBCType.TIME; import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; -import static org.usf.jquery.core.JqueryType.COLUMN; -import static org.usf.jquery.core.JqueryType.COLUMNS; -import static org.usf.jquery.core.JqueryType.FILTER; -import static org.usf.jquery.core.JqueryType.FILTERS; -import static org.usf.jquery.core.JqueryType.ORDER; +import static org.usf.jquery.core.JQueryType.COLUMN; +import static org.usf.jquery.core.JQueryType.COLUMNS; +import static org.usf.jquery.core.JQueryType.FILTER; +import static org.usf.jquery.core.JQueryType.FILTERS; +import static org.usf.jquery.core.JQueryType.ORDER; import static org.usf.jquery.core.Parameter.optional; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.ParameterSet.ofParameters; @@ -28,12 +28,12 @@ import java.time.ZonedDateTime; import java.util.stream.Stream; -import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; -import org.usf.jquery.core.InternalQuery; import org.usf.jquery.core.JDBCType; +import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; -import org.usf.jquery.core.JqueryType; +import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.ViewQuery; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -63,8 +63,8 @@ public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType. } for(var type : types) { try { - if(type instanceof JqueryType) { - return jqueryArgParser((JqueryType) type).parse(entry, td); + if(type instanceof JQueryType) { + return jqueryArgParser((JQueryType) type).parse(entry, td); } else if(type instanceof JDBCType) { return jdbcArgParser((JDBCType) type).parse(entry, td); @@ -106,7 +106,7 @@ public static JDBCArgumentParser jdbcArgParser(JDBCType type) { } } - public static JavaArgumentParser jqueryArgParser(@NonNull JqueryType type) { + public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type) { switch (type) { case COLUMN: return RequestEntryChain::evalColumn; case FILTER: return RequestEntryChain::evalFilter; @@ -121,11 +121,12 @@ public static JavaArgumentParser jqueryArgParser(@NonNull JqueryType type) { } //move it => RequestEntryChain - private static InternalQuery evalQuery(RequestEntryChain re, TableDecorator td) {//move it + @Deprecated(forRemoval = true) + private static ViewQuery evalQuery(RequestEntryChain re, TableDecorator td) {//move it var args = re.evalFunction(td, "query", ofParameters(required(COLUMNS), optional(FILTERS))); //.distinct - var cols = (DBColumn[]) args[0]; + var cols = (TaggableColumn[]) args[0]; var flts = args.length > 1 ? (DBFilter[]) args[1] : null; - return new InternalQuery(cols, flts); + return new ViewQuery(td.identity(), null, cols).filters(flts); } private static Object parseUnknown(String s) { diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 133723e2..c2ca1164 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -56,10 +56,7 @@ static ColumnDecorator ofColumn(String ref, ColumnBuilder cb) { public String identity() { return ref; // default column tag } - @Override - public String reference() { - return ref; - } + @Override public ColumnBuilder builder() { return cb; diff --git a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java new file mode 100644 index 00000000..6c468d9c --- /dev/null +++ b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java @@ -0,0 +1,17 @@ +package org.usf.jquery.web; + +import org.usf.jquery.core.DBView; +import org.usf.jquery.core.ViewQuery; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; + +@Getter +@RequiredArgsConstructor +final class CompletableViewQuery implements DBView { + + @Delegate + private final ViewQuery query; + +} diff --git a/src/main/java/org/usf/jquery/web/EvalException.java b/src/main/java/org/usf/jquery/web/EvalException.java index 07d0449e..196be33e 100644 --- a/src/main/java/org/usf/jquery/web/EvalException.java +++ b/src/main/java/org/usf/jquery/web/EvalException.java @@ -8,7 +8,6 @@ * */ @SuppressWarnings("serial") - public final class EvalException extends WebException { public EvalException(String message) { diff --git a/src/main/java/org/usf/jquery/web/RequestContext.java b/src/main/java/org/usf/jquery/web/RequestContext.java new file mode 100644 index 00000000..cb97b156 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/RequestContext.java @@ -0,0 +1,46 @@ +package org.usf.jquery.web; + +import static java.util.Objects.isNull; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.usf.jquery.core.DBView; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class RequestContext { + + private static final ThreadLocal local = new ThreadLocal<>(); + + private final Map views = new LinkedHashMap<>(); + + public DBView getViews(String name) { + return views.get(name); + } + + public void setViews(String name, DBView v) { + views.put(name, v); + } + + public DBView[] views() { + return views.values().stream() + .map(v-> v instanceof CompletableViewQuery ? ((CompletableViewQuery)v).getQuery() : v) + .toArray(DBView[]::new); + } + + public static final RequestContext requestContext() { + var rc = local.get(); + if(isNull(rc)) { + rc = new RequestContext(); + local.set(rc); + } + return rc; + } + + public static final void clearContext() { + local.remove(); + } +} diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 1c5550fb..eb47c81a 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -6,16 +6,17 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.Comparator.lookupComparator; +import static org.usf.jquery.core.DBColumn.allColumns; import static org.usf.jquery.core.Operator.lookupOperator; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.WindowView.windowColumn; import static org.usf.jquery.web.ArgumentParsers.parse; import static org.usf.jquery.web.ColumnDecorator.ofColumn; import static org.usf.jquery.web.JQueryContext.context; +import static org.usf.jquery.web.RequestContext.requestContext; import java.util.List; import java.util.Optional; @@ -26,7 +27,7 @@ import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; import org.usf.jquery.core.DBOrder; -import org.usf.jquery.core.JqueryType; +import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.LogicalOperator; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; @@ -35,6 +36,7 @@ import org.usf.jquery.core.TypedComparator; import org.usf.jquery.core.TypedOperator; import org.usf.jquery.core.Utils; +import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.WindowFunction; import lombok.AccessLevel; @@ -63,9 +65,10 @@ public RequestEntryChain(String value) { } public TaggableColumn evalColumn(TableDecorator td) { - var r = chainResourceOperations(td, false); + var r = chainResourceOperations(td, false) + .orElseThrow(()-> cannotEvaluateException("column", this)); if(r.entry.isLast()) { - if(nonNull(r.entry.tag)) { //TD required if operation + if(nonNull(r.entry.tag)) { //TD: required tag if operation return r.col.as(r.entry.tag); } return r.col instanceof TaggableColumn @@ -76,7 +79,8 @@ public TaggableColumn evalColumn(TableDecorator td) { } public DBOrder evalOrder(TableDecorator td) { - var r = chainResourceOperations(td, false); + var r = chainResourceOperations(td, false) + .orElseThrow(()-> cannotEvaluateException("order", this)); if(r.entry.isLast()) { // no order return r.col.order(); } @@ -93,22 +97,40 @@ public DBFilter evalFilter(TableDecorator td) { } public DBFilter evalFilter(TableDecorator td, List values) { - var r = chainResourceOperations(td, true); - if(r.entry.isLast()) { // no comparator - return defaultComparatorEntry(values) - .toComparison(td, r.col) - .orElseThrow(); //cannot be empty + var o = chainResourceOperations(td, true); + if(o.isPresent()) { + var r = o.get(); + if(r.entry.isLast()) { // no comparator + r.entry.setNext(defaultComparatorEntry(values)); + } + r.entry.next.updateArgs(values); + return r.entry.next.chainComparator(td, r.cd, r.col); } - var e = r.entry.next; + if(next() && context().isDeclaredTable(value)) { + var c = context().getTable(value).criteria(next.value); + if(nonNull(c)) { + next.updateArgs(values); + return next.chainComparator(td, c.build(toStringArray(next.args))); + } + } + var c = td.criteria(value); + if(nonNull(c)) { + updateArgs(values); + return chainComparator(td, c.build(toStringArray(args))); + } + return null; + } + + //column.eq=v1 + private void updateArgs(List values) { if(!isEmpty(values)) { - if(!e.isLast()) { - throw new RequestSyntaxException(e + "=" + Utils.toString(values.toArray())); + if(isLast() && isNull(args)) { + setArgs(values); } - if(isNull(e.args)) { - e.setArgs(values); + else { + throw new RequestSyntaxException(this + "=" + Utils.toString(values.toArray())); } } - return e.chainComparator(td, r.cd, r.col); } public Object[] evalFunction(TableDecorator td, String fnName, ParameterSet ps) { @@ -118,7 +140,7 @@ public Object[] evalFunction(TableDecorator td, String fnName, ParameterSet ps) throw cannotEvaluateException(fnName, this); } - public Object[] evalArrayFunction(TableDecorator td, String fnName, JqueryType type) { + public Object[] evalArrayFunction(TableDecorator td, String fnName, JQueryType type) { if(fnName.equals(value)) { var c = type.typeClass(); if(!c.isArray()) { //basic type @@ -128,36 +150,6 @@ public Object[] evalArrayFunction(TableDecorator td, String fnName, JqueryType t } throw cannotEvaluateException(fnName, this); } - - ResourceCursor chainResourceOperations(TableDecorator td, boolean filter) { - var r = lookupResource(td); - var e = r.entry.next; - while(nonNull(e)) { - var c = e.toOperation(td, r.col, fn-> true); - if(c.isEmpty()) { - break; - } // preserve last non null column - r.entry = e; - r.col = filter && "over".equals(e.value) - ? windowColumn(r.td.table(), c.get().as(r.cd.identity())) - : c.get(); - e = e.next; - } - return r; - } - - Optional toOperation(TableDecorator td, DBColumn col, Predicate pre) { - return lookupOperator(value).filter(pre).map(fn-> { - var c = col; - if(isNull(c) && isEmpty(args) && "count".equals(value)) { // id is MAJ - c = b-> { - b.view(td.table()); // important! register view - return "*"; - }; - } - return fillArgs(td, c, fn); - }); - } static RequestEntryChain defaultComparatorEntry(List values) { String cmp; @@ -167,25 +159,26 @@ static RequestEntryChain defaultComparatorEntry(List values) else { cmp = values.size() > 1 ? "in" : "eq"; } - var e = new RequestEntryChain(cmp); - e.setArgs(values); - return e; + return new RequestEntryChain(cmp); // do not set args } DBFilter chainComparator(TableDecorator td, ColumnDecorator cd, DBColumn col){ - var f = toComparison(td, col).orElse(null); //eval comparator first => avoid overriding + var f = lookupComparator(value).map(c-> fillArgs(td, col, c)).orElse(null); //eval comparator first => avoid overriding if(isNull(f) && col instanceof TaggableColumn) { //no operation - var c = cd.criteria(value); + var c = cd.criteria(value); //criteria lookup if(nonNull(c)) { f = col.filter(c.build(toStringArray(args))); } } - if(isNull(f)) { - throw cannotEvaluateException("comparison|criteria", this); + if(nonNull(f)) { + return chainComparator(td, f); } - var e = this; - while(e.next()) { - e = e.next; + throw cannotEvaluateException("comparison|criteria", this); + } + + DBFilter chainComparator(TableDecorator td, DBFilter f){ + var e = next; + while(nonNull(e)) { if(e.value.matches("and|or")) { var op = LogicalOperator.valueOf(e.value.toUpperCase()); if(!isEmpty(e.args) && e.args.size() == 1) { @@ -198,36 +191,73 @@ DBFilter chainComparator(TableDecorator td, ColumnDecorator cd, DBColumn col){ else { throw cannotEvaluateException("logical operator", e); } + e = e.next; } return f; } + private Optional chainResourceOperations(TableDecorator td, boolean filter) { + return lookupResource(td).map(r-> { + var e = r.entry.next; + while(nonNull(e)) { // chain until !operator + var c = e.toOperation(td, r.col, fn-> true); + if(c.isEmpty()) { + break; + } + r.entry = e; + r.col = filter && "over".equals(e.value) + ? windowColumn(r.td, c.get().as(r.cd.identity())) + : c.get(); + e = e.next; + } + return r; + }); + } - Optional toComparison(TableDecorator td, DBObject col) { - return lookupComparator(value).map(c-> fillArgs(td, col, c)); + private static DBColumn windowColumn(TableDecorator td, TaggableColumn column) { + var v = td.table(); + var vw = requestContext().getViews(td.identity()); // TD use tag ? + if(vw instanceof CompletableViewQuery) { // already create + ((CompletableViewQuery)vw).columns(column); + } + else { + if(isNull(vw)) { + vw = v; + } + else if(!vw.equals(v)) { + throw new IllegalStateException(); // 2 view same named + } + vw = new CompletableViewQuery(vw.window(td.identity(), column)); + requestContext().setViews(td.identity(), vw); // same name + } + return new ViewColumn(v, doubleQuote(column.tagname()), null, column.getType()); } - private ResourceCursor lookupResource(TableDecorator td) { + private Optional lookupResource(TableDecorator td) { if(next() && context().isDeclaredTable(value)) { //sometimes td.id == cd.id - var rc = next.lookupViewResource(context().getTable(value), - fn-> fn.unwrap().getClass() == WindowFunction.class); // only window function - if(nonNull(rc)) { + var rc = next.lookupViewResource(context().getTable(value), RequestEntryChain::isWindowFunction); + if(rc.isPresent()) { + requireNoArgs(); // noArgs on valid resource return rc; } } - var rc = lookupViewResource(td, fn-> true); // all operations - if(nonNull(rc)) { - return rc; - } - throw cannotEvaluateException("resource", this); + return lookupViewResource(td, fn-> true); // all operations } - - private ResourceCursor lookupViewResource(TableDecorator td, Predicate pre) { + + private Optional lookupViewResource(TableDecorator td, Predicate pre) { return context().isDeclaredColumn(value) - ? new ResourceCursor(td, context().getColumn(requireNoArgs().value), this) - : toOperation(td, null, pre) - .map(op-> new ResourceCursor(td, ofColumn(value, b-> op), this)) - .orElse(null); + ? Optional.of(new ResourceCursor(td, context().getColumn(requireNoArgs().value), this)) + : toOperation(td, null, pre).map(op-> new ResourceCursor(td, ofColumn(value, b-> op), this)); + } + + private Optional toOperation(TableDecorator td, DBColumn col, Predicate pre) { + return lookupOperator(value).filter(pre).map(fn-> { + var c = col; + if(isNull(c) && isEmpty(args) && "count".equals(value)) { // id is MAJ + c = allColumns(td.table()); + } + return fillArgs(td, c, fn); + }); } private DBFilter fillArgs(TableDecorator td, DBObject col, TypedComparator cmp) { @@ -267,7 +297,7 @@ private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps, IntFun }); return arr; } - + RequestEntryChain requireNoArgs() { if(isNull(args)) { return this; @@ -305,13 +335,17 @@ public String toString() { return isNull(tag) ? s : s + ":" + tag; } - static String[] toStringArray(List entries) { + private static boolean isWindowFunction(TypedOperator op) { + return op.unwrap().getClass() == WindowFunction.class; // !instanceOf : only window function + } + + private static String[] toStringArray(List entries) { return entries.stream() .map(e-> isNull(e.value) ? null : e.toString()) .toArray(String[]::new); } - static EvalException cannotEvaluateException(String type, RequestEntryChain entry) { + private static EvalException cannotEvaluateException(String type, RequestEntryChain entry) { return EvalException.cannotEvaluateException(type, entry.toString()); } diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index e67719f5..9e2083a4 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -15,6 +15,8 @@ import static org.usf.jquery.web.JQueryContext.database; import static org.usf.jquery.web.MissingParameterException.missingParameterException; import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; +import static org.usf.jquery.web.RequestContext.clearContext; +import static org.usf.jquery.web.RequestContext.requestContext; import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.RequestParser.parseEntry; @@ -28,9 +30,9 @@ import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBTable; +import org.usf.jquery.core.DBView; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; -import org.usf.jquery.core.TaggableView; import org.usf.jquery.core.ViewColumn; /** @@ -46,10 +48,10 @@ public interface TableDecorator { Optional columnName(ColumnDecorator cd); - default TaggableView table() { //optim + default DBView table() { //optim var b = builder(); return nonNull(b) - ? b.build().as(identity()) + ? b.build(identity()) : new DBTable(tableName(), identity()); } @@ -71,13 +73,14 @@ default CriteriaBuilder criteria(String name) { //!aggregation } default RequestQueryBuilder query(Map parameterMap) { + clearContext(); currentDatabase(database().getType()); //table database var query = new RequestQueryBuilder(); parseColumns(query, parameterMap); parseFilters(query, parameterMap); parseOrders (query, parameterMap); - parseFetch(query, parameterMap); - return query; + parseFetch(query, parameterMap); + return query.views(requestContext().views()); } default void parseColumns(RequestQueryBuilder query, Map parameters) { diff --git a/src/main/java/org/usf/jquery/web/ViewBuilder.java b/src/main/java/org/usf/jquery/web/ViewBuilder.java index 280c91ed..6fe6880a 100644 --- a/src/main/java/org/usf/jquery/web/ViewBuilder.java +++ b/src/main/java/org/usf/jquery/web/ViewBuilder.java @@ -10,5 +10,5 @@ @FunctionalInterface public interface ViewBuilder { - DBView build(); + DBView build(String id); } diff --git a/src/main/java/org/usf/jquery/web/WebException.java b/src/main/java/org/usf/jquery/web/WebException.java index ca81b733..1666bbe2 100644 --- a/src/main/java/org/usf/jquery/web/WebException.java +++ b/src/main/java/org/usf/jquery/web/WebException.java @@ -1,6 +1,6 @@ package org.usf.jquery.web; -import org.usf.jquery.core.JqueryException; +import org.usf.jquery.core.JQueryException; /** * @@ -8,7 +8,7 @@ * */ @SuppressWarnings("serial") -public class WebException extends JqueryException { +public class WebException extends JQueryException { public WebException(String message) { super(message); From 8154342f2391049bd711a01972e262dd25f0c55c Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 11 Feb 2024 02:30:01 +0100 Subject: [PATCH 071/298] edit --- .../java/org/usf/jquery/core/DBTable.java | 13 ++++++------- src/main/java/org/usf/jquery/core/DBView.java | 3 ++- .../jquery/core/QueryParameterBuilder.java | 2 +- .../java/org/usf/jquery/core/ViewQuery.java | 19 +++++++++++++------ .../org/usf/jquery/web/ArgumentParsers.java | 2 +- .../usf/jquery/web/CompletableViewQuery.java | 1 - .../org/usf/jquery/web/RequestContext.java | 12 +++++------- .../org/usf/jquery/web/RequestEntryChain.java | 12 +++--------- .../org/usf/jquery/web/TableDecorator.java | 2 +- 9 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBTable.java b/src/main/java/org/usf/jquery/core/DBTable.java index dc01644a..405f872c 100644 --- a/src/main/java/org/usf/jquery/core/DBTable.java +++ b/src/main/java/org/usf/jquery/core/DBTable.java @@ -3,7 +3,6 @@ import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.member; -import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; /** @@ -11,21 +10,21 @@ * @author u$f * */ -@EqualsAndHashCode(of = "tag") @RequiredArgsConstructor public class DBTable implements DBView { + private final String id; private final String name; - private final String tag; //only for equals - public DBTable(String name) { - this(name, null); - } - @Override public String sql(QueryParameterBuilder builder) { return member(builder.getSchema(), name); } + + @Override + public String id() { + return id; + } @Override public String toString() { diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index e25256f7..839c009b 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -9,9 +9,10 @@ * @author u$f * */ -@FunctionalInterface public interface DBView extends DBObject { + String id(); + @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNoArgs(args, DBColumn.class::getSimpleName); diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 5ac1cca0..4f9e106d 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -44,7 +44,7 @@ public String view(@NonNull DBView view) { return null; } for(var i=0; i 1 ? (DBFilter[]) args[1] : null; - return new ViewQuery(td.identity(), null, cols).filters(flts); + return new ViewQuery(td.identity(), cols).filters(flts); } private static Object parseUnknown(String s) { diff --git a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java index 6c468d9c..13963004 100644 --- a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java +++ b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java @@ -7,7 +7,6 @@ import lombok.RequiredArgsConstructor; import lombok.experimental.Delegate; -@Getter @RequiredArgsConstructor final class CompletableViewQuery implements DBView { diff --git a/src/main/java/org/usf/jquery/web/RequestContext.java b/src/main/java/org/usf/jquery/web/RequestContext.java index cb97b156..d49e1bdf 100644 --- a/src/main/java/org/usf/jquery/web/RequestContext.java +++ b/src/main/java/org/usf/jquery/web/RequestContext.java @@ -17,18 +17,16 @@ public final class RequestContext { private final Map views = new LinkedHashMap<>(); - public DBView getViews(String name) { - return views.get(name); + public DBView getView(DBView v) { + return views.get(v.id()); } - public void setViews(String name, DBView v) { - views.put(name, v); + public void setViews(DBView v) { + views.put(v.id(), v); } public DBView[] views() { - return views.values().stream() - .map(v-> v instanceof CompletableViewQuery ? ((CompletableViewQuery)v).getQuery() : v) - .toArray(DBView[]::new); + return views.values().toArray(DBView[]::new); } public static final RequestContext requestContext() { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index eb47c81a..c2ca4a16 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -216,19 +216,13 @@ private Optional chainResourceOperations(TableDecorator td, bool private static DBColumn windowColumn(TableDecorator td, TaggableColumn column) { var v = td.table(); - var vw = requestContext().getViews(td.identity()); // TD use tag ? + var vw = requestContext().getView(v); // TD use tag ? if(vw instanceof CompletableViewQuery) { // already create ((CompletableViewQuery)vw).columns(column); } else { - if(isNull(vw)) { - vw = v; - } - else if(!vw.equals(v)) { - throw new IllegalStateException(); // 2 view same named - } - vw = new CompletableViewQuery(vw.window(td.identity(), column)); - requestContext().setViews(td.identity(), vw); // same name + vw = new CompletableViewQuery((isNull(vw) ? v : vw).window(td.identity(), column)); + requestContext().setViews(vw); // same name } return new ViewColumn(v, doubleQuote(column.tagname()), null, column.getType()); } diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index 9e2083a4..cbe414b4 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -52,7 +52,7 @@ default DBView table() { //optim var b = builder(); return nonNull(b) ? b.build(identity()) - : new DBTable(tableName(), identity()); + : new DBTable(identity(), tableName()); } default TaggableColumn column(ColumnDecorator cd) { From de3ba9d016b444f03801ecfdd31e7a7742365955 Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 11 Feb 2024 02:35:41 +0100 Subject: [PATCH 072/298] edit --- src/main/java/org/usf/jquery/web/CompletableViewQuery.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java index 13963004..12d9f1df 100644 --- a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java +++ b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java @@ -3,7 +3,6 @@ import org.usf.jquery.core.DBView; import org.usf.jquery.core.ViewQuery; -import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.experimental.Delegate; From 1ee95cd5037b7eaba12f73894bf7a397e7f9ba6f Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 13 Feb 2024 21:37:26 +0100 Subject: [PATCH 073/298] edit --- .../java/org/usf/jquery/core/Comparator.java | 19 +-- .../java/org/usf/jquery/core/JQueryType.java | 7 +- .../java/org/usf/jquery/core/Operator.java | 9 +- .../org/usf/jquery/core/ParameterSet.java | 10 +- .../core/{OverClause.java => Partition.java} | 26 +-- .../jquery/core/QueryParameterBuilder.java | 9 +- .../usf/jquery/core/RequestQueryBuilder.java | 15 +- .../org/usf/jquery/core/TypedComparator.java | 28 +--- .../java/org/usf/jquery/core/ViewQuery.java | 22 +-- .../org/usf/jquery/web/ArgumentParsers.java | 40 ++--- .../usf/jquery/web/BadSyntaxException.java | 18 +++ .../org/usf/jquery/web/ColumnDecorator.java | 2 +- .../usf/jquery/web/CompletableViewQuery.java | 3 +- .../java/org/usf/jquery/web/Constants.java | 12 +- .../org/usf/jquery/web/EvalException.java | 1 + .../org/usf/jquery/web/JQueryContext.java | 10 +- .../org/usf/jquery/web/RequestContext.java | 54 ++++++- .../org/usf/jquery/web/RequestEntryChain.java | 149 ++++++++++++------ .../jquery/web/RequestSyntaxException.java | 15 -- .../org/usf/jquery/web/TableDecorator.java | 10 ++ 20 files changed, 271 insertions(+), 188 deletions(-) rename src/main/java/org/usf/jquery/core/{OverClause.java => Partition.java} (61%) create mode 100644 src/main/java/org/usf/jquery/web/BadSyntaxException.java delete mode 100644 src/main/java/org/usf/jquery/web/RequestSyntaxException.java diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index fff1b254..58e81160 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -3,7 +3,6 @@ import static java.util.Arrays.copyOfRange; import static org.usf.jquery.core.ArgTypeRef.firstArgJdbcType; import static org.usf.jquery.core.JDBCType.VARCHAR; -import static org.usf.jquery.core.JQueryType.QUERY; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; import static org.usf.jquery.core.ParameterSet.ofParameters; @@ -33,27 +32,27 @@ default ComparisonExpression expression(Object right) { } static TypedComparator eq() { - return new TypedComparator(basicComparator("="), required(), required()); + return new TypedComparator(basicComparator("="), required(), required(firstArgJdbcType())); } static TypedComparator ne() { - return new TypedComparator(basicComparator("<>"), required(), required()); + return new TypedComparator(basicComparator("<>"), required(), required(firstArgJdbcType())); } static TypedComparator lt() { - return new TypedComparator(basicComparator("<"), required(), required()); + return new TypedComparator(basicComparator("<"), required(), required(firstArgJdbcType())); } static TypedComparator le() { - return new TypedComparator(basicComparator("<="), required(), required()); + return new TypedComparator(basicComparator("<="), required(), required(firstArgJdbcType())); } static TypedComparator gt() { - return new TypedComparator(basicComparator(">"), required(), required()); + return new TypedComparator(basicComparator(">"), required(), required(firstArgJdbcType())); } static TypedComparator ge() { - return new TypedComparator(basicComparator(">="), required(), required()); + return new TypedComparator(basicComparator(">="), required(), required(firstArgJdbcType())); } //string comparator @@ -112,14 +111,12 @@ static TypedComparator notNull() { static TypedComparator in() { return new TypedComparator(inComparator("IN"), - ofParameters(required(), varargs(firstArgJdbcType())), - ofParameters(required(), required(QUERY))); + ofParameters(required(), varargs(firstArgJdbcType()))); } static TypedComparator notIn() { return new TypedComparator(inComparator("NOT IN"), - ofParameters(required(), varargs(firstArgJdbcType())), - ofParameters(required(), required(QUERY))); + ofParameters(required(), varargs(firstArgJdbcType()))); } static BasicComparator basicComparator(final String name) { diff --git a/src/main/java/org/usf/jquery/core/JQueryType.java b/src/main/java/org/usf/jquery/core/JQueryType.java index fa77920f..51d4fa4c 100644 --- a/src/main/java/org/usf/jquery/core/JQueryType.java +++ b/src/main/java/org/usf/jquery/core/JQueryType.java @@ -10,14 +10,11 @@ @RequiredArgsConstructor public enum JQueryType implements JavaType { - COLUMN(DBColumn.class), + COLUMN(TaggableColumn.class), //TD DBColumn !? FILTER(DBFilter.class), ORDER(DBOrder.class), QUERY(ViewQuery.class), - PARTITIONS(DBColumn[].class), - COLUMNS(DBColumn[].class), - FILTERS(DBFilter[].class), - ORDERS(DBOrder[].class); + PARTITION(Partition.class); //expression, WHEN_THEN, ... private final Class type; diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 7d87ee35..64a198cc 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -11,8 +11,7 @@ import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.JQueryType.COLUMN; -import static org.usf.jquery.core.JQueryType.ORDERS; -import static org.usf.jquery.core.JQueryType.PARTITIONS; +import static org.usf.jquery.core.JQueryType.PARTITION; import static org.usf.jquery.core.Parameter.optional; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; @@ -277,14 +276,12 @@ static TypedOperator denseRank() { //pipe functions static TypedOperator over() { - return new TypedOperator(firstArgJdbcType(), pipe("OVER"), required(COLUMN), optional(PARTITIONS), optional(ORDERS)) { + return new TypedOperator(firstArgJdbcType(), pipe("OVER"), required(COLUMN), required(PARTITION)) { @Override public OperationColumn args(Object... args) { return super.args(args).aggregation(false); //over aggregation functions } - }.argsMapper(args-> new Object[] {args[0], new OverClause( - args.length > 1 ? (DBColumn[])args[1] : null, - args.length > 2 ? (DBOrder[]) args[2] : null)}); + }; } // constant operators diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index 62c0e283..b605e1dc 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -18,15 +18,15 @@ */ @Getter @RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public final class ParameterSet { +public final class ParameterSet { //there is no Singleton implementation, dummy sonar rule - private static final Parameter[] NO_PARAM = new Parameter[0]; + public static final ParameterSet NO_PARAM = new ParameterSet(0, new Parameter[0]); private final int nReqArgs; private final Parameter[] parameters; public Object[] args(Object... args) { - var arr = isNull(args) ? NO_PARAM : args; + var arr = isNull(args) ? new Object[0] : args; forEach(arr.length, (p,i)-> { if(!p.accept(i, arr)) { throw badArgumentTypeException(p.types(args), arr[i]); @@ -54,10 +54,10 @@ public void forEach(int nArgs, ObjIntConsumer cons) { public boolean isVarags() { return parameters.length > 0 && parameters[parameters.length-1].isVarargs(); } - + public static ParameterSet ofParameters(Parameter... parameters) { if(isNull(parameters)) { - return new ParameterSet(0, NO_PARAM); + return NO_PARAM; } var i=0; for(; i it) { this.it = it; return this; } + + public ViewQuery as(String tag) { + return new ViewQuery(tag, this); + } public RequestQuery build(){ return build(null); diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index a9abf0f2..dc809655 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -1,8 +1,7 @@ package org.usf.jquery.core; -import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.ParameterSet.ofParameters; -import static org.usf.jquery.core.Utils.isEmpty; import java.util.function.UnaryOperator; @@ -20,37 +19,24 @@ public final class TypedComparator implements Comparator { @Delegate private final Comparator comparator; private final ParameterSet parameterSet; - private final ParameterSet[] overloads; private UnaryOperator argMapper; public TypedComparator(Comparator comparator, Parameter... parameters) { this(comparator, ofParameters(parameters)); } - public TypedComparator(Comparator comparator, ParameterSet parameterSet, ParameterSet... overloads) { + public TypedComparator(Comparator comparator, ParameterSet parameterSet) { this.comparator = comparator; this.parameterSet = parameterSet; - this.overloads = overloads; } + @Override public DBFilter args(Object... args) { - try { - return internalArgs(parameterSet, args); - } catch (RuntimeException e) { - if(!isEmpty(overloads)) { - for(var ps : overloads) { - try { - return internalArgs(ps, args); - } catch (RuntimeException e1) { /* do not throw exception */ } - } - } - throw e; //wrap exception + args = parameterSet.args(args); + if(nonNull(argMapper)) { + args = argMapper.apply(args); } - } - - private DBFilter internalArgs(ParameterSet ps, Object... args) { - args = ps.args(args); - return comparator.args(isNull(argMapper) ? args : argMapper.apply(args)); + return comparator.args(args); } public Comparator unwrap() { diff --git a/src/main/java/org/usf/jquery/core/ViewQuery.java b/src/main/java/org/usf/jquery/core/ViewQuery.java index 064ccc0f..25bca072 100644 --- a/src/main/java/org/usf/jquery/core/ViewQuery.java +++ b/src/main/java/org/usf/jquery/core/ViewQuery.java @@ -2,7 +2,10 @@ import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import org.usf.jquery.core.JavaType.Typed; + import lombok.AccessLevel; +import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -11,10 +14,11 @@ * @author u$f * */ -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public final class ViewQuery implements DBView { +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public final class ViewQuery implements DBView, Typed { private final String id; + @Getter private final RequestQueryBuilder query; public ViewQuery(@NonNull String id, @NonNull TaggableColumn... columns) { @@ -32,15 +36,13 @@ public String sql(QueryParameterBuilder builder) { public String id() { return id; } - - public ViewQuery columns(TaggableColumn... columns) { - query.columns(columns); - return this; - } - public ViewQuery filters(DBFilter... filters){ - query.filters(filters); - return this; + @Override + public JavaType getType() { + if(query.getColumns().size() == 1) { + return query.getColumns().get(0).getType(); + } + throw new IllegalStateException(query.getColumns().isEmpty() ? "no columns" : "too many columns"); } @Override diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 9865c203..dc3d666c 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -8,13 +8,7 @@ import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; import static org.usf.jquery.core.JQueryType.COLUMN; -import static org.usf.jquery.core.JQueryType.COLUMNS; -import static org.usf.jquery.core.JQueryType.FILTER; -import static org.usf.jquery.core.JQueryType.FILTERS; -import static org.usf.jquery.core.JQueryType.ORDER; -import static org.usf.jquery.core.Parameter.optional; -import static org.usf.jquery.core.Parameter.required; -import static org.usf.jquery.core.ParameterSet.ofParameters; +import static org.usf.jquery.core.JQueryType.QUERY; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.web.ParseException.cannotParseException; @@ -28,12 +22,9 @@ import java.time.ZonedDateTime; import java.util.stream.Stream; -import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; -import org.usf.jquery.core.TaggableColumn; -import org.usf.jquery.core.ViewQuery; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -56,7 +47,10 @@ public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType. if(isEmpty(types) || Stream.of(types).allMatch(JDBCType.class::isInstance)) { try { return jqueryArgParser(COLUMN).parse(entry, td); //only with JDBC types - } catch (Exception e) {/*do not throw exception*/} + } catch (EvalException e) {/*do not throw exception*/} + try { + return jqueryArgParser(QUERY).parse(entry, td); //only with JDBC types + } catch (EvalException e) {/*do not throw exception*/} if(isEmpty(types)) { return jdbcArgParser(null).parse(entry, td); } @@ -108,27 +102,15 @@ public static JDBCArgumentParser jdbcArgParser(JDBCType type) { public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type) { switch (type) { - case COLUMN: return RequestEntryChain::evalColumn; - case FILTER: return RequestEntryChain::evalFilter; - case ORDER: return RequestEntryChain::evalOrder; - case QUERY: return ArgumentParsers::evalQuery; - case PARTITIONS: return (re, td)-> re.evalArrayFunction(td, Constants.PARTITION, COLUMN); - case COLUMNS: return (re, td)-> re.evalArrayFunction(td, Constants.COLUMN, COLUMN); - case FILTERS: return (re, td)-> re.evalArrayFunction(td, Constants.FILTER, FILTER); - case ORDERS: return (re, td)-> re.evalArrayFunction(td, Constants.ORDER, ORDER); - default: throw unsupportedTypeException(type); + case COLUMN: return RequestEntryChain::evalColumn; + case FILTER: return RequestEntryChain::evalFilter; + case ORDER: return RequestEntryChain::evalOrder; + case QUERY: return RequestEntryChain::evalQuery; + case PARTITION: return RequestEntryChain::evalPartition; + default: throw unsupportedTypeException(type); } } - //move it => RequestEntryChain - @Deprecated(forRemoval = true) - private static ViewQuery evalQuery(RequestEntryChain re, TableDecorator td) {//move it - var args = re.evalFunction(td, "query", ofParameters(required(COLUMNS), optional(FILTERS))); //.distinct - var cols = (TaggableColumn[]) args[0]; - var flts = args.length > 1 ? (DBFilter[]) args[1] : null; - return new ViewQuery(td.identity(), cols).filters(flts); - } - private static Object parseUnknown(String s) { for(var p : STD_PRS) { try { diff --git a/src/main/java/org/usf/jquery/web/BadSyntaxException.java b/src/main/java/org/usf/jquery/web/BadSyntaxException.java new file mode 100644 index 00000000..34c8c0b8 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/BadSyntaxException.java @@ -0,0 +1,18 @@ +package org.usf.jquery.web; + +/** + * + * @author u$f + * + */ +@SuppressWarnings("serial") +public class BadSyntaxException extends WebException { + + public BadSyntaxException(String message) { + super(message); + } + + public static BadSyntaxException badSyntaxException(String o) { + return new BadSyntaxException("bad synthax : " + o); + } +} diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index c2ca1164..7a347b5d 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -24,7 +24,7 @@ default JDBCArgumentParser parser(TableDecorator td){ // override parser | forma return jdbcArgParser(dataType(td)); } - default JDBCType dataType(TableDecorator td) { + default JDBCType dataType(TableDecorator td) { // only if !builder return td.metadata().columnMetada(this) .map(ColumnMetadata::getDataType) .orElse(null); diff --git a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java index 12d9f1df..12637961 100644 --- a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java +++ b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java @@ -3,10 +3,11 @@ import org.usf.jquery.core.DBView; import org.usf.jquery.core.ViewQuery; +import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.Delegate; -@RequiredArgsConstructor +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) final class CompletableViewQuery implements DBView { @Delegate diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index 7c10066f..f5fcf959 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -13,9 +13,12 @@ */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class Constants { - - public static final String COLUMN = "column"; - public static final String COLUMN_DISTINCT = "column.distinct"; + + public static final String VIEW = "view"; + public static final String SELECT = "select"; //select + public static final String COLUMN = "column"; //select + public static final String DISTINCT = "distinct"; //select.distinct + public static final String COLUMN_DISTINCT = "column.distinct"; //select.distinct public static final String FILTER = "filter"; public static final String ORDER = "order"; public static final String FETCH = "fetch"; @@ -25,7 +28,8 @@ public final class Constants { public static final String REVISION_MODE = "revision.mode"; //not standard static final Set RESERVED_WORDS = - Set.of(COLUMN, COLUMN_DISTINCT, FILTER, ORDER, OFFSET, FETCH, PARTITION, REVISION, REVISION_MODE); //metadata ? + Set.of(VIEW, COLUMN, COLUMN_DISTINCT, FILTER, ORDER, OFFSET, FETCH, + PARTITION, REVISION, REVISION_MODE); //metadata ? static final YearMonth[] EMPTY_REVISION = new YearMonth[0]; //not standard diff --git a/src/main/java/org/usf/jquery/web/EvalException.java b/src/main/java/org/usf/jquery/web/EvalException.java index 196be33e..fb7c6a38 100644 --- a/src/main/java/org/usf/jquery/web/EvalException.java +++ b/src/main/java/org/usf/jquery/web/EvalException.java @@ -17,4 +17,5 @@ public EvalException(String message) { static EvalException cannotEvaluateException(String type, String expression) { return new EvalException("cannot evaluate " + type + " " + quote(expression)); } + } diff --git a/src/main/java/org/usf/jquery/web/JQueryContext.java b/src/main/java/org/usf/jquery/web/JQueryContext.java index 198d9781..406f61db 100644 --- a/src/main/java/org/usf/jquery/web/JQueryContext.java +++ b/src/main/java/org/usf/jquery/web/JQueryContext.java @@ -15,6 +15,7 @@ import javax.sql.DataSource; import lombok.AccessLevel; +import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -23,6 +24,7 @@ * @author u$f * */ +@Getter(AccessLevel.PACKAGE) @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class JQueryContext { @@ -71,12 +73,4 @@ public ColumnDecorator getColumn(String value) { return ofNullable(columns.get(value)) .orElseThrow(()-> throwNoSuchColumnException(value)); } - - public Collection tables(){ - return tables.values(); - } - - public Collection columns(){ - return columns.values(); - } } diff --git a/src/main/java/org/usf/jquery/web/RequestContext.java b/src/main/java/org/usf/jquery/web/RequestContext.java index d49e1bdf..fe3ed10b 100644 --- a/src/main/java/org/usf/jquery/web/RequestContext.java +++ b/src/main/java/org/usf/jquery/web/RequestContext.java @@ -1,27 +1,66 @@ package org.usf.jquery.web; import static java.util.Objects.isNull; +import static java.util.Optional.ofNullable; +import static org.usf.jquery.web.JQueryContext.context; +import static org.usf.jquery.web.NoSuchResourceException.throwNoSuchColumnException; +import static org.usf.jquery.web.NoSuchResourceException.throwNoSuchTableException; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import org.usf.jquery.core.DBView; import lombok.AccessLevel; -import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; -@NoArgsConstructor(access = AccessLevel.PRIVATE) +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class RequestContext { private static final ThreadLocal local = new ThreadLocal<>(); - private final Map views = new LinkedHashMap<>(); + private final Map tdMap; + private final Map columns; + private final Map views = new LinkedHashMap<>(); //work views + + public boolean isDeclaredView(String id) { + return tdMap.containsKey(id); + } + + public TableDecorator getViewDecorator(String id) { + return ofNullable(tdMap.get(id)). + orElseThrow(()-> throwNoSuchTableException(id)); + } + + public void putViewDecorator(TableDecorator td) { + if(tdMap.containsKey(td.identity())) { + throw new IllegalArgumentException(td.identity() + "already exist"); + } + tdMap.put(td.identity(), td); + } + + public boolean isDeclaredColumn(String id) { + return columns.containsKey(id); + } + + public ColumnDecorator getColumnDecorator(String value) { + return ofNullable(columns.get(value)) + .orElseThrow(()-> throwNoSuchColumnException(value)); + } + + public void putColumnDecorator(ColumnDecorator cd) { + if(columns.containsKey(cd.identity())) { + throw new IllegalArgumentException(cd.identity() + "already exist"); + } + columns.put(cd.identity(), cd); + } - public DBView getView(DBView v) { - return views.get(v.id()); + public DBView getView(String id) { + return views.get(id); } - public void setViews(DBView v) { + public void putView(DBView v) { views.put(v.id(), v); } @@ -32,7 +71,8 @@ public DBView[] views() { public static final RequestContext requestContext() { var rc = local.get(); if(isNull(rc)) { - rc = new RequestContext(); + var jc = context(); + rc = new RequestContext(new HashMap<>(jc.getTables()), new HashMap<>(jc.getColumns())); local.set(rc); } return rc; diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index c2ca4a16..885f35f2 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -6,7 +6,7 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.Comparator.lookupComparator; -import static org.usf.jquery.core.DBColumn.allColumns; +import static org.usf.jquery.core.JDBCType.INTEGER; import static org.usf.jquery.core.Operator.lookupOperator; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; @@ -15,6 +15,14 @@ import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.web.ArgumentParsers.parse; import static org.usf.jquery.web.ColumnDecorator.ofColumn; +import static org.usf.jquery.web.Constants.COLUMN; +import static org.usf.jquery.web.Constants.DISTINCT; +import static org.usf.jquery.web.Constants.FETCH; +import static org.usf.jquery.web.Constants.FILTER; +import static org.usf.jquery.web.Constants.OFFSET; +import static org.usf.jquery.web.Constants.ORDER; +import static org.usf.jquery.web.Constants.PARTITION; +import static org.usf.jquery.web.Constants.SELECT; import static org.usf.jquery.web.JQueryContext.context; import static org.usf.jquery.web.RequestContext.requestContext; @@ -28,15 +36,19 @@ import org.usf.jquery.core.DBObject; import org.usf.jquery.core.DBOrder; import org.usf.jquery.core.JQueryType; +import org.usf.jquery.core.JavaType; import org.usf.jquery.core.LogicalOperator; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; import org.usf.jquery.core.ParameterSet; +import org.usf.jquery.core.Partition; +import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedComparator; import org.usf.jquery.core.TypedOperator; import org.usf.jquery.core.Utils; import org.usf.jquery.core.ViewColumn; +import org.usf.jquery.core.ViewQuery; import org.usf.jquery.core.WindowFunction; import lombok.AccessLevel; @@ -64,9 +76,46 @@ public RequestEntryChain(String value) { this(value, false); } + public ViewQuery evalQuery(TableDecorator td) { + if(SELECT.equals(value)) { + var q = new RequestQueryBuilder().columns(toColumnArgs(td)); + var e = this; + while(e.next()) { //preserve last entry + e = e.next; + switch(e.value) {//column not permitted + case DISTINCT: e.requireNoArgs(); q.distinct(); break; + case FILTER: q.filters(e.toFilterArgs(td)); break; + case ORDER: q.orders(e.toOderArgs(td)); break; //not sure + case OFFSET: q.offset(e.toIntArg(td)); break; + case FETCH: q.fetch(e.toIntArg(td)); break; + default: throw badSyntaxException(e); + } + } + return q.as(e.tag); //TD require tag to register query + } + throw cannotEvaluateException(SELECT, this); + } + + public Partition evalPartition(TableDecorator td) { + if(PARTITION.equals(value)) { + var p = new Partition(toColumnArgs(td)); + if(next()) { + var e = next; + if(ORDER.equals(e.value)) {//column not permitted + p.orders(e.toOderArgs(td)); //not sure + } + else { + throw badSyntaxException(e); + } + } + return p; + } + throw cannotEvaluateException(PARTITION, this); + } + public TaggableColumn evalColumn(TableDecorator td) { var r = chainResourceOperations(td, false) - .orElseThrow(()-> cannotEvaluateException("column", this)); + .orElseThrow(()-> cannotEvaluateException(COLUMN, this)); if(r.entry.isLast()) { if(nonNull(r.entry.tag)) { //TD: required tag if operation return r.col.as(r.entry.tag); @@ -75,12 +124,12 @@ public TaggableColumn evalColumn(TableDecorator td) { ? (TaggableColumn) r.col : r.col.as(r.cd.identity()); } - throw cannotEvaluateException("operation", r.entry.next); + throw badSyntaxException(r.entry.next); } public DBOrder evalOrder(TableDecorator td) { var r = chainResourceOperations(td, false) - .orElseThrow(()-> cannotEvaluateException("order", this)); + .orElseThrow(()-> cannotEvaluateException(ORDER, this)); if(r.entry.isLast()) { // no order return r.col.order(); } @@ -89,7 +138,7 @@ public DBOrder evalOrder(TableDecorator td) { var o = Order.valueOf(e.requireNoArgs().value.toUpperCase()); // noArgs on valid order return r.col.order(o); } - throw cannotEvaluateException("order", e); + throw badSyntaxException(e); } public DBFilter evalFilter(TableDecorator td) { @@ -128,36 +177,18 @@ private void updateArgs(List values) { setArgs(values); } else { - throw new RequestSyntaxException(this + "=" + Utils.toString(values.toArray())); + throw new BadSyntaxException(this + "=" + Utils.toString(values.toArray())); } } } - public Object[] evalFunction(TableDecorator td, String fnName, ParameterSet ps) { - if(fnName.equals(value)) { - return requireNoNext().toArgs(td, null, ps, Object[]::new); - } - throw cannotEvaluateException(fnName, this); - } - - public Object[] evalArrayFunction(TableDecorator td, String fnName, JQueryType type) { - if(fnName.equals(value)) { - var c = type.typeClass(); - if(!c.isArray()) { //basic type - return toArgs(td, null, ofParameters(required(type), varargs(type)), s-> (Object[]) newInstance(c, s)); - } - throw new UnsupportedOperationException(); - } - throw cannotEvaluateException(fnName, this); - } - static RequestEntryChain defaultComparatorEntry(List values) { String cmp; if(isEmpty(values)) { cmp = "isNull"; } else { - cmp = values.size() > 1 ? "in" : "eq"; + cmp = values.size() > 1 ? "in" : "eq"; //query ?? } return new RequestEntryChain(cmp); // do not set args } @@ -216,20 +247,20 @@ private Optional chainResourceOperations(TableDecorator td, bool private static DBColumn windowColumn(TableDecorator td, TaggableColumn column) { var v = td.table(); - var vw = requestContext().getView(v); // TD use tag ? + var vw = requestContext().getView(v.id()); // TD use tag ? if(vw instanceof CompletableViewQuery) { // already create - ((CompletableViewQuery)vw).columns(column); + ((CompletableViewQuery)vw).getQuery().columns(column); } else { vw = new CompletableViewQuery((isNull(vw) ? v : vw).window(td.identity(), column)); - requestContext().setViews(vw); // same name + requestContext().putView(vw); // same name } return new ViewColumn(v, doubleQuote(column.tagname()), null, column.getType()); } private Optional lookupResource(TableDecorator td) { - if(next() && context().isDeclaredTable(value)) { //sometimes td.id == cd.id - var rc = next.lookupViewResource(context().getTable(value), RequestEntryChain::isWindowFunction); + if(next() && requestContext().isDeclaredView(value)) { //sometimes td.id == cd.id + var rc = next.lookupViewResource(requestContext().getViewDecorator(value), RequestEntryChain::isWindowFunction); if(rc.isPresent()) { requireNoArgs(); // noArgs on valid resource return rc; @@ -239,7 +270,7 @@ private Optional lookupResource(TableDecorator td) { } private Optional lookupViewResource(TableDecorator td, Predicate pre) { - return context().isDeclaredColumn(value) + return requestContext().isDeclaredColumn(value) ? Optional.of(new ResourceCursor(td, context().getColumn(requireNoArgs().value), this)) : toOperation(td, null, pre).map(op-> new ResourceCursor(td, ofColumn(value, b-> op), this)); } @@ -248,31 +279,52 @@ private Optional toOperation(TableDecorator td, DBColumn col, P return lookupOperator(value).filter(pre).map(fn-> { var c = col; if(isNull(c) && isEmpty(args) && "count".equals(value)) { // id is MAJ - c = allColumns(td.table()); + c = b-> { + b.view(td.table()); // important! register view + return "*"; + }; } return fillArgs(td, c, fn); }); } private DBFilter fillArgs(TableDecorator td, DBObject col, TypedComparator cmp) { - try { - return cmp.args(toArgs(td, col, cmp.getParameterSet(), Object[]::new)); - } - catch(Exception e) { - for(var ps : cmp.getOverloads()) { - try { - return cmp.args(toArgs(td, col, ps, Object[]::new)); - } - catch(Exception e1) { - System.out.println(e1); - } - } - throw e; - } + return cmp.args(toArgs(td, col, cmp.getParameterSet())); } private OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator opr) { - return opr.args(toArgs(td, col, opr.getParameterSet(), Object[]::new)); + return opr.args(toArgs(td, col, opr.getParameterSet())); + } + + private TaggableColumn[] toColumnArgs(TableDecorator td) { + return (TaggableColumn[]) toArgs(td, null, JQueryType.COLUMN, false); + } + + private DBOrder[] toOderArgs(TableDecorator td) { + return (DBOrder[]) toArgs(td, null, JQueryType.ORDER, false); + } + + private DBFilter[] toFilterArgs(TableDecorator td) { + return (DBFilter[]) toArgs(td, null, JQueryType.FILTER, false); + } + + private Object[] toArgs(TableDecorator td, DBObject col, JavaType type, boolean allowEmpty) { + var c = type.typeClass(); + if(c.isArray()) { + throw new UnsupportedOperationException("arrayOf " + c); + } + var ps = allowEmpty + ? ofParameters(varargs(type)) + : ofParameters(required(type), varargs(type)); + return toArgs(td, col, ps, s-> (Object[]) newInstance(c, s)); + } + + private int toIntArg(TableDecorator td) { + return (Integer) toArgs(td, null, ofParameters(required(INTEGER)))[0]; + } + + private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps) { + return toArgs(td, col, ps, Object[]::new); } private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps, IntFunction arrFn) { @@ -342,6 +394,9 @@ private static String[] toStringArray(List entries) { private static EvalException cannotEvaluateException(String type, RequestEntryChain entry) { return EvalException.cannotEvaluateException(type, entry.toString()); } + public static BadSyntaxException badSyntaxException(RequestEntryChain entry) { + return BadSyntaxException.badSyntaxException(entry.toString()); + } @RequiredArgsConstructor static final class ResourceCursor { diff --git a/src/main/java/org/usf/jquery/web/RequestSyntaxException.java b/src/main/java/org/usf/jquery/web/RequestSyntaxException.java deleted file mode 100644 index 416ec1e2..00000000 --- a/src/main/java/org/usf/jquery/web/RequestSyntaxException.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.usf.jquery.web; - -/** - * - * @author u$f - * - */ -@SuppressWarnings("serial") -public class RequestSyntaxException extends WebException { - - public RequestSyntaxException(String message) { - super(message); - } - -} diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index cbe414b4..5c88bc75 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -76,6 +76,7 @@ default RequestQueryBuilder query(Map parameterMap) { clearContext(); currentDatabase(database().getType()); //table database var query = new RequestQueryBuilder(); + parseViews(query, parameterMap); parseColumns(query, parameterMap); parseFilters(query, parameterMap); parseOrders (query, parameterMap); @@ -83,6 +84,14 @@ default RequestQueryBuilder query(Map parameterMap) { return query.views(requestContext().views()); } + default void parseViews(RequestQueryBuilder query, Map parameters) { +// if(parameters.containsKey(VIEW)) { +// Stream.of(parameters.get(VIEW)) +// .flatMap(c-> parseEntries(c).stream()) +// .forEach(e-> requestContext().putViewDecorator(e.evalView(this))); //declare only +// } + } + default void parseColumns(RequestQueryBuilder query, Map parameters) { if(parameters.containsKey(COLUMN_DISTINCT) && parameters.containsKey(COLUMN)) { throw new IllegalArgumentException("cannot use both parameters " + quote(COLUMN_DISTINCT) + " and " + quote(COLUMN)); @@ -151,4 +160,5 @@ default TableMetadata createMetadata(Collection columns) { static Stream flatParameters(String... arr) { //number local separator return Stream.of(arr).flatMap(v-> Stream.of(v.split(","))); } + } From 7ab15771b336e1ace5734d46f9053c1c9cde17f3 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 13 Feb 2024 23:12:55 +0100 Subject: [PATCH 074/298] edit --- .../org/usf/jquery/core/ParameterSet.java | 2 +- .../org/usf/jquery/web/ArgumentParsers.java | 35 +++++++++++++------ .../usf/jquery/web/JDBCArgumentParser.java | 17 +++------ .../usf/jquery/web/JavaArgumentParser.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 8 ++--- 5 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index b605e1dc..2fca1fd3 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -65,7 +65,7 @@ public static ParameterSet ofParameters(Parameter... parameters) { throw new IllegalArgumentException("varargs should be the last parameter"); } } - var nReqArgs = i-1; + var nReqArgs = i; for(; i Date: Tue, 13 Feb 2024 23:20:11 +0100 Subject: [PATCH 075/298] edit --- src/main/java/org/usf/jquery/web/ParseException.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ParseException.java b/src/main/java/org/usf/jquery/web/ParseException.java index a761955f..79de52b8 100644 --- a/src/main/java/org/usf/jquery/web/ParseException.java +++ b/src/main/java/org/usf/jquery/web/ParseException.java @@ -18,10 +18,6 @@ public ParseException(String message, Throwable cause) { super(message, cause); } - static ParseException cannotParseException(String type, String value) { - return cannotParseException(type, value, null); - } - static ParseException cannotParseException(String type, String value, Throwable cause) { return new ParseException("cannot parse " + type + " " + quote(value), cause); } From 1388401c0a5f40b000c27048291232f0ff1a52f5 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 13 Feb 2024 23:59:50 +0100 Subject: [PATCH 076/298] edit --- .../java/org/usf/jquery/core/Parameter.java | 9 ++++--- .../org/usf/jquery/core/ParameterSet.java | 25 ++++++++++++++++++- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 8226a76f..54502bac 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -38,9 +38,12 @@ public JavaType[] types(Object[] args) { @Override public String toString() { - return isNull(typeRef) - ? Stream.of(types).map(Object::toString).collect(joining("|")) - : typeRef.toString(); + if(isNull(typeRef)) { + return isEmpty(types) + ? "ANY" + : Stream.of(types).map(Object::toString).collect(joining("|")); + } + return typeRef.toString(); } public static Parameter required(JavaType... types) { diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index 2fca1fd3..ad720394 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -2,10 +2,12 @@ import static java.lang.Math.min; import static java.util.Objects.isNull; +import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.BadArgumentException.badArgumentTypeException; import java.util.function.ObjIntConsumer; +import java.util.stream.Stream; import lombok.AccessLevel; import lombok.Getter; @@ -74,6 +76,27 @@ public static ParameterSet ofParameters(Parameter... parameters) { if(i 0) { + s = Stream.of(parameters).limit(nReqArgs).map(Parameter::toString).collect(joining(", ")); + if(parameters.length > nReqArgs) { + if(nReqArgs > 0) { + s += ", "; + } + s += "[" + Stream.of(parameters).skip(nReqArgs).map(Parameter::toString).collect(joining(", ")); + if(parameters[parameters.length-1].isVarargs()) { + s += "..."; + } + s += "]"; + } + } + return "(" + s + ")"; } } From 77330443aa0fd669d9744dd6d68e6e3ce2ef1949 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 14 Feb 2024 01:00:28 +0100 Subject: [PATCH 077/298] edit --- .../usf/jquery/core/BadArgumentException.java | 1 - .../org/usf/jquery/core/MappingException.java | 2 +- .../org/usf/jquery/core/ParameterSet.java | 4 +--- .../usf/jquery/web/BadSyntaxException.java | 18 ----------------- .../web/IllegalDataAccessException.java | 2 +- .../jquery/web/MissingParameterException.java | 2 +- .../jquery/web/NoSuchResourceException.java | 4 +--- .../org/usf/jquery/web/RequestEntryChain.java | 20 +++++++++---------- .../jquery/web/UnexpectedEntryException.java | 18 +++++++++++++++++ 9 files changed, 33 insertions(+), 38 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/web/BadSyntaxException.java create mode 100644 src/main/java/org/usf/jquery/web/UnexpectedEntryException.java diff --git a/src/main/java/org/usf/jquery/core/BadArgumentException.java b/src/main/java/org/usf/jquery/core/BadArgumentException.java index 81dfdab2..dc338272 100644 --- a/src/main/java/org/usf/jquery/core/BadArgumentException.java +++ b/src/main/java/org/usf/jquery/core/BadArgumentException.java @@ -24,5 +24,4 @@ public static BadArgumentException badArgumentTypeException(JavaType[] types, Ob public static BadArgumentException badArgumentCountException(int count, int actual) { return new BadArgumentException("bad argument count : " + count + " # " + actual); } - } diff --git a/src/main/java/org/usf/jquery/core/MappingException.java b/src/main/java/org/usf/jquery/core/MappingException.java index de5bbec9..fe9cadc1 100644 --- a/src/main/java/org/usf/jquery/core/MappingException.java +++ b/src/main/java/org/usf/jquery/core/MappingException.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; @SuppressWarnings("serial") -public final class MappingException extends RuntimeException { +public final class MappingException extends JQueryException { public MappingException(Throwable cause) { super(cause); diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index ad720394..74992d6f 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -76,9 +76,7 @@ public static ParameterSet ofParameters(Parameter... parameters) { if(i values) { setArgs(values); } else { - throw new BadSyntaxException(this + "=" + Utils.toString(values.toArray())); + throw new UnexpectedEntryException(this + "=" + Utils.toString(values.toArray())); } } } @@ -220,7 +220,7 @@ DBFilter chainComparator(TableDecorator td, DBFilter f){ } } else { - throw cannotEvaluateException("logical operator", e); + throw unexpectedEntryException(e); } e = e.next; } @@ -348,14 +348,14 @@ RequestEntryChain requireNoArgs() { if(isNull(args)) { return this; } - throw new IllegalArgumentException(value + " takes no args"); + throw new UnexpectedEntryException(value + " takes no args : " + this); } RequestEntryChain requireNoNext(){ if(isLast()) { return this; } - throw new IllegalArgumentException(value + " must be the last entry"); + throw new UnexpectedEntryException(value + " must be the last entry : " + this); } public boolean isLast() { @@ -394,8 +394,8 @@ private static String[] toStringArray(List entries) { private static EvalException cannotEvaluateException(String type, RequestEntryChain entry) { return EvalException.cannotEvaluateException(type, entry.toString()); } - public static BadSyntaxException badSyntaxException(RequestEntryChain entry) { - return BadSyntaxException.badSyntaxException(entry.toString()); + public static UnexpectedEntryException unexpectedEntryException(RequestEntryChain entry) { + return UnexpectedEntryException.unexpectedEntryException(entry.toString()); } @RequiredArgsConstructor diff --git a/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java b/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java new file mode 100644 index 00000000..b710d83c --- /dev/null +++ b/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java @@ -0,0 +1,18 @@ +package org.usf.jquery.web; + +/** + * + * @author u$f + * + */ +@SuppressWarnings("serial") +public class UnexpectedEntryException extends WebException { + + public UnexpectedEntryException(String message) { + super(message); + } + + public static UnexpectedEntryException unexpectedEntryException(String o) { + return new UnexpectedEntryException("unexpected entry : " + o); + } +} From 550ed4281d7920700da922a1f7f22ce036d83ab0 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 14 Feb 2024 01:51:08 +0100 Subject: [PATCH 078/298] edit --- .../org/usf/jquery/core/RequestQuery.java | 16 +++--- .../java/org/usf/jquery/core/ViewQuery.java | 3 +- .../org/usf/jquery/web/ArgumentParsers.java | 53 ++++++++++--------- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/RequestQuery.java b/src/main/java/org/usf/jquery/core/RequestQuery.java index 2764f90d..c4d03983 100644 --- a/src/main/java/org/usf/jquery/core/RequestQuery.java +++ b/src/main/java/org/usf/jquery/core/RequestQuery.java @@ -30,14 +30,15 @@ public final class RequestQuery { private final Object[] args; private final int[] argTypes; - public List execute(DataSource ds) { + public List execute(DataSource ds) throws SQLException { return execute(ds, new KeyValueMapper()); } - public T execute(DataSource ds, ResultSetMapper mapper) { // overload with sql types + public T execute(DataSource ds, ResultSetMapper mapper) throws SQLException { // overload with sql types try(var cn = ds.getConnection()){ log.debug("preparing statement : {}", query); try(var ps = cn.prepareStatement(query)){ + log.debug("with parameters : {}", Arrays.toString(args)); if(!isEmpty(args)) { for(var i=0; i T execute(DataSource ds, ResultSetMapper mapper) { // overload wit } } } - log.debug("with parameters : {}", Arrays.toString(args)); log.debug("executing SQL query..."); var bg = currentTimeMillis(); try(var rs = ps.executeQuery()){ log.debug("query executed in {} ms", currentTimeMillis() - bg); - return mapper.map(rs); + try { + return mapper.map(rs); + } + catch(SQLException e) { // re-throw SQLException + throw new MappingException("error while mapping results", e); + } } } } - catch(SQLException e) { // re-throw SQLException - throw new MappingException("error while mapping results", e); - } } } diff --git a/src/main/java/org/usf/jquery/core/ViewQuery.java b/src/main/java/org/usf/jquery/core/ViewQuery.java index 25bca072..94a4a128 100644 --- a/src/main/java/org/usf/jquery/core/ViewQuery.java +++ b/src/main/java/org/usf/jquery/core/ViewQuery.java @@ -42,7 +42,8 @@ public JavaType getType() { if(query.getColumns().size() == 1) { return query.getColumns().get(0).getType(); } - throw new IllegalStateException(query.getColumns().isEmpty() ? "no columns" : "too many columns"); + var msg = query.getColumns().isEmpty() ? "no columns" : "too many columns"; + throw new UnsupportedOperationException(msg + " : " + this); } @Override diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 0c9cd8e8..d2f7c10c 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -20,14 +20,13 @@ import java.time.LocalDate; import java.time.LocalTime; import java.time.ZonedDateTime; +import java.util.Arrays; import java.util.stream.Stream; -import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; import org.usf.jquery.core.JavaType.Typed; -import org.usf.jquery.core.ViewQuery; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -45,35 +44,41 @@ public class ArgumentParsers { jdbcArgParser(BIGINT), jdbcArgParser(DOUBLE), jdbcArgParser(DATE), jdbcArgParser(TIMESTAMP), jdbcArgParser(TIME), jdbcArgParser(TIMESTAMP_WITH_TIMEZONE)}; - + public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType... types) { - if(isEmpty(types) || Stream.of(types).allMatch(JDBCType.class::isInstance)) { - try { - return matchTypes((DBColumn) jqueryArgParser(COLUMN).parseEntry(entry, td), types); //only with JDBC types - } catch (EvalException e) {/*do not throw exception*/} + if(isEmpty(types) || Stream.of(types).allMatch(o-> o.getClass() == JDBCType.class)) { + return parseJdbc(entry, td, Stream.of(types).map(JDBCType.class::cast).toArray(null)); + } + if(Stream.of(types).allMatch(o-> o.getClass() == JQueryType.class)) { + return parseJQuery(entry, td, Stream.of(types).map(JQueryType.class::cast).toArray(null)); + } + throw new UnsupportedOperationException("unsupported types " + Arrays.toString(types)); + } + + public static Object parseJdbc(RequestEntryChain entry, TableDecorator td, JDBCType... types) { + try { + return matchTypes((Typed) parseJQuery(entry, td, COLUMN, QUERY), types); //try parse column | query first + } catch (EvalException e) {/*do not throw exception*/} + if(isEmpty(types)) { + return jdbcArgParser(null).parseEntry(entry, td); + } + for(var type : types) { try { - return matchTypes((ViewQuery) jqueryArgParser(QUERY).parseEntry(entry, td), types); //only with JDBC types - } catch (EvalException e) {/*do not throw exception*/} - if(isEmpty(types)) { - return jdbcArgParser(null).parseEntry(entry, td); - } + return jdbcArgParser(type).parseEntry(entry, td); + } catch (ParseException e) {/*do not throw exception*/} // only parseException } + throw badArgumentTypeException(types, entry.toString()); + } + + public static Object parseJQuery(RequestEntryChain entry, TableDecorator td, JQueryType... types) { for(var type : types) { try { - if(type instanceof JQueryType) { - return jqueryArgParser((JQueryType) type).parseEntry(entry, td); - } - else if(type instanceof JDBCType) { - return jdbcArgParser((JDBCType) type).parseEntry(entry, td); - } - else { - throw unsupportedTypeException(type); - } - } catch (ParseException | EvalException e) {/*do not throw exception*/} // only parseException + return jqueryArgParser(type).parseEntry(entry, td); + } catch (EvalException e) {/*do not throw exception*/} // only parseException } throw badArgumentTypeException(types, entry.toString()); } - + public static JDBCArgumentParser jdbcArgParser(JDBCType type) { if(isNull(type)) { return ArgumentParsers::parseUnknown; @@ -123,7 +128,7 @@ private static Object parseUnknown(String s) { return s; } - private static Object matchTypes(Typed o, JavaType... types) { // only jdbcType + private static Object matchTypes(Typed o, JDBCType... types) { if(isNull(o.getType()) || isEmpty(types)) { return true; } From 2228fed89b784824a5efed5894fbbc7217172504 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 14 Feb 2024 13:18:31 +0100 Subject: [PATCH 079/298] edit --- .../java/org/usf/jquery/core/Parameter.java | 24 +++++++++---------- .../org/usf/jquery/web/ArgumentParsers.java | 16 +++++++++---- .../org/usf/jquery/web/RequestEntryChain.java | 22 ++++------------- 3 files changed, 27 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 54502bac..20a49c26 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Utils.isEmpty; @@ -25,25 +25,25 @@ public final class Parameter { private final boolean varargs; public boolean accept(int idx, Object[] args) { - return isNull(typeRef) - ? isEmpty(types) || Stream.of(types).anyMatch(t-> t.accept(args[idx])) - : typeRef.apply(args).accept(args[idx]); + return nonNull(typeRef) + ? typeRef.apply(args).accept(args[idx]) + : isEmpty(types) || Stream.of(types).anyMatch(t-> t.accept(args[idx])); } public JavaType[] types(Object[] args) { - return isNull(typeRef) - ? types - : new JavaType[] {typeRef.apply(args)}; + return nonNull(typeRef) + ? new JavaType[] {typeRef.apply(args)} + : types; } @Override public String toString() { - if(isNull(typeRef)) { - return isEmpty(types) - ? "ANY" - : Stream.of(types).map(Object::toString).collect(joining("|")); + if(nonNull(typeRef)) { + return typeRef.toString(); } - return typeRef.toString(); + return isEmpty(types) + ? "ANY" + : Stream.of(types).map(Object::toString).collect(joining("|")); } public static Parameter required(JavaType... types) { diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index d2f7c10c..90aa1b56 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -21,8 +21,10 @@ import java.time.LocalTime; import java.time.ZonedDateTime; import java.util.Arrays; +import java.util.function.IntFunction; import java.util.stream.Stream; +import org.usf.jquery.core.BadArgumentException; import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; @@ -47,10 +49,10 @@ public class ArgumentParsers { public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType... types) { if(isEmpty(types) || Stream.of(types).allMatch(o-> o.getClass() == JDBCType.class)) { - return parseJdbc(entry, td, Stream.of(types).map(JDBCType.class::cast).toArray(null)); + return parseJdbc(entry, td, cast(types, JDBCType[].class, JDBCType[]::new)); } if(Stream.of(types).allMatch(o-> o.getClass() == JQueryType.class)) { - return parseJQuery(entry, td, Stream.of(types).map(JQueryType.class::cast).toArray(null)); + return parseJQuery(entry, td, cast(types, JQueryType[].class, JQueryType[]::new)); } throw new UnsupportedOperationException("unsupported types " + Arrays.toString(types)); } @@ -58,14 +60,14 @@ public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType. public static Object parseJdbc(RequestEntryChain entry, TableDecorator td, JDBCType... types) { try { return matchTypes((Typed) parseJQuery(entry, td, COLUMN, QUERY), types); //try parse column | query first - } catch (EvalException e) {/*do not throw exception*/} + } catch (BadArgumentException e) {/*do not throw exception*/} if(isEmpty(types)) { return jdbcArgParser(null).parseEntry(entry, td); } for(var type : types) { try { return jdbcArgParser(type).parseEntry(entry, td); - } catch (ParseException e) {/*do not throw exception*/} // only parseException + } catch (ParseException e) {/*do not throw exception*/} } throw badArgumentTypeException(types, entry.toString()); } @@ -74,7 +76,7 @@ public static Object parseJQuery(RequestEntryChain entry, TableDecorator td, JQu for(var type : types) { try { return jqueryArgParser(type).parseEntry(entry, td); - } catch (EvalException e) {/*do not throw exception*/} // only parseException + } catch (EvalException e) {/*do not throw exception*/} } throw badArgumentTypeException(types, entry.toString()); } @@ -140,6 +142,10 @@ private static Object matchTypes(Typed o, JDBCType... types) { throw badArgumentTypeException(types, o); } + private static T[] cast(JavaType[] types, Class c, IntFunction fn) { + return types.getClass() == c ? c.cast(types) : Stream.of(types).toArray(fn); + } + private static UnsupportedOperationException unsupportedTypeException(JavaType type) { return new UnsupportedOperationException("unsupported type " + type.toString()); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index a13d7a17..7df76e08 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -4,7 +4,6 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.Comparator.lookupComparator; import static org.usf.jquery.core.JDBCType.INTEGER; import static org.usf.jquery.core.Operator.lookupOperator; @@ -44,7 +43,6 @@ import org.usf.jquery.core.Partition; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; -import org.usf.jquery.core.TypedComparator; import org.usf.jquery.core.TypedOperator; import org.usf.jquery.core.Utils; import org.usf.jquery.core.ViewColumn; @@ -194,7 +192,7 @@ static RequestEntryChain defaultComparatorEntry(List values) } DBFilter chainComparator(TableDecorator td, ColumnDecorator cd, DBColumn col){ - var f = lookupComparator(value).map(c-> fillArgs(td, col, c)).orElse(null); //eval comparator first => avoid overriding + var f = lookupComparator(value).map(c-> c.args(toArgs(td, col, c.getParameterSet()))).orElse(null); //eval comparator first => avoid overriding if(isNull(f) && col instanceof TaggableColumn) { //no operation var c = cd.criteria(value); //criteria lookup if(nonNull(c)) { @@ -212,12 +210,8 @@ DBFilter chainComparator(TableDecorator td, DBFilter f){ while(nonNull(e)) { if(e.value.matches("and|or")) { var op = LogicalOperator.valueOf(e.value.toUpperCase()); - if(!isEmpty(e.args) && e.args.size() == 1) { - f = f.append(op, e.args.get(0).evalFilter(td)); - } - else { - throw badArgumentCountException(1, isEmpty(e.args) ? 0 : e.args.size()); - } + f = f.append(op, (DBFilter) e.toArgs(td, null, + ofParameters(required(JQueryType.FILTER)), DBFilter[]::new)[0]); } else { throw unexpectedEntryException(e); @@ -284,18 +278,10 @@ private Optional toOperation(TableDecorator td, DBColumn col, P return "*"; }; } - return fillArgs(td, c, fn); + return fn.args(toArgs(td, c, fn.getParameterSet())); }); } - private DBFilter fillArgs(TableDecorator td, DBObject col, TypedComparator cmp) { - return cmp.args(toArgs(td, col, cmp.getParameterSet())); - } - - private OperationColumn fillArgs(TableDecorator td, DBColumn col, TypedOperator opr) { - return opr.args(toArgs(td, col, opr.getParameterSet())); - } - private TaggableColumn[] toColumnArgs(TableDecorator td, boolean allowEmpty) { return (TaggableColumn[]) toArgs(td, null, JQueryType.COLUMN, allowEmpty); } From 337d970d2164662e60d7fa4ad402ed5ccd694ead Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 15 Feb 2024 00:51:55 +0100 Subject: [PATCH 080/298] edit --- .../usf/jquery/core/BadArgumentException.java | 11 ++-- .../org/usf/jquery/web/ArgumentParsers.java | 41 +++++++-------- .../org/usf/jquery/web/ParseException.java | 6 ++- .../org/usf/jquery/web/RequestEntryChain.java | 50 +++++++++++-------- .../jquery/web/UnexpectedEntryException.java | 9 +++- 5 files changed, 65 insertions(+), 52 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/BadArgumentException.java b/src/main/java/org/usf/jquery/core/BadArgumentException.java index dc338272..3378578d 100644 --- a/src/main/java/org/usf/jquery/core/BadArgumentException.java +++ b/src/main/java/org/usf/jquery/core/BadArgumentException.java @@ -17,11 +17,14 @@ public BadArgumentException(String message) { } public static BadArgumentException badArgumentTypeException(JavaType[] types, Object actual) { - return new BadArgumentException("bad argument type : " + - Stream.of(types).map(Object::toString).collect(joining("|")) + " # " + actual); + return new BadArgumentException("bad argument type, "+ + "expected: " + Stream.of(types).map(Object::toString).collect(joining("|")) + + " but was: " + actual); } - + public static BadArgumentException badArgumentCountException(int count, int actual) { - return new BadArgumentException("bad argument count : " + count + " # " + actual); + return new BadArgumentException("bad argument count, "+ + "expected: " + count + + " but was: " + actual); } } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 90aa1b56..793c7082 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -11,6 +11,7 @@ import static org.usf.jquery.core.JQueryType.COLUMN; import static org.usf.jquery.core.JQueryType.QUERY; import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.web.ParseException.cannotParseException; import java.math.BigDecimal; import java.sql.Date; @@ -24,7 +25,6 @@ import java.util.function.IntFunction; import java.util.stream.Stream; -import org.usf.jquery.core.BadArgumentException; import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; @@ -33,19 +33,20 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; /** * * @author u$f * */ +@Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ArgumentParsers { - private static final JDBCArgumentParser[] STD_PRS = { - jdbcArgParser(BIGINT), jdbcArgParser(DOUBLE), - jdbcArgParser(DATE), jdbcArgParser(TIMESTAMP), - jdbcArgParser(TIME), jdbcArgParser(TIMESTAMP_WITH_TIMEZONE)}; + private static final JDBCType[] STD_TYPES = { + BIGINT, DOUBLE, DATE, TIMESTAMP, + TIME, TIMESTAMP_WITH_TIMEZONE }; public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType... types) { if(isEmpty(types) || Stream.of(types).allMatch(o-> o.getClass() == JDBCType.class)) { @@ -60,31 +61,32 @@ public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType. public static Object parseJdbc(RequestEntryChain entry, TableDecorator td, JDBCType... types) { try { return matchTypes((Typed) parseJQuery(entry, td, COLUMN, QUERY), types); //try parse column | query first - } catch (BadArgumentException e) {/*do not throw exception*/} + } catch (ParseException e) {/*do not throw exception*/} if(isEmpty(types)) { - return jdbcArgParser(null).parseEntry(entry, td); + types = STD_TYPES; } for(var type : types) { try { return jdbcArgParser(type).parseEntry(entry, td); - } catch (ParseException e) {/*do not throw exception*/} + } catch (ParseException e) { /*do not throw exception*/ + log.trace("parse {} : '{}' => {}", type, entry, e.getMessage()); + } } - throw badArgumentTypeException(types, entry.toString()); + throw cannotParseException(entry.toString(), Arrays.toString(types)); } public static Object parseJQuery(RequestEntryChain entry, TableDecorator td, JQueryType... types) { for(var type : types) { try { return jqueryArgParser(type).parseEntry(entry, td); - } catch (EvalException e) {/*do not throw exception*/} + } catch (ParseException e) {/*do not throw exception*/ + log.trace("parse {} : '{}' => {}", type, entry, e.getMessage()); + } } - throw badArgumentTypeException(types, entry.toString()); + throw cannotParseException(entry.toString(), Arrays.toString(types)); } - public static JDBCArgumentParser jdbcArgParser(JDBCType type) { - if(isNull(type)) { - return ArgumentParsers::parseUnknown; - } + public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { switch (type) { case BOOLEAN: return Boolean::parseBoolean; case BIT: return Boolean::parseBoolean; @@ -121,15 +123,6 @@ public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type) { } } - private static Object parseUnknown(String s) { - for(var p : STD_PRS) { - try { - return p.parseValue(s); - } catch(ParseException e) {/*do not throw exception*/} - } - return s; - } - private static Object matchTypes(Typed o, JDBCType... types) { if(isNull(o.getType()) || isEmpty(types)) { return true; diff --git a/src/main/java/org/usf/jquery/web/ParseException.java b/src/main/java/org/usf/jquery/web/ParseException.java index 79de52b8..c23c6599 100644 --- a/src/main/java/org/usf/jquery/web/ParseException.java +++ b/src/main/java/org/usf/jquery/web/ParseException.java @@ -17,8 +17,12 @@ public ParseException(String message) { public ParseException(String message, Throwable cause) { super(message, cause); } + + static ParseException cannotParseException(String type, String value) { + return cannotParseException(type, value, null); + } static ParseException cannotParseException(String type, String value, Throwable cause) { - return new ParseException("cannot parse " + type + " " + quote(value), cause); + return new ParseException("cannot parse " + quote(value) + " as " + quote(type), cause); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 7df76e08..ce8e9165 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -4,6 +4,7 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.BadArgumentException.badArgumentTypeException; import static org.usf.jquery.core.Comparator.lookupComparator; import static org.usf.jquery.core.JDBCType.INTEGER; import static org.usf.jquery.core.Operator.lookupOperator; @@ -86,12 +87,12 @@ public ViewQuery evalQuery(TableDecorator td) { case ORDER: q.orders(e.toOderArgs(td)); break; //not sure case OFFSET: q.offset(e.toIntArg(td)); break; case FETCH: q.fetch(e.toIntArg(td)); break; - default: throw unexpectedEntryException(e); + default: throw unexpectedEntryException(e, DISTINCT, FILTER, ORDER, OFFSET, FETCH); } } return q.as(e.tag); //TD require tag to register query } - throw cannotEvaluateException(SELECT, this); + throw cannotParseException(SELECT, this); } public Partition evalPartition(TableDecorator td) { @@ -103,17 +104,17 @@ public Partition evalPartition(TableDecorator td) { p.orders(e.toOderArgs(td)); //not sure } else { - throw unexpectedEntryException(e); + throw unexpectedEntryException(e, ORDER); } } return p; } - throw cannotEvaluateException(PARTITION, this); + throw cannotParseException(PARTITION, this); } public TaggableColumn evalColumn(TableDecorator td) { var r = chainResourceOperations(td, false) - .orElseThrow(()-> cannotEvaluateException(COLUMN, this)); + .orElseThrow(()-> cannotParseException(COLUMN, this)); if(r.entry.isLast()) { if(nonNull(r.entry.tag)) { //TD: required tag if operation return r.col.as(r.entry.tag); @@ -127,7 +128,7 @@ public TaggableColumn evalColumn(TableDecorator td) { public DBOrder evalOrder(TableDecorator td) { var r = chainResourceOperations(td, false) - .orElseThrow(()-> cannotEvaluateException(ORDER, this)); + .orElseThrow(()-> cannotParseException(ORDER, this)); if(r.entry.isLast()) { // no order return r.col.order(); } @@ -136,7 +137,7 @@ public DBOrder evalOrder(TableDecorator td) { var o = Order.valueOf(e.requireNoArgs().value.toUpperCase()); // noArgs on valid order return r.col.order(o); } - throw unexpectedEntryException(e); + throw unexpectedEntryException(e, "asc|desc"); } public DBFilter evalFilter(TableDecorator td) { @@ -202,7 +203,7 @@ DBFilter chainComparator(TableDecorator td, ColumnDecorator cd, DBColumn col){ if(nonNull(f)) { return chainComparator(td, f); } - throw cannotEvaluateException("comparison|criteria", this); + throw cannotParseException("comparison|criteria", this); } DBFilter chainComparator(TableDecorator td, DBFilter f){ @@ -214,7 +215,7 @@ DBFilter chainComparator(TableDecorator td, DBFilter f){ ofParameters(required(JQueryType.FILTER)), DBFilter[]::new)[0]); } else { - throw unexpectedEntryException(e); + throw unexpectedEntryException(e, "and|or"); } e = e.next; } @@ -286,18 +287,18 @@ private TaggableColumn[] toColumnArgs(TableDecorator td, boolean allowEmpty) { return (TaggableColumn[]) toArgs(td, null, JQueryType.COLUMN, allowEmpty); } - private DBOrder[] toOderArgs(TableDecorator td) { - return (DBOrder[]) toArgs(td, null, JQueryType.ORDER, false); - } - private DBFilter[] toFilterArgs(TableDecorator td) { return (DBFilter[]) toArgs(td, null, JQueryType.FILTER, false); } + + private DBOrder[] toOderArgs(TableDecorator td) { + return (DBOrder[]) toArgs(td, null, JQueryType.ORDER, false); + } private Object[] toArgs(TableDecorator td, DBObject col, JavaType type, boolean allowEmpty) { var c = type.typeClass(); if(c.isArray()) { - throw new UnsupportedOperationException("arrayOf " + c); + throw new UnsupportedOperationException("cannot create array of " + c); } var ps = allowEmpty ? ofParameters(varargs(type)) @@ -322,9 +323,16 @@ private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps, IntFun ps.forEach(arr.length, (p,i)-> { if(i>=inc) { //arg0 already parsed var e = args.get(i-inc); - arr[i] = isNull(e.value) || e.text - ? e.requireNoArgs().value - : parse(e, td, p.types(arr)); + if(isNull(e.value) || e.text) { + arr[i] = e.requireNoArgs().value; + } + else { + try { + arr[i] = parse(e, td, p.types(arr)); + } catch (ParseException ex) { + throw badArgumentTypeException(p.getTypes(), e); + } + } } }); return arr; @@ -377,11 +385,11 @@ private static String[] toStringArray(List entries) { .toArray(String[]::new); } - private static EvalException cannotEvaluateException(String type, RequestEntryChain entry) { - return EvalException.cannotEvaluateException(type, entry.toString()); + private static ParseException cannotParseException(String type, RequestEntryChain entry) { + return ParseException.cannotParseException(type, entry.toString()); } - public static UnexpectedEntryException unexpectedEntryException(RequestEntryChain entry) { - return UnexpectedEntryException.unexpectedEntryException(entry.toString()); + public static UnexpectedEntryException unexpectedEntryException(RequestEntryChain entry, String... expected) { + return UnexpectedEntryException.unexpectedEntryException(entry.toString(), expected); } @RequiredArgsConstructor diff --git a/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java b/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java index b710d83c..5f2df799 100644 --- a/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java +++ b/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java @@ -1,5 +1,9 @@ package org.usf.jquery.web; +import static java.lang.String.join; +import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.core.Utils.isEmpty; + /** * * @author u$f @@ -12,7 +16,8 @@ public UnexpectedEntryException(String message) { super(message); } - public static UnexpectedEntryException unexpectedEntryException(String o) { - return new UnexpectedEntryException("unexpected entry : " + o); + public static UnexpectedEntryException unexpectedEntryException(String entry, String... expected) { + return new UnexpectedEntryException("unexpected entry : " + quote(entry) + + (isEmpty(expected) ? "" :" expect : " + join("|", expected))); } } From 4138071817c5a3d8709ff5b626f5c87cf1242188 Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 18 Feb 2024 15:52:11 +0100 Subject: [PATCH 081/298] eidt --- .../usf/jquery/core/BadArgumentException.java | 10 +- .../usf/jquery/core/RequestQueryBuilder.java | 9 +- .../java/org/usf/jquery/core/ViewQuery.java | 8 +- .../org/usf/jquery/web/ArgumentParsers.java | 13 +- .../usf/jquery/web/CompletableViewQuery.java | 9 + .../org/usf/jquery/web/ParseException.java | 4 +- .../org/usf/jquery/web/RequestContext.java | 41 ++--- .../org/usf/jquery/web/RequestEntryChain.java | 165 ++++++++++-------- .../jquery/web/UnexpectedEntryException.java | 6 +- 9 files changed, 142 insertions(+), 123 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/BadArgumentException.java b/src/main/java/org/usf/jquery/core/BadArgumentException.java index 3378578d..6f16c1da 100644 --- a/src/main/java/org/usf/jquery/core/BadArgumentException.java +++ b/src/main/java/org/usf/jquery/core/BadArgumentException.java @@ -16,10 +16,18 @@ public BadArgumentException(String message) { super(message); } + public BadArgumentException(String message, Throwable cause) { + super(message, cause); + } + public static BadArgumentException badArgumentTypeException(JavaType[] types, Object actual) { + return badArgumentTypeException(types, actual, null); + } + + public static BadArgumentException badArgumentTypeException(JavaType[] types, Object actual, Exception e) { return new BadArgumentException("bad argument type, "+ "expected: " + Stream.of(types).map(Object::toString).collect(joining("|")) + - " but was: " + actual); + " but was: " + actual, e); } public static BadArgumentException badArgumentCountException(int count, int actual) { diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 27cc22b2..a76ea4a1 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -114,15 +114,15 @@ public RequestQuery build(String schema) { } public final void build(SqlStringBuilder sb, QueryParameterBuilder pb){ - select(sb, pb); //declare all view before FROM where(sb, pb); groupBy(sb); having(sb, pb); orderBy(sb, pb); fetch(sb); + sb.sb.insert(0, select(sb, pb)); //declare all view before FROM) } - void select(SqlStringBuilder sb, QueryParameterBuilder pb){ + String select(SqlStringBuilder sb, QueryParameterBuilder pb){ if(currentDatabase() == TERADATA) { if(nonNull(offset)) { throw new UnsupportedOperationException(""); @@ -131,13 +131,14 @@ void select(SqlStringBuilder sb, QueryParameterBuilder pb){ throw new UnsupportedOperationException("Top N option is not supported with DISTINCT option."); } } - sb.append("SELECT") + return new SqlStringBuilder(100).append("SELECT") .appendIf(distinct, ()-> " DISTINCT") .appendIf(nonNull(fetch), ()-> " TOP " + fetch) .append(SPACE) .appendEach(columns, SCOMA, o-> o.sqlWithTag(pb)) .appendIf(!pb.views().isEmpty(), " FROM ") //TODO finish this - .appendEach(pb.views(), SCOMA, o-> o.sqlWithTag(pb)); + .appendEach(pb.views(), SCOMA, o-> o.sqlWithTag(pb)) + .toString(); } void where(SqlStringBuilder sb, QueryParameterBuilder pb){ diff --git a/src/main/java/org/usf/jquery/core/ViewQuery.java b/src/main/java/org/usf/jquery/core/ViewQuery.java index 94a4a128..729ee670 100644 --- a/src/main/java/org/usf/jquery/core/ViewQuery.java +++ b/src/main/java/org/usf/jquery/core/ViewQuery.java @@ -27,9 +27,9 @@ public ViewQuery(@NonNull String id, @NonNull TaggableColumn... columns) { @Override public String sql(QueryParameterBuilder builder) { - var s = new SqlStringBuilder(100).append("("); - query.build(s, builder.subQuery()); - return s.append(")").toString(); + var s = new SqlStringBuilder(100); + query.build(s, builder.subQuery()); //important! build query first + return "(" + s.toString() + ")"; } @Override @@ -43,7 +43,7 @@ public JavaType getType() { return query.getColumns().get(0).getType(); } var msg = query.getColumns().isEmpty() ? "no columns" : "too many columns"; - throw new UnsupportedOperationException(msg + " : " + this); + throw new UnsupportedOperationException("typeof " + msg + " : " + this); } @Override diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 793c7082..0f4d0364 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -11,7 +11,6 @@ import static org.usf.jquery.core.JQueryType.COLUMN; import static org.usf.jquery.core.JQueryType.QUERY; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.web.ParseException.cannotParseException; import java.math.BigDecimal; import java.sql.Date; @@ -59,6 +58,7 @@ public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType. } public static Object parseJdbc(RequestEntryChain entry, TableDecorator td, JDBCType... types) { + ParseException ex = null; // preserve last exception try { return matchTypes((Typed) parseJQuery(entry, td, COLUMN, QUERY), types); //try parse column | query first } catch (ParseException e) {/*do not throw exception*/} @@ -70,20 +70,23 @@ public static Object parseJdbc(RequestEntryChain entry, TableDecorator td, JDBCT return jdbcArgParser(type).parseEntry(entry, td); } catch (ParseException e) { /*do not throw exception*/ log.trace("parse {} : '{}' => {}", type, entry, e.getMessage()); + ex = e; } } - throw cannotParseException(entry.toString(), Arrays.toString(types)); + throw ex; } public static Object parseJQuery(RequestEntryChain entry, TableDecorator td, JQueryType... types) { + ParseException ex = null; // preserve last exception for(var type : types) { try { return jqueryArgParser(type).parseEntry(entry, td); } catch (ParseException e) {/*do not throw exception*/ log.trace("parse {} : '{}' => {}", type, entry, e.getMessage()); + ex = e; } } - throw cannotParseException(entry.toString(), Arrays.toString(types)); + throw ex; } public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { @@ -118,14 +121,14 @@ public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type) { case FILTER: return RequestEntryChain::evalFilter; case ORDER: return RequestEntryChain::evalOrder; case QUERY: return RequestEntryChain::evalQuery; - case PARTITION: return RequestEntryChain::evalPartition; + case PARTITION: return RequestEntryChain::evalPartition; default: throw unsupportedTypeException(type); } } private static Object matchTypes(Typed o, JDBCType... types) { if(isNull(o.getType()) || isEmpty(types)) { - return true; + return o; } for(var t : types) { if(t.accept(o)) { diff --git a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java index 12637961..c50c4270 100644 --- a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java +++ b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java @@ -7,10 +7,19 @@ import lombok.RequiredArgsConstructor; import lombok.experimental.Delegate; +/** + * + * @author u$f + * + */ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) final class CompletableViewQuery implements DBView { @Delegate private final ViewQuery query; + @Override + public String toString() { + return query.toString(); + } } diff --git a/src/main/java/org/usf/jquery/web/ParseException.java b/src/main/java/org/usf/jquery/web/ParseException.java index c23c6599..a5b135c2 100644 --- a/src/main/java/org/usf/jquery/web/ParseException.java +++ b/src/main/java/org/usf/jquery/web/ParseException.java @@ -23,6 +23,8 @@ static ParseException cannotParseException(String type, String value) { } static ParseException cannotParseException(String type, String value, Throwable cause) { - return new ParseException("cannot parse " + quote(value) + " as " + quote(type), cause); + return new ParseException("cannot parse entry, " + + "expected: " + quote(type) + + " but was: " + quote(value), cause); } } diff --git a/src/main/java/org/usf/jquery/web/RequestContext.java b/src/main/java/org/usf/jquery/web/RequestContext.java index fe3ed10b..ebfa3081 100644 --- a/src/main/java/org/usf/jquery/web/RequestContext.java +++ b/src/main/java/org/usf/jquery/web/RequestContext.java @@ -3,18 +3,19 @@ import static java.util.Objects.isNull; import static java.util.Optional.ofNullable; import static org.usf.jquery.web.JQueryContext.context; -import static org.usf.jquery.web.NoSuchResourceException.throwNoSuchColumnException; -import static org.usf.jquery.web.NoSuchResourceException.throwNoSuchTableException; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Optional; import org.usf.jquery.core.DBView; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +@Slf4j @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class RequestContext { @@ -24,38 +25,16 @@ public final class RequestContext { private final Map columns; private final Map views = new LinkedHashMap<>(); //work views - public boolean isDeclaredView(String id) { - return tdMap.containsKey(id); - } - - public TableDecorator getViewDecorator(String id) { - return ofNullable(tdMap.get(id)). - orElseThrow(()-> throwNoSuchTableException(id)); - } - - public void putViewDecorator(TableDecorator td) { - if(tdMap.containsKey(td.identity())) { - throw new IllegalArgumentException(td.identity() + "already exist"); - } - tdMap.put(td.identity(), td); + public Optional lookupViewDecorator(String id) { + log.trace("lookup view decorator : {}", id); + return ofNullable(tdMap.get(id)); } - public boolean isDeclaredColumn(String id) { - return columns.containsKey(id); + public Optional lookupColumnDecorator(String id) { + log.trace("lookup column decorator : {}", id); + return ofNullable(columns.get(id)); } - public ColumnDecorator getColumnDecorator(String value) { - return ofNullable(columns.get(value)) - .orElseThrow(()-> throwNoSuchColumnException(value)); - } - - public void putColumnDecorator(ColumnDecorator cd) { - if(columns.containsKey(cd.identity())) { - throw new IllegalArgumentException(cd.identity() + "already exist"); - } - columns.put(cd.identity(), cd); - } - public DBView getView(String id) { return views.get(id); } @@ -71,7 +50,7 @@ public DBView[] views() { public static final RequestContext requestContext() { var rc = local.get(); if(isNull(rc)) { - var jc = context(); + var jc = context(); //can filter view & column rc = new RequestContext(new HashMap<>(jc.getTables()), new HashMap<>(jc.getColumns())); local.set(rc); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index ce8e9165..b2a65626 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -3,7 +3,9 @@ import static java.lang.reflect.Array.newInstance; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; +import static java.util.Optional.empty; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.BadArgumentException.badArgumentTypeException; import static org.usf.jquery.core.Comparator.lookupComparator; import static org.usf.jquery.core.JDBCType.INTEGER; @@ -23,7 +25,7 @@ import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.PARTITION; import static org.usf.jquery.web.Constants.SELECT; -import static org.usf.jquery.web.JQueryContext.context; +import static org.usf.jquery.web.ParseException.cannotParseException; import static org.usf.jquery.web.RequestContext.requestContext; import java.util.List; @@ -54,12 +56,14 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; /** * * @author u$f * */ +@Slf4j @Getter @Setter(value = AccessLevel.PACKAGE) @RequiredArgsConstructor @@ -81,7 +85,7 @@ public ViewQuery evalQuery(TableDecorator td) { var e = this; while(e.next()) { //preserve last entry e = e.next; - switch(e.value) {//column not permitted + switch(e.value) {//column not allowed case DISTINCT: e.requireNoArgs(); q.distinct(); break; case FILTER: q.filters(e.toFilterArgs(td)); break; case ORDER: q.orders(e.toOderArgs(td)); break; //not sure @@ -92,7 +96,7 @@ public ViewQuery evalQuery(TableDecorator td) { } return q.as(e.tag); //TD require tag to register query } - throw cannotParseException(SELECT, this); + throw cannotParseException(SELECT, this.toString()); } public Partition evalPartition(TableDecorator td) { @@ -100,7 +104,7 @@ public Partition evalPartition(TableDecorator td) { var p = new Partition(toColumnArgs(td, true)); if(next()) { var e = next; - if(ORDER.equals(e.value)) {//column not permitted + if(ORDER.equals(e.value)) {//column not allowed p.orders(e.toOderArgs(td)); //not sure } else { @@ -109,26 +113,26 @@ public Partition evalPartition(TableDecorator td) { } return p; } - throw cannotParseException(PARTITION, this); + throw cannotParseException(PARTITION, this.toString()); } public TaggableColumn evalColumn(TableDecorator td) { - var r = chainResourceOperations(td, false) - .orElseThrow(()-> cannotParseException(COLUMN, this)); + var r = chainResourceOperations(td, false); //throws ParseException if(r.entry.isLast()) { if(nonNull(r.entry.tag)) { //TD: required tag if operation return r.col.as(r.entry.tag); } - return r.col instanceof TaggableColumn - ? (TaggableColumn) r.col - : r.col.as(r.cd.identity()); + if(r.col instanceof TaggableColumn) { + return (TaggableColumn) r.col ; + } + log.warn("tag missing : {}", this); + return r.col.as(r.cd.identity()); } throw unexpectedEntryException(r.entry.next); } public DBOrder evalOrder(TableDecorator td) { - var r = chainResourceOperations(td, false) - .orElseThrow(()-> cannotParseException(ORDER, this)); + var r = chainResourceOperations(td, false); //throws ParseException if(r.entry.isLast()) { // no order return r.col.order(); } @@ -145,32 +149,43 @@ public DBFilter evalFilter(TableDecorator td) { } public DBFilter evalFilter(TableDecorator td, List values) { - var o = chainResourceOperations(td, true); - if(o.isPresent()) { - var r = o.get(); - if(r.entry.isLast()) { // no comparator - r.entry.setNext(defaultComparatorEntry(values)); + try { + var r = chainResourceOperations(td, true); + var e = r.entry; + if(e.isLast()) { // no comparator + e.setNext(defaultComparatorEntry(values)); } - r.entry.next.updateArgs(values); - return r.entry.next.chainComparator(td, r.cd, r.col); + return e.next.updateArgs(values) + .chainComparator(td, r.cd, r.col); } - if(next() && context().isDeclaredTable(value)) { - var c = context().getTable(value).criteria(next.value); - if(nonNull(c)) { - next.updateArgs(values); - return next.chainComparator(td, c.build(toStringArray(next.args))); - } + catch(ParseException e) { + return tableCriteria(td, values) + .orElseThrow(()-> cannotParseException(FILTER, this.toString(), e)); + } + } + + Optional tableCriteria(TableDecorator td, List values) { + RequestEntryChain e = null; + CriteriaBuilder c = null; + var res = requestContext().lookupViewDecorator(value); + if(res.isPresent() && next()) { + c = res.get().criteria(next.value); + e = next; // only if nonNull + } + if(isNull(c)) { + c = td.criteria(value); + e = this; } - var c = td.criteria(value); if(nonNull(c)) { - updateArgs(values); - return chainComparator(td, c.build(toStringArray(args))); + e.updateArgs(values); + var f = e.chainComparator(td, c.build(toStringArray(args))); + return Optional.of(f); } - return null; + return empty(); } //column.eq=v1 - private void updateArgs(List values) { + private RequestEntryChain updateArgs(List values) { if(!isEmpty(values)) { if(isLast() && isNull(args)) { setArgs(values); @@ -179,6 +194,7 @@ private void updateArgs(List values) { throw new UnexpectedEntryException(this + "=" + Utils.toString(values.toArray())); } } + return this; } static RequestEntryChain defaultComparatorEntry(List values) { @@ -203,7 +219,7 @@ DBFilter chainComparator(TableDecorator td, ColumnDecorator cd, DBColumn col){ if(nonNull(f)) { return chainComparator(td, f); } - throw cannotParseException("comparison|criteria", this); + throw cannotParseException("comparison|criteria", this.toString()); } DBFilter chainComparator(TableDecorator td, DBFilter f){ @@ -222,40 +238,39 @@ DBFilter chainComparator(TableDecorator td, DBFilter f){ return f; } - private Optional chainResourceOperations(TableDecorator td, boolean filter) { - return lookupResource(td).map(r-> { - var e = r.entry.next; - while(nonNull(e)) { // chain until !operator - var c = e.toOperation(td, r.col, fn-> true); - if(c.isEmpty()) { - break; - } - r.entry = e; - r.col = filter && "over".equals(e.value) - ? windowColumn(r.td, c.get().as(r.cd.identity())) - : c.get(); - e = e.next; - } - return r; - }); + private ResourceCursor chainResourceOperations(TableDecorator td, boolean filter) { + var r = lookupResource(td).orElseThrow(()-> cannotParseException(COLUMN, this.toString())); + var e = r.entry.next; + while(nonNull(e)) { // chain until !operator + var c = e.toOperation(td, r.col, fn-> true); + if(c.isEmpty()) { + break; + } + r.entry = e; + r.col = filter && "over".equals(e.value) + ? windowColumn(r.td, c.get().as(r.cd.identity())) + : c.get(); + e = e.next; + } + return r; } private static DBColumn windowColumn(TableDecorator td, TaggableColumn column) { - var v = td.table(); - var vw = requestContext().getView(v.id()); // TD use tag ? + var vw = requestContext().getView(td.identity()); if(vw instanceof CompletableViewQuery) { // already create ((CompletableViewQuery)vw).getQuery().columns(column); } else { - vw = new CompletableViewQuery((isNull(vw) ? v : vw).window(td.identity(), column)); + vw = new CompletableViewQuery((isNull(vw) ? td.table() : vw).window(td.identity(), column)); requestContext().putView(vw); // same name } - return new ViewColumn(v, doubleQuote(column.tagname()), null, column.getType()); + return new ViewColumn(vw, doubleQuote(column.tagname()), null, column.getType()); } private Optional lookupResource(TableDecorator td) { - if(next() && requestContext().isDeclaredView(value)) { //sometimes td.id == cd.id - var rc = next.lookupViewResource(requestContext().getViewDecorator(value), RequestEntryChain::isWindowFunction); + if(next()) { //check td.cd first + var rc = requestContext().lookupViewDecorator(value) + .flatMap(v-> next.lookupViewResource(v, RequestEntryChain::isWindowFunction)); if(rc.isPresent()) { requireNoArgs(); // noArgs on valid resource return rc; @@ -265,9 +280,14 @@ private Optional lookupResource(TableDecorator td) { } private Optional lookupViewResource(TableDecorator td, Predicate pre) { - return requestContext().isDeclaredColumn(value) - ? Optional.of(new ResourceCursor(td, context().getColumn(requireNoArgs().value), this)) - : toOperation(td, null, pre).map(op-> new ResourceCursor(td, ofColumn(value, b-> op), this)); + var res = requestContext().lookupColumnDecorator(value); + if(res.isPresent()) { + requireNoArgs(); + } + else { + res = toOperation(td, null, pre).map(op-> ofColumn(value, b-> op)); + } + return res.map(cd-> new ResourceCursor(td, cd, this)); } private Optional toOperation(TableDecorator td, DBColumn col, Predicate pre) { @@ -284,26 +304,26 @@ private Optional toOperation(TableDecorator td, DBColumn col, P } private TaggableColumn[] toColumnArgs(TableDecorator td, boolean allowEmpty) { - return (TaggableColumn[]) toArgs(td, null, JQueryType.COLUMN, allowEmpty); + return (TaggableColumn[]) toArgs(td, JQueryType.COLUMN, allowEmpty); } private DBFilter[] toFilterArgs(TableDecorator td) { - return (DBFilter[]) toArgs(td, null, JQueryType.FILTER, false); + return (DBFilter[]) toArgs(td, JQueryType.FILTER, false); } private DBOrder[] toOderArgs(TableDecorator td) { - return (DBOrder[]) toArgs(td, null, JQueryType.ORDER, false); + return (DBOrder[]) toArgs(td, JQueryType.ORDER, false); } - private Object[] toArgs(TableDecorator td, DBObject col, JavaType type, boolean allowEmpty) { + private Object[] toArgs(TableDecorator td, JavaType type, boolean allowEmpty) { var c = type.typeClass(); - if(c.isArray()) { - throw new UnsupportedOperationException("cannot create array of " + c); + if(DBObject.class.isAssignableFrom(c)) { // JQuery types & !array + var ps = allowEmpty + ? ofParameters(varargs(type)) + : ofParameters(required(type), varargs(type)); + return toArgs(td, null, ps, s-> (Object[]) newInstance(c, s)); } - var ps = allowEmpty - ? ofParameters(varargs(type)) - : ofParameters(required(type), varargs(type)); - return toArgs(td, col, ps, s-> (Object[]) newInstance(c, s)); + throw new UnsupportedOperationException("cannot create array of " + c); } private int toIntArg(TableDecorator td) { @@ -315,7 +335,7 @@ private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps) { } private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps, IntFunction arrFn) { - int inc = nonNull(col) ? 1 : 0; + int inc = isNull(col) ? 0 : 1; var arr = arrFn.apply(isNull(args) ? inc : args.size() + inc); if(nonNull(col)) { arr[0] = col; @@ -324,13 +344,13 @@ private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps, IntFun if(i>=inc) { //arg0 already parsed var e = args.get(i-inc); if(isNull(e.value) || e.text) { - arr[i] = e.requireNoArgs().value; + arr[i] = e.requireNoArgs().value; //checked later } else { try { arr[i] = parse(e, td, p.types(arr)); - } catch (ParseException ex) { - throw badArgumentTypeException(p.getTypes(), e); + } catch (ParseException ex) { // do not throw parse exception + throw badArgumentTypeException(p.types(arr), e, ex); } } } @@ -342,7 +362,7 @@ RequestEntryChain requireNoArgs() { if(isNull(args)) { return this; } - throw new UnexpectedEntryException(value + " takes no args : " + this); + throw badArgumentCountException(0, args.size()); } RequestEntryChain requireNoNext(){ @@ -385,9 +405,6 @@ private static String[] toStringArray(List entries) { .toArray(String[]::new); } - private static ParseException cannotParseException(String type, RequestEntryChain entry) { - return ParseException.cannotParseException(type, entry.toString()); - } public static UnexpectedEntryException unexpectedEntryException(RequestEntryChain entry, String... expected) { return UnexpectedEntryException.unexpectedEntryException(entry.toString(), expected); } diff --git a/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java b/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java index 5f2df799..eba64130 100644 --- a/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java +++ b/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java @@ -2,7 +2,6 @@ import static java.lang.String.join; import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.core.Utils.isEmpty; /** * @@ -17,7 +16,8 @@ public UnexpectedEntryException(String message) { } public static UnexpectedEntryException unexpectedEntryException(String entry, String... expected) { - return new UnexpectedEntryException("unexpected entry : " + quote(entry) + - (isEmpty(expected) ? "" :" expect : " + join("|", expected))); + return new UnexpectedEntryException("unexpected entry, " + + "expected: " + join("|", expected) + + " but was: " + quote(entry)); } } From 8e20f0c0da5f2fc6b6741d42dd9e543c90666863 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 20 Feb 2024 09:44:40 +0100 Subject: [PATCH 082/298] edit --- .../usf/jquery/core/BadArgumentException.java | 15 +-- .../org/usf/jquery/core/JQueryException.java | 6 ++ .../org/usf/jquery/core/KeyValueMapper.java | 5 +- .../org/usf/jquery/core/ParameterSet.java | 19 ++-- .../org/usf/jquery/core/RequestQuery.java | 4 +- .../usf/jquery/core/RequestQueryBuilder.java | 4 +- .../org/usf/jquery/core/TypedComparator.java | 5 + .../org/usf/jquery/core/TypedOperator.java | 5 + .../java/org/usf/jquery/core/ViewQuery.java | 3 +- .../org/usf/jquery/web/ArgumentParsers.java | 4 +- .../org/usf/jquery/web/ParseException.java | 6 +- .../org/usf/jquery/web/RequestEntryChain.java | 95 +++++++++---------- .../jquery/web/RequestQueryParamResolver.java | 6 ++ .../org/usf/jquery/web/TableDecorator.java | 5 +- .../jquery/web/UnexpectedEntryException.java | 5 +- 15 files changed, 101 insertions(+), 86 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/BadArgumentException.java b/src/main/java/org/usf/jquery/core/BadArgumentException.java index 6f16c1da..4e791e5a 100644 --- a/src/main/java/org/usf/jquery/core/BadArgumentException.java +++ b/src/main/java/org/usf/jquery/core/BadArgumentException.java @@ -21,18 +21,19 @@ public BadArgumentException(String message, Throwable cause) { } public static BadArgumentException badArgumentTypeException(JavaType[] types, Object actual) { - return badArgumentTypeException(types, actual, null); + return badArgumentTypeException(types, actual, null); } public static BadArgumentException badArgumentTypeException(JavaType[] types, Object actual, Exception e) { - return new BadArgumentException("bad argument type, "+ - "expected: " + Stream.of(types).map(Object::toString).collect(joining("|")) + - " but was: " + actual, e); + return new BadArgumentException(formatMessage("bad argument type", + Stream.of(types).map(Object::toString).collect(joining("|")), actual), e); } public static BadArgumentException badArgumentCountException(int count, int actual) { - return new BadArgumentException("bad argument count, "+ - "expected: " + count + - " but was: " + actual); + return new BadArgumentException(formatMessage("bad argument count", count, actual)); + } + + public static BadArgumentException badArgumentsException(String exp, String actual, Exception e) { + return new BadArgumentException(formatMessage("bad arguments", exp, actual), e); } } diff --git a/src/main/java/org/usf/jquery/core/JQueryException.java b/src/main/java/org/usf/jquery/core/JQueryException.java index c585bf58..7f6a9877 100644 --- a/src/main/java/org/usf/jquery/core/JQueryException.java +++ b/src/main/java/org/usf/jquery/core/JQueryException.java @@ -19,4 +19,10 @@ public JQueryException(Throwable cause) { public JQueryException(String message, Throwable cause) { super(message, cause); } + + protected static String formatMessage(String msg, Object expected, Object actual) { + return msg + ", " + + "expected: " + expected + + " but was: " + actual; + } } diff --git a/src/main/java/org/usf/jquery/core/KeyValueMapper.java b/src/main/java/org/usf/jquery/core/KeyValueMapper.java index e26dae80..b4248903 100644 --- a/src/main/java/org/usf/jquery/core/KeyValueMapper.java +++ b/src/main/java/org/usf/jquery/core/KeyValueMapper.java @@ -19,7 +19,7 @@ public final class KeyValueMapper implements ResultSetMapper> @Override public List map(ResultSet rs) throws SQLException { - log.debug("mapping results..."); + log.trace("mapping results..."); var bg = currentTimeMillis(); var results = new LinkedList(); var columnNames = declaredColumns(rs); @@ -30,8 +30,7 @@ public List map(ResultSet rs) throws SQLException { } results.add(model); } - log.info("{} rows mapped in {} ms", results.size(), currentTimeMillis() - bg); + log.trace("{} rows mapped in {} ms", results.size(), currentTimeMillis() - bg); return results; } - } diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index 74992d6f..5707ab32 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -5,7 +5,9 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.BadArgumentException.badArgumentTypeException; +import static org.usf.jquery.core.BadArgumentException.badArgumentsException; +import java.util.Arrays; import java.util.function.ObjIntConsumer; import java.util.stream.Stream; @@ -29,12 +31,17 @@ public final class ParameterSet { //there is no Singleton implementation, dummy public Object[] args(Object... args) { var arr = isNull(args) ? new Object[0] : args; - forEach(arr.length, (p,i)-> { - if(!p.accept(i, arr)) { - throw badArgumentTypeException(p.types(args), arr[i]); - } - }); - return arr; + try { + forEach(arr.length, (p,i)-> { + if(!p.accept(i, arr)) { + throw badArgumentTypeException(p.types(args), arr[i]); + } + }); + return arr; + } + catch (BadArgumentException e) { + throw badArgumentsException(this.toString(), Arrays.toString(arr), e); + } } public void forEach(int nArgs, ObjIntConsumer cons) { diff --git a/src/main/java/org/usf/jquery/core/RequestQuery.java b/src/main/java/org/usf/jquery/core/RequestQuery.java index c4d03983..180d0022 100644 --- a/src/main/java/org/usf/jquery/core/RequestQuery.java +++ b/src/main/java/org/usf/jquery/core/RequestQuery.java @@ -49,10 +49,10 @@ public T execute(DataSource ds, ResultSetMapper mapper) throws SQLExcepti } } } - log.debug("executing SQL query..."); + log.trace("executing SQL query..."); var bg = currentTimeMillis(); try(var rs = ps.executeQuery()){ - log.debug("query executed in {} ms", currentTimeMillis() - bg); + log.trace("query executed in {} ms", currentTimeMillis() - bg); try { return mapper.map(rs); } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index a76ea4a1..7652e307 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -96,7 +96,7 @@ public RequestQuery build(){ } public RequestQuery build(String schema) { - log.debug("building query..."); + log.trace("building query..."); // requireNonEmpty(tables); requireNonEmpty(columns); var bg = currentTimeMillis(); @@ -109,7 +109,7 @@ public RequestQuery build(String schema) { else { sb.forEach(it, " UNION ALL ", o-> build(sb, pb)); } - log.debug("query built in {} ms", currentTimeMillis() - bg); + log.trace("query built in {} ms", currentTimeMillis() - bg); return new RequestQuery(sb.toString(), pb.args(), pb.argTypes()); } diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index dc809655..75a43111 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -47,4 +47,9 @@ public TypedComparator argsMapper(UnaryOperator argMapper) { this.argMapper = argMapper; return this; } + + @Override + public String toString() { + return comparator.id() + parameterSet.toString(); + } } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index d06f9863..d7741382 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -49,4 +49,9 @@ public TypedOperator argsMapper(UnaryOperator argMapper) { this.argMapper = argMapper; return this; } + + @Override + public String toString() { + return operator.id() + parameterSet.toString(); + } } diff --git a/src/main/java/org/usf/jquery/core/ViewQuery.java b/src/main/java/org/usf/jquery/core/ViewQuery.java index 729ee670..518585da 100644 --- a/src/main/java/org/usf/jquery/core/ViewQuery.java +++ b/src/main/java/org/usf/jquery/core/ViewQuery.java @@ -1,6 +1,7 @@ package org.usf.jquery.core; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.SqlStringBuilder.parenthese; import org.usf.jquery.core.JavaType.Typed; @@ -29,7 +30,7 @@ public ViewQuery(@NonNull String id, @NonNull TaggableColumn... columns) { public String sql(QueryParameterBuilder builder) { var s = new SqlStringBuilder(100); query.build(s, builder.subQuery()); //important! build query first - return "(" + s.toString() + ")"; + return parenthese(s.toString()); } @Override diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 0f4d0364..0459a7e0 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -139,7 +139,9 @@ private static Object matchTypes(Typed o, JDBCType... types) { } private static T[] cast(JavaType[] types, Class c, IntFunction fn) { - return types.getClass() == c ? c.cast(types) : Stream.of(types).toArray(fn); + return types.getClass() == c + ? c.cast(types) + : Stream.of(types).toArray(fn); } private static UnsupportedOperationException unsupportedTypeException(JavaType type) { diff --git a/src/main/java/org/usf/jquery/web/ParseException.java b/src/main/java/org/usf/jquery/web/ParseException.java index a5b135c2..4686e4b4 100644 --- a/src/main/java/org/usf/jquery/web/ParseException.java +++ b/src/main/java/org/usf/jquery/web/ParseException.java @@ -1,7 +1,5 @@ package org.usf.jquery.web; -import static org.usf.jquery.core.SqlStringBuilder.quote; - /** * * @author u$f @@ -23,8 +21,6 @@ static ParseException cannotParseException(String type, String value) { } static ParseException cannotParseException(String type, String value, Throwable cause) { - return new ParseException("cannot parse entry, " - + "expected: " + quote(type) - + " but was: " + quote(value), cause); + return new ParseException(formatMessage("cannot parse entry", type, value), cause); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index b2a65626..bd78a33f 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -6,7 +6,7 @@ import static java.util.Optional.empty; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; -import static org.usf.jquery.core.BadArgumentException.badArgumentTypeException; +import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.Comparator.lookupComparator; import static org.usf.jquery.core.JDBCType.INTEGER; import static org.usf.jquery.core.Operator.lookupOperator; @@ -27,12 +27,14 @@ import static org.usf.jquery.web.Constants.SELECT; import static org.usf.jquery.web.ParseException.cannotParseException; import static org.usf.jquery.web.RequestContext.requestContext; +import static org.usf.jquery.web.UnexpectedEntryException.unexpectedEntryException; import java.util.List; import java.util.Optional; import java.util.function.IntFunction; import java.util.function.Predicate; +import org.usf.jquery.core.BadArgumentException; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; @@ -89,9 +91,9 @@ public ViewQuery evalQuery(TableDecorator td) { case DISTINCT: e.requireNoArgs(); q.distinct(); break; case FILTER: q.filters(e.toFilterArgs(td)); break; case ORDER: q.orders(e.toOderArgs(td)); break; //not sure - case OFFSET: q.offset(e.toIntArg(td)); break; - case FETCH: q.fetch(e.toIntArg(td)); break; - default: throw unexpectedEntryException(e, DISTINCT, FILTER, ORDER, OFFSET, FETCH); + case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; + case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; + default: throw unexpectedEntryException(e.toString(), DISTINCT, FILTER, ORDER, OFFSET, FETCH); } } return q.as(e.tag); //TD require tag to register query @@ -102,22 +104,22 @@ public ViewQuery evalQuery(TableDecorator td) { public Partition evalPartition(TableDecorator td) { if(PARTITION.equals(value)) { var p = new Partition(toColumnArgs(td, true)); - if(next()) { + if(next()) { //TD loop var e = next; if(ORDER.equals(e.value)) {//column not allowed p.orders(e.toOderArgs(td)); //not sure } else { - throw unexpectedEntryException(e, ORDER); + throw unexpectedEntryException(e.toString(), ORDER); } - } + }//require no tag return p; } throw cannotParseException(PARTITION, this.toString()); } public TaggableColumn evalColumn(TableDecorator td) { - var r = chainResourceOperations(td, false); //throws ParseException + var r = chainColumnOperations(td, false); //throws ParseException if(r.entry.isLast()) { if(nonNull(r.entry.tag)) { //TD: required tag if operation return r.col.as(r.entry.tag); @@ -128,11 +130,11 @@ public TaggableColumn evalColumn(TableDecorator td) { log.warn("tag missing : {}", this); return r.col.as(r.cd.identity()); } - throw unexpectedEntryException(r.entry.next); + throw unexpectedEntryException(r.entry.next.toString(), "[view].column[.op]*"); } public DBOrder evalOrder(TableDecorator td) { - var r = chainResourceOperations(td, false); //throws ParseException + var r = chainColumnOperations(td, false); //throws ParseException if(r.entry.isLast()) { // no order return r.col.order(); } @@ -141,7 +143,7 @@ public DBOrder evalOrder(TableDecorator td) { var o = Order.valueOf(e.requireNoArgs().value.toUpperCase()); // noArgs on valid order return r.col.order(o); } - throw unexpectedEntryException(e, "asc|desc"); + throw unexpectedEntryException(e.toString(), "asc|desc"); } public DBFilter evalFilter(TableDecorator td) { @@ -150,10 +152,17 @@ public DBFilter evalFilter(TableDecorator td) { public DBFilter evalFilter(TableDecorator td, List values) { try { - var r = chainResourceOperations(td, true); + var r = chainColumnOperations(td, true); var e = r.entry; - if(e.isLast()) { // no comparator - e.setNext(defaultComparatorEntry(values)); + if(e.isLast()) { // no comparator, no criteria + String cmp; + if(isEmpty(values)) { + cmp = "isNull"; + } + else { + cmp = values.size() > 1 ? "in" : "eq"; //query ?? + } + e.setNext(new RequestEntryChain(cmp)); // do not set args; } return e.next.updateArgs(values) .chainComparator(td, r.cd, r.col); @@ -183,7 +192,7 @@ Optional tableCriteria(TableDecorator td, List valu } return empty(); } - + //column.eq=v1 private RequestEntryChain updateArgs(List values) { if(!isEmpty(values)) { @@ -197,17 +206,6 @@ private RequestEntryChain updateArgs(List values) { return this; } - static RequestEntryChain defaultComparatorEntry(List values) { - String cmp; - if(isEmpty(values)) { - cmp = "isNull"; - } - else { - cmp = values.size() > 1 ? "in" : "eq"; //query ?? - } - return new RequestEntryChain(cmp); // do not set args - } - DBFilter chainComparator(TableDecorator td, ColumnDecorator cd, DBColumn col){ var f = lookupComparator(value).map(c-> c.args(toArgs(td, col, c.getParameterSet()))).orElse(null); //eval comparator first => avoid overriding if(isNull(f) && col instanceof TaggableColumn) { //no operation @@ -227,19 +225,18 @@ DBFilter chainComparator(TableDecorator td, DBFilter f){ while(nonNull(e)) { if(e.value.matches("and|or")) { var op = LogicalOperator.valueOf(e.value.toUpperCase()); - f = f.append(op, (DBFilter) e.toArgs(td, null, - ofParameters(required(JQueryType.FILTER)), DBFilter[]::new)[0]); + f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); } else { - throw unexpectedEntryException(e, "and|or"); + throw unexpectedEntryException(e.toString(), "and|or"); } e = e.next; } return f; } - private ResourceCursor chainResourceOperations(TableDecorator td, boolean filter) { - var r = lookupResource(td).orElseThrow(()-> cannotParseException(COLUMN, this.toString())); + private ResourceCursor chainColumnOperations(TableDecorator td, boolean filter) { + var r = lookupResource(td).orElseThrow(()-> cannotParseException(JQueryType.COLUMN, this.toString())); var e = r.entry.next; while(nonNull(e)) { // chain until !operator var c = e.toOperation(td, r.col, fn-> true); @@ -326,8 +323,8 @@ private Object[] toArgs(TableDecorator td, JavaType type, boolean allowEmpty) { throw new UnsupportedOperationException("cannot create array of " + c); } - private int toIntArg(TableDecorator td) { - return (Integer) toArgs(td, null, ofParameters(required(INTEGER)))[0]; + private Object toOneArg(TableDecorator td, JavaType type) { + return toArgs(td, null, ofParameters(required(type)))[0]; } private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps) { @@ -340,22 +337,20 @@ private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps, IntFun if(nonNull(col)) { arr[0] = col; } - ps.forEach(arr.length, (p,i)-> { - if(i>=inc) { //arg0 already parsed - var e = args.get(i-inc); - if(isNull(e.value) || e.text) { - arr[i] = e.requireNoArgs().value; //checked later - } - else { - try { - arr[i] = parse(e, td, p.types(arr)); - } catch (ParseException ex) { // do not throw parse exception - throw badArgumentTypeException(p.types(arr), e, ex); - } + try { + ps.forEach(arr.length, (p,i)-> { + if(i>=inc) { //arg0 already parsed + var e = args.get(i-inc); + arr[i] = isNull(e.value) || e.text + ? e.requireNoArgs().value + : parse(e, td, p.types(arr)); } - } - }); - return arr; + }); + return arr; + } + catch (ParseException | BadArgumentException e) { + throw badArgumentsException(ps.toString(), this.toString(), e); + } } RequestEntryChain requireNoArgs() { @@ -405,10 +400,6 @@ private static String[] toStringArray(List entries) { .toArray(String[]::new); } - public static UnexpectedEntryException unexpectedEntryException(RequestEntryChain entry, String... expected) { - return UnexpectedEntryException.unexpectedEntryException(entry.toString(), expected); - } - @RequiredArgsConstructor static final class ResourceCursor { diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index a592bfa9..919b56b2 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -1,5 +1,6 @@ package org.usf.jquery.web; +import static java.lang.System.currentTimeMillis; import static org.usf.jquery.core.Utils.isPresent; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; @@ -13,16 +14,20 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; /** * * @author u$f * */ +@Slf4j @RequiredArgsConstructor public final class RequestQueryParamResolver { public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull Map parameterMap) { + var t = System.currentTimeMillis(); + log.trace("parsing request..."); parameterMap = new LinkedHashMap<>(parameterMap); //unmodifiable map if(!parameterMap.containsKey(COLUMN) && !parameterMap.containsKey(COLUMN_DISTINCT)) { parameterMap.put(COLUMN, ant.defaultColumns()); @@ -36,6 +41,7 @@ public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull if(ant.aggregationOnly() && !req.isAggregation()) { throw new IllegalDataAccessException("non aggregation query"); } + log.trace("request parsed in {} ms", currentTimeMillis() - t); return req; } } diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index 5c88bc75..f105558a 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -15,7 +15,6 @@ import static org.usf.jquery.web.JQueryContext.database; import static org.usf.jquery.web.MissingParameterException.missingParameterException; import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; -import static org.usf.jquery.web.RequestContext.clearContext; import static org.usf.jquery.web.RequestContext.requestContext; import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; @@ -73,14 +72,14 @@ default CriteriaBuilder criteria(String name) { //!aggregation } default RequestQueryBuilder query(Map parameterMap) { - clearContext(); currentDatabase(database().getType()); //table database var query = new RequestQueryBuilder(); parseViews(query, parameterMap); parseColumns(query, parameterMap); parseFilters(query, parameterMap); parseOrders (query, parameterMap); - parseFetch(query, parameterMap); + parseFetch(query, parameterMap); + return query.views(requestContext().views()); } diff --git a/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java b/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java index eba64130..06e160dc 100644 --- a/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java +++ b/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java @@ -1,7 +1,6 @@ package org.usf.jquery.web; import static java.lang.String.join; -import static org.usf.jquery.core.SqlStringBuilder.quote; /** * @@ -16,8 +15,6 @@ public UnexpectedEntryException(String message) { } public static UnexpectedEntryException unexpectedEntryException(String entry, String... expected) { - return new UnexpectedEntryException("unexpected entry, " + - "expected: " + join("|", expected) + - " but was: " + quote(entry)); + return new UnexpectedEntryException(formatMessage("unexpected entry, ", join("|", expected), entry)); } } From dbce91fe5bf8a2629729dea2f92edb11192a765f Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 20 Feb 2024 10:09:55 +0100 Subject: [PATCH 083/298] edit --- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index bd78a33f..ecd7a173 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -236,7 +236,7 @@ DBFilter chainComparator(TableDecorator td, DBFilter f){ } private ResourceCursor chainColumnOperations(TableDecorator td, boolean filter) { - var r = lookupResource(td).orElseThrow(()-> cannotParseException(JQueryType.COLUMN, this.toString())); + var r = lookupResource(td).orElseThrow(()-> cannotParseException(COLUMN, this.toString())); var e = r.entry.next; while(nonNull(e)) { // chain until !operator var c = e.toOperation(td, r.col, fn-> true); From 46efb819ac385242e73d2f63bf1c5e55ae5c0263 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 20 Feb 2024 22:34:25 +0100 Subject: [PATCH 084/298] edit --- .../java/org/usf/jquery/core/QueryParameterBuilder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 2400cb73..7fb09cc2 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -26,7 +26,7 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class QueryParameterBuilder { - private static final String ARG = "?"; + private static final String P_ARG = "?"; @Getter private final String schema; @@ -89,7 +89,7 @@ public String appendLitteral(Object o) { //TD : stringify value using db defaul private String appendArg(JDBCType type, Object o) { argTypes.add(type); args.add(o); - return ARG; + return P_ARG; } public Object[] args() { @@ -108,7 +108,7 @@ static String nParameter(int n){ if(n < 1){ return EMPTY; } - return n == 1 ? ARG : ARG + (COMA + ARG).repeat(n-1); + return n == 1 ? P_ARG : P_ARG + (COMA + P_ARG).repeat(n-1); } public static String formatValue(Object o) { From 202fc4d4628a815a84a0f7e5b6241eeb921b977f Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 6 Mar 2024 09:44:13 +0100 Subject: [PATCH 085/298] edit --- .../java/org/usf/jquery/core/CaseColumn.java | 9 ++-- .../java/org/usf/jquery/core/DBQuery.java | 24 +++++++++ .../jquery/core/QueryParameterBuilder.java | 40 ++++++++++----- .../usf/jquery/core/RequestQueryBuilder.java | 40 +++++++++++---- .../org/usf/jquery/core/SqlStringBuilder.java | 20 ++++++-- .../java/org/usf/jquery/core/ViewQuery.java | 14 ++--- .../org/usf/jquery/core/WhenExpression.java | 9 +++- .../org/usf/jquery/web/ArgumentParsers.java | 6 +-- .../org/usf/jquery/web/ColumnDecorator.java | 19 ++++++- .../usf/jquery/web/CompletableViewQuery.java | 4 +- .../org/usf/jquery/web/QueryDecorator.java | 51 +++++++++++++++++++ .../org/usf/jquery/web/RequestContext.java | 46 ++++++++++++----- .../org/usf/jquery/web/RequestEntryChain.java | 49 ++++++++++++------ .../org/usf/jquery/web/TableDecorator.java | 48 ++++++++--------- .../org/usf/jquery/web/TableMetadata.java | 15 +++--- 15 files changed, 281 insertions(+), 113 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/DBQuery.java create mode 100644 src/main/java/org/usf/jquery/web/QueryDecorator.java diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index cb3369d5..ed9b4a8c 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -6,7 +6,7 @@ import java.util.Collection; import java.util.LinkedList; -import java.util.Optional; +import java.util.Objects; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -17,7 +17,7 @@ * */ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public final class CaseColumn implements DBColumn { +public final class CaseColumn implements DBColumn { // TD override isAggregation private final Collection expressions = new LinkedList<>(); @@ -32,10 +32,9 @@ public String sql(QueryParameterBuilder builder) { @Override public JavaType getType() { return expressions.stream() - .map(JDBCType::typeOf) - .filter(Optional::isPresent) // should have same type + .map(WhenExpression::getType) + .filter(Objects::nonNull) // should have same type .findAny() - .orElseGet(Optional::empty) .orElse(null); } diff --git a/src/main/java/org/usf/jquery/core/DBQuery.java b/src/main/java/org/usf/jquery/core/DBQuery.java new file mode 100644 index 00000000..4f3e09f4 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/DBQuery.java @@ -0,0 +1,24 @@ +package org.usf.jquery.core; + +import java.util.Collection; + +import org.usf.jquery.core.JavaType.Typed; + +/** + * + * @author u$f + * + */ +public interface DBQuery extends DBView, Typed { + + Collection columns(); + + @Override + default JavaType getType() { + var cols = columns(); + if(cols.size() == 1) { + return cols.iterator().next().getType(); + } + throw new UnsupportedOperationException((cols.isEmpty() ? "no columns" : "too many columns") + " : " + this); + } +} diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 7fb09cc2..360a2b00 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -9,21 +9,23 @@ import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.isEmpty; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NonNull; -import lombok.RequiredArgsConstructor; +import lombok.Setter; /** * * @author u$f * */ -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@Setter(AccessLevel.PACKAGE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public final class QueryParameterBuilder { private static final String P_ARG = "?"; @@ -34,6 +36,7 @@ public final class QueryParameterBuilder { private final List args; private final List argTypes; private final List views; //indexed + private Integer index; public List views(){ return views; @@ -87,11 +90,22 @@ public String appendLitteral(Object o) { //TD : stringify value using db defaul } private String appendArg(JDBCType type, Object o) { - argTypes.add(type); - args.add(o); + if(isNull(index)) { + argTypes.add(type); + args.add(o); + } + else { + argTypes.add(index, type); + args.add(index, o); + index++; + } return P_ARG; } + public int argCount() { + return args.size(); + } + public Object[] args() { return dynamic() ? args.toArray() : null; } @@ -112,24 +126,22 @@ static String nParameter(int n){ } public static String formatValue(Object o) { - if(nonNull(o)){ - return o instanceof Number - ? o.toString() - : quote(o.toString()); + if(o instanceof Number){ + return o.toString(); } - return "null"; + return nonNull(o) ? quote(o.toString()) : "null"; } public QueryParameterBuilder withValue() { - return new QueryParameterBuilder(schema, vPrefix, null, null, views); + return new QueryParameterBuilder(schema, vPrefix, null, null, views, index); } public QueryParameterBuilder subQuery() { - return new QueryParameterBuilder(schema, isNull(vPrefix) ? null : vPrefix + "_s", args, argTypes, new LinkedList<>()); + return new QueryParameterBuilder(schema, isNull(vPrefix) ? null : vPrefix + "_s", args, argTypes, new ArrayList<>(), index); } public static QueryParameterBuilder addWithValue() { - return new QueryParameterBuilder(null, null, null, null, null); //no args + return new QueryParameterBuilder(null, null, null, null, null, null); //no args } public static QueryParameterBuilder parametrized(List views) { @@ -137,6 +149,6 @@ public static QueryParameterBuilder parametrized(List views) { } public static QueryParameterBuilder parametrized(String schema, List views) { - return new QueryParameterBuilder(schema, "v", new LinkedList<>(), new LinkedList<>(), views); + return new QueryParameterBuilder(schema, "v", new ArrayList<>(), new ArrayList<>(), views, null); } } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 7652e307..33a91739 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -17,6 +17,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.stream.Stream; import lombok.Getter; @@ -76,7 +77,6 @@ public RequestQueryBuilder fetch(Integer fetch) { return this; } - public RequestQueryBuilder offset(Integer offset) { this.offset = offset; return this; @@ -87,6 +87,10 @@ public RequestQueryBuilder repeat(@NonNull Iterator it) { return this; } + public Optional getColumn(String id){ + return columns.stream().filter(c-> c.tagname().contains(id)).findAny(); + } + public ViewQuery as(String tag) { return new ViewQuery(tag, this); } @@ -102,7 +106,6 @@ public RequestQuery build(String schema) { var bg = currentTimeMillis(); var pb = parametrized(schema, views); var sb = new SqlStringBuilder(1000); //avg -// pb.tables(tables.stream().map(TaggableView::tagname).toArray(String[]::new)); if(isNull(it)) { build(sb, pb); } @@ -114,15 +117,19 @@ public RequestQuery build(String schema) { } public final void build(SqlStringBuilder sb, QueryParameterBuilder pb){ + views.forEach(pb::view); + select(sb, pb); + var queryIdx = sb.sb.length(); + var argsIdx = pb.argCount(); where(sb, pb); groupBy(sb); having(sb, pb); orderBy(sb, pb); fetch(sb); - sb.sb.insert(0, select(sb, pb)); //declare all view before FROM) + from(sb, pb, queryIdx, argsIdx); //declare all view before FROM) } - String select(SqlStringBuilder sb, QueryParameterBuilder pb){ + void select(SqlStringBuilder sb, QueryParameterBuilder pb){ if(currentDatabase() == TERADATA) { if(nonNull(offset)) { throw new UnsupportedOperationException(""); @@ -131,14 +138,20 @@ String select(SqlStringBuilder sb, QueryParameterBuilder pb){ throw new UnsupportedOperationException("Top N option is not supported with DISTINCT option."); } } - return new SqlStringBuilder(100).append("SELECT") - .appendIf(distinct, ()-> " DISTINCT") - .appendIf(nonNull(fetch), ()-> " TOP " + fetch) + sb.append("SELECT") + .appendIf(distinct, " DISTINCT") + .appendIf(nonNull(fetch), ()-> " TOP " + fetch) //??????? .append(SPACE) - .appendEach(columns, SCOMA, o-> o.sqlWithTag(pb)) - .appendIf(!pb.views().isEmpty(), " FROM ") //TODO finish this - .appendEach(pb.views(), SCOMA, o-> o.sqlWithTag(pb)) - .toString(); + .appendEach(columns, SCOMA, o-> o.sqlWithTag(pb)); + } + + void from(SqlStringBuilder sb, QueryParameterBuilder pb, int queryIdx, int argsIdx) { + if(!pb.views().isEmpty()) { + sb.setOffset(queryIdx); + pb.setIndex(argsIdx); + sb.append(" FROM ") + .appendEach(pb.views(), SCOMA, o-> o.sqlWithTag(pb)); + } } void where(SqlStringBuilder sb, QueryParameterBuilder pb){ @@ -203,4 +216,9 @@ public boolean isAggregation() { private static boolean groupable(DBColumn column) { return !column.isAggregation() && !column.isConstant(); } + + @Override + public String toString() { + return this.build().getQuery(); + } } diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index 23f7fe6c..94bd98d6 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -9,11 +9,14 @@ import java.util.function.Function; import java.util.function.Supplier; +import lombok.Setter; + /** * * @author u$f * */ +@Setter public final class SqlStringBuilder { static final String EMPTY = ""; @@ -24,7 +27,8 @@ public final class SqlStringBuilder { static final String SCOMA = COMA + SPACE; final StringBuilder sb; - + Integer offset; + public SqlStringBuilder(int capacity) { this.sb = new StringBuilder(capacity); } @@ -56,10 +60,10 @@ public SqlStringBuilder appendEach(Collection list, String separator, Fun public SqlStringBuilder appendEach(Collection list, String separator, String prefix, Function fn) { if(!list.isEmpty()) { var it = list.iterator(); - this.sb.append(prefix).append(fn.apply(it.next())); + append(prefix).append(fn.apply(it.next())); var before = separator + prefix; while(it.hasNext()) { - this.sb.append(before).append(fn.apply(it.next())); + append(before).append(fn.apply(it.next())); } } return this; @@ -69,7 +73,7 @@ public SqlStringBuilder forEach(Iterator it, String separator, Consumer SqlStringBuilder forEach(Iterator it, String separator, Consumer columns() { + return query.getColumns(); } @Override diff --git a/src/main/java/org/usf/jquery/core/WhenExpression.java b/src/main/java/org/usf/jquery/core/WhenExpression.java index ba6e4de4..69442218 100644 --- a/src/main/java/org/usf/jquery/core/WhenExpression.java +++ b/src/main/java/org/usf/jquery/core/WhenExpression.java @@ -3,6 +3,8 @@ import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.Validation.requireNoArgs; +import org.usf.jquery.core.JavaType.Typed; + import lombok.RequiredArgsConstructor; /** @@ -11,7 +13,7 @@ * */ @RequiredArgsConstructor -final class WhenExpression implements DBExpression { +final class WhenExpression implements DBExpression, Typed { private final DBFilter filter; private final Object value; @@ -31,6 +33,11 @@ public String sql(QueryParameterBuilder arg) { .append(" THEN "); return sb.append(arg.appendLitteral(value)).toString(); } + + @Override + public JavaType getType() { + return JDBCType.typeOf(value).orElse(null); + } @Override public String toString() { diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 0459a7e0..3c6d26d2 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -49,10 +49,10 @@ public class ArgumentParsers { public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType... types) { if(isEmpty(types) || Stream.of(types).allMatch(o-> o.getClass() == JDBCType.class)) { - return parseJdbc(entry, td, cast(types, JDBCType[].class, JDBCType[]::new)); + return parseJdbc(entry, td, castArray(types, JDBCType[].class, JDBCType[]::new)); } if(Stream.of(types).allMatch(o-> o.getClass() == JQueryType.class)) { - return parseJQuery(entry, td, cast(types, JQueryType[].class, JQueryType[]::new)); + return parseJQuery(entry, td, castArray(types, JQueryType[].class, JQueryType[]::new)); } throw new UnsupportedOperationException("unsupported types " + Arrays.toString(types)); } @@ -138,7 +138,7 @@ private static Object matchTypes(Typed o, JDBCType... types) { throw badArgumentTypeException(types, o); } - private static T[] cast(JavaType[] types, Class c, IntFunction fn) { + private static T[] castArray(JavaType[] types, Class c, IntFunction fn) { return types.getClass() == c ? c.cast(types) : Stream.of(types).toArray(fn); diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 7a347b5d..c63b5e14 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -1,9 +1,14 @@ package org.usf.jquery.web; +import static java.util.Objects.nonNull; import static org.usf.jquery.web.ArgumentParsers.jdbcArgParser; +import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.JDBCType; +import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.Validation; +import org.usf.jquery.core.ViewColumn; /** * @@ -20,6 +25,16 @@ default String reference() { //JSON return identity(); } + default TaggableColumn from(TableDecorator td) { + var b = builder(); + return nonNull(b) + ? b.build(td).as(reference()) + : td.columnName(this) //recursive call + .map(Validation::requireLegalVariable) + .map(cn-> new ViewColumn(td.table(), cn, reference(), dataType(td))) + .orElseThrow(()-> undeclaredResouceException(td.identity(), identity())); + } + default JDBCArgumentParser parser(TableDecorator td){ // override parser | format | local return jdbcArgParser(dataType(td)); } @@ -27,10 +42,10 @@ default JDBCArgumentParser parser(TableDecorator td){ // override parser | forma default JDBCType dataType(TableDecorator td) { // only if !builder return td.metadata().columnMetada(this) .map(ColumnMetadata::getDataType) - .orElse(null); + .orElse(null); } - default ColumnBuilder builder() { + default ColumnBuilder builder() { //set type if null return null; // no builder by default } diff --git a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java index c50c4270..72c4bffd 100644 --- a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java +++ b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java @@ -1,6 +1,6 @@ package org.usf.jquery.web; -import org.usf.jquery.core.DBView; +import org.usf.jquery.core.DBQuery; import org.usf.jquery.core.ViewQuery; import lombok.AccessLevel; @@ -13,7 +13,7 @@ * */ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) -final class CompletableViewQuery implements DBView { +final class CompletableViewQuery implements DBQuery { @Delegate private final ViewQuery query; diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java new file mode 100644 index 00000000..36520912 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -0,0 +1,51 @@ +package org.usf.jquery.web; + +import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; +import static org.usf.jquery.web.ColumnDecorator.ofColumn; + +import java.util.Optional; + +import org.usf.jquery.core.DBQuery; +import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.ViewColumn; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +final class QueryDecorator implements TableDecorator { + + private final DBQuery query; + + @Override + public String tableName() { + return null; + } + + @Override + public String identity() { + return query.id(); + } + + @Override + public Optional columnName(ColumnDecorator cd) { + return column(cd.identity()).map(TaggableColumn::tagname); + } + + public Optional lookupColumnDecorator(String cn) { + return column(cn) + .map(c-> ofColumn(cn, td-> new ViewColumn(table(), doubleQuote(c.tagname()), c.tagname(), c.getType()))); + } + + private final Optional column(String cn){ + return query.columns().stream() + .filter(c-> c.tagname().equals(cn)) + .findAny(); + } + + @Override + public ViewBuilder builder() { + return v-> query; + } +} diff --git a/src/main/java/org/usf/jquery/web/RequestContext.java b/src/main/java/org/usf/jquery/web/RequestContext.java index ebfa3081..cb707e74 100644 --- a/src/main/java/org/usf/jquery/web/RequestContext.java +++ b/src/main/java/org/usf/jquery/web/RequestContext.java @@ -4,12 +4,14 @@ import static java.util.Optional.ofNullable; import static org.usf.jquery.web.JQueryContext.context; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; -import org.usf.jquery.core.DBView; +import org.usf.jquery.core.DBQuery; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -21,33 +23,45 @@ public final class RequestContext { private static final ThreadLocal local = new ThreadLocal<>(); - private final Map tdMap; - private final Map columns; - private final Map views = new LinkedHashMap<>(); //work views + private final Map viewDecorators; + private final Map columnDecorators; + private final Map workQueries = new LinkedHashMap<>(); //work queries public Optional lookupViewDecorator(String id) { log.trace("lookup view decorator : {}", id); - return ofNullable(tdMap.get(id)); + return ofNullable(viewDecorators.get(id)); } public Optional lookupColumnDecorator(String id) { log.trace("lookup column decorator : {}", id); - return ofNullable(columns.get(id)); + return ofNullable(columnDecorators.get(id)); } - public DBView getView(String id) { - return views.get(id); + public Optional lookupView(String id) { + log.trace("lookup view : {}", id); + return ofNullable(workQueries.get(id)); } - public void putView(DBView v) { - views.put(v.id(), v); + public void putViewDecorator(TableDecorator v) { + if(!viewDecorators.containsKey(v.identity())) { + viewDecorators.put(v.identity(), v); + } + else { + throw new IllegalArgumentException(v.identity() + " already exist"); + } } - public DBView[] views() { - return views.values().toArray(DBView[]::new); + public void putWorkQuery(DBQuery v) { + workQueries.put(v.id(), v); } - - public static final RequestContext requestContext() { + + public Collection popQueries() { + var q = new ArrayList<>(workQueries.values()); + workQueries.clear(); + return q; + } + + public static final RequestContext currentContext() { var rc = local.get(); if(isNull(rc)) { var jc = context(); //can filter view & column @@ -56,6 +70,10 @@ public static final RequestContext requestContext() { } return rc; } + + public void clearWorkQueries() { + workQueries.clear(); + } public static final void clearContext() { local.remove(); diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index ecd7a173..364caace 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -26,7 +26,7 @@ import static org.usf.jquery.web.Constants.PARTITION; import static org.usf.jquery.web.Constants.SELECT; import static org.usf.jquery.web.ParseException.cannotParseException; -import static org.usf.jquery.web.RequestContext.requestContext; +import static org.usf.jquery.web.RequestContext.currentContext; import static org.usf.jquery.web.UnexpectedEntryException.unexpectedEntryException; import java.util.List; @@ -39,6 +39,7 @@ import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; import org.usf.jquery.core.DBOrder; +import org.usf.jquery.core.DBQuery; import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; import org.usf.jquery.core.LogicalOperator; @@ -55,6 +56,7 @@ import org.usf.jquery.core.WindowFunction; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; @@ -68,6 +70,7 @@ @Slf4j @Getter @Setter(value = AccessLevel.PACKAGE) +@AllArgsConstructor @RequiredArgsConstructor final class RequestEntryChain { @@ -80,8 +83,12 @@ final class RequestEntryChain { public RequestEntryChain(String value) { this(value, false); } - + public ViewQuery evalQuery(TableDecorator td) { + return evalQuery(td, false); + } + + public ViewQuery evalQuery(TableDecorator td, boolean requireTag) { if(SELECT.equals(value)) { var q = new RequestQueryBuilder().columns(toColumnArgs(td, false)); var e = this; @@ -96,7 +103,11 @@ public ViewQuery evalQuery(TableDecorator td) { default: throw unexpectedEntryException(e.toString(), DISTINCT, FILTER, ORDER, OFFSET, FETCH); } } - return q.as(e.tag); //TD require tag to register query + if(requireTag && isNull(e.tag)) { + throw new IllegalArgumentException("require tag"); + } + q.views(currentContext().popQueries().toArray(DBQuery[]::new)); + return q.as(e.tag); } throw cannotParseException(SELECT, this.toString()); } @@ -162,6 +173,7 @@ public DBFilter evalFilter(TableDecorator td, List values) { else { cmp = values.size() > 1 ? "in" : "eq"; //query ?? } + e = e.copy(); e.setNext(new RequestEntryChain(cmp)); // do not set args; } return e.next.updateArgs(values) @@ -176,7 +188,7 @@ public DBFilter evalFilter(TableDecorator td, List values) { Optional tableCriteria(TableDecorator td, List values) { RequestEntryChain e = null; CriteriaBuilder c = null; - var res = requestContext().lookupViewDecorator(value); + var res = currentContext().lookupViewDecorator(value); if(res.isPresent() && next()) { c = res.get().criteria(next.value); e = next; // only if nonNull @@ -195,15 +207,17 @@ Optional tableCriteria(TableDecorator td, List valu //column.eq=v1 private RequestEntryChain updateArgs(List values) { + var e = this; if(!isEmpty(values)) { if(isLast() && isNull(args)) { - setArgs(values); + e = copy(); + e.setArgs(values); } else { throw new UnexpectedEntryException(this + "=" + Utils.toString(values.toArray())); } } - return this; + return e; } DBFilter chainComparator(TableDecorator td, ColumnDecorator cd, DBColumn col){ @@ -253,20 +267,20 @@ private ResourceCursor chainColumnOperations(TableDecorator td, boolean filter) } private static DBColumn windowColumn(TableDecorator td, TaggableColumn column) { - var vw = requestContext().getView(td.identity()); + var vw = currentContext().lookupView(td.identity()).orElse(null); if(vw instanceof CompletableViewQuery) { // already create ((CompletableViewQuery)vw).getQuery().columns(column); } else { vw = new CompletableViewQuery((isNull(vw) ? td.table() : vw).window(td.identity(), column)); - requestContext().putView(vw); // same name + currentContext().putWorkQuery(vw); // same name } return new ViewColumn(vw, doubleQuote(column.tagname()), null, column.getType()); } private Optional lookupResource(TableDecorator td) { if(next()) { //check td.cd first - var rc = requestContext().lookupViewDecorator(value) + var rc = currentContext().lookupViewDecorator(value) .flatMap(v-> next.lookupViewResource(v, RequestEntryChain::isWindowFunction)); if(rc.isPresent()) { requireNoArgs(); // noArgs on valid resource @@ -277,7 +291,9 @@ private Optional lookupResource(TableDecorator td) { } private Optional lookupViewResource(TableDecorator td, Predicate pre) { - var res = requestContext().lookupColumnDecorator(value); + var res = td.getClass() == QueryDecorator.class + ? ((QueryDecorator)td).lookupColumnDecorator(value) + : currentContext().lookupColumnDecorator(value); if(res.isPresent()) { requireNoArgs(); } @@ -286,7 +302,7 @@ private Optional lookupViewResource(TableDecorator td, Predicate } return res.map(cd-> new ResourceCursor(td, cd, this)); } - + private Optional toOperation(TableDecorator td, DBColumn col, Predicate pre) { return lookupOperator(value).filter(pre).map(fn-> { var c = col; @@ -320,7 +336,7 @@ private Object[] toArgs(TableDecorator td, JavaType type, boolean allowEmpty) { : ofParameters(required(type), varargs(type)); return toArgs(td, null, ps, s-> (Object[]) newInstance(c, s)); } - throw new UnsupportedOperationException("cannot create array of " + c); + throw new UnsupportedOperationException("cannot instanitate type " + c); } private Object toOneArg(TableDecorator td, JavaType type) { @@ -390,8 +406,12 @@ public String toString() { return isNull(tag) ? s : s + ":" + tag; } + protected RequestEntryChain copy() { + return new RequestEntryChain(value, text, next, args, tag); + } + private static boolean isWindowFunction(TypedOperator op) { - return op.unwrap().getClass() == WindowFunction.class; // !instanceOf : only window function + return op.unwrap() instanceof WindowFunction; // WindowFunction + COUNT } private static String[] toStringArray(List entries) { @@ -400,7 +420,6 @@ private static String[] toStringArray(List entries) { .toArray(String[]::new); } - @RequiredArgsConstructor static final class ResourceCursor { private final TableDecorator td; @@ -414,7 +433,7 @@ public ResourceCursor(TableDecorator td, ColumnDecorator cd, RequestEntryChain e this.entry = entry; this.col = td.column(cd); } - + @Override public String toString() { return td + "." + cd + " => " + entry.toString(); diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index f105558a..766509ff 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -5,17 +5,17 @@ import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; import static org.usf.jquery.web.Constants.FETCH; import static org.usf.jquery.web.Constants.OFFSET; import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.RESERVED_WORDS; +import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.JQueryContext.database; import static org.usf.jquery.web.MissingParameterException.missingParameterException; -import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; -import static org.usf.jquery.web.RequestContext.requestContext; +import static org.usf.jquery.web.RequestContext.clearContext; +import static org.usf.jquery.web.RequestContext.currentContext; import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.RequestParser.parseEntry; @@ -28,11 +28,11 @@ import java.util.stream.Stream; import org.usf.jquery.core.DBFilter; +import org.usf.jquery.core.DBQuery; import org.usf.jquery.core.DBTable; import org.usf.jquery.core.DBView; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; -import org.usf.jquery.core.ViewColumn; /** * @@ -55,12 +55,7 @@ default DBView table() { //optim } default TaggableColumn column(ColumnDecorator cd) { - var b = cd.builder(); - if(nonNull(b)) { - return b.build(this).as(cd.reference()); - } - var cn = columnName(cd).orElseThrow(()-> undeclaredResouceException(identity(), cd.identity())); - return new ViewColumn(table(), requireLegalVariable(cn), cd.reference(), cd.dataType(this)); + return cd.from(this); } default ViewBuilder builder() { @@ -72,23 +67,28 @@ default CriteriaBuilder criteria(String name) { //!aggregation } default RequestQueryBuilder query(Map parameterMap) { - currentDatabase(database().getType()); //table database - var query = new RequestQueryBuilder(); - parseViews(query, parameterMap); - parseColumns(query, parameterMap); - parseFilters(query, parameterMap); - parseOrders (query, parameterMap); - parseFetch(query, parameterMap); - - return query.views(requestContext().views()); + try { + currentDatabase(database().getType()); //table database + var query = new RequestQueryBuilder(); + parseViews(query, parameterMap); + parseColumns(query, parameterMap); + parseFilters(query, parameterMap); + parseOrders (query, parameterMap); + parseFetch(query, parameterMap); + query.views(currentContext().popQueries().toArray(DBQuery[]::new)); + return query; + } + finally { + clearContext(); + } } default void parseViews(RequestQueryBuilder query, Map parameters) { -// if(parameters.containsKey(VIEW)) { -// Stream.of(parameters.get(VIEW)) -// .flatMap(c-> parseEntries(c).stream()) -// .forEach(e-> requestContext().putViewDecorator(e.evalView(this))); //declare only -// } + if(parameters.containsKey(VIEW)) { + Stream.of(parameters.get(VIEW)) + .flatMap(c-> parseEntries(c).stream()) + .forEach(e-> currentContext().putViewDecorator(new QueryDecorator(e.evalQuery(this, true)))); + } } default void parseColumns(RequestQueryBuilder query, Map parameters) { diff --git a/src/main/java/org/usf/jquery/web/TableMetadata.java b/src/main/java/org/usf/jquery/web/TableMetadata.java index ed1b494f..2ae9f2a7 100644 --- a/src/main/java/org/usf/jquery/web/TableMetadata.java +++ b/src/main/java/org/usf/jquery/web/TableMetadata.java @@ -1,10 +1,9 @@ package org.usf.jquery.web; import static java.lang.String.join; -import static java.util.Collections.emptyMap; +import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableMap; import static java.util.Objects.nonNull; -import static java.util.Optional.empty; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; import static org.usf.jquery.core.JDBCType.OTHER; @@ -44,9 +43,9 @@ public class TableMetadata { private Instant lastUpdate; public Optional columnMetada(ColumnDecorator cd) { - return columns.containsKey(cd.identity()) ? Optional.of(columns.get(cd.identity())) : empty(); + return Optional.ofNullable(columns.get(cd.identity())); } - + public void fetch() throws SQLException { //individually table fetching try(var cn = database().getDataSource().getConnection()) { fetch(cn.getMetaData()); @@ -72,14 +71,14 @@ void fetch(DatabaseMetaData metadata) throws SQLException { throw new NoSuchElementException("column(s) [" + join(", ", dbMap.keySet()) + "] not found in " + tablename); } } + + static TableMetadata emptyMetadata(TableDecorator table) { + return tableMetadata(table, emptyList()); + } static TableMetadata tableMetadata(TableDecorator table, Collection columns) { return new TableMetadata(table.tableName(), declaredColumns(table, columns)); } - - static TableMetadata emptyMetadata(TableDecorator table) { - return new TableMetadata(table.tableName(), emptyMap()); - } static Map declaredColumns(TableDecorator table, Collection columns){ return unmodifiableMap(columns.stream().reduce(new LinkedHashMap(), (m, cd)-> { From ffa02fc0ee8fd0c3427f4652d4b511d0d69af55b Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 6 Mar 2024 21:27:15 +0100 Subject: [PATCH 086/298] edit --- .../core/{NestedSql.java => Aggregable.java} | 5 ++- .../usf/jquery/core/AggregateFunction.java | 4 --- .../usf/jquery/core/ColumnFilterGroup.java | 3 +- .../usf/jquery/core/ColumnSingleFilter.java | 3 +- .../usf/jquery/core/ComparisonExpression.java | 2 +- .../core/ComparisonExpressionGroup.java | 3 +- .../core/ComparisonSingleExpression.java | 2 +- .../java/org/usf/jquery/core/DBColumn.java | 25 +++++++------- .../java/org/usf/jquery/core/DBFilter.java | 2 +- .../java/org/usf/jquery/core/DBOrder.java | 2 ++ .../java/org/usf/jquery/core/Groupable.java | 14 ++++++++ .../org/usf/jquery/core/OperationColumn.java | 33 +++++++++---------- .../java/org/usf/jquery/core/Operator.java | 9 ++--- .../java/org/usf/jquery/core/Partition.java | 17 +++++++++- .../usf/jquery/core/RequestQueryBuilder.java | 16 ++++----- .../org/usf/jquery/core/WhenExpression.java | 3 +- 16 files changed, 79 insertions(+), 64 deletions(-) rename src/main/java/org/usf/jquery/core/{NestedSql.java => Aggregable.java} (62%) create mode 100644 src/main/java/org/usf/jquery/core/Groupable.java diff --git a/src/main/java/org/usf/jquery/core/NestedSql.java b/src/main/java/org/usf/jquery/core/Aggregable.java similarity index 62% rename from src/main/java/org/usf/jquery/core/NestedSql.java rename to src/main/java/org/usf/jquery/core/Aggregable.java index 61544544..545bf8fd 100644 --- a/src/main/java/org/usf/jquery/core/NestedSql.java +++ b/src/main/java/org/usf/jquery/core/Aggregable.java @@ -5,14 +5,13 @@ * @author u$f * */ -public interface NestedSql { +public interface Aggregable { default boolean isAggregation() { return false; } static boolean aggregation(Object o) { - return o instanceof NestedSql && ((NestedSql)o).isAggregation(); + return o instanceof Aggregable && ((Aggregable)o).isAggregation(); } - } diff --git a/src/main/java/org/usf/jquery/core/AggregateFunction.java b/src/main/java/org/usf/jquery/core/AggregateFunction.java index cbe7b1a8..ff447c4d 100644 --- a/src/main/java/org/usf/jquery/core/AggregateFunction.java +++ b/src/main/java/org/usf/jquery/core/AggregateFunction.java @@ -8,8 +8,4 @@ @FunctionalInterface public interface AggregateFunction extends WindowFunction { - @Override - default boolean isAggregation() { - return true; - } } diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index ce4e8424..cde87a53 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -35,8 +35,7 @@ public String sql(QueryParameterBuilder builder) { @Override public boolean isAggregation() { - return filters.stream() - .anyMatch(NestedSql::isAggregation); + return filters.stream().anyMatch(DBFilter::isAggregation); } @Override diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index b88b2b69..3edf33c5 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -2,7 +2,6 @@ import static org.usf.jquery.core.LogicalOperator.AND; import static org.usf.jquery.core.LogicalOperator.OR; -import static org.usf.jquery.core.NestedSql.aggregation; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import lombok.AccessLevel; @@ -29,7 +28,7 @@ public String sql(QueryParameterBuilder ph) { @Override public boolean isAggregation() { - return column.isAggregation() || aggregation(expression); + return column.isAggregation() || expression.isAggregation(); } @Override diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpression.java b/src/main/java/org/usf/jquery/core/ComparisonExpression.java index 2b000e85..2d003c3a 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpression.java @@ -9,7 +9,7 @@ * @author u$f * */ -public interface ComparisonExpression extends DBExpression, NestedSql, Chainable { +public interface ComparisonExpression extends DBExpression, Aggregable, Chainable { @Override default String sql(QueryParameterBuilder builder, Object[] args) { diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index 1022f412..33f4d39e 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -36,8 +36,7 @@ public String sql(QueryParameterBuilder builder, Object operand) { @Override public boolean isAggregation() { - return expressions.stream() - .allMatch(NestedSql::aggregation); + return expressions.stream().anyMatch(ComparisonExpression::isAggregation); } @Override diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index d5b38af5..d133d720 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -2,7 +2,7 @@ import static java.util.Objects.nonNull; import static org.usf.jquery.core.DBColumn.column; -import static org.usf.jquery.core.NestedSql.aggregation; +import static org.usf.jquery.core.Aggregable.aggregation; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import java.util.LinkedList; diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 5c5af726..e3a9339f 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -7,6 +7,7 @@ import java.util.Objects; import java.util.function.Supplier; +import java.util.stream.Stream; import org.usf.jquery.core.CaseSingleColumnBuilder.WhenFilterBridge; import org.usf.jquery.core.JavaType.Typed; @@ -19,7 +20,7 @@ * */ @FunctionalInterface -public interface DBColumn extends DBObject, Typed, NestedSql { +public interface DBColumn extends DBObject, Typed, Groupable { @Override default String sql(QueryParameterBuilder builder, Object[] args) { @@ -33,9 +34,10 @@ default String sql(QueryParameterBuilder builder, Object[] args) { default boolean isAggregation() { return false; } - - default boolean isConstant() { - return false; + + @Override + default Stream groupKeys() { + return Stream.of(this); } default JavaType getType() { @@ -164,16 +166,17 @@ public String sql(QueryParameterBuilder arg) { } @Override - public boolean isConstant() { - return true; + public boolean isAggregation() { + return false; + } + + @Override + public Stream groupKeys() { + return Stream.empty(); } }; } - - static boolean isColumnConstant(Object o) { - return !(o instanceof DBColumn) || ((DBColumn)o).isConstant(); - } - + static OperationColumn count() { return count(column("*")); } diff --git a/src/main/java/org/usf/jquery/core/DBFilter.java b/src/main/java/org/usf/jquery/core/DBFilter.java index 63a5718c..71ba6dc6 100644 --- a/src/main/java/org/usf/jquery/core/DBFilter.java +++ b/src/main/java/org/usf/jquery/core/DBFilter.java @@ -8,7 +8,7 @@ * */ @FunctionalInterface -public interface DBFilter extends DBObject, NestedSql, Chainable { +public interface DBFilter extends DBObject, Aggregable, Chainable { String sql(QueryParameterBuilder builder); diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index c62caaa7..5a897e5e 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -6,6 +6,7 @@ import static org.usf.jquery.core.Validation.requireNoArgs; import lombok.AccessLevel; +import lombok.Getter; import lombok.RequiredArgsConstructor; /** @@ -13,6 +14,7 @@ * @author u$f * */ +@Getter(AccessLevel.PACKAGE) @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class DBOrder implements DBObject { diff --git a/src/main/java/org/usf/jquery/core/Groupable.java b/src/main/java/org/usf/jquery/core/Groupable.java new file mode 100644 index 00000000..2a13f703 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/Groupable.java @@ -0,0 +1,14 @@ +package org.usf.jquery.core; + +import java.util.stream.Stream; + +/** + * + * @author u$f + * + */ +public interface Groupable extends Aggregable { + + Stream groupKeys(); + +} diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 5ce173e4..41e35aea 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -2,7 +2,6 @@ import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; -import java.util.Objects; import java.util.stream.Stream; import lombok.AccessLevel; @@ -21,7 +20,6 @@ public final class OperationColumn implements DBColumn { private final Operator operator; private final Object[] args; private final JavaType type; - private Boolean aggregation; public OperationColumn(Operator operation, Object[] args) { this(operation, args, null); @@ -31,7 +29,7 @@ public OperationColumn(Operator operation, Object[] args) { public String sql(QueryParameterBuilder builder) { return operator.sql(builder, args); } - + @Override public JavaType getType() { return type; @@ -39,27 +37,26 @@ public JavaType getType() { @Override public boolean isAggregation() { - if(Objects.isNull(aggregation)) { - return operator.isAggregation() - || Stream.of(args).anyMatch(NestedSql::aggregation); - } - return aggregation; + return operator instanceof AggregateFunction + || (!isOver() && Stream.of(args).anyMatch(Aggregable::aggregation)); //can do better } - - //see Operator::over - OperationColumn aggregation(boolean aggregation) { - this.aggregation = aggregation; - return this; - } - + @Override - public boolean isConstant() { - return Stream.of(args).allMatch(DBColumn::isColumnConstant); + public Stream groupKeys() { + if(isOver()) { + return ((Partition)args[1]).groupKeys(); + } + return operator instanceof AggregateFunction || operator instanceof ConstantOperator + ? Stream.empty() + : DBColumn.super.groupKeys(); + } + + private boolean isOver() { + return "OVER".equals(operator.id()); } @Override public String toString() { return sql(addWithValue()); } - } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 64a198cc..6883f213 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -26,7 +26,7 @@ * @author u$f * */ -public interface Operator extends DBProcessor, NestedSql { +public interface Operator extends DBProcessor { String id(); @@ -276,12 +276,7 @@ static TypedOperator denseRank() { //pipe functions static TypedOperator over() { - return new TypedOperator(firstArgJdbcType(), pipe("OVER"), required(COLUMN), required(PARTITION)) { - @Override - public OperationColumn args(Object... args) { - return super.args(args).aggregation(false); //over aggregation functions - } - }; + return new TypedOperator(firstArgJdbcType(), pipe("OVER"), required(COLUMN), required(PARTITION)); } // constant operators diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index 88d3db3c..ae4e5c45 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -1,9 +1,12 @@ package org.usf.jquery.core; +import static java.util.stream.Stream.concat; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireNoArgs; +import java.util.stream.Stream; + import lombok.RequiredArgsConstructor; /** @@ -12,7 +15,7 @@ * */ @RequiredArgsConstructor -public final class Partition implements DBObject { +public final class Partition implements DBObject, Groupable { private final DBColumn[] columns; private DBOrder[] orders; @@ -42,4 +45,16 @@ String sql(QueryParameterBuilder builder) { } return sb.toString(); } + + @Override + public Stream groupKeys() { + Stream s = Stream.empty(); + if(!isEmpty(columns)) { + s = Stream.of(columns); + } + if(!isEmpty(orders)) { + s = concat(s, Stream.of(orders).map(DBOrder::getColumn)); + } + return s; + } } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 33a91739..64bda77f 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -122,7 +122,7 @@ public final void build(SqlStringBuilder sb, QueryParameterBuilder pb){ var queryIdx = sb.sb.length(); var argsIdx = pb.argCount(); where(sb, pb); - groupBy(sb); + groupBy(sb, pb); having(sb, pb); orderBy(sb, pb); fetch(sb); @@ -164,12 +164,12 @@ void where(SqlStringBuilder sb, QueryParameterBuilder pb){ } } - void groupBy(SqlStringBuilder sb){ - if(isAggregation()) { + void groupBy(SqlStringBuilder sb, QueryParameterBuilder pb){ + if(isAggregation()) { // check filter var expr = columns.stream() - .filter(RequestQueryBuilder::groupable) - .map(TaggableColumn::tagname) //add alias - .map(SqlStringBuilder::doubleQuote) //sql ?? + .filter(not(DBColumn::isAggregation)) + .flatMap(DBColumn::groupKeys) + .map(c-> columns.contains(c) ? ((TaggableColumn)c).tagname() : c.sql(pb)) //add alias .collect(joining(SCOMA)); if(!expr.isEmpty()) { sb.append(" GROUP BY ").append(expr); @@ -213,10 +213,6 @@ public boolean isAggregation() { filters.stream().anyMatch(DBFilter::isAggregation); } - private static boolean groupable(DBColumn column) { - return !column.isAggregation() && !column.isConstant(); - } - @Override public String toString() { return this.build().getQuery(); diff --git a/src/main/java/org/usf/jquery/core/WhenExpression.java b/src/main/java/org/usf/jquery/core/WhenExpression.java index 69442218..710d0e54 100644 --- a/src/main/java/org/usf/jquery/core/WhenExpression.java +++ b/src/main/java/org/usf/jquery/core/WhenExpression.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -36,7 +37,7 @@ public String sql(QueryParameterBuilder arg) { @Override public JavaType getType() { - return JDBCType.typeOf(value).orElse(null); + return typeOf(value).orElse(null); } @Override From 67e05e4d8194404671370950e716f5780be80b3c Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 7 Mar 2024 00:00:12 +0100 Subject: [PATCH 087/298] edit --- .../org/usf/jquery/core/OperationColumn.java | 4 +- .../usf/jquery/core/RequestQueryBuilder.java | 42 ++++++++++++------ .../java/org/usf/jquery/core/ViewJoin.java | 43 +++++++++++++++++++ 3 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/ViewJoin.java diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 41e35aea..dab11bf5 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -5,7 +5,6 @@ import java.util.stream.Stream; import lombok.AccessLevel; -import lombok.Getter; import lombok.RequiredArgsConstructor; /** @@ -13,7 +12,6 @@ * @author u$f * */ -@Getter(AccessLevel.PACKAGE) @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class OperationColumn implements DBColumn { @@ -51,7 +49,7 @@ public Stream groupKeys() { : DBColumn.super.groupKeys(); } - private boolean isOver() { + private boolean isOver() { //specific operator return "OVER".equals(operator.id()); } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 64bda77f..90d25bad 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -34,9 +34,10 @@ public class RequestQueryBuilder { private final List columns = new LinkedList<>(); - private final List filters = new LinkedList<>(); //WERE & HAVING + private final List filters = new LinkedList<>(); //WHERE & HAVING private final List views = new LinkedList<>(); private final List orders = new LinkedList<>(); + private final List joins = new LinkedList<>(); private Iterator it; private boolean distinct; private Integer fetch; @@ -67,6 +68,11 @@ public RequestQueryBuilder orders(@NonNull DBOrder... orders) { return this; } + public RequestQueryBuilder joins(@NonNull ViewJoin joins) { + Stream.of(joins).forEach(this.joins::add); + return this; + } + // the LIMIT clause is not in SQL standard. public RequestQueryBuilder fetch(Integer offset, Integer fetch) { return offset(offset).fetch(fetch); @@ -119,14 +125,17 @@ public RequestQuery build(String schema) { public final void build(SqlStringBuilder sb, QueryParameterBuilder pb){ views.forEach(pb::view); select(sb, pb); - var queryIdx = sb.sb.length(); + var queryIdx = sb.sb.length(); //pos mark var argsIdx = pb.argCount(); where(sb, pb); groupBy(sb, pb); having(sb, pb); orderBy(sb, pb); fetch(sb); - from(sb, pb, queryIdx, argsIdx); //declare all view before FROM) + sb.setOffset(queryIdx); + pb.setIndex(argsIdx); + from(sb, pb); //declare all view before FROM) + join(sb, pb); } void select(SqlStringBuilder sb, QueryParameterBuilder pb){ @@ -140,17 +149,27 @@ void select(SqlStringBuilder sb, QueryParameterBuilder pb){ } sb.append("SELECT") .appendIf(distinct, " DISTINCT") - .appendIf(nonNull(fetch), ()-> " TOP " + fetch) //??????? + .appendIf(nonNull(fetch) && currentDatabase() == TERADATA, ()-> " TOP " + fetch) //??????? .append(SPACE) .appendEach(columns, SCOMA, o-> o.sqlWithTag(pb)); } - void from(SqlStringBuilder sb, QueryParameterBuilder pb, int queryIdx, int argsIdx) { - if(!pb.views().isEmpty()) { - sb.setOffset(queryIdx); - pb.setIndex(argsIdx); + void from(SqlStringBuilder sb, QueryParameterBuilder pb) { + var vList = pb.views(); + if(!joins.isEmpty()) { + vList = vList.stream() + .filter(v-> joins.stream().noneMatch(j-> j.id().equals(v.id()))) + .collect(toList()); + } + if(!vList.isEmpty()) { sb.append(" FROM ") - .appendEach(pb.views(), SCOMA, o-> o.sqlWithTag(pb)); + .appendEach(vList, SCOMA, o-> o.sqlWithTag(pb)); + } + } + + void join(SqlStringBuilder sb, QueryParameterBuilder pb) { + if(!joins.isEmpty()) { + sb.append(SPACE).appendEach(joins, SPACE, v-> v.sql(pb)); } } @@ -165,7 +184,7 @@ void where(SqlStringBuilder sb, QueryParameterBuilder pb){ } void groupBy(SqlStringBuilder sb, QueryParameterBuilder pb){ - if(isAggregation()) { // check filter + if(isAggregation()) { // also check filter var expr = columns.stream() .filter(not(DBColumn::isAggregation)) .flatMap(DBColumn::groupKeys) @@ -174,9 +193,6 @@ void groupBy(SqlStringBuilder sb, QueryParameterBuilder pb){ if(!expr.isEmpty()) { sb.append(" GROUP BY ").append(expr); } - else if(columns.size() > 1) { - //throw new RuntimeException("require groupBy columns"); CONST !? - } } } diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java new file mode 100644 index 00000000..4e8a5702 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -0,0 +1,43 @@ +package org.usf.jquery.core; + +import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.LogicalOperator.AND; +import static org.usf.jquery.core.Validation.requireNoArgs; + +import java.util.Collection; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ViewJoin implements DBObject { + + private final DBView view; + private final JoinType joinType; + private final Collection filters; + + @Override + public String sql(QueryParameterBuilder builder, Object[] args) { + requireNoArgs(args, DBColumn.class::getSimpleName); + return sql(builder); + } + + public String sql(QueryParameterBuilder builder) { + return joinType + " JOIN " + view.sqlWithTag(builder) + + " ON " + filters.stream().map(f-> f.sql(builder)).collect(joining(AND.sql())); + } + + public String id() { + return view.id(); + } + + enum JoinType { + + INNER, LEFT, RIGHT, FULL { + @Override + public String toString() { + return name() + " OUTER"; + } + }; + } + +} From ec47fbfd1d92bffcb7ea40818f63be8196ca4045 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 6 Jun 2024 11:10:25 +0200 Subject: [PATCH 088/298] edit --- src/main/java/org/usf/jquery/core/DBView.java | 4 -- .../java/org/usf/jquery/core/JQueryType.java | 2 +- .../usf/jquery/core/RequestQueryBuilder.java | 4 +- src/main/java/org/usf/jquery/core/Utils.java | 2 + .../java/org/usf/jquery/core/ViewJoin.java | 49 ++++++++++++++----- .../java/org/usf/jquery/web/Constants.java | 1 + .../org/usf/jquery/web/QueryDecorator.java | 5 ++ .../org/usf/jquery/web/RequestContext.java | 4 -- .../org/usf/jquery/web/RequestEntryChain.java | 22 ++++++++- .../org/usf/jquery/web/TableDecorator.java | 1 - 10 files changed, 69 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 839c009b..14c53257 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.DBColumn.allColumns; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -29,7 +28,4 @@ default ViewQuery select(String tag, TaggableColumn... columns) { return new ViewQuery(tag, columns); } - default ViewQuery window(String tag, TaggableColumn column) { - return new ViewQuery(tag, allColumns(this).as(null), column); - } } diff --git a/src/main/java/org/usf/jquery/core/JQueryType.java b/src/main/java/org/usf/jquery/core/JQueryType.java index 51d4fa4c..5fca5202 100644 --- a/src/main/java/org/usf/jquery/core/JQueryType.java +++ b/src/main/java/org/usf/jquery/core/JQueryType.java @@ -10,12 +10,12 @@ @RequiredArgsConstructor public enum JQueryType implements JavaType { + VIEW(DBView.class), COLUMN(TaggableColumn.class), //TD DBColumn !? FILTER(DBFilter.class), ORDER(DBOrder.class), QUERY(ViewQuery.class), PARTITION(Partition.class); - //expression, WHEN_THEN, ... private final Class type; diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 90d25bad..54d8379d 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -225,8 +225,8 @@ void fetch(SqlStringBuilder sb) { } public boolean isAggregation() { - return columns.stream().anyMatch(DBColumn::isAggregation) || - filters.stream().anyMatch(DBFilter::isAggregation); + return columns.stream().anyMatch(Aggregable::isAggregation) || + filters.stream().anyMatch(Aggregable::isAggregation); } @Override diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index d73951f2..f3b444c2 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -49,4 +49,6 @@ public static Database currentDatabase() { public static void currentDatabase(Database db) { context.set(db); } + + } diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index 4e8a5702..a7f0d1f4 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -3,17 +3,21 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.LogicalOperator.AND; import static org.usf.jquery.core.Validation.requireNoArgs; +import static org.usf.jquery.core.ViewJoin.JoinType.FULL; +import static org.usf.jquery.core.ViewJoin.JoinType.INNER; +import static org.usf.jquery.core.ViewJoin.JoinType.LEFT; +import static org.usf.jquery.core.ViewJoin.JoinType.RIGHT; -import java.util.Collection; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class ViewJoin implements DBObject { - private final DBView view; private final JoinType joinType; - private final Collection filters; + private final DBView view; + private final DBFilter[] filters; @Override public String sql(QueryParameterBuilder builder, Object[] args) { @@ -22,22 +26,43 @@ public String sql(QueryParameterBuilder builder, Object[] args) { } public String sql(QueryParameterBuilder builder) { - return joinType + " JOIN " + view.sqlWithTag(builder) + - " ON " + filters.stream().map(f-> f.sql(builder)).collect(joining(AND.sql())); + return joinType + " JOIN " + view.sqlWithTag(builder) + " ON " + + Stream.of(filters).map(f-> f.sql(builder)).collect(joining(AND.sql())); } public String id() { return view.id(); } - enum JoinType { + public static ViewJoin innerJoin(DBView view, DBFilter... filters) { + return join(INNER, view, filters); + } + + public static ViewJoin leftJoin(DBView view, DBFilter... filters) { + return join(LEFT, view, filters); + } + + public static ViewJoin rightJoin(DBView view, DBFilter... filters) { + return join(RIGHT, view, filters); + } - INNER, LEFT, RIGHT, FULL { - @Override - public String toString() { - return name() + " OUTER"; - } - }; + public static ViewJoin fullJoin(DBView view, DBFilter... filters) { + return join(FULL, view, filters); + } + + public static ViewJoin join(JoinType jt, DBView view, DBFilter... filters) { + return new ViewJoin(jt, view, filters); } + + public enum JoinType { + INNER, LEFT, RIGHT, FULL; + + public static String pattern() { + return Stream.of(values()) + .map(JoinType::name) + .map(String::toLowerCase) + .collect(joining("|")); + } + } } diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index f5fcf959..712e8412 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -23,6 +23,7 @@ public final class Constants { public static final String ORDER = "order"; public static final String FETCH = "fetch"; public static final String OFFSET = "offset"; + public static final String JOIN = "join"; public static final String PARTITION = "partition"; public static final String REVISION = "revision"; //not standard public static final String REVISION_MODE = "revision.mode"; //not standard diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 36520912..eddf3287 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -12,6 +12,11 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; +/** + * + * @author u$f + * + */ @Getter @RequiredArgsConstructor final class QueryDecorator implements TableDecorator { diff --git a/src/main/java/org/usf/jquery/web/RequestContext.java b/src/main/java/org/usf/jquery/web/RequestContext.java index cb707e74..d3f9bf3c 100644 --- a/src/main/java/org/usf/jquery/web/RequestContext.java +++ b/src/main/java/org/usf/jquery/web/RequestContext.java @@ -70,10 +70,6 @@ public static final RequestContext currentContext() { } return rc; } - - public void clearWorkQueries() { - workQueries.clear(); - } public static final void clearContext() { local.remove(); diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 364caace..c04403f3 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -8,6 +8,7 @@ import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.Comparator.lookupComparator; +import static org.usf.jquery.core.DBColumn.allColumns; import static org.usf.jquery.core.JDBCType.INTEGER; import static org.usf.jquery.core.Operator.lookupOperator; import static org.usf.jquery.core.Parameter.required; @@ -15,12 +16,14 @@ import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.core.ViewJoin.join; import static org.usf.jquery.web.ArgumentParsers.parse; import static org.usf.jquery.web.ColumnDecorator.ofColumn; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.DISTINCT; import static org.usf.jquery.web.Constants.FETCH; import static org.usf.jquery.web.Constants.FILTER; +import static org.usf.jquery.web.Constants.JOIN; import static org.usf.jquery.web.Constants.OFFSET; import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.PARTITION; @@ -34,12 +37,15 @@ import java.util.function.IntFunction; import java.util.function.Predicate; +import javax.swing.text.View; + import org.usf.jquery.core.BadArgumentException; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; import org.usf.jquery.core.DBOrder; import org.usf.jquery.core.DBQuery; +import org.usf.jquery.core.DBView; import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; import org.usf.jquery.core.LogicalOperator; @@ -52,6 +58,8 @@ import org.usf.jquery.core.TypedOperator; import org.usf.jquery.core.Utils; import org.usf.jquery.core.ViewColumn; +import org.usf.jquery.core.ViewJoin; +import org.usf.jquery.core.ViewJoin.JoinType; import org.usf.jquery.core.ViewQuery; import org.usf.jquery.core.WindowFunction; @@ -88,6 +96,17 @@ public ViewQuery evalQuery(TableDecorator td) { return evalQuery(td, false); } + + public ViewJoin evalJoin(TableDecorator td) { + if(value.matches(JoinType.pattern())) { + var jt = JoinType.valueOf(value); + var args = toArgs(td, null, null); + return join(jt, (DBView)args[0], (DBFilter[])args[0]); + } + throw cannotParseException(JOIN, this.toString()); //TD + } + + public ViewQuery evalQuery(TableDecorator td, boolean requireTag) { if(SELECT.equals(value)) { var q = new RequestQueryBuilder().columns(toColumnArgs(td, false)); @@ -272,7 +291,8 @@ private static DBColumn windowColumn(TableDecorator td, TaggableColumn column) { ((CompletableViewQuery)vw).getQuery().columns(column); } else { - vw = new CompletableViewQuery((isNull(vw) ? td.table() : vw).window(td.identity(), column)); + var view = isNull(vw) ? td.table() : vw; + vw = new CompletableViewQuery(view.select(td.identity(), allColumns(view).as(null), column)); currentContext().putWorkQuery(vw); // same name } return new ViewColumn(vw, doubleQuote(column.tagname()), null, column.getType()); diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index 766509ff..b23719ee 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -159,5 +159,4 @@ default TableMetadata createMetadata(Collection columns) { static Stream flatParameters(String... arr) { //number local separator return Stream.of(arr).flatMap(v-> Stream.of(v.split(","))); } - } From 3c3ea01d306c9ce88e16815ce4a0770423123a87 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 19 Jun 2024 10:45:26 +0200 Subject: [PATCH 089/298] edit --- .../org/usf/jquery/core/ColumnFilterGroup.java | 14 +++++++++----- .../jquery/core/ComparisonExpressionGroup.java | 18 ++++++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index cde87a53..d099c5d8 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -4,11 +4,14 @@ import static java.util.stream.Collectors.toList; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.stream.Stream; +import lombok.AccessLevel; import lombok.NonNull; +import lombok.RequiredArgsConstructor; /** * @@ -16,6 +19,7 @@ * */ //@see ComparisonExpressionGroup +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class ColumnFilterGroup implements DBFilter { private final LogicalOperator operator; @@ -40,11 +44,11 @@ public boolean isAggregation() { @Override public DBFilter append(LogicalOperator op, DBFilter filter) { - if(operator == op) { - filters.add(filter); - return this; - } - return new ColumnFilterGroup(op, this, filter); + var gpe = operator == op + ? new ColumnFilterGroup(op, new ArrayList<>(filters)) + : new ColumnFilterGroup(op, this); + gpe.filters.add(filter); + return gpe; } @Override diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index 33f4d39e..0e888924 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -9,7 +9,9 @@ import java.util.Collection; import java.util.stream.Stream; +import lombok.AccessLevel; import lombok.NonNull; +import lombok.RequiredArgsConstructor; /** * @@ -17,16 +19,16 @@ * */ //@see ColumnFilterGroup +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class ComparisonExpressionGroup implements ComparisonExpression { private final LogicalOperator operator; private final Collection expressions; public ComparisonExpressionGroup(@NonNull LogicalOperator operator, ComparisonExpression... expressions) { - this.operator = operator; - this.expressions = expressions == null + this(operator, expressions == null ? new ArrayList<>() - : Stream.of(expressions).collect(toList()); + : Stream.of(expressions).collect(toList())); } @Override @@ -41,11 +43,11 @@ public boolean isAggregation() { @Override public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) { - if(operator == op) { - expressions.add(exp); - return this; - } - return new ComparisonExpressionGroup(op, this, exp); + var gpe = operator == op + ? new ComparisonExpressionGroup(op, new ArrayList<>(expressions)) + : new ComparisonExpressionGroup(op, this); + gpe.expressions.add(exp); + return gpe; } @Override From 2dd1042e3395a091fa44d254531eb1b441f3601e Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 2 Jul 2024 21:04:27 +0200 Subject: [PATCH 090/298] edit --- pom.xml | 6 +- .../java/org/usf/jquery/core/Aggregable.java | 2 +- .../usf/jquery/core/ArithmeticOperator.java | 2 +- .../org/usf/jquery/core/BasicComparator.java | 4 +- .../java/org/usf/jquery/core/CaseColumn.java | 2 +- .../org/usf/jquery/core/CastFunction.java | 13 ++-- .../java/org/usf/jquery/core/Comparator.java | 67 +++++++++++++------ .../org/usf/jquery/core/ConstantOperator.java | 3 +- .../java/org/usf/jquery/core/DBProcessor.java | 5 +- .../org/usf/jquery/core/ExtractFunction.java | 2 +- .../org/usf/jquery/core/FunctionOperator.java | 2 +- .../java/org/usf/jquery/core/InCompartor.java | 6 +- .../java/org/usf/jquery/core/JDBCType.java | 6 +- .../org/usf/jquery/core/NullComparator.java | 2 +- .../org/usf/jquery/core/ParameterSet.java | 2 +- .../java/org/usf/jquery/core/Partition.java | 4 +- .../org/usf/jquery/core/PipeFunction.java | 2 +- .../jquery/core/QueryParameterBuilder.java | 52 ++++++++------ .../org/usf/jquery/core/StringComparator.java | 7 +- .../org/usf/jquery/core/TypedComparator.java | 29 ++++---- .../org/usf/jquery/core/TypedOperator.java | 14 +--- .../java/org/usf/jquery/core/ViewJoin.java | 9 ++- .../org/usf/jquery/core/WhenExpression.java | 2 +- 23 files changed, 131 insertions(+), 112 deletions(-) diff --git a/pom.xml b/pom.xml index 5296837f..f84facae 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ - 11 + 17 5.8.1 UTF-8 UTF-8 @@ -39,13 +39,13 @@ org.projectlombok lombok - 1.18.22 + 1.18.32 provided org.slf4j slf4j-api - 1.7.25 + 1.7.36 provided diff --git a/src/main/java/org/usf/jquery/core/Aggregable.java b/src/main/java/org/usf/jquery/core/Aggregable.java index 545bf8fd..f0ff4ea1 100644 --- a/src/main/java/org/usf/jquery/core/Aggregable.java +++ b/src/main/java/org/usf/jquery/core/Aggregable.java @@ -12,6 +12,6 @@ default boolean isAggregation() { } static boolean aggregation(Object o) { - return o instanceof Aggregable && ((Aggregable)o).isAggregation(); + return o instanceof Aggregable agg && agg.isAggregation(); } } diff --git a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java index bfc49868..3a094e70 100644 --- a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java +++ b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java @@ -13,6 +13,6 @@ interface ArithmeticOperator extends Operator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, ArithmeticOperator.class::getSimpleName); - return builder.appendLitteral(args[0]) + id() + builder.appendLitteral(args[1]); + return builder.appendLiteral(args[0]) + id() + builder.appendLiteral(args[1]); } } diff --git a/src/main/java/org/usf/jquery/core/BasicComparator.java b/src/main/java/org/usf/jquery/core/BasicComparator.java index 024d112d..3c59402c 100644 --- a/src/main/java/org/usf/jquery/core/BasicComparator.java +++ b/src/main/java/org/usf/jquery/core/BasicComparator.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.Validation.requireNArgs; /** @@ -14,7 +13,6 @@ public interface BasicComparator extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, BasicComparator.class::getSimpleName); - var type = typeOf(args[1]).orElseThrow(Comparator::typeCannotBeNullException); // null 'cmp' null - return builder.appendLitteral(args[0]) + id() + builder.appendParameter(type, args[1]); + return builder.appendParameter(args[0]) + id() + builder.appendParameter(args[1]); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index ed9b4a8c..65bd7301 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -23,7 +23,7 @@ public final class CaseColumn implements DBColumn { // TD override isAggregation @Override public String sql(QueryParameterBuilder builder) { - var b = builder.withValue(); + var b = builder.withValue(); //force literal parameter return expressions.stream() //empty !? .map(o-> o.sql(b)) .collect(joining(SPACE, "CASE ", " END")); diff --git a/src/main/java/org/usf/jquery/core/CastFunction.java b/src/main/java/org/usf/jquery/core/CastFunction.java index 3ca361db..064292ca 100644 --- a/src/main/java/org/usf/jquery/core/CastFunction.java +++ b/src/main/java/org/usf/jquery/core/CastFunction.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.SqlStringBuilder.COMA; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; /** @@ -21,15 +20,13 @@ default String id() { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireAtLeastNArgs(1, args, ()-> id() + "_AS_" + asType()); - var sb = new SqlStringBuilder(id()).append("(") - .append(builder.appendLitteral(args[0])).append(" AS ").append(asType()); + var sb = new SqlStringBuilder(id()) + .append("(") + .append(builder.appendLiteral(args[0])).append(" AS ").append(asType()); if(args.length > 1) { sb.append("(") - .append(builder.appendLitteral(args[1])); - for(int i=2; i o + "%")); + return like(o-> o + "%"); } static TypedComparator endsLike() { - return like().argsMapper(wildcardSecondArg(o-> "%" + o)); + return like(o-> "%" + o); } static TypedComparator contentLike() { - return like().argsMapper(wildcardSecondArg(o-> "%" + o + "%")); + return like(o-> "%" + o + "%"); } static TypedComparator startsNotLike() { - return notLike().argsMapper(wildcardSecondArg(o-> o + "%")); + return notLike(o-> o + "%"); } static TypedComparator endsNotLike() { - return notLike().argsMapper(wildcardSecondArg(o-> "%" + o)); + return notLike(o-> "%" + o); } static TypedComparator contentNotLike() { - return notLike().argsMapper(wildcardSecondArg(o-> "%" + o + "%")); + return notLike(o-> "%" + o + "%"); } - + static TypedComparator like() { - return new TypedComparator(stringComparator("LIKE"), required(VARCHAR), required(VARCHAR)); + return like(null); } static TypedComparator iLike() { - return new TypedComparator(stringComparator("ILIKE"), required(VARCHAR), required(VARCHAR)); + return iLike(null); } - + static TypedComparator notLike() { - return new TypedComparator(stringComparator("NOT LIKE"), required(VARCHAR), required(VARCHAR)); + return notLike(null); } static TypedComparator notILike() { - return new TypedComparator(stringComparator("NOT ILIKE"), required(VARCHAR), required(VARCHAR)); + return notILike(null); + } + + static TypedComparator like(UnaryOperator wilcard) { + return new TypedComparator(stringComparator("LIKE", wilcard), required(VARCHAR), required(VARCHAR)); + } + + static TypedComparator iLike(UnaryOperator wilcard) { + return new TypedComparator(stringComparator("ILIKE", wilcard), required(VARCHAR), required(VARCHAR)); + } + + static TypedComparator notLike(UnaryOperator wilcard) { + return new TypedComparator(stringComparator("NOT LIKE", wilcard), required(VARCHAR), required(VARCHAR)); + } + + static TypedComparator notILike(UnaryOperator wilcard) { + return new TypedComparator(stringComparator("NOT ILIKE", wilcard), required(VARCHAR), required(VARCHAR)); } //null comparator @@ -123,8 +140,23 @@ static BasicComparator basicComparator(final String name) { return ()-> name; } - static StringComparator stringComparator(final String name) { - return ()-> name; + static StringComparator stringComparator(final String name, UnaryOperator wilcard) { + if(Objects.isNull(wilcard)) { + return ()-> name; + } + return new StringComparator() { + @Override + public String id() { + return name; + } + @Override + public Object wildcardArg(Object o) { + if(Objects.isNull(o) || o instanceof DBObject) { + throw new UnsupportedOperationException("cannot wildcards parameter: " + o); + } + return wilcard.apply(o); + } + }; } static NullComparator nullComparator(final String name) { @@ -139,13 +171,6 @@ static Optional lookupComparator(String op) { return DBProcessor.lookup(Comparator.class, TypedComparator.class, op); } - private static UnaryOperator wildcardSecondArg(UnaryOperator fn) { - return args-> { - args[1] = fn.apply(args[1]); - return args; - }; - } - static IllegalArgumentException typeCannotBeNullException() { return new IllegalArgumentException("type cannot be null"); } diff --git a/src/main/java/org/usf/jquery/core/ConstantOperator.java b/src/main/java/org/usf/jquery/core/ConstantOperator.java index 1a0f56a6..a10d49c4 100644 --- a/src/main/java/org/usf/jquery/core/ConstantOperator.java +++ b/src/main/java/org/usf/jquery/core/ConstantOperator.java @@ -13,7 +13,6 @@ public interface ConstantOperator extends Operator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNoArgs(args, ConstantOperator.class::getSimpleName); - return id(); + return id(); //use parentheses !? } - } diff --git a/src/main/java/org/usf/jquery/core/DBProcessor.java b/src/main/java/org/usf/jquery/core/DBProcessor.java index 121b7fd8..40730db7 100644 --- a/src/main/java/org/usf/jquery/core/DBProcessor.java +++ b/src/main/java/org/usf/jquery/core/DBProcessor.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static java.lang.reflect.Modifier.isPrivate; import static java.lang.reflect.Modifier.isStatic; import static java.util.Optional.empty; @@ -16,8 +17,8 @@ public interface DBProcessor extends DBObject { static Optional lookup(Class clazz, Class ext, String op) { try { - var m = clazz.getMethod(op); - if(m.getReturnType() == ext && isStatic(m.getModifiers()) && m.getParameterCount() == 0) { // no private static + var m = clazz.getMethod(op); //no parameter + if(m.getReturnType() == ext && isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) { return Optional.of(ext.cast(m.invoke(null))); } } catch (Exception e) {/* do not throw exception */} diff --git a/src/main/java/org/usf/jquery/core/ExtractFunction.java b/src/main/java/org/usf/jquery/core/ExtractFunction.java index 5b4d14d1..ec663845 100644 --- a/src/main/java/org/usf/jquery/core/ExtractFunction.java +++ b/src/main/java/org/usf/jquery/core/ExtractFunction.java @@ -20,6 +20,6 @@ default String id() { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(1, args, this::id); - return id() + "(" + field() + " FROM " + builder.appendLitteral(args[0]) + ")"; + return id() + "(" + field() + " FROM " + builder.appendLiteral(args[0]) + ")"; } } diff --git a/src/main/java/org/usf/jquery/core/FunctionOperator.java b/src/main/java/org/usf/jquery/core/FunctionOperator.java index 15e29cc5..d9cc99bc 100644 --- a/src/main/java/org/usf/jquery/core/FunctionOperator.java +++ b/src/main/java/org/usf/jquery/core/FunctionOperator.java @@ -12,7 +12,7 @@ public interface FunctionOperator extends Operator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { return new SqlStringBuilder(id()) - .append("(").append(builder.appendLitteralArray(args)).append(")")//accept any + .append("(").append(builder.appendLiteralArray(args)).append(")") //accept any .toString(); } } diff --git a/src/main/java/org/usf/jquery/core/InCompartor.java b/src/main/java/org/usf/jquery/core/InCompartor.java index eb1874ea..2bd325a8 100644 --- a/src/main/java/org/usf/jquery/core/InCompartor.java +++ b/src/main/java/org/usf/jquery/core/InCompartor.java @@ -1,7 +1,5 @@ package org.usf.jquery.core; -import static java.util.Arrays.copyOfRange; -import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.SqlStringBuilder.parenthese; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; @@ -17,8 +15,6 @@ public interface InCompartor extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireAtLeastNArgs(2, args, InCompartor.class::getSimpleName); - var type = typeOf(args[0]).orElseThrow(Comparator::typeCannotBeNullException); - var varg = copyOfRange(args, 1, args.length); - return builder.appendLitteral(args[0]) + SPACE + id() + parenthese(args.length == 2 && args[1] instanceof ViewQuery ? builder.appendLitteral(args[1]) : builder.appendArrayParameter(type, varg)); + return builder.appendParameter(args[0]) + SPACE + id() + parenthese(builder.appendArrayParameter(args, 1)); } } diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index 44021d8f..643fae03 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -96,9 +96,9 @@ private static boolean isString(Object o) { } public static Optional typeOf(Object o) { - if(o instanceof Typed) { - var t = ((Typed) o).getType(); - return t instanceof JDBCType ? Optional.of((JDBCType) t) : empty(); + if(o instanceof Typed to) { + var type = to.getType(); + return type instanceof JDBCType jType ? Optional.of(jType) : empty(); } return Optional.of(o).flatMap(v-> findType(e-> e.typeClass().isInstance(o))); } diff --git a/src/main/java/org/usf/jquery/core/NullComparator.java b/src/main/java/org/usf/jquery/core/NullComparator.java index bb7b8171..8b6b7555 100644 --- a/src/main/java/org/usf/jquery/core/NullComparator.java +++ b/src/main/java/org/usf/jquery/core/NullComparator.java @@ -14,6 +14,6 @@ public interface NullComparator extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(1, args, NullComparator.class::getSimpleName); - return builder.appendLitteral(args[0]) + SPACE + id(); + return builder.appendParameter(args[0]) + SPACE + id(); } } diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index 5707ab32..a7aadfef 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -29,7 +29,7 @@ public final class ParameterSet { //there is no Singleton implementation, dummy private final int nReqArgs; private final Parameter[] parameters; - public Object[] args(Object... args) { + public Object[] assertArguments(Object... args) { var arr = isNull(args) ? new Object[0] : args; try { forEach(arr.length, (p,i)-> { diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index ae4e5c45..2f86538e 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -37,11 +37,11 @@ public String sql(QueryParameterBuilder builder, Object[] args) { String sql(QueryParameterBuilder builder) { var sb = new SqlStringBuilder(100); if(!isEmpty(columns)) { - sb.append("PARTITION BY ").append(builder.appendLitteralArray(columns)); + sb.append("PARTITION BY ").append(builder.appendLiteralArray(columns)); } if(!isEmpty(orders)) { //require orders sb.appendIf(!isEmpty(columns), SPACE) - .append("ORDER BY ").append(builder.appendLitteralArray(orders)); + .append("ORDER BY ").append(builder.appendLiteralArray(orders)); } return sb.toString(); } diff --git a/src/main/java/org/usf/jquery/core/PipeFunction.java b/src/main/java/org/usf/jquery/core/PipeFunction.java index ad2918eb..463553a1 100644 --- a/src/main/java/org/usf/jquery/core/PipeFunction.java +++ b/src/main/java/org/usf/jquery/core/PipeFunction.java @@ -15,7 +15,7 @@ public interface PipeFunction extends FunctionOperator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireAtLeastNArgs(1, args, ()-> "Pipe function"); - return builder.appendLitteral(args[0]) + SPACE + return builder.appendLiteral(args[0]) + SPACE + FunctionOperator.super.sql(builder, copyOfRange(args, 1, args.length)); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 360a2b00..feab9184 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -3,6 +3,7 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.SqlStringBuilder.COMA; import static org.usf.jquery.core.SqlStringBuilder.EMPTY; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; @@ -16,7 +17,6 @@ import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.NonNull; import lombok.Setter; /** @@ -42,8 +42,8 @@ public List views(){ return views; } - public String view(@NonNull DBView view) { - if(isNull(vPrefix)) { + public String view(DBView view) { + if(isNull(vPrefix) || isNull(view)) { //view can be null return null; } for(var i=0; i=arr.length) { //throw !? return EMPTY; } - for(var o : arr){ - appendArg(type, o); + for(var i=from; i=arr.length ? EMPTY : Stream.of(arr) //throw !? + .map(this::appendLiteral) .collect(joining(SCOMA)); } - public String appendParameter(JDBCType type, Object o) { + public String appendParameter(Object o) { if(dynamic()) { - return o instanceof DBObject - ? ((DBObject)o).sql(this, null) - : appendArg(type, o); + if(o instanceof DBObject jo) { + return jo.sql(this, null); + } + var t = typeOf(o); //o=null=>empty + if(t.isPresent()) { + return appendArg(t.get(), o); + } } - return appendLitteral(o); + return appendLiteral(o); } - public String appendLitteral(Object o) { //TD : stringify value using db default pattern - return o instanceof DBObject - ? ((DBObject)o).sql(this, null) + public String appendLiteral(Object o) { //TD : stringify value using db default pattern + return o instanceof DBObject jo + ? jo.sql(this, null) : formatValue(o); } diff --git a/src/main/java/org/usf/jquery/core/StringComparator.java b/src/main/java/org/usf/jquery/core/StringComparator.java index 7538862a..20616352 100644 --- a/src/main/java/org/usf/jquery/core/StringComparator.java +++ b/src/main/java/org/usf/jquery/core/StringComparator.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.SqlStringBuilder.space; import static org.usf.jquery.core.Validation.requireNArgs; @@ -15,6 +14,10 @@ public interface StringComparator extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, StringComparator.class::getSimpleName); - return builder.appendLitteral(args[0]) + space(id()) + builder.appendParameter(VARCHAR, args[1]); + return builder.appendParameter(args[0]) + space(id()) + builder.appendParameter(wildcardArg(args[1])); + } + + default Object wildcardArg(Object o) { + return o; } } diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 75a43111..1a752495 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -1,12 +1,8 @@ package org.usf.jquery.core; -import static java.util.Objects.nonNull; import static org.usf.jquery.core.ParameterSet.ofParameters; -import java.util.function.UnaryOperator; - import lombok.Getter; -import lombok.experimental.Delegate; /** * @@ -16,10 +12,8 @@ @Getter public final class TypedComparator implements Comparator { - @Delegate private final Comparator comparator; private final ParameterSet parameterSet; - private UnaryOperator argMapper; public TypedComparator(Comparator comparator, Parameter... parameters) { this(comparator, ofParameters(parameters)); @@ -31,23 +25,24 @@ public TypedComparator(Comparator comparator, ParameterSet parameterSet) { } @Override - public DBFilter args(Object... args) { - args = parameterSet.args(args); - if(nonNull(argMapper)) { - args = argMapper.apply(args); - } - return comparator.args(args); + public String id() { + return comparator.id(); + } + + @Override + public String sql(QueryParameterBuilder builder, Object[] args) { + return comparator.sql(builder, parameterSet.assertArguments(args)); } + @Override + public DBFilter args(Object... args) { + return comparator.args(parameterSet.assertArguments(args)); + } + public Comparator unwrap() { return comparator; } - public TypedComparator argsMapper(UnaryOperator argMapper) { - this.argMapper = argMapper; - return this; - } - @Override public String toString() { return comparator.id() + parameterSet.toString(); diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index d7741382..1005658b 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -1,10 +1,7 @@ package org.usf.jquery.core; -import static java.util.Objects.nonNull; import static org.usf.jquery.core.ParameterSet.ofParameters; -import java.util.function.UnaryOperator; - import lombok.Getter; import lombok.experimental.Delegate; @@ -20,7 +17,6 @@ public class TypedOperator implements Operator { private final Operator operator; private final ArgTypeRef typeFn; private final ParameterSet parameterSet; - private UnaryOperator argMapper; public TypedOperator(JavaType type, Operator function, Parameter... args) { this(o-> type, function, args); @@ -34,10 +30,7 @@ public TypedOperator(ArgTypeRef typeFn, Operator function, Parameter... paramete @Override public OperationColumn args(Object... args) { - args = parameterSet.args(args); - if(nonNull(argMapper)) { - args = argMapper.apply(args); - } + args = parameterSet.assertArguments(args); return new OperationColumn(operator, args, typeFn.apply(args)); } @@ -45,11 +38,6 @@ public Operator unwrap() { return operator; } - public TypedOperator argsMapper(UnaryOperator argMapper) { - this.argMapper = argMapper; - return this; - } - @Override public String toString() { return operator.id() + parameterSet.toString(); diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index a7f0d1f4..fac12b1e 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -12,6 +12,11 @@ import lombok.RequiredArgsConstructor; +/** + * + * @author u$f + * + */ @RequiredArgsConstructor public class ViewJoin implements DBObject { @@ -21,12 +26,12 @@ public class ViewJoin implements DBObject { @Override public String sql(QueryParameterBuilder builder, Object[] args) { - requireNoArgs(args, DBColumn.class::getSimpleName); + requireNoArgs(args, ViewJoin.class::getSimpleName); return sql(builder); } public String sql(QueryParameterBuilder builder) { - return joinType + " JOIN " + view.sqlWithTag(builder) + " ON " + + return joinType + " JOIN " + view.sql(builder) + " ON " + Stream.of(filters).map(f-> f.sql(builder)).collect(joining(AND.sql())); } diff --git a/src/main/java/org/usf/jquery/core/WhenExpression.java b/src/main/java/org/usf/jquery/core/WhenExpression.java index 710d0e54..17c08660 100644 --- a/src/main/java/org/usf/jquery/core/WhenExpression.java +++ b/src/main/java/org/usf/jquery/core/WhenExpression.java @@ -32,7 +32,7 @@ public String sql(QueryParameterBuilder arg) { : sb.append("WHEN ") .append(filter.sql(arg)) .append(" THEN "); - return sb.append(arg.appendLitteral(value)).toString(); + return sb.append(arg.appendLiteral(value)).toString(); } @Override From c974ad388897fdf38d04801bbfeaf2eecc6622a4 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 2 Jul 2024 21:13:22 +0200 Subject: [PATCH 091/298] edit --- src/main/java/org/usf/jquery/core/TypedOperator.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 1005658b..2417b3ba 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -3,7 +3,6 @@ import static org.usf.jquery.core.ParameterSet.ofParameters; import lombok.Getter; -import lombok.experimental.Delegate; /** * @@ -13,7 +12,6 @@ @Getter public class TypedOperator implements Operator { - @Delegate private final Operator operator; private final ArgTypeRef typeFn; private final ParameterSet parameterSet; @@ -27,6 +25,15 @@ public TypedOperator(ArgTypeRef typeFn, Operator function, Parameter... paramete this.typeFn = typeFn; this.parameterSet = ofParameters(parameter); } + + @Override + public String id() { + return operator.id(); + } + @Override + public String sql(QueryParameterBuilder builder, Object[] args) { + return operator.sql(builder, parameterSet.assertArguments(args)); + } @Override public OperationColumn args(Object... args) { @@ -42,4 +49,5 @@ public Operator unwrap() { public String toString() { return operator.id() + parameterSet.toString(); } + } From 7fc642c4ebdfc503c9a6c63f586f9af930187f6c Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 2 Jul 2024 21:48:50 +0200 Subject: [PATCH 092/298] edit --- src/main/java/org/usf/jquery/core/Comparator.java | 7 ++----- src/main/java/org/usf/jquery/core/TypedComparator.java | 6 +----- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 6c5c4c8d..74755258 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -5,7 +5,6 @@ import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; -import static org.usf.jquery.core.ParameterSet.ofParameters; import java.util.Objects; import java.util.Optional; @@ -127,13 +126,11 @@ static TypedComparator notNull() { //in comparator static TypedComparator in() { - return new TypedComparator(inComparator("IN"), - ofParameters(required(), varargs(firstArgJdbcType()))); + return new TypedComparator(inComparator("IN"), required(), varargs(firstArgJdbcType())); } static TypedComparator notIn() { - return new TypedComparator(inComparator("NOT IN"), - ofParameters(required(), varargs(firstArgJdbcType()))); + return new TypedComparator(inComparator("NOT IN"), required(), varargs(firstArgJdbcType())); } static BasicComparator basicComparator(final String name) { diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 1a752495..ad9ddd17 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -16,12 +16,8 @@ public final class TypedComparator implements Comparator { private final ParameterSet parameterSet; public TypedComparator(Comparator comparator, Parameter... parameters) { - this(comparator, ofParameters(parameters)); - } - - public TypedComparator(Comparator comparator, ParameterSet parameterSet) { this.comparator = comparator; - this.parameterSet = parameterSet; + this.parameterSet = ofParameters(parameters); } @Override From 7e908bdcfb49112446c9b298bf95b14ab5994ac8 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 3 Jul 2024 00:21:28 +0200 Subject: [PATCH 093/298] edit --- .../usf/jquery/core/ColumnFilterGroup.java | 37 +++++++----------- .../usf/jquery/core/ColumnSingleFilter.java | 25 ++---------- .../java/org/usf/jquery/core/Comparator.java | 6 +-- .../usf/jquery/core/ComparisonExpression.java | 21 +++++----- .../core/ComparisonExpressionGroup.java | 38 +++++++------------ .../core/ComparisonSingleExpression.java | 20 ++++------ .../java/org/usf/jquery/core/DBColumn.java | 12 +++--- .../java/org/usf/jquery/core/DBFilter.java | 4 +- .../java/org/usf/jquery/core/DBOrder.java | 1 - .../java/org/usf/jquery/core/Operator.java | 4 ++ .../org/usf/jquery/core/SqlStringBuilder.java | 4 ++ .../org/usf/jquery/core/TypedOperator.java | 4 +- src/main/java/org/usf/jquery/core/Utils.java | 9 ++++- .../org/usf/jquery/core/WhenExpression.java | 3 +- 14 files changed, 77 insertions(+), 111 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index d099c5d8..143f0e0c 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -1,59 +1,48 @@ package org.usf.jquery.core; import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.Utils.arrayJoin; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedList; import java.util.stream.Stream; -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - /** * * @author u$f * */ //@see ComparisonExpressionGroup -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class ColumnFilterGroup implements DBFilter { private final LogicalOperator operator; - private final Collection filters; - - ColumnFilterGroup(@NonNull LogicalOperator operator, DBFilter... filters) {//assert length > 1 + private final DBFilter[] filters; + + ColumnFilterGroup(LogicalOperator operator, DBFilter... filters) { this.operator = operator; - this.filters = filters == null - ? new LinkedList<>() - : Stream.of(filters).collect(toList()); + this.filters = filters; } - + @Override public String sql(QueryParameterBuilder builder) { - return "(" + filters.stream().map(o-> o.sql(builder)).collect(joining(operator.sql())) + ")"; + return "(" + Stream.of(filters) + .map(o-> o.sql(builder)) + .collect(joining(operator.sql())) + ")"; } @Override public boolean isAggregation() { - return filters.stream().anyMatch(DBFilter::isAggregation); + return Stream.of(filters).anyMatch(DBFilter::isAggregation); } @Override public DBFilter append(LogicalOperator op, DBFilter filter) { - var gpe = operator == op - ? new ColumnFilterGroup(op, new ArrayList<>(filters)) - : new ColumnFilterGroup(op, this); - gpe.filters.add(filter); - return gpe; + return operator == op + ? new ColumnFilterGroup(op, arrayJoin(filters, filter)) + : new ColumnFilterGroup(op, this, filter); } @Override public String toString() { return sql(addWithValue()); } - } diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index 3edf33c5..85970dbf 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -1,11 +1,9 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.LogicalOperator.AND; -import static org.usf.jquery.core.LogicalOperator.OR; +import static org.usf.jquery.core.Aggregable.aggregation; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import lombok.AccessLevel; -import lombok.NonNull; import lombok.RequiredArgsConstructor; /** @@ -16,19 +14,17 @@ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class ColumnSingleFilter implements DBFilter { - @NonNull - private final DBColumn column; - @NonNull + private final Object left; private final ComparisonExpression expression; @Override public String sql(QueryParameterBuilder ph) { - return expression.sql(ph, column); + return expression.sql(ph, left); } @Override public boolean isAggregation() { - return column.isAggregation() || expression.isAggregation(); + return aggregation(left) || expression.isAggregation(); } @Override @@ -36,19 +32,6 @@ public ColumnFilterGroup append(LogicalOperator op, DBFilter filter) { return new ColumnFilterGroup(op, this, filter); } - public DBFilter and(ComparisonExpression exp) { - return append(AND, exp); - } - - public DBFilter or(ComparisonExpression exp) { - return append(OR, exp); - } - - public ColumnSingleFilter append(LogicalOperator op, ComparisonExpression exp) { - var nex = expression.append(op, exp); //@see OperatorExpressionGroup - return nex == exp ? this : new ColumnSingleFilter(column, nex); - } - @Override public String toString() { return sql(addWithValue()); diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 74755258..4d242295 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -21,13 +21,13 @@ public interface Comparator extends DBProcessor { @Override default DBFilter args(Object... args) { - return new ColumnSingleFilter((DBColumn)args[0], - this.expression(copyOfRange(args, 1, args.length))); // no type + return new ColumnSingleFilter(args[0], + expression(copyOfRange(args, 1, args.length))); // no type } //basic comparator - default ComparisonExpression expression(Object right) { + default ComparisonExpression expression(Object... right) { return new ComparisonSingleExpression(this, right); } diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpression.java b/src/main/java/org/usf/jquery/core/ComparisonExpression.java index 2d003c3a..580f2912 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpression.java @@ -19,27 +19,27 @@ default String sql(QueryParameterBuilder builder, Object[] args) { String sql(QueryParameterBuilder builder, Object left); // do change method order - static ComparisonExpression equal(Object right) { + static ComparisonExpression eq(Object right) { return Comparator.eq().expression(right); } - static ComparisonExpression notEqual(Object right) { + static ComparisonExpression ne(Object right) { return Comparator.ne().expression(right); } - static ComparisonExpression lessThan(Object right) { + static ComparisonExpression lt(Object right) { return Comparator.lt().expression(right); } - static ComparisonExpression lessOrEqual(Object right) { + static ComparisonExpression le(Object right) { return Comparator.le().expression(right); } - static ComparisonExpression greaterThan(Object right) { + static ComparisonExpression gt(Object right) { return Comparator.gt().expression(right); } - static ComparisonExpression greaterOrEqual(Object right) { + static ComparisonExpression ge(Object right) { return Comparator.ge().expression(right); } @@ -60,21 +60,20 @@ static ComparisonExpression notILike(Object right) { } static ComparisonExpression isNull() { - return Comparator.isNull().expression(null); + return Comparator.isNull().expression(); } static ComparisonExpression isNotNull() { - return Comparator.notNull().expression(null); + return Comparator.notNull().expression(); } @SuppressWarnings("unchecked") static ComparisonExpression in(@NonNull T... right) { - return Comparator.in().expression(right); + return Comparator.in().expression((Object[])right); } @SuppressWarnings("unchecked") static ComparisonExpression notIn(@NonNull T... right) { - return Comparator.notIn().expression(right); + return Comparator.notIn().expression((Object[])right); } - } diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index 0e888924..f379d832 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -1,58 +1,48 @@ package org.usf.jquery.core; import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; -import static org.usf.jquery.core.SqlStringBuilder.EMPTY; +import static org.usf.jquery.core.Utils.arrayJoin; -import java.util.ArrayList; -import java.util.Collection; import java.util.stream.Stream; -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - /** * * @author u$f * */ //@see ColumnFilterGroup -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class ComparisonExpressionGroup implements ComparisonExpression { private final LogicalOperator operator; - private final Collection expressions; + private final ComparisonExpression[] expressions; - public ComparisonExpressionGroup(@NonNull LogicalOperator operator, ComparisonExpression... expressions) { - this(operator, expressions == null - ? new ArrayList<>() - : Stream.of(expressions).collect(toList())); + ComparisonExpressionGroup(LogicalOperator operator, ComparisonExpression... expressions) { + this.operator = operator; + this.expressions = expressions; } - + @Override public String sql(QueryParameterBuilder builder, Object operand) { - return "(" + expressions.stream().map(o-> o.sql(builder, operand)).collect(joining(operator.sql())) + ")"; + return "(" + Stream.of(expressions) + .map(o-> o.sql(builder, operand)) + .collect(joining(operator.sql())) + ")"; } @Override public boolean isAggregation() { - return expressions.stream().anyMatch(ComparisonExpression::isAggregation); + return Stream.of(expressions).anyMatch(ComparisonExpression::isAggregation); } @Override public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) { - var gpe = operator == op - ? new ComparisonExpressionGroup(op, new ArrayList<>(expressions)) - : new ComparisonExpressionGroup(op, this); - gpe.expressions.add(exp); - return gpe; + return operator == op + ? new ComparisonExpressionGroup(op, arrayJoin(expressions, exp)) + : new ComparisonExpressionGroup(op, this, exp); } @Override public String toString() { - return sql(addWithValue(), EMPTY); + return sql(addWithValue(), ""); } - } diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index d133d720..967c87b5 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -1,11 +1,10 @@ package org.usf.jquery.core; +import static java.util.Collections.addAll; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.DBColumn.column; -import static org.usf.jquery.core.Aggregable.aggregation; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.stream.Stream; import lombok.AccessLevel; @@ -20,26 +19,21 @@ public final class ComparisonSingleExpression implements ComparisonExpression { private final Comparator comparator; - private final Object right; //null|array|any + private final Object[] right; @Override public String sql(QueryParameterBuilder builder, Object left) { - var param = new LinkedList<>(); + var param = new ArrayList<>(); param.add(left); if(nonNull(right)) { - if(right.getClass().isArray()) { - Stream.of((Object[])right).forEach(param::add); - } - else { - param.add(right); - } + addAll(param, right); } return comparator.sql(builder, param.toArray()); } @Override public boolean isAggregation() { - return aggregation(right); + return nonNull(right) && Stream.of(right).anyMatch(Aggregable::aggregation); } @Override @@ -49,6 +43,6 @@ public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) @Override public String toString() { - return sql(addWithValue(), column("")); + return sql(addWithValue(), ""); } } diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index e3a9339f..2bc4e7a5 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -58,27 +58,27 @@ default DBOrder order(Order order) { // filters default ColumnSingleFilter equal(Object value) { - return filter(ComparisonExpression.equal(value)); + return filter(ComparisonExpression.eq(value)); } default ColumnSingleFilter notEqual(Object value) { - return filter(ComparisonExpression.notEqual(value)); + return filter(ComparisonExpression.ne(value)); } default ColumnSingleFilter greaterThan(Object value) { - return filter(ComparisonExpression.greaterThan(value)); + return filter(ComparisonExpression.gt(value)); } default ColumnSingleFilter greaterOrEqual(Object value) { - return filter(ComparisonExpression.greaterOrEqual(value)); + return filter(ComparisonExpression.ge(value)); } default ColumnSingleFilter lessThan(Object value) { - return filter(ComparisonExpression.lessThan(value)); + return filter(ComparisonExpression.lt(value)); } default ColumnSingleFilter lessOrEqual(Object value) { - return filter(ComparisonExpression.lessOrEqual(value)); + return filter(ComparisonExpression.le(value)); } default ColumnSingleFilter like(Object value) { diff --git a/src/main/java/org/usf/jquery/core/DBFilter.java b/src/main/java/org/usf/jquery/core/DBFilter.java index 71ba6dc6..4e224c41 100644 --- a/src/main/java/org/usf/jquery/core/DBFilter.java +++ b/src/main/java/org/usf/jquery/core/DBFilter.java @@ -9,14 +9,14 @@ */ @FunctionalInterface public interface DBFilter extends DBObject, Aggregable, Chainable { - - String sql(QueryParameterBuilder builder); @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNoArgs(args, DBFilter.class::getSimpleName); return sql(builder); } + + String sql(QueryParameterBuilder builder); default DBFilter append(LogicalOperator op, DBFilter filter) { throw new UnsupportedOperationException(); //explicitly overridden diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index 5a897e5e..5384a305 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -41,5 +41,4 @@ public String sql(QueryParameterBuilder builder) { public String toString() { return sql(addWithValue()); } - } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 6883f213..87b30a00 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -48,6 +48,10 @@ public String id() { default OperationColumn args(Object... args) { return new OperationColumn(this, args); // no type } + + default OperationColumn args(JavaType type, Object... args) { + return new OperationColumn(this, args, type); + } //Arithmetic operations diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index 94bd98d6..ce34e545 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -48,6 +48,10 @@ public SqlStringBuilder appendIf(boolean condition, Supplier sup) { public SqlStringBuilder appendIf(boolean condition, Supplier sup, Supplier orSup) { return append(condition ? sup.get() : orSup.get()); } + + public SqlStringBuilder appendIf(boolean condition, String sup, String orElse) { + return append(condition ? sup : orElse); + } public SqlStringBuilder appendEach(Collection list, String separator) { return appendEach(list, separator, EMPTY, identity()); diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 2417b3ba..041a9c37 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -16,8 +16,8 @@ public class TypedOperator implements Operator { private final ArgTypeRef typeFn; private final ParameterSet parameterSet; - public TypedOperator(JavaType type, Operator function, Parameter... args) { - this(o-> type, function, args); + public TypedOperator(JavaType type, Operator function, Parameter... parameter) { + this(o-> type, function, parameter); } public TypedOperator(ArgTypeRef typeFn, Operator function, Parameter... parameter) { diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index f3b444c2..94c6ed9b 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static java.util.Arrays.copyOf; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; @@ -19,7 +20,7 @@ public final class Utils { //move this static ThreadLocal context = new ThreadLocal<>(); // change it - + public static final int UNLIMITED = -1; public static boolean isPresent(T[] a) { @@ -50,5 +51,9 @@ public static void currentDatabase(Database db) { context.set(db); } - + public static T[] arrayJoin(T[] arr, T o) { + var res = copyOf(arr, arr.length+1); + res[arr.length] = o; + return res; + } } diff --git a/src/main/java/org/usf/jquery/core/WhenExpression.java b/src/main/java/org/usf/jquery/core/WhenExpression.java index 17c08660..ab3fe699 100644 --- a/src/main/java/org/usf/jquery/core/WhenExpression.java +++ b/src/main/java/org/usf/jquery/core/WhenExpression.java @@ -42,11 +42,10 @@ public JavaType getType() { @Override public String toString() { - return sql(addWithValue(), null); + return sql(addWithValue()); } public static WhenExpression orElse(Object value) { return new WhenExpression(null, value); } - } From bdd137da8fde374a352f8ddfb3e583c21ec5dcba Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 3 Jul 2024 18:39:26 +0200 Subject: [PATCH 094/298] edit --- src/main/java/org/usf/jquery/core/CaseColumn.java | 4 ++-- src/main/java/org/usf/jquery/core/Chainable.java | 2 +- .../java/org/usf/jquery/core/ColumnFilterGroup.java | 3 ++- .../usf/jquery/core/ComparisonSingleExpression.java | 2 +- src/main/java/org/usf/jquery/core/NamedColumn.java | 10 ++++------ src/main/java/org/usf/jquery/core/TaggableColumn.java | 8 +++++--- src/main/java/org/usf/jquery/core/WhenExpression.java | 3 ++- 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 65bd7301..5ab9de40 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -4,8 +4,8 @@ import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; +import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedList; import java.util.Objects; import lombok.AccessLevel; @@ -19,7 +19,7 @@ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class CaseColumn implements DBColumn { // TD override isAggregation - private final Collection expressions = new LinkedList<>(); + private final Collection expressions = new ArrayList<>(); @Override public String sql(QueryParameterBuilder builder) { diff --git a/src/main/java/org/usf/jquery/core/Chainable.java b/src/main/java/org/usf/jquery/core/Chainable.java index f8ea3c5c..bb1dc53c 100644 --- a/src/main/java/org/usf/jquery/core/Chainable.java +++ b/src/main/java/org/usf/jquery/core/Chainable.java @@ -8,7 +8,7 @@ * @author u$f * */ -public interface Chainable { +public interface Chainable> { T append(LogicalOperator op, T exp); diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index 143f0e0c..1e92f3d7 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.Utils.arrayJoin; @@ -31,7 +32,7 @@ public String sql(QueryParameterBuilder builder) { @Override public boolean isAggregation() { - return Stream.of(filters).anyMatch(DBFilter::isAggregation); + return nonNull(filters) && Stream.of(filters).anyMatch(DBFilter::isAggregation); } @Override diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index 967c87b5..689bbf0e 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -19,7 +19,7 @@ public final class ComparisonSingleExpression implements ComparisonExpression { private final Comparator comparator; - private final Object[] right; + private final Object[] right; //nullable @Override public String sql(QueryParameterBuilder builder, Object left) { diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index 8c9c58da..df273109 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import java.util.Objects; + import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.Delegate; @@ -14,7 +16,7 @@ public final class NamedColumn implements TaggableColumn { @Delegate private final DBColumn column; - private final String tag; + private final String tag; //nullable @Override public String tagname() { @@ -23,11 +25,7 @@ public String tagname() { @Override public NamedColumn as(String name) { // map - return tag.equals(name) ? this : new NamedColumn(unwrap(), name); - } - - public DBColumn unwrap() { - return column; + return Objects.equals(name, tag) ? this : new NamedColumn(column, name); } @Override diff --git a/src/main/java/org/usf/jquery/core/TaggableColumn.java b/src/main/java/org/usf/jquery/core/TaggableColumn.java index 702c250d..3f68dc76 100644 --- a/src/main/java/org/usf/jquery/core/TaggableColumn.java +++ b/src/main/java/org/usf/jquery/core/TaggableColumn.java @@ -1,9 +1,8 @@ package org.usf.jquery.core; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; -import java.util.Objects; - /** * * @author u$f @@ -15,6 +14,9 @@ public interface TaggableColumn extends DBColumn { default String sqlWithTag(QueryParameterBuilder builder) { var s = sql(builder); - return Objects.isNull(tagname()) ? s : s + " AS " + doubleQuote(tagname()); + if(nonNull(tagname())) { + s += " AS " + doubleQuote(tagname()); + } + return s; } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/WhenExpression.java b/src/main/java/org/usf/jquery/core/WhenExpression.java index ab3fe699..2b61fced 100644 --- a/src/main/java/org/usf/jquery/core/WhenExpression.java +++ b/src/main/java/org/usf/jquery/core/WhenExpression.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static java.util.Objects.isNull; import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -27,7 +28,7 @@ public String sql(QueryParameterBuilder builder, Object[] args) { public String sql(QueryParameterBuilder arg) { var sb = new StringBuilder(50); - sb = filter == null + sb = isNull(filter) ? sb.append("ELSE ") : sb.append("WHEN ") .append(filter.sql(arg)) From bc332b4b33823c881cdd501d7e20988f91d85513 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 4 Jul 2024 12:50:26 +0200 Subject: [PATCH 095/298] edit --- src/main/java/org/usf/jquery/core/Comparator.java | 2 +- src/main/java/org/usf/jquery/core/DBTable.java | 4 ++++ src/main/java/org/usf/jquery/core/DBView.java | 3 +-- .../java/org/usf/jquery/core/QueryParameterBuilder.java | 2 +- src/main/java/org/usf/jquery/core/ViewColumn.java | 7 ++++++- src/main/java/org/usf/jquery/core/ViewQuery.java | 3 +-- src/main/java/org/usf/jquery/web/ColumnDecorator.java | 2 +- 7 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 4d242295..e4ad1129 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -119,7 +119,7 @@ static TypedComparator isNull() { return new TypedComparator(nullComparator("IS NULL"), required()); } - static TypedComparator notNull() { + static TypedComparator notNull() { //isNotNUll return new TypedComparator(nullComparator("IS NOT NULL"), required()); } diff --git a/src/main/java/org/usf/jquery/core/DBTable.java b/src/main/java/org/usf/jquery/core/DBTable.java index 405f872c..056282dd 100644 --- a/src/main/java/org/usf/jquery/core/DBTable.java +++ b/src/main/java/org/usf/jquery/core/DBTable.java @@ -16,6 +16,10 @@ public class DBTable implements DBView { private final String id; private final String name; + public DBTable(String name) { //core usage + this(name, name); + } + @Override public String sql(QueryParameterBuilder builder) { return member(builder.getSchema(), name); diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 14c53257..6b92f018 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -14,7 +14,7 @@ public interface DBView extends DBObject { @Override default String sql(QueryParameterBuilder builder, Object[] args) { - requireNoArgs(args, DBColumn.class::getSimpleName); + requireNoArgs(args, DBView.class::getSimpleName); return sql(builder); } @@ -27,5 +27,4 @@ default String sqlWithTag(QueryParameterBuilder builder) { default ViewQuery select(String tag, TaggableColumn... columns) { return new ViewQuery(tag, columns); } - } diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index feab9184..94933dba 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -43,7 +43,7 @@ public List views(){ } public String view(DBView view) { - if(isNull(vPrefix) || isNull(view)) { //view can be null + if(isNull(vPrefix)) { //view can be null return null; } for(var i=0; i new ViewColumn(td.table(), cn, reference(), dataType(td))) .orElseThrow(()-> undeclaredResouceException(td.identity(), identity())); } From 32446af122330f7834b80d0dd34e9dd9475d0eab Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 25 Jul 2024 00:17:49 +0200 Subject: [PATCH 096/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 2 +- .../java/org/usf/jquery/core/DBQuery.java | 1 + src/main/java/org/usf/jquery/core/DBView.java | 6 +- .../java/org/usf/jquery/core/JQueryType.java | 2 +- .../java/org/usf/jquery/core/JavaType.java | 2 +- .../java/org/usf/jquery/core/NamedColumn.java | 3 +- .../core/{ViewQuery.java => QueryView.java} | 4 +- .../usf/jquery/core/RequestQueryBuilder.java | 4 +- .../core/{DBTable.java => TableView.java} | 17 +++- .../java/org/usf/jquery/core/ViewColumn.java | 6 +- .../org/usf/jquery/web/ColumnDecorator.java | 37 ++----- .../org/usf/jquery/web/ColumnMetadata.java | 42 +++++--- .../usf/jquery/web/CompletableViewQuery.java | 4 +- .../org/usf/jquery/web/DatabaseMetadata.java | 14 +-- .../org/usf/jquery/web/JQueryContext.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 8 +- .../org/usf/jquery/web/RevisionIterator.java | 6 +- .../org/usf/jquery/web/TableDecorator.java | 80 +++++++++++----- .../org/usf/jquery/web/TableMetadata.java | 96 +++++++++++-------- .../usf/jquery/web/YearTableDecorator.java | 4 +- 20 files changed, 200 insertions(+), 140 deletions(-) rename src/main/java/org/usf/jquery/core/{ViewQuery.java => QueryView.java} (89%) rename src/main/java/org/usf/jquery/core/{DBTable.java => TableView.java} (54%) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 2bc4e7a5..5ae8a083 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -40,7 +40,7 @@ default Stream groupKeys() { return Stream.of(this); } - default JavaType getType() { + default JDBCType getType() { return null; } diff --git a/src/main/java/org/usf/jquery/core/DBQuery.java b/src/main/java/org/usf/jquery/core/DBQuery.java index 4f3e09f4..34761a66 100644 --- a/src/main/java/org/usf/jquery/core/DBQuery.java +++ b/src/main/java/org/usf/jquery/core/DBQuery.java @@ -11,6 +11,7 @@ */ public interface DBQuery extends DBView, Typed { + @Deprecated Collection columns(); @Override diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 6b92f018..79312b6d 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -10,7 +10,7 @@ */ public interface DBView extends DBObject { - String id(); + String id(); //technical use @Override default String sql(QueryParameterBuilder builder, Object[] args) { @@ -24,7 +24,7 @@ default String sqlWithTag(QueryParameterBuilder builder) { String sql(QueryParameterBuilder builder); - default ViewQuery select(String tag, TaggableColumn... columns) { - return new ViewQuery(tag, columns); + default QueryView select(String tag, TaggableColumn... columns) { + return new QueryView(tag, columns); } } diff --git a/src/main/java/org/usf/jquery/core/JQueryType.java b/src/main/java/org/usf/jquery/core/JQueryType.java index 5fca5202..020e925d 100644 --- a/src/main/java/org/usf/jquery/core/JQueryType.java +++ b/src/main/java/org/usf/jquery/core/JQueryType.java @@ -14,7 +14,7 @@ public enum JQueryType implements JavaType { COLUMN(TaggableColumn.class), //TD DBColumn !? FILTER(DBFilter.class), ORDER(DBOrder.class), - QUERY(ViewQuery.class), + QUERY(QueryView.class), PARTITION(Partition.class); private final Class type; diff --git a/src/main/java/org/usf/jquery/core/JavaType.java b/src/main/java/org/usf/jquery/core/JavaType.java index c0db1ce8..85af9f06 100644 --- a/src/main/java/org/usf/jquery/core/JavaType.java +++ b/src/main/java/org/usf/jquery/core/JavaType.java @@ -18,6 +18,6 @@ default boolean accept(Object o) { public interface Typed { - JavaType getType(); // return null by default + JDBCType getType(); // return null by default } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index df273109..42d14cbb 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -16,7 +16,8 @@ public final class NamedColumn implements TaggableColumn { @Delegate private final DBColumn column; - private final String tag; //nullable + private final String tag; //nullable + //+ type @Override public String tagname() { diff --git a/src/main/java/org/usf/jquery/core/ViewQuery.java b/src/main/java/org/usf/jquery/core/QueryView.java similarity index 89% rename from src/main/java/org/usf/jquery/core/ViewQuery.java rename to src/main/java/org/usf/jquery/core/QueryView.java index 2e42fffb..3215c530 100644 --- a/src/main/java/org/usf/jquery/core/ViewQuery.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -15,13 +15,13 @@ * */ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public final class ViewQuery implements DBQuery { +public final class QueryView implements DBQuery { private final String id; @Getter // remove this private final RequestQueryBuilder query; - public ViewQuery(String id, TaggableColumn... columns) { + public QueryView(String id, TaggableColumn... columns) { this(id, new RequestQueryBuilder().columns(columns)); } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 54d8379d..9cd87d7a 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -97,8 +97,8 @@ public Optional getColumn(String id){ return columns.stream().filter(c-> c.tagname().contains(id)).findAny(); } - public ViewQuery as(String tag) { - return new ViewQuery(tag, this); + public QueryView as(String tag) { + return new QueryView(tag, this); } public RequestQuery build(){ diff --git a/src/main/java/org/usf/jquery/core/DBTable.java b/src/main/java/org/usf/jquery/core/TableView.java similarity index 54% rename from src/main/java/org/usf/jquery/core/DBTable.java rename to src/main/java/org/usf/jquery/core/TableView.java index 056282dd..c01bc229 100644 --- a/src/main/java/org/usf/jquery/core/DBTable.java +++ b/src/main/java/org/usf/jquery/core/TableView.java @@ -1,8 +1,10 @@ package org.usf.jquery.core; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.member; +import lombok.Getter; import lombok.RequiredArgsConstructor; /** @@ -10,19 +12,26 @@ * @author u$f * */ +@Getter @RequiredArgsConstructor -public class DBTable implements DBView { +public class TableView implements DBView { private final String id; + private final String schema; private final String name; - public DBTable(String name) { //core usage - this(name, name); + public TableView(String name) { + this(name, null, name); //use tablename as id + } + + public TableView(String schema, String name) { + this(name, schema, name); //use tablename as id } @Override public String sql(QueryParameterBuilder builder) { - return member(builder.getSchema(), name); + var scm = nonNull(schema) ? schema : builder.getSchema(); //priority order + return member(scm, name); } @Override diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index ab3dfcd5..775cc312 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -4,6 +4,7 @@ import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.member; +import lombok.Getter; import lombok.RequiredArgsConstructor; /** @@ -11,13 +12,14 @@ * @author u$f * */ +@Getter @RequiredArgsConstructor public final class ViewColumn implements TaggableColumn { private final DBView view; private final String name; private final String tag; - private final JavaType type; + private final JDBCType type; public ViewColumn(String name, String tag) { this(null, name, tag, null); @@ -33,7 +35,7 @@ public String sql(QueryParameterBuilder arg) { } @Override - public JavaType getType() { + public JDBCType getType() { return type; } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 5cdb9c16..766d8329 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -1,14 +1,7 @@ package org.usf.jquery.web; -import static java.util.Objects.nonNull; -import static org.usf.jquery.web.ArgumentParsers.jdbcArgParser; -import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; - import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.JDBCType; -import org.usf.jquery.core.TaggableColumn; -import org.usf.jquery.core.Validation; -import org.usf.jquery.core.ViewColumn; /** * @@ -19,38 +12,26 @@ @FunctionalInterface public interface ColumnDecorator { - String identity(); //URL + String identity(); //URL unique - default String reference() { //JSON + default String reference(TableDecorator td) { //JSON return identity(); } - default TaggableColumn from(TableDecorator td) { - var b = builder(); - return nonNull(b) - ? b.build(td).as(reference()) - : td.columnName(this) //recursive call - .map(Validation::requireLegalVariable) //do it on init - .map(cn-> new ViewColumn(td.table(), cn, reference(), dataType(td))) - .orElseThrow(()-> undeclaredResouceException(td.identity(), identity())); - } - - default JDBCArgumentParser parser(TableDecorator td){ // override parser | format | local - return jdbcArgParser(dataType(td)); + default JDBCType type(TableDecorator td) { + return null; } - default JDBCType dataType(TableDecorator td) { // only if !builder - return td.metadata().columnMetada(this) - .map(ColumnMetadata::getDataType) - .orElse(null); + default JDBCArgumentParser parser(TableDecorator td){ // override parser | format | local | validation + return null; // jdbcArgParser(dataType(td)) } - default ColumnBuilder builder() { //set type if null + default ColumnBuilder builder(TableDecorator td) { //set type if null return null; // no builder by default } default CriteriaBuilder criteria(String name) { - return null; // no criteria by default + return null; // no criteria by default } default String pattern(TableDecorator td) { @@ -73,7 +54,7 @@ public String identity() { } @Override - public ColumnBuilder builder() { + public ColumnBuilder builder(TableDecorator td) { return cb; } }; diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index 1c533acd..ca938c75 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -1,21 +1,26 @@ package org.usf.jquery.web; import static java.lang.Integer.MAX_VALUE; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.JDBCType.DECIMAL; import static org.usf.jquery.core.JDBCType.DOUBLE; import static org.usf.jquery.core.JDBCType.FLOAT; import static org.usf.jquery.core.JDBCType.NUMERIC; +import static org.usf.jquery.core.JDBCType.OTHER; import static org.usf.jquery.core.JDBCType.REAL; +import static org.usf.jquery.core.JDBCType.fromDataType; import static org.usf.jquery.core.Utils.UNLIMITED; +import static org.usf.jquery.core.Validation.requireLegalVariable; import java.sql.Timestamp; import org.usf.jquery.core.JDBCType; +import org.usf.jquery.core.Validation; +import org.usf.jquery.core.ViewColumn; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; import lombok.ToString; /** @@ -24,25 +29,36 @@ * */ @Getter -@Setter(value = AccessLevel.PACKAGE) @ToString -@RequiredArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) public final class ColumnMetadata { - private final String columnName; - private JDBCType dataType = null; - private int dataSize = UNLIMITED; - private int precision = UNLIMITED; + private ViewColumn column; + private int dataSize; + private int precision; + private final boolean overConfigured; + @Deprecated ColumnMetadata reset() { - this.dataType = null; this.dataSize = UNLIMITED; this.precision = UNLIMITED; return this; } - + + public void update(int type, int size, int precision) { + if(!overConfigured) { + var ct = fromDataType(type).orElse(OTHER); + if(ct != column.getType()) { + + column = new ViewColumn(column.getView(), column.getName(), column.getTag(), ct); + } + this.dataSize = size; + this.precision = precision; + } + } + public String toJavaType(){ - return dataType.typeClass().getSimpleName(); + return column.getType().typeClass().getSimpleName(); } public String toSqlType(){ @@ -58,4 +74,8 @@ public String toSqlType(){ } return s; } + + public static ColumnMetadata columnMetadata(ViewColumn col) { + return new ColumnMetadata(col, UNLIMITED, UNLIMITED, nonNull(col.getType())); + } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java index 72c4bffd..a45cc960 100644 --- a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java +++ b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java @@ -1,7 +1,7 @@ package org.usf.jquery.web; import org.usf.jquery.core.DBQuery; -import org.usf.jquery.core.ViewQuery; +import org.usf.jquery.core.QueryView; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -16,7 +16,7 @@ final class CompletableViewQuery implements DBQuery { @Delegate - private final ViewQuery query; + private final QueryView query; @Override public String toString() { diff --git a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java index f34469f0..4de4c7a8 100644 --- a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java +++ b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java @@ -19,6 +19,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.function.Supplier; import java.util.stream.Stream; import javax.sql.DataSource; @@ -49,8 +50,8 @@ public final class DatabaseMetadata { @Getter private Database type; - public Optional tableMetada(TableDecorator td){ - return ofNullable(tables.get(td.identity())); + public TableMetadata tableMetada(TableDecorator td, Supplier supp){ + return tables.computeIfAbsent(td.identity(), id-> supp.get()); } public void fetch() { @@ -65,12 +66,11 @@ public void fetch() { var metadata = cn.getMetaData(); type = Database.of(metadata.getDatabaseProductName()).orElse(null); for(var t : tables.values()) { - log.info("Scanning table '{}' metadata...", t.getTablename()); + log.info("Scanning table '{}' metadata...", t.getView()); t.fetch(metadata); logTableColumns(t.getColumns()); - if(t instanceof YearTableMetadata) { - var yt = (YearTableMetadata) t; - log.info("Scanning table '{}' revisions...", t.getTablename()); + if(t instanceof YearTableMetadata yt) { + log.info("Scanning table '{}' revisions...", t.getView()); yt.fetchRevisions(cn); logRevisions(yt.getRevisions()); } @@ -93,7 +93,7 @@ static void logTableColumns(Map map) { log.info(bar); map.entrySet().forEach(e-> log.info(format(pattern, e.getKey(), e.getValue().toJavaType(), - e.getValue().getColumnName(), e.getValue().toSqlType()))); + e.getValue().getColumn(), e.getValue().toSqlType()))); log.info(bar); } } diff --git a/src/main/java/org/usf/jquery/web/JQueryContext.java b/src/main/java/org/usf/jquery/web/JQueryContext.java index 406f61db..5404a674 100644 --- a/src/main/java/org/usf/jquery/web/JQueryContext.java +++ b/src/main/java/org/usf/jquery/web/JQueryContext.java @@ -51,7 +51,7 @@ public static JQueryContext register( return instance; } - public DatabaseMetadata bind(DataSource ds){ + public DatabaseMetadata bind(DataSource ds){ //default schema database = create(ds, tables.values(), columns.values()); return database; } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index c04403f3..776213e0 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -60,7 +60,7 @@ import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; import org.usf.jquery.core.ViewJoin.JoinType; -import org.usf.jquery.core.ViewQuery; +import org.usf.jquery.core.QueryView; import org.usf.jquery.core.WindowFunction; import lombok.AccessLevel; @@ -92,7 +92,7 @@ public RequestEntryChain(String value) { this(value, false); } - public ViewQuery evalQuery(TableDecorator td) { + public QueryView evalQuery(TableDecorator td) { return evalQuery(td, false); } @@ -107,7 +107,9 @@ public ViewJoin evalJoin(TableDecorator td) { } - public ViewQuery evalQuery(TableDecorator td, boolean requireTag) { + //evalView query|view:alias + + public QueryView evalQuery(TableDecorator td, boolean requireTag) { if(SELECT.equals(value)) { var q = new RequestQueryBuilder().columns(toColumnArgs(td, false)); var e = this; diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index fe4884e6..33533750 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -12,7 +12,7 @@ import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; -import org.usf.jquery.core.DBTable; +import org.usf.jquery.core.TableView; import org.usf.jquery.core.QueryParameterBuilder; import lombok.RequiredArgsConstructor; @@ -53,8 +53,8 @@ public static RevisionIterator iterator(YearMonth[] revisions){ throw new IllegalArgumentException("no revision"); } - static DBTable yearTable(String name, String tagname) { - return new DBTable(name, tagname) { + static TableView yearTable(String name, String tagname) { + return new TableView(name, tagname) { @Override public String sql(QueryParameterBuilder builder) { return super.sql(builder) + "_" + currentRev.get().getKey(); diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/TableDecorator.java index b23719ee..e100c450 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/TableDecorator.java @@ -2,9 +2,12 @@ import static java.lang.Integer.parseInt; import static java.util.Objects.nonNull; +import static java.util.stream.Collectors.toUnmodifiableMap; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.core.Validation.requireLegalVariable; +import static org.usf.jquery.web.ColumnMetadata.columnMetadata; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; import static org.usf.jquery.web.Constants.FETCH; @@ -12,27 +15,29 @@ import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.RESERVED_WORDS; import static org.usf.jquery.web.Constants.VIEW; +import static org.usf.jquery.web.JQueryContext.context; import static org.usf.jquery.web.JQueryContext.database; import static org.usf.jquery.web.MissingParameterException.missingParameterException; +import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; import static org.usf.jquery.web.RequestContext.clearContext; import static org.usf.jquery.web.RequestContext.currentContext; import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.RequestParser.parseEntry; -import static org.usf.jquery.web.TableMetadata.emptyMetadata; -import static org.usf.jquery.web.TableMetadata.tableMetadata; -import java.util.Collection; import java.util.Map; -import java.util.Optional; +import java.util.function.Function; import java.util.stream.Stream; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBQuery; -import org.usf.jquery.core.DBTable; import org.usf.jquery.core.DBView; import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.TableView; import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.ViewColumn; + +import lombok.NonNull; /** * @@ -43,19 +48,24 @@ public interface TableDecorator { String identity(); //URL - String tableName(); //SQL check schema.table + String tableName(); // schema.table || nullable if builder - Optional columnName(ColumnDecorator cd); + String columnName(ColumnDecorator cd); - default DBView table() { //optim - var b = builder(); - return nonNull(b) - ? b.build(identity()) - : new DBTable(identity(), tableName()); + default DBView table() { + return metadata().getView(); } - default TaggableColumn column(ColumnDecorator cd) { - return cd.from(this); + default TaggableColumn column(@NonNull ColumnDecorator cd) { + var c = metadata().columnMetada(cd); //priority + if(nonNull(c)) { + return c.getColumn(); + } + var b = cd.builder(this); + if(nonNull(b)) { + return b.build(this).as(cd.reference(this)); //set type + } + throw undeclaredResouceException(identity(), cd.identity()); } default ViewBuilder builder() { @@ -63,7 +73,38 @@ default ViewBuilder builder() { } default CriteriaBuilder criteria(String name) { //!aggregation - return null; // no criteria by default + return null; // no criteria by default + } + + default TableMetadata metadata() { + return database().tableMetada(this, ()-> new TableMetadata(tableView(), declaredColumns())); + } + + default DBView tableView() { + var tn = tableName(); + if(nonNull(tn)){ + var idx = tn.indexOf('.'); //schema.view + return idx == -1 + ? new TableView(requireLegalVariable(tn)) + : new TableView(requireLegalVariable(tn.substring(0, idx)), + requireLegalVariable(tn.substring(idx, tn.length()))); + } + var b = builder(); + if(nonNull(b)){ + return b.build(identity()); + } + throw new IllegalArgumentException("viewName or builder expected in " + this); + } + + default Map declaredColumns(){ + var columns = context().getColumns().values(); + return columns.stream().mapMulti((cd, cons)-> { + var cn = columnName(cd); + if(nonNull(cn)) { //only physical column + var col = new ViewColumn(table(), cn, cd.reference(this), cd.type(this)); + cons.accept(columnMetadata(col)); + } //tag = reference + }).collect(toUnmodifiableMap(cm-> cm.getColumn().getTag(), Function.identity())); } default RequestQueryBuilder query(Map parameterMap) { @@ -147,15 +188,6 @@ private static Integer requirePositiveInt(String key, Map para return null; } - default TableMetadata metadata() { - return database().tableMetada(this) - .orElseGet(()-> emptyMetadata(this)); // not binded - } - - default TableMetadata createMetadata(Collection columns) { - return tableMetadata(this, columns); - } - static Stream flatParameters(String... arr) { //number local separator return Stream.of(arr).flatMap(v-> Stream.of(v.split(","))); } diff --git a/src/main/java/org/usf/jquery/web/TableMetadata.java b/src/main/java/org/usf/jquery/web/TableMetadata.java index 2ae9f2a7..ee5c2e2f 100644 --- a/src/main/java/org/usf/jquery/web/TableMetadata.java +++ b/src/main/java/org/usf/jquery/web/TableMetadata.java @@ -1,28 +1,28 @@ package org.usf.jquery.web; import static java.lang.String.join; -import static java.util.Collections.emptyList; -import static java.util.Collections.unmodifiableMap; import static java.util.Objects.nonNull; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; -import static org.usf.jquery.core.JDBCType.OTHER; -import static org.usf.jquery.core.JDBCType.fromDataType; +import static org.usf.jquery.core.QueryParameterBuilder.parametrized; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.web.JQueryContext.database; import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.time.Instant; -import java.util.Collection; -import java.util.LinkedHashMap; +import java.util.ArrayList; +import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; -import java.util.Optional; + +import org.usf.jquery.core.DBQuery; +import org.usf.jquery.core.DBView; +import org.usf.jquery.core.QueryParameterBuilder; +import org.usf.jquery.core.TableView; import lombok.AccessLevel; import lombok.Getter; -import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.ToString; @@ -33,17 +33,21 @@ */ @ToString @Getter(AccessLevel.PACKAGE) -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) public class TableMetadata { - private final String tablename; - private final Map columns; + private DBView view; //nullable if query + private Map columns; @Getter @Setter(AccessLevel.PACKAGE) private Instant lastUpdate; + + public TableMetadata(DBView view, Map columns) { + this.view = view; + this.columns = columns; + } - public Optional columnMetada(ColumnDecorator cd) { - return Optional.ofNullable(columns.get(cd.identity())); + public ColumnMetadata columnMetada(ColumnDecorator cd) { + return columns.get(cd.identity()); } public void fetch() throws SQLException { //individually table fetching @@ -53,37 +57,45 @@ public void fetch() throws SQLException { //individually table fetching } void fetch(DatabaseMetaData metadata) throws SQLException { - var dbMap = columns.values().stream().collect(toMap(ColumnMetadata::getColumnName, identity())); - try(var rs = metadata.getColumns(null, null, tablename, null)){ - if(!rs.next()) { - throw new NoSuchElementException(quote(tablename) + " table not found"); + if(view instanceof TableView tv) { + try(var rs = metadata.getColumns(null, tv.getSchema(), tv.getName(), null)){ + if(rs.next()) { + var db = columns.values().stream().collect(toMap(m-> m.getColumn().getName(), identity())); + do { + var cm = db.remove(rs.getString("COLUMN_NAME")); + if(nonNull(cm)) { + cm.update( + rs.getInt("DATA_TYPE"), + rs.getInt("COLUMN_SIZE"), + rs.getInt("DECIMAL_DIGITS")); + } // else undeclared column + } while(rs.next()); + if(!db.isEmpty()) { + throw new NoSuchElementException("column(s) [" + join(", ", db.keySet()) + "] not found in " + view.toString()); + } + } + else { + throw new NoSuchElementException(quote(view.toString()) + " table not found"); + } } - do { - var meta = dbMap.remove(rs.getString("COLUMN_NAME")); - if(nonNull(meta)) { - meta.setDataType(fromDataType(rs.getInt("DATA_TYPE")).orElse(OTHER)); - meta.setDataSize(rs.getInt("COLUMN_SIZE")); - meta.setPrecision(rs.getInt("DECIMAL_DIGITS")); - } // else undeclared column - } while(rs.next()); } - if(!dbMap.isEmpty()) { - throw new NoSuchElementException("column(s) [" + join(", ", dbMap.keySet()) + "] not found in " + tablename); + else if(view instanceof DBQuery qr) { + var b = parametrized(new ArrayList<>()); + try(var ps = metadata.getConnection().prepareStatement("SELECT * FROM(" + qr.sql(b) + ") WHERE 1=0"); + var rs = ps.executeQuery()){ + var db = new HashMap<>(columns); + var meta = rs.getMetaData(); + for(var i=1; i<=meta.getColumnCount(); i++) { + var cm = db.remove(meta.getColumnName(i)); + if(nonNull(cm)) { + cm.update(meta.getColumnType(i), meta.getColumnDisplaySize(i), meta.getPrecision(i)); + } // else undeclared column + } + if(!db.isEmpty()) { + throw new NoSuchElementException("column(s) [" + join(", ", db.keySet()) + "] not found in " + view.toString()); + } + } + view = null; } } - - static TableMetadata emptyMetadata(TableDecorator table) { - return tableMetadata(table, emptyList()); - } - - static TableMetadata tableMetadata(TableDecorator table, Collection columns) { - return new TableMetadata(table.tableName(), declaredColumns(table, columns)); - } - - static Map declaredColumns(TableDecorator table, Collection columns){ - return unmodifiableMap(columns.stream().reduce(new LinkedHashMap(), (m, cd)-> { - table.columnName(cd).map(ColumnMetadata::new).ifPresent(cm-> m.put(cd.identity(), cm)); - return m; - }, (m1, m2)-> m1)); - } } diff --git a/src/main/java/org/usf/jquery/web/YearTableDecorator.java b/src/main/java/org/usf/jquery/web/YearTableDecorator.java index 30b8772a..5b1aaf37 100644 --- a/src/main/java/org/usf/jquery/web/YearTableDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearTableDecorator.java @@ -31,7 +31,7 @@ import java.util.function.UnaryOperator; import java.util.stream.Stream; -import org.usf.jquery.core.DBTable; +import org.usf.jquery.core.TableView; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; @@ -55,7 +55,7 @@ default YearMonth[] availableRevisions() {//reduce data revision access } @Override - default DBTable table() { + default TableView table() { return yearTable(tableName(), identity()); } From e61f6d29f5c979ab8549ccbb9b1dfebc40be2168 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 25 Jul 2024 17:19:08 +0200 Subject: [PATCH 097/298] edit --- .../java/org/usf/jquery/core/ArgTypeRef.java | 2 +- .../java/org/usf/jquery/core/CaseColumn.java | 2 +- .../java/org/usf/jquery/core/DBQuery.java | 2 +- .../org/usf/jquery/core/OperationColumn.java | 4 +- .../java/org/usf/jquery/core/Operator.java | 2 +- .../usf/jquery/core/RequestQueryBuilder.java | 2 +- .../java/org/usf/jquery/core/TableView.java | 7 +- .../org/usf/jquery/core/TypedOperator.java | 2 +- src/main/java/org/usf/jquery/core/Utils.java | 1 + .../java/org/usf/jquery/core/Validation.java | 4 +- .../org/usf/jquery/core/WhenExpression.java | 2 +- .../org/usf/jquery/web/ArgumentParsers.java | 6 +- .../org/usf/jquery/web/ColumnBuilder.java | 2 +- .../org/usf/jquery/web/ColumnDecorator.java | 16 +-- .../org/usf/jquery/web/ColumnMetadata.java | 23 ++-- .../usf/jquery/web/DatabaseConfiguration.java | 64 +++++++++ .../org/usf/jquery/web/DatabaseDecorator.java | 35 +++++ .../org/usf/jquery/web/DatabaseManager.java | 59 +++++++++ .../org/usf/jquery/web/DatabaseMetadata.java | 68 ++++------ .../usf/jquery/web/JDBCArgumentParser.java | 2 +- .../org/usf/jquery/web/JQueryContext.java | 76 ----------- .../usf/jquery/web/JavaArgumentParser.java | 2 +- .../org/usf/jquery/web/QueryDecorator.java | 11 +- .../org/usf/jquery/web/RequestContext.java | 6 +- .../org/usf/jquery/web/RequestEntryChain.java | 54 ++++---- .../org/usf/jquery/web/RequestQueryParam.java | 6 +- .../jquery/web/RequestQueryParamResolver.java | 10 +- .../org/usf/jquery/web/RevisionIterator.java | 2 +- .../org/usf/jquery/web/TableMetadata.java | 101 --------------- ...TableDecorator.java => ViewDecorator.java} | 68 +++------- .../java/org/usf/jquery/web/ViewMetadata.java | 122 ++++++++++++++++++ .../usf/jquery/web/YearTableDecorator.java | 25 ++-- .../org/usf/jquery/web/YearTableMetadata.java | 51 ++++---- .../usf/jquery/web/RequestEntryChainTest.java | 13 +- 34 files changed, 442 insertions(+), 410 deletions(-) create mode 100644 src/main/java/org/usf/jquery/web/DatabaseConfiguration.java create mode 100644 src/main/java/org/usf/jquery/web/DatabaseDecorator.java create mode 100644 src/main/java/org/usf/jquery/web/DatabaseManager.java delete mode 100644 src/main/java/org/usf/jquery/web/JQueryContext.java delete mode 100644 src/main/java/org/usf/jquery/web/TableMetadata.java rename src/main/java/org/usf/jquery/web/{TableDecorator.java => ViewDecorator.java} (72%) create mode 100644 src/main/java/org/usf/jquery/web/ViewMetadata.java diff --git a/src/main/java/org/usf/jquery/core/ArgTypeRef.java b/src/main/java/org/usf/jquery/core/ArgTypeRef.java index 7b78aa84..696743f6 100644 --- a/src/main/java/org/usf/jquery/core/ArgTypeRef.java +++ b/src/main/java/org/usf/jquery/core/ArgTypeRef.java @@ -10,7 +10,7 @@ * @author u$f * */ -interface ArgTypeRef extends Function { +interface ArgTypeRef extends Function { static ArgTypeRef firstArgJdbcType() { return arr-> typeOf(requireAtLeastNArgs(1, arr, diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 5ab9de40..91ef97ce 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -30,7 +30,7 @@ public String sql(QueryParameterBuilder builder) { } @Override - public JavaType getType() { + public JDBCType getType() { return expressions.stream() .map(WhenExpression::getType) .filter(Objects::nonNull) // should have same type diff --git a/src/main/java/org/usf/jquery/core/DBQuery.java b/src/main/java/org/usf/jquery/core/DBQuery.java index 34761a66..5d492386 100644 --- a/src/main/java/org/usf/jquery/core/DBQuery.java +++ b/src/main/java/org/usf/jquery/core/DBQuery.java @@ -15,7 +15,7 @@ public interface DBQuery extends DBView, Typed { Collection columns(); @Override - default JavaType getType() { + default JDBCType getType() { var cols = columns(); if(cols.size() == 1) { return cols.iterator().next().getType(); diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index dab11bf5..f8f047f5 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -17,7 +17,7 @@ public final class OperationColumn implements DBColumn { private final Operator operator; private final Object[] args; - private final JavaType type; + private final JDBCType type; public OperationColumn(Operator operation, Object[] args) { this(operation, args, null); @@ -29,7 +29,7 @@ public String sql(QueryParameterBuilder builder) { } @Override - public JavaType getType() { + public JDBCType getType() { return type; } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 87b30a00..6b2ca408 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -49,7 +49,7 @@ default OperationColumn args(Object... args) { return new OperationColumn(this, args); // no type } - default OperationColumn args(JavaType type, Object... args) { + default OperationColumn args(JDBCType type, Object... args) { return new OperationColumn(this, args, type); } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 9cd87d7a..5a52709e 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -108,7 +108,7 @@ public RequestQuery build(){ public RequestQuery build(String schema) { log.trace("building query..."); // requireNonEmpty(tables); - requireNonEmpty(columns); + requireNonEmpty(columns, "columns"); var bg = currentTimeMillis(); var pb = parametrized(schema, views); var sb = new SqlStringBuilder(1000); //avg diff --git a/src/main/java/org/usf/jquery/core/TableView.java b/src/main/java/org/usf/jquery/core/TableView.java index c01bc229..0e019c6d 100644 --- a/src/main/java/org/usf/jquery/core/TableView.java +++ b/src/main/java/org/usf/jquery/core/TableView.java @@ -30,8 +30,11 @@ public TableView(String schema, String name) { @Override public String sql(QueryParameterBuilder builder) { - var scm = nonNull(schema) ? schema : builder.getSchema(); //priority order - return member(scm, name); + return member(schema(builder.getSchema()), name); + } + + public String schema(String defaultSchema) { + return nonNull(schema) ? schema : defaultSchema; //priority order } @Override diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 041a9c37..32a6327a 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -16,7 +16,7 @@ public class TypedOperator implements Operator { private final ArgTypeRef typeFn; private final ParameterSet parameterSet; - public TypedOperator(JavaType type, Operator function, Parameter... parameter) { + public TypedOperator(JDBCType type, Operator function, Parameter... parameter) { this(o-> type, function, parameter); } diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 94c6ed9b..c4bebf82 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -47,6 +47,7 @@ public static Database currentDatabase() { return context.get(); } + @Deprecated public static void currentDatabase(Database db) { context.set(db); } diff --git a/src/main/java/org/usf/jquery/core/Validation.java b/src/main/java/org/usf/jquery/core/Validation.java index 470e6774..1ca99f7f 100644 --- a/src/main/java/org/usf/jquery/core/Validation.java +++ b/src/main/java/org/usf/jquery/core/Validation.java @@ -39,8 +39,8 @@ public static T[] requireNonEmpty(T[] arr){ return arr; } - public static Collection requireNonEmpty(Collection c){ - illegalArgumentIf(isNull(c) || c.isEmpty(), "empty collection"); + public static Collection requireNonEmpty(Collection c, String name){ + illegalArgumentIf(isNull(c) || c.isEmpty(), name + " is empty"); return c; } diff --git a/src/main/java/org/usf/jquery/core/WhenExpression.java b/src/main/java/org/usf/jquery/core/WhenExpression.java index 2b61fced..5eb54580 100644 --- a/src/main/java/org/usf/jquery/core/WhenExpression.java +++ b/src/main/java/org/usf/jquery/core/WhenExpression.java @@ -37,7 +37,7 @@ public String sql(QueryParameterBuilder arg) { } @Override - public JavaType getType() { + public JDBCType getType() { return typeOf(value).orElse(null); } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 3c6d26d2..d0c51f18 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -47,7 +47,7 @@ public class ArgumentParsers { BIGINT, DOUBLE, DATE, TIMESTAMP, TIME, TIMESTAMP_WITH_TIMEZONE }; - public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType... types) { + public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType... types) { if(isEmpty(types) || Stream.of(types).allMatch(o-> o.getClass() == JDBCType.class)) { return parseJdbc(entry, td, castArray(types, JDBCType[].class, JDBCType[]::new)); } @@ -57,7 +57,7 @@ public static Object parse(RequestEntryChain entry, TableDecorator td, JavaType. throw new UnsupportedOperationException("unsupported types " + Arrays.toString(types)); } - public static Object parseJdbc(RequestEntryChain entry, TableDecorator td, JDBCType... types) { + public static Object parseJdbc(RequestEntryChain entry, ViewDecorator td, JDBCType... types) { ParseException ex = null; // preserve last exception try { return matchTypes((Typed) parseJQuery(entry, td, COLUMN, QUERY), types); //try parse column | query first @@ -76,7 +76,7 @@ public static Object parseJdbc(RequestEntryChain entry, TableDecorator td, JDBCT throw ex; } - public static Object parseJQuery(RequestEntryChain entry, TableDecorator td, JQueryType... types) { + public static Object parseJQuery(RequestEntryChain entry, ViewDecorator td, JQueryType... types) { ParseException ex = null; // preserve last exception for(var type : types) { try { diff --git a/src/main/java/org/usf/jquery/web/ColumnBuilder.java b/src/main/java/org/usf/jquery/web/ColumnBuilder.java index 12c9245d..37abd15c 100644 --- a/src/main/java/org/usf/jquery/web/ColumnBuilder.java +++ b/src/main/java/org/usf/jquery/web/ColumnBuilder.java @@ -10,5 +10,5 @@ @FunctionalInterface public interface ColumnBuilder { - DBColumn build(TableDecorator table); + DBColumn build(ViewDecorator table); } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 766d8329..6d879481 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -14,19 +14,19 @@ public interface ColumnDecorator { String identity(); //URL unique - default String reference(TableDecorator td) { //JSON + default String reference(ViewDecorator td) { //JSON return identity(); } - default JDBCType type(TableDecorator td) { + default JDBCType type(ViewDecorator td) { return null; } - default JDBCArgumentParser parser(TableDecorator td){ // override parser | format | local | validation + default JDBCArgumentParser parser(ViewDecorator td){ // override parser | format | local | validation return null; // jdbcArgParser(dataType(td)) } - default ColumnBuilder builder(TableDecorator td) { //set type if null + default ColumnBuilder builder(ViewDecorator td) { //set type if null return null; // no builder by default } @@ -34,15 +34,15 @@ default CriteriaBuilder criteria(String name) { return null; // no criteria by default } - default String pattern(TableDecorator td) { + default String pattern(ViewDecorator td) { throw new UnsupportedOperationException(); //improve API security and performance } - default boolean canSelect(TableDecorator td) { + default boolean canSelect(ViewDecorator td) { throw new UnsupportedOperationException(); //authorization inject } - default boolean canFilter(TableDecorator td) { + default boolean canFilter(ViewDecorator td) { throw new UnsupportedOperationException(); //authorization inject } @@ -54,7 +54,7 @@ public String identity() { } @Override - public ColumnBuilder builder(TableDecorator td) { + public ColumnBuilder builder(ViewDecorator td) { return cb; } }; diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index ca938c75..2e608d0b 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -10,12 +10,9 @@ import static org.usf.jquery.core.JDBCType.REAL; import static org.usf.jquery.core.JDBCType.fromDataType; import static org.usf.jquery.core.Utils.UNLIMITED; -import static org.usf.jquery.core.Validation.requireLegalVariable; import java.sql.Timestamp; -import org.usf.jquery.core.JDBCType; -import org.usf.jquery.core.Validation; import org.usf.jquery.core.ViewColumn; import lombok.AccessLevel; @@ -49,7 +46,6 @@ public void update(int type, int size, int precision) { if(!overConfigured) { var ct = fromDataType(type).orElse(OTHER); if(ct != column.getType()) { - column = new ViewColumn(column.getView(), column.getName(), column.getTag(), ct); } this.dataSize = size; @@ -62,15 +58,18 @@ public String toJavaType(){ } public String toSqlType(){ + var dataType = column.getType(); var s = dataType.name(); - if(dataType.typeClass() == String.class && dataSize < MAX_VALUE) { - s+= "(" + dataSize + ")"; - } - if(dataType.typeClass() == Timestamp.class) { - s+= "(" + precision + ")"; - } - if(dataType == REAL || dataType == NUMERIC || dataType == DECIMAL || dataType == FLOAT || dataType == DOUBLE) { - s+= "(" + dataSize + "," + precision + ")"; + if(!overConfigured) { + if(dataType.typeClass() == String.class && dataSize < MAX_VALUE) { + s+= "(" + dataSize + ")"; + } + if(dataType.typeClass() == Timestamp.class) { + s+= "(" + precision + ")"; + } + if(dataType == REAL || dataType == NUMERIC || dataType == DECIMAL || dataType == FLOAT || dataType == DOUBLE) { + s+= "(" + dataSize + "," + precision + ")"; + } } return s; } diff --git a/src/main/java/org/usf/jquery/web/DatabaseConfiguration.java b/src/main/java/org/usf/jquery/web/DatabaseConfiguration.java new file mode 100644 index 00000000..2756d580 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/DatabaseConfiguration.java @@ -0,0 +1,64 @@ +package org.usf.jquery.web; + +import static java.util.Objects.nonNull; +import static java.util.Objects.requireNonNull; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toUnmodifiableMap; +import static org.usf.jquery.core.Validation.requireNonEmpty; +import static org.usf.jquery.web.NoSuchResourceException.throwNoSuchTableException; + +import java.util.Collection; +import java.util.Map; +import java.util.function.Function; + +import javax.sql.DataSource; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * + * @author u$f + * + */ +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class DatabaseConfiguration { + + private final DatabaseDecorator database; + private final Map views; + private final Map columns; + private final DataSource dataSource; //optional + private final String schema; //optional + + public ViewDecorator getTable(String name) { + var vd = views.get(name); + if(nonNull(vd)) { + return vd; + } + throw throwNoSuchTableException(name); + } + + public static final DatabaseConfiguration of(DatabaseDecorator database, + Collection views, Collection columns) { + return of(database, views, columns, null, null); + } + + public static final DatabaseConfiguration of(DatabaseDecorator database, + Collection views, Collection columns, DataSource ds) { + return of(database, views, columns, ds, null); + } + + public static final DatabaseConfiguration of(DatabaseDecorator database, + Collection views, Collection columns, DataSource ds, String schema) { + return new DatabaseConfiguration(requireNonNull(database, "configuration.database"), + unmodifiableMap(requireNonEmpty(views, "configuration.views"), ViewDecorator::identity), + unmodifiableMap(requireNonEmpty(columns, "configuration.columns"), ColumnDecorator::identity), + ds, schema); + } + + static Map unmodifiableMap(Collection c, Function fn){ + return c.stream().collect(toUnmodifiableMap(fn, identity())); + } +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/DatabaseDecorator.java b/src/main/java/org/usf/jquery/web/DatabaseDecorator.java new file mode 100644 index 00000000..53011f2c --- /dev/null +++ b/src/main/java/org/usf/jquery/web/DatabaseDecorator.java @@ -0,0 +1,35 @@ +package org.usf.jquery.web; + +import static java.util.Objects.nonNull; +import static org.usf.jquery.core.Validation.requireLegalVariable; + +import org.usf.jquery.core.DBView; +import org.usf.jquery.core.TableView; + +/** + * + * @author u$f + * + */ +public interface DatabaseDecorator { + + String identity(); //URL + + String viewName(ViewDecorator vd); //[schema.]table + + default DBView tableView(ViewDecorator vd) { + var tn = viewName(vd); + if(nonNull(tn)){ + var idx = tn.indexOf('.'); + return idx == -1 + ? new TableView(requireLegalVariable(tn)) + : new TableView(requireLegalVariable(tn.substring(0, idx)), + requireLegalVariable(tn.substring(idx, tn.length()))); + } + var b = vd.builder(); + if(nonNull(b)){ + return b.build(vd.identity()); //safe ? + } + throw new IllegalStateException("require viewName or builder on " + vd); + } +} diff --git a/src/main/java/org/usf/jquery/web/DatabaseManager.java b/src/main/java/org/usf/jquery/web/DatabaseManager.java new file mode 100644 index 00000000..31d5790b --- /dev/null +++ b/src/main/java/org/usf/jquery/web/DatabaseManager.java @@ -0,0 +1,59 @@ +package org.usf.jquery.web; + +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; +import static org.usf.jquery.core.SqlStringBuilder.quote; + +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * + * @author u$f + * + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class DatabaseManager { + + private static final Map DATABASES = new HashMap<>(); + private static final ThreadLocal LOCAL_DB = new ThreadLocal<>(); + + public void register(DatabaseConfiguration config) { + DATABASES.compute(config.getDatabase().identity(), (id,dm)->{ + if(isNull(dm)) { + return new DatabaseMetadata(config); + } + throw new IllegalStateException("database configuration conflict " + quote(id)); + }); + } + + public static DatabaseMetadata currentDatabase() { + var db = LOCAL_DB.get(); + if(nonNull(db)) { + return db; + } + if(DATABASES.size() == 1) { //default database + return DATABASES.values().iterator().next(); + } + throw DATABASES.isEmpty() + ? new NoSuchElementException("no database configured") + : new IllegalStateException("no database selected"); + } + + public static DatabaseMetadata switchDatabase(String name) { + var db = DATABASES.get(name); + if(nonNull(db)) { + LOCAL_DB.set(db); + return db; + } + throw new NoSuchElementException(quote(name) + " was not initialized"); + } + + public static void releaseDatabase() { + LOCAL_DB.remove(); + } +} diff --git a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java index 4de4c7a8..97c99c5d 100644 --- a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java +++ b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java @@ -3,28 +3,27 @@ import static java.lang.String.format; import static java.lang.System.currentTimeMillis; import static java.time.Instant.now; -import static java.util.Collections.emptyMap; import static java.util.Comparator.comparing; import static java.util.Objects.isNull; -import static java.util.Optional.ofNullable; +import static java.util.Objects.nonNull; +import static java.util.function.Function.identity; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toUnmodifiableMap; import static org.usf.jquery.core.Utils.isPresent; +import static org.usf.jquery.core.Validation.requireLegalVariable; +import static org.usf.jquery.web.ColumnMetadata.columnMetadata; import java.sql.SQLException; import java.time.Instant; import java.time.YearMonth; -import java.util.Collection; +import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import java.util.Optional; -import java.util.function.Supplier; import java.util.stream.Stream; -import javax.sql.DataSource; - import org.usf.jquery.core.Database; +import org.usf.jquery.core.ViewColumn; import lombok.AccessLevel; import lombok.Getter; @@ -37,38 +36,48 @@ * */ @Slf4j -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@Getter(AccessLevel.PACKAGE) +@RequiredArgsConstructor public final class DatabaseMetadata { private final Object mutex = new Object(); - @Getter(AccessLevel.PACKAGE) - private final DataSource dataSource; //nullable if no sync - private final Map tables; //empty if no sync + private final DatabaseConfiguration config; + private final Map tables = new HashMap<>(); //lazy loading @Getter private Instant lastUpdate; @Getter private Database type; - public TableMetadata tableMetada(TableDecorator td, Supplier supp){ - return tables.computeIfAbsent(td.identity(), id-> supp.get()); + public ViewMetadata viewMetadata(ViewDecorator td){ + return tables.computeIfAbsent(td.identity(), id-> { + var view = config.getDatabase().tableView(td); + var meta = config.getColumns().values().stream().mapMulti((cd, acc)-> { + var cn = td.columnName(cd); + if(nonNull(cn)) { //ViewColumn only + var col = new ViewColumn(view, requireLegalVariable(cn), requireLegalVariable(cd.reference(td)), cd.type(td)); + acc.accept(columnMetadata(col)); + } //tag = reference + }).collect(toUnmodifiableMap(cm-> cm.getColumn().getTag(), identity())); + return new ViewMetadata(view, meta); + }); } public void fetch() { - if(isNull(dataSource) || tables.isEmpty()) { + var ds = config.getDataSource(); + if(isNull(ds) || tables.isEmpty()) { log.warn("database resources not initialized"); //full scan ? next release return; } synchronized (mutex) { //thread safe var time = currentTimeMillis(); - log.info("Scanning database metadata..."); - try(var cn = dataSource.getConnection()){ + log.info("scanning database metadata..."); + try(var cn = ds.getConnection()){ var metadata = cn.getMetaData(); type = Database.of(metadata.getDatabaseProductName()).orElse(null); for(var t : tables.values()) { log.info("Scanning table '{}' metadata...", t.getView()); - t.fetch(metadata); - logTableColumns(t.getColumns()); + t.fetch(metadata, config.getSchema()); if(t instanceof YearTableMetadata yt) { log.info("Scanning table '{}' revisions...", t.getView()); yt.fetchRevisions(cn); @@ -84,20 +93,6 @@ public void fetch() { } } - static void logTableColumns(Map map) { - if(!map.isEmpty()) { - var pattern = "|%-20s|%-15s|%-25s|%-20s|"; - var bar = format(pattern, "", "", "", "").replace("|", "+").replace(" ", "-"); - log.info(bar); - log.info(format(pattern, "ID", "CLASS", "COLUMN", "TYPE")); - log.info(bar); - map.entrySet().forEach(e-> - log.info(format(pattern, e.getKey(), e.getValue().toJavaType(), - e.getValue().getColumn(), e.getValue().toSqlType()))); - log.info(bar); - } - } - static void logRevisions(YearMonth[] revs) { if(isPresent(revs)) { var pattern = "|%-5s|%-40s|"; @@ -111,14 +106,5 @@ static void logRevisions(YearMonth[] revs) { log.info(bar); } } - - static DatabaseMetadata create(DataSource ds, Collection tables, Collection columns) { - return new DatabaseMetadata(ds, tables.stream() - .collect(toUnmodifiableMap(TableDecorator::identity, t-> t.createMetadata(columns)))); - } - - static DatabaseMetadata emptyMetadata() { - return new DatabaseMetadata(null, emptyMap()); - } } diff --git a/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java index fca1c8a6..f13b0319 100644 --- a/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java +++ b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java @@ -13,7 +13,7 @@ public interface JDBCArgumentParser extends JavaArgumentParser { Object nativeParse(String v); @Override - default Object parseEntry(RequestEntryChain entry, TableDecorator td) { + default Object parseEntry(RequestEntryChain entry, ViewDecorator td) { return parseValue(entry.requireNoArgs().getValue()); } diff --git a/src/main/java/org/usf/jquery/web/JQueryContext.java b/src/main/java/org/usf/jquery/web/JQueryContext.java deleted file mode 100644 index 5404a674..00000000 --- a/src/main/java/org/usf/jquery/web/JQueryContext.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.usf.jquery.web; - -import static java.util.Objects.requireNonNull; -import static java.util.Objects.requireNonNullElseGet; -import static java.util.Optional.ofNullable; -import static java.util.function.Function.identity; -import static java.util.stream.Collectors.toUnmodifiableMap; -import static org.usf.jquery.web.DatabaseMetadata.create; -import static org.usf.jquery.web.NoSuchResourceException.throwNoSuchColumnException; -import static org.usf.jquery.web.NoSuchResourceException.throwNoSuchTableException; - -import java.util.Collection; -import java.util.Map; - -import javax.sql.DataSource; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -/** - * - * @author u$f - * - */ -@Getter(AccessLevel.PACKAGE) -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public final class JQueryContext { - - private static JQueryContext instance; //!important : default init - - private final Map tables; - private final Map columns; - private DatabaseMetadata database; - - public static JQueryContext context(){ - return requireNonNull(instance, ()-> "JQuery has not been initialized"); - } - - public static DatabaseMetadata database(){ - return requireNonNullElseGet(context().database, DatabaseMetadata::emptyMetadata); // by default - } - - public static JQueryContext register( - @NonNull Collection tables, - @NonNull Collection columns){ - instance = new JQueryContext( - tables.stream().collect(toUnmodifiableMap(TableDecorator::identity, identity())), - columns.stream().collect(toUnmodifiableMap(ColumnDecorator::identity, identity()))); - return instance; - } - - public DatabaseMetadata bind(DataSource ds){ //default schema - database = create(ds, tables.values(), columns.values()); - return database; - } - - public boolean isDeclaredTable(String id) { - return tables.containsKey(id); - } - - public TableDecorator getTable(String value) { - return ofNullable(tables.get(value)). - orElseThrow(()-> throwNoSuchTableException(value)); - } - - public boolean isDeclaredColumn(String id) { - return columns.containsKey(id); - } - - public ColumnDecorator getColumn(String value) { - return ofNullable(columns.get(value)) - .orElseThrow(()-> throwNoSuchColumnException(value)); - } -} diff --git a/src/main/java/org/usf/jquery/web/JavaArgumentParser.java b/src/main/java/org/usf/jquery/web/JavaArgumentParser.java index 7ef94a6e..cc10d2fc 100644 --- a/src/main/java/org/usf/jquery/web/JavaArgumentParser.java +++ b/src/main/java/org/usf/jquery/web/JavaArgumentParser.java @@ -8,5 +8,5 @@ @FunctionalInterface public interface JavaArgumentParser { - Object parseEntry(RequestEntryChain entry, TableDecorator td); + Object parseEntry(RequestEntryChain entry, ViewDecorator td); } diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index eddf3287..02a9e3a9 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -19,23 +19,18 @@ */ @Getter @RequiredArgsConstructor -final class QueryDecorator implements TableDecorator { +final class QueryDecorator implements ViewDecorator { private final DBQuery query; - @Override - public String tableName() { - return null; - } - @Override public String identity() { return query.id(); } @Override - public Optional columnName(ColumnDecorator cd) { - return column(cd.identity()).map(TaggableColumn::tagname); + public String columnName(ColumnDecorator cd) { + return column(cd.identity()).map(TaggableColumn::tagname).orElse(null); } public Optional lookupColumnDecorator(String cn) { diff --git a/src/main/java/org/usf/jquery/web/RequestContext.java b/src/main/java/org/usf/jquery/web/RequestContext.java index d3f9bf3c..cd00734f 100644 --- a/src/main/java/org/usf/jquery/web/RequestContext.java +++ b/src/main/java/org/usf/jquery/web/RequestContext.java @@ -23,11 +23,11 @@ public final class RequestContext { private static final ThreadLocal local = new ThreadLocal<>(); - private final Map viewDecorators; + private final Map viewDecorators; private final Map columnDecorators; private final Map workQueries = new LinkedHashMap<>(); //work queries - public Optional lookupViewDecorator(String id) { + public Optional lookupViewDecorator(String id) { log.trace("lookup view decorator : {}", id); return ofNullable(viewDecorators.get(id)); } @@ -42,7 +42,7 @@ public Optional lookupView(String id) { return ofNullable(workQueries.get(id)); } - public void putViewDecorator(TableDecorator v) { + public void putViewDecorator(ViewDecorator v) { if(!viewDecorators.containsKey(v.identity())) { viewDecorators.put(v.identity(), v); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 776213e0..d257798e 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -37,8 +37,6 @@ import java.util.function.IntFunction; import java.util.function.Predicate; -import javax.swing.text.View; - import org.usf.jquery.core.BadArgumentException; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; @@ -53,6 +51,7 @@ import org.usf.jquery.core.Order; import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; +import org.usf.jquery.core.QueryView; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedOperator; @@ -60,7 +59,6 @@ import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; import org.usf.jquery.core.ViewJoin.JoinType; -import org.usf.jquery.core.QueryView; import org.usf.jquery.core.WindowFunction; import lombok.AccessLevel; @@ -92,12 +90,12 @@ public RequestEntryChain(String value) { this(value, false); } - public QueryView evalQuery(TableDecorator td) { + public QueryView evalQuery(ViewDecorator td) { return evalQuery(td, false); } - public ViewJoin evalJoin(TableDecorator td) { + public ViewJoin evalJoin(ViewDecorator td) { if(value.matches(JoinType.pattern())) { var jt = JoinType.valueOf(value); var args = toArgs(td, null, null); @@ -109,7 +107,7 @@ public ViewJoin evalJoin(TableDecorator td) { //evalView query|view:alias - public QueryView evalQuery(TableDecorator td, boolean requireTag) { + public QueryView evalQuery(ViewDecorator td, boolean requireTag) { if(SELECT.equals(value)) { var q = new RequestQueryBuilder().columns(toColumnArgs(td, false)); var e = this; @@ -133,7 +131,7 @@ public QueryView evalQuery(TableDecorator td, boolean requireTag) { throw cannotParseException(SELECT, this.toString()); } - public Partition evalPartition(TableDecorator td) { + public Partition evalPartition(ViewDecorator td) { if(PARTITION.equals(value)) { var p = new Partition(toColumnArgs(td, true)); if(next()) { //TD loop @@ -150,7 +148,7 @@ public Partition evalPartition(TableDecorator td) { throw cannotParseException(PARTITION, this.toString()); } - public TaggableColumn evalColumn(TableDecorator td) { + public TaggableColumn evalColumn(ViewDecorator td) { var r = chainColumnOperations(td, false); //throws ParseException if(r.entry.isLast()) { if(nonNull(r.entry.tag)) { //TD: required tag if operation @@ -165,7 +163,7 @@ public TaggableColumn evalColumn(TableDecorator td) { throw unexpectedEntryException(r.entry.next.toString(), "[view].column[.op]*"); } - public DBOrder evalOrder(TableDecorator td) { + public DBOrder evalOrder(ViewDecorator td) { var r = chainColumnOperations(td, false); //throws ParseException if(r.entry.isLast()) { // no order return r.col.order(); @@ -178,11 +176,11 @@ public DBOrder evalOrder(TableDecorator td) { throw unexpectedEntryException(e.toString(), "asc|desc"); } - public DBFilter evalFilter(TableDecorator td) { + public DBFilter evalFilter(ViewDecorator td) { return evalFilter(td, null); } - public DBFilter evalFilter(TableDecorator td, List values) { + public DBFilter evalFilter(ViewDecorator td, List values) { try { var r = chainColumnOperations(td, true); var e = r.entry; @@ -206,7 +204,7 @@ public DBFilter evalFilter(TableDecorator td, List values) { } } - Optional tableCriteria(TableDecorator td, List values) { + Optional tableCriteria(ViewDecorator td, List values) { RequestEntryChain e = null; CriteriaBuilder c = null; var res = currentContext().lookupViewDecorator(value); @@ -241,7 +239,7 @@ private RequestEntryChain updateArgs(List values) { return e; } - DBFilter chainComparator(TableDecorator td, ColumnDecorator cd, DBColumn col){ + DBFilter chainComparator(ViewDecorator td, ColumnDecorator cd, DBColumn col){ var f = lookupComparator(value).map(c-> c.args(toArgs(td, col, c.getParameterSet()))).orElse(null); //eval comparator first => avoid overriding if(isNull(f) && col instanceof TaggableColumn) { //no operation var c = cd.criteria(value); //criteria lookup @@ -255,7 +253,7 @@ DBFilter chainComparator(TableDecorator td, ColumnDecorator cd, DBColumn col){ throw cannotParseException("comparison|criteria", this.toString()); } - DBFilter chainComparator(TableDecorator td, DBFilter f){ + DBFilter chainComparator(ViewDecorator td, DBFilter f){ var e = next; while(nonNull(e)) { if(e.value.matches("and|or")) { @@ -270,7 +268,7 @@ DBFilter chainComparator(TableDecorator td, DBFilter f){ return f; } - private ResourceCursor chainColumnOperations(TableDecorator td, boolean filter) { + private ResourceCursor chainColumnOperations(ViewDecorator td, boolean filter) { var r = lookupResource(td).orElseThrow(()-> cannotParseException(COLUMN, this.toString())); var e = r.entry.next; while(nonNull(e)) { // chain until !operator @@ -287,7 +285,7 @@ private ResourceCursor chainColumnOperations(TableDecorator td, boolean filter) return r; } - private static DBColumn windowColumn(TableDecorator td, TaggableColumn column) { + private static DBColumn windowColumn(ViewDecorator td, TaggableColumn column) { var vw = currentContext().lookupView(td.identity()).orElse(null); if(vw instanceof CompletableViewQuery) { // already create ((CompletableViewQuery)vw).getQuery().columns(column); @@ -300,7 +298,7 @@ private static DBColumn windowColumn(TableDecorator td, TaggableColumn column) { return new ViewColumn(vw, doubleQuote(column.tagname()), null, column.getType()); } - private Optional lookupResource(TableDecorator td) { + private Optional lookupResource(ViewDecorator td) { if(next()) { //check td.cd first var rc = currentContext().lookupViewDecorator(value) .flatMap(v-> next.lookupViewResource(v, RequestEntryChain::isWindowFunction)); @@ -312,7 +310,7 @@ private Optional lookupResource(TableDecorator td) { return lookupViewResource(td, fn-> true); // all operations } - private Optional lookupViewResource(TableDecorator td, Predicate pre) { + private Optional lookupViewResource(ViewDecorator td, Predicate pre) { var res = td.getClass() == QueryDecorator.class ? ((QueryDecorator)td).lookupColumnDecorator(value) : currentContext().lookupColumnDecorator(value); @@ -325,7 +323,7 @@ private Optional lookupViewResource(TableDecorator td, Predicate return res.map(cd-> new ResourceCursor(td, cd, this)); } - private Optional toOperation(TableDecorator td, DBColumn col, Predicate pre) { + private Optional toOperation(ViewDecorator td, DBColumn col, Predicate pre) { return lookupOperator(value).filter(pre).map(fn-> { var c = col; if(isNull(c) && isEmpty(args) && "count".equals(value)) { // id is MAJ @@ -338,19 +336,19 @@ private Optional toOperation(TableDecorator td, DBColumn col, P }); } - private TaggableColumn[] toColumnArgs(TableDecorator td, boolean allowEmpty) { + private TaggableColumn[] toColumnArgs(ViewDecorator td, boolean allowEmpty) { return (TaggableColumn[]) toArgs(td, JQueryType.COLUMN, allowEmpty); } - private DBFilter[] toFilterArgs(TableDecorator td) { + private DBFilter[] toFilterArgs(ViewDecorator td) { return (DBFilter[]) toArgs(td, JQueryType.FILTER, false); } - private DBOrder[] toOderArgs(TableDecorator td) { + private DBOrder[] toOderArgs(ViewDecorator td) { return (DBOrder[]) toArgs(td, JQueryType.ORDER, false); } - private Object[] toArgs(TableDecorator td, JavaType type, boolean allowEmpty) { + private Object[] toArgs(ViewDecorator td, JavaType type, boolean allowEmpty) { var c = type.typeClass(); if(DBObject.class.isAssignableFrom(c)) { // JQuery types & !array var ps = allowEmpty @@ -361,15 +359,15 @@ private Object[] toArgs(TableDecorator td, JavaType type, boolean allowEmpty) { throw new UnsupportedOperationException("cannot instanitate type " + c); } - private Object toOneArg(TableDecorator td, JavaType type) { + private Object toOneArg(ViewDecorator td, JavaType type) { return toArgs(td, null, ofParameters(required(type)))[0]; } - private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps) { + private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps) { return toArgs(td, col, ps, Object[]::new); } - private Object[] toArgs(TableDecorator td, DBObject col, ParameterSet ps, IntFunction arrFn) { + private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunction arrFn) { int inc = isNull(col) ? 0 : 1; var arr = arrFn.apply(isNull(args) ? inc : args.size() + inc); if(nonNull(col)) { @@ -444,12 +442,12 @@ private static String[] toStringArray(List entries) { static final class ResourceCursor { - private final TableDecorator td; + private final ViewDecorator td; private final ColumnDecorator cd; private RequestEntryChain entry; private DBColumn col; - public ResourceCursor(TableDecorator td, ColumnDecorator cd, RequestEntryChain entry) { + public ResourceCursor(ViewDecorator td, ColumnDecorator cd, RequestEntryChain entry) { this.td = td; this.cd = cd; this.entry = entry; diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParam.java b/src/main/java/org/usf/jquery/web/RequestQueryParam.java index 1ab83a26..df7a7ecc 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParam.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParam.java @@ -16,11 +16,13 @@ @Documented public @interface RequestQueryParam { - String name(); //table identity + String view(); //view identity + + String database() default ""; //optional database identity String[] defaultColumns() default {}; - String[] ignoreParameters() default {}; //should not be parsed by JQuery + String[] ignoreParameters() default {}; // !parsed boolean aggregationOnly() default false; // else throw IllegalDataAccessException } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index 919b56b2..ac7265c0 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -4,7 +4,8 @@ import static org.usf.jquery.core.Utils.isPresent; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; -import static org.usf.jquery.web.JQueryContext.context; +import static org.usf.jquery.web.DatabaseManager.currentDatabase; +import static org.usf.jquery.web.DatabaseManager.switchDatabase; import java.util.LinkedHashMap; import java.util.Map; @@ -26,7 +27,7 @@ public final class RequestQueryParamResolver { public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull Map parameterMap) { - var t = System.currentTimeMillis(); + var t = currentTimeMillis(); log.trace("parsing request..."); parameterMap = new LinkedHashMap<>(parameterMap); //unmodifiable map if(!parameterMap.containsKey(COLUMN) && !parameterMap.containsKey(COLUMN_DISTINCT)) { @@ -35,8 +36,9 @@ public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull if(isPresent(ant.ignoreParameters())) { Stream.of(ant.ignoreParameters()).forEach(parameterMap::remove); } - var req = context() - .getTable(ant.name()) + var db = ant.database().isEmpty() ? currentDatabase() : switchDatabase(ant.database()); + var req = db.getConfig() + .getTable(ant.view()) .query(parameterMap); //may edit map if(ant.aggregationOnly() && !req.isAggregation()) { throw new IllegalDataAccessException("non aggregation query"); diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index 33533750..59b51da4 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -12,8 +12,8 @@ import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; -import org.usf.jquery.core.TableView; import org.usf.jquery.core.QueryParameterBuilder; +import org.usf.jquery.core.TableView; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/org/usf/jquery/web/TableMetadata.java b/src/main/java/org/usf/jquery/web/TableMetadata.java deleted file mode 100644 index ee5c2e2f..00000000 --- a/src/main/java/org/usf/jquery/web/TableMetadata.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.usf.jquery.web; - -import static java.lang.String.join; -import static java.util.Objects.nonNull; -import static java.util.function.Function.identity; -import static java.util.stream.Collectors.toMap; -import static org.usf.jquery.core.QueryParameterBuilder.parametrized; -import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.web.JQueryContext.database; - -import java.sql.DatabaseMetaData; -import java.sql.SQLException; -import java.time.Instant; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; - -import org.usf.jquery.core.DBQuery; -import org.usf.jquery.core.DBView; -import org.usf.jquery.core.QueryParameterBuilder; -import org.usf.jquery.core.TableView; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - -/** - * - * @author u$f - * - */ -@ToString -@Getter(AccessLevel.PACKAGE) -public class TableMetadata { - - private DBView view; //nullable if query - private Map columns; - @Getter - @Setter(AccessLevel.PACKAGE) - private Instant lastUpdate; - - public TableMetadata(DBView view, Map columns) { - this.view = view; - this.columns = columns; - } - - public ColumnMetadata columnMetada(ColumnDecorator cd) { - return columns.get(cd.identity()); - } - - public void fetch() throws SQLException { //individually table fetching - try(var cn = database().getDataSource().getConnection()) { - fetch(cn.getMetaData()); - } - } - - void fetch(DatabaseMetaData metadata) throws SQLException { - if(view instanceof TableView tv) { - try(var rs = metadata.getColumns(null, tv.getSchema(), tv.getName(), null)){ - if(rs.next()) { - var db = columns.values().stream().collect(toMap(m-> m.getColumn().getName(), identity())); - do { - var cm = db.remove(rs.getString("COLUMN_NAME")); - if(nonNull(cm)) { - cm.update( - rs.getInt("DATA_TYPE"), - rs.getInt("COLUMN_SIZE"), - rs.getInt("DECIMAL_DIGITS")); - } // else undeclared column - } while(rs.next()); - if(!db.isEmpty()) { - throw new NoSuchElementException("column(s) [" + join(", ", db.keySet()) + "] not found in " + view.toString()); - } - } - else { - throw new NoSuchElementException(quote(view.toString()) + " table not found"); - } - } - } - else if(view instanceof DBQuery qr) { - var b = parametrized(new ArrayList<>()); - try(var ps = metadata.getConnection().prepareStatement("SELECT * FROM(" + qr.sql(b) + ") WHERE 1=0"); - var rs = ps.executeQuery()){ - var db = new HashMap<>(columns); - var meta = rs.getMetaData(); - for(var i=1; i<=meta.getColumnCount(); i++) { - var cm = db.remove(meta.getColumnName(i)); - if(nonNull(cm)) { - cm.update(meta.getColumnType(i), meta.getColumnDisplaySize(i), meta.getPrecision(i)); - } // else undeclared column - } - if(!db.isEmpty()) { - throw new NoSuchElementException("column(s) [" + join(", ", db.keySet()) + "] not found in " + view.toString()); - } - } - view = null; - } - } -} diff --git a/src/main/java/org/usf/jquery/web/TableDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java similarity index 72% rename from src/main/java/org/usf/jquery/web/TableDecorator.java rename to src/main/java/org/usf/jquery/web/ViewDecorator.java index e100c450..c71d2393 100644 --- a/src/main/java/org/usf/jquery/web/TableDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -2,12 +2,8 @@ import static java.lang.Integer.parseInt; import static java.util.Objects.nonNull; -import static java.util.stream.Collectors.toUnmodifiableMap; import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Validation.requireLegalVariable; -import static org.usf.jquery.web.ColumnMetadata.columnMetadata; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; import static org.usf.jquery.web.Constants.FETCH; @@ -15,8 +11,7 @@ import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.RESERVED_WORDS; import static org.usf.jquery.web.Constants.VIEW; -import static org.usf.jquery.web.JQueryContext.context; -import static org.usf.jquery.web.JQueryContext.database; +import static org.usf.jquery.web.DatabaseManager.currentDatabase; import static org.usf.jquery.web.MissingParameterException.missingParameterException; import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; import static org.usf.jquery.web.RequestContext.clearContext; @@ -26,16 +21,14 @@ import static org.usf.jquery.web.RequestParser.parseEntry; import java.util.Map; -import java.util.function.Function; import java.util.stream.Stream; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBQuery; import org.usf.jquery.core.DBView; import org.usf.jquery.core.RequestQueryBuilder; -import org.usf.jquery.core.TableView; import org.usf.jquery.core.TaggableColumn; -import org.usf.jquery.core.ViewColumn; +import org.usf.jquery.core.Utils; import lombok.NonNull; @@ -44,20 +37,26 @@ * @author u$f * */ -public interface TableDecorator { +public interface ViewDecorator { String identity(); //URL - String tableName(); // schema.table || nullable if builder - String columnName(ColumnDecorator cd); + default ViewBuilder builder() { + return null; // no builder by default + } + + default CriteriaBuilder criteria(String name) { //!aggregation + return null; // no criteria by default + } + default DBView table() { - return metadata().getView(); + return metadata().getView(); //cached view } default TaggableColumn column(@NonNull ColumnDecorator cd) { - var c = metadata().columnMetada(cd); //priority + var c = metadata().columnMetada(cd); //priority order if(nonNull(c)) { return c.getColumn(); } @@ -67,49 +66,14 @@ default TaggableColumn column(@NonNull ColumnDecorator cd) { } throw undeclaredResouceException(identity(), cd.identity()); } - - default ViewBuilder builder() { - return null; // no builder by default - } - - default CriteriaBuilder criteria(String name) { //!aggregation - return null; // no criteria by default - } - default TableMetadata metadata() { - return database().tableMetada(this, ()-> new TableMetadata(tableView(), declaredColumns())); - } - - default DBView tableView() { - var tn = tableName(); - if(nonNull(tn)){ - var idx = tn.indexOf('.'); //schema.view - return idx == -1 - ? new TableView(requireLegalVariable(tn)) - : new TableView(requireLegalVariable(tn.substring(0, idx)), - requireLegalVariable(tn.substring(idx, tn.length()))); - } - var b = builder(); - if(nonNull(b)){ - return b.build(identity()); - } - throw new IllegalArgumentException("viewName or builder expected in " + this); - } - - default Map declaredColumns(){ - var columns = context().getColumns().values(); - return columns.stream().mapMulti((cd, cons)-> { - var cn = columnName(cd); - if(nonNull(cn)) { //only physical column - var col = new ViewColumn(table(), cn, cd.reference(this), cd.type(this)); - cons.accept(columnMetadata(col)); - } //tag = reference - }).collect(toUnmodifiableMap(cm-> cm.getColumn().getTag(), Function.identity())); + default ViewMetadata metadata() { + return currentDatabase().viewMetadata(this); } default RequestQueryBuilder query(Map parameterMap) { try { - currentDatabase(database().getType()); //table database + Utils.currentDatabase(currentDatabase().getType()); //table database var query = new RequestQueryBuilder(); parseViews(query, parameterMap); parseColumns(query, parameterMap); diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java new file mode 100644 index 00000000..2e0ddebd --- /dev/null +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -0,0 +1,122 @@ +package org.usf.jquery.web; + +import static java.lang.String.format; +import static java.lang.String.join; +import static java.util.Objects.nonNull; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; +import static org.usf.jquery.core.QueryParameterBuilder.parametrized; +import static org.usf.jquery.core.SqlStringBuilder.quote; + +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; + +import org.usf.jquery.core.DBQuery; +import org.usf.jquery.core.DBView; +import org.usf.jquery.core.TableView; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +/** + * + * @author u$f + * + */ +@ToString +@Slf4j +@Getter(AccessLevel.PACKAGE) +@RequiredArgsConstructor +public class ViewMetadata { + + private final DBView view; //cache + private final Map columns; + @Getter + @Setter(AccessLevel.PACKAGE) + private Instant lastUpdate; + + public ColumnMetadata columnMetada(ColumnDecorator cd) { + return columns.get(cd.identity()); + } + + void fetch(DatabaseMetaData metadata, String schema) throws SQLException { + if(view instanceof TableView tab) { + fetch(metadata, tab, schema); + } + else if(view instanceof DBQuery query) { + fetch(metadata, query); + } + else { + throw new UnsupportedOperationException("unsupported view " + view); + } + logTableColumns(); + } + + void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { + try(var rs = metadata.getColumns(null, view.schema(schema), view.getName(), null)){ + if(rs.next()) { + var db = columns.values().stream().collect(toMap(m-> m.getColumn().getName(), identity())); + do { + var cm = db.remove(rs.getString("COLUMN_NAME")); + if(nonNull(cm)) { + cm.update( + rs.getInt("DATA_TYPE"), + rs.getInt("COLUMN_SIZE"), + rs.getInt("DECIMAL_DIGITS")); + } // else undeclared column + } while(rs.next()); + if(!db.isEmpty()) { + throw columnsNotFoundException(db); + } + } + else { + throw new NoSuchElementException(quote(view.toString()) + " table not found"); + } + } + } + + void fetch(DatabaseMetaData metadata, DBQuery qr) throws SQLException { + var query = qr.sql(parametrized(new ArrayList<>())); + try(var ps = metadata.getConnection().prepareStatement("SELECT * FROM(" + query + ") WHERE 1=0"); + var rs = ps.executeQuery()){ + var db = new HashMap<>(columns); + var meta = rs.getMetaData(); + for(var i=1; i<=meta.getColumnCount(); i++) { + var cm = db.remove(meta.getColumnLabel(i)); //tag or name + if(nonNull(cm)) { + cm.update(meta.getColumnType(i), meta.getColumnDisplaySize(i), meta.getPrecision(i)); + } // else undeclared column + } + if(!db.isEmpty()) { + throw columnsNotFoundException(db); + } + } + } + + void logTableColumns() { + if(!columns.isEmpty() && log.isInfoEnabled()) { + var pattern = "|%-20s|%-15s|%-25s|%-20s|"; + var bar = format(pattern, "", "", "", "").replace("|", "+").replace(" ", "-"); + log.info(bar); + log.info(format(pattern, "ID", "CLASS", "COLUMN", "TYPE")); + log.info(bar); + columns.entrySet().forEach(e-> + log.info(format(pattern, e.getKey(), e.getValue().toJavaType(), + e.getValue().getColumn(), e.getValue().toSqlType()))); + log.info(bar); + } + } + + NoSuchElementException columnsNotFoundException(Map db) { + return new NoSuchElementException("column(s) [" + join(", ", db.keySet()) + "] not found in " + view.toString()); + } +} diff --git a/src/main/java/org/usf/jquery/web/YearTableDecorator.java b/src/main/java/org/usf/jquery/web/YearTableDecorator.java index 5b1aaf37..ceb2b3d6 100644 --- a/src/main/java/org/usf/jquery/web/YearTableDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearTableDecorator.java @@ -17,13 +17,10 @@ import static org.usf.jquery.web.RevisionIterator.monthFilter; import static org.usf.jquery.web.RevisionIterator.yearColumn; import static org.usf.jquery.web.RevisionIterator.yearTable; -import static org.usf.jquery.web.TableDecorator.flatParameters; -import static org.usf.jquery.web.YearTableMetadata.emptyMetadata; -import static org.usf.jquery.web.YearTableMetadata.yearTableMetadata; +import static org.usf.jquery.web.ViewDecorator.flatParameters; import java.time.Year; import java.time.YearMonth; -import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -31,8 +28,8 @@ import java.util.function.UnaryOperator; import java.util.stream.Stream; -import org.usf.jquery.core.TableView; import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.TableView; import org.usf.jquery.core.TaggableColumn; /** @@ -40,7 +37,7 @@ * @author u$f * */ -public interface YearTableDecorator extends TableDecorator { +public interface YearTableDecorator extends ViewDecorator { Optional monthRevision(); @@ -56,12 +53,12 @@ default YearMonth[] availableRevisions() {//reduce data revision access @Override default TableView table() { - return yearTable(tableName(), identity()); + return yearTable(viewName(), identity()); } @Override default RequestQueryBuilder query(Map parameterMap) { - var query = TableDecorator.super.query(parameterMap); + var query = ViewDecorator.super.query(parameterMap); monthRevision().map(this::column) .ifPresent(c-> query.filters(monthFilter(c))); return query.repeat(iterator(parseRevisions(parameterMap))); @@ -89,8 +86,8 @@ default YearMonth[] parseRevisions(Map parameterMap) { @Override default TaggableColumn column(ColumnDecorator column) { return column == yearRevision() - ? yearColumn().as(yearRevision().reference()) - : TableDecorator.super.column(column); + ? yearColumn().as(yearRevision().reference(this)) + : ViewDecorator.super.column(column); } default UnaryOperator revisionMode(String mode) { @@ -165,13 +162,7 @@ default YearMonth parseYearMonth(String revision) { @Override default YearTableMetadata metadata() { - return (YearTableMetadata) database().tableMetada(this) //safe cast - .orElseGet(()-> emptyMetadata(this)); //not binded - } - - @Override - default YearTableMetadata createMetadata(Collection columns) { - return yearTableMetadata(this, columns); + return (YearTableMetadata) database().viewMetadata(this, ()-> yearTableMetadata(this)); //safe cast } default String defaultRevisionMode() { diff --git a/src/main/java/org/usf/jquery/web/YearTableMetadata.java b/src/main/java/org/usf/jquery/web/YearTableMetadata.java index 96072954..98ab744d 100644 --- a/src/main/java/org/usf/jquery/web/YearTableMetadata.java +++ b/src/main/java/org/usf/jquery/web/YearTableMetadata.java @@ -2,7 +2,6 @@ import static java.lang.String.join; import static java.time.Month.DECEMBER; -import static java.util.Collections.emptyMap; import static java.util.Comparator.reverseOrder; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; @@ -10,7 +9,6 @@ import static java.util.function.Function.identity; import static java.util.function.Predicate.not; import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; import static org.usf.jquery.core.JDBCType.OTHER; import static org.usf.jquery.core.JDBCType.typeOf; @@ -23,7 +21,6 @@ import java.sql.SQLException; import java.time.Year; import java.time.YearMonth; -import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -33,6 +30,9 @@ import java.util.Optional; import java.util.Set; +import org.usf.jquery.core.DBView; +import org.usf.jquery.core.TableView; + import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -42,17 +42,17 @@ * */ @Slf4j -public final class YearTableMetadata extends TableMetadata { +public final class YearTableMetadata extends ViewMetadata { private static final String PATTERN = "_20**"; - private final String revisionColumn; //nullable + private final String revisionColumn; //optional private final Set tablenames = new LinkedHashSet<>(); @Getter private YearMonth[] revisions; - private YearTableMetadata(String tablename, String revisionColumn, Map columns) { - super(tablename, columns); + private YearTableMetadata(DBView view, String revisionColumn, Map columns) { + super(view, columns); this.revisionColumn = revisionColumn; this.revisions = EMPTY_REVISION; //by default avoid NullPointerException } @@ -70,24 +70,24 @@ public void fetch() throws SQLException { //individually fetching } @Override - void fetch(DatabaseMetaData metadata) throws SQLException { + void fetch(DatabaseMetaData metadata, TableView view) throws SQLException { tablenames.clear(); - var dbMap = getColumns().values().stream().collect(toMap(ColumnMetadata::getColumnName, ColumnMetadata::reset)); //important! reset columns + var dbMap = getColumns().values().stream().collect(toMap(cm-> cm.getColumn().getName(), ColumnMetadata::reset)); //important! reset columns Set dirtyColumns = new LinkedHashSet<>(); Map> columnTables = new LinkedHashMap<>(); - try(var rs = metadata.getColumns(null, null, getTablename() + "_20__", null)){ + try(var rs = metadata.getColumns(null, view.getSchema(), view.getName() + "_20__", null)){ if(!rs.next()) { - throw new NoSuchElementException("no tables found with pattern " + getTablename() + PATTERN); + throw new NoSuchElementException("no tables found with pattern " + view + PATTERN); } do { var tn = rs.getString("TABLE_NAME"); var cm = dbMap.get(rs.getString("COLUMN_NAME")); - if(nonNull(cm) && tn.matches(getTablename() + "_20\\d{2}")) {// untyped SQL pattern + if(nonNull(cm) && tn.matches(view.getName() + "_20\\d{2}")) {// untyped SQL pattern tablenames.add(tn); - columnTables.computeIfAbsent(cm.getColumnName(), k-> new LinkedHashSet<>()).add(tn); + columnTables.computeIfAbsent(cm.getColumn().getName(), k-> new LinkedHashSet<>()).add(tn); var type = rs.getInt("DATA_TYPE"); var size = rs.getInt("COLUMN_SIZE"); - if(isNull(cm.getDataType())) { //first time + if(isNull(cm.getColumn().getType())) { //first time cm.setDataType(typeOf(type).orElse(OTHER)); cm.setDataSize(size); } @@ -98,15 +98,15 @@ else if(cm.getDataType().getValue() != type || cm.getDataSize() != size) { } while(rs.next()); } if(!dbMap.keySet().equals(columnTables.keySet())) { - var missingCols = dbMap.keySet().stream().filter(not(columnTables.keySet()::contains)).collect(toList()); - throw new NoSuchElementException("column(s) [" + join(", ", missingCols) + "] not found in any table " + getTablename() + PATTERN); + var missingCols = dbMap.keySet().stream().filter(not(columnTables.keySet()::contains)).toList(); + throw new NoSuchElementException("column(s) [" + join(", ", missingCols) + "] not found in any table " + view + PATTERN); } - var missingCols = columnTables.entrySet().stream().filter(e-> !e.getValue().equals(tablenames)).map(Entry::getKey).collect(toList()); + var missingCols = columnTables.entrySet().stream().filter(e-> !e.getValue().equals(tablenames)).map(Entry::getKey).toList(); if(!missingCols.isEmpty()) { - throw new IllegalStateException("column(s) [" + join(", ", missingCols) + "] must be present in all tables " + getTablename() + PATTERN); + throw new IllegalStateException("column(s) [" + join(", ", missingCols) + "] must be present in all tables " + view + PATTERN); } if(!dirtyColumns.isEmpty()) { - throw new IllegalStateException("column(s) [" + join(", ", dirtyColumns) + "] must have the same definition in all tables " + getTablename() + PATTERN); + throw new IllegalStateException("column(s) [" + join(", ", dirtyColumns) + "] must have the same definition in all tables " + view + PATTERN); } } @@ -143,15 +143,10 @@ void fetchRevisions(Connection cn) { // change this call } } - static YearTableMetadata yearTableMetadata(YearTableDecorator table, Collection columns) { - return new YearTableMetadata(table.tableName(), - table.monthRevision().flatMap(table::columnName).orElse(null), - declaredColumns(table, columns)); + static YearTableMetadata yearTableMetadata(YearTableDecorator table) { + return new YearTableMetadata(table.viewName(), + table.monthRevision().map(table::columnName).orElse(null), + table.declaredColumns()); } - static YearTableMetadata emptyMetadata(YearTableDecorator table) { - return new YearTableMetadata(table.tableName(), - table.monthRevision().flatMap(table::columnName).orElse(null), - emptyMap()); - } } diff --git a/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java b/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java index d7199cf0..2038812c 100644 --- a/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java +++ b/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java @@ -3,8 +3,6 @@ import static java.util.Collections.emptyList; import static org.usf.jquery.web.RequestParser.parseEntry; -import java.util.Optional; - import org.junit.jupiter.api.Test; class RequestEntryChainTest { @@ -12,12 +10,7 @@ class RequestEntryChainTest { @Test void test() { JQueryContext.register(emptyList(), emptyList()); - parseEntry("count()").evalColumn(new TableDecorator() { - - @Override - public String tableName() { - return null; - } + parseEntry("count()").evalColumn(new ViewDecorator() { @Override public String identity() { @@ -25,8 +18,8 @@ public String identity() { } @Override - public Optional columnName(ColumnDecorator cd) { - return Optional.empty(); + public String columnName(ColumnDecorator cd) { + return null; } }); } From caacd7a0149f149d7b30714454436619bf13f1c4 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 30 Jul 2024 00:03:04 +0200 Subject: [PATCH 098/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 2 +- .../java/org/usf/jquery/core/TableView.java | 10 +- .../java/org/usf/jquery/core/ViewJoin.java | 3 +- .../org/usf/jquery/web/ColumnBuilder.java | 2 +- .../org/usf/jquery/web/ColumnDecorator.java | 6 +- .../usf/jquery/web/ContextEnvironment.java | 131 ++++++++++++++++++ .../org/usf/jquery/web/ContextManager.java | 65 +++++++++ .../usf/jquery/web/DatabaseConfiguration.java | 64 --------- .../org/usf/jquery/web/DatabaseDecorator.java | 23 +-- .../org/usf/jquery/web/DatabaseManager.java | 59 -------- .../org/usf/jquery/web/DatabaseMetadata.java | 26 ++-- .../java/org/usf/jquery/web/JoinBuilder.java | 14 ++ .../jquery/web/NoSuchResourceException.java | 2 +- .../org/usf/jquery/web/QueryDecorator.java | 38 ++--- .../org/usf/jquery/web/RequestContext.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 18 +-- .../org/usf/jquery/web/RequestQueryParam.java | 6 +- .../jquery/web/RequestQueryParamResolver.java | 28 ++-- .../org/usf/jquery/web/RevisionIterator.java | 5 +- .../java/org/usf/jquery/web/ViewBuilder.java | 3 +- .../org/usf/jquery/web/ViewDecorator.java | 69 +++++++-- .../java/org/usf/jquery/web/ViewMetadata.java | 57 +++++--- .../org/usf/jquery/web/YearTableMetadata.java | 23 ++- ...eDecorator.java => YearViewDecorator.java} | 16 ++- 24 files changed, 399 insertions(+), 273 deletions(-) create mode 100644 src/main/java/org/usf/jquery/web/ContextEnvironment.java create mode 100644 src/main/java/org/usf/jquery/web/ContextManager.java delete mode 100644 src/main/java/org/usf/jquery/web/DatabaseConfiguration.java delete mode 100644 src/main/java/org/usf/jquery/web/DatabaseManager.java create mode 100644 src/main/java/org/usf/jquery/web/JoinBuilder.java rename src/main/java/org/usf/jquery/web/{YearTableDecorator.java => YearViewDecorator.java} (92%) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 5ae8a083..afe4b532 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -162,7 +162,7 @@ static DBColumn constant(Supplier value) { @Override public String sql(QueryParameterBuilder arg) { - return formatValue(value.get()); + return formatValue(value.get()); //lazy } @Override diff --git a/src/main/java/org/usf/jquery/core/TableView.java b/src/main/java/org/usf/jquery/core/TableView.java index 0e019c6d..dd5d4e66 100644 --- a/src/main/java/org/usf/jquery/core/TableView.java +++ b/src/main/java/org/usf/jquery/core/TableView.java @@ -30,11 +30,7 @@ public TableView(String schema, String name) { @Override public String sql(QueryParameterBuilder builder) { - return member(schema(builder.getSchema()), name); - } - - public String schema(String defaultSchema) { - return nonNull(schema) ? schema : defaultSchema; //priority order + return member(getSchemaOrElse(builder.getSchema()), name); } @Override @@ -42,6 +38,10 @@ public String id() { return id; } + public String getSchemaOrElse(String defaultSchema) { + return nonNull(schema) ? schema : defaultSchema; //priority order + } + @Override public String toString() { return sql(addWithValue()); diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index fac12b1e..38ad6d2d 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -18,11 +18,12 @@ * */ @RequiredArgsConstructor -public class ViewJoin implements DBObject { +public class ViewJoin implements DBObject { private final JoinType joinType; private final DBView view; private final DBFilter[] filters; + //join results !? @Override public String sql(QueryParameterBuilder builder, Object[] args) { diff --git a/src/main/java/org/usf/jquery/web/ColumnBuilder.java b/src/main/java/org/usf/jquery/web/ColumnBuilder.java index 37abd15c..d9f09f00 100644 --- a/src/main/java/org/usf/jquery/web/ColumnBuilder.java +++ b/src/main/java/org/usf/jquery/web/ColumnBuilder.java @@ -10,5 +10,5 @@ @FunctionalInterface public interface ColumnBuilder { - DBColumn build(ViewDecorator table); + DBColumn build(ViewDecorator vd); } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 6d879481..41e177f8 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -19,11 +19,11 @@ default String reference(ViewDecorator td) { //JSON } default JDBCType type(ViewDecorator td) { - return null; + return null; // auto type } default JDBCArgumentParser parser(ViewDecorator td){ // override parser | format | local | validation - return null; // jdbcArgParser(dataType(td)) + return null; // auto parser } default ColumnBuilder builder(ViewDecorator td) { //set type if null @@ -45,7 +45,7 @@ default boolean canSelect(ViewDecorator td) { default boolean canFilter(ViewDecorator td) { throw new UnsupportedOperationException(); //authorization inject } - + static ColumnDecorator ofColumn(String ref, ColumnBuilder cb) { return new ColumnDecorator() { @Override diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java new file mode 100644 index 00000000..ad56e4cc --- /dev/null +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -0,0 +1,131 @@ +package org.usf.jquery.web; + +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; +import static java.util.Objects.requireNonNull; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toUnmodifiableMap; +import static org.usf.jquery.core.Validation.requireLegalVariable; +import static org.usf.jquery.core.Validation.requireNonEmpty; +import static org.usf.jquery.web.NoSuchResourceException.noSuchViewException; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import javax.sql.DataSource; + +import org.usf.jquery.core.QueryView; +import org.usf.jquery.core.Validation; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * + * @author u$f + * + */ +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public final class ContextEnvironment { + + private final DatabaseDecorator database; + private final Map views; + private final Map columns; + private final DataSource dataSource; //optional + private final String schema; //optional + private final DatabaseMetadata metadata; + + public ContextEnvironment(ContextEnvironment ctx) { + this.database = ctx.database; + this.views = new HashMap<>(ctx.views); //modifiable + this.columns = new HashMap<>(ctx.columns); //modifiable + this.dataSource = ctx.dataSource; + this.schema = ctx.schema; + this.metadata = ctx.metadata; + } + + public ViewDecorator getTable(String name) { + var vd = views.get(name); + if(nonNull(vd)) { + return vd; + } + throw noSuchViewException(name); + } + + ViewMetadata computeTableMetadata(ViewDecorator vd, Function, ViewMetadata> fn) { + return metadata.getTables().computeIfAbsent(vd.identity(), key-> fn.apply(columns.values())); + } + + void registerQuery(QueryView query) { + views.compute(query.id(), (k,v)-> { + if(isNull(v)){ + return new QueryDecorator(query); + } + throw new IllegalStateException("already exists"); + }); + query.getQuery().getColumns() + .stream().map(c-> c::tagname) + .forEach(cd-> columns.compute(cd.identity(), (k,v)-> { + if(isNull(v)) { + return cd; + } + throw new IllegalStateException("already exists"); + })); + } + + void fetch() { + if(nonNull(dataSource)) { + try(var cnx = dataSource.getConnection()) { + views.values().stream().sorted(this::comparator).forEach(v->{ //parallel + try { + v.metadata().fetch(cnx.getMetaData(), schema); + } + catch (Exception e) { + throw new WebException("error while fetching table metadata " + v.identity(), e); + } + }); + } + catch(SQLException e) { + throw new WebException("", e); + } + } + } + + int comparator(ViewDecorator v1, ViewDecorator v2) { + var n1 = database.viewName(v1); + var n2 = database.viewName(v2); + if((isNull(v1) && isNull(v2)) || (nonNull(n1) && nonNull(n2))) { + return 0; + } + return nonNull(n1) ? 1 : -1; + } + + public static final ContextEnvironment of(DatabaseDecorator database, + Collection views, Collection columns) { + return of(database, views, columns, null, null); + } + + public static final ContextEnvironment of(DatabaseDecorator database, + Collection views, Collection columns, DataSource ds) { + return of(database, views, columns, ds, null); + } + + public static final ContextEnvironment of(DatabaseDecorator database, + Collection views, Collection columns, DataSource ds, String schema) { + requireLegalVariable(database.identity()); + return new ContextEnvironment( + requireNonNull(database, "configuration.database"), + unmodifiableIdentityMap(requireNonEmpty(views, "configuration.views"), ViewDecorator::identity), + unmodifiableIdentityMap(requireNonEmpty(columns, "configuration.columns"), ColumnDecorator::identity), + ds, schema, new DatabaseMetadata()); + } + + static Map unmodifiableIdentityMap(Collection c, Function fn){ + return c.stream().collect(toUnmodifiableMap(fn.andThen(Validation::requireLegalVariable), identity())); + } +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/ContextManager.java b/src/main/java/org/usf/jquery/web/ContextManager.java new file mode 100644 index 00000000..20ff6d8e --- /dev/null +++ b/src/main/java/org/usf/jquery/web/ContextManager.java @@ -0,0 +1,65 @@ +package org.usf.jquery.web; + +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; +import static org.usf.jquery.core.SqlStringBuilder.quote; + +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * + * @author u$f + * + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class ContextManager { + + private static final Map CONTEXTS = new HashMap<>(); + private static final ThreadLocal CURRENT = new ThreadLocal<>(); + + public static void register(ContextEnvironment config) { + CONTEXTS.compute(config.getDatabase().identity(), (id,dm)-> { + if(isNull(dm)) { + return config; + } + throw new IllegalStateException("context environement conflict " + quote(id)); + }); + config.fetch(); //outer fetch + } + + public static ContextEnvironment currentContext() { + var ctx = CURRENT.get(); + if(nonNull(ctx)) { + return ctx; + } + if(CONTEXTS.size() == 1) { //default database + return setCurrentContext(CONTEXTS.values().iterator().next()); + } + throw CONTEXTS.isEmpty() + ? new NoSuchElementException("no database configured") + : new IllegalStateException("no database selected"); + } + + static ContextEnvironment context(String database){ + var ctx = CONTEXTS.get(database); + if(nonNull(ctx)) { + return setCurrentContext(ctx); + } + throw new NoSuchElementException(database); + } + + static ContextEnvironment setCurrentContext(ContextEnvironment ctx) { + ctx = new ContextEnvironment(ctx); //copy + CURRENT.set(ctx); + return ctx; + } + + static void releaseContext() { + CURRENT.remove(); + } +} diff --git a/src/main/java/org/usf/jquery/web/DatabaseConfiguration.java b/src/main/java/org/usf/jquery/web/DatabaseConfiguration.java deleted file mode 100644 index 2756d580..00000000 --- a/src/main/java/org/usf/jquery/web/DatabaseConfiguration.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.usf.jquery.web; - -import static java.util.Objects.nonNull; -import static java.util.Objects.requireNonNull; -import static java.util.function.Function.identity; -import static java.util.stream.Collectors.toUnmodifiableMap; -import static org.usf.jquery.core.Validation.requireNonEmpty; -import static org.usf.jquery.web.NoSuchResourceException.throwNoSuchTableException; - -import java.util.Collection; -import java.util.Map; -import java.util.function.Function; - -import javax.sql.DataSource; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * - * @author u$f - * - */ -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public final class DatabaseConfiguration { - - private final DatabaseDecorator database; - private final Map views; - private final Map columns; - private final DataSource dataSource; //optional - private final String schema; //optional - - public ViewDecorator getTable(String name) { - var vd = views.get(name); - if(nonNull(vd)) { - return vd; - } - throw throwNoSuchTableException(name); - } - - public static final DatabaseConfiguration of(DatabaseDecorator database, - Collection views, Collection columns) { - return of(database, views, columns, null, null); - } - - public static final DatabaseConfiguration of(DatabaseDecorator database, - Collection views, Collection columns, DataSource ds) { - return of(database, views, columns, ds, null); - } - - public static final DatabaseConfiguration of(DatabaseDecorator database, - Collection views, Collection columns, DataSource ds, String schema) { - return new DatabaseConfiguration(requireNonNull(database, "configuration.database"), - unmodifiableMap(requireNonEmpty(views, "configuration.views"), ViewDecorator::identity), - unmodifiableMap(requireNonEmpty(columns, "configuration.columns"), ColumnDecorator::identity), - ds, schema); - } - - static Map unmodifiableMap(Collection c, Function fn){ - return c.stream().collect(toUnmodifiableMap(fn, identity())); - } -} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/DatabaseDecorator.java b/src/main/java/org/usf/jquery/web/DatabaseDecorator.java index 53011f2c..35f3f7ae 100644 --- a/src/main/java/org/usf/jquery/web/DatabaseDecorator.java +++ b/src/main/java/org/usf/jquery/web/DatabaseDecorator.java @@ -1,11 +1,5 @@ package org.usf.jquery.web; -import static java.util.Objects.nonNull; -import static org.usf.jquery.core.Validation.requireLegalVariable; - -import org.usf.jquery.core.DBView; -import org.usf.jquery.core.TableView; - /** * * @author u$f @@ -16,20 +10,5 @@ public interface DatabaseDecorator { String identity(); //URL String viewName(ViewDecorator vd); //[schema.]table - - default DBView tableView(ViewDecorator vd) { - var tn = viewName(vd); - if(nonNull(tn)){ - var idx = tn.indexOf('.'); - return idx == -1 - ? new TableView(requireLegalVariable(tn)) - : new TableView(requireLegalVariable(tn.substring(0, idx)), - requireLegalVariable(tn.substring(idx, tn.length()))); - } - var b = vd.builder(); - if(nonNull(b)){ - return b.build(vd.identity()); //safe ? - } - throw new IllegalStateException("require viewName or builder on " + vd); - } + } diff --git a/src/main/java/org/usf/jquery/web/DatabaseManager.java b/src/main/java/org/usf/jquery/web/DatabaseManager.java deleted file mode 100644 index 31d5790b..00000000 --- a/src/main/java/org/usf/jquery/web/DatabaseManager.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.usf.jquery.web; - -import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; -import static org.usf.jquery.core.SqlStringBuilder.quote; - -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; - -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - -/** - * - * @author u$f - * - */ -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public final class DatabaseManager { - - private static final Map DATABASES = new HashMap<>(); - private static final ThreadLocal LOCAL_DB = new ThreadLocal<>(); - - public void register(DatabaseConfiguration config) { - DATABASES.compute(config.getDatabase().identity(), (id,dm)->{ - if(isNull(dm)) { - return new DatabaseMetadata(config); - } - throw new IllegalStateException("database configuration conflict " + quote(id)); - }); - } - - public static DatabaseMetadata currentDatabase() { - var db = LOCAL_DB.get(); - if(nonNull(db)) { - return db; - } - if(DATABASES.size() == 1) { //default database - return DATABASES.values().iterator().next(); - } - throw DATABASES.isEmpty() - ? new NoSuchElementException("no database configured") - : new IllegalStateException("no database selected"); - } - - public static DatabaseMetadata switchDatabase(String name) { - var db = DATABASES.get(name); - if(nonNull(db)) { - LOCAL_DB.set(db); - return db; - } - throw new NoSuchElementException(quote(name) + " was not initialized"); - } - - public static void releaseDatabase() { - LOCAL_DB.remove(); - } -} diff --git a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java index 97c99c5d..5813601d 100644 --- a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java +++ b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java @@ -20,8 +20,11 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.function.Supplier; import java.util.stream.Stream; +import javax.sql.DataSource; + import org.usf.jquery.core.Database; import org.usf.jquery.core.ViewColumn; @@ -37,35 +40,22 @@ */ @Slf4j @Getter(AccessLevel.PACKAGE) -@RequiredArgsConstructor public final class DatabaseMetadata { private final Object mutex = new Object(); - private final DatabaseConfiguration config; private final Map tables = new HashMap<>(); //lazy loading @Getter private Instant lastUpdate; @Getter private Database type; - public ViewMetadata viewMetadata(ViewDecorator td){ - return tables.computeIfAbsent(td.identity(), id-> { - var view = config.getDatabase().tableView(td); - var meta = config.getColumns().values().stream().mapMulti((cd, acc)-> { - var cn = td.columnName(cd); - if(nonNull(cn)) { //ViewColumn only - var col = new ViewColumn(view, requireLegalVariable(cn), requireLegalVariable(cd.reference(td)), cd.type(td)); - acc.accept(columnMetadata(col)); - } //tag = reference - }).collect(toUnmodifiableMap(cm-> cm.getColumn().getTag(), identity())); - return new ViewMetadata(view, meta); - }); + public ViewMetadata viewMetadata(ViewDecorator td, Supplier supp){ + return tables.computeIfAbsent(td.identity(), id-> supp.get()); } - public void fetch() { - var ds = config.getDataSource(); - if(isNull(ds) || tables.isEmpty()) { + public void fetch(DataSource ds) { + if(tables.isEmpty()) { log.warn("database resources not initialized"); //full scan ? next release return; } @@ -83,7 +73,7 @@ public void fetch() { yt.fetchRevisions(cn); logRevisions(yt.getRevisions()); } - t.setLastUpdate(now()); +// t.setLastUpdate(now()); } lastUpdate = now(); log.info("Completed metadata scan in {} ms", currentTimeMillis() - time); diff --git a/src/main/java/org/usf/jquery/web/JoinBuilder.java b/src/main/java/org/usf/jquery/web/JoinBuilder.java new file mode 100644 index 00000000..d85edae7 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/JoinBuilder.java @@ -0,0 +1,14 @@ +package org.usf.jquery.web; + +import org.usf.jquery.core.ViewJoin; + +/** + * + * @author u$f + * + */ +public interface JoinBuilder { + + ViewJoin[] build(ViewDecorator[] selectedViews); + +} diff --git a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java index a740fae6..2879bbe5 100644 --- a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java +++ b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java @@ -14,7 +14,7 @@ public NoSuchResourceException(String s) { super(s); } - static NoSuchResourceException throwNoSuchTableException(String resource) { + static NoSuchResourceException noSuchViewException(String resource) { return noSuchResouceException("view", resource); } diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 02a9e3a9..74e989b0 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -1,15 +1,14 @@ package org.usf.jquery.web; -import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; -import static org.usf.jquery.web.ColumnDecorator.ofColumn; +import static org.usf.jquery.web.NoSuchResourceException.throwNoSuchColumnException; -import java.util.Optional; - -import org.usf.jquery.core.DBQuery; +import org.usf.jquery.core.DBView; +import org.usf.jquery.core.QueryView; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.ViewColumn; import lombok.Getter; +import lombok.NonNull; import lombok.RequiredArgsConstructor; /** @@ -21,31 +20,34 @@ @RequiredArgsConstructor final class QueryDecorator implements ViewDecorator { - private final DBQuery query; - + private final QueryView query; + @Override - public String identity() { + public String identity() { return query.id(); } @Override public String columnName(ColumnDecorator cd) { - return column(cd.identity()).map(TaggableColumn::tagname).orElse(null); + return null; } - public Optional lookupColumnDecorator(String cn) { - return column(cn) - .map(c-> ofColumn(cn, td-> new ViewColumn(table(), doubleQuote(c.tagname()), c.tagname(), c.getType()))); + @Override + public DBView view() { + return query; } - private final Optional column(String cn){ - return query.columns().stream() - .filter(c-> c.tagname().equals(cn)) - .findAny(); + @Override + public TaggableColumn column(@NonNull ColumnDecorator cd) { + return query.getQuery().getColumns().stream() + .filter(c-> c.tagname().equals(cd.identity())) //tagname nullable ! + .findAny() + .map(c-> new ViewColumn(query, c.tagname(), c.tagname(), c.getType())) + .orElseThrow(()-> throwNoSuchColumnException(cd.identity())); } @Override - public ViewBuilder builder() { - return v-> query; + public ViewMetadata metadata() { + throw new UnsupportedOperationException("query metadata"); } } diff --git a/src/main/java/org/usf/jquery/web/RequestContext.java b/src/main/java/org/usf/jquery/web/RequestContext.java index cd00734f..22fa602e 100644 --- a/src/main/java/org/usf/jquery/web/RequestContext.java +++ b/src/main/java/org/usf/jquery/web/RequestContext.java @@ -61,7 +61,7 @@ public Collection popQueries() { return q; } - public static final RequestContext currentContext() { + public static final RequestContext currentContext_() { var rc = local.get(); if(isNull(rc)) { var jc = context(); //can filter view & column diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index d257798e..bb0bc304 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -29,7 +29,7 @@ import static org.usf.jquery.web.Constants.PARTITION; import static org.usf.jquery.web.Constants.SELECT; import static org.usf.jquery.web.ParseException.cannotParseException; -import static org.usf.jquery.web.RequestContext.currentContext; +import static org.usf.jquery.web.RequestContext.currentContext_; import static org.usf.jquery.web.UnexpectedEntryException.unexpectedEntryException; import java.util.List; @@ -125,7 +125,7 @@ public QueryView evalQuery(ViewDecorator td, boolean requireTag) { if(requireTag && isNull(e.tag)) { throw new IllegalArgumentException("require tag"); } - q.views(currentContext().popQueries().toArray(DBQuery[]::new)); + q.views(currentContext_().popQueries().toArray(DBQuery[]::new)); return q.as(e.tag); } throw cannotParseException(SELECT, this.toString()); @@ -207,7 +207,7 @@ public DBFilter evalFilter(ViewDecorator td, List values) { Optional tableCriteria(ViewDecorator td, List values) { RequestEntryChain e = null; CriteriaBuilder c = null; - var res = currentContext().lookupViewDecorator(value); + var res = currentContext_().lookupViewDecorator(value); if(res.isPresent() && next()) { c = res.get().criteria(next.value); e = next; // only if nonNull @@ -286,21 +286,21 @@ private ResourceCursor chainColumnOperations(ViewDecorator td, boolean filter) { } private static DBColumn windowColumn(ViewDecorator td, TaggableColumn column) { - var vw = currentContext().lookupView(td.identity()).orElse(null); + var vw = currentContext_().lookupView(td.identity()).orElse(null); if(vw instanceof CompletableViewQuery) { // already create ((CompletableViewQuery)vw).getQuery().columns(column); } else { - var view = isNull(vw) ? td.table() : vw; + var view = isNull(vw) ? td.view() : vw; vw = new CompletableViewQuery(view.select(td.identity(), allColumns(view).as(null), column)); - currentContext().putWorkQuery(vw); // same name + currentContext_().putWorkQuery(vw); // same name } return new ViewColumn(vw, doubleQuote(column.tagname()), null, column.getType()); } private Optional lookupResource(ViewDecorator td) { if(next()) { //check td.cd first - var rc = currentContext().lookupViewDecorator(value) + var rc = currentContext_().lookupViewDecorator(value) .flatMap(v-> next.lookupViewResource(v, RequestEntryChain::isWindowFunction)); if(rc.isPresent()) { requireNoArgs(); // noArgs on valid resource @@ -313,7 +313,7 @@ private Optional lookupResource(ViewDecorator td) { private Optional lookupViewResource(ViewDecorator td, Predicate pre) { var res = td.getClass() == QueryDecorator.class ? ((QueryDecorator)td).lookupColumnDecorator(value) - : currentContext().lookupColumnDecorator(value); + : currentContext_().lookupColumnDecorator(value); if(res.isPresent()) { requireNoArgs(); } @@ -328,7 +328,7 @@ private Optional toOperation(ViewDecorator td, DBColumn col, Pr var c = col; if(isNull(c) && isEmpty(args) && "count".equals(value)) { // id is MAJ c = b-> { - b.view(td.table()); // important! register view + b.view(td.view()); // important! register view return "*"; }; } diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParam.java b/src/main/java/org/usf/jquery/web/RequestQueryParam.java index df7a7ecc..405e07a8 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParam.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParam.java @@ -22,7 +22,11 @@ String[] defaultColumns() default {}; - String[] ignoreParameters() default {}; // !parsed + String[] ignoreParameters() default {}; // will be not parsed boolean aggregationOnly() default false; // else throw IllegalDataAccessException + + //allowWorkView + + //allowJoinView } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index ac7265c0..d5105389 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -4,8 +4,9 @@ import static org.usf.jquery.core.Utils.isPresent; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; -import static org.usf.jquery.web.DatabaseManager.currentDatabase; -import static org.usf.jquery.web.DatabaseManager.switchDatabase; +import static org.usf.jquery.web.ContextManager.context; +import static org.usf.jquery.web.ContextManager.currentContext; +import static org.usf.jquery.web.ContextManager.releaseContext; import java.util.LinkedHashMap; import java.util.Map; @@ -24,7 +25,7 @@ */ @Slf4j @RequiredArgsConstructor -public final class RequestQueryParamResolver { +public final class RequestQueryParamResolver {//spring connection bridge public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull Map parameterMap) { var t = currentTimeMillis(); @@ -36,14 +37,21 @@ public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull if(isPresent(ant.ignoreParameters())) { Stream.of(ant.ignoreParameters()).forEach(parameterMap::remove); } - var db = ant.database().isEmpty() ? currentDatabase() : switchDatabase(ant.database()); - var req = db.getConfig() - .getTable(ant.view()) - .query(parameterMap); //may edit map - if(ant.aggregationOnly() && !req.isAggregation()) { + var ctx = ant.database().isEmpty() + ? currentContext() + : context(ant.database()); + try { + var req = ctx + .getTable(ant.view()) + .query(parameterMap); //may edit map + if(!ant.aggregationOnly() || req.isAggregation()) { + log.trace("request parsed in {} ms", currentTimeMillis() - t); + return req; + } throw new IllegalDataAccessException("non aggregation query"); } - log.trace("request parsed in {} ms", currentTimeMillis() - t); - return req; + finally { + releaseContext(); + } } } diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index 59b51da4..4c15b95d 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -53,8 +53,8 @@ public static RevisionIterator iterator(YearMonth[] revisions){ throw new IllegalArgumentException("no revision"); } - static TableView yearTable(String name, String tagname) { - return new TableView(name, tagname) { + static TableView yearTable(String schema, String name) { + return new TableView(schema, name) { @Override public String sql(QueryParameterBuilder builder) { return super.sql(builder) + "_" + currentRev.get().getKey(); @@ -75,5 +75,4 @@ static DBFilter monthFilter(DBColumn column) { return filter.sql(b); }; } - } diff --git a/src/main/java/org/usf/jquery/web/ViewBuilder.java b/src/main/java/org/usf/jquery/web/ViewBuilder.java index 6fe6880a..7ff0de5e 100644 --- a/src/main/java/org/usf/jquery/web/ViewBuilder.java +++ b/src/main/java/org/usf/jquery/web/ViewBuilder.java @@ -10,5 +10,6 @@ @FunctionalInterface public interface ViewBuilder { - DBView build(String id); + DBView build(); + } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index c71d2393..821a7327 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -2,8 +2,13 @@ import static java.lang.Integer.parseInt; import static java.util.Objects.nonNull; +import static java.util.Objects.requireNonNull; +import static java.util.Objects.requireNonNullElseGet; +import static java.util.stream.Collectors.toUnmodifiableMap; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.core.Validation.requireLegalVariable; +import static org.usf.jquery.web.ColumnMetadata.columnMetadata; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; import static org.usf.jquery.web.Constants.FETCH; @@ -11,24 +16,28 @@ import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.RESERVED_WORDS; import static org.usf.jquery.web.Constants.VIEW; -import static org.usf.jquery.web.DatabaseManager.currentDatabase; +import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.MissingParameterException.missingParameterException; +import static org.usf.jquery.web.NoSuchResourceException.noSuchViewException; import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; import static org.usf.jquery.web.RequestContext.clearContext; -import static org.usf.jquery.web.RequestContext.currentContext; +import static org.usf.jquery.web.RequestContext.currentContext_; import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.RequestParser.parseEntry; import java.util.Map; +import java.util.function.Function; import java.util.stream.Stream; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBQuery; import org.usf.jquery.core.DBView; import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.TableView; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.Utils; +import org.usf.jquery.core.ViewColumn; import lombok.NonNull; @@ -44,22 +53,30 @@ public interface ViewDecorator { String columnName(ColumnDecorator cd); default ViewBuilder builder() { - return null; // no builder by default + return this::buildView; //avoid recursive call } default CriteriaBuilder criteria(String name) { //!aggregation return null; // no criteria by default } - default DBView table() { - return metadata().getView(); //cached view + default JoinBuilder joiner() { + return null; // no builder by default + } + + default DBView view() { + return requireNonNullElseGet(metadata().getView(), builder()::build); //nullable !? } default TaggableColumn column(@NonNull ColumnDecorator cd) { - var c = metadata().columnMetada(cd); //priority order + var c = metadata().columnMetada(cd); if(nonNull(c)) { return c.getColumn(); } + var cn = columnName(cd); + if(nonNull(cn)) { //ViewColumn only + return buildViewColumn(cd, view(), cn); + } var b = cd.builder(this); if(nonNull(b)) { return b.build(this).as(cd.reference(this)); //set type @@ -68,19 +85,47 @@ default TaggableColumn column(@NonNull ColumnDecorator cd) { } default ViewMetadata metadata() { - return currentDatabase().viewMetadata(this); + return currentContext().computeTableMetadata(this, cols-> { + var view = requireNonNull(builder(), identity() + ".builder cannot be null").build(); + var meta = cols.stream().mapMulti((cd, acc)-> { + var cn = columnName(cd); //ViewColumn only + if(nonNull(cn)) { + acc.accept(columnMetadata(buildViewColumn(cd, view, cn))); + } //tag = reference + }).collect(toUnmodifiableMap(cm-> cm.getColumn().getTag(), Function.identity())); //tag = identity + return new ViewMetadata(view, meta); + }); + } + + private DBView buildView() { + var tn = currentContext().getDatabase().viewName(this); + if(nonNull(tn)){ + var idx = tn.indexOf('.'); + return idx == -1 + ? new TableView(requireLegalVariable(tn)) + : new TableView(requireLegalVariable(tn.substring(0, idx)), + requireLegalVariable(tn.substring(idx, tn.length()))); + } + throw noSuchViewException(identity()); + } + + private ViewColumn buildViewColumn(ColumnDecorator cd, DBView view, String name) { + return new ViewColumn(view, + requireLegalVariable(name), + requireLegalVariable(cd.reference(this)), + cd.type(this)); } default RequestQueryBuilder query(Map parameterMap) { try { - Utils.currentDatabase(currentDatabase().getType()); //table database + Utils.currentDatabase(currentContext().getMetadata().getType()); //table database var query = new RequestQueryBuilder(); parseViews(query, parameterMap); parseColumns(query, parameterMap); - parseFilters(query, parameterMap); parseOrders (query, parameterMap); parseFetch(query, parameterMap); - query.views(currentContext().popQueries().toArray(DBQuery[]::new)); + parseFilters(query, parameterMap); + query.views(currentContext_().popQueries().toArray(DBQuery[]::new)); return query; } finally { @@ -92,7 +137,7 @@ default void parseViews(RequestQueryBuilder query, Map paramet if(parameters.containsKey(VIEW)) { Stream.of(parameters.get(VIEW)) .flatMap(c-> parseEntries(c).stream()) - .forEach(e-> currentContext().putViewDecorator(new QueryDecorator(e.evalQuery(this, true)))); + .forEach(e-> currentContext().registerQuery(e.evalQuery(this, true))); } } @@ -116,7 +161,7 @@ default void parseColumns(RequestQueryBuilder query, Map param default void parseFilters(RequestQueryBuilder query, Map parameters) { parameters.entrySet().stream() - .filter(e-> !RESERVED_WORDS.contains(e.getKey())) +// .filter(e-> !RESERVED_WORDS.contains(e.getKey())) .flatMap(e-> { var re = parseEntry(e.getKey()); return Stream.of(e.getValue()).map(v-> re.evalFilter(this, parseArgs(v))); diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index 2e0ddebd..5c248a16 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -2,6 +2,8 @@ import static java.lang.String.format; import static java.lang.String.join; +import static java.lang.System.currentTimeMillis; +import static java.time.Instant.now; import static java.util.Objects.nonNull; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; @@ -23,7 +25,6 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; @@ -38,31 +39,39 @@ @RequiredArgsConstructor public class ViewMetadata { + private static final Object LOG_LOCK = new Object(); + private final Object mutex = new Object(); + private final DBView view; //cache private final Map columns; @Getter - @Setter(AccessLevel.PACKAGE) private Instant lastUpdate; public ColumnMetadata columnMetada(ColumnDecorator cd) { return columns.get(cd.identity()); } - void fetch(DatabaseMetaData metadata, String schema) throws SQLException { - if(view instanceof TableView tab) { - fetch(metadata, tab, schema); - } - else if(view instanceof DBQuery query) { - fetch(metadata, query); - } - else { - throw new UnsupportedOperationException("unsupported view " + view); + final void fetch(DatabaseMetaData metadata, String schema) throws SQLException { + synchronized (mutex) { + var time = currentTimeMillis(); + log.info("scanning table '{}' metadata...", view); + if(view instanceof TableView tab) { + fetch(metadata, tab, schema); + } + else if(view instanceof DBQuery query) { + fetch(metadata, query); + } + else { + throw new UnsupportedOperationException("unsupported view type " + view); + } + lastUpdate = now(); + log.info("metadata scanned in {} ms", currentTimeMillis() - time); } - logTableColumns(); + printViewColumnMap(); } void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { - try(var rs = metadata.getColumns(null, view.schema(schema), view.getName(), null)){ + try(var rs = metadata.getColumns(null, view.getSchemaOrElse(schema), view.getName(), null)){ if(rs.next()) { var db = columns.values().stream().collect(toMap(m-> m.getColumn().getName(), identity())); do { @@ -102,17 +111,19 @@ void fetch(DatabaseMetaData metadata, DBQuery qr) throws SQLException { } } - void logTableColumns() { + void printViewColumnMap() { if(!columns.isEmpty() && log.isInfoEnabled()) { - var pattern = "|%-20s|%-15s|%-25s|%-20s|"; - var bar = format(pattern, "", "", "", "").replace("|", "+").replace(" ", "-"); - log.info(bar); - log.info(format(pattern, "ID", "CLASS", "COLUMN", "TYPE")); - log.info(bar); - columns.entrySet().forEach(e-> - log.info(format(pattern, e.getKey(), e.getValue().toJavaType(), - e.getValue().getColumn(), e.getValue().toSqlType()))); - log.info(bar); + synchronized (LOG_LOCK) { + var pattern = "|%-20s|%-15s|%-25s|%-20s|"; + var bar = format(pattern, "", "", "", "").replace("|", "+").replace(" ", "-"); + log.info(bar); + log.info(format(pattern, "ID", "CLASS", "COLUMN", "TYPE")); + log.info(bar); + columns.entrySet().forEach(e-> + log.info(format(pattern, e.getKey(), e.getValue().toJavaType(), + e.getValue().getColumn(), e.getValue().toSqlType()))); + log.info(bar); + } } } diff --git a/src/main/java/org/usf/jquery/web/YearTableMetadata.java b/src/main/java/org/usf/jquery/web/YearTableMetadata.java index 98ab744d..a0899be3 100644 --- a/src/main/java/org/usf/jquery/web/YearTableMetadata.java +++ b/src/main/java/org/usf/jquery/web/YearTableMetadata.java @@ -30,6 +30,7 @@ import java.util.Optional; import java.util.Set; +import org.usf.jquery.core.DBQuery; import org.usf.jquery.core.DBView; import org.usf.jquery.core.TableView; @@ -60,22 +61,14 @@ private YearTableMetadata(DBView view, String revisionColumn, Map latestRevision() { return isEmpty(revisions) ? empty() : Optional.of(revisions[0]); } - - @Override - public void fetch() throws SQLException { //individually fetching - try(var cn = database().getDataSource().getConnection()) { - fetch(cn.getMetaData()); - fetchRevisions(cn); - } - } - + @Override - void fetch(DatabaseMetaData metadata, TableView view) throws SQLException { + void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { tablenames.clear(); var dbMap = getColumns().values().stream().collect(toMap(cm-> cm.getColumn().getName(), ColumnMetadata::reset)); //important! reset columns Set dirtyColumns = new LinkedHashSet<>(); Map> columnTables = new LinkedHashMap<>(); - try(var rs = metadata.getColumns(null, view.getSchema(), view.getName() + "_20__", null)){ + try(var rs = metadata.getColumns(null, view.getSchemaOrElse(schema), view.getName() + "_20__", null)){ if(!rs.next()) { throw new NoSuchElementException("no tables found with pattern " + view + PATTERN); } @@ -110,6 +103,11 @@ else if(cm.getDataType().getValue() != type || cm.getDataSize() != size) { } } + @Override + void fetch(DatabaseMetaData metadata, DBQuery qr) throws SQLException { + throw new UnsupportedOperationException("query"); + } + void fetchRevisions(Connection cn) { // change this call if(isEmpty(tablenames)) { return; @@ -143,10 +141,9 @@ void fetchRevisions(Connection cn) { // change this call } } - static YearTableMetadata yearTableMetadata(YearTableDecorator table) { + static YearTableMetadata yearTableMetadata(YearViewDecorator table) { return new YearTableMetadata(table.viewName(), table.monthRevision().map(table::columnName).orElse(null), table.declaredColumns()); } - } diff --git a/src/main/java/org/usf/jquery/web/YearTableDecorator.java b/src/main/java/org/usf/jquery/web/YearViewDecorator.java similarity index 92% rename from src/main/java/org/usf/jquery/web/YearTableDecorator.java rename to src/main/java/org/usf/jquery/web/YearViewDecorator.java index ceb2b3d6..faa2b339 100644 --- a/src/main/java/org/usf/jquery/web/YearTableDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearViewDecorator.java @@ -10,6 +10,7 @@ import static org.usf.jquery.web.Constants.EMPTY_REVISION; import static org.usf.jquery.web.Constants.REVISION; import static org.usf.jquery.web.Constants.REVISION_MODE; +import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.JQueryContext.database; import static org.usf.jquery.web.NoSuchResourceException.noSuchResouceException; import static org.usf.jquery.web.ParseException.cannotParseException; @@ -37,22 +38,23 @@ * @author u$f * */ -public interface YearTableDecorator extends ViewDecorator { - - Optional monthRevision(); +@Deprecated +public interface YearViewDecorator extends ViewDecorator { - ColumnDecorator yearRevision(); // !table column + ColumnDecorator yearRevision(); //!table column + + ColumnDecorator monthRevision(); //optional /** * loaded from db if null * */ - default YearMonth[] availableRevisions() {//reduce data revision access + default YearMonth[] availableRevisions() {//cache return metadata().getRevisions(); } @Override - default TableView table() { + default TableView view() { return yearTable(viewName(), identity()); } @@ -162,7 +164,7 @@ default YearMonth parseYearMonth(String revision) { @Override default YearTableMetadata metadata() { - return (YearTableMetadata) database().viewMetadata(this, ()-> yearTableMetadata(this)); //safe cast + return (YearTableMetadata) currentContext().computeTableMetadata(this, null); //safe cast } default String defaultRevisionMode() { From 4de1272edf942643374a973021e5601d72f131e0 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 30 Jul 2024 01:36:19 +0200 Subject: [PATCH 099/298] edit --- src/main/java/org/usf/jquery/core/Utils.java | 5 ++ .../usf/jquery/web/ContextEnvironment.java | 51 +++++------- .../org/usf/jquery/web/ContextManager.java | 1 - .../org/usf/jquery/web/DatabaseMetadata.java | 82 ++----------------- .../usf/jquery/web/IterableViewDecorator.java | 14 ++++ .../jquery/web/RequestQueryParamResolver.java | 2 +- .../java/org/usf/jquery/web/ViewMetadata.java | 13 +-- .../org/usf/jquery/web/YearTableMetadata.java | 20 +++++ 8 files changed, 75 insertions(+), 113 deletions(-) create mode 100644 src/main/java/org/usf/jquery/web/IterableViewDecorator.java diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index c4bebf82..4486e1c6 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -6,6 +6,7 @@ import static java.util.stream.Collectors.joining; import java.util.Collection; +import java.util.Map; import java.util.stream.Stream; import lombok.AccessLevel; @@ -34,6 +35,10 @@ public static boolean isEmpty(T[] a) { public static boolean isEmpty(Collection c) { return isNull(c) || c.isEmpty(); } + + public static boolean isEmpty(Map c) { + return isNull(c) || c.isEmpty(); + } public static boolean isBlank(String s) { return isNull(s) || s.isBlank(); diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index ad56e4cc..d3dd06c1 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -17,12 +17,14 @@ import javax.sql.DataSource; +import org.usf.jquery.core.JQueryException; import org.usf.jquery.core.QueryView; import org.usf.jquery.core.Validation; import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; /** * @@ -30,6 +32,7 @@ * */ @Getter +@Slf4j @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class ContextEnvironment { @@ -49,7 +52,7 @@ public ContextEnvironment(ContextEnvironment ctx) { this.metadata = ctx.metadata; } - public ViewDecorator getTable(String name) { + public ViewDecorator lookupTable(String name) { var vd = views.get(name); if(nonNull(vd)) { return vd; @@ -58,9 +61,26 @@ public ViewDecorator getTable(String name) { } ViewMetadata computeTableMetadata(ViewDecorator vd, Function, ViewMetadata> fn) { - return metadata.getTables().computeIfAbsent(vd.identity(), key-> fn.apply(columns.values())); + var meta = metadata.getTables().computeIfAbsent(vd.identity(), key-> fn.apply(columns.values())); + if(nonNull(dataSource)) { //outer fetch + synchronized(meta) { + if(isNull(meta.getLastUpdate())) { + fetch(meta); + } + } + } + return meta; } + private void fetch(ViewMetadata metadata) { + try(var cnx = dataSource.getConnection()) { + metadata.fetch(cnx.getMetaData(), schema); + } + catch(SQLException | JQueryException e) { + log.error("error while scanning database metadata", e); + } + } + void registerQuery(QueryView query) { views.compute(query.id(), (k,v)-> { if(isNull(v)){ @@ -78,33 +98,6 @@ void registerQuery(QueryView query) { })); } - void fetch() { - if(nonNull(dataSource)) { - try(var cnx = dataSource.getConnection()) { - views.values().stream().sorted(this::comparator).forEach(v->{ //parallel - try { - v.metadata().fetch(cnx.getMetaData(), schema); - } - catch (Exception e) { - throw new WebException("error while fetching table metadata " + v.identity(), e); - } - }); - } - catch(SQLException e) { - throw new WebException("", e); - } - } - } - - int comparator(ViewDecorator v1, ViewDecorator v2) { - var n1 = database.viewName(v1); - var n2 = database.viewName(v2); - if((isNull(v1) && isNull(v2)) || (nonNull(n1) && nonNull(n2))) { - return 0; - } - return nonNull(n1) ? 1 : -1; - } - public static final ContextEnvironment of(DatabaseDecorator database, Collection views, Collection columns) { return of(database, views, columns, null, null); diff --git a/src/main/java/org/usf/jquery/web/ContextManager.java b/src/main/java/org/usf/jquery/web/ContextManager.java index 20ff6d8e..2d29b7c8 100644 --- a/src/main/java/org/usf/jquery/web/ContextManager.java +++ b/src/main/java/org/usf/jquery/web/ContextManager.java @@ -29,7 +29,6 @@ public static void register(ContextEnvironment config) { } throw new IllegalStateException("context environement conflict " + quote(id)); }); - config.fetch(); //outer fetch } public static ContextEnvironment currentContext() { diff --git a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java index 5813601d..1b6921f3 100644 --- a/src/main/java/org/usf/jquery/web/DatabaseMetadata.java +++ b/src/main/java/org/usf/jquery/web/DatabaseMetadata.java @@ -1,100 +1,30 @@ package org.usf.jquery.web; -import static java.lang.String.format; -import static java.lang.System.currentTimeMillis; -import static java.time.Instant.now; -import static java.util.Comparator.comparing; -import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; -import static java.util.function.Function.identity; -import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toUnmodifiableMap; -import static org.usf.jquery.core.Utils.isPresent; -import static org.usf.jquery.core.Validation.requireLegalVariable; -import static org.usf.jquery.web.ColumnMetadata.columnMetadata; +import static java.util.Collections.synchronizedMap; +import java.sql.DatabaseMetaData; import java.sql.SQLException; -import java.time.Instant; -import java.time.YearMonth; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; -import java.util.Map.Entry; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import javax.sql.DataSource; import org.usf.jquery.core.Database; -import org.usf.jquery.core.ViewColumn; import lombok.AccessLevel; import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; /** * * @author u$f * */ -@Slf4j @Getter(AccessLevel.PACKAGE) public final class DatabaseMetadata { - private final Object mutex = new Object(); - - private final Map tables = new HashMap<>(); //lazy loading - @Getter - private Instant lastUpdate; - @Getter + private final Map tables = synchronizedMap(new LinkedHashMap<>()); //lazy loading private Database type; - - public ViewMetadata viewMetadata(ViewDecorator td, Supplier supp){ - return tables.computeIfAbsent(td.identity(), id-> supp.get()); - } - - public void fetch(DataSource ds) { - if(tables.isEmpty()) { - log.warn("database resources not initialized"); //full scan ? next release - return; - } - synchronized (mutex) { //thread safe - var time = currentTimeMillis(); - log.info("scanning database metadata..."); - try(var cn = ds.getConnection()){ - var metadata = cn.getMetaData(); - type = Database.of(metadata.getDatabaseProductName()).orElse(null); - for(var t : tables.values()) { - log.info("Scanning table '{}' metadata...", t.getView()); - t.fetch(metadata, config.getSchema()); - if(t instanceof YearTableMetadata yt) { - log.info("Scanning table '{}' revisions...", t.getView()); - yt.fetchRevisions(cn); - logRevisions(yt.getRevisions()); - } -// t.setLastUpdate(now()); - } - lastUpdate = now(); - log.info("Completed metadata scan in {} ms", currentTimeMillis() - time); - } catch (SQLException e) { - log.error("Error while scanning database metadata", e); - } - } - } - static void logRevisions(YearMonth[] revs) { - if(isPresent(revs)) { - var pattern = "|%-5s|%-40s|"; - var bar = format(pattern, "", "").replace("|", "+").replace(" ", "-"); - var map = Stream.of(revs).collect(groupingBy(YearMonth::getYear)); - log.info(bar); - log.info(format(pattern, "YEAR", "MONTHS")); - log.info(bar); - map.entrySet().stream().sorted(comparing(Entry::getKey)).forEach(e-> - log.info(format(pattern, e.getKey(), e.getValue().stream().map(o-> o.getMonthValue() + "").collect(joining(", "))))); - log.info(bar); - } + public void fetch(DatabaseMetaData metadata) throws SQLException { + type = Database.of(metadata.getDatabaseProductName()).orElse(null); } } diff --git a/src/main/java/org/usf/jquery/web/IterableViewDecorator.java b/src/main/java/org/usf/jquery/web/IterableViewDecorator.java new file mode 100644 index 00000000..0c1f7960 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/IterableViewDecorator.java @@ -0,0 +1,14 @@ +package org.usf.jquery.web; + +import org.usf.jquery.core.DBView; + +/** + * + * @author u$f + * + */ +public interface IterableViewDecorator extends ViewDecorator { + + void tableName(String name, T ctx); + +} diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index d5105389..24405254 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -42,7 +42,7 @@ public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull : context(ant.database()); try { var req = ctx - .getTable(ant.view()) + .lookupTable(ant.view()) .query(parameterMap); //may edit map if(!ant.aggregationOnly() || req.isAggregation()) { log.trace("request parsed in {} ms", currentTimeMillis() - t); diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index 5c248a16..be0f69fa 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -9,6 +9,7 @@ import static java.util.stream.Collectors.toMap; import static org.usf.jquery.core.QueryParameterBuilder.parametrized; import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.core.Utils.isEmpty; import java.sql.DatabaseMetaData; import java.sql.SQLException; @@ -40,10 +41,9 @@ public class ViewMetadata { private static final Object LOG_LOCK = new Object(); - private final Object mutex = new Object(); private final DBView view; //cache - private final Map columns; + private final Map columns; //empty!? @Getter private Instant lastUpdate; @@ -51,8 +51,8 @@ public ColumnMetadata columnMetada(ColumnDecorator cd) { return columns.get(cd.identity()); } - final void fetch(DatabaseMetaData metadata, String schema) throws SQLException { - synchronized (mutex) { + final ViewMetadata fetch(DatabaseMetaData metadata, String schema) throws SQLException { + if(!isEmpty(columns)) { var time = currentTimeMillis(); log.info("scanning table '{}' metadata...", view); if(view instanceof TableView tab) { @@ -66,8 +66,9 @@ else if(view instanceof DBQuery query) { } lastUpdate = now(); log.info("metadata scanned in {} ms", currentTimeMillis() - time); + printViewColumnMap(); } - printViewColumnMap(); + return this; } void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { @@ -113,7 +114,7 @@ void fetch(DatabaseMetaData metadata, DBQuery qr) throws SQLException { void printViewColumnMap() { if(!columns.isEmpty() && log.isInfoEnabled()) { - synchronized (LOG_LOCK) { + synchronized(LOG_LOCK) { var pattern = "|%-20s|%-15s|%-25s|%-20s|"; var bar = format(pattern, "", "", "", "").replace("|", "+").replace(" ", "-"); log.info(bar); diff --git a/src/main/java/org/usf/jquery/web/YearTableMetadata.java b/src/main/java/org/usf/jquery/web/YearTableMetadata.java index a0899be3..51355081 100644 --- a/src/main/java/org/usf/jquery/web/YearTableMetadata.java +++ b/src/main/java/org/usf/jquery/web/YearTableMetadata.java @@ -1,18 +1,22 @@ package org.usf.jquery.web; +import static java.lang.String.format; import static java.lang.String.join; import static java.time.Month.DECEMBER; +import static java.util.Comparator.comparing; import static java.util.Comparator.reverseOrder; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Optional.empty; import static java.util.function.Function.identity; import static java.util.function.Predicate.not; +import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toMap; import static org.usf.jquery.core.JDBCType.OTHER; import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.core.Utils.isPresent; import static org.usf.jquery.web.Constants.EMPTY_REVISION; import static org.usf.jquery.web.JQueryContext.database; @@ -26,6 +30,7 @@ import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; +import java.util.stream.Stream; import java.util.NoSuchElementException; import java.util.Optional; import java.util.Set; @@ -146,4 +151,19 @@ static YearTableMetadata yearTableMetadata(YearViewDecorator table) { table.monthRevision().map(table::columnName).orElse(null), table.declaredColumns()); } + + @Deprecated + static void logRevisions(YearMonth[] revs) { + if(isPresent(revs)) { + var pattern = "|%-5s|%-40s|"; + var bar = format(pattern, "", "").replace("|", "+").replace(" ", "-"); + var map = Stream.of(revs).collect(groupingBy(YearMonth::getYear)); + log.info(bar); + log.info(format(pattern, "YEAR", "MONTHS")); + log.info(bar); + map.entrySet().stream().sorted(comparing(Entry::getKey)).forEach(e-> + log.info(format(pattern, e.getKey(), e.getValue().stream().map(o-> o.getMonthValue() + "").collect(joining(", "))))); + log.info(bar); + } + } } From 535e838a9f6cbf9cd79e5d94152a72c1504b9601 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 1 Aug 2024 00:32:59 +0200 Subject: [PATCH 100/298] edit --- .../usf/jquery/core/ColumnSingleFilter.java | 2 +- .../usf/jquery/core/ComparisonExpression.java | 2 +- .../core/ComparisonExpressionGroup.java | 2 +- .../core/ComparisonSingleExpression.java | 2 +- .../java/org/usf/jquery/core/DBColumn.java | 6 +- .../java/org/usf/jquery/core/DBFilter.java | 2 +- src/main/java/org/usf/jquery/core/DBView.java | 5 +- .../java/org/usf/jquery/core/Groupable.java | 3 +- .../core/{Aggregable.java => Nested.java} | 6 +- .../org/usf/jquery/core/OperationColumn.java | 9 +- .../jquery/core/QueryParameterBuilder.java | 24 ++- .../java/org/usf/jquery/core/QueryView.java | 16 +- .../usf/jquery/core/RequestQueryBuilder.java | 17 +- .../org/usf/jquery/core/WhenExpression.java | 3 +- .../org/usf/jquery/web/ArgumentParsers.java | 10 +- .../org/usf/jquery/web/ColumnDecorator.java | 6 +- .../web/ConflictingResourceException.java | 18 +++ .../usf/jquery/web/ContextEnvironment.java | 90 ++++++----- .../usf/jquery/web/EntryParseException.java | 36 +++++ .../usf/jquery/web/JDBCArgumentParser.java | 4 +- .../org/usf/jquery/web/ParseException.java | 26 ---- .../org/usf/jquery/web/QueryDecorator.java | 33 ++-- .../org/usf/jquery/web/RequestEntryChain.java | 147 +++++++++--------- .../org/usf/jquery/web/RequestParser.java | 12 +- .../jquery/web/RequestQueryParamResolver.java | 4 +- .../jquery/web/UnexpectedEntryException.java | 20 --- .../org/usf/jquery/web/ViewDecorator.java | 3 +- .../org/usf/jquery/web/YearViewDecorator.java | 4 +- .../org/usf/jquery/web/RequestParserTest.java | 2 +- 29 files changed, 284 insertions(+), 230 deletions(-) rename src/main/java/org/usf/jquery/core/{Aggregable.java => Nested.java} (63%) create mode 100644 src/main/java/org/usf/jquery/web/ConflictingResourceException.java create mode 100644 src/main/java/org/usf/jquery/web/EntryParseException.java delete mode 100644 src/main/java/org/usf/jquery/web/ParseException.java delete mode 100644 src/main/java/org/usf/jquery/web/UnexpectedEntryException.java diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index 85970dbf..5de65f74 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Aggregable.aggregation; +import static org.usf.jquery.core.Nested.aggregation; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import lombok.AccessLevel; diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpression.java b/src/main/java/org/usf/jquery/core/ComparisonExpression.java index 580f2912..006f81bb 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpression.java @@ -9,7 +9,7 @@ * @author u$f * */ -public interface ComparisonExpression extends DBExpression, Aggregable, Chainable { +public interface ComparisonExpression extends DBExpression, Nested, Chainable { @Override default String sql(QueryParameterBuilder builder, Object[] args) { diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index f379d832..4a42c383 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -33,7 +33,7 @@ public String sql(QueryParameterBuilder builder, Object operand) { public boolean isAggregation() { return Stream.of(expressions).anyMatch(ComparisonExpression::isAggregation); } - + @Override public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) { return operator == op diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index 689bbf0e..d2843ef1 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -33,7 +33,7 @@ public String sql(QueryParameterBuilder builder, Object left) { @Override public boolean isAggregation() { - return nonNull(right) && Stream.of(right).anyMatch(Aggregable::aggregation); + return nonNull(right) && Stream.of(right).anyMatch(Nested::aggregation); } @Override diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index afe4b532..a1850975 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -43,7 +43,7 @@ default Stream groupKeys() { default JDBCType getType() { return null; } - + default NamedColumn as(String name) { return new NamedColumn(this, Objects.isNull(name) ? null : requireLegalVariable(name)); } @@ -145,8 +145,8 @@ static DBColumn column(@NonNull String value) { return b-> value; } - static DBColumn allColumns(@NonNull DBView view) { - return column(view, "*"); + static TaggableColumn allColumns(@NonNull DBView view) { + return column(view, "*").as(null); //no tag } static DBColumn column(@NonNull DBView view, @NonNull String value) { diff --git a/src/main/java/org/usf/jquery/core/DBFilter.java b/src/main/java/org/usf/jquery/core/DBFilter.java index 4e224c41..9aaabcc4 100644 --- a/src/main/java/org/usf/jquery/core/DBFilter.java +++ b/src/main/java/org/usf/jquery/core/DBFilter.java @@ -8,7 +8,7 @@ * */ @FunctionalInterface -public interface DBFilter extends DBObject, Aggregable, Chainable { +public interface DBFilter extends DBObject, Nested, Chainable { @Override default String sql(QueryParameterBuilder builder, Object[] args) { diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 79312b6d..f59bcb51 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -10,6 +10,7 @@ */ public interface DBView extends DBObject { + @Deprecated String id(); //technical use @Override @@ -23,8 +24,4 @@ default String sqlWithTag(QueryParameterBuilder builder) { } String sql(QueryParameterBuilder builder); - - default QueryView select(String tag, TaggableColumn... columns) { - return new QueryView(tag, columns); - } } diff --git a/src/main/java/org/usf/jquery/core/Groupable.java b/src/main/java/org/usf/jquery/core/Groupable.java index 2a13f703..883ce7a2 100644 --- a/src/main/java/org/usf/jquery/core/Groupable.java +++ b/src/main/java/org/usf/jquery/core/Groupable.java @@ -7,8 +7,7 @@ * @author u$f * */ -public interface Groupable extends Aggregable { +public interface Groupable extends Nested { Stream groupKeys(); - } diff --git a/src/main/java/org/usf/jquery/core/Aggregable.java b/src/main/java/org/usf/jquery/core/Nested.java similarity index 63% rename from src/main/java/org/usf/jquery/core/Aggregable.java rename to src/main/java/org/usf/jquery/core/Nested.java index f0ff4ea1..ab05a596 100644 --- a/src/main/java/org/usf/jquery/core/Aggregable.java +++ b/src/main/java/org/usf/jquery/core/Nested.java @@ -5,13 +5,13 @@ * @author u$f * */ -public interface Aggregable { +public interface Nested { default boolean isAggregation() { return false; } - + static boolean aggregation(Object o) { - return o instanceof Aggregable agg && agg.isAggregation(); + return o instanceof Nested nes && nes.isAggregation(); } } diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index f8f047f5..864a5cb9 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -35,13 +35,13 @@ public JDBCType getType() { @Override public boolean isAggregation() { - return operator instanceof AggregateFunction - || (!isOver() && Stream.of(args).anyMatch(Aggregable::aggregation)); //can do better + return operator instanceof AggregateFunction || + (!isOverFunction() && Stream.of(args).anyMatch(Nested::aggregation)); //can do better } @Override public Stream groupKeys() { - if(isOver()) { + if(isOverFunction()) { return ((Partition)args[1]).groupKeys(); } return operator instanceof AggregateFunction || operator instanceof ConstantOperator @@ -49,7 +49,7 @@ public Stream groupKeys() { : DBColumn.super.groupKeys(); } - private boolean isOver() { //specific operator + boolean isOverFunction() { return "OVER".equals(operator.id()); } @@ -57,4 +57,5 @@ private boolean isOver() { //specific operator public String toString() { return sql(addWithValue()); } + } diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 94933dba..16fac279 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -11,7 +11,9 @@ import static org.usf.jquery.core.Utils.isEmpty; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Stream; import lombok.AccessLevel; @@ -36,6 +38,9 @@ public final class QueryParameterBuilder { private final List args; private final List argTypes; private final List views; //indexed + + private final Map overView = new HashMap<>(); + private Integer index; public List views(){ @@ -46,14 +51,25 @@ public String view(DBView view) { if(isNull(vPrefix)) { //view can be null return null; } - for(var i=0; i -1) { + return vPrefix + (idx+1); } views.add(view); return vPrefix + views.size(); } + + public void overView(DBView oldView, DBView newView) { //WindowFunction + var idx = views.indexOf(oldView); + if(idx > -1) { + views.set(idx, newView); //replace + } + else { + views.add(newView); + } + overView.put(oldView, newView); + } public String appendArrayParameter(Object[] arr) { return appendArrayParameter(arr, 0); diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index 3215c530..37d86844 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -19,16 +19,12 @@ public final class QueryView implements DBQuery { private final String id; @Getter // remove this - private final RequestQueryBuilder query; + private final RequestQueryBuilder builder; - public QueryView(String id, TaggableColumn... columns) { - this(id, new RequestQueryBuilder().columns(columns)); - } - @Override - public String sql(QueryParameterBuilder builder) { + public String sql(QueryParameterBuilder param) { var s = new SqlStringBuilder(100); - query.build(s, builder.subQuery()); //important! build query first + builder.build(s, param.subQuery()); //important! build query first return parenthese(s.toString()); } @@ -39,11 +35,11 @@ public String id() { @Override public Collection columns() { - return query.getColumns(); + return builder.getColumns(); } - + @Override public String toString() { return sql(addWithValue()); - } + } } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 5a52709e..0b815385 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -1,6 +1,7 @@ package org.usf.jquery.core; import static java.lang.System.currentTimeMillis; +import static java.util.Collections.addAll; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.function.Predicate.not; @@ -54,22 +55,22 @@ public RequestQueryBuilder views(@NonNull DBView... views) { } public RequestQueryBuilder columns(@NonNull TaggableColumn... columns) { - Stream.of(columns).forEach(this.columns::add); + addAll(this.columns, columns); return this; } public RequestQueryBuilder filters(@NonNull DBFilter... filters){ - Stream.of(filters).forEach(this.filters::add); + addAll(this.filters, filters); return this; } public RequestQueryBuilder orders(@NonNull DBOrder... orders) { - Stream.of(orders).forEach(this.orders::add); + addAll(this.orders, orders); return this; } public RequestQueryBuilder joins(@NonNull ViewJoin joins) { - Stream.of(joins).forEach(this.joins::add); + addAll(this.joins, joins); return this; } @@ -97,7 +98,7 @@ public Optional getColumn(String id){ return columns.stream().filter(c-> c.tagname().contains(id)).findAny(); } - public QueryView as(String tag) { + public QueryView asView(String tag) { return new QueryView(tag, this); } @@ -159,7 +160,7 @@ void from(SqlStringBuilder sb, QueryParameterBuilder pb) { if(!joins.isEmpty()) { vList = vList.stream() .filter(v-> joins.stream().noneMatch(j-> j.id().equals(v.id()))) - .collect(toList()); + .toList(); } if(!vList.isEmpty()) { sb.append(" FROM ") @@ -225,8 +226,8 @@ void fetch(SqlStringBuilder sb) { } public boolean isAggregation() { - return columns.stream().anyMatch(Aggregable::isAggregation) || - filters.stream().anyMatch(Aggregable::isAggregation); + return columns.stream().anyMatch(Nested::isAggregation) || + filters.stream().anyMatch(Nested::isAggregation); } @Override diff --git a/src/main/java/org/usf/jquery/core/WhenExpression.java b/src/main/java/org/usf/jquery/core/WhenExpression.java index 5eb54580..fcb62a74 100644 --- a/src/main/java/org/usf/jquery/core/WhenExpression.java +++ b/src/main/java/org/usf/jquery/core/WhenExpression.java @@ -4,6 +4,7 @@ import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.Validation.requireNoArgs; +import static org.usf.jquery.core.Nested.*; import org.usf.jquery.core.JavaType.Typed; @@ -35,7 +36,7 @@ public String sql(QueryParameterBuilder arg) { .append(" THEN "); return sb.append(arg.appendLiteral(value)).toString(); } - + @Override public JDBCType getType() { return typeOf(value).orElse(null); diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index d0c51f18..53475dd0 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -58,17 +58,17 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType.. } public static Object parseJdbc(RequestEntryChain entry, ViewDecorator td, JDBCType... types) { - ParseException ex = null; // preserve last exception + EntryParseException ex = null; // preserve last exception try { return matchTypes((Typed) parseJQuery(entry, td, COLUMN, QUERY), types); //try parse column | query first - } catch (ParseException e) {/*do not throw exception*/} + } catch (EntryParseException e) {/*do not throw exception*/} if(isEmpty(types)) { types = STD_TYPES; } for(var type : types) { try { return jdbcArgParser(type).parseEntry(entry, td); - } catch (ParseException e) { /*do not throw exception*/ + } catch (EntryParseException e) { /*do not throw exception*/ log.trace("parse {} : '{}' => {}", type, entry, e.getMessage()); ex = e; } @@ -77,11 +77,11 @@ public static Object parseJdbc(RequestEntryChain entry, ViewDecorator td, JDBCTy } public static Object parseJQuery(RequestEntryChain entry, ViewDecorator td, JQueryType... types) { - ParseException ex = null; // preserve last exception + EntryParseException ex = null; // preserve last exception for(var type : types) { try { return jqueryArgParser(type).parseEntry(entry, td); - } catch (ParseException e) {/*do not throw exception*/ + } catch (EntryParseException e) {/*do not throw exception*/ log.trace("parse {} : '{}' => {}", type, entry, e.getMessage()); ex = e; } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 41e177f8..e8dcfec1 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -1,6 +1,7 @@ package org.usf.jquery.web; import org.usf.jquery.core.ComparisonExpression; +import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.JDBCType; /** @@ -46,7 +47,8 @@ default boolean canFilter(ViewDecorator td) { throw new UnsupportedOperationException(); //authorization inject } - static ColumnDecorator ofColumn(String ref, ColumnBuilder cb) { + static ColumnDecorator ofColumn(String ref, DBColumn column) { + final ColumnBuilder builder = b-> column; return new ColumnDecorator() { @Override public String identity() { @@ -55,7 +57,7 @@ public String identity() { @Override public ColumnBuilder builder(ViewDecorator td) { - return cb; + return builder; } }; } diff --git a/src/main/java/org/usf/jquery/web/ConflictingResourceException.java b/src/main/java/org/usf/jquery/web/ConflictingResourceException.java new file mode 100644 index 00000000..6cd91c7d --- /dev/null +++ b/src/main/java/org/usf/jquery/web/ConflictingResourceException.java @@ -0,0 +1,18 @@ +package org.usf.jquery.web; + +/** + * + * @author u$f + * + */ +@SuppressWarnings("serial") +public class ConflictingResourceException extends WebException { + + public ConflictingResourceException(String message) { + super(message); + } + + public static ConflictingResourceException resourceAlreadyExistsException(String name, String value) { + return new ConflictingResourceException(name + " already exists : " + value); + } +} diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index d3dd06c1..cfca33d8 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -3,22 +3,27 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; +import static java.util.Optional.ofNullable; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toUnmodifiableMap; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNonEmpty; -import static org.usf.jquery.web.NoSuchResourceException.noSuchViewException; +import static org.usf.jquery.web.ConflictingResourceException.resourceAlreadyExistsException; import java.sql.SQLException; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.function.Function; +import java.util.function.Supplier; import javax.sql.DataSource; +import org.usf.jquery.core.DBView; import org.usf.jquery.core.JQueryException; import org.usf.jquery.core.QueryView; +import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.Validation; import lombok.AccessLevel; @@ -31,8 +36,8 @@ * @author u$f * */ -@Getter @Slf4j +@Getter @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class ContextEnvironment { @@ -41,7 +46,10 @@ public final class ContextEnvironment { private final Map columns; private final DataSource dataSource; //optional private final String schema; //optional - private final DatabaseMetadata metadata; + private final DatabaseMetadata metadata = new DatabaseMetadata(); + + private final Map overView = new HashMap<>(); + private final Map declaredColumns = new HashMap<>(); public ContextEnvironment(ContextEnvironment ctx) { this.database = ctx.database; @@ -49,15 +57,40 @@ public ContextEnvironment(ContextEnvironment ctx) { this.columns = new HashMap<>(ctx.columns); //modifiable this.dataSource = ctx.dataSource; this.schema = ctx.schema; - this.metadata = ctx.metadata; } - public ViewDecorator lookupTable(String name) { - var vd = views.get(name); - if(nonNull(vd)) { - return vd; - } - throw noSuchViewException(name); + public Optional lookupRegistredView(String name) { + return ofNullable(views.get(name)); + } + + public Optional lookupRegistredColumn(String name) { + return ofNullable(columns.get(name)); + } + + Optional lookupDeclaredColumn(String name) { + return ofNullable(declaredColumns.get(name)); + } + + void declareView(ViewDecorator view) { + views.compute(view.identity(), (k,v)-> { + if(isNull(v)){ + return view; + } + throw resourceAlreadyExistsException("view", k); + }); + } + + void declareColumn(TaggableColumn col) { + declaredColumns.compute(col.tagname(), (k,v)-> { + if(isNull(v)){ + return col; + } + throw resourceAlreadyExistsException("column", k); + }); + } + + QueryView overView(DBView v1, Supplier supp) { + return overView.computeIfAbsent(v1, k-> supp.get()); } ViewMetadata computeTableMetadata(ViewDecorator vd, Function, ViewMetadata> fn) { @@ -65,39 +98,18 @@ ViewMetadata computeTableMetadata(ViewDecorator vd, Function { - if(isNull(v)){ - return new QueryDecorator(query); - } - throw new IllegalStateException("already exists"); - }); - query.getQuery().getColumns() - .stream().map(c-> c::tagname) - .forEach(cd-> columns.compute(cd.identity(), (k,v)-> { - if(isNull(v)) { - return cd; - } - throw new IllegalStateException("already exists"); - })); - } - public static final ContextEnvironment of(DatabaseDecorator database, Collection views, Collection columns) { return of(database, views, columns, null, null); @@ -109,13 +121,13 @@ public static final ContextEnvironment of(DatabaseDecorator database, } public static final ContextEnvironment of(DatabaseDecorator database, - Collection views, Collection columns, DataSource ds, String schema) { + Collection views, Collection columns, DataSource ds, String schema) { requireLegalVariable(database.identity()); return new ContextEnvironment( requireNonNull(database, "configuration.database"), unmodifiableIdentityMap(requireNonEmpty(views, "configuration.views"), ViewDecorator::identity), unmodifiableIdentityMap(requireNonEmpty(columns, "configuration.columns"), ColumnDecorator::identity), - ds, schema, new DatabaseMetadata()); + ds, schema); } static Map unmodifiableIdentityMap(Collection c, Function fn){ diff --git a/src/main/java/org/usf/jquery/web/EntryParseException.java b/src/main/java/org/usf/jquery/web/EntryParseException.java new file mode 100644 index 00000000..029a544c --- /dev/null +++ b/src/main/java/org/usf/jquery/web/EntryParseException.java @@ -0,0 +1,36 @@ +package org.usf.jquery.web; + +import static java.lang.String.format; + +/** + * + * @author u$f + * + */ +@SuppressWarnings("serial") +public final class EntryParseException extends WebException { + + public EntryParseException(String message) { + super(message); + } + + public EntryParseException(String message, Throwable cause) { + super(message, cause); + } + + static EntryParseException cannotParseEntryException(String type, String value) { + return cannotParseEntryException(type, value, null); + } + + static EntryParseException cannotParseEntryException(String type, String value, Throwable cause) { + return new EntryParseException(format("cannot parse %s : '%s'", type, value), cause); + } + + static EntryParseException unexpectedEntryException(String entry) { + return new EntryParseException(format("unexpected entry : '%s'", entry)); + } + + static EntryParseException requireEntryException(String name) { + return new EntryParseException(name + " required"); + } +} diff --git a/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java index f13b0319..6ae42144 100644 --- a/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java +++ b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java @@ -1,6 +1,6 @@ package org.usf.jquery.web; -import static org.usf.jquery.web.ParseException.cannotParseException; +import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; /** * @@ -22,7 +22,7 @@ default Object parseValue(String v) { return nativeParse(v); } catch(Exception e) { - throw cannotParseException(toString(), v, e); + throw cannotParseEntryException(toString(), v, e); } } } diff --git a/src/main/java/org/usf/jquery/web/ParseException.java b/src/main/java/org/usf/jquery/web/ParseException.java deleted file mode 100644 index 4686e4b4..00000000 --- a/src/main/java/org/usf/jquery/web/ParseException.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.usf.jquery.web; - -/** - * - * @author u$f - * - */ -@SuppressWarnings("serial") -public final class ParseException extends WebException { - - public ParseException(String message) { - super(message); - } - - public ParseException(String message, Throwable cause) { - super(message, cause); - } - - static ParseException cannotParseException(String type, String value) { - return cannotParseException(type, value, null); - } - - static ParseException cannotParseException(String type, String value, Throwable cause) { - return new ParseException(formatMessage("cannot parse entry", type, value), cause); - } -} diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 74e989b0..710aa834 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -28,26 +28,39 @@ public String identity() { } @Override - public String columnName(ColumnDecorator cd) { - return null; + public DBView view() { + return query; + } + + public TaggableColumn column(String id) { + return query.getBuilder().getColumns().stream() + .filter(c-> c.tagname().equals(id)) //tagname nullable ! + .findAny() + .map(c-> new ViewColumn(query, c.tagname(), c.tagname(), c.getType())) + .orElseThrow(()-> throwNoSuchColumnException(id)); } @Override - public DBView view() { - return query; + public String columnName(ColumnDecorator cd) { + throw unsupportedOperationException("columnName"); } @Override public TaggableColumn column(@NonNull ColumnDecorator cd) { - return query.getQuery().getColumns().stream() - .filter(c-> c.tagname().equals(cd.identity())) //tagname nullable ! - .findAny() - .map(c-> new ViewColumn(query, c.tagname(), c.tagname(), c.getType())) - .orElseThrow(()-> throwNoSuchColumnException(cd.identity())); + throw unsupportedOperationException("column"); + } + + @Override + public ViewBuilder builder() { + throw unsupportedOperationException("builder"); } @Override public ViewMetadata metadata() { - throw new UnsupportedOperationException("query metadata"); + throw unsupportedOperationException("metadata"); + } + + UnsupportedOperationException unsupportedOperationException(String method) { + return new UnsupportedOperationException(this.getClass().getSimpleName() + "." + method); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index bb0bc304..ca3f58e0 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -4,6 +4,7 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Optional.empty; +import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.BadArgumentException.badArgumentsException; @@ -28,7 +29,10 @@ import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.PARTITION; import static org.usf.jquery.web.Constants.SELECT; -import static org.usf.jquery.web.ParseException.cannotParseException; +import static org.usf.jquery.web.ContextManager.currentContext; +import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; +import static org.usf.jquery.web.EntryParseException.requireEntryException; +import static org.usf.jquery.web.EntryParseException.unexpectedEntryException; import static org.usf.jquery.web.RequestContext.currentContext_; import static org.usf.jquery.web.UnexpectedEntryException.unexpectedEntryException; @@ -48,6 +52,7 @@ import org.usf.jquery.core.JavaType; import org.usf.jquery.core.LogicalOperator; import org.usf.jquery.core.OperationColumn; +import org.usf.jquery.core.Operator; import org.usf.jquery.core.Order; import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; @@ -90,7 +95,7 @@ public RequestEntryChain(String value) { this(value, false); } - public QueryView evalQuery(ViewDecorator td) { + public ViewDecorator evalQuery(ViewDecorator td) { return evalQuery(td, false); } @@ -98,20 +103,20 @@ public QueryView evalQuery(ViewDecorator td) { public ViewJoin evalJoin(ViewDecorator td) { if(value.matches(JoinType.pattern())) { var jt = JoinType.valueOf(value); - var args = toArgs(td, null, null); + var args = toArgs( td, null, null); return join(jt, (DBView)args[0], (DBFilter[])args[0]); } - throw cannotParseException(JOIN, this.toString()); //TD + throw cannotParseEntryException(JOIN, this.toString()); //TD } //evalView query|view:alias - public QueryView evalQuery(ViewDecorator td, boolean requireTag) { + public ViewDecorator evalQuery(ViewDecorator td, boolean requireTag) { //sub context if(SELECT.equals(value)) { var q = new RequestQueryBuilder().columns(toColumnArgs(td, false)); var e = this; - while(e.next()) { //preserve last entry + while(e.hasNext()) { //preserve last entry e = e.next; switch(e.value) {//column not allowed case DISTINCT: e.requireNoArgs(); q.distinct(); break; @@ -126,15 +131,15 @@ public QueryView evalQuery(ViewDecorator td, boolean requireTag) { throw new IllegalArgumentException("require tag"); } q.views(currentContext_().popQueries().toArray(DBQuery[]::new)); - return q.as(e.tag); + return new QueryDecorator(q.asView(e.tag)); } - throw cannotParseException(SELECT, this.toString()); + throw cannotParseEntryException(SELECT, this.toString()); } public Partition evalPartition(ViewDecorator td) { if(PARTITION.equals(value)) { var p = new Partition(toColumnArgs(td, true)); - if(next()) { //TD loop + if(hasNext()) { //TD loop var e = next; if(ORDER.equals(e.value)) {//column not allowed p.orders(e.toOderArgs(td)); //not sure @@ -145,7 +150,7 @@ public Partition evalPartition(ViewDecorator td) { }//require no tag return p; } - throw cannotParseException(PARTITION, this.toString()); + throw cannotParseEntryException(PARTITION, this.toString()); } public TaggableColumn evalColumn(ViewDecorator td) { @@ -154,13 +159,12 @@ public TaggableColumn evalColumn(ViewDecorator td) { if(nonNull(r.entry.tag)) { //TD: required tag if operation return r.col.as(r.entry.tag); } - if(r.col instanceof TaggableColumn) { - return (TaggableColumn) r.col ; + if(r.col instanceof TaggableColumn col) { + return col; } - log.warn("tag missing : {}", this); - return r.col.as(r.cd.identity()); + throw requireEntryException("tag"); } - throw unexpectedEntryException(r.entry.next.toString(), "[view].column[.op]*"); + throw unexpectedEntryException(r.entry.next.toString()); } public DBOrder evalOrder(ViewDecorator td) { @@ -169,11 +173,14 @@ public DBOrder evalOrder(ViewDecorator td) { return r.col.order(); } var e = r.entry.next; - if(e.isLast() && e.value.matches("asc|desc")) { // next must be last - var o = Order.valueOf(e.requireNoArgs().value.toUpperCase()); // noArgs on valid order - return r.col.order(o); + if(e.isLast()) { // next must be last + if(e.value.matches("asc|desc")) { + var o = Order.valueOf(e.requireNoArgs().value.toUpperCase()); // order takes no args + return r.col.order(o); + } + cannotParseEntryException(ORDER, e.toString()); } - throw unexpectedEntryException(e.toString(), "asc|desc"); + throw unexpectedEntryException(e.toString()); } public DBFilter evalFilter(ViewDecorator td) { @@ -198,9 +205,9 @@ public DBFilter evalFilter(ViewDecorator td, List values) { return e.next.updateArgs(values) .chainComparator(td, r.cd, r.col); } - catch(ParseException e) { + catch(EntryParseException e) { return tableCriteria(td, values) - .orElseThrow(()-> cannotParseException(FILTER, this.toString(), e)); + .orElseThrow(()-> cannotParseEntryException(FILTER, this.toString(), e)); } } @@ -208,7 +215,7 @@ Optional tableCriteria(ViewDecorator td, List value RequestEntryChain e = null; CriteriaBuilder c = null; var res = currentContext_().lookupViewDecorator(value); - if(res.isPresent() && next()) { + if(res.isPresent() && hasNext()) { c = res.get().criteria(next.value); e = next; // only if nonNull } @@ -250,7 +257,7 @@ DBFilter chainComparator(ViewDecorator td, ColumnDecorator cd, DBColumn col){ if(nonNull(f)) { return chainComparator(td, f); } - throw cannotParseException("comparison|criteria", this.toString()); + throw cannotParseEntryException("comparison|criteria", this.toString()); } DBFilter chainComparator(ViewDecorator td, DBFilter f){ @@ -269,66 +276,62 @@ DBFilter chainComparator(ViewDecorator td, DBFilter f){ } private ResourceCursor chainColumnOperations(ViewDecorator td, boolean filter) { - var r = lookupResource(td).orElseThrow(()-> cannotParseException(COLUMN, this.toString())); + var r = lookupResource(td).orElseThrow(()-> cannotParseEntryException(COLUMN, this.toString())); var e = r.entry.next; while(nonNull(e)) { // chain until !operator - var c = e.toOperation(td, r.col, fn-> true); - if(c.isEmpty()) { + var o = e.lookupOperation(td, r.col, fn-> true); + if(o.isEmpty()) { break; } r.entry = e; - r.col = filter && "over".equals(e.value) - ? windowColumn(r.td, c.get().as(r.cd.identity())) - : c.get(); + r.col = filter && "over".equals(e.value) + ? windowColumn(r.td, o.get().as(r.cd.identity())) //require tag | random tag + : o.get(); e = e.next; } return r; } - private static DBColumn windowColumn(ViewDecorator td, TaggableColumn column) { - var vw = currentContext_().lookupView(td.identity()).orElse(null); - if(vw instanceof CompletableViewQuery) { // already create - ((CompletableViewQuery)vw).getQuery().columns(column); - } - else { - var view = isNull(vw) ? td.view() : vw; - vw = new CompletableViewQuery(view.select(td.identity(), allColumns(view).as(null), column)); - currentContext_().putWorkQuery(vw); // same name - } - return new ViewColumn(vw, doubleQuote(column.tagname()), null, column.getType()); + private static DBColumn windowColumn(ViewDecorator vd, TaggableColumn col) { + var v = vd.view(); + currentContext().overView(v, ()-> new RequestQueryBuilder() + .columns(allColumns(v)) + .asView(vd.identity())).getBuilder().columns(col); //append over column + return new ViewColumn(v, doubleQuote(col.tagname()), null, col.getType()); } - private Optional lookupResource(ViewDecorator td) { - if(next()) { //check td.cd first - var rc = currentContext_().lookupViewDecorator(value) + private Optional lookupResource(ViewDecorator td) { //do not change priority + if(hasNext()) { //check td.cd first + var rc = currentContext().lookupRegistredView(value) .flatMap(v-> next.lookupViewResource(v, RequestEntryChain::isWindowFunction)); if(rc.isPresent()) { - requireNoArgs(); // noArgs on valid resource + requireNoArgs(); //view takes no args return rc; } } - return lookupViewResource(td, fn-> true); // all operations + return currentContext().lookupDeclaredColumn(value) //declared column + .map(c-> new ResourceCursor(null, null, requireNoArgs(), c)) + .or(()-> lookupViewResource(td, fn-> true)); //registered column } private Optional lookupViewResource(ViewDecorator td, Predicate pre) { - var res = td.getClass() == QueryDecorator.class - ? ((QueryDecorator)td).lookupColumnDecorator(value) - : currentContext_().lookupColumnDecorator(value); - if(res.isPresent()) { - requireNoArgs(); - } - else { - res = toOperation(td, null, pre).map(op-> ofColumn(value, b-> op)); + if(td instanceof QueryDecorator qd) { //query column + var res = ofNullable(qd.column(value)).map(c-> new ResourceCursor(td, null, requireNoArgs(), c)); + if(res.isPresent()) { + return res; + } } - return res.map(cd-> new ResourceCursor(td, cd, this)); + return currentContext().lookupRegistredColumn(value) + .map(cd-> new ResourceCursor(td, cd, requireNoArgs(), td.column(cd))) + .or(()-> lookupOperation(td, null, pre).map(col-> new ResourceCursor(td, null, this, col))); } - private Optional toOperation(ViewDecorator td, DBColumn col, Predicate pre) { - return lookupOperator(value).filter(pre).map(fn-> { + private Optional lookupOperation(ViewDecorator td, DBColumn col, Predicate opr) { + return lookupOperator(value).filter(opr).map(fn-> { var c = col; - if(isNull(c) && isEmpty(args) && "count".equals(value)) { // id is MAJ + if(isNull(c) && isEmpty(args) && "COUNT".equals(fn.id())) { c = b-> { - b.view(td.view()); // important! register view + b.view(td.view()); // declare view return "*"; }; } @@ -384,16 +387,24 @@ private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunc }); return arr; } - catch (ParseException | BadArgumentException e) { + catch (EntryParseException | BadArgumentException e) { throw badArgumentsException(ps.toString(), this.toString(), e); } } + + + RequestEntryChain requireTag() { + if(nonNull(tag)) { + return this; + } + throw new UnexpectedEntryException(value + " must be the last entry : " + this); + } RequestEntryChain requireNoArgs() { if(isNull(args)) { return this; } - throw badArgumentCountException(0, args.size()); + throw badArgumentCountException(0, args.size()); //TODO } RequestEntryChain requireNoNext(){ @@ -407,7 +418,7 @@ public boolean isLast() { return isNull(next); } - public boolean next() { + public boolean hasNext() { return nonNull(next); } @@ -440,23 +451,19 @@ private static String[] toStringArray(List entries) { .toArray(String[]::new); } + @AllArgsConstructor static final class ResourceCursor { - private final ViewDecorator td; - private final ColumnDecorator cd; + private final ViewDecorator td; //optional + private ColumnDecorator cd; //optional private RequestEntryChain entry; private DBColumn col; - - public ResourceCursor(ViewDecorator td, ColumnDecorator cd, RequestEntryChain entry) { - this.td = td; - this.cd = cd; - this.entry = entry; - this.col = td.column(cd); - } + //chained !? @Override public String toString() { return td + "." + cd + " => " + entry.toString(); } } + } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 082fc523..dd8206d7 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -115,7 +115,7 @@ else if(!require) { //break condition private void requireChar(char rc) { if(c != rc) { - throw new ParseException("'" + rc + "' expected at index=" + idx); // before ends + throw new EntryParseException("'" + rc + "' expected at index=" + idx); // before ends } } @@ -125,15 +125,15 @@ private String requireLegalVariable(String s) { } throw s.isEmpty() && idx < size ? unexpectedCharException() - : new ParseException("illegal variable name : " + quote(s)); + : new EntryParseException("illegal variable name : " + quote(s)); } - private ParseException unexpectedCharException() { - return new ParseException("unexpected character '" + c + "' at index=" + idx); //end + private EntryParseException unexpectedCharException() { + return new EntryParseException("unexpected character '" + c + "' at index=" + idx); //end } - private ParseException somethingExpectedException() { - return new ParseException("something expected after '" + s.charAt(size-1) + "'"); + private EntryParseException somethingExpectedException() { + return new EntryParseException("something expected after '" + s.charAt(size-1) + "'"); } private static boolean legalTxtChar(char c) { diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index 24405254..8cd3cc7d 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -7,6 +7,7 @@ import static org.usf.jquery.web.ContextManager.context; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.ContextManager.releaseContext; +import static org.usf.jquery.web.NoSuchResourceException.noSuchViewException; import java.util.LinkedHashMap; import java.util.Map; @@ -42,7 +43,8 @@ public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull : context(ant.database()); try { var req = ctx - .lookupTable(ant.view()) + .lookupRegistredView(ant.view()) + .orElseThrow(()-> noSuchViewException(ant.view())) .query(parameterMap); //may edit map if(!ant.aggregationOnly() || req.isAggregation()) { log.trace("request parsed in {} ms", currentTimeMillis() - t); diff --git a/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java b/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java deleted file mode 100644 index 06e160dc..00000000 --- a/src/main/java/org/usf/jquery/web/UnexpectedEntryException.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.usf.jquery.web; - -import static java.lang.String.join; - -/** - * - * @author u$f - * - */ -@SuppressWarnings("serial") -public class UnexpectedEntryException extends WebException { - - public UnexpectedEntryException(String message) { - super(message); - } - - public static UnexpectedEntryException unexpectedEntryException(String entry, String... expected) { - return new UnexpectedEntryException(formatMessage("unexpected entry, ", join("|", expected), entry)); - } -} diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 821a7327..692f86ae 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -14,7 +14,6 @@ import static org.usf.jquery.web.Constants.FETCH; import static org.usf.jquery.web.Constants.OFFSET; import static org.usf.jquery.web.Constants.ORDER; -import static org.usf.jquery.web.Constants.RESERVED_WORDS; import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.MissingParameterException.missingParameterException; @@ -137,7 +136,7 @@ default void parseViews(RequestQueryBuilder query, Map paramet if(parameters.containsKey(VIEW)) { Stream.of(parameters.get(VIEW)) .flatMap(c-> parseEntries(c).stream()) - .forEach(e-> currentContext().registerQuery(e.evalQuery(this, true))); + .forEach(e-> currentContext().declareView(e.evalQuery(this, true))); } } diff --git a/src/main/java/org/usf/jquery/web/YearViewDecorator.java b/src/main/java/org/usf/jquery/web/YearViewDecorator.java index faa2b339..d39dad31 100644 --- a/src/main/java/org/usf/jquery/web/YearViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearViewDecorator.java @@ -13,7 +13,7 @@ import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.JQueryContext.database; import static org.usf.jquery.web.NoSuchResourceException.noSuchResouceException; -import static org.usf.jquery.web.ParseException.cannotParseException; +import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import static org.usf.jquery.web.RevisionIterator.iterator; import static org.usf.jquery.web.RevisionIterator.monthFilter; import static org.usf.jquery.web.RevisionIterator.yearColumn; @@ -158,7 +158,7 @@ default YearMonth parseYearMonth(String revision) { return YearMonth.parse(revision); } catch (Exception e) { - throw cannotParseException(REVISION, revision ,e); + throw cannotParseEntryException(REVISION, revision ,e); } } diff --git a/src/test/java/org/usf/jquery/web/RequestParserTest.java b/src/test/java/org/usf/jquery/web/RequestParserTest.java index ca5d9933..bdf2a4cc 100644 --- a/src/test/java/org/usf/jquery/web/RequestParserTest.java +++ b/src/test/java/org/usf/jquery/web/RequestParserTest.java @@ -75,6 +75,6 @@ void testParse(String s) { "aa.fn(\"a:3,b,c,d&\")", }) void testParse2(String s) { - assertThrows(ParseException.class, ()-> parseEntry(s)); + assertThrows(EntryParseException.class, ()-> parseEntry(s)); } } From d8aac230066d50fa37ddf3f9f77165c2763d8c3a Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 1 Aug 2024 01:01:26 +0200 Subject: [PATCH 101/298] edit --- .../java/org/usf/jquery/web/Constants.java | 3 +- .../usf/jquery/web/EntryParseException.java | 6 ++++ .../org/usf/jquery/web/RequestEntryChain.java | 35 ++++++++++--------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index 712e8412..1bc8f2ce 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -15,7 +15,7 @@ public final class Constants { public static final String VIEW = "view"; - public static final String SELECT = "select"; //select + public static final String SELECT = "select"; public static final String COLUMN = "column"; //select public static final String DISTINCT = "distinct"; //select.distinct public static final String COLUMN_DISTINCT = "column.distinct"; //select.distinct @@ -24,6 +24,7 @@ public final class Constants { public static final String FETCH = "fetch"; public static final String OFFSET = "offset"; public static final String JOIN = "join"; + public static final String TAG = "tag"; public static final String PARTITION = "partition"; public static final String REVISION = "revision"; //not standard public static final String REVISION_MODE = "revision.mode"; //not standard diff --git a/src/main/java/org/usf/jquery/web/EntryParseException.java b/src/main/java/org/usf/jquery/web/EntryParseException.java index 029a544c..c906f2d6 100644 --- a/src/main/java/org/usf/jquery/web/EntryParseException.java +++ b/src/main/java/org/usf/jquery/web/EntryParseException.java @@ -33,4 +33,10 @@ static EntryParseException unexpectedEntryException(String entry) { static EntryParseException requireEntryException(String name) { return new EntryParseException(name + " required"); } + + static EntryParseException entryTackesNoArgException(String name, String entry) { + return new EntryParseException(format("%s takes no arg : '%s'", name, entry)); + } + + } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index ca3f58e0..cc3f4370 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -29,13 +29,16 @@ import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.PARTITION; import static org.usf.jquery.web.Constants.SELECT; +import static org.usf.jquery.web.Constants.TAG; +import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; +import static org.usf.jquery.web.EntryParseException.entryTackesNoArgException; import static org.usf.jquery.web.EntryParseException.requireEntryException; import static org.usf.jquery.web.EntryParseException.unexpectedEntryException; import static org.usf.jquery.web.RequestContext.currentContext_; -import static org.usf.jquery.web.UnexpectedEntryException.unexpectedEntryException; +import java.text.ParseException; import java.util.List; import java.util.Optional; import java.util.function.IntFunction; @@ -119,21 +122,20 @@ public ViewDecorator evalQuery(ViewDecorator td, boolean requireTag) { //sub con while(e.hasNext()) { //preserve last entry e = e.next; switch(e.value) {//column not allowed - case DISTINCT: e.requireNoArgs(); q.distinct(); break; + case DISTINCT: e.requireNoArgs(DISTINCT); q.distinct(); break; case FILTER: q.filters(e.toFilterArgs(td)); break; case ORDER: q.orders(e.toOderArgs(td)); break; //not sure case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; - default: throw unexpectedEntryException(e.toString(), DISTINCT, FILTER, ORDER, OFFSET, FETCH); + default: throw unexpectedEntryException(e.toString()); } } if(requireTag && isNull(e.tag)) { - throw new IllegalArgumentException("require tag"); + throw requireEntryException(TAG); } - q.views(currentContext_().popQueries().toArray(DBQuery[]::new)); return new QueryDecorator(q.asView(e.tag)); } - throw cannotParseEntryException(SELECT, this.toString()); + throw cannotParseEntryException(SELECT, toString()); } public Partition evalPartition(ViewDecorator td) { @@ -145,7 +147,7 @@ public Partition evalPartition(ViewDecorator td) { p.orders(e.toOderArgs(td)); //not sure } else { - throw unexpectedEntryException(e.toString(), ORDER); + throw unexpectedEntryException(e.toString()); } }//require no tag return p; @@ -162,7 +164,7 @@ public TaggableColumn evalColumn(ViewDecorator td) { if(r.col instanceof TaggableColumn col) { return col; } - throw requireEntryException("tag"); + throw requireEntryException(TAG); } throw unexpectedEntryException(r.entry.next.toString()); } @@ -175,7 +177,7 @@ public DBOrder evalOrder(ViewDecorator td) { var e = r.entry.next; if(e.isLast()) { // next must be last if(e.value.matches("asc|desc")) { - var o = Order.valueOf(e.requireNoArgs().value.toUpperCase()); // order takes no args + var o = Order.valueOf(e.requireNoArgs(ORDER).value.toUpperCase()); // order takes no args return r.col.order(o); } cannotParseEntryException(ORDER, e.toString()); @@ -305,24 +307,24 @@ private Optional lookupResource(ViewDecorator td) { //do not cha var rc = currentContext().lookupRegistredView(value) .flatMap(v-> next.lookupViewResource(v, RequestEntryChain::isWindowFunction)); if(rc.isPresent()) { - requireNoArgs(); //view takes no args + requireNoArgs(VIEW); //view takes no args return rc; } } return currentContext().lookupDeclaredColumn(value) //declared column - .map(c-> new ResourceCursor(null, null, requireNoArgs(), c)) + .map(c-> new ResourceCursor(null, null, requireNoArgs(COLUMN), c)) .or(()-> lookupViewResource(td, fn-> true)); //registered column } private Optional lookupViewResource(ViewDecorator td, Predicate pre) { if(td instanceof QueryDecorator qd) { //query column - var res = ofNullable(qd.column(value)).map(c-> new ResourceCursor(td, null, requireNoArgs(), c)); + var res = ofNullable(qd.column(value)).map(c-> new ResourceCursor(td, null, requireNoArgs(COLUMN), c)); if(res.isPresent()) { return res; } } return currentContext().lookupRegistredColumn(value) - .map(cd-> new ResourceCursor(td, cd, requireNoArgs(), td.column(cd))) + .map(cd-> new ResourceCursor(td, cd, requireNoArgs(COLUMN), td.column(cd))) .or(()-> lookupOperation(td, null, pre).map(col-> new ResourceCursor(td, null, this, col))); } @@ -391,13 +393,12 @@ private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunc throw badArgumentsException(ps.toString(), this.toString(), e); } } - - RequestEntryChain requireTag() { - if(nonNull(tag)) { + RequestEntryChain requireNoArgs(String name) { + if(isNull(args)) { return this; } - throw new UnexpectedEntryException(value + " must be the last entry : " + this); + throw entryTackesNoArgException(name, toString()); } RequestEntryChain requireNoArgs() { From 4a985dc37e84094cd823e735d8fbb60b83019e74 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 1 Aug 2024 22:49:51 +0200 Subject: [PATCH 102/298] edit --- src/main/java/org/usf/jquery/core/DBView.java | 4 +- .../java/org/usf/jquery/core/QueryView.java | 6 -- .../usf/jquery/core/RequestQueryBuilder.java | 4 +- .../java/org/usf/jquery/core/TableView.java | 12 +-- .../java/org/usf/jquery/core/ViewJoin.java | 4 - .../org/usf/jquery/web/ColumnDecorator.java | 15 ---- .../org/usf/jquery/web/ColumnMetadata.java | 34 ++++---- .../usf/jquery/web/CompletableViewQuery.java | 25 ------ .../usf/jquery/web/ContextEnvironment.java | 21 +++-- .../org/usf/jquery/web/ContextManager.java | 11 ++- .../jquery/web/NoSuchResourceException.java | 17 ++-- .../org/usf/jquery/web/QueryDecorator.java | 5 +- .../org/usf/jquery/web/RequestContext.java | 77 ------------------ .../org/usf/jquery/web/RequestEntryChain.java | 9 +-- .../org/usf/jquery/web/ViewDecorator.java | 78 ++++++------------- .../java/org/usf/jquery/web/ViewMetadata.java | 6 +- 16 files changed, 89 insertions(+), 239 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/web/CompletableViewQuery.java delete mode 100644 src/main/java/org/usf/jquery/web/RequestContext.java diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index f59bcb51..803af0f8 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -8,11 +8,9 @@ * @author u$f * */ +@FunctionalInterface public interface DBView extends DBObject { - @Deprecated - String id(); //technical use - @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNoArgs(args, DBView.class::getSimpleName); diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index 37d86844..7ce08216 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -17,7 +17,6 @@ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class QueryView implements DBQuery { - private final String id; @Getter // remove this private final RequestQueryBuilder builder; @@ -28,11 +27,6 @@ public String sql(QueryParameterBuilder param) { return parenthese(s.toString()); } - @Override - public String id() { - return id; - } - @Override public Collection columns() { return builder.getColumns(); diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 0b815385..3d97deba 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -98,8 +98,8 @@ public Optional getColumn(String id){ return columns.stream().filter(c-> c.tagname().contains(id)).findAny(); } - public QueryView asView(String tag) { - return new QueryView(tag, this); + public QueryView asView() { + return new QueryView(this); } public RequestQuery build(){ diff --git a/src/main/java/org/usf/jquery/core/TableView.java b/src/main/java/org/usf/jquery/core/TableView.java index dd5d4e66..24e7794a 100644 --- a/src/main/java/org/usf/jquery/core/TableView.java +++ b/src/main/java/org/usf/jquery/core/TableView.java @@ -16,16 +16,11 @@ @RequiredArgsConstructor public class TableView implements DBView { - private final String id; private final String schema; private final String name; public TableView(String name) { - this(name, null, name); //use tablename as id - } - - public TableView(String schema, String name) { - this(name, schema, name); //use tablename as id + this(name, null); //use tablename as id } @Override @@ -33,11 +28,6 @@ public String sql(QueryParameterBuilder builder) { return member(getSchemaOrElse(builder.getSchema()), name); } - @Override - public String id() { - return id; - } - public String getSchemaOrElse(String defaultSchema) { return nonNull(schema) ? schema : defaultSchema; //priority order } diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index 38ad6d2d..250ee1a2 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -36,10 +36,6 @@ public String sql(QueryParameterBuilder builder) { Stream.of(filters).map(f-> f.sql(builder)).collect(joining(AND.sql())); } - public String id() { - return view.id(); - } - public static ViewJoin innerJoin(DBView view, DBFilter... filters) { return join(INNER, view, filters); } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index e8dcfec1..3787009d 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -1,7 +1,6 @@ package org.usf.jquery.web; import org.usf.jquery.core.ComparisonExpression; -import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.JDBCType; /** @@ -47,18 +46,4 @@ default boolean canFilter(ViewDecorator td) { throw new UnsupportedOperationException(); //authorization inject } - static ColumnDecorator ofColumn(String ref, DBColumn column) { - final ColumnBuilder builder = b-> column; - return new ColumnDecorator() { - @Override - public String identity() { - return ref; // default column tag - } - - @Override - public ColumnBuilder builder(ViewDecorator td) { - return builder; - } - }; - } } diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index 2e608d0b..37bc7100 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -13,7 +13,7 @@ import java.sql.Timestamp; -import org.usf.jquery.core.ViewColumn; +import org.usf.jquery.core.JDBCType; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -29,52 +29,52 @@ @ToString @AllArgsConstructor(access = AccessLevel.PRIVATE) public final class ColumnMetadata { - - private ViewColumn column; + + private final String name; + private JDBCType type; private int dataSize; private int precision; private final boolean overConfigured; @Deprecated ColumnMetadata reset() { - this.dataSize = UNLIMITED; - this.precision = UNLIMITED; + if(!overConfigured) { + this.type = null; + this.dataSize = UNLIMITED; + this.precision = UNLIMITED; + } return this; } public void update(int type, int size, int precision) { if(!overConfigured) { - var ct = fromDataType(type).orElse(OTHER); - if(ct != column.getType()) { - column = new ViewColumn(column.getView(), column.getName(), column.getTag(), ct); - } + this.type = fromDataType(type).orElse(OTHER); this.dataSize = size; this.precision = precision; } } public String toJavaType(){ - return column.getType().typeClass().getSimpleName(); + return type.typeClass().getSimpleName(); } public String toSqlType(){ - var dataType = column.getType(); - var s = dataType.name(); + var s = type.name(); if(!overConfigured) { - if(dataType.typeClass() == String.class && dataSize < MAX_VALUE) { + if(type.typeClass() == String.class && dataSize < MAX_VALUE) { s+= "(" + dataSize + ")"; } - if(dataType.typeClass() == Timestamp.class) { + if(type.typeClass() == Timestamp.class) { s+= "(" + precision + ")"; } - if(dataType == REAL || dataType == NUMERIC || dataType == DECIMAL || dataType == FLOAT || dataType == DOUBLE) { + if(type == REAL || type == NUMERIC || type == DECIMAL || type == FLOAT || type == DOUBLE) { s+= "(" + dataSize + "," + precision + ")"; } } return s; } - public static ColumnMetadata columnMetadata(ViewColumn col) { - return new ColumnMetadata(col, UNLIMITED, UNLIMITED, nonNull(col.getType())); + public static ColumnMetadata columnMetadata(String name, JDBCType type) { + return new ColumnMetadata(name, type, UNLIMITED, UNLIMITED, nonNull(type)); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java b/src/main/java/org/usf/jquery/web/CompletableViewQuery.java deleted file mode 100644 index a45cc960..00000000 --- a/src/main/java/org/usf/jquery/web/CompletableViewQuery.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.usf.jquery.web; - -import org.usf.jquery.core.DBQuery; -import org.usf.jquery.core.QueryView; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; - -/** - * - * @author u$f - * - */ -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) -final class CompletableViewQuery implements DBQuery { - - @Delegate - private final QueryView query; - - @Override - public String toString() { - return query.toString(); - } -} diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index cfca33d8..dac45aed 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -9,6 +9,8 @@ import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNonEmpty; import static org.usf.jquery.web.ConflictingResourceException.resourceAlreadyExistsException; +import static org.usf.jquery.web.Constants.COLUMN; +import static org.usf.jquery.web.Constants.VIEW; import java.sql.SQLException; import java.util.Collection; @@ -47,7 +49,8 @@ public final class ContextEnvironment { private final DataSource dataSource; //optional private final String schema; //optional private final DatabaseMetadata metadata = new DatabaseMetadata(); - + //runtime scope + private final Map viewCache = new HashMap<>(); private final Map overView = new HashMap<>(); private final Map declaredColumns = new HashMap<>(); @@ -71,12 +74,16 @@ Optional lookupDeclaredColumn(String name) { return ofNullable(declaredColumns.get(name)); } + public DBView getView(ViewDecorator vd, Supplier supp) { + return ofNullable(viewCache.get(vd)).orElseGet(supp); + } + void declareView(ViewDecorator view) { views.compute(view.identity(), (k,v)-> { if(isNull(v)){ return view; } - throw resourceAlreadyExistsException("view", k); + throw resourceAlreadyExistsException(VIEW, k); }); } @@ -85,12 +92,12 @@ void declareColumn(TaggableColumn col) { if(isNull(v)){ return col; } - throw resourceAlreadyExistsException("column", k); + throw resourceAlreadyExistsException(COLUMN, k); }); } - QueryView overView(DBView v1, Supplier supp) { - return overView.computeIfAbsent(v1, k-> supp.get()); + QueryView overView(DBView view, Supplier supp) { + return overView.computeIfAbsent(view, k-> supp.get()); } ViewMetadata computeTableMetadata(ViewDecorator vd, Function, ViewMetadata> fn) { @@ -125,8 +132,8 @@ public static final ContextEnvironment of(DatabaseDecorator database, requireLegalVariable(database.identity()); return new ContextEnvironment( requireNonNull(database, "configuration.database"), - unmodifiableIdentityMap(requireNonEmpty(views, "configuration.views"), ViewDecorator::identity), - unmodifiableIdentityMap(requireNonEmpty(columns, "configuration.columns"), ColumnDecorator::identity), + unmodifiableIdentityMap(requireNonEmpty(views, database.identity() + ".views"), ViewDecorator::identity), + unmodifiableIdentityMap(requireNonEmpty(columns, database.identity() + ".columns"), ColumnDecorator::identity), ds, schema); } diff --git a/src/main/java/org/usf/jquery/web/ContextManager.java b/src/main/java/org/usf/jquery/web/ContextManager.java index 2d29b7c8..39ea9463 100644 --- a/src/main/java/org/usf/jquery/web/ContextManager.java +++ b/src/main/java/org/usf/jquery/web/ContextManager.java @@ -2,12 +2,15 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.web.ConflictingResourceException.resourceAlreadyExistsException; +import static org.usf.jquery.web.NoSuchResourceException.noSuchDatabaseException; import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; +import org.usf.jquery.core.Utils; + import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -27,7 +30,7 @@ public static void register(ContextEnvironment config) { if(isNull(dm)) { return config; } - throw new IllegalStateException("context environement conflict " + quote(id)); + throw resourceAlreadyExistsException("context", id); }); } @@ -49,16 +52,18 @@ static ContextEnvironment context(String database){ if(nonNull(ctx)) { return setCurrentContext(ctx); } - throw new NoSuchElementException(database); + throw noSuchDatabaseException(database); } static ContextEnvironment setCurrentContext(ContextEnvironment ctx) { ctx = new ContextEnvironment(ctx); //copy CURRENT.set(ctx); + Utils.currentDatabase(ctx.getMetadata().getType()); //table database return ctx; } static void releaseContext() { CURRENT.remove(); + Utils.currentDatabase(null); //table database } } diff --git a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java index 2879bbe5..4cdf69b3 100644 --- a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java +++ b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java @@ -1,6 +1,8 @@ package org.usf.jquery.web; import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.web.Constants.COLUMN; +import static org.usf.jquery.web.Constants.VIEW; /** * @@ -14,20 +16,23 @@ public NoSuchResourceException(String s) { super(s); } + static NoSuchResourceException noSuchDatabaseException(String resource) { + return noSuchResouceException(VIEW, resource); + } + static NoSuchResourceException noSuchViewException(String resource) { - return noSuchResouceException("view", resource); + return noSuchResouceException(VIEW, resource); } - static NoSuchResourceException throwNoSuchColumnException(String resource) { - return noSuchResouceException("column", resource); + static NoSuchResourceException noSuchColumnException(String resource) { + return noSuchResouceException(COLUMN, resource); } static NoSuchResourceException noSuchResouceException(String type, String resource) { return new NoSuchResourceException(quote(resource) + " " + type + " not found"); } - static NoSuchResourceException undeclaredResouceException(String view, String column) { - return new NoSuchResourceException("column " + quote(column) + " is not declared in " + quote(view) + " view"); + static NoSuchResourceException undeclaredResouceException(String child, String parent) { + return new NoSuchResourceException(quote(child) + " was not declared in " + quote(parent)); } - } diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 710aa834..835a85f0 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -1,6 +1,6 @@ package org.usf.jquery.web; -import static org.usf.jquery.web.NoSuchResourceException.throwNoSuchColumnException; +import static org.usf.jquery.web.NoSuchResourceException.noSuchColumnException; import org.usf.jquery.core.DBView; import org.usf.jquery.core.QueryView; @@ -20,6 +20,7 @@ @RequiredArgsConstructor final class QueryDecorator implements ViewDecorator { + private final String id; private final QueryView query; @Override @@ -37,7 +38,7 @@ public TaggableColumn column(String id) { .filter(c-> c.tagname().equals(id)) //tagname nullable ! .findAny() .map(c-> new ViewColumn(query, c.tagname(), c.tagname(), c.getType())) - .orElseThrow(()-> throwNoSuchColumnException(id)); + .orElseThrow(()-> noSuchColumnException(id)); } @Override diff --git a/src/main/java/org/usf/jquery/web/RequestContext.java b/src/main/java/org/usf/jquery/web/RequestContext.java deleted file mode 100644 index 22fa602e..00000000 --- a/src/main/java/org/usf/jquery/web/RequestContext.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.usf.jquery.web; - -import static java.util.Objects.isNull; -import static java.util.Optional.ofNullable; -import static org.usf.jquery.web.JQueryContext.context; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -import org.usf.jquery.core.DBQuery; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public final class RequestContext { - - private static final ThreadLocal local = new ThreadLocal<>(); - - private final Map viewDecorators; - private final Map columnDecorators; - private final Map workQueries = new LinkedHashMap<>(); //work queries - - public Optional lookupViewDecorator(String id) { - log.trace("lookup view decorator : {}", id); - return ofNullable(viewDecorators.get(id)); - } - - public Optional lookupColumnDecorator(String id) { - log.trace("lookup column decorator : {}", id); - return ofNullable(columnDecorators.get(id)); - } - - public Optional lookupView(String id) { - log.trace("lookup view : {}", id); - return ofNullable(workQueries.get(id)); - } - - public void putViewDecorator(ViewDecorator v) { - if(!viewDecorators.containsKey(v.identity())) { - viewDecorators.put(v.identity(), v); - } - else { - throw new IllegalArgumentException(v.identity() + " already exist"); - } - } - - public void putWorkQuery(DBQuery v) { - workQueries.put(v.id(), v); - } - - public Collection popQueries() { - var q = new ArrayList<>(workQueries.values()); - workQueries.clear(); - return q; - } - - public static final RequestContext currentContext_() { - var rc = local.get(); - if(isNull(rc)) { - var jc = context(); //can filter view & column - rc = new RequestContext(new HashMap<>(jc.getTables()), new HashMap<>(jc.getColumns())); - local.set(rc); - } - return rc; - } - - public static final void clearContext() { - local.remove(); - } -} diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index cc3f4370..4e010f17 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -112,7 +112,6 @@ public ViewJoin evalJoin(ViewDecorator td) { throw cannotParseEntryException(JOIN, this.toString()); //TD } - //evalView query|view:alias public ViewDecorator evalQuery(ViewDecorator td, boolean requireTag) { //sub context @@ -133,7 +132,7 @@ public ViewDecorator evalQuery(ViewDecorator td, boolean requireTag) { //sub con if(requireTag && isNull(e.tag)) { throw requireEntryException(TAG); } - return new QueryDecorator(q.asView(e.tag)); + return new QueryDecorator(e.tag, q.asView()); } throw cannotParseEntryException(SELECT, toString()); } @@ -250,7 +249,7 @@ private RequestEntryChain updateArgs(List values) { DBFilter chainComparator(ViewDecorator td, ColumnDecorator cd, DBColumn col){ var f = lookupComparator(value).map(c-> c.args(toArgs(td, col, c.getParameterSet()))).orElse(null); //eval comparator first => avoid overriding - if(isNull(f) && col instanceof TaggableColumn) { //no operation + if(isNull(f) && nonNull(col)) { //no operation var c = cd.criteria(value); //criteria lookup if(nonNull(c)) { f = col.filter(c.build(toStringArray(args))); @@ -270,7 +269,7 @@ DBFilter chainComparator(ViewDecorator td, DBFilter f){ f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); } else { - throw unexpectedEntryException(e.toString(), "and|or"); + throw cannotParseEntryException("LogicalOperator", e.toString()); } e = e.next; } @@ -298,7 +297,7 @@ private static DBColumn windowColumn(ViewDecorator vd, TaggableColumn col) { var v = vd.view(); currentContext().overView(v, ()-> new RequestQueryBuilder() .columns(allColumns(v)) - .asView(vd.identity())).getBuilder().columns(col); //append over column + .asView()).getBuilder().columns(col); //append over column return new ViewColumn(v, doubleQuote(col.tagname()), null, col.getType()); } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 692f86ae..fe5d1bb9 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -3,7 +3,7 @@ import static java.lang.Integer.parseInt; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; -import static java.util.Objects.requireNonNullElseGet; +import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toUnmodifiableMap; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.isEmpty; @@ -17,10 +17,7 @@ import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.MissingParameterException.missingParameterException; -import static org.usf.jquery.web.NoSuchResourceException.noSuchViewException; import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; -import static org.usf.jquery.web.RequestContext.clearContext; -import static org.usf.jquery.web.RequestContext.currentContext_; import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.RequestParser.parseEntry; @@ -30,12 +27,10 @@ import java.util.stream.Stream; import org.usf.jquery.core.DBFilter; -import org.usf.jquery.core.DBQuery; import org.usf.jquery.core.DBView; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TableView; import org.usf.jquery.core.TaggableColumn; -import org.usf.jquery.core.Utils; import org.usf.jquery.core.ViewColumn; import lombok.NonNull; @@ -56,44 +51,27 @@ default ViewBuilder builder() { } default CriteriaBuilder criteria(String name) { //!aggregation - return null; // no criteria by default + return null; //no criteria by default } - default JoinBuilder joiner() { - return null; // no builder by default + default JoinBuilder joiner(String name) { + return null; //no builder by default } - default DBView view() { - return requireNonNullElseGet(metadata().getView(), builder()::build); //nullable !? + default DBView view() { //final + return currentContext().getView(this, requireNonNull(builder(), identity() + ".builder")::build); //nullable !? } default TaggableColumn column(@NonNull ColumnDecorator cd) { - var c = metadata().columnMetada(cd); - if(nonNull(c)) { - return c.getColumn(); - } - var cn = columnName(cd); - if(nonNull(cn)) { //ViewColumn only - return buildViewColumn(cd, view(), cn); + var meta = metadata().columnMetadata(cd); + if(nonNull(meta)) { + return new ViewColumn(view(), meta.getName(), cd.reference(this), meta.getType()); } var b = cd.builder(this); if(nonNull(b)) { return b.build(this).as(cd.reference(this)); //set type } - throw undeclaredResouceException(identity(), cd.identity()); - } - - default ViewMetadata metadata() { - return currentContext().computeTableMetadata(this, cols-> { - var view = requireNonNull(builder(), identity() + ".builder cannot be null").build(); - var meta = cols.stream().mapMulti((cd, acc)-> { - var cn = columnName(cd); //ViewColumn only - if(nonNull(cn)) { - acc.accept(columnMetadata(buildViewColumn(cd, view, cn))); - } //tag = reference - }).collect(toUnmodifiableMap(cm-> cm.getColumn().getTag(), Function.identity())); //tag = identity - return new ViewMetadata(view, meta); - }); + throw undeclaredResouceException(cd.identity(), identity()); } private DBView buildView() { @@ -105,31 +83,25 @@ private DBView buildView() { : new TableView(requireLegalVariable(tn.substring(0, idx)), requireLegalVariable(tn.substring(idx, tn.length()))); } - throw noSuchViewException(identity()); + throw undeclaredResouceException(identity(), currentContext().getDatabase().identity()); } - - private ViewColumn buildViewColumn(ColumnDecorator cd, DBView view, String name) { - return new ViewColumn(view, - requireLegalVariable(name), - requireLegalVariable(cd.reference(this)), - cd.type(this)); + + default ViewMetadata metadata() { + return currentContext().computeTableMetadata(this, cols-> new ViewMetadata(view(), + cols.stream().mapMulti((cd, acc)-> ofNullable(columnName(cd)) + .map(cn-> columnMetadata(cn, cd.type(this))) + .ifPresent(acc)) //view column only + .collect(toUnmodifiableMap(ColumnMetadata::getName, Function.identity())))); } default RequestQueryBuilder query(Map parameterMap) { - try { - Utils.currentDatabase(currentContext().getMetadata().getType()); //table database - var query = new RequestQueryBuilder(); - parseViews(query, parameterMap); - parseColumns(query, parameterMap); - parseOrders (query, parameterMap); - parseFetch(query, parameterMap); - parseFilters(query, parameterMap); - query.views(currentContext_().popQueries().toArray(DBQuery[]::new)); - return query; - } - finally { - clearContext(); - } + var query = new RequestQueryBuilder(); + parseViews(query, parameterMap); + parseColumns(query, parameterMap); + parseOrders(query, parameterMap); + parseFetch(query, parameterMap); + parseFilters(query, parameterMap); + return query; } default void parseViews(RequestQueryBuilder query, Map parameters) { diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index be0f69fa..868cef13 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -47,7 +47,7 @@ public class ViewMetadata { @Getter private Instant lastUpdate; - public ColumnMetadata columnMetada(ColumnDecorator cd) { + public ColumnMetadata columnMetadata(ColumnDecorator cd) { return columns.get(cd.identity()); } @@ -74,7 +74,7 @@ else if(view instanceof DBQuery query) { void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { try(var rs = metadata.getColumns(null, view.getSchemaOrElse(schema), view.getName(), null)){ if(rs.next()) { - var db = columns.values().stream().collect(toMap(m-> m.getColumn().getName(), identity())); + var db = columns.values().stream().collect(toMap(m-> m.getName(), identity())); do { var cm = db.remove(rs.getString("COLUMN_NAME")); if(nonNull(cm)) { @@ -122,7 +122,7 @@ void printViewColumnMap() { log.info(bar); columns.entrySet().forEach(e-> log.info(format(pattern, e.getKey(), e.getValue().toJavaType(), - e.getValue().getColumn(), e.getValue().toSqlType()))); + e.getValue().getName(), e.getValue().toSqlType()))); log.info(bar); } } From 39255b7a38b9d4d173e249640f602df9b9c8183d Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 3 Aug 2024 00:45:21 +0200 Subject: [PATCH 103/298] edit --- .../usf/jquery/core/AggregateFunction.java | 2 +- .../usf/jquery/web/ContextEnvironment.java | 4 +- .../usf/jquery/web/EntryParseException.java | 12 +- .../usf/jquery/web/IterableViewDecorator.java | 2 - .../java/org/usf/jquery/web/JoinBuilder.java | 2 +- .../org/usf/jquery/web/QueryDecorator.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 193 ++++++++++++------ .../org/usf/jquery/web/ViewDecorator.java | 8 +- .../org/usf/jquery/web/YearTableMetadata.java | 10 +- 9 files changed, 145 insertions(+), 90 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/AggregateFunction.java b/src/main/java/org/usf/jquery/core/AggregateFunction.java index ff447c4d..1a28bc35 100644 --- a/src/main/java/org/usf/jquery/core/AggregateFunction.java +++ b/src/main/java/org/usf/jquery/core/AggregateFunction.java @@ -6,6 +6,6 @@ * */ @FunctionalInterface -public interface AggregateFunction extends WindowFunction { +public interface AggregateFunction extends FunctionOperator { } diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index dac45aed..3297e02f 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -87,8 +87,8 @@ void declareView(ViewDecorator view) { }); } - void declareColumn(TaggableColumn col) { - declaredColumns.compute(col.tagname(), (k,v)-> { + TaggableColumn declareColumn(TaggableColumn col) { + return declaredColumns.compute(col.tagname(), (k,v)-> { if(isNull(v)){ return col; } diff --git a/src/main/java/org/usf/jquery/web/EntryParseException.java b/src/main/java/org/usf/jquery/web/EntryParseException.java index c906f2d6..ce441fd8 100644 --- a/src/main/java/org/usf/jquery/web/EntryParseException.java +++ b/src/main/java/org/usf/jquery/web/EntryParseException.java @@ -18,16 +18,16 @@ public EntryParseException(String message, Throwable cause) { super(message, cause); } - static EntryParseException cannotParseEntryException(String type, String value) { - return cannotParseEntryException(type, value, null); + static EntryParseException cannotParseEntryException(String type, RequestEntryChain entry) { + return cannotParseEntryException(type, entry, null); } - static EntryParseException cannotParseEntryException(String type, String value, Throwable cause) { - return new EntryParseException(format("cannot parse %s : '%s'", type, value), cause); + static EntryParseException cannotParseEntryException(String type, RequestEntryChain entry, Throwable cause) { + return new EntryParseException(format("cannot parse %s : '%s'", type, entry.toString()), cause); } - static EntryParseException unexpectedEntryException(String entry) { - return new EntryParseException(format("unexpected entry : '%s'", entry)); + static EntryParseException unexpectedEntryException(RequestEntryChain entry) { + return new EntryParseException(format("unexpected entry : '%s'", entry.toString())); } static EntryParseException requireEntryException(String name) { diff --git a/src/main/java/org/usf/jquery/web/IterableViewDecorator.java b/src/main/java/org/usf/jquery/web/IterableViewDecorator.java index 0c1f7960..0d24c8ac 100644 --- a/src/main/java/org/usf/jquery/web/IterableViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/IterableViewDecorator.java @@ -1,7 +1,5 @@ package org.usf.jquery.web; -import org.usf.jquery.core.DBView; - /** * * @author u$f diff --git a/src/main/java/org/usf/jquery/web/JoinBuilder.java b/src/main/java/org/usf/jquery/web/JoinBuilder.java index d85edae7..c7b85351 100644 --- a/src/main/java/org/usf/jquery/web/JoinBuilder.java +++ b/src/main/java/org/usf/jquery/web/JoinBuilder.java @@ -9,6 +9,6 @@ */ public interface JoinBuilder { - ViewJoin[] build(ViewDecorator[] selectedViews); + ViewJoin[] build(); } diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 835a85f0..3a24cff2 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -25,7 +25,7 @@ final class QueryDecorator implements ViewDecorator { @Override public String identity() { - return query.id(); + return id; } @Override diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 4e010f17..ae8a2f7a 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -1,13 +1,18 @@ package org.usf.jquery.web; +import static java.lang.String.format; import static java.lang.reflect.Array.newInstance; +import static java.util.Collections.emptyList; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; +import static java.util.Objects.requireNonNull; import static java.util.Optional.empty; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.BadArgumentException.badArgumentsException; +import static org.usf.jquery.core.Comparator.eq; +import static org.usf.jquery.core.Comparator.in; import static org.usf.jquery.core.Comparator.lookupComparator; import static org.usf.jquery.core.DBColumn.allColumns; import static org.usf.jquery.core.JDBCType.INTEGER; @@ -19,7 +24,6 @@ import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.ViewJoin.join; import static org.usf.jquery.web.ArgumentParsers.parse; -import static org.usf.jquery.web.ColumnDecorator.ofColumn; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.DISTINCT; import static org.usf.jquery.web.Constants.FETCH; @@ -39,12 +43,17 @@ import static org.usf.jquery.web.RequestContext.currentContext_; import java.text.ParseException; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.function.IntFunction; import java.util.function.Predicate; +import org.usf.jquery.core.AggregateFunction; import org.usf.jquery.core.BadArgumentException; +import org.usf.jquery.core.Comparator; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; @@ -109,7 +118,7 @@ public ViewJoin evalJoin(ViewDecorator td) { var args = toArgs( td, null, null); return join(jt, (DBView)args[0], (DBFilter[])args[0]); } - throw cannotParseEntryException(JOIN, this.toString()); //TD + throw cannotParseEntryException(JOIN, this); //TD } //evalView query|view:alias @@ -126,7 +135,7 @@ public ViewDecorator evalQuery(ViewDecorator td, boolean requireTag) { //sub con case ORDER: q.orders(e.toOderArgs(td)); break; //not sure case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; - default: throw unexpectedEntryException(e.toString()); + default: throw unexpectedEntryException(e); } } if(requireTag && isNull(e.tag)) { @@ -134,7 +143,7 @@ public ViewDecorator evalQuery(ViewDecorator td, boolean requireTag) { //sub con } return new QueryDecorator(e.tag, q.asView()); } - throw cannotParseEntryException(SELECT, toString()); + throw cannotParseEntryException(SELECT, this); } public Partition evalPartition(ViewDecorator td) { @@ -146,73 +155,109 @@ public Partition evalPartition(ViewDecorator td) { p.orders(e.toOderArgs(td)); //not sure } else { - throw unexpectedEntryException(e.toString()); + throw unexpectedEntryException(e); } }//require no tag return p; } - throw cannotParseEntryException(PARTITION, this.toString()); + throw cannotParseEntryException(PARTITION, this); } public TaggableColumn evalColumn(ViewDecorator td) { - var r = chainColumnOperations(td, false); //throws ParseException - if(r.entry.isLast()) { - if(nonNull(r.entry.tag)) { //TD: required tag if operation - return r.col.as(r.entry.tag); - } - if(r.col instanceof TaggableColumn col) { - return col; + try { + var r = chainColumnOperations(td, false).orElseThrow(); //throws ParseException + if(r.entry.isLast()) { + if(nonNull(r.entry.tag)) { //TD: required tag if operation + return r.col.as(r.entry.tag); + } + if(r.col instanceof TaggableColumn col) { + return col; + } + throw requireEntryException(TAG); } - throw requireEntryException(TAG); + throw unexpectedEntryException(r.entry.next); + } catch (Exception e) { + throw cannotParseEntryException(COLUMN, this, e); } - throw unexpectedEntryException(r.entry.next.toString()); } public DBOrder evalOrder(ViewDecorator td) { - var r = chainColumnOperations(td, false); //throws ParseException - if(r.entry.isLast()) { // no order - return r.col.order(); - } - var e = r.entry.next; - if(e.isLast()) { // next must be last - if(e.value.matches("asc|desc")) { - var o = Order.valueOf(e.requireNoArgs(ORDER).value.toUpperCase()); // order takes no args - return r.col.order(o); + try { + var r = chainColumnOperations(td, false).orElseThrow(); //throws ParseException + if(r.entry.isLast()) { // no order + return r.col.order(); } - cannotParseEntryException(ORDER, e.toString()); + var e = r.entry.next; + if(r.entry.next.isLast()) { // next must be last + if(e.value.matches("asc|desc")) { + var o = Order.valueOf(e.requireNoArgs(ORDER).value.toUpperCase()); // order takes no args + return r.col.order(o); + } + cannotParseEntryException(ORDER, e); + } + throw unexpectedEntryException(e); + } catch (Exception e) { + throw cannotParseEntryException(ORDER, this, e); } - throw unexpectedEntryException(e.toString()); } public DBFilter evalFilter(ViewDecorator td) { - return evalFilter(td, null); + return evalFilter(td, emptyList()); } public DBFilter evalFilter(ViewDecorator td, List values) { try { - var r = chainColumnOperations(td, true); - var e = r.entry; - if(e.isLast()) { // no comparator, no criteria - String cmp; - if(isEmpty(values)) { - cmp = "isNull"; + var res = chainColumnOperations(td, true); + DBFilter f; + if(res.isEmpty()) { //not a column + f = tableCriteria(td, values); + } + else { + var rc = res.get(); + if(rc.entry.isLast()) { + var fn = values.size() == 1 ? eq() : in(); + f = fn.args(new RequestEntryChain(null, false, null, values, null) + .toArgs(rc.td, rc.col, fn.getParameterSet())); } else { - cmp = values.size() > 1 ? "in" : "eq"; //query ?? + f = rc.entry.next.columnCriteria(rc.td, rc.cd, rc.col, values); } - e = e.copy(); - e.setNext(new RequestEntryChain(cmp)); // do not set args; } - return e.next.updateArgs(values) - .chainComparator(td, r.cd, r.col); + return chainComparator(td, f); + } + catch (Exception e) { + throw cannotParseEntryException(FILTER, this, e); + } + } + + DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List values) { + if(!values.isEmpty()) { + if(isNull(args) && isLast()) { + args = values; + } + else { + //throw + } } - catch(EntryParseException e) { - return tableCriteria(td, values) - .orElseThrow(()-> cannotParseEntryException(FILTER, this.toString(), e)); + var cmp = lookupComparator(value); + if(cmp.isPresent()) { + var fn = cmp.get(); + return fn.args(toArgs(vc, col, fn.getParameterSet())); + } + if(nonNull(cd)) { + var c = cd.criteria(value); //criteria lookup + if(nonNull(c)) { + var strArgs = toStringArray(args); + var ce = requireNonNull(c.build(strArgs), + ()-> format("%s.builder(%s).build(%s)", cd.identity(), value, Arrays.toString(strArgs))); + return col.filter(ce); + } + throw cannotParseEntryException("comparison|criteria", this); } + throw cannotParseEntryException("comparison", this); } - Optional tableCriteria(ViewDecorator td, List values) { + DBFilter tableCriteria(ViewDecorator td, List values) { RequestEntryChain e = null; CriteriaBuilder c = null; var res = currentContext_().lookupViewDecorator(value); @@ -247,21 +292,24 @@ private RequestEntryChain updateArgs(List values) { return e; } - DBFilter chainComparator(ViewDecorator td, ColumnDecorator cd, DBColumn col){ + DBFilter columnCriteria(ViewDecorator td, ColumnDecorator cd, DBColumn col){ var f = lookupComparator(value).map(c-> c.args(toArgs(td, col, c.getParameterSet()))).orElse(null); //eval comparator first => avoid overriding - if(isNull(f) && nonNull(col)) { //no operation + if(isNull(f) && nonNull(cd)) { //no operation var c = cd.criteria(value); //criteria lookup if(nonNull(c)) { - f = col.filter(c.build(toStringArray(args))); + var strArgs = toStringArray(args); + var ce = requireNonNull(c.build(strArgs), + ()-> format("%s.builder(%s).build(%s)", cd.identity(), value, Arrays.toString(strArgs))); + f = col.filter(ce); } } if(nonNull(f)) { return chainComparator(td, f); } - throw cannotParseEntryException("comparison|criteria", this.toString()); + throw cannotParseEntryException("comparison|criteria", this); } - DBFilter chainComparator(ViewDecorator td, DBFilter f){ + DBFilter chainComparator(ViewDecorator td, DBFilter f) { var e = next; while(nonNull(e)) { if(e.value.matches("and|or")) { @@ -269,35 +317,37 @@ DBFilter chainComparator(ViewDecorator td, DBFilter f){ f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); } else { - throw cannotParseEntryException("LogicalOperator", e.toString()); + throw cannotParseEntryException("LogicalOperator", e); } e = e.next; } return f; } - private ResourceCursor chainColumnOperations(ViewDecorator td, boolean filter) { - var r = lookupResource(td).orElseThrow(()-> cannotParseEntryException(COLUMN, this.toString())); - var e = r.entry.next; - while(nonNull(e)) { // chain until !operator - var o = e.lookupOperation(td, r.col, fn-> true); - if(o.isEmpty()) { - break; - } - r.entry = e; - r.col = filter && "over".equals(e.value) - ? windowColumn(r.td, o.get().as(r.cd.identity())) //require tag | random tag - : o.get(); - e = e.next; - } - return r; + private Optional chainColumnOperations(ViewDecorator td, boolean filter) { + return lookupResource(td).map(r-> { + var e = r.entry.next; + while(nonNull(e)) { // chain until !operator + var o = e.lookupOperation(td, r.col, fn-> true); //accept all operation + if(o.isEmpty()) { + break; + } + r.cd = null; + r.entry = e; + r.col = filter && "over".equals(e.value) + ? windowColumn(r.td, o.get().as(r.cd.identity())) //require tag | random tag + : o.get(); + e = e.next; + } + return r; + }); } private static DBColumn windowColumn(ViewDecorator vd, TaggableColumn col) { var v = vd.view(); currentContext().overView(v, ()-> new RequestQueryBuilder() - .columns(allColumns(v)) - .asView()).getBuilder().columns(col); //append over column + .columns(allColumns(v)).asView()) + .getBuilder().columns(col); //append over column return new ViewColumn(v, doubleQuote(col.tagname()), null, col.getType()); } @@ -317,7 +367,8 @@ private Optional lookupResource(ViewDecorator td) { //do not cha private Optional lookupViewResource(ViewDecorator td, Predicate pre) { if(td instanceof QueryDecorator qd) { //query column - var res = ofNullable(qd.column(value)).map(c-> new ResourceCursor(td, null, requireNoArgs(COLUMN), c)); + var res = ofNullable(qd.column(value)) + .map(c-> new ResourceCursor(td, null, requireNoArgs(COLUMN), c)); if(res.isPresent()) { return res; } @@ -330,7 +381,7 @@ private Optional lookupViewResource(ViewDecorator td, Predicate< private Optional lookupOperation(ViewDecorator td, DBColumn col, Predicate opr) { return lookupOperator(value).filter(opr).map(fn-> { var c = col; - if(isNull(c) && isEmpty(args) && "COUNT".equals(fn.id())) { + if(isNull(c) && isEmpty(args) && isCountFunction(fn)) { c = b-> { b.view(td.view()); // declare view return "*"; @@ -442,7 +493,13 @@ protected RequestEntryChain copy() { } private static boolean isWindowFunction(TypedOperator op) { - return op.unwrap() instanceof WindowFunction; // WindowFunction + COUNT + var fn = op.unwrap(); + return fn instanceof WindowFunction || + fn instanceof AggregateFunction; + } + + private static boolean isCountFunction(TypedOperator fn) { + return "COUNT".equals(fn.id()); } private static String[] toStringArray(List entries) { diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index fe5d1bb9..cd190682 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -2,7 +2,6 @@ import static java.lang.Integer.parseInt; import static java.util.Objects.nonNull; -import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toUnmodifiableMap; import static org.usf.jquery.core.SqlStringBuilder.quote; @@ -47,7 +46,7 @@ public interface ViewDecorator { String columnName(ColumnDecorator cd); default ViewBuilder builder() { - return this::buildView; //avoid recursive call + return this::buildView; } default CriteriaBuilder criteria(String name) { //!aggregation @@ -59,7 +58,7 @@ default JoinBuilder joiner(String name) { } default DBView view() { //final - return currentContext().getView(this, requireNonNull(builder(), identity() + ".builder")::build); //nullable !? + return currentContext().getView(this, builder()::build); } default TaggableColumn column(@NonNull ColumnDecorator cd) { @@ -127,7 +126,8 @@ default void parseColumns(RequestQueryBuilder query, Map param } Stream.of(cols) .flatMap(v-> parseEntries(v).stream()) - .forEach(e-> query.columns(e.evalColumn(this))); + .map(e-> e.evalColumn(this)) + .forEach(c-> query.columns(currentContext().declareColumn(c))); } default void parseFilters(RequestQueryBuilder query, Map parameters) { diff --git a/src/main/java/org/usf/jquery/web/YearTableMetadata.java b/src/main/java/org/usf/jquery/web/YearTableMetadata.java index 51355081..bf5bba56 100644 --- a/src/main/java/org/usf/jquery/web/YearTableMetadata.java +++ b/src/main/java/org/usf/jquery/web/YearTableMetadata.java @@ -70,7 +70,7 @@ public Optional latestRevision() { @Override void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { tablenames.clear(); - var dbMap = getColumns().values().stream().collect(toMap(cm-> cm.getColumn().getName(), ColumnMetadata::reset)); //important! reset columns + var dbMap = getColumns().values().stream().collect(toMap(cm-> cm.getName(), ColumnMetadata::reset)); //important! reset columns Set dirtyColumns = new LinkedHashSet<>(); Map> columnTables = new LinkedHashMap<>(); try(var rs = metadata.getColumns(null, view.getSchemaOrElse(schema), view.getName() + "_20__", null)){ @@ -82,15 +82,15 @@ void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLE var cm = dbMap.get(rs.getString("COLUMN_NAME")); if(nonNull(cm) && tn.matches(view.getName() + "_20\\d{2}")) {// untyped SQL pattern tablenames.add(tn); - columnTables.computeIfAbsent(cm.getColumn().getName(), k-> new LinkedHashSet<>()).add(tn); + columnTables.computeIfAbsent(cm.getName(), k-> new LinkedHashSet<>()).add(tn); var type = rs.getInt("DATA_TYPE"); var size = rs.getInt("COLUMN_SIZE"); - if(isNull(cm.getColumn().getType())) { //first time + if(isNull(cm.getType())) { //first time cm.setDataType(typeOf(type).orElse(OTHER)); cm.setDataSize(size); } - else if(cm.getDataType().getValue() != type || cm.getDataSize() != size) { - dirtyColumns.add(cm.getColumnName()); + else if(cm.getType().getValue() != type || cm.getDataSize() != size) { + dirtyColumns.add(cm.getName()); } }//else undeclared column } while(rs.next()); From 0ce7611d1d37ecfbdc55a66d6bb789cda179bea6 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 3 Aug 2024 00:51:51 +0200 Subject: [PATCH 104/298] edit --- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index ae8a2f7a..b33e4b0b 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -236,7 +236,7 @@ DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List args = values; } else { - //throw + throw new IllegalStateException(toString() + "=" + values); } } var cmp = lookupComparator(value); From 26437179e34753bd0bf2b33aa2d596ce0deba31e Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 3 Aug 2024 01:45:16 +0200 Subject: [PATCH 105/298] edit --- .../org/usf/jquery/web/RequestEntryChain.java | 69 +++++-------------- 1 file changed, 18 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index b33e4b0b..e61d1e96 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -40,7 +40,6 @@ import static org.usf.jquery.web.EntryParseException.entryTackesNoArgException; import static org.usf.jquery.web.EntryParseException.requireEntryException; import static org.usf.jquery.web.EntryParseException.unexpectedEntryException; -import static org.usf.jquery.web.RequestContext.currentContext_; import java.text.ParseException; import java.util.Arrays; @@ -143,6 +142,7 @@ public ViewDecorator evalQuery(ViewDecorator td, boolean requireTag) { //sub con } return new QueryDecorator(e.tag, q.asView()); } + //TODO parse view::alias throw cannotParseEntryException(SELECT, this); } @@ -225,7 +225,7 @@ public DBFilter evalFilter(ViewDecorator td, List values) { } return chainComparator(td, f); } - catch (Exception e) { + catch(Exception e) { throw cannotParseEntryException(FILTER, this, e); } } @@ -236,7 +236,7 @@ DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List args = values; } else { - throw new IllegalStateException(toString() + "=" + values); + throw new IllegalStateException(toString() + "=" + values); // args + parameters } } var cmp = lookupComparator(value); @@ -258,59 +258,26 @@ DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List } DBFilter tableCriteria(ViewDecorator td, List values) { - RequestEntryChain e = null; - CriteriaBuilder c = null; - var res = currentContext_().lookupViewDecorator(value); - if(res.isPresent() && hasNext()) { - c = res.get().criteria(next.value); - e = next; // only if nonNull - } - if(isNull(c)) { - c = td.criteria(value); - e = this; - } - if(nonNull(c)) { - e.updateArgs(values); - var f = e.chainComparator(td, c.build(toStringArray(args))); - return Optional.of(f); - } - return empty(); - } - - //column.eq=v1 - private RequestEntryChain updateArgs(List values) { - var e = this; - if(!isEmpty(values)) { - if(isLast() && isNull(args)) { - e = copy(); - e.setArgs(values); - } - else { - throw new UnexpectedEntryException(this + "=" + Utils.toString(values.toArray())); + RequestEntryChain e; + CriteriaBuilder cb = null; + if(hasNext()) { + var res = currentContext().lookupRegistredView(value).map(v-> v.criteria(next.value)); + if(res.isPresent()){ + cb = res.get(); + e = next; } } - return e; - } - - DBFilter columnCriteria(ViewDecorator td, ColumnDecorator cd, DBColumn col){ - var f = lookupComparator(value).map(c-> c.args(toArgs(td, col, c.getParameterSet()))).orElse(null); //eval comparator first => avoid overriding - if(isNull(f) && nonNull(cd)) { //no operation - var c = cd.criteria(value); //criteria lookup - if(nonNull(c)) { - var strArgs = toStringArray(args); - var ce = requireNonNull(c.build(strArgs), - ()-> format("%s.builder(%s).build(%s)", cd.identity(), value, Arrays.toString(strArgs))); - f = col.filter(ce); - } - } - if(nonNull(f)) { - return chainComparator(td, f); + if(isNull(cb)) { + cb = td.criteria(value); + e = this; } - throw cannotParseEntryException("comparison|criteria", this); + var strArgs = toStringArray(values); + return requireNonNull(cb.build(strArgs), + ()-> format("%s.builder(%s).build(%s)", td.identity(), value, Arrays.toString(strArgs))); } DBFilter chainComparator(ViewDecorator td, DBFilter f) { - var e = next; + var e = next; //TODO => this while(nonNull(e)) { if(e.value.matches("and|or")) { var op = LogicalOperator.valueOf(e.value.toUpperCase()); @@ -335,7 +302,7 @@ private Optional chainColumnOperations(ViewDecorator td, boolean r.cd = null; r.entry = e; r.col = filter && "over".equals(e.value) - ? windowColumn(r.td, o.get().as(r.cd.identity())) //require tag | random tag + ? windowColumn(r.td, o.get().as(r.cd.identity())) //TODO require tag | random tag : o.get(); e = e.next; } From 8c5ff1261725cfc52a2173aa8486c8d3577dade2 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 3 Aug 2024 02:00:16 +0200 Subject: [PATCH 106/298] edit --- .../usf/jquery/web/IterableViewDecorator.java | 14 ++++++ .../org/usf/jquery/web/ModelIterator.java | 44 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/main/java/org/usf/jquery/web/ModelIterator.java diff --git a/src/main/java/org/usf/jquery/web/IterableViewDecorator.java b/src/main/java/org/usf/jquery/web/IterableViewDecorator.java index 0d24c8ac..11154cb4 100644 --- a/src/main/java/org/usf/jquery/web/IterableViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/IterableViewDecorator.java @@ -1,5 +1,11 @@ package org.usf.jquery.web; +import static org.usf.jquery.web.ModelIterator.iterator; + +import java.util.Map; + +import org.usf.jquery.core.RequestQueryBuilder; + /** * * @author u$f @@ -9,4 +15,12 @@ public interface IterableViewDecorator extends ViewDecorator { void tableName(String name, T ctx); + T[] parseIterable(Map parameterMap); + + @Override + default void parseFilters(RequestQueryBuilder query, Map parameters) { + query.repeat(iterator(parseIterable(parameters))); + ViewDecorator.super.parseFilters(query, parameters); + } + } diff --git a/src/main/java/org/usf/jquery/web/ModelIterator.java b/src/main/java/org/usf/jquery/web/ModelIterator.java new file mode 100644 index 00000000..7b26971a --- /dev/null +++ b/src/main/java/org/usf/jquery/web/ModelIterator.java @@ -0,0 +1,44 @@ +package org.usf.jquery.web; + +import static org.usf.jquery.core.Utils.isPresent; + +import java.util.Iterator; +import java.util.stream.Stream; + +import lombok.RequiredArgsConstructor; + +/** + * + * @author u$f + * + */ +@RequiredArgsConstructor +public final class ModelIterator implements Iterator { + + private static final ThreadLocal currentRev = new ThreadLocal<>(); + + private final Iterator it; + + @Override + public boolean hasNext() { + if(it.hasNext()) { + return true; + } + currentRev.remove(); + return false; + } + + @Override + public T next() { + var rev = it.next(); + currentRev.set(rev); + return rev; + } + + public static ModelIterator iterator(T[] model){ + if(isPresent(model)) { + return new ModelIterator<>(Stream.of(model).iterator()); + } + throw new IllegalArgumentException("no revision"); + } +} From 1a1f8e4fb4845c0bf85b494e807aad45787c14d1 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 5 Aug 2024 23:24:01 +0200 Subject: [PATCH 107/298] edit --- .../java/org/usf/jquery/web/Constants.java | 2 + .../usf/jquery/web/ContextEnvironment.java | 35 +++-- .../org/usf/jquery/web/ContextManager.java | 2 +- .../org/usf/jquery/web/CriteriaBuilder.java | 3 +- .../usf/jquery/web/EntryParseException.java | 10 +- .../usf/jquery/web/IterableViewDecorator.java | 1 - .../org/usf/jquery/web/ModelIterator.java | 6 +- .../jquery/web/NoSuchResourceException.java | 6 +- .../org/usf/jquery/web/QueryDecorator.java | 8 +- .../org/usf/jquery/web/RequestEntryChain.java | 134 ++++++++---------- .../org/usf/jquery/web/YearTableMetadata.java | 1 - 11 files changed, 104 insertions(+), 104 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index 1bc8f2ce..f157d31c 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -26,7 +26,9 @@ public final class Constants { public static final String JOIN = "join"; public static final String TAG = "tag"; public static final String PARTITION = "partition"; + @Deprecated public static final String REVISION = "revision"; //not standard + @Deprecated public static final String REVISION_MODE = "revision.mode"; //not standard static final Set RESERVED_WORDS = diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index 3297e02f..948075b3 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -78,7 +78,7 @@ public DBView getView(ViewDecorator vd, Supplier supp) { return ofNullable(viewCache.get(vd)).orElseGet(supp); } - void declareView(ViewDecorator view) { + void declareView(ViewDecorator view) { //additional request views views.compute(view.identity(), (k,v)-> { if(isNull(v)){ return view; @@ -101,20 +101,26 @@ QueryView overView(DBView view, Supplier supp) { } ViewMetadata computeTableMetadata(ViewDecorator vd, Function, ViewMetadata> fn) { - var meta = metadata.getTables().computeIfAbsent(vd.identity(), key-> fn.apply(columns.values())); + return metadata.getTables().computeIfAbsent(vd.identity(), key-> fn.apply(columns.values())); + } + + ContextEnvironment bind() { if(nonNull(dataSource)) { //outer fetch - synchronized(meta) { - if(isNull(meta.getLastUpdate())) { - try(var cnx = dataSource.getConnection()) { - meta.fetch(cnx.getMetaData(), schema); - } - catch(SQLException | JQueryException e) { - log.error("error while scanning database metadata", e); + for(var v : views.values()) { + var meta = requireNonNull(v.metadata(), v.identity() + ".metadata"); + synchronized(meta) { + if(isNull(meta.getLastUpdate())) { + try(var cnx = dataSource.getConnection()) { + meta.fetch(cnx.getMetaData(), schema); + } + catch(SQLException | JQueryException e) { + log.error("error while scanning database metadata", e); + } } } } } - return meta; + return this; } public static final ContextEnvironment of(DatabaseDecorator database, @@ -132,12 +138,13 @@ public static final ContextEnvironment of(DatabaseDecorator database, requireLegalVariable(database.identity()); return new ContextEnvironment( requireNonNull(database, "configuration.database"), - unmodifiableIdentityMap(requireNonEmpty(views, database.identity() + ".views"), ViewDecorator::identity), - unmodifiableIdentityMap(requireNonEmpty(columns, database.identity() + ".columns"), ColumnDecorator::identity), + unmodifiableIdentityMap(views, ViewDecorator::identity, database.identity() + ".views"), + unmodifiableIdentityMap(columns, ColumnDecorator::identity, database.identity() + ".columns"), ds, schema); } - static Map unmodifiableIdentityMap(Collection c, Function fn){ - return c.stream().collect(toUnmodifiableMap(fn.andThen(Validation::requireLegalVariable), identity())); + static Map unmodifiableIdentityMap(Collection c, Function fn, String msg){ + return requireNonEmpty(c, msg).stream() + .collect(toUnmodifiableMap(fn.andThen(Validation::requireLegalVariable), identity())); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/ContextManager.java b/src/main/java/org/usf/jquery/web/ContextManager.java index 39ea9463..c50d7c83 100644 --- a/src/main/java/org/usf/jquery/web/ContextManager.java +++ b/src/main/java/org/usf/jquery/web/ContextManager.java @@ -28,7 +28,7 @@ public final class ContextManager { public static void register(ContextEnvironment config) { CONTEXTS.compute(config.getDatabase().identity(), (id,dm)-> { if(isNull(dm)) { - return config; + return config.bind(); } throw resourceAlreadyExistsException("context", id); }); diff --git a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java index 0966f752..a94cb06b 100644 --- a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java +++ b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java @@ -3,6 +3,7 @@ import static java.util.Optional.ofNullable; import static org.usf.jquery.core.LogicalOperator.OR; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; +import static org.usf.jquery.web.EvalException.cannotEvaluateException; import java.util.stream.Stream; @@ -22,7 +23,7 @@ public interface CriteriaBuilder> { default T build(String... args) { return Stream.of(requireAtLeastNArgs(1, args, CriteriaBuilder.class::getSimpleName)) .map(v-> ofNullable(criteria(v)) - .orElseThrow(()-> EvalException.cannotEvaluateException("criteria value", v))) + .orElseThrow(()-> cannotEvaluateException("criteria value", v))) .reduce((e1, e2)-> e1.append(combiner(), e2)) .orElseThrow(); } diff --git a/src/main/java/org/usf/jquery/web/EntryParseException.java b/src/main/java/org/usf/jquery/web/EntryParseException.java index ce441fd8..478a242c 100644 --- a/src/main/java/org/usf/jquery/web/EntryParseException.java +++ b/src/main/java/org/usf/jquery/web/EntryParseException.java @@ -23,20 +23,18 @@ static EntryParseException cannotParseEntryException(String type, RequestEntryCh } static EntryParseException cannotParseEntryException(String type, RequestEntryChain entry, Throwable cause) { - return new EntryParseException(format("cannot parse %s : '%s'", type, entry.toString()), cause); + return new EntryParseException(format("cannot parse %s : '%s'", type, entry), cause); } static EntryParseException unexpectedEntryException(RequestEntryChain entry) { - return new EntryParseException(format("unexpected entry : '%s'", entry.toString())); + return new EntryParseException(format("unexpected entry : '%s'", entry)); } static EntryParseException requireEntryException(String name) { return new EntryParseException(name + " required"); } - static EntryParseException entryTackesNoArgException(String name, String entry) { + static EntryParseException badEntryArgsException(String name, RequestEntryChain entry) { return new EntryParseException(format("%s takes no arg : '%s'", name, entry)); - } - - + } } diff --git a/src/main/java/org/usf/jquery/web/IterableViewDecorator.java b/src/main/java/org/usf/jquery/web/IterableViewDecorator.java index 11154cb4..40063c8d 100644 --- a/src/main/java/org/usf/jquery/web/IterableViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/IterableViewDecorator.java @@ -22,5 +22,4 @@ default void parseFilters(RequestQueryBuilder query, Map param query.repeat(iterator(parseIterable(parameters))); ViewDecorator.super.parseFilters(query, parameters); } - } diff --git a/src/main/java/org/usf/jquery/web/ModelIterator.java b/src/main/java/org/usf/jquery/web/ModelIterator.java index 7b26971a..6ef26a20 100644 --- a/src/main/java/org/usf/jquery/web/ModelIterator.java +++ b/src/main/java/org/usf/jquery/web/ModelIterator.java @@ -1,6 +1,6 @@ package org.usf.jquery.web; -import static org.usf.jquery.core.Utils.isPresent; +import static org.usf.jquery.core.Utils.isEmpty; import java.util.Iterator; import java.util.stream.Stream; @@ -24,7 +24,7 @@ public boolean hasNext() { if(it.hasNext()) { return true; } - currentRev.remove(); + currentRev.remove(); //auto clean return false; } @@ -36,7 +36,7 @@ public T next() { } public static ModelIterator iterator(T[] model){ - if(isPresent(model)) { + if(!isEmpty(model)) { return new ModelIterator<>(Stream.of(model).iterator()); } throw new IllegalArgumentException("no revision"); diff --git a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java index 4cdf69b3..2c9f559c 100644 --- a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java +++ b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java @@ -17,7 +17,7 @@ public NoSuchResourceException(String s) { } static NoSuchResourceException noSuchDatabaseException(String resource) { - return noSuchResouceException(VIEW, resource); + return noSuchResouceException("database", resource); } static NoSuchResourceException noSuchViewException(String resource) { @@ -27,6 +27,10 @@ static NoSuchResourceException noSuchViewException(String resource) { static NoSuchResourceException noSuchColumnException(String resource) { return noSuchResouceException(COLUMN, resource); } + + static NoSuchResourceException noSuchResouceException(String resource) { + return noSuchResouceException("resource", resource); + } static NoSuchResourceException noSuchResouceException(String type, String resource) { return new NoSuchResourceException(quote(resource) + " " + type + " not found"); diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 3a24cff2..cb938e23 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -1,7 +1,5 @@ package org.usf.jquery.web; -import static org.usf.jquery.web.NoSuchResourceException.noSuchColumnException; - import org.usf.jquery.core.DBView; import org.usf.jquery.core.QueryView; import org.usf.jquery.core.TaggableColumn; @@ -38,7 +36,7 @@ public TaggableColumn column(String id) { .filter(c-> c.tagname().equals(id)) //tagname nullable ! .findAny() .map(c-> new ViewColumn(query, c.tagname(), c.tagname(), c.getType())) - .orElseThrow(()-> noSuchColumnException(id)); + .orElse(null); } @Override @@ -61,7 +59,7 @@ public ViewMetadata metadata() { throw unsupportedOperationException("metadata"); } - UnsupportedOperationException unsupportedOperationException(String method) { - return new UnsupportedOperationException(this.getClass().getSimpleName() + "." + method); + private UnsupportedOperationException unsupportedOperationException(String method) { + return new UnsupportedOperationException(identity() + "." + method); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index e61d1e96..b129ccb3 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -1,6 +1,5 @@ package org.usf.jquery.web; -import static java.lang.String.format; import static java.lang.reflect.Array.newInstance; import static java.util.Collections.emptyList; import static java.util.Objects.isNull; @@ -36,42 +35,34 @@ import static org.usf.jquery.web.Constants.TAG; import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; +import static org.usf.jquery.web.EntryParseException.badEntryArgsException; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; -import static org.usf.jquery.web.EntryParseException.entryTackesNoArgException; import static org.usf.jquery.web.EntryParseException.requireEntryException; import static org.usf.jquery.web.EntryParseException.unexpectedEntryException; +import static org.usf.jquery.web.NoSuchResourceException.noSuchResouceException; -import java.text.ParseException; -import java.util.Arrays; -import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.function.IntFunction; import java.util.function.Predicate; import org.usf.jquery.core.AggregateFunction; import org.usf.jquery.core.BadArgumentException; -import org.usf.jquery.core.Comparator; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; import org.usf.jquery.core.DBOrder; -import org.usf.jquery.core.DBQuery; import org.usf.jquery.core.DBView; import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; import org.usf.jquery.core.LogicalOperator; import org.usf.jquery.core.OperationColumn; -import org.usf.jquery.core.Operator; import org.usf.jquery.core.Order; import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; -import org.usf.jquery.core.QueryView; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedOperator; -import org.usf.jquery.core.Utils; import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; import org.usf.jquery.core.ViewJoin.JoinType; @@ -165,12 +156,13 @@ public Partition evalPartition(ViewDecorator td) { public TaggableColumn evalColumn(ViewDecorator td) { try { - var r = chainColumnOperations(td, false).orElseThrow(); //throws ParseException + var r = chainColumnOperations(td, false) + .orElseThrow(()-> noSuchResouceException(toString())); if(r.entry.isLast()) { - if(nonNull(r.entry.tag)) { //TD: required tag if operation + if(nonNull(r.entry.tag)) { return r.col.as(r.entry.tag); } - if(r.col instanceof TaggableColumn col) { + if(r.col instanceof TaggableColumn col) { // no operation return col; } throw requireEntryException(TAG); @@ -183,19 +175,19 @@ public TaggableColumn evalColumn(ViewDecorator td) { public DBOrder evalOrder(ViewDecorator td) { try { - var r = chainColumnOperations(td, false).orElseThrow(); //throws ParseException - if(r.entry.isLast()) { // no order + var r = chainColumnOperations(td, false) + .orElseThrow(()-> noSuchResouceException(toString())); + if(r.entry.isLast()) { // default order return r.col.order(); } - var e = r.entry.next; - if(r.entry.next.isLast()) { // next must be last - if(e.value.matches("asc|desc")) { - var o = Order.valueOf(e.requireNoArgs(ORDER).value.toUpperCase()); // order takes no args - return r.col.order(o); + var ord = r.entry.next; + if(ord.value.matches("asc|desc")) { + if(ord.isLast()) { // next must be last + return r.col.order(Order.valueOf(ord.requireNoArgs(ORDER).value.toUpperCase())); } - cannotParseEntryException(ORDER, e); + throw unexpectedEntryException(ord.next); } - throw unexpectedEntryException(e); + throw noSuchResouceException(ORDER, ord.value); } catch (Exception e) { throw cannotParseEntryException(ORDER, this, e); } @@ -208,83 +200,83 @@ public DBFilter evalFilter(ViewDecorator td) { public DBFilter evalFilter(ViewDecorator td, List values) { try { var res = chainColumnOperations(td, true); - DBFilter f; if(res.isEmpty()) { //not a column - f = tableCriteria(td, values); + return viewCriteria(td, values) + .orElseThrow(()-> noSuchResouceException(toString())); } - else { - var rc = res.get(); - if(rc.entry.isLast()) { - var fn = values.size() == 1 ? eq() : in(); - f = fn.args(new RequestEntryChain(null, false, null, values, null) - .toArgs(rc.td, rc.col, fn.getParameterSet())); - } - else { - f = rc.entry.next.columnCriteria(rc.td, rc.cd, rc.col, values); - } + var rc = res.get(); + if(rc.entry.isLast()) { //no comparator, no criteria + var fn = requireNonNull(values).size() == 1 ? eq() : in(); //non empty + var e = new RequestEntryChain(null, false, null, values, null); + return fn.args(e.toArgs(rc.td, rc.col, fn.getParameterSet())); //no chain } - return chainComparator(td, f); + return rc.entry.next.columnCriteria(rc.td, rc.cd, rc.col, values); } catch(Exception e) { throw cannotParseEntryException(FILTER, this, e); } } - - DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List values) { - if(!values.isEmpty()) { - if(isNull(args) && isLast()) { - args = values; - } - else { - throw new IllegalStateException(toString() + "=" + values); // args + parameters + + Optional viewCriteria(ViewDecorator td, List values) { + CriteriaBuilder b = null; + RequestEntryChain e = this; + if(hasNext()) { + var res = currentContext().lookupRegistredView(value).map(v-> v.criteria(next.value)); + if(res.isPresent()){ + b = res.get(); + e = next; } } + if(isNull(b)) { + b = td.criteria(value); + e = this; + } + if(nonNull(b)) { + return Optional.of(e.assertArguments(values) + .chainComparator(td, b.build(toStringArray(args)))); + } + return empty(); + } + + DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List values) { var cmp = lookupComparator(value); if(cmp.isPresent()) { var fn = cmp.get(); - return fn.args(toArgs(vc, col, fn.getParameterSet())); + return assertArguments(values) + .chainComparator(vc, fn.args(toArgs(vc, col, fn.getParameterSet()))); } - if(nonNull(cd)) { + if(nonNull(cd)) { // no operation var c = cd.criteria(value); //criteria lookup if(nonNull(c)) { - var strArgs = toStringArray(args); - var ce = requireNonNull(c.build(strArgs), - ()-> format("%s.builder(%s).build(%s)", cd.identity(), value, Arrays.toString(strArgs))); - return col.filter(ce); + return assertArguments(values) + .chainComparator(vc, col.filter(c.build(toStringArray(args)))); } - throw cannotParseEntryException("comparison|criteria", this); + throw noSuchResouceException("comparator|criteria", value); } - throw cannotParseEntryException("comparison", this); + throw noSuchResouceException("comparator", value); } - DBFilter tableCriteria(ViewDecorator td, List values) { - RequestEntryChain e; - CriteriaBuilder cb = null; - if(hasNext()) { - var res = currentContext().lookupRegistredView(value).map(v-> v.criteria(next.value)); - if(res.isPresent()){ - cb = res.get(); - e = next; + private RequestEntryChain assertArguments(List values) { + if(!isEmpty(values)) { + if(isNull(args) && isLast()) { //update args + args = values; + } + else { + throw new IllegalStateException(this + "=" + values); //denied } } - if(isNull(cb)) { - cb = td.criteria(value); - e = this; - } - var strArgs = toStringArray(values); - return requireNonNull(cb.build(strArgs), - ()-> format("%s.builder(%s).build(%s)", td.identity(), value, Arrays.toString(strArgs))); + return this; } DBFilter chainComparator(ViewDecorator td, DBFilter f) { - var e = next; //TODO => this + var e = next; while(nonNull(e)) { if(e.value.matches("and|or")) { var op = LogicalOperator.valueOf(e.value.toUpperCase()); f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); } else { - throw cannotParseEntryException("LogicalOperator", e); + throw noSuchResouceException("LogicalOperator(and|or)", e.value); } e = e.next; } @@ -415,7 +407,7 @@ RequestEntryChain requireNoArgs(String name) { if(isNull(args)) { return this; } - throw entryTackesNoArgException(name, toString()); + throw badEntryArgsException(name, this); } RequestEntryChain requireNoArgs() { @@ -429,7 +421,7 @@ RequestEntryChain requireNoNext(){ if(isLast()) { return this; } - throw new UnexpectedEntryException(value + " must be the last entry : " + this); + throw unexpectedEntryException(next); } public boolean isLast() { diff --git a/src/main/java/org/usf/jquery/web/YearTableMetadata.java b/src/main/java/org/usf/jquery/web/YearTableMetadata.java index bf5bba56..db483fcc 100644 --- a/src/main/java/org/usf/jquery/web/YearTableMetadata.java +++ b/src/main/java/org/usf/jquery/web/YearTableMetadata.java @@ -18,7 +18,6 @@ import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Utils.isPresent; import static org.usf.jquery.web.Constants.EMPTY_REVISION; -import static org.usf.jquery.web.JQueryContext.database; import java.sql.Connection; import java.sql.DatabaseMetaData; From c74319886ddf50dad63c1304da171420e156c3bb Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 6 Aug 2024 00:22:50 +0200 Subject: [PATCH 108/298] edit --- .../java/org/usf/jquery/web/Constants.java | 1 - .../usf/jquery/web/EntryParseException.java | 4 +- .../jquery/web/NoSuchResourceException.java | 4 -- .../org/usf/jquery/web/RequestEntryChain.java | 53 +++++++++++-------- .../org/usf/jquery/web/ViewDecorator.java | 4 +- 5 files changed, 34 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index f157d31c..8558625d 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -24,7 +24,6 @@ public final class Constants { public static final String FETCH = "fetch"; public static final String OFFSET = "offset"; public static final String JOIN = "join"; - public static final String TAG = "tag"; public static final String PARTITION = "partition"; @Deprecated public static final String REVISION = "revision"; //not standard diff --git a/src/main/java/org/usf/jquery/web/EntryParseException.java b/src/main/java/org/usf/jquery/web/EntryParseException.java index 478a242c..a86654e8 100644 --- a/src/main/java/org/usf/jquery/web/EntryParseException.java +++ b/src/main/java/org/usf/jquery/web/EntryParseException.java @@ -30,8 +30,8 @@ static EntryParseException unexpectedEntryException(RequestEntryChain entry) { return new EntryParseException(format("unexpected entry : '%s'", entry)); } - static EntryParseException requireEntryException(String name) { - return new EntryParseException(name + " required"); + static EntryParseException requireEntryException(String name, String entry) { + return new EntryParseException(format("%s required after '%s'", name, entry)); } static EntryParseException badEntryArgsException(String name, RequestEntryChain entry) { diff --git a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java index 2c9f559c..fa04f334 100644 --- a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java +++ b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java @@ -28,10 +28,6 @@ static NoSuchResourceException noSuchColumnException(String resource) { return noSuchResouceException(COLUMN, resource); } - static NoSuchResourceException noSuchResouceException(String resource) { - return noSuchResouceException("resource", resource); - } - static NoSuchResourceException noSuchResouceException(String type, String resource) { return new NoSuchResourceException(quote(resource) + " " + type + " not found"); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index b129ccb3..bb3377f5 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -32,13 +32,12 @@ import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.PARTITION; import static org.usf.jquery.web.Constants.SELECT; -import static org.usf.jquery.web.Constants.TAG; import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.EntryParseException.badEntryArgsException; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; -import static org.usf.jquery.web.EntryParseException.requireEntryException; -import static org.usf.jquery.web.EntryParseException.unexpectedEntryException; +import static org.usf.jquery.web.EntrySyntaxException.requireEntryTagException; +import static org.usf.jquery.web.EntrySyntaxException.unexpectedEntryException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResouceException; import java.util.List; @@ -113,7 +112,26 @@ public ViewJoin evalJoin(ViewDecorator td) { //evalView query|view:alias - public ViewDecorator evalQuery(ViewDecorator td, boolean requireTag) { //sub context + public ViewDecorator evalView(ViewDecorator vd) { + try { + var res = currentContext().lookupRegistredView(value); + if(res.isPresent()) { + if(requireNoArgs().isLast()) { + if(nonNull(tag)) { + return new ViewDecoratorWrapper(res.get(), tag); + } + throw requireEntryTagException(this); + } + throw unexpectedEntryException(next); + } + return evalQuery(vd, true).orElseThrow(); + } + catch (Exception e) { + throw cannotParseEntryException(VIEW, this, e); + } + } + + Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub context if(SELECT.equals(value)) { var q = new RequestQueryBuilder().columns(toColumnArgs(td, false)); var e = this; @@ -128,13 +146,12 @@ public ViewDecorator evalQuery(ViewDecorator td, boolean requireTag) { //sub con default: throw unexpectedEntryException(e); } } - if(requireTag && isNull(e.tag)) { - throw requireEntryException(TAG); + if(!requireTag || nonNull(e.tag)) { + return Optional.of(new QueryDecorator(e.tag, q.asView())); } - return new QueryDecorator(e.tag, q.asView()); + throw requireEntryTagException(e); } - //TODO parse view::alias - throw cannotParseEntryException(SELECT, this); + return empty(); } public Partition evalPartition(ViewDecorator td) { @@ -156,8 +173,7 @@ public Partition evalPartition(ViewDecorator td) { public TaggableColumn evalColumn(ViewDecorator td) { try { - var r = chainColumnOperations(td, false) - .orElseThrow(()-> noSuchResouceException(toString())); + var r = chainColumnOperations(td, false).orElseThrow(); if(r.entry.isLast()) { if(nonNull(r.entry.tag)) { return r.col.as(r.entry.tag); @@ -165,7 +181,7 @@ public TaggableColumn evalColumn(ViewDecorator td) { if(r.col instanceof TaggableColumn col) { // no operation return col; } - throw requireEntryException(TAG); + throw requireEntryTagException(r.entry); } throw unexpectedEntryException(r.entry.next); } catch (Exception e) { @@ -175,8 +191,7 @@ public TaggableColumn evalColumn(ViewDecorator td) { public DBOrder evalOrder(ViewDecorator td) { try { - var r = chainColumnOperations(td, false) - .orElseThrow(()-> noSuchResouceException(toString())); + var r = chainColumnOperations(td, false).orElseThrow(); if(r.entry.isLast()) { // default order return r.col.order(); } @@ -201,8 +216,7 @@ public DBFilter evalFilter(ViewDecorator td, List values) { try { var res = chainColumnOperations(td, true); if(res.isEmpty()) { //not a column - return viewCriteria(td, values) - .orElseThrow(()-> noSuchResouceException(toString())); + return viewCriteria(td, values).orElseThrow(); } var rc = res.get(); if(rc.entry.isLast()) { //no comparator, no criteria @@ -417,13 +431,6 @@ RequestEntryChain requireNoArgs() { throw badArgumentCountException(0, args.size()); //TODO } - RequestEntryChain requireNoNext(){ - if(isLast()) { - return this; - } - throw unexpectedEntryException(next); - } - public boolean isLast() { return isNull(next); } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index cd190682..6cbc5065 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -107,7 +107,7 @@ default void parseViews(RequestQueryBuilder query, Map paramet if(parameters.containsKey(VIEW)) { Stream.of(parameters.get(VIEW)) .flatMap(c-> parseEntries(c).stream()) - .forEach(e-> currentContext().declareView(e.evalQuery(this, true))); + .forEach(e-> currentContext().declareView(e.evalView(this))); } } @@ -167,7 +167,7 @@ private static Integer requirePositiveInt(String key, Map para } return null; } - + static Stream flatParameters(String... arr) { //number local separator return Stream.of(arr).flatMap(v-> Stream.of(v.split(","))); } From 8b5c5a7ed77a809dbce76874a87a5309122c600f Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 6 Aug 2024 09:50:33 +0200 Subject: [PATCH 109/298] edit --- .../usf/jquery/web/EntryParseException.java | 8 ---- .../org/usf/jquery/web/RequestEntryChain.java | 47 ++++++++++++++----- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/EntryParseException.java b/src/main/java/org/usf/jquery/web/EntryParseException.java index a86654e8..2c904aaa 100644 --- a/src/main/java/org/usf/jquery/web/EntryParseException.java +++ b/src/main/java/org/usf/jquery/web/EntryParseException.java @@ -25,14 +25,6 @@ static EntryParseException cannotParseEntryException(String type, RequestEntryCh static EntryParseException cannotParseEntryException(String type, RequestEntryChain entry, Throwable cause) { return new EntryParseException(format("cannot parse %s : '%s'", type, entry), cause); } - - static EntryParseException unexpectedEntryException(RequestEntryChain entry) { - return new EntryParseException(format("unexpected entry : '%s'", entry)); - } - - static EntryParseException requireEntryException(String name, String entry) { - return new EntryParseException(format("%s required after '%s'", name, entry)); - } static EntryParseException badEntryArgsException(String name, RequestEntryChain entry) { return new EntryParseException(format("%s takes no arg : '%s'", name, entry)); diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index bb3377f5..ad3c6047 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -96,23 +96,44 @@ public RequestEntryChain(String value) { this(value, false); } - public ViewDecorator evalQuery(ViewDecorator td) { - return evalQuery(td, false); + public ViewJoin[] evalJoin(ViewDecorator td) { + try { + RequestEntryChain e = null; + JoinBuilder j = null; + if(hasNext()) { + var res = currentContext().lookupRegistredView(value).map(v-> v.joiner(next.value)); + if(res.isPresent()) { + j = res.get(); + e = next; + } + } + if(isNull(j)) { + j = td.joiner(value); + e = this; + } + if(nonNull(j)) { + if(e.isLast()) { + e.requireNoArgs(); + return j.build(); + } + throw unexpectedEntryException(e.next); + } + } + catch (Exception e) { + throw cannotParseEntryException(JOIN, this, e); + } } - - public ViewJoin evalJoin(ViewDecorator td) { - if(value.matches(JoinType.pattern())) { - var jt = JoinType.valueOf(value); - var args = toArgs( td, null, null); - return join(jt, (DBView)args[0], (DBFilter[])args[0]); + public ViewDecorator evalQuery(ViewDecorator td) { + try { + return evalQuery(td, false).orElseThrow(); + } + catch (Exception e) { + throw cannotParseEntryException("query", this, e); } - throw cannotParseEntryException(JOIN, this); //TD } - //evalView query|view:alias - - public ViewDecorator evalView(ViewDecorator vd) { + public ViewDecorator evalView(ViewDecorator vd) {// [query|view]:alias try { var res = currentContext().lookupRegistredView(value); if(res.isPresent()) { @@ -124,7 +145,7 @@ public ViewDecorator evalView(ViewDecorator vd) { } throw unexpectedEntryException(next); } - return evalQuery(vd, true).orElseThrow(); + return evalQuery(vd, true).orElseThrow(); //not a query } catch (Exception e) { throw cannotParseEntryException(VIEW, this, e); From 594bbf54f4d791949b8f340d3cf7b98b9a73acfe Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 6 Aug 2024 23:47:54 +0200 Subject: [PATCH 110/298] edit --- .../org/usf/jquery/web/ArgumentParsers.java | 1 + .../usf/jquery/web/EntrySyntaxException.java | 28 +++ .../jquery/web/NoSuchResourceException.java | 11 +- .../org/usf/jquery/web/RequestEntryChain.java | 219 ++++++++---------- .../usf/jquery/web/ViewDecoratorWrapper.java | 22 ++ .../org/usf/jquery/web/YearViewDecorator.java | 4 +- .../usf/jquery/web/RequestEntryChainTest.java | 27 --- 7 files changed, 161 insertions(+), 151 deletions(-) create mode 100644 src/main/java/org/usf/jquery/web/EntrySyntaxException.java create mode 100644 src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java delete mode 100644 src/test/java/org/usf/jquery/web/RequestEntryChainTest.java diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 53475dd0..79fd81a1 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -122,6 +122,7 @@ public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type) { case ORDER: return RequestEntryChain::evalOrder; case QUERY: return RequestEntryChain::evalQuery; case PARTITION: return RequestEntryChain::evalPartition; + //TODO join default: throw unsupportedTypeException(type); } } diff --git a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java new file mode 100644 index 00000000..9da5132d --- /dev/null +++ b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java @@ -0,0 +1,28 @@ +package org.usf.jquery.web; + +import static java.lang.String.format; + +/** + * + * @author u$f + * + */ +@SuppressWarnings("serial") +public final class EntrySyntaxException extends WebException { + + public EntrySyntaxException(String message) { + super(message); + } + + static EntrySyntaxException requireEntryTagException(RequestEntryChain entry) { + return new EntrySyntaxException(format("%s: required", entry)); + } + + static EntrySyntaxException requireNoArgsEntryException(RequestEntryChain entry) { + return new EntrySyntaxException(format("%s takes no args : %s", entry.getValue(), entry)); + } + + static EntrySyntaxException unexpectedEntryException(RequestEntryChain entry) { + return new EntrySyntaxException(format("unexpected entry : %s", entry)); + } +} diff --git a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java index fa04f334..c81b2f56 100644 --- a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java +++ b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java @@ -1,5 +1,6 @@ package org.usf.jquery.web; +import static java.lang.String.format; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.VIEW; @@ -17,19 +18,19 @@ public NoSuchResourceException(String s) { } static NoSuchResourceException noSuchDatabaseException(String resource) { - return noSuchResouceException("database", resource); + return noSuchResourceException("database", resource); } static NoSuchResourceException noSuchViewException(String resource) { - return noSuchResouceException(VIEW, resource); + return noSuchResourceException(VIEW, resource); } static NoSuchResourceException noSuchColumnException(String resource) { - return noSuchResouceException(COLUMN, resource); + return noSuchResourceException(COLUMN, resource); } - static NoSuchResourceException noSuchResouceException(String type, String resource) { - return new NoSuchResourceException(quote(resource) + " " + type + " not found"); + static NoSuchResourceException noSuchResourceException(String type, String resource) { + return new NoSuchResourceException(format("%s='%s'", type, resource)); } static NoSuchResourceException undeclaredResouceException(String child, String parent) { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index ad3c6047..090e4d02 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -1,5 +1,6 @@ package org.usf.jquery.web; +import static java.lang.String.format; import static java.lang.reflect.Array.newInstance; import static java.util.Collections.emptyList; import static java.util.Objects.isNull; @@ -8,7 +9,6 @@ import static java.util.Optional.empty; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.Comparator.eq; import static org.usf.jquery.core.Comparator.in; @@ -21,7 +21,6 @@ import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.ViewJoin.join; import static org.usf.jquery.web.ArgumentParsers.parse; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.DISTINCT; @@ -34,11 +33,12 @@ import static org.usf.jquery.web.Constants.SELECT; import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; -import static org.usf.jquery.web.EntryParseException.badEntryArgsException; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import static org.usf.jquery.web.EntrySyntaxException.requireEntryTagException; +import static org.usf.jquery.web.EntrySyntaxException.requireNoArgsEntryException; import static org.usf.jquery.web.EntrySyntaxException.unexpectedEntryException; -import static org.usf.jquery.web.NoSuchResourceException.noSuchResouceException; +import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; +import static org.usf.jquery.web.NoSuchResourceException.noSuchViewException; import java.util.List; import java.util.Optional; @@ -51,7 +51,6 @@ import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; import org.usf.jquery.core.DBOrder; -import org.usf.jquery.core.DBView; import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; import org.usf.jquery.core.LogicalOperator; @@ -64,7 +63,6 @@ import org.usf.jquery.core.TypedOperator; import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; -import org.usf.jquery.core.ViewJoin.JoinType; import org.usf.jquery.core.WindowFunction; import lombok.AccessLevel; @@ -95,35 +93,19 @@ final class RequestEntryChain { public RequestEntryChain(String value) { this(value, false); } - - public ViewJoin[] evalJoin(ViewDecorator td) { + + public ViewDecorator evalView(ViewDecorator vd) {// [query|view]:alias try { - RequestEntryChain e = null; - JoinBuilder j = null; - if(hasNext()) { - var res = currentContext().lookupRegistredView(value).map(v-> v.joiner(next.value)); - if(res.isPresent()) { - j = res.get(); - e = next; - } - } - if(isNull(j)) { - j = td.joiner(value); - e = this; - } - if(nonNull(j)) { - if(e.isLast()) { - e.requireNoArgs(); - return j.build(); - } - throw unexpectedEntryException(e.next); - } + var res = currentContext().lookupRegistredView(value); + return res.isPresent() //check args & next only if view exists + ? new ViewDecoratorWrapper(res.get(), requireNoArgs().requireNoNext().requireTag()) + : evalQuery(vd, true).orElseThrow(); } catch (Exception e) { - throw cannotParseEntryException(JOIN, this, e); + throw cannotParseEntryException(VIEW, this, e); } } - + public ViewDecorator evalQuery(ViewDecorator td) { try { return evalQuery(td, false).orElseThrow(); @@ -133,25 +115,6 @@ public ViewDecorator evalQuery(ViewDecorator td) { } } - public ViewDecorator evalView(ViewDecorator vd) {// [query|view]:alias - try { - var res = currentContext().lookupRegistredView(value); - if(res.isPresent()) { - if(requireNoArgs().isLast()) { - if(nonNull(tag)) { - return new ViewDecoratorWrapper(res.get(), tag); - } - throw requireEntryTagException(this); - } - throw unexpectedEntryException(next); - } - return evalQuery(vd, true).orElseThrow(); //not a query - } - catch (Exception e) { - throw cannotParseEntryException(VIEW, this, e); - } - } - Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub context if(SELECT.equals(value)) { var q = new RequestQueryBuilder().columns(toColumnArgs(td, false)); @@ -159,7 +122,7 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub while(e.hasNext()) { //preserve last entry e = e.next; switch(e.value) {//column not allowed - case DISTINCT: e.requireNoArgs(DISTINCT); q.distinct(); break; + case DISTINCT: e.requireNoArgs(); q.distinct(); break; case FILTER: q.filters(e.toFilterArgs(td)); break; case ORDER: q.orders(e.toOderArgs(td)); break; //not sure case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; @@ -167,10 +130,7 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub default: throw unexpectedEntryException(e); } } - if(!requireTag || nonNull(e.tag)) { - return Optional.of(new QueryDecorator(e.tag, q.asView())); - } - throw requireEntryTagException(e); + return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); } return empty(); } @@ -191,25 +151,45 @@ public Partition evalPartition(ViewDecorator td) { } throw cannotParseEntryException(PARTITION, this); } + + //[view.]joiner + public ViewJoin[] evalJoin(ViewDecorator vd) { + try { + var e = this; + if(hasNext()) { + vd = currentContext().lookupRegistredView(value).orElseThrow(()-> noSuchViewException(value)); + e = requireNoArgs().next; //check args only if view exists + } + var join = vd.joiner(e.value); + if(nonNull(join)) { + e.requireNoArgs().requireNoNext(); //check args & next only if joiner exists + return requireNonNull(join.build(), format("%s.joiner(%s).build()", vd.identity(), value)); + } + throw noSuchResourceException(vd.identity()+".joiner", e.value); + } + catch (Exception e) { + throw cannotParseEntryException(JOIN, this, e); + } + } + //[view.]column[.operator]* public TaggableColumn evalColumn(ViewDecorator td) { try { var r = chainColumnOperations(td, false).orElseThrow(); - if(r.entry.isLast()) { - if(nonNull(r.entry.tag)) { - return r.col.as(r.entry.tag); - } - if(r.col instanceof TaggableColumn col) { // no operation - return col; - } - throw requireEntryTagException(r.entry); + r.entry.requireNoNext(); //check next only if column exists + if(nonNull(r.entry.tag)) { + return r.col.as(r.entry.tag); } - throw unexpectedEntryException(r.entry.next); + if(r.col instanceof TaggableColumn col) { + return col; + } + throw requireEntryTagException(r.entry); } catch (Exception e) { throw cannotParseEntryException(COLUMN, this, e); } } + //[view.]column[.operator]*[.order] public DBOrder evalOrder(ViewDecorator td) { try { var r = chainColumnOperations(td, false).orElseThrow(); @@ -217,13 +197,11 @@ public DBOrder evalOrder(ViewDecorator td) { return r.col.order(); } var ord = r.entry.next; - if(ord.value.matches("asc|desc")) { - if(ord.isLast()) { // next must be last - return r.col.order(Order.valueOf(ord.requireNoArgs(ORDER).value.toUpperCase())); - } - throw unexpectedEntryException(ord.next); + if(ord.value.matches("asc|desc")) {//check args & next only if order exists + var s = ord.requireNoArgs().requireNoNext().value.toUpperCase(); + return r.col.order(Order.valueOf(s)); } - throw noSuchResouceException(ORDER, ord.value); + throw noSuchResourceException(ORDER, ord.value); } catch (Exception e) { throw cannotParseEntryException(ORDER, this, e); } @@ -233,11 +211,17 @@ public DBFilter evalFilter(ViewDecorator td) { return evalFilter(td, emptyList()); } - public DBFilter evalFilter(ViewDecorator td, List values) { + //[view.]criteria | [view.]column.criteria | [view.]column[.operator]*[.comparator] + public DBFilter evalFilter(ViewDecorator td, List values) { //supply values try { var res = chainColumnOperations(td, true); if(res.isEmpty()) { //not a column - return viewCriteria(td, values).orElseThrow(); + try { + return viewCriteria(td, values); + } + catch (NoSuchResourceException e) { + + } } var rc = res.get(); if(rc.entry.isLast()) { //no comparator, no criteria @@ -252,9 +236,9 @@ public DBFilter evalFilter(ViewDecorator td, List values) { } } - Optional viewCriteria(ViewDecorator td, List values) { + DBFilter viewCriteria(ViewDecorator td, List values) { CriteriaBuilder b = null; - RequestEntryChain e = this; + RequestEntryChain e = null; if(hasNext()) { var res = currentContext().lookupRegistredView(value).map(v-> v.criteria(next.value)); if(res.isPresent()){ @@ -267,40 +251,38 @@ Optional viewCriteria(ViewDecorator td, List values e = this; } if(nonNull(b)) { - return Optional.of(e.assertArguments(values) - .chainComparator(td, b.build(toStringArray(args)))); + var f = b.build(toStringArray(e.assertOuterParameters(values))); //nonNull !? + return e.chainComparator(td, f); } - return empty(); + throw noSuchResourceException("view.criteria", e.value); } DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List values) { var cmp = lookupComparator(value); if(cmp.isPresent()) { var fn = cmp.get(); - return assertArguments(values) - .chainComparator(vc, fn.args(toArgs(vc, col, fn.getParameterSet()))); + var cp = new RequestEntryChain(null, false, null, assertOuterParameters(values), null); //do not set args=values + return chainComparator(vc, fn.args(cp.toArgs(vc, col, fn.getParameterSet()))); } if(nonNull(cd)) { // no operation var c = cd.criteria(value); //criteria lookup if(nonNull(c)) { - return assertArguments(values) - .chainComparator(vc, col.filter(c.build(toStringArray(args)))); + var ex = c.build(toStringArray(assertOuterParameters(values))); //nonNull !? + return chainComparator(vc, col.filter(ex)); } - throw noSuchResouceException("comparator|criteria", value); + throw noSuchResourceException("comparator|criteria", value); } - throw noSuchResouceException("comparator", value); + throw noSuchResourceException("comparator", value); } - private RequestEntryChain assertArguments(List values) { - if(!isEmpty(values)) { - if(isNull(args) && isLast()) { //update args - args = values; - } - else { - throw new IllegalStateException(this + "=" + values); //denied - } + private List assertOuterParameters(List values) { + if(isEmpty(values)) { + return args; + } + if(isNull(args) && isLast()) { //update args + return values; //!danger @see ViewDecorator } - return this; + throw new IllegalStateException(this + "=" + values); //denied } DBFilter chainComparator(ViewDecorator td, DBFilter f) { @@ -311,25 +293,25 @@ DBFilter chainComparator(ViewDecorator td, DBFilter f) { f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); } else { - throw noSuchResouceException("LogicalOperator(and|or)", e.value); + throw noSuchResourceException("LogicalOperator(and|or)", e.value); } e = e.next; } return f; } - private Optional chainColumnOperations(ViewDecorator td, boolean filter) { + private Optional chainColumnOperations(ViewDecorator td, boolean filter) { return lookupResource(td).map(r-> { var e = r.entry.next; - while(nonNull(e)) { // chain until !operator - var o = e.lookupOperation(td, r.col, fn-> true); //accept all operation + while(nonNull(e)) { //chain until !operator + var o = e.lookupOperation(td, r.col, fn-> true); //accept all if(o.isEmpty()) { break; - } + } r.cd = null; r.entry = e; r.col = filter && "over".equals(e.value) - ? windowColumn(r.td, o.get().as(r.cd.identity())) //TODO require tag | random tag + ? windowColumn(r.td, o.get()) : o.get(); e = e.next; } @@ -337,39 +319,40 @@ private Optional chainColumnOperations(ViewDecorator td, boolean }); } - private static DBColumn windowColumn(ViewDecorator vd, TaggableColumn col) { + private static DBColumn windowColumn(ViewDecorator vd, DBColumn col) { var v = vd.view(); + var tag = "over_" + vd.identity() + "_" + col.hashCode(); //over_view_hash currentContext().overView(v, ()-> new RequestQueryBuilder() .columns(allColumns(v)).asView()) - .getBuilder().columns(col); //append over column - return new ViewColumn(v, doubleQuote(col.tagname()), null, col.getType()); + .getBuilder().columns(col.as(tag)); //append over column + return new ViewColumn(v, doubleQuote(tag), null, col.getType()); } - private Optional lookupResource(ViewDecorator td) { //do not change priority + private Optional lookupResource(ViewDecorator td) { //do not change priority if(hasNext()) { //check td.cd first var rc = currentContext().lookupRegistredView(value) .flatMap(v-> next.lookupViewResource(v, RequestEntryChain::isWindowFunction)); if(rc.isPresent()) { - requireNoArgs(VIEW); //view takes no args + requireNoArgs(); //view takes no args return rc; } } return currentContext().lookupDeclaredColumn(value) //declared column - .map(c-> new ResourceCursor(null, null, requireNoArgs(COLUMN), c)) + .map(c-> new ViewResource(null, null, requireNoArgs(), c)) .or(()-> lookupViewResource(td, fn-> true)); //registered column } - private Optional lookupViewResource(ViewDecorator td, Predicate pre) { + private Optional lookupViewResource(ViewDecorator td, Predicate pre) { if(td instanceof QueryDecorator qd) { //query column var res = ofNullable(qd.column(value)) - .map(c-> new ResourceCursor(td, null, requireNoArgs(COLUMN), c)); + .map(c-> new ViewResource(td, null, requireNoArgs(), c)); if(res.isPresent()) { return res; } } return currentContext().lookupRegistredColumn(value) - .map(cd-> new ResourceCursor(td, cd, requireNoArgs(COLUMN), td.column(cd))) - .or(()-> lookupOperation(td, null, pre).map(col-> new ResourceCursor(td, null, this, col))); + .map(cd-> new ViewResource(td, cd, requireNoArgs(), td.column(cd))) + .or(()-> lookupOperation(td, null, pre).map(col-> new ViewResource(td, null, this, col))); } private Optional lookupOperation(ViewDecorator td, DBColumn col, Predicate opr) { @@ -438,18 +421,25 @@ private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunc } } - RequestEntryChain requireNoArgs(String name) { - if(isNull(args)) { + RequestEntryChain requireNoNext() { + if(isLast()) { return this; } - throw badEntryArgsException(name, this); + throw unexpectedEntryException(next); } RequestEntryChain requireNoArgs() { if(isNull(args)) { return this; } - throw badArgumentCountException(0, args.size()); //TODO + throw requireNoArgsEntryException(this); + } + + String requireTag() { + if(nonNull(tag)) { + return tag; + } + throw requireEntryTagException(this); } public boolean isLast() { @@ -475,10 +465,6 @@ public String toString() { return isNull(tag) ? s : s + ":" + tag; } - protected RequestEntryChain copy() { - return new RequestEntryChain(value, text, next, args, tag); - } - private static boolean isWindowFunction(TypedOperator op) { var fn = op.unwrap(); return fn instanceof WindowFunction || @@ -496,7 +482,7 @@ private static String[] toStringArray(List entries) { } @AllArgsConstructor - static final class ResourceCursor { + static final class ViewResource { private final ViewDecorator td; //optional private ColumnDecorator cd; //optional @@ -509,5 +495,4 @@ public String toString() { return td + "." + cd + " => " + entry.toString(); } } - } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java b/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java new file mode 100644 index 00000000..fd8560da --- /dev/null +++ b/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java @@ -0,0 +1,22 @@ +package org.usf.jquery.web; + +import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; + +/** + * + * @author u$f + * + */ +@RequiredArgsConstructor +final class ViewDecoratorWrapper implements ViewDecorator { + + @Delegate + private final ViewDecorator view; + private final String id; + + @Override + public String identity() { + return id; + } +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/YearViewDecorator.java b/src/main/java/org/usf/jquery/web/YearViewDecorator.java index d39dad31..c70ba134 100644 --- a/src/main/java/org/usf/jquery/web/YearViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearViewDecorator.java @@ -12,7 +12,7 @@ import static org.usf.jquery.web.Constants.REVISION_MODE; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.JQueryContext.database; -import static org.usf.jquery.web.NoSuchResourceException.noSuchResouceException; +import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import static org.usf.jquery.web.RevisionIterator.iterator; import static org.usf.jquery.web.RevisionIterator.monthFilter; @@ -79,7 +79,7 @@ default YearMonth[] parseRevisions(Map parameterMap) { : new YearMonth[] {now()}; var revs = mod.apply(values); if(isEmpty(revs)) { - throw noSuchResouceException(REVISION, + throw noSuchResourceException(REVISION, Stream.of(values).map(YearMonth::toString).collect(joining(", "))); //require available revisions } return revs; diff --git a/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java b/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java deleted file mode 100644 index 2038812c..00000000 --- a/src/test/java/org/usf/jquery/web/RequestEntryChainTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.usf.jquery.web; - -import static java.util.Collections.emptyList; -import static org.usf.jquery.web.RequestParser.parseEntry; - -import org.junit.jupiter.api.Test; - -class RequestEntryChainTest { - - @Test - void test() { - JQueryContext.register(emptyList(), emptyList()); - parseEntry("count()").evalColumn(new ViewDecorator() { - - @Override - public String identity() { - return null; - } - - @Override - public String columnName(ColumnDecorator cd) { - return null; - } - }); - } - -} From 84993f09609a6a5202bf06cc75714c81eafb99f9 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 7 Aug 2024 09:38:39 +0200 Subject: [PATCH 111/298] edit --- .../org/usf/jquery/web/RequestEntryChain.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 090e4d02..6aeeaff3 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -261,7 +261,7 @@ DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List var cmp = lookupComparator(value); if(cmp.isPresent()) { var fn = cmp.get(); - var cp = new RequestEntryChain(null, false, null, assertOuterParameters(values), null); //do not set args=values + var cp = new RequestEntryChain(null, false, null, assertOuterParameters(values), null); return chainComparator(vc, fn.args(cp.toArgs(vc, col, fn.getParameterSet()))); } if(nonNull(cd)) { // no operation @@ -279,25 +279,30 @@ private List assertOuterParameters(List va if(isEmpty(values)) { return args; } - if(isNull(args) && isLast()) { //update args - return values; //!danger @see ViewDecorator + if(isNull(args) && isLast()) { //do not set args=values + return values; } throw new IllegalStateException(this + "=" + values); //denied } DBFilter chainComparator(ViewDecorator td, DBFilter f) { var e = next; - while(nonNull(e)) { - if(e.value.matches("and|or")) { - var op = LogicalOperator.valueOf(e.value.toUpperCase()); - f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); - } - else { - throw noSuchResourceException("LogicalOperator(and|or)", e.value); + try { + while(nonNull(e)) { + if(e.value.matches("and|or")) { + var op = LogicalOperator.valueOf(e.value.toUpperCase()); + f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); + } + else { + throw noSuchResourceException("LogicalOperator(and|or)", e.value); + } + e = e.next; } - e = e.next; + return f; + } + catch (Exception ex) { + throw cannotParseEntryException("comparator.chain", e, ex); } - return f; } private Optional chainColumnOperations(ViewDecorator td, boolean filter) { From 6ad03b2eeb39e77a53037efc7538487ea310eea4 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 7 Aug 2024 13:43:03 +0200 Subject: [PATCH 112/298] edit --- .../web/ConflictingResourceException.java | 18 --- .../java/org/usf/jquery/web/Constants.java | 1 + .../usf/jquery/web/ContextEnvironment.java | 9 +- .../org/usf/jquery/web/ContextManager.java | 6 +- .../usf/jquery/web/EntrySyntaxException.java | 18 ++- .../web/IllegalDataAccessException.java | 9 -- .../jquery/web/MissingParameterException.java | 1 + .../jquery/web/NoSuchResourceException.java | 18 +-- .../org/usf/jquery/web/QueryDecorator.java | 2 + .../org/usf/jquery/web/RequestEntryChain.java | 146 ++++++++++-------- .../jquery/web/RequestQueryParamResolver.java | 10 +- .../jquery/web/ResourceAccessException.java | 23 +++ 12 files changed, 134 insertions(+), 127 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/web/ConflictingResourceException.java delete mode 100644 src/main/java/org/usf/jquery/web/IllegalDataAccessException.java create mode 100644 src/main/java/org/usf/jquery/web/ResourceAccessException.java diff --git a/src/main/java/org/usf/jquery/web/ConflictingResourceException.java b/src/main/java/org/usf/jquery/web/ConflictingResourceException.java deleted file mode 100644 index 6cd91c7d..00000000 --- a/src/main/java/org/usf/jquery/web/ConflictingResourceException.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.usf.jquery.web; - -/** - * - * @author u$f - * - */ -@SuppressWarnings("serial") -public class ConflictingResourceException extends WebException { - - public ConflictingResourceException(String message) { - super(message); - } - - public static ConflictingResourceException resourceAlreadyExistsException(String name, String value) { - return new ConflictingResourceException(name + " already exists : " + value); - } -} diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index 8558625d..ff6e5390 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -14,6 +14,7 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class Constants { + public static final String QUERY = "query"; public static final String VIEW = "view"; public static final String SELECT = "select"; public static final String COLUMN = "column"; //select diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index 948075b3..f8432f2f 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -8,7 +8,7 @@ import static java.util.stream.Collectors.toUnmodifiableMap; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNonEmpty; -import static org.usf.jquery.web.ConflictingResourceException.resourceAlreadyExistsException; +import static org.usf.jquery.web.ResourceAccessException.resourceAlreadyExistsException; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.VIEW; @@ -62,11 +62,11 @@ public ContextEnvironment(ContextEnvironment ctx) { this.schema = ctx.schema; } - public Optional lookupRegistredView(String name) { + public Optional lookupRegisteredView(String name) { return ofNullable(views.get(name)); } - public Optional lookupRegistredColumn(String name) { + public Optional lookupRegisteredColumn(String name) { return ofNullable(columns.get(name)); } @@ -88,6 +88,9 @@ void declareView(ViewDecorator view) { //additional request views } TaggableColumn declareColumn(TaggableColumn col) { + if(views.containsKey(col.tagname())) { //cannot overwrite registered views + throw resourceAlreadyExistsException(COLUMN, col.tagname()); + } //but can overwrite registered columns return declaredColumns.compute(col.tagname(), (k,v)-> { if(isNull(v)){ return col; diff --git a/src/main/java/org/usf/jquery/web/ContextManager.java b/src/main/java/org/usf/jquery/web/ContextManager.java index c50d7c83..24e0f420 100644 --- a/src/main/java/org/usf/jquery/web/ContextManager.java +++ b/src/main/java/org/usf/jquery/web/ContextManager.java @@ -2,8 +2,8 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; -import static org.usf.jquery.web.ConflictingResourceException.resourceAlreadyExistsException; -import static org.usf.jquery.web.NoSuchResourceException.noSuchDatabaseException; +import static org.usf.jquery.web.ResourceAccessException.resourceAlreadyExistsException; +import static org.usf.jquery.web.NoSuchResourceException.*; import java.util.HashMap; import java.util.Map; @@ -52,7 +52,7 @@ static ContextEnvironment context(String database){ if(nonNull(ctx)) { return setCurrentContext(ctx); } - throw noSuchDatabaseException(database); + throw noSuchResourceException("database", database); } static ContextEnvironment setCurrentContext(ContextEnvironment ctx) { diff --git a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java index 9da5132d..89b9f900 100644 --- a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java +++ b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java @@ -13,16 +13,20 @@ public final class EntrySyntaxException extends WebException { public EntrySyntaxException(String message) { super(message); } - - static EntrySyntaxException requireEntryTagException(RequestEntryChain entry) { - return new EntrySyntaxException(format("%s: required", entry)); + + static EntrySyntaxException unexpectedEntryException(RequestEntryChain entry) { + return new EntrySyntaxException(format("unexpected entry : %s", entry)); + } + + static EntrySyntaxException unexpectedEntryValueException(RequestEntryChain entry) { + return new EntrySyntaxException(format("unexpected value : %s", entry)); } - static EntrySyntaxException requireNoArgsEntryException(RequestEntryChain entry) { + static EntrySyntaxException unexpectedEntryArgsException(RequestEntryChain entry) { return new EntrySyntaxException(format("%s takes no args : %s", entry.getValue(), entry)); } - - static EntrySyntaxException unexpectedEntryException(RequestEntryChain entry) { - return new EntrySyntaxException(format("unexpected entry : %s", entry)); + + static EntrySyntaxException expectedEntryTagException(RequestEntryChain entry) { + return new EntrySyntaxException(format("expected : %s", entry)); } } diff --git a/src/main/java/org/usf/jquery/web/IllegalDataAccessException.java b/src/main/java/org/usf/jquery/web/IllegalDataAccessException.java deleted file mode 100644 index 27d97970..00000000 --- a/src/main/java/org/usf/jquery/web/IllegalDataAccessException.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.usf.jquery.web; - -@SuppressWarnings("serial") -public final class IllegalDataAccessException extends WebException { - - public IllegalDataAccessException(String s) { - super(s); - } -} diff --git a/src/main/java/org/usf/jquery/web/MissingParameterException.java b/src/main/java/org/usf/jquery/web/MissingParameterException.java index 73e22868..b3e8ea5c 100644 --- a/src/main/java/org/usf/jquery/web/MissingParameterException.java +++ b/src/main/java/org/usf/jquery/web/MissingParameterException.java @@ -11,6 +11,7 @@ * @author u$f * */ +@Deprecated(forRemoval = true) @SuppressWarnings("serial") public final class MissingParameterException extends WebException { diff --git a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java index c81b2f56..39ee70bd 100644 --- a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java +++ b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java @@ -2,8 +2,6 @@ import static java.lang.String.format; import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.web.Constants.COLUMN; -import static org.usf.jquery.web.Constants.VIEW; /** * @@ -16,21 +14,9 @@ final class NoSuchResourceException extends WebException { public NoSuchResourceException(String s) { super(s); } - - static NoSuchResourceException noSuchDatabaseException(String resource) { - return noSuchResourceException("database", resource); - } - - static NoSuchResourceException noSuchViewException(String resource) { - return noSuchResourceException(VIEW, resource); - } - static NoSuchResourceException noSuchColumnException(String resource) { - return noSuchResourceException(COLUMN, resource); - } - - static NoSuchResourceException noSuchResourceException(String type, String resource) { - return new NoSuchResourceException(format("%s='%s'", type, resource)); + static NoSuchResourceException w(String type, String resource) { + return new NoSuchResourceException(format("%s: '%s'", type, resource)); } static NoSuchResourceException undeclaredResouceException(String child, String parent) { diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index cb938e23..b46a9778 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -1,5 +1,7 @@ package org.usf.jquery.web; +import java.util.Objects; + import org.usf.jquery.core.DBView; import org.usf.jquery.core.QueryView; import org.usf.jquery.core.TaggableColumn; diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 6aeeaff3..0732330b 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -30,15 +30,16 @@ import static org.usf.jquery.web.Constants.OFFSET; import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.PARTITION; +import static org.usf.jquery.web.Constants.QUERY; import static org.usf.jquery.web.Constants.SELECT; import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; -import static org.usf.jquery.web.EntrySyntaxException.requireEntryTagException; -import static org.usf.jquery.web.EntrySyntaxException.requireNoArgsEntryException; +import static org.usf.jquery.web.EntrySyntaxException.expectedEntryTagException; +import static org.usf.jquery.web.EntrySyntaxException.unexpectedEntryArgsException; import static org.usf.jquery.web.EntrySyntaxException.unexpectedEntryException; +import static org.usf.jquery.web.EntrySyntaxException.unexpectedEntryValueException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; -import static org.usf.jquery.web.NoSuchResourceException.noSuchViewException; import java.util.List; import java.util.Optional; @@ -94,12 +95,13 @@ public RequestEntryChain(String value) { this(value, false); } - public ViewDecorator evalView(ViewDecorator vd) {// [query|view]:alias + // [view|query]:alias + public ViewDecorator evalView(ViewDecorator vd) { try { - var res = currentContext().lookupRegistredView(value); - return res.isPresent() //check args & next only if view exists - ? new ViewDecoratorWrapper(res.get(), requireNoArgs().requireNoNext().requireTag()) - : evalQuery(vd, true).orElseThrow(); + return currentContext().lookupRegisteredView(value) //check args & next only if view exists + .map(v-> new ViewDecoratorWrapper(v, requireNoArgs().requireNoNext().requireTag())) + .or(()-> evalQuery(vd, true)) + .orElseThrow(()-> noSuchResourceException(VIEW, value)); } catch (Exception e) { throw cannotParseEntryException(VIEW, this, e); @@ -108,18 +110,20 @@ public ViewDecorator evalView(ViewDecorator vd) {// [query|view]:alias public ViewDecorator evalQuery(ViewDecorator td) { try { - return evalQuery(td, false).orElseThrow(); + return evalQuery(td, false) + .orElseThrow(()-> unexpectedEntryValueException(this)); } catch (Exception e) { - throw cannotParseEntryException("query", this, e); + throw cannotParseEntryException(QUERY, this, e); } } + //select[.distinct|filter|order|offset|fetch]* Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub context if(SELECT.equals(value)) { var q = new RequestQueryBuilder().columns(toColumnArgs(td, false)); var e = this; - while(e.hasNext()) { //preserve last entry + while(e.hasNext()) { e = e.next; switch(e.value) {//column not allowed case DISTINCT: e.requireNoArgs(); q.distinct(); break; @@ -127,37 +131,21 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub case ORDER: q.orders(e.toOderArgs(td)); break; //not sure case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; - default: throw unexpectedEntryException(e); + default: throw unexpectedEntryValueException(e); } } return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); } return empty(); } - - public Partition evalPartition(ViewDecorator td) { - if(PARTITION.equals(value)) { - var p = new Partition(toColumnArgs(td, true)); - if(hasNext()) { //TD loop - var e = next; - if(ORDER.equals(e.value)) {//column not allowed - p.orders(e.toOderArgs(td)); //not sure - } - else { - throw unexpectedEntryException(e); - } - }//require no tag - return p; - } - throw cannotParseEntryException(PARTITION, this); - } //[view.]joiner public ViewJoin[] evalJoin(ViewDecorator vd) { try { var e = this; - if(hasNext()) { - vd = currentContext().lookupRegistredView(value).orElseThrow(()-> noSuchViewException(value)); + if(hasNext()) { + vd = currentContext().lookupRegisteredView(value) + .orElseThrow(()-> noSuchResourceException(VIEW, value)); e = requireNoArgs().next; //check args only if view exists } var join = vd.joiner(e.value); @@ -165,17 +153,40 @@ public ViewJoin[] evalJoin(ViewDecorator vd) { e.requireNoArgs().requireNoNext(); //check args & next only if joiner exists return requireNonNull(join.build(), format("%s.joiner(%s).build()", vd.identity(), value)); } - throw noSuchResourceException(vd.identity()+".joiner", e.value); + throw noSuchResourceException(vd.identity() + ".joiner", e.value); } catch (Exception e) { throw cannotParseEntryException(JOIN, this, e); } } + public Partition evalPartition(ViewDecorator td) { + try { + if(PARTITION.equals(value)) { + var p = new Partition(toColumnArgs(td, true)); + if(hasNext()) { //TD loop + var e = next; + if(ORDER.equals(e.value)) {//column not allowed + p.orders(e.toOderArgs(td)); //not sure + } + else { + throw unexpectedEntryValueException(e); + } + } + return p; + } + throw unexpectedEntryValueException(this); //unknown + } + catch (Exception e) { + throw cannotParseEntryException(PARTITION, this, e); + } + } + //[view.]column[.operator]* public TaggableColumn evalColumn(ViewDecorator td) { try { - var r = chainColumnOperations(td, false).orElseThrow(); + var r = chainColumnOperations(td, false) + .orElseThrow(()-> noSuchViewResourceException(COLUMN, this)); r.entry.requireNoNext(); //check next only if column exists if(nonNull(r.entry.tag)) { return r.col.as(r.entry.tag); @@ -183,7 +194,7 @@ public TaggableColumn evalColumn(ViewDecorator td) { if(r.col instanceof TaggableColumn col) { return col; } - throw requireEntryTagException(r.entry); + throw expectedEntryTagException(r.entry); } catch (Exception e) { throw cannotParseEntryException(COLUMN, this, e); } @@ -192,7 +203,8 @@ public TaggableColumn evalColumn(ViewDecorator td) { //[view.]column[.operator]*[.order] public DBOrder evalOrder(ViewDecorator td) { try { - var r = chainColumnOperations(td, false).orElseThrow(); + var r = chainColumnOperations(td, false) + .orElseThrow(()-> noSuchViewResourceException(COLUMN, this)); if(r.entry.isLast()) { // default order return r.col.order(); } @@ -201,7 +213,7 @@ public DBOrder evalOrder(ViewDecorator td) { var s = ord.requireNoArgs().requireNoNext().value.toUpperCase(); return r.col.order(Order.valueOf(s)); } - throw noSuchResourceException(ORDER, ord.value); + throw unexpectedEntryValueException(ord); } catch (Exception e) { throw cannotParseEntryException(ORDER, this, e); } @@ -216,12 +228,8 @@ public DBFilter evalFilter(ViewDecorator td, List values) { / try { var res = chainColumnOperations(td, true); if(res.isEmpty()) { //not a column - try { - return viewCriteria(td, values); - } - catch (NoSuchResourceException e) { - - } + return viewCriteria(td, values) + .orElseThrow(()-> noSuchViewResourceException(COLUMN, this)); } var rc = res.get(); if(rc.entry.isLast()) { //no comparator, no criteria @@ -236,11 +244,12 @@ public DBFilter evalFilter(ViewDecorator td, List values) { / } } - DBFilter viewCriteria(ViewDecorator td, List values) { + //[view.]criteria + Optional viewCriteria(ViewDecorator td, List values) { CriteriaBuilder b = null; RequestEntryChain e = null; - if(hasNext()) { - var res = currentContext().lookupRegistredView(value).map(v-> v.criteria(next.value)); + if(hasNext()) { //view.id == column.id + var res = currentContext().lookupRegisteredView(value).map(v-> v.criteria(next.value)); if(res.isPresent()){ b = res.get(); e = next; @@ -252,9 +261,9 @@ DBFilter viewCriteria(ViewDecorator td, List values) { } if(nonNull(b)) { var f = b.build(toStringArray(e.assertOuterParameters(values))); //nonNull !? - return e.chainComparator(td, f); + return Optional.of(e.chainComparator(td, f)); } - throw noSuchResourceException("view.criteria", e.value); + return Optional.empty(); } DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List values) { @@ -287,22 +296,17 @@ private List assertOuterParameters(List va DBFilter chainComparator(ViewDecorator td, DBFilter f) { var e = next; - try { - while(nonNull(e)) { - if(e.value.matches("and|or")) { - var op = LogicalOperator.valueOf(e.value.toUpperCase()); - f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); - } - else { - throw noSuchResourceException("LogicalOperator(and|or)", e.value); - } - e = e.next; + while(nonNull(e)) { + if(e.value.matches("and|or")) { + var op = LogicalOperator.valueOf(e.value.toUpperCase()); + f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); } - return f; - } - catch (Exception ex) { - throw cannotParseEntryException("comparator.chain", e, ex); + else { + throw unexpectedEntryValueException(e); + } + e = e.next; } + return f; } private Optional chainColumnOperations(ViewDecorator td, boolean filter) { @@ -334,15 +338,15 @@ private static DBColumn windowColumn(ViewDecorator vd, DBColumn col) { } private Optional lookupResource(ViewDecorator td) { //do not change priority - if(hasNext()) { //check td.cd first - var rc = currentContext().lookupRegistredView(value) + if(hasNext()) { //view.id == column.id + var rc = currentContext().lookupRegisteredView(value) .flatMap(v-> next.lookupViewResource(v, RequestEntryChain::isWindowFunction)); if(rc.isPresent()) { requireNoArgs(); //view takes no args return rc; } } - return currentContext().lookupDeclaredColumn(value) //declared column + return currentContext().lookupDeclaredColumn(value) //declared column first .map(c-> new ViewResource(null, null, requireNoArgs(), c)) .or(()-> lookupViewResource(td, fn-> true)); //registered column } @@ -355,7 +359,7 @@ private Optional lookupViewResource(ViewDecorator td, Predicate new ViewResource(td, cd, requireNoArgs(), td.column(cd))) .or(()-> lookupOperation(td, null, pre).map(col-> new ViewResource(td, null, this, col))); } @@ -437,14 +441,14 @@ RequestEntryChain requireNoArgs() { if(isNull(args)) { return this; } - throw requireNoArgsEntryException(this); + throw unexpectedEntryArgsException(this); } String requireTag() { if(nonNull(tag)) { return tag; } - throw requireEntryTagException(this); + throw expectedEntryTagException(this); } public boolean isLast() { @@ -485,6 +489,14 @@ private static String[] toStringArray(List entries) { .map(e-> isNull(e.value) ? null : e.toString()) .toArray(String[]::new); } + + static NoSuchResourceException noSuchViewResourceException(String type, RequestEntryChain e) { + return noSuchResourceException(type, + e.hasNext() && currentContext().lookupRegisteredColumn(e.value).isPresent() + ? e.value + "." + e.next.value + : e.value); + } + @AllArgsConstructor static final class ViewResource { diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index 8cd3cc7d..a0460dac 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -4,10 +4,12 @@ import static org.usf.jquery.core.Utils.isPresent; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; +import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.context; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.ContextManager.releaseContext; -import static org.usf.jquery.web.NoSuchResourceException.noSuchViewException; +import static org.usf.jquery.web.NoSuchResourceException.*; +import static org.usf.jquery.web.ResourceAccessException.accessDeniedException; import java.util.LinkedHashMap; import java.util.Map; @@ -43,14 +45,14 @@ public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull : context(ant.database()); try { var req = ctx - .lookupRegistredView(ant.view()) - .orElseThrow(()-> noSuchViewException(ant.view())) + .lookupRegisteredView(ant.view()) + .orElseThrow(()-> noSuchResourceException(VIEW, ant.view())) .query(parameterMap); //may edit map if(!ant.aggregationOnly() || req.isAggregation()) { log.trace("request parsed in {} ms", currentTimeMillis() - t); return req; } - throw new IllegalDataAccessException("non aggregation query"); + throw accessDeniedException("non-aggregate query"); } finally { releaseContext(); diff --git a/src/main/java/org/usf/jquery/web/ResourceAccessException.java b/src/main/java/org/usf/jquery/web/ResourceAccessException.java new file mode 100644 index 00000000..86a28496 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/ResourceAccessException.java @@ -0,0 +1,23 @@ +package org.usf.jquery.web; + +/** + * + * @author u$f + * + */ +@SuppressWarnings("serial") +public class ResourceAccessException extends WebException { //read & write access + + public ResourceAccessException(String message) { + super(message); + } + + public static ResourceAccessException resourceAlreadyExistsException(String name, String value) { + return new ResourceAccessException(name + " already exists : " + value); + } + + + public static ResourceAccessException accessDeniedException(String reason) { + return new ResourceAccessException("access denied : " + reason); + } +} From 9158f36f6ab45ec9972690bc43c36115532dc7fd Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 7 Aug 2024 22:56:55 +0200 Subject: [PATCH 113/298] edit --- .../java/org/usf/jquery/core/Partition.java | 10 +-- .../org/usf/jquery/core/WhenExpression.java | 1 - .../usf/jquery/web/ContextEnvironment.java | 4 +- .../org/usf/jquery/web/ContextManager.java | 2 +- .../jquery/web/NoSuchResourceException.java | 2 +- .../org/usf/jquery/web/QueryDecorator.java | 2 - .../org/usf/jquery/web/RequestEntryChain.java | 83 ++++++++++--------- .../jquery/web/RequestQueryParamResolver.java | 2 +- .../jquery/web/ResourceAccessException.java | 1 - .../org/usf/jquery/web/YearTableMetadata.java | 2 +- .../org/usf/jquery/web/YearViewDecorator.java | 5 +- 11 files changed, 50 insertions(+), 64 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index 2f86538e..411e0f32 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -18,15 +18,7 @@ public final class Partition implements DBObject, Groupable { private final DBColumn[] columns; - private DBOrder[] orders; - - public Partition() { - this(null); - } - - public void orders(DBOrder[] orders) { - this.orders = orders; - } + private final DBOrder[] orders; @Override public String sql(QueryParameterBuilder builder, Object[] args) { diff --git a/src/main/java/org/usf/jquery/core/WhenExpression.java b/src/main/java/org/usf/jquery/core/WhenExpression.java index fcb62a74..252fd185 100644 --- a/src/main/java/org/usf/jquery/core/WhenExpression.java +++ b/src/main/java/org/usf/jquery/core/WhenExpression.java @@ -4,7 +4,6 @@ import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.Validation.requireNoArgs; -import static org.usf.jquery.core.Nested.*; import org.usf.jquery.core.JavaType.Typed; diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index f8432f2f..d0dbc038 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -8,9 +8,9 @@ import static java.util.stream.Collectors.toUnmodifiableMap; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNonEmpty; -import static org.usf.jquery.web.ResourceAccessException.resourceAlreadyExistsException; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.VIEW; +import static org.usf.jquery.web.ResourceAccessException.resourceAlreadyExistsException; import java.sql.SQLException; import java.util.Collection; @@ -89,7 +89,7 @@ void declareView(ViewDecorator view) { //additional request views TaggableColumn declareColumn(TaggableColumn col) { if(views.containsKey(col.tagname())) { //cannot overwrite registered views - throw resourceAlreadyExistsException(COLUMN, col.tagname()); + throw resourceAlreadyExistsException(VIEW, col.tagname()); } //but can overwrite registered columns return declaredColumns.compute(col.tagname(), (k,v)-> { if(isNull(v)){ diff --git a/src/main/java/org/usf/jquery/web/ContextManager.java b/src/main/java/org/usf/jquery/web/ContextManager.java index 24e0f420..43d094ab 100644 --- a/src/main/java/org/usf/jquery/web/ContextManager.java +++ b/src/main/java/org/usf/jquery/web/ContextManager.java @@ -2,8 +2,8 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; +import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.ResourceAccessException.resourceAlreadyExistsException; -import static org.usf.jquery.web.NoSuchResourceException.*; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java index 39ee70bd..8b15da62 100644 --- a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java +++ b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java @@ -15,7 +15,7 @@ public NoSuchResourceException(String s) { super(s); } - static NoSuchResourceException w(String type, String resource) { + static NoSuchResourceException noSuchResourceException(String type, String resource) { return new NoSuchResourceException(format("%s: '%s'", type, resource)); } diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index b46a9778..cb938e23 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -1,7 +1,5 @@ package org.usf.jquery.web; -import java.util.Objects; - import org.usf.jquery.core.DBView; import org.usf.jquery.core.QueryView; import org.usf.jquery.core.TaggableColumn; diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 0732330b..e6a7658c 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -1,7 +1,7 @@ package org.usf.jquery.web; -import static java.lang.String.format; import static java.lang.reflect.Array.newInstance; +import static java.util.Collections.addAll; import static java.util.Collections.emptyList; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; @@ -41,6 +41,7 @@ import static org.usf.jquery.web.EntrySyntaxException.unexpectedEntryValueException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.function.IntFunction; @@ -95,7 +96,7 @@ public RequestEntryChain(String value) { this(value, false); } - // [view|query]:alias + // [view|query]:tag public ViewDecorator evalView(ViewDecorator vd) { try { return currentContext().lookupRegisteredView(value) //check args & next only if view exists @@ -151,7 +152,7 @@ public ViewJoin[] evalJoin(ViewDecorator vd) { var join = vd.joiner(e.value); if(nonNull(join)) { e.requireNoArgs().requireNoNext(); //check args & next only if joiner exists - return requireNonNull(join.build(), format("%s.joiner(%s).build()", vd.identity(), value)); + return requireNonNull(join.build(), "view.joiner: " + e); } throw noSuchResourceException(vd.identity() + ".joiner", e.value); } @@ -160,22 +161,23 @@ public ViewJoin[] evalJoin(ViewDecorator vd) { } } + //[partition.order]* public Partition evalPartition(ViewDecorator td) { + List cols = new ArrayList<>(); + List ords = new ArrayList<>(); try { - if(PARTITION.equals(value)) { - var p = new Partition(toColumnArgs(td, true)); - if(hasNext()) { //TD loop - var e = next; - if(ORDER.equals(e.value)) {//column not allowed - p.orders(e.toOderArgs(td)); //not sure - } - else { - throw unexpectedEntryValueException(e); - } + var e = this; + do { + switch (e.value) { + case PARTITION: addAll(cols, e.toColumnArgs(td, true)); break; + case ORDER: addAll(ords, e.toOderArgs(td)); break; + default: throw unexpectedEntryValueException(e); } - return p; - } - throw unexpectedEntryValueException(this); //unknown + e = e.next; + } while(nonNull(e)); + return new Partition( + cols.toArray(DBColumn[]::new), + ords.toArray(DBOrder[]::new)); } catch (Exception e) { throw cannotParseEntryException(PARTITION, this, e); @@ -186,7 +188,7 @@ public Partition evalPartition(ViewDecorator td) { public TaggableColumn evalColumn(ViewDecorator td) { try { var r = chainColumnOperations(td, false) - .orElseThrow(()-> noSuchViewResourceException(COLUMN, this)); + .orElseThrow(()-> noSuchViewColumnException(this)); r.entry.requireNoNext(); //check next only if column exists if(nonNull(r.entry.tag)) { return r.col.as(r.entry.tag); @@ -204,12 +206,12 @@ public TaggableColumn evalColumn(ViewDecorator td) { public DBOrder evalOrder(ViewDecorator td) { try { var r = chainColumnOperations(td, false) - .orElseThrow(()-> noSuchViewResourceException(COLUMN, this)); + .orElseThrow(()-> noSuchViewColumnException(this)); if(r.entry.isLast()) { // default order return r.col.order(); } var ord = r.entry.next; - if(ord.value.matches("asc|desc")) {//check args & next only if order exists + if(ord.value.matches("asc|desc")) { //check args & next only if order exists var s = ord.requireNoArgs().requireNoNext().value.toUpperCase(); return r.col.order(Order.valueOf(s)); } @@ -224,20 +226,20 @@ public DBFilter evalFilter(ViewDecorator td) { } //[view.]criteria | [view.]column.criteria | [view.]column[.operator]*[.comparator] - public DBFilter evalFilter(ViewDecorator td, List values) { //supply values + public DBFilter evalFilter(ViewDecorator vd, List values) { //supply values try { - var res = chainColumnOperations(td, true); + var res = chainColumnOperations(vd, true); if(res.isEmpty()) { //not a column - return viewCriteria(td, values) - .orElseThrow(()-> noSuchViewResourceException(COLUMN, this)); + return viewCriteria(vd, values) + .orElseThrow(()-> noSuchViewColumnException(this)); } var rc = res.get(); if(rc.entry.isLast()) { //no comparator, no criteria var fn = requireNonNull(values).size() == 1 ? eq() : in(); //non empty var e = new RequestEntryChain(null, false, null, values, null); - return fn.args(e.toArgs(rc.td, rc.col, fn.getParameterSet())); //no chain + return fn.args(e.toArgs(vd, rc.col, fn.getParameterSet())); //no chain } - return rc.entry.next.columnCriteria(rc.td, rc.cd, rc.col, values); + return rc.entry.next.columnCriteria(vd, rc.cd, rc.col, values); } catch(Exception e) { throw cannotParseEntryException(FILTER, this, e); @@ -260,7 +262,8 @@ Optional viewCriteria(ViewDecorator td, List values e = this; } if(nonNull(b)) { - var f = b.build(toStringArray(e.assertOuterParameters(values))); //nonNull !? + var strArgs = toStringArray(e.assertOuterParameters(values)); + var f = requireNonNull(b.build(strArgs), "view.criteria: " + e); return Optional.of(e.chainComparator(td, f)); } return Optional.empty(); @@ -274,9 +277,10 @@ DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List return chainComparator(vc, fn.args(cp.toArgs(vc, col, fn.getParameterSet()))); } if(nonNull(cd)) { // no operation - var c = cd.criteria(value); //criteria lookup + var c = cd.criteria(value); if(nonNull(c)) { - var ex = c.build(toStringArray(assertOuterParameters(values))); //nonNull !? + var strArgs = toStringArray(assertOuterParameters(values)); + var ex = requireNonNull(c.build(strArgs), "column.criteria: " + this); return chainComparator(vc, col.filter(ex)); } throw noSuchResourceException("comparator|criteria", value); @@ -300,11 +304,11 @@ DBFilter chainComparator(ViewDecorator td, DBFilter f) { if(e.value.matches("and|or")) { var op = LogicalOperator.valueOf(e.value.toUpperCase()); f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); + e = e.next; } else { throw unexpectedEntryValueException(e); } - e = e.next; } return f; } @@ -320,7 +324,7 @@ private Optional chainColumnOperations(ViewDecorator td, boolean f r.cd = null; r.entry = e; r.col = filter && "over".equals(e.value) - ? windowColumn(r.td, o.get()) + ? windowColumn(r.vd, o.get()) : o.get(); e = e.next; } @@ -329,7 +333,7 @@ private Optional chainColumnOperations(ViewDecorator td, boolean f } private static DBColumn windowColumn(ViewDecorator vd, DBColumn col) { - var v = vd.view(); + var v = requireNonNull(vd, "column view").view(); //Declared column var tag = "over_" + vd.identity() + "_" + col.hashCode(); //over_view_hash currentContext().overView(v, ()-> new RequestQueryBuilder() .columns(allColumns(v)).asView()) @@ -353,15 +357,12 @@ private Optional lookupResource(ViewDecorator td) { //do not chang private Optional lookupViewResource(ViewDecorator td, Predicate pre) { if(td instanceof QueryDecorator qd) { //query column - var res = ofNullable(qd.column(value)) - .map(c-> new ViewResource(td, null, requireNoArgs(), c)); - if(res.isPresent()) { - return res; - } + return ofNullable(qd.column(value)) + .map(c-> new ViewResource(td, null, requireNoArgs(), c)); //no column decorator } return currentContext().lookupRegisteredColumn(value) .map(cd-> new ViewResource(td, cd, requireNoArgs(), td.column(cd))) - .or(()-> lookupOperation(td, null, pre).map(col-> new ViewResource(td, null, this, col))); + .or(()-> lookupOperation(td, null, pre).map(col-> new ViewResource(td, null, this, col))); //no column decorator } private Optional lookupOperation(ViewDecorator td, DBColumn col, Predicate opr) { @@ -490,8 +491,8 @@ private static String[] toStringArray(List entries) { .toArray(String[]::new); } - static NoSuchResourceException noSuchViewResourceException(String type, RequestEntryChain e) { - return noSuchResourceException(type, + static NoSuchResourceException noSuchViewColumnException(RequestEntryChain e) { + return noSuchResourceException(COLUMN, e.hasNext() && currentContext().lookupRegisteredColumn(e.value).isPresent() ? e.value + "." + e.next.value : e.value); @@ -501,7 +502,7 @@ static NoSuchResourceException noSuchViewResourceException(String type, RequestE @AllArgsConstructor static final class ViewResource { - private final ViewDecorator td; //optional + private ViewDecorator vd; //optional private ColumnDecorator cd; //optional private RequestEntryChain entry; private DBColumn col; @@ -509,7 +510,7 @@ static final class ViewResource { @Override public String toString() { - return td + "." + cd + " => " + entry.toString(); + return vd + "." + cd + " => " + entry.toString(); } } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index a0460dac..9c4b6258 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -8,7 +8,7 @@ import static org.usf.jquery.web.ContextManager.context; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.ContextManager.releaseContext; -import static org.usf.jquery.web.NoSuchResourceException.*; +import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.ResourceAccessException.accessDeniedException; import java.util.LinkedHashMap; diff --git a/src/main/java/org/usf/jquery/web/ResourceAccessException.java b/src/main/java/org/usf/jquery/web/ResourceAccessException.java index 86a28496..9a947bbf 100644 --- a/src/main/java/org/usf/jquery/web/ResourceAccessException.java +++ b/src/main/java/org/usf/jquery/web/ResourceAccessException.java @@ -16,7 +16,6 @@ public static ResourceAccessException resourceAlreadyExistsException(String name return new ResourceAccessException(name + " already exists : " + value); } - public static ResourceAccessException accessDeniedException(String reason) { return new ResourceAccessException("access denied : " + reason); } diff --git a/src/main/java/org/usf/jquery/web/YearTableMetadata.java b/src/main/java/org/usf/jquery/web/YearTableMetadata.java index db483fcc..ed7dc058 100644 --- a/src/main/java/org/usf/jquery/web/YearTableMetadata.java +++ b/src/main/java/org/usf/jquery/web/YearTableMetadata.java @@ -29,10 +29,10 @@ import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; -import java.util.stream.Stream; import java.util.NoSuchElementException; import java.util.Optional; import java.util.Set; +import java.util.stream.Stream; import org.usf.jquery.core.DBQuery; import org.usf.jquery.core.DBView; diff --git a/src/main/java/org/usf/jquery/web/YearViewDecorator.java b/src/main/java/org/usf/jquery/web/YearViewDecorator.java index c70ba134..d0ec8de6 100644 --- a/src/main/java/org/usf/jquery/web/YearViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearViewDecorator.java @@ -11,11 +11,9 @@ import static org.usf.jquery.web.Constants.REVISION; import static org.usf.jquery.web.Constants.REVISION_MODE; import static org.usf.jquery.web.ContextManager.currentContext; -import static org.usf.jquery.web.JQueryContext.database; -import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; +import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.RevisionIterator.iterator; -import static org.usf.jquery.web.RevisionIterator.monthFilter; import static org.usf.jquery.web.RevisionIterator.yearColumn; import static org.usf.jquery.web.RevisionIterator.yearTable; import static org.usf.jquery.web.ViewDecorator.flatParameters; @@ -25,7 +23,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.function.UnaryOperator; import java.util.stream.Stream; From d89723b982061759bdf1ac4441ef2b8553393948 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 7 Aug 2024 23:05:33 +0200 Subject: [PATCH 114/298] edit --- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index e6a7658c..3b5562c6 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -476,9 +476,8 @@ public String toString() { } private static boolean isWindowFunction(TypedOperator op) { - var fn = op.unwrap(); - return fn instanceof WindowFunction || - fn instanceof AggregateFunction; + return isCountFunction(op) + || op.unwrap() instanceof WindowFunction; } private static boolean isCountFunction(TypedOperator fn) { @@ -492,8 +491,8 @@ private static String[] toStringArray(List entries) { } static NoSuchResourceException noSuchViewColumnException(RequestEntryChain e) { - return noSuchResourceException(COLUMN, - e.hasNext() && currentContext().lookupRegisteredColumn(e.value).isPresent() + return noSuchResourceException(COLUMN, e.hasNext() + && currentContext().lookupRegisteredColumn(e.value).isPresent() ? e.value + "." + e.next.value : e.value); } From c95525fabcd8e6d8436677f31cd5d95dcea28943 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 9 Aug 2024 14:20:45 +0200 Subject: [PATCH 115/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 10 ++--- src/main/java/org/usf/jquery/core/DBView.java | 10 ++++- .../java/org/usf/jquery/core/NamedColumn.java | 5 --- .../java/org/usf/jquery/core/Operator.java | 8 ++-- .../jquery/core/QueryParameterBuilder.java | 33 ++++------------ .../usf/jquery/core/RequestQueryBuilder.java | 20 +--------- .../org/usf/jquery/core/SqlStringBuilder.java | 14 +------ .../java/org/usf/jquery/core/TableView.java | 14 +++++-- .../org/usf/jquery/web/ColumnDecorator.java | 9 ++--- .../org/usf/jquery/web/ColumnMetadata.java | 6 ++- .../usf/jquery/web/ContextEnvironment.java | 8 ++-- .../org/usf/jquery/web/ContextManager.java | 3 +- .../usf/jquery/web/JDBCArgumentParser.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 13 ++----- .../org/usf/jquery/web/ViewDecorator.java | 38 ++++++++++--------- .../usf/jquery/web/ViewDecoratorWrapper.java | 30 +++++++++++++-- .../java/org/usf/jquery/web/ViewMetadata.java | 6 +-- 17 files changed, 108 insertions(+), 121 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index a1850975..09652da1 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -146,11 +146,11 @@ static DBColumn column(@NonNull String value) { } static TaggableColumn allColumns(@NonNull DBView view) { - return column(view, "*").as(null); //no tag - } - - static DBColumn column(@NonNull DBView view, @NonNull String value) { - return b-> member(b.view(view), value); + DBColumn c = b-> { + b.view(view); + return "*"; //avoid view.* as "" + }; + return c.as(null); } static DBColumn constant(Object value) { diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 803af0f8..0941bf13 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -1,6 +1,8 @@ package org.usf.jquery.core; +import static java.util.Objects.isNull; import static org.usf.jquery.core.SqlStringBuilder.SPACE; +import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; /** @@ -17,9 +19,13 @@ default String sql(QueryParameterBuilder builder, Object[] args) { return sql(builder); } + String sql(QueryParameterBuilder builder); + + default NamedView as(String tag) { + return new NamedView(this, isNull(tag) ? null : requireLegalVariable(tag)); + } + default String sqlWithTag(QueryParameterBuilder builder) { return sql(builder) + SPACE + builder.view(this); } - - String sql(QueryParameterBuilder builder); } diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index 42d14cbb..ad350092 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -28,9 +28,4 @@ public String tagname() { public NamedColumn as(String name) { // map return Objects.equals(name, tag) ? this : new NamedColumn(column, name); } - - @Override - public String toString() { - return column.toString(); - } } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 6b2ca408..ace68946 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -56,19 +56,19 @@ default OperationColumn args(JDBCType type, Object... args) { //Arithmetic operations static TypedOperator plus() { - return new TypedOperator(DOUBLE, operator("+"), required(DOUBLE), required(DOUBLE)); + return new TypedOperator(DOUBLE, operator("+"), required(), required(firstArgJdbcType())); } static TypedOperator minus() { - return new TypedOperator(DOUBLE, operator("-"), required(DOUBLE), required(DOUBLE)); //date|datetime + return new TypedOperator(DOUBLE, operator("-"), required(), required(firstArgJdbcType())); //date|datetime } static TypedOperator multiply() { - return new TypedOperator(DOUBLE, operator("*"), required(DOUBLE), required(DOUBLE)); + return new TypedOperator(DOUBLE, operator("*"), required(), required(firstArgJdbcType())); } static TypedOperator divide() { - return new TypedOperator(DOUBLE, operator("/"), required(DOUBLE), required(DOUBLE)); + return new TypedOperator(DOUBLE, operator("/"), required(), required(firstArgJdbcType())); } //numeric functions diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 16fac279..772b3c84 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -40,9 +40,7 @@ public final class QueryParameterBuilder { private final List views; //indexed private final Map overView = new HashMap<>(); - - private Integer index; - + public List views(){ return views; } @@ -118,22 +116,11 @@ public String appendLiteral(Object o) { //TD : stringify value using db default } private String appendArg(JDBCType type, Object o) { - if(isNull(index)) { - argTypes.add(type); - args.add(o); - } - else { - argTypes.add(index, type); - args.add(index, o); - index++; - } + argTypes.add(type); + args.add(o); return P_ARG; } - public int argCount() { - return args.size(); - } - public Object[] args() { return dynamic() ? args.toArray() : null; } @@ -161,22 +148,18 @@ public static String formatValue(Object o) { } public QueryParameterBuilder withValue() { - return new QueryParameterBuilder(schema, vPrefix, null, null, views, index); + return new QueryParameterBuilder(schema, vPrefix, null, null, views); } public QueryParameterBuilder subQuery() { - return new QueryParameterBuilder(schema, isNull(vPrefix) ? null : vPrefix + "_s", args, argTypes, new ArrayList<>(), index); + return new QueryParameterBuilder(schema, isNull(vPrefix) ? null : vPrefix + "_s", args, argTypes, new ArrayList<>()); } public static QueryParameterBuilder addWithValue() { - return new QueryParameterBuilder(null, null, null, null, null, null); //no args + return new QueryParameterBuilder(null, null, null, null, null); //no args } - public static QueryParameterBuilder parametrized(List views) { - return parametrized(null, views); - } - - public static QueryParameterBuilder parametrized(String schema, List views) { - return new QueryParameterBuilder(schema, "v", new ArrayList<>(), new ArrayList<>(), views, null); + public static QueryParameterBuilder parametrized(String schema) { + return new QueryParameterBuilder(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); } } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 3d97deba..5efa966a 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -36,7 +36,6 @@ public class RequestQueryBuilder { private final List columns = new LinkedList<>(); private final List filters = new LinkedList<>(); //WHERE & HAVING - private final List views = new LinkedList<>(); private final List orders = new LinkedList<>(); private final List joins = new LinkedList<>(); private Iterator it; @@ -49,11 +48,6 @@ public RequestQueryBuilder distinct() { return this; } - public RequestQueryBuilder views(@NonNull DBView... views) { - Stream.of(views).forEach(this.views::add); - return this; - } - public RequestQueryBuilder columns(@NonNull TaggableColumn... columns) { addAll(this.columns, columns); return this; @@ -111,7 +105,7 @@ public RequestQuery build(String schema) { // requireNonEmpty(tables); requireNonEmpty(columns, "columns"); var bg = currentTimeMillis(); - var pb = parametrized(schema, views); + var pb = parametrized(schema); var sb = new SqlStringBuilder(1000); //avg if(isNull(it)) { build(sb, pb); @@ -124,18 +118,13 @@ public RequestQuery build(String schema) { } public final void build(SqlStringBuilder sb, QueryParameterBuilder pb){ - views.forEach(pb::view); select(sb, pb); - var queryIdx = sb.sb.length(); //pos mark - var argsIdx = pb.argCount(); + from(sb, pb); where(sb, pb); groupBy(sb, pb); having(sb, pb); orderBy(sb, pb); fetch(sb); - sb.setOffset(queryIdx); - pb.setIndex(argsIdx); - from(sb, pb); //declare all view before FROM) join(sb, pb); } @@ -157,11 +146,6 @@ void select(SqlStringBuilder sb, QueryParameterBuilder pb){ void from(SqlStringBuilder sb, QueryParameterBuilder pb) { var vList = pb.views(); - if(!joins.isEmpty()) { - vList = vList.stream() - .filter(v-> joins.stream().noneMatch(j-> j.id().equals(v.id()))) - .toList(); - } if(!vList.isEmpty()) { sb.append(" FROM ") .appendEach(vList, SCOMA, o-> o.sqlWithTag(pb)); diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index ce34e545..95aad3e9 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -9,14 +9,11 @@ import java.util.function.Function; import java.util.function.Supplier; -import lombok.Setter; - /** * * @author u$f * */ -@Setter public final class SqlStringBuilder { static final String EMPTY = ""; @@ -26,8 +23,7 @@ public final class SqlStringBuilder { static final String DQUOT = "\""; static final String SCOMA = COMA + SPACE; - final StringBuilder sb; - Integer offset; + private final StringBuilder sb; public SqlStringBuilder(int capacity) { this.sb = new StringBuilder(capacity); @@ -85,13 +81,7 @@ public SqlStringBuilder forEach(Iterator it, String separator, Consumer lookupDeclaredColumn(String name) { } public DBView getView(ViewDecorator vd, Supplier supp) { - return ofNullable(viewCache.get(vd)).orElseGet(supp); + return viewCache.computeIfAbsent(vd, k-> supp.get()); } void declareView(ViewDecorator view) { //additional request views @@ -126,17 +126,17 @@ ContextEnvironment bind() { return this; } - public static final ContextEnvironment of(DatabaseDecorator database, + public static ContextEnvironment of(DatabaseDecorator database, Collection views, Collection columns) { return of(database, views, columns, null, null); } - public static final ContextEnvironment of(DatabaseDecorator database, + public static ContextEnvironment of(DatabaseDecorator database, Collection views, Collection columns, DataSource ds) { return of(database, views, columns, ds, null); } - public static final ContextEnvironment of(DatabaseDecorator database, + public static ContextEnvironment of(DatabaseDecorator database, Collection views, Collection columns, DataSource ds, String schema) { requireLegalVariable(database.identity()); return new ContextEnvironment( diff --git a/src/main/java/org/usf/jquery/web/ContextManager.java b/src/main/java/org/usf/jquery/web/ContextManager.java index 43d094ab..7a1d0b0c 100644 --- a/src/main/java/org/usf/jquery/web/ContextManager.java +++ b/src/main/java/org/usf/jquery/web/ContextManager.java @@ -28,10 +28,11 @@ public final class ContextManager { public static void register(ContextEnvironment config) { CONTEXTS.compute(config.getDatabase().identity(), (id,dm)-> { if(isNull(dm)) { - return config.bind(); + return config; } throw resourceAlreadyExistsException("context", id); }); + config.bind(); // outer bind } public static ContextEnvironment currentContext() { diff --git a/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java index 6ae42144..7506a729 100644 --- a/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java +++ b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java @@ -22,7 +22,7 @@ default Object parseValue(String v) { return nativeParse(v); } catch(Exception e) { - throw cannotParseEntryException(toString(), v, e); + throw cannotParseEntryException("", null, e); //TODO finish } } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 3b5562c6..e1dedeab 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -47,7 +47,6 @@ import java.util.function.IntFunction; import java.util.function.Predicate; -import org.usf.jquery.core.AggregateFunction; import org.usf.jquery.core.BadArgumentException; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; @@ -191,7 +190,7 @@ public TaggableColumn evalColumn(ViewDecorator td) { .orElseThrow(()-> noSuchViewColumnException(this)); r.entry.requireNoNext(); //check next only if column exists if(nonNull(r.entry.tag)) { - return r.col.as(r.entry.tag); + return currentContext().declareColumn(r.col.as(r.entry.tag)); } if(r.col instanceof TaggableColumn col) { return col; @@ -365,16 +364,13 @@ private Optional lookupViewResource(ViewDecorator td, Predicate lookupOperation(td, null, pre).map(col-> new ViewResource(td, null, this, col))); //no column decorator } - private Optional lookupOperation(ViewDecorator td, DBColumn col, Predicate opr) { + private Optional lookupOperation(ViewDecorator vd, DBColumn col, Predicate opr) { return lookupOperator(value).filter(opr).map(fn-> { var c = col; if(isNull(c) && isEmpty(args) && isCountFunction(fn)) { - c = b-> { - b.view(td.view()); // declare view - return "*"; - }; + c = allColumns(vd.view()); } - return fn.args(toArgs(td, c, fn.getParameterSet())); + return fn.args(toArgs(vd, c, fn.getParameterSet())); }); } @@ -497,7 +493,6 @@ && currentContext().lookupRegisteredColumn(e.value).isPresent() : e.value); } - @AllArgsConstructor static final class ViewResource { diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 6cbc5065..d9e36896 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -22,7 +22,7 @@ import static org.usf.jquery.web.RequestParser.parseEntry; import java.util.Map; -import java.util.function.Function; +import java.util.Map.Entry; import java.util.stream.Stream; import org.usf.jquery.core.DBFilter; @@ -57,11 +57,11 @@ default JoinBuilder joiner(String name) { return null; //no builder by default } - default DBView view() { //final + default DBView view() {//final return currentContext().getView(this, builder()::build); } - default TaggableColumn column(@NonNull ColumnDecorator cd) { + default TaggableColumn column(@NonNull ColumnDecorator cd) {//final var meta = metadata().columnMetadata(cd); if(nonNull(meta)) { return new ViewColumn(view(), meta.getName(), cd.reference(this), meta.getType()); @@ -73,24 +73,24 @@ default TaggableColumn column(@NonNull ColumnDecorator cd) { throw undeclaredResouceException(cd.identity(), identity()); } - private DBView buildView() { + private TableView buildView() { var tn = currentContext().getDatabase().viewName(this); if(nonNull(tn)){ var idx = tn.indexOf('.'); return idx == -1 - ? new TableView(requireLegalVariable(tn)) + ? new TableView(null, requireLegalVariable(tn), identity()) : new TableView(requireLegalVariable(tn.substring(0, idx)), - requireLegalVariable(tn.substring(idx, tn.length()))); + requireLegalVariable(tn.substring(idx, tn.length())), identity()); } throw undeclaredResouceException(identity(), currentContext().getDatabase().identity()); } default ViewMetadata metadata() { return currentContext().computeTableMetadata(this, cols-> new ViewMetadata(view(), - cols.stream().mapMulti((cd, acc)-> ofNullable(columnName(cd)) - .map(cn-> columnMetadata(cn, cd.type(this))) + cols.stream().>mapMulti((cd, acc)-> ofNullable(columnName(cd)) + .map(cn-> Map.entry(cd.identity(), columnMetadata(cn, cd.type(this)))) .ifPresent(acc)) //view column only - .collect(toUnmodifiableMap(ColumnMetadata::getName, Function.identity())))); + .collect(toUnmodifiableMap(Entry::getKey, Entry::getValue)))); } default RequestQueryBuilder query(Map parameterMap) { @@ -105,7 +105,7 @@ default RequestQueryBuilder query(Map parameterMap) { default void parseViews(RequestQueryBuilder query, Map parameters) { if(parameters.containsKey(VIEW)) { - Stream.of(parameters.get(VIEW)) + Stream.of(parameters.remove(VIEW)) .flatMap(c-> parseEntries(c).stream()) .forEach(e-> currentContext().declareView(e.evalView(this))); } @@ -115,19 +115,21 @@ default void parseColumns(RequestQueryBuilder query, Map param if(parameters.containsKey(COLUMN_DISTINCT) && parameters.containsKey(COLUMN)) { throw new IllegalArgumentException("cannot use both parameters " + quote(COLUMN_DISTINCT) + " and " + quote(COLUMN)); } - var cols = parameters.containsKey(COLUMN_DISTINCT) - ? parameters.get(COLUMN_DISTINCT) - : parameters.get(COLUMN); //can be combined in PG (distinct on) + String[] cols; + if(parameters.containsKey(COLUMN_DISTINCT)) { + cols = parameters.remove(COLUMN_DISTINCT); + query.distinct(); + } + else { + cols = parameters.remove(COLUMN); + } if(isEmpty(cols)) { throw missingParameterException(COLUMN, COLUMN_DISTINCT); } - if(parameters.containsKey(COLUMN_DISTINCT)){ - query.distinct(); - } Stream.of(cols) .flatMap(v-> parseEntries(v).stream()) .map(e-> e.evalColumn(this)) - .forEach(c-> query.columns(currentContext().declareColumn(c))); + .forEach(query::columns); } default void parseFilters(RequestQueryBuilder query, Map parameters) { @@ -142,7 +144,7 @@ default void parseFilters(RequestQueryBuilder query, Map param default void parseOrders(RequestQueryBuilder query, Map parameters) { if(parameters.containsKey(ORDER)) { - Stream.of(parameters.get(ORDER)) + Stream.of(parameters.remove(ORDER)) .flatMap(c-> parseEntries(c).stream()) .forEach(e-> query.orders(e.evalOrder(this))); } diff --git a/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java b/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java index fd8560da..99df1f99 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java +++ b/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java @@ -1,7 +1,8 @@ package org.usf.jquery.web; +import org.usf.jquery.core.DBFilter; + import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; /** * @@ -10,13 +11,34 @@ */ @RequiredArgsConstructor final class ViewDecoratorWrapper implements ViewDecorator { - - @Delegate - private final ViewDecorator view; + + //do no use @Delegate + private final ViewDecorator view; private final String id; @Override public String identity() { return id; } + + @Override + public String columnName(ColumnDecorator cd) { + return view.columnName(cd); + } + + @Override + public ViewBuilder builder() { + return ()-> view.builder().build()::sql; //different reference + } + + @Override + public CriteriaBuilder criteria(String name) { + return view.criteria(name); + } + + @Override + public JoinBuilder joiner(String name) { + return view.joiner(name); + } + } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index 868cef13..7f95b0bf 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -59,7 +59,7 @@ final ViewMetadata fetch(DatabaseMetaData metadata, String schema) throws SQLExc fetch(metadata, tab, schema); } else if(view instanceof DBQuery query) { - fetch(metadata, query); + fetch(metadata, query, schema); } else { throw new UnsupportedOperationException("unsupported view type " + view); @@ -94,8 +94,8 @@ void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLE } } - void fetch(DatabaseMetaData metadata, DBQuery qr) throws SQLException { - var query = qr.sql(parametrized(new ArrayList<>())); + void fetch(DatabaseMetaData metadata, DBQuery qr, String schema) throws SQLException { + var query = qr.sql(parametrized(schema)); try(var ps = metadata.getConnection().prepareStatement("SELECT * FROM(" + query + ") WHERE 1=0"); var rs = ps.executeQuery()){ var db = new HashMap<>(columns); From ed74c85dc1ab4be1c00c20e39a0f95a15f5ce74c Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 12 Aug 2024 20:59:54 +0200 Subject: [PATCH 116/298] edit --- .../org/usf/jquery/core/ArithmeticOperator.java | 2 +- src/main/java/org/usf/jquery/core/DBColumn.java | 1 - .../java/org/usf/jquery/core/DBProcessor.java | 2 +- src/main/java/org/usf/jquery/core/DBView.java | 6 ------ src/main/java/org/usf/jquery/core/Database.java | 2 +- src/main/java/org/usf/jquery/core/Operator.java | 10 +++++----- .../org/usf/jquery/core/RequestQueryBuilder.java | 1 - src/main/java/org/usf/jquery/core/TableView.java | 7 +------ .../java/org/usf/jquery/core/TypedOperator.java | 15 ++------------- .../org/usf/jquery/web/ContextEnvironment.java | 5 +++-- .../org/usf/jquery/web/EntrySyntaxException.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 2 +- .../usf/jquery/web/RequestQueryParamResolver.java | 10 ++++++---- .../java/org/usf/jquery/web/ViewDecorator.java | 2 +- 14 files changed, 23 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java index 3a094e70..70335657 100644 --- a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java +++ b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java @@ -13,6 +13,6 @@ interface ArithmeticOperator extends Operator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(2, args, ArithmeticOperator.class::getSimpleName); - return builder.appendLiteral(args[0]) + id() + builder.appendLiteral(args[1]); + return "(" + builder.appendLiteral(args[0]) + id() + builder.appendLiteral(args[1]) + ")"; } } diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 09652da1..ce7c8aa0 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -1,7 +1,6 @@ package org.usf.jquery.core; import static org.usf.jquery.core.QueryParameterBuilder.formatValue; -import static org.usf.jquery.core.SqlStringBuilder.member; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; diff --git a/src/main/java/org/usf/jquery/core/DBProcessor.java b/src/main/java/org/usf/jquery/core/DBProcessor.java index 40730db7..1c54f0c5 100644 --- a/src/main/java/org/usf/jquery/core/DBProcessor.java +++ b/src/main/java/org/usf/jquery/core/DBProcessor.java @@ -15,7 +15,7 @@ public interface DBProcessor extends DBObject { T args(Object... args); - static Optional lookup(Class clazz, Class ext, String op) { + static Optional lookup(Class clazz, Class ext, String op) { try { var m = clazz.getMethod(op); //no parameter if(m.getReturnType() == ext && isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) { diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 0941bf13..13f5031e 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -1,8 +1,6 @@ package org.usf.jquery.core; -import static java.util.Objects.isNull; import static org.usf.jquery.core.SqlStringBuilder.SPACE; -import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; /** @@ -21,10 +19,6 @@ default String sql(QueryParameterBuilder builder, Object[] args) { String sql(QueryParameterBuilder builder); - default NamedView as(String tag) { - return new NamedView(this, isNull(tag) ? null : requireLegalVariable(tag)); - } - default String sqlWithTag(QueryParameterBuilder builder) { return sql(builder) + SPACE + builder.view(this); } diff --git a/src/main/java/org/usf/jquery/core/Database.java b/src/main/java/org/usf/jquery/core/Database.java index a75dfccc..106c30c8 100644 --- a/src/main/java/org/usf/jquery/core/Database.java +++ b/src/main/java/org/usf/jquery/core/Database.java @@ -10,7 +10,7 @@ */ public enum Database { - MYSQL, POSTGRESQL, ORACLE, SQLSERVER, TERADATA; + MYSQL, POSTGRESQL, ORACLE, SQLSERVER, TERADATA, H2; public static Optional of(String name) { var v = name.toUpperCase(); diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index ace68946..26aa2878 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -46,7 +46,7 @@ public String id() { }; default OperationColumn args(Object... args) { - return new OperationColumn(this, args); // no type + return args(null, args); // no type } default OperationColumn args(JDBCType type, Object... args) { @@ -56,19 +56,19 @@ default OperationColumn args(JDBCType type, Object... args) { //Arithmetic operations static TypedOperator plus() { - return new TypedOperator(DOUBLE, operator("+"), required(), required(firstArgJdbcType())); + return new TypedOperator(firstArgJdbcType(), operator("+"), required(), required(firstArgJdbcType())); } static TypedOperator minus() { - return new TypedOperator(DOUBLE, operator("-"), required(), required(firstArgJdbcType())); //date|datetime + return new TypedOperator(firstArgJdbcType(), operator("-"), required(), required(firstArgJdbcType())); //date|datetime } static TypedOperator multiply() { - return new TypedOperator(DOUBLE, operator("*"), required(), required(firstArgJdbcType())); + return new TypedOperator(firstArgJdbcType(), operator("*"), required(), required(firstArgJdbcType())); } static TypedOperator divide() { - return new TypedOperator(DOUBLE, operator("/"), required(), required(firstArgJdbcType())); + return new TypedOperator(firstArgJdbcType(), operator("/"), required(), required(firstArgJdbcType())); } //numeric functions diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 5efa966a..ef1569dc 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -19,7 +19,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Optional; -import java.util.stream.Stream; import lombok.Getter; import lombok.NonNull; diff --git a/src/main/java/org/usf/jquery/core/TableView.java b/src/main/java/org/usf/jquery/core/TableView.java index aeb8c1a5..589383db 100644 --- a/src/main/java/org/usf/jquery/core/TableView.java +++ b/src/main/java/org/usf/jquery/core/TableView.java @@ -14,7 +14,7 @@ */ @Getter @RequiredArgsConstructor -public class TableView implements TaggableView { +public class TableView implements DBView { private final String schema; private final String name; @@ -33,11 +33,6 @@ public String getSchemaOrElse(String defaultSchema) { return nonNull(schema) ? schema : defaultSchema; //schema priority order } - @Override - public String tagname() { - return tag; - } - @Override public String toString() { return sql(addWithValue()); diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 32a6327a..913ee15a 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -10,7 +10,7 @@ * */ @Getter -public class TypedOperator implements Operator { +public class TypedOperator { private final Operator operator; private final ArgTypeRef typeFn; @@ -26,19 +26,9 @@ public TypedOperator(ArgTypeRef typeFn, Operator function, Parameter... paramete this.parameterSet = ofParameters(parameter); } - @Override - public String id() { - return operator.id(); - } - @Override - public String sql(QueryParameterBuilder builder, Object[] args) { - return operator.sql(builder, parameterSet.assertArguments(args)); - } - - @Override public OperationColumn args(Object... args) { args = parameterSet.assertArguments(args); - return new OperationColumn(operator, args, typeFn.apply(args)); + return operator.args(typeFn.apply(args), args); } public Operator unwrap() { @@ -49,5 +39,4 @@ public Operator unwrap() { public String toString() { return operator.id() + parameterSet.toString(); } - } diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index d32af123..e50d5485 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -48,7 +48,7 @@ public final class ContextEnvironment { private final Map columns; private final DataSource dataSource; //optional private final String schema; //optional - private final DatabaseMetadata metadata = new DatabaseMetadata(); + private final DatabaseMetadata metadata; //runtime scope private final Map viewCache = new HashMap<>(); private final Map overView = new HashMap<>(); @@ -60,6 +60,7 @@ public ContextEnvironment(ContextEnvironment ctx) { this.columns = new HashMap<>(ctx.columns); //modifiable this.dataSource = ctx.dataSource; this.schema = ctx.schema; + this.metadata = ctx.metadata; } public Optional lookupRegisteredView(String name) { @@ -143,7 +144,7 @@ public static ContextEnvironment of(DatabaseDecorator database, requireNonNull(database, "configuration.database"), unmodifiableIdentityMap(views, ViewDecorator::identity, database.identity() + ".views"), unmodifiableIdentityMap(columns, ColumnDecorator::identity, database.identity() + ".columns"), - ds, schema); + ds, schema, new DatabaseMetadata()); } static Map unmodifiableIdentityMap(Collection c, Function fn, String msg){ diff --git a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java index 89b9f900..4e1738b8 100644 --- a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java +++ b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java @@ -27,6 +27,6 @@ static EntrySyntaxException unexpectedEntryArgsException(RequestEntryChain entry } static EntrySyntaxException expectedEntryTagException(RequestEntryChain entry) { - return new EntrySyntaxException(format("expected : %s", entry)); + return new EntrySyntaxException(format("expected after '%s'", entry)); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index e1dedeab..e717546c 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -477,7 +477,7 @@ private static boolean isWindowFunction(TypedOperator op) { } private static boolean isCountFunction(TypedOperator fn) { - return "COUNT".equals(fn.id()); + return "COUNT".equals(fn.unwrap().id()); } private static String[] toStringArray(List entries) { diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index 9c4b6258..a3cd92bf 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -1,7 +1,8 @@ package org.usf.jquery.web; import static java.lang.System.currentTimeMillis; -import static org.usf.jquery.core.Utils.isPresent; +import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; import static org.usf.jquery.web.Constants.VIEW; @@ -16,6 +17,7 @@ import java.util.stream.Stream; import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.Validation; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -37,7 +39,7 @@ public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull if(!parameterMap.containsKey(COLUMN) && !parameterMap.containsKey(COLUMN_DISTINCT)) { parameterMap.put(COLUMN, ant.defaultColumns()); } - if(isPresent(ant.ignoreParameters())) { + if(!isEmpty(ant.ignoreParameters())) { Stream.of(ant.ignoreParameters()).forEach(parameterMap::remove); } var ctx = ant.database().isEmpty() @@ -45,11 +47,11 @@ public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull : context(ant.database()); try { var req = ctx - .lookupRegisteredView(ant.view()) + .lookupRegisteredView(requireLegalVariable(ant.view())) .orElseThrow(()-> noSuchResourceException(VIEW, ant.view())) .query(parameterMap); //may edit map + log.trace("request parsed in {} ms", currentTimeMillis() - t); if(!ant.aggregationOnly() || req.isAggregation()) { - log.trace("request parsed in {} ms", currentTimeMillis() - t); return req; } throw accessDeniedException("non-aggregate query"); diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index d9e36896..a3456581 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -157,7 +157,7 @@ default void parseFetch(RequestQueryBuilder query, Map paramet private static Integer requirePositiveInt(String key, Map parameters) { if(parameters.containsKey(key)) { - var values = parameters.get(key); + var values = parameters.remove(key); if(values.length == 1) { var v = parseInt(values[0]); if(v >= 0) { From ecfd90704989a1dff20ad126b6589acf83efd99a Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 13 Aug 2024 00:01:06 +0200 Subject: [PATCH 117/298] edit --- .../java/org/usf/jquery/core/Comparator.java | 3 -- .../java/org/usf/jquery/core/DBColumn.java | 38 +++++++++---------- .../org/usf/jquery/core/ParameterSet.java | 12 ++++-- .../org/usf/jquery/core/TypedComparator.java | 15 ++------ .../org/usf/jquery/core/TypedOperator.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 6 +-- 6 files changed, 35 insertions(+), 41 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index e4ad1129..a2158961 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -168,7 +168,4 @@ static Optional lookupComparator(String op) { return DBProcessor.lookup(Comparator.class, TypedComparator.class, op); } - static IllegalArgumentException typeCannotBeNullException() { - return new IllegalArgumentException("type cannot be null"); - } } diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index ce7c8aa0..498f7105 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -121,19 +121,19 @@ default ColumnSingleFilter filter(ComparisonExpression exp) { // operations default OperationColumn plus(Object o) { - return Operator.plus().args(this, o); + return Operator.plus().operation(this, o); } default OperationColumn minus(Object o) { - return Operator.minus().args(this, o); + return Operator.minus().operation(this, o); } default OperationColumn multiply(Object o) { - return Operator.multiply().args(this, o); + return Operator.multiply().operation(this, o); } default OperationColumn divide(Object o) { - return Operator.divide().args(this, o); + return Operator.divide().operation(this, o); } default WhenFilterBridge when(ComparisonExpression ex) { @@ -181,65 +181,65 @@ static OperationColumn count() { } static OperationColumn count(Object arg) { - return Operator.count().args(arg); + return Operator.count().operation(arg); } static OperationColumn min(Object arg) { - return Operator.min().args(arg); + return Operator.min().operation(arg); } static OperationColumn max(Object arg) { - return Operator.max().args(arg); + return Operator.max().operation(arg); } static OperationColumn sum(Object arg) { - return Operator.sum().args(arg); + return Operator.sum().operation(arg); } static OperationColumn avg(Object arg) { - return Operator.avg().args(arg); + return Operator.avg().operation(arg); } //numeric static OperationColumn abs(Object arg) { - return Operator.abs().args(arg); + return Operator.abs().operation(arg); } static OperationColumn sqrt(Object arg) { - return Operator.sqrt().args(arg); + return Operator.sqrt().operation(arg); } static OperationColumn trunc(Object arg) { - return Operator.trunc().args(arg); + return Operator.trunc().operation(arg); } static OperationColumn ceil(Object arg) { - return Operator.ceil().args(arg); + return Operator.ceil().operation(arg); } static OperationColumn floor(Object arg) { - return Operator.floor().args(arg); + return Operator.floor().operation(arg); } //string static OperationColumn trim(Object arg) { - return Operator.trim().args(arg); + return Operator.trim().operation(arg); } static OperationColumn length(Object arg) { - return Operator.length().args(arg); + return Operator.length().operation(arg); } static OperationColumn upper(Object arg) { - return Operator.upper().args(arg); + return Operator.upper().operation(arg); } static OperationColumn lower(Object arg) { - return Operator.lower().args(arg); + return Operator.lower().operation(arg); } static OperationColumn substring(Object arg, int start, int length) { - return Operator.substring().args(arg, start, length); + return Operator.substring().operation(arg, start, length); } } diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index a7aadfef..df21609d 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -24,17 +24,21 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class ParameterSet { //there is no Singleton implementation, dummy sonar rule - public static final ParameterSet NO_PARAM = new ParameterSet(0, new Parameter[0]); + static final ParameterSet NO_PARAM = new ParameterSet(0, new Parameter[0]); private final int nReqArgs; private final Parameter[] parameters; - + public Object[] assertArguments(Object... args) { + return assertArgumentsFrom(0, args); + } + + public Object[] assertArgumentsFrom(int idx, Object... args) { var arr = isNull(args) ? new Object[0] : args; try { forEach(arr.length, (p,i)-> { - if(!p.accept(i, arr)) { - throw badArgumentTypeException(p.types(args), arr[i]); + if(i>=idx && !p.accept(i, arr)) { + throw badArgumentTypeException(p.types(arr), arr[i]); } }); return arr; diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index ad9ddd17..6cacb68f 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -10,7 +10,7 @@ * */ @Getter -public final class TypedComparator implements Comparator { +public final class TypedComparator { private final Comparator comparator; private final ParameterSet parameterSet; @@ -20,18 +20,11 @@ public TypedComparator(Comparator comparator, Parameter... parameters) { this.parameterSet = ofParameters(parameters); } - @Override - public String id() { - return comparator.id(); - } - - @Override - public String sql(QueryParameterBuilder builder, Object[] args) { - return comparator.sql(builder, parameterSet.assertArguments(args)); + public ComparisonExpression expression(Object... right) { + return comparator.expression(parameterSet.assertArgumentsFrom(1, right)); //no left } - @Override - public DBFilter args(Object... args) { + public DBFilter filter(Object... args) { return comparator.args(parameterSet.assertArguments(args)); } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 913ee15a..a88eeeea 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -26,7 +26,7 @@ public TypedOperator(ArgTypeRef typeFn, Operator function, Parameter... paramete this.parameterSet = ofParameters(parameter); } - public OperationColumn args(Object... args) { + public OperationColumn operation(Object... args) { args = parameterSet.assertArguments(args); return operator.args(typeFn.apply(args), args); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index e717546c..c6008803 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -236,7 +236,7 @@ public DBFilter evalFilter(ViewDecorator vd, List values) { / if(rc.entry.isLast()) { //no comparator, no criteria var fn = requireNonNull(values).size() == 1 ? eq() : in(); //non empty var e = new RequestEntryChain(null, false, null, values, null); - return fn.args(e.toArgs(vd, rc.col, fn.getParameterSet())); //no chain + return fn.filter(e.toArgs(vd, rc.col, fn.getParameterSet())); //no chain } return rc.entry.next.columnCriteria(vd, rc.cd, rc.col, values); } @@ -273,7 +273,7 @@ DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List if(cmp.isPresent()) { var fn = cmp.get(); var cp = new RequestEntryChain(null, false, null, assertOuterParameters(values), null); - return chainComparator(vc, fn.args(cp.toArgs(vc, col, fn.getParameterSet()))); + return chainComparator(vc, fn.filter(cp.toArgs(vc, col, fn.getParameterSet()))); } if(nonNull(cd)) { // no operation var c = cd.criteria(value); @@ -370,7 +370,7 @@ private Optional lookupOperation(ViewDecorator vd, DBColumn col if(isNull(c) && isEmpty(args) && isCountFunction(fn)) { c = allColumns(vd.view()); } - return fn.args(toArgs(vd, c, fn.getParameterSet())); + return fn.operation(toArgs(vd, c, fn.getParameterSet())); }); } From c3710b3e3c9a386c5e30f83c62abcc42f7575c81 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 13 Aug 2024 11:43:12 +0200 Subject: [PATCH 118/298] edit --- .../java/org/usf/jquery/core/Comparator.java | 1 - .../java/org/usf/jquery/core/Operator.java | 22 ----------------- .../java/org/usf/jquery/core/Parameter.java | 24 ++++++++++--------- 3 files changed, 13 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index a2158961..56994777 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -167,5 +167,4 @@ static InCompartor inComparator(final String name) { static Optional lookupComparator(String op) { return DBProcessor.lookup(Comparator.class, TypedComparator.class, op); } - } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 26aa2878..082a7d66 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -15,9 +15,7 @@ import static org.usf.jquery.core.Parameter.optional; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; -import static org.usf.jquery.core.QueryParameterBuilder.formatValue; import static org.usf.jquery.core.Utils.currentDatabase; -import static org.usf.jquery.core.Validation.requireNArgs; import java.util.Optional; @@ -30,21 +28,6 @@ public interface Operator extends DBProcessor { String id(); - @Deprecated(forRemoval = true) - static final Operator VALUE_RETURN = new Operator() { - - @Override - public String sql(QueryParameterBuilder builder, Object[] args) { - requireNArgs(1, args, this::id); - return formatValue(args[0]); - } - - @Override - public String id() { - return "value"; - } - }; - default OperationColumn args(Object... args) { return args(null, args); // no type } @@ -296,11 +279,6 @@ static TypedOperator ctime() { static TypedOperator ctimestamp() { return new TypedOperator(TIMESTAMP_WITH_TIMEZONE, constant("CURRENT_TIMESTAMP")); } - - @Deprecated(forRemoval = true) - static TypedOperator value() { - return new TypedOperator(firstArgJdbcType(), VALUE_RETURN, required()); - } static ArithmeticOperator operator(String symbol) { return ()-> symbol; diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 20a49c26..45f75d9e 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Utils.isEmpty; @@ -25,25 +26,26 @@ public final class Parameter { private final boolean varargs; public boolean accept(int idx, Object[] args) { - return nonNull(typeRef) - ? typeRef.apply(args).accept(args[idx]) - : isEmpty(types) || Stream.of(types).anyMatch(t-> t.accept(args[idx])); + var arr = types(args); + return isEmpty(arr) || Stream.of(arr).anyMatch(t-> t.accept(args[idx])); } public JavaType[] types(Object[] args) { - return nonNull(typeRef) - ? new JavaType[] {typeRef.apply(args)} - : types; + if(isNull(typeRef)) { + return types; + } + var t = typeRef.apply(args); //nullable + return isNull(t) ? null : new JavaType[] {t}; } @Override public String toString() { - if(nonNull(typeRef)) { - return typeRef.toString(); + if(isNull(typeRef)) { + return isEmpty(types) + ? "ANY" + : Stream.of(types).map(Object::toString).collect(joining("|")); } - return isEmpty(types) - ? "ANY" - : Stream.of(types).map(Object::toString).collect(joining("|")); + return typeRef.toString(); } public static Parameter required(JavaType... types) { From 60936767c0cbe6b9c5214b4c0c6f71e2468ca09d Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 13 Aug 2024 12:36:56 +0200 Subject: [PATCH 119/298] edit --- src/main/java/org/usf/jquery/core/Parameter.java | 1 - .../java/org/usf/jquery/web/ArgumentParsers.java | 13 +++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 45f75d9e..ee32e431 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -1,7 +1,6 @@ package org.usf.jquery.core; import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Utils.isEmpty; diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 79fd81a1..2ac1037a 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -1,6 +1,7 @@ package org.usf.jquery.web; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.BadArgumentException.badArgumentTypeException; import static org.usf.jquery.core.JDBCType.BIGINT; import static org.usf.jquery.core.JDBCType.DATE; @@ -21,6 +22,7 @@ import java.time.LocalTime; import java.time.ZonedDateTime; import java.util.Arrays; +import java.util.Objects; import java.util.function.IntFunction; import java.util.stream.Stream; @@ -31,7 +33,7 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; -import lombok.NonNull; +import lombok.NonNull; import lombok.extern.slf4j.Slf4j; /** @@ -140,9 +142,12 @@ private static Object matchTypes(Typed o, JDBCType... types) { } private static T[] castArray(JavaType[] types, Class c, IntFunction fn) { - return types.getClass() == c - ? c.cast(types) - : Stream.of(types).toArray(fn); + if(nonNull(types)) { + return types.getClass() == c + ? c.cast(types) + : Stream.of(types).toArray(fn); + } + return null; } private static UnsupportedOperationException unsupportedTypeException(JavaType type) { From 7c8f29850ac89fd98292fcafdc477982ddb0b320 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 13 Aug 2024 15:21:13 +0200 Subject: [PATCH 120/298] edit --- .../usf/jquery/core/RequestQueryBuilder.java | 21 ++++++++++--------- .../org/usf/jquery/web/ArgumentParsers.java | 2 +- .../java/org/usf/jquery/web/ViewMetadata.java | 8 +++---- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index ef1569dc..486ce7a1 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -117,14 +117,16 @@ public RequestQuery build(String schema) { } public final void build(SqlStringBuilder sb, QueryParameterBuilder pb){ + var sub = new SqlStringBuilder(100); + join(sub, pb); + where(sub, pb); + groupBy(sub, pb); + having(sub, pb); + orderBy(sub, pb); + fetch(sub); select(sb, pb); from(sb, pb); - where(sb, pb); - groupBy(sb, pb); - having(sb, pb); - orderBy(sb, pb); - fetch(sb); - join(sb, pb); + sb.append(sub.toString()); //TODO optim } void select(SqlStringBuilder sb, QueryParameterBuilder pb){ @@ -144,10 +146,9 @@ void select(SqlStringBuilder sb, QueryParameterBuilder pb){ } void from(SqlStringBuilder sb, QueryParameterBuilder pb) { - var vList = pb.views(); - if(!vList.isEmpty()) { - sb.append(" FROM ") - .appendEach(vList, SCOMA, o-> o.sqlWithTag(pb)); + var views = pb.views(); + if(!views.isEmpty()) { + sb.append(" FROM ").appendEach(views, SCOMA, v-> v.sqlWithTag(pb)); } } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 2ac1037a..4027cd72 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -33,7 +33,7 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; -import lombok.NonNull; +import lombok.NonNull; import lombok.extern.slf4j.Slf4j; /** diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index 7f95b0bf..a9cfbae4 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -14,8 +14,6 @@ import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.time.Instant; -import java.util.ArrayList; -import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; @@ -74,7 +72,7 @@ else if(view instanceof DBQuery query) { void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { try(var rs = metadata.getColumns(null, view.getSchemaOrElse(schema), view.getName(), null)){ if(rs.next()) { - var db = columns.values().stream().collect(toMap(m-> m.getName(), identity())); + var db = columns.values().stream().collect(toMap(m-> m.getName(), identity())); //reverse key do { var cm = db.remove(rs.getString("COLUMN_NAME")); if(nonNull(cm)) { @@ -98,12 +96,12 @@ void fetch(DatabaseMetaData metadata, DBQuery qr, String schema) throws SQLExcep var query = qr.sql(parametrized(schema)); try(var ps = metadata.getConnection().prepareStatement("SELECT * FROM(" + query + ") WHERE 1=0"); var rs = ps.executeQuery()){ - var db = new HashMap<>(columns); + var db = columns.values().stream().collect(toMap(m-> m.getName(), identity())); //reverse key var meta = rs.getMetaData(); for(var i=1; i<=meta.getColumnCount(); i++) { var cm = db.remove(meta.getColumnLabel(i)); //tag or name if(nonNull(cm)) { - cm.update(meta.getColumnType(i), meta.getColumnDisplaySize(i), meta.getPrecision(i)); + cm.update(meta.getColumnType(i), meta.getPrecision(i), meta.getScale(i)); } // else undeclared column } if(!db.isEmpty()) { From 48b96ce8e7ea1b6a00e101c6c42d220cb47fd610 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 13 Aug 2024 20:50:07 +0200 Subject: [PATCH 121/298] edit --- src/main/java/org/usf/jquery/core/DBView.java | 5 +- .../jquery/core/QueryParameterBuilder.java | 14 ++---- src/main/java/org/usf/jquery/core/Utils.java | 5 -- .../org/usf/jquery/web/RevisionIterator.java | 4 +- .../java/org/usf/jquery/web/ViewMetadata.java | 50 ++++++++++--------- .../org/usf/jquery/web/YearTableMetadata.java | 2 +- 6 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 13f5031e..18e3860f 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static java.util.Objects.isNull; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -20,6 +21,8 @@ default String sql(QueryParameterBuilder builder, Object[] args) { String sql(QueryParameterBuilder builder); default String sqlWithTag(QueryParameterBuilder builder) { - return sql(builder) + SPACE + builder.view(this); + var tag = builder.view(this); + var sql = sql(builder); + return isNull(tag) ? sql : sql + SPACE + tag; } } diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 772b3c84..18adc0c6 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -46,16 +46,12 @@ public List views(){ } public String view(DBView view) { - if(isNull(vPrefix)) { //view can be null - return null; + var idx = views.indexOf(overView.getOrDefault(view, view)); + if(idx < 0) { + idx = views.size(); + views.add(view); } - view = overView.getOrDefault(view, view); - var idx = views.indexOf(view); - if(idx > -1) { - return vPrefix + (idx+1); - } - views.add(view); - return vPrefix + views.size(); + return isNull(vPrefix) ? null : vPrefix + (idx+1); } public void overView(DBView oldView, DBView newView) { //WindowFunction diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 4486e1c6..25288ce1 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -2,7 +2,6 @@ import static java.util.Arrays.copyOf; import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; import java.util.Collection; @@ -24,10 +23,6 @@ public final class Utils { public static final int UNLIMITED = -1; - public static boolean isPresent(T[] a) { - return nonNull(a) && a.length > 0; - } - public static boolean isEmpty(T[] a) { return isNull(a) || a.length == 0; } diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index 4c15b95d..cecf8046 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -2,6 +2,7 @@ import static java.util.stream.Collectors.groupingBy; import static org.usf.jquery.core.DBColumn.constant; +import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Utils.isPresent; import java.time.YearMonth; @@ -14,6 +15,7 @@ import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.QueryParameterBuilder; import org.usf.jquery.core.TableView; +import org.usf.jquery.core.Utils; import lombok.RequiredArgsConstructor; @@ -46,7 +48,7 @@ public Entry> next() { } public static RevisionIterator iterator(YearMonth[] revisions){ - if(isPresent(revisions)) { + if(!isEmpty(revisions)) { var map = Stream.of(revisions).collect(groupingBy(YearMonth::getYear)); return new RevisionIterator(map.entrySet().iterator()); } diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index a9cfbae4..cec79ef7 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -16,8 +16,8 @@ import java.time.Instant; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; -import org.usf.jquery.core.DBQuery; import org.usf.jquery.core.DBView; import org.usf.jquery.core.TableView; @@ -41,7 +41,7 @@ public class ViewMetadata { private static final Object LOG_LOCK = new Object(); private final DBView view; //cache - private final Map columns; //empty!? + private final Map columns; //key=identity @Getter private Instant lastUpdate; @@ -52,27 +52,27 @@ public ColumnMetadata columnMetadata(ColumnDecorator cd) { final ViewMetadata fetch(DatabaseMetaData metadata, String schema) throws SQLException { if(!isEmpty(columns)) { var time = currentTimeMillis(); - log.info("scanning table '{}' metadata...", view); + log.info("scanning view '{}' metadata...", view); if(view instanceof TableView tab) { fetch(metadata, tab, schema); } - else if(view instanceof DBQuery query) { - fetch(metadata, query, schema); - } else { - throw new UnsupportedOperationException("unsupported view type " + view); + fetch(metadata, view, schema); } lastUpdate = now(); - log.info("metadata scanned in {} ms", currentTimeMillis() - time); + log.trace("'{}' metadata scanned in {} ms", view, currentTimeMillis() - time); printViewColumnMap(); } + else { + log.warn("'{}' has no declared columns", view); + } return this; } void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { try(var rs = metadata.getColumns(null, view.getSchemaOrElse(schema), view.getName(), null)){ if(rs.next()) { - var db = columns.values().stream().collect(toMap(m-> m.getName(), identity())); //reverse key + var db = reverseMapKeys(); //reverse key do { var cm = db.remove(rs.getString("COLUMN_NAME")); if(nonNull(cm)) { @@ -82,8 +82,8 @@ void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLE rs.getInt("DECIMAL_DIGITS")); } // else undeclared column } while(rs.next()); - if(!db.isEmpty()) { - throw columnsNotFoundException(db); + if(!db.isEmpty()) { //no such columns + throw columnsNotFoundException(db.keySet()); } } else { @@ -92,11 +92,11 @@ void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLE } } - void fetch(DatabaseMetaData metadata, DBQuery qr, String schema) throws SQLException { - var query = qr.sql(parametrized(schema)); - try(var ps = metadata.getConnection().prepareStatement("SELECT * FROM(" + query + ") WHERE 1=0"); + void fetch(DatabaseMetaData metadata, DBView qr, String schema) throws SQLException { + var query = "SELECT * FROM " + qr.sql(parametrized(schema)) + " WHERE 1=0"; // rows=0 + try(var ps = metadata.getConnection().prepareStatement(query); var rs = ps.executeQuery()){ - var db = columns.values().stream().collect(toMap(m-> m.getName(), identity())); //reverse key + var db = reverseMapKeys(); var meta = rs.getMetaData(); for(var i=1; i<=meta.getColumnCount(); i++) { var cm = db.remove(meta.getColumnLabel(i)); //tag or name @@ -104,29 +104,33 @@ void fetch(DatabaseMetaData metadata, DBQuery qr, String schema) throws SQLExcep cm.update(meta.getColumnType(i), meta.getPrecision(i), meta.getScale(i)); } // else undeclared column } - if(!db.isEmpty()) { - throw columnsNotFoundException(db); + if(!db.isEmpty()) { //no such columns + throw columnsNotFoundException(db.keySet()); } } } + private Map reverseMapKeys(){ //key=columnName + return columns.values().stream().collect(toMap(ColumnMetadata::getName, identity())); + } + void printViewColumnMap() { if(!columns.isEmpty() && log.isInfoEnabled()) { synchronized(LOG_LOCK) { - var pattern = "|%-20s|%-15s|%-25s|%-20s|"; - var bar = format(pattern, "", "", "", "").replace("|", "+").replace(" ", "-"); + var ptr = "|%-20s|%-15s|%-25s|%-20s|"; + var bar = format(ptr, "", "", "", "").replace("|", "+").replace(" ", "-"); log.info(bar); - log.info(format(pattern, "ID", "CLASS", "COLUMN", "TYPE")); + log.info(format(ptr, "ID", "CLASS", "COLUMN", "TYPE")); log.info(bar); columns.entrySet().forEach(e-> - log.info(format(pattern, e.getKey(), e.getValue().toJavaType(), + log.info(format(ptr, e.getKey(), e.getValue().toJavaType(), e.getValue().getName(), e.getValue().toSqlType()))); log.info(bar); } } } - NoSuchElementException columnsNotFoundException(Map db) { - return new NoSuchElementException("column(s) [" + join(", ", db.keySet()) + "] not found in " + view.toString()); + NoSuchElementException columnsNotFoundException(Set columns) { + return new NoSuchElementException("column(s) [" + join(", ", columns) + "] not found in " + view); } } diff --git a/src/main/java/org/usf/jquery/web/YearTableMetadata.java b/src/main/java/org/usf/jquery/web/YearTableMetadata.java index ed7dc058..b71ddfac 100644 --- a/src/main/java/org/usf/jquery/web/YearTableMetadata.java +++ b/src/main/java/org/usf/jquery/web/YearTableMetadata.java @@ -153,7 +153,7 @@ static YearTableMetadata yearTableMetadata(YearViewDecorator table) { @Deprecated static void logRevisions(YearMonth[] revs) { - if(isPresent(revs)) { + if(!isEmpty(revs)) { var pattern = "|%-5s|%-40s|"; var bar = format(pattern, "", "").replace("|", "+").replace(" ", "-"); var map = Stream.of(revs).collect(groupingBy(YearMonth::getYear)); From 6facbdc0e4224b5732fb686c244ba0ac193230e3 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 13 Aug 2024 22:52:58 +0200 Subject: [PATCH 122/298] edit --- .../org/usf/jquery/web/ArgumentParsers.java | 1 - .../usf/jquery/web/ContextEnvironment.java | 27 +++++++++++-------- .../jquery/web/RequestQueryParamResolver.java | 1 - .../org/usf/jquery/web/RevisionIterator.java | 2 -- .../org/usf/jquery/web/ViewDecorator.java | 9 ++++--- .../org/usf/jquery/web/YearTableMetadata.java | 1 - 6 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 4027cd72..baaa1697 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -22,7 +22,6 @@ import java.time.LocalTime; import java.time.ZonedDateTime; import java.util.Arrays; -import java.util.Objects; import java.util.function.IntFunction; import java.util.stream.Stream; diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index e50d5485..d6eaa7f2 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -1,11 +1,12 @@ package org.usf.jquery.web; +import static java.util.Collections.unmodifiableMap; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static java.util.function.Function.identity; -import static java.util.stream.Collectors.toUnmodifiableMap; +import static java.util.stream.Collectors.toMap; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNonEmpty; import static org.usf.jquery.web.Constants.COLUMN; @@ -15,10 +16,12 @@ import java.sql.SQLException; import java.util.Collection; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collector; import javax.sql.DataSource; @@ -50,7 +53,6 @@ public final class ContextEnvironment { private final String schema; //optional private final DatabaseMetadata metadata; //runtime scope - private final Map viewCache = new HashMap<>(); private final Map overView = new HashMap<>(); private final Map declaredColumns = new HashMap<>(); @@ -75,10 +77,6 @@ Optional lookupDeclaredColumn(String name) { return ofNullable(declaredColumns.get(name)); } - public DBView getView(ViewDecorator vd, Supplier supp) { - return viewCache.computeIfAbsent(vd, k-> supp.get()); - } - void declareView(ViewDecorator view) { //additional request views views.compute(view.identity(), (k,v)-> { if(isNull(v)){ @@ -139,16 +137,23 @@ public static ContextEnvironment of(DatabaseDecorator database, public static ContextEnvironment of(DatabaseDecorator database, Collection views, Collection columns, DataSource ds, String schema) { - requireLegalVariable(database.identity()); - return new ContextEnvironment( - requireNonNull(database, "configuration.database"), + requireLegalVariable(requireNonNull(database, "configuration.database").identity()); + return new ContextEnvironment(database, unmodifiableIdentityMap(views, ViewDecorator::identity, database.identity() + ".views"), unmodifiableIdentityMap(columns, ColumnDecorator::identity, database.identity() + ".columns"), ds, schema, new DatabaseMetadata()); } static Map unmodifiableIdentityMap(Collection c, Function fn, String msg){ - return requireNonEmpty(c, msg).stream() - .collect(toUnmodifiableMap(fn.andThen(Validation::requireLegalVariable), identity())); + return unmodifiableMap(requireNonEmpty(c, msg).stream() + .collect(toLinkedMap(fn.andThen(Validation::requireLegalVariable), identity()))); + } + + static Collector> toLinkedMap( + Function keyMapper, + Function valueMapper) { + return toMap(keyMapper, valueMapper, + (v1, v2) -> {throw new IllegalStateException("!parallel");}, + LinkedHashMap::new); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index a3cd92bf..4ace5acc 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -17,7 +17,6 @@ import java.util.stream.Stream; import org.usf.jquery.core.RequestQueryBuilder; -import org.usf.jquery.core.Validation; import lombok.NonNull; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index cecf8046..9198785a 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -3,7 +3,6 @@ import static java.util.stream.Collectors.groupingBy; import static org.usf.jquery.core.DBColumn.constant; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Utils.isPresent; import java.time.YearMonth; import java.util.Iterator; @@ -15,7 +14,6 @@ import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.QueryParameterBuilder; import org.usf.jquery.core.TableView; -import org.usf.jquery.core.Utils; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index a3456581..59c3f7b0 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -1,7 +1,9 @@ package org.usf.jquery.web; import static java.lang.Integer.parseInt; +import static java.util.Map.entry; import static java.util.Objects.nonNull; +import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toUnmodifiableMap; import static org.usf.jquery.core.SqlStringBuilder.quote; @@ -58,7 +60,7 @@ default JoinBuilder joiner(String name) { } default DBView view() {//final - return currentContext().getView(this, builder()::build); + return metadata().getView(); } default TaggableColumn column(@NonNull ColumnDecorator cd) {//final @@ -86,9 +88,10 @@ private TableView buildView() { } default ViewMetadata metadata() { - return currentContext().computeTableMetadata(this, cols-> new ViewMetadata(view(), + var view = requireNonNull(builder(), identity() + ".builder").build(); + return currentContext().computeTableMetadata(this, cols-> new ViewMetadata(view, cols.stream().>mapMulti((cd, acc)-> ofNullable(columnName(cd)) - .map(cn-> Map.entry(cd.identity(), columnMetadata(cn, cd.type(this)))) + .map(cn-> entry(cd.identity(), columnMetadata(cn, cd.type(this)))) .ifPresent(acc)) //view column only .collect(toUnmodifiableMap(Entry::getKey, Entry::getValue)))); } diff --git a/src/main/java/org/usf/jquery/web/YearTableMetadata.java b/src/main/java/org/usf/jquery/web/YearTableMetadata.java index b71ddfac..b075136b 100644 --- a/src/main/java/org/usf/jquery/web/YearTableMetadata.java +++ b/src/main/java/org/usf/jquery/web/YearTableMetadata.java @@ -16,7 +16,6 @@ import static org.usf.jquery.core.JDBCType.OTHER; import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Utils.isPresent; import static org.usf.jquery.web.Constants.EMPTY_REVISION; import java.sql.Connection; From d8b294861fd8dde669b945082313dec4937e01ac Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 14 Aug 2024 00:05:14 +0200 Subject: [PATCH 123/298] edit --- .../jquery/core/QueryParameterBuilder.java | 10 +------- .../usf/jquery/core/RequestQueryBuilder.java | 8 +++++++ .../usf/jquery/web/ContextEnvironment.java | 12 ++++------ .../org/usf/jquery/web/ViewDecorator.java | 23 ++++++++++--------- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 18adc0c6..c918d816 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -38,7 +38,6 @@ public final class QueryParameterBuilder { private final List args; private final List argTypes; private final List views; //indexed - private final Map overView = new HashMap<>(); public List views(){ @@ -54,14 +53,7 @@ public String view(DBView view) { return isNull(vPrefix) ? null : vPrefix + (idx+1); } - public void overView(DBView oldView, DBView newView) { //WindowFunction - var idx = views.indexOf(oldView); - if(idx > -1) { - views.set(idx, newView); //replace - } - else { - views.add(newView); - } + public void overView(DBView oldView, DBView newView) { overView.put(oldView, newView); } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 486ce7a1..62d4be69 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -15,13 +15,17 @@ import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Validation.requireNonEmpty; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Optional; import lombok.Getter; import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; /** @@ -42,6 +46,9 @@ public class RequestQueryBuilder { private Integer fetch; private Integer offset; + @Setter + private Map overView; + public RequestQueryBuilder distinct() { distinct = true; return this; @@ -105,6 +112,7 @@ public RequestQuery build(String schema) { requireNonEmpty(columns, "columns"); var bg = currentTimeMillis(); var pb = parametrized(schema); + overView.forEach(pb::overView); //over clause var sb = new SqlStringBuilder(1000); //avg if(isNull(it)) { build(sb, pb); diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index d6eaa7f2..d001a000 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -111,13 +111,11 @@ ContextEnvironment bind() { for(var v : views.values()) { var meta = requireNonNull(v.metadata(), v.identity() + ".metadata"); synchronized(meta) { - if(isNull(meta.getLastUpdate())) { - try(var cnx = dataSource.getConnection()) { - meta.fetch(cnx.getMetaData(), schema); - } - catch(SQLException | JQueryException e) { - log.error("error while scanning database metadata", e); - } + try(var cnx = dataSource.getConnection()) { + meta.fetch(cnx.getMetaData(), schema); + } + catch(SQLException | JQueryException e) { + log.error("error while scanning database metadata", e); } } } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 59c3f7b0..633fe3b4 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -59,7 +59,7 @@ default JoinBuilder joiner(String name) { return null; //no builder by default } - default DBView view() {//final + default DBView view() { return metadata().getView(); } @@ -103,6 +103,7 @@ default RequestQueryBuilder query(Map parameterMap) { parseOrders(query, parameterMap); parseFetch(query, parameterMap); parseFilters(query, parameterMap); + query.setOverView(currentContext().getOverView()); //over clause return query; } @@ -135,16 +136,6 @@ default void parseColumns(RequestQueryBuilder query, Map param .forEach(query::columns); } - default void parseFilters(RequestQueryBuilder query, Map parameters) { - parameters.entrySet().stream() -// .filter(e-> !RESERVED_WORDS.contains(e.getKey())) - .flatMap(e-> { - var re = parseEntry(e.getKey()); - return Stream.of(e.getValue()).map(v-> re.evalFilter(this, parseArgs(v))); - }) - .forEach(query::filters); - } - default void parseOrders(RequestQueryBuilder query, Map parameters) { if(parameters.containsKey(ORDER)) { Stream.of(parameters.remove(ORDER)) @@ -157,6 +148,16 @@ default void parseFetch(RequestQueryBuilder query, Map paramet query.fetch(requirePositiveInt(OFFSET, parameters), requirePositiveInt(FETCH, parameters)); } + + default void parseFilters(RequestQueryBuilder query, Map parameters) { + parameters.entrySet().stream() +// .filter(e-> !RESERVED_WORDS.contains(e.getKey())) + .flatMap(e-> { + var re = parseEntry(e.getKey()); + return Stream.of(e.getValue()).map(v-> re.evalFilter(this, parseArgs(v))); + }) + .forEach(query::filters); + } private static Integer requirePositiveInt(String key, Map parameters) { if(parameters.containsKey(key)) { From bb8d4b6a10d6e7a4817cb6a9b7ace633f74baa75 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 17 Aug 2024 15:57:32 +0200 Subject: [PATCH 124/298] edit --- .../java/org/usf/jquery/core/JQueryType.java | 5 +-- .../java/org/usf/jquery/core/Operator.java | 2 +- .../usf/jquery/core/RequestQueryBuilder.java | 2 +- .../org/usf/jquery/web/ArgumentParsers.java | 5 +-- .../usf/jquery/web/JDBCArgumentParser.java | 8 ++--- .../org/usf/jquery/web/RequestEntryChain.java | 34 +++++++++++-------- .../org/usf/jquery/web/ViewDecorator.java | 17 +++++++--- 7 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/JQueryType.java b/src/main/java/org/usf/jquery/core/JQueryType.java index 020e925d..2c55ebb0 100644 --- a/src/main/java/org/usf/jquery/core/JQueryType.java +++ b/src/main/java/org/usf/jquery/core/JQueryType.java @@ -11,10 +11,12 @@ public enum JQueryType implements JavaType { VIEW(DBView.class), - COLUMN(TaggableColumn.class), //TD DBColumn !? + COLUMN(DBColumn.class), + TAGGABLE(TaggableColumn.class), FILTER(DBFilter.class), ORDER(DBOrder.class), QUERY(QueryView.class), + JOIN(ViewJoin[].class), PARTITION(Partition.class); private final Class type; @@ -23,5 +25,4 @@ public enum JQueryType implements JavaType { public Class typeClass() { return type; } - } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 082a7d66..dcdde603 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -263,7 +263,7 @@ static TypedOperator denseRank() { //pipe functions static TypedOperator over() { - return new TypedOperator(firstArgJdbcType(), pipe("OVER"), required(COLUMN), required(PARTITION)); + return new TypedOperator(firstArgJdbcType(), pipe("OVER"), required(COLUMN), required(PARTITION)); //optional !? } // constant operators diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 62d4be69..606775ea 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -69,7 +69,7 @@ public RequestQueryBuilder orders(@NonNull DBOrder... orders) { return this; } - public RequestQueryBuilder joins(@NonNull ViewJoin joins) { + public RequestQueryBuilder joins(@NonNull ViewJoin... joins) { addAll(this.joins, joins); return this; } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index baaa1697..af7abf93 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -118,12 +118,13 @@ public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type) { switch (type) { - case COLUMN: return RequestEntryChain::evalColumn; + case TAGGABLE: return (e,v)-> e.evalColumn(v, true, false); + case COLUMN: return (e,v)-> e.evalColumn(v, false, false); case FILTER: return RequestEntryChain::evalFilter; case ORDER: return RequestEntryChain::evalOrder; case QUERY: return RequestEntryChain::evalQuery; + case JOIN: return RequestEntryChain::evalJoin; case PARTITION: return RequestEntryChain::evalPartition; - //TODO join default: throw unsupportedTypeException(type); } } diff --git a/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java index 7506a729..c2cbac87 100644 --- a/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java +++ b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java @@ -14,15 +14,11 @@ public interface JDBCArgumentParser extends JavaArgumentParser { @Override default Object parseEntry(RequestEntryChain entry, ViewDecorator td) { - return parseValue(entry.requireNoArgs().getValue()); - } - - default Object parseValue(String v) { try { - return nativeParse(v); + return nativeParse(entry.requireNoArgs().requireNoNext().getValue()); } catch(Exception e) { - throw cannotParseEntryException("", null, e); //TODO finish + throw cannotParseEntryException("value", entry, e); } } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index c6008803..5bb270e0 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -121,7 +121,7 @@ public ViewDecorator evalQuery(ViewDecorator td) { //select[.distinct|filter|order|offset|fetch]* Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub context if(SELECT.equals(value)) { - var q = new RequestQueryBuilder().columns(toColumnArgs(td, false)); + var q = new RequestQueryBuilder().columns(toTaggableArgs(td)); var e = this; while(e.hasNext()) { e = e.next; @@ -129,6 +129,7 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub case DISTINCT: e.requireNoArgs(); q.distinct(); break; case FILTER: q.filters(e.toFilterArgs(td)); break; case ORDER: q.orders(e.toOderArgs(td)); break; //not sure + case JOIN: q.joins(e.evalJoin(td)); break; case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; default: throw unexpectedEntryValueException(e); @@ -168,7 +169,7 @@ public Partition evalPartition(ViewDecorator td) { var e = this; do { switch (e.value) { - case PARTITION: addAll(cols, e.toColumnArgs(td, true)); break; + case PARTITION: addAll(cols, toColumnArgs(td)); break; case ORDER: addAll(ords, e.toOderArgs(td)); break; default: throw unexpectedEntryValueException(e); } @@ -184,16 +185,17 @@ public Partition evalPartition(ViewDecorator td) { } //[view.]column[.operator]* - public TaggableColumn evalColumn(ViewDecorator td) { + public DBColumn evalColumn(ViewDecorator td, boolean requireTag, boolean declare) { try { var r = chainColumnOperations(td, false) .orElseThrow(()-> noSuchViewColumnException(this)); r.entry.requireNoNext(); //check next only if column exists if(nonNull(r.entry.tag)) { - return currentContext().declareColumn(r.col.as(r.entry.tag)); + var c = r.col.as(r.entry.tag); + return declare ? currentContext().declareColumn(c) : c; } - if(r.col instanceof TaggableColumn col) { - return col; + if(!requireTag || r.col instanceof TaggableColumn) { + return r.col; } throw expectedEntryTagException(r.entry); } catch (Exception e) { @@ -374,27 +376,29 @@ private Optional lookupOperation(ViewDecorator vd, DBColumn col }); } - private TaggableColumn[] toColumnArgs(ViewDecorator td, boolean allowEmpty) { - return (TaggableColumn[]) toArgs(td, JQueryType.COLUMN, allowEmpty); + private TaggableColumn[] toTaggableArgs(ViewDecorator td) { + return (TaggableColumn[]) toArgs(td, JQueryType.TAGGABLE); + } + + private DBColumn[] toColumnArgs(ViewDecorator td) { + return (DBColumn[]) toArgs(td, JQueryType.COLUMN); } private DBFilter[] toFilterArgs(ViewDecorator td) { - return (DBFilter[]) toArgs(td, JQueryType.FILTER, false); + return (DBFilter[]) toArgs(td, JQueryType.FILTER); } private DBOrder[] toOderArgs(ViewDecorator td) { - return (DBOrder[]) toArgs(td, JQueryType.ORDER, false); + return (DBOrder[]) toArgs(td, JQueryType.ORDER); } - private Object[] toArgs(ViewDecorator td, JavaType type, boolean allowEmpty) { + private Object[] toArgs(ViewDecorator td, JavaType type) { var c = type.typeClass(); if(DBObject.class.isAssignableFrom(c)) { // JQuery types & !array - var ps = allowEmpty - ? ofParameters(varargs(type)) - : ofParameters(required(type), varargs(type)); + var ps = ofParameters(required(type), varargs(type)); return toArgs(td, null, ps, s-> (Object[]) newInstance(c, s)); } - throw new UnsupportedOperationException("cannot instanitate type " + c); + throw new UnsupportedOperationException("cannot instantiate type " + c); } private Object toOneArg(ViewDecorator td, JavaType type) { diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 633fe3b4..fac0378d 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -13,6 +13,7 @@ import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; import static org.usf.jquery.web.Constants.FETCH; +import static org.usf.jquery.web.Constants.JOIN; import static org.usf.jquery.web.Constants.OFFSET; import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.VIEW; @@ -101,9 +102,10 @@ default RequestQueryBuilder query(Map parameterMap) { parseViews(query, parameterMap); parseColumns(query, parameterMap); parseOrders(query, parameterMap); + parseJoin(query, parameterMap); parseFetch(query, parameterMap); parseFilters(query, parameterMap); - query.setOverView(currentContext().getOverView()); //over clause + query.setOverView(currentContext().getOverView()); //over clause: after filters return query; } @@ -132,7 +134,7 @@ default void parseColumns(RequestQueryBuilder query, Map param } Stream.of(cols) .flatMap(v-> parseEntries(v).stream()) - .map(e-> e.evalColumn(this)) + .map(e-> (TaggableColumn)e.evalColumn(this, true, true)) .forEach(query::columns); } @@ -143,12 +145,19 @@ default void parseOrders(RequestQueryBuilder query, Map parame .forEach(e-> query.orders(e.evalOrder(this))); } } - + default void parseJoin(RequestQueryBuilder query, Map parameters) { + if(parameters.containsKey(JOIN)) { + Stream.of(parameters.remove(JOIN)) + .flatMap(c-> parseEntries(c).stream()) + .forEach(e-> query.joins(e.evalJoin(this))); + } + } + default void parseFetch(RequestQueryBuilder query, Map parameters) { query.fetch(requirePositiveInt(OFFSET, parameters), requirePositiveInt(FETCH, parameters)); } - + default void parseFilters(RequestQueryBuilder query, Map parameters) { parameters.entrySet().stream() // .filter(e-> !RESERVED_WORDS.contains(e.getKey())) From e10501ed5d681c90c322c9bcb588fa23b6ce39c0 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 17 Aug 2024 18:25:20 +0200 Subject: [PATCH 125/298] edit --- src/main/java/org/usf/jquery/core/NamedColumn.java | 7 +++++++ .../org/usf/jquery/core/QueryParameterBuilder.java | 14 +++++++++----- .../java/org/usf/jquery/web/RequestEntryChain.java | 1 + 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index ad350092..578d44f5 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; + import java.util.Objects; import lombok.AccessLevel; @@ -28,4 +30,9 @@ public String tagname() { public NamedColumn as(String name) { // map return Objects.equals(name, tag) ? this : new NamedColumn(column, name); } + + @Override + public String toString() { + return this.sqlWithTag(addWithValue()); + } } diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index c918d816..c40b1fa6 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -45,12 +45,16 @@ public List views(){ } public String view(DBView view) { - var idx = views.indexOf(overView.getOrDefault(view, view)); - if(idx < 0) { - idx = views.size(); - views.add(view); + if(nonNull(views)) { + var v = overView.getOrDefault(view, view); + var idx = views.indexOf(v); + if(idx < 0) { + idx = views.size(); + views.add(v); + } + return isNull(vPrefix) ? null : vPrefix + (idx+1); } - return isNull(vPrefix) ? null : vPrefix + (idx+1); + return null; } public void overView(DBView oldView, DBView newView) { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 5bb270e0..5038052f 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -59,6 +59,7 @@ import org.usf.jquery.core.Order; import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; +import org.usf.jquery.core.QueryParameterBuilder; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedOperator; From 86617bc72f524c9a77fd0c2b399893cee16be6b7 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 17 Aug 2024 19:03:38 +0200 Subject: [PATCH 126/298] edit --- .../org/usf/jquery/core/CastFunction.java | 2 +- .../jquery/core/QueryParameterBuilder.java | 32 ++++++++++--------- .../usf/jquery/core/RequestQueryBuilder.java | 5 +-- .../java/org/usf/jquery/web/ViewMetadata.java | 3 +- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CastFunction.java b/src/main/java/org/usf/jquery/core/CastFunction.java index 064292ca..6c6826d9 100644 --- a/src/main/java/org/usf/jquery/core/CastFunction.java +++ b/src/main/java/org/usf/jquery/core/CastFunction.java @@ -25,7 +25,7 @@ default String sql(QueryParameterBuilder builder, Object[] args) { .append(builder.appendLiteral(args[0])).append(" AS ").append(asType()); if(args.length > 1) { sb.append("(") - .append(builder.appendArrayParameter(args, 1)) + .append(builder.appendLiteralArray(args, 1)) .append(")"); } return sb.append(")").toString(); diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index c40b1fa6..0bbc5a6f 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static java.util.Collections.emptyMap; +import static java.util.Collections.unmodifiableMap; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; @@ -11,6 +13,7 @@ import static org.usf.jquery.core.Utils.isEmpty; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -38,23 +41,20 @@ public final class QueryParameterBuilder { private final List args; private final List argTypes; private final List views; //indexed - private final Map overView = new HashMap<>(); + private final Map overView; public List views(){ return views; } public String view(DBView view) { - if(nonNull(views)) { - var v = overView.getOrDefault(view, view); - var idx = views.indexOf(v); - if(idx < 0) { - idx = views.size(); - views.add(v); - } - return isNull(vPrefix) ? null : vPrefix + (idx+1); + var v = overView.getOrDefault(view, view); + var idx = views.indexOf(v); + if(idx < 0) { + idx = views.size(); + views.add(v); } - return null; + return isNull(vPrefix) ? null : vPrefix + (idx+1); } public void overView(DBView oldView, DBView newView) { @@ -84,6 +84,7 @@ public String appendLiteralArray(Object[] arr) { public String appendLiteralArray(Object[] arr, int from) { return isEmpty(arr) || from>=arr.length ? EMPTY : Stream.of(arr) //throw !? + .skip(from) .map(this::appendLiteral) .collect(joining(SCOMA)); } @@ -140,18 +141,19 @@ public static String formatValue(Object o) { } public QueryParameterBuilder withValue() { - return new QueryParameterBuilder(schema, vPrefix, null, null, views); + return new QueryParameterBuilder(schema, vPrefix, null, null, views, overView); } public QueryParameterBuilder subQuery() { - return new QueryParameterBuilder(schema, isNull(vPrefix) ? null : vPrefix + "_s", args, argTypes, new ArrayList<>()); + var pre = isNull(vPrefix) ? null : vPrefix + "_s"; + return new QueryParameterBuilder(schema, pre, args, argTypes, new ArrayList<>(), emptyMap()); } public static QueryParameterBuilder addWithValue() { - return new QueryParameterBuilder(null, null, null, null, null); //no args + return new QueryParameterBuilder(null, null, null, null, new ArrayList<>(), emptyMap()); //no args } - public static QueryParameterBuilder parametrized(String schema) { - return new QueryParameterBuilder(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + public static QueryParameterBuilder parametrized(String schema, Map overView) { + return new QueryParameterBuilder(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), unmodifiableMap(overView)); } } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 606775ea..3900976d 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -15,7 +15,6 @@ import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Validation.requireNonEmpty; -import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -24,7 +23,6 @@ import lombok.Getter; import lombok.NonNull; -import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -111,8 +109,7 @@ public RequestQuery build(String schema) { // requireNonEmpty(tables); requireNonEmpty(columns, "columns"); var bg = currentTimeMillis(); - var pb = parametrized(schema); - overView.forEach(pb::overView); //over clause + var pb = parametrized(schema, overView); //over clause var sb = new SqlStringBuilder(1000); //avg if(isNull(it)) { build(sb, pb); diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index cec79ef7..fe4e3bd8 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -4,6 +4,7 @@ import static java.lang.String.join; import static java.lang.System.currentTimeMillis; import static java.time.Instant.now; +import static java.util.Collections.emptyMap; import static java.util.Objects.nonNull; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; @@ -93,7 +94,7 @@ void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLE } void fetch(DatabaseMetaData metadata, DBView qr, String schema) throws SQLException { - var query = "SELECT * FROM " + qr.sql(parametrized(schema)) + " WHERE 1=0"; // rows=0 + var query = "SELECT * FROM " + qr.sql(parametrized(schema, emptyMap())) + " WHERE 1=0"; // rows=0 try(var ps = metadata.getConnection().prepareStatement(query); var rs = ps.executeQuery()){ var db = reverseMapKeys(); From f8bb3cc8f312b4ddfb056c87873ecdd04062ba64 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 17 Aug 2024 19:18:48 +0200 Subject: [PATCH 127/298] edit --- .../org/usf/jquery/core/QueryParameterBuilder.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 0bbc5a6f..b5dd9392 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -13,8 +13,6 @@ import static org.usf.jquery.core.Utils.isEmpty; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Stream; @@ -22,14 +20,12 @@ import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.Setter; /** * * @author u$f * */ -@Setter(AccessLevel.PACKAGE) @AllArgsConstructor(access = AccessLevel.PRIVATE) public final class QueryParameterBuilder { @@ -48,19 +44,15 @@ public List views(){ } public String view(DBView view) { - var v = overView.getOrDefault(view, view); - var idx = views.indexOf(v); + view = overView.getOrDefault(view, view); + var idx = views.indexOf(view); if(idx < 0) { idx = views.size(); - views.add(v); + views.add(view); } return isNull(vPrefix) ? null : vPrefix + (idx+1); } - public void overView(DBView oldView, DBView newView) { - overView.put(oldView, newView); - } - public String appendArrayParameter(Object[] arr) { return appendArrayParameter(arr, 0); } From b389ca6cb11c7b1bae93f49ebba1dab275a729c0 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 17 Aug 2024 19:38:18 +0200 Subject: [PATCH 128/298] edit --- .../jquery/core/QueryParameterBuilder.java | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index b5dd9392..74dd41dc 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -4,13 +4,13 @@ import static java.util.Collections.unmodifiableMap; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.SqlStringBuilder.COMA; import static org.usf.jquery.core.SqlStringBuilder.EMPTY; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.core.Utils.isEmpty; import java.util.ArrayList; import java.util.List; @@ -34,9 +34,9 @@ public final class QueryParameterBuilder { @Getter private final String schema; private final String vPrefix; - private final List args; + private final List args; //dynamic flag private final List argTypes; - private final List views; //indexed + private final List views; //indexed view private final Map overView; public List views(){ @@ -58,16 +58,16 @@ public String appendArrayParameter(Object[] arr) { } public String appendArrayParameter(Object[] arr, int from) { - if(dynamic()) { - if(isEmpty(arr) || from>=arr.length) { //throw !? - return EMPTY; - } - for(var i=from; i=" + arr.length); } public String appendLiteralArray(Object[] arr) { @@ -75,10 +75,13 @@ public String appendLiteralArray(Object[] arr) { } public String appendLiteralArray(Object[] arr, int from) { - return isEmpty(arr) || from>=arr.length ? EMPTY : Stream.of(arr) //throw !? - .skip(from) - .map(this::appendLiteral) - .collect(joining(SCOMA)); + if(from < requireNonNull(arr).length) { + return Stream.of(arr) + .skip(from) + .map(this::appendLiteral) + .collect(joining(SCOMA)); + } + throw new IllegalStateException(from + ">=" + arr.length); } public String appendParameter(Object o) { From 26c3df65a9760b3eea650642b1e04e88262220ed Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 17 Aug 2024 20:40:07 +0200 Subject: [PATCH 129/298] edit --- .../jquery/core/QueryParameterBuilder.java | 12 ++++--- .../java/org/usf/jquery/core/QueryView.java | 2 +- .../usf/jquery/core/RequestQueryBuilder.java | 32 ++++++------------- .../org/usf/jquery/web/RequestEntryChain.java | 1 - .../jquery/web/RequestQueryParamResolver.java | 16 +++++----- .../org/usf/jquery/web/ViewDecorator.java | 18 +++++++---- 6 files changed, 37 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 74dd41dc..2fa30070 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -2,7 +2,6 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; -import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; @@ -50,7 +49,7 @@ public String view(DBView view) { idx = views.size(); views.add(view); } - return isNull(vPrefix) ? null : vPrefix + (idx+1); + return vPrefix + (idx+1); } public String appendArrayParameter(Object[] arr) { @@ -140,12 +139,15 @@ public QueryParameterBuilder withValue() { } public QueryParameterBuilder subQuery() { - var pre = isNull(vPrefix) ? null : vPrefix + "_s"; - return new QueryParameterBuilder(schema, pre, args, argTypes, new ArrayList<>(), emptyMap()); + return new QueryParameterBuilder(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), emptyMap()); } public static QueryParameterBuilder addWithValue() { - return new QueryParameterBuilder(null, null, null, null, new ArrayList<>(), emptyMap()); //no args + return addWithValue(null, emptyMap()); //no args + } + + public static QueryParameterBuilder addWithValue(String schema, Map overView) { + return new QueryParameterBuilder(schema, "v", null, null, new ArrayList<>(), unmodifiableMap(overView)); //no args } public static QueryParameterBuilder parametrized(String schema, Map overView) { diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index 7ce08216..92e81aeb 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -31,7 +31,7 @@ public String sql(QueryParameterBuilder param) { public Collection columns() { return builder.getColumns(); } - + @Override public String toString() { return sql(addWithValue()); diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 3900976d..318ab09a 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -6,7 +6,6 @@ import static java.util.Objects.nonNull; import static java.util.function.Predicate.not; import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; import static org.usf.jquery.core.Database.TERADATA; import static org.usf.jquery.core.LogicalOperator.AND; import static org.usf.jquery.core.QueryParameterBuilder.parametrized; @@ -15,11 +14,10 @@ import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Validation.requireNonEmpty; +import java.util.ArrayList; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Optional; import lombok.Getter; import lombok.NonNull; @@ -35,10 +33,10 @@ @Getter public class RequestQueryBuilder { - private final List columns = new LinkedList<>(); - private final List filters = new LinkedList<>(); //WHERE & HAVING - private final List orders = new LinkedList<>(); - private final List joins = new LinkedList<>(); + private final List columns = new ArrayList<>(); + private final List filters = new ArrayList<>(); //WHERE & HAVING + private final List orders = new ArrayList<>(); + private final List joins = new ArrayList<>(); private Iterator it; private boolean distinct; private Integer fetch; @@ -47,11 +45,6 @@ public class RequestQueryBuilder { @Setter private Map overView; - public RequestQueryBuilder distinct() { - distinct = true; - return this; - } - public RequestQueryBuilder columns(@NonNull TaggableColumn... columns) { addAll(this.columns, columns); return this; @@ -71,11 +64,6 @@ public RequestQueryBuilder joins(@NonNull ViewJoin... joins) { addAll(this.joins, joins); return this; } - - // the LIMIT clause is not in SQL standard. - public RequestQueryBuilder fetch(Integer offset, Integer fetch) { - return offset(offset).fetch(fetch); - } public RequestQueryBuilder fetch(Integer fetch) { this.fetch = fetch; @@ -92,8 +80,9 @@ public RequestQueryBuilder repeat(@NonNull Iterator it) { return this; } - public Optional getColumn(String id){ - return columns.stream().filter(c-> c.tagname().contains(id)).findAny(); + public RequestQueryBuilder distinct() { + distinct = true; + return this; } public QueryView asView() { @@ -106,7 +95,6 @@ public RequestQuery build(){ public RequestQuery build(String schema) { log.trace("building query..."); -// requireNonEmpty(tables); requireNonEmpty(columns, "columns"); var bg = currentTimeMillis(); var pb = parametrized(schema, overView); //over clause @@ -130,7 +118,7 @@ public final void build(SqlStringBuilder sb, QueryParameterBuilder pb){ orderBy(sub, pb); fetch(sub); select(sb, pb); - from(sb, pb); + from(sb, pb); //enumerate all view before from clause sb.append(sub.toString()); //TODO optim } @@ -189,7 +177,7 @@ void groupBy(SqlStringBuilder sb, QueryParameterBuilder pb){ void having(SqlStringBuilder sb, QueryParameterBuilder pb){ var having = filters.stream() .filter(DBFilter::isAggregation) - .collect(toList()); + .toList(); if(!having.isEmpty()) { sb.append(" HAVING ") .appendEach(having, AND.sql(), f-> f.sql(pb)); diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 5038052f..5bb270e0 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -59,7 +59,6 @@ import org.usf.jquery.core.Order; import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; -import org.usf.jquery.core.QueryParameterBuilder; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedOperator; diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index 4ace5acc..3b000961 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -2,7 +2,6 @@ import static java.lang.System.currentTimeMillis; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; import static org.usf.jquery.web.Constants.VIEW; @@ -14,7 +13,6 @@ import java.util.LinkedHashMap; import java.util.Map; -import java.util.stream.Stream; import org.usf.jquery.core.RequestQueryBuilder; @@ -34,19 +32,21 @@ public final class RequestQueryParamResolver {//spring connection bridge public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull Map parameterMap) { var t = currentTimeMillis(); log.trace("parsing request..."); - parameterMap = new LinkedHashMap<>(parameterMap); //unmodifiable map - if(!parameterMap.containsKey(COLUMN) && !parameterMap.containsKey(COLUMN_DISTINCT)) { - parameterMap.put(COLUMN, ant.defaultColumns()); + parameterMap = new LinkedHashMap<>(parameterMap); //modifiable map + preserve order + if(!parameterMap.containsKey(COLUMN_DISTINCT)) { + parameterMap.computeIfAbsent(COLUMN, k-> ant.defaultColumns()); } if(!isEmpty(ant.ignoreParameters())) { - Stream.of(ant.ignoreParameters()).forEach(parameterMap::remove); + for(var k : ant.ignoreParameters()) { + parameterMap.remove(k); + } } var ctx = ant.database().isEmpty() - ? currentContext() + ? currentContext() : context(ant.database()); try { var req = ctx - .lookupRegisteredView(requireLegalVariable(ant.view())) + .lookupRegisteredView(ant.view()) .orElseThrow(()-> noSuchResourceException(VIEW, ant.view())) .query(parameterMap); //may edit map log.trace("request parsed in {} ms", currentTimeMillis() - t); diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index fac0378d..b247935b 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -99,12 +99,13 @@ default ViewMetadata metadata() { default RequestQueryBuilder query(Map parameterMap) { var query = new RequestQueryBuilder(); - parseViews(query, parameterMap); + parseViews(query, parameterMap); //variable isolation !? parseColumns(query, parameterMap); parseOrders(query, parameterMap); parseJoin(query, parameterMap); parseFetch(query, parameterMap); - parseFilters(query, parameterMap); + parseOffset(query, parameterMap); + parseFilters(query, parameterMap); //remove all entries before parse filters query.setOverView(currentContext().getOverView()); //over clause: after filters return query; } @@ -118,8 +119,8 @@ default void parseViews(RequestQueryBuilder query, Map paramet } default void parseColumns(RequestQueryBuilder query, Map parameters) { - if(parameters.containsKey(COLUMN_DISTINCT) && parameters.containsKey(COLUMN)) { - throw new IllegalArgumentException("cannot use both parameters " + quote(COLUMN_DISTINCT) + " and " + quote(COLUMN)); + if(parameters.containsKey(COLUMN) && parameters.containsKey(COLUMN_DISTINCT)) { + throw new IllegalStateException("both parameters are present " + quote(COLUMN_DISTINCT) + " and " + quote(COLUMN)); } String[] cols; if(parameters.containsKey(COLUMN_DISTINCT)) { @@ -134,7 +135,7 @@ default void parseColumns(RequestQueryBuilder query, Map param } Stream.of(cols) .flatMap(v-> parseEntries(v).stream()) - .map(e-> (TaggableColumn)e.evalColumn(this, true, true)) + .map(e-> (TaggableColumn) e.evalColumn(this, true, true)) .forEach(query::columns); } @@ -154,8 +155,11 @@ default void parseJoin(RequestQueryBuilder query, Map paramete } default void parseFetch(RequestQueryBuilder query, Map parameters) { - query.fetch(requirePositiveInt(OFFSET, parameters), - requirePositiveInt(FETCH, parameters)); + query.fetch(requirePositiveInt(FETCH, parameters)); + } + + default void parseOffset(RequestQueryBuilder query, Map parameters) { + query.fetch(requirePositiveInt(OFFSET, parameters)); } default void parseFilters(RequestQueryBuilder query, Map parameters) { From af23bfceea7c56b3fce61b8a9983e352f9e204e1 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 17 Aug 2024 20:52:01 +0200 Subject: [PATCH 130/298] edit --- .../org/usf/jquery/core/RequestQueryBuilder.java | 12 ++++++------ src/main/java/org/usf/jquery/web/ViewDecorator.java | 11 ++++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 318ab09a..c8c17281 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -65,18 +65,18 @@ public RequestQueryBuilder joins(@NonNull ViewJoin... joins) { return this; } - public RequestQueryBuilder fetch(Integer fetch) { - this.fetch = fetch; + public RequestQueryBuilder repeat(@NonNull Iterator it) { + this.it = it; return this; } - public RequestQueryBuilder offset(Integer offset) { - this.offset = offset; + public RequestQueryBuilder fetch(int fetch) { + this.fetch = fetch; return this; } - public RequestQueryBuilder repeat(@NonNull Iterator it) { - this.it = it; + public RequestQueryBuilder offset(int offset) { + this.offset = offset; return this; } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index b247935b..929b2d6c 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -26,6 +26,7 @@ import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.stream.Stream; import org.usf.jquery.core.DBFilter; @@ -155,11 +156,11 @@ default void parseJoin(RequestQueryBuilder query, Map paramete } default void parseFetch(RequestQueryBuilder query, Map parameters) { - query.fetch(requirePositiveInt(FETCH, parameters)); + requirePositiveInt(FETCH, parameters).ifPresent(query::fetch); } default void parseOffset(RequestQueryBuilder query, Map parameters) { - query.fetch(requirePositiveInt(OFFSET, parameters)); + requirePositiveInt(OFFSET, parameters).ifPresent(query::offset); } default void parseFilters(RequestQueryBuilder query, Map parameters) { @@ -172,19 +173,19 @@ default void parseFilters(RequestQueryBuilder query, Map param .forEach(query::filters); } - private static Integer requirePositiveInt(String key, Map parameters) { + private static Optional requirePositiveInt(String key, Map parameters) { if(parameters.containsKey(key)) { var values = parameters.remove(key); if(values.length == 1) { var v = parseInt(values[0]); if(v >= 0) { - return v; + return Optional.of(v); } throw new IllegalArgumentException(key + " cannot be negative"); } throw new IllegalArgumentException("too many value"); } - return null; + return Optional.empty(); } static Stream flatParameters(String... arr) { //number local separator From 85d32030d6b3edbd46b3407e751d507e4c1d8e4b Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 17 Aug 2024 20:56:49 +0200 Subject: [PATCH 131/298] edit --- src/main/java/org/usf/jquery/core/RequestQueryBuilder.java | 7 +++++-- src/main/java/org/usf/jquery/web/ViewDecorator.java | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index c8c17281..89e7c039 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -21,7 +21,6 @@ import lombok.Getter; import lombok.NonNull; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; /** @@ -42,7 +41,6 @@ public class RequestQueryBuilder { private Integer fetch; private Integer offset; - @Setter private Map overView; public RequestQueryBuilder columns(@NonNull TaggableColumn... columns) { @@ -85,6 +83,11 @@ public RequestQueryBuilder distinct() { return this; } + public RequestQueryBuilder overViews(Map overs) { + overView.putAll(overs); + return this; + } + public QueryView asView() { return new QueryView(this); } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 929b2d6c..f54e036c 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -107,7 +107,7 @@ default RequestQueryBuilder query(Map parameterMap) { parseFetch(query, parameterMap); parseOffset(query, parameterMap); parseFilters(query, parameterMap); //remove all entries before parse filters - query.setOverView(currentContext().getOverView()); //over clause: after filters + query.overViews(currentContext().getOverView()); //over clause: after filters return query; } From 438ec75ba8d18126d3fec99542d511f4f25dcf7c Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 17 Aug 2024 21:00:07 +0200 Subject: [PATCH 132/298] edit --- src/main/java/org/usf/jquery/web/ViewDecorator.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index f54e036c..81a10981 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -131,13 +131,13 @@ default void parseColumns(RequestQueryBuilder query, Map param else { cols = parameters.remove(COLUMN); } - if(isEmpty(cols)) { - throw missingParameterException(COLUMN, COLUMN_DISTINCT); + if(!isEmpty(cols)) { + Stream.of(cols) + .flatMap(v-> parseEntries(v).stream()) + .map(e-> (TaggableColumn) e.evalColumn(this, true, true)) + .forEach(query::columns); } - Stream.of(cols) - .flatMap(v-> parseEntries(v).stream()) - .map(e-> (TaggableColumn) e.evalColumn(this, true, true)) - .forEach(query::columns); + throw missingParameterException(COLUMN, COLUMN_DISTINCT); } default void parseOrders(RequestQueryBuilder query, Map parameters) { From 11bbd635b452ec7a59723a874bc4f86f16a66506 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 17 Aug 2024 23:18:58 +0200 Subject: [PATCH 133/298] edit --- .../java/org/usf/jquery/core/Comparator.java | 4 +-- .../java/org/usf/jquery/core/DBColumn.java | 17 ++++++------ .../java/org/usf/jquery/core/DBQuery.java | 25 ----------------- .../java/org/usf/jquery/core/JQueryType.java | 2 +- .../java/org/usf/jquery/core/QueryColumn.java | 27 +++++++++++++++++++ .../java/org/usf/jquery/core/QueryView.java | 21 +++++++-------- .../usf/jquery/core/RequestQueryBuilder.java | 4 +-- .../org/usf/jquery/web/ArgumentParsers.java | 9 ++----- .../org/usf/jquery/web/RequestEntryChain.java | 24 +++++++++++++++-- .../org/usf/jquery/web/RevisionIterator.java | 2 +- .../org/usf/jquery/web/ViewDecorator.java | 4 ++- 11 files changed, 78 insertions(+), 61 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/core/DBQuery.java create mode 100644 src/main/java/org/usf/jquery/core/QueryColumn.java diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 56994777..eeae3768 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -126,11 +126,11 @@ static TypedComparator notNull() { //isNotNUll //in comparator static TypedComparator in() { - return new TypedComparator(inComparator("IN"), required(), varargs(firstArgJdbcType())); + return new TypedComparator(inComparator("IN"), required(), required(firstArgJdbcType()), varargs(firstArgJdbcType())); } static TypedComparator notIn() { - return new TypedComparator(inComparator("NOT IN"), required(), varargs(firstArgJdbcType())); + return new TypedComparator(inComparator("NOT IN"), required(), required(firstArgJdbcType()), varargs(firstArgJdbcType())); } static BasicComparator basicComparator(final String name) { diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 498f7105..81556fe0 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -56,27 +56,27 @@ default DBOrder order(Order order) { } // filters - default ColumnSingleFilter equal(Object value) { + default ColumnSingleFilter eq(Object value) { return filter(ComparisonExpression.eq(value)); } - default ColumnSingleFilter notEqual(Object value) { + default ColumnSingleFilter ne(Object value) { return filter(ComparisonExpression.ne(value)); } - default ColumnSingleFilter greaterThan(Object value) { + default ColumnSingleFilter gt(Object value) { return filter(ComparisonExpression.gt(value)); } - default ColumnSingleFilter greaterOrEqual(Object value) { + default ColumnSingleFilter ge(Object value) { return filter(ComparisonExpression.ge(value)); } - default ColumnSingleFilter lessThan(Object value) { + default ColumnSingleFilter lt(Object value) { return filter(ComparisonExpression.lt(value)); } - default ColumnSingleFilter lessOrEqual(Object value) { + default ColumnSingleFilter le(Object value) { return filter(ComparisonExpression.le(value)); } @@ -145,11 +145,10 @@ static DBColumn column(@NonNull String value) { } static TaggableColumn allColumns(@NonNull DBView view) { - DBColumn c = b-> { + return ((DBColumn) b-> { b.view(view); return "*"; //avoid view.* as "" - }; - return c.as(null); + }).as(null); } static DBColumn constant(Object value) { diff --git a/src/main/java/org/usf/jquery/core/DBQuery.java b/src/main/java/org/usf/jquery/core/DBQuery.java deleted file mode 100644 index 5d492386..00000000 --- a/src/main/java/org/usf/jquery/core/DBQuery.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.usf.jquery.core; - -import java.util.Collection; - -import org.usf.jquery.core.JavaType.Typed; - -/** - * - * @author u$f - * - */ -public interface DBQuery extends DBView, Typed { - - @Deprecated - Collection columns(); - - @Override - default JDBCType getType() { - var cols = columns(); - if(cols.size() == 1) { - return cols.iterator().next().getType(); - } - throw new UnsupportedOperationException((cols.isEmpty() ? "no columns" : "too many columns") + " : " + this); - } -} diff --git a/src/main/java/org/usf/jquery/core/JQueryType.java b/src/main/java/org/usf/jquery/core/JQueryType.java index 2c55ebb0..e5daaa0a 100644 --- a/src/main/java/org/usf/jquery/core/JQueryType.java +++ b/src/main/java/org/usf/jquery/core/JQueryType.java @@ -12,7 +12,7 @@ public enum JQueryType implements JavaType { VIEW(DBView.class), COLUMN(DBColumn.class), - TAGGABLE(TaggableColumn.class), + NAMED_COLUMN(TaggableColumn.class), FILTER(DBFilter.class), ORDER(DBOrder.class), QUERY(QueryView.class), diff --git a/src/main/java/org/usf/jquery/core/QueryColumn.java b/src/main/java/org/usf/jquery/core/QueryColumn.java new file mode 100644 index 00000000..333a1f38 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/QueryColumn.java @@ -0,0 +1,27 @@ +package org.usf.jquery.core; + +import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.Validation.requireNArgs; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public class QueryColumn implements DBColumn { + + private final QueryView query; + @Getter + private final JDBCType type; + + @Override + public String sql(QueryParameterBuilder builder) { + requireNArgs(1, query.getBuilder().getColumns().toArray(), ()-> ""); //TODO + return query.sql(builder); + } + + @Override + public String toString() { + return sql(addWithValue()); + } +} diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index 92e81aeb..9bf9ea90 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -1,9 +1,6 @@ package org.usf.jquery.core; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; -import static org.usf.jquery.core.SqlStringBuilder.parenthese; - -import java.util.Collection; import lombok.AccessLevel; import lombok.Getter; @@ -15,21 +12,23 @@ * */ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public final class QueryView implements DBQuery { +public final class QueryView implements DBView { @Getter // remove this private final RequestQueryBuilder builder; @Override public String sql(QueryParameterBuilder param) { - var s = new SqlStringBuilder(100); - builder.build(s, param.subQuery()); //important! build query first - return parenthese(s.toString()); + var s = new SqlStringBuilder(100).append("("); + builder.build(s, param.subQuery()); + return s.append(")").toString(); } - - @Override - public Collection columns() { - return builder.getColumns(); + + public DBColumn asColumn(){ + if(builder.getColumns().size() == 1) { + return new QueryColumn(this, builder.getColumns().get(0).getType()); + } + throw new IllegalStateException();//TODO } @Override diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 89e7c039..ebe7413d 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -15,6 +15,7 @@ import static org.usf.jquery.core.Validation.requireNonEmpty; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -36,13 +37,12 @@ public class RequestQueryBuilder { private final List filters = new ArrayList<>(); //WHERE & HAVING private final List orders = new ArrayList<>(); private final List joins = new ArrayList<>(); + private final Map overView = new HashMap<>(); private Iterator it; private boolean distinct; private Integer fetch; private Integer offset; - private Map overView; - public RequestQueryBuilder columns(@NonNull TaggableColumn... columns) { addAll(this.columns, columns); return this; diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index af7abf93..ab87bff8 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -9,8 +9,6 @@ import static org.usf.jquery.core.JDBCType.TIME; import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; -import static org.usf.jquery.core.JQueryType.COLUMN; -import static org.usf.jquery.core.JQueryType.QUERY; import static org.usf.jquery.core.Utils.isEmpty; import java.math.BigDecimal; @@ -59,13 +57,10 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType.. } public static Object parseJdbc(RequestEntryChain entry, ViewDecorator td, JDBCType... types) { - EntryParseException ex = null; // preserve last exception - try { - return matchTypes((Typed) parseJQuery(entry, td, COLUMN, QUERY), types); //try parse column | query first - } catch (EntryParseException e) {/*do not throw exception*/} if(isEmpty(types)) { types = STD_TYPES; } + EntryParseException ex = null; // preserve last exception for(var type : types) { try { return jdbcArgParser(type).parseEntry(entry, td); @@ -118,7 +113,7 @@ public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type) { switch (type) { - case TAGGABLE: return (e,v)-> e.evalColumn(v, true, false); + case NAMED_COLUMN: return (e,v)-> e.evalColumn(v, true, false); case COLUMN: return (e,v)-> e.evalColumn(v, false, false); case FILTER: return RequestEntryChain::evalFilter; case ORDER: return RequestEntryChain::evalOrder; diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 5bb270e0..279445fb 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -57,8 +57,10 @@ import org.usf.jquery.core.LogicalOperator; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; +import org.usf.jquery.core.Parameter; import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; +import org.usf.jquery.core.QueryColumn; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedOperator; @@ -96,7 +98,7 @@ public RequestEntryChain(String value) { } // [view|query]:tag - public ViewDecorator evalView(ViewDecorator vd) { + public ViewDecorator evalView(ViewDecorator vd) { //TODO level isolation try { return currentContext().lookupRegisteredView(value) //check args & next only if view exists .map(v-> new ViewDecoratorWrapper(v, requireNoArgs().requireNoNext().requireTag())) @@ -108,6 +110,10 @@ public ViewDecorator evalView(ViewDecorator vd) { } } + public QueryColumn evalQueryColumn(ViewDecorator td) { + return null; + } + public ViewDecorator evalQuery(ViewDecorator td) { try { return evalQuery(td, false) @@ -377,7 +383,7 @@ private Optional lookupOperation(ViewDecorator vd, DBColumn col } private TaggableColumn[] toTaggableArgs(ViewDecorator td) { - return (TaggableColumn[]) toArgs(td, JQueryType.TAGGABLE); + return (TaggableColumn[]) toArgs(td, JQueryType.NAMED_COLUMN); } private DBColumn[] toColumnArgs(ViewDecorator td) { @@ -404,6 +410,20 @@ private Object[] toArgs(ViewDecorator td, JavaType type) { private Object toOneArg(ViewDecorator td, JavaType type) { return toArgs(td, null, ofParameters(required(type)))[0]; } + + private Object[] toFilterArgs(ViewDecorator td, DBObject col, ParameterSet ps) { + if(ps.getNReqArgs() == 2) { + try { + return toArgs(td, col, ofParameters(required(JQueryType.COLUMN))); + } + catch (Exception e) {} //TODO explicit exception + try { + return toArgs(td, col, ofParameters(required(JQueryType.QUERY))); + } + catch (Exception e) {} //TODO explicit exception + } + return toArgs(td, col, ps); + } private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps) { return toArgs(td, col, ps, Object[]::new); diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index 9198785a..ca390d4f 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -70,7 +70,7 @@ static DBFilter monthFilter(DBColumn column) { return b-> { var values = currentRev.get().getValue(); //get it on build var filter = values.size() == 1 - ? column.equal(values.get(0).getMonthValue()) + ? column.eq(values.get(0).getMonthValue()) : column.in(values.stream().map(YearMonth::getMonthValue).toArray(Integer[]::new)); return filter.sql(b); }; diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 81a10981..45bc7d93 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -137,7 +137,9 @@ default void parseColumns(RequestQueryBuilder query, Map param .map(e-> (TaggableColumn) e.evalColumn(this, true, true)) .forEach(query::columns); } - throw missingParameterException(COLUMN, COLUMN_DISTINCT); + else { + throw missingParameterException(COLUMN, COLUMN_DISTINCT); + } } default void parseOrders(RequestQueryBuilder query, Map parameters) { From a196ba28d8d27dd688735e8c2dd164681f906dd6 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 17 Aug 2024 23:59:13 +0200 Subject: [PATCH 134/298] edit --- .../java/org/usf/jquery/core/JQueryType.java | 1 + .../jquery/core/QueryParameterBuilder.java | 20 +++++++++---------- .../java/org/usf/jquery/core/QueryView.java | 2 +- .../org/usf/jquery/web/ArgumentParsers.java | 15 +++++++------- .../org/usf/jquery/web/RequestEntryChain.java | 17 ++++++++++------ 5 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/JQueryType.java b/src/main/java/org/usf/jquery/core/JQueryType.java index e5daaa0a..2528fa15 100644 --- a/src/main/java/org/usf/jquery/core/JQueryType.java +++ b/src/main/java/org/usf/jquery/core/JQueryType.java @@ -13,6 +13,7 @@ public enum JQueryType implements JavaType { VIEW(DBView.class), COLUMN(DBColumn.class), NAMED_COLUMN(TaggableColumn.class), + QUERY_COLUMN(QueryColumn.class), FILTER(DBFilter.class), ORDER(DBOrder.class), QUERY(QueryView.class), diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 2fa30070..8515aa27 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.stream.Stream; import lombok.AccessLevel; @@ -57,16 +58,9 @@ public String appendArrayParameter(Object[] arr) { } public String appendArrayParameter(Object[] arr, int from) { - if(from < requireNonNull(arr).length) { - if(dynamic()) { - for(var i=from; i=" + arr.length); + return dynamic() + ? appendArray(arr, from, this::appendParameter) + : appendLiteralArray(arr, from); } public String appendLiteralArray(Object[] arr) { @@ -74,10 +68,14 @@ public String appendLiteralArray(Object[] arr) { } public String appendLiteralArray(Object[] arr, int from) { + return appendArray(arr, from, this::appendLiteral); + } + + String appendArray(Object[] arr, int from, Function fn) { if(from < requireNonNull(arr).length) { return Stream.of(arr) .skip(from) - .map(this::appendLiteral) + .map(fn) .collect(joining(SCOMA)); } throw new IllegalStateException(from + ">=" + arr.length); diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index 9bf9ea90..af3e36ae 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -24,7 +24,7 @@ public String sql(QueryParameterBuilder param) { return s.append(")").toString(); } - public DBColumn asColumn(){ + public QueryColumn asColumn(){ if(builder.getColumns().size() == 1) { return new QueryColumn(this, builder.getColumns().get(0).getType()); } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index ab87bff8..47c5594b 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -114,13 +114,14 @@ public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type) { switch (type) { case NAMED_COLUMN: return (e,v)-> e.evalColumn(v, true, false); - case COLUMN: return (e,v)-> e.evalColumn(v, false, false); - case FILTER: return RequestEntryChain::evalFilter; - case ORDER: return RequestEntryChain::evalOrder; - case QUERY: return RequestEntryChain::evalQuery; - case JOIN: return RequestEntryChain::evalJoin; - case PARTITION: return RequestEntryChain::evalPartition; - default: throw unsupportedTypeException(type); + case QUERY_COLUMN: return RequestEntryChain::evalQueryColumn; + case COLUMN: return (e,v)-> e.evalColumn(v, false, false); + case FILTER: return RequestEntryChain::evalFilter; + case ORDER: return RequestEntryChain::evalOrder; + case QUERY: return RequestEntryChain::evalQuery; + case JOIN: return RequestEntryChain::evalJoin; + case PARTITION: return RequestEntryChain::evalPartition; + default: throw unsupportedTypeException(type); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 279445fb..b4eae575 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -61,6 +61,7 @@ import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; import org.usf.jquery.core.QueryColumn; +import org.usf.jquery.core.QueryView; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedOperator; @@ -111,7 +112,10 @@ public ViewDecorator evalView(ViewDecorator vd) { //TODO level isolation } public QueryColumn evalQueryColumn(ViewDecorator td) { - return null; + return evalQuery(td, false) + .map(QueryDecorator::getQuery) + .map(QueryView::asColumn) + .orElseThrow(); //TODO exception } public ViewDecorator evalQuery(ViewDecorator td) { @@ -125,7 +129,7 @@ public ViewDecorator evalQuery(ViewDecorator td) { } //select[.distinct|filter|order|offset|fetch]* - Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub context + Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub context if(SELECT.equals(value)) { var q = new RequestQueryBuilder().columns(toTaggableArgs(td)); var e = this; @@ -244,7 +248,7 @@ public DBFilter evalFilter(ViewDecorator vd, List values) { / if(rc.entry.isLast()) { //no comparator, no criteria var fn = requireNonNull(values).size() == 1 ? eq() : in(); //non empty var e = new RequestEntryChain(null, false, null, values, null); - return fn.filter(e.toArgs(vd, rc.col, fn.getParameterSet())); //no chain + return fn.filter(e.toFilterArgs(vd, rc.col, fn.getParameterSet())); //no chain } return rc.entry.next.columnCriteria(vd, rc.cd, rc.col, values); } @@ -281,7 +285,7 @@ DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List if(cmp.isPresent()) { var fn = cmp.get(); var cp = new RequestEntryChain(null, false, null, assertOuterParameters(values), null); - return chainComparator(vc, fn.filter(cp.toArgs(vc, col, fn.getParameterSet()))); + return chainComparator(vc, fn.filter(cp.toFilterArgs(vc, col, fn.getParameterSet()))); } if(nonNull(cd)) { // no operation var c = cd.criteria(value); @@ -413,12 +417,13 @@ private Object toOneArg(ViewDecorator td, JavaType type) { private Object[] toFilterArgs(ViewDecorator td, DBObject col, ParameterSet ps) { if(ps.getNReqArgs() == 2) { + var first = ps.getParameters()[0]; try { - return toArgs(td, col, ofParameters(required(JQueryType.COLUMN))); + return toArgs(td, col, ofParameters(first, required(JQueryType.COLUMN))); } catch (Exception e) {} //TODO explicit exception try { - return toArgs(td, col, ofParameters(required(JQueryType.QUERY))); + return toArgs(td, col, ofParameters(first, required(JQueryType.QUERY_COLUMN))); } catch (Exception e) {} //TODO explicit exception } From e39e10c107c0b30e9f68c9cd26790949975a168f Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 18 Aug 2024 00:19:18 +0200 Subject: [PATCH 135/298] eidt --- .../org/usf/jquery/core/QueryParameterBuilder.java | 10 +++++----- .../java/org/usf/jquery/web/RequestEntryChain.java | 7 +++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 8515aa27..cda4d754 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -39,10 +39,6 @@ public final class QueryParameterBuilder { private final List views; //indexed view private final Map overView; - public List views(){ - return views; - } - public String view(DBView view) { view = overView.getOrDefault(view, view); var idx = views.indexOf(view); @@ -53,6 +49,10 @@ public String view(DBView view) { return vPrefix + (idx+1); } + public List views(){ + return views; + } + public String appendArrayParameter(Object[] arr) { return appendArrayParameter(arr, 0); } @@ -60,7 +60,7 @@ public String appendArrayParameter(Object[] arr) { public String appendArrayParameter(Object[] arr, int from) { return dynamic() ? appendArray(arr, from, this::appendParameter) - : appendLiteralArray(arr, from); + : appendLiteralArray(arr, from); } public String appendLiteralArray(Object[] arr) { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index b4eae575..74932085 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -57,7 +57,6 @@ import org.usf.jquery.core.LogicalOperator; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; -import org.usf.jquery.core.Parameter; import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; import org.usf.jquery.core.QueryColumn; @@ -99,7 +98,7 @@ public RequestEntryChain(String value) { } // [view|query]:tag - public ViewDecorator evalView(ViewDecorator vd) { //TODO level isolation + public ViewDecorator evalView(ViewDecorator vd) { try { return currentContext().lookupRegisteredView(value) //check args & next only if view exists .map(v-> new ViewDecoratorWrapper(v, requireNoArgs().requireNoNext().requireTag())) @@ -115,10 +114,10 @@ public QueryColumn evalQueryColumn(ViewDecorator td) { return evalQuery(td, false) .map(QueryDecorator::getQuery) .map(QueryView::asColumn) - .orElseThrow(); //TODO exception + .orElseThrow(()-> cannotParseEntryException(QUERY, this)); } - public ViewDecorator evalQuery(ViewDecorator td) { + public ViewDecorator evalQuery(ViewDecorator td) { //TODO level isolation : window function try { return evalQuery(td, false) .orElseThrow(()-> unexpectedEntryValueException(this)); From 91670d3032cf015cf806b3ceb406c757e9f40cde Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 18 Aug 2024 01:09:38 +0200 Subject: [PATCH 136/298] edit --- .../org/usf/jquery/web/ArgumentParsers.java | 69 ++++--------------- .../org/usf/jquery/web/RequestEntryChain.java | 5 +- 2 files changed, 17 insertions(+), 57 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 47c5594b..74f61615 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -1,8 +1,7 @@ package org.usf.jquery.web; -import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; -import static org.usf.jquery.core.BadArgumentException.badArgumentTypeException; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.JDBCType.BIGINT; import static org.usf.jquery.core.JDBCType.DATE; import static org.usf.jquery.core.JDBCType.DOUBLE; @@ -10,6 +9,7 @@ import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import java.math.BigDecimal; import java.sql.Date; @@ -19,14 +19,11 @@ import java.time.LocalDate; import java.time.LocalTime; import java.time.ZonedDateTime; -import java.util.Arrays; -import java.util.function.IntFunction; import java.util.stream.Stream; import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; -import org.usf.jquery.core.JavaType.Typed; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -47,42 +44,25 @@ public class ArgumentParsers { TIME, TIMESTAMP_WITH_TIMEZONE }; public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType... types) { - if(isEmpty(types) || Stream.of(types).allMatch(o-> o.getClass() == JDBCType.class)) { - return parseJdbc(entry, td, castArray(types, JDBCType[].class, JDBCType[]::new)); - } - if(Stream.of(types).allMatch(o-> o.getClass() == JQueryType.class)) { - return parseJQuery(entry, td, castArray(types, JQueryType[].class, JQueryType[]::new)); - } - throw new UnsupportedOperationException("unsupported types " + Arrays.toString(types)); - } - - public static Object parseJdbc(RequestEntryChain entry, ViewDecorator td, JDBCType... types) { if(isEmpty(types)) { types = STD_TYPES; } - EntryParseException ex = null; // preserve last exception for(var type : types) { try { - return jdbcArgParser(type).parseEntry(entry, td); + if(type instanceof JDBCType jt) { + return jdbcArgParser(jt).parseEntry(entry, td); + } + if(type instanceof JQueryType jt) { + return jqueryArgParser(jt).parseEntry(entry, td); + } + else { + throw new UnsupportedOperationException(requireNonNull(type, "type is null").toString()); + } } catch (EntryParseException e) { /*do not throw exception*/ log.trace("parse {} : '{}' => {}", type, entry, e.getMessage()); - ex = e; } } - throw ex; - } - - public static Object parseJQuery(RequestEntryChain entry, ViewDecorator td, JQueryType... types) { - EntryParseException ex = null; // preserve last exception - for(var type : types) { - try { - return jqueryArgParser(type).parseEntry(entry, td); - } catch (EntryParseException e) {/*do not throw exception*/ - log.trace("parse {} : '{}' => {}", type, entry, e.getMessage()); - ex = e; - } - } - throw ex; + throw cannotParseEntryException(Stream.of(types).map(Object::toString).collect(joining("|")), entry); } public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { @@ -124,28 +104,7 @@ public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type) { default: throw unsupportedTypeException(type); } } - - private static Object matchTypes(Typed o, JDBCType... types) { - if(isNull(o.getType()) || isEmpty(types)) { - return o; - } - for(var t : types) { - if(t.accept(o)) { - return o; - } - } - throw badArgumentTypeException(types, o); - } - - private static T[] castArray(JavaType[] types, Class c, IntFunction fn) { - if(nonNull(types)) { - return types.getClass() == c - ? c.cast(types) - : Stream.of(types).toArray(fn); - } - return null; - } - + private static UnsupportedOperationException unsupportedTypeException(JavaType type) { return new UnsupportedOperationException("unsupported type " + type.toString()); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 74932085..6236a195 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -47,6 +47,7 @@ import java.util.function.IntFunction; import java.util.function.Predicate; +import org.usf.jquery.core.AggregateFunction; import org.usf.jquery.core.BadArgumentException; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; @@ -500,8 +501,8 @@ public String toString() { } private static boolean isWindowFunction(TypedOperator op) { - return isCountFunction(op) - || op.unwrap() instanceof WindowFunction; + var fn = op.unwrap(); + return fn instanceof WindowFunction || fn instanceof AggregateFunction; //rank() | sum(col) } private static boolean isCountFunction(TypedOperator fn) { From 268d8a41b65285431c60fce2ddb7c843dd8367ae Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 18 Aug 2024 02:20:41 +0200 Subject: [PATCH 137/298] edit --- .../usf/jquery/core/BadArgumentException.java | 11 +----- src/main/java/org/usf/jquery/core/Utils.java | 11 ++++-- .../org/usf/jquery/web/ArgumentParsers.java | 11 +++--- .../usf/jquery/web/EntrySyntaxException.java | 10 ++++- .../usf/jquery/web/JDBCArgumentParser.java | 5 ++- .../org/usf/jquery/web/RequestEntryChain.java | 39 +++++++++++-------- 6 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/BadArgumentException.java b/src/main/java/org/usf/jquery/core/BadArgumentException.java index 4e791e5a..09c4799e 100644 --- a/src/main/java/org/usf/jquery/core/BadArgumentException.java +++ b/src/main/java/org/usf/jquery/core/BadArgumentException.java @@ -1,8 +1,6 @@ package org.usf.jquery.core; -import static java.util.stream.Collectors.joining; - -import java.util.stream.Stream; +import static org.usf.jquery.core.Utils.join; /** * @@ -21,12 +19,7 @@ public BadArgumentException(String message, Throwable cause) { } public static BadArgumentException badArgumentTypeException(JavaType[] types, Object actual) { - return badArgumentTypeException(types, actual, null); - } - - public static BadArgumentException badArgumentTypeException(JavaType[] types, Object actual, Exception e) { - return new BadArgumentException(formatMessage("bad argument type", - Stream.of(types).map(Object::toString).collect(joining("|")), actual), e); + return new BadArgumentException(formatMessage("bad argument type", join("|", types), actual)); } public static BadArgumentException badArgumentCountException(int count, int actual) { diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 25288ce1..73358bab 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -39,13 +39,16 @@ public static boolean isBlank(String s) { return isNull(s) || s.isBlank(); } - public static String toString(Object[] a) { - return Stream.of(a).map(Object::toString).collect(joining(",")); - } - public static Database currentDatabase() { return context.get(); } + + @SuppressWarnings("unchecked") + public static String join(String delemiter, T... args) { + return isNull(args) + ? null + : Stream.of(args).map(Object::toString).collect(joining(delemiter)); + } @Deprecated public static void currentDatabase(Database db) { diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 74f61615..89191484 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -1,7 +1,6 @@ package org.usf.jquery.web; import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.JDBCType.BIGINT; import static org.usf.jquery.core.JDBCType.DATE; import static org.usf.jquery.core.JDBCType.DOUBLE; @@ -9,6 +8,7 @@ import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.core.Utils.join; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import java.math.BigDecimal; @@ -19,7 +19,6 @@ import java.time.LocalDate; import java.time.LocalTime; import java.time.ZonedDateTime; -import java.util.stream.Stream; import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.JQueryType; @@ -47,6 +46,7 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType.. if(isEmpty(types)) { types = STD_TYPES; } + Exception e = null; for(var type : types) { try { if(type instanceof JDBCType jt) { @@ -58,11 +58,12 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType.. else { throw new UnsupportedOperationException(requireNonNull(type, "type is null").toString()); } - } catch (EntryParseException e) { /*do not throw exception*/ - log.trace("parse {} : '{}' => {}", type, entry, e.getMessage()); + } catch (EntryParseException ex) { /*do not throw exception*/ + log.trace("parse {} : '{}' => {}", type, entry, ex.getMessage()); + e = ex; } } - throw cannotParseEntryException(Stream.of(types).map(Object::toString).collect(joining("|")), entry); + throw cannotParseEntryException(join("|", types), entry, types.length == 1 ? e : null); } public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { diff --git a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java index 4e1738b8..7c86225f 100644 --- a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java +++ b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java @@ -13,18 +13,26 @@ public final class EntrySyntaxException extends WebException { public EntrySyntaxException(String message) { super(message); } + + public EntrySyntaxException(String message, Throwable cause) { + super(message, cause); + } static EntrySyntaxException unexpectedEntryException(RequestEntryChain entry) { return new EntrySyntaxException(format("unexpected entry : %s", entry)); } static EntrySyntaxException unexpectedEntryValueException(RequestEntryChain entry) { - return new EntrySyntaxException(format("unexpected value : %s", entry)); + return new EntrySyntaxException(format("unexpected value : %s", entry)); } static EntrySyntaxException unexpectedEntryArgsException(RequestEntryChain entry) { return new EntrySyntaxException(format("%s takes no args : %s", entry.getValue(), entry)); } + + static EntrySyntaxException badEntryArgsException(RequestEntryChain entry, Exception e) { + return new EntrySyntaxException("bad argument : " + entry, e); + } static EntrySyntaxException expectedEntryTagException(RequestEntryChain entry) { return new EntrySyntaxException(format("expected after '%s'", entry)); diff --git a/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java index c2cbac87..d337dc97 100644 --- a/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java +++ b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java @@ -14,11 +14,14 @@ public interface JDBCArgumentParser extends JavaArgumentParser { @Override default Object parseEntry(RequestEntryChain entry, ViewDecorator td) { + Object v = null; try { - return nativeParse(entry.requireNoArgs().requireNoNext().getValue()); + v = nativeParse(entry.getValue()); } catch(Exception e) { throw cannotParseEntryException("value", entry, e); } + entry.requireNoArgs().requireNoNext(); //check after parse + return v; } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 6236a195..e45468e4 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -9,7 +9,6 @@ import static java.util.Optional.empty; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.Comparator.eq; import static org.usf.jquery.core.Comparator.in; import static org.usf.jquery.core.Comparator.lookupComparator; @@ -35,6 +34,7 @@ import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; +import static org.usf.jquery.web.EntrySyntaxException.badEntryArgsException; import static org.usf.jquery.web.EntrySyntaxException.expectedEntryTagException; import static org.usf.jquery.web.EntrySyntaxException.unexpectedEntryArgsException; import static org.usf.jquery.web.EntrySyntaxException.unexpectedEntryException; @@ -131,21 +131,26 @@ public ViewDecorator evalQuery(ViewDecorator td) { //TODO level isolation : wind //select[.distinct|filter|order|offset|fetch]* Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub context if(SELECT.equals(value)) { - var q = new RequestQueryBuilder().columns(toTaggableArgs(td)); var e = this; - while(e.hasNext()) { - e = e.next; - switch(e.value) {//column not allowed - case DISTINCT: e.requireNoArgs(); q.distinct(); break; - case FILTER: q.filters(e.toFilterArgs(td)); break; - case ORDER: q.orders(e.toOderArgs(td)); break; //not sure - case JOIN: q.joins(e.evalJoin(td)); break; - case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; - case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; - default: throw unexpectedEntryValueException(e); + try { + var q = new RequestQueryBuilder().columns(toTaggableArgs(td)); + while(e.hasNext()) { + e = e.next; + switch(e.value) {//column not allowed + case DISTINCT: e.requireNoArgs(); q.distinct(); break; + case FILTER: q.filters(e.toFilterArgs(td)); break; + case ORDER: q.orders(e.toOderArgs(td)); break; //not sure + case JOIN: q.joins(e.evalJoin(td)); break; + case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; + case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; + default: throw unexpectedEntryValueException(e); + } } + return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); + } + catch (Exception ex) { + throw badEntryArgsException(e, ex); } - return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); } return empty(); } @@ -421,11 +426,11 @@ private Object[] toFilterArgs(ViewDecorator td, DBObject col, ParameterSet ps) { try { return toArgs(td, col, ofParameters(first, required(JQueryType.COLUMN))); } - catch (Exception e) {} //TODO explicit exception + catch (EntryParseException e) {} //TODO explicit exception try { return toArgs(td, col, ofParameters(first, required(JQueryType.QUERY_COLUMN))); } - catch (Exception e) {} //TODO explicit exception + catch (EntryParseException e) {} //TODO explicit exception } return toArgs(td, col, ps); } @@ -451,8 +456,8 @@ private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunc }); return arr; } - catch (EntryParseException | BadArgumentException e) { - throw badArgumentsException(ps.toString(), this.toString(), e); + catch (BadArgumentException e) { + throw badEntryArgsException(this, e); } } From 443fb66d1e2d34ed0fed11042f65a7a51b0db951 Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 18 Aug 2024 02:42:15 +0200 Subject: [PATCH 138/298] edit --- .../java/org/usf/jquery/core/ParameterSet.java | 17 ++++++----------- .../org/usf/jquery/core/TypedComparator.java | 15 +++++++++++++-- .../java/org/usf/jquery/core/TypedOperator.java | 11 +++++++++-- .../jquery/web/RequestQueryParamResolver.java | 4 ++++ 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index df21609d..545eaae2 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -35,17 +35,12 @@ public Object[] assertArguments(Object... args) { public Object[] assertArgumentsFrom(int idx, Object... args) { var arr = isNull(args) ? new Object[0] : args; - try { - forEach(arr.length, (p,i)-> { - if(i>=idx && !p.accept(i, arr)) { - throw badArgumentTypeException(p.types(arr), arr[i]); - } - }); - return arr; - } - catch (BadArgumentException e) { - throw badArgumentsException(this.toString(), Arrays.toString(arr), e); - } + forEach(arr.length, (p,i)-> { + if(i>=idx && !p.accept(i, arr)) { + throw badArgumentTypeException(p.types(arr), arr[i]); + } + }); + return arr; } public void forEach(int nArgs, ObjIntConsumer cons) { diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 6cacb68f..5fccb027 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -1,7 +1,10 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.ParameterSet.ofParameters; +import java.util.Arrays; + import lombok.Getter; /** @@ -21,11 +24,19 @@ public TypedComparator(Comparator comparator, Parameter... parameters) { } public ComparisonExpression expression(Object... right) { - return comparator.expression(parameterSet.assertArgumentsFrom(1, right)); //no left + try { + return comparator.expression(parameterSet.assertArgumentsFrom(1, right)); //no left + } catch (BadArgumentException e) { + throw badArgumentsException(this.toString(), Arrays.toString(right), e); + } } public DBFilter filter(Object... args) { - return comparator.args(parameterSet.assertArguments(args)); + try { + return comparator.args(parameterSet.assertArguments(args)); + } catch (BadArgumentException e) { + throw badArgumentsException(this.toString(), Arrays.toString(args), e); + } } public Comparator unwrap() { diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index a88eeeea..6b158a06 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -1,7 +1,10 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.ParameterSet.ofParameters; +import java.util.Arrays; + import lombok.Getter; /** @@ -27,8 +30,12 @@ public TypedOperator(ArgTypeRef typeFn, Operator function, Parameter... paramete } public OperationColumn operation(Object... args) { - args = parameterSet.assertArguments(args); - return operator.args(typeFn.apply(args), args); + try { + args = parameterSet.assertArguments(args); + return operator.args(typeFn.apply(args), args); + } catch (BadArgumentException e) { + throw badArgumentsException(this.toString(), Arrays.toString(args), e); + } } public Operator unwrap() { diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index 3b000961..070b5da0 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -55,6 +55,10 @@ public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull } throw accessDeniedException("non-aggregate query"); } + catch (Exception e) { + e.printStackTrace(); + throw e; + } finally { releaseContext(); } From 7d6bc5a5e06cf3cd566f88371393a36d965c3772 Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 18 Aug 2024 02:52:15 +0200 Subject: [PATCH 139/298] edit --- src/main/java/org/usf/jquery/core/TypedComparator.java | 6 ++++-- src/main/java/org/usf/jquery/core/TypedOperator.java | 4 +++- src/main/java/org/usf/jquery/core/Utils.java | 7 ++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 5fccb027..788a335e 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -2,6 +2,8 @@ import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.ParameterSet.ofParameters; +import static org.usf.jquery.core.SqlStringBuilder.SCOMA; +import static org.usf.jquery.core.Utils.join; import java.util.Arrays; @@ -27,7 +29,7 @@ public ComparisonExpression expression(Object... right) { try { return comparator.expression(parameterSet.assertArgumentsFrom(1, right)); //no left } catch (BadArgumentException e) { - throw badArgumentsException(this.toString(), Arrays.toString(right), e); + throw badArgumentsException(toString(), comparator.id() + join(SCOMA, "(", ")", right), e); } } @@ -35,7 +37,7 @@ public DBFilter filter(Object... args) { try { return comparator.args(parameterSet.assertArguments(args)); } catch (BadArgumentException e) { - throw badArgumentsException(this.toString(), Arrays.toString(args), e); + throw badArgumentsException(toString(), comparator.id() + join(SCOMA, "(", ")", args), e); } } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 6b158a06..c5e1cb9a 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -2,6 +2,8 @@ import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.ParameterSet.ofParameters; +import static org.usf.jquery.core.SqlStringBuilder.SCOMA; +import static org.usf.jquery.core.Utils.join; import java.util.Arrays; @@ -34,7 +36,7 @@ public OperationColumn operation(Object... args) { args = parameterSet.assertArguments(args); return operator.args(typeFn.apply(args), args); } catch (BadArgumentException e) { - throw badArgumentsException(this.toString(), Arrays.toString(args), e); + throw badArgumentsException(toString(), operator.id() + join(SCOMA, "(", ")", args), e); } } diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 73358bab..86f243eb 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -45,9 +45,14 @@ public static Database currentDatabase() { @SuppressWarnings("unchecked") public static String join(String delemiter, T... args) { + return join(delemiter, "", "", args); + } + + @SuppressWarnings("unchecked") + public static String join(String delemiter, String before, String after, T... args) { return isNull(args) ? null - : Stream.of(args).map(Object::toString).collect(joining(delemiter)); + : Stream.of(args).map(Object::toString).collect(joining(delemiter, before, after)); } @Deprecated From 08e07d6cb0a170abf55db248d980cc72588a908d Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 18 Aug 2024 03:00:41 +0200 Subject: [PATCH 140/298] edit --- src/main/java/org/usf/jquery/core/BadArgumentException.java | 3 ++- src/main/java/org/usf/jquery/core/ParameterSet.java | 2 -- src/main/java/org/usf/jquery/core/TypedComparator.java | 2 -- src/main/java/org/usf/jquery/core/TypedOperator.java | 2 -- 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/BadArgumentException.java b/src/main/java/org/usf/jquery/core/BadArgumentException.java index 09c4799e..6a7ae553 100644 --- a/src/main/java/org/usf/jquery/core/BadArgumentException.java +++ b/src/main/java/org/usf/jquery/core/BadArgumentException.java @@ -19,7 +19,8 @@ public BadArgumentException(String message, Throwable cause) { } public static BadArgumentException badArgumentTypeException(JavaType[] types, Object actual) { - return new BadArgumentException(formatMessage("bad argument type", join("|", types), actual)); + String type = actual instanceof DBColumn c ? ":"+ c.getType() : ""; + return new BadArgumentException(formatMessage("bad argument type", join("|", types), actual + type)); } public static BadArgumentException badArgumentCountException(int count, int actual) { diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index 545eaae2..c64536fd 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -5,9 +5,7 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.BadArgumentException.badArgumentCountException; import static org.usf.jquery.core.BadArgumentException.badArgumentTypeException; -import static org.usf.jquery.core.BadArgumentException.badArgumentsException; -import java.util.Arrays; import java.util.function.ObjIntConsumer; import java.util.stream.Stream; diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 788a335e..15d186ec 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -5,8 +5,6 @@ import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.Utils.join; -import java.util.Arrays; - import lombok.Getter; /** diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index c5e1cb9a..4fea0a52 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -5,8 +5,6 @@ import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.Utils.join; -import java.util.Arrays; - import lombok.Getter; /** From 315f324b99a819b3edab57cbfb8688e9f8999ee9 Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 18 Aug 2024 03:13:56 +0200 Subject: [PATCH 141/298] edit --- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index e45468e4..d27f9d05 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -289,7 +289,7 @@ DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List var cmp = lookupComparator(value); if(cmp.isPresent()) { var fn = cmp.get(); - var cp = new RequestEntryChain(null, false, null, assertOuterParameters(values), null); + var cp = new RequestEntryChain(value, false, null, assertOuterParameters(values), null); return chainComparator(vc, fn.filter(cp.toFilterArgs(vc, col, fn.getParameterSet()))); } if(nonNull(cd)) { // no operation From b171fdaca35fa372c20773ba20aa92ced83409fd Mon Sep 17 00:00:00 2001 From: u$f Date: Sun, 18 Aug 2024 13:19:42 +0200 Subject: [PATCH 142/298] edit --- .../java/org/usf/jquery/core/QueryView.java | 2 +- .../org/usf/jquery/core/TypedComparator.java | 2 +- .../org/usf/jquery/web/ArgumentParsers.java | 2 +- .../usf/jquery/web/EntryParseException.java | 7 +- .../usf/jquery/web/EntrySyntaxException.java | 12 ++ .../org/usf/jquery/web/EvalException.java | 1 + .../jquery/web/NoSuchResourceException.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 198 ++++++++---------- 8 files changed, 107 insertions(+), 119 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index af3e36ae..493e40f4 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -28,7 +28,7 @@ public QueryColumn asColumn(){ if(builder.getColumns().size() == 1) { return new QueryColumn(this, builder.getColumns().get(0).getType()); } - throw new IllegalStateException();//TODO + throw new IllegalStateException("too many column"); } @Override diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 15d186ec..c4cda30a 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -34,7 +34,7 @@ public ComparisonExpression expression(Object... right) { public DBFilter filter(Object... args) { try { return comparator.args(parameterSet.assertArguments(args)); - } catch (BadArgumentException e) { + } catch (BadArgumentException e) { //TODO message throw badArgumentsException(toString(), comparator.id() + join(SCOMA, "(", ")", args), e); } } diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 89191484..402ed505 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -58,7 +58,7 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType.. else { throw new UnsupportedOperationException(requireNonNull(type, "type is null").toString()); } - } catch (EntryParseException ex) { /*do not throw exception*/ + } catch (NoSuchResourceException | EntryParseException ex) { /*do not throw exception*/ log.trace("parse {} : '{}' => {}", type, entry, ex.getMessage()); e = ex; } diff --git a/src/main/java/org/usf/jquery/web/EntryParseException.java b/src/main/java/org/usf/jquery/web/EntryParseException.java index 2c904aaa..94ce24ff 100644 --- a/src/main/java/org/usf/jquery/web/EntryParseException.java +++ b/src/main/java/org/usf/jquery/web/EntryParseException.java @@ -17,7 +17,8 @@ public EntryParseException(String message) { public EntryParseException(String message, Throwable cause) { super(message, cause); } - + + static EntryParseException cannotParseEntryException(String type, RequestEntryChain entry) { return cannotParseEntryException(type, entry, null); } @@ -25,8 +26,4 @@ static EntryParseException cannotParseEntryException(String type, RequestEntryCh static EntryParseException cannotParseEntryException(String type, RequestEntryChain entry, Throwable cause) { return new EntryParseException(format("cannot parse %s : '%s'", type, entry), cause); } - - static EntryParseException badEntryArgsException(String name, RequestEntryChain entry) { - return new EntryParseException(format("%s takes no arg : '%s'", name, entry)); - } } diff --git a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java index 7c86225f..1451bf3f 100644 --- a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java +++ b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java @@ -37,4 +37,16 @@ static EntrySyntaxException badEntryArgsException(RequestEntryChain entry, Excep static EntrySyntaxException expectedEntryTagException(RequestEntryChain entry) { return new EntrySyntaxException(format("expected after '%s'", entry)); } + + + + + static EntrySyntaxException badEntrySyntaxException(String type, String value) { + return badEntrySyntaxException(type, value, null); + } + + static EntrySyntaxException badEntrySyntaxException(String type, String value, Exception e) { + return new EntrySyntaxException(format("bad %s : %s", type, value), e); + } + } diff --git a/src/main/java/org/usf/jquery/web/EvalException.java b/src/main/java/org/usf/jquery/web/EvalException.java index fb7c6a38..840ab4f7 100644 --- a/src/main/java/org/usf/jquery/web/EvalException.java +++ b/src/main/java/org/usf/jquery/web/EvalException.java @@ -7,6 +7,7 @@ * @author u$f * */ +@Deprecated @SuppressWarnings("serial") public final class EvalException extends WebException { diff --git a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java index 8b15da62..be81bd80 100644 --- a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java +++ b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java @@ -16,7 +16,7 @@ public NoSuchResourceException(String s) { } static NoSuchResourceException noSuchResourceException(String type, String resource) { - return new NoSuchResourceException(format("%s: '%s'", type, resource)); + return new NoSuchResourceException(format("no such %s: '%s'", type, resource)); } static NoSuchResourceException undeclaredResouceException(String child, String parent) { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index d27f9d05..19d47878 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -20,6 +20,7 @@ import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.core.Utils.join; import static org.usf.jquery.web.ArgumentParsers.parse; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.DISTINCT; @@ -35,10 +36,9 @@ import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import static org.usf.jquery.web.EntrySyntaxException.badEntryArgsException; +import static org.usf.jquery.web.EntrySyntaxException.badEntrySyntaxException; import static org.usf.jquery.web.EntrySyntaxException.expectedEntryTagException; import static org.usf.jquery.web.EntrySyntaxException.unexpectedEntryArgsException; -import static org.usf.jquery.web.EntrySyntaxException.unexpectedEntryException; -import static org.usf.jquery.web.EntrySyntaxException.unexpectedEntryValueException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import java.util.ArrayList; @@ -46,6 +46,7 @@ import java.util.Optional; import java.util.function.IntFunction; import java.util.function.Predicate; +import java.util.stream.Stream; import org.usf.jquery.core.AggregateFunction; import org.usf.jquery.core.BadArgumentException; @@ -74,20 +75,22 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; -import lombok.extern.slf4j.Slf4j; /** * * @author u$f * */ -@Slf4j @Getter @Setter(value = AccessLevel.PACKAGE) @AllArgsConstructor @RequiredArgsConstructor final class RequestEntryChain { + + private static final String ORDER_PATTERN = enumPattern(Order.class); + private static final String LOGIC_PATTERN = enumPattern(LogicalOperator.class); + private final String value; private final boolean text; //"string" private RequestEntryChain next; @@ -100,15 +103,10 @@ public RequestEntryChain(String value) { // [view|query]:tag public ViewDecorator evalView(ViewDecorator vd) { - try { - return currentContext().lookupRegisteredView(value) //check args & next only if view exists - .map(v-> new ViewDecoratorWrapper(v, requireNoArgs().requireNoNext().requireTag())) - .or(()-> evalQuery(vd, true)) - .orElseThrow(()-> noSuchResourceException(VIEW, value)); - } - catch (Exception e) { - throw cannotParseEntryException(VIEW, this, e); - } + return currentContext().lookupRegisteredView(value) //check args & next only if view exists + .map(v-> new ViewDecoratorWrapper(v, requireNoArgs().requireNoNext().requireTag())) + .or(()-> evalQuery(vd, true)) + .orElseThrow(()-> noSuchResourceException(VIEW, value)); } public QueryColumn evalQueryColumn(ViewDecorator td) { @@ -118,14 +116,9 @@ public QueryColumn evalQueryColumn(ViewDecorator td) { .orElseThrow(()-> cannotParseEntryException(QUERY, this)); } - public ViewDecorator evalQuery(ViewDecorator td) { //TODO level isolation : window function - try { - return evalQuery(td, false) - .orElseThrow(()-> unexpectedEntryValueException(this)); - } - catch (Exception e) { - throw cannotParseEntryException(QUERY, this, e); - } + //TODO level isolation : window function + public ViewDecorator evalQuery(ViewDecorator td) { + return evalQuery(td, false).orElseThrow(()-> cannotParseEntryException(QUERY, this)); } //select[.distinct|filter|order|offset|fetch]* @@ -143,13 +136,13 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub case JOIN: q.joins(e.evalJoin(td)); break; case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; - default: throw unexpectedEntryValueException(e); + default: throw badEntrySyntaxException(join("|", DISTINCT, FILTER, ORDER, JOIN, OFFSET, FETCH), e.value); } } return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); } catch (Exception ex) { - throw badEntryArgsException(e, ex); + throw badEntrySyntaxException(QUERY, e.toString(), ex); } } return empty(); @@ -157,84 +150,66 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub //[view.]joiner public ViewJoin[] evalJoin(ViewDecorator vd) { - try { - var e = this; - if(hasNext()) { - vd = currentContext().lookupRegisteredView(value) - .orElseThrow(()-> noSuchResourceException(VIEW, value)); - e = requireNoArgs().next; //check args only if view exists - } - var join = vd.joiner(e.value); - if(nonNull(join)) { - e.requireNoArgs().requireNoNext(); //check args & next only if joiner exists - return requireNonNull(join.build(), "view.joiner: " + e); - } - throw noSuchResourceException(vd.identity() + ".joiner", e.value); + var e = this; + if(hasNext()) { + vd = currentContext().lookupRegisteredView(value) + .orElseThrow(()-> noSuchResourceException(VIEW, value)); + e = requireNoArgs().next; //check args only if view exists } - catch (Exception e) { - throw cannotParseEntryException(JOIN, this, e); + var join = vd.joiner(e.value); + if(nonNull(join)) { + e.requireNoArgs().requireNoNext(); //check args & next only if joiner exists + return requireNonNull(join.build(), "view.joiner: " + e); } + throw noSuchResourceException(vd.identity() + ".joiner", e.value); } //[partition.order]* public Partition evalPartition(ViewDecorator td) { List cols = new ArrayList<>(); List ords = new ArrayList<>(); - try { - var e = this; - do { - switch (e.value) { - case PARTITION: addAll(cols, toColumnArgs(td)); break; - case ORDER: addAll(ords, e.toOderArgs(td)); break; - default: throw unexpectedEntryValueException(e); - } - e = e.next; - } while(nonNull(e)); - return new Partition( - cols.toArray(DBColumn[]::new), - ords.toArray(DBOrder[]::new)); - } - catch (Exception e) { - throw cannotParseEntryException(PARTITION, this, e); - } + var e = this; + do { + switch (e.value) { + case PARTITION: addAll(cols, toColumnArgs(td)); break; + case ORDER: addAll(ords, e.toOderArgs(td)); break; + default: throw badEntrySyntaxException(join("|", PARTITION, ORDER), e.value); + } + e = e.next; + } while(nonNull(e)); + return new Partition( + cols.toArray(DBColumn[]::new), + ords.toArray(DBOrder[]::new)); } //[view.]column[.operator]* public DBColumn evalColumn(ViewDecorator td, boolean requireTag, boolean declare) { - try { - var r = chainColumnOperations(td, false) - .orElseThrow(()-> noSuchViewColumnException(this)); - r.entry.requireNoNext(); //check next only if column exists - if(nonNull(r.entry.tag)) { - var c = r.col.as(r.entry.tag); - return declare ? currentContext().declareColumn(c) : c; - } - if(!requireTag || r.col instanceof TaggableColumn) { - return r.col; - } - throw expectedEntryTagException(r.entry); - } catch (Exception e) { - throw cannotParseEntryException(COLUMN, this, e); + var r = chainColumnOperations(td, false) + .orElseThrow(()-> noSuchViewColumnException(this)); + r.entry.requireNoNext(); //check next only if column exists + if(nonNull(r.entry.tag)) { + var c = r.col.as(r.entry.tag); + return declare ? currentContext().declareColumn(c) : c; } + if(!requireTag || r.col instanceof TaggableColumn) { + return r.col; + } + throw expectedEntryTagException(r.entry); } //[view.]column[.operator]*[.order] public DBOrder evalOrder(ViewDecorator td) { - try { - var r = chainColumnOperations(td, false) - .orElseThrow(()-> noSuchViewColumnException(this)); - if(r.entry.isLast()) { // default order - return r.col.order(); - } - var ord = r.entry.next; - if(ord.value.matches("asc|desc")) { //check args & next only if order exists - var s = ord.requireNoArgs().requireNoNext().value.toUpperCase(); - return r.col.order(Order.valueOf(s)); - } - throw unexpectedEntryValueException(ord); - } catch (Exception e) { - throw cannotParseEntryException(ORDER, this, e); + var r = chainColumnOperations(td, false) + .orElseThrow(()-> noSuchViewColumnException(this)); + if(r.entry.isLast()) { // default order + return r.col.order(); } + var ord = r.entry.next; + if(ord.value.matches(ORDER_PATTERN)) { //check args & next only if order exists + var s = ord.requireNoArgs().requireNoNext().value.toUpperCase(); + return r.col.order(Order.valueOf(s)); + } + throw badEntrySyntaxException(ORDER_PATTERN, ord.value); } public DBFilter evalFilter(ViewDecorator td) { @@ -243,23 +218,18 @@ public DBFilter evalFilter(ViewDecorator td) { //[view.]criteria | [view.]column.criteria | [view.]column[.operator]*[.comparator] public DBFilter evalFilter(ViewDecorator vd, List values) { //supply values - try { - var res = chainColumnOperations(vd, true); - if(res.isEmpty()) { //not a column - return viewCriteria(vd, values) - .orElseThrow(()-> noSuchViewColumnException(this)); - } - var rc = res.get(); - if(rc.entry.isLast()) { //no comparator, no criteria - var fn = requireNonNull(values).size() == 1 ? eq() : in(); //non empty - var e = new RequestEntryChain(null, false, null, values, null); - return fn.filter(e.toFilterArgs(vd, rc.col, fn.getParameterSet())); //no chain - } - return rc.entry.next.columnCriteria(vd, rc.cd, rc.col, values); + var res = chainColumnOperations(vd, true); + if(res.isEmpty()) { //not a column + return viewCriteria(vd, values) + .orElseThrow(()-> noSuchViewColumnException(this)); } - catch(Exception e) { - throw cannotParseEntryException(FILTER, this, e); + var rc = res.get(); + if(rc.entry.isLast()) { //no comparator, no criteria + var fn = requireNonNull(values).size() == 1 ? eq() : in(); //non empty + var e = new RequestEntryChain(null, false, null, values, null); + return fn.filter(e.toFilterArgs(vd, rc.col, fn.getParameterSet())); //no chain } + return rc.entry.next.columnCriteria(vd, rc.cd, rc.col, values); } //[view.]criteria @@ -317,13 +287,13 @@ private List assertOuterParameters(List va DBFilter chainComparator(ViewDecorator td, DBFilter f) { var e = next; while(nonNull(e)) { - if(e.value.matches("and|or")) { + if(e.value.matches(LOGIC_PATTERN)) { var op = LogicalOperator.valueOf(e.value.toUpperCase()); f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); e = e.next; } else { - throw unexpectedEntryValueException(e); + throw badEntrySyntaxException(LOGIC_PATTERN, e.value); } } return f; @@ -424,13 +394,9 @@ private Object[] toFilterArgs(ViewDecorator td, DBObject col, ParameterSet ps) { if(ps.getNReqArgs() == 2) { var first = ps.getParameters()[0]; try { - return toArgs(td, col, ofParameters(first, required(JQueryType.COLUMN))); + return toArgs(td, col, ofParameters(first, required(JQueryType.QUERY_COLUMN, JQueryType.COLUMN))); } catch (EntryParseException e) {} //TODO explicit exception - try { - return toArgs(td, col, ofParameters(first, required(JQueryType.QUERY_COLUMN))); - } - catch (EntryParseException e) {} //TODO explicit exception } return toArgs(td, col, ps); } @@ -465,7 +431,7 @@ RequestEntryChain requireNoNext() { if(isLast()) { return this; } - throw unexpectedEntryException(next); + throw new EntrySyntaxException("unexpected entry : " + value + "[." + next + "]"); } RequestEntryChain requireNoArgs() { @@ -479,7 +445,7 @@ String requireTag() { if(nonNull(tag)) { return tag; } - throw expectedEntryTagException(this); + throw new EntrySyntaxException("expected tag : " + this + "[:tag]"); } public boolean isLast() { @@ -515,9 +481,12 @@ private static boolean isCountFunction(TypedOperator fn) { } private static String[] toStringArray(List entries) { - return entries.stream() - .map(e-> isNull(e.value) ? null : e.toString()) - .toArray(String[]::new); + if(!isEmpty(entries)) { + return entries.stream() + .map(e-> isNull(e.value) ? null : e.toString()) + .toArray(String[]::new); + } + return new String[0]; } static NoSuchResourceException noSuchViewColumnException(RequestEntryChain e) { @@ -526,6 +495,14 @@ && currentContext().lookupRegisteredColumn(e.value).isPresent() ? e.value + "." + e.next.value : e.value); } + + static String enumPattern(Class> c) { + return Stream.of(c.getEnumConstants()) + .map(Enum::name) + .map(String::toLowerCase) + .collect(joining("|")); + } + @AllArgsConstructor static final class ViewResource { @@ -541,4 +518,5 @@ public String toString() { return vd + "." + cd + " => " + entry.toString(); } } + } \ No newline at end of file From d8916d99dc4a98698d0520b13b05e34a85ba83b7 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 19 Aug 2024 19:01:56 +0200 Subject: [PATCH 143/298] edit --- .../java/org/usf/jquery/core/Parameter.java | 5 +- .../org/usf/jquery/web/RequestEntryChain.java | 55 +++++++++++-------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index ee32e431..3b4bb5b2 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -3,6 +3,7 @@ import static java.util.Objects.isNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.core.Utils.join; import java.util.stream.Stream; @@ -40,9 +41,7 @@ public JavaType[] types(Object[] args) { @Override public String toString() { if(isNull(typeRef)) { - return isEmpty(types) - ? "ANY" - : Stream.of(types).map(Object::toString).collect(joining("|")); + return isEmpty(types) ? "ANY" : join("|", types); } return typeRef.toString(); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 19d47878..c4343e5c 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -115,8 +115,8 @@ public QueryColumn evalQueryColumn(ViewDecorator td) { .map(QueryView::asColumn) .orElseThrow(()-> cannotParseEntryException(QUERY, this)); } - - //TODO level isolation : window function + + //TODO level isolation : window function public ViewDecorator evalQuery(ViewDecorator td) { return evalQuery(td, false).orElseThrow(()-> cannotParseEntryException(QUERY, this)); } @@ -369,19 +369,24 @@ private DBColumn[] toColumnArgs(ViewDecorator td) { return (DBColumn[]) toArgs(td, JQueryType.COLUMN); } - private DBFilter[] toFilterArgs(ViewDecorator td) { - return (DBFilter[]) toArgs(td, JQueryType.FILTER); - } - private DBOrder[] toOderArgs(ViewDecorator td) { return (DBOrder[]) toArgs(td, JQueryType.ORDER); } + + private DBFilter[] toFilterArgs(ViewDecorator td) { + return (DBFilter[]) toArgs(td, JQueryType.FILTER); + } private Object[] toArgs(ViewDecorator td, JavaType type) { var c = type.typeClass(); if(DBObject.class.isAssignableFrom(c)) { // JQuery types & !array var ps = ofParameters(required(type), varargs(type)); - return toArgs(td, null, ps, s-> (Object[]) newInstance(c, s)); + try { + return toArgs(td, null, ps, s-> (Object[]) newInstance(c, s)); + } + catch (Exception e) { + throw badEntryArgsException(this, e); + } } throw new UnsupportedOperationException("cannot instantiate type " + c); } @@ -394,15 +399,22 @@ private Object[] toFilterArgs(ViewDecorator td, DBObject col, ParameterSet ps) { if(ps.getNReqArgs() == 2) { var first = ps.getParameters()[0]; try { - return toArgs(td, col, ofParameters(first, required(JQueryType.QUERY_COLUMN, JQueryType.COLUMN))); + return toArgs(td, col, ofParameters(first, required(JQueryType.QUERY_COLUMN, JQueryType.COLUMN)), Object[]::new); + } + catch (EntryParseException | NoSuchResourceException e) { + //do not throw exception } - catch (EntryParseException e) {} //TODO explicit exception } return toArgs(td, col, ps); } private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps) { - return toArgs(td, col, ps, Object[]::new); + try { + return toArgs(td, col, ps, Object[]::new); + } + catch (Exception e) { + throw badEntryArgsException(this, e); + } } private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunction arrFn) { @@ -411,20 +423,15 @@ private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunc if(nonNull(col)) { arr[0] = col; } - try { - ps.forEach(arr.length, (p,i)-> { - if(i>=inc) { //arg0 already parsed - var e = args.get(i-inc); - arr[i] = isNull(e.value) || e.text - ? e.requireNoArgs().value - : parse(e, td, p.types(arr)); - } - }); - return arr; - } - catch (BadArgumentException e) { - throw badEntryArgsException(this, e); - } + ps.forEach(arr.length, (p,i)-> { + if(i>=inc) { //arg0 already parsed + var e = args.get(i-inc); + arr[i] = isNull(e.value) || e.text + ? e.requireNoArgs().value + : parse(e, td, p.types(arr)); + } + }); + return arr; } RequestEntryChain requireNoNext() { From 343a55cba015f796befb9d578a79e6a6cf364dce Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 19 Aug 2024 20:00:17 +0200 Subject: [PATCH 144/298] edit --- .../org/usf/jquery/web/ArgumentParsers.java | 19 ++++-- .../org/usf/jquery/web/RequestEntryChain.java | 60 +++++++------------ 2 files changed, 35 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 402ed505..38f31ec0 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -1,5 +1,6 @@ package org.usf.jquery.web; +import static java.util.Collections.addAll; import static java.util.Objects.requireNonNull; import static org.usf.jquery.core.JDBCType.BIGINT; import static org.usf.jquery.core.JDBCType.DATE; @@ -7,6 +8,8 @@ import static org.usf.jquery.core.JDBCType.TIME; import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; +import static org.usf.jquery.core.JQueryType.COLUMN; +import static org.usf.jquery.core.JQueryType.QUERY; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Utils.join; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; @@ -19,6 +22,9 @@ import java.time.LocalDate; import java.time.LocalTime; import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.JQueryType; @@ -43,17 +49,20 @@ public class ArgumentParsers { TIME, TIMESTAMP_WITH_TIMEZONE }; public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType... types) { - if(isEmpty(types)) { - types = STD_TYPES; + List list = new ArrayList<>(); + if(isEmpty(types) || Stream.of(types).anyMatch(JDBCType.class::isInstance)) { + list.add(COLUMN); + list.add(QUERY); + addAll(list, isEmpty(types) ? STD_TYPES : types); } Exception e = null; - for(var type : types) { + for(var type : list) { try { if(type instanceof JDBCType jt) { return jdbcArgParser(jt).parseEntry(entry, td); } - if(type instanceof JQueryType jt) { - return jqueryArgParser(jt).parseEntry(entry, td); + if(type instanceof JQueryType t) { + return jqueryArgParser(t).parseEntry(entry, td); } else { throw new UnsupportedOperationException(requireNonNull(type, "type is null").toString()); diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index c4343e5c..185e6e8f 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -227,7 +227,7 @@ public DBFilter evalFilter(ViewDecorator vd, List values) { / if(rc.entry.isLast()) { //no comparator, no criteria var fn = requireNonNull(values).size() == 1 ? eq() : in(); //non empty var e = new RequestEntryChain(null, false, null, values, null); - return fn.filter(e.toFilterArgs(vd, rc.col, fn.getParameterSet())); //no chain + return fn.filter(e.toArgs(vd, rc.col, fn.getParameterSet())); //no chain } return rc.entry.next.columnCriteria(vd, rc.cd, rc.col, values); } @@ -260,7 +260,7 @@ DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List if(cmp.isPresent()) { var fn = cmp.get(); var cp = new RequestEntryChain(value, false, null, assertOuterParameters(values), null); - return chainComparator(vc, fn.filter(cp.toFilterArgs(vc, col, fn.getParameterSet()))); + return chainComparator(vc, fn.filter(cp.toArgs(vc, col, fn.getParameterSet()))); } if(nonNull(cd)) { // no operation var c = cd.criteria(value); @@ -376,45 +376,22 @@ private DBOrder[] toOderArgs(ViewDecorator td) { private DBFilter[] toFilterArgs(ViewDecorator td) { return (DBFilter[]) toArgs(td, JQueryType.FILTER); } + + private Object toOneArg(ViewDecorator td, JavaType type) { + return toArgs(td, null, ofParameters(required(type)))[0]; + } private Object[] toArgs(ViewDecorator td, JavaType type) { var c = type.typeClass(); if(DBObject.class.isAssignableFrom(c)) { // JQuery types & !array var ps = ofParameters(required(type), varargs(type)); - try { - return toArgs(td, null, ps, s-> (Object[]) newInstance(c, s)); - } - catch (Exception e) { - throw badEntryArgsException(this, e); - } + return toArgs(td, null, ps, s-> (Object[]) newInstance(c, s)); } throw new UnsupportedOperationException("cannot instantiate type " + c); } - private Object toOneArg(ViewDecorator td, JavaType type) { - return toArgs(td, null, ofParameters(required(type)))[0]; - } - - private Object[] toFilterArgs(ViewDecorator td, DBObject col, ParameterSet ps) { - if(ps.getNReqArgs() == 2) { - var first = ps.getParameters()[0]; - try { - return toArgs(td, col, ofParameters(first, required(JQueryType.QUERY_COLUMN, JQueryType.COLUMN)), Object[]::new); - } - catch (EntryParseException | NoSuchResourceException e) { - //do not throw exception - } - } - return toArgs(td, col, ps); - } - private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps) { - try { - return toArgs(td, col, ps, Object[]::new); - } - catch (Exception e) { - throw badEntryArgsException(this, e); - } + return toArgs(td, col, ps, Object[]::new); } private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunction arrFn) { @@ -423,14 +400,19 @@ private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunc if(nonNull(col)) { arr[0] = col; } - ps.forEach(arr.length, (p,i)-> { - if(i>=inc) { //arg0 already parsed - var e = args.get(i-inc); - arr[i] = isNull(e.value) || e.text - ? e.requireNoArgs().value - : parse(e, td, p.types(arr)); - } - }); + try { + ps.forEach(arr.length, (p,i)-> { + if(i>=inc) { //arg0 already parsed + var e = args.get(i-inc); + arr[i] = isNull(e.value) || e.text + ? e.requireNoArgs().value + : parse(e, td, p.types(arr)); + } + }); + } + catch (Exception e) { + throw badEntryArgsException(this, e); + } return arr; } From d19d3c9b3526ced1aea5a2c53edfd88d03b2bb14 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 19 Aug 2024 20:46:37 +0200 Subject: [PATCH 145/298] edit --- .../usf/jquery/web/EntrySyntaxException.java | 20 +------- .../org/usf/jquery/web/RequestEntryChain.java | 46 ++++++++++--------- 2 files changed, 25 insertions(+), 41 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java index 1451bf3f..11534431 100644 --- a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java +++ b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java @@ -18,27 +18,9 @@ public EntrySyntaxException(String message, Throwable cause) { super(message, cause); } - static EntrySyntaxException unexpectedEntryException(RequestEntryChain entry) { - return new EntrySyntaxException(format("unexpected entry : %s", entry)); - } - - static EntrySyntaxException unexpectedEntryValueException(RequestEntryChain entry) { - return new EntrySyntaxException(format("unexpected value : %s", entry)); - } - - static EntrySyntaxException unexpectedEntryArgsException(RequestEntryChain entry) { - return new EntrySyntaxException(format("%s takes no args : %s", entry.getValue(), entry)); - } - static EntrySyntaxException badEntryArgsException(RequestEntryChain entry, Exception e) { return new EntrySyntaxException("bad argument : " + entry, e); - } - - static EntrySyntaxException expectedEntryTagException(RequestEntryChain entry) { - return new EntrySyntaxException(format("expected after '%s'", entry)); - } - - + } static EntrySyntaxException badEntrySyntaxException(String type, String value) { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 185e6e8f..20ca590b 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -1,6 +1,8 @@ package org.usf.jquery.web; +import static java.lang.String.format; import static java.lang.reflect.Array.newInstance; +import static java.util.Arrays.asList; import static java.util.Collections.addAll; import static java.util.Collections.emptyList; import static java.util.Objects.isNull; @@ -35,10 +37,7 @@ import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; -import static org.usf.jquery.web.EntrySyntaxException.badEntryArgsException; import static org.usf.jquery.web.EntrySyntaxException.badEntrySyntaxException; -import static org.usf.jquery.web.EntrySyntaxException.expectedEntryTagException; -import static org.usf.jquery.web.EntrySyntaxException.unexpectedEntryArgsException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import java.util.ArrayList; @@ -49,7 +48,6 @@ import java.util.stream.Stream; import org.usf.jquery.core.AggregateFunction; -import org.usf.jquery.core.BadArgumentException; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; @@ -66,6 +64,7 @@ import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedOperator; +import org.usf.jquery.core.Utils; import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; import org.usf.jquery.core.WindowFunction; @@ -86,7 +85,6 @@ @AllArgsConstructor @RequiredArgsConstructor final class RequestEntryChain { - private static final String ORDER_PATTERN = enumPattern(Order.class); private static final String LOGIC_PATTERN = enumPattern(LogicalOperator.class); @@ -194,7 +192,7 @@ public DBColumn evalColumn(ViewDecorator td, boolean requireTag, boolean declare if(!requireTag || r.col instanceof TaggableColumn) { return r.col; } - throw expectedEntryTagException(r.entry); + throw expectedEntryTagException(); } //[view.]column[.operator]*[.order] @@ -376,10 +374,6 @@ private DBOrder[] toOderArgs(ViewDecorator td) { private DBFilter[] toFilterArgs(ViewDecorator td) { return (DBFilter[]) toArgs(td, JQueryType.FILTER); } - - private Object toOneArg(ViewDecorator td, JavaType type) { - return toArgs(td, null, ofParameters(required(type)))[0]; - } private Object[] toArgs(ViewDecorator td, JavaType type) { var c = type.typeClass(); @@ -390,6 +384,10 @@ private Object[] toArgs(ViewDecorator td, JavaType type) { throw new UnsupportedOperationException("cannot instantiate type " + c); } + private Object toOneArg(ViewDecorator td, JavaType type) { + return toArgs(td, null, ofParameters(required(type)))[0]; + } + private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps) { return toArgs(td, col, ps, Object[]::new); } @@ -411,30 +409,34 @@ private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunc }); } catch (Exception e) { - throw badEntryArgsException(this, e); + throw new EntrySyntaxException(format("bad entry arguments : %s[(%s)]", value, isNull(args) ? "" : join(", ", args.toArray())), e); } return arr; } - - RequestEntryChain requireNoNext() { - if(isLast()) { - return this; + + String requireTag() { + if(nonNull(tag)) { + return tag; } - throw new EntrySyntaxException("unexpected entry : " + value + "[." + next + "]"); + throw new EntrySyntaxException(format("expected tag : %s[:tag]", this)); } RequestEntryChain requireNoArgs() { if(isNull(args)) { return this; } - throw unexpectedEntryArgsException(this); + throw new EntrySyntaxException(format("unexpected entry args : %s[(%s)]", value, join(", ", args.toArray()))); } - - String requireTag() { - if(nonNull(tag)) { - return tag; + + RequestEntryChain requireNoNext() { + if(isLast()) { + return this; } - throw new EntrySyntaxException("expected tag : " + this + "[:tag]"); + throw expectedEntryTagException(); + } + + EntrySyntaxException expectedEntryTagException() { + throw new EntrySyntaxException(format("unexpected entry : %s[.%s]", value, next)); } public boolean isLast() { From 9f39b55efb99fdc8fe47d832824da5d42b5d7061 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 19 Aug 2024 21:46:30 +0200 Subject: [PATCH 146/298] edit --- .../usf/jquery/core/BadArgumentException.java | 2 +- .../java/org/usf/jquery/core/Parameter.java | 2 +- .../org/usf/jquery/core/TypedComparator.java | 6 +-- .../org/usf/jquery/core/TypedOperator.java | 4 +- src/main/java/org/usf/jquery/core/Utils.java | 4 +- .../usf/jquery/web/EntrySyntaxException.java | 16 ------ .../org/usf/jquery/web/RequestEntryChain.java | 53 ++++++++++--------- 7 files changed, 37 insertions(+), 50 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/BadArgumentException.java b/src/main/java/org/usf/jquery/core/BadArgumentException.java index 6a7ae553..6c958e2e 100644 --- a/src/main/java/org/usf/jquery/core/BadArgumentException.java +++ b/src/main/java/org/usf/jquery/core/BadArgumentException.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Utils.join; +import static org.usf.jquery.core.Utils.joinAndDelemit; /** * diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 3b4bb5b2..9ec36764 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -1,9 +1,9 @@ package org.usf.jquery.core; import static java.util.Objects.isNull; -import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Utils.join; +import static org.usf.jquery.core.Utils.joinAndDelemit; import java.util.stream.Stream; diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index c4cda30a..9e84c087 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -3,7 +3,7 @@ import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.Utils.join; +import static org.usf.jquery.core.Utils.joinAndDelemit; import lombok.Getter; @@ -27,7 +27,7 @@ public ComparisonExpression expression(Object... right) { try { return comparator.expression(parameterSet.assertArgumentsFrom(1, right)); //no left } catch (BadArgumentException e) { - throw badArgumentsException(toString(), comparator.id() + join(SCOMA, "(", ")", right), e); + throw badArgumentsException(toString(), comparator.id() + joinAndDelemit(SCOMA, "(", ")", right), e); } } @@ -35,7 +35,7 @@ public DBFilter filter(Object... args) { try { return comparator.args(parameterSet.assertArguments(args)); } catch (BadArgumentException e) { //TODO message - throw badArgumentsException(toString(), comparator.id() + join(SCOMA, "(", ")", args), e); + throw badArgumentsException(toString(), comparator.id() + joinAndDelemit(SCOMA, "(", ")", args), e); } } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 4fea0a52..ca91a058 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -3,7 +3,7 @@ import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.Utils.join; +import static org.usf.jquery.core.Utils.joinAndDelemit; import lombok.Getter; @@ -34,7 +34,7 @@ public OperationColumn operation(Object... args) { args = parameterSet.assertArguments(args); return operator.args(typeFn.apply(args), args); } catch (BadArgumentException e) { - throw badArgumentsException(toString(), operator.id() + join(SCOMA, "(", ")", args), e); + throw badArgumentsException(toString(), operator.id() + joinAndDelemit(SCOMA, "(", ")", args), e); } } diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 86f243eb..59e76305 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -45,11 +45,11 @@ public static Database currentDatabase() { @SuppressWarnings("unchecked") public static String join(String delemiter, T... args) { - return join(delemiter, "", "", args); + return joinAndDelemit(delemiter, "", "", args); } @SuppressWarnings("unchecked") - public static String join(String delemiter, String before, String after, T... args) { + public static String joinAndDelemit(String delemiter, String before, String after, T... args) { return isNull(args) ? null : Stream.of(args).map(Object::toString).collect(joining(delemiter, before, after)); diff --git a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java index 11534431..68d9c253 100644 --- a/src/main/java/org/usf/jquery/web/EntrySyntaxException.java +++ b/src/main/java/org/usf/jquery/web/EntrySyntaxException.java @@ -1,7 +1,5 @@ package org.usf.jquery.web; -import static java.lang.String.format; - /** * * @author u$f @@ -17,18 +15,4 @@ public EntrySyntaxException(String message) { public EntrySyntaxException(String message, Throwable cause) { super(message, cause); } - - static EntrySyntaxException badEntryArgsException(RequestEntryChain entry, Exception e) { - return new EntrySyntaxException("bad argument : " + entry, e); - } - - - static EntrySyntaxException badEntrySyntaxException(String type, String value) { - return badEntrySyntaxException(type, value, null); - } - - static EntrySyntaxException badEntrySyntaxException(String type, String value, Exception e) { - return new EntrySyntaxException(format("bad %s : %s", type, value), e); - } - } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 20ca590b..e7be5436 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -2,7 +2,6 @@ import static java.lang.String.format; import static java.lang.reflect.Array.newInstance; -import static java.util.Arrays.asList; import static java.util.Collections.addAll; import static java.util.Collections.emptyList; import static java.util.Objects.isNull; @@ -37,7 +36,6 @@ import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; -import static org.usf.jquery.web.EntrySyntaxException.badEntrySyntaxException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import java.util.ArrayList; @@ -124,13 +122,13 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub if(SELECT.equals(value)) { var e = this; try { - var q = new RequestQueryBuilder().columns(toTaggableArgs(td)); + var q = new RequestQueryBuilder().columns(taggableVarargs(td)); while(e.hasNext()) { e = e.next; switch(e.value) {//column not allowed case DISTINCT: e.requireNoArgs(); q.distinct(); break; - case FILTER: q.filters(e.toFilterArgs(td)); break; - case ORDER: q.orders(e.toOderArgs(td)); break; //not sure + case FILTER: q.filters(e.filterVarargs(td)); break; + case ORDER: q.orders(e.oderVarargs(td)); break; //not sure case JOIN: q.joins(e.evalJoin(td)); break; case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; @@ -140,7 +138,7 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); } catch (Exception ex) { - throw badEntrySyntaxException(QUERY, e.toString(), ex); + throw new EntrySyntaxException("incorrect query syntax: " + e); } } return empty(); @@ -169,9 +167,11 @@ public Partition evalPartition(ViewDecorator td) { var e = this; do { switch (e.value) { - case PARTITION: addAll(cols, toColumnArgs(td)); break; - case ORDER: addAll(ords, e.toOderArgs(td)); break; - default: throw badEntrySyntaxException(join("|", PARTITION, ORDER), e.value); + case PARTITION: addAll(cols, columnVarargs(td)); break; + case ORDER: addAll(ords, e.oderVarargs(td)); break; + default: throw e==this + ? cannotParseEntryException(PARTITION, e) //first entry + : badEntrySyntaxException(join("|", PARTITION, ORDER), e.value); } e = e.next; } while(nonNull(e)); @@ -192,7 +192,7 @@ public DBColumn evalColumn(ViewDecorator td, boolean requireTag, boolean declare if(!requireTag || r.col instanceof TaggableColumn) { return r.col; } - throw expectedEntryTagException(); + throw expectedEntryTagException(r.entry); } //[view.]column[.operator]*[.order] @@ -359,23 +359,23 @@ private Optional lookupOperation(ViewDecorator vd, DBColumn col }); } - private TaggableColumn[] toTaggableArgs(ViewDecorator td) { - return (TaggableColumn[]) toArgs(td, JQueryType.NAMED_COLUMN); + private TaggableColumn[] taggableVarargs(ViewDecorator td) { + return (TaggableColumn[]) typeVarargs(td, JQueryType.NAMED_COLUMN); } - private DBColumn[] toColumnArgs(ViewDecorator td) { - return (DBColumn[]) toArgs(td, JQueryType.COLUMN); + private DBColumn[] columnVarargs(ViewDecorator td) { + return (DBColumn[]) typeVarargs(td, JQueryType.COLUMN); } - private DBOrder[] toOderArgs(ViewDecorator td) { - return (DBOrder[]) toArgs(td, JQueryType.ORDER); + private DBOrder[] oderVarargs(ViewDecorator td) { + return (DBOrder[]) typeVarargs(td, JQueryType.ORDER); } - private DBFilter[] toFilterArgs(ViewDecorator td) { - return (DBFilter[]) toArgs(td, JQueryType.FILTER); + private DBFilter[] filterVarargs(ViewDecorator td) { + return (DBFilter[]) typeVarargs(td, JQueryType.FILTER); } - private Object[] toArgs(ViewDecorator td, JavaType type) { + private Object[] typeVarargs(ViewDecorator td, JavaType type) { var c = type.typeClass(); if(DBObject.class.isAssignableFrom(c)) { // JQuery types & !array var ps = ofParameters(required(type), varargs(type)); @@ -425,18 +425,14 @@ RequestEntryChain requireNoArgs() { if(isNull(args)) { return this; } - throw new EntrySyntaxException(format("unexpected entry args : %s[(%s)]", value, join(", ", args.toArray()))); + throw new EntrySyntaxException(format("unexpected entry args : %s[(%s)]", value, join(", ", args.toArray()))); } RequestEntryChain requireNoNext() { if(isLast()) { return this; } - throw expectedEntryTagException(); - } - - EntrySyntaxException expectedEntryTagException() { - throw new EntrySyntaxException(format("unexpected entry : %s[.%s]", value, next)); + throw expectedEntryTagException(this); } public boolean isLast() { @@ -494,6 +490,13 @@ static String enumPattern(Class> c) { .collect(joining("|")); } + static EntrySyntaxException badEntrySyntaxException(String type, String value) { + return new EntrySyntaxException(format("incorrect syntax expected: %s, bat was: %s", type, value)); + } + + static EntrySyntaxException expectedEntryTagException(RequestEntryChain e) { + throw new EntrySyntaxException(format("unexpected entry : %s[.%s]", e.value, e.next)); + } @AllArgsConstructor static final class ViewResource { From 64ed42dde338beff007c3cc8f9f9ddf53aba2197 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 19 Aug 2024 22:00:34 +0200 Subject: [PATCH 147/298] edit --- src/main/java/org/usf/jquery/core/BadArgumentException.java | 2 +- src/main/java/org/usf/jquery/web/ArgumentParsers.java | 6 +++--- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/BadArgumentException.java b/src/main/java/org/usf/jquery/core/BadArgumentException.java index 6c958e2e..6a7ae553 100644 --- a/src/main/java/org/usf/jquery/core/BadArgumentException.java +++ b/src/main/java/org/usf/jquery/core/BadArgumentException.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Utils.joinAndDelemit; +import static org.usf.jquery.core.Utils.join; /** * diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 38f31ec0..cef1fec7 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -9,7 +9,7 @@ import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; import static org.usf.jquery.core.JQueryType.COLUMN; -import static org.usf.jquery.core.JQueryType.QUERY; +import static org.usf.jquery.core.JQueryType.QUERY_COLUMN; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Utils.join; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; @@ -52,9 +52,9 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType.. List list = new ArrayList<>(); if(isEmpty(types) || Stream.of(types).anyMatch(JDBCType.class::isInstance)) { list.add(COLUMN); - list.add(QUERY); - addAll(list, isEmpty(types) ? STD_TYPES : types); + list.add(QUERY_COLUMN); } + addAll(list, isEmpty(types) ? STD_TYPES : types); Exception e = null; for(var type : list) { try { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index e7be5436..cbda7890 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -138,7 +138,7 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); } catch (Exception ex) { - throw new EntrySyntaxException("incorrect query syntax: " + e); + throw new EntrySyntaxException("incorrect query syntax: " + e, ex); } } return empty(); From 26f9097a5146ef0a31cfb7d86dd5b7697b58f43a Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 20 Aug 2024 09:38:21 +0200 Subject: [PATCH 148/298] edit --- .../jquery/core/QueryParameterBuilder.java | 12 +++++++++-- .../usf/jquery/core/RequestQueryBuilder.java | 2 +- .../org/usf/jquery/web/ArgumentParsers.java | 20 +++++++------------ 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index cda4d754..e4d34df6 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -2,6 +2,7 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; +import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; @@ -10,10 +11,12 @@ import static org.usf.jquery.core.SqlStringBuilder.EMPTY; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.SqlStringBuilder.quote; +import static org.usf.jquery.core.Utils.isEmpty; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Function; import java.util.stream.Stream; @@ -72,13 +75,18 @@ public String appendLiteralArray(Object[] arr, int from) { } String appendArray(Object[] arr, int from, Function fn) { - if(from < requireNonNull(arr).length) { + if(isEmpty(arr)) { + if(from == 0) { + return EMPTY; + } + } + else if(from < arr.length) { return Stream.of(arr) .skip(from) .map(fn) .collect(joining(SCOMA)); } - throw new IllegalStateException(from + ">=" + arr.length); + throw new IndexOutOfBoundsException(from + ">=" + requireNonNull(arr, "arr is null").length); } public String appendParameter(Object o) { diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index ebe7413d..4ff04c62 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -169,7 +169,7 @@ void groupBy(SqlStringBuilder sb, QueryParameterBuilder pb){ var expr = columns.stream() .filter(not(DBColumn::isAggregation)) .flatMap(DBColumn::groupKeys) - .map(c-> columns.contains(c) ? ((TaggableColumn)c).tagname() : c.sql(pb)) //add alias + .map(c-> !(c instanceof ViewColumn) && columns.contains(c) ? ((TaggableColumn)c).tagname() : c.sql(pb)) //add alias .collect(joining(SCOMA)); if(!expr.isEmpty()) { sb.append(" GROUP BY ").append(expr); diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index cef1fec7..29d5d297 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -58,8 +58,8 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType.. Exception e = null; for(var type : list) { try { - if(type instanceof JDBCType jt) { - return jdbcArgParser(jt).parseEntry(entry, td); + if(type instanceof JDBCType t) { + return jdbcArgParser(t).parseEntry(entry, td); } if(type instanceof JQueryType t) { return jqueryArgParser(t).parseEntry(entry, td); @@ -77,21 +77,15 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType.. public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { switch (type) { - case BOOLEAN: return Boolean::parseBoolean; - case BIT: return Boolean::parseBoolean; + case BOOLEAN, BIT: return Boolean::parseBoolean; case TINYINT: return Byte::parseByte; case SMALLINT: return Short::parseShort; case INTEGER: return Integer::parseInt; case BIGINT: return Long::parseLong; case REAL: return Float::parseFloat; - case FLOAT: return Double::parseDouble; - case DOUBLE: return Double::parseDouble; - case NUMERIC: return BigDecimal::new; - case DECIMAL: return BigDecimal::new; - case CHAR: return v-> v; - case VARCHAR: return v-> v; - case NVARCHAR: return v-> v; - case LONGNVARCHAR: return v-> v; + case FLOAT, DOUBLE: return Double::parseDouble; + case NUMERIC, DECIMAL: return BigDecimal::new; + case CHAR, VARCHAR, NVARCHAR, LONGNVARCHAR: return v-> v; case DATE: return v-> Date.valueOf(LocalDate.parse(v)); case TIME: return v-> Time.valueOf(LocalTime.parse(v)); case TIMESTAMP: return v-> Timestamp.from(Instant.parse(v)); @@ -103,8 +97,8 @@ public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type) { switch (type) { - case NAMED_COLUMN: return (e,v)-> e.evalColumn(v, true, false); case QUERY_COLUMN: return RequestEntryChain::evalQueryColumn; + case NAMED_COLUMN: return (e,v)-> e.evalColumn(v, true, false); case COLUMN: return (e,v)-> e.evalColumn(v, false, false); case FILTER: return RequestEntryChain::evalFilter; case ORDER: return RequestEntryChain::evalOrder; From f615b0a4dd8338f24648b541c329532ae4c0c824 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 20 Aug 2024 10:43:51 +0200 Subject: [PATCH 149/298] edit --- .../usf/jquery/core/BadArgumentException.java | 4 ++-- src/main/java/org/usf/jquery/core/Operator.java | 2 +- .../java/org/usf/jquery/core/Parameter.java | 6 +++--- .../org/usf/jquery/core/TypedComparator.java | 6 +++--- .../java/org/usf/jquery/core/TypedOperator.java | 4 ++-- src/main/java/org/usf/jquery/core/Utils.java | 17 +++++++++++++---- .../org/usf/jquery/web/ArgumentParsers.java | 6 +++--- .../org/usf/jquery/web/EntryParseException.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 16 ++++++++-------- 9 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/BadArgumentException.java b/src/main/java/org/usf/jquery/core/BadArgumentException.java index 6a7ae553..169608aa 100644 --- a/src/main/java/org/usf/jquery/core/BadArgumentException.java +++ b/src/main/java/org/usf/jquery/core/BadArgumentException.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Utils.join; +import static org.usf.jquery.core.Utils.joinArray; /** * @@ -20,7 +20,7 @@ public BadArgumentException(String message, Throwable cause) { public static BadArgumentException badArgumentTypeException(JavaType[] types, Object actual) { String type = actual instanceof DBColumn c ? ":"+ c.getType() : ""; - return new BadArgumentException(formatMessage("bad argument type", join("|", types), actual + type)); + return new BadArgumentException(formatMessage("bad argument type", joinArray("|", types), actual + type)); } public static BadArgumentException badArgumentCountException(int count, int actual) { diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index dcdde603..f66abb82 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -199,7 +199,7 @@ static TypedOperator epoch() { //cast functions static TypedOperator varchar() { - return new TypedOperator(VARCHAR, cast("VARCHAR"), required(), required(INTEGER)); //any + return new TypedOperator(VARCHAR, cast("VARCHAR"), required(), optional(INTEGER)); //any } static TypedOperator date() { diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 9ec36764..29a88519 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -2,8 +2,8 @@ import static java.util.Objects.isNull; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Utils.join; -import static org.usf.jquery.core.Utils.joinAndDelemit; +import static org.usf.jquery.core.Utils.joinArray; +import static org.usf.jquery.core.Utils.joinAndDelemitArray; import java.util.stream.Stream; @@ -41,7 +41,7 @@ public JavaType[] types(Object[] args) { @Override public String toString() { if(isNull(typeRef)) { - return isEmpty(types) ? "ANY" : join("|", types); + return isEmpty(types) ? "ANY" : joinArray("|", types); } return typeRef.toString(); } diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 9e84c087..0372e1d7 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -3,7 +3,7 @@ import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.Utils.joinAndDelemit; +import static org.usf.jquery.core.Utils.joinAndDelemitArray; import lombok.Getter; @@ -27,7 +27,7 @@ public ComparisonExpression expression(Object... right) { try { return comparator.expression(parameterSet.assertArgumentsFrom(1, right)); //no left } catch (BadArgumentException e) { - throw badArgumentsException(toString(), comparator.id() + joinAndDelemit(SCOMA, "(", ")", right), e); + throw badArgumentsException(toString(), comparator.id() + joinAndDelemitArray(SCOMA, "(", ")", right), e); } } @@ -35,7 +35,7 @@ public DBFilter filter(Object... args) { try { return comparator.args(parameterSet.assertArguments(args)); } catch (BadArgumentException e) { //TODO message - throw badArgumentsException(toString(), comparator.id() + joinAndDelemit(SCOMA, "(", ")", args), e); + throw badArgumentsException(toString(), comparator.id() + joinAndDelemitArray(SCOMA, "(", ")", args), e); } } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index ca91a058..8a8a9905 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -3,7 +3,7 @@ import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.Utils.joinAndDelemit; +import static org.usf.jquery.core.Utils.joinAndDelemitArray; import lombok.Getter; @@ -34,7 +34,7 @@ public OperationColumn operation(Object... args) { args = parameterSet.assertArguments(args); return operator.args(typeFn.apply(args), args); } catch (BadArgumentException e) { - throw badArgumentsException(toString(), operator.id() + joinAndDelemit(SCOMA, "(", ")", args), e); + throw badArgumentsException(toString(), operator.id() + joinAndDelemitArray(SCOMA, "(", ")", args), e); } } diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 59e76305..cf446bb7 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -44,15 +44,24 @@ public static Database currentDatabase() { } @SuppressWarnings("unchecked") - public static String join(String delemiter, T... args) { - return joinAndDelemit(delemiter, "", "", args); + public static String joinArray(String delemiter, T... args) { + return joinAndDelemitArray(delemiter, "", "", args); } @SuppressWarnings("unchecked") - public static String joinAndDelemit(String delemiter, String before, String after, T... args) { + public static String joinAndDelemitArray(String delemiter, String before, String after, T... args) { return isNull(args) ? null - : Stream.of(args).map(Object::toString).collect(joining(delemiter, before, after)); + : joinAndDelemit(delemiter, before, after, Stream.of(args)); + } + + + public static String join(String delemiter, Stream args) { + return joinAndDelemit(delemiter, "", "", args); + } + + public static String joinAndDelemit(String delemiter, String before, String after, Stream args) { + return args.map(Object::toString).collect(joining(delemiter, before, after)); } @Deprecated diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 29d5d297..5cc77a36 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -67,12 +67,12 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType.. else { throw new UnsupportedOperationException(requireNonNull(type, "type is null").toString()); } - } catch (NoSuchResourceException | EntryParseException ex) { /*do not throw exception*/ - log.trace("parse {} : '{}' => {}", type, entry, ex.getMessage()); + } catch (NoSuchResourceException | EntryParseException ex) { //do not throw exception + log.trace("parse '{}' as {} => {}", entry, type, ex.getMessage()); e = ex; } } - throw cannotParseEntryException(join("|", types), entry, types.length == 1 ? e : null); + throw cannotParseEntryException(join("|", list.stream()), entry, list.size() > 1 ? e : null); } public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { diff --git a/src/main/java/org/usf/jquery/web/EntryParseException.java b/src/main/java/org/usf/jquery/web/EntryParseException.java index 94ce24ff..b41896f5 100644 --- a/src/main/java/org/usf/jquery/web/EntryParseException.java +++ b/src/main/java/org/usf/jquery/web/EntryParseException.java @@ -24,6 +24,6 @@ static EntryParseException cannotParseEntryException(String type, RequestEntryCh } static EntryParseException cannotParseEntryException(String type, RequestEntryChain entry, Throwable cause) { - return new EntryParseException(format("cannot parse %s : '%s'", type, entry), cause); + return new EntryParseException(format("cannot parse %s : '%s'", entry, type), cause); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index cbda7890..1ae81c4d 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -21,7 +21,7 @@ import static org.usf.jquery.core.ParameterSet.ofParameters; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Utils.join; +import static org.usf.jquery.core.Utils.joinArray; import static org.usf.jquery.web.ArgumentParsers.parse; import static org.usf.jquery.web.Constants.COLUMN; import static org.usf.jquery.web.Constants.DISTINCT; @@ -132,7 +132,7 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub case JOIN: q.joins(e.evalJoin(td)); break; case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; - default: throw badEntrySyntaxException(join("|", DISTINCT, FILTER, ORDER, JOIN, OFFSET, FETCH), e.value); + default: throw badEntrySyntaxException(joinArray("|", DISTINCT, FILTER, ORDER, JOIN, OFFSET, FETCH), e.value); } } return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); @@ -171,7 +171,7 @@ public Partition evalPartition(ViewDecorator td) { case ORDER: addAll(ords, e.oderVarargs(td)); break; default: throw e==this ? cannotParseEntryException(PARTITION, e) //first entry - : badEntrySyntaxException(join("|", PARTITION, ORDER), e.value); + : badEntrySyntaxException(joinArray("|", PARTITION, ORDER), e.value); } e = e.next; } while(nonNull(e)); @@ -409,7 +409,7 @@ private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunc }); } catch (Exception e) { - throw new EntrySyntaxException(format("bad entry arguments : %s[(%s)]", value, isNull(args) ? "" : join(", ", args.toArray())), e); + throw new EntrySyntaxException(format("bad entry arguments : %s[(%s)]", value, isNull(args) ? "" : joinArray(", ", args.toArray())), e); } return arr; } @@ -418,21 +418,21 @@ String requireTag() { if(nonNull(tag)) { return tag; } - throw new EntrySyntaxException(format("expected tag : %s[:tag]", this)); + throw expectedEntryTagException(this); } RequestEntryChain requireNoArgs() { if(isNull(args)) { return this; } - throw new EntrySyntaxException(format("unexpected entry args : %s[(%s)]", value, join(", ", args.toArray()))); + throw new EntrySyntaxException(format("unexpected entry args : %s[(%s)]", value, joinArray(", ", args.toArray()))); } RequestEntryChain requireNoNext() { if(isLast()) { return this; } - throw expectedEntryTagException(this); + throw new EntrySyntaxException(format("unexpected entry : %s[.%s]", value, next)); } public boolean isLast() { @@ -495,7 +495,7 @@ static EntrySyntaxException badEntrySyntaxException(String type, String value) { } static EntrySyntaxException expectedEntryTagException(RequestEntryChain e) { - throw new EntrySyntaxException(format("unexpected entry : %s[.%s]", e.value, e.next)); + throw new EntrySyntaxException(format("expected tag : %s[:tag]", e)); } @AllArgsConstructor From 3efb3a656573a2b8f501e7c3802b09cf0ef15fcc Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 20 Aug 2024 11:08:47 +0200 Subject: [PATCH 150/298] edit --- src/main/java/org/usf/jquery/core/Operator.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index f66abb82..217b3e1e 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -206,6 +206,10 @@ static TypedOperator date() { return new TypedOperator(DATE, cast("DATE"), required(VARCHAR, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } + static TypedOperator timestamp() { + return new TypedOperator(TIMESTAMP, cast("TIMESTAMP"), required(VARCHAR, DATE)); + } + static TypedOperator integer() { return new TypedOperator(INTEGER, cast("INTEGER"), required(VARCHAR, DOUBLE)); } From 84fc7ed2c665897b50595074325b7b89bb93c143 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 21 Aug 2024 11:08:32 +0200 Subject: [PATCH 151/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 5 -- .../java/org/usf/jquery/core/Database.java | 11 +++- .../java/org/usf/jquery/core/Operator.java | 2 +- .../java/org/usf/jquery/core/Parameter.java | 1 - .../jquery/core/QueryParameterBuilder.java | 7 +- .../usf/jquery/core/RequestQueryBuilder.java | 13 +++- src/main/java/org/usf/jquery/core/Utils.java | 14 +--- .../org/usf/jquery/web/ColumnMetadata.java | 8 +-- .../java/org/usf/jquery/web/Constants.java | 11 +--- .../usf/jquery/web/ContextEnvironment.java | 6 +- .../org/usf/jquery/web/ContextManager.java | 15 ++--- .../org/usf/jquery/web/CriteriaBuilder.java | 4 +- .../org/usf/jquery/web/DatabaseDecorator.java | 1 - .../usf/jquery/web/EntryParseException.java | 1 - .../org/usf/jquery/web/EvalException.java | 22 ------- .../usf/jquery/web/IterableViewDecorator.java | 38 ++++++++++- .../jquery/web/MissingParameterException.java | 27 -------- .../org/usf/jquery/web/ModelIterator.java | 10 +-- .../jquery/web/NoSuchResourceException.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 8 ++- .../jquery/web/RequestQueryParamResolver.java | 1 + .../jquery/web/ResourceAccessException.java | 2 +- .../org/usf/jquery/web/ViewDecorator.java | 6 +- .../java/org/usf/jquery/web/ViewMetadata.java | 4 +- .../java/org/usf/jquery/web/WebException.java | 4 +- .../org/usf/jquery/web/YearTableMetadata.java | 2 +- .../org/usf/jquery/web/YearViewDecorator.java | 65 ++++++++++++++----- 27 files changed, 148 insertions(+), 142 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/web/EvalException.java delete mode 100644 src/main/java/org/usf/jquery/web/MissingParameterException.java diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 81556fe0..e33cf1ef 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -163,11 +163,6 @@ public String sql(QueryParameterBuilder arg) { return formatValue(value.get()); //lazy } - @Override - public boolean isAggregation() { - return false; - } - @Override public Stream groupKeys() { return Stream.empty(); diff --git a/src/main/java/org/usf/jquery/core/Database.java b/src/main/java/org/usf/jquery/core/Database.java index 106c30c8..9004e79a 100644 --- a/src/main/java/org/usf/jquery/core/Database.java +++ b/src/main/java/org/usf/jquery/core/Database.java @@ -12,11 +12,20 @@ public enum Database { MYSQL, POSTGRESQL, ORACLE, SQLSERVER, TERADATA, H2; + private static final ThreadLocal local = new ThreadLocal<>(); + + public static Database currentDatabase() { + return local.get(); + } + + static void setCurrentDatabase(Database db) { + local.set(db); + } + public static Optional of(String name) { var v = name.toUpperCase(); return Stream.of(values()) .filter(d-> v.contains(d.name())) .findAny(); } - } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 217b3e1e..2f2904e3 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -2,6 +2,7 @@ import static org.usf.jquery.core.ArgTypeRef.firstArgJdbcType; import static org.usf.jquery.core.Database.TERADATA; +import static org.usf.jquery.core.Database.currentDatabase; import static org.usf.jquery.core.JDBCType.BIGINT; import static org.usf.jquery.core.JDBCType.DATE; import static org.usf.jquery.core.JDBCType.DOUBLE; @@ -15,7 +16,6 @@ import static org.usf.jquery.core.Parameter.optional; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; -import static org.usf.jquery.core.Utils.currentDatabase; import java.util.Optional; diff --git a/src/main/java/org/usf/jquery/core/Parameter.java b/src/main/java/org/usf/jquery/core/Parameter.java index 29a88519..7ef0f327 100644 --- a/src/main/java/org/usf/jquery/core/Parameter.java +++ b/src/main/java/org/usf/jquery/core/Parameter.java @@ -3,7 +3,6 @@ import static java.util.Objects.isNull; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Utils.joinArray; -import static org.usf.jquery.core.Utils.joinAndDelemitArray; import java.util.stream.Stream; diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index e4d34df6..c301ec2e 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -2,9 +2,7 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; -import static java.util.Objects.isNull; import static java.util.Objects.nonNull; -import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.SqlStringBuilder.COMA; @@ -16,7 +14,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.function.Function; import java.util.stream.Stream; @@ -80,13 +77,13 @@ String appendArray(Object[] arr, int from, Function fn) { return EMPTY; } } - else if(from < arr.length) { + else if(from >= 0 && from < arr.length) { return Stream.of(arr) .skip(from) .map(fn) .collect(joining(SCOMA)); } - throw new IndexOutOfBoundsException(from + ">=" + requireNonNull(arr, "arr is null").length); + throw new IndexOutOfBoundsException(from); } public String appendParameter(Object o) { diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 4ff04c62..bebbc4df 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -7,11 +7,12 @@ import static java.util.function.Predicate.not; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Database.TERADATA; +import static org.usf.jquery.core.Database.currentDatabase; +import static org.usf.jquery.core.Database.setCurrentDatabase; import static org.usf.jquery.core.LogicalOperator.AND; import static org.usf.jquery.core.QueryParameterBuilder.parametrized; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.SqlStringBuilder.SPACE; -import static org.usf.jquery.core.Utils.currentDatabase; import static org.usf.jquery.core.Validation.requireNonEmpty; import java.util.ArrayList; @@ -32,7 +33,7 @@ @Slf4j @Getter public class RequestQueryBuilder { - + private final List columns = new ArrayList<>(); private final List filters = new ArrayList<>(); //WHERE & HAVING private final List orders = new ArrayList<>(); @@ -43,6 +44,14 @@ public class RequestQueryBuilder { private Integer fetch; private Integer offset; + public RequestQueryBuilder() { + this(null); + } + + public RequestQueryBuilder(Database target) { + setCurrentDatabase(target); + } + public RequestQueryBuilder columns(@NonNull TaggableColumn... columns) { addAll(this.columns, columns); return this; diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index cf446bb7..23dfcc20 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -18,9 +18,7 @@ */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class Utils { - //move this - static ThreadLocal context = new ThreadLocal<>(); // change it - + public static final int UNLIMITED = -1; public static boolean isEmpty(T[] a) { @@ -39,10 +37,6 @@ public static boolean isBlank(String s) { return isNull(s) || s.isBlank(); } - public static Database currentDatabase() { - return context.get(); - } - @SuppressWarnings("unchecked") public static String joinArray(String delemiter, T... args) { return joinAndDelemitArray(delemiter, "", "", args); @@ -54,7 +48,6 @@ public static String joinAndDelemitArray(String delemiter, String before, St ? null : joinAndDelemit(delemiter, before, after, Stream.of(args)); } - public static String join(String delemiter, Stream args) { return joinAndDelemit(delemiter, "", "", args); @@ -63,11 +56,6 @@ public static String join(String delemiter, Stream args) { public static String joinAndDelemit(String delemiter, String before, String after, Stream args) { return args.map(Object::toString).collect(joining(delemiter, before, after)); } - - @Deprecated - public static void currentDatabase(Database db) { - context.set(db); - } public static T[] arrayJoin(T[] arr, T o) { var res = copyOf(arr, arr.length+1); diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index a38f5a76..68542c5e 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -61,7 +61,10 @@ public String toJavaType(){ public String toSqlType(){ var s = type.name(); - if(!overConfigured) { + if(overConfigured) { + s+="!"; + } + else { if(type.typeClass() == String.class && dataSize < MAX_VALUE) { s+= "(" + dataSize + ")"; } @@ -72,9 +75,6 @@ public String toSqlType(){ s+= "(" + dataSize + "," + precision + ")"; } } - else { - s+="!"; - } return s; } diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index ff6e5390..fc78e12d 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -1,7 +1,6 @@ package org.usf.jquery.web; import java.time.YearMonth; -import java.util.Set; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -14,6 +13,7 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class Constants { + public static final String DATABASE = "database"; public static final String QUERY = "query"; public static final String VIEW = "view"; public static final String SELECT = "select"; @@ -26,15 +26,6 @@ public final class Constants { public static final String OFFSET = "offset"; public static final String JOIN = "join"; public static final String PARTITION = "partition"; - @Deprecated - public static final String REVISION = "revision"; //not standard - @Deprecated - public static final String REVISION_MODE = "revision.mode"; //not standard - - static final Set RESERVED_WORDS = - Set.of(VIEW, COLUMN, COLUMN_DISTINCT, FILTER, ORDER, OFFSET, FETCH, - PARTITION, REVISION, REVISION_MODE); //metadata ? static final YearMonth[] EMPTY_REVISION = new YearMonth[0]; //not standard - } diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index d001a000..8c175807 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -56,7 +56,7 @@ public final class ContextEnvironment { private final Map overView = new HashMap<>(); private final Map declaredColumns = new HashMap<>(); - public ContextEnvironment(ContextEnvironment ctx) { + ContextEnvironment(ContextEnvironment ctx) { this.database = ctx.database; this.views = new HashMap<>(ctx.views); //modifiable this.columns = new HashMap<>(ctx.columns); //modifiable @@ -115,7 +115,7 @@ ContextEnvironment bind() { meta.fetch(cnx.getMetaData(), schema); } catch(SQLException | JQueryException e) { - log.error("error while scanning database metadata", e); + log.error("error while scanning '{}' metadata", v.identity(), e); } } } @@ -137,7 +137,7 @@ public static ContextEnvironment of(DatabaseDecorator database, Collection views, Collection columns, DataSource ds, String schema) { requireLegalVariable(requireNonNull(database, "configuration.database").identity()); return new ContextEnvironment(database, - unmodifiableIdentityMap(views, ViewDecorator::identity, database.identity() + ".views"), + unmodifiableIdentityMap(views, ViewDecorator::identity, database.identity() + ".views"), //preserve views order unmodifiableIdentityMap(columns, ColumnDecorator::identity, database.identity() + ".columns"), ds, schema, new DatabaseMetadata()); } diff --git a/src/main/java/org/usf/jquery/web/ContextManager.java b/src/main/java/org/usf/jquery/web/ContextManager.java index 7a1d0b0c..ef44a0d2 100644 --- a/src/main/java/org/usf/jquery/web/ContextManager.java +++ b/src/main/java/org/usf/jquery/web/ContextManager.java @@ -2,6 +2,7 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; +import static org.usf.jquery.web.Constants.DATABASE; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.ResourceAccessException.resourceAlreadyExistsException; @@ -9,8 +10,6 @@ import java.util.Map; import java.util.NoSuchElementException; -import org.usf.jquery.core.Utils; - import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -30,7 +29,7 @@ public static void register(ContextEnvironment config) { if(isNull(dm)) { return config; } - throw resourceAlreadyExistsException("context", id); + throw resourceAlreadyExistsException(DATABASE, id); }); config.bind(); // outer bind } @@ -41,7 +40,8 @@ public static ContextEnvironment currentContext() { return ctx; } if(CONTEXTS.size() == 1) { //default database - return setCurrentContext(CONTEXTS.values().iterator().next()); + ctx = CONTEXTS.values().iterator().next(); + return setCurrentContext(new ContextEnvironment(ctx)); } throw CONTEXTS.isEmpty() ? new NoSuchElementException("no database configured") @@ -51,20 +51,17 @@ public static ContextEnvironment currentContext() { static ContextEnvironment context(String database){ var ctx = CONTEXTS.get(database); if(nonNull(ctx)) { - return setCurrentContext(ctx); + return setCurrentContext(new ContextEnvironment(ctx)); } - throw noSuchResourceException("database", database); + throw noSuchResourceException(DATABASE, database); } static ContextEnvironment setCurrentContext(ContextEnvironment ctx) { - ctx = new ContextEnvironment(ctx); //copy CURRENT.set(ctx); - Utils.currentDatabase(ctx.getMetadata().getType()); //table database return ctx; } static void releaseContext() { CURRENT.remove(); - Utils.currentDatabase(null); //table database } } diff --git a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java index a94cb06b..00826b9b 100644 --- a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java +++ b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java @@ -3,7 +3,7 @@ import static java.util.Optional.ofNullable; import static org.usf.jquery.core.LogicalOperator.OR; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; -import static org.usf.jquery.web.EvalException.cannotEvaluateException; +import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import java.util.stream.Stream; @@ -23,7 +23,7 @@ public interface CriteriaBuilder> { default T build(String... args) { return Stream.of(requireAtLeastNArgs(1, args, CriteriaBuilder.class::getSimpleName)) .map(v-> ofNullable(criteria(v)) - .orElseThrow(()-> cannotEvaluateException("criteria value", v))) + .orElseThrow(()-> noSuchResourceException("criteria value", v))) .reduce((e1, e2)-> e1.append(combiner(), e2)) .orElseThrow(); } diff --git a/src/main/java/org/usf/jquery/web/DatabaseDecorator.java b/src/main/java/org/usf/jquery/web/DatabaseDecorator.java index 35f3f7ae..4771dc1e 100644 --- a/src/main/java/org/usf/jquery/web/DatabaseDecorator.java +++ b/src/main/java/org/usf/jquery/web/DatabaseDecorator.java @@ -10,5 +10,4 @@ public interface DatabaseDecorator { String identity(); //URL String viewName(ViewDecorator vd); //[schema.]table - } diff --git a/src/main/java/org/usf/jquery/web/EntryParseException.java b/src/main/java/org/usf/jquery/web/EntryParseException.java index b41896f5..a939b294 100644 --- a/src/main/java/org/usf/jquery/web/EntryParseException.java +++ b/src/main/java/org/usf/jquery/web/EntryParseException.java @@ -18,7 +18,6 @@ public EntryParseException(String message, Throwable cause) { super(message, cause); } - static EntryParseException cannotParseEntryException(String type, RequestEntryChain entry) { return cannotParseEntryException(type, entry, null); } diff --git a/src/main/java/org/usf/jquery/web/EvalException.java b/src/main/java/org/usf/jquery/web/EvalException.java deleted file mode 100644 index 840ab4f7..00000000 --- a/src/main/java/org/usf/jquery/web/EvalException.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.usf.jquery.web; - -import static org.usf.jquery.core.SqlStringBuilder.quote; - -/** - * - * @author u$f - * - */ -@Deprecated -@SuppressWarnings("serial") -public final class EvalException extends WebException { - - public EvalException(String message) { - super(message); - } - - static EvalException cannotEvaluateException(String type, String expression) { - return new EvalException("cannot evaluate " + type + " " + quote(expression)); - } - -} diff --git a/src/main/java/org/usf/jquery/web/IterableViewDecorator.java b/src/main/java/org/usf/jquery/web/IterableViewDecorator.java index 40063c8d..7df54a77 100644 --- a/src/main/java/org/usf/jquery/web/IterableViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/IterableViewDecorator.java @@ -1,10 +1,16 @@ package org.usf.jquery.web; +import static java.util.Objects.nonNull; +import static org.usf.jquery.core.SqlStringBuilder.member; import static org.usf.jquery.web.ModelIterator.iterator; +import java.util.Collection; import java.util.Map; +import org.usf.jquery.core.DBFilter; +import org.usf.jquery.core.QueryParameterBuilder; import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.TableView; /** * @@ -12,14 +18,40 @@ * */ public interface IterableViewDecorator extends ViewDecorator { + + @Override + default ViewBuilder builder() { + var v = (TableView) ViewDecorator.super.builder().build(); + return ()-> new TableView(v.getSchema(), v.getName()) { + @Override + public String sql(QueryParameterBuilder builder) { + return member(getSchemaOrElse(builder.getSchema()), viewName(v.getName())); + } + }; + } - void tableName(String name, T ctx); + default String viewName(String name){ + return name; + } - T[] parseIterable(Map parameterMap); + Collection parseIterable(Map parameterMap); @Override default void parseFilters(RequestQueryBuilder query, Map parameters) { - query.repeat(iterator(parseIterable(parameters))); + var it = iterator(parseIterable(parameters)); + var fr = filter(); //optional + if(nonNull(fr)) { + query.filters(b-> fr.sql(b)); + } ViewDecorator.super.parseFilters(query, parameters); + query.repeat(it); + } + + default DBFilter filter(){ + return null; + } + + default T currentModel() { + return null; } } diff --git a/src/main/java/org/usf/jquery/web/MissingParameterException.java b/src/main/java/org/usf/jquery/web/MissingParameterException.java deleted file mode 100644 index b3e8ea5c..00000000 --- a/src/main/java/org/usf/jquery/web/MissingParameterException.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.usf.jquery.web; - -import static java.util.stream.Collectors.joining; - -import java.util.stream.Stream; - -import org.usf.jquery.core.SqlStringBuilder; - -/** - * - * @author u$f - * - */ -@Deprecated(forRemoval = true) -@SuppressWarnings("serial") -public final class MissingParameterException extends WebException { - - public MissingParameterException(String s) { - super(s); - } - - public static MissingParameterException missingParameterException(String... parameters) { - return new MissingParameterException("require " + - Stream.of(parameters).map(SqlStringBuilder::quote).collect(joining(" or ")) + " parameter"); - } - -} diff --git a/src/main/java/org/usf/jquery/web/ModelIterator.java b/src/main/java/org/usf/jquery/web/ModelIterator.java index 6ef26a20..0de2059d 100644 --- a/src/main/java/org/usf/jquery/web/ModelIterator.java +++ b/src/main/java/org/usf/jquery/web/ModelIterator.java @@ -2,8 +2,8 @@ import static org.usf.jquery.core.Utils.isEmpty; +import java.util.Collection; import java.util.Iterator; -import java.util.stream.Stream; import lombok.RequiredArgsConstructor; @@ -34,11 +34,11 @@ public T next() { currentRev.set(rev); return rev; } - - public static ModelIterator iterator(T[] model){ + + public static ModelIterator iterator(Collection model){ if(!isEmpty(model)) { - return new ModelIterator<>(Stream.of(model).iterator()); + return new ModelIterator<>(model.iterator()); } - throw new IllegalArgumentException("no revision"); + throw new IllegalArgumentException("empty array"); } } diff --git a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java index be81bd80..99cc556a 100644 --- a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java +++ b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java @@ -9,7 +9,7 @@ * */ @SuppressWarnings("serial") -final class NoSuchResourceException extends WebException { +public final class NoSuchResourceException extends WebException { public NoSuchResourceException(String s) { super(s); diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 1ae81c4d..50ed945f 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -35,6 +35,7 @@ import static org.usf.jquery.web.Constants.SELECT; import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; +import static org.usf.jquery.web.ContextManager.setCurrentContext; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; @@ -62,7 +63,6 @@ import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedOperator; -import org.usf.jquery.core.Utils; import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; import org.usf.jquery.core.WindowFunction; @@ -121,6 +121,8 @@ public ViewDecorator evalQuery(ViewDecorator td) { Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub context if(SELECT.equals(value)) { var e = this; + var ctx = currentContext(); + setCurrentContext(new ContextEnvironment(ctx)); //sub context : inherits only declared views try { var q = new RequestQueryBuilder().columns(taggableVarargs(td)); while(e.hasNext()) { @@ -135,11 +137,15 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub default: throw badEntrySyntaxException(joinArray("|", DISTINCT, FILTER, ORDER, JOIN, OFFSET, FETCH), e.value); } } + q.overViews(currentContext().getOverView()); return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); } catch (Exception ex) { throw new EntrySyntaxException("incorrect query syntax: " + e, ex); } + finally { + setCurrentContext(ctx); + } } return empty(); } diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index 070b5da0..8c9333e0 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -41,6 +41,7 @@ public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull parameterMap.remove(k); } } + releaseContext(); //safe++ var ctx = ant.database().isEmpty() ? currentContext() : context(ant.database()); diff --git a/src/main/java/org/usf/jquery/web/ResourceAccessException.java b/src/main/java/org/usf/jquery/web/ResourceAccessException.java index 9a947bbf..17f34737 100644 --- a/src/main/java/org/usf/jquery/web/ResourceAccessException.java +++ b/src/main/java/org/usf/jquery/web/ResourceAccessException.java @@ -6,7 +6,7 @@ * */ @SuppressWarnings("serial") -public class ResourceAccessException extends WebException { //read & write access +public final class ResourceAccessException extends WebException { //read & write access public ResourceAccessException(String message) { super(message); diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 45bc7d93..e56ebeac 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -1,6 +1,7 @@ package org.usf.jquery.web; import static java.lang.Integer.parseInt; +import static java.lang.String.format; import static java.util.Map.entry; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; @@ -18,7 +19,6 @@ import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; -import static org.usf.jquery.web.MissingParameterException.missingParameterException; import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; @@ -99,7 +99,7 @@ default ViewMetadata metadata() { } default RequestQueryBuilder query(Map parameterMap) { - var query = new RequestQueryBuilder(); + var query = new RequestQueryBuilder(currentContext().getMetadata().getType()); parseViews(query, parameterMap); //variable isolation !? parseColumns(query, parameterMap); parseOrders(query, parameterMap); @@ -138,7 +138,7 @@ default void parseColumns(RequestQueryBuilder query, Map param .forEach(query::columns); } else { - throw missingParameterException(COLUMN, COLUMN_DISTINCT); + throw new IllegalArgumentException(format("requrie %s or %s parameter", COLUMN, COLUMN_DISTINCT)); } } diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index fe4e3bd8..c1ab1e15 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -55,7 +55,7 @@ final ViewMetadata fetch(DatabaseMetaData metadata, String schema) throws SQLExc var time = currentTimeMillis(); log.info("scanning view '{}' metadata...", view); if(view instanceof TableView tab) { - fetch(metadata, tab, schema); + fetchView(metadata, tab, schema); } else { fetch(metadata, view, schema); @@ -70,7 +70,7 @@ final ViewMetadata fetch(DatabaseMetaData metadata, String schema) throws SQLExc return this; } - void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { + void fetchView(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { try(var rs = metadata.getColumns(null, view.getSchemaOrElse(schema), view.getName(), null)){ if(rs.next()) { var db = reverseMapKeys(); //reverse key diff --git a/src/main/java/org/usf/jquery/web/WebException.java b/src/main/java/org/usf/jquery/web/WebException.java index 1666bbe2..9b1587aa 100644 --- a/src/main/java/org/usf/jquery/web/WebException.java +++ b/src/main/java/org/usf/jquery/web/WebException.java @@ -1,14 +1,12 @@ package org.usf.jquery.web; -import org.usf.jquery.core.JQueryException; - /** * * @author u$f * */ @SuppressWarnings("serial") -public class WebException extends JQueryException { +public class WebException extends RuntimeException { public WebException(String message) { super(message); diff --git a/src/main/java/org/usf/jquery/web/YearTableMetadata.java b/src/main/java/org/usf/jquery/web/YearTableMetadata.java index b075136b..084dfd28 100644 --- a/src/main/java/org/usf/jquery/web/YearTableMetadata.java +++ b/src/main/java/org/usf/jquery/web/YearTableMetadata.java @@ -66,7 +66,7 @@ public Optional latestRevision() { } @Override - void fetch(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { + void fetchView(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { tablenames.clear(); var dbMap = getColumns().values().stream().collect(toMap(cm-> cm.getName(), ColumnMetadata::reset)); //important! reset columns Set dirtyColumns = new LinkedHashSet<>(); diff --git a/src/main/java/org/usf/jquery/web/YearViewDecorator.java b/src/main/java/org/usf/jquery/web/YearViewDecorator.java index d0ec8de6..cc3a05ed 100644 --- a/src/main/java/org/usf/jquery/web/YearViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearViewDecorator.java @@ -4,30 +4,30 @@ import static java.time.Month.DECEMBER; import static java.time.YearMonth.now; import static java.util.Objects.nonNull; +import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Utils.isBlank; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.web.Constants.EMPTY_REVISION; -import static org.usf.jquery.web.Constants.REVISION; -import static org.usf.jquery.web.Constants.REVISION_MODE; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.RevisionIterator.iterator; import static org.usf.jquery.web.RevisionIterator.yearColumn; -import static org.usf.jquery.web.RevisionIterator.yearTable; import static org.usf.jquery.web.ViewDecorator.flatParameters; import java.time.Year; import java.time.YearMonth; +import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.function.UnaryOperator; import java.util.stream.Stream; +import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.RequestQueryBuilder; -import org.usf.jquery.core.TableView; import org.usf.jquery.core.TaggableColumn; /** @@ -35,13 +35,53 @@ * @author u$f * */ -@Deprecated -public interface YearViewDecorator extends ViewDecorator { +public interface YearViewDecorator extends IterableViewDecorator>> { + static final String REVISION = "revision"; + static final String REVISION_MODE = "revision.mode"; + + @Override + default String viewName(String name) { + return name + "_" + currentModel().getKey(); //get it during build + } + + @Override + default DBFilter filter(){ + if(nonNull(monthRevision())) { + var c = column(monthRevision()); + var v = currentModel().getValue(); //get it during build + return v.size() == 1 + ? c.eq(v.get(0).getMonthValue()) + : c.in(v.stream().map(YearMonth::getMonthValue).toArray(Integer[]::new)); + } + return null; + } + + @Override + default Collection>> parseIterable(Map parameterMap) { + var arr = parameterMap.get(REVISION_MODE); + if(nonNull(arr) && arr.length > 1) { + throw new IllegalArgumentException("too many " + REVISION_MODE + " " + join(", ", arr)); //multiple values + } + var mod = revisionMode(isEmpty(arr) ? defaultRevisionMode() : arr[0]); + var values = parameterMap.containsKey(REVISION) + ? flatParameters(parameterMap.get(REVISION)) + .map(this::parseYearMonth) + .toArray(YearMonth[]::new) + : new YearMonth[] {now()}; + var revs = mod.apply(values); + if(!isEmpty(revs)) { + return Stream.of(revs).collect(groupingBy(YearMonth::getYear)).entrySet(); + } + throw noSuchResourceException(REVISION, + Stream.of(values).map(YearMonth::toString).collect(joining(", "))); //require available revisions + } + + ColumnDecorator yearRevision(); //!table column ColumnDecorator monthRevision(); //optional - + /** * loaded from db if null * @@ -49,17 +89,12 @@ public interface YearViewDecorator extends ViewDecorator { default YearMonth[] availableRevisions() {//cache return metadata().getRevisions(); } - - @Override - default TableView view() { - return yearTable(viewName(), identity()); - } @Override default RequestQueryBuilder query(Map parameterMap) { var query = ViewDecorator.super.query(parameterMap); monthRevision().map(this::column) - .ifPresent(c-> query.filters(monthFilter(c))); + .ifPresent(c-> query.filters(RevisionIterator.monthFilter(c))); return query.repeat(iterator(parseRevisions(parameterMap))); } @@ -94,7 +129,7 @@ default UnaryOperator revisionMode(String mode) { case "strict" : return this::strictRevisions; case "preceding" : return this::precedingRevisions; case "succeeding" : return this::succeedingRevisions; - default : throw EvalException.cannotEvaluateException(REVISION_MODE, mode); + default : throw noSuchResourceException(REVISION_MODE, mode); } } @@ -167,5 +202,5 @@ default YearTableMetadata metadata() { default String defaultRevisionMode() { return "strict"; } - + } From 4d912ca644752c326b0d10c8c7e1dc2ff3caeb78 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 21 Aug 2024 13:42:36 +0200 Subject: [PATCH 152/298] edit --- .../org/usf/jquery/web/ArgumentParsers.java | 18 +++++++++++------- .../java/org/usf/jquery/web/Constants.java | 1 + .../usf/jquery/web/EntryParseException.java | 4 ++-- .../org/usf/jquery/web/RequestEntryChain.java | 7 ++++--- .../java/org/usf/jquery/web/ViewDecorator.java | 2 +- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 5cc77a36..0346d5af 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -8,10 +8,10 @@ import static org.usf.jquery.core.JDBCType.TIME; import static org.usf.jquery.core.JDBCType.TIMESTAMP; import static org.usf.jquery.core.JDBCType.TIMESTAMP_WITH_TIMEZONE; +import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.JQueryType.COLUMN; import static org.usf.jquery.core.JQueryType.QUERY_COLUMN; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.core.Utils.join; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import java.math.BigDecimal; @@ -46,7 +46,7 @@ public class ArgumentParsers { private static final JDBCType[] STD_TYPES = { BIGINT, DOUBLE, DATE, TIMESTAMP, - TIME, TIMESTAMP_WITH_TIMEZONE }; + TIME, TIMESTAMP_WITH_TIMEZONE, VARCHAR }; //varchar !? public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType... types) { List list = new ArrayList<>(); @@ -55,7 +55,7 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType.. list.add(QUERY_COLUMN); } addAll(list, isEmpty(types) ? STD_TYPES : types); - Exception e = null; + var exList = new ArrayList(); for(var type : list) { try { if(type instanceof JDBCType t) { @@ -67,12 +67,16 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType.. else { throw new UnsupportedOperationException(requireNonNull(type, "type is null").toString()); } - } catch (NoSuchResourceException | EntryParseException ex) { //do not throw exception - log.trace("parse '{}' as {} => {}", entry, type, ex.getMessage()); - e = ex; + } catch (NoSuchResourceException | EntryParseException e) { //do not throw exception + exList.add(e); } } - throw cannotParseEntryException(join("|", list.stream()), entry, list.size() > 1 ? e : null); + if(exList.size() > 1) { + for(int i=0; i {}", entry, list.get(i), exList.get(i).getMessage()); + } + } + throw cannotParseEntryException("entry", entry, exList.size() == 1 ? exList.get(0) : null); } public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index fc78e12d..aca93d35 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -27,5 +27,6 @@ public final class Constants { public static final String JOIN = "join"; public static final String PARTITION = "partition"; + @Deprecated static final YearMonth[] EMPTY_REVISION = new YearMonth[0]; //not standard } diff --git a/src/main/java/org/usf/jquery/web/EntryParseException.java b/src/main/java/org/usf/jquery/web/EntryParseException.java index a939b294..faf3a618 100644 --- a/src/main/java/org/usf/jquery/web/EntryParseException.java +++ b/src/main/java/org/usf/jquery/web/EntryParseException.java @@ -17,12 +17,12 @@ public EntryParseException(String message) { public EntryParseException(String message, Throwable cause) { super(message, cause); } - + static EntryParseException cannotParseEntryException(String type, RequestEntryChain entry) { return cannotParseEntryException(type, entry, null); } static EntryParseException cannotParseEntryException(String type, RequestEntryChain entry, Throwable cause) { - return new EntryParseException(format("cannot parse %s : '%s'", entry, type), cause); + return new EntryParseException(format("cannot parse %s : '%s'", type, entry), cause); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 50ed945f..7779fe8c 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -7,6 +7,7 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; +import static java.util.Objects.requireNonNullElse; import static java.util.Optional.empty; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; @@ -112,7 +113,6 @@ public QueryColumn evalQueryColumn(ViewDecorator td) { .orElseThrow(()-> cannotParseEntryException(QUERY, this)); } - //TODO level isolation : window function public ViewDecorator evalQuery(ViewDecorator td) { return evalQuery(td, false).orElseThrow(()-> cannotParseEntryException(QUERY, this)); } @@ -140,7 +140,7 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub q.overViews(currentContext().getOverView()); return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); } - catch (Exception ex) { + catch (EntryParseException | NoSuchResourceException ex) { throw new EntrySyntaxException("incorrect query syntax: " + e, ex); } finally { @@ -415,7 +415,8 @@ private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunc }); } catch (Exception e) { - throw new EntrySyntaxException(format("bad entry arguments : %s[(%s)]", value, isNull(args) ? "" : joinArray(", ", args.toArray())), e); + throw new EntrySyntaxException(format("bad entry arguments : %s[(%s)]", + requireNonNullElse(value, ""), isNull(args) ? "" : joinArray(", ", args.toArray())), e); } return arr; } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index e56ebeac..82529bbf 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -100,7 +100,7 @@ default ViewMetadata metadata() { default RequestQueryBuilder query(Map parameterMap) { var query = new RequestQueryBuilder(currentContext().getMetadata().getType()); - parseViews(query, parameterMap); //variable isolation !? + parseViews(query, parameterMap); parseColumns(query, parameterMap); parseOrders(query, parameterMap); parseJoin(query, parameterMap); From d700e47d39f99c6766fec04ffbf56615197a2f76 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 21 Aug 2024 18:40:17 +0200 Subject: [PATCH 153/298] edit --- src/main/java/org/usf/jquery/core/ParameterSet.java | 8 ++++---- .../java/org/usf/jquery/web/ContextEnvironment.java | 12 +++++------- src/main/java/org/usf/jquery/web/ContextManager.java | 6 +++--- .../org/usf/jquery/web/NoSuchResourceException.java | 5 ----- .../java/org/usf/jquery/web/RequestEntryChain.java | 4 ++-- src/main/java/org/usf/jquery/web/RequestParser.java | 4 ++-- .../usf/jquery/web/RequestQueryParamResolver.java | 3 +-- .../org/usf/jquery/web/ResourceAccessException.java | 10 ++++++---- src/main/java/org/usf/jquery/web/ViewDecorator.java | 2 +- src/main/java/org/usf/jquery/web/WebException.java | 4 ---- 10 files changed, 24 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index c64536fd..80e479f8 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -31,11 +31,11 @@ public Object[] assertArguments(Object... args) { return assertArgumentsFrom(0, args); } - public Object[] assertArgumentsFrom(int idx, Object... args) { + public Object[] assertArgumentsFrom(int idx, Object... args) { //partial assert var arr = isNull(args) ? new Object[0] : args; - forEach(arr.length, (p,i)-> { - if(i>=idx && !p.accept(i, arr)) { - throw badArgumentTypeException(p.types(arr), arr[i]); + forEach(arr.length+idx, (p,i)-> { + if(i>=idx && !p.accept(i-idx, arr)) { + throw badArgumentTypeException(p.types(arr), arr[i-idx]); } }); return arr; diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index 8c175807..6c02ed75 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -9,8 +9,6 @@ import static java.util.stream.Collectors.toMap; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNonEmpty; -import static org.usf.jquery.web.Constants.COLUMN; -import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ResourceAccessException.resourceAlreadyExistsException; import java.sql.SQLException; @@ -82,19 +80,19 @@ void declareView(ViewDecorator view) { //additional request views if(isNull(v)){ return view; } - throw resourceAlreadyExistsException(VIEW, k); + throw resourceAlreadyExistsException(k, v); }); } TaggableColumn declareColumn(TaggableColumn col) { - if(views.containsKey(col.tagname())) { //cannot overwrite registered views - throw resourceAlreadyExistsException(VIEW, col.tagname()); - } //but can overwrite registered columns + views.computeIfPresent(col.tagname(), (k,v)-> { //cannot overwrite registered views + throw resourceAlreadyExistsException(k, v); + }); //but can overwrite registered columns return declaredColumns.compute(col.tagname(), (k,v)-> { if(isNull(v)){ return col; } - throw resourceAlreadyExistsException(COLUMN, k); + throw resourceAlreadyExistsException(k, v); }); } diff --git a/src/main/java/org/usf/jquery/web/ContextManager.java b/src/main/java/org/usf/jquery/web/ContextManager.java index ef44a0d2..a9a5ed77 100644 --- a/src/main/java/org/usf/jquery/web/ContextManager.java +++ b/src/main/java/org/usf/jquery/web/ContextManager.java @@ -25,11 +25,11 @@ public final class ContextManager { private static final ThreadLocal CURRENT = new ThreadLocal<>(); public static void register(ContextEnvironment config) { - CONTEXTS.compute(config.getDatabase().identity(), (id,dm)-> { - if(isNull(dm)) { + CONTEXTS.compute(config.getDatabase().identity(), (k,v)-> { + if(isNull(v)) { return config; } - throw resourceAlreadyExistsException(DATABASE, id); + throw resourceAlreadyExistsException(k, v); }); config.bind(); // outer bind } diff --git a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java index 99cc556a..01f2dc9e 100644 --- a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java +++ b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java @@ -1,7 +1,6 @@ package org.usf.jquery.web; import static java.lang.String.format; -import static org.usf.jquery.core.SqlStringBuilder.quote; /** * @@ -18,8 +17,4 @@ public NoSuchResourceException(String s) { static NoSuchResourceException noSuchResourceException(String type, String resource) { return new NoSuchResourceException(format("no such %s: '%s'", type, resource)); } - - static NoSuchResourceException undeclaredResouceException(String child, String parent) { - return new NoSuchResourceException(quote(child) + " was not declared in " + quote(parent)); - } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 7779fe8c..fee82a0f 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -130,7 +130,7 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub switch(e.value) {//column not allowed case DISTINCT: e.requireNoArgs(); q.distinct(); break; case FILTER: q.filters(e.filterVarargs(td)); break; - case ORDER: q.orders(e.oderVarargs(td)); break; //not sure + case ORDER: q.orders(e.oderVarargs(td)); break; case JOIN: q.joins(e.evalJoin(td)); break; case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; @@ -485,7 +485,7 @@ private static String[] toStringArray(List entries) { static NoSuchResourceException noSuchViewColumnException(RequestEntryChain e) { return noSuchResourceException(COLUMN, e.hasNext() - && currentContext().lookupRegisteredColumn(e.value).isPresent() + && currentContext().lookupRegisteredView(e.value).isPresent() ? e.value + "." + e.next.value : e.value); } diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index dd8206d7..619c5e43 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -136,8 +136,8 @@ private EntryParseException somethingExpectedException() { return new EntryParseException("something expected after '" + s.charAt(size-1) + "'"); } - private static boolean legalTxtChar(char c) { - return c != '"' && c != '\'' && c != '&' && c != '?' && c != '='; //avoid SQL injection & HTTP reserved symbol + private static boolean legalTxtChar(char c) { //avoid SQL injection & HTTP reserved symbol + return c != '"' && c != '\'' && c != '&' && c != '?' && c != '='; } private static boolean legalValChar(char c) { diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index 8c9333e0..e2c1f89a 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -9,7 +9,6 @@ import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.ContextManager.releaseContext; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; -import static org.usf.jquery.web.ResourceAccessException.accessDeniedException; import java.util.LinkedHashMap; import java.util.Map; @@ -54,7 +53,7 @@ public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull if(!ant.aggregationOnly() || req.isAggregation()) { return req; } - throw accessDeniedException("non-aggregate query"); + throw new ResourceAccessException("non-aggregate query"); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/org/usf/jquery/web/ResourceAccessException.java b/src/main/java/org/usf/jquery/web/ResourceAccessException.java index 17f34737..4d1b5b8d 100644 --- a/src/main/java/org/usf/jquery/web/ResourceAccessException.java +++ b/src/main/java/org/usf/jquery/web/ResourceAccessException.java @@ -1,5 +1,7 @@ package org.usf.jquery.web; +import static org.usf.jquery.core.SqlStringBuilder.quote; + /** * * @author u$f @@ -12,11 +14,11 @@ public ResourceAccessException(String message) { super(message); } - public static ResourceAccessException resourceAlreadyExistsException(String name, String value) { - return new ResourceAccessException(name + " already exists : " + value); + public static ResourceAccessException resourceAlreadyExistsException(String name, Object value) { + return new ResourceAccessException(quote(name) + " is already exists : " + value); } - public static ResourceAccessException accessDeniedException(String reason) { - return new ResourceAccessException("access denied : " + reason); + public static ResourceAccessException undeclaredResouceException(String child, String parent) { + return new ResourceAccessException(quote(child) + " is not member of " + quote(parent)); } } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 82529bbf..304c0946 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -19,10 +19,10 @@ import static org.usf.jquery.web.Constants.ORDER; import static org.usf.jquery.web.Constants.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; -import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.RequestParser.parseEntry; +import static org.usf.jquery.web.ResourceAccessException.undeclaredResouceException; import java.util.Map; import java.util.Map.Entry; diff --git a/src/main/java/org/usf/jquery/web/WebException.java b/src/main/java/org/usf/jquery/web/WebException.java index 9b1587aa..4629d218 100644 --- a/src/main/java/org/usf/jquery/web/WebException.java +++ b/src/main/java/org/usf/jquery/web/WebException.java @@ -12,10 +12,6 @@ public WebException(String message) { super(message); } - public WebException(Throwable cause) { - super(cause); - } - public WebException(String message, Throwable cause) { super(message, cause); } From c222fbbf244998d975b83ac9ed63d68c95a8c0c8 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 21 Aug 2024 19:15:57 +0200 Subject: [PATCH 154/298] edit --- src/main/java/org/usf/jquery/core/RequestQueryBuilder.java | 4 +++- src/main/java/org/usf/jquery/core/ViewJoin.java | 4 +++- src/main/java/org/usf/jquery/web/JoinBuilder.java | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index bebbc4df..2d09f613 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -20,6 +20,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import lombok.Getter; import lombok.NonNull; @@ -151,7 +152,8 @@ void select(SqlStringBuilder sb, QueryParameterBuilder pb){ } void from(SqlStringBuilder sb, QueryParameterBuilder pb) { - var views = pb.views(); + var excludes = joins.stream().map(ViewJoin::getView).toList(); + var views = pb.views().stream().filter(not(excludes::contains)).toList(); //do not remove views if(!views.isEmpty()) { sb.append(" FROM ").appendEach(views, SCOMA, v-> v.sqlWithTag(pb)); } diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index 250ee1a2..3f03a5a9 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -10,6 +10,7 @@ import java.util.stream.Stream; +import lombok.Getter; import lombok.RequiredArgsConstructor; /** @@ -17,6 +18,7 @@ * @author u$f * */ +@Getter @RequiredArgsConstructor public class ViewJoin implements DBObject { @@ -32,7 +34,7 @@ public String sql(QueryParameterBuilder builder, Object[] args) { } public String sql(QueryParameterBuilder builder) { - return joinType + " JOIN " + view.sql(builder) + " ON " + + return joinType + " JOIN " + view.sqlWithTag(builder) + " ON " + Stream.of(filters).map(f-> f.sql(builder)).collect(joining(AND.sql())); } diff --git a/src/main/java/org/usf/jquery/web/JoinBuilder.java b/src/main/java/org/usf/jquery/web/JoinBuilder.java index c7b85351..b0131659 100644 --- a/src/main/java/org/usf/jquery/web/JoinBuilder.java +++ b/src/main/java/org/usf/jquery/web/JoinBuilder.java @@ -7,6 +7,7 @@ * @author u$f * */ +@FunctionalInterface public interface JoinBuilder { ViewJoin[] build(); From 6de163d44000046dc46f48c69dc06c57d188bcc4 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 21 Aug 2024 20:19:50 +0200 Subject: [PATCH 155/298] edit --- src/main/java/org/usf/jquery/core/RequestQueryBuilder.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 2d09f613..969e28c1 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -20,7 +20,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.function.Predicate; import lombok.Getter; import lombok.NonNull; @@ -130,7 +129,7 @@ public final void build(SqlStringBuilder sb, QueryParameterBuilder pb){ having(sub, pb); orderBy(sub, pb); fetch(sub); - select(sb, pb); + select(sb, pb); from(sb, pb); //enumerate all view before from clause sb.append(sub.toString()); //TODO optim } From 2ee56d1fa71bc8c1ad85fc89820e16a6bdfd729e Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 22 Aug 2024 01:10:29 +0200 Subject: [PATCH 156/298] edit --- .../java/org/usf/jquery/core/Comparator.java | 7 +- .../java/org/usf/jquery/core/DBProcessor.java | 4 +- .../java/org/usf/jquery/core/Operator.java | 22 +++- .../org/usf/jquery/core/TypedComparator.java | 2 +- .../java/org/usf/jquery/web/Constants.java | 3 - .../org/usf/jquery/web/RevisionIterator.java | 4 +- .../org/usf/jquery/web/YearViewDecorator.java | 106 +++++++----------- 7 files changed, 66 insertions(+), 82 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index eeae3768..e1b6f801 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -15,19 +15,18 @@ * @author u$f * */ -public interface Comparator extends DBProcessor { +public interface Comparator extends DBProcessor { String id(); - @Override - default DBFilter args(Object... args) { + default ColumnSingleFilter filter(Object... args) { return new ColumnSingleFilter(args[0], expression(copyOfRange(args, 1, args.length))); // no type } //basic comparator - default ComparisonExpression expression(Object... right) { + default ComparisonSingleExpression expression(Object... right) { return new ComparisonSingleExpression(this, right); } diff --git a/src/main/java/org/usf/jquery/core/DBProcessor.java b/src/main/java/org/usf/jquery/core/DBProcessor.java index 1c54f0c5..94427654 100644 --- a/src/main/java/org/usf/jquery/core/DBProcessor.java +++ b/src/main/java/org/usf/jquery/core/DBProcessor.java @@ -11,9 +11,7 @@ * @author u$f * */ -public interface DBProcessor extends DBObject { - - T args(Object... args); +public interface DBProcessor extends DBObject { static Optional lookup(Class clazz, Class ext, String op) { try { diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 2f2904e3..73c0eb7f 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -16,6 +16,7 @@ import static org.usf.jquery.core.Parameter.optional; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; +import static org.usf.jquery.core.Validation.requireNArgs; import java.util.Optional; @@ -24,7 +25,7 @@ * @author u$f * */ -public interface Operator extends DBProcessor { +public interface Operator extends DBProcessor { String id(); @@ -150,6 +151,14 @@ static TypedOperator substring() { //int start, int length static TypedOperator concat() { return new TypedOperator(VARCHAR, function("CONCAT"), required(VARCHAR), required(VARCHAR), varargs(VARCHAR)); } + + static TypedOperator lpad() { + return new TypedOperator(VARCHAR, function("LPAD"), required(BIGINT, VARCHAR), required(INTEGER), required(VARCHAR)); + } + + static TypedOperator rpad() { + return new TypedOperator(VARCHAR, function("RPAD"), required(BIGINT, VARCHAR), required(INTEGER), required(VARCHAR)); + } //temporal functions @@ -195,7 +204,16 @@ static TypedOperator second() { static TypedOperator epoch() { return new TypedOperator(BIGINT, extract("EPOCH"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata } - + static TypedOperator yearMonth() { + var op = new CombinedOperator("year", (t, args)->{ + var col = requireNArgs(1, args, ()-> "yearMonth")[0]; + return concat().operation( + lpad().operation(year().operation(col), 4, "0"), "-", + lpad().operation(month().operation(col), 2, "0")); + }); + return new TypedOperator(BIGINT, op, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata + } + //cast functions static TypedOperator varchar() { diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 0372e1d7..b958b204 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -33,7 +33,7 @@ public ComparisonExpression expression(Object... right) { public DBFilter filter(Object... args) { try { - return comparator.args(parameterSet.assertArguments(args)); + return comparator.filter(parameterSet.assertArguments(args)); } catch (BadArgumentException e) { //TODO message throw badArgumentsException(toString(), comparator.id() + joinAndDelemitArray(SCOMA, "(", ")", args), e); } diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index aca93d35..c12020aa 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -26,7 +26,4 @@ public final class Constants { public static final String OFFSET = "offset"; public static final String JOIN = "join"; public static final String PARTITION = "partition"; - - @Deprecated - static final YearMonth[] EMPTY_REVISION = new YearMonth[0]; //not standard } diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index ca390d4f..574175f4 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -53,8 +53,8 @@ public static RevisionIterator iterator(YearMonth[] revisions){ throw new IllegalArgumentException("no revision"); } - static TableView yearTable(String schema, String name) { - return new TableView(schema, name) { + static TableView yearTable(TableView view) { + return new TableView(view.getSchema(), view.getName()) { @Override public String sql(QueryParameterBuilder builder) { return super.sql(builder) + "_" + currentRev.get().getKey(); diff --git a/src/main/java/org/usf/jquery/web/YearViewDecorator.java b/src/main/java/org/usf/jquery/web/YearViewDecorator.java index cc3a05ed..a3912907 100644 --- a/src/main/java/org/usf/jquery/web/YearViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearViewDecorator.java @@ -4,30 +4,30 @@ import static java.time.Month.DECEMBER; import static java.time.YearMonth.now; import static java.util.Objects.nonNull; -import static java.util.stream.Collectors.groupingBy; +import static java.util.Objects.requireNonNull; +import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Utils.isBlank; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.web.Constants.EMPTY_REVISION; import static org.usf.jquery.web.ContextManager.currentContext; -import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.RevisionIterator.iterator; +import static org.usf.jquery.web.RevisionIterator.monthFilter; import static org.usf.jquery.web.RevisionIterator.yearColumn; +import static org.usf.jquery.web.RevisionIterator.yearTable; import static org.usf.jquery.web.ViewDecorator.flatParameters; import java.time.Year; import java.time.YearMonth; -import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.function.UnaryOperator; import java.util.stream.Stream; -import org.usf.jquery.core.DBFilter; +import org.usf.jquery.core.DBView; import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.TableView; import org.usf.jquery.core.TaggableColumn; /** @@ -35,77 +35,49 @@ * @author u$f * */ -public interface YearViewDecorator extends IterableViewDecorator>> { +public interface YearViewDecorator extends ViewDecorator { + static final YearMonth[] EMPTY_REVISION = new YearMonth[0]; static final String REVISION = "revision"; static final String REVISION_MODE = "revision.mode"; - - @Override - default String viewName(String name) { - return name + "_" + currentModel().getKey(); //get it during build - } - - @Override - default DBFilter filter(){ - if(nonNull(monthRevision())) { - var c = column(monthRevision()); - var v = currentModel().getValue(); //get it during build - return v.size() == 1 - ? c.eq(v.get(0).getMonthValue()) - : c.in(v.stream().map(YearMonth::getMonthValue).toArray(Integer[]::new)); - } - return null; - } - - @Override - default Collection>> parseIterable(Map parameterMap) { - var arr = parameterMap.get(REVISION_MODE); - if(nonNull(arr) && arr.length > 1) { - throw new IllegalArgumentException("too many " + REVISION_MODE + " " + join(", ", arr)); //multiple values - } - var mod = revisionMode(isEmpty(arr) ? defaultRevisionMode() : arr[0]); - var values = parameterMap.containsKey(REVISION) - ? flatParameters(parameterMap.get(REVISION)) - .map(this::parseYearMonth) - .toArray(YearMonth[]::new) - : new YearMonth[] {now()}; - var revs = mod.apply(values); - if(!isEmpty(revs)) { - return Stream.of(revs).collect(groupingBy(YearMonth::getYear)).entrySet(); - } - throw noSuchResourceException(REVISION, - Stream.of(values).map(YearMonth::toString).collect(joining(", "))); //require available revisions - } - ColumnDecorator yearRevision(); //!table column ColumnDecorator monthRevision(); //optional - /** - * loaded from db if null - * - */ - default YearMonth[] availableRevisions() {//cache - return metadata().getRevisions(); - } + @Override + default DBView view() { + var v = ViewDecorator.super.builder().build(); + if(v instanceof TableView t) { + return yearTable(t); + } + throw new UnsupportedOperationException(requireNonNull(v).getClass().getSimpleName()); + } + + @Override + default TaggableColumn column(ColumnDecorator column) { + var cd = yearRevision(); + return cd.equals(column) + ? yearColumn().as(cd.reference(this)) + : ViewDecorator.super.column(column); + } @Override default RequestQueryBuilder query(Map parameterMap) { var query = ViewDecorator.super.query(parameterMap); - monthRevision().map(this::column) - .ifPresent(c-> query.filters(RevisionIterator.monthFilter(c))); + ofNullable(monthRevision()).map(this::column) + .ifPresent(c-> query.filters(monthFilter(c))); return query.repeat(iterator(parseRevisions(parameterMap))); } default YearMonth[] parseRevisions(Map parameterMap) { - var arr = parameterMap.get(REVISION_MODE); + var arr = parameterMap.remove(REVISION_MODE); if(nonNull(arr) && arr.length > 1) { - throw EvalException.cannotEvaluateException(REVISION, join(", ", arr)); //multiple values + throw new IllegalArgumentException("too many " + REVISION_MODE + " " + join(", ", arr)); //multiple values } var mod = revisionMode(isEmpty(arr) || isBlank(arr[0]) ? defaultRevisionMode() : arr[0]); var values = parameterMap.containsKey(REVISION) - ? flatParameters(parameterMap.get(REVISION)) + ? flatParameters(parameterMap.remove(REVISION)) .map(this::parseYearMonth) .toArray(YearMonth[]::new) : new YearMonth[] {now()}; @@ -116,20 +88,13 @@ default YearMonth[] parseRevisions(Map parameterMap) { } return revs; } - - @Override - default TaggableColumn column(ColumnDecorator column) { - return column == yearRevision() - ? yearColumn().as(yearRevision().reference(this)) - : ViewDecorator.super.column(column); - } default UnaryOperator revisionMode(String mode) { switch(mode) { case "strict" : return this::strictRevisions; case "preceding" : return this::precedingRevisions; case "succeeding" : return this::succeedingRevisions; - default : throw noSuchResourceException(REVISION_MODE, mode); + default : throw new IllegalArgumentException("cannot parse revision mode " + mode); } } @@ -181,6 +146,14 @@ default YearMonth[] succeedingRevisions(YearMonth[] values) { } return list.isEmpty() ? EMPTY_REVISION : list.toArray(YearMonth[]::new); } + + /** + * loaded from db if null + * + */ + default YearMonth[] availableRevisions() {//cache + return metadata().getRevisions(); + } default YearMonth parseYearMonth(String revision) { if(revision.matches("^\\d{4}$")) { @@ -190,7 +163,7 @@ default YearMonth parseYearMonth(String revision) { return YearMonth.parse(revision); } catch (Exception e) { - throw cannotParseEntryException(REVISION, revision ,e); + throw new IllegalArgumentException("cannot parse revision" + revision, e); } } @@ -202,5 +175,4 @@ default YearTableMetadata metadata() { default String defaultRevisionMode() { return "strict"; } - } From ab875f365cb4aaea12811b5f110ae2ae1f9d5840 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 22 Aug 2024 01:45:22 +0200 Subject: [PATCH 157/298] edit --- .../org/usf/jquery/core/CombinedOperator.java | 22 +++++++++++++++++++ .../java/org/usf/jquery/core/Operator.java | 11 ++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/CombinedOperator.java diff --git a/src/main/java/org/usf/jquery/core/CombinedOperator.java b/src/main/java/org/usf/jquery/core/CombinedOperator.java new file mode 100644 index 00000000..9d061ead --- /dev/null +++ b/src/main/java/org/usf/jquery/core/CombinedOperator.java @@ -0,0 +1,22 @@ +package org.usf.jquery.core; + +/** + * + * @author u$f + * + */ +public interface CombinedOperator extends Operator { + + @Override + OperationColumn args(JDBCType type, Object... args); + + @Override + default String id() { + return null; + } + + @Override + default String sql(QueryParameterBuilder builder, Object[] args) { + throw new UnsupportedOperationException("sql"); + } +} diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 73c0eb7f..1301bc30 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -27,7 +27,7 @@ */ public interface Operator extends DBProcessor { - String id(); + String id(); //nullable default OperationColumn args(Object... args) { return args(null, args); // no type @@ -204,14 +204,17 @@ static TypedOperator second() { static TypedOperator epoch() { return new TypedOperator(BIGINT, extract("EPOCH"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata } + + //combined functions + static TypedOperator yearMonth() { - var op = new CombinedOperator("year", (t, args)->{ + CombinedOperator op = (t, args)-> { var col = requireNArgs(1, args, ()-> "yearMonth")[0]; return concat().operation( lpad().operation(year().operation(col), 4, "0"), "-", lpad().operation(month().operation(col), 2, "0")); - }); - return new TypedOperator(BIGINT, op, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata + }; + return new TypedOperator(VARCHAR, op, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata } //cast functions From 83eb1fdce4be4a722f473a3f8c05cd40e81bf36a Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 22 Aug 2024 01:50:15 +0200 Subject: [PATCH 158/298] edit --- src/main/java/org/usf/jquery/core/CombinedOperator.java | 2 +- src/main/java/org/usf/jquery/core/Operator.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CombinedOperator.java b/src/main/java/org/usf/jquery/core/CombinedOperator.java index 9d061ead..3cf90cda 100644 --- a/src/main/java/org/usf/jquery/core/CombinedOperator.java +++ b/src/main/java/org/usf/jquery/core/CombinedOperator.java @@ -8,7 +8,7 @@ public interface CombinedOperator extends Operator { @Override - OperationColumn args(JDBCType type, Object... args); + OperationColumn args(Object... args); @Override default String id() { diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 1301bc30..f1e17eec 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -208,7 +208,7 @@ static TypedOperator epoch() { //combined functions static TypedOperator yearMonth() { - CombinedOperator op = (t, args)-> { + CombinedOperator op = args-> { var col = requireNArgs(1, args, ()-> "yearMonth")[0]; return concat().operation( lpad().operation(year().operation(col), 4, "0"), "-", From baeb28f122f29f6f9e06c7df15b42c06c313b81c Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 23 Aug 2024 10:10:42 +0200 Subject: [PATCH 159/298] edit --- .../java/org/usf/jquery/core/JDBCType.java | 3 +- .../org/usf/jquery/web/ViewDecorator.java | 13 +++++--- .../org/usf/jquery/web/YearTableMetadata.java | 31 ++++++++----------- .../org/usf/jquery/web/YearViewDecorator.java | 7 ++++- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index 643fae03..9a05304f 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -97,8 +97,7 @@ private static boolean isString(Object o) { public static Optional typeOf(Object o) { if(o instanceof Typed to) { - var type = to.getType(); - return type instanceof JDBCType jType ? Optional.of(jType) : empty(); + return Optional.of(to.getType()); } return Optional.of(o).flatMap(v-> findType(e-> e.typeClass().isInstance(o))); } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 304c0946..ad277679 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -24,6 +24,7 @@ import static org.usf.jquery.web.RequestParser.parseEntry; import static org.usf.jquery.web.ResourceAccessException.undeclaredResouceException; +import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; @@ -91,11 +92,15 @@ private TableView buildView() { default ViewMetadata metadata() { var view = requireNonNull(builder(), identity() + ".builder").build(); - return currentContext().computeTableMetadata(this, cols-> new ViewMetadata(view, - cols.stream().>mapMulti((cd, acc)-> ofNullable(columnName(cd)) - .map(cn-> entry(cd.identity(), columnMetadata(cn, cd.type(this)))) + return currentContext().computeTableMetadata(this, cols-> + new ViewMetadata(view, declaredColumns(this, cols))); + } + + static Map declaredColumns(ViewDecorator vd, Collection cols){ + return cols.stream().>mapMulti((cd, acc)-> ofNullable(vd.columnName(cd)) + .map(cn-> entry(cd.identity(), columnMetadata(cn, cd.type(vd)))) .ifPresent(acc)) //view column only - .collect(toUnmodifiableMap(Entry::getKey, Entry::getValue)))); + .collect(toUnmodifiableMap(Entry::getKey, Entry::getValue)); } default RequestQueryBuilder query(Map parameterMap) { diff --git a/src/main/java/org/usf/jquery/web/YearTableMetadata.java b/src/main/java/org/usf/jquery/web/YearTableMetadata.java index 084dfd28..b0adf474 100644 --- a/src/main/java/org/usf/jquery/web/YearTableMetadata.java +++ b/src/main/java/org/usf/jquery/web/YearTableMetadata.java @@ -16,7 +16,7 @@ import static org.usf.jquery.core.JDBCType.OTHER; import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.web.Constants.EMPTY_REVISION; +import static org.usf.jquery.web.YearViewDecorator.EMPTY_REVISION; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -33,7 +33,6 @@ import java.util.Set; import java.util.stream.Stream; -import org.usf.jquery.core.DBQuery; import org.usf.jquery.core.DBView; import org.usf.jquery.core.TableView; @@ -55,7 +54,7 @@ public final class YearTableMetadata extends ViewMetadata { @Getter private YearMonth[] revisions; - private YearTableMetadata(DBView view, String revisionColumn, Map columns) { + YearTableMetadata(DBView view, String revisionColumn, Map columns) { super(view, columns); this.revisionColumn = revisionColumn; this.revisions = EMPTY_REVISION; //by default avoid NullPointerException @@ -81,14 +80,16 @@ void fetchView(DatabaseMetaData metadata, TableView view, String schema) throws if(nonNull(cm) && tn.matches(view.getName() + "_20\\d{2}")) {// untyped SQL pattern tablenames.add(tn); columnTables.computeIfAbsent(cm.getName(), k-> new LinkedHashSet<>()).add(tn); - var type = rs.getInt("DATA_TYPE"); - var size = rs.getInt("COLUMN_SIZE"); - if(isNull(cm.getType())) { //first time - cm.setDataType(typeOf(type).orElse(OTHER)); - cm.setDataSize(size); - } - else if(cm.getType().getValue() != type || cm.getDataSize() != size) { - dirtyColumns.add(cm.getName()); + if(!cm.isOverConfigured()) { + var type = rs.getInt("DATA_TYPE"); + var size = rs.getInt("COLUMN_SIZE"); + var digt = rs.getInt("DECIMAL_DIGITS"); + if(isNull(cm.getType())) { //first time + cm.update(type, size, digt); + } + else if(cm.getType().getValue() != type || cm.getDataSize() != size || cm.getPrecision() != digt) { + dirtyColumns.add(cm.getName()); + } } }//else undeclared column } while(rs.next()); @@ -107,7 +108,7 @@ else if(cm.getType().getValue() != type || cm.getDataSize() != size) { } @Override - void fetch(DatabaseMetaData metadata, DBQuery qr) throws SQLException { + void fetch(DatabaseMetaData metadata, DBView qr, String schema) throws SQLException { throw new UnsupportedOperationException("query"); } @@ -143,12 +144,6 @@ void fetchRevisions(Connection cn) { // change this call } } } - - static YearTableMetadata yearTableMetadata(YearViewDecorator table) { - return new YearTableMetadata(table.viewName(), - table.monthRevision().map(table::columnName).orElse(null), - table.declaredColumns()); - } @Deprecated static void logRevisions(YearMonth[] revs) { diff --git a/src/main/java/org/usf/jquery/web/YearViewDecorator.java b/src/main/java/org/usf/jquery/web/YearViewDecorator.java index a3912907..b1923d5d 100644 --- a/src/main/java/org/usf/jquery/web/YearViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearViewDecorator.java @@ -15,6 +15,7 @@ import static org.usf.jquery.web.RevisionIterator.monthFilter; import static org.usf.jquery.web.RevisionIterator.yearColumn; import static org.usf.jquery.web.RevisionIterator.yearTable; +import static org.usf.jquery.web.ViewDecorator.declaredColumns; import static org.usf.jquery.web.ViewDecorator.flatParameters; import java.time.Year; @@ -169,7 +170,11 @@ default YearMonth parseYearMonth(String revision) { @Override default YearTableMetadata metadata() { - return (YearTableMetadata) currentContext().computeTableMetadata(this, null); //safe cast + return (YearTableMetadata) currentContext().computeTableMetadata(this, cols->{ + return new YearTableMetadata(view(), + ofNullable(monthRevision()).map(this::columnName).orElse(null), + declaredColumns(this, cols)); + }); //safe cast } default String defaultRevisionMode() { From 2baa8c903c062ad3498eecd4b94b4bd184759b43 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 23 Aug 2024 13:43:30 +0200 Subject: [PATCH 160/298] edit --- .../java/org/usf/jquery/core/CombinedOperator.java | 10 +++++++++- src/main/java/org/usf/jquery/core/Operator.java | 4 ---- src/main/java/org/usf/jquery/web/ColumnMetadata.java | 1 - src/main/java/org/usf/jquery/web/Constants.java | 2 -- .../java/org/usf/jquery/web/YearTableMetadata.java | 10 ++++------ 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CombinedOperator.java b/src/main/java/org/usf/jquery/core/CombinedOperator.java index 3cf90cda..c92eb09b 100644 --- a/src/main/java/org/usf/jquery/core/CombinedOperator.java +++ b/src/main/java/org/usf/jquery/core/CombinedOperator.java @@ -7,8 +7,16 @@ */ public interface CombinedOperator extends Operator { - @Override OperationColumn args(Object... args); + + @Override + default OperationColumn args(JDBCType type, Object... args) { + var c = args(args); + if(c.getType() == type) { + return c; + } + throw new IllegalStateException("mismatch type : " + type); + } @Override default String id() { diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index f1e17eec..f998ddf9 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -29,10 +29,6 @@ public interface Operator extends DBProcessor { String id(); //nullable - default OperationColumn args(Object... args) { - return args(null, args); // no type - } - default OperationColumn args(JDBCType type, Object... args) { return new OperationColumn(this, args, type); } diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index 68542c5e..c4db901f 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -36,7 +36,6 @@ public final class ColumnMetadata { private int precision; private final boolean overConfigured; - @Deprecated ColumnMetadata reset() { if(!overConfigured) { this.type = null; diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Constants.java index c12020aa..6620131a 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Constants.java @@ -1,7 +1,5 @@ package org.usf.jquery.web; -import java.time.YearMonth; - import lombok.AccessLevel; import lombok.NoArgsConstructor; diff --git a/src/main/java/org/usf/jquery/web/YearTableMetadata.java b/src/main/java/org/usf/jquery/web/YearTableMetadata.java index b0adf474..9ea3da1f 100644 --- a/src/main/java/org/usf/jquery/web/YearTableMetadata.java +++ b/src/main/java/org/usf/jquery/web/YearTableMetadata.java @@ -13,8 +13,6 @@ import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toMap; -import static org.usf.jquery.core.JDBCType.OTHER; -import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.web.YearViewDecorator.EMPTY_REVISION; @@ -143,14 +141,14 @@ void fetchRevisions(Connection cn) { // change this call this.revisions = EMPTY_REVISION; } } + logRevisions(); } - @Deprecated - static void logRevisions(YearMonth[] revs) { - if(!isEmpty(revs)) { + void logRevisions() { + if(!isEmpty(revisions)) { var pattern = "|%-5s|%-40s|"; var bar = format(pattern, "", "").replace("|", "+").replace(" ", "-"); - var map = Stream.of(revs).collect(groupingBy(YearMonth::getYear)); + var map = Stream.of(revisions).collect(groupingBy(YearMonth::getYear)); log.info(bar); log.info(format(pattern, "YEAR", "MONTHS")); log.info(bar); From dfa8aaa5ad49a47fe969754f40c69947fb130edb Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 23 Aug 2024 13:53:25 +0200 Subject: [PATCH 161/298] edit --- src/main/java/org/usf/jquery/core/CombinedOperator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/usf/jquery/core/CombinedOperator.java b/src/main/java/org/usf/jquery/core/CombinedOperator.java index c92eb09b..e7cb50dc 100644 --- a/src/main/java/org/usf/jquery/core/CombinedOperator.java +++ b/src/main/java/org/usf/jquery/core/CombinedOperator.java @@ -5,6 +5,7 @@ * @author u$f * */ +@FunctionalInterface public interface CombinedOperator extends Operator { OperationColumn args(Object... args); From b825c3959e463afbc3ce9f18d613fab22730bec9 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 23 Aug 2024 13:56:57 +0200 Subject: [PATCH 162/298] edit --- src/main/java/org/usf/jquery/core/CombinedOperator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CombinedOperator.java b/src/main/java/org/usf/jquery/core/CombinedOperator.java index e7cb50dc..1c233e4d 100644 --- a/src/main/java/org/usf/jquery/core/CombinedOperator.java +++ b/src/main/java/org/usf/jquery/core/CombinedOperator.java @@ -13,10 +13,10 @@ public interface CombinedOperator extends Operator { @Override default OperationColumn args(JDBCType type, Object... args) { var c = args(args); - if(c.getType() == type) { + if(type == c.getType()) { return c; } - throw new IllegalStateException("mismatch type : " + type); + throw new IllegalStateException("type mismatch : " + type); } @Override From 21d02b27ed670097f44c27c932012067b4d7d821 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 23 Aug 2024 14:15:03 +0200 Subject: [PATCH 163/298] edit --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1148492b..61955f62 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,10 +20,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Build & Scan Project env: From 8b92304dbe54ff4213939cc88a08186fd5a718c5 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 23 Aug 2024 15:56:43 +0200 Subject: [PATCH 164/298] edit --- .../java/org/usf/jquery/core/ViewJoin.java | 7 ------ .../usf/jquery/web/ContextEnvironment.java | 24 +++++++++++++++---- .../org/usf/jquery/web/ContextManager.java | 2 +- .../web/{Constants.java => Parameters.java} | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 22 ++++++++--------- .../jquery/web/RequestQueryParamResolver.java | 6 ++--- .../org/usf/jquery/web/ViewDecorator.java | 14 +++++------ 7 files changed, 43 insertions(+), 34 deletions(-) rename src/main/java/org/usf/jquery/web/{Constants.java => Parameters.java} (96%) diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index 3f03a5a9..901c91ba 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -61,12 +61,5 @@ public static ViewJoin join(JoinType jt, DBView view, DBFilter... filters) { public enum JoinType { INNER, LEFT, RIGHT, FULL; - - public static String pattern() { - return Stream.of(values()) - .map(JoinType::name) - .map(String::toLowerCase) - .collect(joining("|")); - } } } diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index 6c02ed75..e9330375 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -1,5 +1,6 @@ package org.usf.jquery.web; +import static java.lang.reflect.Modifier.isStatic; import static java.util.Collections.unmodifiableMap; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; @@ -7,19 +8,23 @@ import static java.util.Optional.ofNullable; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNonEmpty; import static org.usf.jquery.web.ResourceAccessException.resourceAlreadyExistsException; +import java.lang.reflect.Field; import java.sql.SQLException; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; +import java.util.stream.Stream; import javax.sql.DataSource; @@ -27,7 +32,6 @@ import org.usf.jquery.core.JQueryException; import org.usf.jquery.core.QueryView; import org.usf.jquery.core.TaggableColumn; -import org.usf.jquery.core.Validation; import lombok.AccessLevel; import lombok.Getter; @@ -44,6 +48,11 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class ContextEnvironment { + private static final Set RESERVED_WORDS = Stream.of(Parameters.class.getDeclaredFields()) + .filter(f-> isStatic(f.getModifiers())) + .map(Field::getName) + .collect(toSet()); + private final DatabaseDecorator database; private final Map views; private final Map columns; @@ -105,7 +114,7 @@ ViewMetadata computeTableMetadata(ViewDecorator vd, Function views, Collection columns, DataSource ds, String schema) { - requireLegalVariable(requireNonNull(database, "configuration.database").identity()); + assertIdentity(requireNonNull(database, "configuration.database").identity()); return new ContextEnvironment(database, unmodifiableIdentityMap(views, ViewDecorator::identity, database.identity() + ".views"), //preserve views order unmodifiableIdentityMap(columns, ColumnDecorator::identity, database.identity() + ".columns"), @@ -142,7 +151,7 @@ public static ContextEnvironment of(DatabaseDecorator database, static Map unmodifiableIdentityMap(Collection c, Function fn, String msg){ return unmodifiableMap(requireNonEmpty(c, msg).stream() - .collect(toLinkedMap(fn.andThen(Validation::requireLegalVariable), identity()))); + .collect(toLinkedMap(fn.andThen(ContextEnvironment::assertIdentity), identity()))); } static Collector> toLinkedMap( @@ -152,4 +161,11 @@ static Map unmodifiableIdentityMap(Collection c, Function {throw new IllegalStateException("!parallel");}, LinkedHashMap::new); } + + static String assertIdentity(String id) { + if(!RESERVED_WORDS.contains(requireLegalVariable(id))){ + return id; + } + throw new IllegalArgumentException("reserved word cannot be used as an identifier: " + id); + } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/ContextManager.java b/src/main/java/org/usf/jquery/web/ContextManager.java index a9a5ed77..d1d42bb5 100644 --- a/src/main/java/org/usf/jquery/web/ContextManager.java +++ b/src/main/java/org/usf/jquery/web/ContextManager.java @@ -2,7 +2,7 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; -import static org.usf.jquery.web.Constants.DATABASE; +import static org.usf.jquery.web.Parameters.DATABASE; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.ResourceAccessException.resourceAlreadyExistsException; diff --git a/src/main/java/org/usf/jquery/web/Constants.java b/src/main/java/org/usf/jquery/web/Parameters.java similarity index 96% rename from src/main/java/org/usf/jquery/web/Constants.java rename to src/main/java/org/usf/jquery/web/Parameters.java index 6620131a..4d201800 100644 --- a/src/main/java/org/usf/jquery/web/Constants.java +++ b/src/main/java/org/usf/jquery/web/Parameters.java @@ -9,7 +9,7 @@ * */ @NoArgsConstructor(access = AccessLevel.PRIVATE) -public final class Constants { +public final class Parameters { public static final String DATABASE = "database"; public static final String QUERY = "query"; diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index fee82a0f..437df7a6 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -24,17 +24,17 @@ import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Utils.joinArray; import static org.usf.jquery.web.ArgumentParsers.parse; -import static org.usf.jquery.web.Constants.COLUMN; -import static org.usf.jquery.web.Constants.DISTINCT; -import static org.usf.jquery.web.Constants.FETCH; -import static org.usf.jquery.web.Constants.FILTER; -import static org.usf.jquery.web.Constants.JOIN; -import static org.usf.jquery.web.Constants.OFFSET; -import static org.usf.jquery.web.Constants.ORDER; -import static org.usf.jquery.web.Constants.PARTITION; -import static org.usf.jquery.web.Constants.QUERY; -import static org.usf.jquery.web.Constants.SELECT; -import static org.usf.jquery.web.Constants.VIEW; +import static org.usf.jquery.web.Parameters.COLUMN; +import static org.usf.jquery.web.Parameters.DISTINCT; +import static org.usf.jquery.web.Parameters.FETCH; +import static org.usf.jquery.web.Parameters.FILTER; +import static org.usf.jquery.web.Parameters.JOIN; +import static org.usf.jquery.web.Parameters.OFFSET; +import static org.usf.jquery.web.Parameters.ORDER; +import static org.usf.jquery.web.Parameters.PARTITION; +import static org.usf.jquery.web.Parameters.QUERY; +import static org.usf.jquery.web.Parameters.SELECT; +import static org.usf.jquery.web.Parameters.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.ContextManager.setCurrentContext; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index e2c1f89a..6b72a843 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -2,9 +2,9 @@ import static java.lang.System.currentTimeMillis; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.web.Constants.COLUMN; -import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; -import static org.usf.jquery.web.Constants.VIEW; +import static org.usf.jquery.web.Parameters.COLUMN; +import static org.usf.jquery.web.Parameters.COLUMN_DISTINCT; +import static org.usf.jquery.web.Parameters.VIEW; import static org.usf.jquery.web.ContextManager.context; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.ContextManager.releaseContext; diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index ad277679..715e014c 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -11,13 +11,13 @@ import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.web.ColumnMetadata.columnMetadata; -import static org.usf.jquery.web.Constants.COLUMN; -import static org.usf.jquery.web.Constants.COLUMN_DISTINCT; -import static org.usf.jquery.web.Constants.FETCH; -import static org.usf.jquery.web.Constants.JOIN; -import static org.usf.jquery.web.Constants.OFFSET; -import static org.usf.jquery.web.Constants.ORDER; -import static org.usf.jquery.web.Constants.VIEW; +import static org.usf.jquery.web.Parameters.COLUMN; +import static org.usf.jquery.web.Parameters.COLUMN_DISTINCT; +import static org.usf.jquery.web.Parameters.FETCH; +import static org.usf.jquery.web.Parameters.JOIN; +import static org.usf.jquery.web.Parameters.OFFSET; +import static org.usf.jquery.web.Parameters.ORDER; +import static org.usf.jquery.web.Parameters.VIEW; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; From 33f16b2ea9ef7756f98a439d84ab1ad6f0d4aa44 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 23 Aug 2024 17:16:34 +0200 Subject: [PATCH 165/298] edit --- .../usf/jquery/core/AggregateFunction.java | 2 +- .../java/org/usf/jquery/core/ArgTypeRef.java | 2 +- .../usf/jquery/core/ArithmeticOperator.java | 2 +- .../org/usf/jquery/core/CastFunction.java | 2 +- .../org/usf/jquery/core/ExtractFunction.java | 2 +- .../java/org/usf/jquery/core/JoinType.java | 6 +++ .../org/usf/jquery/core/PipeFunction.java | 2 +- .../java/org/usf/jquery/core/QueryColumn.java | 9 ++++- .../java/org/usf/jquery/core/Validation.java | 38 +++---------------- .../java/org/usf/jquery/core/ViewJoin.java | 13 ++----- 10 files changed, 29 insertions(+), 49 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/JoinType.java diff --git a/src/main/java/org/usf/jquery/core/AggregateFunction.java b/src/main/java/org/usf/jquery/core/AggregateFunction.java index 1a28bc35..da71f32e 100644 --- a/src/main/java/org/usf/jquery/core/AggregateFunction.java +++ b/src/main/java/org/usf/jquery/core/AggregateFunction.java @@ -8,4 +8,4 @@ @FunctionalInterface public interface AggregateFunction extends FunctionOperator { -} +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/ArgTypeRef.java b/src/main/java/org/usf/jquery/core/ArgTypeRef.java index 696743f6..ef50b0b2 100644 --- a/src/main/java/org/usf/jquery/core/ArgTypeRef.java +++ b/src/main/java/org/usf/jquery/core/ArgTypeRef.java @@ -14,6 +14,6 @@ interface ArgTypeRef extends Function { static ArgTypeRef firstArgJdbcType() { return arr-> typeOf(requireAtLeastNArgs(1, arr, - ()-> "ArgTypeRef function")[0]).orElse(null); // not sure + ArgTypeRef.class::getSimpleName)[0]).orElse(null); // not sure } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java index 70335657..2d3cca12 100644 --- a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java +++ b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java @@ -12,7 +12,7 @@ interface ArithmeticOperator extends Operator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { - requireNArgs(2, args, ArithmeticOperator.class::getSimpleName); + requireNArgs(2, args, ArithmeticException.class::getSimpleName); return "(" + builder.appendLiteral(args[0]) + id() + builder.appendLiteral(args[1]) + ")"; } } diff --git a/src/main/java/org/usf/jquery/core/CastFunction.java b/src/main/java/org/usf/jquery/core/CastFunction.java index 6c6826d9..a5c39ac5 100644 --- a/src/main/java/org/usf/jquery/core/CastFunction.java +++ b/src/main/java/org/usf/jquery/core/CastFunction.java @@ -19,7 +19,7 @@ default String id() { @Override default String sql(QueryParameterBuilder builder, Object[] args) { - requireAtLeastNArgs(1, args, ()-> id() + "_AS_" + asType()); + requireAtLeastNArgs(1, args, CastFunction.class::getSimpleName); var sb = new SqlStringBuilder(id()) .append("(") .append(builder.appendLiteral(args[0])).append(" AS ").append(asType()); diff --git a/src/main/java/org/usf/jquery/core/ExtractFunction.java b/src/main/java/org/usf/jquery/core/ExtractFunction.java index ec663845..71ab1ce1 100644 --- a/src/main/java/org/usf/jquery/core/ExtractFunction.java +++ b/src/main/java/org/usf/jquery/core/ExtractFunction.java @@ -19,7 +19,7 @@ default String id() { @Override default String sql(QueryParameterBuilder builder, Object[] args) { - requireNArgs(1, args, this::id); + requireNArgs(1, args, ExtractFunction.class::getSimpleName); return id() + "(" + field() + " FROM " + builder.appendLiteral(args[0]) + ")"; } } diff --git a/src/main/java/org/usf/jquery/core/JoinType.java b/src/main/java/org/usf/jquery/core/JoinType.java new file mode 100644 index 00000000..534c4e99 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/JoinType.java @@ -0,0 +1,6 @@ +package org.usf.jquery.core; + +public enum JoinType { + + INNER, LEFT, RIGHT, FULL; +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/PipeFunction.java b/src/main/java/org/usf/jquery/core/PipeFunction.java index 463553a1..e6adc249 100644 --- a/src/main/java/org/usf/jquery/core/PipeFunction.java +++ b/src/main/java/org/usf/jquery/core/PipeFunction.java @@ -14,7 +14,7 @@ public interface PipeFunction extends FunctionOperator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { - requireAtLeastNArgs(1, args, ()-> "Pipe function"); + requireAtLeastNArgs(1, args, PipeFunction.class::getSimpleName); return builder.appendLiteral(args[0]) + SPACE + FunctionOperator.super.sql(builder, copyOfRange(args, 1, args.length)); } diff --git a/src/main/java/org/usf/jquery/core/QueryColumn.java b/src/main/java/org/usf/jquery/core/QueryColumn.java index 333a1f38..df2094f6 100644 --- a/src/main/java/org/usf/jquery/core/QueryColumn.java +++ b/src/main/java/org/usf/jquery/core/QueryColumn.java @@ -7,8 +7,13 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; +/** + * + * @author u$f + * + */ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public class QueryColumn implements DBColumn { +public final class QueryColumn implements DBColumn { private final QueryView query; @Getter @@ -16,7 +21,7 @@ public class QueryColumn implements DBColumn { @Override public String sql(QueryParameterBuilder builder) { - requireNArgs(1, query.getBuilder().getColumns().toArray(), ()-> ""); //TODO + requireNArgs(1, query.getBuilder().getColumns().toArray(), ()-> "require only one column: " + query); return query.sql(builder); } diff --git a/src/main/java/org/usf/jquery/core/Validation.java b/src/main/java/org/usf/jquery/core/Validation.java index 1ca99f7f..8989ecf0 100644 --- a/src/main/java/org/usf/jquery/core/Validation.java +++ b/src/main/java/org/usf/jquery/core/Validation.java @@ -1,11 +1,11 @@ package org.usf.jquery.core; +import static java.lang.String.format; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import java.util.Collection; import java.util.function.Supplier; -import java.util.function.UnaryOperator; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -21,59 +21,33 @@ public final class Validation { public static final String VAR_PATTERN = "[a-zA-Z]\\w*"; public static String requireLegalVariable(String s) { - return requireLegalVariable(s, v-> "illegal variable name : " + v); - } - - public static String requireLegalVariable(String s, UnaryOperator msg) { - illegalArgumentIf(isNull(s) || !s.matches(VAR_PATTERN), ()-> msg.apply(s)); - return s; - } - - public static String requireNonBlank(String s) { - illegalArgumentIf(isNull(s) || s.isBlank(), "empty string"); + illegalArgumentIf(isNull(s) || !s.matches(VAR_PATTERN), ()-> "illegal variable name: " + s); return s; } - public static T[] requireNonEmpty(T[] arr){ - illegalArgumentIf(isNull(arr) || arr.length == 0, "empty array"); - return arr; - } - public static Collection requireNonEmpty(Collection c, String name){ - illegalArgumentIf(isNull(c) || c.isEmpty(), name + " is empty"); + illegalArgumentIf(isNull(c) || c.isEmpty(), ()-> name + " is empty"); return c; } public static T[] requireNoArgs(T[] args, Supplier name) { - illegalArgumentIf(nonNull(args) && args.length > 0, ()-> name.get() + " takes no parameters"); + illegalArgumentIf(nonNull(args) && args.length > 0, ()-> format("'%s' takes no arguments", name.get())); return args; } public static T[] requireNArgs(int n, T[] args, Supplier name) { - illegalArgumentIf(isNull(args) || args.length != n, ()-> name.get() + " takes " + n + " parameters"); + illegalArgumentIf(isNull(args) || args.length != n, ()-> format("'%s' takes %d arguments", name.get(), n)); return args; } public static T[] requireAtLeastNArgs(int n, T[] args, Supplier name) { - illegalArgumentIf(isNull(args) || args.length < n, ()-> name.get() + " takes at least " + n + " parameters"); + illegalArgumentIf(isNull(args) || args.length < n, ()-> format("'%s' takes at least %d arguments", name.get(), n)); return args; } - public static T[] requireAtMostNArgs(int n, T[] args, Supplier name) { - illegalArgumentIf(nonNull(args) && args.length > n, ()-> name.get() + " takes at most" + n + " parameters"); - return args; - } - - public static void illegalArgumentIf(boolean test, String msg) { - if(test) { - throw new IllegalArgumentException(msg); - } - } - public static void illegalArgumentIf(boolean test, Supplier supplier) { if(test) { throw new IllegalArgumentException(supplier.get()); } } - } diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index 901c91ba..8ef05761 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -1,12 +1,12 @@ package org.usf.jquery.core; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.JoinType.FULL; +import static org.usf.jquery.core.JoinType.INNER; +import static org.usf.jquery.core.JoinType.LEFT; +import static org.usf.jquery.core.JoinType.RIGHT; import static org.usf.jquery.core.LogicalOperator.AND; import static org.usf.jquery.core.Validation.requireNoArgs; -import static org.usf.jquery.core.ViewJoin.JoinType.FULL; -import static org.usf.jquery.core.ViewJoin.JoinType.INNER; -import static org.usf.jquery.core.ViewJoin.JoinType.LEFT; -import static org.usf.jquery.core.ViewJoin.JoinType.RIGHT; import java.util.stream.Stream; @@ -57,9 +57,4 @@ public static ViewJoin fullJoin(DBView view, DBFilter... filters) { public static ViewJoin join(JoinType jt, DBView view, DBFilter... filters) { return new ViewJoin(jt, view, filters); } - - public enum JoinType { - - INNER, LEFT, RIGHT, FULL; - } } From eacd8b168ff7ea718a2e08d9f3f1e8a31c4eadb2 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 23 Aug 2024 21:58:49 +0200 Subject: [PATCH 166/298] edit --- .../org/usf/jquery/web/ColumnMetadata.java | 16 +++--- .../usf/jquery/web/ContextEnvironment.java | 20 +++---- .../org/usf/jquery/web/ContextManager.java | 2 +- .../org/usf/jquery/web/DatabaseMetadata.java | 14 +++-- .../usf/jquery/web/IterableViewDecorator.java | 57 ------------------- .../org/usf/jquery/web/ModelIterator.java | 44 -------------- .../org/usf/jquery/web/RequestEntryChain.java | 8 +-- .../jquery/web/RequestQueryParamResolver.java | 6 +- .../org/usf/jquery/web/ViewDecorator.java | 2 +- .../usf/jquery/web/ViewDecoratorWrapper.java | 1 - .../java/org/usf/jquery/web/ViewMetadata.java | 23 +++++--- .../org/usf/jquery/web/YearViewDecorator.java | 15 +++-- 12 files changed, 58 insertions(+), 150 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/web/IterableViewDecorator.java delete mode 100644 src/main/java/org/usf/jquery/web/ModelIterator.java diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index c4db901f..1b339869 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -36,6 +36,14 @@ public final class ColumnMetadata { private int precision; private final boolean overConfigured; + public void update(int type, int size, int precision) { + if(!overConfigured) { + this.type = fromDataType(type).orElse(OTHER); + this.dataSize = size; + this.precision = precision; + } + } + ColumnMetadata reset() { if(!overConfigured) { this.type = null; @@ -45,14 +53,6 @@ ColumnMetadata reset() { return this; } - public void update(int type, int size, int precision) { - if(!overConfigured) { - this.type = fromDataType(type).orElse(OTHER); - this.dataSize = size; - this.precision = precision; - } - } - public String toJavaType(){ var t = type.typeClass().getSimpleName(); return overConfigured ? t+"!" : t; diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index e9330375..f3afe319 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -36,14 +36,12 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; /** * * @author u$f * */ -@Slf4j @Getter @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class ContextEnvironment { @@ -115,17 +113,19 @@ ViewMetadata computeTableMetadata(ViewDecorator vd, Function tables = synchronizedMap(new LinkedHashMap<>()); //lazy loading private Database type; - public void fetch(DatabaseMetaData metadata) throws SQLException { - type = Database.of(metadata.getDatabaseProductName()).orElse(null); + public void fetch(DatabaseMetaData metadata) { + try { + type = Database.of(metadata.getDatabaseProductName()).orElse(null); + } + catch (SQLException e) { + log.warn("error while scanning database metadata", e); + } } -} - +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/IterableViewDecorator.java b/src/main/java/org/usf/jquery/web/IterableViewDecorator.java deleted file mode 100644 index 7df54a77..00000000 --- a/src/main/java/org/usf/jquery/web/IterableViewDecorator.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.usf.jquery.web; - -import static java.util.Objects.nonNull; -import static org.usf.jquery.core.SqlStringBuilder.member; -import static org.usf.jquery.web.ModelIterator.iterator; - -import java.util.Collection; -import java.util.Map; - -import org.usf.jquery.core.DBFilter; -import org.usf.jquery.core.QueryParameterBuilder; -import org.usf.jquery.core.RequestQueryBuilder; -import org.usf.jquery.core.TableView; - -/** - * - * @author u$f - * - */ -public interface IterableViewDecorator extends ViewDecorator { - - @Override - default ViewBuilder builder() { - var v = (TableView) ViewDecorator.super.builder().build(); - return ()-> new TableView(v.getSchema(), v.getName()) { - @Override - public String sql(QueryParameterBuilder builder) { - return member(getSchemaOrElse(builder.getSchema()), viewName(v.getName())); - } - }; - } - - default String viewName(String name){ - return name; - } - - Collection parseIterable(Map parameterMap); - - @Override - default void parseFilters(RequestQueryBuilder query, Map parameters) { - var it = iterator(parseIterable(parameters)); - var fr = filter(); //optional - if(nonNull(fr)) { - query.filters(b-> fr.sql(b)); - } - ViewDecorator.super.parseFilters(query, parameters); - query.repeat(it); - } - - default DBFilter filter(){ - return null; - } - - default T currentModel() { - return null; - } -} diff --git a/src/main/java/org/usf/jquery/web/ModelIterator.java b/src/main/java/org/usf/jquery/web/ModelIterator.java deleted file mode 100644 index 0de2059d..00000000 --- a/src/main/java/org/usf/jquery/web/ModelIterator.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.usf.jquery.web; - -import static org.usf.jquery.core.Utils.isEmpty; - -import java.util.Collection; -import java.util.Iterator; - -import lombok.RequiredArgsConstructor; - -/** - * - * @author u$f - * - */ -@RequiredArgsConstructor -public final class ModelIterator implements Iterator { - - private static final ThreadLocal currentRev = new ThreadLocal<>(); - - private final Iterator it; - - @Override - public boolean hasNext() { - if(it.hasNext()) { - return true; - } - currentRev.remove(); //auto clean - return false; - } - - @Override - public T next() { - var rev = it.next(); - currentRev.set(rev); - return rev; - } - - public static ModelIterator iterator(Collection model){ - if(!isEmpty(model)) { - return new ModelIterator<>(model.iterator()); - } - throw new IllegalArgumentException("empty array"); - } -} diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 437df7a6..8be62921 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -24,6 +24,10 @@ import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Utils.joinArray; import static org.usf.jquery.web.ArgumentParsers.parse; +import static org.usf.jquery.web.ContextManager.currentContext; +import static org.usf.jquery.web.ContextManager.setCurrentContext; +import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; +import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.Parameters.COLUMN; import static org.usf.jquery.web.Parameters.DISTINCT; import static org.usf.jquery.web.Parameters.FETCH; @@ -35,10 +39,6 @@ import static org.usf.jquery.web.Parameters.QUERY; import static org.usf.jquery.web.Parameters.SELECT; import static org.usf.jquery.web.Parameters.VIEW; -import static org.usf.jquery.web.ContextManager.currentContext; -import static org.usf.jquery.web.ContextManager.setCurrentContext; -import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; -import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index 6b72a843..612f5978 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -2,13 +2,13 @@ import static java.lang.System.currentTimeMillis; import static org.usf.jquery.core.Utils.isEmpty; -import static org.usf.jquery.web.Parameters.COLUMN; -import static org.usf.jquery.web.Parameters.COLUMN_DISTINCT; -import static org.usf.jquery.web.Parameters.VIEW; import static org.usf.jquery.web.ContextManager.context; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.ContextManager.releaseContext; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; +import static org.usf.jquery.web.Parameters.COLUMN; +import static org.usf.jquery.web.Parameters.COLUMN_DISTINCT; +import static org.usf.jquery.web.Parameters.VIEW; import java.util.LinkedHashMap; import java.util.Map; diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 715e014c..b544a3b2 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -11,6 +11,7 @@ import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.web.ColumnMetadata.columnMetadata; +import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.Parameters.COLUMN; import static org.usf.jquery.web.Parameters.COLUMN_DISTINCT; import static org.usf.jquery.web.Parameters.FETCH; @@ -18,7 +19,6 @@ import static org.usf.jquery.web.Parameters.OFFSET; import static org.usf.jquery.web.Parameters.ORDER; import static org.usf.jquery.web.Parameters.VIEW; -import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.RequestParser.parseEntry; diff --git a/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java b/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java index 99df1f99..972ced26 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java +++ b/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java @@ -40,5 +40,4 @@ public CriteriaBuilder criteria(String name) { public JoinBuilder joiner(String name) { return view.joiner(name); } - } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index c1ab1e15..722a9194 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -52,17 +52,22 @@ public ColumnMetadata columnMetadata(ColumnDecorator cd) { final ViewMetadata fetch(DatabaseMetaData metadata, String schema) throws SQLException { if(!isEmpty(columns)) { - var time = currentTimeMillis(); - log.info("scanning view '{}' metadata...", view); - if(view instanceof TableView tab) { - fetchView(metadata, tab, schema); + try { + var time = currentTimeMillis(); + log.info("scanning view '{}' metadata...", view); + if(view instanceof TableView tab) { + fetchView(metadata, tab, schema); + } + else { + fetch(metadata, view, schema); + } + lastUpdate = now(); + log.trace("'{}' metadata scanned in {} ms", view, currentTimeMillis() - time); + printViewColumnMap(); } - else { - fetch(metadata, view, schema); + catch(Exception e) { + log.error("error while scanning '{}' metadata", identity(), e); } - lastUpdate = now(); - log.trace("'{}' metadata scanned in {} ms", view, currentTimeMillis() - time); - printViewColumnMap(); } else { log.warn("'{}' has no declared columns", view); diff --git a/src/main/java/org/usf/jquery/web/YearViewDecorator.java b/src/main/java/org/usf/jquery/web/YearViewDecorator.java index b1923d5d..d17f8aff 100644 --- a/src/main/java/org/usf/jquery/web/YearViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearViewDecorator.java @@ -64,11 +64,11 @@ default TaggableColumn column(ColumnDecorator column) { } @Override - default RequestQueryBuilder query(Map parameterMap) { - var query = ViewDecorator.super.query(parameterMap); + default void parseFilters(RequestQueryBuilder query, Map parameterMap) { ofNullable(monthRevision()).map(this::column) .ifPresent(c-> query.filters(monthFilter(c))); - return query.repeat(iterator(parseRevisions(parameterMap))); + query.repeat(iterator(parseRevisions(parameterMap))); + ViewDecorator.super.parseFilters(query, parameterMap); } default YearMonth[] parseRevisions(Map parameterMap) { @@ -170,11 +170,10 @@ default YearMonth parseYearMonth(String revision) { @Override default YearTableMetadata metadata() { - return (YearTableMetadata) currentContext().computeTableMetadata(this, cols->{ - return new YearTableMetadata(view(), - ofNullable(monthRevision()).map(this::columnName).orElse(null), - declaredColumns(this, cols)); - }); //safe cast + return (YearTableMetadata) currentContext().computeTableMetadata(this, cols-> new YearTableMetadata(view(), + ofNullable(monthRevision()) + .map(this::columnName).orElse(null), + declaredColumns(this, cols))); //safe cast } default String defaultRevisionMode() { From 653bdffabd5ea5afccc8c8c802981459e9067f9d Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 24 Aug 2024 01:41:59 +0200 Subject: [PATCH 167/298] edit --- .../org/usf/jquery/web/RequestParser.java | 111 ++++++++++-------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 619c5e43..36efc38b 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -5,8 +5,9 @@ import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Validation.VAR_PATTERN; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; +import java.util.function.Supplier; /** * @@ -27,74 +28,86 @@ private RequestParser(String s) { } public static RequestEntryChain parseEntry(String s) { - return new RequestParser(s).parseEntry(false, false); + return new RequestParser(s).parseEntry(); } public static List parseEntries(String s) { - return new RequestParser(s).parseEntries(true, false); + return new RequestParser(s).parseEntries(false); } public static List parseArgs(String s) { - return new RequestParser(s).parseEntries(true, true); + return new RequestParser(s).parseEntries(false); } - private List parseEntries(boolean multiple, boolean argument) { - var entries = new LinkedList(); - entries.add(parseEntry(multiple, argument)); + private List parseEntries(boolean inner) { + var entries = new ArrayList(); + entries.add(parseEntry(true)); while(c == ',') { - nextChar(!argument); - entries.add(parseEntry(multiple, argument)); - } - return entries.size() == 1 && isNull(entries.get(0).getValue()) //avoid () => (null) - ? emptyList() - : entries; - } - - private RequestEntryChain parseEntry(boolean multiple, boolean argument) { - var entry = argument - ? nextEntry() - : new RequestEntryChain(requireLegalVariable(nextVar())); - if(c == '(') { //operator nextChar(true); - entry.setArgs(parseEntries(true, true)); // no args | null - requireChar(')'); //nextChar - nextChar(false); + entries.add(parseEntry(true)); } - if(c == '.') { - nextChar(true); - entry.setNext(parseEntry(multiple, argument)); + if((idx == size && c == 0) || (inner && c == ')')) { + return entries.size() == 1 && isNull(entries.get(0).getValue()) //avoid () => (null) + ? emptyList() + : entries; } - if(c == ':' && !argument) { - nextChar(true); - entry.setTag(requireLegalVariable(nextVar())); - } - if((idx == size && c == 0) || (c == ',' && multiple) || (c == ')' && argument)) { - return entry; + throw unexpectedCharException(); + } + + private RequestEntryChain parseEntry() { + var e = parseEntry(true); + if(idx == size && c == 0) { + return e; } throw unexpectedCharException(); } - private RequestEntryChain nextEntry() { - var from = idx; + private RequestEntryChain parseEntry(boolean txt) { + RequestEntryChain entry = null; if(c == '"') { - nextChar(true); - nextWhile(RequestParser::legalTxtChar); //accept any - requireChar('"'); //nextChar - nextChar(false); - return new RequestEntryChain(s.substring(from+1, idx-1), true); + if(txt) { + nextChar(true); + var from = idx; + nextWhile(RequestParser::legalTxtChar); //accept any + requireChar('"'); //nextChar + entry = new RequestEntryChain(s.substring(from, idx), true); //no next, no args, no tag + nextChar(false); + } } - var v = nextVar(); //to optim - if((idx == size || s.charAt(idx) == '.' || !legalValChar(c)) && v.matches(VAR_PATTERN)) { - return new RequestEntryChain(v); + else { + entry = new RequestEntryChain(nextVal()); + if(c == '(') { //operator + nextChar(true); + entry.setArgs(parseEntries(true)); // no args | null + requireChar(')'); //nextChar + nextChar(false); + } + if(c == '.') { + nextChar(true); + entry.setNext(parseEntry(false)); + } + if(c == ':') { + nextChar(true); + entry.setTag(requireLegalVariable(nextVar().get())); + } + } + return entry; + } + + private String nextVal() { + var from = idx; + var v = nextVar(); + if((idx == size || c == '.') && legalLetter(s.charAt(from))) { //^[a-zA-Z] + return v.get(); } nextWhile(RequestParser::legalValChar); - return new RequestEntryChain(from == idx ? null : s.substring(from, idx)); // empty => null + return from == idx ? null : s.substring(from, idx); // empty => null } - private String nextVar() { + private Supplier nextVar() { var from = idx; nextWhile(RequestParser::legalVarChar); - return s.substring(from, idx); + return ()-> s.substring(from, idx); } private void nextWhile(CharPredicate cp) { @@ -125,7 +138,7 @@ private String requireLegalVariable(String s) { } throw s.isEmpty() && idx < size ? unexpectedCharException() - : new EntryParseException("illegal variable name : " + quote(s)); + : new EntryParseException("illegal identifier : " + quote(s)); } private EntryParseException unexpectedCharException() { @@ -145,7 +158,11 @@ private static boolean legalValChar(char c) { } private static boolean legalVarChar(char c) { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z' ) || (c >= '0' && c <= '9') || c == '_'; + return legalLetter(c) || (c >= '0' && c <= '9') || c == '_'; + } + + private static boolean legalLetter(char c){ + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z' ); } @FunctionalInterface From 3977283110dd4f2d94390cb6780c894d2f380af7 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 24 Aug 2024 20:09:09 +0200 Subject: [PATCH 168/298] edit --- .../java/org/usf/jquery/web/RequestParser.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 36efc38b..d20ce7ff 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -97,9 +97,9 @@ private RequestEntryChain parseEntry(boolean txt) { private String nextVal() { var from = idx; var v = nextVar(); - if((idx == size || c == '.') && legalLetter(s.charAt(from))) { //^[a-zA-Z] + if((idx == size || c == '.') && from > idx && legalLetter(s.charAt(from))) { //^[a-zA-Z] return v.get(); - } + } //!variable nextWhile(RequestParser::legalValChar); return from == idx ? null : s.substring(from, idx); // empty => null } @@ -111,18 +111,24 @@ private Supplier nextVar() { } private void nextWhile(CharPredicate cp) { - while(idxsize"); } } From eac4968a01b2d2e1dbd7fefb201b4da000aaa6b2 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 24 Aug 2024 20:35:56 +0200 Subject: [PATCH 169/298] edit --- src/main/java/org/usf/jquery/web/RequestParser.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index d20ce7ff..52807cb4 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -46,8 +46,8 @@ private List parseEntries(boolean inner) { nextChar(true); entries.add(parseEntry(true)); } - if((idx == size && c == 0) || (inner && c == ')')) { - return entries.size() == 1 && isNull(entries.get(0).getValue()) //avoid () => (null) + if(idx == size || (inner && c == ')')) { + return entries.size() == 1 && isNull(entries.get(0).getValue()) //check this ? emptyList() : entries; } @@ -56,7 +56,7 @@ private List parseEntries(boolean inner) { private RequestEntryChain parseEntry() { var e = parseEntry(true); - if(idx == size && c == 0) { + if(idx == size) { return e; } throw unexpectedCharException(); @@ -97,7 +97,7 @@ private RequestEntryChain parseEntry(boolean txt) { private String nextVal() { var from = idx; var v = nextVar(); - if((idx == size || c == '.') && from > idx && legalLetter(s.charAt(from))) { //^[a-zA-Z] + if((idx == size || c == '.' || c == ':') && from Date: Sat, 24 Aug 2024 21:15:25 +0200 Subject: [PATCH 170/298] edit --- src/main/java/org/usf/jquery/web/RequestParser.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 52807cb4..5f670751 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -147,12 +147,12 @@ private String requireLegalVariable(String s) { : new EntryParseException("illegal identifier : " + quote(s)); } - private EntryParseException unexpectedCharException() { - return new EntryParseException("unexpected character '" + c + "' at index=" + idx); //end + private EntrySyntaxException unexpectedCharException() { + return new EntrySyntaxException("unexpected character '" + c + "' at index=" + idx); //end } - private EntryParseException somethingExpectedException() { - return new EntryParseException("something expected after '" + s.charAt(size-1) + "'"); + private EntrySyntaxException somethingExpectedException() { + return new EntrySyntaxException("something expected after '" + s.charAt(size-1) + "'"); } private static boolean legalTxtChar(char c) { //avoid SQL injection & HTTP reserved symbol From 1c1b18618b685b5ce1e49f813282d369d57d5be8 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 24 Aug 2024 21:31:47 +0200 Subject: [PATCH 171/298] edit --- src/main/java/org/usf/jquery/web/RequestParser.java | 6 +----- src/main/java/org/usf/jquery/web/ViewDecorator.java | 4 +--- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 5f670751..0be4b81e 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -35,10 +35,6 @@ public static List parseEntries(String s) { return new RequestParser(s).parseEntries(false); } - public static List parseArgs(String s) { - return new RequestParser(s).parseEntries(false); - } - private List parseEntries(boolean inner) { var entries = new ArrayList(); entries.add(parseEntry(true)); @@ -100,7 +96,7 @@ private String nextVal() { if((idx == size || c == '.' || c == ':') && from null } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index b544a3b2..33cab44a 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -19,7 +19,6 @@ import static org.usf.jquery.web.Parameters.OFFSET; import static org.usf.jquery.web.Parameters.ORDER; import static org.usf.jquery.web.Parameters.VIEW; -import static org.usf.jquery.web.RequestParser.parseArgs; import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.RequestParser.parseEntry; import static org.usf.jquery.web.ResourceAccessException.undeclaredResouceException; @@ -172,10 +171,9 @@ default void parseOffset(RequestQueryBuilder query, Map parame default void parseFilters(RequestQueryBuilder query, Map parameters) { parameters.entrySet().stream() -// .filter(e-> !RESERVED_WORDS.contains(e.getKey())) .flatMap(e-> { var re = parseEntry(e.getKey()); - return Stream.of(e.getValue()).map(v-> re.evalFilter(this, parseArgs(v))); + return Stream.of(e.getValue()).map(v-> re.evalFilter(this, parseEntries(v))); }) .forEach(query::filters); } From fa655dbee023961adf40eabb5fa15c89b8d39386 Mon Sep 17 00:00:00 2001 From: u$f Date: Sat, 24 Aug 2024 22:08:15 +0200 Subject: [PATCH 172/298] edit --- src/main/java/org/usf/jquery/web/RequestParser.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 0be4b81e..82892bd6 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -17,7 +17,7 @@ public final class RequestParser { private final String s; - private int size; + private final int size; private int idx; private char c; @@ -117,7 +117,7 @@ private void nextChar(boolean require) { if(++idx < size) { c = s.charAt(idx); } - else if(idx == size) { //break condition + else if(idx == size) { c = 0; if(require) { throw somethingExpectedException(); @@ -140,7 +140,7 @@ private String requireLegalVariable(String s) { } throw s.isEmpty() && idx < size ? unexpectedCharException() - : new EntryParseException("illegal identifier : " + quote(s)); + : new EntrySyntaxException("illegal identifier : " + quote(s)); } private EntrySyntaxException unexpectedCharException() { @@ -164,7 +164,7 @@ private static boolean legalVarChar(char c) { } private static boolean legalLetter(char c){ - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z' ); + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } @FunctionalInterface From d55dd489e82fe7143507e00fad929e38e02115f5 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 26 Aug 2024 10:01:31 +0200 Subject: [PATCH 173/298] eidt --- src/main/java/org/usf/jquery/web/RequestParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 82892bd6..28a5a52d 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -163,7 +163,7 @@ private static boolean legalVarChar(char c) { return legalLetter(c) || (c >= '0' && c <= '9') || c == '_'; } - private static boolean legalLetter(char c){ + private static boolean legalLetter(char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } From 9aff92496c6bf03a5d8df2f420849e6542ea9048 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 27 Aug 2024 11:12:43 +0200 Subject: [PATCH 174/298] edit --- src/main/java/org/usf/jquery/core/Comparator.java | 3 ++- src/main/java/org/usf/jquery/core/Operator.java | 3 ++- src/main/java/org/usf/jquery/web/ViewMetadata.java | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index e1b6f801..42ab732f 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -5,6 +5,7 @@ import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; +import static org.usf.jquery.core.DBProcessor.lookup; import java.util.Objects; import java.util.Optional; @@ -164,6 +165,6 @@ static InCompartor inComparator(final String name) { } static Optional lookupComparator(String op) { - return DBProcessor.lookup(Comparator.class, TypedComparator.class, op); + return lookup(Comparator.class, TypedComparator.class, op); } } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index f998ddf9..b740f6f7 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -1,6 +1,7 @@ package org.usf.jquery.core; import static org.usf.jquery.core.ArgTypeRef.firstArgJdbcType; +import static org.usf.jquery.core.DBProcessor.lookup; import static org.usf.jquery.core.Database.TERADATA; import static org.usf.jquery.core.Database.currentDatabase; import static org.usf.jquery.core.JDBCType.BIGINT; @@ -334,6 +335,6 @@ static ConstantOperator constant(String name) { } static Optional lookupOperator(String op) { - return DBProcessor.lookup(Operator.class, TypedOperator.class, op); + return lookup(Operator.class, TypedOperator.class, op); } } diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index 722a9194..5886d8c1 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -99,7 +99,7 @@ void fetchView(DatabaseMetaData metadata, TableView view, String schema) throws } void fetch(DatabaseMetaData metadata, DBView qr, String schema) throws SQLException { - var query = "SELECT * FROM " + qr.sql(parametrized(schema, emptyMap())) + " WHERE 1=0"; // rows=0 + var query = "SELECT * FROM " + qr.sql(parametrized(schema, emptyMap())) + " AS v0 WHERE 1=0"; // rows=0 try(var ps = metadata.getConnection().prepareStatement(query); var rs = ps.executeQuery()){ var db = reverseMapKeys(); From 60c8499bf56cbacd7b809d30d76870fca84f0c83 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 27 Aug 2024 17:07:17 +0200 Subject: [PATCH 175/298] edit --- .../org/usf/jquery/web/RequestEntryChain.java | 47 ++++++++++++++----- .../org/usf/jquery/web/ViewDecorator.java | 15 +++--- .../usf/jquery/web/ViewDecoratorWrapper.java | 4 +- .../org/usf/jquery/web/YearViewDecorator.java | 3 +- 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 8be62921..b5f300a7 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -158,32 +158,57 @@ public ViewJoin[] evalJoin(ViewDecorator vd) { .orElseThrow(()-> noSuchResourceException(VIEW, value)); e = requireNoArgs().next; //check args only if view exists } - var join = vd.joiner(e.value); + var join = vd.join(e.value); if(nonNull(join)) { e.requireNoArgs().requireNoNext(); //check args & next only if joiner exists - return requireNonNull(join.build(), "view.joiner: " + e); + return requireNonNull(join.build(), vd.identity() + ".join: " + e); } - throw noSuchResourceException(vd.identity() + ".joiner", e.value); + throw noSuchResourceException(vd.identity() + ".join", e.value); } //[partition.order]* - public Partition evalPartition(ViewDecorator td) { + public Partition evalPartition(ViewDecorator vd) { + var e = this; + if(hasNext()) { + var res = currentContext().lookupRegisteredView(value); + if(res.isPresent()) { + vd = res.get(); + e = requireNoArgs().next; //check args only if view exists + } + } + var par = vd.partition(e.value); + if(nonNull(par)) { + e.requireNoArgs().requireNoNext(); + return requireNonNull(par.build(), vd.identity() + ".partition: " + e); + } + if(e == this) { // not view + var res = evalPartition2(vd); + if(res.isPresent()) { + return res.get(); + } + } + throw noSuchResourceException(vd.identity() + ".partition", e.value); + } + + private Optional evalPartition2(ViewDecorator vd) { List cols = new ArrayList<>(); List ords = new ArrayList<>(); var e = this; do { switch (e.value) { - case PARTITION: addAll(cols, columnVarargs(td)); break; - case ORDER: addAll(ords, e.oderVarargs(td)); break; - default: throw e==this - ? cannotParseEntryException(PARTITION, e) //first entry - : badEntrySyntaxException(joinArray("|", PARTITION, ORDER), e.value); + case PARTITION: addAll(cols, columnVarargs(vd)); break; + case ORDER: addAll(ords, e.oderVarargs(vd)); break; + default: + if(e == this) { + return empty(); //cannotParseEntryException(PARTITION, e) //first entry + } + throw badEntrySyntaxException(joinArray("|", PARTITION, ORDER), e.value); } e = e.next; } while(nonNull(e)); - return new Partition( + return Optional.of(new Partition( cols.toArray(DBColumn[]::new), - ords.toArray(DBOrder[]::new)); + ords.toArray(DBOrder[]::new))); } //[view.]column[.operator]* diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 33cab44a..52e7fe86 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -47,7 +47,7 @@ public interface ViewDecorator { String identity(); //URL - String columnName(ColumnDecorator cd); + String columnName(ColumnDecorator cd); default ViewBuilder builder() { return this::buildView; @@ -57,10 +57,14 @@ default CriteriaBuilder criteria(String name) { //!aggregation return null; //no criteria by default } - default JoinBuilder joiner(String name) { - return null; //no builder by default + default JoinBuilder join(String name) { + return null; //no join by default } - + + default PartitionBuilder partition(String name) { + return null; //no partition by default + } + default DBView view() { return metadata().getView(); } @@ -90,9 +94,8 @@ private TableView buildView() { } default ViewMetadata metadata() { - var view = requireNonNull(builder(), identity() + ".builder").build(); return currentContext().computeTableMetadata(this, cols-> - new ViewMetadata(view, declaredColumns(this, cols))); + new ViewMetadata(requireNonNull(builder(), identity() + ".builder").build(), declaredColumns(this, cols))); } static Map declaredColumns(ViewDecorator vd, Collection cols){ diff --git a/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java b/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java index 972ced26..61aebe0d 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java +++ b/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java @@ -37,7 +37,7 @@ public CriteriaBuilder criteria(String name) { } @Override - public JoinBuilder joiner(String name) { - return view.joiner(name); + public JoinBuilder join(String name) { + return view.join(name); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/YearViewDecorator.java b/src/main/java/org/usf/jquery/web/YearViewDecorator.java index d17f8aff..bcac1986 100644 --- a/src/main/java/org/usf/jquery/web/YearViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearViewDecorator.java @@ -1,6 +1,5 @@ package org.usf.jquery.web; -import static java.lang.String.join; import static java.time.Month.DECEMBER; import static java.time.YearMonth.now; import static java.util.Objects.nonNull; @@ -74,7 +73,7 @@ default void parseFilters(RequestQueryBuilder query, Map param default YearMonth[] parseRevisions(Map parameterMap) { var arr = parameterMap.remove(REVISION_MODE); if(nonNull(arr) && arr.length > 1) { - throw new IllegalArgumentException("too many " + REVISION_MODE + " " + join(", ", arr)); //multiple values + throw new IllegalArgumentException("too many " + REVISION_MODE + " " + String.join(", ", arr)); //multiple values } var mod = revisionMode(isEmpty(arr) || isBlank(arr[0]) ? defaultRevisionMode() : arr[0]); var values = parameterMap.containsKey(REVISION) From 0156a2769d39e2faf46633a1fc0c0cb330daeff2 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 27 Aug 2024 17:22:48 +0200 Subject: [PATCH 176/298] edit --- .../java/org/usf/jquery/web/PartitionBuilder.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/org/usf/jquery/web/PartitionBuilder.java diff --git a/src/main/java/org/usf/jquery/web/PartitionBuilder.java b/src/main/java/org/usf/jquery/web/PartitionBuilder.java new file mode 100644 index 00000000..aa774ce0 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/PartitionBuilder.java @@ -0,0 +1,15 @@ +package org.usf.jquery.web; + +import org.usf.jquery.core.Partition; + +/** + * + * @author u$f + * + */ +@FunctionalInterface +public interface PartitionBuilder { + + Partition build(); + +} From 8199e1452222c7fba8abe6d40eb8bec711941457 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 27 Aug 2024 18:29:23 +0200 Subject: [PATCH 177/298] edit --- .../java/org/usf/jquery/web/ArgumentParsers.java | 2 +- .../java/org/usf/jquery/web/RequestEntryChain.java | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 0346d5af..3cb242ac 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -102,7 +102,7 @@ public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type) { switch (type) { case QUERY_COLUMN: return RequestEntryChain::evalQueryColumn; - case NAMED_COLUMN: return (e,v)-> e.evalColumn(v, true, false); + case NAMED_COLUMN: return (e,v)-> e.evalColumn(v, true, true); //separate query context case COLUMN: return (e,v)-> e.evalColumn(v, false, false); case FILTER: return RequestEntryChain::evalFilter; case ORDER: return RequestEntryChain::evalOrder; diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index b5f300a7..a265ecdb 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -371,13 +371,12 @@ private Optional lookupResource(ViewDecorator td) { //do not chang } private Optional lookupViewResource(ViewDecorator td, Predicate pre) { - if(td instanceof QueryDecorator qd) { //query column - return ofNullable(qd.column(value)) - .map(c-> new ViewResource(td, null, requireNoArgs(), c)); //no column decorator - } - return currentContext().lookupRegisteredColumn(value) - .map(cd-> new ViewResource(td, cd, requireNoArgs(), td.column(cd))) - .or(()-> lookupOperation(td, null, pre).map(col-> new ViewResource(td, null, this, col))); //no column decorator + var res = td instanceof QueryDecorator qd + ? ofNullable(qd.column(value)) + .map(c-> new ViewResource(td, null, requireNoArgs(), c)) + : currentContext().lookupRegisteredColumn(value) + .map(cd-> new ViewResource(td, cd, requireNoArgs(), td.column(cd))); + return res.or(()-> lookupOperation(td, null, pre).map(col-> new ViewResource(td, null, this, col))); } private Optional lookupOperation(ViewDecorator vd, DBColumn col, Predicate opr) { From b6fa61eb346d2af74c76c10ff1ef9912c74da9ad Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 27 Aug 2024 18:51:25 +0200 Subject: [PATCH 178/298] edit --- .../usf/jquery/core/AsciiResultMapper.java | 18 ++++---------- .../org/usf/jquery/web/view/Chart2DView.java | 3 +-- .../org/usf/jquery/web/view/ChartMappers.java | 24 +++++++++---------- .../org/usf/jquery/web/view/PieChartView.java | 2 +- .../org/usf/jquery/web/view/SankeyView.java | 3 +-- .../jquery/web/view/TimelineChartView.java | 6 ++--- .../usf/jquery/web/view/WebViewMapper.java | 23 ++++++------------ 7 files changed, 29 insertions(+), 50 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/AsciiResultMapper.java b/src/main/java/org/usf/jquery/core/AsciiResultMapper.java index 2f251293..16ee162f 100644 --- a/src/main/java/org/usf/jquery/core/AsciiResultMapper.java +++ b/src/main/java/org/usf/jquery/core/AsciiResultMapper.java @@ -94,20 +94,10 @@ public Void map(ResultSet rs) throws SQLException { } private static boolean isNumer(int type) { - switch (type) { - case BOOLEAN: - case BIT: - case TINYINT: - case SMALLINT: - case INTEGER: - case BIGINT: - case REAL: - case FLOAT: - case DOUBLE: - case NUMERIC: - case DECIMAL: return true; - default: return false; - } + return switch (type) { + case BOOLEAN, BIT, TINYINT, SMALLINT, INTEGER, BIGINT, REAL, FLOAT, DOUBLE, NUMERIC, DECIMAL: yield true; + default: yield false; + }; } private static Object[] array(int size, String v) { diff --git a/src/main/java/org/usf/jquery/web/view/Chart2DView.java b/src/main/java/org/usf/jquery/web/view/Chart2DView.java index a4a3c3f7..f81d0a3e 100644 --- a/src/main/java/org/usf/jquery/web/view/Chart2DView.java +++ b/src/main/java/org/usf/jquery/web/view/Chart2DView.java @@ -4,7 +4,6 @@ import static java.lang.System.lineSeparator; import static java.nio.file.Files.readString; import static java.util.Map.ofEntries; -import static java.util.stream.Collectors.toList; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.web.view.WebViewMapper.DataTable.fromMetaData; import static org.usf.jquery.web.view.WebViewMapper.WebType.NUMBER; @@ -54,7 +53,7 @@ public Void map(ResultSet rs) throws SQLException { var sb1 = new StringBuilder(); var xAxis = dt.getXAxis(); sb1.append("[").append(doubleQuote(xAxis.getType().typeName())).append(",").append(doubleQuote(xAxis.getName())).append("]"); - var cols = dt.getRows().stream().flatMap(c-> c.stream().skip(1)).map(Entry::getKey).distinct().sorted().collect(toList()); + var cols = dt.getRows().stream().flatMap(c-> c.stream().skip(1)).map(Entry::getKey).distinct().sorted().toList(); if(cols.isEmpty()) { //no data for(var c : dt.getYAxis()) { sb1.append(",[").append(doubleQuote(NUMBER.typeName())).append(",").append(doubleQuote(c.getName())).append("]"); diff --git a/src/main/java/org/usf/jquery/web/view/ChartMappers.java b/src/main/java/org/usf/jquery/web/view/ChartMappers.java index f09c38dc..dfe54bef 100644 --- a/src/main/java/org/usf/jquery/web/view/ChartMappers.java +++ b/src/main/java/org/usf/jquery/web/view/ChartMappers.java @@ -20,19 +20,19 @@ public final class ChartMappers { public static WebViewMapper webChart(String view, Writer w) { - switch (view) { - case "table" : return new TableView(w); - case "pie" : return new PieChartView(w); - case "column" : return columnChart(w); - case "bar" : return barChart(w); - case "area" : return areaChart(w); - case "combo" : return comboChart(w); - case "line" : return lineChart(w); - case "timeline" : return new TimelineChartView(w); - case "calendar" : return new CalendarView(w); - case "sankey" : return new SankeyView(w); + return switch (view) { + case "table" : yield new TableView(w); + case "pie" : yield new PieChartView(w); + case "column" : yield columnChart(w); + case "bar" : yield barChart(w); + case "area" : yield areaChart(w); + case "combo" : yield comboChart(w); + case "line" : yield lineChart(w); + case "timeline" : yield new TimelineChartView(w); + case "calendar" : yield new CalendarView(w); + case "sankey" : yield new SankeyView(w); default : throw new IllegalArgumentException(view); - } + }; } } diff --git a/src/main/java/org/usf/jquery/web/view/PieChartView.java b/src/main/java/org/usf/jquery/web/view/PieChartView.java index 3c5726e1..7c089fb9 100644 --- a/src/main/java/org/usf/jquery/web/view/PieChartView.java +++ b/src/main/java/org/usf/jquery/web/view/PieChartView.java @@ -45,7 +45,7 @@ public Void map(ResultSet rs) throws SQLException { //scroll. var bg = currentTimeMillis(); var rw = 0; var cols = TableColumn.columns(rs.getMetaData()); - var numb = Stream.of(cols).filter(c-> c.getType() == NUMBER).collect(toList()); + var numb = Stream.of(cols).filter(c-> c.getType() == NUMBER).toList(); if(numb.isEmpty()) { throw new IllegalArgumentException("require number column"); } diff --git a/src/main/java/org/usf/jquery/web/view/SankeyView.java b/src/main/java/org/usf/jquery/web/view/SankeyView.java index 563fa829..83f0ffbb 100644 --- a/src/main/java/org/usf/jquery/web/view/SankeyView.java +++ b/src/main/java/org/usf/jquery/web/view/SankeyView.java @@ -3,7 +3,6 @@ import static java.lang.System.currentTimeMillis; import static java.lang.System.lineSeparator; import static java.nio.file.Files.readString; -import static java.util.stream.Collectors.toList; import static org.usf.jquery.web.view.WebViewMapper.TableColumn.columns; import static org.usf.jquery.web.view.WebViewMapper.WebType.NUMBER; import static org.usf.jquery.web.view.WebViewMapper.WebType.STRING; @@ -46,7 +45,7 @@ public Void map(ResultSet rs) throws SQLException { var bg = currentTimeMillis(); var rw = 0; var cols = columns(rs.getMetaData()); - var xAxis = Stream.of(cols).filter(c-> c.getType() == STRING).collect(toList()); + var xAxis = Stream.of(cols).filter(c-> c.getType() == STRING).toList(); if(xAxis.size() != 2) { throw new IllegalArgumentException("require [STRING, STRING, NUMBER] columns"); } diff --git a/src/main/java/org/usf/jquery/web/view/TimelineChartView.java b/src/main/java/org/usf/jquery/web/view/TimelineChartView.java index 2c0f01ea..e29fcfd8 100644 --- a/src/main/java/org/usf/jquery/web/view/TimelineChartView.java +++ b/src/main/java/org/usf/jquery/web/view/TimelineChartView.java @@ -45,15 +45,15 @@ public Void map(ResultSet rs) throws SQLException { var bg = currentTimeMillis(); var rw = 0; var cols = columns(rs.getMetaData()); - var numb = Stream.of(cols).filter(c-> c.getType() == NUMBER).collect(toList()); + var numb = Stream.of(cols).filter(c-> c.getType() == NUMBER).toList(); if(numb.isEmpty()) { - numb = Stream.of(cols).filter(c-> c.getType().isDate()).collect(toList()); + numb = Stream.of(cols).filter(c-> c.getType().isDate()).toList(); } if(numb.isEmpty() || numb.size() != 2) { throw new IllegalArgumentException("require NUMBER or DATE columns"); } var yAxis = numb; - var xAxis = Stream.of(cols).filter(c-> yAxis.stream().noneMatch(v-> v.getName().equals(c.getName()))).collect(toList()); + var xAxis = Stream.of(cols).filter(c-> yAxis.stream().noneMatch(v-> v.getName().equals(c.getName()))).toList(); if(xAxis.isEmpty()) { } diff --git a/src/main/java/org/usf/jquery/web/view/WebViewMapper.java b/src/main/java/org/usf/jquery/web/view/WebViewMapper.java index 060df2d7..1655091d 100644 --- a/src/main/java/org/usf/jquery/web/view/WebViewMapper.java +++ b/src/main/java/org/usf/jquery/web/view/WebViewMapper.java @@ -87,23 +87,14 @@ public String typeName() { } static WebType typeOf(int type) { - switch (type) { - case Types.BOOLEAN: return BOOLEAN; - case Types.BIT: - case Types.TINYINT: - case Types.SMALLINT: - case Types.INTEGER: - case Types.BIGINT: - case Types.REAL: - case Types.FLOAT: - case Types.DOUBLE: - case Types.NUMERIC: - case Types.DECIMAL: return NUMBER; - case Types.DATE: return DATE; - case Types.TIMESTAMP: return DATETIME; + return switch (type) { + case Types.BOOLEAN: yield BOOLEAN; + case Types.BIT, Types.TINYINT, Types.SMALLINT, Types.INTEGER, Types.BIGINT, Types.REAL, Types.FLOAT, Types.DOUBLE, Types.NUMERIC, Types.DECIMAL: yield NUMBER; + case Types.DATE: yield DATE; + case Types.TIMESTAMP: yield DATETIME; //case Types.TIME: //need explicit cast format !? - default: return STRING; - } + default: yield STRING; + }; } } From 8edf9fcb5785d2f2455d60142585f3ef18cfe31a Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 27 Aug 2024 18:54:52 +0200 Subject: [PATCH 179/298] edit --- src/main/java/org/usf/jquery/web/RequestParser.java | 2 +- src/test/java/org/usf/jquery/web/RequestParserTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 28a5a52d..0717e0f5 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -130,7 +130,7 @@ else if(idx == size) { private void requireChar(char rc) { if(c != rc) { - throw new EntryParseException("'" + rc + "' expected at index=" + idx); // before ends + throw new EntrySyntaxException("'" + rc + "' expected at index=" + idx); // before ends } } diff --git a/src/test/java/org/usf/jquery/web/RequestParserTest.java b/src/test/java/org/usf/jquery/web/RequestParserTest.java index bdf2a4cc..14cf5328 100644 --- a/src/test/java/org/usf/jquery/web/RequestParserTest.java +++ b/src/test/java/org/usf/jquery/web/RequestParserTest.java @@ -75,6 +75,6 @@ void testParse(String s) { "aa.fn(\"a:3,b,c,d&\")", }) void testParse2(String s) { - assertThrows(EntryParseException.class, ()-> parseEntry(s)); + assertThrows(EntrySyntaxException.class, ()-> parseEntry(s)); } } From c5521a9a62e1194406d9bf83c5c5cccd6168c7f7 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 27 Aug 2024 22:50:21 +0200 Subject: [PATCH 180/298] edit --- .../org/usf/jquery/web/RequestParser.java | 118 +++++++----------- .../org/usf/jquery/web/RequestParserTest.java | 14 +-- 2 files changed, 51 insertions(+), 81 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 0717e0f5..2ca2339f 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -1,13 +1,12 @@ package org.usf.jquery.web; import static java.util.Collections.emptyList; -import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Validation.VAR_PATTERN; import java.util.ArrayList; import java.util.List; -import java.util.function.Supplier; /** * @@ -22,95 +21,72 @@ public final class RequestParser { private char c; private RequestParser(String s) { - this.s = s; + this.s = requireNonNull(s, "value is null"); this.size = s.length(); this.c = size == 0 ? 0 : s.charAt(idx); } public static RequestEntryChain parseEntry(String s) { - return new RequestParser(s).parseEntry(); + return new RequestParser(s).parseEntries(false, c-> false).get(0); } public static List parseEntries(String s) { - return new RequestParser(s).parseEntries(false); + return s.isEmpty() ? emptyList() : new RequestParser(s).parseEntries(true, c-> false); } - private List parseEntries(boolean inner) { + private List parseEntries(boolean multiple, CharPredicate until) { var entries = new ArrayList(); - entries.add(parseEntry(true)); - while(c == ',') { - nextChar(true); - entries.add(parseEntry(true)); + entries.add(parseEntry()); + if(multiple) { + while(c == ',') { + nextChar(true); + entries.add(parseEntry()); + } } - if(idx == size || (inner && c == ')')) { - return entries.size() == 1 && isNull(entries.get(0).getValue()) //check this - ? emptyList() - : entries; + if(idx == size || until.test(c)) { + return entries; } - throw unexpectedCharException(); + throw new EntrySyntaxException("unexpected character '" + c + "' at index=" + idx); //end } private RequestEntryChain parseEntry() { - var e = parseEntry(true); - if(idx == size) { - return e; - } - throw unexpectedCharException(); - } - - private RequestEntryChain parseEntry(boolean txt) { - RequestEntryChain entry = null; - if(c == '"') { - if(txt) { - nextChar(true); - var from = idx; - nextWhile(RequestParser::legalTxtChar); //accept any - requireChar('"'); //nextChar - entry = new RequestEntryChain(s.substring(from, idx), true); //no next, no args, no tag - nextChar(false); - } - } - else { - entry = new RequestEntryChain(nextVal()); + if(legalLetter(c)) { + var entry = new RequestEntryChain(requireLegalIdentifier(nextWhile(RequestParser::legalVarChar))); if(c == '(') { //operator nextChar(true); - entry.setArgs(parseEntries(true)); // no args | null - requireChar(')'); //nextChar + entry.setArgs(parseEntries(true, c-> c==')')); // + requireChar(')'); nextChar(false); } if(c == '.') { nextChar(true); - entry.setNext(parseEntry(false)); + if(legalLetter(c)) { //require identifier after '.' + entry.setNext(parseEntry()); + } } if(c == ':') { nextChar(true); - entry.setTag(requireLegalVariable(nextVar().get())); + entry.setTag(requireLegalIdentifier(nextWhile(RequestParser::legalVarChar))); } + return entry; } - return entry; - } - - private String nextVal() { - var from = idx; - var v = nextVar(); - if((idx == size || c == '.' || c == ':') && from null + if(c == '"') { + nextChar(true); + var txt = nextWhile(RequestParser::legalTxtChar); + requireChar('"'); + nextChar(false); + return new RequestEntryChain(txt, true); //no next, no args, no tag + } + return new RequestEntryChain(legalNumber(c) || c == '-' ? nextWhile(RequestParser::legalValChar) : null); // decimal negative? & instant format } - private Supplier nextVar() { + private String nextWhile(CharPredicate cp) { var from = idx; - nextWhile(RequestParser::legalVarChar); - return ()-> s.substring(from, idx); - } - - private void nextWhile(CharPredicate cp) { while(idxsize"); + throw new ArrayIndexOutOfBoundsException("idx>size"); } } @@ -134,24 +110,14 @@ private void requireChar(char rc) { } } - private String requireLegalVariable(String s) { + private static String requireLegalIdentifier(String s) { if(s.matches(VAR_PATTERN)) { return s; } - throw s.isEmpty() && idx < size - ? unexpectedCharException() - : new EntrySyntaxException("illegal identifier : " + quote(s)); - } - - private EntrySyntaxException unexpectedCharException() { - return new EntrySyntaxException("unexpected character '" + c + "' at index=" + idx); //end - } - - private EntrySyntaxException somethingExpectedException() { - return new EntrySyntaxException("something expected after '" + s.charAt(size-1) + "'"); + throw new EntrySyntaxException("illegal identifier : " + quote(s)); } - private static boolean legalTxtChar(char c) { //avoid SQL injection & HTTP reserved symbol + private static boolean legalTxtChar(char c) { //avoid SQL / HTTP reserved symbol return c != '"' && c != '\'' && c != '&' && c != '?' && c != '='; } @@ -160,13 +126,17 @@ private static boolean legalValChar(char c) { } private static boolean legalVarChar(char c) { - return legalLetter(c) || (c >= '0' && c <= '9') || c == '_'; + return legalLetter(c) || legalNumber(c) || c == '_'; } - + private static boolean legalLetter(char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } + private static boolean legalNumber(char c) { + return c >= '0' && c <= '9'; + } + @FunctionalInterface interface CharPredicate { boolean test(char c); diff --git a/src/test/java/org/usf/jquery/web/RequestParserTest.java b/src/test/java/org/usf/jquery/web/RequestParserTest.java index 14cf5328..57a9ea5e 100644 --- a/src/test/java/org/usf/jquery/web/RequestParserTest.java +++ b/src/test/java/org/usf/jquery/web/RequestParserTest.java @@ -31,10 +31,10 @@ class RequestParserTest { "concat(v.c,\"abc\",\"123\")", "mod(abs(trunc(exp())))", "co1.mod(6.acc).trunc(3).plus(100)", //6.acc as value - "co1.concat(co1.trunc(2).string(10), 1234)", //6.acc as value + "co1.concat(co1.trunc(2).string(10),1234)", //6.acc as value "co1.concat(,123)", //null value "co1.concat(123,)", //null value - "co1.concat(, ,123,)", //null value + "co1.concat(,,123,)", //null value "aa.fn(3.3,b,c,d)", "aa.fn(,3.3,b,c,d,)", "aa.fn(2020-01-01,b,c,d)", @@ -46,9 +46,9 @@ void testParse(String s) { @ParameterizedTest @ValueSource(strings = { - "", - "12345", - "1name", +// "", +// "12345", +// "1name", "_column", "(column", ")column", @@ -62,11 +62,11 @@ void testParse(String s) { "column,", "column:", "column.", - "\"column\"", +// "\"column\"", "function(arg", "function(arg))", "123(a,b,c)", - "123.abc", +// "123.abc", "fn(3@c)", "fn(\"", "fn(\"arg", From aa5b5ad3d9a95e17a955a27b32861c541578720e Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 27 Aug 2024 23:34:32 +0200 Subject: [PATCH 181/298] edit --- .github/workflows/main.yml | 2 +- src/main/java/org/usf/jquery/core/JDBCType.java | 7 ++++--- src/main/java/org/usf/jquery/web/RequestParser.java | 10 ++++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 61955f62..6be9914a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -44,7 +44,7 @@ jobs: - name: Set up JDK 11 uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '17' distribution: 'temurin' server-id: ossrh server-username: MAVEN_USERNAME diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index 9a05304f..e576dd25 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -2,6 +2,7 @@ import static java.util.Objects.isNull; import static java.util.Optional.empty; +import static java.util.Optional.ofNullable; /** * @@ -72,8 +73,8 @@ public Class typeClass() { @Override public boolean accept(Object o) { - if(o instanceof Typed) { - var t = ((Typed) o).getType(); + if(o instanceof Typed v) { + var t = v.getType(); return t == this || isNull(t) || superType.isAssignableFrom(t.typeClass()); } return isNull(o) || matcher.test(o); @@ -99,7 +100,7 @@ public static Optional typeOf(Object o) { if(o instanceof Typed to) { return Optional.of(to.getType()); } - return Optional.of(o).flatMap(v-> findType(e-> e.typeClass().isInstance(o))); + return ofNullable(o).flatMap(v-> findType(e-> e.typeClass().isInstance(o))); } public static Optional fromDataType(int value) { diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 2ca2339f..3cfa4693 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -21,7 +21,7 @@ public final class RequestParser { private char c; private RequestParser(String s) { - this.s = requireNonNull(s, "value is null"); + this.s = s; this.size = s.length(); this.c = size == 0 ? 0 : s.charAt(idx); } @@ -31,7 +31,9 @@ public static RequestEntryChain parseEntry(String s) { } public static List parseEntries(String s) { - return s.isEmpty() ? emptyList() : new RequestParser(s).parseEntries(true, c-> false); + return requireNonNull(s, "value is null").isEmpty() + ? emptyList() + : new RequestParser(s).parseEntries(true, c-> false); } private List parseEntries(boolean multiple, CharPredicate until) { @@ -39,7 +41,7 @@ private List parseEntries(boolean multiple, CharPredicate unt entries.add(parseEntry()); if(multiple) { while(c == ',') { - nextChar(true); + nextChar(false); //null parameter entries.add(parseEntry()); } } @@ -77,7 +79,7 @@ private RequestEntryChain parseEntry() { nextChar(false); return new RequestEntryChain(txt, true); //no next, no args, no tag } - return new RequestEntryChain(legalNumber(c) || c == '-' ? nextWhile(RequestParser::legalValChar) : null); // decimal negative? & instant format + return new RequestEntryChain(legalNumber(c) || c == '-' ? nextWhile(RequestParser::legalValChar) : null); // decimal negative? | instant format } private String nextWhile(CharPredicate cp) { From 396db5f0f7e5949fff732dab4636a882419dd664 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 27 Aug 2024 23:41:29 +0200 Subject: [PATCH 182/298] edit --- src/main/java/org/usf/jquery/web/ViewMetadata.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index 5886d8c1..92d5fc05 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -99,9 +99,8 @@ void fetchView(DatabaseMetaData metadata, TableView view, String schema) throws } void fetch(DatabaseMetaData metadata, DBView qr, String schema) throws SQLException { - var query = "SELECT * FROM " + qr.sql(parametrized(schema, emptyMap())) + " AS v0 WHERE 1=0"; // rows=0 - try(var ps = metadata.getConnection().prepareStatement(query); - var rs = ps.executeQuery()){ + try(var ps = metadata.getConnection().createStatement(); + var rs = ps.executeQuery("SELECT * FROM " + qr.sql(parametrized(schema, emptyMap())) + " AS v0 WHERE 1=0")){ var db = reverseMapKeys(); var meta = rs.getMetaData(); for(var i=1; i<=meta.getColumnCount(); i++) { From 94199968441c4deb6514ec43080bc23cd3b4feac Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 00:01:44 +0200 Subject: [PATCH 183/298] eidt --- .../java/org/usf/jquery/core/JDBCType.java | 2 +- .../org/usf/jquery/core/RequestQuery.java | 51 ++++++++++--------- .../java/org/usf/jquery/web/ViewMetadata.java | 12 +++-- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index e576dd25..de2c4422 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -98,7 +98,7 @@ private static boolean isString(Object o) { public static Optional typeOf(Object o) { if(o instanceof Typed to) { - return Optional.of(to.getType()); + return ofNullable(to.getType()); } return ofNullable(o).flatMap(v-> findType(e-> e.typeClass().isInstance(o))); } diff --git a/src/main/java/org/usf/jquery/core/RequestQuery.java b/src/main/java/org/usf/jquery/core/RequestQuery.java index 180d0022..7398081b 100644 --- a/src/main/java/org/usf/jquery/core/RequestQuery.java +++ b/src/main/java/org/usf/jquery/core/RequestQuery.java @@ -4,6 +4,7 @@ import static java.util.Objects.isNull; import static org.usf.jquery.core.Utils.isEmpty; +import java.sql.Connection; import java.sql.SQLException; import java.util.Arrays; import java.util.List; @@ -33,32 +34,36 @@ public final class RequestQuery { public List execute(DataSource ds) throws SQLException { return execute(ds, new KeyValueMapper()); } - + public T execute(DataSource ds, ResultSetMapper mapper) throws SQLException { // overload with sql types try(var cn = ds.getConnection()){ - log.debug("preparing statement : {}", query); - try(var ps = cn.prepareStatement(query)){ - log.debug("with parameters : {}", Arrays.toString(args)); - if(!isEmpty(args)) { - for(var i=0; i T execute(Connection cn, ResultSetMapper mapper) throws SQLException { + log.debug("preparing statement : {}", query); + try(var ps = cn.prepareStatement(query)){ + log.debug("with parameters : {}", Arrays.toString(args)); + if(!isEmpty(args)) { + for(var i=0; i{ var db = reverseMapKeys(); var meta = rs.getMetaData(); for(var i=1; i<=meta.getColumnCount(); i++) { @@ -112,7 +113,8 @@ void fetch(DatabaseMetaData metadata, DBView qr, String schema) throws SQLExcept if(!db.isEmpty()) { //no such columns throw columnsNotFoundException(db.keySet()); } - } + return null; + }); } private Map reverseMapKeys(){ //key=columnName From 91884fe7ea7f0f8245bd49c38b5aa34c6c718fcd Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 12:38:12 +0200 Subject: [PATCH 184/298] edit --- .../org/usf/jquery/core/KeyValueMapper.java | 18 ++++++++-------- .../jquery/core/QueryParameterBuilder.java | 4 ++-- .../java/org/usf/jquery/core/QueryView.java | 2 +- .../org/usf/jquery/core/RequestQuery.java | 4 ++-- .../usf/jquery/core/ResultSetConsumer.java | 21 +++++++++++++++++++ .../java/org/usf/jquery/web/ViewMetadata.java | 4 ++-- 6 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/ResultSetConsumer.java diff --git a/src/main/java/org/usf/jquery/core/KeyValueMapper.java b/src/main/java/org/usf/jquery/core/KeyValueMapper.java index b4248903..8c2980e1 100644 --- a/src/main/java/org/usf/jquery/core/KeyValueMapper.java +++ b/src/main/java/org/usf/jquery/core/KeyValueMapper.java @@ -20,17 +20,17 @@ public final class KeyValueMapper implements ResultSetMapper> @Override public List map(ResultSet rs) throws SQLException { log.trace("mapping results..."); - var bg = currentTimeMillis(); - var results = new LinkedList(); - var columnNames = declaredColumns(rs); + var t = currentTimeMillis(); + var res = new LinkedList(); + var cols = declaredColumns(rs); while(rs.next()) { - var model = new DynamicModel(); - for(var i=0; i(), emptyMap()); + public QueryParameterBuilder subQuery(Map overView) { + return new QueryParameterBuilder(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), unmodifiableMap(overView)); } public static QueryParameterBuilder addWithValue() { diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index 493e40f4..aa07f500 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -20,7 +20,7 @@ public final class QueryView implements DBView { @Override public String sql(QueryParameterBuilder param) { var s = new SqlStringBuilder(100).append("("); - builder.build(s, param.subQuery()); + builder.build(s, param.subQuery(builder.getOverView())); return s.append(")").toString(); } diff --git a/src/main/java/org/usf/jquery/core/RequestQuery.java b/src/main/java/org/usf/jquery/core/RequestQuery.java index 7398081b..88e4a2c2 100644 --- a/src/main/java/org/usf/jquery/core/RequestQuery.java +++ b/src/main/java/org/usf/jquery/core/RequestQuery.java @@ -34,7 +34,7 @@ public final class RequestQuery { public List execute(DataSource ds) throws SQLException { return execute(ds, new KeyValueMapper()); } - + public T execute(DataSource ds, ResultSetMapper mapper) throws SQLException { // overload with sql types try(var cn = ds.getConnection()){ return execute(cn, mapper); @@ -43,8 +43,8 @@ public T execute(DataSource ds, ResultSetMapper mapper) throws SQLExcepti public T execute(Connection cn, ResultSetMapper mapper) throws SQLException { log.debug("preparing statement : {}", query); + log.debug("using arguments : {}", Arrays.toString(args)); //before prepare try(var ps = cn.prepareStatement(query)){ - log.debug("with parameters : {}", Arrays.toString(args)); if(!isEmpty(args)) { for(var i=0; i { + + void fetch(ResultSet rs) throws SQLException; + + @Override + default Void map(ResultSet rs) throws SQLException { + this.fetch(rs); + return null; + } +} diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index 64b052db..934dcb56 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -21,6 +21,7 @@ import org.usf.jquery.core.DBView; import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.ResultSetConsumer; import org.usf.jquery.core.TableView; import lombok.AccessLevel; @@ -101,7 +102,7 @@ void fetchView(DatabaseMetaData metadata, TableView view, String schema) throws void fetch(DatabaseMetaData metadata, DBView qr, String schema) throws SQLException { var query = new RequestQueryBuilder().columns(allColumns(qr)).filters(constant(1).eq(constant(0))); //no data - query.build(schema).execute(metadata.getConnection(), rs->{ + query.build(schema).execute(metadata.getConnection(), (ResultSetConsumer) rs->{ var db = reverseMapKeys(); var meta = rs.getMetaData(); for(var i=1; i<=meta.getColumnCount(); i++) { @@ -113,7 +114,6 @@ void fetch(DatabaseMetaData metadata, DBView qr, String schema) throws SQLExcept if(!db.isEmpty()) { //no such columns throw columnsNotFoundException(db.keySet()); } - return null; }); } From 6af7b274222f9afdc35c22b0fccdc06aa79883a5 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 15:58:22 +0200 Subject: [PATCH 185/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 396 ++++++++++++++---- .../org/usf/jquery/core/TypedComparator.java | 2 +- src/main/java/org/usf/jquery/core/Utils.java | 6 +- 3 files changed, 311 insertions(+), 93 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index e33cf1ef..918ddfb9 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -1,6 +1,7 @@ package org.usf.jquery.core; import static org.usf.jquery.core.QueryParameterBuilder.formatValue; +import static org.usf.jquery.core.Utils.arrayJoin; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -47,78 +48,95 @@ default NamedColumn as(String name) { return new NamedColumn(this, Objects.isNull(name) ? null : requireLegalVariable(name)); } - default DBOrder order() { - return new DBOrder(this); - } - - default DBOrder order(Order order) { - return new DBOrder(this, order); - } - // filters + default ColumnSingleFilter eq(Object value) { - return filter(ComparisonExpression.eq(value)); + return Comparator.eq().filter(this, value); } default ColumnSingleFilter ne(Object value) { - return filter(ComparisonExpression.ne(value)); + return Comparator.ne().filter(this, value); + } + + default ColumnSingleFilter lt(Object value) { + return Comparator.lt().filter(this, value); + } + + default ColumnSingleFilter le(Object value) { + return Comparator.le().filter(this, value); } default ColumnSingleFilter gt(Object value) { - return filter(ComparisonExpression.gt(value)); + return Comparator.gt().filter(this, value); } default ColumnSingleFilter ge(Object value) { - return filter(ComparisonExpression.ge(value)); + return Comparator.ge().filter(this, value); } - default ColumnSingleFilter lt(Object value) { - return filter(ComparisonExpression.lt(value)); + default ColumnSingleFilter like(Object value) { + return Comparator.like().filter(this, value); + } + + default ColumnSingleFilter startsLike(Object value) { + return Comparator.startsLike().filter(this, value); } - default ColumnSingleFilter le(Object value) { - return filter(ComparisonExpression.le(value)); + default ColumnSingleFilter endsLike(Object value) { + return Comparator.endsLike().filter(this, value); } - default ColumnSingleFilter like(Object value) { - return filter(ComparisonExpression.like(value)); + default ColumnSingleFilter contentLike(Object value) { + return Comparator.contentLike().filter(this, value); + } + + default ColumnSingleFilter startsNotLike(Object value) { + return Comparator.startsNotLike().filter(this, value); + } + + default ColumnSingleFilter endsNotLike(Object value) { + return Comparator.endsNotLike().filter(this, value); + } + + default ColumnSingleFilter contentNotLike(Object value) { + return Comparator.contentNotLike().filter(this, value); } default ColumnSingleFilter notLike(Object value) { - return filter(ComparisonExpression.notLike(value)); + return Comparator.notLike().filter(this, value); } default ColumnSingleFilter ilike(Object value) { - return filter(ComparisonExpression.iLike(value)); + return Comparator.iLike().filter(this, value); } default ColumnSingleFilter notILike(Object value) { - return filter(ComparisonExpression.notILike(value)); + return Comparator.notILike().filter(this, value); } - @SuppressWarnings("unchecked") - default ColumnSingleFilter in(T... values) { - return filter(ComparisonExpression.in(values)); + default ColumnSingleFilter isNull() { + return Comparator.isNull().filter(this); } - @SuppressWarnings("unchecked") - default ColumnSingleFilter notIn(T... values) { - return filter(ComparisonExpression.notIn(values)); + default ColumnSingleFilter notNull() { + return Comparator.notNull().filter(this); } - default ColumnSingleFilter isNull() { - return filter(ComparisonExpression.isNull()); + @SuppressWarnings("unchecked") + default ColumnSingleFilter in(T... arr) { + return Comparator.in().filter(arrayJoin(arr, this, 0)); } - default ColumnSingleFilter isNotNull() { - return filter(ComparisonExpression.isNotNull()); + @SuppressWarnings("unchecked") + default ColumnSingleFilter notIn(T... arr) { + return Comparator.notIn().filter(arrayJoin(arr, this, 0)); } - + default ColumnSingleFilter filter(ComparisonExpression exp) { return new ColumnSingleFilter(this, exp); } - // operations + // arithmetic operations default OperationColumn plus(Object o) { return Operator.plus().operation(this, o); @@ -136,104 +154,300 @@ default OperationColumn divide(Object o) { return Operator.divide().operation(this, o); } - default WhenFilterBridge when(ComparisonExpression ex) { - return new CaseSingleColumnBuilder(this).when(ex); + //numeric functions + + default OperationColumn sqrt(Object o) { + return Operator.sqrt().operation(this, o); } - static DBColumn column(@NonNull String value) { - return b-> value; + default OperationColumn exp(Object o) { + return Operator.exp().operation(this, o); + } + + default OperationColumn log(Object o) { + return Operator.log().operation(this, o); + } + + default OperationColumn abs(Object o) { + return Operator.abs().operation(this, o); } - static TaggableColumn allColumns(@NonNull DBView view) { - return ((DBColumn) b-> { - b.view(view); - return "*"; //avoid view.* as "" - }).as(null); + default OperationColumn ceil(Object o) { + return Operator.ceil().operation(this, o); + } + + default OperationColumn floor(Object o) { + return Operator.floor().operation(this, o); + } + + default OperationColumn trunc(Object o) { + return Operator.trunc().operation(this, o); } - static DBColumn constant(Object value) { - return constant(()-> value); + default OperationColumn round(Object o) { + return Operator.round().operation(this, o); + } + + default OperationColumn mod(Object o) { + return Operator.mod().operation(this, o); } + + default OperationColumn pow(Object o) { + return Operator.pow().operation(this, o); + } + + //string functions - static DBColumn constant(Supplier value) { - return new DBColumn() { - - @Override - public String sql(QueryParameterBuilder arg) { - return formatValue(value.get()); //lazy - } - - @Override - public Stream groupKeys() { - return Stream.empty(); - } - }; + default OperationColumn length() { + return Operator.length().operation(this); + } + + default OperationColumn trim() { + return Operator.trim().operation(this); + } + + default OperationColumn ltrim() { + return Operator.ltrim().operation(this); + } + + default OperationColumn rtrim() { + return Operator.rtrim().operation(this); + } + + default OperationColumn upper() { + return Operator.upper().operation(this); + } + + default OperationColumn lower() { + return Operator.lower().operation(this); + } + + default OperationColumn initcap() { + return Operator.initcap().operation(this); + } + + default OperationColumn reverse() { + return Operator.reverse().operation(this); + } + + default OperationColumn left(int n) { + return Operator.left().operation(this, n); + } + + default OperationColumn right(int n) { + return Operator.pow().operation(this, n); + } + + default OperationColumn replace(String oldValue, String newValue) { + return Operator.replace().operation(this, oldValue, newValue); + } + + default OperationColumn substring(int start, int end) { + return Operator.substring().operation(this, start, end); + } + + default OperationColumn concat(String... str) { + return Operator.concat().operation(arrayJoin(str, this, 0)); + } + + default OperationColumn pow(int n, String value) { + return Operator.pow().operation(this, n, value); + } + + default OperationColumn rpad(int n, String value) { + return Operator.rpad().operation(this, n, value); } - static OperationColumn count() { - return count(column("*")); + //temporal functions + + default OperationColumn year() { + return Operator.year().operation(this); + } + + default OperationColumn month() { + return Operator.month().operation(this); } - static OperationColumn count(Object arg) { - return Operator.count().operation(arg); + default OperationColumn week() { + return Operator.week().operation(this); + } + + default OperationColumn day() { + return Operator.day().operation(this); + } + + default OperationColumn dow() { + return Operator.dow().operation(this); + } + + default OperationColumn doy() { + return Operator.doy().operation(this); } - static OperationColumn min(Object arg) { - return Operator.min().operation(arg); + default OperationColumn hour() { + return Operator.hour().operation(this); } - static OperationColumn max(Object arg) { - return Operator.max().operation(arg); + default OperationColumn minute() { + return Operator.minute().operation(this); + } + + default OperationColumn second() { + return Operator.second().operation(this); + } + + default OperationColumn epoch() { + return Operator.epoch().operation(this); + } + + default OperationColumn yearMonth() { + return Operator.yearMonth().operation(this); } + + //cast functions - static OperationColumn sum(Object arg) { - return Operator.sum().operation(arg); + default OperationColumn varchar() { + return Operator.varchar().operation(this); + } + + default OperationColumn varchar(int size) { + return Operator.varchar().operation(this, size); + } + + default OperationColumn date() { + return Operator.date().operation(this); + } + + default OperationColumn timestamp() { + return Operator.timestamp().operation(this); + } + + default OperationColumn integer() { + return Operator.integer().operation(this); } - static OperationColumn avg(Object arg) { - return Operator.avg().operation(arg); + default OperationColumn bigint() { + return Operator.bigint().operation(this); } - //numeric + default OperationColumn decimal() { + return Operator.decimal().operation(this); + } - static OperationColumn abs(Object arg) { - return Operator.abs().operation(arg); + default OperationColumn decimal(int digit, int precision) { + return Operator.decimal().operation(this, digit, precision); } + + //aggregate functions - static OperationColumn sqrt(Object arg) { - return Operator.sqrt().operation(arg); + default OperationColumn coalesce(Object o) { + return Operator.coalesce().operation(this, o); } - static OperationColumn trunc(Object arg) { - return Operator.trunc().operation(arg); + default OperationColumn count() { + return Operator.count().operation(this); } - static OperationColumn ceil(Object arg) { - return Operator.ceil().operation(arg); + default OperationColumn min() { + return Operator.min().operation(this); } - static OperationColumn floor(Object arg) { - return Operator.floor().operation(arg); + default OperationColumn max() { + return Operator.max().operation(this); + } + + default OperationColumn sum() { + return Operator.sum().operation(this); + } + + default OperationColumn avg() { + return Operator.avg().operation(this); } - //string - static OperationColumn trim(Object arg) { - return Operator.trim().operation(arg); + + //window functions + + default OperationColumn rank() { + return Operator.rank().operation(this); + } + + default OperationColumn rowNumber() { + return Operator.rowNumber().operation(this); + } + + default OperationColumn denseRank() { + return Operator.denseRank().operation(this); } - static OperationColumn length(Object arg) { - return Operator.length().operation(arg); + //pipe functions + + default OperationColumn over(Partition part) { + return Operator.over().operation(this, part); } - static OperationColumn upper(Object arg) { - return Operator.upper().operation(arg); + //orders + + default DBOrder order() { + return new DBOrder(this); + } + + default DBOrder asc() { + return new DBOrder(this, Order.ASC); + } + + default DBOrder desc() { + return new DBOrder(this, Order.ASC); + } + + default WhenFilterBridge when(ComparisonExpression ex) { + return new CaseSingleColumnBuilder(this).when(ex); + } + + // constants + + static OperationColumn cdate() { + return Operator.cdate().operation(); + } + + static OperationColumn ctime() { + return Operator.ctime().operation(); + } + + static OperationColumn ctimestamp() { + return Operator.ctimestamp().operation(); } - static OperationColumn lower(Object arg) { - return Operator.lower().operation(arg); + static OperationColumn countAll() { + return Operator.count().operation(column("*")); } - static OperationColumn substring(Object arg, int start, int length) { - return Operator.substring().operation(arg, start, length); + static DBColumn column(@NonNull String value) { + return b-> value; + } + + static TaggableColumn allColumns(@NonNull DBView view) { + return ((DBColumn) b-> { + b.view(view); + return "*"; //avoid view.* as "" + }).as(null); + } + + static DBColumn constant(Object value) { + return constant(()-> value); + } + + static DBColumn constant(Supplier value) { + return new DBColumn() { + + @Override + public String sql(QueryParameterBuilder arg) { + return formatValue(value.get()); //lazy + } + + @Override + public Stream groupKeys() { + return Stream.empty(); + } + }; } } diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index b958b204..23c0602d 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -31,7 +31,7 @@ public ComparisonExpression expression(Object... right) { } } - public DBFilter filter(Object... args) { + public ColumnSingleFilter filter(Object... args) { try { return comparator.filter(parameterSet.assertArguments(args)); } catch (BadArgumentException e) { //TODO message diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 23dfcc20..85f50ca5 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -58,8 +58,12 @@ public static String joinAndDelemit(String delemiter, String before, String } public static T[] arrayJoin(T[] arr, T o) { + return arrayJoin(arr, o, arr.length); + } + + public static T[] arrayJoin(T[] arr, T o, int idx) { var res = copyOf(arr, arr.length+1); - res[arr.length] = o; + res[idx] = o; return res; } } From 848f22b05027778f00d75c4c5effa730cec7ce38 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 16:02:48 +0200 Subject: [PATCH 186/298] edit --- src/main/java/org/usf/jquery/core/DBColumn.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 918ddfb9..606ea83e 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -246,7 +246,7 @@ default OperationColumn substring(int start, int end) { return Operator.substring().operation(this, start, end); } - default OperationColumn concat(String... str) { + default OperationColumn concat(Object... str) { return Operator.concat().operation(arrayJoin(str, this, 0)); } @@ -338,11 +338,13 @@ default OperationColumn decimal(int digit, int precision) { return Operator.decimal().operation(this, digit, precision); } - //aggregate functions + //other functions default OperationColumn coalesce(Object o) { return Operator.coalesce().operation(this, o); } + + //aggregate functions default OperationColumn count() { return Operator.count().operation(this); From 3187d369fcae9f9ff69dcc81ed7b2b9231394691 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 16:05:29 +0200 Subject: [PATCH 187/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 606ea83e..cb3aef3e 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -365,21 +365,6 @@ default OperationColumn sum() { default OperationColumn avg() { return Operator.avg().operation(this); } - - - //window functions - - default OperationColumn rank() { - return Operator.rank().operation(this); - } - - default OperationColumn rowNumber() { - return Operator.rowNumber().operation(this); - } - - default OperationColumn denseRank() { - return Operator.denseRank().operation(this); - } //pipe functions @@ -423,6 +408,20 @@ static OperationColumn countAll() { return Operator.count().operation(column("*")); } + //window functions + + static OperationColumn rank() { + return Operator.rank().operation(); + } + + static OperationColumn rowNumber() { + return Operator.rowNumber().operation(); + } + + static OperationColumn denseRank() { + return Operator.denseRank().operation(); + } + static DBColumn column(@NonNull String value) { return b-> value; } From 63748c6f6d1230b55122b9ca7ce21ac29081ebd8 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 16:13:03 +0200 Subject: [PATCH 188/298] edit --- src/main/java/org/usf/jquery/core/DBColumn.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index cb3aef3e..9b821ea9 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.Order.ASC; +import static org.usf.jquery.core.Order.DESC; import static org.usf.jquery.core.QueryParameterBuilder.formatValue; import static org.usf.jquery.core.Utils.arrayJoin; import static org.usf.jquery.core.Validation.requireLegalVariable; @@ -379,11 +381,15 @@ default DBOrder order() { } default DBOrder asc() { - return new DBOrder(this, Order.ASC); + return order(ASC); } default DBOrder desc() { - return new DBOrder(this, Order.ASC); + return order(DESC); + } + + default DBOrder order(Order order) { + return new DBOrder(this, order); } default WhenFilterBridge when(ComparisonExpression ex) { From aa0e0fbab5dd557c63accdc497a085b92bfecd98 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 17:20:08 +0200 Subject: [PATCH 189/298] edit --- .../java/org/usf/jquery/core/ColumnFilterGroup.java | 1 + src/main/java/org/usf/jquery/core/DBColumn.java | 12 ++++++------ src/main/java/org/usf/jquery/core/Utils.java | 11 ++++++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index 1e92f3d7..c44e6d55 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -4,6 +4,7 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.Utils.arrayJoin; +import static org.usf.jquery.core.Utils.arrayJoinFirst; import java.util.stream.Stream; diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 9b821ea9..2fa2faaf 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -3,7 +3,7 @@ import static org.usf.jquery.core.Order.ASC; import static org.usf.jquery.core.Order.DESC; import static org.usf.jquery.core.QueryParameterBuilder.formatValue; -import static org.usf.jquery.core.Utils.arrayJoin; +import static org.usf.jquery.core.Utils.arrayJoinFirst; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -126,12 +126,12 @@ default ColumnSingleFilter notNull() { @SuppressWarnings("unchecked") default ColumnSingleFilter in(T... arr) { - return Comparator.in().filter(arrayJoin(arr, this, 0)); + return Comparator.in().filter(arrayJoinFirst(arr, this)); } @SuppressWarnings("unchecked") default ColumnSingleFilter notIn(T... arr) { - return Comparator.notIn().filter(arrayJoin(arr, this, 0)); + return Comparator.notIn().filter(arrayJoinFirst(arr, this)); } default ColumnSingleFilter filter(ComparisonExpression exp) { @@ -249,11 +249,11 @@ default OperationColumn substring(int start, int end) { } default OperationColumn concat(Object... str) { - return Operator.concat().operation(arrayJoin(str, this, 0)); + return Operator.concat().operation(arrayJoinFirst(str, this)); } - default OperationColumn pow(int n, String value) { - return Operator.pow().operation(this, n, value); + default OperationColumn lpad(int n, String value) { + return Operator.lpad().operation(this, n, value); } default OperationColumn rpad(int n, String value) { diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 85f50ca5..db4fa034 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -1,9 +1,11 @@ package org.usf.jquery.core; +import static java.lang.System.arraycopy; import static java.util.Arrays.copyOf; import static java.util.Objects.isNull; import static java.util.stream.Collectors.joining; +import java.lang.reflect.Array; import java.util.Collection; import java.util.Map; import java.util.stream.Stream; @@ -58,12 +60,15 @@ public static String joinAndDelemit(String delemiter, String before, String } public static T[] arrayJoin(T[] arr, T o) { - return arrayJoin(arr, o, arr.length); + var res = copyOf(arr, arr.length+1); + res[arr.length+1] = o; + return res; } - public static T[] arrayJoin(T[] arr, T o, int idx) { + public static T[] arrayJoinFirst(T[] arr, T o) { var res = copyOf(arr, arr.length+1); - res[idx] = o; + arraycopy(arr, 0, res, 1, arr.length); + res[0] = o; return res; } } From 971f50604a8a9334fc88327aab4cc4273373ba78 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 17:35:58 +0200 Subject: [PATCH 190/298] edit --- src/main/java/org/usf/jquery/core/ColumnFilterGroup.java | 1 - src/main/java/org/usf/jquery/core/Operator.java | 4 ++-- src/main/java/org/usf/jquery/core/Utils.java | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index c44e6d55..1e92f3d7 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -4,7 +4,6 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; import static org.usf.jquery.core.Utils.arrayJoin; -import static org.usf.jquery.core.Utils.arrayJoinFirst; import java.util.stream.Stream; diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index b740f6f7..1d39952e 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -208,8 +208,8 @@ static TypedOperator yearMonth() { CombinedOperator op = args-> { var col = requireNArgs(1, args, ()-> "yearMonth")[0]; return concat().operation( - lpad().operation(year().operation(col), 4, "0"), "-", - lpad().operation(month().operation(col), 2, "0")); + lpad().operation(varchar().operation(year().operation(col)), 4, "0"), "-", //varchar => postgres + lpad().operation(varchar().operation(month().operation(col)), 2, "0")); }; return new TypedOperator(VARCHAR, op, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata } diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index db4fa034..809f37fb 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -5,7 +5,6 @@ import static java.util.Objects.isNull; import static java.util.stream.Collectors.joining; -import java.lang.reflect.Array; import java.util.Collection; import java.util.Map; import java.util.stream.Stream; From 0ddb7b1b8c0eb5a362b218c6db36701430b4ef5f Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 17:44:00 +0200 Subject: [PATCH 191/298] edit --- src/main/java/org/usf/jquery/core/Utils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 809f37fb..cfcc557e 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -60,7 +60,7 @@ public static String joinAndDelemit(String delemiter, String before, String public static T[] arrayJoin(T[] arr, T o) { var res = copyOf(arr, arr.length+1); - res[arr.length+1] = o; + res[arr.length] = o; return res; } From 25aecc1c96acd7a11b810ab6b73edd8ca8e53602 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 18:38:04 +0200 Subject: [PATCH 192/298] edit --- src/main/java/org/usf/jquery/core/DBColumn.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 2fa2faaf..05364150 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -377,7 +377,7 @@ default OperationColumn over(Partition part) { //orders default DBOrder order() { - return new DBOrder(this); + return order(null); //default } default DBOrder asc() { From 01b8b5d65ebd33667e2a37e965adbaf6b6462809 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 19:05:27 +0200 Subject: [PATCH 193/298] edit --- .../java/org/usf/jquery/core/Comparator.java | 12 ++++++++++-- .../java/org/usf/jquery/core/DBColumn.java | 4 ++++ .../{InCompartor.java => InComparator.java} | 4 ++-- .../org/usf/jquery/core/RangeComparator.java | 19 +++++++++++++++++++ .../jquery/web/view/TimelineChartView.java | 1 - 5 files changed, 35 insertions(+), 5 deletions(-) rename src/main/java/org/usf/jquery/core/{InCompartor.java => InComparator.java} (79%) create mode 100644 src/main/java/org/usf/jquery/core/RangeComparator.java diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 42ab732f..ffaa63eb 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -2,10 +2,10 @@ import static java.util.Arrays.copyOfRange; import static org.usf.jquery.core.ArgTypeRef.firstArgJdbcType; +import static org.usf.jquery.core.DBProcessor.lookup; import static org.usf.jquery.core.JDBCType.VARCHAR; import static org.usf.jquery.core.Parameter.required; import static org.usf.jquery.core.Parameter.varargs; -import static org.usf.jquery.core.DBProcessor.lookup; import java.util.Objects; import java.util.Optional; @@ -55,6 +55,10 @@ static TypedComparator ge() { return new TypedComparator(basicComparator(">="), required(), required(firstArgJdbcType())); } + static TypedComparator between() { + return new TypedComparator(rangeComparator("BETWEEN"), required(), required(firstArgJdbcType()), required(firstArgJdbcType())); + } + //string comparator static TypedComparator startsLike() { @@ -160,7 +164,11 @@ static NullComparator nullComparator(final String name) { return ()-> name; } - static InCompartor inComparator(final String name) { + static InComparator inComparator(final String name) { + return ()-> name; + } + + static RangeComparator rangeComparator(final String name) { return ()-> name; } diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 05364150..99c2ab24 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -76,6 +76,10 @@ default ColumnSingleFilter ge(Object value) { return Comparator.ge().filter(this, value); } + default ColumnSingleFilter between(Object min, Object max) { + return Comparator.between().filter(this, min, max); + } + default ColumnSingleFilter like(Object value) { return Comparator.like().filter(this, value); } diff --git a/src/main/java/org/usf/jquery/core/InCompartor.java b/src/main/java/org/usf/jquery/core/InComparator.java similarity index 79% rename from src/main/java/org/usf/jquery/core/InCompartor.java rename to src/main/java/org/usf/jquery/core/InComparator.java index 2bd325a8..415a378a 100644 --- a/src/main/java/org/usf/jquery/core/InCompartor.java +++ b/src/main/java/org/usf/jquery/core/InComparator.java @@ -10,11 +10,11 @@ * */ @FunctionalInterface -public interface InCompartor extends Comparator { +public interface InComparator extends Comparator { @Override default String sql(QueryParameterBuilder builder, Object[] args) { - requireAtLeastNArgs(2, args, InCompartor.class::getSimpleName); + requireAtLeastNArgs(2, args, InComparator.class::getSimpleName); return builder.appendParameter(args[0]) + SPACE + id() + parenthese(builder.appendArrayParameter(args, 1)); } } diff --git a/src/main/java/org/usf/jquery/core/RangeComparator.java b/src/main/java/org/usf/jquery/core/RangeComparator.java new file mode 100644 index 00000000..06d9355e --- /dev/null +++ b/src/main/java/org/usf/jquery/core/RangeComparator.java @@ -0,0 +1,19 @@ +package org.usf.jquery.core; + +import static org.usf.jquery.core.Validation.requireNArgs; + +/** + * + * @author u$f + * + */ +@FunctionalInterface +public interface RangeComparator extends Comparator { + + @Override + default String sql(QueryParameterBuilder builder, Object[] args) { + requireNArgs(3, args, RangeComparator.class::getSimpleName); + return builder.appendParameter(args[0]) + " " + id() + + " " + builder.appendParameter(args[1]) + " AND " + builder.appendParameter(args[2]); + } +} diff --git a/src/main/java/org/usf/jquery/web/view/TimelineChartView.java b/src/main/java/org/usf/jquery/web/view/TimelineChartView.java index e29fcfd8..638dda2d 100644 --- a/src/main/java/org/usf/jquery/web/view/TimelineChartView.java +++ b/src/main/java/org/usf/jquery/web/view/TimelineChartView.java @@ -3,7 +3,6 @@ import static java.lang.System.currentTimeMillis; import static java.lang.System.lineSeparator; import static java.nio.file.Files.readString; -import static java.util.stream.Collectors.toList; import static org.usf.jquery.web.view.WebViewMapper.TableColumn.columns; import static org.usf.jquery.web.view.WebViewMapper.WebType.NUMBER; import static org.usf.jquery.web.view.WebViewMapper.WebType.STRING; From 433f64d7902e99d6ae33b4ac63d24ea8987d73fa Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 21:10:46 +0200 Subject: [PATCH 194/298] edit --- .../org/usf/jquery/core/LogicalOperator.java | 4 +++ .../org/usf/jquery/core/RequestContext.java | 26 ++++++++++++++++++ .../usf/jquery/core/RequestQueryBuilder.java | 15 ++++++++++- .../org/usf/jquery/web/ChainableCriteria.java | 14 ++++++++++ .../org/usf/jquery/web/CriteriaBuilder.java | 27 ++++++++++--------- 5 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/RequestContext.java create mode 100644 src/main/java/org/usf/jquery/web/ChainableCriteria.java diff --git a/src/main/java/org/usf/jquery/core/LogicalOperator.java b/src/main/java/org/usf/jquery/core/LogicalOperator.java index 4ed0ce39..04eb5c4d 100644 --- a/src/main/java/org/usf/jquery/core/LogicalOperator.java +++ b/src/main/java/org/usf/jquery/core/LogicalOperator.java @@ -14,4 +14,8 @@ public enum LogicalOperator { public String sql() { return space(name()); } + + public > T combine(T o1, T o2) { + return o1.append(this, o2); + } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/RequestContext.java b/src/main/java/org/usf/jquery/core/RequestContext.java new file mode 100644 index 00000000..7cf872e0 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/RequestContext.java @@ -0,0 +1,26 @@ +package org.usf.jquery.core; + +import static org.usf.jquery.core.DBColumn.allColumns; + +import java.util.Optional; +import java.util.function.Supplier; + +/** + * + * @author u$f + * + */ +public interface RequestContext { + + Optional lookupDeclaredColumn(String name); + + QueryView overView(DBView view, Supplier supp); + + default ViewColumn overView(DBView view, TaggableColumn column) { + overView(view, ()-> new RequestQueryBuilder().columns(allColumns(view)).asView()) + .getBuilder().columns(column); + return new ViewColumn(view, column.tagname(), null, column.getType()); + } + + //sub query filters, orders, ... +} diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 969e28c1..2e7c53ab 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -20,6 +20,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; import lombok.Getter; import lombok.NonNull; @@ -32,7 +34,7 @@ */ @Slf4j @Getter -public class RequestQueryBuilder { +public class RequestQueryBuilder implements RequestContext { private final List columns = new ArrayList<>(); private final List filters = new ArrayList<>(); //WHERE & HAVING @@ -52,6 +54,16 @@ public RequestQueryBuilder(Database target) { setCurrentDatabase(target); } + @Override + public Optional lookupDeclaredColumn(String name) { + return columns.stream().filter(c-> name.equals(c.tagname())).findAny(); + } + + @Override + public QueryView overView(DBView view, Supplier supp) { + return overView.computeIfAbsent(view, k-> supp.get()); + } + public RequestQueryBuilder columns(@NonNull TaggableColumn... columns) { addAll(this.columns, columns); return this; @@ -92,6 +104,7 @@ public RequestQueryBuilder distinct() { return this; } + @Deprecated public RequestQueryBuilder overViews(Map overs) { overView.putAll(overs); return this; diff --git a/src/main/java/org/usf/jquery/web/ChainableCriteria.java b/src/main/java/org/usf/jquery/web/ChainableCriteria.java new file mode 100644 index 00000000..4e9fbef8 --- /dev/null +++ b/src/main/java/org/usf/jquery/web/ChainableCriteria.java @@ -0,0 +1,14 @@ +package org.usf.jquery.web; + +import org.usf.jquery.core.Chainable; + +/** + * + * @author u$f + * + */ +@FunctionalInterface +public interface ChainableCriteria> { + + T criteria(String arg); +} diff --git a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java index 00826b9b..e793996b 100644 --- a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java +++ b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java @@ -1,9 +1,8 @@ package org.usf.jquery.web; -import static java.util.Optional.ofNullable; import static org.usf.jquery.core.LogicalOperator.OR; -import static org.usf.jquery.core.Validation.requireAtLeastNArgs; -import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; +import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.core.Validation.requireNArgs; import java.util.stream.Stream; @@ -18,17 +17,19 @@ @FunctionalInterface public interface CriteriaBuilder> { - T criteria(String arg); + T build(String... arg); - default T build(String... args) { - return Stream.of(requireAtLeastNArgs(1, args, CriteriaBuilder.class::getSimpleName)) - .map(v-> ofNullable(criteria(v)) - .orElseThrow(()-> noSuchResourceException("criteria value", v))) - .reduce((e1, e2)-> e1.append(combiner(), e2)) - .orElseThrow(); + static > CriteriaBuilder singleArg(ChainableCriteria cr){ + return args-> cr.criteria(isEmpty(args) ? null : requireNArgs(1, args, ()-> "single arg criteria")[0]); } - - default LogicalOperator combiner() { - return OR; + + static > CriteriaBuilder multiArgs(ChainableCriteria cr){ + return multiArgs(OR, cr); + } + + static > CriteriaBuilder multiArgs(LogicalOperator op, ChainableCriteria cr){ + return args-> isEmpty(args) + ? cr.criteria(null) + : Stream.of(args).map(cr::criteria).reduce(op::combine).orElseThrow(); } } From 279a8f3f36ddf75ec442d8ea19307a24f7e92059 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 28 Aug 2024 22:58:46 +0200 Subject: [PATCH 195/298] edit --- src/main/java/org/usf/jquery/core/RequestContext.java | 2 +- src/main/java/org/usf/jquery/core/RequestQueryBuilder.java | 2 +- src/main/java/org/usf/jquery/web/QueryDecorator.java | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/RequestContext.java b/src/main/java/org/usf/jquery/core/RequestContext.java index 7cf872e0..a8f058ee 100644 --- a/src/main/java/org/usf/jquery/core/RequestContext.java +++ b/src/main/java/org/usf/jquery/core/RequestContext.java @@ -12,7 +12,7 @@ */ public interface RequestContext { - Optional lookupDeclaredColumn(String name); + Optional declaredColumn(String name); QueryView overView(DBView view, Supplier supp); diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 2e7c53ab..caee9cf0 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -55,7 +55,7 @@ public RequestQueryBuilder(Database target) { } @Override - public Optional lookupDeclaredColumn(String name) { + public Optional declaredColumn(String name) { return columns.stream().filter(c-> name.equals(c.tagname())).findAny(); } diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index cb938e23..8ddd2cb5 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -32,9 +32,7 @@ public DBView view() { } public TaggableColumn column(String id) { - return query.getBuilder().getColumns().stream() - .filter(c-> c.tagname().equals(id)) //tagname nullable ! - .findAny() + return query.getBuilder().declaredColumn(id) .map(c-> new ViewColumn(query, c.tagname(), c.tagname(), c.getType())) .orElse(null); } From 4f4c7bb94a3c44f927a9b69797136822cb8d567b Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 29 Aug 2024 09:31:57 +0200 Subject: [PATCH 196/298] edit --- src/main/java/org/usf/jquery/web/RequestParser.java | 5 ++++- src/test/java/org/usf/jquery/web/RequestParserTest.java | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 3cfa4693..a738d787 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -72,6 +72,9 @@ private RequestEntryChain parseEntry() { } return entry; } + if(legalNumber(c)) { + return new RequestEntryChain(nextWhile(RequestParser::legalValChar)); + } if(c == '"') { nextChar(true); var txt = nextWhile(RequestParser::legalTxtChar); @@ -79,7 +82,7 @@ private RequestEntryChain parseEntry() { nextChar(false); return new RequestEntryChain(txt, true); //no next, no args, no tag } - return new RequestEntryChain(legalNumber(c) || c == '-' ? nextWhile(RequestParser::legalValChar) : null); // decimal negative? | instant format + return new RequestEntryChain(null); } private String nextWhile(CharPredicate cp) { diff --git a/src/test/java/org/usf/jquery/web/RequestParserTest.java b/src/test/java/org/usf/jquery/web/RequestParserTest.java index 57a9ea5e..18f31592 100644 --- a/src/test/java/org/usf/jquery/web/RequestParserTest.java +++ b/src/test/java/org/usf/jquery/web/RequestParserTest.java @@ -50,6 +50,9 @@ void testParse(String s) { // "12345", // "1name", "_column", + "column.\"val\"", + "column.a-b", + "_.column", "(column", ")column", "\"column", From 55df73a01b4cccb21ce1c7830fca556349f5417d Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 29 Aug 2024 12:24:38 +0200 Subject: [PATCH 197/298] edit --- src/main/java/org/usf/jquery/web/CriteriaBuilder.java | 2 +- src/main/java/org/usf/jquery/web/JDBCArgumentParser.java | 3 +-- src/main/java/org/usf/jquery/web/RequestParser.java | 5 ++++- src/test/java/org/usf/jquery/web/RequestParserTest.java | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java index e793996b..eafa03af 100644 --- a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java +++ b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java @@ -18,7 +18,7 @@ public interface CriteriaBuilder> { T build(String... arg); - + static > CriteriaBuilder singleArg(ChainableCriteria cr){ return args-> cr.criteria(isEmpty(args) ? null : requireNArgs(1, args, ()-> "single arg criteria")[0]); } diff --git a/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java index d337dc97..bde72484 100644 --- a/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java +++ b/src/main/java/org/usf/jquery/web/JDBCArgumentParser.java @@ -16,12 +16,11 @@ public interface JDBCArgumentParser extends JavaArgumentParser { default Object parseEntry(RequestEntryChain entry, ViewDecorator td) { Object v = null; try { - v = nativeParse(entry.getValue()); + v = nativeParse(entry.toString()); //!value } catch(Exception e) { throw cannotParseEntryException("value", entry, e); } - entry.requireNoArgs().requireNoNext(); //check after parse return v; } } diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index a738d787..5e9cc066 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -65,6 +65,9 @@ private RequestEntryChain parseEntry() { if(legalLetter(c)) { //require identifier after '.' entry.setNext(parseEntry()); } + else { //avoid .:tag or .( + throw new EntrySyntaxException("unexpected character '" + c + "' at index=" + idx); //end + } } if(c == ':') { nextChar(true); @@ -72,7 +75,7 @@ private RequestEntryChain parseEntry() { } return entry; } - if(legalNumber(c)) { + if(legalNumber(c) || c == '-') { //negative return new RequestEntryChain(nextWhile(RequestParser::legalValChar)); } if(c == '"') { diff --git a/src/test/java/org/usf/jquery/web/RequestParserTest.java b/src/test/java/org/usf/jquery/web/RequestParserTest.java index 18f31592..4dddf573 100644 --- a/src/test/java/org/usf/jquery/web/RequestParserTest.java +++ b/src/test/java/org/usf/jquery/web/RequestParserTest.java @@ -51,6 +51,7 @@ void testParse(String s) { // "1name", "_column", "column.\"val\"", + "column.:tag", "column.a-b", "_.column", "(column", From ef48ac50f5c3a439232dd42805b6a3fa863e7538 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 29 Aug 2024 20:41:17 +0200 Subject: [PATCH 198/298] edit --- src/main/java/org/usf/jquery/core/DBView.java | 4 ++-- .../core/{RequestContext.java => QueryContext.java} | 12 +++++++----- .../org/usf/jquery/core/RequestQueryBuilder.java | 2 +- src/main/java/org/usf/jquery/web/RequestParser.java | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) rename src/main/java/org/usf/jquery/core/{RequestContext.java => QueryContext.java} (68%) diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 18e3860f..2e2f70db 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -11,14 +11,14 @@ */ @FunctionalInterface public interface DBView extends DBObject { + + String sql(QueryParameterBuilder builder); @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNoArgs(args, DBView.class::getSimpleName); return sql(builder); } - - String sql(QueryParameterBuilder builder); default String sqlWithTag(QueryParameterBuilder builder) { var tag = builder.view(this); diff --git a/src/main/java/org/usf/jquery/core/RequestContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java similarity index 68% rename from src/main/java/org/usf/jquery/core/RequestContext.java rename to src/main/java/org/usf/jquery/core/QueryContext.java index a8f058ee..4416d0f7 100644 --- a/src/main/java/org/usf/jquery/core/RequestContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -10,17 +10,19 @@ * @author u$f * */ -public interface RequestContext { +public interface QueryContext { Optional declaredColumn(String name); QueryView overView(DBView view, Supplier supp); + default QueryView overView(DBView view) { + return overView(view, ()-> new RequestQueryBuilder().columns(allColumns(view)).asView()); + } + default ViewColumn overView(DBView view, TaggableColumn column) { - overView(view, ()-> new RequestQueryBuilder().columns(allColumns(view)).asView()) - .getBuilder().columns(column); + overView(view).getBuilder().columns(column); return new ViewColumn(view, column.tagname(), null, column.getType()); } - //sub query filters, orders, ... -} +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index caee9cf0..ba4ff230 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -34,7 +34,7 @@ */ @Slf4j @Getter -public class RequestQueryBuilder implements RequestContext { +public class RequestQueryBuilder implements QueryContext { private final List columns = new ArrayList<>(); private final List filters = new ArrayList<>(); //WHERE & HAVING diff --git a/src/main/java/org/usf/jquery/web/RequestParser.java b/src/main/java/org/usf/jquery/web/RequestParser.java index 5e9cc066..0de248e2 100644 --- a/src/main/java/org/usf/jquery/web/RequestParser.java +++ b/src/main/java/org/usf/jquery/web/RequestParser.java @@ -75,7 +75,7 @@ private RequestEntryChain parseEntry() { } return entry; } - if(legalNumber(c) || c == '-') { //negative + if(legalNumber(c) || c == '-') { //negative number return new RequestEntryChain(nextWhile(RequestParser::legalValChar)); } if(c == '"') { From edc765fb082775dffc58afcfad3278b1e69402fd Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 29 Aug 2024 22:20:04 +0200 Subject: [PATCH 199/298] edit --- .../java/org/usf/jquery/core/ArgTypeRef.java | 4 +- .../usf/jquery/core/ColumnFilterGroup.java | 4 +- .../java/org/usf/jquery/core/DBColumn.java | 4 +- .../usf/jquery/core/RequestQueryBuilder.java | 8 +- src/main/java/org/usf/jquery/core/Utils.java | 4 +- .../org/usf/jquery/web/ArgumentParsers.java | 21 +-- .../usf/jquery/web/ContextEnvironment.java | 31 +--- .../org/usf/jquery/web/RequestEntryChain.java | 145 ++++++++---------- .../org/usf/jquery/web/ViewDecorator.java | 9 +- .../usf/jquery/web/ViewDecoratorWrapper.java | 5 + 10 files changed, 99 insertions(+), 136 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ArgTypeRef.java b/src/main/java/org/usf/jquery/core/ArgTypeRef.java index ef50b0b2..2a30daf4 100644 --- a/src/main/java/org/usf/jquery/core/ArgTypeRef.java +++ b/src/main/java/org/usf/jquery/core/ArgTypeRef.java @@ -13,7 +13,7 @@ interface ArgTypeRef extends Function { static ArgTypeRef firstArgJdbcType() { - return arr-> typeOf(requireAtLeastNArgs(1, arr, - ArgTypeRef.class::getSimpleName)[0]).orElse(null); // not sure + return arr-> typeOf(requireAtLeastNArgs(1, arr, ArgTypeRef.class::getSimpleName)[0]) + .orElse(null); // not sure } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index 1e92f3d7..77f6f6ca 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -25,9 +25,9 @@ public final class ColumnFilterGroup implements DBFilter { @Override public String sql(QueryParameterBuilder builder) { - return "(" + Stream.of(filters) + return Stream.of(filters) .map(o-> o.sql(builder)) - .collect(joining(operator.sql())) + ")"; + .collect(joining(operator.sql(), "(", ")")); } @Override diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 99c2ab24..2a5b7271 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -24,13 +24,13 @@ @FunctionalInterface public interface DBColumn extends DBObject, Typed, Groupable { + String sql(QueryParameterBuilder builder); + @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNoArgs(args, DBColumn.class::getSimpleName); return sql(builder); } - - String sql(QueryParameterBuilder builder); @Override default boolean isAggregation() { diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index ba4ff230..77e4031f 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -103,13 +103,7 @@ public RequestQueryBuilder distinct() { distinct = true; return this; } - - @Deprecated - public RequestQueryBuilder overViews(Map overs) { - overView.putAll(overs); - return this; - } - + public QueryView asView() { return new QueryView(this); } diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index cfcc557e..1bc60e77 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -64,8 +64,8 @@ public static T[] arrayJoin(T[] arr, T o) { return res; } - public static T[] arrayJoinFirst(T[] arr, T o) { - var res = copyOf(arr, arr.length+1); + public static Object[] arrayJoinFirst(Object[] arr, Object o) { + var res = new Object[arr.length+1]; arraycopy(arr, 0, res, 1, arr.length); res[0] = o; return res; diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 3cb242ac..7704ef35 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -29,6 +29,7 @@ import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; +import org.usf.jquery.core.QueryContext; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -48,7 +49,7 @@ public class ArgumentParsers { BIGINT, DOUBLE, DATE, TIMESTAMP, TIME, TIMESTAMP_WITH_TIMEZONE, VARCHAR }; //varchar !? - public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType... types) { + public static Object parse(RequestEntryChain entry, ViewDecorator td, QueryContext ctx, JavaType... types) { List list = new ArrayList<>(); if(isEmpty(types) || Stream.of(types).anyMatch(JDBCType.class::isInstance)) { list.add(COLUMN); @@ -62,7 +63,7 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType.. return jdbcArgParser(t).parseEntry(entry, td); } if(type instanceof JQueryType t) { - return jqueryArgParser(t).parseEntry(entry, td); + return jqueryArgParser(t, ctx).parseEntry(entry, td); } else { throw new UnsupportedOperationException(requireNonNull(type, "type is null").toString()); @@ -99,16 +100,16 @@ public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { } } - public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type) { + public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type, QueryContext ctx) { switch (type) { - case QUERY_COLUMN: return RequestEntryChain::evalQueryColumn; - case NAMED_COLUMN: return (e,v)-> e.evalColumn(v, true, true); //separate query context - case COLUMN: return (e,v)-> e.evalColumn(v, false, false); - case FILTER: return RequestEntryChain::evalFilter; - case ORDER: return RequestEntryChain::evalOrder; - case QUERY: return RequestEntryChain::evalQuery; + case QUERY_COLUMN: return (e,v)-> e.evalQueryColumn(v, ctx); + case NAMED_COLUMN: return (e,v)-> e.evalColumn(v, ctx, true); //separate query context + case COLUMN: return (e,v)-> e.evalColumn(v, ctx, false); + case FILTER: return (e,v)-> e.evalFilter(v, ctx); + case ORDER: return (e,v)-> e.evalOrder(v, ctx); + case QUERY: return (e,v)-> e.evalQuery(v, ctx); case JOIN: return RequestEntryChain::evalJoin; - case PARTITION: return RequestEntryChain::evalPartition; + case PARTITION: return (e,v)-> e.evalPartition(v, ctx); default: throw unsupportedTypeException(type); } } diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index f3afe319..8c726970 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -22,16 +22,12 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; -import java.util.function.Supplier; import java.util.stream.Collector; import java.util.stream.Stream; import javax.sql.DataSource; -import org.usf.jquery.core.DBView; import org.usf.jquery.core.JQueryException; -import org.usf.jquery.core.QueryView; -import org.usf.jquery.core.TaggableColumn; import lombok.AccessLevel; import lombok.Getter; @@ -57,14 +53,11 @@ public final class ContextEnvironment { private final DataSource dataSource; //optional private final String schema; //optional private final DatabaseMetadata metadata; - //runtime scope - private final Map overView = new HashMap<>(); - private final Map declaredColumns = new HashMap<>(); ContextEnvironment(ContextEnvironment ctx) { this.database = ctx.database; this.views = new HashMap<>(ctx.views); //modifiable - this.columns = new HashMap<>(ctx.columns); //modifiable + this.columns = ctx.columns; this.dataSource = ctx.dataSource; this.schema = ctx.schema; this.metadata = ctx.metadata; @@ -77,11 +70,7 @@ public Optional lookupRegisteredView(String name) { public Optional lookupRegisteredColumn(String name) { return ofNullable(columns.get(name)); } - - Optional lookupDeclaredColumn(String name) { - return ofNullable(declaredColumns.get(name)); - } - + void declareView(ViewDecorator view) { //additional request views views.compute(view.identity(), (k,v)-> { if(isNull(v)){ @@ -91,22 +80,6 @@ void declareView(ViewDecorator view) { //additional request views }); } - TaggableColumn declareColumn(TaggableColumn col) { - views.computeIfPresent(col.tagname(), (k,v)-> { //cannot overwrite registered views - throw resourceAlreadyExistsException(k, v); - }); //but can overwrite registered columns - return declaredColumns.compute(col.tagname(), (k,v)-> { - if(isNull(v)){ - return col; - } - throw resourceAlreadyExistsException(k, v); - }); - } - - QueryView overView(DBView view, Supplier supp) { - return overView.computeIfAbsent(view, k-> supp.get()); - } - ViewMetadata computeTableMetadata(ViewDecorator vd, Function, ViewMetadata> fn) { return metadata.getTables().computeIfAbsent(vd.identity(), key-> fn.apply(columns.values())); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index a265ecdb..f3065594 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -25,7 +25,6 @@ import static org.usf.jquery.core.Utils.joinArray; import static org.usf.jquery.web.ArgumentParsers.parse; import static org.usf.jquery.web.ContextManager.currentContext; -import static org.usf.jquery.web.ContextManager.setCurrentContext; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.Parameters.COLUMN; @@ -60,6 +59,7 @@ import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; import org.usf.jquery.core.QueryColumn; +import org.usf.jquery.core.QueryContext; import org.usf.jquery.core.QueryView; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; @@ -99,53 +99,47 @@ public RequestEntryChain(String value) { } // [view|query]:tag - public ViewDecorator evalView(ViewDecorator vd) { + public ViewDecorator evalView(ViewDecorator vd, QueryContext ctx) { return currentContext().lookupRegisteredView(value) //check args & next only if view exists .map(v-> new ViewDecoratorWrapper(v, requireNoArgs().requireNoNext().requireTag())) - .or(()-> evalQuery(vd, true)) + .or(()-> evalQuery(vd, ctx, true)) .orElseThrow(()-> noSuchResourceException(VIEW, value)); } - public QueryColumn evalQueryColumn(ViewDecorator td) { - return evalQuery(td, false) + public QueryColumn evalQueryColumn(ViewDecorator td, QueryContext ctx) { + return evalQuery(td, ctx, false) .map(QueryDecorator::getQuery) .map(QueryView::asColumn) .orElseThrow(()-> cannotParseEntryException(QUERY, this)); } - public ViewDecorator evalQuery(ViewDecorator td) { - return evalQuery(td, false).orElseThrow(()-> cannotParseEntryException(QUERY, this)); + public ViewDecorator evalQuery(ViewDecorator td, QueryContext ctx) { + return evalQuery(td, ctx, false).orElseThrow(()-> cannotParseEntryException(QUERY, this)); } //select[.distinct|filter|order|offset|fetch]* - Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub context + Optional evalQuery(ViewDecorator td, QueryContext ctx, boolean requireTag) { //sub context if(SELECT.equals(value)) { var e = this; - var ctx = currentContext(); - setCurrentContext(new ContextEnvironment(ctx)); //sub context : inherits only declared views try { - var q = new RequestQueryBuilder().columns(taggableVarargs(td)); + var q = new RequestQueryBuilder().columns(taggableVarargs(td, ctx)); while(e.hasNext()) { e = e.next; switch(e.value) {//column not allowed case DISTINCT: e.requireNoArgs(); q.distinct(); break; - case FILTER: q.filters(e.filterVarargs(td)); break; - case ORDER: q.orders(e.oderVarargs(td)); break; + case FILTER: q.filters(e.filterVarargs(td, ctx)); break; + case ORDER: q.orders(e.oderVarargs(td, ctx)); break; case JOIN: q.joins(e.evalJoin(td)); break; - case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; - case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; + case OFFSET: q.offset((int)e.toOneArg(td, ctx, INTEGER)); break; + case FETCH: q.fetch((int)e.toOneArg(td, ctx, INTEGER)); break; default: throw badEntrySyntaxException(joinArray("|", DISTINCT, FILTER, ORDER, JOIN, OFFSET, FETCH), e.value); } } - q.overViews(currentContext().getOverView()); return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); } catch (EntryParseException | NoSuchResourceException ex) { throw new EntrySyntaxException("incorrect query syntax: " + e, ex); } - finally { - setCurrentContext(ctx); - } } return empty(); } @@ -167,7 +161,7 @@ public ViewJoin[] evalJoin(ViewDecorator vd) { } //[partition.order]* - public Partition evalPartition(ViewDecorator vd) { + public Partition evalPartition(ViewDecorator vd, QueryContext ctx) { var e = this; if(hasNext()) { var res = currentContext().lookupRegisteredView(value); @@ -182,7 +176,7 @@ public Partition evalPartition(ViewDecorator vd) { return requireNonNull(par.build(), vd.identity() + ".partition: " + e); } if(e == this) { // not view - var res = evalPartition2(vd); + var res = evalPartition2(vd, ctx); if(res.isPresent()) { return res.get(); } @@ -190,14 +184,14 @@ public Partition evalPartition(ViewDecorator vd) { throw noSuchResourceException(vd.identity() + ".partition", e.value); } - private Optional evalPartition2(ViewDecorator vd) { + private Optional evalPartition2(ViewDecorator vd, QueryContext ctx) { List cols = new ArrayList<>(); List ords = new ArrayList<>(); var e = this; do { switch (e.value) { - case PARTITION: addAll(cols, columnVarargs(vd)); break; - case ORDER: addAll(ords, e.oderVarargs(vd)); break; + case PARTITION: addAll(cols, columnVarargs(vd, ctx)); break; + case ORDER: addAll(ords, e.oderVarargs(vd, ctx)); break; default: if(e == this) { return empty(); //cannotParseEntryException(PARTITION, e) //first entry @@ -212,13 +206,12 @@ private Optional evalPartition2(ViewDecorator vd) { } //[view.]column[.operator]* - public DBColumn evalColumn(ViewDecorator td, boolean requireTag, boolean declare) { - var r = chainColumnOperations(td, false) + public DBColumn evalColumn(ViewDecorator td, QueryContext ctx, boolean requireTag) { + var r = chainColumnOperations(td, ctx, false) .orElseThrow(()-> noSuchViewColumnException(this)); r.entry.requireNoNext(); //check next only if column exists if(nonNull(r.entry.tag)) { - var c = r.col.as(r.entry.tag); - return declare ? currentContext().declareColumn(c) : c; + return r.col.as(r.entry.tag); } if(!requireTag || r.col instanceof TaggableColumn) { return r.col; @@ -227,8 +220,8 @@ public DBColumn evalColumn(ViewDecorator td, boolean requireTag, boolean declare } //[view.]column[.operator]*[.order] - public DBOrder evalOrder(ViewDecorator td) { - var r = chainColumnOperations(td, false) + public DBOrder evalOrder(ViewDecorator td, QueryContext ctx) { + var r = chainColumnOperations(td, ctx, false) .orElseThrow(()-> noSuchViewColumnException(this)); if(r.entry.isLast()) { // default order return r.col.order(); @@ -241,28 +234,28 @@ public DBOrder evalOrder(ViewDecorator td) { throw badEntrySyntaxException(ORDER_PATTERN, ord.value); } - public DBFilter evalFilter(ViewDecorator td) { - return evalFilter(td, emptyList()); + public DBFilter evalFilter(ViewDecorator td, QueryContext ctx) { + return evalFilter(td, ctx, emptyList()); } //[view.]criteria | [view.]column.criteria | [view.]column[.operator]*[.comparator] - public DBFilter evalFilter(ViewDecorator vd, List values) { //supply values - var res = chainColumnOperations(vd, true); + public DBFilter evalFilter(ViewDecorator vd, QueryContext ctx, List values) { //supply values + var res = chainColumnOperations(vd, ctx, true); if(res.isEmpty()) { //not a column - return viewCriteria(vd, values) + return viewCriteria(vd, ctx, values) .orElseThrow(()-> noSuchViewColumnException(this)); } var rc = res.get(); if(rc.entry.isLast()) { //no comparator, no criteria var fn = requireNonNull(values).size() == 1 ? eq() : in(); //non empty var e = new RequestEntryChain(null, false, null, values, null); - return fn.filter(e.toArgs(vd, rc.col, fn.getParameterSet())); //no chain + return fn.filter(e.toArgs(vd, ctx, rc.col, fn.getParameterSet())); //no chain } - return rc.entry.next.columnCriteria(vd, rc.cd, rc.col, values); + return rc.entry.next.columnCriteria(vd, ctx, rc.cd, rc.col, values); } //[view.]criteria - Optional viewCriteria(ViewDecorator td, List values) { + Optional viewCriteria(ViewDecorator td, QueryContext ctx, List values) { CriteriaBuilder b = null; RequestEntryChain e = null; if(hasNext()) { //view.id == column.id @@ -279,24 +272,24 @@ Optional viewCriteria(ViewDecorator td, List values if(nonNull(b)) { var strArgs = toStringArray(e.assertOuterParameters(values)); var f = requireNonNull(b.build(strArgs), "view.criteria: " + e); - return Optional.of(e.chainComparator(td, f)); + return Optional.of(e.chainComparator(td, ctx, f)); } return Optional.empty(); } - DBFilter columnCriteria(ViewDecorator vc, ColumnDecorator cd, DBColumn col, List values) { + DBFilter columnCriteria(ViewDecorator vc, QueryContext ctx, ColumnDecorator cd, DBColumn col, List values) { var cmp = lookupComparator(value); if(cmp.isPresent()) { var fn = cmp.get(); var cp = new RequestEntryChain(value, false, null, assertOuterParameters(values), null); - return chainComparator(vc, fn.filter(cp.toArgs(vc, col, fn.getParameterSet()))); + return chainComparator(vc, ctx, fn.filter(cp.toArgs(vc, ctx, col, fn.getParameterSet()))); } if(nonNull(cd)) { // no operation var c = cd.criteria(value); if(nonNull(c)) { var strArgs = toStringArray(assertOuterParameters(values)); var ex = requireNonNull(c.build(strArgs), "column.criteria: " + this); - return chainComparator(vc, col.filter(ex)); + return chainComparator(vc, ctx, col.filter(ex)); } throw noSuchResourceException("comparator|criteria", value); } @@ -313,12 +306,12 @@ private List assertOuterParameters(List va throw new IllegalStateException(this + "=" + values); //denied } - DBFilter chainComparator(ViewDecorator td, DBFilter f) { + DBFilter chainComparator(ViewDecorator td, QueryContext ctx, DBFilter f) { var e = next; while(nonNull(e)) { if(e.value.matches(LOGIC_PATTERN)) { var op = LogicalOperator.valueOf(e.value.toUpperCase()); - f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); + f = f.append(op, (DBFilter) e.toOneArg(td, ctx, JQueryType.FILTER)); e = e.next; } else { @@ -328,18 +321,18 @@ DBFilter chainComparator(ViewDecorator td, DBFilter f) { return f; } - private Optional chainColumnOperations(ViewDecorator td, boolean filter) { - return lookupResource(td).map(r-> { + private Optional chainColumnOperations(ViewDecorator td, QueryContext ctx, boolean filter) { + return lookupResource(td, ctx).map(r-> { var e = r.entry.next; while(nonNull(e)) { //chain until !operator - var o = e.lookupOperation(td, r.col, fn-> true); //accept all + var o = e.lookupOperation(td, ctx, r.col, fn-> true); //accept all if(o.isEmpty()) { break; } r.cd = null; r.entry = e; r.col = filter && "over".equals(e.value) - ? windowColumn(r.vd, o.get()) + ? windowColumn(r.vd, ctx, o.get()) : o.get(); e = e.next; } @@ -347,82 +340,80 @@ private Optional chainColumnOperations(ViewDecorator td, boolean f }); } - private static DBColumn windowColumn(ViewDecorator vd, DBColumn col) { - var v = requireNonNull(vd, "column view").view(); //Declared column + private static DBColumn windowColumn(ViewDecorator vd, QueryContext ctx, DBColumn col) { + var v = vd.view(); var tag = "over_" + vd.identity() + "_" + col.hashCode(); //over_view_hash - currentContext().overView(v, ()-> new RequestQueryBuilder() - .columns(allColumns(v)).asView()) - .getBuilder().columns(col.as(tag)); //append over column + ctx.overView(v).getBuilder().columns(col.as(tag)); //append over colum return new ViewColumn(v, doubleQuote(tag), null, col.getType()); } - private Optional lookupResource(ViewDecorator td) { //do not change priority + private Optional lookupResource(ViewDecorator td, QueryContext ctx) { //do not change priority if(hasNext()) { //view.id == column.id var rc = currentContext().lookupRegisteredView(value) - .flatMap(v-> next.lookupViewResource(v, RequestEntryChain::isWindowFunction)); + .flatMap(v-> next.lookupViewResource(v, ctx, RequestEntryChain::isWindowFunction)); if(rc.isPresent()) { requireNoArgs(); //view takes no args return rc; } } - return currentContext().lookupDeclaredColumn(value) //declared column first + return ctx.declaredColumn(value) //declared column first .map(c-> new ViewResource(null, null, requireNoArgs(), c)) - .or(()-> lookupViewResource(td, fn-> true)); //registered column + .or(()-> lookupViewResource(td, ctx, fn-> true)); //registered column } - private Optional lookupViewResource(ViewDecorator td, Predicate pre) { + private Optional lookupViewResource(ViewDecorator td, QueryContext ctx, Predicate pre) { var res = td instanceof QueryDecorator qd ? ofNullable(qd.column(value)) .map(c-> new ViewResource(td, null, requireNoArgs(), c)) : currentContext().lookupRegisteredColumn(value) .map(cd-> new ViewResource(td, cd, requireNoArgs(), td.column(cd))); - return res.or(()-> lookupOperation(td, null, pre).map(col-> new ViewResource(td, null, this, col))); + return res.or(()-> lookupOperation(td, ctx, null, pre).map(col-> new ViewResource(td, null, this, col))); } - private Optional lookupOperation(ViewDecorator vd, DBColumn col, Predicate opr) { + private Optional lookupOperation(ViewDecorator vd, QueryContext ctx, DBColumn col, Predicate opr) { return lookupOperator(value).filter(opr).map(fn-> { var c = col; if(isNull(c) && isEmpty(args) && isCountFunction(fn)) { c = allColumns(vd.view()); } - return fn.operation(toArgs(vd, c, fn.getParameterSet())); + return fn.operation(toArgs(vd, ctx, c, fn.getParameterSet())); }); } - private TaggableColumn[] taggableVarargs(ViewDecorator td) { - return (TaggableColumn[]) typeVarargs(td, JQueryType.NAMED_COLUMN); + private TaggableColumn[] taggableVarargs(ViewDecorator td, QueryContext ctx) { + return (TaggableColumn[]) typeVarargs(td, ctx, JQueryType.NAMED_COLUMN); } - private DBColumn[] columnVarargs(ViewDecorator td) { - return (DBColumn[]) typeVarargs(td, JQueryType.COLUMN); + private DBColumn[] columnVarargs(ViewDecorator td, QueryContext ctx) { + return (DBColumn[]) typeVarargs(td, ctx, JQueryType.COLUMN); } - private DBOrder[] oderVarargs(ViewDecorator td) { - return (DBOrder[]) typeVarargs(td, JQueryType.ORDER); + private DBOrder[] oderVarargs(ViewDecorator td, QueryContext ctx) { + return (DBOrder[]) typeVarargs(td, ctx, JQueryType.ORDER); } - private DBFilter[] filterVarargs(ViewDecorator td) { - return (DBFilter[]) typeVarargs(td, JQueryType.FILTER); + private DBFilter[] filterVarargs(ViewDecorator td, QueryContext ctx) { + return (DBFilter[]) typeVarargs(td, ctx, JQueryType.FILTER); } - private Object[] typeVarargs(ViewDecorator td, JavaType type) { + private Object[] typeVarargs(ViewDecorator td, QueryContext ctx, JavaType type) { var c = type.typeClass(); if(DBObject.class.isAssignableFrom(c)) { // JQuery types & !array var ps = ofParameters(required(type), varargs(type)); - return toArgs(td, null, ps, s-> (Object[]) newInstance(c, s)); + return toArgs(td, ctx, null, ps, s-> (Object[]) newInstance(c, s)); } throw new UnsupportedOperationException("cannot instantiate type " + c); } - private Object toOneArg(ViewDecorator td, JavaType type) { - return toArgs(td, null, ofParameters(required(type)))[0]; + private Object toOneArg(ViewDecorator td, QueryContext ctx, JavaType type) { + return toArgs(td, ctx, null, ofParameters(required(type)))[0]; } - private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps) { - return toArgs(td, col, ps, Object[]::new); + private Object[] toArgs(ViewDecorator td, QueryContext ctx, DBObject col, ParameterSet ps) { + return toArgs(td, ctx, col, ps, Object[]::new); } - private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunction arrFn) { + private Object[] toArgs(ViewDecorator td, QueryContext ctx, DBObject col, ParameterSet ps, IntFunction arrFn) { int inc = isNull(col) ? 0 : 1; var arr = arrFn.apply(isNull(args) ? inc : args.size() + inc); if(nonNull(col)) { @@ -434,7 +425,7 @@ private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunc var e = args.get(i-inc); arr[i] = isNull(e.value) || e.text ? e.requireNoArgs().value - : parse(e, td, p.types(arr)); + : parse(e, td, ctx, p.types(arr)); } }); } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 52e7fe86..6aa1d602 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -114,7 +114,6 @@ default RequestQueryBuilder query(Map parameterMap) { parseFetch(query, parameterMap); parseOffset(query, parameterMap); parseFilters(query, parameterMap); //remove all entries before parse filters - query.overViews(currentContext().getOverView()); //over clause: after filters return query; } @@ -122,7 +121,7 @@ default void parseViews(RequestQueryBuilder query, Map paramet if(parameters.containsKey(VIEW)) { Stream.of(parameters.remove(VIEW)) .flatMap(c-> parseEntries(c).stream()) - .forEach(e-> currentContext().declareView(e.evalView(this))); + .forEach(e-> currentContext().declareView(e.evalView(this, query))); } } @@ -141,7 +140,7 @@ default void parseColumns(RequestQueryBuilder query, Map param if(!isEmpty(cols)) { Stream.of(cols) .flatMap(v-> parseEntries(v).stream()) - .map(e-> (TaggableColumn) e.evalColumn(this, true, true)) + .map(e-> (TaggableColumn) e.evalColumn(this, query, true)) .forEach(query::columns); } else { @@ -153,7 +152,7 @@ default void parseOrders(RequestQueryBuilder query, Map parame if(parameters.containsKey(ORDER)) { Stream.of(parameters.remove(ORDER)) .flatMap(c-> parseEntries(c).stream()) - .forEach(e-> query.orders(e.evalOrder(this))); + .forEach(e-> query.orders(e.evalOrder(this, query))); } } default void parseJoin(RequestQueryBuilder query, Map parameters) { @@ -176,7 +175,7 @@ default void parseFilters(RequestQueryBuilder query, Map param parameters.entrySet().stream() .flatMap(e-> { var re = parseEntry(e.getKey()); - return Stream.of(e.getValue()).map(v-> re.evalFilter(this, parseEntries(v))); + return Stream.of(e.getValue()).map(v-> re.evalFilter(this, query, parseEntries(v))); }) .forEach(query::filters); } diff --git a/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java b/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java index 61aebe0d..c068b74d 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java +++ b/src/main/java/org/usf/jquery/web/ViewDecoratorWrapper.java @@ -40,4 +40,9 @@ public CriteriaBuilder criteria(String name) { public JoinBuilder join(String name) { return view.join(name); } + + @Override + public PartitionBuilder partition(String name) { + return view.partition(name); + } } \ No newline at end of file From ce5f3d9ae620295a29ce550ede06246779b5cea6 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 29 Aug 2024 22:24:46 +0200 Subject: [PATCH 200/298] edit --- src/main/java/org/usf/jquery/core/ColumnFilterGroup.java | 4 ++-- .../org/usf/jquery/core/ComparisonExpressionGroup.java | 4 ++-- src/main/java/org/usf/jquery/core/DBColumn.java | 8 ++++---- src/main/java/org/usf/jquery/core/Utils.java | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index 77f6f6ca..50243947 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -3,7 +3,7 @@ import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; -import static org.usf.jquery.core.Utils.arrayJoin; +import static org.usf.jquery.core.Utils.appendLast; import java.util.stream.Stream; @@ -38,7 +38,7 @@ public boolean isAggregation() { @Override public DBFilter append(LogicalOperator op, DBFilter filter) { return operator == op - ? new ColumnFilterGroup(op, arrayJoin(filters, filter)) + ? new ColumnFilterGroup(op, appendLast(filters, filter)) : new ColumnFilterGroup(op, this, filter); } diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index 4a42c383..006d8d74 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -2,7 +2,7 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; -import static org.usf.jquery.core.Utils.arrayJoin; +import static org.usf.jquery.core.Utils.appendLast; import java.util.stream.Stream; @@ -37,7 +37,7 @@ public boolean isAggregation() { @Override public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) { return operator == op - ? new ComparisonExpressionGroup(op, arrayJoin(expressions, exp)) + ? new ComparisonExpressionGroup(op, appendLast(expressions, exp)) : new ComparisonExpressionGroup(op, this, exp); } diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 2a5b7271..e546d002 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -3,7 +3,7 @@ import static org.usf.jquery.core.Order.ASC; import static org.usf.jquery.core.Order.DESC; import static org.usf.jquery.core.QueryParameterBuilder.formatValue; -import static org.usf.jquery.core.Utils.arrayJoinFirst; +import static org.usf.jquery.core.Utils.appendFirst; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -130,12 +130,12 @@ default ColumnSingleFilter notNull() { @SuppressWarnings("unchecked") default ColumnSingleFilter in(T... arr) { - return Comparator.in().filter(arrayJoinFirst(arr, this)); + return Comparator.in().filter(appendFirst(arr, this)); } @SuppressWarnings("unchecked") default ColumnSingleFilter notIn(T... arr) { - return Comparator.notIn().filter(arrayJoinFirst(arr, this)); + return Comparator.notIn().filter(appendFirst(arr, this)); } default ColumnSingleFilter filter(ComparisonExpression exp) { @@ -253,7 +253,7 @@ default OperationColumn substring(int start, int end) { } default OperationColumn concat(Object... str) { - return Operator.concat().operation(arrayJoinFirst(str, this)); + return Operator.concat().operation(appendFirst(str, this)); } default OperationColumn lpad(int n, String value) { diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 1bc60e77..e0d5928e 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -58,13 +58,13 @@ public static String joinAndDelemit(String delemiter, String before, String return args.map(Object::toString).collect(joining(delemiter, before, after)); } - public static T[] arrayJoin(T[] arr, T o) { + public static T[] appendLast(T[] arr, T o) { var res = copyOf(arr, arr.length+1); res[arr.length] = o; return res; } - public static Object[] arrayJoinFirst(Object[] arr, Object o) { + public static Object[] appendFirst(Object[] arr, Object o) { var res = new Object[arr.length+1]; arraycopy(arr, 0, res, 1, arr.length); res[0] = o; From 92210be893ed029bfadb9b18d308415aafff1486 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 29 Aug 2024 22:35:19 +0200 Subject: [PATCH 201/298] edit --- src/main/java/org/usf/jquery/core/ComparisonExpression.java | 4 ++-- src/main/java/org/usf/jquery/core/DBFilter.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpression.java b/src/main/java/org/usf/jquery/core/ComparisonExpression.java index 006f81bb..eabd1cc0 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpression.java @@ -11,13 +11,13 @@ */ public interface ComparisonExpression extends DBExpression, Nested, Chainable { + String sql(QueryParameterBuilder builder, Object left); // do change method order + @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(1, args, ComparisonExpression.class::getSimpleName); return sql(builder, args[0]); } - - String sql(QueryParameterBuilder builder, Object left); // do change method order static ComparisonExpression eq(Object right) { return Comparator.eq().expression(right); diff --git a/src/main/java/org/usf/jquery/core/DBFilter.java b/src/main/java/org/usf/jquery/core/DBFilter.java index 9aaabcc4..542e8f9a 100644 --- a/src/main/java/org/usf/jquery/core/DBFilter.java +++ b/src/main/java/org/usf/jquery/core/DBFilter.java @@ -9,14 +9,14 @@ */ @FunctionalInterface public interface DBFilter extends DBObject, Nested, Chainable { + + String sql(QueryParameterBuilder builder); @Override default String sql(QueryParameterBuilder builder, Object[] args) { requireNoArgs(args, DBFilter.class::getSimpleName); return sql(builder); } - - String sql(QueryParameterBuilder builder); default DBFilter append(LogicalOperator op, DBFilter filter) { throw new UnsupportedOperationException(); //explicitly overridden From d37711774c6c79803ec72a1cabca9fef32bc3d05 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 29 Aug 2024 22:42:35 +0200 Subject: [PATCH 202/298] edit --- src/main/java/org/usf/jquery/core/QueryView.java | 2 +- src/main/java/org/usf/jquery/core/RangeComparator.java | 3 ++- src/main/java/org/usf/jquery/core/TypedComparator.java | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index aa07f500..4a067fd0 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -14,7 +14,7 @@ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class QueryView implements DBView { - @Getter // remove this + @Getter private final RequestQueryBuilder builder; @Override diff --git a/src/main/java/org/usf/jquery/core/RangeComparator.java b/src/main/java/org/usf/jquery/core/RangeComparator.java index 06d9355e..90c199ca 100644 --- a/src/main/java/org/usf/jquery/core/RangeComparator.java +++ b/src/main/java/org/usf/jquery/core/RangeComparator.java @@ -14,6 +14,7 @@ public interface RangeComparator extends Comparator { default String sql(QueryParameterBuilder builder, Object[] args) { requireNArgs(3, args, RangeComparator.class::getSimpleName); return builder.appendParameter(args[0]) + " " + id() + - " " + builder.appendParameter(args[1]) + " AND " + builder.appendParameter(args[2]); + " " + builder.appendParameter(args[1]) + + " AND " + builder.appendParameter(args[2]); } } diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 23c0602d..94e5c600 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -34,7 +34,7 @@ public ComparisonExpression expression(Object... right) { public ColumnSingleFilter filter(Object... args) { try { return comparator.filter(parameterSet.assertArguments(args)); - } catch (BadArgumentException e) { //TODO message + } catch (BadArgumentException e) { throw badArgumentsException(toString(), comparator.id() + joinAndDelemitArray(SCOMA, "(", ")", args), e); } } From 4817878634ee453aef6878f6b51c3e22ba39bc96 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 30 Aug 2024 11:36:51 +0200 Subject: [PATCH 203/298] edit --- .../java/org/usf/jquery/core/CombinedOperator.java | 2 +- .../usf/jquery/core/ComparisonExpressionGroup.java | 4 ++-- src/main/java/org/usf/jquery/core/Mappers.java | 5 +++++ src/main/java/org/usf/jquery/core/Order.java | 5 +++++ .../org/usf/jquery/core/QueryParameterBuilder.java | 2 +- .../org/usf/jquery/core/RequestQueryBuilder.java | 2 +- src/main/java/org/usf/jquery/core/ViewJoin.java | 14 +++++--------- 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CombinedOperator.java b/src/main/java/org/usf/jquery/core/CombinedOperator.java index 1c233e4d..79300396 100644 --- a/src/main/java/org/usf/jquery/core/CombinedOperator.java +++ b/src/main/java/org/usf/jquery/core/CombinedOperator.java @@ -26,6 +26,6 @@ default String id() { @Override default String sql(QueryParameterBuilder builder, Object[] args) { - throw new UnsupportedOperationException("sql"); + throw new UnsupportedOperationException("CombinedOperator::sql"); } } diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index 006d8d74..cf227915 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -24,9 +24,9 @@ public final class ComparisonExpressionGroup implements ComparisonExpression { @Override public String sql(QueryParameterBuilder builder, Object operand) { - return "(" + Stream.of(expressions) + return Stream.of(expressions) .map(o-> o.sql(builder, operand)) - .collect(joining(operator.sql())) + ")"; + .collect(joining(operator.sql(), "(", ")")); } @Override diff --git a/src/main/java/org/usf/jquery/core/Mappers.java b/src/main/java/org/usf/jquery/core/Mappers.java index 3e8d393e..a4c82b6c 100644 --- a/src/main/java/org/usf/jquery/core/Mappers.java +++ b/src/main/java/org/usf/jquery/core/Mappers.java @@ -10,6 +10,11 @@ import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +/** + * + * @author u$f + * + */ @Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class Mappers { diff --git a/src/main/java/org/usf/jquery/core/Order.java b/src/main/java/org/usf/jquery/core/Order.java index 33518d42..c4b6d670 100644 --- a/src/main/java/org/usf/jquery/core/Order.java +++ b/src/main/java/org/usf/jquery/core/Order.java @@ -1,5 +1,10 @@ package org.usf.jquery.core; +/** + * + * @author u$f + * + */ public enum Order { ASC, DESC; diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index abf9f7d2..44b0ddcd 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -26,12 +26,12 @@ * @author u$f * */ +@Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) public final class QueryParameterBuilder { private static final String P_ARG = "?"; - @Getter private final String schema; private final String vPrefix; private final List args; //dynamic flag diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 77e4031f..6054cdc4 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -158,7 +158,7 @@ void select(SqlStringBuilder sb, QueryParameterBuilder pb){ } void from(SqlStringBuilder sb, QueryParameterBuilder pb) { - var excludes = joins.stream().map(ViewJoin::getView).toList(); + var excludes = joins.stream().map(ViewJoin::getView).map(v-> pb.getOverView().getOrDefault(v, v)).toList(); var views = pb.views().stream().filter(not(excludes::contains)).toList(); //do not remove views if(!views.isEmpty()) { sb.append(" FROM ").appendEach(views, SCOMA, v-> v.sqlWithTag(pb)); diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index 8ef05761..7286add6 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -20,7 +20,7 @@ */ @Getter @RequiredArgsConstructor -public class ViewJoin implements DBObject { +public final class ViewJoin implements DBObject { private final JoinType joinType; private final DBView view; @@ -39,22 +39,18 @@ public String sql(QueryParameterBuilder builder) { } public static ViewJoin innerJoin(DBView view, DBFilter... filters) { - return join(INNER, view, filters); + return new ViewJoin(INNER, view, filters); } public static ViewJoin leftJoin(DBView view, DBFilter... filters) { - return join(LEFT, view, filters); + return new ViewJoin(LEFT, view, filters); } public static ViewJoin rightJoin(DBView view, DBFilter... filters) { - return join(RIGHT, view, filters); + return new ViewJoin(RIGHT, view, filters); } public static ViewJoin fullJoin(DBView view, DBFilter... filters) { - return join(FULL, view, filters); - } - - public static ViewJoin join(JoinType jt, DBView view, DBFilter... filters) { - return new ViewJoin(jt, view, filters); + return new ViewJoin(FULL, view, filters); } } From 91039b0bb790ebe4b29b934e7564130afad4282f Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 30 Aug 2024 11:53:30 +0200 Subject: [PATCH 204/298] edit --- .../java/org/usf/jquery/web/ChainableCriteria.java | 3 ++- src/main/java/org/usf/jquery/web/CriteriaBuilder.java | 11 ++++++----- .../java/org/usf/jquery/web/RequestEntryChain.java | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ChainableCriteria.java b/src/main/java/org/usf/jquery/web/ChainableCriteria.java index 4e9fbef8..d0590927 100644 --- a/src/main/java/org/usf/jquery/web/ChainableCriteria.java +++ b/src/main/java/org/usf/jquery/web/ChainableCriteria.java @@ -1,6 +1,7 @@ package org.usf.jquery.web; import org.usf.jquery.core.Chainable; +import org.usf.jquery.core.QueryContext; /** * @@ -10,5 +11,5 @@ @FunctionalInterface public interface ChainableCriteria> { - T criteria(String arg); + T criteria(QueryContext ctx, String arg); } diff --git a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java index eafa03af..e783a6f7 100644 --- a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java +++ b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java @@ -8,6 +8,7 @@ import org.usf.jquery.core.Chainable; import org.usf.jquery.core.LogicalOperator; +import org.usf.jquery.core.QueryContext; /** * @@ -17,10 +18,10 @@ @FunctionalInterface public interface CriteriaBuilder> { - T build(String... arg); + T build(QueryContext ctx, String... arg); static > CriteriaBuilder singleArg(ChainableCriteria cr){ - return args-> cr.criteria(isEmpty(args) ? null : requireNArgs(1, args, ()-> "single arg criteria")[0]); + return (ctx, args)-> cr.criteria(ctx, isEmpty(args) ? null : requireNArgs(1, args, ()-> "single arg criteria")[0]); } static > CriteriaBuilder multiArgs(ChainableCriteria cr){ @@ -28,8 +29,8 @@ static > CriteriaBuilder multiArgs(ChainableCriteria> CriteriaBuilder multiArgs(LogicalOperator op, ChainableCriteria cr){ - return args-> isEmpty(args) - ? cr.criteria(null) - : Stream.of(args).map(cr::criteria).reduce(op::combine).orElseThrow(); + return (ctx, args)-> isEmpty(args) + ? cr.criteria(ctx, null) + : Stream.of(args).map(c-> cr.criteria(ctx, c)).reduce(op::combine).orElseThrow(); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index f3065594..0c625e8f 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -271,7 +271,7 @@ Optional viewCriteria(ViewDecorator td, QueryContext ctx, List Date: Fri, 30 Aug 2024 12:42:40 +0200 Subject: [PATCH 205/298] edit --- src/main/java/org/usf/jquery/core/DBView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 2e2f70db..6ba1d051 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -22,7 +22,7 @@ default String sql(QueryParameterBuilder builder, Object[] args) { default String sqlWithTag(QueryParameterBuilder builder) { var tag = builder.view(this); - var sql = sql(builder); + var sql = builder.getOverView().getOrDefault(this, this).sql(builder); //!important return isNull(tag) ? sql : sql + SPACE + tag; } } From 621e1f28880101218a9733cdbc5c7a3121136cdc Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 30 Aug 2024 14:11:20 +0200 Subject: [PATCH 206/298] edit --- src/main/java/org/usf/jquery/core/DBColumn.java | 2 +- src/main/java/org/usf/jquery/core/DBView.java | 4 ++-- .../java/org/usf/jquery/core/QueryParameterBuilder.java | 8 ++++++-- .../java/org/usf/jquery/core/RequestQueryBuilder.java | 2 +- src/main/java/org/usf/jquery/core/ViewColumn.java | 8 ++++---- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index e546d002..71a1b7c8 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -438,7 +438,7 @@ static DBColumn column(@NonNull String value) { static TaggableColumn allColumns(@NonNull DBView view) { return ((DBColumn) b-> { - b.view(view); + b.viewAlias(view); return "*"; //avoid view.* as "" }).as(null); } diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 6ba1d051..70e26e5e 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -21,8 +21,8 @@ default String sql(QueryParameterBuilder builder, Object[] args) { } default String sqlWithTag(QueryParameterBuilder builder) { - var tag = builder.view(this); - var sql = builder.getOverView().getOrDefault(this, this).sql(builder); //!important + var tag = builder.viewAlias(this); + var sql = builder.viewOverload(this).sql(builder); //!important return isNull(tag) ? sql : sql + SPACE + tag; } } diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java index 44b0ddcd..c9510309 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java @@ -26,12 +26,12 @@ * @author u$f * */ -@Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) public final class QueryParameterBuilder { private static final String P_ARG = "?"; + @Getter private final String schema; private final String vPrefix; private final List args; //dynamic flag @@ -39,7 +39,7 @@ public final class QueryParameterBuilder { private final List views; //indexed view private final Map overView; - public String view(DBView view) { + public String viewAlias(DBView view) { view = overView.getOrDefault(view, view); var idx = views.indexOf(view); if(idx < 0) { @@ -49,6 +49,10 @@ public String view(DBView view) { return vPrefix + (idx+1); } + public DBView viewOverload(DBView view) { + return overView.getOrDefault(view, view); + } + public List views(){ return views; } diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 6054cdc4..20547c61 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -158,7 +158,7 @@ void select(SqlStringBuilder sb, QueryParameterBuilder pb){ } void from(SqlStringBuilder sb, QueryParameterBuilder pb) { - var excludes = joins.stream().map(ViewJoin::getView).map(v-> pb.getOverView().getOrDefault(v, v)).toList(); + var excludes = joins.stream().map(ViewJoin::getView).map(pb::viewOverload).toList(); var views = pb.views().stream().filter(not(excludes::contains)).toList(); //do not remove views if(!views.isEmpty()) { sb.append(" FROM ").appendEach(views, SCOMA, v-> v.sqlWithTag(pb)); diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index 775cc312..b3e96733 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -16,10 +16,10 @@ @RequiredArgsConstructor public final class ViewColumn implements TaggableColumn { - private final DBView view; + private final DBView view; //optional private final String name; - private final String tag; - private final JDBCType type; + private final String tag; //optional + private final JDBCType type; //optional public ViewColumn(String name, String tag) { this(null, name, tag, null); @@ -31,7 +31,7 @@ public ViewColumn(DBView view, String name, String tag) { @Override public String sql(QueryParameterBuilder arg) { - return nonNull(view) ? member(arg.view(view), name) : name; + return nonNull(view) ? member(arg.viewAlias(view), name) : name; } @Override From 04ff171247b3d54bb2da68bd6db65f497da82465 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 30 Aug 2024 23:18:07 +0200 Subject: [PATCH 207/298] edit --- .../org/usf/jquery/web/ArgumentParsers.java | 7 ++- .../java/org/usf/jquery/web/JoinBuilder.java | 3 +- .../org/usf/jquery/web/PartitionBuilder.java | 3 +- .../org/usf/jquery/web/QueryDecorator.java | 6 ++- .../org/usf/jquery/web/RequestEntryChain.java | 48 ++++++++++++------- .../org/usf/jquery/web/ViewDecorator.java | 2 +- 6 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 7704ef35..1c27be74 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -33,7 +33,6 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; -import lombok.NonNull; import lombok.extern.slf4j.Slf4j; /** @@ -80,7 +79,7 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, QueryConte throw cannotParseEntryException("entry", entry, exList.size() == 1 ? exList.get(0) : null); } - public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { + public static JDBCArgumentParser jdbcArgParser(JDBCType type) { switch (type) { case BOOLEAN, BIT: return Boolean::parseBoolean; case TINYINT: return Byte::parseByte; @@ -100,7 +99,7 @@ public static JDBCArgumentParser jdbcArgParser(@NonNull JDBCType type) { } } - public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type, QueryContext ctx) { + public static JavaArgumentParser jqueryArgParser(JQueryType type, QueryContext ctx) { switch (type) { case QUERY_COLUMN: return (e,v)-> e.evalQueryColumn(v, ctx); case NAMED_COLUMN: return (e,v)-> e.evalColumn(v, ctx, true); //separate query context @@ -108,7 +107,7 @@ public static JavaArgumentParser jqueryArgParser(@NonNull JQueryType type, Query case FILTER: return (e,v)-> e.evalFilter(v, ctx); case ORDER: return (e,v)-> e.evalOrder(v, ctx); case QUERY: return (e,v)-> e.evalQuery(v, ctx); - case JOIN: return RequestEntryChain::evalJoin; + case JOIN: return (e,v)-> e.evalJoin(v, ctx); case PARTITION: return (e,v)-> e.evalPartition(v, ctx); default: throw unsupportedTypeException(type); } diff --git a/src/main/java/org/usf/jquery/web/JoinBuilder.java b/src/main/java/org/usf/jquery/web/JoinBuilder.java index b0131659..5e5e046a 100644 --- a/src/main/java/org/usf/jquery/web/JoinBuilder.java +++ b/src/main/java/org/usf/jquery/web/JoinBuilder.java @@ -1,5 +1,6 @@ package org.usf.jquery.web; +import org.usf.jquery.core.QueryContext; import org.usf.jquery.core.ViewJoin; /** @@ -10,6 +11,6 @@ @FunctionalInterface public interface JoinBuilder { - ViewJoin[] build(); + ViewJoin[] build(QueryContext qc); } diff --git a/src/main/java/org/usf/jquery/web/PartitionBuilder.java b/src/main/java/org/usf/jquery/web/PartitionBuilder.java index aa774ce0..0d819d7b 100644 --- a/src/main/java/org/usf/jquery/web/PartitionBuilder.java +++ b/src/main/java/org/usf/jquery/web/PartitionBuilder.java @@ -1,6 +1,7 @@ package org.usf.jquery.web; import org.usf.jquery.core.Partition; +import org.usf.jquery.core.QueryContext; /** * @@ -10,6 +11,6 @@ @FunctionalInterface public interface PartitionBuilder { - Partition build(); + Partition build(QueryContext ctx); } diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 8ddd2cb5..23380204 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -1,5 +1,7 @@ package org.usf.jquery.web; +import static org.usf.jquery.web.ResourceAccessException.undeclaredResouceException; + import org.usf.jquery.core.DBView; import org.usf.jquery.core.QueryView; import org.usf.jquery.core.TaggableColumn; @@ -34,7 +36,7 @@ public DBView view() { public TaggableColumn column(String id) { return query.getBuilder().declaredColumn(id) .map(c-> new ViewColumn(query, c.tagname(), c.tagname(), c.getType())) - .orElse(null); + .orElseThrow(()-> undeclaredResouceException(id, identity())); } @Override @@ -58,6 +60,6 @@ public ViewMetadata metadata() { } private UnsupportedOperationException unsupportedOperationException(String method) { - return new UnsupportedOperationException(identity() + "." + method); + return new UnsupportedOperationException(identity() + "::" + method); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 0c625e8f..d9cc9377 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -129,7 +129,7 @@ Optional evalQuery(ViewDecorator td, QueryContext ctx, boolean r case DISTINCT: e.requireNoArgs(); q.distinct(); break; case FILTER: q.filters(e.filterVarargs(td, ctx)); break; case ORDER: q.orders(e.oderVarargs(td, ctx)); break; - case JOIN: q.joins(e.evalJoin(td)); break; + case JOIN: q.joins(e.evalJoin(td, ctx)); break; case OFFSET: q.offset((int)e.toOneArg(td, ctx, INTEGER)); break; case FETCH: q.fetch((int)e.toOneArg(td, ctx, INTEGER)); break; default: throw badEntrySyntaxException(joinArray("|", DISTINCT, FILTER, ORDER, JOIN, OFFSET, FETCH), e.value); @@ -145,7 +145,7 @@ Optional evalQuery(ViewDecorator td, QueryContext ctx, boolean r } //[view.]joiner - public ViewJoin[] evalJoin(ViewDecorator vd) { + public ViewJoin[] evalJoin(ViewDecorator vd, QueryContext ctx) { var e = this; if(hasNext()) { vd = currentContext().lookupRegisteredView(value) @@ -155,7 +155,7 @@ public ViewJoin[] evalJoin(ViewDecorator vd) { var join = vd.join(e.value); if(nonNull(join)) { e.requireNoArgs().requireNoNext(); //check args & next only if joiner exists - return requireNonNull(join.build(), vd.identity() + ".join: " + e); + return requireNonNull(join.build(ctx), vd.identity() + ".join: " + e); } throw noSuchResourceException(vd.identity() + ".join", e.value); } @@ -173,7 +173,7 @@ public Partition evalPartition(ViewDecorator vd, QueryContext ctx) { var par = vd.partition(e.value); if(nonNull(par)) { e.requireNoArgs().requireNoNext(); - return requireNonNull(par.build(), vd.identity() + ".partition: " + e); + return requireNonNull(par.build(ctx), vd.identity() + ".partition: " + e); } if(e == this) { // not view var res = evalPartition2(vd, ctx); @@ -349,25 +349,32 @@ private static DBColumn windowColumn(ViewDecorator vd, QueryContext ctx, DBColum private Optional lookupResource(ViewDecorator td, QueryContext ctx) { //do not change priority if(hasNext()) { //view.id == column.id - var rc = currentContext().lookupRegisteredView(value) - .flatMap(v-> next.lookupViewResource(v, ctx, RequestEntryChain::isWindowFunction)); + var rc = currentContext().lookupRegisteredView(value); if(rc.isPresent()) { - requireNoArgs(); //view takes no args - return rc; + var v = rc.get(); + var res = next.lookupQueryResource(v) //declared query first + .or(()-> next.lookupViewResource(td, ctx, RequestEntryChain::isWindowFunction)); + if(res.isPresent()) { + requireNoArgs(); + return res; + } } } - return ctx.declaredColumn(value) //declared column first - .map(c-> new ViewResource(null, null, requireNoArgs(), c)) + return ctx.declaredColumn(value) + .map(c-> new ViewResource(null, null, requireNoArgs(), c)) //declared column first .or(()-> lookupViewResource(td, ctx, fn-> true)); //registered column } + + private Optional lookupQueryResource(ViewDecorator vd) { + return vd instanceof QueryDecorator qd + ? Optional.of(new ViewResource(qd, null, requireNoArgs(), qd.column(value))) + : empty(); + } private Optional lookupViewResource(ViewDecorator td, QueryContext ctx, Predicate pre) { - var res = td instanceof QueryDecorator qd - ? ofNullable(qd.column(value)) - .map(c-> new ViewResource(td, null, requireNoArgs(), c)) - : currentContext().lookupRegisteredColumn(value) - .map(cd-> new ViewResource(td, cd, requireNoArgs(), td.column(cd))); - return res.or(()-> lookupOperation(td, ctx, null, pre).map(col-> new ViewResource(td, null, this, col))); + return currentContext().lookupRegisteredColumn(value) + .flatMap(cd-> declaredColumn(td, cd).map(c-> new ViewResource(td, cd, requireNoArgs(), c))) + .or(()-> lookupOperation(td, ctx, null, pre).map(col-> new ViewResource(td, null, this, col))); } private Optional lookupOperation(ViewDecorator vd, QueryContext ctx, DBColumn col, Predicate opr) { @@ -379,6 +386,15 @@ private Optional lookupOperation(ViewDecorator vd, QueryContext return fn.operation(toArgs(vd, ctx, c, fn.getParameterSet())); }); } + + static Optional declaredColumn(ViewDecorator td, ColumnDecorator cd) { + try { + return Optional.of(td.column(cd)); + } + catch(Exception e) { + return Optional.empty(); + } + } private TaggableColumn[] taggableVarargs(ViewDecorator td, QueryContext ctx) { return (TaggableColumn[]) typeVarargs(td, ctx, JQueryType.NAMED_COLUMN); diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 6aa1d602..7c8beab0 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -159,7 +159,7 @@ default void parseJoin(RequestQueryBuilder query, Map paramete if(parameters.containsKey(JOIN)) { Stream.of(parameters.remove(JOIN)) .flatMap(c-> parseEntries(c).stream()) - .forEach(e-> query.joins(e.evalJoin(this))); + .forEach(e-> query.joins(e.evalJoin(this, query))); } } From 2fa90d6fa0244ce4d7a35e9360a06324c03f1166 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 2 Sep 2024 10:15:32 +0200 Subject: [PATCH 208/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 6 ++-- .../org/usf/jquery/web/RequestEntryChain.java | 31 ++++++++++--------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 71a1b7c8..8508a094 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -3,6 +3,7 @@ import static org.usf.jquery.core.Order.ASC; import static org.usf.jquery.core.Order.DESC; import static org.usf.jquery.core.QueryParameterBuilder.formatValue; +import static org.usf.jquery.core.SqlStringBuilder.member; import static org.usf.jquery.core.Utils.appendFirst; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -437,10 +438,7 @@ static DBColumn column(@NonNull String value) { } static TaggableColumn allColumns(@NonNull DBView view) { - return ((DBColumn) b-> { - b.viewAlias(view); - return "*"; //avoid view.* as "" - }).as(null); + return ((DBColumn) b-> member(b.viewAlias(view), "*")).as(null); } static DBColumn constant(Object value) { diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index d9cc9377..3a814c36 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -9,7 +9,6 @@ import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNullElse; import static java.util.Optional.empty; -import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Comparator.eq; import static org.usf.jquery.core.Comparator.in; @@ -325,7 +324,7 @@ private Optional chainColumnOperations(ViewDecorator td, QueryCont return lookupResource(td, ctx).map(r-> { var e = r.entry.next; while(nonNull(e)) { //chain until !operator - var o = e.lookupOperation(td, ctx, r.col, fn-> true); //accept all + var o = e.lookupOperation(td, ctx, r.col, null, fn-> true); //accept all if(o.isEmpty()) { break; } @@ -347,13 +346,12 @@ private static DBColumn windowColumn(ViewDecorator vd, QueryContext ctx, DBColum return new ViewColumn(v, doubleQuote(tag), null, col.getType()); } - private Optional lookupResource(ViewDecorator td, QueryContext ctx) { //do not change priority + private Optional lookupResource(ViewDecorator vd, QueryContext ctx) { //do not change priority if(hasNext()) { //view.id == column.id var rc = currentContext().lookupRegisteredView(value); if(rc.isPresent()) { - var v = rc.get(); - var res = next.lookupQueryResource(v) //declared query first - .or(()-> next.lookupViewResource(td, ctx, RequestEntryChain::isWindowFunction)); + var res = next.lookupQueryResource(rc.get()) //declared query first + .or(()-> next.lookupViewResource(vd, ctx, rc.get(), RequestEntryChain::isWindowFunction)); if(res.isPresent()) { requireNoArgs(); return res; @@ -362,26 +360,29 @@ private Optional lookupResource(ViewDecorator td, QueryContext ctx } return ctx.declaredColumn(value) .map(c-> new ViewResource(null, null, requireNoArgs(), c)) //declared column first - .or(()-> lookupViewResource(td, ctx, fn-> true)); //registered column + .or(()-> lookupViewResource(vd, ctx, null, fn-> true)); //registered column } private Optional lookupQueryResource(ViewDecorator vd) { - return vd instanceof QueryDecorator qd - ? Optional.of(new ViewResource(qd, null, requireNoArgs(), qd.column(value))) - : empty(); + if(vd instanceof QueryDecorator qd) { + try { + return Optional.of(new ViewResource(qd, null, requireNoArgs(), qd.column(value))); + } catch (Exception e) {/*do not throw exception*/} + } + return empty(); } - private Optional lookupViewResource(ViewDecorator td, QueryContext ctx, Predicate pre) { + private Optional lookupViewResource(ViewDecorator td, QueryContext ctx, ViewDecorator current, Predicate pre) { return currentContext().lookupRegisteredColumn(value) .flatMap(cd-> declaredColumn(td, cd).map(c-> new ViewResource(td, cd, requireNoArgs(), c))) - .or(()-> lookupOperation(td, ctx, null, pre).map(col-> new ViewResource(td, null, this, col))); + .or(()-> lookupOperation(td, ctx, null, current, pre).map(col-> new ViewResource(td, null, this, col))); } - private Optional lookupOperation(ViewDecorator vd, QueryContext ctx, DBColumn col, Predicate opr) { + private Optional lookupOperation(ViewDecorator vd, QueryContext ctx, DBColumn col, ViewDecorator current, Predicate opr) { return lookupOperator(value).filter(opr).map(fn-> { var c = col; if(isNull(c) && isEmpty(args) && isCountFunction(fn)) { - c = allColumns(vd.view()); + c = allColumns(requireNonNullElse(current, vd).view()); } return fn.operation(toArgs(vd, ctx, c, fn.getParameterSet())); }); @@ -392,7 +393,7 @@ static Optional declaredColumn(ViewDecorator td, ColumnDecorator return Optional.of(td.column(cd)); } catch(Exception e) { - return Optional.empty(); + return empty(); } } From 8f851b0269540a986e5d14cf4b9ff15c8c4f352b Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 2 Sep 2024 10:39:45 +0200 Subject: [PATCH 209/298] edit --- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 3a814c36..621c88f3 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -373,8 +373,9 @@ private Optional lookupQueryResource(ViewDecorator vd) { } private Optional lookupViewResource(ViewDecorator td, QueryContext ctx, ViewDecorator current, Predicate pre) { + var cur = requireNonNullElse(current, td); return currentContext().lookupRegisteredColumn(value) - .flatMap(cd-> declaredColumn(td, cd).map(c-> new ViewResource(td, cd, requireNoArgs(), c))) + .flatMap(cd-> declaredColumn(cur, cd).map(c-> new ViewResource(cur, cd, requireNoArgs(), c))) .or(()-> lookupOperation(td, ctx, null, current, pre).map(col-> new ViewResource(td, null, this, col))); } From 920b2d53b8b912f67fb4862b4d50392774f43247 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 2 Sep 2024 11:14:18 +0200 Subject: [PATCH 210/298] editr --- src/main/java/org/usf/jquery/core/QueryContext.java | 4 ---- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java index 4416d0f7..f51d8d0d 100644 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -20,9 +20,5 @@ default QueryView overView(DBView view) { return overView(view, ()-> new RequestQueryBuilder().columns(allColumns(view)).asView()); } - default ViewColumn overView(DBView view, TaggableColumn column) { - overView(view).getBuilder().columns(column); - return new ViewColumn(view, column.tagname(), null, column.getType()); - } //sub query filters, orders, ... } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 621c88f3..e961b9c7 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -376,7 +376,7 @@ private Optional lookupViewResource(ViewDecorator td, QueryContext var cur = requireNonNullElse(current, td); return currentContext().lookupRegisteredColumn(value) .flatMap(cd-> declaredColumn(cur, cd).map(c-> new ViewResource(cur, cd, requireNoArgs(), c))) - .or(()-> lookupOperation(td, ctx, null, current, pre).map(col-> new ViewResource(td, null, this, col))); + .or(()-> lookupOperation(td, ctx, null, current, pre).map(col-> new ViewResource(cur, null, this, col))); } private Optional lookupOperation(ViewDecorator vd, QueryContext ctx, DBColumn col, ViewDecorator current, Predicate opr) { From 12bab79ba9687d0c83c916437aae1d2c9f8dfea6 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 2 Sep 2024 23:01:28 +0200 Subject: [PATCH 211/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 3 +- .../org/usf/jquery/core/QueryContext.java | 2 +- .../usf/jquery/core/RequestQueryBuilder.java | 7 ++- .../org/usf/jquery/web/QueryDecorator.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 56 ++++++++++++------- 5 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 8508a094..3f007e84 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -3,7 +3,6 @@ import static org.usf.jquery.core.Order.ASC; import static org.usf.jquery.core.Order.DESC; import static org.usf.jquery.core.QueryParameterBuilder.formatValue; -import static org.usf.jquery.core.SqlStringBuilder.member; import static org.usf.jquery.core.Utils.appendFirst; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -438,7 +437,7 @@ static DBColumn column(@NonNull String value) { } static TaggableColumn allColumns(@NonNull DBView view) { - return ((DBColumn) b-> member(b.viewAlias(view), "*")).as(null); + return new ViewColumn(view, "*", null) ; //TODO check this } static DBColumn constant(Object value) { diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java index f51d8d0d..b32c2c42 100644 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -12,7 +12,7 @@ */ public interface QueryContext { - Optional declaredColumn(String name); + Optional lookupDeclaredColumn(String name); QueryView overView(DBView view, Supplier supp); diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 20547c61..16a9ff2c 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -55,8 +55,11 @@ public RequestQueryBuilder(Database target) { } @Override - public Optional declaredColumn(String name) { - return columns.stream().filter(c-> name.equals(c.tagname())).findAny(); + public Optional lookupDeclaredColumn(String name) { + return columns.stream() + .filter(NamedColumn.class::isInstance) + .filter(c-> name.equals(c.tagname())) + .findAny(); } @Override diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 23380204..d865b817 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -34,7 +34,7 @@ public DBView view() { } public TaggableColumn column(String id) { - return query.getBuilder().declaredColumn(id) + return query.getBuilder().lookupDeclaredColumn(id) .map(c-> new ViewColumn(query, c.tagname(), c.tagname(), c.getType())) .orElseThrow(()-> undeclaredResouceException(id, identity())); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index e961b9c7..90d3652d 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -9,6 +9,7 @@ import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNullElse; import static java.util.Optional.empty; +import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Comparator.eq; import static org.usf.jquery.core.Comparator.in; @@ -46,6 +47,7 @@ import java.util.stream.Stream; import org.usf.jquery.core.AggregateFunction; +import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBObject; @@ -322,19 +324,22 @@ DBFilter chainComparator(ViewDecorator td, QueryContext ctx, DBFilter f) { private Optional chainColumnOperations(ViewDecorator td, QueryContext ctx, boolean filter) { return lookupResource(td, ctx).map(r-> { - var e = r.entry.next; - while(nonNull(e)) { //chain until !operator - var o = e.lookupOperation(td, ctx, r.col, null, fn-> true); //accept all - if(o.isEmpty()) { - break; + if(nonNull(r.col)) { + var e = r.entry.next; + while(nonNull(e)) { //chain until !operator + var o = e.lookupOperation(td, ctx, r.col, null, fn-> true); //accept all + if(o.isEmpty()) { + break; + } + r.cd = null; + r.entry = e; + r.col = filter && "over".equals(e.value) + ? windowColumn(r.vd, ctx, o.get()) + : o.get(); + e = e.next; } - r.cd = null; - r.entry = e; - r.col = filter && "over".equals(e.value) - ? windowColumn(r.vd, ctx, o.get()) - : o.get(); - e = e.next; - } + + } //else view criteria return r; }); } @@ -358,15 +363,15 @@ private Optional lookupResource(ViewDecorator vd, QueryContext ctx } } } - return ctx.declaredColumn(value) - .map(c-> new ViewResource(null, null, requireNoArgs(), c)) //declared column first + return ctx.lookupDeclaredColumn(value).map(c-> new ViewResource(requireNoArgs(), null, null, c)) //declared column first .or(()-> lookupViewResource(vd, ctx, null, fn-> true)); //registered column } private Optional lookupQueryResource(ViewDecorator vd) { if(vd instanceof QueryDecorator qd) { try { - return Optional.of(new ViewResource(qd, null, requireNoArgs(), qd.column(value))); + var col = qd.column(value); + return Optional.of(new ViewResource(requireNoArgs(), qd, null, col)); } catch (Exception e) {/*do not throw exception*/} } return empty(); @@ -374,9 +379,11 @@ private Optional lookupQueryResource(ViewDecorator vd) { private Optional lookupViewResource(ViewDecorator td, QueryContext ctx, ViewDecorator current, Predicate pre) { var cur = requireNonNullElse(current, td); - return currentContext().lookupRegisteredColumn(value) - .flatMap(cd-> declaredColumn(cur, cd).map(c-> new ViewResource(cur, cd, requireNoArgs(), c))) - .or(()-> lookupOperation(td, ctx, null, current, pre).map(col-> new ViewResource(cur, null, this, col))); + return lookupOperation(td, ctx, null, current, pre) + .map(col-> new ViewResource(this, cur, null, col)) + .or(()-> currentContext().lookupRegisteredColumn(value) + .flatMap(cd-> declaredColumn(cur, cd).map(c-> new ViewResource(requireNoArgs(), cur, cd, c)))) + .or(()-> ofNullable(cur.criteria(value)).map(crt-> new ViewResource(this, cur, crt))); } private Optional lookupOperation(ViewDecorator vd, QueryContext ctx, DBColumn col, ViewDecorator current, Predicate opr) { @@ -541,16 +548,23 @@ static EntrySyntaxException expectedEntryTagException(RequestEntryChain e) { @AllArgsConstructor static final class ViewResource { + private RequestEntryChain entry; private ViewDecorator vd; //optional private ColumnDecorator cd; //optional - private RequestEntryChain entry; private DBColumn col; - //chained !? + private CriteriaBuilder viewCrt; + public ViewResource(RequestEntryChain entry, ViewDecorator vd, ColumnDecorator cd, DBColumn col) { + this(entry, vd, cd, col, null); + } + + public ViewResource(RequestEntryChain entry, ViewDecorator vd, CriteriaBuilder viewCrt) { + this(entry, vd, null, null, viewCrt); + } + @Override public String toString() { return vd + "." + cd + " => " + entry.toString(); } } - } \ No newline at end of file From a98d0582358230e29ab5e250d5a1c93d6310e9fe Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 3 Sep 2024 16:12:40 +0200 Subject: [PATCH 212/298] edit --- .../org/usf/jquery/web/RequestEntryChain.java | 121 +++++++++--------- 1 file changed, 60 insertions(+), 61 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 90d3652d..d2df27f6 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -10,6 +10,7 @@ import static java.util.Objects.requireNonNullElse; import static java.util.Optional.empty; import static java.util.Optional.ofNullable; +import static java.util.function.Predicate.not; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Comparator.eq; import static org.usf.jquery.core.Comparator.in; @@ -209,6 +210,7 @@ private Optional evalPartition2(ViewDecorator vd, QueryContext ctx) { //[view.]column[.operator]* public DBColumn evalColumn(ViewDecorator td, QueryContext ctx, boolean requireTag) { var r = chainColumnOperations(td, ctx, false) + .filter(not(ViewResource::isCriteria)) //!criteria .orElseThrow(()-> noSuchViewColumnException(this)); r.entry.requireNoNext(); //check next only if column exists if(nonNull(r.entry.tag)) { @@ -223,6 +225,7 @@ public DBColumn evalColumn(ViewDecorator td, QueryContext ctx, boolean requireTa //[view.]column[.operator]*[.order] public DBOrder evalOrder(ViewDecorator td, QueryContext ctx) { var r = chainColumnOperations(td, ctx, false) + .filter(not(ViewResource::isCriteria)) //!criteria .orElseThrow(()-> noSuchViewColumnException(this)); if(r.entry.isLast()) { // default order return r.col.order(); @@ -243,60 +246,36 @@ public DBFilter evalFilter(ViewDecorator td, QueryContext ctx) { public DBFilter evalFilter(ViewDecorator vd, QueryContext ctx, List values) { //supply values var res = chainColumnOperations(vd, ctx, true); if(res.isEmpty()) { //not a column - return viewCriteria(vd, ctx, values) - .orElseThrow(()-> noSuchViewColumnException(this)); + throw noSuchViewColumnException(this); } var rc = res.get(); - if(rc.entry.isLast()) { //no comparator, no criteria - var fn = requireNonNull(values).size() == 1 ? eq() : in(); //non empty - var e = new RequestEntryChain(null, false, null, values, null); - return fn.filter(e.toArgs(vd, ctx, rc.col, fn.getParameterSet())); //no chain - } - return rc.entry.next.columnCriteria(vd, ctx, rc.cd, rc.col, values); - } - - //[view.]criteria - Optional viewCriteria(ViewDecorator td, QueryContext ctx, List values) { - CriteriaBuilder b = null; - RequestEntryChain e = null; - if(hasNext()) { //view.id == column.id - var res = currentContext().lookupRegisteredView(value).map(v-> v.criteria(next.value)); - if(res.isPresent()){ - b = res.get(); - e = next; + if(!rc.isCriteria() && nonNull(rc.col)) { + if(rc.entry.isLast()) { // no comparator, no criteria + var fn = requireNonNull(values).size() == 1 ? eq() : in(); //non empty + var e = new RequestEntryChain(null, false, null, values, null); + return fn.filter(e.toArgs(vd, ctx, rc.col, fn.getParameterSet())); //no chain } - } - if(isNull(b)) { - b = td.criteria(value); - e = this; - } - if(nonNull(b)) { - var strArgs = toStringArray(e.assertOuterParameters(values)); - var f = requireNonNull(b.build(ctx, strArgs), "view.criteria: " + e); - return Optional.of(e.chainComparator(td, ctx, f)); - } - return Optional.empty(); - } - - DBFilter columnCriteria(ViewDecorator vc, QueryContext ctx, ColumnDecorator cd, DBColumn col, List values) { - var cmp = lookupComparator(value); - if(cmp.isPresent()) { - var fn = cmp.get(); - var cp = new RequestEntryChain(value, false, null, assertOuterParameters(values), null); - return chainComparator(vc, ctx, fn.filter(cp.toArgs(vc, ctx, col, fn.getParameterSet()))); - } - if(nonNull(cd)) { // no operation - var c = cd.criteria(value); - if(nonNull(c)) { - var strArgs = toStringArray(assertOuterParameters(values)); - var ex = requireNonNull(c.build(ctx, strArgs), "column.criteria: " + this); - return chainComparator(vc, ctx, col.filter(ex)); + var cmp = lookupComparator(next.value); + if(cmp.isPresent()) { + var fn = cmp.get(); + var cp = new RequestEntryChain(value, false, null, assertOuterParameters(values), null); + return next.chainComparator(vd, ctx, fn.filter(cp.toArgs(vd, ctx, rc.col, fn.getParameterSet()))); } throw noSuchResourceException("comparator|criteria", value); } - throw noSuchResourceException("comparator", value); + if(nonNull(rc.viewCrt)) { //view criteria + var strArgs = toStringArray(rc.entry.assertOuterParameters(values)); + var f = requireNonNull(rc.viewCrt.build(ctx, strArgs), "view.criteria: " + rc.entry); + return rc.entry.chainComparator(vd, ctx, f); + } + if(nonNull(rc.colCrt) && nonNull(rc.col)) { //column criteria + var strArgs = toStringArray(rc.entry.assertOuterParameters(values)); + var ex = requireNonNull(rc.colCrt.build(ctx, strArgs), "column.criteria: " + rc.entry); + return rc.entry.chainComparator(vd, ctx, rc.col.filter(ex)); + } + throw new IllegalStateException("illegal ViewResource: " + rc); } - + private List assertOuterParameters(List values) { if(isEmpty(values)) { return args; @@ -363,7 +342,8 @@ private Optional lookupResource(ViewDecorator vd, QueryContext ctx } } } - return ctx.lookupDeclaredColumn(value).map(c-> new ViewResource(requireNoArgs(), null, null, c)) //declared column first + return ctx.lookupDeclaredColumn(value) + .map(c-> new ViewResource(requireNoArgs(), null, null, c)) //declared column first .or(()-> lookupViewResource(vd, ctx, null, fn-> true)); //registered column } @@ -377,12 +357,12 @@ private Optional lookupQueryResource(ViewDecorator vd) { return empty(); } - private Optional lookupViewResource(ViewDecorator td, QueryContext ctx, ViewDecorator current, Predicate pre) { - var cur = requireNonNullElse(current, td); - return lookupOperation(td, ctx, null, current, pre) - .map(col-> new ViewResource(this, cur, null, col)) - .or(()-> currentContext().lookupRegisteredColumn(value) - .flatMap(cd-> declaredColumn(cur, cd).map(c-> new ViewResource(requireNoArgs(), cur, cd, c)))) + //view[.operator|column|criteria] + private Optional lookupViewResource(ViewDecorator vd, QueryContext ctx, ViewDecorator current, Predicate pre) { + var cur = requireNonNullElse(current, vd); + return lookupOperation(vd, ctx, null, current, pre) + .map(col-> new ViewResource(this, cur, null, col)) + .or(()-> lookupColumnResource(cur)) .or(()-> ofNullable(cur.criteria(value)).map(crt-> new ViewResource(this, cur, crt))); } @@ -396,13 +376,23 @@ private Optional lookupOperation(ViewDecorator vd, QueryContext }); } - static Optional declaredColumn(ViewDecorator td, ColumnDecorator cd) { + Optional lookupColumnResource(ViewDecorator td) { try { - return Optional.of(td.column(cd)); - } - catch(Exception e) { - return empty(); + var res = currentContext().lookupRegisteredColumn(value); + if(res.isPresent()) { + var cd = res.get(); + var col = td.column(cd); //throw exception + if(hasNext()) { + var crt = res.get().criteria(next.value); + if(nonNull(crt)) { + return Optional.of(new ViewResource(next, td, cd, col, crt)); + } + } + return Optional.of(new ViewResource(next, td, cd, col)); + } } + catch(Exception e) {/*do not throw exception*/} + return empty(); } private TaggableColumn[] taggableVarargs(ViewDecorator td, QueryContext ctx) { @@ -553,15 +543,24 @@ static final class ViewResource { private ColumnDecorator cd; //optional private DBColumn col; private CriteriaBuilder viewCrt; + private CriteriaBuilder colCrt; public ViewResource(RequestEntryChain entry, ViewDecorator vd, ColumnDecorator cd, DBColumn col) { - this(entry, vd, cd, col, null); + this(entry, vd, cd, col, null, null); + } + + public ViewResource(RequestEntryChain entry, ViewDecorator vd, ColumnDecorator cd, DBColumn col, CriteriaBuilder colCrt) { + this(entry, vd, cd, col, null, colCrt); } public ViewResource(RequestEntryChain entry, ViewDecorator vd, CriteriaBuilder viewCrt) { - this(entry, vd, null, null, viewCrt); + this(entry, vd, null, null, viewCrt, null); } + private boolean isCriteria() { + return nonNull(viewCrt) || nonNull(colCrt); + } + @Override public String toString() { return vd + "." + cd + " => " + entry.toString(); From 8e98a8bda52e511d53cf38f2c9b3652fcb2168e5 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 3 Sep 2024 17:54:32 +0200 Subject: [PATCH 213/298] edit --- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index d2df27f6..6ba90ff3 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -255,11 +255,12 @@ public DBFilter evalFilter(ViewDecorator vd, QueryContext ctx, List lookupColumnResource(ViewDecorator td) { return Optional.of(new ViewResource(next, td, cd, col, crt)); } } - return Optional.of(new ViewResource(next, td, cd, col)); + return Optional.of(new ViewResource(this, td, cd, col)); } } catch(Exception e) {/*do not throw exception*/} From f31e8115e5a8241425f868dd4d47970395fc0843 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 3 Sep 2024 23:40:37 +0200 Subject: [PATCH 214/298] edit --- .../org/usf/jquery/core/TypedOperator.java | 10 ++- .../org/usf/jquery/web/RequestEntryChain.java | 75 +++++++++---------- 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 8a8a9905..6338ff11 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -37,11 +37,15 @@ public OperationColumn operation(Object... args) { throw badArgumentsException(toString(), operator.id() + joinAndDelemitArray(SCOMA, "(", ")", args), e); } } + + public boolean isCountFunction() { + return "COUNT".equals(operator.id()); + } - public Operator unwrap() { - return operator; + public boolean isWindowFunction() { + return operator instanceof WindowFunction; } - + @Override public String toString() { return operator.id() + parameterSet.toString(); diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 6ba90ff3..94ba3d85 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -44,10 +44,8 @@ import java.util.List; import java.util.Optional; import java.util.function.IntFunction; -import java.util.function.Predicate; import java.util.stream.Stream; -import org.usf.jquery.core.AggregateFunction; import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; @@ -68,7 +66,6 @@ import org.usf.jquery.core.TypedOperator; import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; -import org.usf.jquery.core.WindowFunction; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -242,10 +239,10 @@ public DBFilter evalFilter(ViewDecorator td, QueryContext ctx) { return evalFilter(td, ctx, emptyList()); } - //[view.]criteria | [view.]column.criteria | [view.]column[.operator]*[.comparator] - public DBFilter evalFilter(ViewDecorator vd, QueryContext ctx, List values) { //supply values + //[view.]criteria | [view.]column.criteria | [view.]column[.operator]*[.comparator][.and|or(comparator)]* + public DBFilter evalFilter(ViewDecorator vd, QueryContext ctx, List values) { //use CD.parser var res = chainColumnOperations(vd, ctx, true); - if(res.isEmpty()) { //not a column + if(res.isEmpty()) { throw noSuchViewColumnException(this); } var rc = res.get(); @@ -307,7 +304,7 @@ private Optional chainColumnOperations(ViewDecorator td, QueryCont if(nonNull(r.col)) { var e = r.entry.next; while(nonNull(e)) { //chain until !operator - var o = e.lookupOperation(td, ctx, r.col, null, fn-> true); //accept all + var o = e.lookupOperation(td, ctx, r.col, null); //accept all if(o.isEmpty()) { break; } @@ -318,7 +315,6 @@ private Optional chainColumnOperations(ViewDecorator td, QueryCont : o.get(); e = e.next; } - } //else view criteria return r; }); @@ -331,23 +327,25 @@ private static DBColumn windowColumn(ViewDecorator vd, QueryContext ctx, DBColum return new ViewColumn(v, doubleQuote(tag), null, col.getType()); } + //view.resource | resource private Optional lookupResource(ViewDecorator vd, QueryContext ctx) { //do not change priority - if(hasNext()) { //view.id == column.id + if(hasNext()) { var rc = currentContext().lookupRegisteredView(value); if(rc.isPresent()) { var res = next.lookupQueryResource(rc.get()) //declared query first - .or(()-> next.lookupViewResource(vd, ctx, rc.get(), RequestEntryChain::isWindowFunction)); + .or(()-> next.lookupViewResource(vd, ctx, rc.get())); if(res.isPresent()) { requireNoArgs(); return res; } } - } + } //view.id == column.id return ctx.lookupDeclaredColumn(value) .map(c-> new ViewResource(requireNoArgs(), null, null, c)) //declared column first - .or(()-> lookupViewResource(vd, ctx, null, fn-> true)); //registered column + .or(()-> lookupViewResource(vd, ctx, null)); //registered column } + //query.column private Optional lookupQueryResource(ViewDecorator vd) { if(vd instanceof QueryDecorator qd) { try { @@ -358,41 +356,45 @@ private Optional lookupQueryResource(ViewDecorator vd) { return empty(); } - //view[.operator|column|criteria] - private Optional lookupViewResource(ViewDecorator vd, QueryContext ctx, ViewDecorator current, Predicate pre) { + //view[.operator|column[.criteria]|criteria] + private Optional lookupViewResource(ViewDecorator vd, QueryContext ctx, ViewDecorator current) { var cur = requireNonNullElse(current, vd); - return lookupOperation(vd, ctx, null, current, pre) + return lookupOperation(vd, ctx, null, current) .map(col-> new ViewResource(this, cur, null, col)) .or(()-> lookupColumnResource(cur)) .or(()-> ofNullable(cur.criteria(value)).map(crt-> new ViewResource(this, cur, crt))); } - private Optional lookupOperation(ViewDecorator vd, QueryContext ctx, DBColumn col, ViewDecorator current, Predicate opr) { - return lookupOperator(value).filter(opr).map(fn-> { + private Optional lookupOperation(ViewDecorator vd, QueryContext ctx, DBColumn col, ViewDecorator current) { + var res = lookupOperator(value); + if(isNull(col) && nonNull(current)) { //view.[count|rank|rowNumber|danseRank] + res = res.filter(op-> op.isCountFunction() || op.isWindowFunction()); + } + return res.map(fn-> { var c = col; - if(isNull(c) && isEmpty(args) && isCountFunction(fn)) { + if(isNull(c) && isEmpty(args) && fn.isCountFunction()) { c = allColumns(requireNonNullElse(current, vd).view()); } return fn.operation(toArgs(vd, ctx, c, fn.getParameterSet())); }); } - Optional lookupColumnResource(ViewDecorator td) { - try { - var res = currentContext().lookupRegisteredColumn(value); - if(res.isPresent()) { - var cd = res.get(); + private Optional lookupColumnResource(ViewDecorator td) { + var res = currentContext().lookupRegisteredColumn(value); + if(res.isPresent()) { + var cd = res.get(); + try { var col = td.column(cd); //throw exception - if(hasNext()) { - var crt = res.get().criteria(next.value); + if(hasNext() && lookupComparator(next.value).isEmpty()) { //!comparator + var crt = cd.criteria(next.value); if(nonNull(crt)) { - return Optional.of(new ViewResource(next, td, cd, col, crt)); + return Optional.of(new ViewResource(requireNoArgs().next, td, cd, col, crt)); } } - return Optional.of(new ViewResource(this, td, cd, col)); + return Optional.of(new ViewResource(requireNoArgs(), td, cd, col)); } + catch(Exception e) {/*do not throw exception*/} //TD specific exception } - catch(Exception e) {/*do not throw exception*/} return empty(); } @@ -495,16 +497,7 @@ public String toString() { } return isNull(tag) ? s : s + ":" + tag; } - - private static boolean isWindowFunction(TypedOperator op) { - var fn = op.unwrap(); - return fn instanceof WindowFunction || fn instanceof AggregateFunction; //rank() | sum(col) - } - - private static boolean isCountFunction(TypedOperator fn) { - return "COUNT".equals(fn.unwrap().id()); - } - + private static String[] toStringArray(List entries) { if(!isEmpty(entries)) { return entries.stream() @@ -540,8 +533,8 @@ static EntrySyntaxException expectedEntryTagException(RequestEntryChain e) { static final class ViewResource { private RequestEntryChain entry; - private ViewDecorator vd; //optional - private ColumnDecorator cd; //optional + private ViewDecorator vd; + private ColumnDecorator cd; private DBColumn col; private CriteriaBuilder viewCrt; private CriteriaBuilder colCrt; @@ -564,7 +557,7 @@ private boolean isCriteria() { @Override public String toString() { - return vd + "." + cd + " => " + entry.toString(); + return entry.toString(); } } } \ No newline at end of file From 245f630cfe4e7c7a394ea974878b057f187a962c Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 4 Sep 2024 10:56:57 +0200 Subject: [PATCH 215/298] edit --- .../org/usf/jquery/web/ColumnDecorator.java | 6 +- .../org/usf/jquery/web/RequestEntryChain.java | 87 +++++++++++-------- 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 8a331dfc..1a0f1c40 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -22,9 +22,9 @@ default JDBCType type(ViewDecorator vd) { return null; // auto type } - default JDBCArgumentParser parser(ViewDecorator vd){ // override parser | format | local | validation - return null; // auto parser - } +// default JDBCArgumentParser parser(ViewDecorator vd){ // override parser | format | local | validation +// return null; // auto parser +// } default ColumnBuilder builder(ViewDecorator vd) { //set type if null return null; // no builder by default diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 94ba3d85..a08a0817 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -46,6 +46,7 @@ import java.util.function.IntFunction; import java.util.stream.Stream; +import org.usf.jquery.core.Comparator; import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; @@ -63,6 +64,7 @@ import org.usf.jquery.core.QueryView; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.TypedComparator; import org.usf.jquery.core.TypedOperator; import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; @@ -207,7 +209,6 @@ private Optional evalPartition2(ViewDecorator vd, QueryContext ctx) { //[view.]column[.operator]* public DBColumn evalColumn(ViewDecorator td, QueryContext ctx, boolean requireTag) { var r = chainColumnOperations(td, ctx, false) - .filter(not(ViewResource::isCriteria)) //!criteria .orElseThrow(()-> noSuchViewColumnException(this)); r.entry.requireNoNext(); //check next only if column exists if(nonNull(r.entry.tag)) { @@ -222,7 +223,6 @@ public DBColumn evalColumn(ViewDecorator td, QueryContext ctx, boolean requireTa //[view.]column[.operator]*[.order] public DBOrder evalOrder(ViewDecorator td, QueryContext ctx) { var r = chainColumnOperations(td, ctx, false) - .filter(not(ViewResource::isCriteria)) //!criteria .orElseThrow(()-> noSuchViewColumnException(this)); if(r.entry.isLast()) { // default order return r.col.order(); @@ -246,30 +246,28 @@ public DBFilter evalFilter(ViewDecorator vd, QueryContext ctx, List chainColumnOperations(ViewDecorator td, QueryContext ctx, boolean filter) { - return lookupResource(td, ctx).map(r-> { - if(nonNull(r.col)) { + return lookupResource(td, ctx, filter).map(r-> { + if(r.isColumn()) { // !criteria & !comparator var e = r.entry.next; while(nonNull(e)) { //chain until !operator var o = e.lookupOperation(td, ctx, r.col, null); //accept all @@ -328,12 +326,12 @@ private static DBColumn windowColumn(ViewDecorator vd, QueryContext ctx, DBColum } //view.resource | resource - private Optional lookupResource(ViewDecorator vd, QueryContext ctx) { //do not change priority + private Optional lookupResource(ViewDecorator vd, QueryContext ctx, boolean filter) { //do not change priority if(hasNext()) { var rc = currentContext().lookupRegisteredView(value); if(rc.isPresent()) { var res = next.lookupQueryResource(rc.get()) //declared query first - .or(()-> next.lookupViewResource(vd, ctx, rc.get())); + .or(()-> next.lookupViewResource(vd, ctx, rc.get(), filter)); if(res.isPresent()) { requireNoArgs(); return res; @@ -342,7 +340,7 @@ private Optional lookupResource(ViewDecorator vd, QueryContext ctx } //view.id == column.id return ctx.lookupDeclaredColumn(value) .map(c-> new ViewResource(requireNoArgs(), null, null, c)) //declared column first - .or(()-> lookupViewResource(vd, ctx, null)); //registered column + .or(()-> lookupViewResource(vd, ctx, null, filter)); //registered column } //query.column @@ -357,12 +355,12 @@ private Optional lookupQueryResource(ViewDecorator vd) { } //view[.operator|column[.criteria]|criteria] - private Optional lookupViewResource(ViewDecorator vd, QueryContext ctx, ViewDecorator current) { + private Optional lookupViewResource(ViewDecorator vd, QueryContext ctx, ViewDecorator current, boolean filter) { var cur = requireNonNullElse(current, vd); - return lookupOperation(vd, ctx, null, current) + var res = lookupOperation(vd, ctx, null, current) .map(col-> new ViewResource(this, cur, null, col)) - .or(()-> lookupColumnResource(cur)) - .or(()-> ofNullable(cur.criteria(value)).map(crt-> new ViewResource(this, cur, crt))); + .or(()-> lookupColumnResource(cur, filter)); + return filter ? res.or(()-> ofNullable(cur.criteria(value)).map(crt-> new ViewResource(this, cur, crt))) : res; } private Optional lookupOperation(ViewDecorator vd, QueryContext ctx, DBColumn col, ViewDecorator current) { @@ -379,13 +377,17 @@ private Optional lookupOperation(ViewDecorator vd, QueryContext }); } - private Optional lookupColumnResource(ViewDecorator td) { + private Optional lookupColumnResource(ViewDecorator td, boolean filter) { var res = currentContext().lookupRegisteredColumn(value); if(res.isPresent()) { var cd = res.get(); try { var col = td.column(cd); //throw exception - if(hasNext() && lookupComparator(next.value).isEmpty()) { //!comparator + if(filter && hasNext()) { + var cmp = lookupComparator(next.value); + if(cmp.isPresent()) { + return Optional.of(new ViewResource(requireNoArgs().next, td, cd, col, cmp.get())); + } var crt = cd.criteria(next.value); if(nonNull(crt)) { return Optional.of(new ViewResource(requireNoArgs().next, td, cd, col, crt)); @@ -508,7 +510,7 @@ private static String[] toStringArray(List entries) { } static NoSuchResourceException noSuchViewColumnException(RequestEntryChain e) { - return noSuchResourceException(COLUMN, e.hasNext() + return noSuchResourceException("resource", e.hasNext() && currentContext().lookupRegisteredView(e.value).isPresent() ? e.value + "." + e.next.value : e.value); @@ -529,7 +531,7 @@ static EntrySyntaxException expectedEntryTagException(RequestEntryChain e) { throw new EntrySyntaxException(format("expected tag : %s[:tag]", e)); } - @AllArgsConstructor + @AllArgsConstructor(access = AccessLevel.PRIVATE) static final class ViewResource { private RequestEntryChain entry; @@ -538,23 +540,32 @@ static final class ViewResource { private DBColumn col; private CriteriaBuilder viewCrt; private CriteriaBuilder colCrt; + private TypedComparator cmp; public ViewResource(RequestEntryChain entry, ViewDecorator vd, ColumnDecorator cd, DBColumn col) { - this(entry, vd, cd, col, null, null); + this(entry, vd, cd, col, null, null, null); } public ViewResource(RequestEntryChain entry, ViewDecorator vd, ColumnDecorator cd, DBColumn col, CriteriaBuilder colCrt) { - this(entry, vd, cd, col, null, colCrt); + this(entry, vd, cd, col, null, colCrt, null); + } + + public ViewResource(RequestEntryChain entry, ViewDecorator vd, ColumnDecorator cd, DBColumn col, TypedComparator cmp) { + this(entry, vd, cd, col, null, null, cmp); } public ViewResource(RequestEntryChain entry, ViewDecorator vd, CriteriaBuilder viewCrt) { - this(entry, vd, null, null, viewCrt, null); + this(entry, vd, null, null, viewCrt, null, null); } - private boolean isCriteria() { + boolean isCriteria() { return nonNull(viewCrt) || nonNull(colCrt); } + boolean isColumn() { + return nonNull(col) && isNull(colCrt) && isNull(cmp); + } + @Override public String toString() { return entry.toString(); From 9bf0638f5b8510eacdc28d300f9fed25f2b746e0 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 4 Sep 2024 11:22:56 +0200 Subject: [PATCH 216/298] edit --- .../org/usf/jquery/web/RequestEntryChain.java | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index a08a0817..7965217c 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -10,7 +10,6 @@ import static java.util.Objects.requireNonNullElse; import static java.util.Optional.empty; import static java.util.Optional.ofNullable; -import static java.util.function.Predicate.not; import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Comparator.eq; import static org.usf.jquery.core.Comparator.in; @@ -28,7 +27,6 @@ import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; -import static org.usf.jquery.web.Parameters.COLUMN; import static org.usf.jquery.web.Parameters.DISTINCT; import static org.usf.jquery.web.Parameters.FETCH; import static org.usf.jquery.web.Parameters.FILTER; @@ -46,7 +44,6 @@ import java.util.function.IntFunction; import java.util.stream.Stream; -import org.usf.jquery.core.Comparator; import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; @@ -65,7 +62,6 @@ import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TaggableColumn; import org.usf.jquery.core.TypedComparator; -import org.usf.jquery.core.TypedOperator; import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; @@ -262,7 +258,7 @@ else if(nonNull(rc.col)) { var cp = new RequestEntryChain(rc.entry.value, false, null, rc.entry.assertOuterParameters(values), null); return rc.entry.chainComparator(vd, ctx, rc.cmp.filter(cp.toArgs(vd, ctx, rc.col, rc.cmp.getParameterSet()))); } - if(rc.entry.isLast()) { // no comparator, no criteria + if(rc.entry.isLast()) { // no criteria, no comparator var fn = requireNonNull(values).size() == 1 ? eq() : in(); //non empty var e = new RequestEntryChain(null, false, null, values, null); return fn.filter(e.toArgs(vd, ctx, rc.col, fn.getParameterSet())); //no chain @@ -297,23 +293,23 @@ DBFilter chainComparator(ViewDecorator td, QueryContext ctx, DBFilter f) { return f; } - private Optional chainColumnOperations(ViewDecorator td, QueryContext ctx, boolean filter) { - return lookupResource(td, ctx, filter).map(r-> { - if(r.isColumn()) { // !criteria & !comparator + private Optional chainColumnOperations(ViewDecorator vd, QueryContext ctx, boolean filter) { + return lookupResource(vd, ctx, filter).map(r-> { + if(!r.isFilter()) { // !criteria & !comparator var e = r.entry.next; while(nonNull(e)) { //chain until !operator - var o = e.lookupOperation(td, ctx, r.col, null); //accept all - if(o.isEmpty()) { + var res = lookupOperator(value); + if(res.isEmpty()) { break; } + var fn = res.get(); + var o = fn.operation(toArgs(vd, ctx, r.col, fn.getParameterSet())); r.cd = null; r.entry = e; - r.col = filter && "over".equals(e.value) - ? windowColumn(r.vd, ctx, o.get()) - : o.get(); + r.col = filter && "over".equals(e.value) ? windowColumn(r.vd, ctx, o) : o; e = e.next; } - } //else view criteria + } //else filter return r; }); } @@ -558,14 +554,14 @@ public ViewResource(RequestEntryChain entry, ViewDecorator vd, CriteriaBuilder Date: Wed, 4 Sep 2024 12:41:14 +0200 Subject: [PATCH 217/298] edit --- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 7965217c..c2032a03 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -298,7 +298,7 @@ private Optional chainColumnOperations(ViewDecorator vd, QueryCont if(!r.isFilter()) { // !criteria & !comparator var e = r.entry.next; while(nonNull(e)) { //chain until !operator - var res = lookupOperator(value); + var res = lookupOperator(e.value); if(res.isEmpty()) { break; } From b9b528f54b1560836155ec5d5320d133a3639485 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 4 Sep 2024 12:53:00 +0200 Subject: [PATCH 218/298] edit --- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index c2032a03..3bff5e38 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -303,7 +303,7 @@ private Optional chainColumnOperations(ViewDecorator vd, QueryCont break; } var fn = res.get(); - var o = fn.operation(toArgs(vd, ctx, r.col, fn.getParameterSet())); + var o = fn.operation(r.entry.toArgs(vd, ctx, r.col, fn.getParameterSet())); r.cd = null; r.entry = e; r.col = filter && "over".equals(e.value) ? windowColumn(r.vd, ctx, o) : o; From 9fd5498e7c0675c6e64ab2fd41d59d8221890afd Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 4 Sep 2024 12:57:58 +0200 Subject: [PATCH 219/298] edit --- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 3bff5e38..0d59bb5d 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -303,7 +303,7 @@ private Optional chainColumnOperations(ViewDecorator vd, QueryCont break; } var fn = res.get(); - var o = fn.operation(r.entry.toArgs(vd, ctx, r.col, fn.getParameterSet())); + var o = fn.operation(e.toArgs(vd, ctx, r.col, fn.getParameterSet())); r.cd = null; r.entry = e; r.col = filter && "over".equals(e.value) ? windowColumn(r.vd, ctx, o) : o; From c91db649721f1258ac01a42baf4bb21d2840831d Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 4 Sep 2024 18:21:17 +0200 Subject: [PATCH 220/298] edit --- .../java/org/usf/jquery/core/Comparator.java | 7 +- .../java/org/usf/jquery/core/DBProcessor.java | 15 ++-- .../java/org/usf/jquery/core/Operator.java | 7 +- .../org/usf/jquery/web/RequestEntryChain.java | 84 ++++++++----------- .../usf/jquery/core/FunctionOperatorTest.java | 38 +++++++++ 5 files changed, 97 insertions(+), 54 deletions(-) create mode 100644 src/test/java/org/usf/jquery/core/FunctionOperatorTest.java diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index ffaa63eb..3c2305ca 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -9,6 +9,7 @@ import java.util.Objects; import java.util.Optional; +import java.util.function.Predicate; import java.util.function.UnaryOperator; /** @@ -173,6 +174,10 @@ static RangeComparator rangeComparator(final String name) { } static Optional lookupComparator(String op) { - return lookup(Comparator.class, TypedComparator.class, op); + return lookup(Comparator.class, TypedComparator.class, op, null); + } + + static Optional lookupComparator(String op, Predicate pre) { + return lookup(Comparator.class, TypedComparator.class, op, pre); } } diff --git a/src/main/java/org/usf/jquery/core/DBProcessor.java b/src/main/java/org/usf/jquery/core/DBProcessor.java index 94427654..14362d51 100644 --- a/src/main/java/org/usf/jquery/core/DBProcessor.java +++ b/src/main/java/org/usf/jquery/core/DBProcessor.java @@ -2,9 +2,11 @@ import static java.lang.reflect.Modifier.isPrivate; import static java.lang.reflect.Modifier.isStatic; +import static java.util.Objects.isNull; import static java.util.Optional.empty; import java.util.Optional; +import java.util.function.Predicate; /** * @@ -12,12 +14,15 @@ * */ public interface DBProcessor extends DBObject { - - static Optional lookup(Class clazz, Class ext, String op) { + + static Optional lookup(Class clazz, Class type, String name, Predicate pre) { try { - var m = clazz.getMethod(op); //no parameter - if(m.getReturnType() == ext && isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) { - return Optional.of(ext.cast(m.invoke(null))); + var m = clazz.getMethod(name); //no parameter + if(m.getReturnType() == type && m.getParameterCount() == 0 && isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) { + var o = type.cast(m.invoke(null)); + if(isNull(pre) || pre.test(o)) { + return Optional.of(o); + } } } catch (Exception e) {/* do not throw exception */} return empty(); diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 1d39952e..20f5b07a 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -20,6 +20,7 @@ import static org.usf.jquery.core.Validation.requireNArgs; import java.util.Optional; +import java.util.function.Predicate; /** * @@ -335,6 +336,10 @@ static ConstantOperator constant(String name) { } static Optional lookupOperator(String op) { - return lookup(Operator.class, TypedOperator.class, op); + return lookup(Operator.class, TypedOperator.class, op, null); + } + + static Optional lookupOperator(String op, Predicate pre) { + return lookup(Operator.class, TypedOperator.class, op, pre); } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 0d59bb5d..6ea175f7 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -204,8 +204,7 @@ private Optional evalPartition2(ViewDecorator vd, QueryContext ctx) { //[view.]column[.operator]* public DBColumn evalColumn(ViewDecorator td, QueryContext ctx, boolean requireTag) { - var r = chainColumnOperations(td, ctx, false) - .orElseThrow(()-> noSuchViewColumnException(this)); + var r = chainColumnOperations(td, ctx, false); r.entry.requireNoNext(); //check next only if column exists if(nonNull(r.entry.tag)) { return r.col.as(r.entry.tag); @@ -218,8 +217,7 @@ public DBColumn evalColumn(ViewDecorator td, QueryContext ctx, boolean requireTa //[view.]column[.operator]*[.order] public DBOrder evalOrder(ViewDecorator td, QueryContext ctx) { - var r = chainColumnOperations(td, ctx, false) - .orElseThrow(()-> noSuchViewColumnException(this)); + var r = chainColumnOperations(td, ctx, false); if(r.entry.isLast()) { // default order return r.col.order(); } @@ -237,11 +235,7 @@ public DBFilter evalFilter(ViewDecorator td, QueryContext ctx) { //[view.]criteria | [view.]column.criteria | [view.]column[.operator]*[.comparator][.and|or(comparator)]* public DBFilter evalFilter(ViewDecorator vd, QueryContext ctx, List values) { //use CD.parser - var res = chainColumnOperations(vd, ctx, true); - if(res.isEmpty()) { - throw noSuchViewColumnException(this); - } - var rc = res.get(); + var rc = chainColumnOperations(vd, ctx, true); if(rc.isCriteria()) { var strArgs = toStringArray(rc.entry.assertOuterParameters(values)); if(nonNull(rc.viewCrt)) { //view criteria @@ -293,25 +287,24 @@ DBFilter chainComparator(ViewDecorator td, QueryContext ctx, DBFilter f) { return f; } - private Optional chainColumnOperations(ViewDecorator vd, QueryContext ctx, boolean filter) { - return lookupResource(vd, ctx, filter).map(r-> { - if(!r.isFilter()) { // !criteria & !comparator - var e = r.entry.next; - while(nonNull(e)) { //chain until !operator - var res = lookupOperator(e.value); - if(res.isEmpty()) { - break; - } - var fn = res.get(); - var o = fn.operation(e.toArgs(vd, ctx, r.col, fn.getParameterSet())); - r.cd = null; - r.entry = e; - r.col = filter && "over".equals(e.value) ? windowColumn(r.vd, ctx, o) : o; - e = e.next; + private ViewResource chainColumnOperations(ViewDecorator vd, QueryContext ctx, boolean filter) { + var r = lookupResource(vd, ctx, filter); + if(!r.isFilter()) { // !criteria & !comparator + var e = r.entry.next; + while(nonNull(e)) { //chain until !operator + var res = lookupOperator(e.value); + if(res.isEmpty()) { + break; } - } //else filter - return r; - }); + var fn = res.get(); + var o = fn.operation(e.toArgs(vd, ctx, r.col, fn.getParameterSet())); + r.cd = null; + r.entry = e; + r.col = filter && "over".equals(e.value) ? windowColumn(r.vd, ctx, o) : o; + e = e.next; + } + } //else filter + return r; } private static DBColumn windowColumn(ViewDecorator vd, QueryContext ctx, DBColumn col) { @@ -322,7 +315,7 @@ private static DBColumn windowColumn(ViewDecorator vd, QueryContext ctx, DBColum } //view.resource | resource - private Optional lookupResource(ViewDecorator vd, QueryContext ctx, boolean filter) { //do not change priority + private ViewResource lookupResource(ViewDecorator vd, QueryContext ctx, boolean filter) { //do not change priority if(hasNext()) { var rc = currentContext().lookupRegisteredView(value); if(rc.isPresent()) { @@ -330,13 +323,14 @@ private Optional lookupResource(ViewDecorator vd, QueryContext ctx .or(()-> next.lookupViewResource(vd, ctx, rc.get(), filter)); if(res.isPresent()) { requireNoArgs(); - return res; + return res.get(); } } } //view.id == column.id return ctx.lookupDeclaredColumn(value) .map(c-> new ViewResource(requireNoArgs(), null, null, c)) //declared column first - .or(()-> lookupViewResource(vd, ctx, null, filter)); //registered column + .or(()-> lookupViewResource(vd, ctx, null, filter)) //registered column + .orElseThrow(()-> noSuchViewColumnException(this)); //no such resource } //query.column @@ -353,23 +347,19 @@ private Optional lookupQueryResource(ViewDecorator vd) { //view[.operator|column[.criteria]|criteria] private Optional lookupViewResource(ViewDecorator vd, QueryContext ctx, ViewDecorator current, boolean filter) { var cur = requireNonNullElse(current, vd); - var res = lookupOperation(vd, ctx, null, current) + var res = lookupViewOperation(vd, ctx, current) .map(col-> new ViewResource(this, cur, null, col)) .or(()-> lookupColumnResource(cur, filter)); return filter ? res.or(()-> ofNullable(cur.criteria(value)).map(crt-> new ViewResource(this, cur, crt))) : res; } - private Optional lookupOperation(ViewDecorator vd, QueryContext ctx, DBColumn col, ViewDecorator current) { - var res = lookupOperator(value); - if(isNull(col) && nonNull(current)) { //view.[count|rank|rowNumber|danseRank] - res = res.filter(op-> op.isCountFunction() || op.isWindowFunction()); - } - return res.map(fn-> { - var c = col; - if(isNull(c) && isEmpty(args) && fn.isCountFunction()) { - c = allColumns(requireNonNullElse(current, vd).view()); - } - return fn.operation(toArgs(vd, ctx, c, fn.getParameterSet())); + private Optional lookupViewOperation(ViewDecorator vd, QueryContext ctx, ViewDecorator current) { + //view.[count|rank|rowNumber|danseRank] only + return lookupOperator(value, isNull(current) ? null : op-> op.isCountFunction() || op.isWindowFunction()).map(fn-> { + var col = isEmpty(args) && fn.isCountFunction() + ? allColumns(requireNonNullElse(current, vd).view()) + : null; + return fn.operation(toArgs(vd, ctx, col, fn.getParameterSet())); }); } @@ -384,7 +374,7 @@ private Optional lookupColumnResource(ViewDecorator td, boolean fi if(cmp.isPresent()) { return Optional.of(new ViewResource(requireNoArgs().next, td, cd, col, cmp.get())); } - var crt = cd.criteria(next.value); + var crt = cd.criteria(next.value); //TD !operator if(nonNull(crt)) { return Optional.of(new ViewResource(requireNoArgs().next, td, cd, col, crt)); } @@ -539,19 +529,19 @@ static final class ViewResource { private TypedComparator cmp; public ViewResource(RequestEntryChain entry, ViewDecorator vd, ColumnDecorator cd, DBColumn col) { - this(entry, vd, cd, col, null, null, null); + this(entry, vd, cd, col, null, null, null); //[view.]column } public ViewResource(RequestEntryChain entry, ViewDecorator vd, ColumnDecorator cd, DBColumn col, CriteriaBuilder colCrt) { - this(entry, vd, cd, col, null, colCrt, null); + this(entry, vd, cd, col, null, colCrt, null); //[view.]colum.criteria } public ViewResource(RequestEntryChain entry, ViewDecorator vd, ColumnDecorator cd, DBColumn col, TypedComparator cmp) { - this(entry, vd, cd, col, null, null, cmp); + this(entry, vd, cd, col, null, null, cmp); //[view.]colum.comparator } public ViewResource(RequestEntryChain entry, ViewDecorator vd, CriteriaBuilder viewCrt) { - this(entry, vd, null, null, viewCrt, null, null); + this(entry, vd, null, null, viewCrt, null, null); //[view.]criteria } boolean isFilter() { diff --git a/src/test/java/org/usf/jquery/core/FunctionOperatorTest.java b/src/test/java/org/usf/jquery/core/FunctionOperatorTest.java new file mode 100644 index 00000000..211df7f3 --- /dev/null +++ b/src/test/java/org/usf/jquery/core/FunctionOperatorTest.java @@ -0,0 +1,38 @@ +package org.usf.jquery.core; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; + +import org.junit.jupiter.api.Test; + +class FunctionOperatorTest { + + @Test + void testSql() { + FunctionOperator fn = ()-> "dummy"; + assertEquals("dummy()", fn.sql(addWithValue(), new Object[] {})); + } + + @Test + void testSql2() { + FunctionOperator fn = ()-> "dummy"; + assertEquals("dummy('toto', 123)", fn.sql(addWithValue(), new Object[] {"toto", 123})); + } + + @Test + void testSql3() { + FunctionOperator fn = ()-> "dummy"; + DBColumn col = b-> "col1"; + assertEquals("dummy(col1, 123)", fn.sql(addWithValue(), new Object[] {col, 123})); + } + + @Test + void testSql4() { + FunctionOperator fn = ()-> "dummy"; + DBColumn col1 = b-> "col1"; + DBColumn col2 = b-> "col2"; + DBColumn col3 = b-> "col3"; + assertEquals("dummy(col1, col2, col3)", fn.sql(addWithValue(), new Object[] {col1, col2, col3})); + } + +} From b1d6ccf148ec69dbd30f3e3ffa9a256f937d6ade Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 4 Sep 2024 18:26:43 +0200 Subject: [PATCH 221/298] edit --- .../java/org/usf/jquery/web/RequestQueryParamResolver.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java index 612f5978..6685a547 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java @@ -55,10 +55,6 @@ public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull } throw new ResourceAccessException("non-aggregate query"); } - catch (Exception e) { - e.printStackTrace(); - throw e; - } finally { releaseContext(); } From b1db726e6feca2e21171a50c972f6147fcfcad21 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 4 Sep 2024 22:46:38 +0200 Subject: [PATCH 222/298] edit --- .../jquery/core/CaseSingleColumnBuilder.java | 3 +- .../java/org/usf/jquery/core/ColumnProxy.java | 46 +++++++++++ .../java/org/usf/jquery/core/DBColumn.java | 19 +++-- .../java/org/usf/jquery/core/JDBCType.java | 81 +++++++++---------- .../java/org/usf/jquery/core/JQueryType.java | 6 +- .../java/org/usf/jquery/core/JavaType.java | 4 +- .../java/org/usf/jquery/core/NamedColumn.java | 38 +++------ .../org/usf/jquery/core/OperationColumn.java | 1 - .../java/org/usf/jquery/core/QueryColumn.java | 2 +- .../org/usf/jquery/core/QueryContext.java | 2 +- .../usf/jquery/core/RequestQueryBuilder.java | 12 +-- .../org/usf/jquery/core/TaggableColumn.java | 22 ----- .../java/org/usf/jquery/core/ViewColumn.java | 24 +----- .../org/usf/jquery/web/ColumnDecorator.java | 6 +- .../org/usf/jquery/web/ColumnMetadata.java | 6 +- .../org/usf/jquery/web/QueryDecorator.java | 8 +- .../org/usf/jquery/web/RequestEntryChain.java | 28 ++++--- .../org/usf/jquery/web/ViewDecorator.java | 10 +-- .../org/usf/jquery/web/YearViewDecorator.java | 4 +- 19 files changed, 157 insertions(+), 165 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/ColumnProxy.java delete mode 100644 src/main/java/org/usf/jquery/core/TaggableColumn.java diff --git a/src/main/java/org/usf/jquery/core/CaseSingleColumnBuilder.java b/src/main/java/org/usf/jquery/core/CaseSingleColumnBuilder.java index 9316bd9b..5316d071 100644 --- a/src/main/java/org/usf/jquery/core/CaseSingleColumnBuilder.java +++ b/src/main/java/org/usf/jquery/core/CaseSingleColumnBuilder.java @@ -49,13 +49,12 @@ public CaseColumn end() { return caseColumn; } - private CaseColumn orElseExp(Object elseValue) { caseColumn.append(WhenExpression.orElse(elseValue)); return caseColumn; } - public NamedColumn as(String tagname) { + public ColumnProxy as(String tagname) { return caseColumn.as(tagname); } diff --git a/src/main/java/org/usf/jquery/core/ColumnProxy.java b/src/main/java/org/usf/jquery/core/ColumnProxy.java new file mode 100644 index 00000000..50ee62c1 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/ColumnProxy.java @@ -0,0 +1,46 @@ +package org.usf.jquery.core; + +import static java.util.Objects.nonNull; +import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; + +import java.util.Objects; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * + * @author u$f + * + */ +@Getter +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public final class ColumnProxy implements NamedColumn { + + private final DBColumn column; + private final JDBCType type; //optional + private final String tag; //optional + + @Override + public JDBCType getType() { + return nonNull(type) ? type : column.getType(); + } + + @Override + public String sql(QueryParameterBuilder builder) { + return column.sql(builder); + } + + @Override + public ColumnProxy as(String name, JDBCType type) { // map + return Objects.equals(this.tag, name) && Objects.equals(this.type, type) + ? this + : new ColumnProxy(column, type, name); + } + + @Override + public String toString() { + return this.sqlWithTag(addWithValue()); + } +} diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 3f007e84..d4346d3c 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -31,6 +31,11 @@ default String sql(QueryParameterBuilder builder, Object[] args) { requireNoArgs(args, DBColumn.class::getSimpleName); return sql(builder); } + + @Override + default JDBCType getType() { + return null; + } @Override default boolean isAggregation() { @@ -42,12 +47,12 @@ default Stream groupKeys() { return Stream.of(this); } - default JDBCType getType() { - return null; + default ColumnProxy as(String name) { + return as(name, null); } - default NamedColumn as(String name) { - return new NamedColumn(this, Objects.isNull(name) ? null : requireLegalVariable(name)); + default ColumnProxy as(String name, JDBCType type) { + return new ColumnProxy(this, type, Objects.isNull(name) ? null : requireLegalVariable(name)); } // filters @@ -76,7 +81,7 @@ default ColumnSingleFilter ge(Object value) { return Comparator.ge().filter(this, value); } - default ColumnSingleFilter between(Object min, Object max) { + default ColumnSingleFilter between(Object min, Object max) { //included return Comparator.between().filter(this, min, max); } @@ -436,8 +441,8 @@ static DBColumn column(@NonNull String value) { return b-> value; } - static TaggableColumn allColumns(@NonNull DBView view) { - return new ViewColumn(view, "*", null) ; //TODO check this + static NamedColumn allColumns(@NonNull DBView view) { + return new ViewColumn("*", view, null, null) ; //TODO check this } static DBColumn constant(Object value) { diff --git a/src/main/java/org/usf/jquery/core/JDBCType.java b/src/main/java/org/usf/jquery/core/JDBCType.java index de2c4422..65849955 100644 --- a/src/main/java/org/usf/jquery/core/JDBCType.java +++ b/src/main/java/org/usf/jquery/core/JDBCType.java @@ -1,14 +1,8 @@ package org.usf.jquery.core; import static java.util.Objects.isNull; -import static java.util.Optional.empty; import static java.util.Optional.ofNullable; -/** - * - * @author u$f - * - */ import java.math.BigDecimal; import java.sql.Date; import java.sql.Time; @@ -16,8 +10,8 @@ import java.sql.Types; import java.util.Optional; import java.util.function.Predicate; +import java.util.stream.Stream; -import lombok.Getter; import lombok.RequiredArgsConstructor; /** @@ -27,30 +21,29 @@ * @author u$f * */ -@Getter @RequiredArgsConstructor public enum JDBCType implements JavaType { BOOLEAN(Types.BOOLEAN, Boolean.class, JDBCType::isBoolean), BIT(Types.BIT, Boolean.class, JDBCType::isBoolean), - TINYINT(Types.TINYINT, Byte.class, Number.class, Number.class::isInstance), - SMALLINT(Types.SMALLINT, Short.class, Number.class, Number.class::isInstance), - INTEGER(Types.INTEGER, Integer.class, Number.class, Number.class::isInstance), - BIGINT(Types.BIGINT, Long.class, Number.class, Number.class::isInstance), - REAL(Types.REAL, Float.class, Number.class, Number.class::isInstance), - FLOAT(Types.FLOAT, Double.class, Number.class, Number.class::isInstance), - DOUBLE(Types.DOUBLE, Double.class, Number.class, Number.class::isInstance), - NUMERIC(Types.NUMERIC, BigDecimal.class, Number.class, Number.class::isInstance), - DECIMAL(Types.DECIMAL, BigDecimal.class, Number.class, Number.class::isInstance), + TINYINT(Types.TINYINT, Byte.class, Number.class), + SMALLINT(Types.SMALLINT, Short.class, Number.class), + INTEGER(Types.INTEGER, Integer.class, Number.class), + BIGINT(Types.BIGINT, Long.class, Number.class), + REAL(Types.REAL, Float.class, Number.class), + FLOAT(Types.FLOAT, Double.class, Number.class), + DOUBLE(Types.DOUBLE, Double.class, Number.class), + NUMERIC(Types.NUMERIC, BigDecimal.class, Number.class), + DECIMAL(Types.DECIMAL, BigDecimal.class, Number.class), CHAR(Types.CHAR, Character.class, JDBCType::isChar), //teradata !char VARCHAR(Types.VARCHAR, String.class, JDBCType::isString), NVARCHAR(Types.NVARCHAR, String.class, JDBCType::isString), LONGNVARCHAR(Types.LONGNVARCHAR, String.class, JDBCType::isString), - DATE(Types.DATE, Date.class, Date.class::isInstance), - TIME(Types.TIME, Time.class, Time.class::isInstance), - TIMESTAMP(Types.TIMESTAMP, Timestamp.class, Timestamp.class::isInstance), - TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class, Timestamp.class::isInstance), - OTHER(Types.OTHER, Object.class, null) { //readonly + DATE(Types.DATE, Date.class), + TIME(Types.TIME, Time.class), + TIMESTAMP(Types.TIMESTAMP, Timestamp.class), + TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE, Timestamp.class), + OTHER(Types.OTHER, Object.class) { //readonly @Override public boolean accept(Object o) { return false; @@ -59,27 +52,39 @@ public boolean accept(Object o) { private final int value; private final Class type; - private final Class superType; - private final Predicate matcher; + private final Predicate> typeMatcher; + private final Predicate valueMatcher; + + private JDBCType(int value, Class type) { + this(value, type, type); + } - private JDBCType(int value, Class type, Predicate matcher) { - this(value, type, type, matcher); + private JDBCType(int value, Class type, Class supr) { //same parent + this(value, type, supr::isAssignableFrom, supr::isInstance); } - @Override - public Class typeClass() { + private JDBCType(int value, Class type, Predicate valueMatcher) { + this(value, type, type::isAssignableFrom, valueMatcher); + } + + public Class getCorrespondingClass() { return type; } + public int getValue() { + return value; + } + @Override public boolean accept(Object o) { if(o instanceof Typed v) { var t = v.getType(); - return t == this || isNull(t) || superType.isAssignableFrom(t.typeClass()); + return t == this || isNull(t) || typeMatcher.test(t.getCorrespondingClass()); } - return isNull(o) || matcher.test(o); + return isNull(o) || valueMatcher.test(o); } + private static boolean isBoolean(Object o) { return o.getClass() == Boolean.class || o.equals(0) || o.equals(1) @@ -97,22 +102,16 @@ private static boolean isString(Object o) { } public static Optional typeOf(Object o) { - if(o instanceof Typed to) { - return ofNullable(to.getType()); - } - return ofNullable(o).flatMap(v-> findType(e-> e.typeClass().isInstance(o))); + return o instanceof Typed t + ? ofNullable(t.getType()) + : ofNullable(o).flatMap(v-> findType(e-> e.getCorrespondingClass().isInstance(o))); } public static Optional fromDataType(int value) { return findType(t-> t.value == value); } - public static Optional findType(Predicate predicate) { - for(var t : values()) { - if(predicate.test(t)) { - return Optional.of(t); - } - } - return empty(); + public static Optional findType(Predicate pre) { + return Stream.of(values()).filter(pre).findAny(); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/JQueryType.java b/src/main/java/org/usf/jquery/core/JQueryType.java index 2528fa15..caa90389 100644 --- a/src/main/java/org/usf/jquery/core/JQueryType.java +++ b/src/main/java/org/usf/jquery/core/JQueryType.java @@ -12,7 +12,7 @@ public enum JQueryType implements JavaType { VIEW(DBView.class), COLUMN(DBColumn.class), - NAMED_COLUMN(TaggableColumn.class), + NAMED_COLUMN(NamedColumn.class), QUERY_COLUMN(QueryColumn.class), FILTER(DBFilter.class), ORDER(DBOrder.class), @@ -21,9 +21,9 @@ public enum JQueryType implements JavaType { PARTITION(Partition.class); private final Class type; - + @Override - public Class typeClass() { + public Class getCorrespondingClass() { return type; } } diff --git a/src/main/java/org/usf/jquery/core/JavaType.java b/src/main/java/org/usf/jquery/core/JavaType.java index 85af9f06..46564145 100644 --- a/src/main/java/org/usf/jquery/core/JavaType.java +++ b/src/main/java/org/usf/jquery/core/JavaType.java @@ -10,10 +10,10 @@ @FunctionalInterface public interface JavaType { - Class typeClass(); + Class getCorrespondingClass(); default boolean accept(Object o) { - return isNull(o) || typeClass().isInstance(o); + return isNull(o) || getCorrespondingClass().isInstance(o); } public interface Typed { diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index 578d44f5..eabf82a1 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -1,38 +1,22 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; - -import java.util.Objects; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; +import static java.util.Objects.nonNull; +import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; /** * * @author u$f * */ -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public final class NamedColumn implements TaggableColumn { - - @Delegate - private final DBColumn column; - private final String tag; //nullable - //+ type +public interface NamedColumn extends DBColumn { - @Override - public String tagname() { - return tag; - } - - @Override - public NamedColumn as(String name) { // map - return Objects.equals(name, tag) ? this : new NamedColumn(column, name); - } + String getTag(); - @Override - public String toString() { - return this.sqlWithTag(addWithValue()); + default String sqlWithTag(QueryParameterBuilder builder) { + var s = sql(builder); + if(nonNull(getTag())) { + s += " AS " + doubleQuote(getTag()); + } + return s; } -} +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 864a5cb9..06c9bb72 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -57,5 +57,4 @@ boolean isOverFunction() { public String toString() { return sql(addWithValue()); } - } diff --git a/src/main/java/org/usf/jquery/core/QueryColumn.java b/src/main/java/org/usf/jquery/core/QueryColumn.java index df2094f6..84c8cabd 100644 --- a/src/main/java/org/usf/jquery/core/QueryColumn.java +++ b/src/main/java/org/usf/jquery/core/QueryColumn.java @@ -21,7 +21,7 @@ public final class QueryColumn implements DBColumn { @Override public String sql(QueryParameterBuilder builder) { - requireNArgs(1, query.getBuilder().getColumns().toArray(), ()-> "require only one column: " + query); + requireNArgs(1, query.getBuilder().getColumns().toArray(), QueryColumn.class::getSimpleName); return query.sql(builder); } diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java index b32c2c42..28544761 100644 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -12,7 +12,7 @@ */ public interface QueryContext { - Optional lookupDeclaredColumn(String name); + Optional lookupDeclaredColumn(String name); QueryView overView(DBView view, Supplier supp); diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java index 16a9ff2c..f9ea292e 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java @@ -36,7 +36,7 @@ @Getter public class RequestQueryBuilder implements QueryContext { - private final List columns = new ArrayList<>(); + private final List columns = new ArrayList<>(); private final List filters = new ArrayList<>(); //WHERE & HAVING private final List orders = new ArrayList<>(); private final List joins = new ArrayList<>(); @@ -55,10 +55,10 @@ public RequestQueryBuilder(Database target) { } @Override - public Optional lookupDeclaredColumn(String name) { + public Optional lookupDeclaredColumn(String name) { return columns.stream() - .filter(NamedColumn.class::isInstance) - .filter(c-> name.equals(c.tagname())) + .filter(ColumnProxy.class::isInstance) + .filter(c-> name.equals(c.getTag())) .findAny(); } @@ -67,7 +67,7 @@ public QueryView overView(DBView view, Supplier supp) { return overView.computeIfAbsent(view, k-> supp.get()); } - public RequestQueryBuilder columns(@NonNull TaggableColumn... columns) { + public RequestQueryBuilder columns(@NonNull NamedColumn... columns) { addAll(this.columns, columns); return this; } @@ -189,7 +189,7 @@ void groupBy(SqlStringBuilder sb, QueryParameterBuilder pb){ var expr = columns.stream() .filter(not(DBColumn::isAggregation)) .flatMap(DBColumn::groupKeys) - .map(c-> !(c instanceof ViewColumn) && columns.contains(c) ? ((TaggableColumn)c).tagname() : c.sql(pb)) //add alias + .map(c-> !(c instanceof ViewColumn) && columns.contains(c) ? ((NamedColumn)c).getTag() : c.sql(pb)) //add alias .collect(joining(SCOMA)); if(!expr.isEmpty()) { sb.append(" GROUP BY ").append(expr); diff --git a/src/main/java/org/usf/jquery/core/TaggableColumn.java b/src/main/java/org/usf/jquery/core/TaggableColumn.java deleted file mode 100644 index 3f68dc76..00000000 --- a/src/main/java/org/usf/jquery/core/TaggableColumn.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.usf.jquery.core; - -import static java.util.Objects.nonNull; -import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; - -/** - * - * @author u$f - * - */ -public interface TaggableColumn extends DBColumn { - - String tagname(); //JSON & TAG - - default String sqlWithTag(QueryParameterBuilder builder) { - var s = sql(builder); - if(nonNull(tagname())) { - s += " AS " + doubleQuote(tagname()); - } - return s; - } -} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index b3e96733..cc716677 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -14,36 +14,18 @@ */ @Getter @RequiredArgsConstructor -public final class ViewColumn implements TaggableColumn { +public final class ViewColumn implements NamedColumn { - private final DBView view; //optional private final String name; - private final String tag; //optional + private final DBView view; //optional private final JDBCType type; //optional + private final String tag; //optional - public ViewColumn(String name, String tag) { - this(null, name, tag, null); - } - - public ViewColumn(DBView view, String name, String tag) { - this(view, name, tag, null); - } - @Override public String sql(QueryParameterBuilder arg) { return nonNull(view) ? member(arg.viewAlias(view), name) : name; } - @Override - public JDBCType getType() { - return type; - } - - @Override - public String tagname() { - return tag; - } - @Override public String toString() { return sql(addWithValue()); diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index 1a0f1c40..c7335d73 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -22,10 +22,6 @@ default JDBCType type(ViewDecorator vd) { return null; // auto type } -// default JDBCArgumentParser parser(ViewDecorator vd){ // override parser | format | local | validation -// return null; // auto parser -// } - default ColumnBuilder builder(ViewDecorator vd) { //set type if null return null; // no builder by default } @@ -33,6 +29,8 @@ default ColumnBuilder builder(ViewDecorator vd) { //set type if null default CriteriaBuilder criteria(String name) { return null; // no criteria by default } + +// default JDBCArgumentParser parser(ViewDecorator vd) // override parser | format | local | validation default String pattern(ViewDecorator td) { throw new UnsupportedOperationException(); //improve API security and performance diff --git a/src/main/java/org/usf/jquery/web/ColumnMetadata.java b/src/main/java/org/usf/jquery/web/ColumnMetadata.java index 1b339869..17791bef 100644 --- a/src/main/java/org/usf/jquery/web/ColumnMetadata.java +++ b/src/main/java/org/usf/jquery/web/ColumnMetadata.java @@ -54,7 +54,7 @@ ColumnMetadata reset() { } public String toJavaType(){ - var t = type.typeClass().getSimpleName(); + var t = type.getCorrespondingClass().getSimpleName(); return overConfigured ? t+"!" : t; } @@ -64,10 +64,10 @@ public String toSqlType(){ s+="!"; } else { - if(type.typeClass() == String.class && dataSize < MAX_VALUE) { + if(type.getCorrespondingClass() == String.class && dataSize < MAX_VALUE) { s+= "(" + dataSize + ")"; } - if(type.typeClass() == Timestamp.class) { + if(type.getCorrespondingClass() == Timestamp.class) { s+= "(" + precision + ")"; } if(type == REAL || type == NUMERIC || type == DECIMAL || type == FLOAT || type == DOUBLE) { diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index d865b817..74430937 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -4,7 +4,7 @@ import org.usf.jquery.core.DBView; import org.usf.jquery.core.QueryView; -import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.NamedColumn; import org.usf.jquery.core.ViewColumn; import lombok.Getter; @@ -33,9 +33,9 @@ public DBView view() { return query; } - public TaggableColumn column(String id) { + public NamedColumn column(String id) { return query.getBuilder().lookupDeclaredColumn(id) - .map(c-> new ViewColumn(query, c.tagname(), c.tagname(), c.getType())) + .map(c-> new ViewColumn(c.getTag(), query, c.getType(), null)) .orElseThrow(()-> undeclaredResouceException(id, identity())); } @@ -45,7 +45,7 @@ public String columnName(ColumnDecorator cd) { } @Override - public TaggableColumn column(@NonNull ColumnDecorator cd) { + public NamedColumn column(@NonNull ColumnDecorator cd) { throw unsupportedOperationException("column"); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 6ea175f7..d6c11e99 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -60,7 +60,7 @@ import org.usf.jquery.core.QueryContext; import org.usf.jquery.core.QueryView; import org.usf.jquery.core.RequestQueryBuilder; -import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.NamedColumn; import org.usf.jquery.core.TypedComparator; import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; @@ -209,7 +209,7 @@ public DBColumn evalColumn(ViewDecorator td, QueryContext ctx, boolean requireTa if(nonNull(r.entry.tag)) { return r.col.as(r.entry.tag); } - if(!requireTag || r.col instanceof TaggableColumn) { + if(!requireTag || r.col instanceof NamedColumn) { return r.col; } throw expectedEntryTagException(r.entry); @@ -293,15 +293,17 @@ private ViewResource chainColumnOperations(ViewDecorator vd, QueryContext ctx, b var e = r.entry.next; while(nonNull(e)) { //chain until !operator var res = lookupOperator(e.value); - if(res.isEmpty()) { + if(res.isPresent()) { + var fn = res.get(); + var o = fn.operation(e.toArgs(vd, ctx, r.col, fn.getParameterSet())); + r.cd = null; + r.entry = e; + r.col = filter && "over".equals(e.value) ? windowColumn(r.vd, ctx, o) : o; + e = e.next; + } + else { break; } - var fn = res.get(); - var o = fn.operation(e.toArgs(vd, ctx, r.col, fn.getParameterSet())); - r.cd = null; - r.entry = e; - r.col = filter && "over".equals(e.value) ? windowColumn(r.vd, ctx, o) : o; - e = e.next; } } //else filter return r; @@ -311,7 +313,7 @@ private static DBColumn windowColumn(ViewDecorator vd, QueryContext ctx, DBColum var v = vd.view(); var tag = "over_" + vd.identity() + "_" + col.hashCode(); //over_view_hash ctx.overView(v).getBuilder().columns(col.as(tag)); //append over colum - return new ViewColumn(v, doubleQuote(tag), null, col.getType()); + return new ViewColumn(doubleQuote(tag), v, col.getType(), null); } //view.resource | resource @@ -386,8 +388,8 @@ private Optional lookupColumnResource(ViewDecorator td, boolean fi return empty(); } - private TaggableColumn[] taggableVarargs(ViewDecorator td, QueryContext ctx) { - return (TaggableColumn[]) typeVarargs(td, ctx, JQueryType.NAMED_COLUMN); + private NamedColumn[] taggableVarargs(ViewDecorator td, QueryContext ctx) { + return (NamedColumn[]) typeVarargs(td, ctx, JQueryType.NAMED_COLUMN); } private DBColumn[] columnVarargs(ViewDecorator td, QueryContext ctx) { @@ -403,7 +405,7 @@ private DBFilter[] filterVarargs(ViewDecorator td, QueryContext ctx) { } private Object[] typeVarargs(ViewDecorator td, QueryContext ctx, JavaType type) { - var c = type.typeClass(); + var c = type.getCorrespondingClass(); if(DBObject.class.isAssignableFrom(c)) { // JQuery types & !array var ps = ofParameters(required(type), varargs(type)); return toArgs(td, ctx, null, ps, s-> (Object[]) newInstance(c, s)); diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 7c8beab0..225bd17f 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -33,7 +33,7 @@ import org.usf.jquery.core.DBView; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TableView; -import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.NamedColumn; import org.usf.jquery.core.ViewColumn; import lombok.NonNull; @@ -69,14 +69,14 @@ default DBView view() { return metadata().getView(); } - default TaggableColumn column(@NonNull ColumnDecorator cd) {//final + default NamedColumn column(@NonNull ColumnDecorator cd) {//final var meta = metadata().columnMetadata(cd); if(nonNull(meta)) { - return new ViewColumn(view(), meta.getName(), cd.reference(this), meta.getType()); + return new ViewColumn(meta.getName(), view(), meta.getType(), cd.reference(this)); } var b = cd.builder(this); if(nonNull(b)) { - return b.build(this).as(cd.reference(this)); //set type + return b.build(this).as(cd.reference(this), cd.type(this)); } throw undeclaredResouceException(cd.identity(), identity()); } @@ -140,7 +140,7 @@ default void parseColumns(RequestQueryBuilder query, Map param if(!isEmpty(cols)) { Stream.of(cols) .flatMap(v-> parseEntries(v).stream()) - .map(e-> (TaggableColumn) e.evalColumn(this, query, true)) + .map(e-> (NamedColumn) e.evalColumn(this, query, true)) .forEach(query::columns); } else { diff --git a/src/main/java/org/usf/jquery/web/YearViewDecorator.java b/src/main/java/org/usf/jquery/web/YearViewDecorator.java index bcac1986..daf427c0 100644 --- a/src/main/java/org/usf/jquery/web/YearViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearViewDecorator.java @@ -28,7 +28,7 @@ import org.usf.jquery.core.DBView; import org.usf.jquery.core.RequestQueryBuilder; import org.usf.jquery.core.TableView; -import org.usf.jquery.core.TaggableColumn; +import org.usf.jquery.core.NamedColumn; /** * @@ -55,7 +55,7 @@ default DBView view() { } @Override - default TaggableColumn column(ColumnDecorator column) { + default NamedColumn column(ColumnDecorator column) { var cd = yearRevision(); return cd.equals(column) ? yearColumn().as(cd.reference(this)) From 196996a45bc349f95637a1713baca644a9aa884b Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 5 Sep 2024 10:41:21 +0200 Subject: [PATCH 223/298] edit --- .../usf/jquery/core/ArithmeticOperator.java | 2 +- .../org/usf/jquery/core/BasicComparator.java | 2 +- .../java/org/usf/jquery/core/CaseColumn.java | 4 +- .../org/usf/jquery/core/CastFunction.java | 2 +- .../usf/jquery/core/ColumnFilterGroup.java | 4 +- .../java/org/usf/jquery/core/ColumnProxy.java | 4 +- .../usf/jquery/core/ColumnSingleFilter.java | 4 +- .../org/usf/jquery/core/CombinedOperator.java | 2 +- .../usf/jquery/core/ComparisonExpression.java | 4 +- .../core/ComparisonExpressionGroup.java | 4 +- .../core/ComparisonSingleExpression.java | 4 +- .../org/usf/jquery/core/ConstantOperator.java | 2 +- .../java/org/usf/jquery/core/DBColumn.java | 8 +-- .../java/org/usf/jquery/core/DBFilter.java | 4 +- .../java/org/usf/jquery/core/DBObject.java | 2 +- .../java/org/usf/jquery/core/DBOrder.java | 6 +- src/main/java/org/usf/jquery/core/DBView.java | 6 +- .../org/usf/jquery/core/ExtractFunction.java | 2 +- .../org/usf/jquery/core/FunctionOperator.java | 2 +- .../org/usf/jquery/core/InComparator.java | 2 +- .../java/org/usf/jquery/core/NamedColumn.java | 2 +- .../org/usf/jquery/core/NullComparator.java | 2 +- .../org/usf/jquery/core/OperationColumn.java | 4 +- .../java/org/usf/jquery/core/Partition.java | 4 +- .../org/usf/jquery/core/PipeFunction.java | 2 +- ...estQueryBuilder.java => QueryBuilder.java} | 56 +++++++++---------- .../java/org/usf/jquery/core/QueryColumn.java | 4 +- .../org/usf/jquery/core/QueryContext.java | 4 +- ...ameterBuilder.java => QueryVariables.java} | 24 ++++---- .../java/org/usf/jquery/core/QueryView.java | 6 +- .../org/usf/jquery/core/RangeComparator.java | 2 +- .../org/usf/jquery/core/StringComparator.java | 2 +- .../java/org/usf/jquery/core/TableView.java | 6 +- .../java/org/usf/jquery/core/ViewColumn.java | 6 +- .../java/org/usf/jquery/core/ViewJoin.java | 10 ++-- .../org/usf/jquery/core/WhenExpression.java | 12 ++-- .../org/usf/jquery/web/RequestEntryChain.java | 4 +- ...ver.java => RequestParameterResolver.java} | 6 +- .../org/usf/jquery/web/RevisionIterator.java | 4 +- .../org/usf/jquery/web/ViewDecorator.java | 20 +++---- .../java/org/usf/jquery/web/ViewMetadata.java | 4 +- .../org/usf/jquery/web/YearViewDecorator.java | 4 +- .../usf/jquery/core/FunctionOperatorTest.java | 2 +- 43 files changed, 129 insertions(+), 131 deletions(-) rename src/main/java/org/usf/jquery/core/{RequestQueryBuilder.java => QueryBuilder.java} (80%) rename src/main/java/org/usf/jquery/core/{QueryParameterBuilder.java => QueryVariables.java} (77%) rename src/main/java/org/usf/jquery/web/{RequestQueryParamResolver.java => RequestParameterResolver.java} (87%) diff --git a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java index 2d3cca12..4b784415 100644 --- a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java +++ b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java @@ -11,7 +11,7 @@ interface ArithmeticOperator extends Operator { @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireNArgs(2, args, ArithmeticException.class::getSimpleName); return "(" + builder.appendLiteral(args[0]) + id() + builder.appendLiteral(args[1]) + ")"; } diff --git a/src/main/java/org/usf/jquery/core/BasicComparator.java b/src/main/java/org/usf/jquery/core/BasicComparator.java index 3c59402c..9dcd9137 100644 --- a/src/main/java/org/usf/jquery/core/BasicComparator.java +++ b/src/main/java/org/usf/jquery/core/BasicComparator.java @@ -11,7 +11,7 @@ public interface BasicComparator extends Comparator { @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireNArgs(2, args, BasicComparator.class::getSimpleName); return builder.appendParameter(args[0]) + id() + builder.appendParameter(args[1]); } diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 91ef97ce..9a89fbd8 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import java.util.ArrayList; @@ -22,7 +22,7 @@ public final class CaseColumn implements DBColumn { // TD override isAggregation private final Collection expressions = new ArrayList<>(); @Override - public String sql(QueryParameterBuilder builder) { + public String sql(QueryVariables builder) { var b = builder.withValue(); //force literal parameter return expressions.stream() //empty !? .map(o-> o.sql(b)) diff --git a/src/main/java/org/usf/jquery/core/CastFunction.java b/src/main/java/org/usf/jquery/core/CastFunction.java index a5c39ac5..76be987b 100644 --- a/src/main/java/org/usf/jquery/core/CastFunction.java +++ b/src/main/java/org/usf/jquery/core/CastFunction.java @@ -18,7 +18,7 @@ default String id() { } @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireAtLeastNArgs(1, args, CastFunction.class::getSimpleName); var sb = new SqlStringBuilder(id()) .append("(") diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index 50243947..c8dfec7d 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -2,7 +2,7 @@ import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Utils.appendLast; import java.util.stream.Stream; @@ -24,7 +24,7 @@ public final class ColumnFilterGroup implements DBFilter { } @Override - public String sql(QueryParameterBuilder builder) { + public String sql(QueryVariables builder) { return Stream.of(filters) .map(o-> o.sql(builder)) .collect(joining(operator.sql(), "(", ")")); diff --git a/src/main/java/org/usf/jquery/core/ColumnProxy.java b/src/main/java/org/usf/jquery/core/ColumnProxy.java index 50ee62c1..2eaa9825 100644 --- a/src/main/java/org/usf/jquery/core/ColumnProxy.java +++ b/src/main/java/org/usf/jquery/core/ColumnProxy.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import java.util.Objects; @@ -28,7 +28,7 @@ public JDBCType getType() { } @Override - public String sql(QueryParameterBuilder builder) { + public String sql(QueryVariables builder) { return column.sql(builder); } diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index 5de65f74..12c60ac4 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; import static org.usf.jquery.core.Nested.aggregation; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -18,7 +18,7 @@ public final class ColumnSingleFilter implements DBFilter { private final ComparisonExpression expression; @Override - public String sql(QueryParameterBuilder ph) { + public String sql(QueryVariables ph) { return expression.sql(ph, left); } diff --git a/src/main/java/org/usf/jquery/core/CombinedOperator.java b/src/main/java/org/usf/jquery/core/CombinedOperator.java index 79300396..2328b75d 100644 --- a/src/main/java/org/usf/jquery/core/CombinedOperator.java +++ b/src/main/java/org/usf/jquery/core/CombinedOperator.java @@ -25,7 +25,7 @@ default String id() { } @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { throw new UnsupportedOperationException("CombinedOperator::sql"); } } diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpression.java b/src/main/java/org/usf/jquery/core/ComparisonExpression.java index eabd1cc0..94b8c6df 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpression.java @@ -11,10 +11,10 @@ */ public interface ComparisonExpression extends DBExpression, Nested, Chainable { - String sql(QueryParameterBuilder builder, Object left); // do change method order + String sql(QueryVariables builder, Object left); // do change method order @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireNArgs(1, args, ComparisonExpression.class::getSimpleName); return sql(builder, args[0]); } diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index cf227915..915098ec 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Utils.appendLast; import java.util.stream.Stream; @@ -23,7 +23,7 @@ public final class ComparisonExpressionGroup implements ComparisonExpression { } @Override - public String sql(QueryParameterBuilder builder, Object operand) { + public String sql(QueryVariables builder, Object operand) { return Stream.of(expressions) .map(o-> o.sql(builder, operand)) .collect(joining(operator.sql(), "(", ")")); diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index d2843ef1..9fb6f11c 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -2,7 +2,7 @@ import static java.util.Collections.addAll; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import java.util.ArrayList; import java.util.stream.Stream; @@ -22,7 +22,7 @@ public final class ComparisonSingleExpression implements ComparisonExpression { private final Object[] right; //nullable @Override - public String sql(QueryParameterBuilder builder, Object left) { + public String sql(QueryVariables builder, Object left) { var param = new ArrayList<>(); param.add(left); if(nonNull(right)) { diff --git a/src/main/java/org/usf/jquery/core/ConstantOperator.java b/src/main/java/org/usf/jquery/core/ConstantOperator.java index a10d49c4..3e4f2a08 100644 --- a/src/main/java/org/usf/jquery/core/ConstantOperator.java +++ b/src/main/java/org/usf/jquery/core/ConstantOperator.java @@ -11,7 +11,7 @@ public interface ConstantOperator extends Operator { @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireNoArgs(args, ConstantOperator.class::getSimpleName); return id(); //use parentheses !? } diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index d4346d3c..1f364bd1 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -2,7 +2,7 @@ import static org.usf.jquery.core.Order.ASC; import static org.usf.jquery.core.Order.DESC; -import static org.usf.jquery.core.QueryParameterBuilder.formatValue; +import static org.usf.jquery.core.QueryVariables.formatValue; import static org.usf.jquery.core.Utils.appendFirst; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -24,10 +24,10 @@ @FunctionalInterface public interface DBColumn extends DBObject, Typed, Groupable { - String sql(QueryParameterBuilder builder); + String sql(QueryVariables builder); @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireNoArgs(args, DBColumn.class::getSimpleName); return sql(builder); } @@ -453,7 +453,7 @@ static DBColumn constant(Supplier value) { return new DBColumn() { @Override - public String sql(QueryParameterBuilder arg) { + public String sql(QueryVariables arg) { return formatValue(value.get()); //lazy } diff --git a/src/main/java/org/usf/jquery/core/DBFilter.java b/src/main/java/org/usf/jquery/core/DBFilter.java index 542e8f9a..bb55c44a 100644 --- a/src/main/java/org/usf/jquery/core/DBFilter.java +++ b/src/main/java/org/usf/jquery/core/DBFilter.java @@ -10,10 +10,10 @@ @FunctionalInterface public interface DBFilter extends DBObject, Nested, Chainable { - String sql(QueryParameterBuilder builder); + String sql(QueryVariables builder); @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireNoArgs(args, DBFilter.class::getSimpleName); return sql(builder); } diff --git a/src/main/java/org/usf/jquery/core/DBObject.java b/src/main/java/org/usf/jquery/core/DBObject.java index 43f3ff35..76c0abfe 100644 --- a/src/main/java/org/usf/jquery/core/DBObject.java +++ b/src/main/java/org/usf/jquery/core/DBObject.java @@ -8,6 +8,6 @@ @FunctionalInterface public interface DBObject { - String sql(QueryParameterBuilder builder, Object[] args); + String sql(QueryVariables builder, Object[] args); } diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index 5384a305..b586473b 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; import static java.util.Objects.isNull; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -26,12 +26,12 @@ public DBOrder(DBColumn column) { } @Override - public String sql(QueryParameterBuilder builder, Object[] args) { + public String sql(QueryVariables builder, Object[] args) { requireNoArgs(args, DBOrder.class::getSimpleName); return sql(builder); } - public String sql(QueryParameterBuilder builder) { + public String sql(QueryVariables builder) { return isNull(order) ? column.sql(builder) : column.sql(builder) + SPACE + order.name(); diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 70e26e5e..efd60704 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -12,15 +12,15 @@ @FunctionalInterface public interface DBView extends DBObject { - String sql(QueryParameterBuilder builder); + String sql(QueryVariables builder); @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireNoArgs(args, DBView.class::getSimpleName); return sql(builder); } - default String sqlWithTag(QueryParameterBuilder builder) { + default String sqlWithTag(QueryVariables builder) { var tag = builder.viewAlias(this); var sql = builder.viewOverload(this).sql(builder); //!important return isNull(tag) ? sql : sql + SPACE + tag; diff --git a/src/main/java/org/usf/jquery/core/ExtractFunction.java b/src/main/java/org/usf/jquery/core/ExtractFunction.java index 71ab1ce1..8b944064 100644 --- a/src/main/java/org/usf/jquery/core/ExtractFunction.java +++ b/src/main/java/org/usf/jquery/core/ExtractFunction.java @@ -18,7 +18,7 @@ default String id() { } @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireNArgs(1, args, ExtractFunction.class::getSimpleName); return id() + "(" + field() + " FROM " + builder.appendLiteral(args[0]) + ")"; } diff --git a/src/main/java/org/usf/jquery/core/FunctionOperator.java b/src/main/java/org/usf/jquery/core/FunctionOperator.java index d9cc99bc..17bb1cda 100644 --- a/src/main/java/org/usf/jquery/core/FunctionOperator.java +++ b/src/main/java/org/usf/jquery/core/FunctionOperator.java @@ -10,7 +10,7 @@ public interface FunctionOperator extends Operator { @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { return new SqlStringBuilder(id()) .append("(").append(builder.appendLiteralArray(args)).append(")") //accept any .toString(); diff --git a/src/main/java/org/usf/jquery/core/InComparator.java b/src/main/java/org/usf/jquery/core/InComparator.java index 415a378a..8a127313 100644 --- a/src/main/java/org/usf/jquery/core/InComparator.java +++ b/src/main/java/org/usf/jquery/core/InComparator.java @@ -13,7 +13,7 @@ public interface InComparator extends Comparator { @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireAtLeastNArgs(2, args, InComparator.class::getSimpleName); return builder.appendParameter(args[0]) + SPACE + id() + parenthese(builder.appendArrayParameter(args, 1)); } diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index eabf82a1..9819102b 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -12,7 +12,7 @@ public interface NamedColumn extends DBColumn { String getTag(); - default String sqlWithTag(QueryParameterBuilder builder) { + default String sqlWithTag(QueryVariables builder) { var s = sql(builder); if(nonNull(getTag())) { s += " AS " + doubleQuote(getTag()); diff --git a/src/main/java/org/usf/jquery/core/NullComparator.java b/src/main/java/org/usf/jquery/core/NullComparator.java index 8b6b7555..362d5576 100644 --- a/src/main/java/org/usf/jquery/core/NullComparator.java +++ b/src/main/java/org/usf/jquery/core/NullComparator.java @@ -12,7 +12,7 @@ public interface NullComparator extends Comparator { @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireNArgs(1, args, NullComparator.class::getSimpleName); return builder.appendParameter(args[0]) + SPACE + id(); } diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 06c9bb72..e00e988a 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import java.util.stream.Stream; @@ -24,7 +24,7 @@ public OperationColumn(Operator operation, Object[] args) { } @Override - public String sql(QueryParameterBuilder builder) { + public String sql(QueryVariables builder) { return operator.sql(builder, args); } diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index 411e0f32..e2978b67 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -21,12 +21,12 @@ public final class Partition implements DBObject, Groupable { private final DBOrder[] orders; @Override - public String sql(QueryParameterBuilder builder, Object[] args) { + public String sql(QueryVariables builder, Object[] args) { requireNoArgs(args, Partition.class::getSimpleName); return sql(builder); } - String sql(QueryParameterBuilder builder) { + String sql(QueryVariables builder) { var sb = new SqlStringBuilder(100); if(!isEmpty(columns)) { sb.append("PARTITION BY ").append(builder.appendLiteralArray(columns)); diff --git a/src/main/java/org/usf/jquery/core/PipeFunction.java b/src/main/java/org/usf/jquery/core/PipeFunction.java index e6adc249..d341ebec 100644 --- a/src/main/java/org/usf/jquery/core/PipeFunction.java +++ b/src/main/java/org/usf/jquery/core/PipeFunction.java @@ -13,7 +13,7 @@ public interface PipeFunction extends FunctionOperator { @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireAtLeastNArgs(1, args, PipeFunction.class::getSimpleName); return builder.appendLiteral(args[0]) + SPACE + FunctionOperator.super.sql(builder, copyOfRange(args, 1, args.length)); diff --git a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java similarity index 80% rename from src/main/java/org/usf/jquery/core/RequestQueryBuilder.java rename to src/main/java/org/usf/jquery/core/QueryBuilder.java index f9ea292e..1abb7115 100644 --- a/src/main/java/org/usf/jquery/core/RequestQueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -10,7 +10,7 @@ import static org.usf.jquery.core.Database.currentDatabase; import static org.usf.jquery.core.Database.setCurrentDatabase; import static org.usf.jquery.core.LogicalOperator.AND; -import static org.usf.jquery.core.QueryParameterBuilder.parametrized; +import static org.usf.jquery.core.QueryVariables.parameterized; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNonEmpty; @@ -34,23 +34,23 @@ */ @Slf4j @Getter -public class RequestQueryBuilder implements QueryContext { +public class QueryBuilder implements QueryContext { private final List columns = new ArrayList<>(); private final List filters = new ArrayList<>(); //WHERE & HAVING private final List orders = new ArrayList<>(); private final List joins = new ArrayList<>(); private final Map overView = new HashMap<>(); - private Iterator it; private boolean distinct; private Integer fetch; private Integer offset; + private Iterator it; - public RequestQueryBuilder() { + public QueryBuilder() { this(null); } - public RequestQueryBuilder(Database target) { + public QueryBuilder(Database target) { setCurrentDatabase(target); } @@ -67,45 +67,45 @@ public QueryView overView(DBView view, Supplier supp) { return overView.computeIfAbsent(view, k-> supp.get()); } - public RequestQueryBuilder columns(@NonNull NamedColumn... columns) { - addAll(this.columns, columns); + public QueryBuilder columns(@NonNull NamedColumn... columns) { + addAll(this.columns, columns); //add only if !exits return this; } - public RequestQueryBuilder filters(@NonNull DBFilter... filters){ + public QueryBuilder filters(@NonNull DBFilter... filters){ addAll(this.filters, filters); return this; } - public RequestQueryBuilder orders(@NonNull DBOrder... orders) { + public QueryBuilder orders(@NonNull DBOrder... orders) { addAll(this.orders, orders); return this; } - public RequestQueryBuilder joins(@NonNull ViewJoin... joins) { + public QueryBuilder joins(@NonNull ViewJoin... joins) { addAll(this.joins, joins); return this; } - public RequestQueryBuilder repeat(@NonNull Iterator it) { - this.it = it; - return this; - } - - public RequestQueryBuilder fetch(int fetch) { + public QueryBuilder fetch(int fetch) { this.fetch = fetch; return this; } - public RequestQueryBuilder offset(int offset) { + public QueryBuilder offset(int offset) { this.offset = offset; return this; } - public RequestQueryBuilder distinct() { + public QueryBuilder distinct() { distinct = true; return this; } + + public QueryBuilder repeat(@NonNull Iterator it) { + this.it = it; + return this; + } public QueryView asView() { return new QueryView(this); @@ -119,7 +119,7 @@ public RequestQuery build(String schema) { log.trace("building query..."); requireNonEmpty(columns, "columns"); var bg = currentTimeMillis(); - var pb = parametrized(schema, overView); //over clause + var pb = parameterized(schema, overView); //over clause var sb = new SqlStringBuilder(1000); //avg if(isNull(it)) { build(sb, pb); @@ -131,7 +131,7 @@ public RequestQuery build(String schema) { return new RequestQuery(sb.toString(), pb.args(), pb.argTypes()); } - public final void build(SqlStringBuilder sb, QueryParameterBuilder pb){ + public final void build(SqlStringBuilder sb, QueryVariables pb){ var sub = new SqlStringBuilder(100); join(sub, pb); where(sub, pb); @@ -140,11 +140,11 @@ public final void build(SqlStringBuilder sb, QueryParameterBuilder pb){ orderBy(sub, pb); fetch(sub); select(sb, pb); - from(sb, pb); //enumerate all view before from clause + from(sb, pb); //enumerate all views before from clause sb.append(sub.toString()); //TODO optim } - void select(SqlStringBuilder sb, QueryParameterBuilder pb){ + void select(SqlStringBuilder sb, QueryVariables pb){ if(currentDatabase() == TERADATA) { if(nonNull(offset)) { throw new UnsupportedOperationException(""); @@ -160,7 +160,7 @@ void select(SqlStringBuilder sb, QueryParameterBuilder pb){ .appendEach(columns, SCOMA, o-> o.sqlWithTag(pb)); } - void from(SqlStringBuilder sb, QueryParameterBuilder pb) { + void from(SqlStringBuilder sb, QueryVariables pb) { var excludes = joins.stream().map(ViewJoin::getView).map(pb::viewOverload).toList(); var views = pb.views().stream().filter(not(excludes::contains)).toList(); //do not remove views if(!views.isEmpty()) { @@ -168,13 +168,13 @@ void from(SqlStringBuilder sb, QueryParameterBuilder pb) { } } - void join(SqlStringBuilder sb, QueryParameterBuilder pb) { + void join(SqlStringBuilder sb, QueryVariables pb) { if(!joins.isEmpty()) { sb.append(SPACE).appendEach(joins, SPACE, v-> v.sql(pb)); } } - void where(SqlStringBuilder sb, QueryParameterBuilder pb){ + void where(SqlStringBuilder sb, QueryVariables pb){ var expr = filters.stream() .filter(not(DBFilter::isAggregation)) .map(f-> f.sql(pb)) @@ -184,7 +184,7 @@ void where(SqlStringBuilder sb, QueryParameterBuilder pb){ } } - void groupBy(SqlStringBuilder sb, QueryParameterBuilder pb){ + void groupBy(SqlStringBuilder sb, QueryVariables pb){ if(isAggregation()) { // also check filter var expr = columns.stream() .filter(not(DBColumn::isAggregation)) @@ -197,7 +197,7 @@ void groupBy(SqlStringBuilder sb, QueryParameterBuilder pb){ } } - void having(SqlStringBuilder sb, QueryParameterBuilder pb){ + void having(SqlStringBuilder sb, QueryVariables pb){ var having = filters.stream() .filter(DBFilter::isAggregation) .toList(); @@ -207,7 +207,7 @@ void having(SqlStringBuilder sb, QueryParameterBuilder pb){ } } - void orderBy(SqlStringBuilder sb, QueryParameterBuilder pb) { + void orderBy(SqlStringBuilder sb, QueryVariables pb) { if(!orders.isEmpty()) { sb.append(" ORDER BY ") .appendEach(orders, SCOMA, o-> o.sql(pb)); diff --git a/src/main/java/org/usf/jquery/core/QueryColumn.java b/src/main/java/org/usf/jquery/core/QueryColumn.java index 84c8cabd..4394bb11 100644 --- a/src/main/java/org/usf/jquery/core/QueryColumn.java +++ b/src/main/java/org/usf/jquery/core/QueryColumn.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Validation.requireNArgs; import lombok.AccessLevel; @@ -20,7 +20,7 @@ public final class QueryColumn implements DBColumn { private final JDBCType type; @Override - public String sql(QueryParameterBuilder builder) { + public String sql(QueryVariables builder) { requireNArgs(1, query.getBuilder().getColumns().toArray(), QueryColumn.class::getSimpleName); return query.sql(builder); } diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java index 28544761..5a425d06 100644 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -17,8 +17,6 @@ public interface QueryContext { QueryView overView(DBView view, Supplier supp); default QueryView overView(DBView view) { - return overView(view, ()-> new RequestQueryBuilder().columns(allColumns(view)).asView()); + return overView(view, ()-> new QueryBuilder().columns(allColumns(view)).asView()); } - - //sub query filters, orders, ... } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java b/src/main/java/org/usf/jquery/core/QueryVariables.java similarity index 77% rename from src/main/java/org/usf/jquery/core/QueryParameterBuilder.java rename to src/main/java/org/usf/jquery/core/QueryVariables.java index c9510309..c8f0f93b 100644 --- a/src/main/java/org/usf/jquery/core/QueryParameterBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryVariables.java @@ -27,20 +27,20 @@ * */ @AllArgsConstructor(access = AccessLevel.PRIVATE) -public final class QueryParameterBuilder { +public final class QueryVariables { private static final String P_ARG = "?"; @Getter private final String schema; private final String vPrefix; - private final List args; //dynamic flag + private final List args; //parameterized flag private final List argTypes; private final List views; //indexed view private final Map overView; public String viewAlias(DBView view) { - view = overView.getOrDefault(view, view); + view = viewOverload(view); var idx = views.indexOf(view); if(idx < 0) { idx = views.size(); @@ -141,23 +141,23 @@ public static String formatValue(Object o) { return nonNull(o) ? quote(o.toString()) : "null"; } - public QueryParameterBuilder withValue() { - return new QueryParameterBuilder(schema, vPrefix, null, null, views, overView); + public QueryVariables withValue() { + return new QueryVariables(schema, vPrefix, null, null, views, overView); } - public QueryParameterBuilder subQuery(Map overView) { - return new QueryParameterBuilder(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), unmodifiableMap(overView)); + public QueryVariables subQuery(Map overView) { + return new QueryVariables(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), unmodifiableMap(overView)); } - public static QueryParameterBuilder addWithValue() { + public static QueryVariables addWithValue() { return addWithValue(null, emptyMap()); //no args } - public static QueryParameterBuilder addWithValue(String schema, Map overView) { - return new QueryParameterBuilder(schema, "v", null, null, new ArrayList<>(), unmodifiableMap(overView)); //no args + public static QueryVariables addWithValue(String schema, Map overView) { + return new QueryVariables(schema, "v", null, null, new ArrayList<>(), unmodifiableMap(overView)); //no args } - public static QueryParameterBuilder parametrized(String schema, Map overView) { - return new QueryParameterBuilder(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), unmodifiableMap(overView)); + public static QueryVariables parameterized(String schema, Map overView) { + return new QueryVariables(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), unmodifiableMap(overView)); } } diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index 4a067fd0..d606f5dc 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import lombok.AccessLevel; import lombok.Getter; @@ -15,10 +15,10 @@ public final class QueryView implements DBView { @Getter - private final RequestQueryBuilder builder; + private final QueryBuilder builder; @Override - public String sql(QueryParameterBuilder param) { + public String sql(QueryVariables param) { var s = new SqlStringBuilder(100).append("("); builder.build(s, param.subQuery(builder.getOverView())); return s.append(")").toString(); diff --git a/src/main/java/org/usf/jquery/core/RangeComparator.java b/src/main/java/org/usf/jquery/core/RangeComparator.java index 90c199ca..21885762 100644 --- a/src/main/java/org/usf/jquery/core/RangeComparator.java +++ b/src/main/java/org/usf/jquery/core/RangeComparator.java @@ -11,7 +11,7 @@ public interface RangeComparator extends Comparator { @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireNArgs(3, args, RangeComparator.class::getSimpleName); return builder.appendParameter(args[0]) + " " + id() + " " + builder.appendParameter(args[1]) + diff --git a/src/main/java/org/usf/jquery/core/StringComparator.java b/src/main/java/org/usf/jquery/core/StringComparator.java index 20616352..8688a54f 100644 --- a/src/main/java/org/usf/jquery/core/StringComparator.java +++ b/src/main/java/org/usf/jquery/core/StringComparator.java @@ -12,7 +12,7 @@ public interface StringComparator extends Comparator { @Override - default String sql(QueryParameterBuilder builder, Object[] args) { + default String sql(QueryVariables builder, Object[] args) { requireNArgs(2, args, StringComparator.class::getSimpleName); return builder.appendParameter(args[0]) + space(id()) + builder.appendParameter(wildcardArg(args[1])); } diff --git a/src/main/java/org/usf/jquery/core/TableView.java b/src/main/java/org/usf/jquery/core/TableView.java index 589383db..3ef011a8 100644 --- a/src/main/java/org/usf/jquery/core/TableView.java +++ b/src/main/java/org/usf/jquery/core/TableView.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.member; import lombok.Getter; @@ -25,8 +25,8 @@ public TableView(String schema, String name) { } @Override - public String sql(QueryParameterBuilder builder) { - return member(getSchemaOrElse(builder.getSchema()), name); + public String sql(QueryVariables qv) { + return member(getSchemaOrElse(qv.getSchema()), name); } public String getSchemaOrElse(String defaultSchema) { diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index cc716677..311fcfe7 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.member; import lombok.Getter; @@ -22,8 +22,8 @@ public final class ViewColumn implements NamedColumn { private final String tag; //optional @Override - public String sql(QueryParameterBuilder arg) { - return nonNull(view) ? member(arg.viewAlias(view), name) : name; + public String sql(QueryVariables qv) { + return nonNull(view) ? member(qv.viewAlias(view), name) : name; } @Override diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index 7286add6..7ed432a5 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -28,14 +28,14 @@ public final class ViewJoin implements DBObject { //join results !? @Override - public String sql(QueryParameterBuilder builder, Object[] args) { + public String sql(QueryVariables qv, Object[] args) { requireNoArgs(args, ViewJoin.class::getSimpleName); - return sql(builder); + return sql(qv); } - public String sql(QueryParameterBuilder builder) { - return joinType + " JOIN " + view.sqlWithTag(builder) + " ON " + - Stream.of(filters).map(f-> f.sql(builder)).collect(joining(AND.sql())); + public String sql(QueryVariables qv) { + return joinType + " JOIN " + view.sqlWithTag(qv) + " ON " + + Stream.of(filters).map(f-> f.sql(qv)).collect(joining(AND.sql())); } public static ViewJoin innerJoin(DBView view, DBFilter... filters) { diff --git a/src/main/java/org/usf/jquery/core/WhenExpression.java b/src/main/java/org/usf/jquery/core/WhenExpression.java index 252fd185..6e215ab9 100644 --- a/src/main/java/org/usf/jquery/core/WhenExpression.java +++ b/src/main/java/org/usf/jquery/core/WhenExpression.java @@ -2,7 +2,7 @@ import static java.util.Objects.isNull; import static org.usf.jquery.core.JDBCType.typeOf; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Validation.requireNoArgs; import org.usf.jquery.core.JavaType.Typed; @@ -21,19 +21,19 @@ final class WhenExpression implements DBExpression, Typed { private final Object value; @Override - public String sql(QueryParameterBuilder builder, Object[] args) { + public String sql(QueryVariables qv, Object[] args) { requireNoArgs(args, WhenExpression.class::getSimpleName); - return sql(builder); + return sql(qv); } - public String sql(QueryParameterBuilder arg) { + public String sql(QueryVariables qv) { var sb = new StringBuilder(50); sb = isNull(filter) ? sb.append("ELSE ") : sb.append("WHEN ") - .append(filter.sql(arg)) + .append(filter.sql(qv)) .append(" THEN "); - return sb.append(arg.appendLiteral(value)).toString(); + return sb.append(qv.appendLiteral(value)).toString(); } @Override diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index d6c11e99..759e673e 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -59,7 +59,7 @@ import org.usf.jquery.core.QueryColumn; import org.usf.jquery.core.QueryContext; import org.usf.jquery.core.QueryView; -import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.QueryBuilder; import org.usf.jquery.core.NamedColumn; import org.usf.jquery.core.TypedComparator; import org.usf.jquery.core.ViewColumn; @@ -119,7 +119,7 @@ Optional evalQuery(ViewDecorator td, QueryContext ctx, boolean r if(SELECT.equals(value)) { var e = this; try { - var q = new RequestQueryBuilder().columns(taggableVarargs(td, ctx)); + var q = new QueryBuilder().columns(taggableVarargs(td, ctx)); while(e.hasNext()) { e = e.next; switch(e.value) {//column not allowed diff --git a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java b/src/main/java/org/usf/jquery/web/RequestParameterResolver.java similarity index 87% rename from src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java rename to src/main/java/org/usf/jquery/web/RequestParameterResolver.java index 6685a547..3c339f4a 100644 --- a/src/main/java/org/usf/jquery/web/RequestQueryParamResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestParameterResolver.java @@ -13,7 +13,7 @@ import java.util.LinkedHashMap; import java.util.Map; -import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.QueryBuilder; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -26,9 +26,9 @@ */ @Slf4j @RequiredArgsConstructor -public final class RequestQueryParamResolver {//spring connection bridge +public final class RequestParameterResolver {//spring connection bridge - public RequestQueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull Map parameterMap) { + public QueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull Map parameterMap) { var t = currentTimeMillis(); log.trace("parsing request..."); parameterMap = new LinkedHashMap<>(parameterMap); //modifiable map + preserve order diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index 574175f4..a4bc916d 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -12,7 +12,7 @@ import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; -import org.usf.jquery.core.QueryParameterBuilder; +import org.usf.jquery.core.QueryVariables; import org.usf.jquery.core.TableView; import lombok.RequiredArgsConstructor; @@ -56,7 +56,7 @@ public static RevisionIterator iterator(YearMonth[] revisions){ static TableView yearTable(TableView view) { return new TableView(view.getSchema(), view.getName()) { @Override - public String sql(QueryParameterBuilder builder) { + public String sql(QueryVariables builder) { return super.sql(builder) + "_" + currentRev.get().getKey(); } }; diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 225bd17f..450fe028 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -31,7 +31,7 @@ import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBView; -import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.QueryBuilder; import org.usf.jquery.core.TableView; import org.usf.jquery.core.NamedColumn; import org.usf.jquery.core.ViewColumn; @@ -105,8 +105,8 @@ static Map declaredColumns(ViewDecorator vd, Collection< .collect(toUnmodifiableMap(Entry::getKey, Entry::getValue)); } - default RequestQueryBuilder query(Map parameterMap) { - var query = new RequestQueryBuilder(currentContext().getMetadata().getType()); + default QueryBuilder query(Map parameterMap) { + var query = new QueryBuilder(currentContext().getMetadata().getType()); parseViews(query, parameterMap); parseColumns(query, parameterMap); parseOrders(query, parameterMap); @@ -117,7 +117,7 @@ default RequestQueryBuilder query(Map parameterMap) { return query; } - default void parseViews(RequestQueryBuilder query, Map parameters) { + default void parseViews(QueryBuilder query, Map parameters) { if(parameters.containsKey(VIEW)) { Stream.of(parameters.remove(VIEW)) .flatMap(c-> parseEntries(c).stream()) @@ -125,7 +125,7 @@ default void parseViews(RequestQueryBuilder query, Map paramet } } - default void parseColumns(RequestQueryBuilder query, Map parameters) { + default void parseColumns(QueryBuilder query, Map parameters) { if(parameters.containsKey(COLUMN) && parameters.containsKey(COLUMN_DISTINCT)) { throw new IllegalStateException("both parameters are present " + quote(COLUMN_DISTINCT) + " and " + quote(COLUMN)); } @@ -148,14 +148,14 @@ default void parseColumns(RequestQueryBuilder query, Map param } } - default void parseOrders(RequestQueryBuilder query, Map parameters) { + default void parseOrders(QueryBuilder query, Map parameters) { if(parameters.containsKey(ORDER)) { Stream.of(parameters.remove(ORDER)) .flatMap(c-> parseEntries(c).stream()) .forEach(e-> query.orders(e.evalOrder(this, query))); } } - default void parseJoin(RequestQueryBuilder query, Map parameters) { + default void parseJoin(QueryBuilder query, Map parameters) { if(parameters.containsKey(JOIN)) { Stream.of(parameters.remove(JOIN)) .flatMap(c-> parseEntries(c).stream()) @@ -163,15 +163,15 @@ default void parseJoin(RequestQueryBuilder query, Map paramete } } - default void parseFetch(RequestQueryBuilder query, Map parameters) { + default void parseFetch(QueryBuilder query, Map parameters) { requirePositiveInt(FETCH, parameters).ifPresent(query::fetch); } - default void parseOffset(RequestQueryBuilder query, Map parameters) { + default void parseOffset(QueryBuilder query, Map parameters) { requirePositiveInt(OFFSET, parameters).ifPresent(query::offset); } - default void parseFilters(RequestQueryBuilder query, Map parameters) { + default void parseFilters(QueryBuilder query, Map parameters) { parameters.entrySet().stream() .flatMap(e-> { var re = parseEntry(e.getKey()); diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index 934dcb56..ab071dba 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -20,7 +20,7 @@ import java.util.Set; import org.usf.jquery.core.DBView; -import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.QueryBuilder; import org.usf.jquery.core.ResultSetConsumer; import org.usf.jquery.core.TableView; @@ -101,7 +101,7 @@ void fetchView(DatabaseMetaData metadata, TableView view, String schema) throws } void fetch(DatabaseMetaData metadata, DBView qr, String schema) throws SQLException { - var query = new RequestQueryBuilder().columns(allColumns(qr)).filters(constant(1).eq(constant(0))); //no data + var query = new QueryBuilder().columns(allColumns(qr)).filters(constant(1).eq(constant(0))); //no data query.build(schema).execute(metadata.getConnection(), (ResultSetConsumer) rs->{ var db = reverseMapKeys(); var meta = rs.getMetaData(); diff --git a/src/main/java/org/usf/jquery/web/YearViewDecorator.java b/src/main/java/org/usf/jquery/web/YearViewDecorator.java index daf427c0..c8e77deb 100644 --- a/src/main/java/org/usf/jquery/web/YearViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearViewDecorator.java @@ -26,7 +26,7 @@ import java.util.stream.Stream; import org.usf.jquery.core.DBView; -import org.usf.jquery.core.RequestQueryBuilder; +import org.usf.jquery.core.QueryBuilder; import org.usf.jquery.core.TableView; import org.usf.jquery.core.NamedColumn; @@ -63,7 +63,7 @@ default NamedColumn column(ColumnDecorator column) { } @Override - default void parseFilters(RequestQueryBuilder query, Map parameterMap) { + default void parseFilters(QueryBuilder query, Map parameterMap) { ofNullable(monthRevision()).map(this::column) .ifPresent(c-> query.filters(monthFilter(c))); query.repeat(iterator(parseRevisions(parameterMap))); diff --git a/src/test/java/org/usf/jquery/core/FunctionOperatorTest.java b/src/test/java/org/usf/jquery/core/FunctionOperatorTest.java index 211df7f3..cc213455 100644 --- a/src/test/java/org/usf/jquery/core/FunctionOperatorTest.java +++ b/src/test/java/org/usf/jquery/core/FunctionOperatorTest.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.usf.jquery.core.QueryParameterBuilder.addWithValue; +import static org.usf.jquery.core.QueryVariables.addWithValue; import org.junit.jupiter.api.Test; From 86244a345fb545936a2a8981ec87e4529d28d3c4 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 5 Sep 2024 14:18:19 +0200 Subject: [PATCH 224/298] edit --- .../java/org/usf/jquery/core/ColumnProxy.java | 7 +- .../jquery/web/NoSuchResourceException.java | 4 ++ .../org/usf/jquery/web/QueryDecorator.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 67 ++++++++++--------- .../jquery/web/ResourceAccessException.java | 4 -- .../org/usf/jquery/web/ViewDecorator.java | 2 +- 6 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ColumnProxy.java b/src/main/java/org/usf/jquery/core/ColumnProxy.java index 2eaa9825..436b07e1 100644 --- a/src/main/java/org/usf/jquery/core/ColumnProxy.java +++ b/src/main/java/org/usf/jquery/core/ColumnProxy.java @@ -8,6 +8,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.experimental.Delegate; /** * @@ -18,6 +19,7 @@ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class ColumnProxy implements NamedColumn { + @Delegate private final DBColumn column; private final JDBCType type; //optional private final String tag; //optional @@ -26,11 +28,6 @@ public final class ColumnProxy implements NamedColumn { public JDBCType getType() { return nonNull(type) ? type : column.getType(); } - - @Override - public String sql(QueryVariables builder) { - return column.sql(builder); - } @Override public ColumnProxy as(String name, JDBCType type) { // map diff --git a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java index 01f2dc9e..06de6623 100644 --- a/src/main/java/org/usf/jquery/web/NoSuchResourceException.java +++ b/src/main/java/org/usf/jquery/web/NoSuchResourceException.java @@ -17,4 +17,8 @@ public NoSuchResourceException(String s) { static NoSuchResourceException noSuchResourceException(String type, String resource) { return new NoSuchResourceException(format("no such %s: '%s'", type, resource)); } + + static NoSuchResourceException undeclaredResouceException(String child, String parent) { + return new NoSuchResourceException(format("'%s' is not member of '%s'", child, parent)); + } } diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 74430937..bc3c3d89 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -1,6 +1,6 @@ package org.usf.jquery.web; -import static org.usf.jquery.web.ResourceAccessException.undeclaredResouceException; +import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; import org.usf.jquery.core.DBView; import org.usf.jquery.core.QueryView; diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 759e673e..1008bd6e 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -1,6 +1,7 @@ package org.usf.jquery.web; import static java.lang.String.format; +import static java.lang.String.join; import static java.lang.reflect.Array.newInstance; import static java.util.Collections.addAll; import static java.util.Collections.emptyList; @@ -41,6 +42,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Function; import java.util.function.IntFunction; import java.util.stream.Stream; @@ -84,6 +87,7 @@ final class RequestEntryChain { private static final String ORDER_PATTERN = enumPattern(Order.class); private static final String LOGIC_PATTERN = enumPattern(LogicalOperator.class); + private static final String PARTITION_PATTERN = join("|", PARTITION, ORDER); private final String value; private final boolean text; //"string" @@ -125,7 +129,7 @@ Optional evalQuery(ViewDecorator td, QueryContext ctx, boolean r switch(e.value) {//column not allowed case DISTINCT: e.requireNoArgs(); q.distinct(); break; case FILTER: q.filters(e.filterVarargs(td, ctx)); break; - case ORDER: q.orders(e.oderVarargs(td, ctx)); break; + case ORDER: q.orders(e.orderVarargs(td, ctx)); break; case JOIN: q.joins(e.evalJoin(td, ctx)); break; case OFFSET: q.offset((int)e.toOneArg(td, ctx, INTEGER)); break; case FETCH: q.fetch((int)e.toOneArg(td, ctx, INTEGER)); break; @@ -145,19 +149,21 @@ Optional evalQuery(ViewDecorator td, QueryContext ctx, boolean r public ViewJoin[] evalJoin(ViewDecorator vd, QueryContext ctx) { var e = this; if(hasNext()) { - vd = currentContext().lookupRegisteredView(value) - .orElseThrow(()-> noSuchResourceException(VIEW, value)); - e = requireNoArgs().next; //check args only if view exists + var res = currentContext().lookupRegisteredView(value); + if(res.isPresent()) { + vd = res.get(); + e = requireNoArgs().next; //check args only if view exists + } } var join = vd.join(e.value); if(nonNull(join)) { e.requireNoArgs().requireNoNext(); //check args & next only if joiner exists - return requireNonNull(join.build(ctx), vd.identity() + ".join: " + e); + return requireNonNull(join.build(ctx), vd.identity() + "." + e.value); } throw noSuchResourceException(vd.identity() + ".join", e.value); } - //[partition.order]* + //[view.]partition | [partition.order]* public Partition evalPartition(ViewDecorator vd, QueryContext ctx) { var e = this; if(hasNext()) { @@ -170,36 +176,33 @@ public Partition evalPartition(ViewDecorator vd, QueryContext ctx) { var par = vd.partition(e.value); if(nonNull(par)) { e.requireNoArgs().requireNoNext(); - return requireNonNull(par.build(ctx), vd.identity() + ".partition: " + e); + return requireNonNull(par.build(ctx), vd.identity() + "." + e.value); } - if(e == this) { // not view - var res = evalPartition2(vd, ctx); - if(res.isPresent()) { - return res.get(); - } + var res = evalPartition2(vd, ctx); + if(res.isPresent()) { + return res.get(); } throw noSuchResourceException(vd.identity() + ".partition", e.value); } private Optional evalPartition2(ViewDecorator vd, QueryContext ctx) { - List cols = new ArrayList<>(); - List ords = new ArrayList<>(); - var e = this; - do { - switch (e.value) { - case PARTITION: addAll(cols, columnVarargs(vd, ctx)); break; - case ORDER: addAll(ords, e.oderVarargs(vd, ctx)); break; - default: - if(e == this) { - return empty(); //cannotParseEntryException(PARTITION, e) //first entry + if(value.matches(PARTITION_PATTERN)) { + List cols = new ArrayList<>(); + List ords = new ArrayList<>(); + var e = this; + do { + switch (e.value) { + case PARTITION: addAll(cols, columnVarargs(vd, ctx)); break; + case ORDER: addAll(ords, e.orderVarargs(vd, ctx)); break; + default: throw badEntrySyntaxException(PARTITION_PATTERN, e.value); } - throw badEntrySyntaxException(joinArray("|", PARTITION, ORDER), e.value); - } - e = e.next; - } while(nonNull(e)); - return Optional.of(new Partition( - cols.toArray(DBColumn[]::new), - ords.toArray(DBOrder[]::new))); + e = e.next; + } while(nonNull(e)); + return Optional.of(new Partition( + cols.toArray(DBColumn[]::new), + ords.toArray(DBOrder[]::new))); + } + return empty(); } //[view.]column[.operator]* @@ -257,7 +260,7 @@ else if(nonNull(rc.col)) { var e = new RequestEntryChain(null, false, null, values, null); return fn.filter(e.toArgs(vd, ctx, rc.col, fn.getParameterSet())); //no chain } - throw noSuchResourceException("comparator|criteria", rc.entry.next.value); + throw noSuchResourceException(isNull(rc.cd) ? "comparator" : "comparator|criteria", rc.entry.next.value); } throw new IllegalStateException("illegal ViewResource: " + rc); } @@ -383,7 +386,7 @@ private Optional lookupColumnResource(ViewDecorator td, boolean fi } return Optional.of(new ViewResource(requireNoArgs(), td, cd, col)); } - catch(Exception e) {/*do not throw exception*/} //TD specific exception + catch(NoSuchResourceException e) {/*do not throw exception*/} } return empty(); } @@ -396,7 +399,7 @@ private DBColumn[] columnVarargs(ViewDecorator td, QueryContext ctx) { return (DBColumn[]) typeVarargs(td, ctx, JQueryType.COLUMN); } - private DBOrder[] oderVarargs(ViewDecorator td, QueryContext ctx) { + private DBOrder[] orderVarargs(ViewDecorator td, QueryContext ctx) { return (DBOrder[]) typeVarargs(td, ctx, JQueryType.ORDER); } diff --git a/src/main/java/org/usf/jquery/web/ResourceAccessException.java b/src/main/java/org/usf/jquery/web/ResourceAccessException.java index 4d1b5b8d..2510b2f5 100644 --- a/src/main/java/org/usf/jquery/web/ResourceAccessException.java +++ b/src/main/java/org/usf/jquery/web/ResourceAccessException.java @@ -17,8 +17,4 @@ public ResourceAccessException(String message) { public static ResourceAccessException resourceAlreadyExistsException(String name, Object value) { return new ResourceAccessException(quote(name) + " is already exists : " + value); } - - public static ResourceAccessException undeclaredResouceException(String child, String parent) { - return new ResourceAccessException(quote(child) + " is not member of " + quote(parent)); - } } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 450fe028..3c74ab56 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -12,6 +12,7 @@ import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.web.ColumnMetadata.columnMetadata; import static org.usf.jquery.web.ContextManager.currentContext; +import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; import static org.usf.jquery.web.Parameters.COLUMN; import static org.usf.jquery.web.Parameters.COLUMN_DISTINCT; import static org.usf.jquery.web.Parameters.FETCH; @@ -21,7 +22,6 @@ import static org.usf.jquery.web.Parameters.VIEW; import static org.usf.jquery.web.RequestParser.parseEntries; import static org.usf.jquery.web.RequestParser.parseEntry; -import static org.usf.jquery.web.ResourceAccessException.undeclaredResouceException; import java.util.Collection; import java.util.Map; From f13d0696acf6ce648598c020799aa2b29acba4fb Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 5 Sep 2024 14:48:07 +0200 Subject: [PATCH 225/298] edit --- .../org/usf/jquery/web/QueryDecorator.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 43 +++++++------------ 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index bc3c3d89..9fce0411 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -18,7 +18,7 @@ */ @Getter @RequiredArgsConstructor -final class QueryDecorator implements ViewDecorator { +final class QueryDecorator implements ViewDecorator { //non public private final String id; private final QueryView query; diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 1008bd6e..d9fc6d7f 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -42,8 +42,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.function.BiFunction; -import java.util.function.Function; import java.util.function.IntFunction; import java.util.stream.Stream; @@ -55,16 +53,15 @@ import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; import org.usf.jquery.core.LogicalOperator; +import org.usf.jquery.core.NamedColumn; import org.usf.jquery.core.OperationColumn; import org.usf.jquery.core.Order; import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; +import org.usf.jquery.core.QueryBuilder; import org.usf.jquery.core.QueryColumn; import org.usf.jquery.core.QueryContext; import org.usf.jquery.core.QueryView; -import org.usf.jquery.core.QueryBuilder; -import org.usf.jquery.core.NamedColumn; -import org.usf.jquery.core.TypedComparator; import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; @@ -251,15 +248,18 @@ public DBFilter evalFilter(ViewDecorator vd, QueryContext ctx, List lookupColumnResource(ViewDecorator td, boolean fi var cd = res.get(); try { var col = td.column(cd); //throw exception - if(filter && hasNext()) { - var cmp = lookupComparator(next.value); - if(cmp.isPresent()) { - return Optional.of(new ViewResource(requireNoArgs().next, td, cd, col, cmp.get())); - } - var crt = cd.criteria(next.value); //TD !operator + if(filter && hasNext() && lookupComparator(next.value).isEmpty() && lookupOperator(next.value).isEmpty()) { + var crt = cd.criteria(next.value); //!operator & !comparator if(nonNull(crt)) { return Optional.of(new ViewResource(requireNoArgs().next, td, cd, col, crt)); } @@ -531,26 +527,17 @@ static final class ViewResource { private DBColumn col; private CriteriaBuilder viewCrt; private CriteriaBuilder colCrt; - private TypedComparator cmp; public ViewResource(RequestEntryChain entry, ViewDecorator vd, ColumnDecorator cd, DBColumn col) { - this(entry, vd, cd, col, null, null, null); //[view.]column + this(entry, vd, cd, col, null, null); //[view.]column } public ViewResource(RequestEntryChain entry, ViewDecorator vd, ColumnDecorator cd, DBColumn col, CriteriaBuilder colCrt) { - this(entry, vd, cd, col, null, colCrt, null); //[view.]colum.criteria - } - - public ViewResource(RequestEntryChain entry, ViewDecorator vd, ColumnDecorator cd, DBColumn col, TypedComparator cmp) { - this(entry, vd, cd, col, null, null, cmp); //[view.]colum.comparator + this(entry, vd, cd, col, null, colCrt); //[view.]colum.criteria } public ViewResource(RequestEntryChain entry, ViewDecorator vd, CriteriaBuilder viewCrt) { - this(entry, vd, null, null, viewCrt, null, null); //[view.]criteria - } - - boolean isFilter() { - return nonNull(cmp) || isCriteria(); + this(entry, vd, null, null, viewCrt, null); //[view.]criteria } boolean isCriteria() { From 801cc022f9e83143d54a2b6a4ebd7ddf55a18903 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 5 Sep 2024 16:03:15 +0200 Subject: [PATCH 226/298] edit --- .../org/usf/jquery/web/RequestEntryChain.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index d9fc6d7f..5c7f0c62 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -62,6 +62,7 @@ import org.usf.jquery.core.QueryColumn; import org.usf.jquery.core.QueryContext; import org.usf.jquery.core.QueryView; +import org.usf.jquery.core.Utils; import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; @@ -230,7 +231,7 @@ public DBOrder evalOrder(ViewDecorator td, QueryContext ctx) { } public DBFilter evalFilter(ViewDecorator td, QueryContext ctx) { - return evalFilter(td, ctx, emptyList()); + return evalFilter(td, ctx, null); //null ==> inner filter } //[view.]criteria | [view.]column.criteria | [view.]column[.operator]*[.comparator][.and|or(comparator)]* @@ -239,19 +240,22 @@ public DBFilter evalFilter(ViewDecorator vd, QueryContext ctx, List Date: Fri, 6 Sep 2024 11:49:46 +0200 Subject: [PATCH 227/298] edit --- .../usf/jquery/core/BadArgumentException.java | 23 +++++++++++++------ .../org/usf/jquery/core/JQueryException.java | 6 ----- .../org/usf/jquery/core/MappingException.java | 9 ++++---- .../org/usf/jquery/core/ParameterSet.java | 4 ++-- .../org/usf/jquery/core/QueryBuilder.java | 8 ++++++- .../org/usf/jquery/core/RangeComparator.java | 8 ++++--- .../org/usf/jquery/core/RequestQuery.java | 4 ++-- .../org/usf/jquery/core/TypedComparator.java | 6 ++--- .../org/usf/jquery/core/TypedOperator.java | 4 +--- .../org/usf/jquery/web/ColumnDecorator.java | 10 ++++---- .../usf/jquery/web/ContextEnvironment.java | 2 +- .../org/usf/jquery/web/ContextManager.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 18 +++++++-------- .../jquery/web/ResourceAccessException.java | 6 ++--- .../java/org/usf/jquery/web/ViewBuilder.java | 1 - 15 files changed, 59 insertions(+), 52 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/BadArgumentException.java b/src/main/java/org/usf/jquery/core/BadArgumentException.java index 169608aa..7b9e7f69 100644 --- a/src/main/java/org/usf/jquery/core/BadArgumentException.java +++ b/src/main/java/org/usf/jquery/core/BadArgumentException.java @@ -1,7 +1,12 @@ package org.usf.jquery.core; +import static java.lang.String.format; +import static org.usf.jquery.core.SqlStringBuilder.SCOMA; +import static org.usf.jquery.core.Utils.joinAndDelemitArray; import static org.usf.jquery.core.Utils.joinArray; +import org.usf.jquery.core.JavaType.Typed; + /** * * @author u$f @@ -18,16 +23,20 @@ public BadArgumentException(String message, Throwable cause) { super(message, cause); } - public static BadArgumentException badArgumentTypeException(JavaType[] types, Object actual) { - String type = actual instanceof DBColumn c ? ":"+ c.getType() : ""; - return new BadArgumentException(formatMessage("bad argument type", joinArray("|", types), actual + type)); + public static BadArgumentException badArgumentCountException(int count, int expect) { + return new BadArgumentException(format("bad argument count: [%d], expected: %d", count, expect)); } - public static BadArgumentException badArgumentCountException(int count, int actual) { - return new BadArgumentException(formatMessage("bad argument count", count, actual)); + public static BadArgumentException badArgumentTypeException(Object obj, JavaType[] types) { + var type = obj instanceof Typed t ? t.getType() : JDBCType.typeOf(obj); + return new BadArgumentException(format("bad argument type: %s[%s], expected: %s", obj, type, joinArray("|", types))); } - public static BadArgumentException badArgumentsException(String exp, String actual, Exception e) { - return new BadArgumentException(formatMessage("bad arguments", exp, actual), e); + public static BadArgumentException badArgumentsException(String type, String id, Object[] args, Exception e) { + return new BadArgumentException(format("bad %s arguments: ", type) + badArgumentsFormat(id, args), e); + } + + public static String badArgumentsFormat(String id, Object... args) { + return id + joinAndDelemitArray(SCOMA, "([", "])", args); } } diff --git a/src/main/java/org/usf/jquery/core/JQueryException.java b/src/main/java/org/usf/jquery/core/JQueryException.java index 7f6a9877..c585bf58 100644 --- a/src/main/java/org/usf/jquery/core/JQueryException.java +++ b/src/main/java/org/usf/jquery/core/JQueryException.java @@ -19,10 +19,4 @@ public JQueryException(Throwable cause) { public JQueryException(String message, Throwable cause) { super(message, cause); } - - protected static String formatMessage(String msg, Object expected, Object actual) { - return msg + ", " + - "expected: " + expected + - " but was: " + actual; - } } diff --git a/src/main/java/org/usf/jquery/core/MappingException.java b/src/main/java/org/usf/jquery/core/MappingException.java index fe9cadc1..84c010b8 100644 --- a/src/main/java/org/usf/jquery/core/MappingException.java +++ b/src/main/java/org/usf/jquery/core/MappingException.java @@ -1,12 +1,13 @@ package org.usf.jquery.core; +/** + * + * @author u$f + * + */ @SuppressWarnings("serial") public final class MappingException extends JQueryException { - public MappingException(Throwable cause) { - super(cause); - } - public MappingException(String message, Throwable cause) { super(message, cause); } diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index 80e479f8..c4a8cdad 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -35,7 +35,7 @@ public Object[] assertArgumentsFrom(int idx, Object... args) { //partial assert var arr = isNull(args) ? new Object[0] : args; forEach(arr.length+idx, (p,i)-> { if(i>=idx && !p.accept(i-idx, arr)) { - throw badArgumentTypeException(p.types(arr), arr[i-idx]); + throw badArgumentTypeException(arr[i-idx], p.types(arr)); } }); return arr; @@ -43,7 +43,7 @@ public Object[] assertArgumentsFrom(int idx, Object... args) { //partial assert public void forEach(int nArgs, ObjIntConsumer cons) { if(nArgs < nReqArgs || (nArgs > parameters.length && !isVarags())) { - throw badArgumentCountException(nReqArgs, nArgs); + throw badArgumentCountException(nArgs, nReqArgs); } var i=0; for(; i supp) { } public QueryBuilder columns(@NonNull NamedColumn... columns) { - addAll(this.columns, columns); //add only if !exits + for(var col : columns) { + if(this.columns.stream().anyMatch(nc-> nc.getTag().equals(col.getTag()))) { + throw resourceAlreadyExistsException(col.getTag()); + } + this.columns.add(col); + } return this; } diff --git a/src/main/java/org/usf/jquery/core/RangeComparator.java b/src/main/java/org/usf/jquery/core/RangeComparator.java index 21885762..7567db8b 100644 --- a/src/main/java/org/usf/jquery/core/RangeComparator.java +++ b/src/main/java/org/usf/jquery/core/RangeComparator.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.LogicalOperator.AND; +import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNArgs; /** @@ -13,8 +15,8 @@ public interface RangeComparator extends Comparator { @Override default String sql(QueryVariables builder, Object[] args) { requireNArgs(3, args, RangeComparator.class::getSimpleName); - return builder.appendParameter(args[0]) + " " + id() + - " " + builder.appendParameter(args[1]) + - " AND " + builder.appendParameter(args[2]); + return builder.appendParameter(args[0]) + SPACE + id() + + SPACE + builder.appendParameter(args[1]) + + AND.sql() + builder.appendParameter(args[2]); } } diff --git a/src/main/java/org/usf/jquery/core/RequestQuery.java b/src/main/java/org/usf/jquery/core/RequestQuery.java index 88e4a2c2..2f78c287 100644 --- a/src/main/java/org/usf/jquery/core/RequestQuery.java +++ b/src/main/java/org/usf/jquery/core/RequestQuery.java @@ -35,7 +35,7 @@ public List execute(DataSource ds) throws SQLException { return execute(ds, new KeyValueMapper()); } - public T execute(DataSource ds, ResultSetMapper mapper) throws SQLException { // overload with sql types + public T execute(DataSource ds, ResultSetMapper mapper) throws SQLException { try(var cn = ds.getConnection()){ return execute(cn, mapper); } @@ -62,7 +62,7 @@ public T execute(Connection cn, ResultSetMapper mapper) throws SQLExcepti try { return mapper.map(rs); } - catch(SQLException e) { // re-throw SQLException + catch(SQLException e) { throw new MappingException("error while mapping results", e); } } diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 94e5c600..ee206bfe 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -2,8 +2,6 @@ import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.ParameterSet.ofParameters; -import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.Utils.joinAndDelemitArray; import lombok.Getter; @@ -27,7 +25,7 @@ public ComparisonExpression expression(Object... right) { try { return comparator.expression(parameterSet.assertArgumentsFrom(1, right)); //no left } catch (BadArgumentException e) { - throw badArgumentsException(toString(), comparator.id() + joinAndDelemitArray(SCOMA, "(", ")", right), e); + throw badArgumentsException("comparator", comparator.id(), right, e); } } @@ -35,7 +33,7 @@ public ColumnSingleFilter filter(Object... args) { try { return comparator.filter(parameterSet.assertArguments(args)); } catch (BadArgumentException e) { - throw badArgumentsException(toString(), comparator.id() + joinAndDelemitArray(SCOMA, "(", ")", args), e); + throw badArgumentsException("comparator", comparator.id(), args, e); } } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 6338ff11..0f057452 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -2,8 +2,6 @@ import static org.usf.jquery.core.BadArgumentException.badArgumentsException; import static org.usf.jquery.core.ParameterSet.ofParameters; -import static org.usf.jquery.core.SqlStringBuilder.SCOMA; -import static org.usf.jquery.core.Utils.joinAndDelemitArray; import lombok.Getter; @@ -34,7 +32,7 @@ public OperationColumn operation(Object... args) { args = parameterSet.assertArguments(args); return operator.args(typeFn.apply(args), args); } catch (BadArgumentException e) { - throw badArgumentsException(toString(), operator.id() + joinAndDelemitArray(SCOMA, "(", ")", args), e); + throw badArgumentsException("operator", operator.id(), args, e); } } diff --git a/src/main/java/org/usf/jquery/web/ColumnDecorator.java b/src/main/java/org/usf/jquery/web/ColumnDecorator.java index c7335d73..73c9eda0 100644 --- a/src/main/java/org/usf/jquery/web/ColumnDecorator.java +++ b/src/main/java/org/usf/jquery/web/ColumnDecorator.java @@ -30,17 +30,19 @@ default CriteriaBuilder criteria(String name) { return null; // no criteria by default } -// default JDBCArgumentParser parser(ViewDecorator vd) // override parser | format | local | validation + default JDBCArgumentParser parser(ViewDecorator vd) { + throw new UnsupportedOperationException("not impl."); // override parser | format | local | validation + } default String pattern(ViewDecorator td) { - throw new UnsupportedOperationException(); //improve API security and performance + throw new UnsupportedOperationException("not impl."); //improve API security and performance } default boolean canSelect(ViewDecorator td) { - throw new UnsupportedOperationException(); //authorization inject + throw new UnsupportedOperationException("not impl."); //authorization inject } default boolean canFilter(ViewDecorator td) { - throw new UnsupportedOperationException(); //authorization inject + throw new UnsupportedOperationException("not impl."); //authorization inject } } diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index 8c726970..2a4ac42e 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -76,7 +76,7 @@ void declareView(ViewDecorator view) { //additional request views if(isNull(v)){ return view; } - throw resourceAlreadyExistsException(k, v); + throw resourceAlreadyExistsException(k); }); } diff --git a/src/main/java/org/usf/jquery/web/ContextManager.java b/src/main/java/org/usf/jquery/web/ContextManager.java index c3a0f5c7..a0300376 100644 --- a/src/main/java/org/usf/jquery/web/ContextManager.java +++ b/src/main/java/org/usf/jquery/web/ContextManager.java @@ -29,7 +29,7 @@ public static void register(ContextEnvironment config) { if(isNull(v)) { return config; } - throw resourceAlreadyExistsException(k, v); + throw resourceAlreadyExistsException(k); }); config.bind(); // outer bind } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 5c7f0c62..1efd85dc 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -4,7 +4,6 @@ import static java.lang.String.join; import static java.lang.reflect.Array.newInstance; import static java.util.Collections.addAll; -import static java.util.Collections.emptyList; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; @@ -62,7 +61,6 @@ import org.usf.jquery.core.QueryColumn; import org.usf.jquery.core.QueryContext; import org.usf.jquery.core.QueryView; -import org.usf.jquery.core.Utils; import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; @@ -131,7 +129,7 @@ Optional evalQuery(ViewDecorator td, QueryContext ctx, boolean r case JOIN: q.joins(e.evalJoin(td, ctx)); break; case OFFSET: q.offset((int)e.toOneArg(td, ctx, INTEGER)); break; case FETCH: q.fetch((int)e.toOneArg(td, ctx, INTEGER)); break; - default: throw badEntrySyntaxException(joinArray("|", DISTINCT, FILTER, ORDER, JOIN, OFFSET, FETCH), e.value); + default: throw badEntrySyntaxException(e.value, join("|", DISTINCT, FILTER, ORDER, JOIN, OFFSET, FETCH)); } } return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); @@ -192,7 +190,7 @@ private Optional evalPartition2(ViewDecorator vd, QueryContext ctx) { switch (e.value) { case PARTITION: addAll(cols, columnVarargs(vd, ctx)); break; case ORDER: addAll(ords, e.orderVarargs(vd, ctx)); break; - default: throw badEntrySyntaxException(PARTITION_PATTERN, e.value); + default: throw badEntrySyntaxException(e.value, PARTITION_PATTERN); } e = e.next; } while(nonNull(e)); @@ -227,7 +225,7 @@ public DBOrder evalOrder(ViewDecorator td, QueryContext ctx) { var s = ord.requireNoArgs().requireNoNext().value.toUpperCase(); return r.col.order(Order.valueOf(s)); } - throw badEntrySyntaxException(ORDER_PATTERN, ord.value); + throw badEntrySyntaxException(ord.value, ORDER_PATTERN); } public DBFilter evalFilter(ViewDecorator td, QueryContext ctx) { @@ -255,7 +253,7 @@ else if(nonNull(rc.col)) { var e = new RequestEntryChain(fn.unwrap().id(), false, null, values, null); return fn.filter(e.toArgs(vd, ctx, rc.col, fn.getParameterSet())); //no chain } - throw badEntrySyntaxException(FILTER, this.value); + throw badEntrySyntaxException(this.toString(), FILTER); } var e = rc.entry.next; var res = lookupComparator(e.value); @@ -288,7 +286,7 @@ DBFilter chainComparator(ViewDecorator td, QueryContext ctx, DBFilter f) { e = e.next; } else { - throw badEntrySyntaxException(LOGIC_PATTERN, e.value); + throw badEntrySyntaxException(e.value, LOGIC_PATTERN); } } return f; @@ -514,12 +512,12 @@ static String enumPattern(Class> c) { .collect(joining("|")); } - static EntrySyntaxException badEntrySyntaxException(String type, String value) { - return new EntrySyntaxException(format("incorrect syntax expected: %s, bat was: %s", type, value)); + static EntrySyntaxException badEntrySyntaxException(String value, String expect) { + return new EntrySyntaxException(format("incorrect syntax: [%s], expected: %s", value, expect)); } static EntrySyntaxException expectedEntryTagException(RequestEntryChain e) { - throw new EntrySyntaxException(format("expected tag : %s[:tag]", e)); + throw new EntrySyntaxException(format("expected tag: %s[:tag]", e)); } @AllArgsConstructor(access = AccessLevel.PRIVATE) diff --git a/src/main/java/org/usf/jquery/web/ResourceAccessException.java b/src/main/java/org/usf/jquery/web/ResourceAccessException.java index 2510b2f5..6c438f64 100644 --- a/src/main/java/org/usf/jquery/web/ResourceAccessException.java +++ b/src/main/java/org/usf/jquery/web/ResourceAccessException.java @@ -1,6 +1,6 @@ package org.usf.jquery.web; -import static org.usf.jquery.core.SqlStringBuilder.quote; +import static java.lang.String.format; /** * @@ -14,7 +14,7 @@ public ResourceAccessException(String message) { super(message); } - public static ResourceAccessException resourceAlreadyExistsException(String name, Object value) { - return new ResourceAccessException(quote(name) + " is already exists : " + value); + public static ResourceAccessException resourceAlreadyExistsException(String name) { + return new ResourceAccessException(format("an other ressource with name='%s' is already exists", name)); } } diff --git a/src/main/java/org/usf/jquery/web/ViewBuilder.java b/src/main/java/org/usf/jquery/web/ViewBuilder.java index 7ff0de5e..280c91ed 100644 --- a/src/main/java/org/usf/jquery/web/ViewBuilder.java +++ b/src/main/java/org/usf/jquery/web/ViewBuilder.java @@ -11,5 +11,4 @@ public interface ViewBuilder { DBView build(); - } From 105d74948130d1387811e4aff92da7f89eb741c2 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 6 Sep 2024 13:51:03 +0200 Subject: [PATCH 228/298] edit --- .../org/usf/jquery/core/CombinedOperator.java | 2 +- .../java/org/usf/jquery/core/Comparator.java | 4 ++ .../org/usf/jquery/core/OperationColumn.java | 4 +- .../java/org/usf/jquery/core/Operator.java | 6 ++- .../org/usf/jquery/core/ParameterSet.java | 12 ++---- .../org/usf/jquery/core/QueryBuilder.java | 8 ++-- .../org/usf/jquery/core/TypedComparator.java | 25 ++++++------- .../org/usf/jquery/core/TypedOperator.java | 37 +++++++++++++------ src/main/java/org/usf/jquery/core/Utils.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 10 +++-- .../jquery/web/RequestParameterResolver.java | 4 ++ 11 files changed, 70 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CombinedOperator.java b/src/main/java/org/usf/jquery/core/CombinedOperator.java index 2328b75d..2055801f 100644 --- a/src/main/java/org/usf/jquery/core/CombinedOperator.java +++ b/src/main/java/org/usf/jquery/core/CombinedOperator.java @@ -11,7 +11,7 @@ public interface CombinedOperator extends Operator { OperationColumn args(Object... args); @Override - default OperationColumn args(JDBCType type, Object... args) { + default OperationColumn operation(JDBCType type, Object... args) { var c = args(args); if(type == c.getType()) { return c; diff --git a/src/main/java/org/usf/jquery/core/Comparator.java b/src/main/java/org/usf/jquery/core/Comparator.java index 3c2305ca..5cd466f6 100644 --- a/src/main/java/org/usf/jquery/core/Comparator.java +++ b/src/main/java/org/usf/jquery/core/Comparator.java @@ -25,6 +25,10 @@ default ColumnSingleFilter filter(Object... args) { return new ColumnSingleFilter(args[0], expression(copyOfRange(args, 1, args.length))); // no type } + + default boolean is(Class type) { + return type.isInstance(this); + } //basic comparator diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index e00e988a..7aa5c5bc 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -35,7 +35,7 @@ public JDBCType getType() { @Override public boolean isAggregation() { - return operator instanceof AggregateFunction || + return operator.is(AggregateFunction.class) || (!isOverFunction() && Stream.of(args).anyMatch(Nested::aggregation)); //can do better } @@ -44,7 +44,7 @@ public Stream groupKeys() { if(isOverFunction()) { return ((Partition)args[1]).groupKeys(); } - return operator instanceof AggregateFunction || operator instanceof ConstantOperator + return operator.is(AggregateFunction.class) || operator.is(ConstantOperator.class) ? Stream.empty() : DBColumn.super.groupKeys(); } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 20f5b07a..3ca61904 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -31,10 +31,14 @@ public interface Operator extends DBProcessor { String id(); //nullable - default OperationColumn args(JDBCType type, Object... args) { + default OperationColumn operation(JDBCType type, Object... args) { return new OperationColumn(this, args, type); } + default boolean is(Class type) { + return type.isInstance(this); + } + //Arithmetic operations static TypedOperator plus() { diff --git a/src/main/java/org/usf/jquery/core/ParameterSet.java b/src/main/java/org/usf/jquery/core/ParameterSet.java index c4a8cdad..b8a5c367 100644 --- a/src/main/java/org/usf/jquery/core/ParameterSet.java +++ b/src/main/java/org/usf/jquery/core/ParameterSet.java @@ -28,20 +28,16 @@ public final class ParameterSet { //there is no Singleton implementation, dummy private final Parameter[] parameters; public Object[] assertArguments(Object... args) { - return assertArgumentsFrom(0, args); - } - - public Object[] assertArgumentsFrom(int idx, Object... args) { //partial assert var arr = isNull(args) ? new Object[0] : args; - forEach(arr.length+idx, (p,i)-> { - if(i>=idx && !p.accept(i-idx, arr)) { - throw badArgumentTypeException(arr[i-idx], p.types(arr)); + eachParameter(arr.length, (p,i)-> { + if(!p.accept(i, arr)) { + throw badArgumentTypeException(arr[i], p.types(arr)); } }); return arr; } - public void forEach(int nArgs, ObjIntConsumer cons) { + public void eachParameter(int nArgs, ObjIntConsumer cons) { if(nArgs < nReqArgs || (nArgs > parameters.length && !isVarags())) { throw badArgumentCountException(nArgs, nReqArgs); } diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index ae275b7c..cdf4fb81 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -69,9 +69,11 @@ public QueryView overView(DBView view, Supplier supp) { } public QueryBuilder columns(@NonNull NamedColumn... columns) { - for(var col : columns) { - if(this.columns.stream().anyMatch(nc-> nc.getTag().equals(col.getTag()))) { - throw resourceAlreadyExistsException(col.getTag()); + for(var col : columns) { //optional tag + if(nonNull(col.getTag()) && this.columns.stream() + .filter(c-> nonNull(c.getTag())) + .anyMatch(nc-> nc.getTag().equals(col.getTag()))) { + throw resourceAlreadyExistsException(col.getTag()); } this.columns.add(col); } diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index ee206bfe..40ec4194 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -11,9 +11,9 @@ * */ @Getter -public final class TypedComparator { +public final class TypedComparator implements Comparator { - private final Comparator comparator; + private final Comparator comparator; // do not delegate private final ParameterSet parameterSet; public TypedComparator(Comparator comparator, Parameter... parameters) { @@ -21,24 +21,23 @@ public TypedComparator(Comparator comparator, Parameter... parameters) { this.parameterSet = ofParameters(parameters); } - public ComparisonExpression expression(Object... right) { - try { - return comparator.expression(parameterSet.assertArgumentsFrom(1, right)); //no left - } catch (BadArgumentException e) { - throw badArgumentsException("comparator", comparator.id(), right, e); - } + @Override + public String id() { + return comparator.id(); } - public ColumnSingleFilter filter(Object... args) { + @Override + public String sql(QueryVariables builder, Object[] args) { try { - return comparator.filter(parameterSet.assertArguments(args)); + return comparator.sql(builder, parameterSet.assertArguments(args)); } catch (BadArgumentException e) { throw badArgumentsException("comparator", comparator.id(), args, e); } } - - public Comparator unwrap() { - return comparator; + + @Override + public boolean is(Class type) { + return comparator.is(type); } @Override diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 0f057452..dd7fca95 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -11,9 +11,9 @@ * */ @Getter -public class TypedOperator { +public class TypedOperator implements Operator { - private final Operator operator; + private final Operator operator; // do not delegate private final ArgTypeRef typeFn; private final ParameterSet parameterSet; @@ -26,22 +26,37 @@ public TypedOperator(ArgTypeRef typeFn, Operator function, Parameter... paramete this.typeFn = typeFn; this.parameterSet = ofParameters(parameter); } - - public OperationColumn operation(Object... args) { + + @Override + public String id() { + return operator.id(); + } + + @Override + public String sql(QueryVariables builder, Object[] args) { try { - args = parameterSet.assertArguments(args); - return operator.args(typeFn.apply(args), args); - } catch (BadArgumentException e) { + return operator.sql(builder, parameterSet.assertArguments(args)); + } + catch (BadArgumentException e) { throw badArgumentsException("operator", operator.id(), args, e); } } + + @Override + public boolean is(Class type) { + return operator.is(type); + } - public boolean isCountFunction() { - return "COUNT".equals(operator.id()); + public OperationColumn operation(Object... args) { + return Operator.super.operation(typeFn.apply(args), args); } - + public boolean isWindowFunction() { - return operator instanceof WindowFunction; + return operator.is(WindowFunction.class); + } + + public boolean isCountFunction() { + return "COUNT".equals(operator.id()); } @Override diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index e0d5928e..88ee5dda 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -46,7 +46,7 @@ public static String joinArray(String delemiter, T... args) { @SuppressWarnings("unchecked") public static String joinAndDelemitArray(String delemiter, String before, String after, T... args) { return isNull(args) - ? null + ? before + after : joinAndDelemit(delemiter, before, after, Stream.of(args)); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 1efd85dc..a9a6170b 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -11,6 +11,7 @@ import static java.util.Optional.empty; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.BadArgumentException.badArgumentsFormat; import static org.usf.jquery.core.Comparator.eq; import static org.usf.jquery.core.Comparator.in; import static org.usf.jquery.core.Comparator.lookupComparator; @@ -44,6 +45,7 @@ import java.util.function.IntFunction; import java.util.stream.Stream; +import org.usf.jquery.core.BadArgumentException; import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; @@ -250,7 +252,7 @@ else if(nonNull(rc.col)) { if(rc.entry.isLast()) { // no criteria, no comparator if(!isEmpty(values)) { var fn = values.size() == 1 ? eq() : in(); //non empty - var e = new RequestEntryChain(fn.unwrap().id(), false, null, values, null); + var e = new RequestEntryChain(fn.id(), false, null, values, null); return fn.filter(e.toArgs(vd, ctx, rc.col, fn.getParameterSet())); //no chain } throw badEntrySyntaxException(this.toString(), FILTER); @@ -429,7 +431,7 @@ private Object[] toArgs(ViewDecorator td, QueryContext ctx, DBObject col, Parame arr[0] = col; } try { - ps.forEach(arr.length, (p,i)-> { + ps.eachParameter(arr.length, (p,i)-> { if(i>=inc) { //arg0 already parsed var e = args.get(i-inc); arr[i] = isNull(e.value) || e.text @@ -439,8 +441,8 @@ private Object[] toArgs(ViewDecorator td, QueryContext ctx, DBObject col, Parame }); } catch (Exception e) { - throw new EntrySyntaxException(format("bad entry arguments : %s[(%s)]", - value, isNull(args) ? "" : joinArray(", ", args.toArray())), e); + throw new EntrySyntaxException("bad entry arguments: " + + badArgumentsFormat(value, nonNull(args) ? args.toArray() : null), e); } return arr; } diff --git a/src/main/java/org/usf/jquery/web/RequestParameterResolver.java b/src/main/java/org/usf/jquery/web/RequestParameterResolver.java index 3c339f4a..3b110d10 100644 --- a/src/main/java/org/usf/jquery/web/RequestParameterResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestParameterResolver.java @@ -55,6 +55,10 @@ public QueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull Map Date: Fri, 6 Sep 2024 15:44:55 +0200 Subject: [PATCH 229/298] edit --- src/main/java/org/usf/jquery/core/JoinType.java | 7 ++++++- src/main/java/org/usf/jquery/core/ViewJoin.java | 5 +++++ src/main/java/org/usf/jquery/web/QueryDecorator.java | 4 +++- .../java/org/usf/jquery/web/RequestEntryChain.java | 11 ++++------- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/JoinType.java b/src/main/java/org/usf/jquery/core/JoinType.java index 534c4e99..8ad572fd 100644 --- a/src/main/java/org/usf/jquery/core/JoinType.java +++ b/src/main/java/org/usf/jquery/core/JoinType.java @@ -1,6 +1,11 @@ package org.usf.jquery.core; +/** + * + * @author u$f + * + */ public enum JoinType { - INNER, LEFT, RIGHT, FULL; + INNER, LEFT, RIGHT, FULL, CROSS; } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index 7ed432a5..98d519a1 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -1,6 +1,7 @@ package org.usf.jquery.core; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.JoinType.CROSS; import static org.usf.jquery.core.JoinType.FULL; import static org.usf.jquery.core.JoinType.INNER; import static org.usf.jquery.core.JoinType.LEFT; @@ -53,4 +54,8 @@ public static ViewJoin rightJoin(DBView view, DBFilter... filters) { public static ViewJoin fullJoin(DBView view, DBFilter... filters) { return new ViewJoin(FULL, view, filters); } + + public static ViewJoin crossJoin(DBView view, DBFilter... filters) { + return new ViewJoin(CROSS, view, filters); + } } diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 9fce0411..1a7234a5 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -34,7 +34,9 @@ public DBView view() { } public NamedColumn column(String id) { - return query.getBuilder().lookupDeclaredColumn(id) + return query.getBuilder().getColumns().stream() //do not use declaredColumn + .filter(c-> id.equals(c.getTag())) + .findAny() .map(c-> new ViewColumn(c.getTag(), query, c.getType(), null)) .orElseThrow(()-> undeclaredResouceException(id, identity())); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index a9a6170b..0d3ec114 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -45,7 +45,6 @@ import java.util.function.IntFunction; import java.util.stream.Stream; -import org.usf.jquery.core.BadArgumentException; import org.usf.jquery.core.ComparisonExpression; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; @@ -339,16 +338,14 @@ private ViewResource lookupResource(ViewDecorator vd, QueryContext ctx, boolean return ctx.lookupDeclaredColumn(value) .map(c-> new ViewResource(requireNoArgs(), null, null, c)) //declared column first .or(()-> lookupViewResource(vd, ctx, null, filter)) //registered column - .orElseThrow(()-> noSuchViewColumnException(this)); //no such resource + .orElseThrow(()-> noSuchViewResourceException(this)); //no such resource } //query.column private Optional lookupQueryResource(ViewDecorator vd) { if(vd instanceof QueryDecorator qd) { - try { - var col = qd.column(value); - return Optional.of(new ViewResource(requireNoArgs(), qd, null, col)); - } catch (Exception e) {/*do not throw exception*/} + var col = qd.column(value); //do not catch exception + return Optional.of(new ViewResource(requireNoArgs(), qd, null, col)); } return empty(); } @@ -500,7 +497,7 @@ private static String[] toStringArray(List entries) { return new String[0]; } - static NoSuchResourceException noSuchViewColumnException(RequestEntryChain e) { + static NoSuchResourceException noSuchViewResourceException(RequestEntryChain e) { return noSuchResourceException("resource", e.hasNext() && currentContext().lookupRegisteredView(e.value).isPresent() ? e.value + "." + e.next.value From d6e189e77eeaf9f0e09d3c579639fc4700f9e3ff Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 9 Sep 2024 17:50:15 +0200 Subject: [PATCH 230/298] edit --- .../java/org/usf/jquery/core/CaseColumn.java | 30 +++-- .../jquery/core/CaseSingleColumnBuilder.java | 4 +- src/main/java/org/usf/jquery/core/Clause.java | 13 ++ .../usf/jquery/core/ColumnFilterGroup.java | 12 +- .../usf/jquery/core/ColumnSingleFilter.java | 18 ++- .../usf/jquery/core/ComparisonExpression.java | 2 +- .../core/ComparisonExpressionGroup.java | 11 +- .../core/ComparisonSingleExpression.java | 14 ++- .../java/org/usf/jquery/core/DBColumn.java | 44 +++---- .../org/usf/jquery/core/DBExpression.java | 11 -- .../java/org/usf/jquery/core/DBFilter.java | 7 +- src/main/java/org/usf/jquery/core/DBView.java | 2 +- .../java/org/usf/jquery/core/Groupable.java | 7 +- .../java/org/usf/jquery/core/JQueryType.java | 2 +- src/main/java/org/usf/jquery/core/Nested.java | 66 ++++++++++- .../org/usf/jquery/core/OperationColumn.java | 73 ++++++++---- .../java/org/usf/jquery/core/Partition.java | 27 +++-- .../org/usf/jquery/core/QueryBuilder.java | 111 ++++++++++-------- .../org/usf/jquery/core/QueryVariables.java | 41 ++++--- .../java/org/usf/jquery/core/QueryView.java | 4 +- ...ueryColumn.java => SingleQueryColumn.java} | 12 +- .../java/org/usf/jquery/core/ViewColumn.java | 10 ++ .../{WhenExpression.java => WhenCase.java} | 30 ++++- .../org/usf/jquery/web/QueryDecorator.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 14 +-- .../org/usf/jquery/web/RevisionIterator.java | 4 +- .../org/usf/jquery/web/ViewDecorator.java | 2 +- .../org/usf/jquery/web/YearViewDecorator.java | 2 +- 28 files changed, 378 insertions(+), 197 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/Clause.java delete mode 100644 src/main/java/org/usf/jquery/core/DBExpression.java rename src/main/java/org/usf/jquery/core/{QueryColumn.java => SingleQueryColumn.java} (57%) rename src/main/java/org/usf/jquery/core/{WhenExpression.java => WhenCase.java} (57%) diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 9a89fbd8..41542eb3 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -19,27 +19,43 @@ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class CaseColumn implements DBColumn { // TD override isAggregation - private final Collection expressions = new ArrayList<>(); + private final Collection whenCases = new ArrayList<>(); //immutable @Override public String sql(QueryVariables builder) { var b = builder.withValue(); //force literal parameter - return expressions.stream() //empty !? + return whenCases.stream() //empty !? .map(o-> o.sql(b)) .collect(joining(SPACE, "CASE ", " END")); } @Override public JDBCType getType() { - return expressions.stream() - .map(WhenExpression::getType) + return whenCases.stream() + .map(WhenCase::getType) .filter(Objects::nonNull) // should have same type .findAny() .orElse(null); } + + @Override + public boolean resolve(QueryBuilder builder) { + var res = false; + for(var c : whenCases) { + res |= c.resolve(builder); + } + return res; + } + + @Override + public void views(Collection views) { + for(var e : whenCases) { + e.views(views); + } + } - public CaseColumn append(WhenExpression we) { - expressions.add(we); + public CaseColumn append(WhenCase we) { + whenCases.add(we); return this; } @@ -50,6 +66,6 @@ public String toString() { public static CaseColumn caseWhen(DBFilter filter, Object value){ return new CaseColumn() - .append(new WhenExpression(filter, value)); + .append(new WhenCase(filter, value)); } } diff --git a/src/main/java/org/usf/jquery/core/CaseSingleColumnBuilder.java b/src/main/java/org/usf/jquery/core/CaseSingleColumnBuilder.java index 5316d071..9efcfc69 100644 --- a/src/main/java/org/usf/jquery/core/CaseSingleColumnBuilder.java +++ b/src/main/java/org/usf/jquery/core/CaseSingleColumnBuilder.java @@ -50,7 +50,7 @@ public CaseColumn end() { } private CaseColumn orElseExp(Object elseValue) { - caseColumn.append(WhenExpression.orElse(elseValue)); + caseColumn.append(WhenCase.orElse(elseValue)); return caseColumn; } @@ -85,7 +85,7 @@ public CaseSingleColumnBuilder then(@NonNull Supplier fn) { } private CaseSingleColumnBuilder thenExp(Object o) { - caseColumn.append(new WhenExpression(requireNonNull(filter), o)); + caseColumn.append(new WhenCase(requireNonNull(filter), o)); filter = null; return CaseSingleColumnBuilder.this; } diff --git a/src/main/java/org/usf/jquery/core/Clause.java b/src/main/java/org/usf/jquery/core/Clause.java new file mode 100644 index 00000000..13633612 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/Clause.java @@ -0,0 +1,13 @@ +package org.usf.jquery.core; + +/** + * + * @author u$f + * + */ +public enum Clause { + + COLUMN, + FILTER, + ORDER; +} diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index c8dfec7d..5af79b52 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -1,10 +1,11 @@ package org.usf.jquery.core; -import static java.util.Objects.nonNull; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.Nested.viewsOfNested; import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Utils.appendLast; +import java.util.Collection; import java.util.stream.Stream; /** @@ -29,10 +30,15 @@ public String sql(QueryVariables builder) { .map(o-> o.sql(builder)) .collect(joining(operator.sql(), "(", ")")); } + + @Override + public boolean resolve(QueryBuilder builder) { + return Nested.resolveAll(filters, builder); + } @Override - public boolean isAggregation() { - return nonNull(filters) && Stream.of(filters).anyMatch(DBFilter::isAggregation); + public void views(Collection views) { + viewsOfNested(views, filters); } @Override diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index 12c60ac4..d275a9c4 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -1,8 +1,10 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Nested.aggregation; +import static org.usf.jquery.core.Nested.viewsOf; import static org.usf.jquery.core.QueryVariables.addWithValue; +import java.util.Collection; + import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -12,7 +14,7 @@ * */ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public final class ColumnSingleFilter implements DBFilter { +public class ColumnSingleFilter implements DBFilter { private final Object left; private final ComparisonExpression expression; @@ -21,10 +23,18 @@ public final class ColumnSingleFilter implements DBFilter { public String sql(QueryVariables ph) { return expression.sql(ph, left); } + + @Override + public boolean resolve(QueryBuilder builder) { + var res1 = Nested.resolve(left, builder); + var res2 = expression.resolve(builder); + return res1 || res2; + } @Override - public boolean isAggregation() { - return aggregation(left) || expression.isAggregation(); + public void views(Collection views) { + viewsOf(views, left); + expression.views(views); } @Override diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpression.java b/src/main/java/org/usf/jquery/core/ComparisonExpression.java index 94b8c6df..2afaa072 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpression.java @@ -9,7 +9,7 @@ * @author u$f * */ -public interface ComparisonExpression extends DBExpression, Nested, Chainable { +public interface ComparisonExpression extends DBObject, Nested, Chainable { String sql(QueryVariables builder, Object left); // do change method order diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index 915098ec..e7ed0c52 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -1,9 +1,11 @@ package org.usf.jquery.core; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.Nested.viewsOfNested; import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Utils.appendLast; +import java.util.Collection; import java.util.stream.Stream; /** @@ -30,8 +32,13 @@ public String sql(QueryVariables builder, Object operand) { } @Override - public boolean isAggregation() { - return Stream.of(expressions).anyMatch(ComparisonExpression::isAggregation); + public boolean resolve(QueryBuilder builder) { + return Nested.resolveAll(expressions, builder); + } + + @Override + public void views(Collection views) { + viewsOfNested(views, expressions); } @Override diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index 9fb6f11c..d51575fb 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -2,10 +2,11 @@ import static java.util.Collections.addAll; import static java.util.Objects.nonNull; +import static org.usf.jquery.core.Nested.viewsOfAll; import static org.usf.jquery.core.QueryVariables.addWithValue; import java.util.ArrayList; -import java.util.stream.Stream; +import java.util.Collection; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -19,7 +20,7 @@ public final class ComparisonSingleExpression implements ComparisonExpression { private final Comparator comparator; - private final Object[] right; //nullable + private final Object[] right; //optional @Override public String sql(QueryVariables builder, Object left) { @@ -32,8 +33,13 @@ public String sql(QueryVariables builder, Object left) { } @Override - public boolean isAggregation() { - return nonNull(right) && Stream.of(right).anyMatch(Nested::aggregation); + public boolean resolve(QueryBuilder builder) { + return Nested.tryResolveAll(builder, right); + } + + @Override + public void views(Collection views) { + viewsOfAll(views, right); } @Override diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 1f364bd1..262b5e6f 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -7,9 +7,9 @@ import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; +import java.util.Collection; import java.util.Objects; import java.util.function.Supplier; -import java.util.stream.Stream; import org.usf.jquery.core.CaseSingleColumnBuilder.WhenFilterBridge; import org.usf.jquery.core.JavaType.Typed; @@ -21,8 +21,7 @@ * @author u$f * */ -@FunctionalInterface -public interface DBColumn extends DBObject, Typed, Groupable { +public interface DBColumn extends DBObject, Typed, Nested { String sql(QueryVariables builder); @@ -32,21 +31,6 @@ default String sql(QueryVariables builder, Object[] args) { return sql(builder); } - @Override - default JDBCType getType() { - return null; - } - - @Override - default boolean isAggregation() { - return false; - } - - @Override - default Stream groupKeys() { - return Stream.of(this); - } - default ColumnProxy as(String name) { return as(name, null); } @@ -419,8 +403,8 @@ static OperationColumn ctimestamp() { return Operator.ctimestamp().operation(); } - static OperationColumn countAll() { - return Operator.count().operation(column("*")); + static OperationColumn countAll(DBView view) { + return Operator.count().operation(allColumns(view)); } //window functions @@ -438,7 +422,7 @@ static OperationColumn denseRank() { } static DBColumn column(@NonNull String value) { - return b-> value; + return new ViewColumn(requireLegalVariable(value), null, null, value); } static NamedColumn allColumns(@NonNull DBView view) { @@ -446,10 +430,10 @@ static NamedColumn allColumns(@NonNull DBView view) { } static DBColumn constant(Object value) { - return constant(()-> value); + return constant(JDBCType.typeOf(value).orElse(null), ()-> value); } - static DBColumn constant(Supplier value) { + static DBColumn constant(JDBCType type, Supplier value) { return new DBColumn() { @Override @@ -458,8 +442,18 @@ public String sql(QueryVariables arg) { } @Override - public Stream groupKeys() { - return Stream.empty(); + public JDBCType getType() { + return type; + } + + @Override + public boolean resolve(QueryBuilder builder) { + return false; + } + + @Override + public void views(Collection views) { + //no view } }; } diff --git a/src/main/java/org/usf/jquery/core/DBExpression.java b/src/main/java/org/usf/jquery/core/DBExpression.java deleted file mode 100644 index 480fa7ef..00000000 --- a/src/main/java/org/usf/jquery/core/DBExpression.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.usf.jquery.core; - -/** - * - * @author u$f - * - */ -@FunctionalInterface -public interface DBExpression extends DBObject { - -} diff --git a/src/main/java/org/usf/jquery/core/DBFilter.java b/src/main/java/org/usf/jquery/core/DBFilter.java index bb55c44a..e6799942 100644 --- a/src/main/java/org/usf/jquery/core/DBFilter.java +++ b/src/main/java/org/usf/jquery/core/DBFilter.java @@ -7,18 +7,15 @@ * @author u$f * */ -@FunctionalInterface public interface DBFilter extends DBObject, Nested, Chainable { String sql(QueryVariables builder); + DBFilter append(LogicalOperator op, DBFilter filter); + @Override default String sql(QueryVariables builder, Object[] args) { requireNoArgs(args, DBFilter.class::getSimpleName); return sql(builder); } - - default DBFilter append(LogicalOperator op, DBFilter filter) { - throw new UnsupportedOperationException(); //explicitly overridden - } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index efd60704..e44b61d9 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -22,7 +22,7 @@ default String sql(QueryVariables builder, Object[] args) { default String sqlWithTag(QueryVariables builder) { var tag = builder.viewAlias(this); - var sql = builder.viewOverload(this).sql(builder); //!important + var sql = builder.viewOverload(this).orElse(this).sql(builder); //!important return isNull(tag) ? sql : sql + SPACE + tag; } } diff --git a/src/main/java/org/usf/jquery/core/Groupable.java b/src/main/java/org/usf/jquery/core/Groupable.java index 883ce7a2..ce9dfd5e 100644 --- a/src/main/java/org/usf/jquery/core/Groupable.java +++ b/src/main/java/org/usf/jquery/core/Groupable.java @@ -1,13 +1,8 @@ package org.usf.jquery.core; -import java.util.stream.Stream; - /** * * @author u$f * */ -public interface Groupable extends Nested { - - Stream groupKeys(); -} +public interface Groupable extends Nested {} diff --git a/src/main/java/org/usf/jquery/core/JQueryType.java b/src/main/java/org/usf/jquery/core/JQueryType.java index caa90389..da4cb7f1 100644 --- a/src/main/java/org/usf/jquery/core/JQueryType.java +++ b/src/main/java/org/usf/jquery/core/JQueryType.java @@ -13,7 +13,7 @@ public enum JQueryType implements JavaType { VIEW(DBView.class), COLUMN(DBColumn.class), NAMED_COLUMN(NamedColumn.class), - QUERY_COLUMN(QueryColumn.class), + QUERY_COLUMN(SingleQueryColumn.class), FILTER(DBFilter.class), ORDER(DBOrder.class), QUERY(QueryView.class), diff --git a/src/main/java/org/usf/jquery/core/Nested.java b/src/main/java/org/usf/jquery/core/Nested.java index ab05a596..ee367945 100644 --- a/src/main/java/org/usf/jquery/core/Nested.java +++ b/src/main/java/org/usf/jquery/core/Nested.java @@ -1,5 +1,12 @@ package org.usf.jquery.core; +import static java.util.function.Function.identity; +import static org.usf.jquery.core.Utils.isEmpty; + +import java.util.Collection; +import java.util.function.Function; +import java.util.function.Predicate; + /** * * @author u$f @@ -7,11 +14,62 @@ */ public interface Nested { - default boolean isAggregation() { - return false; + void views(Collection views); + + boolean resolve(QueryBuilder builder); + + + static boolean tryResolveAll(QueryBuilder builder, Object... args){ + return resolveAll(args, o-> resolve(o, builder)); + } + + static boolean resolveAll(T[] arr, Function fn, QueryBuilder builder){ + return resolveAll(arr, o-> fn.apply(o).resolve(builder)); + } + + static boolean resolveAll(Nested[] arr, QueryBuilder builder){ + return resolveAll(arr, n-> n.resolve(builder)); + } + + static boolean resolveAll(T[] arr, Predicate fn){ + var res = false; + if(!isEmpty(arr)) { + for(var o : arr) { + res |= fn.test(o); + } + } + return res; + } + + static boolean resolve(Object o, QueryBuilder builder) { + return o instanceof Nested n && n.resolve(builder); + } + + + + static void viewsOfNested(Collection views, T[] arr) { + viewsOfNested(views, arr, identity()); + } + + static void viewsOfNested(Collection views, T[] arr, Function fn) { + if(!isEmpty(arr)) { + for(var o : arr) { + fn.apply(o).views(views); + } + } + } + + static void viewsOfAll(Collection views, Object[] arr) { + if(!isEmpty(arr)) { + for(var o : arr) { + viewsOf(views, o); + } + } } - static boolean aggregation(Object o) { - return o instanceof Nested nes && nes.isAggregation(); + static void viewsOf(Collection views, Object o) { + if(o instanceof Nested n) { + n.views(views); + } } } diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 7aa5c5bc..a67325e0 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -1,8 +1,14 @@ package org.usf.jquery.core; +import static java.util.Objects.nonNull; +import static org.usf.jquery.core.Clause.FILTER; +import static org.usf.jquery.core.Nested.viewsOfAll; import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.Validation.requireNArgs; -import java.util.stream.Stream; +import java.util.Collection; +import java.util.HashSet; +import java.util.Objects; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -16,45 +22,72 @@ public final class OperationColumn implements DBColumn { private final Operator operator; - private final Object[] args; - private final JDBCType type; + private final Object[] args; //optional + private final JDBCType type; //optional + private DBColumn overColumn; public OperationColumn(Operator operation, Object[] args) { this(operation, args, null); } @Override - public String sql(QueryVariables builder) { - return operator.sql(builder, args); + public String sql(QueryVariables qv) { + return Objects.isNull(overColumn) ? operator.sql(qv, args) : overColumn.sql(qv); } @Override public JDBCType getType() { - return type; + return Objects.isNull(overColumn) ? type : overColumn.getType(); } - + @Override - public boolean isAggregation() { - return operator.is(AggregateFunction.class) || - (!isOverFunction() && Stream.of(args).anyMatch(Nested::aggregation)); //can do better + public boolean resolve(QueryBuilder builder) { + if(operator.is(AggregateFunction.class)) { + builder.aggregation(); + return true; + } + else if(isOverFunction()) { + if(builder.getClause() == FILTER) { + var views = new HashSet(); + views(views); + if(views.size() == 1) { + var view = views.iterator().next(); + var cTag = "over_" + hashCode(); //over_view_hash + builder.overView(view).getBuilder().columns(new OperationColumn(operator, args, type).as(cTag)); //clone + this.overColumn = new ViewColumn(cTag, view, getType(), null); + return false; + } + throw new UnsupportedOperationException("require only one view"); + } + return requirePartition().resolve(builder); //no aggregation + } + return operator.is(ConstantOperator.class) + || Nested.tryResolveAll(builder, args); } @Override - public Stream groupKeys() { - if(isOverFunction()) { - return ((Partition)args[1]).groupKeys(); + public void views(Collection views) { + if(nonNull(overColumn)) { + overColumn.views(views); + } + else { + viewsOfAll(views, args); } - return operator.is(AggregateFunction.class) || operator.is(ConstantOperator.class) - ? Stream.empty() - : DBColumn.super.groupKeys(); - } - - boolean isOverFunction() { - return "OVER".equals(operator.id()); } @Override public String toString() { return sql(addWithValue()); } + + boolean isOverFunction() { + return "OVER".equals(operator.id()); + } + + private Partition requirePartition() { + if(requireNArgs(2, args, ()-> "over operation")[1] instanceof Partition part) { + return part; + } + throw new IllegalArgumentException("partition parameter expected"); + } } diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index e2978b67..de9bbccc 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -1,11 +1,11 @@ package org.usf.jquery.core; -import static java.util.stream.Stream.concat; +import static org.usf.jquery.core.Nested.viewsOfNested; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireNoArgs; -import java.util.stream.Stream; +import java.util.Collection; import lombok.RequiredArgsConstructor; @@ -15,7 +15,7 @@ * */ @RequiredArgsConstructor -public final class Partition implements DBObject, Groupable { +public final class Partition implements DBObject, Nested { private final DBColumn[] columns; private final DBOrder[] orders; @@ -37,16 +37,17 @@ String sql(QueryVariables builder) { } return sb.toString(); } - + @Override - public Stream groupKeys() { - Stream s = Stream.empty(); - if(!isEmpty(columns)) { - s = Stream.of(columns); - } - if(!isEmpty(orders)) { - s = concat(s, Stream.of(orders).map(DBOrder::getColumn)); - } - return s; + public boolean resolve(QueryBuilder builder) { + var r1 = Nested.resolveAll(columns, builder); + var r2 = Nested.resolveAll(orders, DBOrder::getColumn, builder); + return r1 || r2; + } + + @Override + public void views(Collection views) { + viewsOfNested(views, columns); + viewsOfNested(views, orders, DBOrder::getColumn); } } diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index cdf4fb81..b7c12827 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -6,6 +6,9 @@ import static java.util.Objects.nonNull; import static java.util.function.Predicate.not; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.Clause.COLUMN; +import static org.usf.jquery.core.Clause.FILTER; +import static org.usf.jquery.core.Clause.ORDER; import static org.usf.jquery.core.Database.TERADATA; import static org.usf.jquery.core.Database.currentDatabase; import static org.usf.jquery.core.Database.setCurrentDatabase; @@ -38,15 +41,20 @@ public class QueryBuilder implements QueryContext { private final List columns = new ArrayList<>(); - private final List filters = new ArrayList<>(); //WHERE & HAVING + private final List group = new ArrayList<>(); //WHERE & HAVING + private final List where = new ArrayList<>(); //WHERE & HAVING + private final List having = new ArrayList<>(); //WHERE & HAVING private final List orders = new ArrayList<>(); private final List joins = new ArrayList<>(); private final Map overView = new HashMap<>(); private boolean distinct; + private boolean aggregation; private Integer fetch; private Integer offset; private Iterator it; + private Clause clause; + public QueryBuilder() { this(null); } @@ -69,6 +77,7 @@ public QueryView overView(DBView view, Supplier supp) { } public QueryBuilder columns(@NonNull NamedColumn... columns) { + this.clause = COLUMN; for(var col : columns) { //optional tag if(nonNull(col.getTag()) && this.columns.stream() .filter(c-> nonNull(c.getTag())) @@ -76,17 +85,30 @@ public QueryBuilder columns(@NonNull NamedColumn... columns) { throw resourceAlreadyExistsException(col.getTag()); } this.columns.add(col); + if(!col.resolve(this)) { + group.add(col); + } } return this; } public QueryBuilder filters(@NonNull DBFilter... filters){ - addAll(this.filters, filters); + this.clause = FILTER; + for(var f : filters) { + (f.resolve(this) ? having : where).add(f); + } return this; } public QueryBuilder orders(@NonNull DBOrder... orders) { - addAll(this.orders, orders); + this.clause = ORDER; + for(var o : orders) { + this.orders.add(o); + var c = o.getColumn(); + if(!c.resolve(this)) { + this.group.add(c); + } + } return this; } @@ -114,6 +136,11 @@ public QueryBuilder repeat(@NonNull Iterator it) { this.it = it; return this; } + + QueryBuilder aggregation() { + this.aggregation = true; + return this; + } public QueryView asView() { return new QueryView(this); @@ -141,18 +168,18 @@ public RequestQuery build(String schema) { public final void build(SqlStringBuilder sb, QueryVariables pb){ var sub = new SqlStringBuilder(100); - join(sub, pb); - where(sub, pb); + where(sub, pb); //first resolve over view groupBy(sub, pb); having(sub, pb); orderBy(sub, pb); - fetch(sub); + fetch(sub, pb); select(sb, pb); from(sb, pb); //enumerate all views before from clause + join(sb, pb); sb.append(sub.toString()); //TODO optim } - void select(SqlStringBuilder sb, QueryVariables pb){ + void select(SqlStringBuilder sb, QueryVariables qv){ if(currentDatabase() == TERADATA) { if(nonNull(offset)) { throw new UnsupportedOperationException(""); @@ -165,64 +192,53 @@ void select(SqlStringBuilder sb, QueryVariables pb){ .appendIf(distinct, " DISTINCT") .appendIf(nonNull(fetch) && currentDatabase() == TERADATA, ()-> " TOP " + fetch) //??????? .append(SPACE) - .appendEach(columns, SCOMA, o-> o.sqlWithTag(pb)); + .appendEach(columns, SCOMA, o-> o.sqlWithTag(qv)); } - void from(SqlStringBuilder sb, QueryVariables pb) { - var excludes = joins.stream().map(ViewJoin::getView).map(pb::viewOverload).toList(); - var views = pb.views().stream().filter(not(excludes::contains)).toList(); //do not remove views + void from(SqlStringBuilder sb, QueryVariables qv) { + var excludes = joins.stream().map(ViewJoin::getView).mapMulti((v,c)-> qv.viewOverload(v).ifPresent(c)).toList(); + var views = qv.views().stream().filter(not(excludes::contains)).distinct().toList(); //do not remove views if(!views.isEmpty()) { - sb.append(" FROM ").appendEach(views, SCOMA, v-> v.sqlWithTag(pb)); + sb.append(" FROM ").appendEach(views, SCOMA, v-> v.sqlWithTag(qv)); } } - void join(SqlStringBuilder sb, QueryVariables pb) { + void join(SqlStringBuilder sb, QueryVariables qv) { if(!joins.isEmpty()) { - sb.append(SPACE).appendEach(joins, SPACE, v-> v.sql(pb)); + sb.append(SPACE).appendEach(joins, SPACE, v-> v.sql(qv)); } } - void where(SqlStringBuilder sb, QueryVariables pb){ - var expr = filters.stream() - .filter(not(DBFilter::isAggregation)) - .map(f-> f.sql(pb)) - .collect(joining(AND.sql())); - if(!expr.isEmpty()) { - sb.append(" WHERE ").append(expr); - } + void where(SqlStringBuilder sb, QueryVariables qv){ + if(!where.isEmpty()) { + sb.append(" WHERE ").appendEach(where, AND.sql(), f-> f.sql(qv)); + } + } + + void groupBy(SqlStringBuilder sb, QueryVariables qv){ + if(aggregation && !group.isEmpty()) { + sb.append(" GROUP BY ") + .append(group.stream() + .map(c-> !(c instanceof ViewColumn) && columns.contains(c) ? ((NamedColumn)c).getTag() : c.sql(qv)) //add alias + .collect(joining(SCOMA))); + } } - void groupBy(SqlStringBuilder sb, QueryVariables pb){ - if(isAggregation()) { // also check filter - var expr = columns.stream() - .filter(not(DBColumn::isAggregation)) - .flatMap(DBColumn::groupKeys) - .map(c-> !(c instanceof ViewColumn) && columns.contains(c) ? ((NamedColumn)c).getTag() : c.sql(pb)) //add alias - .collect(joining(SCOMA)); - if(!expr.isEmpty()) { - sb.append(" GROUP BY ").append(expr); - } - } - } - - void having(SqlStringBuilder sb, QueryVariables pb){ - var having = filters.stream() - .filter(DBFilter::isAggregation) - .toList(); - if(!having.isEmpty()) { + void having(SqlStringBuilder sb, QueryVariables qv){ + if(!having.isEmpty()) { sb.append(" HAVING ") - .appendEach(having, AND.sql(), f-> f.sql(pb)); - } + .appendEach(having, AND.sql(), f-> f.sql(qv)); + } } - void orderBy(SqlStringBuilder sb, QueryVariables pb) { + void orderBy(SqlStringBuilder sb, QueryVariables qv) { if(!orders.isEmpty()) { sb.append(" ORDER BY ") - .appendEach(orders, SCOMA, o-> o.sql(pb)); + .appendEach(orders, SCOMA, o-> o.sql(qv)); } } - void fetch(SqlStringBuilder sb) { + void fetch(SqlStringBuilder sb, QueryVariables qv) { if(currentDatabase() != TERADATA) { // TOP n if(nonNull(offset)) { sb.append(" OFFSET ").append(offset.toString()).append(" ROWS"); @@ -232,11 +248,6 @@ void fetch(SqlStringBuilder sb) { } } } - - public boolean isAggregation() { - return columns.stream().anyMatch(Nested::isAggregation) || - filters.stream().anyMatch(Nested::isAggregation); - } @Override public String toString() { diff --git a/src/main/java/org/usf/jquery/core/QueryVariables.java b/src/main/java/org/usf/jquery/core/QueryVariables.java index c8f0f93b..3ea9a0ea 100644 --- a/src/main/java/org/usf/jquery/core/QueryVariables.java +++ b/src/main/java/org/usf/jquery/core/QueryVariables.java @@ -1,9 +1,10 @@ package org.usf.jquery.core; import static java.util.Collections.emptyMap; -import static java.util.Collections.unmodifiableMap; import static java.util.Objects.nonNull; +import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.DBColumn.allColumns; import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.SqlStringBuilder.COMA; import static org.usf.jquery.core.SqlStringBuilder.EMPTY; @@ -12,21 +13,26 @@ import static org.usf.jquery.core.Utils.isEmpty; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Stream; import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; /** * * @author u$f * */ -@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Setter(AccessLevel.PACKAGE) +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class QueryVariables { private static final String P_ARG = "?"; @@ -37,10 +43,9 @@ public final class QueryVariables { private final List args; //parameterized flag private final List argTypes; private final List views; //indexed view - private final Map overView; - + private final Map overView; + public String viewAlias(DBView view) { - view = viewOverload(view); var idx = views.indexOf(view); if(idx < 0) { idx = views.size(); @@ -48,9 +53,17 @@ public String viewAlias(DBView view) { } return vPrefix + (idx+1); } + + public QueryView overView(DBView view) { + return overView(view, ()-> new QueryBuilder().columns(allColumns(view)).asView()); + } + + public QueryView overView(DBView view, Supplier supp) { + return overView.computeIfAbsent(view, k-> supp.get()); + } - public DBView viewOverload(DBView view) { - return overView.getOrDefault(view, view); + public Optional viewOverload(DBView view) { + return ofNullable(overView.get(view)); } public List views(){ @@ -145,19 +158,19 @@ public QueryVariables withValue() { return new QueryVariables(schema, vPrefix, null, null, views, overView); } - public QueryVariables subQuery(Map overView) { - return new QueryVariables(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), unmodifiableMap(overView)); + public QueryVariables subQuery(Map overView) { + return new QueryVariables(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), new HashMap<>(overView)); } public static QueryVariables addWithValue() { return addWithValue(null, emptyMap()); //no args } - public static QueryVariables addWithValue(String schema, Map overView) { - return new QueryVariables(schema, "v", null, null, new ArrayList<>(), unmodifiableMap(overView)); //no args + public static QueryVariables addWithValue(String schema, Map overView) { + return new QueryVariables(schema, "v", null, null, new ArrayList<>(), new HashMap<>(overView)); //no args } - public static QueryVariables parameterized(String schema, Map overView) { - return new QueryVariables(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), unmodifiableMap(overView)); + public static QueryVariables parameterized(String schema, Map overView) { + return new QueryVariables(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new HashMap<>(overView)); } } diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index d606f5dc..e2e09079 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -24,9 +24,9 @@ public String sql(QueryVariables param) { return s.append(")").toString(); } - public QueryColumn asColumn(){ + public SingleQueryColumn asColumn(){ if(builder.getColumns().size() == 1) { - return new QueryColumn(this, builder.getColumns().get(0).getType()); + return new SingleQueryColumn(this, builder.getColumns().get(0).getType()); } throw new IllegalStateException("too many column"); } diff --git a/src/main/java/org/usf/jquery/core/QueryColumn.java b/src/main/java/org/usf/jquery/core/SingleQueryColumn.java similarity index 57% rename from src/main/java/org/usf/jquery/core/QueryColumn.java rename to src/main/java/org/usf/jquery/core/SingleQueryColumn.java index 4394bb11..05710391 100644 --- a/src/main/java/org/usf/jquery/core/QueryColumn.java +++ b/src/main/java/org/usf/jquery/core/SingleQueryColumn.java @@ -2,6 +2,9 @@ import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Validation.requireNArgs; +import static org.usf.jquery.core.Validation.requireNoArgs; + +import org.usf.jquery.core.JavaType.Typed; import lombok.AccessLevel; import lombok.Getter; @@ -13,15 +16,20 @@ * */ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public final class QueryColumn implements DBColumn { +public final class SingleQueryColumn implements DBObject, Typed { private final QueryView query; @Getter private final JDBCType type; @Override + public String sql(QueryVariables builder, Object[] args) { + requireNoArgs(args, SingleQueryColumn.class::getSimpleName); + return sql(builder); + } + public String sql(QueryVariables builder) { - requireNArgs(1, query.getBuilder().getColumns().toArray(), QueryColumn.class::getSimpleName); + requireNArgs(1, query.getBuilder().getColumns().toArray(), SingleQueryColumn.class::getSimpleName); return query.sql(builder); } diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index 311fcfe7..a7418943 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -4,6 +4,8 @@ import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.member; +import java.util.Collection; + import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -26,6 +28,14 @@ public String sql(QueryVariables qv) { return nonNull(view) ? member(qv.viewAlias(view), name) : name; } + public boolean resolve(QueryBuilder builder) { + return false; + } + + public void views(Collection views) { + views.add(view); + } + @Override public String toString() { return sql(addWithValue()); diff --git a/src/main/java/org/usf/jquery/core/WhenExpression.java b/src/main/java/org/usf/jquery/core/WhenCase.java similarity index 57% rename from src/main/java/org/usf/jquery/core/WhenExpression.java rename to src/main/java/org/usf/jquery/core/WhenCase.java index 6e215ab9..adb785d1 100644 --- a/src/main/java/org/usf/jquery/core/WhenExpression.java +++ b/src/main/java/org/usf/jquery/core/WhenCase.java @@ -1,10 +1,14 @@ package org.usf.jquery.core; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.JDBCType.typeOf; +import static org.usf.jquery.core.Nested.viewsOf; import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Validation.requireNoArgs; +import java.util.Collection; + import org.usf.jquery.core.JavaType.Typed; import lombok.RequiredArgsConstructor; @@ -15,14 +19,14 @@ * */ @RequiredArgsConstructor -final class WhenExpression implements DBExpression, Typed { +final class WhenCase implements DBObject, Typed, Nested { - private final DBFilter filter; + private final DBFilter filter; //optional private final Object value; @Override public String sql(QueryVariables qv, Object[] args) { - requireNoArgs(args, WhenExpression.class::getSimpleName); + requireNoArgs(args, WhenCase.class::getSimpleName); return sql(qv); } @@ -41,12 +45,28 @@ public JDBCType getType() { return typeOf(value).orElse(null); } + @Override + public boolean resolve(QueryBuilder builder) { + var r1 = nonNull(filter) && filter.resolve(builder); + var r2 = Nested.resolve(value, builder); + return r1 || r2; + } + + + @Override + public void views(Collection views) { + if(nonNull(filter)) { + filter.views(views); + } + viewsOf(views, value); + } + @Override public String toString() { return sql(addWithValue()); } - public static WhenExpression orElse(Object value) { - return new WhenExpression(null, value); + public static WhenCase orElse(Object value) { + return new WhenCase(null, value); } } diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 1a7234a5..38e5414e 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -3,8 +3,8 @@ import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; import org.usf.jquery.core.DBView; -import org.usf.jquery.core.QueryView; import org.usf.jquery.core.NamedColumn; +import org.usf.jquery.core.QueryView; import org.usf.jquery.core.ViewColumn; import lombok.Getter; diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 0d3ec114..8adc520c 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -59,10 +59,9 @@ import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; import org.usf.jquery.core.QueryBuilder; -import org.usf.jquery.core.QueryColumn; +import org.usf.jquery.core.SingleQueryColumn; import org.usf.jquery.core.QueryContext; import org.usf.jquery.core.QueryView; -import org.usf.jquery.core.ViewColumn; import org.usf.jquery.core.ViewJoin; import lombok.AccessLevel; @@ -104,7 +103,7 @@ public ViewDecorator evalView(ViewDecorator vd, QueryContext ctx) { .orElseThrow(()-> noSuchResourceException(VIEW, value)); } - public QueryColumn evalQueryColumn(ViewDecorator td, QueryContext ctx) { + public SingleQueryColumn evalQueryColumn(ViewDecorator td, QueryContext ctx) { return evalQuery(td, ctx, false) .map(QueryDecorator::getQuery) .map(QueryView::asColumn) @@ -304,7 +303,7 @@ private ViewResource chainColumnOperations(ViewDecorator vd, QueryContext ctx, b var o = fn.operation(e.toArgs(vd, ctx, r.col, fn.getParameterSet())); r.cd = null; r.entry = e; - r.col = filter && "over".equals(e.value) ? windowColumn(r.vd, ctx, o) : o; + r.col = o; e = e.next; } else { @@ -315,13 +314,6 @@ private ViewResource chainColumnOperations(ViewDecorator vd, QueryContext ctx, b return r; } - private static DBColumn windowColumn(ViewDecorator vd, QueryContext ctx, DBColumn col) { - var v = vd.view(); - var tag = "over_" + vd.identity() + "_" + col.hashCode(); //over_view_hash - ctx.overView(v).getBuilder().columns(col.as(tag)); //append over colum - return new ViewColumn(doubleQuote(tag), v, col.getType(), null); - } - //view.resource | resource private ViewResource lookupResource(ViewDecorator vd, QueryContext ctx, boolean filter) { //do not change priority if(hasNext()) { diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index a4bc916d..2783c861 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -2,6 +2,7 @@ import static java.util.stream.Collectors.groupingBy; import static org.usf.jquery.core.DBColumn.constant; +import static org.usf.jquery.core.JDBCType.INTEGER; import static org.usf.jquery.core.Utils.isEmpty; import java.time.YearMonth; @@ -12,6 +13,7 @@ import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; +import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.QueryVariables; import org.usf.jquery.core.TableView; @@ -63,7 +65,7 @@ public String sql(QueryVariables builder) { } static DBColumn yearColumn() { - return constant(()-> currentRev.get().getKey()); //get it on build + return constant(INTEGER, ()-> currentRev.get().getKey()); //get it on build } static DBFilter monthFilter(DBColumn column) { diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 3c74ab56..2384fb17 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -31,9 +31,9 @@ import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBView; +import org.usf.jquery.core.NamedColumn; import org.usf.jquery.core.QueryBuilder; import org.usf.jquery.core.TableView; -import org.usf.jquery.core.NamedColumn; import org.usf.jquery.core.ViewColumn; import lombok.NonNull; diff --git a/src/main/java/org/usf/jquery/web/YearViewDecorator.java b/src/main/java/org/usf/jquery/web/YearViewDecorator.java index c8e77deb..b84fd791 100644 --- a/src/main/java/org/usf/jquery/web/YearViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/YearViewDecorator.java @@ -26,9 +26,9 @@ import java.util.stream.Stream; import org.usf.jquery.core.DBView; +import org.usf.jquery.core.NamedColumn; import org.usf.jquery.core.QueryBuilder; import org.usf.jquery.core.TableView; -import org.usf.jquery.core.NamedColumn; /** * From 9c79b4cc17ed485ca5f29d94e0d9fe1720d7b10a Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 9 Sep 2024 17:53:34 +0200 Subject: [PATCH 231/298] edit --- src/main/java/org/usf/jquery/core/QueryBuilder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index b7c12827..66ac7acd 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -41,9 +41,9 @@ public class QueryBuilder implements QueryContext { private final List columns = new ArrayList<>(); - private final List group = new ArrayList<>(); //WHERE & HAVING - private final List where = new ArrayList<>(); //WHERE & HAVING - private final List having = new ArrayList<>(); //WHERE & HAVING + private final List group = new ArrayList<>(); + private final List where = new ArrayList<>(); + private final List having = new ArrayList<>(); private final List orders = new ArrayList<>(); private final List joins = new ArrayList<>(); private final Map overView = new HashMap<>(); From 20b88be16b5414ecd7a3e76d9abf0f0add99ea58 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 9 Sep 2024 20:07:09 +0200 Subject: [PATCH 232/298] edit --- .../java/org/usf/jquery/core/ArgTypeRef.java | 2 +- .../java/org/usf/jquery/core/CaseColumn.java | 45 ++++----- .../jquery/core/CaseSingleColumnBuilder.java | 93 ------------------- .../java/org/usf/jquery/core/ColumnProxy.java | 14 ++- .../usf/jquery/core/ColumnSingleFilter.java | 3 +- .../core/ComparisonExpressionGroup.java | 6 +- .../core/ComparisonSingleExpression.java | 3 +- .../java/org/usf/jquery/core/DBColumn.java | 15 ++- .../java/org/usf/jquery/core/DBFilter.java | 2 - .../java/org/usf/jquery/core/DBOrder.java | 2 +- .../java/org/usf/jquery/core/Groupable.java | 8 -- .../java/org/usf/jquery/core/Mappers.java | 1 - src/main/java/org/usf/jquery/core/Nested.java | 10 +- .../org/usf/jquery/core/OperationColumn.java | 20 ++-- .../java/org/usf/jquery/core/Operator.java | 4 + .../core/{Order.java => OrderType.java} | 2 +- .../java/org/usf/jquery/core/Partition.java | 5 +- .../org/usf/jquery/core/QueryBuilder.java | 12 ++- .../org/usf/jquery/core/QueryContext.java | 9 -- .../java/org/usf/jquery/core/QueryView.java | 5 +- .../jquery/core/SingleCaseColumnBuilder.java | 33 +++++++ .../usf/jquery/core/SingleQueryColumn.java | 23 +++-- .../java/org/usf/jquery/core/WhenCase.java | 8 +- .../org/usf/jquery/web/RequestEntryChain.java | 6 +- 24 files changed, 125 insertions(+), 206 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/core/CaseSingleColumnBuilder.java delete mode 100644 src/main/java/org/usf/jquery/core/Groupable.java rename src/main/java/org/usf/jquery/core/{Order.java => OrderType.java} (76%) create mode 100644 src/main/java/org/usf/jquery/core/SingleCaseColumnBuilder.java diff --git a/src/main/java/org/usf/jquery/core/ArgTypeRef.java b/src/main/java/org/usf/jquery/core/ArgTypeRef.java index 2a30daf4..e7b2f563 100644 --- a/src/main/java/org/usf/jquery/core/ArgTypeRef.java +++ b/src/main/java/org/usf/jquery/core/ArgTypeRef.java @@ -14,6 +14,6 @@ interface ArgTypeRef extends Function { static ArgTypeRef firstArgJdbcType() { return arr-> typeOf(requireAtLeastNArgs(1, arr, ArgTypeRef.class::getSimpleName)[0]) - .orElse(null); // not sure + .orElse(null); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 41542eb3..67f44bc3 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -1,50 +1,47 @@ package org.usf.jquery.core; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.Nested.resolveAll; import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; +import static org.usf.jquery.core.Validation.requireAtLeastNArgs; -import java.util.ArrayList; import java.util.Collection; import java.util.Objects; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; +import java.util.stream.Stream; /** * * @author u$f * */ -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public final class CaseColumn implements DBColumn { // TD override isAggregation +public final class CaseColumn implements DBColumn { - private final Collection whenCases = new ArrayList<>(); //immutable + private final WhenCase[] whenCases; + public CaseColumn(WhenCase[] whenCases) { + this.whenCases = requireAtLeastNArgs(1, whenCases, CaseColumn.class::getSimpleName); + } + @Override - public String sql(QueryVariables builder) { - var b = builder.withValue(); //force literal parameter - return whenCases.stream() //empty !? - .map(o-> o.sql(b)) + public String sql(QueryVariables vars) { + var sub = vars.withValue(); //force literal parameter + return Stream.of(whenCases) + .map(o-> o.sql(sub)) .collect(joining(SPACE, "CASE ", " END")); } @Override public JDBCType getType() { - return whenCases.stream() + return Stream.of(whenCases) .map(WhenCase::getType) .filter(Objects::nonNull) // should have same type - .findAny() - .orElse(null); + .findAny().orElse(null); } @Override public boolean resolve(QueryBuilder builder) { - var res = false; - for(var c : whenCases) { - res |= c.resolve(builder); - } - return res; + return resolveAll(whenCases, builder); } @Override @@ -54,18 +51,8 @@ public void views(Collection views) { } } - public CaseColumn append(WhenCase we) { - whenCases.add(we); - return this; - } - @Override public String toString() { return sql(addWithValue()); } - - public static CaseColumn caseWhen(DBFilter filter, Object value){ - return new CaseColumn() - .append(new WhenCase(filter, value)); - } } diff --git a/src/main/java/org/usf/jquery/core/CaseSingleColumnBuilder.java b/src/main/java/org/usf/jquery/core/CaseSingleColumnBuilder.java deleted file mode 100644 index 9efcfc69..00000000 --- a/src/main/java/org/usf/jquery/core/CaseSingleColumnBuilder.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.usf.jquery.core; - -import static java.util.Objects.requireNonNull; - -import java.util.function.Supplier; - -import lombok.AllArgsConstructor; -import lombok.NonNull; - -/** - * - * @author u$f - * - */ -@AllArgsConstructor -public final class CaseSingleColumnBuilder { - - private final CaseColumn caseColumn = new CaseColumn(); - private final WhenFilterBridge bridge = new WhenFilterBridge(); - private final DBColumn column; - private DBFilter filter; //temp - - public CaseSingleColumnBuilder(@NonNull DBColumn column) { - this.column = column; - } - - public WhenFilterBridge when(ComparisonExpression exp) { - this.filter = new ColumnSingleFilter(column, exp); - return bridge; - } - - public CaseColumn orElse(int value) { - return orElseExp(value); - } - - public CaseColumn orElse(double value) { - return orElseExp(value); - } - - public CaseColumn orElse(String value) { - return orElseExp(value); - } - - public CaseColumn orElse(DBColumn column) { - return orElseExp(column); - } - - public CaseColumn end() { - return caseColumn; - } - - private CaseColumn orElseExp(Object elseValue) { - caseColumn.append(WhenCase.orElse(elseValue)); - return caseColumn; - } - - public ColumnProxy as(String tagname) { - return caseColumn.as(tagname); - } - - public final class WhenFilterBridge { - - public CaseSingleColumnBuilder thenNull() { - return thenExp(null); - } - - public CaseSingleColumnBuilder then(int value) { - return thenExp(value); - } - - public CaseSingleColumnBuilder then(double value) { - return thenExp(value); - } - - public CaseSingleColumnBuilder then(String value) { - return thenExp(value); - } - - public CaseSingleColumnBuilder then(@NonNull DBColumn column) { - return thenExp(column); - } - - public CaseSingleColumnBuilder then(@NonNull Supplier fn) { - return thenExp(fn); - } - - private CaseSingleColumnBuilder thenExp(Object o) { - caseColumn.append(new WhenCase(requireNonNull(filter), o)); - filter = null; - return CaseSingleColumnBuilder.this; - } - } -} diff --git a/src/main/java/org/usf/jquery/core/ColumnProxy.java b/src/main/java/org/usf/jquery/core/ColumnProxy.java index 436b07e1..afaa6f1e 100644 --- a/src/main/java/org/usf/jquery/core/ColumnProxy.java +++ b/src/main/java/org/usf/jquery/core/ColumnProxy.java @@ -6,7 +6,6 @@ import java.util.Objects; import lombok.AccessLevel; -import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.experimental.Delegate; @@ -15,7 +14,6 @@ * @author u$f * */ -@Getter @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class ColumnProxy implements NamedColumn { @@ -24,11 +22,21 @@ public final class ColumnProxy implements NamedColumn { private final JDBCType type; //optional private final String tag; //optional + @Override + public String getTag() { + return tag; + } + @Override public JDBCType getType() { return nonNull(type) ? type : column.getType(); } + @Override // do not delegate this + public ColumnProxy as(String name) { + return NamedColumn.super.as(name); + } + @Override public ColumnProxy as(String name, JDBCType type) { // map return Objects.equals(this.tag, name) && Objects.equals(this.type, type) @@ -38,6 +46,6 @@ public ColumnProxy as(String name, JDBCType type) { // map @Override public String toString() { - return this.sqlWithTag(addWithValue()); + return this.sql(addWithValue()); } } diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index d275a9c4..c0642106 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.Nested.tryResolve; import static org.usf.jquery.core.Nested.viewsOf; import static org.usf.jquery.core.QueryVariables.addWithValue; @@ -26,7 +27,7 @@ public String sql(QueryVariables ph) { @Override public boolean resolve(QueryBuilder builder) { - var res1 = Nested.resolve(left, builder); + var res1 = tryResolve(left, builder); var res2 = expression.resolve(builder); return res1 || res2; } diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index e7ed0c52..e7438f66 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -1,9 +1,11 @@ package org.usf.jquery.core; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.Nested.resolveAll; import static org.usf.jquery.core.Nested.viewsOfNested; import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Utils.appendLast; +import static org.usf.jquery.core.Validation.requireNArgs; import java.util.Collection; import java.util.stream.Stream; @@ -21,7 +23,7 @@ public final class ComparisonExpressionGroup implements ComparisonExpression { ComparisonExpressionGroup(LogicalOperator operator, ComparisonExpression... expressions) { this.operator = operator; - this.expressions = expressions; + this.expressions = requireNArgs(1, expressions, ComparisonExpressionGroup.class::getSimpleName); } @Override @@ -33,7 +35,7 @@ public String sql(QueryVariables builder, Object operand) { @Override public boolean resolve(QueryBuilder builder) { - return Nested.resolveAll(expressions, builder); + return resolveAll(expressions, builder); } @Override diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index d51575fb..d5761316 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -2,6 +2,7 @@ import static java.util.Collections.addAll; import static java.util.Objects.nonNull; +import static org.usf.jquery.core.Nested.tryResolveAll; import static org.usf.jquery.core.Nested.viewsOfAll; import static org.usf.jquery.core.QueryVariables.addWithValue; @@ -34,7 +35,7 @@ public String sql(QueryVariables builder, Object left) { @Override public boolean resolve(QueryBuilder builder) { - return Nested.tryResolveAll(builder, right); + return tryResolveAll(builder, right); } @Override diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 262b5e6f..7a592e84 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -1,17 +1,16 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Order.ASC; -import static org.usf.jquery.core.Order.DESC; +import static java.util.Objects.nonNull; +import static org.usf.jquery.core.OrderType.ASC; +import static org.usf.jquery.core.OrderType.DESC; import static org.usf.jquery.core.QueryVariables.formatValue; import static org.usf.jquery.core.Utils.appendFirst; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; import java.util.Collection; -import java.util.Objects; import java.util.function.Supplier; -import org.usf.jquery.core.CaseSingleColumnBuilder.WhenFilterBridge; import org.usf.jquery.core.JavaType.Typed; import lombok.NonNull; @@ -36,7 +35,7 @@ default ColumnProxy as(String name) { } default ColumnProxy as(String name, JDBCType type) { - return new ColumnProxy(this, type, Objects.isNull(name) ? null : requireLegalVariable(name)); + return new ColumnProxy(this, type, nonNull(name) ? requireLegalVariable(name) : null); } // filters @@ -381,12 +380,12 @@ default DBOrder desc() { return order(DESC); } - default DBOrder order(Order order) { + default DBOrder order(OrderType order) { return new DBOrder(this, order); } - default WhenFilterBridge when(ComparisonExpression ex) { - return new CaseSingleColumnBuilder(this).when(ex); + default SingleCaseColumnBuilder whenCase() { + return new SingleCaseColumnBuilder(this); } // constants diff --git a/src/main/java/org/usf/jquery/core/DBFilter.java b/src/main/java/org/usf/jquery/core/DBFilter.java index e6799942..f3a96415 100644 --- a/src/main/java/org/usf/jquery/core/DBFilter.java +++ b/src/main/java/org/usf/jquery/core/DBFilter.java @@ -11,8 +11,6 @@ public interface DBFilter extends DBObject, Nested, Chainable { String sql(QueryVariables builder); - DBFilter append(LogicalOperator op, DBFilter filter); - @Override default String sql(QueryVariables builder, Object[] args) { requireNoArgs(args, DBFilter.class::getSimpleName); diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index b586473b..40b3ed76 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -19,7 +19,7 @@ public final class DBOrder implements DBObject { private final DBColumn column; - private final Order order; + private final OrderType order; public DBOrder(DBColumn column) { this(column, null); diff --git a/src/main/java/org/usf/jquery/core/Groupable.java b/src/main/java/org/usf/jquery/core/Groupable.java deleted file mode 100644 index ce9dfd5e..00000000 --- a/src/main/java/org/usf/jquery/core/Groupable.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.usf.jquery.core; - -/** - * - * @author u$f - * - */ -public interface Groupable extends Nested {} diff --git a/src/main/java/org/usf/jquery/core/Mappers.java b/src/main/java/org/usf/jquery/core/Mappers.java index a4c82b6c..d5c189ca 100644 --- a/src/main/java/org/usf/jquery/core/Mappers.java +++ b/src/main/java/org/usf/jquery/core/Mappers.java @@ -42,5 +42,4 @@ public static CsvResultMapper csv(Writer w) { public static CsvResultMapper csv(DataWriter out) { return new CsvResultMapper(out); } - } diff --git a/src/main/java/org/usf/jquery/core/Nested.java b/src/main/java/org/usf/jquery/core/Nested.java index ee367945..8157c704 100644 --- a/src/main/java/org/usf/jquery/core/Nested.java +++ b/src/main/java/org/usf/jquery/core/Nested.java @@ -13,14 +13,13 @@ * */ public interface Nested { - - void views(Collection views); boolean resolve(QueryBuilder builder); - + + void views(Collection views); static boolean tryResolveAll(QueryBuilder builder, Object... args){ - return resolveAll(args, o-> resolve(o, builder)); + return resolveAll(args, o-> tryResolve(o, builder)); } static boolean resolveAll(T[] arr, Function fn, QueryBuilder builder){ @@ -41,11 +40,10 @@ static boolean resolveAll(T[] arr, Predicate fn){ return res; } - static boolean resolve(Object o, QueryBuilder builder) { + static boolean tryResolve(Object o, QueryBuilder builder) { return o instanceof Nested n && n.resolve(builder); } - static void viewsOfNested(Collection views, T[] arr) { viewsOfNested(views, arr, identity()); diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index a67325e0..5289fc25 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -2,13 +2,13 @@ import static java.util.Objects.nonNull; import static org.usf.jquery.core.Clause.FILTER; +import static org.usf.jquery.core.Nested.tryResolveAll; import static org.usf.jquery.core.Nested.viewsOfAll; import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Validation.requireNArgs; import java.util.Collection; import java.util.HashSet; -import java.util.Objects; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -31,13 +31,13 @@ public OperationColumn(Operator operation, Object[] args) { } @Override - public String sql(QueryVariables qv) { - return Objects.isNull(overColumn) ? operator.sql(qv, args) : overColumn.sql(qv); + public String sql(QueryVariables vars) { + return nonNull(overColumn) ? overColumn.sql(vars) : operator.sql(vars, args); } @Override public JDBCType getType() { - return Objects.isNull(overColumn) ? type : overColumn.getType(); + return nonNull(overColumn) ? overColumn.getType() : type; } @Override @@ -46,7 +46,7 @@ public boolean resolve(QueryBuilder builder) { builder.aggregation(); return true; } - else if(isOverFunction()) { + else if(operator.is("OVER")) { if(builder.getClause() == FILTER) { var views = new HashSet(); views(views); @@ -54,7 +54,7 @@ else if(isOverFunction()) { var view = views.iterator().next(); var cTag = "over_" + hashCode(); //over_view_hash builder.overView(view).getBuilder().columns(new OperationColumn(operator, args, type).as(cTag)); //clone - this.overColumn = new ViewColumn(cTag, view, getType(), null); + overColumn = new ViewColumn(cTag, view, type, null); return false; } throw new UnsupportedOperationException("require only one view"); @@ -62,7 +62,7 @@ else if(isOverFunction()) { return requirePartition().resolve(builder); //no aggregation } return operator.is(ConstantOperator.class) - || Nested.tryResolveAll(builder, args); + || tryResolveAll(builder, args); } @Override @@ -74,16 +74,12 @@ public void views(Collection views) { viewsOfAll(views, args); } } - + @Override public String toString() { return sql(addWithValue()); } - boolean isOverFunction() { - return "OVER".equals(operator.id()); - } - private Partition requirePartition() { if(requireNArgs(2, args, ()-> "over operation")[1] instanceof Partition part) { return part; diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 3ca61904..5474115b 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -39,6 +39,10 @@ default boolean is(Class type) { return type.isInstance(this); } + default boolean is(String name) { + return name.equals(id()); + } + //Arithmetic operations static TypedOperator plus() { diff --git a/src/main/java/org/usf/jquery/core/Order.java b/src/main/java/org/usf/jquery/core/OrderType.java similarity index 76% rename from src/main/java/org/usf/jquery/core/Order.java rename to src/main/java/org/usf/jquery/core/OrderType.java index c4b6d670..9ffee41b 100644 --- a/src/main/java/org/usf/jquery/core/Order.java +++ b/src/main/java/org/usf/jquery/core/OrderType.java @@ -5,7 +5,7 @@ * @author u$f * */ -public enum Order { +public enum OrderType { ASC, DESC; diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index de9bbccc..07935b9b 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -1,5 +1,6 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.Nested.resolveAll; import static org.usf.jquery.core.Nested.viewsOfNested; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Utils.isEmpty; @@ -40,8 +41,8 @@ String sql(QueryVariables builder) { @Override public boolean resolve(QueryBuilder builder) { - var r1 = Nested.resolveAll(columns, builder); - var r2 = Nested.resolveAll(orders, DBOrder::getColumn, builder); + var r1 = resolveAll(columns, builder); + var r2 = resolveAll(orders, DBOrder::getColumn, builder); return r1 || r2; } diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 66ac7acd..b4c8c5e5 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -9,6 +9,7 @@ import static org.usf.jquery.core.Clause.COLUMN; import static org.usf.jquery.core.Clause.FILTER; import static org.usf.jquery.core.Clause.ORDER; +import static org.usf.jquery.core.DBColumn.allColumns; import static org.usf.jquery.core.Database.TERADATA; import static org.usf.jquery.core.Database.currentDatabase; import static org.usf.jquery.core.Database.setCurrentDatabase; @@ -38,7 +39,7 @@ */ @Slf4j @Getter -public class QueryBuilder implements QueryContext { +public class QueryBuilder { private final List columns = new ArrayList<>(); private final List group = new ArrayList<>(); @@ -52,7 +53,6 @@ public class QueryBuilder implements QueryContext { private Integer fetch; private Integer offset; private Iterator it; - private Clause clause; public QueryBuilder() { @@ -63,15 +63,17 @@ public QueryBuilder(Database target) { setCurrentDatabase(target); } - @Override public Optional lookupDeclaredColumn(String name) { return columns.stream() .filter(ColumnProxy.class::isInstance) .filter(c-> name.equals(c.getTag())) .findAny(); } + + public QueryView overView(DBView view) { + return overView(view, ()-> new QueryBuilder().columns(allColumns(view)).asView()); + } - @Override public QueryView overView(DBView view, Supplier supp) { return overView.computeIfAbsent(view, k-> supp.get()); } @@ -82,7 +84,7 @@ public QueryBuilder columns(@NonNull NamedColumn... columns) { if(nonNull(col.getTag()) && this.columns.stream() .filter(c-> nonNull(c.getTag())) .anyMatch(nc-> nc.getTag().equals(col.getTag()))) { - throw resourceAlreadyExistsException(col.getTag()); + throw resourceAlreadyExistsException(col.getTag()); } this.columns.add(col); if(!col.resolve(this)) { diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java index 5a425d06..09a65cfa 100644 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -1,9 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.DBColumn.allColumns; - import java.util.Optional; -import java.util.function.Supplier; /** * @@ -13,10 +10,4 @@ public interface QueryContext { Optional lookupDeclaredColumn(String name); - - QueryView overView(DBView view, Supplier supp); - - default QueryView overView(DBView view) { - return overView(view, ()-> new QueryBuilder().columns(allColumns(view)).asView()); - } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index e2e09079..bf17a6ca 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -25,10 +25,7 @@ public String sql(QueryVariables param) { } public SingleQueryColumn asColumn(){ - if(builder.getColumns().size() == 1) { - return new SingleQueryColumn(this, builder.getColumns().get(0).getType()); - } - throw new IllegalStateException("too many column"); + return new SingleQueryColumn(this); } @Override diff --git a/src/main/java/org/usf/jquery/core/SingleCaseColumnBuilder.java b/src/main/java/org/usf/jquery/core/SingleCaseColumnBuilder.java new file mode 100644 index 00000000..d3e990f0 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/SingleCaseColumnBuilder.java @@ -0,0 +1,33 @@ +package org.usf.jquery.core; + +import java.util.ArrayList; +import java.util.List; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + +/** + * + * @author u$f + * + */ +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public final class SingleCaseColumnBuilder { + + private final DBColumn column; + private final List cases = new ArrayList<>(); + + public SingleCaseColumnBuilder when(ComparisonExpression exp, Object then) { + cases.add(new WhenCase(column.filter(exp), then)); + return this; + } + + public CaseColumn orElse(Object o) { + new WhenCase(null, o); + return end(); + } + + public CaseColumn end() { + return new CaseColumn(cases.toArray(WhenCase[]::new)); + } +} diff --git a/src/main/java/org/usf/jquery/core/SingleQueryColumn.java b/src/main/java/org/usf/jquery/core/SingleQueryColumn.java index 05710391..3ad1e60d 100644 --- a/src/main/java/org/usf/jquery/core/SingleQueryColumn.java +++ b/src/main/java/org/usf/jquery/core/SingleQueryColumn.java @@ -1,27 +1,30 @@ package org.usf.jquery.core; import static org.usf.jquery.core.QueryVariables.addWithValue; -import static org.usf.jquery.core.Validation.requireNArgs; import static org.usf.jquery.core.Validation.requireNoArgs; import org.usf.jquery.core.JavaType.Typed; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - /** * * @author u$f * */ -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class SingleQueryColumn implements DBObject, Typed { private final QueryView query; - @Getter private final JDBCType type; + SingleQueryColumn(QueryView query) { + this.query = query; + if(query.getBuilder().getColumns().size() == 1) { + this.type = query.getBuilder().getColumns().get(0).getType(); + } + else{ + throw new IllegalArgumentException("require only one column"); + } + } + @Override public String sql(QueryVariables builder, Object[] args) { requireNoArgs(args, SingleQueryColumn.class::getSimpleName); @@ -29,10 +32,14 @@ public String sql(QueryVariables builder, Object[] args) { } public String sql(QueryVariables builder) { - requireNArgs(1, query.getBuilder().getColumns().toArray(), SingleQueryColumn.class::getSimpleName); return query.sql(builder); } + @Override + public JDBCType getType() { + return type; + } + @Override public String toString() { return sql(addWithValue()); diff --git a/src/main/java/org/usf/jquery/core/WhenCase.java b/src/main/java/org/usf/jquery/core/WhenCase.java index adb785d1..9db9b519 100644 --- a/src/main/java/org/usf/jquery/core/WhenCase.java +++ b/src/main/java/org/usf/jquery/core/WhenCase.java @@ -3,6 +3,7 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static org.usf.jquery.core.JDBCType.typeOf; +import static org.usf.jquery.core.Nested.tryResolve; import static org.usf.jquery.core.Nested.viewsOf; import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -48,11 +49,10 @@ public JDBCType getType() { @Override public boolean resolve(QueryBuilder builder) { var r1 = nonNull(filter) && filter.resolve(builder); - var r2 = Nested.resolve(value, builder); + var r2 = tryResolve(value, builder); return r1 || r2; } - @Override public void views(Collection views) { if(nonNull(filter)) { @@ -65,8 +65,4 @@ public void views(Collection views) { public String toString() { return sql(addWithValue()); } - - public static WhenCase orElse(Object value) { - return new WhenCase(null, value); - } } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 8adc520c..7b845718 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -55,7 +55,7 @@ import org.usf.jquery.core.LogicalOperator; import org.usf.jquery.core.NamedColumn; import org.usf.jquery.core.OperationColumn; -import org.usf.jquery.core.Order; +import org.usf.jquery.core.OrderType; import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; import org.usf.jquery.core.QueryBuilder; @@ -81,7 +81,7 @@ @RequiredArgsConstructor final class RequestEntryChain { - private static final String ORDER_PATTERN = enumPattern(Order.class); + private static final String ORDER_PATTERN = enumPattern(OrderType.class); private static final String LOGIC_PATTERN = enumPattern(LogicalOperator.class); private static final String PARTITION_PATTERN = join("|", PARTITION, ORDER); @@ -223,7 +223,7 @@ public DBOrder evalOrder(ViewDecorator td, QueryContext ctx) { var ord = r.entry.next; if(ord.value.matches(ORDER_PATTERN)) { //check args & next only if order exists var s = ord.requireNoArgs().requireNoNext().value.toUpperCase(); - return r.col.order(Order.valueOf(s)); + return r.col.order(OrderType.valueOf(s)); } throw badEntrySyntaxException(ord.value, ORDER_PATTERN); } From e0bfe55759abf83edf215f3da88022d3cbe5d7e8 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 9 Sep 2024 20:38:15 +0200 Subject: [PATCH 233/298] edit --- .../java/org/usf/jquery/core/ArgTypeRef.java | 1 + .../java/org/usf/jquery/core/CaseColumn.java | 2 +- .../usf/jquery/core/ColumnFilterGroup.java | 6 ++-- .../java/org/usf/jquery/core/DBOrder.java | 2 +- .../java/org/usf/jquery/core/JQueryType.java | 2 +- .../jquery/core/MultiCaseColumnBuilder.java | 28 +++++++++++++++++++ .../java/org/usf/jquery/core/OrderType.java | 1 - .../org/usf/jquery/core/QueryVariables.java | 17 ++--------- .../java/org/usf/jquery/core/QueryView.java | 4 +-- .../jquery/core/SingleCaseColumnBuilder.java | 5 ++-- ...ueryColumn.java => SingleColumnQuery.java} | 6 ++-- .../org/usf/jquery/core/TypedOperator.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 4 +-- 13 files changed, 49 insertions(+), 31 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/MultiCaseColumnBuilder.java rename src/main/java/org/usf/jquery/core/{SingleQueryColumn.java => SingleColumnQuery.java} (83%) diff --git a/src/main/java/org/usf/jquery/core/ArgTypeRef.java b/src/main/java/org/usf/jquery/core/ArgTypeRef.java index e7b2f563..d26d895e 100644 --- a/src/main/java/org/usf/jquery/core/ArgTypeRef.java +++ b/src/main/java/org/usf/jquery/core/ArgTypeRef.java @@ -10,6 +10,7 @@ * @author u$f * */ +@FunctionalInterface interface ArgTypeRef extends Function { static ArgTypeRef firstArgJdbcType() { diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 67f44bc3..95217edd 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -19,7 +19,7 @@ public final class CaseColumn implements DBColumn { private final WhenCase[] whenCases; - public CaseColumn(WhenCase[] whenCases) { + CaseColumn(WhenCase[] whenCases) { this.whenCases = requireAtLeastNArgs(1, whenCases, CaseColumn.class::getSimpleName); } diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index 5af79b52..3a5a311a 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -1,9 +1,11 @@ package org.usf.jquery.core; import static java.util.stream.Collectors.joining; +import static org.usf.jquery.core.Nested.resolveAll; import static org.usf.jquery.core.Nested.viewsOfNested; import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Utils.appendLast; +import static org.usf.jquery.core.Validation.requireAtLeastNArgs; import java.util.Collection; import java.util.stream.Stream; @@ -21,7 +23,7 @@ public final class ColumnFilterGroup implements DBFilter { ColumnFilterGroup(LogicalOperator operator, DBFilter... filters) { this.operator = operator; - this.filters = filters; + this.filters = requireAtLeastNArgs(1, filters, ColumnFilterGroup.class::getSimpleName); } @Override @@ -33,7 +35,7 @@ public String sql(QueryVariables builder) { @Override public boolean resolve(QueryBuilder builder) { - return Nested.resolveAll(filters, builder); + return resolveAll(filters, builder); } @Override diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index 40b3ed76..e7da7085 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -15,7 +15,7 @@ * */ @Getter(AccessLevel.PACKAGE) -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +@RequiredArgsConstructor public final class DBOrder implements DBObject { private final DBColumn column; diff --git a/src/main/java/org/usf/jquery/core/JQueryType.java b/src/main/java/org/usf/jquery/core/JQueryType.java index da4cb7f1..3ecf0445 100644 --- a/src/main/java/org/usf/jquery/core/JQueryType.java +++ b/src/main/java/org/usf/jquery/core/JQueryType.java @@ -13,7 +13,7 @@ public enum JQueryType implements JavaType { VIEW(DBView.class), COLUMN(DBColumn.class), NAMED_COLUMN(NamedColumn.class), - QUERY_COLUMN(SingleQueryColumn.class), + QUERY_COLUMN(SingleColumnQuery.class), FILTER(DBFilter.class), ORDER(DBOrder.class), QUERY(QueryView.class), diff --git a/src/main/java/org/usf/jquery/core/MultiCaseColumnBuilder.java b/src/main/java/org/usf/jquery/core/MultiCaseColumnBuilder.java new file mode 100644 index 00000000..bcab0e23 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/MultiCaseColumnBuilder.java @@ -0,0 +1,28 @@ +package org.usf.jquery.core; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author u$f + * + */ +public final class MultiCaseColumnBuilder { + + private final List cases = new ArrayList<>(); + + public MultiCaseColumnBuilder when(DBFilter filter, Object then) { + cases.add(new WhenCase(filter, then)); + return this; + } + + public CaseColumn orElse(Object o) { + cases.add(new WhenCase(null, o)); + return end(); + } + + public CaseColumn end() { + return new CaseColumn(cases.toArray(WhenCase[]::new)); + } +} diff --git a/src/main/java/org/usf/jquery/core/OrderType.java b/src/main/java/org/usf/jquery/core/OrderType.java index 9ffee41b..cc046771 100644 --- a/src/main/java/org/usf/jquery/core/OrderType.java +++ b/src/main/java/org/usf/jquery/core/OrderType.java @@ -8,5 +8,4 @@ public enum OrderType { ASC, DESC; - } diff --git a/src/main/java/org/usf/jquery/core/QueryVariables.java b/src/main/java/org/usf/jquery/core/QueryVariables.java index 3ea9a0ea..4036dba5 100644 --- a/src/main/java/org/usf/jquery/core/QueryVariables.java +++ b/src/main/java/org/usf/jquery/core/QueryVariables.java @@ -4,7 +4,6 @@ import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; -import static org.usf.jquery.core.DBColumn.allColumns; import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.SqlStringBuilder.COMA; import static org.usf.jquery.core.SqlStringBuilder.EMPTY; @@ -13,12 +12,10 @@ import static org.usf.jquery.core.Utils.isEmpty; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Function; -import java.util.function.Supplier; import java.util.stream.Stream; import lombok.AccessLevel; @@ -53,14 +50,6 @@ public String viewAlias(DBView view) { } return vPrefix + (idx+1); } - - public QueryView overView(DBView view) { - return overView(view, ()-> new QueryBuilder().columns(allColumns(view)).asView()); - } - - public QueryView overView(DBView view, Supplier supp) { - return overView.computeIfAbsent(view, k-> supp.get()); - } public Optional viewOverload(DBView view) { return ofNullable(overView.get(view)); @@ -159,7 +148,7 @@ public QueryVariables withValue() { } public QueryVariables subQuery(Map overView) { - return new QueryVariables(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), new HashMap<>(overView)); + return new QueryVariables(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), overView); } public static QueryVariables addWithValue() { @@ -167,10 +156,10 @@ public static QueryVariables addWithValue() { } public static QueryVariables addWithValue(String schema, Map overView) { - return new QueryVariables(schema, "v", null, null, new ArrayList<>(), new HashMap<>(overView)); //no args + return new QueryVariables(schema, "v", null, null, new ArrayList<>(), overView); //no args } public static QueryVariables parameterized(String schema, Map overView) { - return new QueryVariables(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new HashMap<>(overView)); + return new QueryVariables(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), overView); } } diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index bf17a6ca..7cb65080 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -24,8 +24,8 @@ public String sql(QueryVariables param) { return s.append(")").toString(); } - public SingleQueryColumn asColumn(){ - return new SingleQueryColumn(this); + public SingleColumnQuery asColumn(){ + return new SingleColumnQuery(this); } @Override diff --git a/src/main/java/org/usf/jquery/core/SingleCaseColumnBuilder.java b/src/main/java/org/usf/jquery/core/SingleCaseColumnBuilder.java index d3e990f0..fdd6a8a5 100644 --- a/src/main/java/org/usf/jquery/core/SingleCaseColumnBuilder.java +++ b/src/main/java/org/usf/jquery/core/SingleCaseColumnBuilder.java @@ -3,7 +3,6 @@ import java.util.ArrayList; import java.util.List; -import lombok.AccessLevel; import lombok.RequiredArgsConstructor; /** @@ -11,7 +10,7 @@ * @author u$f * */ -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +@RequiredArgsConstructor public final class SingleCaseColumnBuilder { private final DBColumn column; @@ -23,7 +22,7 @@ public SingleCaseColumnBuilder when(ComparisonExpression exp, Object then) { } public CaseColumn orElse(Object o) { - new WhenCase(null, o); + cases.add(new WhenCase(null, o)); return end(); } diff --git a/src/main/java/org/usf/jquery/core/SingleQueryColumn.java b/src/main/java/org/usf/jquery/core/SingleColumnQuery.java similarity index 83% rename from src/main/java/org/usf/jquery/core/SingleQueryColumn.java rename to src/main/java/org/usf/jquery/core/SingleColumnQuery.java index 3ad1e60d..28a7372c 100644 --- a/src/main/java/org/usf/jquery/core/SingleQueryColumn.java +++ b/src/main/java/org/usf/jquery/core/SingleColumnQuery.java @@ -10,12 +10,12 @@ * @author u$f * */ -public final class SingleQueryColumn implements DBObject, Typed { +public final class SingleColumnQuery implements DBObject, Typed { private final QueryView query; private final JDBCType type; - SingleQueryColumn(QueryView query) { + SingleColumnQuery(QueryView query) { this.query = query; if(query.getBuilder().getColumns().size() == 1) { this.type = query.getBuilder().getColumns().get(0).getType(); @@ -27,7 +27,7 @@ public final class SingleQueryColumn implements DBObject, Typed { @Override public String sql(QueryVariables builder, Object[] args) { - requireNoArgs(args, SingleQueryColumn.class::getSimpleName); + requireNoArgs(args, SingleColumnQuery.class::getSimpleName); return sql(builder); } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index dd7fca95..a27b9019 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -56,7 +56,7 @@ public boolean isWindowFunction() { } public boolean isCountFunction() { - return "COUNT".equals(operator.id()); + return operator.is("COUNT"); } @Override diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 7b845718..e2c7cdd8 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -59,7 +59,7 @@ import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; import org.usf.jquery.core.QueryBuilder; -import org.usf.jquery.core.SingleQueryColumn; +import org.usf.jquery.core.SingleColumnQuery; import org.usf.jquery.core.QueryContext; import org.usf.jquery.core.QueryView; import org.usf.jquery.core.ViewJoin; @@ -103,7 +103,7 @@ public ViewDecorator evalView(ViewDecorator vd, QueryContext ctx) { .orElseThrow(()-> noSuchResourceException(VIEW, value)); } - public SingleQueryColumn evalQueryColumn(ViewDecorator td, QueryContext ctx) { + public SingleColumnQuery evalQueryColumn(ViewDecorator td, QueryContext ctx) { return evalQuery(td, ctx, false) .map(QueryDecorator::getQuery) .map(QueryView::asColumn) From 10928ef7b65a4e226a008562e8da738a28d3e3fa Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 9 Sep 2024 21:22:50 +0200 Subject: [PATCH 234/298] edit --- .../org/usf/jquery/core/QueryContext.java | 13 -- .../org/usf/jquery/web/ArgumentParsers.java | 23 ++- .../org/usf/jquery/web/ChainableCriteria.java | 3 +- .../usf/jquery/web/ContextEnvironment.java | 21 ++- .../org/usf/jquery/web/CriteriaBuilder.java | 11 +- .../java/org/usf/jquery/web/JoinBuilder.java | 3 +- .../org/usf/jquery/web/PartitionBuilder.java | 3 +- .../org/usf/jquery/web/RequestEntryChain.java | 131 +++++++++--------- .../org/usf/jquery/web/RevisionIterator.java | 1 - .../org/usf/jquery/web/ViewDecorator.java | 16 ++- 10 files changed, 115 insertions(+), 110 deletions(-) delete mode 100644 src/main/java/org/usf/jquery/core/QueryContext.java diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java deleted file mode 100644 index 09a65cfa..00000000 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.usf.jquery.core; - -import java.util.Optional; - -/** - * - * @author u$f - * - */ -public interface QueryContext { - - Optional lookupDeclaredColumn(String name); -} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 1c27be74..0b80f01d 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -29,7 +29,6 @@ import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.JQueryType; import org.usf.jquery.core.JavaType; -import org.usf.jquery.core.QueryContext; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -48,7 +47,7 @@ public class ArgumentParsers { BIGINT, DOUBLE, DATE, TIMESTAMP, TIME, TIMESTAMP_WITH_TIMEZONE, VARCHAR }; //varchar !? - public static Object parse(RequestEntryChain entry, ViewDecorator td, QueryContext ctx, JavaType... types) { + public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType... types) { List list = new ArrayList<>(); if(isEmpty(types) || Stream.of(types).anyMatch(JDBCType.class::isInstance)) { list.add(COLUMN); @@ -62,7 +61,7 @@ public static Object parse(RequestEntryChain entry, ViewDecorator td, QueryConte return jdbcArgParser(t).parseEntry(entry, td); } if(type instanceof JQueryType t) { - return jqueryArgParser(t, ctx).parseEntry(entry, td); + return jqueryArgParser(t).parseEntry(entry, td); } else { throw new UnsupportedOperationException(requireNonNull(type, "type is null").toString()); @@ -99,16 +98,16 @@ public static JDBCArgumentParser jdbcArgParser(JDBCType type) { } } - public static JavaArgumentParser jqueryArgParser(JQueryType type, QueryContext ctx) { + public static JavaArgumentParser jqueryArgParser(JQueryType type) { switch (type) { - case QUERY_COLUMN: return (e,v)-> e.evalQueryColumn(v, ctx); - case NAMED_COLUMN: return (e,v)-> e.evalColumn(v, ctx, true); //separate query context - case COLUMN: return (e,v)-> e.evalColumn(v, ctx, false); - case FILTER: return (e,v)-> e.evalFilter(v, ctx); - case ORDER: return (e,v)-> e.evalOrder(v, ctx); - case QUERY: return (e,v)-> e.evalQuery(v, ctx); - case JOIN: return (e,v)-> e.evalJoin(v, ctx); - case PARTITION: return (e,v)-> e.evalPartition(v, ctx); + case QUERY_COLUMN: return RequestEntryChain::evalQueryColumn; + case NAMED_COLUMN: return (e,v)-> e.evalColumn(v, true); //separate query context + case COLUMN: return (e,v)-> e.evalColumn(v, false); + case FILTER: return RequestEntryChain::evalFilter; + case ORDER: return RequestEntryChain::evalOrder; + case QUERY: return RequestEntryChain::evalQuery; + case JOIN: return RequestEntryChain::evalJoin; + case PARTITION: return RequestEntryChain::evalPartition; default: throw unsupportedTypeException(type); } } diff --git a/src/main/java/org/usf/jquery/web/ChainableCriteria.java b/src/main/java/org/usf/jquery/web/ChainableCriteria.java index d0590927..4e9fbef8 100644 --- a/src/main/java/org/usf/jquery/web/ChainableCriteria.java +++ b/src/main/java/org/usf/jquery/web/ChainableCriteria.java @@ -1,7 +1,6 @@ package org.usf.jquery.web; import org.usf.jquery.core.Chainable; -import org.usf.jquery.core.QueryContext; /** * @@ -11,5 +10,5 @@ @FunctionalInterface public interface ChainableCriteria> { - T criteria(QueryContext ctx, String arg); + T criteria(String arg); } diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index 2a4ac42e..a9892cbe 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -28,6 +28,7 @@ import javax.sql.DataSource; import org.usf.jquery.core.JQueryException; +import org.usf.jquery.core.NamedColumn; import lombok.AccessLevel; import lombok.Getter; @@ -54,6 +55,8 @@ public final class ContextEnvironment { private final String schema; //optional private final DatabaseMetadata metadata; + private Map declaredColumns = new HashMap<>(); + ContextEnvironment(ContextEnvironment ctx) { this.database = ctx.database; this.views = new HashMap<>(ctx.views); //modifiable @@ -63,7 +66,7 @@ public final class ContextEnvironment { this.metadata = ctx.metadata; } - public Optional lookupRegisteredView(String name) { + public Optional lookupRegisteredView(String name) { //+ declared return ofNullable(views.get(name)); } @@ -71,6 +74,10 @@ public Optional lookupRegisteredColumn(String name) { return ofNullable(columns.get(name)); } + Optional lookupDeclaredColumn(String name) { + return ofNullable(declaredColumns.get(name)); + } + void declareView(ViewDecorator view) { //additional request views views.compute(view.identity(), (k,v)-> { if(isNull(v)){ @@ -79,6 +86,18 @@ void declareView(ViewDecorator view) { //additional request views throw resourceAlreadyExistsException(k); }); } + + NamedColumn declareColumn(NamedColumn col) { + views.computeIfPresent(col.getTag(), (k,v)-> { //cannot overwrite registered views + throw resourceAlreadyExistsException(k); + }); //but can overwrite registered columns + return declaredColumns.compute(col.getTag(), (k,v)-> { + if(isNull(v)){ + return col; + } + throw resourceAlreadyExistsException(k); + }); + } ViewMetadata computeTableMetadata(ViewDecorator vd, Function, ViewMetadata> fn) { return metadata.getTables().computeIfAbsent(vd.identity(), key-> fn.apply(columns.values())); diff --git a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java index e783a6f7..eafa03af 100644 --- a/src/main/java/org/usf/jquery/web/CriteriaBuilder.java +++ b/src/main/java/org/usf/jquery/web/CriteriaBuilder.java @@ -8,7 +8,6 @@ import org.usf.jquery.core.Chainable; import org.usf.jquery.core.LogicalOperator; -import org.usf.jquery.core.QueryContext; /** * @@ -18,10 +17,10 @@ @FunctionalInterface public interface CriteriaBuilder> { - T build(QueryContext ctx, String... arg); + T build(String... arg); static > CriteriaBuilder singleArg(ChainableCriteria cr){ - return (ctx, args)-> cr.criteria(ctx, isEmpty(args) ? null : requireNArgs(1, args, ()-> "single arg criteria")[0]); + return args-> cr.criteria(isEmpty(args) ? null : requireNArgs(1, args, ()-> "single arg criteria")[0]); } static > CriteriaBuilder multiArgs(ChainableCriteria cr){ @@ -29,8 +28,8 @@ static > CriteriaBuilder multiArgs(ChainableCriteria> CriteriaBuilder multiArgs(LogicalOperator op, ChainableCriteria cr){ - return (ctx, args)-> isEmpty(args) - ? cr.criteria(ctx, null) - : Stream.of(args).map(c-> cr.criteria(ctx, c)).reduce(op::combine).orElseThrow(); + return args-> isEmpty(args) + ? cr.criteria(null) + : Stream.of(args).map(cr::criteria).reduce(op::combine).orElseThrow(); } } diff --git a/src/main/java/org/usf/jquery/web/JoinBuilder.java b/src/main/java/org/usf/jquery/web/JoinBuilder.java index 5e5e046a..b0131659 100644 --- a/src/main/java/org/usf/jquery/web/JoinBuilder.java +++ b/src/main/java/org/usf/jquery/web/JoinBuilder.java @@ -1,6 +1,5 @@ package org.usf.jquery.web; -import org.usf.jquery.core.QueryContext; import org.usf.jquery.core.ViewJoin; /** @@ -11,6 +10,6 @@ @FunctionalInterface public interface JoinBuilder { - ViewJoin[] build(QueryContext qc); + ViewJoin[] build(); } diff --git a/src/main/java/org/usf/jquery/web/PartitionBuilder.java b/src/main/java/org/usf/jquery/web/PartitionBuilder.java index 0d819d7b..aa774ce0 100644 --- a/src/main/java/org/usf/jquery/web/PartitionBuilder.java +++ b/src/main/java/org/usf/jquery/web/PartitionBuilder.java @@ -1,7 +1,6 @@ package org.usf.jquery.web; import org.usf.jquery.core.Partition; -import org.usf.jquery.core.QueryContext; /** * @@ -11,6 +10,6 @@ @FunctionalInterface public interface PartitionBuilder { - Partition build(QueryContext ctx); + Partition build(); } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index e2c7cdd8..cbddb31e 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -59,9 +59,8 @@ import org.usf.jquery.core.ParameterSet; import org.usf.jquery.core.Partition; import org.usf.jquery.core.QueryBuilder; -import org.usf.jquery.core.SingleColumnQuery; -import org.usf.jquery.core.QueryContext; import org.usf.jquery.core.QueryView; +import org.usf.jquery.core.SingleColumnQuery; import org.usf.jquery.core.ViewJoin; import lombok.AccessLevel; @@ -96,39 +95,39 @@ public RequestEntryChain(String value) { } // [view|query]:tag - public ViewDecorator evalView(ViewDecorator vd, QueryContext ctx) { + public ViewDecorator evalView(ViewDecorator vd) { return currentContext().lookupRegisteredView(value) //check args & next only if view exists .map(v-> new ViewDecoratorWrapper(v, requireNoArgs().requireNoNext().requireTag())) - .or(()-> evalQuery(vd, ctx, true)) + .or(()-> evalQuery(vd, true)) .orElseThrow(()-> noSuchResourceException(VIEW, value)); } - public SingleColumnQuery evalQueryColumn(ViewDecorator td, QueryContext ctx) { - return evalQuery(td, ctx, false) + public SingleColumnQuery evalQueryColumn(ViewDecorator td) { + return evalQuery(td, false) .map(QueryDecorator::getQuery) .map(QueryView::asColumn) .orElseThrow(()-> cannotParseEntryException(QUERY, this)); } - public ViewDecorator evalQuery(ViewDecorator td, QueryContext ctx) { - return evalQuery(td, ctx, false).orElseThrow(()-> cannotParseEntryException(QUERY, this)); + public ViewDecorator evalQuery(ViewDecorator td) { + return evalQuery(td, false).orElseThrow(()-> cannotParseEntryException(QUERY, this)); } //select[.distinct|filter|order|offset|fetch]* - Optional evalQuery(ViewDecorator td, QueryContext ctx, boolean requireTag) { //sub context + Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub context if(SELECT.equals(value)) { var e = this; try { - var q = new QueryBuilder().columns(taggableVarargs(td, ctx)); + var q = new QueryBuilder().columns(taggableVarargs(td)); while(e.hasNext()) { e = e.next; switch(e.value) {//column not allowed case DISTINCT: e.requireNoArgs(); q.distinct(); break; - case FILTER: q.filters(e.filterVarargs(td, ctx)); break; - case ORDER: q.orders(e.orderVarargs(td, ctx)); break; - case JOIN: q.joins(e.evalJoin(td, ctx)); break; - case OFFSET: q.offset((int)e.toOneArg(td, ctx, INTEGER)); break; - case FETCH: q.fetch((int)e.toOneArg(td, ctx, INTEGER)); break; + case FILTER: q.filters(e.filterVarargs(td)); break; + case ORDER: q.orders(e.orderVarargs(td)); break; + case JOIN: q.joins(e.evalJoin(td)); break; + case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; + case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; default: throw badEntrySyntaxException(e.value, join("|", DISTINCT, FILTER, ORDER, JOIN, OFFSET, FETCH)); } } @@ -142,7 +141,7 @@ Optional evalQuery(ViewDecorator td, QueryContext ctx, boolean r } //[view.]joiner - public ViewJoin[] evalJoin(ViewDecorator vd, QueryContext ctx) { + public ViewJoin[] evalJoin(ViewDecorator vd) { var e = this; if(hasNext()) { var res = currentContext().lookupRegisteredView(value); @@ -154,13 +153,13 @@ public ViewJoin[] evalJoin(ViewDecorator vd, QueryContext ctx) { var join = vd.join(e.value); if(nonNull(join)) { e.requireNoArgs().requireNoNext(); //check args & next only if joiner exists - return requireNonNull(join.build(ctx), vd.identity() + "." + e.value); + return requireNonNull(join.build(), vd.identity() + "." + e.value); } throw noSuchResourceException(vd.identity() + ".join", e.value); } //[view.]partition | [partition.order]* - public Partition evalPartition(ViewDecorator vd, QueryContext ctx) { + public Partition evalPartition(ViewDecorator vd) { var e = this; if(hasNext()) { var res = currentContext().lookupRegisteredView(value); @@ -172,24 +171,24 @@ public Partition evalPartition(ViewDecorator vd, QueryContext ctx) { var par = vd.partition(e.value); if(nonNull(par)) { e.requireNoArgs().requireNoNext(); - return requireNonNull(par.build(ctx), vd.identity() + "." + e.value); + return requireNonNull(par.build(), vd.identity() + "." + e.value); } - var res = evalPartition2(vd, ctx); + var res = evalPartition2(vd); if(res.isPresent()) { return res.get(); } throw noSuchResourceException(vd.identity() + ".partition", e.value); } - private Optional evalPartition2(ViewDecorator vd, QueryContext ctx) { + private Optional evalPartition2(ViewDecorator vd) { if(value.matches(PARTITION_PATTERN)) { List cols = new ArrayList<>(); List ords = new ArrayList<>(); var e = this; do { switch (e.value) { - case PARTITION: addAll(cols, columnVarargs(vd, ctx)); break; - case ORDER: addAll(ords, e.orderVarargs(vd, ctx)); break; + case PARTITION: addAll(cols, columnVarargs(vd)); break; + case ORDER: addAll(ords, e.orderVarargs(vd)); break; default: throw badEntrySyntaxException(e.value, PARTITION_PATTERN); } e = e.next; @@ -202,8 +201,8 @@ private Optional evalPartition2(ViewDecorator vd, QueryContext ctx) { } //[view.]column[.operator]* - public DBColumn evalColumn(ViewDecorator td, QueryContext ctx, boolean requireTag) { - var r = chainColumnOperations(td, ctx, false); + public DBColumn evalColumn(ViewDecorator td, boolean requireTag) { + var r = chainColumnOperations(td, false); r.entry.requireNoNext(); //check next only if column exists if(nonNull(r.entry.tag)) { return r.col.as(r.entry.tag); @@ -215,8 +214,8 @@ public DBColumn evalColumn(ViewDecorator td, QueryContext ctx, boolean requireTa } //[view.]column[.operator]*[.order] - public DBOrder evalOrder(ViewDecorator td, QueryContext ctx) { - var r = chainColumnOperations(td, ctx, false); + public DBOrder evalOrder(ViewDecorator td) { + var r = chainColumnOperations(td, false); if(r.entry.isLast()) { // default order return r.col.order(); } @@ -228,22 +227,22 @@ public DBOrder evalOrder(ViewDecorator td, QueryContext ctx) { throw badEntrySyntaxException(ord.value, ORDER_PATTERN); } - public DBFilter evalFilter(ViewDecorator td, QueryContext ctx) { - return evalFilter(td, ctx, null); //null ==> inner filter + public DBFilter evalFilter(ViewDecorator td) { + return evalFilter(td, null); //null ==> inner filter } //[view.]criteria | [view.]column.criteria | [view.]column[.operator]*[.comparator][.and|or(comparator)]* - public DBFilter evalFilter(ViewDecorator vd, QueryContext ctx, List values) { //use CD.parser - var rc = chainColumnOperations(vd, ctx, true); + public DBFilter evalFilter(ViewDecorator vd, List values) { //use CD.parser + var rc = chainColumnOperations(vd, true); if(rc.isCriteria()) { var strArgs = toStringArray(rc.entry.assertOuterParameters(values)); if(nonNull(rc.viewCrt)) { //view criteria - var f = requireNonNull(rc.viewCrt.build(ctx, strArgs), rc.vd.identity() + "." + rc.entry.value); - return rc.entry.chainComparator(vd, ctx, f); + var f = requireNonNull(rc.viewCrt.build(strArgs), rc.vd.identity() + "." + rc.entry.value); + return rc.entry.chainComparator(vd, f); } if(nonNull(rc.colCrt) && nonNull(rc.col)) { //column criteria - var ex = requireNonNull(rc.colCrt.build(ctx, strArgs), rc.cd.identity() + "." + rc.entry.value); - return rc.entry.chainComparator(vd, ctx, rc.col.filter(ex)); + var ex = requireNonNull(rc.colCrt.build(strArgs), rc.cd.identity() + "." + rc.entry.value); + return rc.entry.chainComparator(vd, rc.col.filter(ex)); } } else if(nonNull(rc.col)) { @@ -251,7 +250,7 @@ else if(nonNull(rc.col)) { if(!isEmpty(values)) { var fn = values.size() == 1 ? eq() : in(); //non empty var e = new RequestEntryChain(fn.id(), false, null, values, null); - return fn.filter(e.toArgs(vd, ctx, rc.col, fn.getParameterSet())); //no chain + return fn.filter(e.toArgs(vd, rc.col, fn.getParameterSet())); //no chain } throw badEntrySyntaxException(this.toString(), FILTER); } @@ -260,7 +259,7 @@ else if(nonNull(rc.col)) { if(res.isPresent()) { //column comparator var cmp = res.get(); var cp = new RequestEntryChain(e.value, false, null, e.assertOuterParameters(values), null); - return e.chainComparator(vd, ctx, cmp.filter(cp.toArgs(vd, ctx, rc.col, cmp.getParameterSet()))); + return e.chainComparator(vd, cmp.filter(cp.toArgs(vd, rc.col, cmp.getParameterSet()))); } throw noSuchResourceException(isNull(rc.cd) ? "comparator" : "comparator|criteria", rc.entry.next.value); } @@ -277,12 +276,12 @@ private List assertOuterParameters(List va throw new IllegalStateException(this + "=" + values); //denied } - DBFilter chainComparator(ViewDecorator td, QueryContext ctx, DBFilter f) { + DBFilter chainComparator(ViewDecorator td, DBFilter f) { var e = next; while(nonNull(e)) { if(e.value.matches(LOGIC_PATTERN)) { var op = LogicalOperator.valueOf(e.value.toUpperCase()); - f = f.append(op, (DBFilter) e.toOneArg(td, ctx, JQueryType.FILTER)); + f = f.append(op, (DBFilter) e.toOneArg(td, JQueryType.FILTER)); e = e.next; } else { @@ -292,15 +291,15 @@ DBFilter chainComparator(ViewDecorator td, QueryContext ctx, DBFilter f) { return f; } - private ViewResource chainColumnOperations(ViewDecorator vd, QueryContext ctx, boolean filter) { - var r = lookupResource(vd, ctx, filter); + private ViewResource chainColumnOperations(ViewDecorator vd, boolean filter) { + var r = lookupResource(vd, filter); if(!r.isCriteria()) { var e = r.entry.next; while(nonNull(e)) { //chain until !operator var res = lookupOperator(e.value); if(res.isPresent()) { var fn = res.get(); - var o = fn.operation(e.toArgs(vd, ctx, r.col, fn.getParameterSet())); + var o = fn.operation(e.toArgs(vd, r.col, fn.getParameterSet())); r.cd = null; r.entry = e; r.col = o; @@ -315,21 +314,21 @@ private ViewResource chainColumnOperations(ViewDecorator vd, QueryContext ctx, b } //view.resource | resource - private ViewResource lookupResource(ViewDecorator vd, QueryContext ctx, boolean filter) { //do not change priority + private ViewResource lookupResource(ViewDecorator vd, boolean filter) { //do not change priority if(hasNext()) { var rc = currentContext().lookupRegisteredView(value); if(rc.isPresent()) { var res = next.lookupQueryResource(rc.get()) //declared query first - .or(()-> next.lookupViewResource(vd, ctx, rc.get(), filter)); + .or(()-> next.lookupViewResource(vd, rc.get(), filter)); if(res.isPresent()) { requireNoArgs(); return res.get(); } } } //view.id == column.id - return ctx.lookupDeclaredColumn(value) + return currentContext().lookupDeclaredColumn(value) .map(c-> new ViewResource(requireNoArgs(), null, null, c)) //declared column first - .or(()-> lookupViewResource(vd, ctx, null, filter)) //registered column + .or(()-> lookupViewResource(vd, null, filter)) //registered column .orElseThrow(()-> noSuchViewResourceException(this)); //no such resource } @@ -343,21 +342,21 @@ private Optional lookupQueryResource(ViewDecorator vd) { } //view[.operator|column[.criteria]|criteria] - private Optional lookupViewResource(ViewDecorator vd, QueryContext ctx, ViewDecorator current, boolean filter) { + private Optional lookupViewResource(ViewDecorator vd, ViewDecorator current, boolean filter) { var cur = requireNonNullElse(current, vd); - var res = lookupViewOperation(vd, ctx, current) + var res = lookupViewOperation(vd, current) .map(col-> new ViewResource(this, cur, null, col)) .or(()-> lookupColumnResource(cur, filter)); return filter ? res.or(()-> ofNullable(cur.criteria(value)).map(crt-> new ViewResource(this, cur, crt))) : res; } - private Optional lookupViewOperation(ViewDecorator vd, QueryContext ctx, ViewDecorator current) { + private Optional lookupViewOperation(ViewDecorator vd, ViewDecorator current) { //view.[count|rank|rowNumber|danseRank] only return lookupOperator(value, isNull(current) ? null : op-> op.isCountFunction() || op.isWindowFunction()).map(fn-> { var col = isEmpty(args) && fn.isCountFunction() ? allColumns(requireNonNullElse(current, vd).view()) : null; - return fn.operation(toArgs(vd, ctx, col, fn.getParameterSet())); + return fn.operation(toArgs(vd, col, fn.getParameterSet())); }); } @@ -380,40 +379,40 @@ private Optional lookupColumnResource(ViewDecorator td, boolean fi return empty(); } - private NamedColumn[] taggableVarargs(ViewDecorator td, QueryContext ctx) { - return (NamedColumn[]) typeVarargs(td, ctx, JQueryType.NAMED_COLUMN); + private NamedColumn[] taggableVarargs(ViewDecorator td) { + return (NamedColumn[]) typeVarargs(td, JQueryType.NAMED_COLUMN); } - private DBColumn[] columnVarargs(ViewDecorator td, QueryContext ctx) { - return (DBColumn[]) typeVarargs(td, ctx, JQueryType.COLUMN); + private DBColumn[] columnVarargs(ViewDecorator td) { + return (DBColumn[]) typeVarargs(td, JQueryType.COLUMN); } - private DBOrder[] orderVarargs(ViewDecorator td, QueryContext ctx) { - return (DBOrder[]) typeVarargs(td, ctx, JQueryType.ORDER); + private DBOrder[] orderVarargs(ViewDecorator td) { + return (DBOrder[]) typeVarargs(td, JQueryType.ORDER); } - private DBFilter[] filterVarargs(ViewDecorator td, QueryContext ctx) { - return (DBFilter[]) typeVarargs(td, ctx, JQueryType.FILTER); + private DBFilter[] filterVarargs(ViewDecorator td) { + return (DBFilter[]) typeVarargs(td, JQueryType.FILTER); } - private Object[] typeVarargs(ViewDecorator td, QueryContext ctx, JavaType type) { + private Object[] typeVarargs(ViewDecorator td, JavaType type) { var c = type.getCorrespondingClass(); if(DBObject.class.isAssignableFrom(c)) { // JQuery types & !array var ps = ofParameters(required(type), varargs(type)); - return toArgs(td, ctx, null, ps, s-> (Object[]) newInstance(c, s)); + return toArgs(td, null, ps, s-> (Object[]) newInstance(c, s)); } throw new UnsupportedOperationException("cannot instantiate type " + c); } - private Object toOneArg(ViewDecorator td, QueryContext ctx, JavaType type) { - return toArgs(td, ctx, null, ofParameters(required(type)))[0]; + private Object toOneArg(ViewDecorator td, JavaType type) { + return toArgs(td, null, ofParameters(required(type)))[0]; } - private Object[] toArgs(ViewDecorator td, QueryContext ctx, DBObject col, ParameterSet ps) { - return toArgs(td, ctx, col, ps, Object[]::new); + private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps) { + return toArgs(td, col, ps, Object[]::new); } - private Object[] toArgs(ViewDecorator td, QueryContext ctx, DBObject col, ParameterSet ps, IntFunction arrFn) { + private Object[] toArgs(ViewDecorator td, DBObject col, ParameterSet ps, IntFunction arrFn) { int inc = isNull(col) ? 0 : 1; var arr = arrFn.apply(isNull(args) ? inc : args.size() + inc); if(nonNull(col)) { @@ -425,7 +424,7 @@ private Object[] toArgs(ViewDecorator td, QueryContext ctx, DBObject col, Parame var e = args.get(i-inc); arr[i] = isNull(e.value) || e.text ? e.requireNoArgs().value - : parse(e, td, ctx, p.types(arr)); + : parse(e, td, p.types(arr)); } }); } diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index 2783c861..433bb354 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -13,7 +13,6 @@ import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; -import org.usf.jquery.core.JDBCType; import org.usf.jquery.core.QueryVariables; import org.usf.jquery.core.TableView; diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 2384fb17..eb39d923 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -29,6 +29,7 @@ import java.util.Optional; import java.util.stream.Stream; +import org.usf.jquery.core.ColumnProxy; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.DBView; import org.usf.jquery.core.NamedColumn; @@ -121,7 +122,7 @@ default void parseViews(QueryBuilder query, Map parameters) { if(parameters.containsKey(VIEW)) { Stream.of(parameters.remove(VIEW)) .flatMap(c-> parseEntries(c).stream()) - .forEach(e-> currentContext().declareView(e.evalView(this, query))); + .forEach(e-> currentContext().declareView(e.evalView(this))); } } @@ -140,7 +141,12 @@ default void parseColumns(QueryBuilder query, Map parameters) if(!isEmpty(cols)) { Stream.of(cols) .flatMap(v-> parseEntries(v).stream()) - .map(e-> (NamedColumn) e.evalColumn(this, query, true)) + .map(e-> { + var c = e.evalColumn(this, true); + return c instanceof ColumnProxy cp + ? currentContext().declareColumn(cp) + : (NamedColumn) c; + }) .forEach(query::columns); } else { @@ -152,14 +158,14 @@ default void parseOrders(QueryBuilder query, Map parameters) { if(parameters.containsKey(ORDER)) { Stream.of(parameters.remove(ORDER)) .flatMap(c-> parseEntries(c).stream()) - .forEach(e-> query.orders(e.evalOrder(this, query))); + .forEach(e-> query.orders(e.evalOrder(this))); } } default void parseJoin(QueryBuilder query, Map parameters) { if(parameters.containsKey(JOIN)) { Stream.of(parameters.remove(JOIN)) .flatMap(c-> parseEntries(c).stream()) - .forEach(e-> query.joins(e.evalJoin(this, query))); + .forEach(e-> query.joins(e.evalJoin(this))); } } @@ -175,7 +181,7 @@ default void parseFilters(QueryBuilder query, Map parameters) parameters.entrySet().stream() .flatMap(e-> { var re = parseEntry(e.getKey()); - return Stream.of(e.getValue()).map(v-> re.evalFilter(this, query, parseEntries(v))); + return Stream.of(e.getValue()).map(v-> re.evalFilter(this, parseEntries(v))); }) .forEach(query::filters); } From fca587466cad86bc805051d7fb3460f6d6d8643e Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 9 Sep 2024 21:42:12 +0200 Subject: [PATCH 235/298] edit --- .../org/usf/jquery/core/TypedComparator.java | 15 ++++++------- .../org/usf/jquery/core/TypedOperator.java | 21 ++++++++----------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 40ec4194..d90a461d 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -4,6 +4,7 @@ import static org.usf.jquery.core.ParameterSet.ofParameters; import lombok.Getter; +import lombok.experimental.Delegate; /** * @@ -13,7 +14,8 @@ @Getter public final class TypedComparator implements Comparator { - private final Comparator comparator; // do not delegate + @Delegate + private final Comparator comparator; private final ParameterSet parameterSet; public TypedComparator(Comparator comparator, Parameter... parameters) { @@ -21,11 +23,6 @@ public TypedComparator(Comparator comparator, Parameter... parameters) { this.parameterSet = ofParameters(parameters); } - @Override - public String id() { - return comparator.id(); - } - @Override public String sql(QueryVariables builder, Object[] args) { try { @@ -35,9 +32,9 @@ public String sql(QueryVariables builder, Object[] args) { } } - @Override - public boolean is(Class type) { - return comparator.is(type); + @Override // do not delegate this + public ColumnSingleFilter filter(Object... args) { + return Comparator.super.filter(args); } @Override diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index a27b9019..a8190706 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -4,6 +4,7 @@ import static org.usf.jquery.core.ParameterSet.ofParameters; import lombok.Getter; +import lombok.experimental.Delegate; /** * @@ -13,7 +14,8 @@ @Getter public class TypedOperator implements Operator { - private final Operator operator; // do not delegate + @Delegate + private final Operator operator; private final ArgTypeRef typeFn; private final ParameterSet parameterSet; @@ -27,11 +29,6 @@ public TypedOperator(ArgTypeRef typeFn, Operator function, Parameter... paramete this.parameterSet = ofParameters(parameter); } - @Override - public String id() { - return operator.id(); - } - @Override public String sql(QueryVariables builder, Object[] args) { try { @@ -41,16 +38,16 @@ public String sql(QueryVariables builder, Object[] args) { throw badArgumentsException("operator", operator.id(), args, e); } } - - @Override - public boolean is(Class type) { - return operator.is(type); - } public OperationColumn operation(Object... args) { - return Operator.super.operation(typeFn.apply(args), args); + return this.operation(typeFn.apply(args), args); } + @Override // do not delegate this + public OperationColumn operation(JDBCType type, Object... args) { + return Operator.super.operation(type, args); + } + public boolean isWindowFunction() { return operator.is(WindowFunction.class); } From aa84fa4234d90908cd8f3f3f4a712eb5d7543915 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 9 Sep 2024 23:57:24 +0200 Subject: [PATCH 236/298] edit --- .../usf/jquery/core/ColumnSingleFilter.java | 2 +- .../core/ComparisonExpressionGroup.java | 4 +- src/main/java/org/usf/jquery/core/Nested.java | 3 ++ .../java/org/usf/jquery/core/QueryView.java | 2 +- .../org/usf/jquery/web/RevisionIterator.java | 16 +++++--- .../usf/jquery/core/FunctionOperatorTest.java | 38 ------------------- 6 files changed, 17 insertions(+), 48 deletions(-) delete mode 100644 src/test/java/org/usf/jquery/core/FunctionOperatorTest.java diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index c0642106..59e23adf 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -14,7 +14,7 @@ * @author u$f * */ -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +@RequiredArgsConstructor public class ColumnSingleFilter implements DBFilter { private final Object left; diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index e7438f66..bbd29c1e 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -5,7 +5,7 @@ import static org.usf.jquery.core.Nested.viewsOfNested; import static org.usf.jquery.core.QueryVariables.addWithValue; import static org.usf.jquery.core.Utils.appendLast; -import static org.usf.jquery.core.Validation.requireNArgs; +import static org.usf.jquery.core.Validation.requireAtLeastNArgs; import java.util.Collection; import java.util.stream.Stream; @@ -23,7 +23,7 @@ public final class ComparisonExpressionGroup implements ComparisonExpression { ComparisonExpressionGroup(LogicalOperator operator, ComparisonExpression... expressions) { this.operator = operator; - this.expressions = requireNArgs(1, expressions, ComparisonExpressionGroup.class::getSimpleName); + this.expressions = requireAtLeastNArgs(1, expressions, ComparisonExpressionGroup.class::getSimpleName); } @Override diff --git a/src/main/java/org/usf/jquery/core/Nested.java b/src/main/java/org/usf/jquery/core/Nested.java index 8157c704..bfaa0dc4 100644 --- a/src/main/java/org/usf/jquery/core/Nested.java +++ b/src/main/java/org/usf/jquery/core/Nested.java @@ -4,6 +4,9 @@ import static org.usf.jquery.core.Utils.isEmpty; import java.util.Collection; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index 7cb65080..36c46e28 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -11,10 +11,10 @@ * @author u$f * */ +@Getter @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class QueryView implements DBView { - @Getter private final QueryBuilder builder; @Override diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index 433bb354..01b547c4 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -11,6 +11,7 @@ import java.util.Map.Entry; import java.util.stream.Stream; +import org.usf.jquery.core.ColumnSingleFilter; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; import org.usf.jquery.core.QueryVariables; @@ -68,12 +69,15 @@ static DBColumn yearColumn() { } static DBFilter monthFilter(DBColumn column) { - return b-> { - var values = currentRev.get().getValue(); //get it on build - var filter = values.size() == 1 - ? column.eq(values.get(0).getMonthValue()) - : column.in(values.stream().map(YearMonth::getMonthValue).toArray(Integer[]::new)); - return filter.sql(b); + return new ColumnSingleFilter(null, null) { + @Override + public String sql(QueryVariables vars) { + var values = currentRev.get().getValue(); //get it on build + var filter = values.size() == 1 + ? column.eq(values.get(0).getMonthValue()) + : column.in(values.stream().map(YearMonth::getMonthValue).toArray(Integer[]::new)); + return filter.sql(vars); + } }; } } diff --git a/src/test/java/org/usf/jquery/core/FunctionOperatorTest.java b/src/test/java/org/usf/jquery/core/FunctionOperatorTest.java deleted file mode 100644 index cc213455..00000000 --- a/src/test/java/org/usf/jquery/core/FunctionOperatorTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.usf.jquery.core; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.usf.jquery.core.QueryVariables.addWithValue; - -import org.junit.jupiter.api.Test; - -class FunctionOperatorTest { - - @Test - void testSql() { - FunctionOperator fn = ()-> "dummy"; - assertEquals("dummy()", fn.sql(addWithValue(), new Object[] {})); - } - - @Test - void testSql2() { - FunctionOperator fn = ()-> "dummy"; - assertEquals("dummy('toto', 123)", fn.sql(addWithValue(), new Object[] {"toto", 123})); - } - - @Test - void testSql3() { - FunctionOperator fn = ()-> "dummy"; - DBColumn col = b-> "col1"; - assertEquals("dummy(col1, 123)", fn.sql(addWithValue(), new Object[] {col, 123})); - } - - @Test - void testSql4() { - FunctionOperator fn = ()-> "dummy"; - DBColumn col1 = b-> "col1"; - DBColumn col2 = b-> "col2"; - DBColumn col3 = b-> "col3"; - assertEquals("dummy(col1, col2, col3)", fn.sql(addWithValue(), new Object[] {col1, col2, col3})); - } - -} From 0b3c06f025f6fcfa5dc79a634530ec95a10810a9 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 10 Sep 2024 00:23:19 +0200 Subject: [PATCH 237/298] edit --- .../org/usf/jquery/core/ColumnSingleFilter.java | 1 - src/main/java/org/usf/jquery/core/Nested.java | 17 +++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index 59e23adf..238dbd65 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -6,7 +6,6 @@ import java.util.Collection; -import lombok.AccessLevel; import lombok.RequiredArgsConstructor; /** diff --git a/src/main/java/org/usf/jquery/core/Nested.java b/src/main/java/org/usf/jquery/core/Nested.java index bfaa0dc4..9f5ec58e 100644 --- a/src/main/java/org/usf/jquery/core/Nested.java +++ b/src/main/java/org/usf/jquery/core/Nested.java @@ -1,11 +1,8 @@ package org.usf.jquery.core; -import static java.util.function.Function.identity; import static org.usf.jquery.core.Utils.isEmpty; import java.util.Collection; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -49,21 +46,21 @@ static boolean tryResolve(Object o, QueryBuilder builder) { static void viewsOfNested(Collection views, T[] arr) { - viewsOfNested(views, arr, identity()); + viewsOfAll(arr, o-> o.views(views)); } static void viewsOfNested(Collection views, T[] arr, Function fn) { - if(!isEmpty(arr)) { - for(var o : arr) { - fn.apply(o).views(views); - } - } + viewsOfAll(arr, o-> fn.apply(o).views(views)); } static void viewsOfAll(Collection views, Object[] arr) { + viewsOfAll(arr, o-> viewsOf(views, o)); + } + + static void viewsOfAll(T[] arr, Consumer cons) { if(!isEmpty(arr)) { for(var o : arr) { - viewsOf(views, o); + cons.accept(o); } } } From 3b8f71ae2da60496097c630c79c64d93e5fdd10c Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 10 Sep 2024 00:31:31 +0200 Subject: [PATCH 238/298] edit --- src/main/java/org/usf/jquery/core/QueryBuilder.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index b4c8c5e5..9105e15d 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -62,13 +62,6 @@ public QueryBuilder() { public QueryBuilder(Database target) { setCurrentDatabase(target); } - - public Optional lookupDeclaredColumn(String name) { - return columns.stream() - .filter(ColumnProxy.class::isInstance) - .filter(c-> name.equals(c.getTag())) - .findAny(); - } public QueryView overView(DBView view) { return overView(view, ()-> new QueryBuilder().columns(allColumns(view)).asView()); From 3da44335dcfc859d3674b4952bd7dd0b614b5f6f Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 10 Sep 2024 13:56:38 +0200 Subject: [PATCH 239/298] edit --- .../usf/jquery/core/ArithmeticOperator.java | 4 +- .../org/usf/jquery/core/BasicComparator.java | 4 +- .../java/org/usf/jquery/core/CaseColumn.java | 6 +- .../org/usf/jquery/core/CastFunction.java | 2 +- .../usf/jquery/core/ColumnFilterGroup.java | 10 ++-- .../java/org/usf/jquery/core/ColumnProxy.java | 2 +- .../usf/jquery/core/ColumnSingleFilter.java | 12 ++-- .../org/usf/jquery/core/CombinedOperator.java | 2 +- .../usf/jquery/core/ComparisonExpression.java | 6 +- .../core/ComparisonExpressionGroup.java | 6 +- .../core/ComparisonSingleExpression.java | 6 +- .../org/usf/jquery/core/ConstantOperator.java | 2 +- .../java/org/usf/jquery/core/DBColumn.java | 12 ++-- .../java/org/usf/jquery/core/DBFilter.java | 6 +- .../java/org/usf/jquery/core/DBObject.java | 3 +- .../java/org/usf/jquery/core/DBOrder.java | 12 ++-- src/main/java/org/usf/jquery/core/DBView.java | 14 ++--- .../org/usf/jquery/core/ExtractFunction.java | 2 +- .../org/usf/jquery/core/FunctionOperator.java | 4 +- .../org/usf/jquery/core/InComparator.java | 4 +- .../java/org/usf/jquery/core/NamedColumn.java | 4 +- .../org/usf/jquery/core/NullComparator.java | 4 +- .../org/usf/jquery/core/OperationColumn.java | 6 +- .../java/org/usf/jquery/core/Partition.java | 10 ++-- .../org/usf/jquery/core/PipeFunction.java | 2 +- .../org/usf/jquery/core/QueryBuilder.java | 55 +++++++++---------- ...{QueryVariables.java => QueryContext.java} | 20 +++---- .../java/org/usf/jquery/core/QueryView.java | 6 +- .../org/usf/jquery/core/RangeComparator.java | 8 +-- .../usf/jquery/core/SingleColumnQuery.java | 10 ++-- .../org/usf/jquery/core/StringComparator.java | 4 +- .../java/org/usf/jquery/core/TableView.java | 6 +- .../org/usf/jquery/core/TypedComparator.java | 4 +- .../org/usf/jquery/core/TypedOperator.java | 4 +- .../java/org/usf/jquery/core/ViewColumn.java | 6 +- .../java/org/usf/jquery/core/ViewJoin.java | 32 +++++++++-- .../java/org/usf/jquery/core/WhenCase.java | 10 ++-- .../org/usf/jquery/web/QueryDecorator.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 3 +- .../org/usf/jquery/web/RevisionIterator.java | 6 +- 40 files changed, 169 insertions(+), 152 deletions(-) rename src/main/java/org/usf/jquery/core/{QueryVariables.java => QueryContext.java} (82%) diff --git a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java index 4b784415..b872466a 100644 --- a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java +++ b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java @@ -11,8 +11,8 @@ interface ArithmeticOperator extends Operator { @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { requireNArgs(2, args, ArithmeticException.class::getSimpleName); - return "(" + builder.appendLiteral(args[0]) + id() + builder.appendLiteral(args[1]) + ")"; + return "(" + ctx.appendLiteral(args[0]) + id() + ctx.appendLiteral(args[1]) + ")"; } } diff --git a/src/main/java/org/usf/jquery/core/BasicComparator.java b/src/main/java/org/usf/jquery/core/BasicComparator.java index 9dcd9137..b9b480a4 100644 --- a/src/main/java/org/usf/jquery/core/BasicComparator.java +++ b/src/main/java/org/usf/jquery/core/BasicComparator.java @@ -11,8 +11,8 @@ public interface BasicComparator extends Comparator { @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { requireNArgs(2, args, BasicComparator.class::getSimpleName); - return builder.appendParameter(args[0]) + id() + builder.appendParameter(args[1]); + return ctx.appendParameter(args[0]) + id() + ctx.appendParameter(args[1]); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 95217edd..8c896c1f 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -2,7 +2,7 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Nested.resolveAll; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; @@ -24,8 +24,8 @@ public final class CaseColumn implements DBColumn { } @Override - public String sql(QueryVariables vars) { - var sub = vars.withValue(); //force literal parameter + public String sql(QueryContext ctx) { + var sub = ctx.withValue(); //force literal parameter return Stream.of(whenCases) .map(o-> o.sql(sub)) .collect(joining(SPACE, "CASE ", " END")); diff --git a/src/main/java/org/usf/jquery/core/CastFunction.java b/src/main/java/org/usf/jquery/core/CastFunction.java index 76be987b..f8d3b34e 100644 --- a/src/main/java/org/usf/jquery/core/CastFunction.java +++ b/src/main/java/org/usf/jquery/core/CastFunction.java @@ -18,7 +18,7 @@ default String id() { } @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext builder, Object[] args) { requireAtLeastNArgs(1, args, CastFunction.class::getSimpleName); var sb = new SqlStringBuilder(id()) .append("(") diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index 3a5a311a..24d306f6 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -3,7 +3,7 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Nested.resolveAll; import static org.usf.jquery.core.Nested.viewsOfNested; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.Utils.appendLast; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; @@ -27,15 +27,15 @@ public final class ColumnFilterGroup implements DBFilter { } @Override - public String sql(QueryVariables builder) { + public String sql(QueryContext ctx) { return Stream.of(filters) - .map(o-> o.sql(builder)) + .map(o-> o.sql(ctx)) .collect(joining(operator.sql(), "(", ")")); } @Override - public boolean resolve(QueryBuilder builder) { - return resolveAll(filters, builder); + public boolean resolve(QueryBuilder ctx) { + return resolveAll(filters, ctx); } @Override diff --git a/src/main/java/org/usf/jquery/core/ColumnProxy.java b/src/main/java/org/usf/jquery/core/ColumnProxy.java index afaa6f1e..87683789 100644 --- a/src/main/java/org/usf/jquery/core/ColumnProxy.java +++ b/src/main/java/org/usf/jquery/core/ColumnProxy.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import java.util.Objects; diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index 238dbd65..0613d05a 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -2,7 +2,7 @@ import static org.usf.jquery.core.Nested.tryResolve; import static org.usf.jquery.core.Nested.viewsOf; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import java.util.Collection; @@ -20,14 +20,14 @@ public class ColumnSingleFilter implements DBFilter { private final ComparisonExpression expression; @Override - public String sql(QueryVariables ph) { - return expression.sql(ph, left); + public String sql(QueryContext ctx) { + return expression.sql(ctx, left); } @Override - public boolean resolve(QueryBuilder builder) { - var res1 = tryResolve(left, builder); - var res2 = expression.resolve(builder); + public boolean resolve(QueryBuilder ctx) { + var res1 = tryResolve(left, ctx); + var res2 = expression.resolve(ctx); return res1 || res2; } diff --git a/src/main/java/org/usf/jquery/core/CombinedOperator.java b/src/main/java/org/usf/jquery/core/CombinedOperator.java index 2055801f..d45e243f 100644 --- a/src/main/java/org/usf/jquery/core/CombinedOperator.java +++ b/src/main/java/org/usf/jquery/core/CombinedOperator.java @@ -25,7 +25,7 @@ default String id() { } @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { //no SQL throw new UnsupportedOperationException("CombinedOperator::sql"); } } diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpression.java b/src/main/java/org/usf/jquery/core/ComparisonExpression.java index 2afaa072..26ec15a0 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpression.java @@ -11,12 +11,12 @@ */ public interface ComparisonExpression extends DBObject, Nested, Chainable { - String sql(QueryVariables builder, Object left); // do change method order + String sql(QueryContext ctx, Object left); // do change method order @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { requireNArgs(1, args, ComparisonExpression.class::getSimpleName); - return sql(builder, args[0]); + return sql(ctx, args[0]); } static ComparisonExpression eq(Object right) { diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index bbd29c1e..572bf315 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -3,7 +3,7 @@ import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Nested.resolveAll; import static org.usf.jquery.core.Nested.viewsOfNested; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.Utils.appendLast; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; @@ -27,9 +27,9 @@ public final class ComparisonExpressionGroup implements ComparisonExpression { } @Override - public String sql(QueryVariables builder, Object operand) { + public String sql(QueryContext ctx, Object operand) { return Stream.of(expressions) - .map(o-> o.sql(builder, operand)) + .map(o-> o.sql(ctx, operand)) .collect(joining(operator.sql(), "(", ")")); } diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index d5761316..aee6b160 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -4,7 +4,7 @@ import static java.util.Objects.nonNull; import static org.usf.jquery.core.Nested.tryResolveAll; import static org.usf.jquery.core.Nested.viewsOfAll; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import java.util.ArrayList; import java.util.Collection; @@ -24,13 +24,13 @@ public final class ComparisonSingleExpression implements ComparisonExpression { private final Object[] right; //optional @Override - public String sql(QueryVariables builder, Object left) { + public String sql(QueryContext ctx, Object left) { var param = new ArrayList<>(); param.add(left); if(nonNull(right)) { addAll(param, right); } - return comparator.sql(builder, param.toArray()); + return comparator.sql(ctx, param.toArray()); } @Override diff --git a/src/main/java/org/usf/jquery/core/ConstantOperator.java b/src/main/java/org/usf/jquery/core/ConstantOperator.java index 3e4f2a08..6792fe48 100644 --- a/src/main/java/org/usf/jquery/core/ConstantOperator.java +++ b/src/main/java/org/usf/jquery/core/ConstantOperator.java @@ -11,7 +11,7 @@ public interface ConstantOperator extends Operator { @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { requireNoArgs(args, ConstantOperator.class::getSimpleName); return id(); //use parentheses !? } diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 7a592e84..994e8e28 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -3,7 +3,7 @@ import static java.util.Objects.nonNull; import static org.usf.jquery.core.OrderType.ASC; import static org.usf.jquery.core.OrderType.DESC; -import static org.usf.jquery.core.QueryVariables.formatValue; +import static org.usf.jquery.core.QueryContext.formatValue; import static org.usf.jquery.core.Utils.appendFirst; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -22,12 +22,12 @@ */ public interface DBColumn extends DBObject, Typed, Nested { - String sql(QueryVariables builder); + String sql(QueryContext ctx); @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { requireNoArgs(args, DBColumn.class::getSimpleName); - return sql(builder); + return sql(ctx); } default ColumnProxy as(String name) { @@ -384,7 +384,7 @@ default DBOrder order(OrderType order) { return new DBOrder(this, order); } - default SingleCaseColumnBuilder whenCase() { + default SingleCaseColumnBuilder beginCase() { return new SingleCaseColumnBuilder(this); } @@ -436,7 +436,7 @@ static DBColumn constant(JDBCType type, Supplier value) { return new DBColumn() { @Override - public String sql(QueryVariables arg) { + public String sql(QueryContext ctx) { return formatValue(value.get()); //lazy } diff --git a/src/main/java/org/usf/jquery/core/DBFilter.java b/src/main/java/org/usf/jquery/core/DBFilter.java index f3a96415..ed17802c 100644 --- a/src/main/java/org/usf/jquery/core/DBFilter.java +++ b/src/main/java/org/usf/jquery/core/DBFilter.java @@ -9,11 +9,11 @@ */ public interface DBFilter extends DBObject, Nested, Chainable { - String sql(QueryVariables builder); + String sql(QueryContext ctx); @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { requireNoArgs(args, DBFilter.class::getSimpleName); - return sql(builder); + return sql(ctx); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/DBObject.java b/src/main/java/org/usf/jquery/core/DBObject.java index 76c0abfe..ed21fc39 100644 --- a/src/main/java/org/usf/jquery/core/DBObject.java +++ b/src/main/java/org/usf/jquery/core/DBObject.java @@ -8,6 +8,5 @@ @FunctionalInterface public interface DBObject { - String sql(QueryVariables builder, Object[] args); - + String sql(QueryContext ctx, Object[] args); } diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index e7da7085..765dc008 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; import static java.util.Objects.isNull; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -26,15 +26,15 @@ public DBOrder(DBColumn column) { } @Override - public String sql(QueryVariables builder, Object[] args) { + public String sql(QueryContext ctx, Object[] args) { requireNoArgs(args, DBOrder.class::getSimpleName); - return sql(builder); + return sql(ctx); } - public String sql(QueryVariables builder) { + public String sql(QueryContext ctx) { return isNull(order) - ? column.sql(builder) - : column.sql(builder) + SPACE + order.name(); + ? column.sql(ctx) + : column.sql(ctx) + SPACE + order.name(); } @Override diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index e44b61d9..757ed653 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static java.util.Objects.isNull; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -12,17 +11,16 @@ @FunctionalInterface public interface DBView extends DBObject { - String sql(QueryVariables builder); + String sql(QueryContext ctx); @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { requireNoArgs(args, DBView.class::getSimpleName); - return sql(builder); + return sql(ctx); } - default String sqlWithTag(QueryVariables builder) { - var tag = builder.viewAlias(this); - var sql = builder.viewOverload(this).orElse(this).sql(builder); //!important - return isNull(tag) ? sql : sql + SPACE + tag; + default String sqlWithTag(QueryContext ctx) { + return ctx.viewOverload(this).orElse(this).sql(ctx) //!important + + SPACE + ctx.viewAlias(this); } } diff --git a/src/main/java/org/usf/jquery/core/ExtractFunction.java b/src/main/java/org/usf/jquery/core/ExtractFunction.java index 8b944064..30c90cc7 100644 --- a/src/main/java/org/usf/jquery/core/ExtractFunction.java +++ b/src/main/java/org/usf/jquery/core/ExtractFunction.java @@ -18,7 +18,7 @@ default String id() { } @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext builder, Object[] args) { requireNArgs(1, args, ExtractFunction.class::getSimpleName); return id() + "(" + field() + " FROM " + builder.appendLiteral(args[0]) + ")"; } diff --git a/src/main/java/org/usf/jquery/core/FunctionOperator.java b/src/main/java/org/usf/jquery/core/FunctionOperator.java index 17bb1cda..d9a3aaf9 100644 --- a/src/main/java/org/usf/jquery/core/FunctionOperator.java +++ b/src/main/java/org/usf/jquery/core/FunctionOperator.java @@ -10,9 +10,9 @@ public interface FunctionOperator extends Operator { @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { return new SqlStringBuilder(id()) - .append("(").append(builder.appendLiteralArray(args)).append(")") //accept any + .append("(").append(ctx.appendLiteralArray(args)).append(")") //accept any .toString(); } } diff --git a/src/main/java/org/usf/jquery/core/InComparator.java b/src/main/java/org/usf/jquery/core/InComparator.java index 8a127313..466e3360 100644 --- a/src/main/java/org/usf/jquery/core/InComparator.java +++ b/src/main/java/org/usf/jquery/core/InComparator.java @@ -13,8 +13,8 @@ public interface InComparator extends Comparator { @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { requireAtLeastNArgs(2, args, InComparator.class::getSimpleName); - return builder.appendParameter(args[0]) + SPACE + id() + parenthese(builder.appendArrayParameter(args, 1)); + return ctx.appendParameter(args[0]) + SPACE + id() + parenthese(ctx.appendArrayParameter(args, 1)); } } diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index 9819102b..480c5134 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -12,8 +12,8 @@ public interface NamedColumn extends DBColumn { String getTag(); - default String sqlWithTag(QueryVariables builder) { - var s = sql(builder); + default String sqlWithTag(QueryContext ctx) { + var s = sql(ctx); if(nonNull(getTag())) { s += " AS " + doubleQuote(getTag()); } diff --git a/src/main/java/org/usf/jquery/core/NullComparator.java b/src/main/java/org/usf/jquery/core/NullComparator.java index 362d5576..ee5025d8 100644 --- a/src/main/java/org/usf/jquery/core/NullComparator.java +++ b/src/main/java/org/usf/jquery/core/NullComparator.java @@ -12,8 +12,8 @@ public interface NullComparator extends Comparator { @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { requireNArgs(1, args, NullComparator.class::getSimpleName); - return builder.appendParameter(args[0]) + SPACE + id(); + return ctx.appendParameter(args[0]) + SPACE + id(); } } diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 5289fc25..68888937 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -4,7 +4,7 @@ import static org.usf.jquery.core.Clause.FILTER; import static org.usf.jquery.core.Nested.tryResolveAll; import static org.usf.jquery.core.Nested.viewsOfAll; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.Validation.requireNArgs; import java.util.Collection; @@ -31,8 +31,8 @@ public OperationColumn(Operator operation, Object[] args) { } @Override - public String sql(QueryVariables vars) { - return nonNull(overColumn) ? overColumn.sql(vars) : operator.sql(vars, args); + public String sql(QueryContext ctx) { + return nonNull(overColumn) ? overColumn.sql(ctx) : operator.sql(ctx, args); } @Override diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index 07935b9b..af7c9e40 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -22,19 +22,19 @@ public final class Partition implements DBObject, Nested { private final DBOrder[] orders; @Override - public String sql(QueryVariables builder, Object[] args) { + public String sql(QueryContext ctx, Object[] args) { requireNoArgs(args, Partition.class::getSimpleName); - return sql(builder); + return sql(ctx); } - String sql(QueryVariables builder) { + String sql(QueryContext ctx) { var sb = new SqlStringBuilder(100); if(!isEmpty(columns)) { - sb.append("PARTITION BY ").append(builder.appendLiteralArray(columns)); + sb.append("PARTITION BY ").append(ctx.appendLiteralArray(columns)); } if(!isEmpty(orders)) { //require orders sb.appendIf(!isEmpty(columns), SPACE) - .append("ORDER BY ").append(builder.appendLiteralArray(orders)); + .append("ORDER BY ").append(ctx.appendLiteralArray(orders)); } return sb.toString(); } diff --git a/src/main/java/org/usf/jquery/core/PipeFunction.java b/src/main/java/org/usf/jquery/core/PipeFunction.java index d341ebec..e57cb9a8 100644 --- a/src/main/java/org/usf/jquery/core/PipeFunction.java +++ b/src/main/java/org/usf/jquery/core/PipeFunction.java @@ -13,7 +13,7 @@ public interface PipeFunction extends FunctionOperator { @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext builder, Object[] args) { requireAtLeastNArgs(1, args, PipeFunction.class::getSimpleName); return builder.appendLiteral(args[0]) + SPACE + FunctionOperator.super.sql(builder, copyOfRange(args, 1, args.length)); diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 9105e15d..804a275d 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -14,7 +14,7 @@ import static org.usf.jquery.core.Database.currentDatabase; import static org.usf.jquery.core.Database.setCurrentDatabase; import static org.usf.jquery.core.LogicalOperator.AND; -import static org.usf.jquery.core.QueryVariables.parameterized; +import static org.usf.jquery.core.QueryContext.parameterized; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNonEmpty; @@ -25,7 +25,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.function.Supplier; import lombok.Getter; @@ -161,20 +160,20 @@ public RequestQuery build(String schema) { return new RequestQuery(sb.toString(), pb.args(), pb.argTypes()); } - public final void build(SqlStringBuilder sb, QueryVariables pb){ + public final void build(SqlStringBuilder sb, QueryContext ctx){ var sub = new SqlStringBuilder(100); - where(sub, pb); //first resolve over view - groupBy(sub, pb); - having(sub, pb); - orderBy(sub, pb); - fetch(sub, pb); - select(sb, pb); - from(sb, pb); //enumerate all views before from clause - join(sb, pb); + where(sub, ctx); //first resolve over view + groupBy(sub, ctx); + having(sub, ctx); + orderBy(sub, ctx); + fetch(sub, ctx); + select(sb, ctx); + from(sb, ctx); //enumerate all views before from clause + join(sb, ctx); sb.append(sub.toString()); //TODO optim } - void select(SqlStringBuilder sb, QueryVariables qv){ + void select(SqlStringBuilder sb, QueryContext ctx){ if(currentDatabase() == TERADATA) { if(nonNull(offset)) { throw new UnsupportedOperationException(""); @@ -187,53 +186,53 @@ void select(SqlStringBuilder sb, QueryVariables qv){ .appendIf(distinct, " DISTINCT") .appendIf(nonNull(fetch) && currentDatabase() == TERADATA, ()-> " TOP " + fetch) //??????? .append(SPACE) - .appendEach(columns, SCOMA, o-> o.sqlWithTag(qv)); + .appendEach(columns, SCOMA, o-> o.sqlWithTag(ctx)); } - void from(SqlStringBuilder sb, QueryVariables qv) { - var excludes = joins.stream().map(ViewJoin::getView).mapMulti((v,c)-> qv.viewOverload(v).ifPresent(c)).toList(); - var views = qv.views().stream().filter(not(excludes::contains)).distinct().toList(); //do not remove views + void from(SqlStringBuilder sb, QueryContext ctx) { + var excludes = joins.stream().map(ViewJoin::getView).toList(); + var views = ctx.views().stream().filter(not(excludes::contains)).toList(); //do not remove views if(!views.isEmpty()) { - sb.append(" FROM ").appendEach(views, SCOMA, v-> v.sqlWithTag(qv)); + sb.append(" FROM ").appendEach(views, SCOMA, v-> v.sqlWithTag(ctx)); } } - void join(SqlStringBuilder sb, QueryVariables qv) { + void join(SqlStringBuilder sb, QueryContext ctx) { if(!joins.isEmpty()) { - sb.append(SPACE).appendEach(joins, SPACE, v-> v.sql(qv)); + sb.append(SPACE).appendEach(joins, SPACE, v-> v.sql(ctx)); } } - void where(SqlStringBuilder sb, QueryVariables qv){ + void where(SqlStringBuilder sb, QueryContext ctx){ if(!where.isEmpty()) { - sb.append(" WHERE ").appendEach(where, AND.sql(), f-> f.sql(qv)); + sb.append(" WHERE ").appendEach(where, AND.sql(), f-> f.sql(ctx)); } } - void groupBy(SqlStringBuilder sb, QueryVariables qv){ + void groupBy(SqlStringBuilder sb, QueryContext ctx){ if(aggregation && !group.isEmpty()) { sb.append(" GROUP BY ") .append(group.stream() - .map(c-> !(c instanceof ViewColumn) && columns.contains(c) ? ((NamedColumn)c).getTag() : c.sql(qv)) //add alias + .map(c-> !(c instanceof ViewColumn) && columns.contains(c) ? ((NamedColumn)c).getTag() : c.sql(ctx)) //add alias .collect(joining(SCOMA))); } } - void having(SqlStringBuilder sb, QueryVariables qv){ + void having(SqlStringBuilder sb, QueryContext ctx){ if(!having.isEmpty()) { sb.append(" HAVING ") - .appendEach(having, AND.sql(), f-> f.sql(qv)); + .appendEach(having, AND.sql(), f-> f.sql(ctx)); } } - void orderBy(SqlStringBuilder sb, QueryVariables qv) { + void orderBy(SqlStringBuilder sb, QueryContext ctx) { if(!orders.isEmpty()) { sb.append(" ORDER BY ") - .appendEach(orders, SCOMA, o-> o.sql(qv)); + .appendEach(orders, SCOMA, o-> o.sql(ctx)); } } - void fetch(SqlStringBuilder sb, QueryVariables qv) { + void fetch(SqlStringBuilder sb, QueryContext ctx) { if(currentDatabase() != TERADATA) { // TOP n if(nonNull(offset)) { sb.append(" OFFSET ").append(offset.toString()).append(" ROWS"); diff --git a/src/main/java/org/usf/jquery/core/QueryVariables.java b/src/main/java/org/usf/jquery/core/QueryContext.java similarity index 82% rename from src/main/java/org/usf/jquery/core/QueryVariables.java rename to src/main/java/org/usf/jquery/core/QueryContext.java index 4036dba5..caeefd89 100644 --- a/src/main/java/org/usf/jquery/core/QueryVariables.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -30,7 +30,7 @@ */ @Setter(AccessLevel.PACKAGE) @RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public final class QueryVariables { +public final class QueryContext { private static final String P_ARG = "?"; @@ -143,23 +143,23 @@ public static String formatValue(Object o) { return nonNull(o) ? quote(o.toString()) : "null"; } - public QueryVariables withValue() { - return new QueryVariables(schema, vPrefix, null, null, views, overView); + public QueryContext withValue() { + return new QueryContext(schema, vPrefix, null, null, views, overView); } - public QueryVariables subQuery(Map overView) { - return new QueryVariables(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), overView); + public QueryContext subQuery(Map overView) { + return new QueryContext(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), overView); } - public static QueryVariables addWithValue() { + public static QueryContext addWithValue() { return addWithValue(null, emptyMap()); //no args } - public static QueryVariables addWithValue(String schema, Map overView) { - return new QueryVariables(schema, "v", null, null, new ArrayList<>(), overView); //no args + public static QueryContext addWithValue(String schema, Map overView) { + return new QueryContext(schema, "v", null, null, new ArrayList<>(), overView); //no args } - public static QueryVariables parameterized(String schema, Map overView) { - return new QueryVariables(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), overView); + public static QueryContext parameterized(String schema, Map overView) { + return new QueryContext(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), overView); } } diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index 36c46e28..91ba8af0 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import lombok.AccessLevel; import lombok.Getter; @@ -18,9 +18,9 @@ public final class QueryView implements DBView { private final QueryBuilder builder; @Override - public String sql(QueryVariables param) { + public String sql(QueryContext ctx) { var s = new SqlStringBuilder(100).append("("); - builder.build(s, param.subQuery(builder.getOverView())); + builder.build(s, ctx.subQuery(builder.getOverView())); return s.append(")").toString(); } diff --git a/src/main/java/org/usf/jquery/core/RangeComparator.java b/src/main/java/org/usf/jquery/core/RangeComparator.java index 7567db8b..1f225ced 100644 --- a/src/main/java/org/usf/jquery/core/RangeComparator.java +++ b/src/main/java/org/usf/jquery/core/RangeComparator.java @@ -13,10 +13,10 @@ public interface RangeComparator extends Comparator { @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { requireNArgs(3, args, RangeComparator.class::getSimpleName); - return builder.appendParameter(args[0]) + SPACE + id() + - SPACE + builder.appendParameter(args[1]) + - AND.sql() + builder.appendParameter(args[2]); + return ctx.appendParameter(args[0]) + SPACE + id() + + SPACE + ctx.appendParameter(args[1]) + + AND.sql() + ctx.appendParameter(args[2]); } } diff --git a/src/main/java/org/usf/jquery/core/SingleColumnQuery.java b/src/main/java/org/usf/jquery/core/SingleColumnQuery.java index 28a7372c..af9aec68 100644 --- a/src/main/java/org/usf/jquery/core/SingleColumnQuery.java +++ b/src/main/java/org/usf/jquery/core/SingleColumnQuery.java @@ -1,6 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.Validation.requireNoArgs; import org.usf.jquery.core.JavaType.Typed; @@ -26,13 +26,13 @@ public final class SingleColumnQuery implements DBObject, Typed { } @Override - public String sql(QueryVariables builder, Object[] args) { + public String sql(QueryContext ctx, Object[] args) { requireNoArgs(args, SingleColumnQuery.class::getSimpleName); - return sql(builder); + return sql(ctx); } - public String sql(QueryVariables builder) { - return query.sql(builder); + public String sql(QueryContext ctx) { + return query.sql(ctx); } @Override diff --git a/src/main/java/org/usf/jquery/core/StringComparator.java b/src/main/java/org/usf/jquery/core/StringComparator.java index 8688a54f..3f186a89 100644 --- a/src/main/java/org/usf/jquery/core/StringComparator.java +++ b/src/main/java/org/usf/jquery/core/StringComparator.java @@ -12,9 +12,9 @@ public interface StringComparator extends Comparator { @Override - default String sql(QueryVariables builder, Object[] args) { + default String sql(QueryContext ctx, Object[] args) { requireNArgs(2, args, StringComparator.class::getSimpleName); - return builder.appendParameter(args[0]) + space(id()) + builder.appendParameter(wildcardArg(args[1])); + return ctx.appendParameter(args[0]) + space(id()) + ctx.appendParameter(wildcardArg(args[1])); } default Object wildcardArg(Object o) { diff --git a/src/main/java/org/usf/jquery/core/TableView.java b/src/main/java/org/usf/jquery/core/TableView.java index 3ef011a8..9c887080 100644 --- a/src/main/java/org/usf/jquery/core/TableView.java +++ b/src/main/java/org/usf/jquery/core/TableView.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.member; import lombok.Getter; @@ -25,8 +25,8 @@ public TableView(String schema, String name) { } @Override - public String sql(QueryVariables qv) { - return member(getSchemaOrElse(qv.getSchema()), name); + public String sql(QueryContext ctx) { + return member(getSchemaOrElse(ctx.getSchema()), name); } public String getSchemaOrElse(String defaultSchema) { diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index d90a461d..41018ba8 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -24,9 +24,9 @@ public TypedComparator(Comparator comparator, Parameter... parameters) { } @Override - public String sql(QueryVariables builder, Object[] args) { + public String sql(QueryContext ctx, Object[] args) { try { - return comparator.sql(builder, parameterSet.assertArguments(args)); + return comparator.sql(ctx, parameterSet.assertArguments(args)); } catch (BadArgumentException e) { throw badArgumentsException("comparator", comparator.id(), args, e); } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index a8190706..e2ca1c87 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -30,9 +30,9 @@ public TypedOperator(ArgTypeRef typeFn, Operator function, Parameter... paramete } @Override - public String sql(QueryVariables builder, Object[] args) { + public String sql(QueryContext ctx, Object[] args) { try { - return operator.sql(builder, parameterSet.assertArguments(args)); + return operator.sql(ctx, parameterSet.assertArguments(args)); } catch (BadArgumentException e) { throw badArgumentsException("operator", operator.id(), args, e); diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index a7418943..45fc0722 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -1,7 +1,7 @@ package org.usf.jquery.core; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.member; import java.util.Collection; @@ -24,8 +24,8 @@ public final class ViewColumn implements NamedColumn { private final String tag; //optional @Override - public String sql(QueryVariables qv) { - return nonNull(view) ? member(qv.viewAlias(view), name) : name; + public String sql(QueryContext ctx) { + return nonNull(view) ? member(ctx.viewAlias(view), name) : name; } public boolean resolve(QueryBuilder builder) { diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index 98d519a1..ccdc1830 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -7,12 +7,14 @@ import static org.usf.jquery.core.JoinType.LEFT; import static org.usf.jquery.core.JoinType.RIGHT; import static org.usf.jquery.core.LogicalOperator.AND; +import static org.usf.jquery.core.QueryContext.addWithValue; +import static org.usf.jquery.core.Utils.isEmpty; +import static org.usf.jquery.core.Validation.requireAtLeastNArgs; import static org.usf.jquery.core.Validation.requireNoArgs; import java.util.stream.Stream; import lombok.Getter; -import lombok.RequiredArgsConstructor; /** * @@ -20,23 +22,36 @@ * */ @Getter -@RequiredArgsConstructor public final class ViewJoin implements DBObject { private final JoinType joinType; private final DBView view; private final DBFilter[] filters; //join results !? + + public ViewJoin(JoinType joinType, DBView view, DBFilter[] filters) { + super(); + this.joinType = joinType; + this.view = view; + this.filters = joinType == CROSS + ? filters + : requireAtLeastNArgs(1, filters, ViewJoin.class::getSimpleName); + } @Override - public String sql(QueryVariables qv, Object[] args) { + public String sql(QueryContext qv, Object[] args) { requireNoArgs(args, ViewJoin.class::getSimpleName); return sql(qv); } - public String sql(QueryVariables qv) { - return joinType + " JOIN " + view.sqlWithTag(qv) + " ON " + - Stream.of(filters).map(f-> f.sql(qv)).collect(joining(AND.sql())); + public String sql(QueryContext ctx) { + var s = joinType + " JOIN " + view.sqlWithTag(ctx); + if(!isEmpty(filters)) { + s += " ON " + Stream.of(filters) + .map(f-> f.sql(ctx)) + .collect(joining(AND.sql())); + } + return s; } public static ViewJoin innerJoin(DBView view, DBFilter... filters) { @@ -58,4 +73,9 @@ public static ViewJoin fullJoin(DBView view, DBFilter... filters) { public static ViewJoin crossJoin(DBView view, DBFilter... filters) { return new ViewJoin(CROSS, view, filters); } + + @Override + public String toString() { + return sql(addWithValue()); + } } diff --git a/src/main/java/org/usf/jquery/core/WhenCase.java b/src/main/java/org/usf/jquery/core/WhenCase.java index 9db9b519..b92c0593 100644 --- a/src/main/java/org/usf/jquery/core/WhenCase.java +++ b/src/main/java/org/usf/jquery/core/WhenCase.java @@ -5,7 +5,7 @@ import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.Nested.tryResolve; import static org.usf.jquery.core.Nested.viewsOf; -import static org.usf.jquery.core.QueryVariables.addWithValue; +import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.Validation.requireNoArgs; import java.util.Collection; @@ -26,19 +26,19 @@ final class WhenCase implements DBObject, Typed, Nested { private final Object value; @Override - public String sql(QueryVariables qv, Object[] args) { + public String sql(QueryContext qv, Object[] args) { requireNoArgs(args, WhenCase.class::getSimpleName); return sql(qv); } - public String sql(QueryVariables qv) { + public String sql(QueryContext ctx) { var sb = new StringBuilder(50); sb = isNull(filter) ? sb.append("ELSE ") : sb.append("WHEN ") - .append(filter.sql(qv)) + .append(filter.sql(ctx)) .append(" THEN "); - return sb.append(qv.appendLiteral(value)).toString(); + return sb.append(ctx.appendLiteral(value)).toString(); } @Override diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 38e5414e..80a54fac 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -18,7 +18,7 @@ */ @Getter @RequiredArgsConstructor -final class QueryDecorator implements ViewDecorator { //non public +final class QueryDecorator implements ViewDecorator { private final String id; private final QueryView query; diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index cbddb31e..da589a3a 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -341,7 +341,7 @@ private Optional lookupQueryResource(ViewDecorator vd) { return empty(); } - //view[.operator|column[.criteria]|criteria] + //[view].operator|column[.criteria]|criteria private Optional lookupViewResource(ViewDecorator vd, ViewDecorator current, boolean filter) { var cur = requireNonNullElse(current, vd); var res = lookupViewOperation(vd, current) @@ -360,6 +360,7 @@ private Optional lookupViewOperation(ViewDecorator vd, ViewDeco }); } + //[view.]count | rank|rowNumber|denseRank private Optional lookupColumnResource(ViewDecorator td, boolean filter) { var res = currentContext().lookupRegisteredColumn(value); if(res.isPresent()) { diff --git a/src/main/java/org/usf/jquery/web/RevisionIterator.java b/src/main/java/org/usf/jquery/web/RevisionIterator.java index 01b547c4..eaae3dcf 100644 --- a/src/main/java/org/usf/jquery/web/RevisionIterator.java +++ b/src/main/java/org/usf/jquery/web/RevisionIterator.java @@ -14,7 +14,7 @@ import org.usf.jquery.core.ColumnSingleFilter; import org.usf.jquery.core.DBColumn; import org.usf.jquery.core.DBFilter; -import org.usf.jquery.core.QueryVariables; +import org.usf.jquery.core.QueryContext; import org.usf.jquery.core.TableView; import lombok.RequiredArgsConstructor; @@ -58,7 +58,7 @@ public static RevisionIterator iterator(YearMonth[] revisions){ static TableView yearTable(TableView view) { return new TableView(view.getSchema(), view.getName()) { @Override - public String sql(QueryVariables builder) { + public String sql(QueryContext builder) { return super.sql(builder) + "_" + currentRev.get().getKey(); } }; @@ -71,7 +71,7 @@ static DBColumn yearColumn() { static DBFilter monthFilter(DBColumn column) { return new ColumnSingleFilter(null, null) { @Override - public String sql(QueryVariables vars) { + public String sql(QueryContext vars) { var values = currentRev.get().getValue(); //get it on build var filter = values.size() == 1 ? column.eq(values.get(0).getMonthValue()) From f3a7d291bd51d0af2ae4d8b2742d825d9ff8136f Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 10 Sep 2024 14:12:45 +0200 Subject: [PATCH 240/298] edit --- src/main/java/org/usf/jquery/web/RequestEntryChain.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index da589a3a..2b3a8e19 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -349,9 +349,9 @@ private Optional lookupViewResource(ViewDecorator vd, ViewDecorato .or(()-> lookupColumnResource(cur, filter)); return filter ? res.or(()-> ofNullable(cur.criteria(value)).map(crt-> new ViewResource(this, cur, crt))) : res; } - + + //[view.]count | rank|rowNumber|denseRank private Optional lookupViewOperation(ViewDecorator vd, ViewDecorator current) { - //view.[count|rank|rowNumber|danseRank] only return lookupOperator(value, isNull(current) ? null : op-> op.isCountFunction() || op.isWindowFunction()).map(fn-> { var col = isEmpty(args) && fn.isCountFunction() ? allColumns(requireNonNullElse(current, vd).view()) @@ -360,7 +360,7 @@ private Optional lookupViewOperation(ViewDecorator vd, ViewDeco }); } - //[view.]count | rank|rowNumber|denseRank + //[view.]column[.criteria] private Optional lookupColumnResource(ViewDecorator td, boolean filter) { var res = currentContext().lookupRegisteredColumn(value); if(res.isPresent()) { From 21b73321bed2ff6f20cdb7e5b33bbfc23ca7c22d Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 10 Sep 2024 18:00:51 +0200 Subject: [PATCH 241/298] edit --- .../java/org/usf/jquery/core/QueryBuilder.java | 18 +++++++++--------- .../java/org/usf/jquery/core/ViewJoin.java | 3 ++- .../java/org/usf/jquery/web/Parameters.java | 2 +- .../org/usf/jquery/web/RequestEntryChain.java | 10 +++++----- .../java/org/usf/jquery/web/ViewDecorator.java | 10 +++++----- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 804a275d..f214ec47 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -49,7 +49,7 @@ public class QueryBuilder { private final Map overView = new HashMap<>(); private boolean distinct; private boolean aggregation; - private Integer fetch; + private Integer limit; private Integer offset; private Iterator it; private Clause clause; @@ -111,8 +111,8 @@ public QueryBuilder joins(@NonNull ViewJoin... joins) { return this; } - public QueryBuilder fetch(int fetch) { - this.fetch = fetch; + public QueryBuilder limit(int limit) { + this.limit = limit; return this; } @@ -178,13 +178,13 @@ void select(SqlStringBuilder sb, QueryContext ctx){ if(nonNull(offset)) { throw new UnsupportedOperationException(""); } - if(distinct && nonNull(fetch)) { + if(distinct && nonNull(limit)) { throw new UnsupportedOperationException("Top N option is not supported with DISTINCT option."); } } sb.append("SELECT") .appendIf(distinct, " DISTINCT") - .appendIf(nonNull(fetch) && currentDatabase() == TERADATA, ()-> " TOP " + fetch) //??????? + .appendIf(nonNull(limit) && currentDatabase() == TERADATA, ()-> " TOP " + limit) //??????? .append(SPACE) .appendEach(columns, SCOMA, o-> o.sqlWithTag(ctx)); } @@ -234,11 +234,11 @@ void orderBy(SqlStringBuilder sb, QueryContext ctx) { void fetch(SqlStringBuilder sb, QueryContext ctx) { if(currentDatabase() != TERADATA) { // TOP n - if(nonNull(offset)) { - sb.append(" OFFSET ").append(offset.toString()).append(" ROWS"); + if(nonNull(limit)) { + sb.append(" limit ").append(limit.toString()); } - if(nonNull(fetch)) { - sb.append(" FETCH NEXT ").append(fetch.toString()).append(" ROWS ONLY"); + if(nonNull(offset)) { + sb.append(" OFFSET ").append(offset.toString()); } } } diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index ccdc1830..f5171883 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -47,8 +47,9 @@ public String sql(QueryContext qv, Object[] args) { public String sql(QueryContext ctx) { var s = joinType + " JOIN " + view.sqlWithTag(ctx); if(!isEmpty(filters)) { + var val = ctx.withValue(); s += " ON " + Stream.of(filters) - .map(f-> f.sql(ctx)) + .map(f-> f.sql(val)) .collect(joining(AND.sql())); } return s; diff --git a/src/main/java/org/usf/jquery/web/Parameters.java b/src/main/java/org/usf/jquery/web/Parameters.java index 4d201800..45ad7dc5 100644 --- a/src/main/java/org/usf/jquery/web/Parameters.java +++ b/src/main/java/org/usf/jquery/web/Parameters.java @@ -20,8 +20,8 @@ public final class Parameters { public static final String COLUMN_DISTINCT = "column.distinct"; //select.distinct public static final String FILTER = "filter"; public static final String ORDER = "order"; - public static final String FETCH = "fetch"; public static final String OFFSET = "offset"; + public static final String LIMIT = "limit"; public static final String JOIN = "join"; public static final String PARTITION = "partition"; } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 2b3a8e19..6469be2a 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -29,10 +29,10 @@ import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.Parameters.DISTINCT; -import static org.usf.jquery.web.Parameters.FETCH; +import static org.usf.jquery.web.Parameters.OFFSET; import static org.usf.jquery.web.Parameters.FILTER; import static org.usf.jquery.web.Parameters.JOIN; -import static org.usf.jquery.web.Parameters.OFFSET; +import static org.usf.jquery.web.Parameters.LIMIT; import static org.usf.jquery.web.Parameters.ORDER; import static org.usf.jquery.web.Parameters.PARTITION; import static org.usf.jquery.web.Parameters.QUERY; @@ -126,9 +126,9 @@ Optional evalQuery(ViewDecorator td, boolean requireTag) { //sub case FILTER: q.filters(e.filterVarargs(td)); break; case ORDER: q.orders(e.orderVarargs(td)); break; case JOIN: q.joins(e.evalJoin(td)); break; + case LIMIT: q.limit((int)e.toOneArg(td, INTEGER)); break; case OFFSET: q.offset((int)e.toOneArg(td, INTEGER)); break; - case FETCH: q.fetch((int)e.toOneArg(td, INTEGER)); break; - default: throw badEntrySyntaxException(e.value, join("|", DISTINCT, FILTER, ORDER, JOIN, OFFSET, FETCH)); + default: throw badEntrySyntaxException(e.value, join("|", DISTINCT, FILTER, ORDER, JOIN, LIMIT, OFFSET)); } } return Optional.of(new QueryDecorator(requireTag ? e.requireTag() : e.tag, q.asView())); @@ -313,7 +313,7 @@ private ViewResource chainColumnOperations(ViewDecorator vd, boolean filter) { return r; } - //view.resource | resource + //[view|query.]resource private ViewResource lookupResource(ViewDecorator vd, boolean filter) { //do not change priority if(hasNext()) { var rc = currentContext().lookupRegisteredView(value); diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index eb39d923..7916926d 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -15,9 +15,9 @@ import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; import static org.usf.jquery.web.Parameters.COLUMN; import static org.usf.jquery.web.Parameters.COLUMN_DISTINCT; -import static org.usf.jquery.web.Parameters.FETCH; -import static org.usf.jquery.web.Parameters.JOIN; import static org.usf.jquery.web.Parameters.OFFSET; +import static org.usf.jquery.web.Parameters.JOIN; +import static org.usf.jquery.web.Parameters.LIMIT; import static org.usf.jquery.web.Parameters.ORDER; import static org.usf.jquery.web.Parameters.VIEW; import static org.usf.jquery.web.RequestParser.parseEntries; @@ -112,7 +112,7 @@ default QueryBuilder query(Map parameterMap) { parseColumns(query, parameterMap); parseOrders(query, parameterMap); parseJoin(query, parameterMap); - parseFetch(query, parameterMap); + parseLimit(query, parameterMap); parseOffset(query, parameterMap); parseFilters(query, parameterMap); //remove all entries before parse filters return query; @@ -169,8 +169,8 @@ default void parseJoin(QueryBuilder query, Map parameters) { } } - default void parseFetch(QueryBuilder query, Map parameters) { - requirePositiveInt(FETCH, parameters).ifPresent(query::fetch); + default void parseLimit(QueryBuilder query, Map parameters) { + requirePositiveInt(LIMIT, parameters).ifPresent(query::limit); } default void parseOffset(QueryBuilder query, Map parameters) { From f389a3ec6211092444983881fb8d95c4981a7d8d Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 10 Sep 2024 18:22:26 +0200 Subject: [PATCH 242/298] edit --- src/main/java/org/usf/jquery/core/QueryBuilder.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index f214ec47..b5ad5f03 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -166,7 +166,7 @@ public final void build(SqlStringBuilder sb, QueryContext ctx){ groupBy(sub, ctx); having(sub, ctx); orderBy(sub, ctx); - fetch(sub, ctx); + fetch(sub); select(sb, ctx); from(sb, ctx); //enumerate all views before from clause join(sb, ctx); @@ -232,14 +232,10 @@ void orderBy(SqlStringBuilder sb, QueryContext ctx) { } } - void fetch(SqlStringBuilder sb, QueryContext ctx) { + void fetch(SqlStringBuilder sb) { if(currentDatabase() != TERADATA) { // TOP n - if(nonNull(limit)) { - sb.append(" limit ").append(limit.toString()); - } - if(nonNull(offset)) { - sb.append(" OFFSET ").append(offset.toString()); - } + sb.appendIf(nonNull(limit), ()-> " LIMIT " + limit); + sb.appendIf(nonNull(offset), ()-> " OFFSET " + offset); } } From 4a36cde427fb8f6bb61a8361afc174e7683487d2 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 10 Sep 2024 18:37:55 +0200 Subject: [PATCH 243/298] edit --- .../java/org/usf/jquery/core/QueryContext.java | 7 ++++--- .../java/org/usf/jquery/core/WhenCase.java | 11 +++++------ .../java/org/usf/jquery/web/ViewDecorator.java | 18 ++++++++---------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java index caeefd89..3e793113 100644 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -1,6 +1,7 @@ package org.usf.jquery.core; import static java.util.Collections.emptyMap; +import static java.util.Collections.unmodifiableMap; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.joining; @@ -148,7 +149,7 @@ public QueryContext withValue() { } public QueryContext subQuery(Map overView) { - return new QueryContext(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), overView); + return new QueryContext(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), unmodifiableMap(overView)); } public static QueryContext addWithValue() { @@ -156,10 +157,10 @@ public static QueryContext addWithValue() { } public static QueryContext addWithValue(String schema, Map overView) { - return new QueryContext(schema, "v", null, null, new ArrayList<>(), overView); //no args + return new QueryContext(schema, "v", null, null, new ArrayList<>(), unmodifiableMap(overView)); //no args } public static QueryContext parameterized(String schema, Map overView) { - return new QueryContext(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), overView); + return new QueryContext(schema, "v", new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), unmodifiableMap(overView)); } } diff --git a/src/main/java/org/usf/jquery/core/WhenCase.java b/src/main/java/org/usf/jquery/core/WhenCase.java index b92c0593..abb32f7a 100644 --- a/src/main/java/org/usf/jquery/core/WhenCase.java +++ b/src/main/java/org/usf/jquery/core/WhenCase.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.Nested.tryResolve; @@ -33,11 +32,11 @@ public String sql(QueryContext qv, Object[] args) { public String sql(QueryContext ctx) { var sb = new StringBuilder(50); - sb = isNull(filter) - ? sb.append("ELSE ") - : sb.append("WHEN ") - .append(filter.sql(ctx)) - .append(" THEN "); + sb = nonNull(filter) + ? sb.append("WHEN ") + .append(filter.sql(ctx)) + .append(" THEN ") + : sb.append("ELSE "); return sb.append(ctx.appendLiteral(value)).toString(); } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 7916926d..ab52f106 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -5,19 +5,21 @@ import static java.util.Map.entry; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; +import static java.util.Optional.empty; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toUnmodifiableMap; import static org.usf.jquery.core.SqlStringBuilder.quote; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireLegalVariable; +import static org.usf.jquery.core.Validation.requireNArgs; import static org.usf.jquery.web.ColumnMetadata.columnMetadata; import static org.usf.jquery.web.ContextManager.currentContext; import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; import static org.usf.jquery.web.Parameters.COLUMN; import static org.usf.jquery.web.Parameters.COLUMN_DISTINCT; -import static org.usf.jquery.web.Parameters.OFFSET; import static org.usf.jquery.web.Parameters.JOIN; import static org.usf.jquery.web.Parameters.LIMIT; +import static org.usf.jquery.web.Parameters.OFFSET; import static org.usf.jquery.web.Parameters.ORDER; import static org.usf.jquery.web.Parameters.VIEW; import static org.usf.jquery.web.RequestParser.parseEntries; @@ -188,17 +190,13 @@ default void parseFilters(QueryBuilder query, Map parameters) private static Optional requirePositiveInt(String key, Map parameters) { if(parameters.containsKey(key)) { - var values = parameters.remove(key); - if(values.length == 1) { - var v = parseInt(values[0]); - if(v >= 0) { - return Optional.of(v); - } - throw new IllegalArgumentException(key + " cannot be negative"); + var v = parseInt(requireNArgs(1, parameters.remove(key), ()-> key)[0]); + if(v >= 0) { + return Optional.of(v); } - throw new IllegalArgumentException("too many value"); + throw new IllegalArgumentException(key + " cannot be negative"); } - return Optional.empty(); + return empty(); } static Stream flatParameters(String... arr) { //number local separator From 94310f0dff122f1edd8cd85d0aa65ee9dfc0037f Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 10 Sep 2024 18:47:49 +0200 Subject: [PATCH 244/298] edit --- .../java/org/usf/jquery/core/QueryBuilder.java | 4 ++-- .../org/usf/jquery/web/RequestEntryChain.java | 2 +- .../java/org/usf/jquery/web/ViewDecorator.java | 16 +++++++--------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index b5ad5f03..5d8ed59f 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -111,12 +111,12 @@ public QueryBuilder joins(@NonNull ViewJoin... joins) { return this; } - public QueryBuilder limit(int limit) { + public QueryBuilder limit(Integer limit) { this.limit = limit; return this; } - public QueryBuilder offset(int offset) { + public QueryBuilder offset(Integer offset) { this.offset = offset; return this; } diff --git a/src/main/java/org/usf/jquery/web/RequestEntryChain.java b/src/main/java/org/usf/jquery/web/RequestEntryChain.java index 6469be2a..a05db3f1 100644 --- a/src/main/java/org/usf/jquery/web/RequestEntryChain.java +++ b/src/main/java/org/usf/jquery/web/RequestEntryChain.java @@ -29,10 +29,10 @@ import static org.usf.jquery.web.EntryParseException.cannotParseEntryException; import static org.usf.jquery.web.NoSuchResourceException.noSuchResourceException; import static org.usf.jquery.web.Parameters.DISTINCT; -import static org.usf.jquery.web.Parameters.OFFSET; import static org.usf.jquery.web.Parameters.FILTER; import static org.usf.jquery.web.Parameters.JOIN; import static org.usf.jquery.web.Parameters.LIMIT; +import static org.usf.jquery.web.Parameters.OFFSET; import static org.usf.jquery.web.Parameters.ORDER; import static org.usf.jquery.web.Parameters.PARTITION; import static org.usf.jquery.web.Parameters.QUERY; diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index ab52f106..0d2b6382 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -5,7 +5,6 @@ import static java.util.Map.entry; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; -import static java.util.Optional.empty; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toUnmodifiableMap; import static org.usf.jquery.core.SqlStringBuilder.quote; @@ -28,7 +27,6 @@ import java.util.Collection; import java.util.Map; import java.util.Map.Entry; -import java.util.Optional; import java.util.stream.Stream; import org.usf.jquery.core.ColumnProxy; @@ -113,7 +111,7 @@ default QueryBuilder query(Map parameterMap) { parseViews(query, parameterMap); parseColumns(query, parameterMap); parseOrders(query, parameterMap); - parseJoin(query, parameterMap); + parseJoins(query, parameterMap); parseLimit(query, parameterMap); parseOffset(query, parameterMap); parseFilters(query, parameterMap); //remove all entries before parse filters @@ -163,7 +161,7 @@ default void parseOrders(QueryBuilder query, Map parameters) { .forEach(e-> query.orders(e.evalOrder(this))); } } - default void parseJoin(QueryBuilder query, Map parameters) { + default void parseJoins(QueryBuilder query, Map parameters) { if(parameters.containsKey(JOIN)) { Stream.of(parameters.remove(JOIN)) .flatMap(c-> parseEntries(c).stream()) @@ -172,11 +170,11 @@ default void parseJoin(QueryBuilder query, Map parameters) { } default void parseLimit(QueryBuilder query, Map parameters) { - requirePositiveInt(LIMIT, parameters).ifPresent(query::limit); + query.limit(requirePositiveInt(LIMIT, parameters)); } default void parseOffset(QueryBuilder query, Map parameters) { - requirePositiveInt(OFFSET, parameters).ifPresent(query::offset); + query.offset(requirePositiveInt(OFFSET, parameters)); } default void parseFilters(QueryBuilder query, Map parameters) { @@ -188,15 +186,15 @@ default void parseFilters(QueryBuilder query, Map parameters) .forEach(query::filters); } - private static Optional requirePositiveInt(String key, Map parameters) { + private static Integer requirePositiveInt(String key, Map parameters) { if(parameters.containsKey(key)) { var v = parseInt(requireNArgs(1, parameters.remove(key), ()-> key)[0]); if(v >= 0) { - return Optional.of(v); + return v; } throw new IllegalArgumentException(key + " cannot be negative"); } - return empty(); + return null; } static Stream flatParameters(String... arr) { //number local separator From 468310dab4b5a7148a121a44a772bf8a858da2f7 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 10 Sep 2024 22:55:30 +0200 Subject: [PATCH 245/298] edit --- .../java/org/usf/jquery/core/CaseColumn.java | 2 +- .../org/usf/jquery/web/ArgumentParsers.java | 2 +- .../jquery/web/RequestParameterResolver.java | 6 ++-- .../org/usf/jquery/web/ViewDecorator.java | 33 ++++++++----------- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 8c896c1f..33ef4e95 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -50,7 +50,7 @@ public void views(Collection views) { e.views(views); } } - + @Override public String toString() { return sql(addWithValue()); diff --git a/src/main/java/org/usf/jquery/web/ArgumentParsers.java b/src/main/java/org/usf/jquery/web/ArgumentParsers.java index 0b80f01d..6fd6d28d 100644 --- a/src/main/java/org/usf/jquery/web/ArgumentParsers.java +++ b/src/main/java/org/usf/jquery/web/ArgumentParsers.java @@ -45,7 +45,7 @@ public class ArgumentParsers { private static final JDBCType[] STD_TYPES = { BIGINT, DOUBLE, DATE, TIMESTAMP, - TIME, TIMESTAMP_WITH_TIMEZONE, VARCHAR }; //varchar !? + TIME, TIMESTAMP_WITH_TIMEZONE, VARCHAR }; public static Object parse(RequestEntryChain entry, ViewDecorator td, JavaType... types) { List list = new ArrayList<>(); diff --git a/src/main/java/org/usf/jquery/web/RequestParameterResolver.java b/src/main/java/org/usf/jquery/web/RequestParameterResolver.java index 3b110d10..6d833f61 100644 --- a/src/main/java/org/usf/jquery/web/RequestParameterResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestParameterResolver.java @@ -32,15 +32,15 @@ public QueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull Map(parameterMap); //modifiable map + preserve order - if(!parameterMap.containsKey(COLUMN_DISTINCT)) { - parameterMap.computeIfAbsent(COLUMN, k-> ant.defaultColumns()); + if(!parameterMap.containsKey(COLUMN)) { + parameterMap.computeIfAbsent(COLUMN_DISTINCT, k-> ant.defaultColumns()); } if(!isEmpty(ant.ignoreParameters())) { for(var k : ant.ignoreParameters()) { parameterMap.remove(k); } } - releaseContext(); //safe++ + releaseContext(); //safety++ var ctx = ant.database().isEmpty() ? currentContext() : context(ant.database()); diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 0d2b6382..e7c7aaa1 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -7,8 +7,6 @@ import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toUnmodifiableMap; -import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNArgs; import static org.usf.jquery.web.ColumnMetadata.columnMetadata; @@ -54,7 +52,7 @@ default ViewBuilder builder() { return this::buildView; } - default CriteriaBuilder criteria(String name) { //!aggregation + default CriteriaBuilder criteria(String name) { return null; //no criteria by default } @@ -127,18 +125,15 @@ default void parseViews(QueryBuilder query, Map parameters) { } default void parseColumns(QueryBuilder query, Map parameters) { - if(parameters.containsKey(COLUMN) && parameters.containsKey(COLUMN_DISTINCT)) { - throw new IllegalStateException("both parameters are present " + quote(COLUMN_DISTINCT) + " and " + quote(COLUMN)); - } - String[] cols; - if(parameters.containsKey(COLUMN_DISTINCT)) { - cols = parameters.remove(COLUMN_DISTINCT); - query.distinct(); - } - else { - cols = parameters.remove(COLUMN); - } - if(!isEmpty(cols)) { + if(parameters.containsKey(COLUMN) ^ parameters.containsKey(COLUMN_DISTINCT)) { + String[] cols; + if(parameters.containsKey(COLUMN)) { + cols = parameters.remove(COLUMN); + } + else { + cols = parameters.remove(COLUMN_DISTINCT); + query.distinct(); + } Stream.of(cols) .flatMap(v-> parseEntries(v).stream()) .map(e-> { @@ -150,7 +145,7 @@ default void parseColumns(QueryBuilder query, Map parameters) .forEach(query::columns); } else { - throw new IllegalArgumentException(format("requrie %s or %s parameter", COLUMN, COLUMN_DISTINCT)); + throw new IllegalArgumentException(format("requrie '%s' or '%s' parameter", COLUMN, COLUMN_DISTINCT)); } } @@ -192,12 +187,12 @@ private static Integer requirePositiveInt(String key, Map para if(v >= 0) { return v; } - throw new IllegalArgumentException(key + " cannot be negative"); + throw new IllegalArgumentException(key + " parameter cannot be negative"); } return null; } static Stream flatParameters(String... arr) { //number local separator return Stream.of(arr).flatMap(v-> Stream.of(v.split(","))); - } -} + } +} \ No newline at end of file From 092de8829ae52bdc58b3ec71bccfddfeb7caac1b Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 10 Sep 2024 23:45:56 +0200 Subject: [PATCH 246/298] edit --- src/main/java/org/usf/jquery/web/ViewDecorator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index e7c7aaa1..4ece12ec 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -145,7 +145,7 @@ default void parseColumns(QueryBuilder query, Map parameters) .forEach(query::columns); } else { - throw new IllegalArgumentException(format("requrie '%s' or '%s' parameter", COLUMN, COLUMN_DISTINCT)); + throw new IllegalArgumentException(format("require '%s' or '%s' parameter", COLUMN, COLUMN_DISTINCT)); } } From 70636d5e67ca1c2cc3b27fa40f81fe001937c94d Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 11 Sep 2024 10:20:32 +0200 Subject: [PATCH 247/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 12 ++++++ .../java/org/usf/jquery/core/Operator.java | 38 ++++++++++++++++--- .../org/usf/jquery/core/TypedOperator.java | 4 +- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 994e8e28..28932f32 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -298,6 +298,18 @@ default OperationColumn yearMonth() { return Operator.yearMonth().operation(this); } + default OperationColumn yearWeek() { + return Operator.yearWeek().operation(this); + } + + default OperationColumn monthDay() { + return Operator.monthDay().operation(this); + } + + default OperationColumn hourMinute() { + return Operator.hourMinute().operation(this); + } + //cast functions default OperationColumn varchar() { diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 5474115b..979b5159 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -213,14 +213,36 @@ static TypedOperator epoch() { //combined functions - static TypedOperator yearMonth() { + static TypedOperator yearMonth() {//YYYY-MM + CombinedOperator op = args-> left().operation(varchar().operation(requireNArgs(1, args, ()-> "yearMonth")[0]), 7); + return new TypedOperator(VARCHAR, op, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } + + static TypedOperator yearWeek() {//YYYY-'W'WW //!ISO + CombinedOperator op = args-> { + var col = requireNArgs(1, args, ()-> "yearWeek")[0]; + return concat().operation(varchar().operation(year().operation(col)), + "-W", + lpad().operation(varchar().operation(doy().operation(col)), 2, "0")); + }; + return new TypedOperator(VARCHAR, op, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } + + static TypedOperator monthDay() {//MM-DD CombinedOperator op = args-> { - var col = requireNArgs(1, args, ()-> "yearMonth")[0]; - return concat().operation( - lpad().operation(varchar().operation(year().operation(col)), 4, "0"), "-", //varchar => postgres - lpad().operation(varchar().operation(month().operation(col)), 2, "0")); + var col = requireNArgs(1, args, ()-> "monthDay")[0]; + return substring().operation(varchar().operation(col), 6, 5); }; - return new TypedOperator(VARCHAR, op, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); //!Teradata + return new TypedOperator(VARCHAR, op, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } + + static TypedOperator hourMinute() {//HH:MM + CombinedOperator op = args-> { + var col = requireNArgs(1, args, ()-> "houtMinute")[0]; + var time = JDBCType.typeOf(col).filter(t-> t == TIME).isPresent() ? col : time().operation(col); + return left().operation(varchar().operation(time), 5); + }; + return new TypedOperator(VARCHAR, op, required(TIME, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } //cast functions @@ -232,6 +254,10 @@ static TypedOperator varchar() { static TypedOperator date() { return new TypedOperator(DATE, cast("DATE"), required(VARCHAR, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } + + static TypedOperator time() { + return new TypedOperator(TIME, cast("TIME"), required(TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } static TypedOperator timestamp() { return new TypedOperator(TIMESTAMP, cast("TIMESTAMP"), required(VARCHAR, DATE)); diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index e2ca1c87..283a4606 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -45,7 +45,9 @@ public OperationColumn operation(Object... args) { @Override // do not delegate this public OperationColumn operation(JDBCType type, Object... args) { - return Operator.super.operation(type, args); + return operator.is(CombinedOperator.class) + ? operator.operation(type, args) //no sql + : Operator.super.operation(type, args); } public boolean isWindowFunction() { From 1c4864889f2dd58b40dca724925e246b15f286b2 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 11 Sep 2024 10:30:29 +0200 Subject: [PATCH 248/298] edit --- src/main/java/org/usf/jquery/core/Operator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 979b5159..dc09a182 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -238,7 +238,7 @@ static TypedOperator monthDay() {//MM-DD static TypedOperator hourMinute() {//HH:MM CombinedOperator op = args-> { - var col = requireNArgs(1, args, ()-> "houtMinute")[0]; + var col = requireNArgs(1, args, ()-> "hourMinute")[0]; var time = JDBCType.typeOf(col).filter(t-> t == TIME).isPresent() ? col : time().operation(col); return left().operation(varchar().operation(time), 5); }; From 9a6159a14e7f25a2dcde8f06efd84c108366057a Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 11 Sep 2024 12:03:00 +0200 Subject: [PATCH 249/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 28932f32..368bb0c8 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -437,7 +437,29 @@ static DBColumn column(@NonNull String value) { } static NamedColumn allColumns(@NonNull DBView view) { - return new ViewColumn("*", view, null, null) ; //TODO check this + return new DBColumn() { + + @Override + public String sql(QueryContext ctx) { + ctx.viewAlias(view); + return "*"; //lazy + } + + @Override + public JDBCType getType() { + return null; + } + + @Override + public boolean resolve(QueryBuilder builder) { + return true; //agg + } + + @Override + public void views(Collection views) { + views.add(view); + } + }.as(null); } static DBColumn constant(Object value) { From 6c985843aa24b54969ebe7c20e2febf8711a1273 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 11 Sep 2024 12:36:04 +0200 Subject: [PATCH 250/298] edit --- src/main/java/org/usf/jquery/core/CombinedOperator.java | 2 +- src/main/java/org/usf/jquery/core/DBColumn.java | 2 +- src/main/java/org/usf/jquery/core/Operator.java | 7 ++++--- src/main/java/org/usf/jquery/core/TypedOperator.java | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CombinedOperator.java b/src/main/java/org/usf/jquery/core/CombinedOperator.java index d45e243f..e5d87158 100644 --- a/src/main/java/org/usf/jquery/core/CombinedOperator.java +++ b/src/main/java/org/usf/jquery/core/CombinedOperator.java @@ -21,7 +21,7 @@ default OperationColumn operation(JDBCType type, Object... args) { @Override default String id() { - return null; + return "CombinedOperator"; //do better } @Override diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 368bb0c8..437d7dff 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -442,7 +442,7 @@ static NamedColumn allColumns(@NonNull DBView view) { @Override public String sql(QueryContext ctx) { ctx.viewAlias(view); - return "*"; //lazy + return "*"; } @Override diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index dc09a182..e3f5fdb0 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -218,7 +218,7 @@ static TypedOperator yearMonth() {//YYYY-MM return new TypedOperator(VARCHAR, op, required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } - static TypedOperator yearWeek() {//YYYY-'W'WW //!ISO + static TypedOperator yearWeek() {//YYYY-'W'WW CombinedOperator op = args-> { var col = requireNArgs(1, args, ()-> "yearWeek")[0]; return concat().operation(varchar().operation(year().operation(col)), @@ -239,8 +239,9 @@ static TypedOperator monthDay() {//MM-DD static TypedOperator hourMinute() {//HH:MM CombinedOperator op = args-> { var col = requireNArgs(1, args, ()-> "hourMinute")[0]; - var time = JDBCType.typeOf(col).filter(t-> t == TIME).isPresent() ? col : time().operation(col); - return left().operation(varchar().operation(time), 5); + var tim = JDBCType.typeOf(col).filter(t-> t == TIME) + .map(t-> col).orElseGet(()-> time().operation(col)); + return left().operation(varchar().operation(tim), 5); }; return new TypedOperator(VARCHAR, op, required(TIME, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 283a4606..dbef0298 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -46,7 +46,7 @@ public OperationColumn operation(Object... args) { @Override // do not delegate this public OperationColumn operation(JDBCType type, Object... args) { return operator.is(CombinedOperator.class) - ? operator.operation(type, args) //no sql + ? operator.operation(type, parameterSet.assertArguments(args)) //no sql : Operator.super.operation(type, args); } From 0a2a70ce08b2688312100995183168006a05b709 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 11 Sep 2024 13:08:31 +0200 Subject: [PATCH 251/298] edit --- src/main/java/org/usf/jquery/core/DBColumn.java | 2 +- .../java/org/usf/jquery/core/OperationColumn.java | 3 +-- src/main/java/org/usf/jquery/core/Operator.java | 12 ++++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 437d7dff..087850ee 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -452,7 +452,7 @@ public JDBCType getType() { @Override public boolean resolve(QueryBuilder builder) { - return true; //agg + return false; //agg } @Override diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 68888937..8e67c437 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -61,8 +61,7 @@ else if(operator.is("OVER")) { } return requirePartition().resolve(builder); //no aggregation } - return operator.is(ConstantOperator.class) - || tryResolveAll(builder, args); + return !operator.is(ConstantOperator.class) && tryResolveAll(builder, args); } @Override diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index e3f5fdb0..659ae787 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -166,6 +166,10 @@ static TypedOperator rpad() { return new TypedOperator(VARCHAR, function("RPAD"), required(BIGINT, VARCHAR), required(INTEGER), required(VARCHAR)); } + static TypedOperator age() { //td interval type + return new TypedOperator(VARCHAR, function("AGE"), required(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE), optional(DATE, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + } + //temporal functions static TypedOperator year() { @@ -252,6 +256,10 @@ static TypedOperator varchar() { return new TypedOperator(VARCHAR, cast("VARCHAR"), required(), optional(INTEGER)); //any } + static TypedOperator timestamp() { + return new TypedOperator(TIMESTAMP, cast("TIMESTAMP"), required(VARCHAR, DATE)); + } + static TypedOperator date() { return new TypedOperator(DATE, cast("DATE"), required(VARCHAR, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } @@ -260,10 +268,6 @@ static TypedOperator time() { return new TypedOperator(TIME, cast("TIME"), required(TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } - static TypedOperator timestamp() { - return new TypedOperator(TIMESTAMP, cast("TIMESTAMP"), required(VARCHAR, DATE)); - } - static TypedOperator integer() { return new TypedOperator(INTEGER, cast("INTEGER"), required(VARCHAR, DOUBLE)); } From a63ea67c700aa06e946bd9302ec521bd4307d014 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 11 Sep 2024 15:13:27 +0200 Subject: [PATCH 252/298] edit --- src/main/java/org/usf/jquery/core/Operator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 659ae787..89b2701a 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -265,7 +265,7 @@ static TypedOperator date() { } static TypedOperator time() { - return new TypedOperator(TIME, cast("TIME"), required(TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); + return new TypedOperator(TIME, cast("TIME"), required(VARCHAR, TIMESTAMP, TIMESTAMP_WITH_TIMEZONE)); } static TypedOperator integer() { From 36fa42e5c999a16ed196201e32b241d8aedbcc11 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 11 Sep 2024 21:17:55 +0200 Subject: [PATCH 253/298] edit --- .../java/org/usf/jquery/core/DBColumn.java | 55 +++---------------- .../java/org/usf/jquery/core/ValueColumn.java | 40 ++++++++++++++ .../java/org/usf/jquery/core/ViewColumn.java | 6 +- .../org/usf/jquery/web/ViewDecorator.java | 8 +-- 4 files changed, 55 insertions(+), 54 deletions(-) create mode 100644 src/main/java/org/usf/jquery/core/ValueColumn.java diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 087850ee..e73dbc87 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -3,12 +3,10 @@ import static java.util.Objects.nonNull; import static org.usf.jquery.core.OrderType.ASC; import static org.usf.jquery.core.OrderType.DESC; -import static org.usf.jquery.core.QueryContext.formatValue; import static org.usf.jquery.core.Utils.appendFirst; import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; -import java.util.Collection; import java.util.function.Supplier; import org.usf.jquery.core.JavaType.Typed; @@ -432,62 +430,25 @@ static OperationColumn denseRank() { return Operator.denseRank().operation(); } - static DBColumn column(@NonNull String value) { + static ViewColumn column(@NonNull String value) { return new ViewColumn(requireLegalVariable(value), null, null, value); } - static NamedColumn allColumns(@NonNull DBView view) { - return new DBColumn() { - + static ViewColumn allColumns(@NonNull DBView view) { + return new ViewColumn("*", view, null, null) { //no type, no tag @Override public String sql(QueryContext ctx) { ctx.viewAlias(view); - return "*"; + return getName(); //avoid view.* } - - @Override - public JDBCType getType() { - return null; - } - - @Override - public boolean resolve(QueryBuilder builder) { - return false; //agg - } - - @Override - public void views(Collection views) { - views.add(view); - } - }.as(null); + }; } - static DBColumn constant(Object value) { + static ValueColumn constant(Object value) { return constant(JDBCType.typeOf(value).orElse(null), ()-> value); } - static DBColumn constant(JDBCType type, Supplier value) { - return new DBColumn() { - - @Override - public String sql(QueryContext ctx) { - return formatValue(value.get()); //lazy - } - - @Override - public JDBCType getType() { - return type; - } - - @Override - public boolean resolve(QueryBuilder builder) { - return false; - } - - @Override - public void views(Collection views) { - //no view - } - }; + static ValueColumn constant(JDBCType type, Supplier supp) { + return new ValueColumn(type, supp); } } diff --git a/src/main/java/org/usf/jquery/core/ValueColumn.java b/src/main/java/org/usf/jquery/core/ValueColumn.java new file mode 100644 index 00000000..8e2a76a6 --- /dev/null +++ b/src/main/java/org/usf/jquery/core/ValueColumn.java @@ -0,0 +1,40 @@ +package org.usf.jquery.core; + +import static org.usf.jquery.core.QueryContext.formatValue; + +import java.util.Collection; +import java.util.function.Supplier; + +import lombok.RequiredArgsConstructor; + +/** + * + * @author u$f + * + */ +@RequiredArgsConstructor +public class ValueColumn implements DBColumn { + + private final JDBCType type; + private final Supplier supp; + + @Override + public String sql(QueryContext ctx) { + return formatValue(supp.get()); + } + + @Override + public JDBCType getType() { + return type; + } + + @Override + public boolean resolve(QueryBuilder builder) { + return false; + } + + @Override + public void views(Collection views) { + //do nothing + } +} diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index 45fc0722..0d9dbe36 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -16,7 +16,7 @@ */ @Getter @RequiredArgsConstructor -public final class ViewColumn implements NamedColumn { +public class ViewColumn implements NamedColumn { private final String name; private final DBView view; //optional @@ -33,7 +33,9 @@ public boolean resolve(QueryBuilder builder) { } public void views(Collection views) { - views.add(view); + if(nonNull(view)) { + views.add(view); + } } @Override diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 4ece12ec..aef56d3e 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -3,6 +3,7 @@ import static java.lang.Integer.parseInt; import static java.lang.String.format; import static java.util.Map.entry; +import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; @@ -126,11 +127,8 @@ default void parseViews(QueryBuilder query, Map parameters) { default void parseColumns(QueryBuilder query, Map parameters) { if(parameters.containsKey(COLUMN) ^ parameters.containsKey(COLUMN_DISTINCT)) { - String[] cols; - if(parameters.containsKey(COLUMN)) { - cols = parameters.remove(COLUMN); - } - else { + String[] cols = parameters.remove(COLUMN); + if(isNull(cols)) { cols = parameters.remove(COLUMN_DISTINCT); query.distinct(); } From b8788e8dea900929d7a7ad8ecee7c0268e2374b5 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 11 Sep 2024 21:47:19 +0200 Subject: [PATCH 254/298] edit --- src/main/java/org/usf/jquery/core/DBOrder.java | 14 +++++++++++++- src/main/java/org/usf/jquery/core/Nested.java | 9 --------- .../org/usf/jquery/core/OperationColumn.java | 16 ++++++---------- src/main/java/org/usf/jquery/core/Partition.java | 14 ++++++++++---- .../java/org/usf/jquery/core/QueryBuilder.java | 6 ++---- 5 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index 765dc008..7fcf1145 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -5,6 +5,8 @@ import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; +import java.util.Collection; + import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -16,7 +18,7 @@ */ @Getter(AccessLevel.PACKAGE) @RequiredArgsConstructor -public final class DBOrder implements DBObject { +public final class DBOrder implements DBObject, Nested { private final DBColumn column; private final OrderType order; @@ -37,6 +39,16 @@ public String sql(QueryContext ctx) { : column.sql(ctx) + SPACE + order.name(); } + @Override + public boolean resolve(QueryBuilder builder) { + return column.resolve(builder); + } + + @Override + public void views(Collection views) { + column.views(views); + } + @Override public String toString() { return sql(addWithValue()); diff --git a/src/main/java/org/usf/jquery/core/Nested.java b/src/main/java/org/usf/jquery/core/Nested.java index 9f5ec58e..6dee4754 100644 --- a/src/main/java/org/usf/jquery/core/Nested.java +++ b/src/main/java/org/usf/jquery/core/Nested.java @@ -4,7 +4,6 @@ import java.util.Collection; import java.util.function.Consumer; -import java.util.function.Function; import java.util.function.Predicate; /** @@ -22,10 +21,6 @@ static boolean tryResolveAll(QueryBuilder builder, Object... args){ return resolveAll(args, o-> tryResolve(o, builder)); } - static boolean resolveAll(T[] arr, Function fn, QueryBuilder builder){ - return resolveAll(arr, o-> fn.apply(o).resolve(builder)); - } - static boolean resolveAll(Nested[] arr, QueryBuilder builder){ return resolveAll(arr, n-> n.resolve(builder)); } @@ -49,10 +44,6 @@ static void viewsOfNested(Collection views, T[] arr) viewsOfAll(arr, o-> o.views(views)); } - static void viewsOfNested(Collection views, T[] arr, Function fn) { - viewsOfAll(arr, o-> fn.apply(o).views(views)); - } - static void viewsOfAll(Collection views, Object[] arr) { viewsOfAll(arr, o-> viewsOf(views, o)); } diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 8e67c437..9a138c8b 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -26,10 +26,6 @@ public final class OperationColumn implements DBColumn { private final JDBCType type; //optional private DBColumn overColumn; - public OperationColumn(Operator operation, Object[] args) { - this(operation, args, null); - } - @Override public String sql(QueryContext ctx) { return nonNull(overColumn) ? overColumn.sql(ctx) : operator.sql(ctx, args); @@ -41,27 +37,27 @@ public JDBCType getType() { } @Override - public boolean resolve(QueryBuilder builder) { + public boolean resolve(QueryBuilder ctx) { if(operator.is(AggregateFunction.class)) { - builder.aggregation(); + ctx.aggregation(); return true; } else if(operator.is("OVER")) { - if(builder.getClause() == FILTER) { + if(ctx.getClause() == FILTER) { var views = new HashSet(); views(views); if(views.size() == 1) { var view = views.iterator().next(); var cTag = "over_" + hashCode(); //over_view_hash - builder.overView(view).getBuilder().columns(new OperationColumn(operator, args, type).as(cTag)); //clone + ctx.overView(view).getBuilder().columns(new OperationColumn(operator, args, type).as(cTag)); //clone overColumn = new ViewColumn(cTag, view, type, null); return false; } throw new UnsupportedOperationException("require only one view"); } - return requirePartition().resolve(builder); //no aggregation + return requirePartition().resolve(ctx); //no aggregation } - return !operator.is(ConstantOperator.class) && tryResolveAll(builder, args); + return !operator.is(ConstantOperator.class) && tryResolveAll(ctx, args); } @Override diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index af7c9e40..4cd431cb 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -2,6 +2,7 @@ import static org.usf.jquery.core.Nested.resolveAll; import static org.usf.jquery.core.Nested.viewsOfNested; +import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -18,8 +19,8 @@ @RequiredArgsConstructor public final class Partition implements DBObject, Nested { - private final DBColumn[] columns; - private final DBOrder[] orders; + private final DBColumn[] columns;//optional + private final DBOrder[] orders; //optional @Override public String sql(QueryContext ctx, Object[] args) { @@ -42,13 +43,18 @@ String sql(QueryContext ctx) { @Override public boolean resolve(QueryBuilder builder) { var r1 = resolveAll(columns, builder); - var r2 = resolveAll(orders, DBOrder::getColumn, builder); + var r2 = resolveAll(orders, builder); return r1 || r2; } @Override public void views(Collection views) { viewsOfNested(views, columns); - viewsOfNested(views, orders, DBOrder::getColumn); + viewsOfNested(views, orders); + } + + @Override + public String toString() { + return sql(addWithValue()); } } diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 5d8ed59f..6a1b5f41 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -97,10 +97,8 @@ public QueryBuilder filters(@NonNull DBFilter... filters){ public QueryBuilder orders(@NonNull DBOrder... orders) { this.clause = ORDER; for(var o : orders) { - this.orders.add(o); - var c = o.getColumn(); - if(!c.resolve(this)) { - this.group.add(c); + if(this.orders.add(o) && !o.resolve(this)) { + this.group.add(o.getColumn()); } } return this; From b9bc15813548aa8ab0c49b628d24efc9525e3799 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 11 Sep 2024 21:55:02 +0200 Subject: [PATCH 255/298] edit --- src/main/java/org/usf/jquery/core/ViewJoin.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index f5171883..14684f8a 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -51,10 +51,15 @@ public String sql(QueryContext ctx) { s += " ON " + Stream.of(filters) .map(f-> f.sql(val)) .collect(joining(AND.sql())); - } + } //else cross join return s; } + @Override + public String toString() { + return sql(addWithValue()); + } + public static ViewJoin innerJoin(DBView view, DBFilter... filters) { return new ViewJoin(INNER, view, filters); } @@ -74,9 +79,4 @@ public static ViewJoin fullJoin(DBView view, DBFilter... filters) { public static ViewJoin crossJoin(DBView view, DBFilter... filters) { return new ViewJoin(CROSS, view, filters); } - - @Override - public String toString() { - return sql(addWithValue()); - } } From 972059ca1bd12afc3e0322962231262bca6ab4e9 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 11 Sep 2024 22:03:25 +0200 Subject: [PATCH 256/298] edit --- src/main/java/org/usf/jquery/core/ViewJoin.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index 14684f8a..eb5d39cd 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -30,7 +30,6 @@ public final class ViewJoin implements DBObject { //join results !? public ViewJoin(JoinType joinType, DBView view, DBFilter[] filters) { - super(); this.joinType = joinType; this.view = view; this.filters = joinType == CROSS @@ -39,15 +38,15 @@ public ViewJoin(JoinType joinType, DBView view, DBFilter[] filters) { } @Override - public String sql(QueryContext qv, Object[] args) { + public String sql(QueryContext ctx, Object[] args) { requireNoArgs(args, ViewJoin.class::getSimpleName); - return sql(qv); + return sql(ctx); } public String sql(QueryContext ctx) { var s = joinType + " JOIN " + view.sqlWithTag(ctx); if(!isEmpty(filters)) { - var val = ctx.withValue(); + var val = ctx.withValue(); //literal filter s += " ON " + Stream.of(filters) .map(f-> f.sql(val)) .collect(joining(AND.sql())); From 569000b7df8e38fe386a00630c51179c6b8840ab Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 12 Sep 2024 01:15:58 +0200 Subject: [PATCH 257/298] edit --- .../usf/jquery/core/ArithmeticOperator.java | 6 +- .../org/usf/jquery/core/BasicComparator.java | 4 +- .../java/org/usf/jquery/core/CaseColumn.java | 10 +-- .../org/usf/jquery/core/CastFunction.java | 17 ++-- .../usf/jquery/core/ColumnFilterGroup.java | 12 +-- .../java/org/usf/jquery/core/ColumnProxy.java | 3 +- .../usf/jquery/core/ColumnSingleFilter.java | 6 +- .../org/usf/jquery/core/CombinedOperator.java | 2 +- .../usf/jquery/core/ComparisonExpression.java | 6 +- .../core/ComparisonExpressionGroup.java | 12 +-- .../core/ComparisonSingleExpression.java | 7 +- .../org/usf/jquery/core/ConstantOperator.java | 4 +- .../java/org/usf/jquery/core/DBColumn.java | 10 +-- .../java/org/usf/jquery/core/DBFilter.java | 6 +- .../java/org/usf/jquery/core/DBObject.java | 10 ++- .../java/org/usf/jquery/core/DBOrder.java | 16 ++-- src/main/java/org/usf/jquery/core/DBView.java | 14 ++-- .../org/usf/jquery/core/ExtractFunction.java | 5 +- .../org/usf/jquery/core/FunctionOperator.java | 6 +- .../org/usf/jquery/core/InComparator.java | 7 +- .../java/org/usf/jquery/core/NamedColumn.java | 9 +-- .../org/usf/jquery/core/NullComparator.java | 5 +- .../org/usf/jquery/core/OperationColumn.java | 12 ++- .../java/org/usf/jquery/core/Partition.java | 11 +-- .../org/usf/jquery/core/PipeFunction.java | 7 +- .../org/usf/jquery/core/QueryBuilder.java | 27 ++++--- .../java/org/usf/jquery/core/QueryView.java | 12 ++- .../org/usf/jquery/core/RangeComparator.java | 10 +-- .../usf/jquery/core/SingleColumnQuery.java | 11 ++- .../org/usf/jquery/core/SqlStringBuilder.java | 79 ++++++++++++++----- .../org/usf/jquery/core/StringComparator.java | 6 +- .../java/org/usf/jquery/core/TableView.java | 7 +- .../org/usf/jquery/core/TypedComparator.java | 4 +- .../org/usf/jquery/core/TypedOperator.java | 4 +- .../java/org/usf/jquery/core/ValueColumn.java | 9 ++- .../java/org/usf/jquery/core/ViewColumn.java | 8 +- .../java/org/usf/jquery/core/ViewJoin.java | 22 ++---- .../java/org/usf/jquery/core/WhenCase.java | 25 +++--- 38 files changed, 226 insertions(+), 205 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java index b872466a..d4da3fe3 100644 --- a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java +++ b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java @@ -11,8 +11,10 @@ interface ArithmeticOperator extends Operator { @Override - default String sql(QueryContext ctx, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNArgs(2, args, ArithmeticException.class::getSimpleName); - return "(" + ctx.appendLiteral(args[0]) + id() + ctx.appendLiteral(args[1]) + ")"; + sb.openParenthesis() + .append(ctx.appendLiteral(args[0])).append(id()).append(ctx.appendLiteral(args[1])) + .closeParenthesis(); } } diff --git a/src/main/java/org/usf/jquery/core/BasicComparator.java b/src/main/java/org/usf/jquery/core/BasicComparator.java index b9b480a4..899eeeb7 100644 --- a/src/main/java/org/usf/jquery/core/BasicComparator.java +++ b/src/main/java/org/usf/jquery/core/BasicComparator.java @@ -11,8 +11,8 @@ public interface BasicComparator extends Comparator { @Override - default String sql(QueryContext ctx, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNArgs(2, args, BasicComparator.class::getSimpleName); - return ctx.appendParameter(args[0]) + id() + ctx.appendParameter(args[1]); + sb.append(ctx.appendParameter(args[0])).append(id()).append(ctx.appendParameter(args[1])); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 33ef4e95..c850d2a3 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -1,8 +1,6 @@ package org.usf.jquery.core; -import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Nested.resolveAll; -import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; @@ -24,11 +22,9 @@ public final class CaseColumn implements DBColumn { } @Override - public String sql(QueryContext ctx) { + public void sql(SqlStringBuilder sb, QueryContext ctx) { var sub = ctx.withValue(); //force literal parameter - return Stream.of(whenCases) - .map(o-> o.sql(sub)) - .collect(joining(SPACE, "CASE ", " END")); + sb.appendEach(whenCases, SPACE, o-> o.sql(sb, sub), "CASE ", " END"); } @Override @@ -53,6 +49,6 @@ public void views(Collection views) { @Override public String toString() { - return sql(addWithValue()); + return DBObject.toSQL(this); } } diff --git a/src/main/java/org/usf/jquery/core/CastFunction.java b/src/main/java/org/usf/jquery/core/CastFunction.java index f8d3b34e..66de14b8 100644 --- a/src/main/java/org/usf/jquery/core/CastFunction.java +++ b/src/main/java/org/usf/jquery/core/CastFunction.java @@ -18,16 +18,13 @@ default String id() { } @Override - default String sql(QueryContext builder, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireAtLeastNArgs(1, args, CastFunction.class::getSimpleName); - var sb = new SqlStringBuilder(id()) - .append("(") - .append(builder.appendLiteral(args[0])).append(" AS ").append(asType()); - if(args.length > 1) { - sb.append("(") - .append(builder.appendLiteralArray(args, 1)) - .append(")"); - } - return sb.append(")").toString(); + sb.function(id(), ()->{ + sb.append(ctx.appendLiteral(args[0])).as(asType()); + if(args.length > 1) { + sb.parenthesis(()-> ctx.appendLiteralArray(args, 1)); + } + }); } } diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index 24d306f6..5e7ae89e 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -1,14 +1,11 @@ package org.usf.jquery.core; -import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Nested.resolveAll; import static org.usf.jquery.core.Nested.viewsOfNested; -import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.Utils.appendLast; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; import java.util.Collection; -import java.util.stream.Stream; /** * @@ -27,10 +24,9 @@ public final class ColumnFilterGroup implements DBFilter { } @Override - public String sql(QueryContext ctx) { - return Stream.of(filters) - .map(o-> o.sql(ctx)) - .collect(joining(operator.sql(), "(", ")")); + public void sql(SqlStringBuilder sb, QueryContext ctx) { + sb.parenthesis(()-> + sb.appendEach(filters, operator.sql(), o-> o.sql(sb, ctx))); } @Override @@ -52,6 +48,6 @@ public DBFilter append(LogicalOperator op, DBFilter filter) { @Override public String toString() { - return sql(addWithValue()); + return DBObject.toSQL(this); } } diff --git a/src/main/java/org/usf/jquery/core/ColumnProxy.java b/src/main/java/org/usf/jquery/core/ColumnProxy.java index 87683789..4e358efd 100644 --- a/src/main/java/org/usf/jquery/core/ColumnProxy.java +++ b/src/main/java/org/usf/jquery/core/ColumnProxy.java @@ -1,7 +1,6 @@ package org.usf.jquery.core; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.QueryContext.addWithValue; import java.util.Objects; @@ -46,6 +45,6 @@ public ColumnProxy as(String name, JDBCType type) { // map @Override public String toString() { - return this.sql(addWithValue()); + return DBObject.toSQL(this); } } diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index 0613d05a..c120bfbc 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -20,8 +20,8 @@ public class ColumnSingleFilter implements DBFilter { private final ComparisonExpression expression; @Override - public String sql(QueryContext ctx) { - return expression.sql(ctx, left); + public void sql(SqlStringBuilder sb, QueryContext ctx) { + expression.sql(sb, ctx, left); } @Override @@ -44,6 +44,6 @@ public ColumnFilterGroup append(LogicalOperator op, DBFilter filter) { @Override public String toString() { - return sql(addWithValue()); + return DBObject.toSQL(this); } } diff --git a/src/main/java/org/usf/jquery/core/CombinedOperator.java b/src/main/java/org/usf/jquery/core/CombinedOperator.java index e5d87158..8f4c96ec 100644 --- a/src/main/java/org/usf/jquery/core/CombinedOperator.java +++ b/src/main/java/org/usf/jquery/core/CombinedOperator.java @@ -25,7 +25,7 @@ default String id() { } @Override - default String sql(QueryContext ctx, Object[] args) { //no SQL + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { //no SQL throw new UnsupportedOperationException("CombinedOperator::sql"); } } diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpression.java b/src/main/java/org/usf/jquery/core/ComparisonExpression.java index 26ec15a0..612e3bcf 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpression.java @@ -11,12 +11,12 @@ */ public interface ComparisonExpression extends DBObject, Nested, Chainable { - String sql(QueryContext ctx, Object left); // do change method order + void sql(SqlStringBuilder sb, QueryContext ctx, Object left); // do change method order @Override - default String sql(QueryContext ctx, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNArgs(1, args, ComparisonExpression.class::getSimpleName); - return sql(ctx, args[0]); + sql(sb, ctx, args[0]); } static ComparisonExpression eq(Object right) { diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index 572bf315..ea4773a2 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -1,14 +1,11 @@ package org.usf.jquery.core; -import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Nested.resolveAll; import static org.usf.jquery.core.Nested.viewsOfNested; -import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.Utils.appendLast; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; import java.util.Collection; -import java.util.stream.Stream; /** * @@ -27,10 +24,9 @@ public final class ComparisonExpressionGroup implements ComparisonExpression { } @Override - public String sql(QueryContext ctx, Object operand) { - return Stream.of(expressions) - .map(o-> o.sql(ctx, operand)) - .collect(joining(operator.sql(), "(", ")")); + public void sql(SqlStringBuilder sb, QueryContext ctx, Object operand) { + sb.parenthesis(()-> + sb.appendEach(expressions, operator.sql(), o-> o.sql(sb, ctx, operand))); } @Override @@ -52,6 +48,6 @@ public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) @Override public String toString() { - return sql(addWithValue(), ""); + return DBObject.toSQL(this, ""); } } diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index aee6b160..fcb05d14 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -4,7 +4,6 @@ import static java.util.Objects.nonNull; import static org.usf.jquery.core.Nested.tryResolveAll; import static org.usf.jquery.core.Nested.viewsOfAll; -import static org.usf.jquery.core.QueryContext.addWithValue; import java.util.ArrayList; import java.util.Collection; @@ -24,13 +23,13 @@ public final class ComparisonSingleExpression implements ComparisonExpression { private final Object[] right; //optional @Override - public String sql(QueryContext ctx, Object left) { + public void sql(SqlStringBuilder sb, QueryContext ctx, Object left) { var param = new ArrayList<>(); param.add(left); if(nonNull(right)) { addAll(param, right); } - return comparator.sql(ctx, param.toArray()); + comparator.sql(sb, ctx, param.toArray()); } @Override @@ -50,6 +49,6 @@ public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) @Override public String toString() { - return sql(addWithValue(), ""); + return DBObject.toSQL(this); } } diff --git a/src/main/java/org/usf/jquery/core/ConstantOperator.java b/src/main/java/org/usf/jquery/core/ConstantOperator.java index 6792fe48..7c04312f 100644 --- a/src/main/java/org/usf/jquery/core/ConstantOperator.java +++ b/src/main/java/org/usf/jquery/core/ConstantOperator.java @@ -11,8 +11,8 @@ public interface ConstantOperator extends Operator { @Override - default String sql(QueryContext ctx, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNoArgs(args, ConstantOperator.class::getSimpleName); - return id(); //use parentheses !? + sb.append(id()); //use parentheses !? } } diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index e73dbc87..eff6102f 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -20,12 +20,12 @@ */ public interface DBColumn extends DBObject, Typed, Nested { - String sql(QueryContext ctx); + void sql(SqlStringBuilder sb, QueryContext ctx); @Override - default String sql(QueryContext ctx, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNoArgs(args, DBColumn.class::getSimpleName); - return sql(ctx); + sql(sb, ctx); } default ColumnProxy as(String name) { @@ -437,9 +437,9 @@ static ViewColumn column(@NonNull String value) { static ViewColumn allColumns(@NonNull DBView view) { return new ViewColumn("*", view, null, null) { //no type, no tag @Override - public String sql(QueryContext ctx) { + public void sql(SqlStringBuilder sb, QueryContext ctx) { ctx.viewAlias(view); - return getName(); //avoid view.* + sb.append(getName()); //avoid view.* } }; } diff --git a/src/main/java/org/usf/jquery/core/DBFilter.java b/src/main/java/org/usf/jquery/core/DBFilter.java index ed17802c..a976a3ea 100644 --- a/src/main/java/org/usf/jquery/core/DBFilter.java +++ b/src/main/java/org/usf/jquery/core/DBFilter.java @@ -9,11 +9,11 @@ */ public interface DBFilter extends DBObject, Nested, Chainable { - String sql(QueryContext ctx); + void sql(SqlStringBuilder sb, QueryContext ctx); @Override - default String sql(QueryContext ctx, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNoArgs(args, DBFilter.class::getSimpleName); - return sql(ctx); + sql(sb, ctx); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/DBObject.java b/src/main/java/org/usf/jquery/core/DBObject.java index ed21fc39..d46da8b8 100644 --- a/src/main/java/org/usf/jquery/core/DBObject.java +++ b/src/main/java/org/usf/jquery/core/DBObject.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.QueryContext.addWithValue; + /** * * @author u$f @@ -8,5 +10,11 @@ @FunctionalInterface public interface DBObject { - String sql(QueryContext ctx, Object[] args); + void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args); + + static String toSQL(DBObject obj, Object... values) { + var sb = new SqlStringBuilder(); + obj.sql(sb, addWithValue(), values); + return sb.toString(); + } } diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index 7fcf1145..d5f35c15 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -1,7 +1,6 @@ package org.usf.jquery.core; -import static java.util.Objects.isNull; -import static org.usf.jquery.core.QueryContext.addWithValue; +import static java.util.Objects.nonNull; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -28,15 +27,14 @@ public DBOrder(DBColumn column) { } @Override - public String sql(QueryContext ctx, Object[] args) { + public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNoArgs(args, DBOrder.class::getSimpleName); - return sql(ctx); + sql(sb, ctx); } - public String sql(QueryContext ctx) { - return isNull(order) - ? column.sql(ctx) - : column.sql(ctx) + SPACE + order.name(); + public void sql(SqlStringBuilder sb, QueryContext ctx) { + column.sql(sb, ctx); + sb.appendIf(nonNull(order), ()-> SPACE + order.name()); } @Override @@ -51,6 +49,6 @@ public void views(Collection views) { @Override public String toString() { - return sql(addWithValue()); + return DBObject.toSQL(this); } } diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 757ed653..de793fe4 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; /** @@ -11,16 +10,17 @@ @FunctionalInterface public interface DBView extends DBObject { - String sql(QueryContext ctx); + void sql(SqlStringBuilder sb, QueryContext ctx); @Override - default String sql(QueryContext ctx, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNoArgs(args, DBView.class::getSimpleName); - return sql(ctx); + sql(sb, ctx); } - default String sqlWithTag(QueryContext ctx) { - return ctx.viewOverload(this).orElse(this).sql(ctx) //!important - + SPACE + ctx.viewAlias(this); + default void sqlWithTag(SqlStringBuilder sb, QueryContext ctx) { + ctx.viewOverload(this).orElse(this).sql(sb, ctx); //!important + sb.space(); + ctx.viewAlias(this); } } diff --git a/src/main/java/org/usf/jquery/core/ExtractFunction.java b/src/main/java/org/usf/jquery/core/ExtractFunction.java index 30c90cc7..d0970e9b 100644 --- a/src/main/java/org/usf/jquery/core/ExtractFunction.java +++ b/src/main/java/org/usf/jquery/core/ExtractFunction.java @@ -18,8 +18,9 @@ default String id() { } @Override - default String sql(QueryContext builder, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext builder, Object[] args) { requireNArgs(1, args, ExtractFunction.class::getSimpleName); - return id() + "(" + field() + " FROM " + builder.appendLiteral(args[0]) + ")"; + sb.function(id(), ()-> + sb.append(field()).from(builder.appendLiteral(args[0]))); } } diff --git a/src/main/java/org/usf/jquery/core/FunctionOperator.java b/src/main/java/org/usf/jquery/core/FunctionOperator.java index d9a3aaf9..48df5701 100644 --- a/src/main/java/org/usf/jquery/core/FunctionOperator.java +++ b/src/main/java/org/usf/jquery/core/FunctionOperator.java @@ -10,9 +10,7 @@ public interface FunctionOperator extends Operator { @Override - default String sql(QueryContext ctx, Object[] args) { - return new SqlStringBuilder(id()) - .append("(").append(ctx.appendLiteralArray(args)).append(")") //accept any - .toString(); + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { + sb.function(id(), ()-> ctx.appendLiteralArray(args)); } } diff --git a/src/main/java/org/usf/jquery/core/InComparator.java b/src/main/java/org/usf/jquery/core/InComparator.java index 466e3360..d7684c9e 100644 --- a/src/main/java/org/usf/jquery/core/InComparator.java +++ b/src/main/java/org/usf/jquery/core/InComparator.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.SqlStringBuilder.parenthese; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; @@ -13,8 +12,10 @@ public interface InComparator extends Comparator { @Override - default String sql(QueryContext ctx, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireAtLeastNArgs(2, args, InComparator.class::getSimpleName); - return ctx.appendParameter(args[0]) + SPACE + id() + parenthese(ctx.appendArrayParameter(args, 1)); + sb.append(ctx.appendParameter(args[0])) + .space().append(id()) + .append(parenthese(ctx.appendArrayParameter(args, 1))); } } diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index 480c5134..00b7b8f9 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -12,11 +12,8 @@ public interface NamedColumn extends DBColumn { String getTag(); - default String sqlWithTag(QueryContext ctx) { - var s = sql(ctx); - if(nonNull(getTag())) { - s += " AS " + doubleQuote(getTag()); - } - return s; + default void sqlWithTag(SqlStringBuilder sb, QueryContext ctx) { + sql(sb, ctx); + sb.appendIf(nonNull(getTag()), ()-> " AS " + doubleQuote(getTag())); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/NullComparator.java b/src/main/java/org/usf/jquery/core/NullComparator.java index ee5025d8..b1c285de 100644 --- a/src/main/java/org/usf/jquery/core/NullComparator.java +++ b/src/main/java/org/usf/jquery/core/NullComparator.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNArgs; /** @@ -12,8 +11,8 @@ public interface NullComparator extends Comparator { @Override - default String sql(QueryContext ctx, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNArgs(1, args, NullComparator.class::getSimpleName); - return ctx.appendParameter(args[0]) + SPACE + id(); + sb.append(ctx.appendParameter(args[0])).space().append(id()); } } diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 9a138c8b..26f82c7e 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -4,7 +4,6 @@ import static org.usf.jquery.core.Clause.FILTER; import static org.usf.jquery.core.Nested.tryResolveAll; import static org.usf.jquery.core.Nested.viewsOfAll; -import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.Validation.requireNArgs; import java.util.Collection; @@ -27,8 +26,13 @@ public final class OperationColumn implements DBColumn { private DBColumn overColumn; @Override - public String sql(QueryContext ctx) { - return nonNull(overColumn) ? overColumn.sql(ctx) : operator.sql(ctx, args); + public void sql(SqlStringBuilder sb, QueryContext ctx) { + if(nonNull(overColumn)) { + overColumn.sql(sb, ctx); + } + else { + operator.sql(sb, ctx, args); + } } @Override @@ -72,7 +76,7 @@ public void views(Collection views) { @Override public String toString() { - return sql(addWithValue()); + return DBObject.toSQL(this); } private Partition requirePartition() { diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index 4cd431cb..be551f9a 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -2,7 +2,6 @@ import static org.usf.jquery.core.Nested.resolveAll; import static org.usf.jquery.core.Nested.viewsOfNested; -import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireNoArgs; @@ -23,13 +22,12 @@ public final class Partition implements DBObject, Nested { private final DBOrder[] orders; //optional @Override - public String sql(QueryContext ctx, Object[] args) { + public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNoArgs(args, Partition.class::getSimpleName); - return sql(ctx); + sql(sb, ctx); } - String sql(QueryContext ctx) { - var sb = new SqlStringBuilder(100); + void sql(SqlStringBuilder sb, QueryContext ctx) { if(!isEmpty(columns)) { sb.append("PARTITION BY ").append(ctx.appendLiteralArray(columns)); } @@ -37,7 +35,6 @@ String sql(QueryContext ctx) { sb.appendIf(!isEmpty(columns), SPACE) .append("ORDER BY ").append(ctx.appendLiteralArray(orders)); } - return sb.toString(); } @Override @@ -55,6 +52,6 @@ public void views(Collection views) { @Override public String toString() { - return sql(addWithValue()); + return DBObject.toSQL(this); } } diff --git a/src/main/java/org/usf/jquery/core/PipeFunction.java b/src/main/java/org/usf/jquery/core/PipeFunction.java index e57cb9a8..fb5c4e36 100644 --- a/src/main/java/org/usf/jquery/core/PipeFunction.java +++ b/src/main/java/org/usf/jquery/core/PipeFunction.java @@ -1,7 +1,6 @@ package org.usf.jquery.core; import static java.util.Arrays.copyOfRange; -import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; /** @@ -13,9 +12,9 @@ public interface PipeFunction extends FunctionOperator { @Override - default String sql(QueryContext builder, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireAtLeastNArgs(1, args, PipeFunction.class::getSimpleName); - return builder.appendLiteral(args[0]) + SPACE - + FunctionOperator.super.sql(builder, copyOfRange(args, 1, args.length)); + sb.append(ctx.appendLiteral(args[0])).space(); + FunctionOperator.super.sql(sb, ctx, copyOfRange(args, 1, args.length)); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 6a1b5f41..03e67e65 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -5,7 +5,6 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.function.Predicate.not; -import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.Clause.COLUMN; import static org.usf.jquery.core.Clause.FILTER; import static org.usf.jquery.core.Clause.ORDER; @@ -183,50 +182,54 @@ void select(SqlStringBuilder sb, QueryContext ctx){ sb.append("SELECT") .appendIf(distinct, " DISTINCT") .appendIf(nonNull(limit) && currentDatabase() == TERADATA, ()-> " TOP " + limit) //??????? - .append(SPACE) - .appendEach(columns, SCOMA, o-> o.sqlWithTag(ctx)); + .space() + .forEach(columns.iterator(), SCOMA, o-> o.sqlWithTag(sb, ctx)); } void from(SqlStringBuilder sb, QueryContext ctx) { var excludes = joins.stream().map(ViewJoin::getView).toList(); var views = ctx.views().stream().filter(not(excludes::contains)).toList(); //do not remove views if(!views.isEmpty()) { - sb.append(" FROM ").appendEach(views, SCOMA, v-> v.sqlWithTag(ctx)); + sb.from().forEach(views.iterator(), SCOMA, v-> v.sqlWithTag(sb, ctx)); } } void join(SqlStringBuilder sb, QueryContext ctx) { if(!joins.isEmpty()) { - sb.append(SPACE).appendEach(joins, SPACE, v-> v.sql(ctx)); + sb.space().forEach(joins.iterator(), SPACE, v-> v.sql(sb, ctx)); } } void where(SqlStringBuilder sb, QueryContext ctx){ if(!where.isEmpty()) { - sb.append(" WHERE ").appendEach(where, AND.sql(), f-> f.sql(ctx)); + sb.append(" WHERE ").forEach(where.iterator(), AND.sql(), f-> f.sql(sb, ctx)); } } void groupBy(SqlStringBuilder sb, QueryContext ctx){ if(aggregation && !group.isEmpty()) { - sb.append(" GROUP BY ") - .append(group.stream() - .map(c-> !(c instanceof ViewColumn) && columns.contains(c) ? ((NamedColumn)c).getTag() : c.sql(ctx)) //add alias - .collect(joining(SCOMA))); + sb.append(" GROUP BY ").forEach(group.iterator(), SCOMA, c-> { + if(!(c instanceof ViewColumn) && columns.contains(c)) { + sb.append(((NamedColumn)c).getTag()); + } + else { + c.sql(sb, ctx); + } + }); } } void having(SqlStringBuilder sb, QueryContext ctx){ if(!having.isEmpty()) { sb.append(" HAVING ") - .appendEach(having, AND.sql(), f-> f.sql(ctx)); + .forEach(having.iterator(), AND.sql(), f-> f.sql(sb, ctx)); } } void orderBy(SqlStringBuilder sb, QueryContext ctx) { if(!orders.isEmpty()) { sb.append(" ORDER BY ") - .appendEach(orders, SCOMA, o-> o.sql(ctx)); + .forEach(orders.iterator(), SCOMA, o-> o.sql(sb, ctx)); } } diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index 91ba8af0..b064f5e4 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -1,7 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.QueryContext.addWithValue; - import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -18,10 +16,10 @@ public final class QueryView implements DBView { private final QueryBuilder builder; @Override - public String sql(QueryContext ctx) { - var s = new SqlStringBuilder(100).append("("); - builder.build(s, ctx.subQuery(builder.getOverView())); - return s.append(")").toString(); + public void sql(SqlStringBuilder sb, QueryContext ctx) { + sb.openParenthesis(); + builder.build(sb, ctx.subQuery(builder.getOverView())); + sb.closeParenthesis(); } public SingleColumnQuery asColumn(){ @@ -30,6 +28,6 @@ public SingleColumnQuery asColumn(){ @Override public String toString() { - return sql(addWithValue()); + return DBObject.toSQL(this); } } diff --git a/src/main/java/org/usf/jquery/core/RangeComparator.java b/src/main/java/org/usf/jquery/core/RangeComparator.java index 1f225ced..eed769f5 100644 --- a/src/main/java/org/usf/jquery/core/RangeComparator.java +++ b/src/main/java/org/usf/jquery/core/RangeComparator.java @@ -1,7 +1,6 @@ package org.usf.jquery.core; import static org.usf.jquery.core.LogicalOperator.AND; -import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNArgs; /** @@ -13,10 +12,11 @@ public interface RangeComparator extends Comparator { @Override - default String sql(QueryContext ctx, Object[] args) { + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNArgs(3, args, RangeComparator.class::getSimpleName); - return ctx.appendParameter(args[0]) + SPACE + id() + - SPACE + ctx.appendParameter(args[1]) + - AND.sql() + ctx.appendParameter(args[2]); + sb.append(ctx.appendParameter(args[0])) + .spacing(id()) + .append(ctx.appendParameter(args[1])) + .append(AND.sql()).append(ctx.appendParameter(args[2])); } } diff --git a/src/main/java/org/usf/jquery/core/SingleColumnQuery.java b/src/main/java/org/usf/jquery/core/SingleColumnQuery.java index af9aec68..7c4f7ad9 100644 --- a/src/main/java/org/usf/jquery/core/SingleColumnQuery.java +++ b/src/main/java/org/usf/jquery/core/SingleColumnQuery.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.Validation.requireNoArgs; import org.usf.jquery.core.JavaType.Typed; @@ -26,13 +25,13 @@ public final class SingleColumnQuery implements DBObject, Typed { } @Override - public String sql(QueryContext ctx, Object[] args) { + public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNoArgs(args, SingleColumnQuery.class::getSimpleName); - return sql(ctx); + sql(sb, ctx); } - public String sql(QueryContext ctx) { - return query.sql(ctx); + public void sql(SqlStringBuilder sb, QueryContext ctx) { + query.sql(sb, ctx); } @Override @@ -42,6 +41,6 @@ public JDBCType getType() { @Override public String toString() { - return sql(addWithValue()); + return DBObject.toSQL(this); } } diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index 95aad3e9..9d74556b 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -1,12 +1,10 @@ package org.usf.jquery.core; import static java.util.Objects.isNull; -import static java.util.function.Function.identity; +import static org.usf.jquery.core.Utils.isEmpty; -import java.util.Collection; import java.util.Iterator; import java.util.function.Consumer; -import java.util.function.Function; import java.util.function.Supplier; /** @@ -25,6 +23,10 @@ public final class SqlStringBuilder { private final StringBuilder sb; + public SqlStringBuilder() { + this.sb = new StringBuilder(); + } + public SqlStringBuilder(int capacity) { this.sb = new StringBuilder(capacity); } @@ -49,26 +51,24 @@ public SqlStringBuilder appendIf(boolean condition, String sup, String orElse) { return append(condition ? sup : orElse); } - public SqlStringBuilder appendEach(Collection list, String separator) { - return appendEach(list, separator, EMPTY, identity()); - } - - public SqlStringBuilder appendEach(Collection list, String separator, Function fn) { - return appendEach(list, separator, EMPTY, fn); + public SqlStringBuilder appendEach(T[] arr, String separator, Consumer fn) { + return appendEach(arr, separator, fn, EMPTY, EMPTY); } - public SqlStringBuilder appendEach(Collection list, String separator, String prefix, Function fn) { - if(!list.isEmpty()) { - var it = list.iterator(); - append(prefix).append(fn.apply(it.next())); - var before = separator + prefix; - while(it.hasNext()) { - append(before).append(fn.apply(it.next())); + public SqlStringBuilder appendEach(T[] arr, String separator, Consumer fn, String prefix, String suffix) { + sb.append(prefix); + if(!isEmpty(arr)) { + var i=0; + fn.accept(arr[i]); + for(++i; i SqlStringBuilder forEach(Iterator it, String separator, Consumer cons) { if(it.hasNext()) { cons.accept(it.next()); @@ -80,13 +80,50 @@ public SqlStringBuilder forEach(Iterator it, String separator, Consumer supp; @Override - public String sql(QueryContext ctx) { - return formatValue(supp.get()); + public void sql(SqlStringBuilder sb, QueryContext ctx) { + sb.append(formatValue(supp.get())); } @Override @@ -37,4 +37,9 @@ public boolean resolve(QueryBuilder builder) { public void views(Collection views) { //do nothing } + + @Override + public String toString() { + return DBObject.toSQL(this); + } } diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index 0d9dbe36..6f37110b 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -1,8 +1,6 @@ package org.usf.jquery.core; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.QueryContext.addWithValue; -import static org.usf.jquery.core.SqlStringBuilder.member; import java.util.Collection; @@ -24,8 +22,8 @@ public class ViewColumn implements NamedColumn { private final String tag; //optional @Override - public String sql(QueryContext ctx) { - return nonNull(view) ? member(ctx.viewAlias(view), name) : name; + public void sql(SqlStringBuilder sb, QueryContext ctx) { + sb.appendIf(nonNull(view), ()-> ctx.viewAlias(view) + '.').append(name); } public boolean resolve(QueryBuilder builder) { @@ -40,6 +38,6 @@ public void views(Collection views) { @Override public String toString() { - return sql(addWithValue()); + return DBObject.toSQL(this); } } diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index eb5d39cd..b55d9f22 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -1,19 +1,15 @@ package org.usf.jquery.core; -import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.JoinType.CROSS; import static org.usf.jquery.core.JoinType.FULL; import static org.usf.jquery.core.JoinType.INNER; import static org.usf.jquery.core.JoinType.LEFT; import static org.usf.jquery.core.JoinType.RIGHT; import static org.usf.jquery.core.LogicalOperator.AND; -import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; import static org.usf.jquery.core.Validation.requireNoArgs; -import java.util.stream.Stream; - import lombok.Getter; /** @@ -38,25 +34,23 @@ public ViewJoin(JoinType joinType, DBView view, DBFilter[] filters) { } @Override - public String sql(QueryContext ctx, Object[] args) { + public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNoArgs(args, ViewJoin.class::getSimpleName); - return sql(ctx); + sql(sb, ctx); } - public String sql(QueryContext ctx) { - var s = joinType + " JOIN " + view.sqlWithTag(ctx); + public void sql(SqlStringBuilder sb, QueryContext ctx) { + sb.append(joinType + " JOIN "); + view.sqlWithTag(sb, ctx); if(!isEmpty(filters)) { - var val = ctx.withValue(); //literal filter - s += " ON " + Stream.of(filters) - .map(f-> f.sql(val)) - .collect(joining(AND.sql())); + var val = ctx.withValue(); //literal filters + sb.append(" ON ").appendEach(filters, AND.sql(), f-> f.sql(sb, val)); } //else cross join - return s; } @Override public String toString() { - return sql(addWithValue()); + return DBObject.toSQL(this); } public static ViewJoin innerJoin(DBView view, DBFilter... filters) { diff --git a/src/main/java/org/usf/jquery/core/WhenCase.java b/src/main/java/org/usf/jquery/core/WhenCase.java index abb32f7a..10446f33 100644 --- a/src/main/java/org/usf/jquery/core/WhenCase.java +++ b/src/main/java/org/usf/jquery/core/WhenCase.java @@ -4,7 +4,6 @@ import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.Nested.tryResolve; import static org.usf.jquery.core.Nested.viewsOf; -import static org.usf.jquery.core.QueryContext.addWithValue; import static org.usf.jquery.core.Validation.requireNoArgs; import java.util.Collection; @@ -25,19 +24,21 @@ final class WhenCase implements DBObject, Typed, Nested { private final Object value; @Override - public String sql(QueryContext qv, Object[] args) { + public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNoArgs(args, WhenCase.class::getSimpleName); - return sql(qv); + sql(sb, ctx); } - public String sql(QueryContext ctx) { - var sb = new StringBuilder(50); - sb = nonNull(filter) - ? sb.append("WHEN ") - .append(filter.sql(ctx)) - .append(" THEN ") - : sb.append("ELSE "); - return sb.append(ctx.appendLiteral(value)).toString(); + public void sql(SqlStringBuilder sb, QueryContext ctx) { + if(nonNull(filter)) { + sb.append("WHEN "); + filter.sql(sb, ctx); + sb.append(" THEN "); + } + else { + sb.append("ELSE "); + } + sb.append(ctx.appendLiteral(value)); } @Override @@ -62,6 +63,6 @@ public void views(Collection views) { @Override public String toString() { - return sql(addWithValue()); + return DBObject.toSQL(this); } } From eb2fe2448affc7d8143fca73cba65dba326f22a5 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 12 Sep 2024 01:23:29 +0200 Subject: [PATCH 258/298] edit --- .../org/usf/jquery/core/SqlStringBuilder.java | 16 ++++------------ .../org/usf/jquery/web/RevisionIterator.java | 10 ++++++---- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index 9d74556b..fdcbc11f 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -43,14 +43,6 @@ public SqlStringBuilder appendIf(boolean condition, Supplier sup) { return condition ? append(sup.get()) : this; } - public SqlStringBuilder appendIf(boolean condition, Supplier sup, Supplier orSup) { - return append(condition ? sup.get() : orSup.get()); - } - - public SqlStringBuilder appendIf(boolean condition, String sup, String orElse) { - return append(condition ? sup : orElse); - } - public SqlStringBuilder appendEach(T[] arr, String separator, Consumer fn) { return appendEach(arr, separator, fn, EMPTY, EMPTY); } @@ -79,10 +71,6 @@ public SqlStringBuilder forEach(Iterator it, String separator, Consumer Date: Thu, 12 Sep 2024 01:38:06 +0200 Subject: [PATCH 259/298] edit --- .../usf/jquery/core/ArithmeticOperator.java | 5 ++--- .../java/org/usf/jquery/core/CaseColumn.java | 2 +- .../org/usf/jquery/core/CastFunction.java | 2 +- .../usf/jquery/core/ColumnFilterGroup.java | 2 +- .../usf/jquery/core/ColumnSingleFilter.java | 1 - .../core/ComparisonExpressionGroup.java | 2 +- .../org/usf/jquery/core/InComparator.java | 6 ++---- .../java/org/usf/jquery/core/QueryView.java | 5 ++--- .../org/usf/jquery/core/SqlStringBuilder.java | 20 ++++++++++--------- .../java/org/usf/jquery/core/ViewJoin.java | 2 +- 10 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java index d4da3fe3..86df01e5 100644 --- a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java +++ b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java @@ -13,8 +13,7 @@ interface ArithmeticOperator extends Operator { @Override default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNArgs(2, args, ArithmeticException.class::getSimpleName); - sb.openParenthesis() - .append(ctx.appendLiteral(args[0])).append(id()).append(ctx.appendLiteral(args[1])) - .closeParenthesis(); + sb.parenthesis(()-> + sb.append(ctx.appendLiteral(args[0])).append(id()).append(ctx.appendLiteral(args[1]))); } } diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index c850d2a3..4219edd9 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -24,7 +24,7 @@ public final class CaseColumn implements DBColumn { @Override public void sql(SqlStringBuilder sb, QueryContext ctx) { var sub = ctx.withValue(); //force literal parameter - sb.appendEach(whenCases, SPACE, o-> o.sql(sb, sub), "CASE ", " END"); + sb.forEach(whenCases, SPACE, o-> o.sql(sb, sub), "CASE ", " END"); } @Override diff --git a/src/main/java/org/usf/jquery/core/CastFunction.java b/src/main/java/org/usf/jquery/core/CastFunction.java index 66de14b8..819e8a56 100644 --- a/src/main/java/org/usf/jquery/core/CastFunction.java +++ b/src/main/java/org/usf/jquery/core/CastFunction.java @@ -23,7 +23,7 @@ default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { sb.function(id(), ()->{ sb.append(ctx.appendLiteral(args[0])).as(asType()); if(args.length > 1) { - sb.parenthesis(()-> ctx.appendLiteralArray(args, 1)); + sb.parenthesis(ctx.appendLiteralArray(args, 1)); } }); } diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index 5e7ae89e..d3460814 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -26,7 +26,7 @@ public final class ColumnFilterGroup implements DBFilter { @Override public void sql(SqlStringBuilder sb, QueryContext ctx) { sb.parenthesis(()-> - sb.appendEach(filters, operator.sql(), o-> o.sql(sb, ctx))); + sb.forEach(filters, operator.sql(), o-> o.sql(sb, ctx))); } @Override diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index c120bfbc..4ffe6c4c 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -2,7 +2,6 @@ import static org.usf.jquery.core.Nested.tryResolve; import static org.usf.jquery.core.Nested.viewsOf; -import static org.usf.jquery.core.QueryContext.addWithValue; import java.util.Collection; diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index ea4773a2..44768c3c 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -26,7 +26,7 @@ public final class ComparisonExpressionGroup implements ComparisonExpression { @Override public void sql(SqlStringBuilder sb, QueryContext ctx, Object operand) { sb.parenthesis(()-> - sb.appendEach(expressions, operator.sql(), o-> o.sql(sb, ctx, operand))); + sb.forEach(expressions, operator.sql(), o-> o.sql(sb, ctx, operand))); } @Override diff --git a/src/main/java/org/usf/jquery/core/InComparator.java b/src/main/java/org/usf/jquery/core/InComparator.java index d7684c9e..29444b1e 100644 --- a/src/main/java/org/usf/jquery/core/InComparator.java +++ b/src/main/java/org/usf/jquery/core/InComparator.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.SqlStringBuilder.parenthese; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; /** @@ -14,8 +13,7 @@ public interface InComparator extends Comparator { @Override default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireAtLeastNArgs(2, args, InComparator.class::getSimpleName); - sb.append(ctx.appendParameter(args[0])) - .space().append(id()) - .append(parenthese(ctx.appendArrayParameter(args, 1))); + sb.append(ctx.appendParameter(args[0])).space().append(id()).parenthesis( + ctx.appendArrayParameter(args, 1)); } } diff --git a/src/main/java/org/usf/jquery/core/QueryView.java b/src/main/java/org/usf/jquery/core/QueryView.java index b064f5e4..ccfcdbc2 100644 --- a/src/main/java/org/usf/jquery/core/QueryView.java +++ b/src/main/java/org/usf/jquery/core/QueryView.java @@ -17,9 +17,8 @@ public final class QueryView implements DBView { @Override public void sql(SqlStringBuilder sb, QueryContext ctx) { - sb.openParenthesis(); - builder.build(sb, ctx.subQuery(builder.getOverView())); - sb.closeParenthesis(); + sb.parenthesis(()-> + builder.build(sb, ctx.subQuery(builder.getOverView()))); } public SingleColumnQuery asColumn(){ diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index fdcbc11f..4bb92220 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -43,11 +43,11 @@ public SqlStringBuilder appendIf(boolean condition, Supplier sup) { return condition ? append(sup.get()) : this; } - public SqlStringBuilder appendEach(T[] arr, String separator, Consumer fn) { - return appendEach(arr, separator, fn, EMPTY, EMPTY); + public SqlStringBuilder forEach(T[] arr, String separator, Consumer fn) { + return forEach(arr, separator, fn, EMPTY, EMPTY); } - public SqlStringBuilder appendEach(T[] arr, String separator, Consumer fn, String prefix, String suffix) { + public SqlStringBuilder forEach(T[] arr, String separator, Consumer fn, String prefix, String suffix) { sb.append(prefix); if(!isEmpty(arr)) { var i=0; @@ -60,12 +60,12 @@ public SqlStringBuilder appendEach(T[] arr, String separator, Consumer fn sb.append(suffix); return this; } - + public SqlStringBuilder forEach(Iterator it, String separator, Consumer cons) { if(it.hasNext()) { cons.accept(it.next()); while(it.hasNext()) { - append(separator); + sb.append(separator); cons.accept(it.next()); } } @@ -88,6 +88,12 @@ public SqlStringBuilder function(String name, Runnable args) { return append(name).parenthesis(args); } + public SqlStringBuilder parenthesis(String s) { + openParenthesis(); + sb.append(s); + return closeParenthesis(); + } + public SqlStringBuilder parenthesis(Runnable exec) { openParenthesis(); exec.run(); @@ -135,10 +141,6 @@ public static String doubleQuote(String op) { return DQUOT + op + DQUOT; } - public static String parenthese(String op) { - return "(" + op + ")"; - } - public static String member(String parent, String child) { return isNull(parent) ? child : parent + "." + child; } diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index b55d9f22..d4553883 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -44,7 +44,7 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { view.sqlWithTag(sb, ctx); if(!isEmpty(filters)) { var val = ctx.withValue(); //literal filters - sb.append(" ON ").appendEach(filters, AND.sql(), f-> f.sql(sb, val)); + sb.append(" ON ").forEach(filters, AND.sql(), f-> f.sql(sb, val)); } //else cross join } From e758f8121e83199c367a130d0c876b80340e90be Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 12 Sep 2024 11:09:51 +0200 Subject: [PATCH 260/298] edit --- .../usf/jquery/core/ArithmeticOperator.java | 7 +- .../org/usf/jquery/core/BasicComparator.java | 4 +- .../org/usf/jquery/core/CastFunction.java | 5 +- .../org/usf/jquery/core/CombinedOperator.java | 2 +- .../java/org/usf/jquery/core/DBObject.java | 4 +- src/main/java/org/usf/jquery/core/DBView.java | 3 +- .../org/usf/jquery/core/ExtractFunction.java | 6 +- .../org/usf/jquery/core/FunctionOperator.java | 6 +- .../org/usf/jquery/core/InComparator.java | 4 +- .../org/usf/jquery/core/NullComparator.java | 3 +- .../java/org/usf/jquery/core/Partition.java | 8 +- .../org/usf/jquery/core/PipeFunction.java | 6 +- .../org/usf/jquery/core/QueryBuilder.java | 2 +- .../org/usf/jquery/core/QueryContext.java | 77 ++++++++----------- .../org/usf/jquery/core/RangeComparator.java | 9 ++- .../org/usf/jquery/core/SqlStringBuilder.java | 20 ++++- .../org/usf/jquery/core/StringComparator.java | 5 +- .../java/org/usf/jquery/core/WhenCase.java | 2 +- 18 files changed, 95 insertions(+), 78 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java index 86df01e5..301718c4 100644 --- a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java +++ b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java @@ -13,7 +13,10 @@ interface ArithmeticOperator extends Operator { @Override default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNArgs(2, args, ArithmeticException.class::getSimpleName); - sb.parenthesis(()-> - sb.append(ctx.appendLiteral(args[0])).append(id()).append(ctx.appendLiteral(args[1]))); + sb.parenthesis(()->{ + ctx.appendLiteral(sb, args[0]); + sb.append(id()); + ctx.appendLiteral(sb, args[1]); + }); } } diff --git a/src/main/java/org/usf/jquery/core/BasicComparator.java b/src/main/java/org/usf/jquery/core/BasicComparator.java index 899eeeb7..5e8c294f 100644 --- a/src/main/java/org/usf/jquery/core/BasicComparator.java +++ b/src/main/java/org/usf/jquery/core/BasicComparator.java @@ -13,6 +13,8 @@ public interface BasicComparator extends Comparator { @Override default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNArgs(2, args, BasicComparator.class::getSimpleName); - sb.append(ctx.appendParameter(args[0])).append(id()).append(ctx.appendParameter(args[1])); + ctx.appendParameter(sb, args[0]); + sb.append(id()); + ctx.appendParameter(sb, args[1]); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/CastFunction.java b/src/main/java/org/usf/jquery/core/CastFunction.java index 819e8a56..4badf0fb 100644 --- a/src/main/java/org/usf/jquery/core/CastFunction.java +++ b/src/main/java/org/usf/jquery/core/CastFunction.java @@ -21,9 +21,10 @@ default String id() { default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireAtLeastNArgs(1, args, CastFunction.class::getSimpleName); sb.function(id(), ()->{ - sb.append(ctx.appendLiteral(args[0])).as(asType()); + ctx.appendLiteral(sb, args[0]); + sb.as(asType()); if(args.length > 1) { - sb.parenthesis(ctx.appendLiteralArray(args, 1)); + sb.parenthesis(()-> ctx.appendLiteralArray(sb, args, 1)); } }); } diff --git a/src/main/java/org/usf/jquery/core/CombinedOperator.java b/src/main/java/org/usf/jquery/core/CombinedOperator.java index 8f4c96ec..ee9cc982 100644 --- a/src/main/java/org/usf/jquery/core/CombinedOperator.java +++ b/src/main/java/org/usf/jquery/core/CombinedOperator.java @@ -21,7 +21,7 @@ default OperationColumn operation(JDBCType type, Object... args) { @Override default String id() { - return "CombinedOperator"; //do better + return "CombinedOperator"; //try get id } @Override diff --git a/src/main/java/org/usf/jquery/core/DBObject.java b/src/main/java/org/usf/jquery/core/DBObject.java index d46da8b8..5dddcc1d 100644 --- a/src/main/java/org/usf/jquery/core/DBObject.java +++ b/src/main/java/org/usf/jquery/core/DBObject.java @@ -12,9 +12,9 @@ public interface DBObject { void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args); - static String toSQL(DBObject obj, Object... values) { + static String toSQL(DBObject obj, Object... args) { var sb = new SqlStringBuilder(); - obj.sql(sb, addWithValue(), values); + obj.sql(sb, addWithValue(), args); return sb.toString(); } } diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index de793fe4..c6a9a2ef 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -20,7 +20,6 @@ default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { default void sqlWithTag(SqlStringBuilder sb, QueryContext ctx) { ctx.viewOverload(this).orElse(this).sql(sb, ctx); //!important - sb.space(); - ctx.viewAlias(this); + sb.space().append(ctx.viewAlias(this)); } } diff --git a/src/main/java/org/usf/jquery/core/ExtractFunction.java b/src/main/java/org/usf/jquery/core/ExtractFunction.java index d0970e9b..82106e4c 100644 --- a/src/main/java/org/usf/jquery/core/ExtractFunction.java +++ b/src/main/java/org/usf/jquery/core/ExtractFunction.java @@ -20,7 +20,9 @@ default String id() { @Override default void sql(SqlStringBuilder sb, QueryContext builder, Object[] args) { requireNArgs(1, args, ExtractFunction.class::getSimpleName); - sb.function(id(), ()-> - sb.append(field()).from(builder.appendLiteral(args[0]))); + sb.function(id(), ()->{ + sb.append(field()).from(); + builder.appendLiteral(sb, args[0]); + }); } } diff --git a/src/main/java/org/usf/jquery/core/FunctionOperator.java b/src/main/java/org/usf/jquery/core/FunctionOperator.java index 48df5701..8d0c823b 100644 --- a/src/main/java/org/usf/jquery/core/FunctionOperator.java +++ b/src/main/java/org/usf/jquery/core/FunctionOperator.java @@ -11,6 +11,10 @@ public interface FunctionOperator extends Operator { @Override default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { - sb.function(id(), ()-> ctx.appendLiteralArray(args)); + sql(sb, ctx, args, 0); + } + + default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args, int from) { + sb.function(id(), ()-> ctx.appendLiteralArray(sb, args, from)); //avoid array copy } } diff --git a/src/main/java/org/usf/jquery/core/InComparator.java b/src/main/java/org/usf/jquery/core/InComparator.java index 29444b1e..338c58b9 100644 --- a/src/main/java/org/usf/jquery/core/InComparator.java +++ b/src/main/java/org/usf/jquery/core/InComparator.java @@ -13,7 +13,7 @@ public interface InComparator extends Comparator { @Override default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireAtLeastNArgs(2, args, InComparator.class::getSimpleName); - sb.append(ctx.appendParameter(args[0])).space().append(id()).parenthesis( - ctx.appendArrayParameter(args, 1)); + ctx.appendParameter(sb, args[0]); + sb.space().append(id()).parenthesis(()-> ctx.appendArrayParameter(sb, args, 1)); } } diff --git a/src/main/java/org/usf/jquery/core/NullComparator.java b/src/main/java/org/usf/jquery/core/NullComparator.java index b1c285de..a8ae8958 100644 --- a/src/main/java/org/usf/jquery/core/NullComparator.java +++ b/src/main/java/org/usf/jquery/core/NullComparator.java @@ -13,6 +13,7 @@ public interface NullComparator extends Comparator { @Override default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNArgs(1, args, NullComparator.class::getSimpleName); - sb.append(ctx.appendParameter(args[0])).space().append(id()); + ctx.appendParameter(sb, args[0]); + sb.space().append(id()); } } diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index be551f9a..93cf60a6 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -29,11 +29,13 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { void sql(SqlStringBuilder sb, QueryContext ctx) { if(!isEmpty(columns)) { - sb.append("PARTITION BY ").append(ctx.appendLiteralArray(columns)); + sb.append("PARTITION BY "); + ctx.appendLiteralArray(sb, columns); } if(!isEmpty(orders)) { //require orders - sb.appendIf(!isEmpty(columns), SPACE) - .append("ORDER BY ").append(ctx.appendLiteralArray(orders)); + sb.appendIf(!isEmpty(columns), SPACE); + sb.append("ORDER BY "); + ctx.appendLiteralArray(sb, orders); } } diff --git a/src/main/java/org/usf/jquery/core/PipeFunction.java b/src/main/java/org/usf/jquery/core/PipeFunction.java index fb5c4e36..751699d5 100644 --- a/src/main/java/org/usf/jquery/core/PipeFunction.java +++ b/src/main/java/org/usf/jquery/core/PipeFunction.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static java.util.Arrays.copyOfRange; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; /** @@ -14,7 +13,8 @@ public interface PipeFunction extends FunctionOperator { @Override default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireAtLeastNArgs(1, args, PipeFunction.class::getSimpleName); - sb.append(ctx.appendLiteral(args[0])).space(); - FunctionOperator.super.sql(sb, ctx, copyOfRange(args, 1, args.length)); + ctx.appendLiteral(sb, args[0]); + sb.space(); + FunctionOperator.super.sql(sb, ctx, args, 1); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 03e67e65..05325166 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -167,7 +167,7 @@ public final void build(SqlStringBuilder sb, QueryContext ctx){ select(sb, ctx); from(sb, ctx); //enumerate all views before from clause join(sb, ctx); - sb.append(sub.toString()); //TODO optim + sb.append(sub.toString()); } void select(SqlStringBuilder sb, QueryContext ctx){ diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java index 3e793113..67998ba2 100644 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -4,20 +4,16 @@ import static java.util.Collections.unmodifiableMap; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; -import static java.util.stream.Collectors.joining; import static org.usf.jquery.core.JDBCType.typeOf; import static org.usf.jquery.core.SqlStringBuilder.COMA; import static org.usf.jquery.core.SqlStringBuilder.EMPTY; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.SqlStringBuilder.quote; -import static org.usf.jquery.core.Utils.isEmpty; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Stream; import lombok.AccessLevel; import lombok.Getter; @@ -43,6 +39,10 @@ public final class QueryContext { private final List views; //indexed view private final Map overView; + public List views(){ + return views; + } + public String viewAlias(DBView view) { var idx = views.indexOf(view); if(idx < 0) { @@ -56,62 +56,51 @@ public Optional viewOverload(DBView view) { return ofNullable(overView.get(view)); } - public List views(){ - return views; - } - - public String appendArrayParameter(Object[] arr) { - return appendArrayParameter(arr, 0); + public void appendArrayParameter(SqlStringBuilder sb, Object[] arr) { + appendArrayParameter(sb, arr, 0); } - public String appendArrayParameter(Object[] arr, int from) { - return dynamic() - ? appendArray(arr, from, this::appendParameter) - : appendLiteralArray(arr, from); + public void appendArrayParameter(SqlStringBuilder sb, Object[] arr, int from) { + if(dynamic()) { + sb.forEach(arr, from, SCOMA, o-> appendParameter(sb, o)); + } + else { + appendLiteralArray(sb, arr, from); + } } - public String appendLiteralArray(Object[] arr) { - return appendLiteralArray(arr, 0); + public void appendLiteralArray(SqlStringBuilder sb, Object[] arr) { + appendLiteralArray(sb, arr, 0); } - public String appendLiteralArray(Object[] arr, int from) { - return appendArray(arr, from, this::appendLiteral); - } - - String appendArray(Object[] arr, int from, Function fn) { - if(isEmpty(arr)) { - if(from == 0) { - return EMPTY; - } - } - else if(from >= 0 && from < arr.length) { - return Stream.of(arr) - .skip(from) - .map(fn) - .collect(joining(SCOMA)); - } - throw new IndexOutOfBoundsException(from); + public void appendLiteralArray(SqlStringBuilder sb, Object[] arr, int from) { + sb.forEach(arr, from, SCOMA, o-> appendLiteral(sb, o)); } - public String appendParameter(Object o) { + public void appendParameter(SqlStringBuilder sb, Object o) { if(dynamic()) { if(o instanceof DBObject jo) { - return jo.sql(this, null); + jo.sql(sb, this, null); } - var t = typeOf(o); //o=null=>empty - if(t.isPresent()) { - return appendArg(t.get(), o); + else { + var t = typeOf(o); + sb.append(t.isPresent() ? appendArg(t.get(), o) : formatValue(o)); } } - return appendLiteral(o); + else { + appendLiteral(sb, o); + } } - public String appendLiteral(Object o) { //TD : stringify value using db default pattern - return o instanceof DBObject jo - ? jo.sql(this, null) - : formatValue(o); + public void appendLiteral(SqlStringBuilder sb, Object o) { //TD : stringify value using db default pattern + if(o instanceof DBObject jo) { + jo.sql(sb, this, null); + } + else { + sb.append(formatValue(o)); + } } - + private String appendArg(JDBCType type, Object o) { argTypes.add(type); args.add(o); diff --git a/src/main/java/org/usf/jquery/core/RangeComparator.java b/src/main/java/org/usf/jquery/core/RangeComparator.java index eed769f5..2f9ee5f0 100644 --- a/src/main/java/org/usf/jquery/core/RangeComparator.java +++ b/src/main/java/org/usf/jquery/core/RangeComparator.java @@ -14,9 +14,10 @@ public interface RangeComparator extends Comparator { @Override default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNArgs(3, args, RangeComparator.class::getSimpleName); - sb.append(ctx.appendParameter(args[0])) - .spacing(id()) - .append(ctx.appendParameter(args[1])) - .append(AND.sql()).append(ctx.appendParameter(args[2])); + ctx.appendParameter(sb, args[0]); + sb.spacing(id()); + ctx.appendParameter(sb, args[1]); + sb.append(AND.sql()); + ctx.appendParameter(sb, args[2]); } } diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index 4bb92220..3de61c9c 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -1,6 +1,7 @@ package org.usf.jquery.core; import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; import static org.usf.jquery.core.Utils.isEmpty; import java.util.Iterator; @@ -44,13 +45,24 @@ public SqlStringBuilder appendIf(boolean condition, Supplier sup) { } public SqlStringBuilder forEach(T[] arr, String separator, Consumer fn) { - return forEach(arr, separator, fn, EMPTY, EMPTY); + return forEach(arr, 0, separator, fn); } - + + public SqlStringBuilder forEach(T[] arr, int idx, String separator, Consumer fn) { + return forEach(arr, idx, separator, fn, EMPTY, EMPTY); + } + public SqlStringBuilder forEach(T[] arr, String separator, Consumer fn, String prefix, String suffix) { + return forEach(arr, 0, separator, fn, prefix, suffix); + } + + public SqlStringBuilder forEach(T[] arr, int idx, String separator, Consumer fn, String prefix, String suffix) { + if(idx < 0 || (isEmpty(arr) && idx > 0) || idx >= requireNonNull(arr).length) { + throw new IndexOutOfBoundsException(idx); + } sb.append(prefix); - if(!isEmpty(arr)) { - var i=0; + if(!isEmpty(arr)) { //idx < arr.length + var i=idx; fn.accept(arr[i]); for(++i; i Date: Thu, 12 Sep 2024 12:53:38 +0200 Subject: [PATCH 261/298] edit --- .../org/usf/jquery/core/CastFunction.java | 2 +- .../java/org/usf/jquery/core/DBOrder.java | 4 +- .../org/usf/jquery/core/InComparator.java | 2 +- .../java/org/usf/jquery/core/NamedColumn.java | 3 +- .../org/usf/jquery/core/QueryBuilder.java | 4 +- .../org/usf/jquery/core/SqlStringBuilder.java | 46 +++++++++++-------- .../org/usf/jquery/core/StringComparator.java | 2 +- .../java/org/usf/jquery/core/TableView.java | 7 ++- src/main/java/org/usf/jquery/core/Utils.java | 2 +- .../java/org/usf/jquery/core/ViewColumn.java | 2 +- .../java/org/usf/jquery/core/WhenCase.java | 2 +- 11 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CastFunction.java b/src/main/java/org/usf/jquery/core/CastFunction.java index 4badf0fb..5bd8f09f 100644 --- a/src/main/java/org/usf/jquery/core/CastFunction.java +++ b/src/main/java/org/usf/jquery/core/CastFunction.java @@ -20,7 +20,7 @@ default String id() { @Override default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireAtLeastNArgs(1, args, CastFunction.class::getSimpleName); - sb.function(id(), ()->{ + sb.function(id(), ()-> { ctx.appendLiteral(sb, args[0]); sb.as(asType()); if(args.length > 1) { diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index d5f35c15..f7415307 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -1,7 +1,5 @@ package org.usf.jquery.core; -import static java.util.Objects.nonNull; -import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireNoArgs; import java.util.Collection; @@ -34,7 +32,7 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { public void sql(SqlStringBuilder sb, QueryContext ctx) { column.sql(sb, ctx); - sb.appendIf(nonNull(order), ()-> SPACE + order.name()); + sb.runIfNonNull(order, o-> sb.space().append(o.name())); } @Override diff --git a/src/main/java/org/usf/jquery/core/InComparator.java b/src/main/java/org/usf/jquery/core/InComparator.java index 338c58b9..56999c4c 100644 --- a/src/main/java/org/usf/jquery/core/InComparator.java +++ b/src/main/java/org/usf/jquery/core/InComparator.java @@ -14,6 +14,6 @@ public interface InComparator extends Comparator { default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireAtLeastNArgs(2, args, InComparator.class::getSimpleName); ctx.appendParameter(sb, args[0]); - sb.space().append(id()).parenthesis(()-> ctx.appendArrayParameter(sb, args, 1)); + sb.space().function(id(), ()-> ctx.appendArrayParameter(sb, args, 1)); } } diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index 00b7b8f9..e47db7ba 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static java.util.Objects.nonNull; import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; /** @@ -14,6 +13,6 @@ public interface NamedColumn extends DBColumn { default void sqlWithTag(SqlStringBuilder sb, QueryContext ctx) { sql(sb, ctx); - sb.appendIf(nonNull(getTag()), ()-> " AS " + doubleQuote(getTag())); + sb.runIfNonNull(getTag(), v-> sb.as(doubleQuote(v))); } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 05325166..e14264f3 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -235,8 +235,8 @@ void orderBy(SqlStringBuilder sb, QueryContext ctx) { void fetch(SqlStringBuilder sb) { if(currentDatabase() != TERADATA) { // TOP n - sb.appendIf(nonNull(limit), ()-> " LIMIT " + limit); - sb.appendIf(nonNull(offset), ()-> " OFFSET " + offset); + sb.appendIfNonNull(limit, v-> " LIMIT " + v); + sb.appendIfNonNull(offset, v-> " OFFSET " + v); } } diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index 3de61c9c..e75ac8e2 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -1,11 +1,12 @@ package org.usf.jquery.core; -import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static org.usf.jquery.core.Utils.isEmpty; import java.util.Iterator; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Supplier; /** @@ -44,20 +45,31 @@ public SqlStringBuilder appendIf(boolean condition, Supplier sup) { return condition ? append(sup.get()) : this; } - public SqlStringBuilder forEach(T[] arr, String separator, Consumer fn) { - return forEach(arr, 0, separator, fn); + public SqlStringBuilder appendIfNonNull(T o, Function fn) { + return nonNull(o) ? append(fn.apply(o)) : this; } - public SqlStringBuilder forEach(T[] arr, int idx, String separator, Consumer fn) { - return forEach(arr, idx, separator, fn, EMPTY, EMPTY); + public SqlStringBuilder runIfNonNull(T o, Consumer cons) { + if(nonNull(o)) { + cons.accept(o); + } + return this; + } + + public SqlStringBuilder forEach(T[] arr, String delimiter, Consumer fn) { + return forEach(arr, 0, delimiter, fn); + } + + public SqlStringBuilder forEach(T[] arr, int idx, String delimiter, Consumer fn) { + return forEach(arr, idx, delimiter, fn, EMPTY, EMPTY); } - public SqlStringBuilder forEach(T[] arr, String separator, Consumer fn, String prefix, String suffix) { - return forEach(arr, 0, separator, fn, prefix, suffix); + public SqlStringBuilder forEach(T[] arr, String delimiter, Consumer fn, String prefix, String suffix) { + return forEach(arr, 0, delimiter, fn, prefix, suffix); } - public SqlStringBuilder forEach(T[] arr, int idx, String separator, Consumer fn, String prefix, String suffix) { - if(idx < 0 || (isEmpty(arr) && idx > 0) || idx >= requireNonNull(arr).length) { + public SqlStringBuilder forEach(T[] arr, int idx, String delimiter, Consumer fn, String prefix, String suffix) { + if(idx < 0 || (isEmpty(arr) && idx > 0) || idx >= requireNonNull(arr, "arr connot be null").length) { throw new IndexOutOfBoundsException(idx); } sb.append(prefix); @@ -65,7 +77,7 @@ public SqlStringBuilder forEach(T[] arr, int idx, String separator, Consumer var i=idx; fn.accept(arr[i]); for(++i; i> return o; } } diff --git a/src/main/java/org/usf/jquery/core/TableView.java b/src/main/java/org/usf/jquery/core/TableView.java index 3459b01e..5bd9ca69 100644 --- a/src/main/java/org/usf/jquery/core/TableView.java +++ b/src/main/java/org/usf/jquery/core/TableView.java @@ -1,7 +1,6 @@ package org.usf.jquery.core; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.SqlStringBuilder.member; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -15,9 +14,9 @@ @RequiredArgsConstructor public class TableView implements DBView { - private final String schema; + private final String schema; //optional private final String name; - private final String tag; + private final String tag; //optional public TableView(String schema, String name) { this(schema, name, name); @@ -25,7 +24,7 @@ public TableView(String schema, String name) { @Override public void sql(SqlStringBuilder sb, QueryContext ctx) { - sb.append(member(getSchemaOrElse(ctx.getSchema()), name)); + sb.appendIfNonNull(getSchemaOrElse(ctx.getSchema()), v-> v + '.').append(name); } public String getSchemaOrElse(String defaultSchema) { diff --git a/src/main/java/org/usf/jquery/core/Utils.java b/src/main/java/org/usf/jquery/core/Utils.java index 88ee5dda..d1c95cdc 100644 --- a/src/main/java/org/usf/jquery/core/Utils.java +++ b/src/main/java/org/usf/jquery/core/Utils.java @@ -66,8 +66,8 @@ public static T[] appendLast(T[] arr, T o) { public static Object[] appendFirst(Object[] arr, Object o) { var res = new Object[arr.length+1]; - arraycopy(arr, 0, res, 1, arr.length); res[0] = o; + arraycopy(arr, 0, res, 1, arr.length); return res; } } diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index 6f37110b..da55e717 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -23,7 +23,7 @@ public class ViewColumn implements NamedColumn { @Override public void sql(SqlStringBuilder sb, QueryContext ctx) { - sb.appendIf(nonNull(view), ()-> ctx.viewAlias(view) + '.').append(name); + sb.appendIfNonNull(view, v-> ctx.viewAlias(v) + '.').append(name); } public boolean resolve(QueryBuilder builder) { diff --git a/src/main/java/org/usf/jquery/core/WhenCase.java b/src/main/java/org/usf/jquery/core/WhenCase.java index bd581664..45a5db03 100644 --- a/src/main/java/org/usf/jquery/core/WhenCase.java +++ b/src/main/java/org/usf/jquery/core/WhenCase.java @@ -21,7 +21,7 @@ final class WhenCase implements DBObject, Typed, Nested { private final DBFilter filter; //optional - private final Object value; + private final Object value; //then|else @Override public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { From 3dc64c800c957b9c2086ea4bb8e1444b40ccc3fc Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 12 Sep 2024 13:02:24 +0200 Subject: [PATCH 262/298] edit --- .../java/org/usf/jquery/core/CaseColumn.java | 2 +- .../usf/jquery/core/ColumnFilterGroup.java | 2 +- .../core/ComparisonExpressionGroup.java | 2 +- .../org/usf/jquery/core/QueryBuilder.java | 16 +++++++-------- .../org/usf/jquery/core/QueryContext.java | 4 ++-- .../org/usf/jquery/core/SqlStringBuilder.java | 20 ++++++++----------- .../java/org/usf/jquery/core/ViewJoin.java | 2 +- 7 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 4219edd9..ca0ca91a 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -24,7 +24,7 @@ public final class CaseColumn implements DBColumn { @Override public void sql(SqlStringBuilder sb, QueryContext ctx) { var sub = ctx.withValue(); //force literal parameter - sb.forEach(whenCases, SPACE, o-> o.sql(sb, sub), "CASE ", " END"); + sb.runForeach(whenCases, SPACE, o-> o.sql(sb, sub), "CASE ", " END"); } @Override diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index d3460814..681a30e3 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -26,7 +26,7 @@ public final class ColumnFilterGroup implements DBFilter { @Override public void sql(SqlStringBuilder sb, QueryContext ctx) { sb.parenthesis(()-> - sb.forEach(filters, operator.sql(), o-> o.sql(sb, ctx))); + sb.runForeach(filters, operator.sql(), o-> o.sql(sb, ctx))); } @Override diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index 44768c3c..0f11686c 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -26,7 +26,7 @@ public final class ComparisonExpressionGroup implements ComparisonExpression { @Override public void sql(SqlStringBuilder sb, QueryContext ctx, Object operand) { sb.parenthesis(()-> - sb.forEach(expressions, operator.sql(), o-> o.sql(sb, ctx, operand))); + sb.runForeach(expressions, operator.sql(), o-> o.sql(sb, ctx, operand))); } @Override diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index e14264f3..0c969cd5 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -151,7 +151,7 @@ public RequestQuery build(String schema) { build(sb, pb); } else { - sb.forEach(it, " UNION ALL ", o-> build(sb, pb)); + sb.runForeach(it, " UNION ALL ", o-> build(sb, pb)); } log.trace("query built in {} ms", currentTimeMillis() - bg); return new RequestQuery(sb.toString(), pb.args(), pb.argTypes()); @@ -183,32 +183,32 @@ void select(SqlStringBuilder sb, QueryContext ctx){ .appendIf(distinct, " DISTINCT") .appendIf(nonNull(limit) && currentDatabase() == TERADATA, ()-> " TOP " + limit) //??????? .space() - .forEach(columns.iterator(), SCOMA, o-> o.sqlWithTag(sb, ctx)); + .runForeach(columns.iterator(), SCOMA, o-> o.sqlWithTag(sb, ctx)); } void from(SqlStringBuilder sb, QueryContext ctx) { var excludes = joins.stream().map(ViewJoin::getView).toList(); var views = ctx.views().stream().filter(not(excludes::contains)).toList(); //do not remove views if(!views.isEmpty()) { - sb.from().forEach(views.iterator(), SCOMA, v-> v.sqlWithTag(sb, ctx)); + sb.from().runForeach(views.iterator(), SCOMA, v-> v.sqlWithTag(sb, ctx)); } } void join(SqlStringBuilder sb, QueryContext ctx) { if(!joins.isEmpty()) { - sb.space().forEach(joins.iterator(), SPACE, v-> v.sql(sb, ctx)); + sb.space().runForeach(joins.iterator(), SPACE, v-> v.sql(sb, ctx)); } } void where(SqlStringBuilder sb, QueryContext ctx){ if(!where.isEmpty()) { - sb.append(" WHERE ").forEach(where.iterator(), AND.sql(), f-> f.sql(sb, ctx)); + sb.append(" WHERE ").runForeach(where.iterator(), AND.sql(), f-> f.sql(sb, ctx)); } } void groupBy(SqlStringBuilder sb, QueryContext ctx){ if(aggregation && !group.isEmpty()) { - sb.append(" GROUP BY ").forEach(group.iterator(), SCOMA, c-> { + sb.append(" GROUP BY ").runForeach(group.iterator(), SCOMA, c-> { if(!(c instanceof ViewColumn) && columns.contains(c)) { sb.append(((NamedColumn)c).getTag()); } @@ -222,14 +222,14 @@ void groupBy(SqlStringBuilder sb, QueryContext ctx){ void having(SqlStringBuilder sb, QueryContext ctx){ if(!having.isEmpty()) { sb.append(" HAVING ") - .forEach(having.iterator(), AND.sql(), f-> f.sql(sb, ctx)); + .runForeach(having.iterator(), AND.sql(), f-> f.sql(sb, ctx)); } } void orderBy(SqlStringBuilder sb, QueryContext ctx) { if(!orders.isEmpty()) { sb.append(" ORDER BY ") - .forEach(orders.iterator(), SCOMA, o-> o.sql(sb, ctx)); + .runForeach(orders.iterator(), SCOMA, o-> o.sql(sb, ctx)); } } diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java index 67998ba2..5f7ce508 100644 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -62,7 +62,7 @@ public void appendArrayParameter(SqlStringBuilder sb, Object[] arr) { public void appendArrayParameter(SqlStringBuilder sb, Object[] arr, int from) { if(dynamic()) { - sb.forEach(arr, from, SCOMA, o-> appendParameter(sb, o)); + sb.runForeach(arr, from, SCOMA, o-> appendParameter(sb, o)); } else { appendLiteralArray(sb, arr, from); @@ -74,7 +74,7 @@ public void appendLiteralArray(SqlStringBuilder sb, Object[] arr) { } public void appendLiteralArray(SqlStringBuilder sb, Object[] arr, int from) { - sb.forEach(arr, from, SCOMA, o-> appendLiteral(sb, o)); + sb.runForeach(arr, from, SCOMA, o-> appendLiteral(sb, o)); } public void appendParameter(SqlStringBuilder sb, Object o) { diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index e75ac8e2..e097e1ef 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -33,10 +33,6 @@ public SqlStringBuilder(int capacity) { this.sb = new StringBuilder(capacity); } - public SqlStringBuilder(String v) { //buffer++ - this.sb = new StringBuilder(v.length() + 50).append(v); - } - public SqlStringBuilder appendIf(boolean condition, String s) { return condition ? append(s) : this; } @@ -56,19 +52,19 @@ public SqlStringBuilder runIfNonNull(T o, Consumer cons) { return this; } - public SqlStringBuilder forEach(T[] arr, String delimiter, Consumer fn) { - return forEach(arr, 0, delimiter, fn); + public SqlStringBuilder runForeach(T[] arr, String delimiter, Consumer fn) { + return runForeach(arr, 0, delimiter, fn); } - public SqlStringBuilder forEach(T[] arr, int idx, String delimiter, Consumer fn) { - return forEach(arr, idx, delimiter, fn, EMPTY, EMPTY); + public SqlStringBuilder runForeach(T[] arr, int idx, String delimiter, Consumer fn) { + return runForeach(arr, idx, delimiter, fn, EMPTY, EMPTY); } - public SqlStringBuilder forEach(T[] arr, String delimiter, Consumer fn, String prefix, String suffix) { - return forEach(arr, 0, delimiter, fn, prefix, suffix); + public SqlStringBuilder runForeach(T[] arr, String delimiter, Consumer fn, String prefix, String suffix) { + return runForeach(arr, 0, delimiter, fn, prefix, suffix); } - public SqlStringBuilder forEach(T[] arr, int idx, String delimiter, Consumer fn, String prefix, String suffix) { + public SqlStringBuilder runForeach(T[] arr, int idx, String delimiter, Consumer fn, String prefix, String suffix) { if(idx < 0 || (isEmpty(arr) && idx > 0) || idx >= requireNonNull(arr, "arr connot be null").length) { throw new IndexOutOfBoundsException(idx); } @@ -85,7 +81,7 @@ public SqlStringBuilder forEach(T[] arr, int idx, String delimiter, Consumer return this; } - public SqlStringBuilder forEach(Iterator it, String separator, Consumer cons) { + public SqlStringBuilder runForeach(Iterator it, String separator, Consumer cons) { if(it.hasNext()) { cons.accept(it.next()); while(it.hasNext()) { diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index d4553883..f7992897 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -44,7 +44,7 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { view.sqlWithTag(sb, ctx); if(!isEmpty(filters)) { var val = ctx.withValue(); //literal filters - sb.append(" ON ").forEach(filters, AND.sql(), f-> f.sql(sb, val)); + sb.append(" ON ").runForeach(filters, AND.sql(), f-> f.sql(sb, val)); } //else cross join } From 94dfdc8656046471b0e6432f5c996bfbf35dfc3e Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 12 Sep 2024 13:08:56 +0200 Subject: [PATCH 263/298] edit --- src/main/java/org/usf/jquery/core/Database.java | 9 ++++++++- src/main/java/org/usf/jquery/core/QueryBuilder.java | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/Database.java b/src/main/java/org/usf/jquery/core/Database.java index 9004e79a..726ab90b 100644 --- a/src/main/java/org/usf/jquery/core/Database.java +++ b/src/main/java/org/usf/jquery/core/Database.java @@ -1,5 +1,7 @@ package org.usf.jquery.core; +import static java.util.Objects.nonNull; + import java.util.Optional; import java.util.stream.Stream; @@ -19,7 +21,12 @@ public static Database currentDatabase() { } static void setCurrentDatabase(Database db) { - local.set(db); + if(nonNull(db)) { + local.set(db); + } + else { + local.remove(); + } } public static Optional of(String name) { diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 0c969cd5..cfe47bc2 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -242,6 +242,6 @@ void fetch(SqlStringBuilder sb) { @Override public String toString() { - return this.build().getQuery(); + return build().getQuery(); } } From e32c42802b268b4452692080541dd709761f4227 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 12 Sep 2024 13:09:17 +0200 Subject: [PATCH 264/298] edit --- .../java/org/usf/jquery/web/RequestParameterResolver.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/RequestParameterResolver.java b/src/main/java/org/usf/jquery/web/RequestParameterResolver.java index 6d833f61..6daa8a0c 100644 --- a/src/main/java/org/usf/jquery/web/RequestParameterResolver.java +++ b/src/main/java/org/usf/jquery/web/RequestParameterResolver.java @@ -55,10 +55,6 @@ public QueryBuilder requestQuery(@NonNull RequestQueryParam ant, @NonNull Map Date: Thu, 12 Sep 2024 18:14:21 +0200 Subject: [PATCH 265/298] edit --- .../org/usf/jquery/core/FunctionOperator.java | 2 +- .../org/usf/jquery/core/KeyValueMapper.java | 2 +- .../org/usf/jquery/core/SqlStringBuilder.java | 23 +++++++++++-------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/FunctionOperator.java b/src/main/java/org/usf/jquery/core/FunctionOperator.java index 8d0c823b..9f2356e5 100644 --- a/src/main/java/org/usf/jquery/core/FunctionOperator.java +++ b/src/main/java/org/usf/jquery/core/FunctionOperator.java @@ -15,6 +15,6 @@ default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { } default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args, int from) { - sb.function(id(), ()-> ctx.appendLiteralArray(sb, args, from)); //avoid array copy + sb.function(id(), ()-> ctx.appendLiteralArray(sb, args, from)); //avoid sub array } } diff --git a/src/main/java/org/usf/jquery/core/KeyValueMapper.java b/src/main/java/org/usf/jquery/core/KeyValueMapper.java index 8c2980e1..ca589884 100644 --- a/src/main/java/org/usf/jquery/core/KeyValueMapper.java +++ b/src/main/java/org/usf/jquery/core/KeyValueMapper.java @@ -33,4 +33,4 @@ public List map(ResultSet rs) throws SQLException { log.trace("{} rows mapped in {} ms", res.size(), currentTimeMillis()-t); return res; } -} +} \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index e097e1ef..04431207 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -65,19 +65,22 @@ public SqlStringBuilder runForeach(T[] arr, String delimiter, Consumer fn } public SqlStringBuilder runForeach(T[] arr, int idx, String delimiter, Consumer fn, String prefix, String suffix) { - if(idx < 0 || (isEmpty(arr) && idx > 0) || idx >= requireNonNull(arr, "arr connot be null").length) { - throw new IndexOutOfBoundsException(idx); - } - sb.append(prefix); - if(!isEmpty(arr)) { //idx < arr.length - var i=idx; - fn.accept(arr[i]); - for(++i; i Date: Thu, 12 Sep 2024 20:43:43 +0200 Subject: [PATCH 266/298] edit --- src/main/java/org/usf/jquery/core/ArithmeticOperator.java | 2 +- src/main/java/org/usf/jquery/core/SqlStringBuilder.java | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java index 301718c4..566d4e22 100644 --- a/src/main/java/org/usf/jquery/core/ArithmeticOperator.java +++ b/src/main/java/org/usf/jquery/core/ArithmeticOperator.java @@ -8,7 +8,7 @@ * */ @FunctionalInterface -interface ArithmeticOperator extends Operator { +public interface ArithmeticOperator extends Operator { @Override default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index 04431207..e07ea794 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -66,9 +66,9 @@ public SqlStringBuilder runForeach(T[] arr, String delimiter, Consumer fn public SqlStringBuilder runForeach(T[] arr, int idx, String delimiter, Consumer fn, String prefix, String suffix) { requireNonNull(arr, "arr connot be null"); - if((arr.length == 0 && idx == 0) || idx < arr.length) { + if( idx < arr.length || (arr.length == 0 && idx == 0)) { sb.append(prefix); - if(!isEmpty(arr)) { //idx < arr.length + if(!isEmpty(arr)) { var i=idx; fn.accept(arr[i]); for(++i; i SqlStringBuilder runForeach(Iterator it, String separator, Consume } return this; } - - public SqlStringBuilder from(String v) { - return from().append(v); - } public SqlStringBuilder from() { return append(" FROM "); From d547523cd624573fbbad692a4eefdc7d09264a54 Mon Sep 17 00:00:00 2001 From: u$f Date: Thu, 12 Sep 2024 20:44:59 +0200 Subject: [PATCH 267/298] edit --- src/main/java/org/usf/jquery/core/CastFunction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CastFunction.java b/src/main/java/org/usf/jquery/core/CastFunction.java index 5bd8f09f..34076961 100644 --- a/src/main/java/org/usf/jquery/core/CastFunction.java +++ b/src/main/java/org/usf/jquery/core/CastFunction.java @@ -10,7 +10,7 @@ @FunctionalInterface public interface CastFunction extends FunctionOperator { - String asType(); + String type(); @Override default String id() { @@ -22,7 +22,7 @@ default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireAtLeastNArgs(1, args, CastFunction.class::getSimpleName); sb.function(id(), ()-> { ctx.appendLiteral(sb, args[0]); - sb.as(asType()); + sb.as(type()); if(args.length > 1) { sb.parenthesis(()-> ctx.appendLiteralArray(sb, args, 1)); } From 7d1548ebdffebd657dfac9a7d884618a89315dc7 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 13 Sep 2024 13:41:35 +0200 Subject: [PATCH 268/298] edit --- src/main/java/org/usf/jquery/core/OperationColumn.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 26f82c7e..b25ea6f0 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -59,7 +59,8 @@ else if(operator.is("OVER")) { } throw new UnsupportedOperationException("require only one view"); } - return requirePartition().resolve(ctx); //no aggregation + requirePartition().resolve(ctx); //no aggregation + return true; } return !operator.is(ConstantOperator.class) && tryResolveAll(ctx, args); } From 919fa5393919e6e431f42e7f8dcd14fd03cf50a1 Mon Sep 17 00:00:00 2001 From: u$f Date: Fri, 13 Sep 2024 15:00:39 +0200 Subject: [PATCH 269/298] edit --- src/main/java/org/usf/jquery/core/OperationColumn.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index b25ea6f0..bc3ddba3 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -55,12 +55,12 @@ else if(operator.is("OVER")) { var cTag = "over_" + hashCode(); //over_view_hash ctx.overView(view).getBuilder().columns(new OperationColumn(operator, args, type).as(cTag)); //clone overColumn = new ViewColumn(cTag, view, type, null); - return false; + return overColumn.resolve(ctx); } throw new UnsupportedOperationException("require only one view"); } - requirePartition().resolve(ctx); //no aggregation - return true; + requirePartition().resolve(ctx); + return true; //!group by } return !operator.is(ConstantOperator.class) && tryResolveAll(ctx, args); } From ec537fd9f2b5808135e412655f6f8c265cb598ac Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 16 Sep 2024 12:01:31 +0200 Subject: [PATCH 270/298] edit --- .../java/org/usf/jquery/core/CaseColumn.java | 13 ++-- .../usf/jquery/core/ColumnFilterGroup.java | 16 ++-- .../usf/jquery/core/ColumnSingleFilter.java | 18 ++--- .../core/ComparisonExpressionGroup.java | 18 ++--- .../core/ComparisonSingleExpression.java | 16 ++-- .../java/org/usf/jquery/core/DBColumn.java | 1 + .../java/org/usf/jquery/core/DBOrder.java | 10 +-- src/main/java/org/usf/jquery/core/DBView.java | 12 +++ src/main/java/org/usf/jquery/core/Nested.java | 73 ++++++++--------- .../org/usf/jquery/core/OperationColumn.java | 35 ++++----- .../java/org/usf/jquery/core/Partition.java | 28 ++++--- .../org/usf/jquery/core/QueryBuilder.java | 78 +++++++++++++------ .../org/usf/jquery/core/SqlStringBuilder.java | 12 ++- .../java/org/usf/jquery/core/ValueColumn.java | 13 ++-- .../java/org/usf/jquery/core/ViewColumn.java | 16 ++-- .../java/org/usf/jquery/core/WhenCase.java | 21 ++--- 16 files changed, 200 insertions(+), 180 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index ca0ca91a..5598a86f 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -1,11 +1,10 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Nested.resolveAll; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; -import java.util.Collection; import java.util.Objects; +import java.util.function.Consumer; import java.util.stream.Stream; /** @@ -36,15 +35,13 @@ public JDBCType getType() { } @Override - public boolean resolve(QueryBuilder builder) { - return resolveAll(whenCases, builder); + public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + return Nested.tryResolve(builder, groupKeys, (Object[])whenCases); } @Override - public void views(Collection views) { - for(var e : whenCases) { - e.views(views); - } + public void views(Consumer cons) { + Nested.viewsOf(cons, (Object[])whenCases); } @Override diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index 681a30e3..4f2ec302 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -1,11 +1,9 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Nested.resolveAll; -import static org.usf.jquery.core.Nested.viewsOfNested; import static org.usf.jquery.core.Utils.appendLast; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; -import java.util.Collection; +import java.util.function.Consumer; /** * @@ -28,17 +26,17 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { sb.parenthesis(()-> sb.runForeach(filters, operator.sql(), o-> o.sql(sb, ctx))); } - + @Override - public boolean resolve(QueryBuilder ctx) { - return resolveAll(filters, ctx); + public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + return Nested.tryResolve(builder, groupKeys, (Object[])filters); } @Override - public void views(Collection views) { - viewsOfNested(views, filters); + public void views(Consumer cons) { + Nested.viewsOf(cons, (Object[])filters); } - + @Override public DBFilter append(LogicalOperator op, DBFilter filter) { return operator == op diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index 4ffe6c4c..16154a9d 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -1,9 +1,6 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Nested.tryResolve; -import static org.usf.jquery.core.Nested.viewsOf; - -import java.util.Collection; +import java.util.function.Consumer; import lombok.RequiredArgsConstructor; @@ -24,18 +21,15 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public boolean resolve(QueryBuilder ctx) { - var res1 = tryResolve(left, ctx); - var res2 = expression.resolve(ctx); - return res1 || res2; + public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + return Nested.tryResolve(builder, groupKeys, left, expression); } @Override - public void views(Collection views) { - viewsOf(views, left); - expression.views(views); + public void views(Consumer cons) { + Nested.viewsOf(cons, left, expression); } - + @Override public ColumnFilterGroup append(LogicalOperator op, DBFilter filter) { return new ColumnFilterGroup(op, this, filter); diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index 0f11686c..f4f981a7 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -1,11 +1,9 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Nested.resolveAll; -import static org.usf.jquery.core.Nested.viewsOfNested; import static org.usf.jquery.core.Utils.appendLast; import static org.usf.jquery.core.Validation.requireAtLeastNArgs; -import java.util.Collection; +import java.util.function.Consumer; /** * @@ -30,13 +28,8 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object operand) { } @Override - public boolean resolve(QueryBuilder builder) { - return resolveAll(expressions, builder); - } - - @Override - public void views(Collection views) { - viewsOfNested(views, expressions); + public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + return Nested.tryResolve(builder, groupKeys, (Object[])expressions); } @Override @@ -46,6 +39,11 @@ public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) : new ComparisonExpressionGroup(op, this, exp); } + @Override + public void views(Consumer cons) { + Nested.viewsOf(cons, (Object[])expressions); + } + @Override public String toString() { return DBObject.toSQL(this, ""); diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index fcb05d14..c5ad8fcd 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -2,11 +2,9 @@ import static java.util.Collections.addAll; import static java.util.Objects.nonNull; -import static org.usf.jquery.core.Nested.tryResolveAll; -import static org.usf.jquery.core.Nested.viewsOfAll; import java.util.ArrayList; -import java.util.Collection; +import java.util.function.Consumer; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -33,15 +31,15 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object left) { } @Override - public boolean resolve(QueryBuilder builder) { - return tryResolveAll(builder, right); + public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + return Nested.tryResolve(builder, groupKeys, right); } - + @Override - public void views(Collection views) { - viewsOfAll(views, right); + public void views(Consumer cons) { + Nested.viewsOf(cons, right); } - + @Override public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) { return new ComparisonExpressionGroup(op, this, exp); diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index eff6102f..00a75120 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -7,6 +7,7 @@ import static org.usf.jquery.core.Validation.requireLegalVariable; import static org.usf.jquery.core.Validation.requireNoArgs; +import java.util.function.Consumer; import java.util.function.Supplier; import org.usf.jquery.core.JavaType.Typed; diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index f7415307..89536e10 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -2,7 +2,7 @@ import static org.usf.jquery.core.Validation.requireNoArgs; -import java.util.Collection; +import java.util.function.Consumer; import lombok.AccessLevel; import lombok.Getter; @@ -36,13 +36,13 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public boolean resolve(QueryBuilder builder) { - return column.resolve(builder); + public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + return column.resolve(builder, groupKeys); } @Override - public void views(Collection views) { - column.views(views); + public void views(Consumer cons) { + column.views(cons); } @Override diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index c6a9a2ef..8d0bf5a1 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -22,4 +22,16 @@ default void sqlWithTag(SqlStringBuilder sb, QueryContext ctx) { ctx.viewOverload(this).orElse(this).sql(sb, ctx); //!important sb.space().append(ctx.viewAlias(this)); } + + default ViewColumn column(String name) { + return new ViewColumn(name, this, null, null); + } + + default ViewColumn column(String name, JDBCType type) { + return new ViewColumn(name, this, type, null); + } + + default ViewColumn column(String name, JDBCType type, String tag) { + return new ViewColumn(name, this, type, tag); + } } diff --git a/src/main/java/org/usf/jquery/core/Nested.java b/src/main/java/org/usf/jquery/core/Nested.java index 6dee4754..f512f429 100644 --- a/src/main/java/org/usf/jquery/core/Nested.java +++ b/src/main/java/org/usf/jquery/core/Nested.java @@ -1,10 +1,10 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.Clause.FILTER; import static org.usf.jquery.core.Utils.isEmpty; -import java.util.Collection; +import java.util.ArrayList; import java.util.function.Consumer; -import java.util.function.Predicate; /** * @@ -12,53 +12,44 @@ * */ public interface Nested { - - boolean resolve(QueryBuilder builder); - void views(Collection views); - - static boolean tryResolveAll(QueryBuilder builder, Object... args){ - return resolveAll(args, o-> tryResolve(o, builder)); - } - - static boolean resolveAll(Nested[] arr, QueryBuilder builder){ - return resolveAll(arr, n-> n.resolve(builder)); - } + void views(Consumer cons); //collect used views - static boolean resolveAll(T[] arr, Predicate fn){ - var res = false; - if(!isEmpty(arr)) { - for(var o : arr) { - res |= fn.test(o); + boolean resolve(QueryBuilder builder, Consumer cons); + + static void viewsOf(Consumer cons, Object... args) { //collect used views + if(!isEmpty(args)) { + for(var o : args) { + if(o instanceof Nested n) { + n.views(cons); + } } } - return res; - } - - static boolean tryResolve(Object o, QueryBuilder builder) { - return o instanceof Nested n && n.resolve(builder); } - - static void viewsOfNested(Collection views, T[] arr) { - viewsOfAll(arr, o-> o.views(views)); - } - - static void viewsOfAll(Collection views, Object[] arr) { - viewsOfAll(arr, o-> viewsOf(views, o)); - } - - static void viewsOfAll(T[] arr, Consumer cons) { - if(!isEmpty(arr)) { - for(var o : arr) { - cons.accept(o); + static boolean tryResolve(QueryBuilder builder, Consumer cons, Object... args){ + if(!isEmpty(args)) { + if(builder.getClause() == FILTER) { + for(var o : args) { + if(tryResolve(o, builder, cons)) { + return true; //break + } + } + } //else WhenCase filter + var arr = new ArrayList(); + var agg = false; + for(var o : args) { + agg |= tryResolve(o, builder, arr::add); } + if(agg && !arr.isEmpty()) { //partial aggregation + arr.forEach(cons); + } + return agg; } + return false; } - - static void viewsOf(Collection views, Object o) { - if(o instanceof Nested n) { - n.views(views); - } + + static boolean tryResolve(Object o, QueryBuilder builder, Consumer cons) { + return o instanceof Nested n && n.resolve(builder, cons); } } diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index bc3ddba3..90ac74c6 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -2,12 +2,10 @@ import static java.util.Objects.nonNull; import static org.usf.jquery.core.Clause.FILTER; -import static org.usf.jquery.core.Nested.tryResolveAll; -import static org.usf.jquery.core.Nested.viewsOfAll; import static org.usf.jquery.core.Validation.requireNArgs; -import java.util.Collection; import java.util.HashSet; +import java.util.function.Consumer; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -28,7 +26,7 @@ public final class OperationColumn implements DBColumn { @Override public void sql(SqlStringBuilder sb, QueryContext ctx) { if(nonNull(overColumn)) { - overColumn.sql(sb, ctx); + overColumn.sql(sb, ctx); //no args } else { operator.sql(sb, ctx, args); @@ -41,37 +39,36 @@ public JDBCType getType() { } @Override - public boolean resolve(QueryBuilder ctx) { + public boolean resolve(QueryBuilder builder, Consumer groupKeys) { if(operator.is(AggregateFunction.class)) { - ctx.aggregation(); - return true; + builder.setAggregation(true); + return true; //aggregate function calls cannot be nested } - else if(operator.is("OVER")) { - if(ctx.getClause() == FILTER) { + if(operator.is("OVER")) { + if(builder.getClause() == FILTER) { var views = new HashSet(); - views(views); + views(views::add); if(views.size() == 1) { var view = views.iterator().next(); var cTag = "over_" + hashCode(); //over_view_hash - ctx.overView(view).getBuilder().columns(new OperationColumn(operator, args, type).as(cTag)); //clone + builder.overView(view).getBuilder().columns(new OperationColumn(operator, args, type).as(cTag)); //clone overColumn = new ViewColumn(cTag, view, type, null); - return overColumn.resolve(ctx); + return overColumn.resolve(builder, groupKeys); } - throw new UnsupportedOperationException("require only one view"); + throw new UnsupportedOperationException("over multiple views"); } - requirePartition().resolve(ctx); - return true; //!group by + return requirePartition().resolve(builder, groupKeys); //!grouping keys } - return !operator.is(ConstantOperator.class) && tryResolveAll(ctx, args); + return !operator.is(ConstantOperator.class) && Nested.tryResolve(builder, groupKeys, args); } @Override - public void views(Collection views) { + public void views(Consumer cons) { if(nonNull(overColumn)) { - overColumn.views(views); + overColumn.views(cons); } else { - viewsOfAll(views, args); + Nested.viewsOf(cons, args); } } diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index 93cf60a6..c7b77333 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -1,12 +1,11 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Nested.resolveAll; -import static org.usf.jquery.core.Nested.viewsOfNested; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireNoArgs; -import java.util.Collection; +import java.util.function.Consumer; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; @@ -40,16 +39,25 @@ void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public boolean resolve(QueryBuilder builder) { - var r1 = resolveAll(columns, builder); - var r2 = resolveAll(orders, builder); - return r1 || r2; + public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + if(!isEmpty(columns)) { + builder.group(Stream.of(columns) + .filter(c-> !c.resolve(builder, groupKeys)) + .toArray(DBColumn[]::new)); + } + if(!isEmpty(orders)) { + builder.group(Stream.of(orders) + .filter(c-> !c.resolve(builder, groupKeys)) + .map(DBOrder::getColumn) + .toArray(DBColumn[]::new)); + } + return true; //!grouping keys } @Override - public void views(Collection views) { - viewsOfNested(views, columns); - viewsOfNested(views, orders); + public void views(Consumer cons) { + Nested.viewsOf(cons, (Object[])columns); + Nested.viewsOf(cons, (Object[])orders); } @Override diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index cfe47bc2..99406967 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -24,10 +24,13 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import java.util.function.Supplier; +import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; /** @@ -39,14 +42,16 @@ @Getter public class QueryBuilder { + private final List ctes = new ArrayList<>(); private final List columns = new ArrayList<>(); private final List group = new ArrayList<>(); private final List where = new ArrayList<>(); private final List having = new ArrayList<>(); - private final List orders = new ArrayList<>(); private final List joins = new ArrayList<>(); + private final List orders = new ArrayList<>(); private final Map overView = new HashMap<>(); private boolean distinct; + @Setter(AccessLevel.PACKAGE) private boolean aggregation; private Integer limit; private Integer offset; @@ -69,6 +74,11 @@ public QueryView overView(DBView view, Supplier supp) { return overView.computeIfAbsent(view, k-> supp.get()); } + public QueryBuilder ctes(@NonNull QueryView... ctes) { + addAll(this.ctes, ctes); + return this; + } + public QueryBuilder columns(@NonNull NamedColumn... columns) { this.clause = COLUMN; for(var col : columns) { //optional tag @@ -77,32 +87,37 @@ public QueryBuilder columns(@NonNull NamedColumn... columns) { .anyMatch(nc-> nc.getTag().equals(col.getTag()))) { throw resourceAlreadyExistsException(col.getTag()); } - this.columns.add(col); - if(!col.resolve(this)) { - group.add(col); + if(this.columns.add(col) && !col.resolve(this, group::add)) { + this.group.add(col); } } return this; } - - public QueryBuilder filters(@NonNull DBFilter... filters){ - this.clause = FILTER; - for(var f : filters) { - (f.resolve(this) ? having : where).add(f); - } - return this; - } public QueryBuilder orders(@NonNull DBOrder... orders) { this.clause = ORDER; for(var o : orders) { - if(this.orders.add(o) && !o.resolve(this)) { + if(this.orders.add(o) && !o.resolve(this, group::add)) { this.group.add(o.getColumn()); } } return this; } + public QueryBuilder filters(@NonNull DBFilter... filters){ + this.clause = FILTER; + Consumer noCons = v->{}; + for(var f : filters) { + (f.resolve(this, noCons) ? having : where).add(f); + } + return this; + } + + QueryBuilder group(@NonNull DBColumn... columns){ + addAll(this.group, columns); + return this; + } + public QueryBuilder joins(@NonNull ViewJoin... joins) { addAll(this.joins, joins); return this; @@ -128,11 +143,6 @@ public QueryBuilder repeat(@NonNull Iterator it) { return this; } - QueryBuilder aggregation() { - this.aggregation = true; - return this; - } - public QueryView asView() { return new QueryView(this); } @@ -158,17 +168,29 @@ public RequestQuery build(String schema) { } public final void build(SqlStringBuilder sb, QueryContext ctx){ - var sub = new SqlStringBuilder(100); - where(sub, ctx); //first resolve over view + var sub = new SqlStringBuilder(500); + with(sb, ctx); + select(sb, ctx); + join(sub, ctx); + where(sub, ctx); groupBy(sub, ctx); having(sub, ctx); orderBy(sub, ctx); fetch(sub); - select(sb, ctx); from(sb, ctx); //enumerate all views before from clause - join(sb, ctx); sb.append(sub.toString()); } + + void with(SqlStringBuilder sb, QueryContext ctx) { + if(!ctes.isEmpty()) { + sb.append("WITH ") + .runForeach(ctes.iterator(), SCOMA, v->{ + sb.append(ctx.viewAlias(v)).as(); + v.sql(sb, ctx); //query parenthesis + }) + .space(); + } + } void select(SqlStringBuilder sb, QueryContext ctx){ if(currentDatabase() == TERADATA) { @@ -187,8 +209,16 @@ void select(SqlStringBuilder sb, QueryContext ctx){ } void from(SqlStringBuilder sb, QueryContext ctx) { - var excludes = joins.stream().map(ViewJoin::getView).toList(); - var views = ctx.views().stream().filter(not(excludes::contains)).toList(); //do not remove views + var excludes = new ArrayList(); + if(!ctes.isEmpty()) { + ctes.forEach(excludes::add); + } + if(!joins.isEmpty()) { + joins.forEach(j-> excludes.add(j.getView())); + } + var views = excludes.isEmpty() + ? ctx.views() + : ctx.views().stream().filter(not(excludes::contains)).toList(); //do not remove views if(!views.isEmpty()) { sb.from().runForeach(views.iterator(), SCOMA, v-> v.sqlWithTag(sb, ctx)); } diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index e07ea794..710884b2 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -94,13 +94,17 @@ public SqlStringBuilder runForeach(Iterator it, String separator, Consume } return this; } + + public SqlStringBuilder as(String v) { + return as().append(v); + } - public SqlStringBuilder from() { - return append(" FROM "); + public SqlStringBuilder as() { + return append(" AS "); } - public SqlStringBuilder as(String v) { - return append(" AS ").append(v); + public SqlStringBuilder from() { + return append(" FROM "); } public SqlStringBuilder function(String name, Runnable args) { diff --git a/src/main/java/org/usf/jquery/core/ValueColumn.java b/src/main/java/org/usf/jquery/core/ValueColumn.java index afa24d2a..9e7d13de 100644 --- a/src/main/java/org/usf/jquery/core/ValueColumn.java +++ b/src/main/java/org/usf/jquery/core/ValueColumn.java @@ -1,8 +1,9 @@ package org.usf.jquery.core; +import static org.usf.jquery.core.Clause.FILTER; import static org.usf.jquery.core.QueryContext.formatValue; -import java.util.Collection; +import java.util.function.Consumer; import java.util.function.Supplier; import lombok.RequiredArgsConstructor; @@ -29,15 +30,15 @@ public JDBCType getType() { } @Override - public boolean resolve(QueryBuilder builder) { - return false; + public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + return builder.getClause() != FILTER; //COLUMN=> TRUE || FILTER=> FALSE } @Override - public void views(Collection views) { - //do nothing + public void views(Consumer cons) { + //no views } - + @Override public String toString() { return DBObject.toSQL(this); diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index da55e717..d4776f52 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -1,8 +1,6 @@ package org.usf.jquery.core; -import static java.util.Objects.nonNull; - -import java.util.Collection; +import java.util.function.Consumer; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -26,14 +24,14 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { sb.appendIfNonNull(view, v-> ctx.viewAlias(v) + '.').append(name); } - public boolean resolve(QueryBuilder builder) { + @Override + public boolean resolve(QueryBuilder builder, Consumer groupKeys) { return false; } - - public void views(Collection views) { - if(nonNull(view)) { - views.add(view); - } + + @Override + public void views(Consumer cons) { + cons.accept(view); } @Override diff --git a/src/main/java/org/usf/jquery/core/WhenCase.java b/src/main/java/org/usf/jquery/core/WhenCase.java index 45a5db03..6b2bdfb2 100644 --- a/src/main/java/org/usf/jquery/core/WhenCase.java +++ b/src/main/java/org/usf/jquery/core/WhenCase.java @@ -2,11 +2,9 @@ import static java.util.Objects.nonNull; import static org.usf.jquery.core.JDBCType.typeOf; -import static org.usf.jquery.core.Nested.tryResolve; -import static org.usf.jquery.core.Nested.viewsOf; import static org.usf.jquery.core.Validation.requireNoArgs; -import java.util.Collection; +import java.util.function.Consumer; import org.usf.jquery.core.JavaType.Typed; @@ -47,20 +45,15 @@ public JDBCType getType() { } @Override - public boolean resolve(QueryBuilder builder) { - var r1 = nonNull(filter) && filter.resolve(builder); - var r2 = tryResolve(value, builder); - return r1 || r2; + public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + return Nested.tryResolve(builder, groupKeys, filter, value); } - + @Override - public void views(Collection views) { - if(nonNull(filter)) { - filter.views(views); - } - viewsOf(views, value); + public void views(Consumer cons) { + Nested.viewsOf(cons, filter, value); } - + @Override public String toString() { return DBObject.toSQL(this); From abe048af5d034f709499951c966b5a90be398f7a Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 16 Sep 2024 12:50:26 +0200 Subject: [PATCH 271/298] edit --- src/main/java/org/usf/jquery/core/DBColumn.java | 4 ++++ .../org/usf/jquery/core/OperationColumn.java | 4 ++-- .../java/org/usf/jquery/core/Partition.java | 14 +++++++------- .../java/org/usf/jquery/core/QueryBuilder.java | 17 +++++++++-------- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 00a75120..29c9b358 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -373,6 +373,10 @@ default OperationColumn avg() { //pipe functions + default OperationColumn over(DBColumn[] cols, DBOrder[] orders) { + return over(new Partition(cols, orders)); + } + default OperationColumn over(Partition part) { return Operator.over().operation(this, part); } diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 90ac74c6..920e9120 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -55,11 +55,11 @@ public boolean resolve(QueryBuilder builder, Consumer groupKey overColumn = new ViewColumn(cTag, view, type, null); return overColumn.resolve(builder, groupKeys); } - throw new UnsupportedOperationException("over multiple views"); + throw new UnsupportedOperationException("over require only one view"); } return requirePartition().resolve(builder, groupKeys); //!grouping keys } - return !operator.is(ConstantOperator.class) && Nested.tryResolve(builder, groupKeys, args); + return operator.is(ConstantOperator.class) || Nested.tryResolve(builder, groupKeys, args); } @Override diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index c7b77333..6a86bb07 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -41,15 +41,15 @@ void sql(SqlStringBuilder sb, QueryContext ctx) { @Override public boolean resolve(QueryBuilder builder, Consumer groupKeys) { if(!isEmpty(columns)) { - builder.group(Stream.of(columns) - .filter(c-> !c.resolve(builder, groupKeys)) - .toArray(DBColumn[]::new)); + Stream.of(columns) + .filter(c-> !c.resolve(builder, groupKeys)) + .forEach(groupKeys); } if(!isEmpty(orders)) { - builder.group(Stream.of(orders) - .filter(c-> !c.resolve(builder, groupKeys)) - .map(DBOrder::getColumn) - .toArray(DBColumn[]::new)); + Stream.of(orders) + .filter(c-> !c.resolve(builder, groupKeys)) + .map(DBOrder::getColumn) + .forEach(groupKeys); } return true; //!grouping keys } diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 99406967..9ef02a30 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -20,9 +20,10 @@ import static org.usf.jquery.web.ResourceAccessException.resourceAlreadyExistsException; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Supplier; @@ -42,13 +43,13 @@ @Getter public class QueryBuilder { - private final List ctes = new ArrayList<>(); - private final List columns = new ArrayList<>(); - private final List group = new ArrayList<>(); - private final List where = new ArrayList<>(); - private final List having = new ArrayList<>(); - private final List joins = new ArrayList<>(); - private final List orders = new ArrayList<>(); + private final Collection ctes = new ArrayList<>(); + private final Collection columns = new ArrayList<>(); + private final Collection group = new HashSet<>(); + private final Collection where = new ArrayList<>(); + private final Collection having = new ArrayList<>(); + private final Collection joins = new ArrayList<>(); + private final Collection orders = new ArrayList<>(); private final Map overView = new HashMap<>(); private boolean distinct; @Setter(AccessLevel.PACKAGE) From f8bafebe4c1b31a1c48b4795f31a1bb66d3ac192 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 16 Sep 2024 14:26:53 +0200 Subject: [PATCH 272/298] edit --- .../java/org/usf/jquery/core/ColumnProxy.java | 19 +++++++++++++++-- .../java/org/usf/jquery/core/DBColumn.java | 5 +++++ src/main/java/org/usf/jquery/core/Nested.java | 4 ++-- .../org/usf/jquery/core/QueryBuilder.java | 2 +- .../usf/jquery/core/SingleColumnQuery.java | 5 +++-- .../org/usf/jquery/core/TypedComparator.java | 16 ++++++++------ .../org/usf/jquery/core/TypedOperator.java | 21 ++++++++++++++++--- 7 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ColumnProxy.java b/src/main/java/org/usf/jquery/core/ColumnProxy.java index 4e358efd..39042b69 100644 --- a/src/main/java/org/usf/jquery/core/ColumnProxy.java +++ b/src/main/java/org/usf/jquery/core/ColumnProxy.java @@ -3,10 +3,10 @@ import static java.util.Objects.nonNull; import java.util.Objects; +import java.util.function.Consumer; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; /** * @@ -16,7 +16,7 @@ @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class ColumnProxy implements NamedColumn { - @Delegate + //do not @Delegate private final DBColumn column; private final JDBCType type; //optional private final String tag; //optional @@ -25,6 +25,21 @@ public final class ColumnProxy implements NamedColumn { public String getTag() { return tag; } + + @Override + public void sql(SqlStringBuilder sb, QueryContext ctx) { + column.sql(sb, ctx); + } + + @Override + public boolean resolve(QueryBuilder builder, Consumer cons) { + return column.resolve(builder, cons); + } + + @Override + public void views(Consumer cons) { + column.views(cons); + } @Override public JDBCType getType() { diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index 29c9b358..c3f09098 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -446,6 +446,11 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { ctx.viewAlias(view); sb.append(getName()); //avoid view.* } + + @Override + public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + return true; //!group by + } }; } diff --git a/src/main/java/org/usf/jquery/core/Nested.java b/src/main/java/org/usf/jquery/core/Nested.java index f512f429..bd25b7ce 100644 --- a/src/main/java/org/usf/jquery/core/Nested.java +++ b/src/main/java/org/usf/jquery/core/Nested.java @@ -13,10 +13,10 @@ */ public interface Nested { - void views(Consumer cons); //collect used views - boolean resolve(QueryBuilder builder, Consumer cons); + void views(Consumer cons); //collect used views + static void viewsOf(Consumer cons, Object... args) { //collect used views if(!isEmpty(args)) { for(var o : args) { diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 9ef02a30..c180d9cd 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -89,7 +89,7 @@ public QueryBuilder columns(@NonNull NamedColumn... columns) { throw resourceAlreadyExistsException(col.getTag()); } if(this.columns.add(col) && !col.resolve(this, group::add)) { - this.group.add(col); + this.group.add(col); //distinct TD: override equals } } return this; diff --git a/src/main/java/org/usf/jquery/core/SingleColumnQuery.java b/src/main/java/org/usf/jquery/core/SingleColumnQuery.java index 7c4f7ad9..1ec7b451 100644 --- a/src/main/java/org/usf/jquery/core/SingleColumnQuery.java +++ b/src/main/java/org/usf/jquery/core/SingleColumnQuery.java @@ -16,8 +16,9 @@ public final class SingleColumnQuery implements DBObject, Typed { SingleColumnQuery(QueryView query) { this.query = query; - if(query.getBuilder().getColumns().size() == 1) { - this.type = query.getBuilder().getColumns().get(0).getType(); + var cols = query.getBuilder().getColumns(); + if(cols.size() == 1) { + this.type = cols.iterator().next().getType(); } else{ throw new IllegalArgumentException("require only one column"); diff --git a/src/main/java/org/usf/jquery/core/TypedComparator.java b/src/main/java/org/usf/jquery/core/TypedComparator.java index 0e220554..2129aff5 100644 --- a/src/main/java/org/usf/jquery/core/TypedComparator.java +++ b/src/main/java/org/usf/jquery/core/TypedComparator.java @@ -4,7 +4,6 @@ import static org.usf.jquery.core.ParameterSet.ofParameters; import lombok.Getter; -import lombok.experimental.Delegate; /** * @@ -13,8 +12,8 @@ */ @Getter public final class TypedComparator implements Comparator { - - @Delegate + + //do not @Delegate private final Comparator comparator; private final ParameterSet parameterSet; @@ -31,10 +30,15 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { throw badArgumentsException("comparator", comparator.id(), args, e); } } + + @Override + public String id() { + return comparator.id(); + } - @Override // do not delegate this - public ColumnSingleFilter filter(Object... args) { - return Comparator.super.filter(args); + @Override + public boolean is(Class type) { + return comparator.is(type); } @Override diff --git a/src/main/java/org/usf/jquery/core/TypedOperator.java b/src/main/java/org/usf/jquery/core/TypedOperator.java index 5ad0aca9..1c287671 100644 --- a/src/main/java/org/usf/jquery/core/TypedOperator.java +++ b/src/main/java/org/usf/jquery/core/TypedOperator.java @@ -4,7 +4,6 @@ import static org.usf.jquery.core.ParameterSet.ofParameters; import lombok.Getter; -import lombok.experimental.Delegate; /** * @@ -13,8 +12,8 @@ */ @Getter public class TypedOperator implements Operator { - - @Delegate + + //do not @Delegate private final Operator operator; private final ArgTypeRef typeFn; private final ParameterSet parameterSet; @@ -38,6 +37,12 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { throw badArgumentsException("operator", operator.id(), args, e); } } + + @Override + public String id() { + return operator.id(); + } + public OperationColumn operation(Object... args) { return this.operation(typeFn.apply(args), args); @@ -50,6 +55,16 @@ public OperationColumn operation(JDBCType type, Object... args) { : Operator.super.operation(type, args); } + @Override + public boolean is(Class type) { + return operator.is(type); + } + + @Override + public boolean is(String name) { + return operator.is(name); + } + public boolean isWindowFunction() { return operator.is(WindowFunction.class); } From 3f2b7efd4d2e65d891974f6a39a5b99ea5d0c82d Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 16 Sep 2024 16:10:54 +0200 Subject: [PATCH 273/298] edit --- src/main/java/org/usf/jquery/core/ColumnProxy.java | 10 +++++----- src/main/java/org/usf/jquery/core/Partition.java | 4 ---- .../java/org/usf/jquery/core/SingleColumnQuery.java | 4 ---- src/main/java/org/usf/jquery/core/ValueColumn.java | 2 +- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ColumnProxy.java b/src/main/java/org/usf/jquery/core/ColumnProxy.java index 39042b69..c744aa25 100644 --- a/src/main/java/org/usf/jquery/core/ColumnProxy.java +++ b/src/main/java/org/usf/jquery/core/ColumnProxy.java @@ -30,6 +30,11 @@ public String getTag() { public void sql(SqlStringBuilder sb, QueryContext ctx) { column.sql(sb, ctx); } + + @Override + public JDBCType getType() { + return nonNull(type) ? type : column.getType(); + } @Override public boolean resolve(QueryBuilder builder, Consumer cons) { @@ -41,11 +46,6 @@ public void views(Consumer cons) { column.views(cons); } - @Override - public JDBCType getType() { - return nonNull(type) ? type : column.getType(); - } - @Override // do not delegate this public ColumnProxy as(String name) { return NamedColumn.super.as(name); diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index 6a86bb07..9bacefc5 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -23,10 +23,6 @@ public final class Partition implements DBObject, Nested { @Override public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNoArgs(args, Partition.class::getSimpleName); - sql(sb, ctx); - } - - void sql(SqlStringBuilder sb, QueryContext ctx) { if(!isEmpty(columns)) { sb.append("PARTITION BY "); ctx.appendLiteralArray(sb, columns); diff --git a/src/main/java/org/usf/jquery/core/SingleColumnQuery.java b/src/main/java/org/usf/jquery/core/SingleColumnQuery.java index 1ec7b451..06981668 100644 --- a/src/main/java/org/usf/jquery/core/SingleColumnQuery.java +++ b/src/main/java/org/usf/jquery/core/SingleColumnQuery.java @@ -28,10 +28,6 @@ public final class SingleColumnQuery implements DBObject, Typed { @Override public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireNoArgs(args, SingleColumnQuery.class::getSimpleName); - sql(sb, ctx); - } - - public void sql(SqlStringBuilder sb, QueryContext ctx) { query.sql(sb, ctx); } diff --git a/src/main/java/org/usf/jquery/core/ValueColumn.java b/src/main/java/org/usf/jquery/core/ValueColumn.java index 9e7d13de..26c37780 100644 --- a/src/main/java/org/usf/jquery/core/ValueColumn.java +++ b/src/main/java/org/usf/jquery/core/ValueColumn.java @@ -31,7 +31,7 @@ public JDBCType getType() { @Override public boolean resolve(QueryBuilder builder, Consumer groupKeys) { - return builder.getClause() != FILTER; //COLUMN=> TRUE || FILTER=> FALSE + return builder.getClause() != FILTER; //COLUMN=> !GROUPBY || FILTER=> !HAVING } @Override From ccb7cc0eebcf74a46ba7d015ada7a496500175c8 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 16 Sep 2024 17:55:48 +0200 Subject: [PATCH 274/298] edit --- src/main/java/org/usf/jquery/web/QueryDecorator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/QueryDecorator.java b/src/main/java/org/usf/jquery/web/QueryDecorator.java index 80a54fac..3720b83e 100644 --- a/src/main/java/org/usf/jquery/web/QueryDecorator.java +++ b/src/main/java/org/usf/jquery/web/QueryDecorator.java @@ -1,5 +1,6 @@ package org.usf.jquery.web; +import static org.usf.jquery.core.SqlStringBuilder.doubleQuote; import static org.usf.jquery.web.NoSuchResourceException.undeclaredResouceException; import org.usf.jquery.core.DBView; @@ -37,7 +38,7 @@ public NamedColumn column(String id) { return query.getBuilder().getColumns().stream() //do not use declaredColumn .filter(c-> id.equals(c.getTag())) .findAny() - .map(c-> new ViewColumn(c.getTag(), query, c.getType(), null)) + .map(c-> new ViewColumn(doubleQuote(c.getTag()), query, c.getType(), null)) .orElseThrow(()-> undeclaredResouceException(id, identity())); } From 4e25dd11a16823eda60b918b8a7b001854c76c38 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 16 Sep 2024 18:34:06 +0200 Subject: [PATCH 275/298] edit --- src/main/java/org/usf/jquery/core/QueryBuilder.java | 12 +++++++----- src/main/java/org/usf/jquery/core/QueryContext.java | 9 +++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index c180d9cd..5697cadf 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -172,13 +172,15 @@ public final void build(SqlStringBuilder sb, QueryContext ctx){ var sub = new SqlStringBuilder(500); with(sb, ctx); select(sb, ctx); - join(sub, ctx); - where(sub, ctx); - groupBy(sub, ctx); - having(sub, ctx); - orderBy(sub, ctx); + var frk = ctx.fork(); + join(sub, frk); + where(sub, frk); + groupBy(sub, frk); + having(sub, frk); + orderBy(sub, frk); fetch(sub); from(sb, ctx); //enumerate all views before from clause + ctx.join(frk); sb.append(sub.toString()); } diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java index 5f7ce508..ef62c4d9 100644 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -137,6 +137,15 @@ public QueryContext withValue() { return new QueryContext(schema, vPrefix, null, null, views, overView); } + QueryContext fork() { //share schema, views, but not args + return new QueryContext(schema, vPrefix, new ArrayList<>(), new ArrayList<>(), views, overView); + } + + void join(QueryContext ctx) { //share schema, views, but not args + args.addAll(ctx.args); + argTypes.addAll(ctx.argTypes); + } + public QueryContext subQuery(Map overView) { return new QueryContext(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), unmodifiableMap(overView)); } From 038f0c737ace37dab3b78c7c699f39003b08d926 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 16 Sep 2024 19:05:43 +0200 Subject: [PATCH 276/298] edit --- src/main/java/org/usf/jquery/core/QueryBuilder.java | 2 +- src/main/java/org/usf/jquery/core/QueryContext.java | 12 ++++++------ src/main/java/org/usf/jquery/core/ViewJoin.java | 3 +-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 5697cadf..e0a65941 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -169,9 +169,9 @@ public RequestQuery build(String schema) { } public final void build(SqlStringBuilder sb, QueryContext ctx){ - var sub = new SqlStringBuilder(500); with(sb, ctx); select(sb, ctx); + var sub = new SqlStringBuilder(500); var frk = ctx.fork(); join(sub, frk); where(sub, frk); diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java index ef62c4d9..c49590f2 100644 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -137,18 +137,18 @@ public QueryContext withValue() { return new QueryContext(schema, vPrefix, null, null, views, overView); } - QueryContext fork() { //share schema, views, but not args + public QueryContext subQuery(Map overView) { //share schema, prefix, args but not views + return new QueryContext(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), unmodifiableMap(overView)); + } + + QueryContext fork() { //share schema, prefix and views, but not args return new QueryContext(schema, vPrefix, new ArrayList<>(), new ArrayList<>(), views, overView); } - void join(QueryContext ctx) { //share schema, views, but not args + void join(QueryContext ctx) { //join args only args.addAll(ctx.args); argTypes.addAll(ctx.argTypes); } - - public QueryContext subQuery(Map overView) { - return new QueryContext(schema, vPrefix + "_s", args, argTypes, new ArrayList<>(), unmodifiableMap(overView)); - } public static QueryContext addWithValue() { return addWithValue(null, emptyMap()); //no args diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index f7992897..5884ffdf 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -43,8 +43,7 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { sb.append(joinType + " JOIN "); view.sqlWithTag(sb, ctx); if(!isEmpty(filters)) { - var val = ctx.withValue(); //literal filters - sb.append(" ON ").runForeach(filters, AND.sql(), f-> f.sql(sb, val)); + sb.append(" ON ").runForeach(filters, AND.sql(), f-> f.sql(sb, ctx)); } //else cross join } From fb9bd84d2db61881e72b67e7bc555c8ac15a1791 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 16 Sep 2024 19:15:41 +0200 Subject: [PATCH 277/298] edit --- src/main/java/org/usf/jquery/core/DBView.java | 2 +- src/main/java/org/usf/jquery/core/NamedColumn.java | 2 +- src/main/java/org/usf/jquery/core/QueryBuilder.java | 4 ++-- src/main/java/org/usf/jquery/core/QueryContext.java | 2 -- src/main/java/org/usf/jquery/core/ViewJoin.java | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/DBView.java b/src/main/java/org/usf/jquery/core/DBView.java index 8d0bf5a1..1ee913cd 100644 --- a/src/main/java/org/usf/jquery/core/DBView.java +++ b/src/main/java/org/usf/jquery/core/DBView.java @@ -18,7 +18,7 @@ default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { sql(sb, ctx); } - default void sqlWithTag(SqlStringBuilder sb, QueryContext ctx) { + default void sqlUsingTag(SqlStringBuilder sb, QueryContext ctx) { ctx.viewOverload(this).orElse(this).sql(sb, ctx); //!important sb.space().append(ctx.viewAlias(this)); } diff --git a/src/main/java/org/usf/jquery/core/NamedColumn.java b/src/main/java/org/usf/jquery/core/NamedColumn.java index e47db7ba..af5f5ef3 100644 --- a/src/main/java/org/usf/jquery/core/NamedColumn.java +++ b/src/main/java/org/usf/jquery/core/NamedColumn.java @@ -11,7 +11,7 @@ public interface NamedColumn extends DBColumn { String getTag(); - default void sqlWithTag(SqlStringBuilder sb, QueryContext ctx) { + default void sqlUsingTag(SqlStringBuilder sb, QueryContext ctx) { sql(sb, ctx); sb.runIfNonNull(getTag(), v-> sb.as(doubleQuote(v))); } diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index e0a65941..e639119c 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -208,7 +208,7 @@ void select(SqlStringBuilder sb, QueryContext ctx){ .appendIf(distinct, " DISTINCT") .appendIf(nonNull(limit) && currentDatabase() == TERADATA, ()-> " TOP " + limit) //??????? .space() - .runForeach(columns.iterator(), SCOMA, o-> o.sqlWithTag(sb, ctx)); + .runForeach(columns.iterator(), SCOMA, o-> o.sqlUsingTag(sb, ctx)); } void from(SqlStringBuilder sb, QueryContext ctx) { @@ -223,7 +223,7 @@ void from(SqlStringBuilder sb, QueryContext ctx) { ? ctx.views() : ctx.views().stream().filter(not(excludes::contains)).toList(); //do not remove views if(!views.isEmpty()) { - sb.from().runForeach(views.iterator(), SCOMA, v-> v.sqlWithTag(sb, ctx)); + sb.from().runForeach(views.iterator(), SCOMA, v-> v.sqlUsingTag(sb, ctx)); } } diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java index c49590f2..280ad94c 100644 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -18,14 +18,12 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; /** * * @author u$f * */ -@Setter(AccessLevel.PACKAGE) @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public final class QueryContext { diff --git a/src/main/java/org/usf/jquery/core/ViewJoin.java b/src/main/java/org/usf/jquery/core/ViewJoin.java index 5884ffdf..a8b28814 100644 --- a/src/main/java/org/usf/jquery/core/ViewJoin.java +++ b/src/main/java/org/usf/jquery/core/ViewJoin.java @@ -41,7 +41,7 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { public void sql(SqlStringBuilder sb, QueryContext ctx) { sb.append(joinType + " JOIN "); - view.sqlWithTag(sb, ctx); + view.sqlUsingTag(sb, ctx); if(!isEmpty(filters)) { sb.append(" ON ").runForeach(filters, AND.sql(), f-> f.sql(sb, ctx)); } //else cross join From d36d450e6818fd340152a358a0d2f9559e3e821a Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 17 Sep 2024 12:31:24 +0200 Subject: [PATCH 278/298] eidt --- .../org/usf/jquery/core/ExtractFunction.java | 2 +- .../org/usf/jquery/core/QueryBuilder.java | 22 ++++++++++--------- .../org/usf/jquery/core/QueryContext.java | 10 ++++++--- .../org/usf/jquery/core/SqlStringBuilder.java | 4 ---- .../usf/jquery/web/ContextEnvironment.java | 4 ++-- .../org/usf/jquery/web/ViewDecorator.java | 7 +++++- 6 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/ExtractFunction.java b/src/main/java/org/usf/jquery/core/ExtractFunction.java index 82106e4c..f04c520a 100644 --- a/src/main/java/org/usf/jquery/core/ExtractFunction.java +++ b/src/main/java/org/usf/jquery/core/ExtractFunction.java @@ -21,7 +21,7 @@ default String id() { default void sql(SqlStringBuilder sb, QueryContext builder, Object[] args) { requireNArgs(1, args, ExtractFunction.class::getSimpleName); sb.function(id(), ()->{ - sb.append(field()).from(); + sb.append(field()).append(" FROM "); builder.appendLiteral(sb, args[0]); }); } diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index e639119c..252a9e81 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -169,7 +169,7 @@ public RequestQuery build(String schema) { } public final void build(SqlStringBuilder sb, QueryContext ctx){ - with(sb, ctx); + with(sb, ctx); //before build select(sb, ctx); var sub = new SqlStringBuilder(500); var frk = ctx.fork(); @@ -212,18 +212,20 @@ void select(SqlStringBuilder sb, QueryContext ctx){ } void from(SqlStringBuilder sb, QueryContext ctx) { - var excludes = new ArrayList(); - if(!ctes.isEmpty()) { - ctes.forEach(excludes::add); - } + var views = ctx.views(); if(!joins.isEmpty()) { - joins.forEach(j-> excludes.add(j.getView())); + var exp = joins.stream().map(ViewJoin::getView).toList(); + views = ctx.views().stream().filter(not(exp::contains)).toList(); } - var views = excludes.isEmpty() - ? ctx.views() - : ctx.views().stream().filter(not(excludes::contains)).toList(); //do not remove views if(!views.isEmpty()) { - sb.from().runForeach(views.iterator(), SCOMA, v-> v.sqlUsingTag(sb, ctx)); + sb.append(" FROM ").runForeach(views.iterator(), SCOMA, v-> { + if(ctes.contains(v)) { + sb.append(ctx.viewAlias(v)); + } + else { + v.sqlUsingTag(sb, ctx); + } + }); } } diff --git a/src/main/java/org/usf/jquery/core/QueryContext.java b/src/main/java/org/usf/jquery/core/QueryContext.java index 280ad94c..e16c9fcc 100644 --- a/src/main/java/org/usf/jquery/core/QueryContext.java +++ b/src/main/java/org/usf/jquery/core/QueryContext.java @@ -140,12 +140,16 @@ public QueryContext subQuery(Map overView) { //share schema, } QueryContext fork() { //share schema, prefix and views, but not args - return new QueryContext(schema, vPrefix, new ArrayList<>(), new ArrayList<>(), views, overView); + return dynamic() + ? new QueryContext(schema, vPrefix, new ArrayList<>(), new ArrayList<>(), views, overView) + : withValue(); } void join(QueryContext ctx) { //join args only - args.addAll(ctx.args); - argTypes.addAll(ctx.argTypes); + if(dynamic()) { + args.addAll(ctx.args); + argTypes.addAll(ctx.argTypes); + } } public static QueryContext addWithValue() { diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index 710884b2..51707084 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -102,10 +102,6 @@ public SqlStringBuilder as(String v) { public SqlStringBuilder as() { return append(" AS "); } - - public SqlStringBuilder from() { - return append(" FROM "); - } public SqlStringBuilder function(String name, Runnable args) { return append(name).parenthesis(args); diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index a9892cbe..0c8f19be 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -78,8 +78,8 @@ Optional lookupDeclaredColumn(String name) { return ofNullable(declaredColumns.get(name)); } - void declareView(ViewDecorator view) { //additional request views - views.compute(view.identity(), (k,v)-> { + ViewDecorator declareView(ViewDecorator view) { //additional request views + return views.compute(view.identity(), (k,v)-> { if(isNull(v)){ return view; } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index aef56d3e..3618af3b 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -121,7 +121,12 @@ default void parseViews(QueryBuilder query, Map parameters) { if(parameters.containsKey(VIEW)) { Stream.of(parameters.remove(VIEW)) .flatMap(c-> parseEntries(c).stream()) - .forEach(e-> currentContext().declareView(e.evalView(this))); + .map(e-> currentContext().declareView(e.evalView(this))) + .forEach(v->{ //!ViewDecorator + if(v instanceof QueryDecorator qd) { + query.ctes(qd.getQuery()); + } + }); } } From cf1bca8348e1c82b8f782f507f4fbbb84c1a08c8 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 17 Sep 2024 12:37:31 +0200 Subject: [PATCH 279/298] edit --- src/main/java/org/usf/jquery/core/QueryBuilder.java | 5 ----- src/main/java/org/usf/jquery/web/ContextEnvironment.java | 2 +- src/main/java/org/usf/jquery/web/ViewDecorator.java | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 252a9e81..da8b97ef 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -114,11 +114,6 @@ public QueryBuilder filters(@NonNull DBFilter... filters){ return this; } - QueryBuilder group(@NonNull DBColumn... columns){ - addAll(this.group, columns); - return this; - } - public QueryBuilder joins(@NonNull ViewJoin... joins) { addAll(this.joins, joins); return this; diff --git a/src/main/java/org/usf/jquery/web/ContextEnvironment.java b/src/main/java/org/usf/jquery/web/ContextEnvironment.java index 0c8f19be..2fa0c56b 100644 --- a/src/main/java/org/usf/jquery/web/ContextEnvironment.java +++ b/src/main/java/org/usf/jquery/web/ContextEnvironment.java @@ -94,7 +94,7 @@ NamedColumn declareColumn(NamedColumn col) { return declaredColumns.compute(col.getTag(), (k,v)-> { if(isNull(v)){ return col; - } + } //cannot overwrite declared column throw resourceAlreadyExistsException(k); }); } diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 3618af3b..5bc05a98 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -141,7 +141,7 @@ default void parseColumns(QueryBuilder query, Map parameters) .flatMap(v-> parseEntries(v).stream()) .map(e-> { var c = e.evalColumn(this, true); - return c instanceof ColumnProxy cp + return c instanceof ColumnProxy cp ? currentContext().declareColumn(cp) : (NamedColumn) c; }) From 2868e1dbf3dfc0637a2216ff3d0017d187299f1a Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 17 Sep 2024 13:08:52 +0200 Subject: [PATCH 280/298] edit --- .../java/org/usf/jquery/core/ComparisonSingleExpression.java | 3 ++- src/main/java/org/usf/jquery/core/Nested.java | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index c5ad8fcd..3e67c25b 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -47,6 +47,7 @@ public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) @Override public String toString() { - return DBObject.toSQL(this); + var args = new Object[]{null}; + return DBObject.toSQL(this, args); } } diff --git a/src/main/java/org/usf/jquery/core/Nested.java b/src/main/java/org/usf/jquery/core/Nested.java index bd25b7ce..12963d55 100644 --- a/src/main/java/org/usf/jquery/core/Nested.java +++ b/src/main/java/org/usf/jquery/core/Nested.java @@ -35,6 +35,7 @@ static boolean tryResolve(QueryBuilder builder, Consumer cons, return true; //break } } + return false; } //else WhenCase filter var arr = new ArrayList(); var agg = false; From a93926e26dba8be28015963ebc9879c3e13d1f47 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 17 Sep 2024 13:13:11 +0200 Subject: [PATCH 281/298] edit --- .../java/org/usf/jquery/core/ComparisonSingleExpression.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index 3e67c25b..64c5b5f5 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -47,7 +47,7 @@ public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) @Override public String toString() { - var args = new Object[]{null}; + var args = new Object[]{null}; //unknown type return DBObject.toSQL(this, args); } } From a51026c85daf44dc37272e534e7f46d67c5b736d Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 17 Sep 2024 13:57:58 +0200 Subject: [PATCH 282/298] eidt --- src/main/java/org/usf/jquery/core/OperationColumn.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 920e9120..6c5965cd 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -44,7 +44,7 @@ public boolean resolve(QueryBuilder builder, Consumer groupKey builder.setAggregation(true); return true; //aggregate function calls cannot be nested } - if(operator.is("OVER")) { + if(operator.is("OVER")) { //TD : nested aggregation avg(count(*)) over(..) if(builder.getClause() == FILTER) { var views = new HashSet(); views(views::add); From 452357d3ae1ff5ca20d754bab34a2f07df49eda6 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 17 Sep 2024 15:16:50 +0200 Subject: [PATCH 283/298] edit --- .../java/org/usf/jquery/core/OperationColumn.java | 14 +++++++++----- src/main/java/org/usf/jquery/core/Operator.java | 2 +- .../java/org/usf/jquery/core/PipeFunction.java | 2 +- .../java/org/usf/jquery/core/SqlStringBuilder.java | 6 +++--- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 6c5965cd..3e566960 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -2,9 +2,9 @@ import static java.util.Objects.nonNull; import static org.usf.jquery.core.Clause.FILTER; -import static org.usf.jquery.core.Validation.requireNArgs; import java.util.HashSet; +import java.util.Objects; import java.util.function.Consumer; import lombok.AccessLevel; @@ -57,7 +57,8 @@ public boolean resolve(QueryBuilder builder, Consumer groupKey } throw new UnsupportedOperationException("over require only one view"); } - return requirePartition().resolve(builder, groupKeys); //!grouping keys + var par = requirePartition(); //!grouping keys + return Objects.isNull(par) || par.resolve(builder, groupKeys); } return operator.is(ConstantOperator.class) || Nested.tryResolve(builder, groupKeys, args); } @@ -78,9 +79,12 @@ public String toString() { } private Partition requirePartition() { - if(requireNArgs(2, args, ()-> "over operation")[1] instanceof Partition part) { - return part; + if(nonNull(args) && args.length == 2) { + if(args[1] instanceof Partition part) { + return part; + } + throw new IllegalArgumentException("partition parameter expected"); } - throw new IllegalArgumentException("partition parameter expected"); + return null; } } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 89b2701a..228081e1 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -325,7 +325,7 @@ static TypedOperator denseRank() { //pipe functions static TypedOperator over() { - return new TypedOperator(firstArgJdbcType(), pipe("OVER"), required(COLUMN), required(PARTITION)); //optional !? + return new TypedOperator(firstArgJdbcType(), pipe("OVER"), required(COLUMN), optional(PARTITION)); //optional !? } // constant operators diff --git a/src/main/java/org/usf/jquery/core/PipeFunction.java b/src/main/java/org/usf/jquery/core/PipeFunction.java index 751699d5..50e8ff8e 100644 --- a/src/main/java/org/usf/jquery/core/PipeFunction.java +++ b/src/main/java/org/usf/jquery/core/PipeFunction.java @@ -15,6 +15,6 @@ default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { requireAtLeastNArgs(1, args, PipeFunction.class::getSimpleName); ctx.appendLiteral(sb, args[0]); sb.space(); - FunctionOperator.super.sql(sb, ctx, args, 1); + FunctionOperator.super.sql(sb, ctx, args, 1); //optional partition } } \ No newline at end of file diff --git a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java index 51707084..c97367f5 100644 --- a/src/main/java/org/usf/jquery/core/SqlStringBuilder.java +++ b/src/main/java/org/usf/jquery/core/SqlStringBuilder.java @@ -66,7 +66,7 @@ public SqlStringBuilder runForeach(T[] arr, String delimiter, Consumer fn public SqlStringBuilder runForeach(T[] arr, int idx, String delimiter, Consumer fn, String prefix, String suffix) { requireNonNull(arr, "arr connot be null"); - if( idx < arr.length || (arr.length == 0 && idx == 0)) { + if(idx < arr.length) { sb.append(prefix); if(!isEmpty(arr)) { var i=idx; @@ -78,9 +78,9 @@ public SqlStringBuilder runForeach(T[] arr, int idx, String delimiter, Consu } sb.append(suffix); } - else { + else if(idx > arr.length) { throw new IndexOutOfBoundsException(idx); - } + }// idx == arr.length return this; } From 40970cc0c80ffa421266c7af8b3896e9defc5556 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 17 Sep 2024 18:28:36 +0200 Subject: [PATCH 284/298] edit --- src/main/java/org/usf/jquery/core/OperationColumn.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 3e566960..cfaae4b0 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -44,7 +44,7 @@ public boolean resolve(QueryBuilder builder, Consumer groupKey builder.setAggregation(true); return true; //aggregate function calls cannot be nested } - if(operator.is("OVER")) { //TD : nested aggregation avg(count(*)) over(..) + if(operator.is("OVER")) { //TD : nested aggregation avg(count(*)) over(..) => setAggregation if(builder.getClause() == FILTER) { var views = new HashSet(); views(views::add); From e27c68fe857cf4ccfec73d6abcaecceacf3de326 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 17 Sep 2024 18:44:38 +0200 Subject: [PATCH 285/298] edit --- src/main/java/org/usf/jquery/core/OperationColumn.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index cfaae4b0..f3caee82 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -60,7 +60,7 @@ public boolean resolve(QueryBuilder builder, Consumer groupKey var par = requirePartition(); //!grouping keys return Objects.isNull(par) || par.resolve(builder, groupKeys); } - return operator.is(ConstantOperator.class) || Nested.tryResolve(builder, groupKeys, args); + return Nested.tryResolve(builder, groupKeys, args) || operator.is(ConstantOperator.class); } @Override From 2d3090ff00e420d283c642bd685cda3515386ee7 Mon Sep 17 00:00:00 2001 From: u$f Date: Tue, 17 Sep 2024 23:10:24 +0200 Subject: [PATCH 286/298] edit --- .../java/org/usf/jquery/core/CaseColumn.java | 4 +- .../usf/jquery/core/ColumnFilterGroup.java | 4 +- .../java/org/usf/jquery/core/ColumnProxy.java | 2 +- .../usf/jquery/core/ColumnSingleFilter.java | 2 +- .../core/ComparisonExpressionGroup.java | 14 +++---- .../core/ComparisonSingleExpression.java | 2 +- .../java/org/usf/jquery/core/DBColumn.java | 4 +- .../java/org/usf/jquery/core/DBOrder.java | 2 +- src/main/java/org/usf/jquery/core/Nested.java | 39 ++++++++----------- .../org/usf/jquery/core/OperationColumn.java | 25 +++--------- .../java/org/usf/jquery/core/Partition.java | 18 ++------- .../org/usf/jquery/core/QueryBuilder.java | 12 +++--- .../java/org/usf/jquery/core/ValueColumn.java | 5 +-- .../java/org/usf/jquery/core/ViewColumn.java | 5 ++- .../java/org/usf/jquery/core/WhenCase.java | 2 +- 15 files changed, 56 insertions(+), 84 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 5598a86f..829247e5 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -35,8 +35,8 @@ public JDBCType getType() { } @Override - public boolean resolve(QueryBuilder builder, Consumer groupKeys) { - return Nested.tryResolve(builder, groupKeys, (Object[])whenCases); + public int resolve(QueryBuilder builder, Consumer groupKeys) { + return Nested.resolve(builder, groupKeys, whenCases); } @Override diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index 4f2ec302..913da0fe 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -28,8 +28,8 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public boolean resolve(QueryBuilder builder, Consumer groupKeys) { - return Nested.tryResolve(builder, groupKeys, (Object[])filters); + public int resolve(QueryBuilder builder, Consumer groupKeys) { + return Nested.resolve(builder, groupKeys, filters); } @Override diff --git a/src/main/java/org/usf/jquery/core/ColumnProxy.java b/src/main/java/org/usf/jquery/core/ColumnProxy.java index c744aa25..36eaffcd 100644 --- a/src/main/java/org/usf/jquery/core/ColumnProxy.java +++ b/src/main/java/org/usf/jquery/core/ColumnProxy.java @@ -37,7 +37,7 @@ public JDBCType getType() { } @Override - public boolean resolve(QueryBuilder builder, Consumer cons) { + public int resolve(QueryBuilder builder, Consumer cons) { return column.resolve(builder, cons); } diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index 16154a9d..f8ab059c 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -21,7 +21,7 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + public int resolve(QueryBuilder builder, Consumer groupKeys) { return Nested.tryResolve(builder, groupKeys, left, expression); } diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index f4f981a7..803ca19a 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -28,8 +28,13 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object operand) { } @Override - public boolean resolve(QueryBuilder builder, Consumer groupKeys) { - return Nested.tryResolve(builder, groupKeys, (Object[])expressions); + public int resolve(QueryBuilder builder, Consumer groupKeys) { + return Nested.resolve(builder, groupKeys, expressions); + } + + @Override + public void views(Consumer cons) { + Nested.viewsOf(cons, (Object[])expressions); } @Override @@ -39,11 +44,6 @@ public ComparisonExpression append(LogicalOperator op, ComparisonExpression exp) : new ComparisonExpressionGroup(op, this, exp); } - @Override - public void views(Consumer cons) { - Nested.viewsOf(cons, (Object[])expressions); - } - @Override public String toString() { return DBObject.toSQL(this, ""); diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index 64c5b5f5..24efa20e 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -31,7 +31,7 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object left) { } @Override - public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + public int resolve(QueryBuilder builder, Consumer groupKeys) { return Nested.tryResolve(builder, groupKeys, right); } diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index c3f09098..e7a17844 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -448,8 +448,8 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public boolean resolve(QueryBuilder builder, Consumer groupKeys) { - return true; //!group by + public int resolve(QueryBuilder builder, Consumer groupKeys) { + return -1; //!group by } }; } diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index 89536e10..9405e834 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -36,7 +36,7 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + public int resolve(QueryBuilder builder, Consumer groupKeys) { return column.resolve(builder, groupKeys); } diff --git a/src/main/java/org/usf/jquery/core/Nested.java b/src/main/java/org/usf/jquery/core/Nested.java index 12963d55..9b7e8cbd 100644 --- a/src/main/java/org/usf/jquery/core/Nested.java +++ b/src/main/java/org/usf/jquery/core/Nested.java @@ -1,9 +1,8 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Clause.FILTER; +import static java.lang.Math.max; import static org.usf.jquery.core.Utils.isEmpty; -import java.util.ArrayList; import java.util.function.Consumer; /** @@ -13,7 +12,8 @@ */ public interface Nested { - boolean resolve(QueryBuilder builder, Consumer cons); + //0: groupKey, +1: aggregation, -1: other + int resolve(QueryBuilder builder, Consumer cons); void views(Consumer cons); //collect used views @@ -26,31 +26,24 @@ static void viewsOf(Consumer cons, Object... args) { //collect used view } } } - - static boolean tryResolve(QueryBuilder builder, Consumer cons, Object... args){ + + static int resolve(QueryBuilder builder, Consumer cons, T[] args){ + var lvl = -1; if(!isEmpty(args)) { - if(builder.getClause() == FILTER) { - for(var o : args) { - if(tryResolve(o, builder, cons)) { - return true; //break - } - } - return false; - } //else WhenCase filter - var arr = new ArrayList(); - var agg = false; for(var o : args) { - agg |= tryResolve(o, builder, arr::add); - } - if(agg && !arr.isEmpty()) { //partial aggregation - arr.forEach(cons); + lvl = max(lvl, o.resolve(builder, cons)); } - return agg; } - return false; + return lvl; } - static boolean tryResolve(Object o, QueryBuilder builder, Consumer cons) { - return o instanceof Nested n && n.resolve(builder, cons); + static int tryResolve(QueryBuilder builder, Consumer cons, Object... args){ + var lvl = -1; + if(!isEmpty(args)) { + for(var o : args) { + lvl = max(lvl, o instanceof Nested n ? n.resolve(builder, cons) : -1); + } + } + return lvl; } } diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index f3caee82..fd60e7aa 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -4,7 +4,6 @@ import static org.usf.jquery.core.Clause.FILTER; import java.util.HashSet; -import java.util.Objects; import java.util.function.Consumer; import lombok.AccessLevel; @@ -39,12 +38,11 @@ public JDBCType getType() { } @Override - public boolean resolve(QueryBuilder builder, Consumer groupKeys) { - if(operator.is(AggregateFunction.class)) { - builder.setAggregation(true); - return true; //aggregate function calls cannot be nested + public int resolve(QueryBuilder builder, Consumer groupKeys) { + if(operator.is(AggregateFunction.class) || operator.is(WindowFunction.class)) { + return Math.max(1, Nested.tryResolve(builder, c-> {}, args)+1); //if lvl==-1 } - if(operator.is("OVER")) { //TD : nested aggregation avg(count(*)) over(..) => setAggregation + if(operator.is("OVER")) { if(builder.getClause() == FILTER) { var views = new HashSet(); views(views::add); @@ -57,10 +55,9 @@ public boolean resolve(QueryBuilder builder, Consumer groupKey } throw new UnsupportedOperationException("over require only one view"); } - var par = requirePartition(); //!grouping keys - return Objects.isNull(par) || par.resolve(builder, groupKeys); + return Nested.tryResolve(builder, groupKeys, args)-1; } - return Nested.tryResolve(builder, groupKeys, args) || operator.is(ConstantOperator.class); + return Nested.tryResolve(builder, groupKeys, args); } @Override @@ -77,14 +74,4 @@ public void views(Consumer cons) { public String toString() { return DBObject.toSQL(this); } - - private Partition requirePartition() { - if(nonNull(args) && args.length == 2) { - if(args[1] instanceof Partition part) { - return part; - } - throw new IllegalArgumentException("partition parameter expected"); - } - return null; - } } diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index 9bacefc5..f34632a6 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -1,11 +1,11 @@ package org.usf.jquery.core; +import static java.lang.Math.max; import static org.usf.jquery.core.SqlStringBuilder.SPACE; import static org.usf.jquery.core.Utils.isEmpty; import static org.usf.jquery.core.Validation.requireNoArgs; import java.util.function.Consumer; -import java.util.stream.Stream; import lombok.RequiredArgsConstructor; @@ -35,19 +35,9 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { } @Override - public boolean resolve(QueryBuilder builder, Consumer groupKeys) { - if(!isEmpty(columns)) { - Stream.of(columns) - .filter(c-> !c.resolve(builder, groupKeys)) - .forEach(groupKeys); - } - if(!isEmpty(orders)) { - Stream.of(orders) - .filter(c-> !c.resolve(builder, groupKeys)) - .map(DBOrder::getColumn) - .forEach(groupKeys); - } - return true; //!grouping keys + public int resolve(QueryBuilder builder, Consumer groupKeys) { + return max(Nested.resolve(builder, groupKeys, columns), + Nested.resolve(builder, groupKeys, orders)); } @Override diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index da8b97ef..ea93f3ae 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -88,8 +88,9 @@ public QueryBuilder columns(@NonNull NamedColumn... columns) { .anyMatch(nc-> nc.getTag().equals(col.getTag()))) { throw resourceAlreadyExistsException(col.getTag()); } - if(this.columns.add(col) && !col.resolve(this, group::add)) { - this.group.add(col); //distinct TD: override equals + if(this.columns.add(col) && col.resolve(this, group::add) > 0) { +// this.group.add(col); //distinct TD: override equals + aggregation = true; } } return this; @@ -98,8 +99,9 @@ public QueryBuilder columns(@NonNull NamedColumn... columns) { public QueryBuilder orders(@NonNull DBOrder... orders) { this.clause = ORDER; for(var o : orders) { - if(this.orders.add(o) && !o.resolve(this, group::add)) { - this.group.add(o.getColumn()); + if(this.orders.add(o) && o.resolve(this, group::add) > 0) { +// this.group.add(o.getColumn()); + aggregation |= true; } } return this; @@ -109,7 +111,7 @@ public QueryBuilder filters(@NonNull DBFilter... filters){ this.clause = FILTER; Consumer noCons = v->{}; for(var f : filters) { - (f.resolve(this, noCons) ? having : where).add(f); + (f.resolve(this, noCons) > 0 ? having : where).add(f); } return this; } diff --git a/src/main/java/org/usf/jquery/core/ValueColumn.java b/src/main/java/org/usf/jquery/core/ValueColumn.java index 26c37780..8f8485d7 100644 --- a/src/main/java/org/usf/jquery/core/ValueColumn.java +++ b/src/main/java/org/usf/jquery/core/ValueColumn.java @@ -1,6 +1,5 @@ package org.usf.jquery.core; -import static org.usf.jquery.core.Clause.FILTER; import static org.usf.jquery.core.QueryContext.formatValue; import java.util.function.Consumer; @@ -30,8 +29,8 @@ public JDBCType getType() { } @Override - public boolean resolve(QueryBuilder builder, Consumer groupKeys) { - return builder.getClause() != FILTER; //COLUMN=> !GROUPBY || FILTER=> !HAVING + public int resolve(QueryBuilder builder, Consumer groupKeys) { + return -1; } @Override diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index d4776f52..3eaf9e11 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -25,8 +25,9 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public boolean resolve(QueryBuilder builder, Consumer groupKeys) { - return false; + public int resolve(QueryBuilder builder, Consumer groupKeys) { + groupKeys.accept(this); + return 0; } @Override diff --git a/src/main/java/org/usf/jquery/core/WhenCase.java b/src/main/java/org/usf/jquery/core/WhenCase.java index 6b2bdfb2..d5bba128 100644 --- a/src/main/java/org/usf/jquery/core/WhenCase.java +++ b/src/main/java/org/usf/jquery/core/WhenCase.java @@ -45,7 +45,7 @@ public JDBCType getType() { } @Override - public boolean resolve(QueryBuilder builder, Consumer groupKeys) { + public int resolve(QueryBuilder builder, Consumer groupKeys) { return Nested.tryResolve(builder, groupKeys, filter, value); } From 038f5ad0388168312c8cde9af8cd475c25cdb6f4 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 18 Sep 2024 00:46:00 +0200 Subject: [PATCH 287/298] edit --- .../java/org/usf/jquery/core/CaseColumn.java | 6 +-- .../usf/jquery/core/ColumnFilterGroup.java | 6 +-- .../java/org/usf/jquery/core/ColumnProxy.java | 4 +- .../usf/jquery/core/ColumnSingleFilter.java | 6 +-- .../core/ComparisonExpressionGroup.java | 6 +-- .../core/ComparisonSingleExpression.java | 6 +-- .../java/org/usf/jquery/core/DBColumn.java | 2 +- .../java/org/usf/jquery/core/DBOrder.java | 4 +- src/main/java/org/usf/jquery/core/Nested.java | 38 ++++++++++++------- .../org/usf/jquery/core/OperationColumn.java | 12 +++--- .../java/org/usf/jquery/core/Operator.java | 4 ++ .../java/org/usf/jquery/core/Partition.java | 10 ++--- .../org/usf/jquery/core/QueryBuilder.java | 15 ++------ .../java/org/usf/jquery/core/ValueColumn.java | 2 +- .../java/org/usf/jquery/core/ViewColumn.java | 2 +- .../java/org/usf/jquery/core/WhenCase.java | 6 +-- 16 files changed, 68 insertions(+), 61 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 829247e5..46d2bbd2 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -35,13 +35,13 @@ public JDBCType getType() { } @Override - public int resolve(QueryBuilder builder, Consumer groupKeys) { - return Nested.resolve(builder, groupKeys, whenCases); + public int columns(QueryBuilder builder, Consumer groupKeys) { + return Nested.resolveColumn(builder, groupKeys, whenCases); } @Override public void views(Consumer cons) { - Nested.viewsOf(cons, (Object[])whenCases); + Nested.resolveViews(cons, whenCases); } @Override diff --git a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java index 913da0fe..2f0bfb1d 100644 --- a/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java +++ b/src/main/java/org/usf/jquery/core/ColumnFilterGroup.java @@ -28,13 +28,13 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public int resolve(QueryBuilder builder, Consumer groupKeys) { - return Nested.resolve(builder, groupKeys, filters); + public int columns(QueryBuilder builder, Consumer groupKeys) { + return Nested.resolveColumn(builder, groupKeys, filters); } @Override public void views(Consumer cons) { - Nested.viewsOf(cons, (Object[])filters); + Nested.resolveViews(cons, filters); } @Override diff --git a/src/main/java/org/usf/jquery/core/ColumnProxy.java b/src/main/java/org/usf/jquery/core/ColumnProxy.java index 36eaffcd..6e92bebd 100644 --- a/src/main/java/org/usf/jquery/core/ColumnProxy.java +++ b/src/main/java/org/usf/jquery/core/ColumnProxy.java @@ -37,8 +37,8 @@ public JDBCType getType() { } @Override - public int resolve(QueryBuilder builder, Consumer cons) { - return column.resolve(builder, cons); + public int columns(QueryBuilder builder, Consumer cons) { + return column.columns(builder, cons); } @Override diff --git a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java index f8ab059c..b120c808 100644 --- a/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java +++ b/src/main/java/org/usf/jquery/core/ColumnSingleFilter.java @@ -21,13 +21,13 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public int resolve(QueryBuilder builder, Consumer groupKeys) { - return Nested.tryResolve(builder, groupKeys, left, expression); + public int columns(QueryBuilder builder, Consumer groupKeys) { + return Nested.tryResolveColumn(builder, groupKeys, left, expression); } @Override public void views(Consumer cons) { - Nested.viewsOf(cons, left, expression); + Nested.tryResolveViews(cons, left, expression); } @Override diff --git a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java index 803ca19a..80381563 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java +++ b/src/main/java/org/usf/jquery/core/ComparisonExpressionGroup.java @@ -28,13 +28,13 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object operand) { } @Override - public int resolve(QueryBuilder builder, Consumer groupKeys) { - return Nested.resolve(builder, groupKeys, expressions); + public int columns(QueryBuilder builder, Consumer groupKeys) { + return Nested.resolveColumn(builder, groupKeys, expressions); } @Override public void views(Consumer cons) { - Nested.viewsOf(cons, (Object[])expressions); + Nested.resolveViews(cons, expressions); } @Override diff --git a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java index 24efa20e..60698411 100644 --- a/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java +++ b/src/main/java/org/usf/jquery/core/ComparisonSingleExpression.java @@ -31,13 +31,13 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object left) { } @Override - public int resolve(QueryBuilder builder, Consumer groupKeys) { - return Nested.tryResolve(builder, groupKeys, right); + public int columns(QueryBuilder builder, Consumer groupKeys) { + return Nested.tryResolveColumn(builder, groupKeys, right); } @Override public void views(Consumer cons) { - Nested.viewsOf(cons, right); + Nested.tryResolveViews(cons, right); } @Override diff --git a/src/main/java/org/usf/jquery/core/DBColumn.java b/src/main/java/org/usf/jquery/core/DBColumn.java index e7a17844..b6349144 100644 --- a/src/main/java/org/usf/jquery/core/DBColumn.java +++ b/src/main/java/org/usf/jquery/core/DBColumn.java @@ -448,7 +448,7 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public int resolve(QueryBuilder builder, Consumer groupKeys) { + public int columns(QueryBuilder builder, Consumer groupKeys) { return -1; //!group by } }; diff --git a/src/main/java/org/usf/jquery/core/DBOrder.java b/src/main/java/org/usf/jquery/core/DBOrder.java index 9405e834..d5569a33 100644 --- a/src/main/java/org/usf/jquery/core/DBOrder.java +++ b/src/main/java/org/usf/jquery/core/DBOrder.java @@ -36,8 +36,8 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public int resolve(QueryBuilder builder, Consumer groupKeys) { - return column.resolve(builder, groupKeys); + public int columns(QueryBuilder builder, Consumer groupKeys) { + return column.columns(builder, groupKeys); } @Override diff --git a/src/main/java/org/usf/jquery/core/Nested.java b/src/main/java/org/usf/jquery/core/Nested.java index 9b7e8cbd..75e811fc 100644 --- a/src/main/java/org/usf/jquery/core/Nested.java +++ b/src/main/java/org/usf/jquery/core/Nested.java @@ -12,38 +12,48 @@ */ public interface Nested { + static final Consumer DO_NOTHING = o-> {}; + //0: groupKey, +1: aggregation, -1: other - int resolve(QueryBuilder builder, Consumer cons); + int columns(QueryBuilder builder, Consumer cons); void views(Consumer cons); //collect used views - - static void viewsOf(Consumer cons, Object... args) { //collect used views + + static int resolveColumn(QueryBuilder builder, Consumer cons, T[] args){ + var lvl = -1; if(!isEmpty(args)) { - for(var o : args) { - if(o instanceof Nested n) { - n.views(cons); - } + for(var n : args) { + lvl = max(lvl, n.columns(builder, cons)); } } + return lvl; } - - static int resolve(QueryBuilder builder, Consumer cons, T[] args){ + + static int tryResolveColumn(QueryBuilder builder, Consumer cons, Object... args){ var lvl = -1; if(!isEmpty(args)) { for(var o : args) { - lvl = max(lvl, o.resolve(builder, cons)); + lvl = max(lvl, o instanceof Nested n ? n.columns(builder, cons) : -1); } } return lvl; } - static int tryResolve(QueryBuilder builder, Consumer cons, Object... args){ - var lvl = -1; + static void resolveViews(Consumer cons, T[] args) { //collect used views + if(!isEmpty(args)) { + for(var n : args) { + n.views(cons); + } + } + } + + static void tryResolveViews(Consumer cons, Object... args) { //collect used views if(!isEmpty(args)) { for(var o : args) { - lvl = max(lvl, o instanceof Nested n ? n.resolve(builder, cons) : -1); + if(o instanceof Nested n) { + n.views(cons); + } } } - return lvl; } } diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index fd60e7aa..944cb118 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -38,9 +38,9 @@ public JDBCType getType() { } @Override - public int resolve(QueryBuilder builder, Consumer groupKeys) { + public int columns(QueryBuilder builder, Consumer groupKeys) { if(operator.is(AggregateFunction.class) || operator.is(WindowFunction.class)) { - return Math.max(1, Nested.tryResolve(builder, c-> {}, args)+1); //if lvl==-1 + return Math.max(1, Nested.tryResolveColumn(builder, DO_NOTHING, args)+1); //if lvl==-1 } if(operator.is("OVER")) { if(builder.getClause() == FILTER) { @@ -51,13 +51,13 @@ public int resolve(QueryBuilder builder, Consumer groupKeys) { var cTag = "over_" + hashCode(); //over_view_hash builder.overView(view).getBuilder().columns(new OperationColumn(operator, args, type).as(cTag)); //clone overColumn = new ViewColumn(cTag, view, type, null); - return overColumn.resolve(builder, groupKeys); + return overColumn.columns(builder, groupKeys); } throw new UnsupportedOperationException("over require only one view"); } - return Nested.tryResolve(builder, groupKeys, args)-1; + return Nested.tryResolveColumn(builder, groupKeys, args)-1; } - return Nested.tryResolve(builder, groupKeys, args); + return Nested.tryResolveColumn(builder, groupKeys, args); } @Override @@ -66,7 +66,7 @@ public void views(Consumer cons) { overColumn.views(cons); } else { - Nested.viewsOf(cons, args); + Nested.tryResolveViews(cons, args); } } diff --git a/src/main/java/org/usf/jquery/core/Operator.java b/src/main/java/org/usf/jquery/core/Operator.java index 228081e1..affd8085 100644 --- a/src/main/java/org/usf/jquery/core/Operator.java +++ b/src/main/java/org/usf/jquery/core/Operator.java @@ -322,6 +322,10 @@ static TypedOperator denseRank() { return new TypedOperator(INTEGER, window("DENSE_RANK")); // takes no args } + static TypedOperator percentRank() { + return new TypedOperator(INTEGER, window("PERCENT_RANK")); // takes no args + } + //pipe functions static TypedOperator over() { diff --git a/src/main/java/org/usf/jquery/core/Partition.java b/src/main/java/org/usf/jquery/core/Partition.java index f34632a6..2993c9bf 100644 --- a/src/main/java/org/usf/jquery/core/Partition.java +++ b/src/main/java/org/usf/jquery/core/Partition.java @@ -35,15 +35,15 @@ public void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { } @Override - public int resolve(QueryBuilder builder, Consumer groupKeys) { - return max(Nested.resolve(builder, groupKeys, columns), - Nested.resolve(builder, groupKeys, orders)); + public int columns(QueryBuilder builder, Consumer groupKeys) { + return max(Nested.resolveColumn(builder, groupKeys, columns), + Nested.resolveColumn(builder, groupKeys, orders)); } @Override public void views(Consumer cons) { - Nested.viewsOf(cons, (Object[])columns); - Nested.viewsOf(cons, (Object[])orders); + Nested.resolveViews(cons, columns); + Nested.resolveViews(cons, orders); } @Override diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index ea93f3ae..5753c910 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -13,6 +13,7 @@ import static org.usf.jquery.core.Database.currentDatabase; import static org.usf.jquery.core.Database.setCurrentDatabase; import static org.usf.jquery.core.LogicalOperator.AND; +import static org.usf.jquery.core.Nested.DO_NOTHING; import static org.usf.jquery.core.QueryContext.parameterized; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.SqlStringBuilder.SPACE; @@ -25,7 +26,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; -import java.util.function.Consumer; import java.util.function.Supplier; import lombok.AccessLevel; @@ -88,10 +88,7 @@ public QueryBuilder columns(@NonNull NamedColumn... columns) { .anyMatch(nc-> nc.getTag().equals(col.getTag()))) { throw resourceAlreadyExistsException(col.getTag()); } - if(this.columns.add(col) && col.resolve(this, group::add) > 0) { -// this.group.add(col); //distinct TD: override equals - aggregation = true; - } + aggregation |= this.columns.add(col) && col.columns(this, group::add) > 0; } return this; } @@ -99,19 +96,15 @@ public QueryBuilder columns(@NonNull NamedColumn... columns) { public QueryBuilder orders(@NonNull DBOrder... orders) { this.clause = ORDER; for(var o : orders) { - if(this.orders.add(o) && o.resolve(this, group::add) > 0) { -// this.group.add(o.getColumn()); - aggregation |= true; - } + aggregation |= this.orders.add(o) && o.columns(this, group::add) > 0; } return this; } public QueryBuilder filters(@NonNull DBFilter... filters){ this.clause = FILTER; - Consumer noCons = v->{}; for(var f : filters) { - (f.resolve(this, noCons) > 0 ? having : where).add(f); + (f.columns(this, DO_NOTHING) > 0 ? having : where).add(f); } return this; } diff --git a/src/main/java/org/usf/jquery/core/ValueColumn.java b/src/main/java/org/usf/jquery/core/ValueColumn.java index 8f8485d7..b74b2bea 100644 --- a/src/main/java/org/usf/jquery/core/ValueColumn.java +++ b/src/main/java/org/usf/jquery/core/ValueColumn.java @@ -29,7 +29,7 @@ public JDBCType getType() { } @Override - public int resolve(QueryBuilder builder, Consumer groupKeys) { + public int columns(QueryBuilder builder, Consumer groupKeys) { return -1; } diff --git a/src/main/java/org/usf/jquery/core/ViewColumn.java b/src/main/java/org/usf/jquery/core/ViewColumn.java index 3eaf9e11..b6870119 100644 --- a/src/main/java/org/usf/jquery/core/ViewColumn.java +++ b/src/main/java/org/usf/jquery/core/ViewColumn.java @@ -25,7 +25,7 @@ public void sql(SqlStringBuilder sb, QueryContext ctx) { } @Override - public int resolve(QueryBuilder builder, Consumer groupKeys) { + public int columns(QueryBuilder builder, Consumer groupKeys) { groupKeys.accept(this); return 0; } diff --git a/src/main/java/org/usf/jquery/core/WhenCase.java b/src/main/java/org/usf/jquery/core/WhenCase.java index d5bba128..0a0205d5 100644 --- a/src/main/java/org/usf/jquery/core/WhenCase.java +++ b/src/main/java/org/usf/jquery/core/WhenCase.java @@ -45,13 +45,13 @@ public JDBCType getType() { } @Override - public int resolve(QueryBuilder builder, Consumer groupKeys) { - return Nested.tryResolve(builder, groupKeys, filter, value); + public int columns(QueryBuilder builder, Consumer groupKeys) { + return Nested.tryResolveColumn(builder, groupKeys, filter, value); } @Override public void views(Consumer cons) { - Nested.viewsOf(cons, filter, value); + Nested.tryResolveViews(cons, filter, value); } @Override From a7bc8f46748d0d9d2be415e81a65cca62f6c9564 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 18 Sep 2024 11:58:17 +0200 Subject: [PATCH 288/298] edit --- .../java/org/usf/jquery/core/CaseColumn.java | 2 +- src/main/java/org/usf/jquery/core/Nested.java | 27 ++++++++++++++++++- .../org/usf/jquery/core/OperationColumn.java | 2 +- .../org/usf/jquery/core/QueryBuilder.java | 3 +++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/CaseColumn.java b/src/main/java/org/usf/jquery/core/CaseColumn.java index 46d2bbd2..bb70bd7c 100644 --- a/src/main/java/org/usf/jquery/core/CaseColumn.java +++ b/src/main/java/org/usf/jquery/core/CaseColumn.java @@ -36,7 +36,7 @@ public JDBCType getType() { @Override public int columns(QueryBuilder builder, Consumer groupKeys) { - return Nested.resolveColumn(builder, groupKeys, whenCases); + return Nested.resolveColumn(this, builder, groupKeys, whenCases); } @Override diff --git a/src/main/java/org/usf/jquery/core/Nested.java b/src/main/java/org/usf/jquery/core/Nested.java index 75e811fc..3309027d 100644 --- a/src/main/java/org/usf/jquery/core/Nested.java +++ b/src/main/java/org/usf/jquery/core/Nested.java @@ -3,6 +3,7 @@ import static java.lang.Math.max; import static org.usf.jquery.core.Utils.isEmpty; +import java.util.ArrayList; import java.util.function.Consumer; /** @@ -18,7 +19,19 @@ public interface Nested { int columns(QueryBuilder builder, Consumer cons); void views(Consumer cons); //collect used views - + + static int resolveColumn(DBColumn col, QueryBuilder builder, Consumer cons, T[] args){ + var arr = new ArrayList(); + var lvl = resolveColumn(builder, arr::add, args); + if(lvl == 0) { //group keys + cons.accept(col); + } + else if(lvl > 0 && !arr.isEmpty()) { + arr.forEach(cons); + } + return lvl; + } + static int resolveColumn(QueryBuilder builder, Consumer cons, T[] args){ var lvl = -1; if(!isEmpty(args)) { @@ -29,6 +42,18 @@ static int resolveColumn(QueryBuilder builder, Consumer cons, Object... args){ + var arr = new ArrayList(); + var lvl = tryResolveColumn(builder, arr::add, args); + if(lvl == 0) { //group keys + cons.accept(col); + } + else if(lvl > 0 && !arr.isEmpty()) { + arr.forEach(cons); + } + return lvl; + } + static int tryResolveColumn(QueryBuilder builder, Consumer cons, Object... args){ var lvl = -1; if(!isEmpty(args)) { diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 944cb118..5dcd498e 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -57,7 +57,7 @@ public int columns(QueryBuilder builder, Consumer groupKeys) { } return Nested.tryResolveColumn(builder, groupKeys, args)-1; } - return Nested.tryResolveColumn(builder, groupKeys, args); + return Nested.tryResolveColumn(this, builder, groupKeys, args); } @Override diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 5753c910..85dfdbff 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -89,6 +89,9 @@ public QueryBuilder columns(@NonNull NamedColumn... columns) { throw resourceAlreadyExistsException(col.getTag()); } aggregation |= this.columns.add(col) && col.columns(this, group::add) > 0; + + System.out.println(col); + System.out.println("\t\t" + group); } return this; } From a09a3df4b760be520b13d4dfc9e6c6609351f9a0 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 18 Sep 2024 12:07:53 +0200 Subject: [PATCH 289/298] edit --- src/main/java/org/usf/jquery/core/QueryBuilder.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 85dfdbff..5753c910 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -89,9 +89,6 @@ public QueryBuilder columns(@NonNull NamedColumn... columns) { throw resourceAlreadyExistsException(col.getTag()); } aggregation |= this.columns.add(col) && col.columns(this, group::add) > 0; - - System.out.println(col); - System.out.println("\t\t" + group); } return this; } From 7296badec0bd27020bd4c6690479f4415e03ac5d Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 18 Sep 2024 12:17:11 +0200 Subject: [PATCH 290/298] edit --- src/main/java/org/usf/jquery/core/QueryBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 5753c910..38b450b3 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -104,7 +104,7 @@ public QueryBuilder orders(@NonNull DBOrder... orders) { public QueryBuilder filters(@NonNull DBFilter... filters){ this.clause = FILTER; for(var f : filters) { - (f.columns(this, DO_NOTHING) > 0 ? having : where).add(f); + (f.columns(this, group::add) > 0 ? having : where).add(f); } return this; } From 2a3dcccbe5c9191e5ef702fab66f55943be064e4 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 18 Sep 2024 12:21:15 +0200 Subject: [PATCH 291/298] edit --- src/main/java/org/usf/jquery/core/QueryBuilder.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index 38b450b3..cf37b70b 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -104,7 +104,9 @@ public QueryBuilder orders(@NonNull DBOrder... orders) { public QueryBuilder filters(@NonNull DBFilter... filters){ this.clause = FILTER; for(var f : filters) { - (f.columns(this, group::add) > 0 ? having : where).add(f); + var lvl = f.columns(this, group::add); + (lvl > 0 ? having : where).add(f); + aggregation |= lvl > 0; } return this; } From 97df2ec8cd55313e41043db8e96a0ec09f481984 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 18 Sep 2024 13:11:59 +0200 Subject: [PATCH 292/298] edit --- .../java/org/usf/jquery/core/OperationColumn.java | 11 ++++++++++- src/main/java/org/usf/jquery/core/QueryBuilder.java | 13 +++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 5dcd498e..72a4fd18 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -2,6 +2,7 @@ import static java.util.Objects.nonNull; import static org.usf.jquery.core.Clause.FILTER; +import static org.usf.jquery.core.Validation.requireAtLeastNArgs; import java.util.HashSet; import java.util.function.Consumer; @@ -55,11 +56,19 @@ public int columns(QueryBuilder builder, Consumer groupKeys) { } throw new UnsupportedOperationException("over require only one view"); } - return Nested.tryResolveColumn(builder, groupKeys, args)-1; + return resolveOverColumns(builder, groupKeys); } return Nested.tryResolveColumn(this, builder, groupKeys, args); } + private int resolveOverColumns(QueryBuilder builder, Consumer groupKeys) { + requireAtLeastNArgs(1, args, ()-> "over"); + var lvl = Nested.tryResolveColumn(builder, groupKeys, args[0])-1; + return args.length == 1 + ? lvl + : Math.max(lvl, Nested.tryResolveColumn(builder, groupKeys, args[1])); + } + @Override public void views(Consumer cons) { if(nonNull(overColumn)) { diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index cf37b70b..ac04b80e 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -13,7 +13,6 @@ import static org.usf.jquery.core.Database.currentDatabase; import static org.usf.jquery.core.Database.setCurrentDatabase; import static org.usf.jquery.core.LogicalOperator.AND; -import static org.usf.jquery.core.Nested.DO_NOTHING; import static org.usf.jquery.core.QueryContext.parameterized; import static org.usf.jquery.core.SqlStringBuilder.SCOMA; import static org.usf.jquery.core.SqlStringBuilder.SPACE; @@ -28,10 +27,8 @@ import java.util.Map; import java.util.function.Supplier; -import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; /** @@ -52,7 +49,6 @@ public class QueryBuilder { private final Collection orders = new ArrayList<>(); private final Map overView = new HashMap<>(); private boolean distinct; - @Setter(AccessLevel.PACKAGE) private boolean aggregation; private Integer limit; private Integer offset; @@ -105,8 +101,13 @@ public QueryBuilder filters(@NonNull DBFilter... filters){ this.clause = FILTER; for(var f : filters) { var lvl = f.columns(this, group::add); - (lvl > 0 ? having : where).add(f); - aggregation |= lvl > 0; + if(lvl > 0) { + (having).add(f); + aggregation |= true; + } + else { + where.add(f); + } } return this; } From 121f91e197f0c57744076c51e8b9cbe17ef485b9 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 18 Sep 2024 13:18:24 +0200 Subject: [PATCH 293/298] edit --- src/main/java/org/usf/jquery/core/OperationColumn.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/OperationColumn.java b/src/main/java/org/usf/jquery/core/OperationColumn.java index 72a4fd18..a2552b2d 100644 --- a/src/main/java/org/usf/jquery/core/OperationColumn.java +++ b/src/main/java/org/usf/jquery/core/OperationColumn.java @@ -63,10 +63,10 @@ public int columns(QueryBuilder builder, Consumer groupKeys) { private int resolveOverColumns(QueryBuilder builder, Consumer groupKeys) { requireAtLeastNArgs(1, args, ()-> "over"); - var lvl = Nested.tryResolveColumn(builder, groupKeys, args[0])-1; + var lvl = Nested.tryResolveColumn(builder, groupKeys, args[0])-1; //nested aggregate function return args.length == 1 ? lvl - : Math.max(lvl, Nested.tryResolveColumn(builder, groupKeys, args[1])); + : Math.max(lvl, Nested.tryResolveColumn(builder, groupKeys, args[1])); //partition } @Override From 345141b0e70e123b12ed302eb2b6079b4c080ab8 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 18 Sep 2024 13:43:44 +0200 Subject: [PATCH 294/298] edit --- src/main/java/org/usf/jquery/core/QueryBuilder.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/usf/jquery/core/QueryBuilder.java b/src/main/java/org/usf/jquery/core/QueryBuilder.java index ac04b80e..863b0c66 100644 --- a/src/main/java/org/usf/jquery/core/QueryBuilder.java +++ b/src/main/java/org/usf/jquery/core/QueryBuilder.java @@ -100,10 +100,11 @@ public QueryBuilder orders(@NonNull DBOrder... orders) { public QueryBuilder filters(@NonNull DBFilter... filters){ this.clause = FILTER; for(var f : filters) { - var lvl = f.columns(this, group::add); + var arr = new ArrayList(); + var lvl = f.columns(this, arr::add); if(lvl > 0) { - (having).add(f); - aggregation |= true; + aggregation |= having.add(f); + group.addAll(arr); } else { where.add(f); From 8d3958e7e3c066c5b91ff7a7ccdb098da05dcf38 Mon Sep 17 00:00:00 2001 From: ALAMI HARCHALI Youssef Date: Thu, 19 Sep 2024 15:08:37 +0200 Subject: [PATCH 295/298] edit --- src/main/java/org/usf/jquery/core/CastFunction.java | 2 +- src/main/java/org/usf/jquery/core/Chainable.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/core/CastFunction.java b/src/main/java/org/usf/jquery/core/CastFunction.java index 34076961..0b6e5786 100644 --- a/src/main/java/org/usf/jquery/core/CastFunction.java +++ b/src/main/java/org/usf/jquery/core/CastFunction.java @@ -23,7 +23,7 @@ default void sql(SqlStringBuilder sb, QueryContext ctx, Object[] args) { sb.function(id(), ()-> { ctx.appendLiteral(sb, args[0]); sb.as(type()); - if(args.length > 1) { + if(args.length > 1) { //varchar | decimal sb.parenthesis(()-> ctx.appendLiteralArray(sb, args, 1)); } }); diff --git a/src/main/java/org/usf/jquery/core/Chainable.java b/src/main/java/org/usf/jquery/core/Chainable.java index bb1dc53c..c2de6e95 100644 --- a/src/main/java/org/usf/jquery/core/Chainable.java +++ b/src/main/java/org/usf/jquery/core/Chainable.java @@ -8,6 +8,7 @@ * @author u$f * */ +@FunctionalInterface public interface Chainable> { T append(LogicalOperator op, T exp); From 50b251d26c82f16afa3685f8637224dd2c8c6ee8 Mon Sep 17 00:00:00 2001 From: u$f Date: Mon, 23 Sep 2024 08:36:57 +0200 Subject: [PATCH 296/298] edit --- src/main/java/org/usf/jquery/web/ContextManager.java | 7 ++++++- src/main/java/org/usf/jquery/web/ViewDecorator.java | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ContextManager.java b/src/main/java/org/usf/jquery/web/ContextManager.java index a0300376..220f8258 100644 --- a/src/main/java/org/usf/jquery/web/ContextManager.java +++ b/src/main/java/org/usf/jquery/web/ContextManager.java @@ -31,7 +31,12 @@ public static void register(ContextEnvironment config) { } throw resourceAlreadyExistsException(k); }); - config.bind(); // outer bind + setCurrentContext(config); + try { + config.bind(); // outer bind + } finally { + releaseContext(); + } } public static ContextEnvironment currentContext() { diff --git a/src/main/java/org/usf/jquery/web/ViewDecorator.java b/src/main/java/org/usf/jquery/web/ViewDecorator.java index 5bc05a98..0e939fe2 100644 --- a/src/main/java/org/usf/jquery/web/ViewDecorator.java +++ b/src/main/java/org/usf/jquery/web/ViewDecorator.java @@ -88,7 +88,7 @@ private TableView buildView() { return idx == -1 ? new TableView(null, requireLegalVariable(tn), identity()) : new TableView(requireLegalVariable(tn.substring(0, idx)), - requireLegalVariable(tn.substring(idx, tn.length())), identity()); + requireLegalVariable(tn.substring(idx+1, tn.length())), identity()); } throw undeclaredResouceException(identity(), currentContext().getDatabase().identity()); } From be19e6b263889523a3668b8754b0312b73194c10 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 25 Sep 2024 22:57:39 +0200 Subject: [PATCH 297/298] edit --- .../java/org/usf/jquery/web/ViewMetadata.java | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index ab071dba..4484b53a 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -78,24 +78,37 @@ final ViewMetadata fetch(DatabaseMetaData metadata, String schema) throws SQLExc } void fetchView(DatabaseMetaData metadata, TableView view, String schema) throws SQLException { - try(var rs = metadata.getColumns(null, view.getSchemaOrElse(schema), view.getName(), null)){ - if(rs.next()) { - var db = reverseMapKeys(); //reverse key - do { - var cm = db.remove(rs.getString("COLUMN_NAME")); - if(nonNull(cm)) { - cm.update( - rs.getInt("DATA_TYPE"), - rs.getInt("COLUMN_SIZE"), - rs.getInt("DECIMAL_DIGITS")); - } // else undeclared column - } while(rs.next()); - if(!db.isEmpty()) { //no such columns - throw columnsNotFoundException(db.keySet()); + schema = view.getSchemaOrElse(schema); + try(var tm = metadata.getTables(null, schema, view.getName(), null)) { + if(tm.next()) { + if("TABLE".equals(tm.getString("TABLE_TYPE"))) { + try(var rs = metadata.getColumns(null, schema, view.getName(), null)){ + if(rs.next()) { + var db = reverseMapKeys(); //reverse key + do { + var cm = db.remove(rs.getString("COLUMN_NAME")); + if(nonNull(cm)) { + cm.update( + rs.getInt("DATA_TYPE"), + rs.getInt("COLUMN_SIZE"), + rs.getInt("DECIMAL_DIGITS")); + } // else undeclared column + } while(rs.next()); + if(!db.isEmpty()) { //no such columns + throw columnsNotFoundException(db.keySet()); + } + } + else { + throw new IllegalArgumentException("no columns"); + } + } + } + else { + fetch(metadata, view, schema); } } else { - throw new NoSuchElementException(quote(view.toString()) + " table not found"); + throw new NoSuchElementException(quote(view.toString()) + " table or view not found"); } } } From 0e308836d4c8c0cbfe1147e47ea57ec9dc58ddb1 Mon Sep 17 00:00:00 2001 From: u$f Date: Wed, 25 Sep 2024 23:19:48 +0200 Subject: [PATCH 298/298] edit --- src/main/java/org/usf/jquery/web/ViewMetadata.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/usf/jquery/web/ViewMetadata.java b/src/main/java/org/usf/jquery/web/ViewMetadata.java index 4484b53a..f709c6ba 100644 --- a/src/main/java/org/usf/jquery/web/ViewMetadata.java +++ b/src/main/java/org/usf/jquery/web/ViewMetadata.java @@ -68,7 +68,7 @@ final ViewMetadata fetch(DatabaseMetaData metadata, String schema) throws SQLExc printViewColumnMap(); } catch(Exception e) { - log.error("error while scanning '{}' metadata", identity(), e); + log.error("error while scanning '{}' metadata", view.toString(), e); } } else {