mirror of
https://github.com/Canner/WrenAI.git
synced 2026-08-30 18:00:36 +08:00
Remove the legacy connector, Cache server and PG Wire Protocol (#639)
* remove legacy connector and pg-wire related implementations * modified the example and docker file * fix the tests * remove the sqlglot ci flow * fix tests
This commit is contained in:
@@ -15,12 +15,6 @@ ENV ENV_WREN_VERSION=${WREN_VERSION}
|
||||
ENV WREN_JAR=wren-server-${ENV_WREN_VERSION}-executable.jar
|
||||
COPY ${WREN_JAR} ./
|
||||
|
||||
|
||||
COPY wren-sqlglot-server/ ./wren-sqlglot-server/
|
||||
RUN apt-get install -y python3 python3-pip
|
||||
RUN pip3 install -r ./wren-sqlglot-server/requirements.txt
|
||||
|
||||
|
||||
COPY entrypoint.sh ./
|
||||
RUN chmod +x ./entrypoint.sh
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/bin/bash
|
||||
export ENV_MAX_HEAP_SIZE=$2
|
||||
export ENV_MIN_HEAP_SIZE=$3
|
||||
python3 wren-sqlglot-server/main.py &
|
||||
|
||||
# Required add-opens=java.nio=ALL-UNNAMED for Apache arrow in the Snowflake
|
||||
java -Xmx${ENV_MAX_HEAP_SIZE:-"512m"} -Xms${ENV_MIN_HEAP_SIZE:-"64m"} -Dconfig=etc/config.properties \
|
||||
|
||||
@@ -2,5 +2,4 @@
|
||||
|
||||
This is some example Docker Compose project for different data source types. Please go to the respective directory for more information.
|
||||
|
||||
- [BigQuery Example](bigquery-example/README.md)
|
||||
- [DuckDB TPCH Example](duckdb-tpch-example/README.md)
|
||||
@@ -1 +0,0 @@
|
||||
PLATFORM=linux/amd64
|
||||
@@ -1,23 +0,0 @@
|
||||
# Description
|
||||
|
||||
This is an example Docker Compose project for running the Wren Engine.
|
||||
|
||||
# How to use
|
||||
|
||||
1. Set up the platform in `.env` file. (`linux/amd64`, `linux/arm64`)
|
||||
2. Configure settings in the `etc/config.properties` file.
|
||||
3. Place your MDL in `etc/mdl` file after removing the sample MDL file `etc/mdl/sample.json`.
|
||||
- The `mdl` directory should contain only one json file.
|
||||
4. Set up the accounts if you needs or remove the sample accounts if you don't need.
|
||||
- Sample accounts are provided in the `etc/accounts` directory.
|
||||
5. Run the docker-compose in this directory.
|
||||
```bash
|
||||
docker compose --env-file .env up
|
||||
```
|
||||
6. Connect using psql or another PostgreSQL driver using port 7432.
|
||||
- Sample usernames and passwords are `ina` and `wah`, or `azki` and `guess`.
|
||||
- The default database name should match the catalog of the MDL file.
|
||||
- The default schema name should match the schema of the MDL file.
|
||||
```bash
|
||||
psql 'host=localhost user=ina dbname=test_catalog port=7432 options=--search_path=test_schema'
|
||||
```
|
||||
@@ -1,13 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
engine:
|
||||
image: ghcr.io/canner/wren-engine:latest
|
||||
platform: ${PLATFORM}
|
||||
ports:
|
||||
- 8080:8080
|
||||
- 7432:7432
|
||||
volumes:
|
||||
- ./etc:/usr/src/app/etc
|
||||
environment:
|
||||
- SQLGLOT_PORT=8000
|
||||
@@ -1,2 +0,0 @@
|
||||
ina wah
|
||||
azki guess
|
||||
@@ -1,27 +0,0 @@
|
||||
#
|
||||
# /*
|
||||
# * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# * you may not use this file except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.apache.org/licenses/LICENSE-2.0
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# */
|
||||
#
|
||||
node.environment=production
|
||||
wren.directory=/usr/src/app/etc/mdl
|
||||
pg-wire-protocol.auth.file=/usr/src/app/etc/accounts
|
||||
wren.experimental-enable-dynamic-fields=false
|
||||
wren.datasource.type=bigquery
|
||||
bigquery.project-id=
|
||||
bigquery.credentials-key=
|
||||
bigquery.location=
|
||||
bigquery.bucket-name=
|
||||
duckdb.storage.access-key=
|
||||
duckdb.storage.secret-key=
|
||||
pg-wire-protocol.enabled=true
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"catalog": "test_catalog",
|
||||
"schema": "test_schema",
|
||||
"models": []
|
||||
}
|
||||
@@ -11,16 +11,55 @@ You can learn how to set up the Wren Engine with DuckDB data source to analyze y
|
||||
4. Set up your initial SQL and session SQL in `etc/duckdb-init.sql` and `etc/duckdb-session.sql` files.
|
||||
5. Place your MDL in `etc/mdl` file after removing the sample MDL file `etc/mdl/sample.json`.
|
||||
- The `mdl` directory should contain only one json file.
|
||||
6. Set up the accounts if you needs or remove the sample accounts if you don't need.
|
||||
- Sample accounts are provided in the `etc/accounts` directory.
|
||||
7. Run the docker-compose in this directory.
|
||||
```bash
|
||||
docker compose --env-file .env up
|
||||
```
|
||||
8. Connect using psql or another PostgreSQL driver using port 7432.
|
||||
- Sample usernames and passwords are `ina` and `wah`, or `azki` and `guess`.
|
||||
- The default database name should match the catalog of the MDL file.
|
||||
- The default schema name should match the schema of the MDL file.
|
||||
```bash
|
||||
psql 'host=localhost user=ina dbname=wren port=7432 options=--search_path=tpch'
|
||||
```
|
||||
8. Call the Wren Engine API to analyze your data with the request body as below.
|
||||
- URL
|
||||
```
|
||||
GET http://localhost:8080/v1/mdl/preview
|
||||
```
|
||||
- Body: The manifest is the content of the MDL file.
|
||||
```json
|
||||
{
|
||||
"manifest": {
|
||||
"catalog": "wren",
|
||||
"schema": "tpch",
|
||||
"models": [
|
||||
{
|
||||
"name": "Orders",
|
||||
"tableReference": {
|
||||
"catalog": "memory",
|
||||
"schema": "tpch",
|
||||
"table": "orders"
|
||||
},
|
||||
"columns": [
|
||||
{
|
||||
"name": "orderkey",
|
||||
"expression": "o_orderkey",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "custkey",
|
||||
"expression": "o_custkey",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "orderstatus",
|
||||
"expression": "o_orderstatus",
|
||||
"type": "varchar"
|
||||
},
|
||||
{
|
||||
"name": "totalprice",
|
||||
"expression": "o_totalprice",
|
||||
"type": "float"
|
||||
}
|
||||
],
|
||||
"primaryKey": "orderkey"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sql": "select * from Orders"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,9 +6,6 @@ services:
|
||||
platform: ${PLATFORM}
|
||||
ports:
|
||||
- 8080:8080
|
||||
- 7432:7432
|
||||
volumes:
|
||||
- ./etc:/usr/src/app/etc
|
||||
- ./data:/usr/src/app/data
|
||||
environment:
|
||||
- SQLGLOT_PORT=8000
|
||||
@@ -1,2 +0,0 @@
|
||||
ina wah
|
||||
azki guess
|
||||
@@ -15,9 +15,7 @@
|
||||
#
|
||||
node.environment=production
|
||||
wren.directory=/usr/src/app/etc/mdl
|
||||
pg-wire-protocol.auth.file=/usr/src/app/etc/accounts
|
||||
wren.experimental-enable-dynamic-fields=false
|
||||
wren.datasource.type=duckdb
|
||||
duckdb.connector.init-sql-path=/usr/src/app/etc/duckdb-init.sql
|
||||
duckdb.connector.session-sql-path=/usr/src/app/etc/duckdb-session.sql
|
||||
pg-wire-protocol.enabled=true
|
||||
|
||||
@@ -29,11 +29,8 @@
|
||||
<modules>
|
||||
<module>trino-parser</module>
|
||||
<module>wren-base</module>
|
||||
<module>wren-cache</module>
|
||||
<module>wren-main</module>
|
||||
<module>wren-server</module>
|
||||
<module>wren-shaded</module>
|
||||
<module>wren-testing</module>
|
||||
<module>wren-tests</module>
|
||||
</modules>
|
||||
|
||||
@@ -92,15 +89,6 @@
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.cloud</groupId>
|
||||
<artifactId>libraries-bom</artifactId>
|
||||
<version>25.2.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jdbi</groupId>
|
||||
<artifactId>jdbi3-bom</artifactId>
|
||||
@@ -117,12 +105,6 @@
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.carrotsearch</groupId>
|
||||
<artifactId>hppc</artifactId>
|
||||
<version>0.9.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
@@ -147,30 +129,6 @@
|
||||
<version>2.17.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.api</groupId>
|
||||
<artifactId>gax</artifactId>
|
||||
<version>2.16.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.auth</groupId>
|
||||
<artifactId>google-auth-library-credentials</artifactId>
|
||||
<version>1.23.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.auth</groupId>
|
||||
<artifactId>google-auth-library-oauth2-http</artifactId>
|
||||
<version>1.23.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.auto.value</groupId>
|
||||
<artifactId>auto-value-annotations</artifactId>
|
||||
<version>1.10.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
@@ -195,30 +153,12 @@
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.http-client</groupId>
|
||||
<artifactId>google-http-client</artifactId>
|
||||
<version>1.44.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.http-client</groupId>
|
||||
<artifactId>google-http-client-gson</artifactId>
|
||||
<version>1.44.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>3.19.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>2.0.202</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.hubspot.jinjava</groupId>
|
||||
<artifactId>jinjava</artifactId>
|
||||
@@ -243,12 +183,6 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.15</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.airlift</groupId>
|
||||
<artifactId>bootstrap</artifactId>
|
||||
@@ -339,36 +273,6 @@
|
||||
<version>1.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-api</artifactId>
|
||||
<version>1.60.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-context</artifactId>
|
||||
<version>1.60.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.opencensus</groupId>
|
||||
<artifactId>opencensus-api</artifactId>
|
||||
<version>0.31.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.opencensus</groupId>
|
||||
<artifactId>opencensus-contrib-http-util</artifactId>
|
||||
<version>0.31.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.wren</groupId>
|
||||
<artifactId>trino-parser</artifactId>
|
||||
@@ -381,11 +285,6 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.wren</groupId>
|
||||
<artifactId>wren-cache</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.wren</groupId>
|
||||
<artifactId>wren-main</artifactId>
|
||||
@@ -398,18 +297,6 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.wren</groupId>
|
||||
<artifactId>wren-shaded</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.wren</groupId>
|
||||
<artifactId>wren-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.wren</groupId>
|
||||
<artifactId>wren-tests</artifactId>
|
||||
@@ -422,36 +309,18 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>it.unimi.dsi</groupId>
|
||||
<artifactId>fastutil</artifactId>
|
||||
<version>8.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
<version>1.14.13</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.snowflake</groupId>
|
||||
<artifactId>snowflake-jdbc</artifactId>
|
||||
<version>3.15.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
<version>${dep.antlr.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.9</version>
|
||||
</dependency>
|
||||
|
||||
<!-- TODO: move this to Airbase -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
@@ -498,7 +367,7 @@
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.3.1</version>
|
||||
<version>42.3.9</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -710,10 +579,6 @@
|
||||
<excludes>
|
||||
<exclude>**/*jmhTest*.java</exclude>
|
||||
<exclude>**/*jmhType*.java</exclude>
|
||||
<exclude>TestTPCHWithSnowflake.java</exclude>
|
||||
<exclude>TestDeploySnowflakeRuntime</exclude>
|
||||
<exclude>TestWireProtocolTypeWithSnowflake</exclude>
|
||||
<exclude>TestWrenWithSnowflake.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -31,12 +31,6 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.carrotsearch</groupId>
|
||||
<artifactId>hppc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
@@ -79,10 +73,6 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.airlift</groupId>
|
||||
<artifactId>configuration</artifactId>
|
||||
@@ -103,11 +93,6 @@
|
||||
<artifactId>units</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.wren</groupId>
|
||||
<artifactId>trino-parser</artifactId>
|
||||
@@ -118,11 +103,6 @@
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
@@ -156,12 +136,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>it.unimi.dsi</groupId>
|
||||
<artifactId>fastutil</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.trino.execution.sql;
|
||||
|
||||
import io.trino.sql.SqlFormatter;
|
||||
import io.trino.sql.parser.ParsingException;
|
||||
import io.trino.sql.parser.ParsingOptions;
|
||||
import io.trino.sql.parser.SqlParser;
|
||||
import io.trino.sql.tree.Statement;
|
||||
import io.wren.base.WrenException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static io.trino.sql.parser.ParsingOptions.DecimalLiteralTreatment.REJECT;
|
||||
import static io.wren.base.metadata.StandardErrorCode.GENERIC_INTERNAL_ERROR;
|
||||
import static java.lang.String.format;
|
||||
|
||||
public final class SqlFormatterUtil
|
||||
{
|
||||
private SqlFormatterUtil() {}
|
||||
|
||||
public static String getFormattedSql(Statement statement, SqlParser sqlParser)
|
||||
{
|
||||
String sql = SqlFormatter.formatSql(statement);
|
||||
|
||||
// verify round-trip
|
||||
Statement parsed;
|
||||
try {
|
||||
ParsingOptions parsingOptions = new ParsingOptions(REJECT /* formatted SQL should be unambiguous */);
|
||||
parsed = sqlParser.createStatement(sql, parsingOptions);
|
||||
}
|
||||
catch (ParsingException e) {
|
||||
throw formattingFailure(e, "Formatted query does not parse", statement, sql);
|
||||
}
|
||||
if (!statement.equals(parsed)) {
|
||||
throw formattingFailure(null, "Query does not round-trip", statement, sql);
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
private static WrenException formattingFailure(@Nullable Throwable cause, String message, Statement statement, String sql)
|
||||
{
|
||||
WrenException exception = new WrenException(GENERIC_INTERNAL_ERROR, message, cause);
|
||||
exception.addSuppressed(new RuntimeException("Statement: " + statement));
|
||||
exception.addSuppressed(new RuntimeException(format("Formatted: [%s]", sql)));
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,21 @@ package io.wren.base;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.wren.base.type.AnyType;
|
||||
import io.wren.base.type.PGType;
|
||||
import io.wren.base.type.PGTypes;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.google.common.base.MoreObjects.toStringHelper;
|
||||
|
||||
public final class Column
|
||||
{
|
||||
public static Column column(String name, String type)
|
||||
{
|
||||
return new Column(name, type);
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final PGType<?> type;
|
||||
private final String type;
|
||||
|
||||
@JsonCreator
|
||||
public Column(
|
||||
@@ -31,13 +38,7 @@ public final class Column
|
||||
@JsonProperty("type") String type)
|
||||
{
|
||||
this.name = name;
|
||||
this.type = PGTypes.nameToPgType(type).orElse(AnyType.ANY);
|
||||
}
|
||||
|
||||
public Column(String name, PGType<?> type)
|
||||
{
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.type = type.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
@@ -46,14 +47,38 @@ public final class Column
|
||||
return name;
|
||||
}
|
||||
|
||||
public PGType<?> getType()
|
||||
@JsonProperty
|
||||
public String getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
@JsonProperty("type")
|
||||
public String getTypeName()
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
return type.typName();
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Column column = (Column) o;
|
||||
return Objects.equals(name, column.name) &&
|
||||
Objects.equals(type, column.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(name, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return toStringHelper(this)
|
||||
.add("name", name)
|
||||
.add("type", type)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,22 +14,20 @@
|
||||
|
||||
package io.wren.base;
|
||||
|
||||
import io.wren.base.type.PGType;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Parameter
|
||||
{
|
||||
private final PGType<?> type;
|
||||
private final String type;
|
||||
private final Object value;
|
||||
|
||||
public Parameter(PGType<?> type, Object value)
|
||||
public Parameter(String type, Object value)
|
||||
{
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public PGType<?> getType()
|
||||
public String getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
package io.wren.base.client;
|
||||
|
||||
import io.wren.base.Column;
|
||||
import io.wren.base.Parameter;
|
||||
import io.wren.base.metadata.ColumnMetadata;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
@@ -34,7 +34,7 @@ public interface Client
|
||||
|
||||
void executeDDL(String sql);
|
||||
|
||||
List<ColumnMetadata> describe(String sql, List<Parameter> parameters);
|
||||
List<Column> describe(String sql, List<Parameter> parameters);
|
||||
|
||||
List<String> listTables();
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.base.client.duckdb;
|
||||
|
||||
public interface CacheStorageConfig
|
||||
{
|
||||
String generateDuckdbParquetStatement(String path, String tableName);
|
||||
}
|
||||
@@ -19,14 +19,13 @@ import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import io.airlift.log.Logger;
|
||||
import io.airlift.units.DataSize;
|
||||
import io.wren.base.Column;
|
||||
import io.wren.base.Parameter;
|
||||
import io.wren.base.WrenException;
|
||||
import io.wren.base.client.AutoCloseableIterator;
|
||||
import io.wren.base.client.Client;
|
||||
import io.wren.base.client.jdbc.JdbcRecordIterator;
|
||||
import io.wren.base.metadata.ColumnMetadata;
|
||||
import io.wren.base.metadata.StandardErrorCode;
|
||||
import io.wren.base.type.PGType;
|
||||
import org.duckdb.DuckDBConnection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -42,7 +41,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static io.wren.base.client.duckdb.DuckdbTypes.toPGType;
|
||||
import static java.lang.String.format;
|
||||
|
||||
public final class DuckdbClient
|
||||
@@ -50,18 +48,15 @@ public final class DuckdbClient
|
||||
{
|
||||
private static final Logger LOG = Logger.get(DuckdbClient.class);
|
||||
private final DuckDBConfig duckDBConfig;
|
||||
private final CacheStorageConfig cacheStorageConfig;
|
||||
private final DuckDBSettingSQL duckDBSettingSQL;
|
||||
private DuckDBConnection duckDBConnection;
|
||||
private HikariDataSource connectionPool;
|
||||
|
||||
public DuckdbClient(
|
||||
DuckDBConfig duckDBConfig,
|
||||
@Nullable CacheStorageConfig cacheStorageConfig,
|
||||
@Nullable DuckDBSettingSQL duckDBSettingSQL)
|
||||
{
|
||||
this.duckDBConfig = duckDBConfig;
|
||||
this.cacheStorageConfig = cacheStorageConfig;
|
||||
this.duckDBSettingSQL = duckDBSettingSQL;
|
||||
init();
|
||||
}
|
||||
@@ -99,12 +94,11 @@ public final class DuckdbClient
|
||||
|
||||
public synchronized void initPool()
|
||||
{
|
||||
connectionPool = new HikariDataSource(getHikariConfig(duckDBConfig, cacheStorageConfig, duckDBConnection, duckDBSettingSQL));
|
||||
connectionPool = new HikariDataSource(getHikariConfig(duckDBConfig, duckDBConnection, duckDBSettingSQL));
|
||||
}
|
||||
|
||||
private static HikariConfig getHikariConfig(
|
||||
DuckDBConfig duckDBConfig,
|
||||
CacheStorageConfig cacheStorageConfig,
|
||||
DuckDBConnection duckDBConnection,
|
||||
DuckDBSettingSQL duckDBSettingSQL)
|
||||
{
|
||||
@@ -116,12 +110,12 @@ public final class DuckdbClient
|
||||
config.setMinimumIdle(duckDBConfig.getMaxConcurrentTasks());
|
||||
// remain some query slots for metadata queries
|
||||
config.setMaximumPoolSize(duckDBConfig.getMaxConcurrentTasks() + duckDBConfig.getMaxConcurrentMetadataQueries());
|
||||
String initSql = buildConnectionInitSql(duckDBSettingSQL, cacheStorageConfig, duckDBConfig);
|
||||
String initSql = buildConnectionInitSql(duckDBSettingSQL, duckDBConfig);
|
||||
config.setConnectionInitSql(initSql);
|
||||
return config;
|
||||
}
|
||||
|
||||
private static String buildConnectionInitSql(DuckDBSettingSQL duckDBSettingSQL, CacheStorageConfig cacheStorageConfig, DuckDBConfig duckDBConfig)
|
||||
private static String buildConnectionInitSql(DuckDBSettingSQL duckDBSettingSQL, DuckDBConfig duckDBConfig)
|
||||
{
|
||||
List<String> sql = new ArrayList<>();
|
||||
// Both of them should be true in default, however they're some issue in v0.10.3.
|
||||
@@ -135,11 +129,6 @@ public final class DuckdbClient
|
||||
}
|
||||
else {
|
||||
sql.add("SET search_path = 'main'");
|
||||
if (cacheStorageConfig instanceof DuckdbS3StyleStorageConfig) {
|
||||
DuckdbS3StyleStorageConfig duckdbS3StyleStorageConfig = (DuckdbS3StyleStorageConfig) cacheStorageConfig;
|
||||
sql.add(format("SET s3_endpoint='%s'", duckdbS3StyleStorageConfig.getEndpoint()));
|
||||
sql.add(format("SET s3_url_style='%s'", duckdbS3StyleStorageConfig.getUrlStyle()));
|
||||
}
|
||||
sql.add(format("SET home_directory='%s'", duckDBConfig.getHomeDirectory()));
|
||||
}
|
||||
return String.join(";", sql);
|
||||
@@ -173,20 +162,16 @@ public final class DuckdbClient
|
||||
* So we ignore the parameters here.
|
||||
*/
|
||||
@Override
|
||||
public List<ColumnMetadata> describe(String sql, List<Parameter> ignored)
|
||||
public List<Column> describe(String sql, List<Parameter> ignored)
|
||||
{
|
||||
try (Connection connection = createConnection()) {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql);
|
||||
ResultSetMetaData metaData = preparedStatement.getMetaData();
|
||||
int columnCount = metaData.getColumnCount();
|
||||
|
||||
ImmutableList.Builder<ColumnMetadata> builder = ImmutableList.builder();
|
||||
ImmutableList.Builder<Column> builder = ImmutableList.builder();
|
||||
for (int i = 1; i <= columnCount; i++) {
|
||||
PGType<?> type = toPGType(metaData, i);
|
||||
builder.add(ColumnMetadata.builder()
|
||||
.setName(metaData.getColumnName(i))
|
||||
.setType(type)
|
||||
.build());
|
||||
builder.add(new Column(metaData.getColumnName(i), metaData.getColumnTypeName(i)));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
@@ -269,7 +254,6 @@ public final class DuckdbClient
|
||||
public static class Builder
|
||||
{
|
||||
private DuckDBConfig duckDBConfig;
|
||||
private CacheStorageConfig cacheStorageConfig;
|
||||
private DuckDBSettingSQL duckDBSettingSQL;
|
||||
|
||||
public Builder setDuckDBConfig(DuckDBConfig duckDBConfig)
|
||||
@@ -278,12 +262,6 @@ public final class DuckdbClient
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCacheStorageConfig(CacheStorageConfig cacheStorageConfig)
|
||||
{
|
||||
this.cacheStorageConfig = cacheStorageConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDuckDBSettingSQL(DuckDBSettingSQL duckDBSettingSQL)
|
||||
{
|
||||
this.duckDBSettingSQL = duckDBSettingSQL;
|
||||
@@ -292,7 +270,7 @@ public final class DuckdbClient
|
||||
|
||||
public DuckdbClient build()
|
||||
{
|
||||
return new DuckdbClient(duckDBConfig, cacheStorageConfig, duckDBSettingSQL);
|
||||
return new DuckdbClient(duckDBConfig, duckDBSettingSQL);
|
||||
}
|
||||
|
||||
public Optional<DuckdbClient> buildSafely()
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.client.duckdb;
|
||||
|
||||
import io.airlift.configuration.Config;
|
||||
import io.airlift.configuration.ConfigDescription;
|
||||
import io.airlift.configuration.ConfigSecuritySensitive;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
public class DuckdbS3StyleStorageConfig
|
||||
implements CacheStorageConfig
|
||||
{
|
||||
public static final String DUCKDB_STORAGE_ENDPOINT = "duckdb.storage.endpoint";
|
||||
public static final String DUCKDB_STORAGE_ACCESS_KEY = "duckdb.storage.access-key";
|
||||
public static final String DUCKDB_STORAGE_SECRET_KEY = "duckdb.storage.secret-key";
|
||||
public static final String DUCKDB_STORAGE_REGION = "duckdb.storage.region";
|
||||
public static final String DUCKDB_STORAGE_URL_STYLE = "duckdb.storage.url-style";
|
||||
|
||||
// https://duckdb.org/docs/guides/import/s3_import.html
|
||||
private String endpoint = "storage.googleapis.com";
|
||||
private Optional<String> accessKey = Optional.empty();
|
||||
private Optional<String> secretKey = Optional.empty();
|
||||
private Optional<String> region = Optional.empty();
|
||||
private String urlStyle = "path";
|
||||
|
||||
@Config(DUCKDB_STORAGE_ENDPOINT)
|
||||
@ConfigDescription("The storage endpoint; default is storage.googleapis.com")
|
||||
public DuckdbS3StyleStorageConfig setEndpoint(String endpoint)
|
||||
{
|
||||
this.endpoint = endpoint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getEndpoint()
|
||||
{
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
@Config(DUCKDB_STORAGE_ACCESS_KEY)
|
||||
@ConfigDescription("The storage access key")
|
||||
@ConfigSecuritySensitive
|
||||
public DuckdbS3StyleStorageConfig setAccessKey(String accessKey)
|
||||
{
|
||||
this.accessKey = Optional.of(accessKey);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<String> getAccessKey()
|
||||
{
|
||||
return accessKey;
|
||||
}
|
||||
|
||||
@Config(DUCKDB_STORAGE_SECRET_KEY)
|
||||
@ConfigDescription("The storage secret key")
|
||||
@ConfigSecuritySensitive
|
||||
public DuckdbS3StyleStorageConfig setSecretKey(String secretKey)
|
||||
{
|
||||
this.secretKey = Optional.of(secretKey);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<String> getSecretKey()
|
||||
{
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
@Config(DUCKDB_STORAGE_REGION)
|
||||
@ConfigDescription("The storage region")
|
||||
public DuckdbS3StyleStorageConfig setRegion(String region)
|
||||
{
|
||||
this.region = Optional.of(region);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<String> getRegion()
|
||||
{
|
||||
return region;
|
||||
}
|
||||
|
||||
@Config(DUCKDB_STORAGE_URL_STYLE)
|
||||
@ConfigDescription("The storage url style; default is path")
|
||||
public DuckdbS3StyleStorageConfig setUrlStyle(String urlStyle)
|
||||
{
|
||||
this.urlStyle = urlStyle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUrlStyle()
|
||||
{
|
||||
return urlStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateDuckdbParquetStatement(String path, String tableName)
|
||||
{
|
||||
// ref: https://github.com/duckdb/duckdb/issues/1403
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// TODO: check why can't we set s3 access key and secret key in Data source
|
||||
accessKey.ifPresent(accessKey -> sb.append(format("SET s3_access_key_id='%s';\n", accessKey)));
|
||||
secretKey.ifPresent(secretKey -> sb.append(format("SET s3_secret_access_key='%s';\n", secretKey)));
|
||||
sb.append("BEGIN TRANSACTION;\n");
|
||||
sb.append(format("CREATE TABLE \"%s\" AS SELECT * FROM read_parquet('s3://%s');", tableName, path));
|
||||
sb.append("COMMIT;\n");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.client.duckdb;
|
||||
|
||||
public class DuckdbType
|
||||
{
|
||||
private final int jdbcType;
|
||||
private final String name;
|
||||
|
||||
public DuckdbType(int jdbcType, String name)
|
||||
{
|
||||
this.jdbcType = jdbcType;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getJdbcType()
|
||||
{
|
||||
return jdbcType;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.base.client.duckdb;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.wren.base.WrenException;
|
||||
import io.wren.base.type.BigIntType;
|
||||
import io.wren.base.type.BooleanType;
|
||||
import io.wren.base.type.BpCharType;
|
||||
import io.wren.base.type.ByteaType;
|
||||
import io.wren.base.type.CharType;
|
||||
import io.wren.base.type.DateType;
|
||||
import io.wren.base.type.DoubleType;
|
||||
import io.wren.base.type.InetType;
|
||||
import io.wren.base.type.IntegerType;
|
||||
import io.wren.base.type.IntervalType;
|
||||
import io.wren.base.type.JsonType;
|
||||
import io.wren.base.type.NumericType;
|
||||
import io.wren.base.type.OidType;
|
||||
import io.wren.base.type.PGType;
|
||||
import io.wren.base.type.PGTypes;
|
||||
import io.wren.base.type.RealType;
|
||||
import io.wren.base.type.SmallIntType;
|
||||
import io.wren.base.type.TimestampType;
|
||||
import io.wren.base.type.TimestampWithTimeZoneType;
|
||||
import io.wren.base.type.TinyIntType;
|
||||
import io.wren.base.type.UuidType;
|
||||
import io.wren.base.type.VarcharType;
|
||||
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static io.wren.base.metadata.StandardErrorCode.NOT_SUPPORTED;
|
||||
import static java.util.Locale.ENGLISH;
|
||||
|
||||
public final class DuckdbTypes
|
||||
{
|
||||
// other types LIST, ENUM, UTINYINT, USMALLINT, STRUCT, UUID, JSON, UINTEGER, UBIGINT, INTERVAL, MAP
|
||||
public static final DuckdbType BOOLEAN = new DuckdbType(Types.BOOLEAN, "BOOLEAN");
|
||||
public static final DuckdbType BIGINT = new DuckdbType(Types.BIGINT, "BIGINT");
|
||||
public static final DuckdbType HUGEINT = new DuckdbType(Types.DECIMAL, "HUGEINT");
|
||||
public static final DuckdbType BIT = new DuckdbType(Types.BIT, "BIT");
|
||||
public static final DuckdbType BLOB = new DuckdbType(Types.BLOB, "BLOB");
|
||||
public static final DuckdbType DATE = new DuckdbType(Types.DATE, "DATE");
|
||||
public static final DuckdbType DOUBLE = new DuckdbType(Types.DOUBLE, "DOUBLE");
|
||||
public static final DuckdbType REAL = new DuckdbType(Types.REAL, "REAL");
|
||||
public static final DuckdbType FLOAT = new DuckdbType(Types.FLOAT, "FLOAT");
|
||||
public static final DuckdbType DECIMAL = new DuckdbType(Types.DECIMAL, "DECIMAL");
|
||||
public static final DuckdbType INTEGER = new DuckdbType(Types.INTEGER, "INTEGER");
|
||||
public static final DuckdbType SMALLINT = new DuckdbType(Types.SMALLINT, "SMALLINT");
|
||||
public static final DuckdbType TINYINT = new DuckdbType(Types.TINYINT, "TINYINT");
|
||||
// TODO: check
|
||||
public static final DuckdbType INTERVAL = new DuckdbType(Types.OTHER, "INTERVAL");
|
||||
public static final DuckdbType TIME = new DuckdbType(Types.TIME, "TIME");
|
||||
public static final DuckdbType TIMESTAMP = new DuckdbType(Types.TIMESTAMP, "TIMESTAMP");
|
||||
public static final DuckdbType TIMESTAMP_WITH_TIMEZONE = new DuckdbType(Types.TIMESTAMP_WITH_TIMEZONE, "TIMESTAMP WITH TIMEZONE");
|
||||
public static final DuckdbType VARCHAR = new DuckdbType(Types.VARCHAR, "VARCHAR");
|
||||
public static final DuckdbType JSON = new DuckdbType(Types.STRUCT, "JSON");
|
||||
public static final DuckdbType UUID = new DuckdbType(Types.JAVA_OBJECT, "UUID");
|
||||
|
||||
private static final Map<String, DuckdbType> duckdbTypes = ImmutableMap.<String, DuckdbType>builder()
|
||||
.put(BOOLEAN.getName(), BOOLEAN)
|
||||
.put(BIGINT.getName(), BIGINT)
|
||||
.put(BIT.getName(), BIT)
|
||||
.put(BLOB.getName(), BLOB)
|
||||
.put(DATE.getName(), DATE)
|
||||
.put(DOUBLE.getName(), DOUBLE)
|
||||
.put(REAL.getName(), REAL)
|
||||
.put(FLOAT.getName(), FLOAT)
|
||||
.put(DECIMAL.getName(), DECIMAL)
|
||||
.put(INTEGER.getName(), INTEGER)
|
||||
.put(SMALLINT.getName(), SMALLINT)
|
||||
.put(TINYINT.getName(), TINYINT)
|
||||
.put(INTERVAL.getName(), INTERVAL)
|
||||
.put(TIME.getName(), TIME)
|
||||
.put(TIMESTAMP.getName(), TIMESTAMP)
|
||||
.put(TIMESTAMP_WITH_TIMEZONE.getName(), TIMESTAMP_WITH_TIMEZONE)
|
||||
.put(VARCHAR.getName(), VARCHAR)
|
||||
.put(JSON.getName(), JSON)
|
||||
.put(HUGEINT.getName(), HUGEINT)
|
||||
.put(UUID.getName(), UUID)
|
||||
.build();
|
||||
|
||||
private static final Map<Integer, PGType<?>> duckdbTypeToPgTypeMap = ImmutableMap.<Integer, PGType<?>>builder()
|
||||
.put(BOOLEAN.getJdbcType(), BooleanType.BOOLEAN)
|
||||
.put(BLOB.getJdbcType(), ByteaType.BYTEA)
|
||||
.put(TINYINT.getJdbcType(), TinyIntType.TINYINT)
|
||||
.put(SMALLINT.getJdbcType(), SmallIntType.SMALLINT)
|
||||
.put(INTEGER.getJdbcType(), IntegerType.INTEGER)
|
||||
.put(BIGINT.getJdbcType(), BigIntType.BIGINT)
|
||||
.put(FLOAT.getJdbcType(), RealType.REAL)
|
||||
.put(DOUBLE.getJdbcType(), DoubleType.DOUBLE)
|
||||
.put(DECIMAL.getJdbcType(), NumericType.NUMERIC)
|
||||
.put(VARCHAR.getJdbcType(), VarcharType.VARCHAR)
|
||||
.put(DATE.getJdbcType(), DateType.DATE)
|
||||
.put(TIMESTAMP.getJdbcType(), TimestampType.TIMESTAMP)
|
||||
.put(TIMESTAMP_WITH_TIMEZONE.getJdbcType(), TimestampWithTimeZoneType.TIMESTAMP_WITH_TIMEZONE)
|
||||
.put(JSON.getJdbcType(), JsonType.JSON)
|
||||
.build();
|
||||
|
||||
// TODO: RECORD, HSTORE
|
||||
private static final Map<PGType<?>, DuckdbType> pgTypeToDuckdbTypeMap = ImmutableMap.<PGType<?>, DuckdbType>builder()
|
||||
.put(BooleanType.BOOLEAN, BOOLEAN)
|
||||
.put(ByteaType.BYTEA, BLOB)
|
||||
.put(TinyIntType.TINYINT, TINYINT)
|
||||
.put(SmallIntType.SMALLINT, SMALLINT)
|
||||
.put(IntegerType.INTEGER, INTEGER)
|
||||
.put(BigIntType.BIGINT, BIGINT)
|
||||
.put(RealType.REAL, FLOAT)
|
||||
.put(DoubleType.DOUBLE, DOUBLE)
|
||||
.put(NumericType.NUMERIC, DECIMAL)
|
||||
.put(VarcharType.VARCHAR, VARCHAR)
|
||||
.put(VarcharType.TextType.TEXT, VARCHAR)
|
||||
.put(VarcharType.NameType.NAME, VARCHAR)
|
||||
.put(CharType.CHAR, VARCHAR)
|
||||
.put(DateType.DATE, DATE)
|
||||
.put(TimestampType.TIMESTAMP, TIMESTAMP)
|
||||
.put(TimestampWithTimeZoneType.TIMESTAMP_WITH_TIMEZONE, TIMESTAMP_WITH_TIMEZONE)
|
||||
.put(IntervalType.INTERVAL, INTERVAL)
|
||||
.put(JsonType.JSON, JSON)
|
||||
.put(UuidType.UUID, UUID)
|
||||
.put(OidType.OID_INSTANCE, BIGINT)
|
||||
.put(InetType.INET, VARCHAR)
|
||||
.put(BpCharType.BPCHAR, VARCHAR)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* getColumnType only return LIST without inner type if the type is INT[]
|
||||
* But getColumnTypeName will return `INT[]`
|
||||
*/
|
||||
public static PGType<?> toPGType(ResultSetMetaData metaData, int i)
|
||||
throws SQLException
|
||||
{
|
||||
try {
|
||||
return DuckdbTypes.toPGType(metaData.getColumnType(i));
|
||||
}
|
||||
catch (WrenException e) {
|
||||
return DuckdbTypes.toPGType(metaData.getColumnTypeName(i));
|
||||
}
|
||||
}
|
||||
|
||||
public static PGType<?> toPGType(String typeName)
|
||||
{
|
||||
Optional<? extends PGType<?>> pgType = getDuckDBType(getEqualTypeName(typeName))
|
||||
.map(DuckdbType::getJdbcType)
|
||||
.map(duckdbTypeToPgTypeMap::get);
|
||||
|
||||
if (typeName.endsWith("[]")) {
|
||||
pgType = pgType
|
||||
.map(PGType::oid)
|
||||
.map(PGTypes::getArrayType);
|
||||
}
|
||||
|
||||
return pgType.orElseThrow(() -> new WrenException(NOT_SUPPORTED, "Unsupported Type: " + typeName));
|
||||
}
|
||||
|
||||
private static String getEqualTypeName(String typeName)
|
||||
{
|
||||
if (typeName.toLowerCase(ENGLISH).startsWith("decimal")) {
|
||||
return DECIMAL.toString();
|
||||
}
|
||||
if (typeName.toLowerCase(ENGLISH).startsWith("timestamp")) {
|
||||
return TIMESTAMP.toString();
|
||||
}
|
||||
if (typeName.endsWith("[]")) {
|
||||
return typeName.substring(0, typeName.length() - 2);
|
||||
}
|
||||
// TODO: MAP(INT, VARCHAR)
|
||||
// TODO: STRUCT(i INT, j VARCHAR)
|
||||
// TODO: UNION(num INT, text VARCHAR)
|
||||
return typeName;
|
||||
}
|
||||
|
||||
public static PGType<?> toPGType(int type)
|
||||
{
|
||||
return Optional.ofNullable(duckdbTypeToPgTypeMap.get(type))
|
||||
.orElseThrow(() -> new WrenException(NOT_SUPPORTED, "DuckDB unsupported Type: " + type));
|
||||
}
|
||||
|
||||
public static DuckdbType toDuckdbType(PGType<?> type)
|
||||
{
|
||||
return Optional.ofNullable(pgTypeToDuckdbTypeMap.get(type))
|
||||
.orElseThrow(() -> new WrenException(NOT_SUPPORTED, "DuckDB unsupported Type: " + type));
|
||||
}
|
||||
|
||||
public static List<String> getDuckDBTypeNames()
|
||||
{
|
||||
return ImmutableList.copyOf(duckdbTypes.keySet());
|
||||
}
|
||||
|
||||
public static Optional<DuckdbType> getDuckDBType(String name)
|
||||
{
|
||||
return Optional.ofNullable(duckdbTypes.get(name));
|
||||
}
|
||||
|
||||
private DuckdbTypes() {}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.client.duckdb;
|
||||
|
||||
import io.airlift.units.DataSize;
|
||||
import io.wren.base.WrenException;
|
||||
import io.wren.base.metadata.StandardErrorCode;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Locale.ENGLISH;
|
||||
|
||||
public final class DuckdbUtil
|
||||
{
|
||||
private static final Map<String, DataSize.Unit> UNIT_MAP = new HashMap<>();
|
||||
|
||||
// https://github.com/duckdb/duckdb/blob/4c7cb20474baa3a8ca1d5d8ceb22beae0e4c0e4c/src/common/string_util.cpp#L178
|
||||
static {
|
||||
UNIT_MAP.put("BYTE", DataSize.Unit.BYTE);
|
||||
UNIT_MAP.put("BYTES", DataSize.Unit.BYTE);
|
||||
UNIT_MAP.put("KB", DataSize.Unit.KILOBYTE);
|
||||
UNIT_MAP.put("KIB", DataSize.Unit.KILOBYTE);
|
||||
UNIT_MAP.put("MB", DataSize.Unit.MEGABYTE);
|
||||
UNIT_MAP.put("MIB", DataSize.Unit.MEGABYTE);
|
||||
UNIT_MAP.put("GB", DataSize.Unit.GIGABYTE);
|
||||
UNIT_MAP.put("GIB", DataSize.Unit.GIGABYTE);
|
||||
UNIT_MAP.put("TB", DataSize.Unit.TERABYTE);
|
||||
UNIT_MAP.put("TIB", DataSize.Unit.TERABYTE);
|
||||
UNIT_MAP.put("PB", DataSize.Unit.PETABYTE);
|
||||
UNIT_MAP.put("PIB", DataSize.Unit.PETABYTE);
|
||||
}
|
||||
|
||||
private DuckdbUtil() {}
|
||||
|
||||
public static DataSize convertDuckDBUnits(String valueWithUnit)
|
||||
{
|
||||
try {
|
||||
String valueWithUnitUpperCase = valueWithUnit.toUpperCase(ENGLISH);
|
||||
for (Map.Entry<String, DataSize.Unit> entry : UNIT_MAP.entrySet()) {
|
||||
String unit = entry.getKey();
|
||||
if (valueWithUnitUpperCase.endsWith(unit)) {
|
||||
double value = Double.parseDouble(valueWithUnitUpperCase.substring(0, valueWithUnitUpperCase.length() - unit.length()).trim());
|
||||
return DataSize.of((long) value, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new WrenException(StandardErrorCode.GENERIC_INTERNAL_ERROR, String.format("Failed to parse duckdb value %s", valueWithUnit), e);
|
||||
}
|
||||
throw new WrenException(StandardErrorCode.GENERIC_INTERNAL_ERROR, String.format("Failed to parse duckdb value %s", valueWithUnit));
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public abstract class BaseJdbcRecordIterator<T>
|
||||
@@ -41,12 +40,6 @@ public abstract class BaseJdbcRecordIterator<T>
|
||||
|
||||
private boolean hasNext;
|
||||
|
||||
public BaseJdbcRecordIterator(Client client, String sql)
|
||||
throws SQLException
|
||||
{
|
||||
this(client, sql, emptyList());
|
||||
}
|
||||
|
||||
public BaseJdbcRecordIterator(Client client, String sql, List<Parameter> parameters)
|
||||
throws SQLException
|
||||
{
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.base.config;
|
||||
|
||||
import io.airlift.configuration.Config;
|
||||
import io.airlift.configuration.ConfigDescription;
|
||||
import io.airlift.configuration.ConfigSecuritySensitive;
|
||||
import io.airlift.configuration.validation.FileExists;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class BigQueryConfig
|
||||
{
|
||||
public static final String BIGQUERY_CRENDITALS_KEY = "bigquery.credentials-key";
|
||||
public static final String BIGQUERY_CRENDITALS_FILE = "bigquery.credentials-file";
|
||||
public static final String BIGQUERY_PROJECT_ID = "bigquery.project-id";
|
||||
public static final String BIGQUERY_LOCATION = "bigquery.location";
|
||||
public static final String BIGQUERY_BUCKET_NAME = "bigquery.bucket-name";
|
||||
public static final String BIGQUERY_METADATA_SCHEMA_PREFIX = "bigquery.metadata.schema.prefix";
|
||||
private Optional<String> credentialsKey = Optional.empty();
|
||||
private Optional<String> credentialsFile = Optional.empty();
|
||||
private Optional<String> projectId = Optional.empty();
|
||||
|
||||
private Optional<String> location = Optional.empty();
|
||||
|
||||
private Optional<String> bucketName = Optional.empty();
|
||||
private String metadataSchemaPrefix = "";
|
||||
|
||||
public Optional<String> getCredentialsKey()
|
||||
{
|
||||
return credentialsKey;
|
||||
}
|
||||
|
||||
@Config(BIGQUERY_CRENDITALS_KEY)
|
||||
@ConfigDescription("The base64 encoded credentials key")
|
||||
@ConfigSecuritySensitive
|
||||
public BigQueryConfig setCredentialsKey(String credentialsKey)
|
||||
{
|
||||
this.credentialsKey = Optional.ofNullable(credentialsKey);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<@FileExists String> getCredentialsFile()
|
||||
{
|
||||
return credentialsFile;
|
||||
}
|
||||
|
||||
@Config(BIGQUERY_CRENDITALS_FILE)
|
||||
@ConfigDescription("The path to the JSON credentials file")
|
||||
public BigQueryConfig setCredentialsFile(String credentialsFile)
|
||||
{
|
||||
this.credentialsFile = Optional.ofNullable(credentialsFile);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<String> getProjectId()
|
||||
{
|
||||
return projectId;
|
||||
}
|
||||
|
||||
@Config(BIGQUERY_PROJECT_ID)
|
||||
@ConfigDescription("The Google Cloud Project ID where the data reside")
|
||||
public BigQueryConfig setProjectId(String projectId)
|
||||
{
|
||||
this.projectId = Optional.ofNullable(projectId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<String> getLocation()
|
||||
{
|
||||
return location;
|
||||
}
|
||||
|
||||
@Config(BIGQUERY_LOCATION)
|
||||
@ConfigDescription("The Google Cloud Project ID where the data reside")
|
||||
public BigQueryConfig setLocation(String location)
|
||||
{
|
||||
this.location = Optional.ofNullable(location);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<String> getBucketName()
|
||||
{
|
||||
return bucketName;
|
||||
}
|
||||
|
||||
@Config(BIGQUERY_BUCKET_NAME)
|
||||
@ConfigDescription("The Google Cloud bucket name used to temporarily store the metric cached results")
|
||||
public BigQueryConfig setBucketName(String bucketName)
|
||||
{
|
||||
this.bucketName = Optional.ofNullable(bucketName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getMetadataSchemaPrefix()
|
||||
{
|
||||
return metadataSchemaPrefix;
|
||||
}
|
||||
|
||||
@Config(BIGQUERY_METADATA_SCHEMA_PREFIX)
|
||||
@ConfigDescription("Wren needs to create two schemas in BigQuery: wren_temp, pg_catalog. This is a config to add a prefix to the names of these two schemas if it's set.")
|
||||
public BigQueryConfig setMetadataSchemaPrefix(String metadataSchemaPrefix)
|
||||
{
|
||||
this.metadataSchemaPrefix = metadataSchemaPrefix;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,8 @@ import com.google.inject.Inject;
|
||||
import io.airlift.log.Logger;
|
||||
import io.airlift.units.DataSize;
|
||||
import io.wren.base.WrenException;
|
||||
import io.wren.base.client.duckdb.CacheStorageConfig;
|
||||
import io.wren.base.client.duckdb.DuckDBConfig;
|
||||
import io.wren.base.client.duckdb.DuckDBConnectorConfig;
|
||||
import io.wren.base.client.duckdb.DuckdbS3StyleStorageConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
@@ -53,26 +51,7 @@ import static io.wren.base.client.duckdb.DuckDBConfig.DUCKDB_MEMORY_LIMIT;
|
||||
import static io.wren.base.client.duckdb.DuckDBConfig.DUCKDB_TEMP_DIRECTORY;
|
||||
import static io.wren.base.client.duckdb.DuckDBConnectorConfig.DUCKDB_CONNECTOR_INIT_SQL_PATH;
|
||||
import static io.wren.base.client.duckdb.DuckDBConnectorConfig.DUCKDB_CONNECTOR_SESSION_SQL_PATH;
|
||||
import static io.wren.base.client.duckdb.DuckdbS3StyleStorageConfig.DUCKDB_STORAGE_ACCESS_KEY;
|
||||
import static io.wren.base.client.duckdb.DuckdbS3StyleStorageConfig.DUCKDB_STORAGE_ENDPOINT;
|
||||
import static io.wren.base.client.duckdb.DuckdbS3StyleStorageConfig.DUCKDB_STORAGE_REGION;
|
||||
import static io.wren.base.client.duckdb.DuckdbS3StyleStorageConfig.DUCKDB_STORAGE_SECRET_KEY;
|
||||
import static io.wren.base.client.duckdb.DuckdbS3StyleStorageConfig.DUCKDB_STORAGE_URL_STYLE;
|
||||
import static io.wren.base.client.duckdb.FileUtil.ARCHIVED;
|
||||
import static io.wren.base.config.PostgresConfig.POSTGRES_JDBC_URL;
|
||||
import static io.wren.base.config.PostgresConfig.POSTGRES_PASSWORD;
|
||||
import static io.wren.base.config.PostgresConfig.POSTGRES_USER;
|
||||
import static io.wren.base.config.PostgresWireProtocolConfig.PG_WIRE_PROTOCOL_AUTH_FILE;
|
||||
import static io.wren.base.config.PostgresWireProtocolConfig.PG_WIRE_PROTOCOL_NETTY_THREAD_COUNT;
|
||||
import static io.wren.base.config.PostgresWireProtocolConfig.PG_WIRE_PROTOCOL_PORT;
|
||||
import static io.wren.base.config.PostgresWireProtocolConfig.PG_WIRE_PROTOCOL_SSL_ENABLED;
|
||||
import static io.wren.base.config.SnowflakeConfig.SNOWFLAKE_DATABASE;
|
||||
import static io.wren.base.config.SnowflakeConfig.SNOWFLAKE_JDBC_URL;
|
||||
import static io.wren.base.config.SnowflakeConfig.SNOWFLAKE_PASSWORD;
|
||||
import static io.wren.base.config.SnowflakeConfig.SNOWFLAKE_ROLE;
|
||||
import static io.wren.base.config.SnowflakeConfig.SNOWFLAKE_SCHEMA;
|
||||
import static io.wren.base.config.SnowflakeConfig.SNOWFLAKE_USER;
|
||||
import static io.wren.base.config.SnowflakeConfig.SNOWFLAKE_WAREHOUSE;
|
||||
import static io.wren.base.metadata.StandardErrorCode.NOT_FOUND;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
@@ -82,13 +61,8 @@ public class ConfigManager
|
||||
{
|
||||
private static final Logger LOG = Logger.get(ConfigManager.class);
|
||||
private Optional<WrenConfig> wrenConfig;
|
||||
private Optional<PostgresConfig> postgresConfig;
|
||||
private Optional<BigQueryConfig> bigQueryConfig;
|
||||
private Optional<DuckDBConfig> duckDBConfig;
|
||||
private Optional<PostgresWireProtocolConfig> postgresWireProtocolConfig;
|
||||
private Optional<DuckdbS3StyleStorageConfig> duckdbS3StyleStorageConfig;
|
||||
private Optional<DuckDBConnectorConfig> duckDBConnectorConfig;
|
||||
private Optional<SnowflakeConfig> snowflakeConfig;
|
||||
|
||||
private final Map<String, String> configs = new HashMap<>();
|
||||
// All configs set by user and config files. It's used to sync with config file.
|
||||
@@ -100,32 +74,17 @@ public class ConfigManager
|
||||
@Inject
|
||||
public ConfigManager(
|
||||
WrenConfig wrenConfig,
|
||||
PostgresConfig postgresConfig,
|
||||
BigQueryConfig bigQueryConfig,
|
||||
DuckDBConfig duckDBConfig,
|
||||
PostgresWireProtocolConfig postgresWireProtocolConfig,
|
||||
DuckdbS3StyleStorageConfig duckdbS3StyleStorageConfig,
|
||||
DuckDBConnectorConfig duckDBConnectorConfig,
|
||||
SnowflakeConfig snowflakeConfig)
|
||||
DuckDBConnectorConfig duckDBConnectorConfig)
|
||||
{
|
||||
this.wrenConfig = Optional.of(wrenConfig);
|
||||
this.postgresConfig = Optional.of(postgresConfig);
|
||||
this.bigQueryConfig = Optional.of(bigQueryConfig);
|
||||
this.duckDBConfig = Optional.of(duckDBConfig);
|
||||
this.postgresWireProtocolConfig = Optional.of(postgresWireProtocolConfig);
|
||||
this.duckdbS3StyleStorageConfig = Optional.of(duckdbS3StyleStorageConfig);
|
||||
this.duckDBConnectorConfig = Optional.of(duckDBConnectorConfig);
|
||||
this.snowflakeConfig = Optional.of(snowflakeConfig);
|
||||
|
||||
initConfig(
|
||||
wrenConfig,
|
||||
postgresConfig,
|
||||
bigQueryConfig,
|
||||
duckDBConfig,
|
||||
postgresWireProtocolConfig,
|
||||
duckdbS3StyleStorageConfig,
|
||||
duckDBConnectorConfig,
|
||||
snowflakeConfig);
|
||||
duckDBConnectorConfig);
|
||||
|
||||
try {
|
||||
setConfigs.putAll(loadPropertiesFrom(configFile));
|
||||
@@ -137,22 +96,12 @@ public class ConfigManager
|
||||
|
||||
private void initConfig(
|
||||
WrenConfig wrenConfig,
|
||||
PostgresConfig postgresConfig,
|
||||
BigQueryConfig bigQueryConfig,
|
||||
DuckDBConfig duckDBConfig,
|
||||
PostgresWireProtocolConfig postgresWireProtocolConfig,
|
||||
DuckdbS3StyleStorageConfig duckdbS3StyleStorageConfig,
|
||||
DuckDBConnectorConfig duckDBConnectorConfig,
|
||||
SnowflakeConfig snowflakeConfig)
|
||||
DuckDBConnectorConfig duckDBConnectorConfig)
|
||||
{
|
||||
initConfig(WrenConfig.WREN_DIRECTORY, wrenConfig.getWrenMDLDirectory().getPath(), false, true);
|
||||
initConfig(WrenConfig.WREN_DATASOURCE_TYPE, Optional.ofNullable(wrenConfig.getDataSourceType()).map(Enum::name).orElse(null), true, false);
|
||||
initConfig(WrenConfig.WREN_ENABLE_DYNAMIC_FIELDS, Boolean.toString(wrenConfig.getEnableDynamicFields()), false, false);
|
||||
initConfig(DUCKDB_STORAGE_ENDPOINT, duckdbS3StyleStorageConfig.getEndpoint(), false, true);
|
||||
initConfig(DUCKDB_STORAGE_ACCESS_KEY, duckdbS3StyleStorageConfig.getAccessKey().orElse(null), true, false);
|
||||
initConfig(DUCKDB_STORAGE_SECRET_KEY, duckdbS3StyleStorageConfig.getSecretKey().orElse(null), true, false);
|
||||
initConfig(DUCKDB_STORAGE_REGION, duckdbS3StyleStorageConfig.getRegion().orElse(null), true, false);
|
||||
initConfig(DUCKDB_STORAGE_URL_STYLE, duckdbS3StyleStorageConfig.getUrlStyle(), false, false);
|
||||
initConfig(DUCKDB_MEMORY_LIMIT, duckDBConfig.getMemoryLimit().toString(), true, false);
|
||||
initConfig(DUCKDB_HOME_DIRECTORY, duckDBConfig.getHomeDirectory(), true, false);
|
||||
initConfig(DUCKDB_TEMP_DIRECTORY, duckDBConfig.getTempDirectory(), true, false);
|
||||
@@ -161,28 +110,8 @@ public class ConfigManager
|
||||
initConfig(DUCKDB_MAX_CONCURRENT_METADATA_QUERIES, Integer.toString(duckDBConfig.getMaxConcurrentMetadataQueries()), false, true);
|
||||
initConfig(DUCKDB_MAX_CACHE_QUERY_TIMEOUT, Long.toString(duckDBConfig.getMaxCacheQueryTimeout()), false, true);
|
||||
initConfig(DUCKDB_CACHE_TASK_RETRY_DELAY, Long.toString(duckDBConfig.getCacheTaskRetryDelay()), false, true);
|
||||
initConfig(PG_WIRE_PROTOCOL_PORT, postgresWireProtocolConfig.getPort(), false, true);
|
||||
initConfig(PG_WIRE_PROTOCOL_SSL_ENABLED, Boolean.toString(postgresWireProtocolConfig.isSslEnable()), false, true);
|
||||
initConfig(PG_WIRE_PROTOCOL_NETTY_THREAD_COUNT, Integer.toString(postgresWireProtocolConfig.getNettyThreadCount()), false, true);
|
||||
initConfig(PG_WIRE_PROTOCOL_AUTH_FILE, postgresWireProtocolConfig.getAuthFile().getPath(), false, true);
|
||||
initConfig(BigQueryConfig.BIGQUERY_CRENDITALS_KEY, bigQueryConfig.getCredentialsKey().orElse(null), true, false);
|
||||
initConfig(BigQueryConfig.BIGQUERY_CRENDITALS_FILE, bigQueryConfig.getCredentialsFile().orElse(null), true, false);
|
||||
initConfig(BigQueryConfig.BIGQUERY_PROJECT_ID, bigQueryConfig.getProjectId().orElse(null), true, false);
|
||||
initConfig(BigQueryConfig.BIGQUERY_LOCATION, bigQueryConfig.getLocation().orElse(null), true, false);
|
||||
initConfig(BigQueryConfig.BIGQUERY_BUCKET_NAME, bigQueryConfig.getBucketName().orElse(null), true, false);
|
||||
initConfig(BigQueryConfig.BIGQUERY_METADATA_SCHEMA_PREFIX, bigQueryConfig.getMetadataSchemaPrefix(), true, false);
|
||||
initConfig(POSTGRES_JDBC_URL, postgresConfig.getJdbcUrl(), true, false);
|
||||
initConfig(POSTGRES_USER, postgresConfig.getUser(), true, false);
|
||||
initConfig(POSTGRES_PASSWORD, postgresConfig.getPassword(), true, false);
|
||||
initConfig(DUCKDB_CONNECTOR_INIT_SQL_PATH, duckDBConnectorConfig.getInitSQLPath(), false, false);
|
||||
initConfig(DUCKDB_CONNECTOR_SESSION_SQL_PATH, duckDBConnectorConfig.getSessionSQLPath(), false, false);
|
||||
initConfig(SNOWFLAKE_JDBC_URL, snowflakeConfig.getJdbcUrl(), true, false);
|
||||
initConfig(SNOWFLAKE_USER, snowflakeConfig.getUser(), true, false);
|
||||
initConfig(SNOWFLAKE_PASSWORD, snowflakeConfig.getPassword(), true, false);
|
||||
initConfig(SNOWFLAKE_DATABASE, snowflakeConfig.getDatabase().orElse(null), true, false);
|
||||
initConfig(SNOWFLAKE_SCHEMA, snowflakeConfig.getSchema().orElse(null), true, false);
|
||||
initConfig(SNOWFLAKE_WAREHOUSE, snowflakeConfig.getWarehouse().orElse(null), true, false);
|
||||
initConfig(SNOWFLAKE_ROLE, snowflakeConfig.getRole().orElse(null), true, false);
|
||||
}
|
||||
|
||||
private void initConfig(String key, String value, boolean requiredReload, boolean isStatic)
|
||||
@@ -207,20 +136,6 @@ public class ConfigManager
|
||||
return result;
|
||||
});
|
||||
}
|
||||
if (config == BigQueryConfig.class) {
|
||||
return (T) bigQueryConfig.orElseGet(() -> {
|
||||
BigQueryConfig result = getBigQueryConfig();
|
||||
bigQueryConfig = Optional.of(result);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
if (config == PostgresConfig.class) {
|
||||
return (T) postgresConfig.orElseGet(() -> {
|
||||
PostgresConfig result = getPostgresConfig();
|
||||
postgresConfig = Optional.of(result);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
if (config == DuckDBConfig.class) {
|
||||
return (T) duckDBConfig.orElseGet(() -> {
|
||||
DuckDBConfig result = getDuckDBConfig();
|
||||
@@ -228,21 +143,6 @@ public class ConfigManager
|
||||
return result;
|
||||
});
|
||||
}
|
||||
if (config == PostgresWireProtocolConfig.class) {
|
||||
return (T) postgresWireProtocolConfig.orElseGet(() -> {
|
||||
PostgresWireProtocolConfig result = getPostgresWireProtocolConfig();
|
||||
postgresWireProtocolConfig = Optional.of(result);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
if (config == CacheStorageConfig.class &&
|
||||
wrenConfig.map(WrenConfig::getDataSourceType).stream().anyMatch(type -> type == WrenConfig.DataSourceType.BIGQUERY)) {
|
||||
return (T) duckdbS3StyleStorageConfig.orElseGet(() -> {
|
||||
DuckdbS3StyleStorageConfig result = getDuckdbS3StyleStorageConfig();
|
||||
duckdbS3StyleStorageConfig = Optional.of(result);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
if (config == DuckDBConnectorConfig.class) {
|
||||
return (T) duckDBConnectorConfig.orElseGet(() -> {
|
||||
DuckDBConnectorConfig result = getDuckDBConnectorConfig();
|
||||
@@ -250,13 +150,6 @@ public class ConfigManager
|
||||
return result;
|
||||
});
|
||||
}
|
||||
if (config == SnowflakeConfig.class) {
|
||||
return (T) snowflakeConfig.orElseGet(() -> {
|
||||
SnowflakeConfig result = getSnowflakeConfig();
|
||||
snowflakeConfig = Optional.of(result);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
throw new RuntimeException("Unknown config class: " + config.getName());
|
||||
}
|
||||
|
||||
@@ -270,27 +163,6 @@ public class ConfigManager
|
||||
return result;
|
||||
}
|
||||
|
||||
private BigQueryConfig getBigQueryConfig()
|
||||
{
|
||||
BigQueryConfig result = new BigQueryConfig();
|
||||
result.setCredentialsKey(configs.get(BigQueryConfig.BIGQUERY_CRENDITALS_KEY));
|
||||
result.setCredentialsFile(configs.get(BigQueryConfig.BIGQUERY_CRENDITALS_FILE));
|
||||
result.setProjectId(configs.get(BigQueryConfig.BIGQUERY_PROJECT_ID));
|
||||
result.setLocation(configs.get(BigQueryConfig.BIGQUERY_LOCATION));
|
||||
result.setBucketName(configs.get(BigQueryConfig.BIGQUERY_BUCKET_NAME));
|
||||
result.setMetadataSchemaPrefix(configs.get(BigQueryConfig.BIGQUERY_METADATA_SCHEMA_PREFIX));
|
||||
return result;
|
||||
}
|
||||
|
||||
private PostgresConfig getPostgresConfig()
|
||||
{
|
||||
PostgresConfig result = new PostgresConfig();
|
||||
result.setJdbcUrl(configs.get(POSTGRES_JDBC_URL));
|
||||
result.setUser(configs.get(POSTGRES_USER));
|
||||
result.setPassword(configs.get(POSTGRES_PASSWORD));
|
||||
return result;
|
||||
}
|
||||
|
||||
private DuckDBConfig getDuckDBConfig()
|
||||
{
|
||||
DuckDBConfig result = new DuckDBConfig();
|
||||
@@ -304,27 +176,6 @@ public class ConfigManager
|
||||
return result;
|
||||
}
|
||||
|
||||
private PostgresWireProtocolConfig getPostgresWireProtocolConfig()
|
||||
{
|
||||
PostgresWireProtocolConfig result = new PostgresWireProtocolConfig();
|
||||
result.setPort(configs.get(PG_WIRE_PROTOCOL_PORT));
|
||||
result.setSslEnable(Boolean.parseBoolean(configs.get(PG_WIRE_PROTOCOL_SSL_ENABLED)));
|
||||
result.setNettyThreadCount(Integer.parseInt(configs.get(PG_WIRE_PROTOCOL_NETTY_THREAD_COUNT)));
|
||||
result.setAuthFile(new File(configs.get(PG_WIRE_PROTOCOL_AUTH_FILE)));
|
||||
return result;
|
||||
}
|
||||
|
||||
private DuckdbS3StyleStorageConfig getDuckdbS3StyleStorageConfig()
|
||||
{
|
||||
DuckdbS3StyleStorageConfig result = new DuckdbS3StyleStorageConfig();
|
||||
result.setEndpoint(configs.get(DUCKDB_STORAGE_ENDPOINT));
|
||||
result.setAccessKey(configs.get(DUCKDB_STORAGE_ACCESS_KEY));
|
||||
result.setSecretKey(configs.get(DUCKDB_STORAGE_SECRET_KEY));
|
||||
result.setRegion(configs.get(DUCKDB_STORAGE_REGION));
|
||||
result.setUrlStyle(configs.get(DUCKDB_STORAGE_URL_STYLE));
|
||||
return result;
|
||||
}
|
||||
|
||||
private DuckDBConnectorConfig getDuckDBConnectorConfig()
|
||||
{
|
||||
DuckDBConnectorConfig result = new DuckDBConnectorConfig();
|
||||
@@ -333,19 +184,6 @@ public class ConfigManager
|
||||
return result;
|
||||
}
|
||||
|
||||
private SnowflakeConfig getSnowflakeConfig()
|
||||
{
|
||||
SnowflakeConfig config = new SnowflakeConfig();
|
||||
config.setJdbcUrl(configs.get(SNOWFLAKE_JDBC_URL));
|
||||
config.setUser(configs.get(SNOWFLAKE_USER));
|
||||
config.setPassword(configs.get(SNOWFLAKE_PASSWORD));
|
||||
config.setDatabase(configs.get(SNOWFLAKE_DATABASE));
|
||||
config.setSchema(configs.get(SNOWFLAKE_SCHEMA));
|
||||
config.setWarehouse(configs.get(SNOWFLAKE_WAREHOUSE));
|
||||
config.setRole(configs.get(SNOWFLAKE_ROLE));
|
||||
return config;
|
||||
}
|
||||
|
||||
public synchronized boolean setConfigs(List<ConfigEntry> configEntries, boolean reset)
|
||||
{
|
||||
if (reset) {
|
||||
@@ -386,11 +224,7 @@ public class ConfigManager
|
||||
private void resetCache()
|
||||
{
|
||||
wrenConfig = Optional.empty();
|
||||
postgresConfig = Optional.empty();
|
||||
bigQueryConfig = Optional.empty();
|
||||
duckDBConfig = Optional.empty();
|
||||
postgresWireProtocolConfig = Optional.empty();
|
||||
duckdbS3StyleStorageConfig = Optional.empty();
|
||||
duckDBConnectorConfig = Optional.empty();
|
||||
}
|
||||
|
||||
@@ -400,13 +234,8 @@ public class ConfigManager
|
||||
setConfigs.clear();
|
||||
initConfig(
|
||||
new WrenConfig(),
|
||||
new PostgresConfig(),
|
||||
new BigQueryConfig(),
|
||||
new DuckDBConfig(),
|
||||
new PostgresWireProtocolConfig(),
|
||||
new DuckdbS3StyleStorageConfig(),
|
||||
new DuckDBConnectorConfig(),
|
||||
new SnowflakeConfig());
|
||||
new DuckDBConnectorConfig());
|
||||
}
|
||||
|
||||
private void syncFile(Map<String, String> updated)
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.config;
|
||||
|
||||
import io.airlift.configuration.Config;
|
||||
|
||||
public class PostgresConfig
|
||||
{
|
||||
public static final String POSTGRES_JDBC_URL = "postgres.jdbc.url";
|
||||
public static final String POSTGRES_USER = "postgres.user";
|
||||
public static final String POSTGRES_PASSWORD = "postgres.password";
|
||||
private String jdbcUrl;
|
||||
private String user;
|
||||
private String password;
|
||||
|
||||
public String getJdbcUrl()
|
||||
{
|
||||
return jdbcUrl;
|
||||
}
|
||||
|
||||
@Config(POSTGRES_JDBC_URL)
|
||||
public PostgresConfig setJdbcUrl(String jdbcUrl)
|
||||
{
|
||||
this.jdbcUrl = jdbcUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUser()
|
||||
{
|
||||
return user;
|
||||
}
|
||||
|
||||
@Config(POSTGRES_USER)
|
||||
public PostgresConfig setUser(String user)
|
||||
{
|
||||
this.user = user;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPassword()
|
||||
{
|
||||
return password;
|
||||
}
|
||||
|
||||
@Config(POSTGRES_PASSWORD)
|
||||
public PostgresConfig setPassword(String password)
|
||||
{
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.base.config;
|
||||
|
||||
import io.airlift.configuration.Config;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class PostgresWireProtocolConfig
|
||||
{
|
||||
public static final String PG_WIRE_PROTOCOL_ENABLED = "pg-wire-protocol.enabled";
|
||||
public static final String PG_WIRE_PROTOCOL_SSL_ENABLED = "pg-wire-protocol.ssl.enabled";
|
||||
public static final String PG_WIRE_PROTOCOL_NETTY_THREAD_COUNT = "pg-wire-protocol.netty.thread.count";
|
||||
public static final String PG_WIRE_PROTOCOL_AUTH_FILE = "pg-wire-protocol.auth.file";
|
||||
public static final String PG_WIRE_PROTOCOL_PORT = "pg-wire-protocol.port";
|
||||
|
||||
private String port = "7432";
|
||||
private boolean sslEnable;
|
||||
private int nettyThreadCount;
|
||||
private File authFile = new File("etc/accounts");
|
||||
private boolean pgWireProtocolEnabled;
|
||||
|
||||
@NotNull
|
||||
public String getPort()
|
||||
{
|
||||
return port;
|
||||
}
|
||||
|
||||
@Config(PG_WIRE_PROTOCOL_PORT)
|
||||
public PostgresWireProtocolConfig setPort(String port)
|
||||
{
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public boolean isSslEnable()
|
||||
{
|
||||
return sslEnable;
|
||||
}
|
||||
|
||||
@Config(PG_WIRE_PROTOCOL_SSL_ENABLED)
|
||||
public PostgresWireProtocolConfig setSslEnable(boolean sslEnable)
|
||||
{
|
||||
this.sslEnable = sslEnable;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public int getNettyThreadCount()
|
||||
{
|
||||
return nettyThreadCount;
|
||||
}
|
||||
|
||||
@Config(PG_WIRE_PROTOCOL_NETTY_THREAD_COUNT)
|
||||
public PostgresWireProtocolConfig setNettyThreadCount(int nettyThreadCount)
|
||||
{
|
||||
this.nettyThreadCount = nettyThreadCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public File getAuthFile()
|
||||
{
|
||||
return authFile;
|
||||
}
|
||||
|
||||
@Config(PG_WIRE_PROTOCOL_AUTH_FILE)
|
||||
public PostgresWireProtocolConfig setAuthFile(File authFile)
|
||||
{
|
||||
this.authFile = authFile;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Config(PG_WIRE_PROTOCOL_ENABLED)
|
||||
public void setPgWireProtocolEnabled(boolean pgWireProtocolEnabled)
|
||||
{
|
||||
this.pgWireProtocolEnabled = pgWireProtocolEnabled;
|
||||
}
|
||||
|
||||
public boolean isPgWireProtocolEnabled()
|
||||
{
|
||||
return pgWireProtocolEnabled;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.config;
|
||||
|
||||
import io.airlift.configuration.Config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.lang.System.getenv;
|
||||
|
||||
public class SQLGlotConfig
|
||||
{
|
||||
public static final String SQLGLOT_PORT = "sqlglot.port";
|
||||
|
||||
private int port = Optional.ofNullable(getenv("SQLGLOT_PORT"))
|
||||
.map(Integer::parseInt)
|
||||
.orElse(8000);
|
||||
|
||||
public int getPort()
|
||||
{
|
||||
return port;
|
||||
}
|
||||
|
||||
@Config(SQLGLOT_PORT)
|
||||
public void setPort(int port)
|
||||
{
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public static SQLGlotConfig createConfigWithFreePort()
|
||||
{
|
||||
SQLGlotConfig config = new SQLGlotConfig();
|
||||
config.setPort(findFreePort());
|
||||
return config;
|
||||
}
|
||||
|
||||
private static int findFreePort()
|
||||
{
|
||||
try (ServerSocket serverSocket = new ServerSocket(0)) {
|
||||
return serverSocket.getLocalPort();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.config;
|
||||
|
||||
import io.airlift.configuration.Config;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class SnowflakeConfig
|
||||
{
|
||||
public static final String SNOWFLAKE_JDBC_URL = "snowflake.jdbc.url";
|
||||
public static final String SNOWFLAKE_USER = "snowflake.user";
|
||||
public static final String SNOWFLAKE_PASSWORD = "snowflake.password";
|
||||
public static final String SNOWFLAKE_ROLE = "snowflake.role";
|
||||
public static final String SNOWFLAKE_WAREHOUSE = "snowflake.warehouse";
|
||||
public static final String SNOWFLAKE_DATABASE = "snowflake.database";
|
||||
public static final String SNOWFLAKE_SCHEMA = "snowflake.schema";
|
||||
|
||||
private String jdbcUrl;
|
||||
private String user;
|
||||
private String password;
|
||||
private String role;
|
||||
private String warehouse;
|
||||
private String database;
|
||||
private String schema;
|
||||
|
||||
public String getJdbcUrl()
|
||||
{
|
||||
return jdbcUrl;
|
||||
}
|
||||
|
||||
@Config(SNOWFLAKE_JDBC_URL)
|
||||
public SnowflakeConfig setJdbcUrl(String jdbcUrl)
|
||||
{
|
||||
this.jdbcUrl = jdbcUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUser()
|
||||
{
|
||||
return user;
|
||||
}
|
||||
|
||||
@Config(SNOWFLAKE_USER)
|
||||
public SnowflakeConfig setUser(String user)
|
||||
{
|
||||
this.user = user;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPassword()
|
||||
{
|
||||
return password;
|
||||
}
|
||||
|
||||
@Config(SNOWFLAKE_PASSWORD)
|
||||
public SnowflakeConfig setPassword(String password)
|
||||
{
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<String> getRole()
|
||||
{
|
||||
return Optional.ofNullable(role);
|
||||
}
|
||||
|
||||
@Config(SNOWFLAKE_ROLE)
|
||||
public SnowflakeConfig setRole(String role)
|
||||
{
|
||||
this.role = role;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<String> getWarehouse()
|
||||
{
|
||||
return Optional.ofNullable(warehouse);
|
||||
}
|
||||
|
||||
@Config(SNOWFLAKE_WAREHOUSE)
|
||||
public SnowflakeConfig setWarehouse(String warehouse)
|
||||
{
|
||||
this.warehouse = warehouse;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<String> getDatabase()
|
||||
{
|
||||
return Optional.ofNullable(database);
|
||||
}
|
||||
|
||||
@Config(SNOWFLAKE_DATABASE)
|
||||
public SnowflakeConfig setDatabase(String database)
|
||||
{
|
||||
this.database = database;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<String> getSchema()
|
||||
{
|
||||
return Optional.ofNullable(schema);
|
||||
}
|
||||
|
||||
@Config(SNOWFLAKE_SCHEMA)
|
||||
public SnowflakeConfig setSchema(String schema)
|
||||
{
|
||||
this.schema = schema;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.base.function;
|
||||
|
||||
public enum OperatorType
|
||||
{
|
||||
ADD("+", 2),
|
||||
SUBTRACT("-", 2),
|
||||
MULTIPLY("*", 2),
|
||||
DIVIDE("/", 2),
|
||||
MODULUS("%", 2),
|
||||
NEGATION("-", 1),
|
||||
EQUAL("=", 2),
|
||||
COMPARISON("COMPARISON", 2),
|
||||
LESS_THAN("<", 2),
|
||||
LESS_THAN_OR_EQUAL("<=", 2),
|
||||
CAST("CAST", 1),
|
||||
SUBSCRIPT("[]", 2),
|
||||
HASH_CODE("HASH CODE", 1),
|
||||
SATURATED_FLOOR_CAST("SATURATED FLOOR CAST", 1),
|
||||
IS_DISTINCT_FROM("IS DISTINCT FROM", 2),
|
||||
XX_HASH_64("XX HASH 64", 1),
|
||||
INDETERMINATE("INDETERMINATE", 1);
|
||||
|
||||
private final String operator;
|
||||
private final int argumentCount;
|
||||
|
||||
OperatorType(String operator, int argumentCount)
|
||||
{
|
||||
this.operator = operator;
|
||||
this.argumentCount = argumentCount;
|
||||
}
|
||||
|
||||
public String getOperator()
|
||||
{
|
||||
return operator;
|
||||
}
|
||||
|
||||
public int getArgumentCount()
|
||||
{
|
||||
return argumentCount;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.metadata;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.wren.base.pgcatalog.function.FunctionRegistry;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BasicFunctionRegistry
|
||||
implements FunctionRegistry<Function>
|
||||
{
|
||||
private final List<Function> functions = ImmutableList.<Function>builder()
|
||||
.add(BasicFunctions.DATE_TRUNC)
|
||||
.build();
|
||||
|
||||
private final Map<FunctionKey, Function> simpleNameToFunction = new HashMap<>();
|
||||
|
||||
public BasicFunctionRegistry()
|
||||
{
|
||||
// TODO: handle function name overloading
|
||||
// https://github.com/Canner/canner-metric-layer/issues/73
|
||||
// use HashMap to handle multiple same key entries
|
||||
functions.forEach(function -> simpleNameToFunction.put(FunctionKey.functionKey(function.getName(), function.getArguments().map(List::size).orElse(0)), function));
|
||||
}
|
||||
|
||||
public List<Function> getFunctions()
|
||||
{
|
||||
return functions;
|
||||
}
|
||||
|
||||
public Optional<Function> getFunction(String name, int numArgument)
|
||||
{
|
||||
return Optional.ofNullable(simpleNameToFunction.get(FunctionKey.functionKey(name, numArgument)));
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.metadata;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.wren.base.type.DateType;
|
||||
import io.wren.base.type.VarcharType;
|
||||
|
||||
import static io.wren.base.metadata.Function.Argument.argument;
|
||||
|
||||
public class BasicFunctions
|
||||
{
|
||||
private BasicFunctions() {}
|
||||
|
||||
public static final Function DATE_TRUNC = Function.builder()
|
||||
.setName("date_trunc")
|
||||
.setArguments(ImmutableList.of(argument("field", VarcharType.VARCHAR), argument("source", DateType.DATE)))
|
||||
.setReturnType(DateType.DATE)
|
||||
.build();
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.base.metadata;
|
||||
|
||||
import io.wren.base.type.PGType;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class ColumnMetadata
|
||||
{
|
||||
private final String name;
|
||||
private final PGType<?> type;
|
||||
|
||||
private ColumnMetadata(String name, PGType<?> type)
|
||||
{
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public PGType<?> getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder("ColumnMetadata{");
|
||||
sb.append("name='").append(name).append('\'');
|
||||
sb.append(", type=").append(type);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(name, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ColumnMetadata other = (ColumnMetadata) obj;
|
||||
return Objects.equals(this.name, other.name) &&
|
||||
Objects.equals(this.type, other.type);
|
||||
}
|
||||
|
||||
public static Builder builder()
|
||||
{
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder
|
||||
{
|
||||
private String name;
|
||||
private PGType<?> type;
|
||||
|
||||
private Builder() {}
|
||||
|
||||
public Builder setName(String name)
|
||||
{
|
||||
this.name = requireNonNull(name, "name is null");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setType(PGType<?> type)
|
||||
{
|
||||
this.type = requireNonNull(type, "type is null");
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColumnMetadata build()
|
||||
{
|
||||
return new ColumnMetadata(name, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.metadata;
|
||||
|
||||
import io.wren.base.type.PGType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class Function
|
||||
{
|
||||
protected final String name;
|
||||
protected final List<Argument> arguments;
|
||||
protected final PGType returnType;
|
||||
|
||||
public Function(String name, List<Argument> arguments, PGType returnType)
|
||||
{
|
||||
this.name = name;
|
||||
this.arguments = arguments;
|
||||
this.returnType = returnType;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public Optional<List<Argument>> getArguments()
|
||||
{
|
||||
return Optional.ofNullable(arguments);
|
||||
}
|
||||
|
||||
public Optional<PGType> getReturnType()
|
||||
{
|
||||
return Optional.ofNullable(returnType);
|
||||
}
|
||||
|
||||
public static class Argument
|
||||
{
|
||||
public static Argument argument(String name, PGType type)
|
||||
{
|
||||
return new Argument(name, type);
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final PGType type;
|
||||
|
||||
public Argument(String name, PGType type)
|
||||
{
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public PGType getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
public static Builder builder()
|
||||
{
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder
|
||||
{
|
||||
private String name;
|
||||
private List<Argument> arguments;
|
||||
private PGType returnType;
|
||||
|
||||
public Builder setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setArguments(List<Argument> arguments)
|
||||
{
|
||||
this.arguments = arguments;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setReturnType(PGType returnType)
|
||||
{
|
||||
this.returnType = returnType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Function build()
|
||||
{
|
||||
requireNonNull(name, "name is null");
|
||||
return new Function(name, arguments, returnType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.metadata;
|
||||
|
||||
import io.wren.base.pgcatalog.function.DataSourceFunctionRegistry;
|
||||
import io.wren.base.pgcatalog.function.PgMetastoreFunctionRegistry;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class FunctionBundle
|
||||
{
|
||||
private static final BasicFunctionRegistry basicFunctionRegistry;
|
||||
private static final PgMetastoreFunctionRegistry pgMetastoreFunctionRegistry;
|
||||
private static final DataSourceFunctionRegistry datSourceFunctionRegistry;
|
||||
|
||||
private FunctionBundle() {}
|
||||
|
||||
static {
|
||||
basicFunctionRegistry = new BasicFunctionRegistry();
|
||||
pgMetastoreFunctionRegistry = new PgMetastoreFunctionRegistry();
|
||||
datSourceFunctionRegistry = new DataSourceFunctionRegistry();
|
||||
}
|
||||
|
||||
public static Optional<Function> getFunction(String name, int numArgument)
|
||||
{
|
||||
return basicFunctionRegistry.getFunction(name, numArgument)
|
||||
.or(() -> pgMetastoreFunctionRegistry.getFunction(name, numArgument))
|
||||
.or(() -> datSourceFunctionRegistry.getFunction(name, numArgument));
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.metadata;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* TODO: analyze the type of argument expression
|
||||
* https://github.com/Canner/canner-metric-layer/issues/92
|
||||
* <p>
|
||||
* We only support function overloading with different number of argument now. Because
|
||||
* the work of analyze the type of argument is too huge to implement, FunctionKey only
|
||||
* recognizes each function by its name and number of argument.
|
||||
*/
|
||||
public class FunctionKey
|
||||
{
|
||||
public static FunctionKey functionKey(String name, int numArgument)
|
||||
{
|
||||
return new FunctionKey(name, numArgument);
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final int numArgument;
|
||||
|
||||
private FunctionKey(String name, int numArgument)
|
||||
{
|
||||
this.name = name;
|
||||
this.numArgument = numArgument;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(name, numArgument);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
FunctionKey that = (FunctionKey) o;
|
||||
return numArgument == that.numArgument && Objects.equals(name, that.name);
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.base.metadata;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.wren.base.type.PGType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class TableMetadata
|
||||
{
|
||||
private final SchemaTableName table;
|
||||
private final List<ColumnMetadata> columns;
|
||||
|
||||
public static Builder builder(SchemaTableName tableName)
|
||||
{
|
||||
return new Builder(tableName);
|
||||
}
|
||||
|
||||
public TableMetadata(SchemaTableName table, List<ColumnMetadata> columns)
|
||||
{
|
||||
this.table = requireNonNull(table, "table is null");
|
||||
this.columns = List.copyOf(requireNonNull(columns, "columns is null"));
|
||||
}
|
||||
|
||||
public SchemaTableName getTable()
|
||||
{
|
||||
return table;
|
||||
}
|
||||
|
||||
public List<ColumnMetadata> getColumns()
|
||||
{
|
||||
return columns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder("ConnectorTableMetadata{");
|
||||
sb.append("table=").append(table);
|
||||
sb.append(", columns=").append(columns);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static class Builder
|
||||
{
|
||||
private final SchemaTableName tableName;
|
||||
private final ImmutableList.Builder<ColumnMetadata> columns = ImmutableList.builder();
|
||||
|
||||
private Builder(SchemaTableName tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
public Builder column(String columnName, PGType<?> type)
|
||||
{
|
||||
columns.add(ColumnMetadata.builder()
|
||||
.setName(columnName)
|
||||
.setType(type).build());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder column(ColumnMetadata columnMetadata)
|
||||
{
|
||||
columns.add(columnMetadata);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableMetadata build()
|
||||
{
|
||||
return new TableMetadata(tableName, columns.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.pgcatalog.function;
|
||||
|
||||
import io.wren.base.type.BigIntType;
|
||||
import io.wren.base.type.PGArray;
|
||||
import io.wren.base.type.RecordType;
|
||||
import io.wren.base.type.VarcharType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static io.wren.base.metadata.Function.Argument.argument;
|
||||
import static io.wren.base.pgcatalog.function.PgFunction.Language.SQL;
|
||||
import static io.wren.base.type.AnyType.ANY;
|
||||
import static io.wren.base.type.TimestampType.TIMESTAMP;
|
||||
|
||||
public final class BigQueryFunctions
|
||||
{
|
||||
private BigQueryFunctions() {}
|
||||
|
||||
// TODO Support more date/time format https://www.postgresql.org/docs/13/functions-formatting.html#FUNCTIONS-FORMATTING-DATETIME-TABLE
|
||||
// TODO Support more timezone, now only support UTC
|
||||
public static final PgFunction PG_TO_CHAR = PgFunction.builder()
|
||||
.setName("to_char")
|
||||
.setLanguage(SQL)
|
||||
.setDefinition("WITH to_char AS (SELECT " +
|
||||
"CONTAINS_SUBSTR(string_format, 'TZ') as contain_timezone, " +
|
||||
"CAST(TIMESTAMP(value) AS STRING FORMAT REPLACE(REPLACE(string_format, 'MS', 'FF3'), 'TZ', '')) AS timestamp_with_format) " +
|
||||
"SELECT CASE WHEN contain_timezone " +
|
||||
"THEN CONCAT(timestamp_with_format, 'UTC') " +
|
||||
"ELSE timestamp_with_format " +
|
||||
"END " +
|
||||
"FROM to_char")
|
||||
.setSubquery(true)
|
||||
.setArguments(List.of(argument("value", TIMESTAMP), argument("string_format", VarcharType.VARCHAR)))
|
||||
.setReturnType(VarcharType.VARCHAR)
|
||||
.build();
|
||||
|
||||
public static final PgFunction NOW = PgFunction.builder()
|
||||
.setName("now")
|
||||
.setLanguage(SQL)
|
||||
.setDefinition("SELECT CURRENT_DATETIME")
|
||||
.setReturnType(TIMESTAMP)
|
||||
.build();
|
||||
|
||||
// TODO This is a mock function, need to be implemented
|
||||
public static final PgFunction PG_EXPANDARRAY = PgFunction.builder()
|
||||
.setName("_pg_expandarray")
|
||||
.setLanguage(SQL)
|
||||
.setDefinition("CASE WHEN (array_length(int_arr) > 0) THEN cast((int_arr[0], 1) as row(x int, n int)) ELSE NULL END")
|
||||
.setArguments(List.of(argument("int_arr", PGArray.INT4_ARRAY)))
|
||||
.setReturnType(new RecordType(List.of(BigIntType.BIGINT, BigIntType.BIGINT)))
|
||||
.build();
|
||||
|
||||
// TODO If the input is a string only include number, it will be parsed as a number. So substring('123' from '1') would get the wrong answer '123', actual should be '1'
|
||||
// https://github.com/Canner/wren/issues/329
|
||||
public static final PgFunction SUBSTR = PgFunction.builder()
|
||||
.setName("substr")
|
||||
.setLanguage(SQL)
|
||||
.setDefinition("SELECT " +
|
||||
"CASE WHEN REGEXP_CONTAINS(SAFE_CAST(arg2 AS STRING), '^[0-9]*$') IS TRUE\n" +
|
||||
"THEN SUBSTR(arg1, CAST(arg2 AS INT64))\n" +
|
||||
"ELSE REGEXP_EXTRACT(arg1, CAST(arg2 AS STRING))\n" +
|
||||
"END")
|
||||
.setArguments(List.of(argument("arg1", VarcharType.VARCHAR), argument("arg2", ANY)))
|
||||
.setReturnType(VarcharType.VARCHAR)
|
||||
.build();
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.pgcatalog.function;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.wren.base.metadata.FunctionKey;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static io.wren.base.metadata.FunctionKey.functionKey;
|
||||
|
||||
public class DataSourceFunctionRegistry
|
||||
implements FunctionRegistry<PgFunction>
|
||||
{
|
||||
private final List<PgFunction> functions = ImmutableList.<PgFunction>builder()
|
||||
.add(BigQueryFunctions.PG_TO_CHAR)
|
||||
.add(BigQueryFunctions.NOW)
|
||||
.add(BigQueryFunctions.SUBSTR)
|
||||
.build();
|
||||
|
||||
private final Map<FunctionKey, PgFunction> simpleNameToFunction = new HashMap<>();
|
||||
|
||||
public DataSourceFunctionRegistry()
|
||||
{
|
||||
// TODO: handle function name overloading
|
||||
// https://github.com/Canner/canner-metric-layer/issues/73
|
||||
// use HashMap to handle multiple same key entries
|
||||
functions.forEach(function -> simpleNameToFunction.put(functionKey(function.getName(), function.getArguments().map(List::size).orElse(0)), function));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PgFunction> getFunctions()
|
||||
{
|
||||
return functions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<PgFunction> getFunction(String name, int numArgument)
|
||||
{
|
||||
return Optional.ofNullable(simpleNameToFunction.get(functionKey(name, numArgument)));
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.pgcatalog.function;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.wren.base.type.BigIntType;
|
||||
import io.wren.base.type.IntegerType;
|
||||
import io.wren.base.type.PGArray;
|
||||
import io.wren.base.type.RecordType;
|
||||
import io.wren.base.type.VarcharType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static io.wren.base.metadata.Function.Argument.argument;
|
||||
import static io.wren.base.type.BooleanType.BOOLEAN;
|
||||
|
||||
public class DuckDBFunctions
|
||||
{
|
||||
private DuckDBFunctions() {}
|
||||
|
||||
private static final String NULL = "NULL";
|
||||
|
||||
public static final PgFunction CURRENT_DATABASE = PgFunction.builder()
|
||||
.setName("current_database")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setImplemented(true)
|
||||
.build();
|
||||
|
||||
public static final PgFunction CURRENT_SCHEMAS = PgFunction.builder()
|
||||
.setName("current_schemas")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setArguments(ImmutableList.of(argument("includeImplicit", BOOLEAN)))
|
||||
.setImplemented(true)
|
||||
.build();
|
||||
|
||||
public static final PgFunction PG_RELATION_SIZE__INT_VARCHAR___BIGINT = PgFunction.builder()
|
||||
.setName("pg_relation_size")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setDefinition(NULL)
|
||||
.setArguments(ImmutableList.of(argument("relOid", IntegerType.INTEGER), argument("text", VarcharType.VARCHAR)))
|
||||
.setReturnType(BigIntType.BIGINT)
|
||||
.build();
|
||||
|
||||
// It's an overloading of PG_RELATION_SIZE__INT_VARCHAR___BIGINT, no need to implement it.
|
||||
public static final PgFunction PG_RELATION_SIZE__INT___BIGINT = PgFunction.builder()
|
||||
.setName("pg_relation_size")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setArguments(ImmutableList.of(argument("relOid", IntegerType.INTEGER)))
|
||||
.setReturnType(BigIntType.BIGINT)
|
||||
.setImplemented(true)
|
||||
.build();
|
||||
|
||||
public static final PgFunction ARRAY_IN = PgFunction.builder()
|
||||
.setName("array_in")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setDefinition(NULL)
|
||||
.setArguments(ImmutableList.of(argument("ignored", VarcharType.VARCHAR)))
|
||||
.setReturnType(PGArray.VARCHAR_ARRAY)
|
||||
.build();
|
||||
|
||||
public static final PgFunction ARRAY_OUT = PgFunction.builder()
|
||||
.setName("array_out")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setDefinition(NULL)
|
||||
.setArguments(ImmutableList.of(argument("ignored", PGArray.VARCHAR_ARRAY)))
|
||||
.setReturnType(VarcharType.VARCHAR)
|
||||
.build();
|
||||
|
||||
public static final PgFunction ARRAY_RECV = PgFunction.builder()
|
||||
.setName("array_recv")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setDefinition(NULL)
|
||||
.setArguments(ImmutableList.of(argument("ignored", VarcharType.VARCHAR)))
|
||||
.setReturnType(PGArray.VARCHAR_ARRAY)
|
||||
.build();
|
||||
|
||||
public static final PgFunction ARRAY_UPPER = PgFunction.builder()
|
||||
.setName("array_upper")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setDefinition("CASE WHEN dim = 1 THEN array_length(input) ELSE NULL END")
|
||||
.setArguments(ImmutableList.of(argument("input", PGArray.VARCHAR_ARRAY), argument("dim", BigIntType.BIGINT)))
|
||||
.setReturnType(IntegerType.INTEGER)
|
||||
.build();
|
||||
|
||||
public static final PgFunction PG_GET_EXPR = PgFunction.builder()
|
||||
.setName("pg_get_expr")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setDefinition("''")
|
||||
.setArguments(List.of(argument("pg_node", VarcharType.VARCHAR), argument("relation", IntegerType.INTEGER)))
|
||||
.setReturnType(VarcharType.VARCHAR)
|
||||
.setImplemented(true)
|
||||
.build();
|
||||
|
||||
public static final PgFunction PG_GET_EXPR_PRETTY = PgFunction.builder()
|
||||
.setName("pg_get_expr")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setDefinition("SELECT ''")
|
||||
.setArguments(List.of(argument("pg_node", VarcharType.VARCHAR), argument("relation", IntegerType.INTEGER), argument("pretty", BOOLEAN)))
|
||||
.setReturnType(VarcharType.VARCHAR)
|
||||
.setImplemented(true)
|
||||
.build();
|
||||
|
||||
public static final PgFunction FORMAT_TYPE = PgFunction.builder()
|
||||
.setName("format_type")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setDefinition("(select format_pg_type(logical_type, t.type_name) from duckdb_types() t where t.type_name=lower(tname)) || case when typemod>0 then concat('(', typemod//1000, ',', typemod%1000, ')') else '' end")
|
||||
.setArguments(List.of(argument("tname", VarcharType.VARCHAR), argument("typemod", IntegerType.INTEGER)))
|
||||
.setReturnType(VarcharType.VARCHAR)
|
||||
.build();
|
||||
|
||||
public static final PgFunction PG_GET_FUNCTION_RESULT = PgFunction.builder()
|
||||
.setName("pg_get_function_result")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setDefinition("SELECT prorettype FROM pg_proc WHERE oid = func")
|
||||
.setArguments(List.of(argument("func", IntegerType.INTEGER)))
|
||||
.setReturnType(VarcharType.VARCHAR)
|
||||
.build();
|
||||
|
||||
// TODO This is a mock function, need to be implemented
|
||||
public static final PgFunction PG_EXPANDARRAY = PgFunction.builder()
|
||||
.setName("_pg_expandarray")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setDefinition("CASE WHEN (array_length(int_arr) > 0) THEN cast((int_arr[0], 1) as row(x int, n int)) ELSE NULL END")
|
||||
.setArguments(List.of(argument("int_arr", PGArray.INT4_ARRAY)))
|
||||
.setReturnType(new RecordType(List.of(BigIntType.BIGINT, BigIntType.BIGINT)))
|
||||
.build();
|
||||
|
||||
public static final PgFunction REGEXP_LIKE = PgFunction.builder()
|
||||
.setName("regexp_like")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setDefinition("SELECT regexp_matches(arg1, arg2)")
|
||||
.setArguments(List.of(argument("arg1", VarcharType.VARCHAR), argument("arg2", VarcharType.VARCHAR)))
|
||||
.setReturnType(BOOLEAN)
|
||||
.build();
|
||||
|
||||
public static final PgFunction GENERATE_ARRAY = PgFunction.builder()
|
||||
.setName("generate_array")
|
||||
.setLanguage(PgFunction.Language.SQL)
|
||||
.setDefinition("generate_series(start, stop)")
|
||||
.setArguments(List.of(argument("start", BigIntType.BIGINT), argument("stop", BigIntType.BIGINT)))
|
||||
.setReturnType(PGArray.INT4_ARRAY)
|
||||
.build();
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.pgcatalog.function;
|
||||
|
||||
import io.wren.base.metadata.Function;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface FunctionRegistry<F extends Function>
|
||||
{
|
||||
List<F> getFunctions();
|
||||
|
||||
Optional<F> getFunction(String name, int numArgument);
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.pgcatalog.function;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import io.wren.base.metadata.Function;
|
||||
import io.wren.base.type.PGType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class PgFunction
|
||||
extends Function
|
||||
{
|
||||
public static final Pattern PG_FUNCTION_PATTERN = Pattern.compile("(?<functionName>[a-zA-Z]+(_[a-zA-Z0-9]+)*)(__(?<argsType>[a-zA-Z]+(_[a-zA-Z0-9]+)*))?(___(?<returnType>[a-zA-Z]+(_[a-zA-Z0-9]+)*))?");
|
||||
|
||||
public enum Language
|
||||
{
|
||||
SQL,
|
||||
JS
|
||||
}
|
||||
|
||||
public static Builder builder()
|
||||
{
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
private final Language language;
|
||||
private final String definition;
|
||||
private final boolean subquery;
|
||||
// if the function is implemented in the database
|
||||
private final boolean implemented;
|
||||
|
||||
public PgFunction(
|
||||
String name,
|
||||
Language language,
|
||||
List<Argument> arguments,
|
||||
PGType returnType,
|
||||
String definition,
|
||||
boolean subquery,
|
||||
boolean implemented)
|
||||
{
|
||||
super(name, arguments, returnType);
|
||||
this.language = language;
|
||||
this.definition = definition;
|
||||
this.subquery = subquery;
|
||||
this.implemented = implemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some data warehouse(BigQuery) doesn't support function overloading. We should name the function with its argument's type and return type.
|
||||
* For example:
|
||||
* pg_relation_size(relOid int)bigint -> pg_relation_size__int___bigint(relOid int)
|
||||
* pg_relation_size(relOid int, text varchar)bigint -> pg_relation_size__int_varchar___bigint(relOid int, text varchar)
|
||||
*
|
||||
* @return the name used by the remote database.
|
||||
*/
|
||||
public String getRemoteName()
|
||||
{
|
||||
String argString = getArguments().isPresent() ? "__" + Joiner.on("_").join(arguments.stream().map(Argument::getType).map(PGType::typName).collect(toImmutableList())) : "";
|
||||
String returnString = getReturnType().isPresent() ? "___" + returnType.typName() : "";
|
||||
return getName() + argString + returnString;
|
||||
}
|
||||
|
||||
public Language getLanguage()
|
||||
{
|
||||
return language;
|
||||
}
|
||||
|
||||
public String getDefinition()
|
||||
{
|
||||
return definition;
|
||||
}
|
||||
|
||||
public boolean isSubquery()
|
||||
{
|
||||
return subquery;
|
||||
}
|
||||
|
||||
public boolean isImplemented()
|
||||
{
|
||||
return implemented;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder parameterBuilder = new StringBuilder();
|
||||
if (getArguments().isPresent()) {
|
||||
for (Argument argument : getArguments().get()) {
|
||||
parameterBuilder
|
||||
.append(argument.getName()).append(" ")
|
||||
.append(argument.getType()).append(",");
|
||||
}
|
||||
parameterBuilder.setLength(parameterBuilder.length() - 1);
|
||||
}
|
||||
|
||||
return format("%s(%s)%s", getName(), parameterBuilder, getReturnType().isPresent() ? returnType.typName() : "void");
|
||||
}
|
||||
|
||||
public static class Builder
|
||||
extends Function.Builder
|
||||
{
|
||||
private String name;
|
||||
private Language language;
|
||||
|
||||
private String definition;
|
||||
|
||||
private List<Argument> arguments;
|
||||
|
||||
private PGType returnType;
|
||||
private boolean subquery;
|
||||
private boolean implemented;
|
||||
|
||||
public Builder setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setLanguage(Language language)
|
||||
{
|
||||
this.language = language;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDefinition(String definition)
|
||||
{
|
||||
this.definition = definition;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setArguments(List<Argument> arguments)
|
||||
{
|
||||
this.arguments = arguments;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setReturnType(PGType returnType)
|
||||
{
|
||||
this.returnType = returnType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSubquery(boolean subquery)
|
||||
{
|
||||
this.subquery = subquery;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setImplemented(boolean implemented)
|
||||
{
|
||||
this.implemented = implemented;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PgFunction build()
|
||||
{
|
||||
requireNonNull(name, "name is null");
|
||||
requireNonNull(language, "language is null");
|
||||
return new PgFunction(name, language, arguments, returnType, definition, subquery, implemented);
|
||||
}
|
||||
}
|
||||
}
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.pgcatalog.function;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.wren.base.metadata.FunctionKey;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static io.wren.base.metadata.FunctionKey.functionKey;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.ARRAY_IN;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.ARRAY_OUT;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.ARRAY_RECV;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.ARRAY_UPPER;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.CURRENT_DATABASE;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.CURRENT_SCHEMAS;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.FORMAT_TYPE;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.GENERATE_ARRAY;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.PG_EXPANDARRAY;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.PG_GET_EXPR;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.PG_GET_EXPR_PRETTY;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.PG_GET_FUNCTION_RESULT;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.PG_RELATION_SIZE__INT_VARCHAR___BIGINT;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.PG_RELATION_SIZE__INT___BIGINT;
|
||||
import static io.wren.base.pgcatalog.function.DuckDBFunctions.REGEXP_LIKE;
|
||||
|
||||
public final class PgMetastoreFunctionRegistry
|
||||
implements FunctionRegistry<PgFunction>
|
||||
{
|
||||
public final List<PgFunction> functions = ImmutableList.<PgFunction>builder()
|
||||
.add(CURRENT_DATABASE)
|
||||
.add(CURRENT_SCHEMAS)
|
||||
.add(PG_RELATION_SIZE__INT___BIGINT)
|
||||
.add(PG_RELATION_SIZE__INT_VARCHAR___BIGINT)
|
||||
.add(ARRAY_IN)
|
||||
.add(ARRAY_OUT)
|
||||
.add(ARRAY_RECV)
|
||||
.add(ARRAY_UPPER)
|
||||
.add(FORMAT_TYPE)
|
||||
.add(PG_GET_FUNCTION_RESULT)
|
||||
.add(REGEXP_LIKE)
|
||||
.add(GENERATE_ARRAY)
|
||||
.add(PG_EXPANDARRAY)
|
||||
.add(PG_GET_EXPR)
|
||||
.add(PG_GET_EXPR_PRETTY)
|
||||
.build();
|
||||
|
||||
private final Map<FunctionKey, PgFunction> simpleNameToFunction = new HashMap<>();
|
||||
|
||||
public PgMetastoreFunctionRegistry()
|
||||
{
|
||||
// TODO: handle function name overloading
|
||||
// https://github.com/Canner/canner-metric-layer/issues/73
|
||||
// use HashMap to handle multiple same key entries
|
||||
functions.forEach(function -> simpleNameToFunction.put(functionKey(function.getName(), function.getArguments().map(List::size).orElse(0)), function));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PgFunction> getFunctions()
|
||||
{
|
||||
return functions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<PgFunction> getFunction(String name, int numArgument)
|
||||
{
|
||||
return Optional.ofNullable(simpleNameToFunction.get(functionKey(name, numArgument)));
|
||||
}
|
||||
}
|
||||
@@ -1,405 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.sqlrewrite.analyzer;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.trino.sql.tree.ArithmeticBinaryExpression;
|
||||
import io.trino.sql.tree.ArrayConstructor;
|
||||
import io.trino.sql.tree.BetweenPredicate;
|
||||
import io.trino.sql.tree.BinaryLiteral;
|
||||
import io.trino.sql.tree.BooleanLiteral;
|
||||
import io.trino.sql.tree.Cast;
|
||||
import io.trino.sql.tree.CharLiteral;
|
||||
import io.trino.sql.tree.ComparisonExpression;
|
||||
import io.trino.sql.tree.CurrentCatalog;
|
||||
import io.trino.sql.tree.CurrentPath;
|
||||
import io.trino.sql.tree.CurrentSchema;
|
||||
import io.trino.sql.tree.CurrentTime;
|
||||
import io.trino.sql.tree.CurrentUser;
|
||||
import io.trino.sql.tree.DateTimeDataType;
|
||||
import io.trino.sql.tree.DecimalLiteral;
|
||||
import io.trino.sql.tree.DefaultTraversalVisitor;
|
||||
import io.trino.sql.tree.DereferenceExpression;
|
||||
import io.trino.sql.tree.DoubleLiteral;
|
||||
import io.trino.sql.tree.ExistsPredicate;
|
||||
import io.trino.sql.tree.Expression;
|
||||
import io.trino.sql.tree.FunctionCall;
|
||||
import io.trino.sql.tree.GenericDataType;
|
||||
import io.trino.sql.tree.GenericLiteral;
|
||||
import io.trino.sql.tree.Identifier;
|
||||
import io.trino.sql.tree.InPredicate;
|
||||
import io.trino.sql.tree.IntervalDayTimeDataType;
|
||||
import io.trino.sql.tree.IntervalLiteral;
|
||||
import io.trino.sql.tree.IsNotNullPredicate;
|
||||
import io.trino.sql.tree.IsNullPredicate;
|
||||
import io.trino.sql.tree.LikePredicate;
|
||||
import io.trino.sql.tree.LongLiteral;
|
||||
import io.trino.sql.tree.NullLiteral;
|
||||
import io.trino.sql.tree.QualifiedName;
|
||||
import io.trino.sql.tree.QuantifiedComparisonExpression;
|
||||
import io.trino.sql.tree.Row;
|
||||
import io.trino.sql.tree.RowDataType;
|
||||
import io.trino.sql.tree.StringLiteral;
|
||||
import io.trino.sql.tree.SubscriptExpression;
|
||||
import io.trino.sql.tree.TimeLiteral;
|
||||
import io.trino.sql.tree.TimestampLiteral;
|
||||
import io.wren.base.WrenMDL;
|
||||
import io.wren.base.metadata.Function;
|
||||
import io.wren.base.metadata.FunctionBundle;
|
||||
import io.wren.base.type.BigIntType;
|
||||
import io.wren.base.type.BooleanType;
|
||||
import io.wren.base.type.ByteaType;
|
||||
import io.wren.base.type.DateType;
|
||||
import io.wren.base.type.DoubleType;
|
||||
import io.wren.base.type.IntervalType;
|
||||
import io.wren.base.type.NumericType;
|
||||
import io.wren.base.type.PGType;
|
||||
import io.wren.base.type.PGTypes;
|
||||
import io.wren.base.type.RecordType;
|
||||
import io.wren.base.type.TimestampType;
|
||||
import io.wren.base.type.VarcharType;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static io.trino.sql.tree.DereferenceExpression.getQualifiedName;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class ExpressionTypeAnalyzer
|
||||
extends DefaultTraversalVisitor<Void>
|
||||
{
|
||||
public static PGType<?> analyze(WrenMDL mdl, Scope scope, Expression expression)
|
||||
{
|
||||
ExpressionTypeAnalyzer analyzer = new ExpressionTypeAnalyzer(mdl, scope);
|
||||
analyzer.process(expression);
|
||||
return analyzer.result;
|
||||
}
|
||||
|
||||
private final WrenMDL mdl;
|
||||
private final Scope scope;
|
||||
private PGType<?> result;
|
||||
|
||||
public ExpressionTypeAnalyzer(WrenMDL mdl, Scope scope)
|
||||
{
|
||||
this.mdl = requireNonNull(mdl, "mdl is null");
|
||||
this.scope = requireNonNull(scope, "scope is null");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitStringLiteral(StringLiteral node, Void context)
|
||||
{
|
||||
result = VarcharType.VARCHAR;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitDoubleLiteral(DoubleLiteral node, Void context)
|
||||
{
|
||||
result = DoubleType.DOUBLE;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitDecimalLiteral(DecimalLiteral node, Void context)
|
||||
{
|
||||
result = NumericType.NUMERIC;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitGenericLiteral(GenericLiteral node, Void context)
|
||||
{
|
||||
PGTypes.nameToPgType(node.getType())
|
||||
.ifPresent(pgType -> result = pgType);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitTimeLiteral(TimeLiteral node, Void context)
|
||||
{
|
||||
// TODO: we don't support time type yet, so we treat it as timestamp type.
|
||||
result = TimestampType.TIMESTAMP;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitTimestampLiteral(TimestampLiteral node, Void context)
|
||||
{
|
||||
// TODO: timestamp literal may contain timezone, we need to handle it.
|
||||
result = TimestampType.TIMESTAMP;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitIntervalLiteral(IntervalLiteral node, Void context)
|
||||
{
|
||||
result = IntervalType.INTERVAL;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitCharLiteral(CharLiteral node, Void context)
|
||||
{
|
||||
result = VarcharType.VARCHAR;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitBinaryLiteral(BinaryLiteral node, Void context)
|
||||
{
|
||||
result = ByteaType.BYTEA;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitBooleanLiteral(BooleanLiteral node, Void context)
|
||||
{
|
||||
result = BooleanType.BOOLEAN;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitLongLiteral(LongLiteral node, Void context)
|
||||
{
|
||||
result = BigIntType.BIGINT;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitNullLiteral(NullLiteral node, Void context)
|
||||
{
|
||||
return super.visitNullLiteral(node, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitCast(Cast node, Void context)
|
||||
{
|
||||
process(node.getType());
|
||||
// The type is the final output. We don't need to dig into the expression.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitRowDataType(RowDataType node, Void context)
|
||||
{
|
||||
result = RecordType.EMPTY_RECORD;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitDateTimeType(DateTimeDataType node, Void context)
|
||||
{
|
||||
// TODO: it may contain timezone, we need to handle it.
|
||||
result = TimestampType.TIMESTAMP;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitIntervalDataType(IntervalDayTimeDataType node, Void context)
|
||||
{
|
||||
result = IntervalType.INTERVAL;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitGenericDataType(GenericDataType node, Void context)
|
||||
{
|
||||
PGTypes.nameToPgType(node.getName().getValue()).ifPresent(pgType -> result = pgType);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitInPredicate(InPredicate node, Void context)
|
||||
{
|
||||
result = BooleanType.BOOLEAN;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitLikePredicate(LikePredicate node, Void context)
|
||||
{
|
||||
result = BooleanType.BOOLEAN;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitBetweenPredicate(BetweenPredicate node, Void context)
|
||||
{
|
||||
result = BooleanType.BOOLEAN;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitIsNotNullPredicate(IsNotNullPredicate node, Void context)
|
||||
{
|
||||
result = BooleanType.BOOLEAN;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitIsNullPredicate(IsNullPredicate node, Void context)
|
||||
{
|
||||
result = BooleanType.BOOLEAN;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitExists(ExistsPredicate node, Void context)
|
||||
{
|
||||
result = BooleanType.BOOLEAN;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitComparisonExpression(ComparisonExpression node, Void context)
|
||||
{
|
||||
result = BooleanType.BOOLEAN;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitQuantifiedComparisonExpression(QuantifiedComparisonExpression node, Void context)
|
||||
{
|
||||
// TODO: we don't support quantified comparison yet.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitFunctionCall(FunctionCall node, Void context)
|
||||
{
|
||||
FunctionBundle.getFunction(node.getName().getSuffix(), node.getArguments().size())
|
||||
.flatMap(Function::getReturnType)
|
||||
.ifPresent(type -> result = type);
|
||||
// TODO: handle the remote name
|
||||
if (node.getName().getSuffix().equalsIgnoreCase("now") ||
|
||||
node.getName().getSuffix().equalsIgnoreCase("now___timestamp")) {
|
||||
result = TimestampType.TIMESTAMP;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitDereferenceExpression(DereferenceExpression node, Void context)
|
||||
{
|
||||
QualifiedName qualifiedName = getQualifiedName(node);
|
||||
if (qualifiedName != null) {
|
||||
Optional<Field> fieldOptional = scope.getRelationType().getFields().stream()
|
||||
.filter(field -> field.canResolve(qualifiedName))
|
||||
.findFirst();
|
||||
|
||||
fieldOptional.ifPresent(field -> result = getColumnType(field));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitIdentifier(Identifier node, Void context)
|
||||
{
|
||||
QualifiedName qualifiedName = QualifiedName.of(ImmutableList.of(node.getValue()));
|
||||
scope.getRelationType().getFields().stream()
|
||||
.filter(field -> field.canResolve(qualifiedName))
|
||||
.findFirst()
|
||||
.ifPresent(field -> result = getColumnType(field));
|
||||
return null;
|
||||
}
|
||||
|
||||
private PGType<?> getColumnType(Field field)
|
||||
{
|
||||
String objectName = field.getTableName().getSchemaTableName().getTableName();
|
||||
String columnName = field.getColumnName();
|
||||
|
||||
// TODO: support to analyze the column type of CTE.
|
||||
// It could be a remote table or a custom CTE.
|
||||
if (!mdl.isObjectExist(objectName)) {
|
||||
return null;
|
||||
}
|
||||
return mdl.getColumnType(objectName, columnName).flatMap(PGTypes::nameToPgType).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitRow(Row node, Void context)
|
||||
{
|
||||
result = RecordType.EMPTY_RECORD;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitSubscriptExpression(SubscriptExpression node, Void context)
|
||||
{
|
||||
process(node.getBase(), context);
|
||||
if (result != null) {
|
||||
result = PGTypes.getArrayType(result.oid());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitArithmeticBinary(ArithmeticBinaryExpression node, Void context)
|
||||
{
|
||||
// TODO: check the type coercion rule. For now, we just use the left type.
|
||||
process(node.getLeft());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitCurrentTime(CurrentTime node, Void context)
|
||||
{
|
||||
if (node.getFunction().equals(CurrentTime.Function.DATE)) {
|
||||
result = DateType.DATE;
|
||||
}
|
||||
else {
|
||||
result = TimestampType.TIMESTAMP;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitCurrentUser(CurrentUser node, Void context)
|
||||
{
|
||||
result = VarcharType.VARCHAR;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitCurrentSchema(CurrentSchema node, Void context)
|
||||
{
|
||||
result = VarcharType.VARCHAR;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitCurrentCatalog(CurrentCatalog node, Void context)
|
||||
{
|
||||
result = VarcharType.VARCHAR;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitCurrentPath(CurrentPath node, Void context)
|
||||
{
|
||||
result = VarcharType.VARCHAR;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void visitArrayConstructor(ArrayConstructor node, Void context)
|
||||
{
|
||||
// ALl value should be same type in array, we only check first value type here.
|
||||
process(node.getValues().get(0));
|
||||
if (result != null) {
|
||||
result = PGTypes.getArrayType(result.oid());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -57,8 +57,6 @@ import io.wren.base.dto.Model;
|
||||
import io.wren.base.dto.TimeUnit;
|
||||
import io.wren.base.dto.View;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -84,13 +82,7 @@ public final class StatementAnalyzer
|
||||
|
||||
public static Scope analyze(Analysis analysis, Statement statement, SessionContext sessionContext, WrenMDL wrenMDL)
|
||||
{
|
||||
return analyze(analysis, statement, sessionContext, wrenMDL, null);
|
||||
}
|
||||
|
||||
public static Scope analyze(Analysis analysis, Statement statement, SessionContext sessionContext, WrenMDL wrenMDL, TypeCoercion typeCoercion)
|
||||
{
|
||||
Scope queryScope = new Visitor(sessionContext, analysis, wrenMDL, typeCoercion).process(statement, Optional.empty());
|
||||
|
||||
Scope queryScope = new Visitor(sessionContext, analysis, wrenMDL).process(statement, Optional.empty());
|
||||
// add models directly used in sql query
|
||||
analysis.addModels(
|
||||
wrenMDL.listModels().stream()
|
||||
@@ -137,18 +129,15 @@ public final class StatementAnalyzer
|
||||
private final SessionContext sessionContext;
|
||||
private final Analysis analysis;
|
||||
private final WrenMDL wrenMDL;
|
||||
private final Optional<TypeCoercion> typeCoercionOptional;
|
||||
|
||||
public Visitor(
|
||||
SessionContext sessionContext,
|
||||
Analysis analysis,
|
||||
WrenMDL wrenMDL,
|
||||
@Nullable TypeCoercion typeCoercion)
|
||||
WrenMDL wrenMDL)
|
||||
{
|
||||
this.sessionContext = requireNonNull(sessionContext, "sessionContext is null");
|
||||
this.analysis = requireNonNull(analysis, "analysis is null");
|
||||
this.wrenMDL = requireNonNull(wrenMDL, "wrenMDL is null");
|
||||
this.typeCoercionOptional = Optional.ofNullable(typeCoercion);
|
||||
}
|
||||
|
||||
public Scope process(Node node)
|
||||
@@ -214,7 +203,7 @@ public final class StatementAnalyzer
|
||||
{
|
||||
Query query = withQuery.getQuery();
|
||||
Analysis analyzed = new Analysis(query);
|
||||
Optional<Scope> queryScope = Optional.ofNullable(analyze(analyzed, query, sessionContext, wrenMDL, typeCoercionOptional.orElse(null)));
|
||||
Optional<Scope> queryScope = Optional.ofNullable(analyze(analyzed, query, sessionContext, wrenMDL));
|
||||
List<Field> fields;
|
||||
Optional<List<Identifier>> columnNames = withQuery.getColumnNames();
|
||||
if (columnNames.isPresent()) {
|
||||
@@ -395,11 +384,6 @@ public final class StatementAnalyzer
|
||||
analysis.addRequiredSourceNode(scope.getRelationId().getSourceNode()
|
||||
.orElseThrow(() -> new IllegalArgumentException("count(*) should have a followed source")));
|
||||
}
|
||||
|
||||
typeCoercionOptional.ifPresent(typeCoercion -> {
|
||||
Optional<Expression> coerced = typeCoercion.coerceExpression(singleColumn.getExpression(), scope);
|
||||
coerced.ifPresent(expression -> analysis.addTypeCoercion(NodeRef.of(singleColumn.getExpression()), expression));
|
||||
});
|
||||
}
|
||||
|
||||
private Scope analyzeFrom(QuerySpecification node, Optional<Scope> scope)
|
||||
@@ -413,8 +397,6 @@ public final class StatementAnalyzer
|
||||
private void analyzeWhere(Expression node, Scope scope)
|
||||
{
|
||||
ExpressionAnalysis expressionAnalysis = analyzeExpression(node, scope);
|
||||
typeCoercionOptional.flatMap(typeCoercion -> typeCoercion.coerceExpression(node, scope))
|
||||
.ifPresent(expression -> analysis.addTypeCoercion(NodeRef.of(node), expression));
|
||||
}
|
||||
|
||||
private void analyzeWindowSpecification(WindowSpecification windowSpecification, Scope scope)
|
||||
@@ -522,16 +504,6 @@ public final class StatementAnalyzer
|
||||
case JoinOn joinOn:
|
||||
Expression expression = joinOn.getExpression();
|
||||
analyzeExpression(expression, outputScope);
|
||||
typeCoercionOptional.ifPresent(typeCoercion -> {
|
||||
Optional<Expression> coerced = typeCoercion.coerceExpression(expression, outputScope);
|
||||
if (coerced.isPresent()) {
|
||||
JoinOn newJoinOn = new JoinOn(coerced.get());
|
||||
Join newJoin = node.getLocation().isPresent() ?
|
||||
new Join(node.getLocation().get(), node.getType(), node.getLeft(), node.getRight(), Optional.of(newJoinOn)) :
|
||||
new Join(node.getType(), node.getLeft(), node.getRight(), Optional.of(newJoinOn));
|
||||
analysis.addTypeCoercion(NodeRef.of(node), newJoin);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case JoinUsing joinUsing:
|
||||
joinUsing.getColumns().forEach(column -> analyzeExpression(column, outputScope));
|
||||
@@ -574,7 +546,7 @@ public final class StatementAnalyzer
|
||||
@Override
|
||||
protected Scope visitTableSubquery(TableSubquery node, Optional<Scope> scope)
|
||||
{
|
||||
return Optional.ofNullable(analyze(analysis, node.getQuery(), sessionContext, wrenMDL, typeCoercionOptional.orElse(null)))
|
||||
return Optional.ofNullable(analyze(analysis, node.getQuery(), sessionContext, wrenMDL))
|
||||
.map(value -> createAndAssignScope(node, scope, value))
|
||||
.orElseGet(() -> Scope.builder().parent(scope).build());
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.sqlrewrite.analyzer;
|
||||
|
||||
import io.trino.sql.tree.Expression;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface TypeCoercion
|
||||
{
|
||||
Optional<Expression> coerceExpression(Expression expression, Scope scope);
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class AnyType
|
||||
extends PGType<Integer>
|
||||
{
|
||||
// we represent the any data type as integer,
|
||||
// because the postgresql typalign of any is integer.
|
||||
public static final AnyType ANY = new AnyType();
|
||||
private static final int TYPE_LEN = 4;
|
||||
static final int OID = 2276;
|
||||
|
||||
private AnyType()
|
||||
{
|
||||
super(OID, TYPE_LEN, -1, "any");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.PSEUDO.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.PSEUDO.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Integer value)
|
||||
{
|
||||
buffer.writeInt(TYPE_LEN);
|
||||
buffer.writeInt(value);
|
||||
return INT32_BYTE_SIZE + TYPE_LEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull Integer value)
|
||||
{
|
||||
return Integer.toString(value).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
return buffer.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return Integer.parseInt(new String(bytes, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
abstract class BaseTimestampType
|
||||
extends PGType
|
||||
{
|
||||
protected static final int TYPE_LEN = 8;
|
||||
protected static final int TYPE_MOD = -1;
|
||||
|
||||
BaseTimestampType(int oid, int typeLen, int typeMod, @Nonnull String typeName)
|
||||
{
|
||||
super(oid, typeLen, typeMod, typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Object value)
|
||||
{
|
||||
buffer.writeInt(TYPE_LEN);
|
||||
buffer.writeLong(PgDatetimeUtils.toPgTimestamp((long) value));
|
||||
return INT32_BYTE_SIZE + TYPE_LEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.DATETIME.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
checkArgument(valueLength == TYPE_LEN, "valueLength must be %s because timestamp is a 64 bit long. Actual length is %s", TYPE_LEN, valueLength);
|
||||
long microSecondsSince2K = buffer.readLong();
|
||||
return PgDatetimeUtils.toTrinoTimestamp(microSecondsSince2K);
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
public class BigIntType
|
||||
extends PGType<Long>
|
||||
{
|
||||
public static final BigIntType BIGINT = new BigIntType();
|
||||
static final int OID = 20;
|
||||
|
||||
private static final int TYPE_LEN = 8;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
private BigIntType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "int8");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.INT8_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Long value)
|
||||
{
|
||||
buffer.writeInt(TYPE_LEN);
|
||||
buffer.writeLong(value);
|
||||
return INT32_BYTE_SIZE + TYPE_LEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.NUMERIC.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull Long value)
|
||||
{
|
||||
return Long.toString(value).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
checkArgument(valueLength == TYPE_LEN, "length should be %s because long is int64. Actual length: %s", TYPE_LEN, valueLength);
|
||||
return buffer.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return Long.parseLong(new String(bytes, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
public class BooleanType
|
||||
extends PGType<Boolean>
|
||||
{
|
||||
public static final PGType BOOLEAN = new BooleanType();
|
||||
static final int OID = 16;
|
||||
|
||||
private static final int TYPE_LEN = 1;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
private static final byte[] TEXT_TRUE = new byte[] {'t'};
|
||||
private static final byte[] TEXT_FALSE = new byte[] {'f'};
|
||||
|
||||
private static final Collection<ByteBuffer> TRUTH_VALUES = ImmutableSet.of(
|
||||
ByteBuffer.wrap(new byte[] {'1'}),
|
||||
ByteBuffer.wrap(new byte[] {'t'}),
|
||||
ByteBuffer.wrap(new byte[] {'T'}),
|
||||
ByteBuffer.wrap(new byte[] {'t', 'r', 'u', 'e'}),
|
||||
ByteBuffer.wrap(new byte[] {'T', 'R', 'U', 'E'}));
|
||||
|
||||
private BooleanType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "bool");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.BOOL_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.NUMERIC.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Boolean value)
|
||||
{
|
||||
byte byteValue = (byte) (value ? 1 : 0);
|
||||
buffer.writeInt(TYPE_LEN);
|
||||
buffer.writeByte(byteValue);
|
||||
return INT32_BYTE_SIZE + TYPE_LEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull Boolean value)
|
||||
{
|
||||
return value ? TEXT_TRUE : TEXT_FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
checkArgument(valueLength == TYPE_LEN, "length should be %s boolean is just a byte. Actual length: %s", TYPE_LEN, valueLength);
|
||||
byte value = buffer.readByte();
|
||||
switch (value) {
|
||||
case 0:
|
||||
return false;
|
||||
case 1:
|
||||
return true;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported binary bool: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return TRUTH_VALUES.contains(ByteBuffer.wrap(bytes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class BpCharType
|
||||
extends PGType<String>
|
||||
{
|
||||
public static final BpCharType BPCHAR = new BpCharType();
|
||||
static final int OID = 1042;
|
||||
|
||||
private BpCharType()
|
||||
{
|
||||
super(OID, -1, -1, "bpchar");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.BPCHAR_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.STRING.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull String value)
|
||||
{
|
||||
byte[] bytes = value.getBytes(UTF_8);
|
||||
buffer.writeInt(bytes.length);
|
||||
buffer.writeBytes(bytes);
|
||||
return INT32_BYTE_SIZE + bytes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsText(ByteBuf buffer, @Nonnull String value)
|
||||
{
|
||||
return writeAsBinary(buffer, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
byte[] utf8 = new byte[valueLength];
|
||||
buffer.readBytes(utf8);
|
||||
return new String(utf8, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull String value)
|
||||
{
|
||||
return value.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return new String(bytes, UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class ByteaType
|
||||
extends PGType<Object>
|
||||
{
|
||||
public static final int OID = 17;
|
||||
private static final int TYPE_LEN = -1;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
public static final ByteaType BYTEA = new ByteaType();
|
||||
|
||||
private ByteaType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "bytea");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.BYTEA_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.USER_DEFINED_TYPES.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, Object value)
|
||||
{
|
||||
byte[] bytes = encodeHexString((byte[]) value).getBytes(UTF_8);
|
||||
buffer.writeInt(bytes.length);
|
||||
buffer.writeBytes(bytes);
|
||||
return INT32_BYTE_SIZE + bytes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsText(ByteBuf buffer, @Nonnull Object value)
|
||||
{
|
||||
return writeAsBinary(buffer, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
checkArgument(valueLength >= 1, "The length of bytea should be 1 at least.");
|
||||
byte[] bytes = new byte[valueLength];
|
||||
buffer.readBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(Object value)
|
||||
{
|
||||
String strBuilder = "\\" + encodeHexString((byte[]) value);
|
||||
return strBuilder.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private String encodeHexString(byte[] decimalValue)
|
||||
{
|
||||
return "\\x" + Hex.encodeHexString(decimalValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class CharType
|
||||
extends PGType<String>
|
||||
{
|
||||
public static final CharType CHAR = new CharType();
|
||||
static final int OID = 18;
|
||||
|
||||
private CharType()
|
||||
{
|
||||
super(OID, 1, -1, "char");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.CHAR_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.STRING.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull String value)
|
||||
{
|
||||
buffer.writeInt(1);
|
||||
buffer.writeBytes(value.getBytes(UTF_8));
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
checkArgument(valueLength == 1, "The length of char should be 1");
|
||||
return new String(buffer.readBytes(valueLength).array(), UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull String value)
|
||||
{
|
||||
return value.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return new String(bytes, UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.ResolverStyle;
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
|
||||
|
||||
public class DateType
|
||||
extends PGType<LocalDate>
|
||||
{
|
||||
public static final PGType DATE = new DateType();
|
||||
|
||||
private static final int OID = 1082;
|
||||
private static final String NAME = "date";
|
||||
private static final int TYPE_LEN = 4;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
private static final DateTimeFormatter ISO_FORMATTER = new DateTimeFormatterBuilder()
|
||||
.parseCaseInsensitive()
|
||||
.append(ISO_LOCAL_DATE)
|
||||
.toFormatter(Locale.ENGLISH).withResolverStyle(ResolverStyle.STRICT);
|
||||
|
||||
private static final DateTimeFormatter ISO_FORMATTER_AD = new DateTimeFormatterBuilder()
|
||||
.parseCaseInsensitive()
|
||||
.appendPattern("yyyy-MM-dd")
|
||||
.toFormatter(Locale.ENGLISH)
|
||||
.withResolverStyle(ResolverStyle.STRICT);
|
||||
|
||||
private DateType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.DATETIME.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.DATE_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull LocalDate value)
|
||||
{
|
||||
return value.format(ISO_FORMATTER_AD).getBytes(UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
String s = new String(bytes, UTF_8);
|
||||
return LocalDate.parse(s, ISO_FORMATTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull LocalDate value)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return "1970-01-01";
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
public class DoubleType
|
||||
extends PGType<Double>
|
||||
{
|
||||
public static final DoubleType DOUBLE = new DoubleType();
|
||||
static final int OID = 701;
|
||||
|
||||
private static final int TYPE_LEN = 8;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
private DoubleType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "float8");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.FLOAT8_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.NUMERIC.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Double value)
|
||||
{
|
||||
buffer.writeInt(TYPE_LEN);
|
||||
buffer.writeDouble(value);
|
||||
return INT32_BYTE_SIZE + TYPE_LEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull Double value)
|
||||
{
|
||||
return Double.toString(value).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
checkArgument(valueLength == TYPE_LEN, "length should be %s because double is int64. Actual length: %s", TYPE_LEN, valueLength);
|
||||
return buffer.readDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return Double.parseDouble(new String(bytes, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* A Hashmap type in PostgreSQL which keys and values are simple text value.
|
||||
* We used it to represent the presto Map type.
|
||||
* https://www.postgresql.org/docs/current/hstore.html
|
||||
* <p>
|
||||
* TODO: handle non-text keys and value.
|
||||
* Because presto allows map with non-text key and value, we should handle this case.
|
||||
*/
|
||||
public class HstoreType
|
||||
extends PGType<Map<Object, Object>>
|
||||
{
|
||||
public static final HstoreType HSTORE = new HstoreType();
|
||||
|
||||
HstoreType()
|
||||
{
|
||||
super(57640, -1, -1, "hstore");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.HSTORE_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.USER_DEFINED_TYPES.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/postgres/postgres/blob/master/contrib/hstore/hstore_io.c (hstore_send)
|
||||
*/
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, Map<Object, Object> value)
|
||||
{
|
||||
int count = value.size();
|
||||
final int lenIndex = buffer.writerIndex();
|
||||
int bytesWritten = 4 + 4;
|
||||
buffer.writeInt(0);
|
||||
buffer.writeInt(count);
|
||||
|
||||
int valueLen = 0;
|
||||
for (Map.Entry<Object, Object> entry : value.entrySet()) {
|
||||
byte[] key = entry.getKey().toString().getBytes(UTF_8);
|
||||
buffer.writeInt(key.length);
|
||||
buffer.writeBytes(key);
|
||||
valueLen += key.length + 4;
|
||||
if (entry.getValue() == null) {
|
||||
buffer.writeInt(-1);
|
||||
valueLen += 4;
|
||||
}
|
||||
else {
|
||||
byte[] val = entry.getValue().toString().getBytes(UTF_8);
|
||||
buffer.writeInt(val.length);
|
||||
buffer.writeBytes(val);
|
||||
valueLen += val.length + 4;
|
||||
}
|
||||
}
|
||||
int len = bytesWritten + valueLen;
|
||||
buffer.setInt(lenIndex, len);
|
||||
return INT32_BYTE_SIZE + len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Object, Object> readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
throw new UnsupportedOperationException("Input of anonymous hstore type values is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/postgres/postgres/blob/master/contrib/hstore/hstore_io.c (hstore_out)
|
||||
*/
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(Map<Object, Object> value)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Map.Entry<Object, Object> entry : value.entrySet()) {
|
||||
builder.append("\"").append(entry.getKey()).append("\"").append("=>");
|
||||
if (entry.getValue() == null) {
|
||||
builder.append("NULL");
|
||||
}
|
||||
else {
|
||||
builder.append("\"").append(entry.getValue()).append("\"");
|
||||
}
|
||||
builder.append(",");
|
||||
}
|
||||
builder.setLength(builder.length() - 1);
|
||||
return builder.toString().getBytes(UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Object, Object> decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
throw new UnsupportedOperationException("Input of anonymous hstore type values is not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return Map.of();
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class InetType
|
||||
extends PGType<String>
|
||||
{
|
||||
public static final InetType INET = new InetType();
|
||||
static final int OID = 869;
|
||||
|
||||
private static final int TYPE_LEN = -1;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
private InetType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "inet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.INET_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.NETWORK.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return PGType.Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull String value)
|
||||
{
|
||||
throw new UnsupportedOperationException("InetType doesn't support binary format.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull String value)
|
||||
{
|
||||
return value.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
throw new UnsupportedOperationException("InetType doesn't support binary format.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return new String(bytes, UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
public class IntegerType
|
||||
extends PGType<Integer>
|
||||
{
|
||||
static final int OID = 23;
|
||||
|
||||
private static final int TYPE_LEN = 4;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
public static final IntegerType INTEGER = new IntegerType();
|
||||
|
||||
private IntegerType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "int4");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.INT4_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.NUMERIC.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Integer value)
|
||||
{
|
||||
buffer.writeInt(TYPE_LEN);
|
||||
buffer.writeInt(value);
|
||||
return INT32_BYTE_SIZE + TYPE_LEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull Integer value)
|
||||
{
|
||||
return Integer.toString(value).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
checkArgument(valueLength == TYPE_LEN, "length should be %s because int is int32. Actual length: %s", TYPE_LEN, valueLength);
|
||||
return buffer.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return Integer.parseInt(new String(bytes, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.joda.time.Period;
|
||||
import org.joda.time.format.PeriodFormatter;
|
||||
import org.joda.time.format.PeriodFormatterBuilder;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static io.wren.base.Utils.checkArgument;
|
||||
import static java.lang.Math.toIntExact;
|
||||
|
||||
public class IntervalType
|
||||
extends PGType<Period>
|
||||
{
|
||||
private static final int OID = 1186;
|
||||
private static final int TYPE_LEN = 16;
|
||||
private static final int TYPE_MOD = -1;
|
||||
public static final IntervalType INTERVAL = new IntervalType();
|
||||
private static final PeriodFormatter DAY_FORMATTER = new PeriodFormatterBuilder()
|
||||
.appendYears()
|
||||
.appendSuffix(" year", " years")
|
||||
.appendSeparator(" ")
|
||||
.appendMonths()
|
||||
.appendSuffix(" mon", " mons")
|
||||
.appendSeparator(" ")
|
||||
.appendWeeks()
|
||||
.appendSuffix(" weeks")
|
||||
.appendSeparator(" ")
|
||||
.appendDays()
|
||||
.appendSuffix(" day", " days")
|
||||
.toFormatter();
|
||||
|
||||
private static final PeriodFormatter TIME_FORMATTER = new PeriodFormatterBuilder()
|
||||
.printZeroAlways()
|
||||
.minimumPrintedDigits(2)
|
||||
.appendHours()
|
||||
.appendSeparator(":")
|
||||
.minimumPrintedDigits(2)
|
||||
.printZeroAlways()
|
||||
.appendMinutes()
|
||||
.appendSeparator(":")
|
||||
.minimumPrintedDigits(2)
|
||||
.printZeroAlways()
|
||||
.appendSecondsWithOptionalMillis()
|
||||
.toFormatter();
|
||||
|
||||
private static final PeriodFormatter PG_INTERVAL_FORMATTER = new PeriodFormatterBuilder()
|
||||
.appendYears()
|
||||
.appendSuffix(" years ")
|
||||
.appendMonths()
|
||||
.appendSuffix(" mons ")
|
||||
.appendDays()
|
||||
.appendSuffix(" days ")
|
||||
.appendHours()
|
||||
.appendSuffix(" hours ")
|
||||
.appendMinutes()
|
||||
.appendSuffix(" mins ")
|
||||
.appendSecondsWithOptionalMillis()
|
||||
.appendSuffix(" secs")
|
||||
.toFormatter();
|
||||
|
||||
private IntervalType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "interval");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.INTERVAL_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.TIMESPAN.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Period period)
|
||||
{
|
||||
buffer.writeInt(TYPE_LEN);
|
||||
// from PostgreSQL code:
|
||||
// pq_sendint64(&buf, interval->time);
|
||||
// pq_sendint32(&buf, interval->day);
|
||||
// pq_sendint32(&buf, interval->month);
|
||||
buffer.writeLong(
|
||||
(period.getHours() * 60 * 60 * 1000_000L)
|
||||
+ (period.getMinutes() * 60 * 1000_000L)
|
||||
+ (period.getSeconds() * 1000_000L)
|
||||
+ (period.getMillis() * 1000));
|
||||
buffer.writeInt((period.getWeeks() * 7) + period.getDays());
|
||||
buffer.writeInt((period.getYears() * 12) + period.getMonths());
|
||||
return INT32_BYTE_SIZE + TYPE_LEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Period readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
checkArgument(valueLength == TYPE_LEN, "length should be %s because interval is 16. Actual length: %s", TYPE_LEN, valueLength);
|
||||
long micros = buffer.readLong();
|
||||
int days = buffer.readInt();
|
||||
int months = buffer.readInt();
|
||||
|
||||
long microsInAnHour = 60 * 60 * 1000_000L;
|
||||
int hours = toIntExact(micros / microsInAnHour);
|
||||
long microsWithoutHours = micros % microsInAnHour;
|
||||
|
||||
long microsInAMinute = 60 * 1000_000L;
|
||||
int minutes = toIntExact(microsWithoutHours / microsInAMinute);
|
||||
long microsWithoutMinutes = microsWithoutHours % microsInAMinute;
|
||||
|
||||
int seconds = toIntExact(microsWithoutMinutes / 1000_000);
|
||||
int millis = toIntExact((microsWithoutMinutes % 1000_000) / 1000);
|
||||
return new Period(
|
||||
months / 12,
|
||||
months % 12,
|
||||
days / 7,
|
||||
days % 7,
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
millis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull Period value)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(DAY_FORMATTER.print(value));
|
||||
sb.append(" ");
|
||||
// the negative sign need to be placed before the time, like -00:00:01
|
||||
if (value.getHours() < 0 || value.getMinutes() < 0 || value.getSeconds() < 0 || value.getMillis() < 0) {
|
||||
sb.append("-");
|
||||
}
|
||||
Period absValue = new Period(
|
||||
Math.abs(value.getHours()),
|
||||
Math.abs(value.getMinutes()),
|
||||
Math.abs(value.getSeconds()),
|
||||
Math.abs(value.getMillis()));
|
||||
sb.append(TIME_FORMATTER.print(absValue));
|
||||
|
||||
return sb.toString().replace("00:00:00", "").trim().getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Period decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return PG_INTERVAL_FORMATTER.parsePeriod(new String(bytes, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return Period.ZERO;
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class JsonType
|
||||
extends PGType<String>
|
||||
{
|
||||
public static final JsonType JSON = new JsonType();
|
||||
static final int OID = 114;
|
||||
|
||||
private static final int TYPE_LEN = -1;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
private JsonType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.JSON_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.USER_DEFINED_TYPES.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull String value)
|
||||
{
|
||||
byte[] bytes = encodeAsUTF8Text(value);
|
||||
buffer.writeInt(bytes.length);
|
||||
buffer.writeBytes(bytes);
|
||||
return INT32_BYTE_SIZE + bytes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull String value)
|
||||
{
|
||||
return value.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
byte[] bytes = new byte[valueLength];
|
||||
buffer.readBytes(bytes);
|
||||
return decodeUTF8Text(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return new String(bytes, UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.math.MathContext;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class NumericType
|
||||
extends PGType<Number>
|
||||
{
|
||||
static final int OID = 1700;
|
||||
|
||||
private static final int TYPE_LEN = -1;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
private static final short DEC_DIGITS = 4;
|
||||
private static final short NUMERIC_POS = 0x0000;
|
||||
private static final short NUMERIC_NEG = 0x4000;
|
||||
private static final short NUMERIC_NAN = (short) 0xC000;
|
||||
|
||||
public static final NumericType NUMERIC = new NumericType();
|
||||
|
||||
private NumericType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "numeric");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.NUMERIC_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.NUMERIC.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Number value)
|
||||
{
|
||||
return switch (value) {
|
||||
case BigDecimal bigDecimal -> writeAsBinary(buffer, bigDecimal);
|
||||
// TODO: customize the handling of BigInteger for performance.
|
||||
case BigInteger bigInteger -> writeAsBinary(buffer, new BigDecimal(bigInteger));
|
||||
default -> throw new IllegalArgumentException("Unsupported numeric type: " + value.getClass().getName());
|
||||
};
|
||||
}
|
||||
|
||||
private int writeAsBinary(ByteBuf buffer, @Nonnull BigDecimal value)
|
||||
{
|
||||
// Taken from https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/pgwire/types.go#L336
|
||||
// and https://github.com/postgres/postgres/blob/master/src/backend/utils/adt/numeric.c#L6760.
|
||||
// The number is split into chunks of DEC_DIGITS short values while leading and trailing 0's are omitted.
|
||||
// Examples:
|
||||
// * 01234 -> [1234]
|
||||
// * 1234567 -> [0123, 4567], scale 0
|
||||
// * 1.234500 -> [0001, 2345], scale 4
|
||||
// * 1234567.12 -> [0123, 4567, 1200], scale 1
|
||||
// * 1234.0 -> [1234], scale 1
|
||||
// * 0123.45 -> [0123, 4500], scale 2
|
||||
char[] digits = value.unscaledValue().toString().toCharArray();
|
||||
int start = 0;
|
||||
int end = digits.length;
|
||||
while (start < end && (digits[start] == '0' || digits[start] == '-')) {
|
||||
start++;
|
||||
}
|
||||
int dWeight = end - start - value.scale() - 1;
|
||||
while (start < end && digits[end - 1] == '0') {
|
||||
end--;
|
||||
}
|
||||
|
||||
int len = end - start;
|
||||
short weight = 0; // Max DEC_DIGIT block index before decimal point
|
||||
int offset = 0; // Offset inside the first block, e.g. 234.23 has and offset of 1
|
||||
short nDigits = 0; // Number of DEC_DIGIT blocks
|
||||
if (len != 0) {
|
||||
if (dWeight >= 0) {
|
||||
weight = (short) ((dWeight + 1 + DEC_DIGITS - 1) / DEC_DIGITS - 1);
|
||||
}
|
||||
else {
|
||||
weight = (short) (-((-dWeight - 1) / DEC_DIGITS + 1));
|
||||
}
|
||||
offset = (weight + 1) * DEC_DIGITS - (dWeight + 1);
|
||||
nDigits = (short) ((len + offset + DEC_DIGITS - 1) / DEC_DIGITS);
|
||||
}
|
||||
int typeLen = 2 * (4 + nDigits);
|
||||
|
||||
buffer.writeInt(typeLen);
|
||||
buffer.writeShort(nDigits);
|
||||
buffer.writeShort(weight);
|
||||
switch (value.signum()) {
|
||||
case -1:
|
||||
buffer.writeShort(NUMERIC_NEG);
|
||||
break;
|
||||
case 0:
|
||||
buffer.writeShort(NUMERIC_NAN);
|
||||
break;
|
||||
case 1:
|
||||
buffer.writeShort(NUMERIC_POS);
|
||||
break;
|
||||
default:
|
||||
buffer.writeShort(NUMERIC_POS);
|
||||
}
|
||||
buffer.writeShort(value.scale());
|
||||
|
||||
int digitIdx = -offset + start;
|
||||
while (nDigits-- > 0) {
|
||||
short ndigit = 0;
|
||||
// Encode 4 digits into a 16 bit short value
|
||||
for (int nextDigitIdx = digitIdx + DEC_DIGITS; digitIdx < nextDigitIdx; digitIdx++) {
|
||||
ndigit *= 10;
|
||||
if (digitIdx >= start && digitIdx < end) {
|
||||
ndigit += digits[digitIdx] - '0';
|
||||
}
|
||||
}
|
||||
buffer.writeShort(ndigit);
|
||||
}
|
||||
|
||||
return INT32_BYTE_SIZE + typeLen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
// Number of DEC_DIGIT blocks
|
||||
short nDigits = buffer.readShort();
|
||||
// DEC_DIGIT blocks before decimal point
|
||||
short weight = buffer.readShort();
|
||||
short sign = buffer.readShort();
|
||||
short scale = buffer.readShort();
|
||||
|
||||
if (sign == NUMERIC_NAN) {
|
||||
throw new IllegalArgumentException("Infinite or NaN values are not supported");
|
||||
}
|
||||
|
||||
boolean hasDp = scale > 0;
|
||||
int sizeOfBytes = (nDigits * DEC_DIGITS) + (hasDp ? 1 : 0);
|
||||
char[] decDigits = new char[sizeOfBytes];
|
||||
|
||||
int decDigitsIdx = 0;
|
||||
for (int i = 0; i < nDigits; i++) {
|
||||
int decDigit = buffer.readShort();
|
||||
if (decDigit > 0) {
|
||||
// Decode 4 digits from a 16 bit short
|
||||
for (int j = 1000; j > 0 && decDigitsIdx < sizeOfBytes; j /= 10) {
|
||||
int d1 = (decDigit / j);
|
||||
decDigit -= d1 * j;
|
||||
decDigits[decDigitsIdx++] = (char) (d1 + '0');
|
||||
}
|
||||
}
|
||||
if (hasDp && i == weight) {
|
||||
decDigits[decDigitsIdx++] = '.';
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal bd = new BigDecimal(decDigits)
|
||||
.setScale(scale, MathContext.UNLIMITED.getRoundingMode());
|
||||
return sign == NUMERIC_NEG ? bd.negate() : bd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull Number value)
|
||||
{
|
||||
return value.toString().getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return new BigDecimal(new String(bytes, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
public class OidType
|
||||
extends PGType<Long>
|
||||
{
|
||||
public static final OidType OID_INSTANCE = new OidType();
|
||||
|
||||
static final int OID = 26;
|
||||
|
||||
private static final int TYPE_LEN = 8;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
OidType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "oid");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.OID_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.NUMERIC.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, Long value)
|
||||
{
|
||||
buffer.writeInt(TYPE_LEN);
|
||||
buffer.writeLong(value);
|
||||
return INT32_BYTE_SIZE + TYPE_LEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
checkArgument(valueLength == TYPE_LEN, "length should be %s because oid is int32. Actual length: %s", TYPE_LEN, valueLength);
|
||||
return buffer.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(Long value)
|
||||
{
|
||||
return Long.toString(value).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return Long.parseLong(new String(bytes, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,404 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.wren.base.type.parser.PgArrayParserWrapper;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.wren.base.type.BooleanType.BOOLEAN;
|
||||
import static io.wren.base.type.DateType.DATE;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class PGArray
|
||||
extends PGType<List<Object>>
|
||||
{
|
||||
public static final PGArray CHAR_ARRAY = new PGArray(1002, CharType.CHAR);
|
||||
public static final PGArray BPCHAR_ARRAY = new PGArray(1014, BpCharType.BPCHAR);
|
||||
public static final PGArray INT2_ARRAY = new PGArray(1005, SmallIntType.SMALLINT);
|
||||
public static final PGArray INT4_ARRAY = new PGArray(1007, IntegerType.INTEGER);
|
||||
public static final PGArray OID_ARRAY = new PGArray(1028, OidType.OID_INSTANCE);
|
||||
public static final PGArray INT8_ARRAY = new PGArray(1016, BigIntType.BIGINT);
|
||||
public static final PGArray FLOAT4_ARRAY = new PGArray(1021, RealType.REAL);
|
||||
public static final PGArray FLOAT8_ARRAY = new PGArray(1022, DoubleType.DOUBLE);
|
||||
public static final PGArray NUMERIC_ARRAY = new PGArray(1231, NumericType.NUMERIC);
|
||||
public static final PGArray BOOL_ARRAY = new PGArray(1000, BOOLEAN);
|
||||
public static final PGArray VARCHAR_ARRAY = new PGArray(1015, VarcharType.VARCHAR);
|
||||
public static final PGArray TEXT_ARRAY = new PGArray(1009, VarcharType.TextType.TEXT);
|
||||
public static final PGArray NAME_ARRAY = new PGArray(1003, VarcharType.NameType.NAME);
|
||||
public static final PGArray JSON_ARRAY = new PGArray(199, JsonType.JSON);
|
||||
public static final PGArray TIMESTAMP_WITH_TIMEZONE_ARRAY = new PGArray(1185, TimestampWithTimeZoneType.TIMESTAMP_WITH_TIMEZONE);
|
||||
public static final PGArray TIMESTAMP_ARRAY = new PGArray(1115, TimestampType.TIMESTAMP);
|
||||
public static final PGArray DATE_ARRAY = new PGArray(1182, DATE);
|
||||
public static final PGArray UUID_ARRAY = new PGArray(2951, UuidType.UUID);
|
||||
public static final PGArray BYTEA_ARRAY = new PGArray(1001, ByteaType.BYTEA);
|
||||
public static final PGArray INET_ARRAY = new PGArray(1041, InetType.INET);
|
||||
public static final PGArray EMPTY_RECORD_ARRAY = new PGArray(2287, RecordType.EMPTY_RECORD);
|
||||
public static final PGArray HSTORE_ARRAY = new PGArray(57645, HstoreType.HSTORE);
|
||||
public static final PGArray REGPROC_ARRAY = new PGArray(1008, RegprocType.REGPROC);
|
||||
public static final PGArray INTERVAL_ARRAY = new PGArray(1187, IntervalType.INTERVAL);
|
||||
|
||||
// TODO:
|
||||
// public static final PGArray TIMETZ_ARRAY = new PGArray(1270, TimeTZType.INSTANCE);
|
||||
// public static final PGArray POINT_ARRAY = new PGArray(1017, PointType.INSTANCE);
|
||||
// public static final PGArray ANY_ARRAY = new PGArray(
|
||||
// 2277,
|
||||
// AnyType.INSTANCE.typName() + "array",
|
||||
// AnyType.INSTANCE)
|
||||
// {
|
||||
// @Override
|
||||
// public String typeCategory()
|
||||
// {
|
||||
// return TypeCategory.PSEUDO.code();
|
||||
// }
|
||||
// };
|
||||
|
||||
private static final byte[] NULL_BYTES = new byte[] {'N', 'U', 'L', 'L'};
|
||||
|
||||
public static List<PGArray> allArray()
|
||||
{
|
||||
// TODO: support IntervalType array
|
||||
return ImmutableList.of(
|
||||
CHAR_ARRAY,
|
||||
BPCHAR_ARRAY,
|
||||
INT2_ARRAY,
|
||||
INT4_ARRAY,
|
||||
OID_ARRAY,
|
||||
INT8_ARRAY,
|
||||
FLOAT4_ARRAY,
|
||||
FLOAT8_ARRAY,
|
||||
NUMERIC_ARRAY,
|
||||
BOOL_ARRAY,
|
||||
VARCHAR_ARRAY,
|
||||
TEXT_ARRAY,
|
||||
JSON_ARRAY,
|
||||
NAME_ARRAY,
|
||||
TIMESTAMP_ARRAY,
|
||||
TIMESTAMP_WITH_TIMEZONE_ARRAY,
|
||||
DATE_ARRAY,
|
||||
HSTORE_ARRAY,
|
||||
INET_ARRAY,
|
||||
EMPTY_RECORD_ARRAY,
|
||||
UUID_ARRAY,
|
||||
BYTEA_ARRAY);
|
||||
}
|
||||
|
||||
private final PGType<?> innerType;
|
||||
|
||||
PGArray(int oid, String name, PGType<?> innerType)
|
||||
{
|
||||
super(oid, -1, -1, name);
|
||||
this.innerType = innerType;
|
||||
}
|
||||
|
||||
PGArray(int oid, PGType<?> innerType)
|
||||
{
|
||||
this(oid, "_" + innerType.typName(), innerType);
|
||||
}
|
||||
|
||||
public PGType<?> getInnerType()
|
||||
{
|
||||
return innerType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.ARRAY.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return innerType.type();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typElem()
|
||||
{
|
||||
return innerType.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull List<Object> value)
|
||||
{
|
||||
int dimensions = getDimensions(value);
|
||||
|
||||
List<Integer> dimensionsList = new ArrayList<>();
|
||||
buildDimensions(value, dimensionsList, dimensions, 1);
|
||||
|
||||
int bytesWritten = 4 + 4 + 4;
|
||||
final int lenIndex = buffer.writerIndex();
|
||||
buffer.writeInt(0);
|
||||
buffer.writeInt(dimensions);
|
||||
buffer.writeInt(1); // flags bit 0: 0=no-nulls, 1=has-nulls
|
||||
buffer.writeInt(typElem());
|
||||
|
||||
for (Integer dim : dimensionsList) {
|
||||
buffer.writeInt(dim); // upper bound
|
||||
buffer.writeInt(dim); // lower bound
|
||||
bytesWritten += 8;
|
||||
}
|
||||
int len = bytesWritten + writeArrayAsBinary(buffer, value, dimensionsList, 1);
|
||||
buffer.setInt(lenIndex, len);
|
||||
return INT32_BYTE_SIZE + len; // add also the size of the length itself
|
||||
}
|
||||
|
||||
private int getDimensions(@Nonnull Object value)
|
||||
{
|
||||
int dimensions = 0;
|
||||
Object array = value;
|
||||
|
||||
do {
|
||||
dimensions++;
|
||||
List<?> arr = (List<?>) array;
|
||||
if (arr.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
array = null;
|
||||
for (Object o : arr) {
|
||||
if (o == null) {
|
||||
continue;
|
||||
}
|
||||
array = o;
|
||||
}
|
||||
}
|
||||
while (array instanceof List);
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
int dimensions = buffer.readInt();
|
||||
buffer.readInt(); // flags bit 0: 0=no-nulls, 1=has-nulls
|
||||
buffer.readInt(); // element oid
|
||||
if (dimensions == 0) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
int[] dims = new int[dimensions];
|
||||
for (int d = 0; d < dimensions; ++d) {
|
||||
dims[d] = buffer.readInt();
|
||||
buffer.readInt(); // lowerBound ignored
|
||||
}
|
||||
List<Object> values = new ArrayList<>(dims[0]);
|
||||
readArrayAsBinary(buffer, values, dims, 0);
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull List<Object> array)
|
||||
{
|
||||
boolean isJson = JsonType.OID == innerType.oid();
|
||||
List<Byte> encodedValues = new ArrayList<>();
|
||||
encodedValues.add((byte) '{');
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
Object o = array.get(i);
|
||||
if (o instanceof List) { // Nested Array -> recursive call
|
||||
byte[] bytes = encodeAsUTF8Text((List) o);
|
||||
for (byte b : bytes) {
|
||||
encodedValues.add(b);
|
||||
}
|
||||
if (i == 0) {
|
||||
encodedValues.add((byte) ',');
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (i > 0) {
|
||||
encodedValues.add((byte) ',');
|
||||
}
|
||||
byte[] bytes;
|
||||
if (o == null) {
|
||||
bytes = NULL_BYTES;
|
||||
for (byte aByte : bytes) {
|
||||
encodedValues.add(aByte);
|
||||
}
|
||||
}
|
||||
else {
|
||||
bytes = ((PGType) innerType).encodeAsUTF8Text(o);
|
||||
|
||||
if (needDoubleQuoteAround(innerType.oid(), bytes)) {
|
||||
encodedValues.add((byte) '"');
|
||||
}
|
||||
if (isJson) {
|
||||
for (byte aByte : bytes) {
|
||||
// Escape double quotes with backslash for json
|
||||
char ch = (char) aByte;
|
||||
if (ch == '"' || ch == '\\') {
|
||||
encodedValues.add((byte) '\\');
|
||||
}
|
||||
encodedValues.add(aByte);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (byte aByte : bytes) {
|
||||
encodedValues.add(aByte);
|
||||
}
|
||||
}
|
||||
if (needDoubleQuoteAround(innerType.oid(), bytes)) {
|
||||
encodedValues.add((byte) '"');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
encodedValues.add((byte) '}');
|
||||
return Bytes.toArray(encodedValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
/*
|
||||
* text representation:
|
||||
*
|
||||
* 1-dimension integer array:
|
||||
* {10,NULL,NULL,20,30}
|
||||
* {"10",NULL,NULL,"20","30"}
|
||||
* 2-dimension integer array:
|
||||
* {{"10","20"},{"30",NULL,"40}}
|
||||
*
|
||||
* 1-dimension json array:
|
||||
* {"{"x": 10}","{"y": 20}"}
|
||||
* 2-dimension json array:
|
||||
* {{"{"x": 10}","{"y": 20}"},{"{"x": 30}","{"y": 40}"}}
|
||||
*/
|
||||
return (List<Object>) PgArrayParserWrapper.parse(bytes, innerType::decodeUTF8Text);
|
||||
}
|
||||
|
||||
private int buildDimensions(List<Object> values, List<Integer> dimensionsList, int maxDimensions, int currentDimension)
|
||||
{
|
||||
if (values == null) {
|
||||
return 1;
|
||||
}
|
||||
// While elements of array are also arrays
|
||||
if (currentDimension < maxDimensions) {
|
||||
int max = 0;
|
||||
for (Object o : values) {
|
||||
max = Math.max(max, buildDimensions((List<Object>) o, dimensionsList, maxDimensions, currentDimension + 1));
|
||||
}
|
||||
|
||||
if (currentDimension == maxDimensions - 1) {
|
||||
dimensionsList.add(max);
|
||||
}
|
||||
else {
|
||||
Integer current = dimensionsList.get(0);
|
||||
dimensionsList.set(0, Math.max(current, max));
|
||||
}
|
||||
}
|
||||
// Add the dimensions of 1st dimension
|
||||
if (currentDimension == 1) {
|
||||
dimensionsList.add(0, values.size());
|
||||
}
|
||||
return values.size();
|
||||
}
|
||||
|
||||
private int writeArrayAsBinary(ByteBuf buffer, List<Object> array, List<Integer> dimensionsList, int currentDimension)
|
||||
{
|
||||
int bytesWritten = 0;
|
||||
|
||||
if (array == null) {
|
||||
for (int i = 0; i < dimensionsList.get(currentDimension - 1); i++) {
|
||||
buffer.writeInt(-1);
|
||||
bytesWritten += 4;
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
// 2nd to last level
|
||||
if (currentDimension == dimensionsList.size()) {
|
||||
int i = 0;
|
||||
for (Object o : array) {
|
||||
if (o == null) {
|
||||
buffer.writeInt(-1);
|
||||
bytesWritten += 4;
|
||||
}
|
||||
else {
|
||||
bytesWritten += ((PGType) innerType).writeAsBinary(buffer, o);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// Fill in with -1 for up to max dimensions
|
||||
for (; i < dimensionsList.get(currentDimension - 1); i++) {
|
||||
buffer.writeInt(-1);
|
||||
bytesWritten += 4;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (Object o : array) {
|
||||
bytesWritten += writeArrayAsBinary(buffer, (List<Object>) o, dimensionsList, currentDimension + 1);
|
||||
}
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
private void readArrayAsBinary(ByteBuf buffer,
|
||||
final List<Object> array,
|
||||
final int[] dims,
|
||||
final int thisDimension)
|
||||
{
|
||||
if (thisDimension == dims.length - 1) {
|
||||
for (int i = 0; i < dims[thisDimension]; ++i) {
|
||||
int len = buffer.readInt();
|
||||
if (len == -1) {
|
||||
array.add(null);
|
||||
}
|
||||
else {
|
||||
array.add(innerType.readBinaryValue(buffer, len));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < dims[thisDimension]; ++i) {
|
||||
ArrayList<Object> list = new ArrayList<>(dims[thisDimension + 1]);
|
||||
array.add(list);
|
||||
readArrayAsBinary(buffer, list, dims, thisDimension + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean needDoubleQuoteAround(int typeOid, byte[] element)
|
||||
{
|
||||
// The array output routine will put double quotes around element values
|
||||
// if they are empty strings, contain curly braces, delimiter characters, double quotes, backslashes, or white space, or match the word NULL.
|
||||
// https://www.postgresql.org/docs/13/arrays.html#ARRAYS-IO
|
||||
for (byte b : element) {
|
||||
if (b == '{' || b == '}' || b == '"' || b == '\\' || b == ',' || b == '\t' || b == '\n' || b == '\r' || b == '\f' || b == ' ') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (VarcharType.OID == typeOid) {
|
||||
return element.length == 0 || new String(element, UTF_8).equalsIgnoreCase("NULL");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.airlift.log.Logger;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public abstract class PGType<T>
|
||||
{
|
||||
public enum Type
|
||||
{
|
||||
BASE("b"),
|
||||
COMPOSITE("c"),
|
||||
ENUM("e"),
|
||||
DOMAIN("d"),
|
||||
PSEUDO("p"),
|
||||
RANGE("r");
|
||||
|
||||
private final String code;
|
||||
|
||||
Type(String code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String code()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
public enum TypeCategory
|
||||
{
|
||||
ARRAY("A"),
|
||||
BOOLEAN("B"),
|
||||
COMPOSITE("C"),
|
||||
DATETIME("D"),
|
||||
GEOMETRIC("G"),
|
||||
NETWORK("I"),
|
||||
NUMERIC("N"),
|
||||
PSEUDO("P"),
|
||||
RANGE("R"),
|
||||
STRING("S"),
|
||||
TIMESPAN("T"),
|
||||
USER_DEFINED_TYPES("U"),
|
||||
BIT_STRING("V"),
|
||||
UNKNOWN("X");
|
||||
|
||||
private final String code;
|
||||
|
||||
TypeCategory(String code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String code()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
public static final int INT32_BYTE_SIZE = Integer.SIZE / 8;
|
||||
private static final Logger LOGGER = Logger.get(PGType.class);
|
||||
|
||||
private final int oid;
|
||||
private final int typeLen;
|
||||
private final int typeMod;
|
||||
private final String typName;
|
||||
|
||||
protected PGType(int oid, int typeLen, int typeMod, @Nonnull String typName)
|
||||
{
|
||||
this.oid = oid;
|
||||
this.typeLen = typeLen;
|
||||
this.typeMod = typeMod;
|
||||
this.typName = typName;
|
||||
}
|
||||
|
||||
public int oid()
|
||||
{
|
||||
return oid;
|
||||
}
|
||||
|
||||
public short typeLen()
|
||||
{
|
||||
return (short) typeLen;
|
||||
}
|
||||
|
||||
public abstract int typArray();
|
||||
|
||||
public int typeMod()
|
||||
{
|
||||
return typeMod;
|
||||
}
|
||||
|
||||
public String typName()
|
||||
{
|
||||
return typName;
|
||||
}
|
||||
|
||||
public String typInput()
|
||||
{
|
||||
if (typArray() == 0) {
|
||||
return "array_in";
|
||||
}
|
||||
return "any_in";
|
||||
}
|
||||
|
||||
public String typOutput()
|
||||
{
|
||||
if (typArray() == 0) {
|
||||
return "array_out";
|
||||
}
|
||||
return "any_out";
|
||||
}
|
||||
|
||||
public String typReceive()
|
||||
{
|
||||
if (typArray() == 0) {
|
||||
return "array_recv";
|
||||
}
|
||||
return "any_recv";
|
||||
}
|
||||
|
||||
public int typElem()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String typDelim()
|
||||
{
|
||||
return ",";
|
||||
}
|
||||
|
||||
public abstract String typeCategory();
|
||||
|
||||
public abstract String type();
|
||||
|
||||
/**
|
||||
* Write the value as text into the buffer.
|
||||
* <p>
|
||||
* Format:
|
||||
* <pre>
|
||||
* | int32 len (excluding len itself) | byte<b>N</b> value onto the buffer
|
||||
* </pre>
|
||||
*
|
||||
* @return the number of bytes written. (4 (int32) + N)
|
||||
*/
|
||||
public int writeAsText(ByteBuf buffer, @Nonnull T value)
|
||||
{
|
||||
byte[] bytes = encodeAsUTF8Text(value);
|
||||
buffer.writeInt(bytes.length);
|
||||
buffer.writeBytes(bytes);
|
||||
return INT32_BYTE_SIZE + bytes.length;
|
||||
}
|
||||
|
||||
public T readTextValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
byte[] bytes = new byte[valueLength];
|
||||
buffer.readBytes(bytes);
|
||||
try {
|
||||
return decodeUTF8Text(bytes);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
LOGGER.warn("decodeUTF8Text failed. input=%s type=%s",
|
||||
new String(bytes, StandardCharsets.UTF_8), typName);
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the value as binary into the buffer.
|
||||
* <p>
|
||||
* Format:
|
||||
* <pre>
|
||||
* | int32 len (excluding len itself) | byte<b>N</b> value onto the buffer
|
||||
* </pre>
|
||||
*
|
||||
* @return the number of bytes written. (4 (int32) + N)
|
||||
*/
|
||||
public abstract int writeAsBinary(ByteBuf buffer, @Nonnull T value);
|
||||
|
||||
public abstract T readBinaryValue(ByteBuf buffer, int valueLength);
|
||||
|
||||
/**
|
||||
* Return the UTF8 encoded text representation of the value
|
||||
*/
|
||||
public abstract byte[] encodeAsUTF8Text(@Nonnull T value);
|
||||
|
||||
/**
|
||||
* Convert a UTF8 encoded text representation into the actual value
|
||||
*/
|
||||
public abstract T decodeUTF8Text(byte[] bytes);
|
||||
|
||||
public abstract Object getEmptyValue();
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static io.wren.base.type.BooleanType.BOOLEAN;
|
||||
import static io.wren.base.type.HstoreType.HSTORE;
|
||||
import static io.wren.base.type.IntervalType.INTERVAL;
|
||||
import static io.wren.base.type.RealType.REAL;
|
||||
import static io.wren.base.type.TimestampType.TIMESTAMP;
|
||||
import static java.lang.String.format;
|
||||
|
||||
public final class PGTypes
|
||||
{
|
||||
private PGTypes() {}
|
||||
|
||||
private static final Map<Integer, PGType<?>> TYPE_TABLE = new HashMap<>();
|
||||
private static final Map<Integer, PGArray> INNER_TYPE_TO_ARRAY_TABLE;
|
||||
private static final Set<PGType<?>> TYPES;
|
||||
|
||||
private static final Map<String, PGType<?>> TYPE_NAME_TABLE = new HashMap<>();
|
||||
|
||||
static {
|
||||
TYPE_TABLE.put(BOOLEAN.oid(), BOOLEAN);
|
||||
TYPE_TABLE.put(TinyIntType.TINYINT.oid(), TinyIntType.TINYINT);
|
||||
TYPE_TABLE.put(SmallIntType.SMALLINT.oid(), SmallIntType.SMALLINT);
|
||||
TYPE_TABLE.put(IntegerType.INTEGER.oid(), IntegerType.INTEGER);
|
||||
TYPE_TABLE.put(BigIntType.BIGINT.oid(), BigIntType.BIGINT);
|
||||
TYPE_TABLE.put(REAL.oid(), REAL);
|
||||
TYPE_TABLE.put(DoubleType.DOUBLE.oid(), DoubleType.DOUBLE);
|
||||
TYPE_TABLE.put(NumericType.NUMERIC.oid(), NumericType.NUMERIC);
|
||||
TYPE_TABLE.put(VarcharType.VARCHAR.oid(), VarcharType.VARCHAR);
|
||||
TYPE_TABLE.put(CharType.CHAR.oid(), CharType.CHAR);
|
||||
TYPE_TABLE.put(JsonType.JSON.oid(), JsonType.JSON);
|
||||
TYPE_TABLE.put(TIMESTAMP.oid(), TIMESTAMP);
|
||||
TYPE_TABLE.put(TimestampWithTimeZoneType.TIMESTAMP_WITH_TIMEZONE.oid(), TimestampWithTimeZoneType.TIMESTAMP_WITH_TIMEZONE);
|
||||
TYPE_TABLE.put(VarcharType.TextType.TEXT.oid(), VarcharType.TextType.TEXT);
|
||||
TYPE_TABLE.put(VarcharType.NameType.NAME.oid(), VarcharType.NameType.NAME);
|
||||
TYPE_TABLE.put(OidType.OID_INSTANCE.oid(), OidType.OID_INSTANCE);
|
||||
TYPE_TABLE.put(DateType.DATE.oid(), DateType.DATE);
|
||||
TYPE_TABLE.put(ByteaType.BYTEA.oid(), ByteaType.BYTEA);
|
||||
TYPE_TABLE.put(BpCharType.BPCHAR.oid(), BpCharType.BPCHAR);
|
||||
// we handle all unspecified type as text type.
|
||||
TYPE_TABLE.put(0, VarcharType.TextType.TEXT);
|
||||
TYPE_TABLE.put(InetType.INET.oid(), InetType.INET);
|
||||
TYPE_TABLE.put(RecordType.EMPTY_RECORD.oid(), RecordType.EMPTY_RECORD);
|
||||
// Just need a fake instance to do type mapping. We never use the field.
|
||||
TYPE_TABLE.put(HSTORE.oid(), HSTORE);
|
||||
TYPE_TABLE.put(UuidType.UUID.oid(), UuidType.UUID);
|
||||
TYPE_TABLE.put(INTERVAL.oid(), INTERVAL);
|
||||
|
||||
ImmutableMap.Builder<Integer, PGArray> innerToPgTypeBuilder = ImmutableMap.builder();
|
||||
// initial collection types
|
||||
PGArray.allArray().forEach(array -> {
|
||||
PGType<?> innerType = array.getInnerType();
|
||||
TYPE_TABLE.put(array.oid(), array);
|
||||
innerToPgTypeBuilder.put(innerType.oid(), array);
|
||||
});
|
||||
|
||||
INNER_TYPE_TO_ARRAY_TABLE = innerToPgTypeBuilder.build();
|
||||
|
||||
TYPES = new HashSet<>(TYPE_TABLE.values());
|
||||
// the following polymorphic types are added manually,
|
||||
// because there are no corresponding data types in Cannerflow
|
||||
TYPES.add(AnyType.ANY);
|
||||
|
||||
TYPES.forEach(type -> TYPE_NAME_TABLE.put(type.typName().toUpperCase(Locale.ROOT), type));
|
||||
TYPE_NAME_TABLE.put("REAL", REAL);
|
||||
TYPE_NAME_TABLE.put("DOUBLE", DoubleType.DOUBLE);
|
||||
TYPE_NAME_TABLE.put("BOOLEAN", BOOLEAN);
|
||||
TYPE_NAME_TABLE.put("INTEGER", IntegerType.INTEGER);
|
||||
TYPE_NAME_TABLE.put("SMALLINT", SmallIntType.SMALLINT);
|
||||
TYPE_NAME_TABLE.put("BIGINT", BigIntType.BIGINT);
|
||||
TYPE_NAME_TABLE.put("STRING", VarcharType.VARCHAR);
|
||||
TYPE_NAME_TABLE.put("DECIMAL", NumericType.NUMERIC);
|
||||
}
|
||||
|
||||
public static Iterable<PGType<?>> pgTypes()
|
||||
{
|
||||
return TYPES;
|
||||
}
|
||||
|
||||
public static PGType<?> oidToPgType(int oid)
|
||||
{
|
||||
PGType<?> pgType = TYPE_TABLE.get(oid);
|
||||
if (pgType == null) {
|
||||
throw new IllegalArgumentException(
|
||||
format("No oid mapping from '%s' to pg_type", oid));
|
||||
}
|
||||
return pgType;
|
||||
}
|
||||
|
||||
public static PGType<?> getArrayType(int innerOid)
|
||||
{
|
||||
PGType<?> arrayType = INNER_TYPE_TO_ARRAY_TABLE.get(innerOid);
|
||||
if (arrayType == null) {
|
||||
throw new IllegalArgumentException(
|
||||
format("No array type mapping from '%s' to pg_type", innerOid));
|
||||
}
|
||||
return arrayType;
|
||||
}
|
||||
|
||||
public static Optional<PGType<?>> nameToPgType(String name)
|
||||
{
|
||||
return Optional.ofNullable(TYPE_NAME_TABLE.get(name.toUpperCase(Locale.ROOT)));
|
||||
}
|
||||
|
||||
public static PGType<?> toPgRecordArray(PGType<?> innerRecordType)
|
||||
{
|
||||
return new PGArray(2287, innerRecordType);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
public final class PgDatetimeUtils
|
||||
{
|
||||
private PgDatetimeUtils() {}
|
||||
|
||||
public static final int SECS_PER_DAY = 86400;
|
||||
// amount of seconds between 1970-01-01 and 2000-01-01
|
||||
public static final int EPOCH_DIFF_IN_SEC = 946_684_800;
|
||||
public static final long EPOCH_DIFF_IN_MS = EPOCH_DIFF_IN_SEC * 1000L;
|
||||
public static final int EPOCH_DIFF_IN_DAY = EPOCH_DIFF_IN_SEC / SECS_PER_DAY;
|
||||
|
||||
/**
|
||||
* Convert a trino date (unix timestamp in day) into a postgres date
|
||||
* (int days since 2000-01-01)
|
||||
*/
|
||||
public static int toPgDate(int unixTsInDay)
|
||||
{
|
||||
return (unixTsInDay - EPOCH_DIFF_IN_DAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a postgres date (days since 2000-01-01) into a trino
|
||||
* date (unix timestamp in days).
|
||||
*/
|
||||
public static int toTrinoDate(int daysSince2k)
|
||||
{
|
||||
return daysSince2k + EPOCH_DIFF_IN_DAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a presto timestamp (unix timestamp in ms) into a postgres timestamp
|
||||
* (long microseconds since 2000-01-01)
|
||||
*/
|
||||
public static long toPgTimestamp(long unixTsInMs)
|
||||
{
|
||||
return (unixTsInMs - EPOCH_DIFF_IN_MS) * 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a postgres timestamp (seconds since 2000-01-01) into a presto
|
||||
* timestamp (unix timestamp in ms).
|
||||
*/
|
||||
public static long toTrinoTimestamp(long microSecondsSince2k)
|
||||
{
|
||||
return (microSecondsSince2k / 1000) + EPOCH_DIFF_IN_MS;
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class PgOidVectorType
|
||||
extends PGType<List<Integer>>
|
||||
{
|
||||
public static final PgOidVectorType INSTANCE = new PgOidVectorType();
|
||||
private static final int OID = 30;
|
||||
|
||||
PgOidVectorType()
|
||||
{
|
||||
super(OID, -1, -1, "oidvector");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return 1013;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typElem()
|
||||
{
|
||||
return OidType.OID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.ARRAY.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public int writeAsBinary(ByteBuf buffer, List<Integer> value)
|
||||
{
|
||||
return PGArray.INT4_ARRAY.writeAsBinary(buffer, (List) value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public List<Integer> readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
return (List<Integer>) (List) PGArray.INT4_ARRAY.readBinaryValue(buffer, valueLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(List<Integer> value)
|
||||
{
|
||||
return Joiner.on(" ").join(value).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
String string = new String(bytes, StandardCharsets.UTF_8);
|
||||
return listFromOidVectorString(string);
|
||||
}
|
||||
|
||||
public static List<Integer> listFromOidVectorString(String value)
|
||||
{
|
||||
StringTokenizer tokenizer = new StringTokenizer(value, " ");
|
||||
ArrayList<Integer> oids = new ArrayList<>();
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
oids.add(Integer.parseInt(tokenizer.nextToken()));
|
||||
}
|
||||
return oids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static io.wren.base.type.BooleanType.BOOLEAN;
|
||||
import static io.wren.base.type.ByteaType.BYTEA;
|
||||
import static java.util.Locale.ENGLISH;
|
||||
|
||||
public final class PgTypeUtils
|
||||
{
|
||||
private PgTypeUtils() {}
|
||||
|
||||
private static final Map<String, PGType<?>> pgNameToTypeMap;
|
||||
|
||||
static {
|
||||
ImmutableMap.Builder<String, PGType<?>> pgNameToTypeMapBuilder = ImmutableMap.<String, PGType<?>>builder();
|
||||
|
||||
// typName
|
||||
Map<String, PGType<?>> typNameBuilder = ImmutableMap.<String, PGType<?>>builder()
|
||||
.put("bool", BOOLEAN)
|
||||
.put("int2", SmallIntType.SMALLINT)
|
||||
.put("int4", IntegerType.INTEGER)
|
||||
.put("int8", BigIntType.BIGINT)
|
||||
.put("float4", RealType.REAL)
|
||||
.put("float8", DoubleType.DOUBLE)
|
||||
.put("numeric", NumericType.NUMERIC)
|
||||
.put("varchar", VarcharType.VARCHAR)
|
||||
.put("char", CharType.CHAR)
|
||||
.put("date", DateType.DATE)
|
||||
.put("timestamp", TimestampType.TIMESTAMP)
|
||||
.put("json", JsonType.JSON)
|
||||
.put("bytea", BYTEA)
|
||||
.put("record", RecordType.EMPTY_RECORD)
|
||||
.put("interval", IntervalType.INTERVAL)
|
||||
.build();
|
||||
// TODO
|
||||
// .put("name", NAME)
|
||||
// .put("text", TEXT)
|
||||
// .put("inet", INET)
|
||||
// .put("uuid", UUID)
|
||||
// .put("timestamptz", TIMESTAMP_WITH_TIMEZONE)
|
||||
// .put("timestamp with time zone", TIMESTAMP_WITH_TIMEZONE)
|
||||
pgNameToTypeMapBuilder.putAll(typNameBuilder);
|
||||
|
||||
// alias name
|
||||
Map<String, PGType<?>> aliasNameBuilder = ImmutableMap.<String, PGType<?>>builder()
|
||||
.put("boolean", BOOLEAN)
|
||||
.put("smallint", SmallIntType.SMALLINT)
|
||||
.put("int", IntegerType.INTEGER)
|
||||
.put("integer", IntegerType.INTEGER)
|
||||
.put("bigint", BigIntType.BIGINT)
|
||||
.put("real", RealType.REAL)
|
||||
.put("decimal", NumericType.NUMERIC)
|
||||
.put("double precision", DoubleType.DOUBLE)
|
||||
.put("character", CharType.CHAR)
|
||||
.put("character varying", VarcharType.VARCHAR)
|
||||
.build();
|
||||
pgNameToTypeMapBuilder.putAll(aliasNameBuilder);
|
||||
|
||||
// array name
|
||||
typNameBuilder.forEach((name, type) -> {
|
||||
// TODO Support interval array
|
||||
if (type == IntervalType.INTERVAL) {
|
||||
return;
|
||||
}
|
||||
PGType<?> arrayType = PGTypes.getArrayType(type.oid());
|
||||
pgNameToTypeMapBuilder.put("_" + name, arrayType);
|
||||
pgNameToTypeMapBuilder.put(name + "[]", arrayType);
|
||||
pgNameToTypeMapBuilder.put(name + " array", arrayType);
|
||||
});
|
||||
|
||||
aliasNameBuilder.forEach((name, type) -> {
|
||||
PGType<?> arrayType = PGTypes.getArrayType(type.oid());
|
||||
pgNameToTypeMapBuilder.put(name + "[]", arrayType);
|
||||
pgNameToTypeMapBuilder.put(name + " array", arrayType);
|
||||
});
|
||||
|
||||
pgNameToTypeMap = pgNameToTypeMapBuilder.build();
|
||||
}
|
||||
|
||||
public static Optional<PGType<?>> pgNameToType(String name)
|
||||
{
|
||||
String lowerCaseName = name.toLowerCase(ENGLISH);
|
||||
return pgNameToTypeMap.containsKey(lowerCaseName) ? Optional.of(pgNameToTypeMap.get(lowerCaseName)) : Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
public class RealType
|
||||
extends PGType<Float>
|
||||
{
|
||||
public static final RealType REAL = new RealType();
|
||||
static final int OID = 700;
|
||||
|
||||
private static final int TYPE_LEN = 4;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
private RealType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "float4");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.FLOAT4_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.NUMERIC.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Float value)
|
||||
{
|
||||
buffer.writeInt(TYPE_LEN);
|
||||
buffer.writeFloat(value);
|
||||
return INT32_BYTE_SIZE + TYPE_LEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull Float value)
|
||||
{
|
||||
return Float.toString(value).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
checkArgument(valueLength == TYPE_LEN, "length should be %s because float is int32. Actual length: %s", TYPE_LEN, valueLength);
|
||||
return buffer.readFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return Float.parseFloat(new String(bytes, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import com.carrotsearch.hppc.ByteArrayList;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
|
||||
public class RecordType
|
||||
extends PGType<Map<String, Object>>
|
||||
{
|
||||
public static final RecordType EMPTY_RECORD = new RecordType(List.of());
|
||||
private static final int OID = 2249;
|
||||
private static final String NAME = "record";
|
||||
|
||||
private final List<PGType<?>> fieldTypes;
|
||||
|
||||
public RecordType(List<PGType<?>> fieldTypes)
|
||||
{
|
||||
super(OID, -1, -1, NAME);
|
||||
this.fieldTypes = fieldTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.EMPTY_RECORD_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.PSEUDO.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.PSEUDO.code();
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, Map<String, Object> record)
|
||||
{
|
||||
final int startWriterIndex = buffer.writerIndex();
|
||||
buffer.writeInt(0); // reserve space for the length of the record; updated later
|
||||
buffer.writeInt(fieldTypes.size());
|
||||
int bytesWritten = 4;
|
||||
List<Map.Entry<String, Object>> entries = record.entrySet().stream().collect(toImmutableList());
|
||||
for (int i = 0; i < fieldTypes.size(); i++) {
|
||||
PGType fieldType = fieldTypes.get(i);
|
||||
|
||||
buffer.writeInt(fieldType.oid());
|
||||
bytesWritten += 4;
|
||||
|
||||
Map.Entry<String, Object> entry = entries.get(i);
|
||||
if (entry.getValue() == null) {
|
||||
buffer.writeInt(-1); // -1 data length signals a NULL
|
||||
bytesWritten += 4;
|
||||
continue;
|
||||
}
|
||||
bytesWritten += fieldType.writeAsBinary(buffer, entry.getValue());
|
||||
}
|
||||
buffer.setInt(startWriterIndex, bytesWritten);
|
||||
return 4 + bytesWritten;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
throw new UnsupportedOperationException("Input of anonymous record type values is not implemented");
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(Map<String, Object> record)
|
||||
{
|
||||
ByteArrayList bytes = new ByteArrayList();
|
||||
// See PostgreSQL src/backend/utils/adt/rowtypes.c record_out(PG_FUNCTION_ARGS)
|
||||
bytes.add((byte) '(');
|
||||
List<Map.Entry<String, Object>> rows = record.entrySet().stream().collect(toImmutableList());
|
||||
for (int i = 0; i < record.size(); i++) {
|
||||
PGType fieldType = fieldTypes.get(i);
|
||||
Map.Entry<String, Object> row = rows.get(i);
|
||||
|
||||
if (i > 0) {
|
||||
bytes.add((byte) ',');
|
||||
}
|
||||
if (row.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] encodedValue = fieldType.encodeAsUTF8Text(row.getValue());
|
||||
boolean needQuotes = encodedValue.length == 0;
|
||||
for (byte b : encodedValue) {
|
||||
char c = (char) b;
|
||||
if (c == '"' || c == '\\' || c == '(' || c == ')' || c == ',' || Character.isWhitespace(c)) {
|
||||
needQuotes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needQuotes) {
|
||||
bytes.add((byte) '\"');
|
||||
}
|
||||
bytes.add(encodedValue);
|
||||
if (needQuotes) {
|
||||
bytes.add((byte) '\"');
|
||||
}
|
||||
}
|
||||
bytes.add((byte) ')');
|
||||
return bytes.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
throw new UnsupportedOperationException("Input of record type values is not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return Map.of();
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class RegprocType
|
||||
extends PGType<String>
|
||||
{
|
||||
public static final RegprocType REGPROC = new RegprocType();
|
||||
|
||||
private static final int OID = 24;
|
||||
// TODO: It's 4 in PostgreSQL because pg use oid to present this type but we use name.
|
||||
private static final int TYPE_LEN = -1;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
RegprocType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "regproc");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.REGPROC_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.NUMERIC.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull String value)
|
||||
{
|
||||
byte[] bytes = value.getBytes(UTF_8);
|
||||
buffer.writeInt(bytes.length);
|
||||
buffer.writeBytes(bytes);
|
||||
return INT32_BYTE_SIZE + bytes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsText(ByteBuf buffer, @Nonnull String value)
|
||||
{
|
||||
return writeAsBinary(buffer, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
byte[] utf8 = new byte[valueLength];
|
||||
buffer.readBytes(utf8);
|
||||
return new String(utf8, UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull String value)
|
||||
{
|
||||
return value.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return new String(bytes, UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static io.wren.base.Utils.checkArgument;
|
||||
|
||||
public class SmallIntType
|
||||
extends PGType<Short>
|
||||
{
|
||||
public static final SmallIntType SMALLINT = new SmallIntType();
|
||||
private static final int OID = 21;
|
||||
|
||||
private static final int TYPE_LEN = 2;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
private SmallIntType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "int2");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.INT2_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.NUMERIC.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Short value)
|
||||
{
|
||||
buffer.writeInt(TYPE_LEN);
|
||||
buffer.writeShort(value);
|
||||
return INT32_BYTE_SIZE + TYPE_LEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull Short value)
|
||||
{
|
||||
return Short.toString(value).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
checkArgument(valueLength == TYPE_LEN, "length should be %s because short is int16. Actual length: %s", TYPE_LEN, valueLength);
|
||||
return buffer.readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return Short.parseShort(new String(bytes, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.base.type;
|
||||
|
||||
public final class StandardTypes
|
||||
{
|
||||
public static final String BIGINT = "bigint";
|
||||
public static final String INTEGER = "integer";
|
||||
public static final String SMALLINT = "smallint";
|
||||
public static final String TINYINT = "tinyint";
|
||||
public static final String BOOLEAN = "boolean";
|
||||
public static final String DATE = "date";
|
||||
public static final String DECIMAL = "decimal";
|
||||
public static final String REAL = "real";
|
||||
public static final String DOUBLE = "double";
|
||||
public static final String HYPER_LOG_LOG = "HyperLogLog";
|
||||
public static final String QDIGEST = "qdigest";
|
||||
public static final String TDIGEST = "tdigest";
|
||||
public static final String P4_HYPER_LOG_LOG = "P4HyperLogLog";
|
||||
public static final String INTERVAL_DAY_TO_SECOND = "interval day to second";
|
||||
public static final String INTERVAL_YEAR_TO_MONTH = "interval year to month";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
public static final String TIMESTAMP_WITH_TIME_ZONE = "timestamp with time zone";
|
||||
public static final String TIME = "time";
|
||||
public static final String TIME_WITH_TIME_ZONE = "time with time zone";
|
||||
public static final String BYTEA = "bytea";
|
||||
public static final String VARCHAR = "varchar";
|
||||
public static final String CHAR = "char";
|
||||
public static final String ROW = "row";
|
||||
public static final String TEXT = "text";
|
||||
public static final String NAME = "name";
|
||||
public static final String ARRAY = "array";
|
||||
public static final String MAP = "map";
|
||||
public static final String JSON = "json";
|
||||
public static final String IPADDRESS = "ipaddress";
|
||||
public static final String GEOMETRY = "Geometry";
|
||||
public static final String BING_TILE = "BingTile";
|
||||
public static final String UUID = "uuid";
|
||||
|
||||
private StandardTypes() {}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.ResolverStyle;
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
|
||||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME;
|
||||
import static java.util.Locale.ENGLISH;
|
||||
|
||||
public class TimestampType
|
||||
extends BaseTimestampType
|
||||
{
|
||||
public static final PGType<?> TIMESTAMP = new TimestampType();
|
||||
|
||||
private static final int OID = 1114;
|
||||
private static final String NAME = "timestamp";
|
||||
|
||||
// TODO support timestamp with precision dynamically
|
||||
// BigQuery support precision with 6
|
||||
private static final DateTimeFormatter PG_TIMESTAMP = new DateTimeFormatterBuilder()
|
||||
.parseCaseInsensitive()
|
||||
.appendPattern("yyyy-MM-dd HH:mm:ss.SSSSSS")
|
||||
.toFormatter(ENGLISH)
|
||||
.withResolverStyle(ResolverStyle.STRICT);
|
||||
|
||||
private static final DateTimeFormatter PARSER_WITH_OPTIONAL_ERA = new DateTimeFormatterBuilder()
|
||||
.parseCaseInsensitive()
|
||||
.append(ISO_LOCAL_DATE)
|
||||
.optionalStart()
|
||||
.appendLiteral(' ')
|
||||
.append(ISO_LOCAL_TIME)
|
||||
.optionalStart()
|
||||
.appendPattern("[VV][x][xx][xxx]")
|
||||
.optionalStart()
|
||||
.appendLiteral(' ')
|
||||
.appendPattern("G")
|
||||
.toFormatter(Locale.ENGLISH).withResolverStyle(ResolverStyle.STRICT);
|
||||
|
||||
private TimestampType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.TIMESTAMP_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull Object value)
|
||||
{
|
||||
LocalDateTime dt = (LocalDateTime) value;
|
||||
return PG_TIMESTAMP.format(dt).getBytes(UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
String s = new String(bytes, UTF_8);
|
||||
LocalDateTime dt = LocalDateTime.parse(s, PARSER_WITH_OPTIONAL_ERA);
|
||||
return PG_TIMESTAMP.format(dt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Object value)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return "1970-01-01 00:00:00";
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.ResolverStyle;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
|
||||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME;
|
||||
import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
|
||||
|
||||
public class TimestampWithTimeZoneType
|
||||
extends BaseTimestampType
|
||||
{
|
||||
public static final PGType TIMESTAMP_WITH_TIMEZONE = new TimestampWithTimeZoneType();
|
||||
public static final DateTimeFormatter PG_TIMESTAMP = new DateTimeFormatterBuilder()
|
||||
.parseCaseInsensitive()
|
||||
.append(ISO_LOCAL_DATE)
|
||||
.optionalStart()
|
||||
.appendLiteral(' ')
|
||||
.append(ISO_LOCAL_TIME)
|
||||
.optionalStart()
|
||||
.appendLiteral(' ')
|
||||
.optionalEnd()
|
||||
.appendPattern("[VV][x][xx][xxx][z]")
|
||||
.toFormatter(Locale.ENGLISH).withResolverStyle(ResolverStyle.STRICT);
|
||||
|
||||
public static final org.joda.time.format.DateTimeFormatter ISO_FORMATTER =
|
||||
DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss.SSSZ").withLocale(Locale.ENGLISH);
|
||||
|
||||
private static final int OID = 1184;
|
||||
private static final String NAME = "timestamptz";
|
||||
|
||||
private TimestampWithTimeZoneType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.TIMESTAMP_WITH_TIMEZONE_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull Object value)
|
||||
{
|
||||
// TODO: consider AD, BC and dynamic fraction precision.
|
||||
if (value instanceof String) {
|
||||
return ((String) value).getBytes(UTF_8);
|
||||
}
|
||||
return ISO_FORMATTER.print(Instant.from((TemporalAccessor) value).getLong(MILLI_OF_SECOND))
|
||||
.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
String dtString = new String(bytes, UTF_8);
|
||||
ZonedDateTime zonedDateTime = tryParse(dtString);
|
||||
return ISO_FORMATTER.print(Instant.from(zonedDateTime).toEpochMilli());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ZonedDateTime tryParse(String timeString)
|
||||
{
|
||||
// Postgres TimestampTz format
|
||||
return ZonedDateTime.parse(timeString, PG_TIMESTAMP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Object value)
|
||||
{
|
||||
throw new UnsupportedOperationException("Not implemented yet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return "1970-01-01 00:00:00.000000+01:00";
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.wren.base.WrenException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static io.wren.base.metadata.StandardErrorCode.GENERIC_INTERNAL_ERROR;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* This class is write-only class. We never read any data by it and won't show it to pg_type list.
|
||||
* It is a wrap of SmallIntType. Actually, PostgreSQL doesn't have TinyInt.
|
||||
* To handle Presto TinyInt, we use this to transform Presto TinyInt to Pg SmallInt.
|
||||
*/
|
||||
public class TinyIntType
|
||||
extends PGType<Byte>
|
||||
{
|
||||
public static final TinyIntType TINYINT = new TinyIntType();
|
||||
private static final int OID = 21;
|
||||
|
||||
private static final int TYPE_LEN = 2;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
private TinyIntType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "int2");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.INT2_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.NUMERIC.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull Byte value)
|
||||
{
|
||||
buffer.writeInt(TYPE_LEN);
|
||||
buffer.writeShort(value);
|
||||
return INT32_BYTE_SIZE + TYPE_LEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull Byte value)
|
||||
{
|
||||
return Byte.toString(value).getBytes(UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
throw new WrenException(GENERIC_INTERNAL_ERROR, new IllegalAccessException("PostgreSQL doesn't have TinyIntType. We never read TinyInt from client."));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
throw new WrenException(GENERIC_INTERNAL_ERROR, new IllegalAccessException("PostgreSQL doesn't have TinyIntType. We never read TinyInt from client."));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class UuidType
|
||||
extends PGType<String>
|
||||
{
|
||||
public static final UuidType UUID = new UuidType();
|
||||
static final int OID = 2950;
|
||||
|
||||
private static final int TYPE_LEN = -1;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
private UuidType()
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "uuid");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return PGArray.UUID_ARRAY.oid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return PGType.TypeCategory.USER_DEFINED_TYPES.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return PGType.Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull String value)
|
||||
{
|
||||
byte[] bytes = encodeAsUTF8Text(value);
|
||||
buffer.writeInt(bytes.length);
|
||||
buffer.writeBytes(bytes);
|
||||
return INT32_BYTE_SIZE + bytes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull String value)
|
||||
{
|
||||
return value.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
byte[] bytes = new byte[valueLength];
|
||||
buffer.readBytes(bytes);
|
||||
return decodeUTF8Text(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return new String(bytes, UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class VarcharType
|
||||
extends PGType<String>
|
||||
{
|
||||
static final int OID = 1043;
|
||||
private static final int ARRAY_OID = 1015;
|
||||
private static final int TYPE_LEN = -1;
|
||||
private static final int TYPE_MOD = -1;
|
||||
|
||||
public static final VarcharType VARCHAR = new VarcharType(ARRAY_OID);
|
||||
|
||||
private final int typArray;
|
||||
|
||||
private VarcharType(int typArray)
|
||||
{
|
||||
super(OID, TYPE_LEN, TYPE_MOD, "varchar");
|
||||
this.typArray = typArray;
|
||||
}
|
||||
|
||||
private VarcharType(int oid, int typArray, int maxLength, String aliasName)
|
||||
{
|
||||
super(oid, maxLength, TYPE_MOD, aliasName);
|
||||
this.typArray = typArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typArray()
|
||||
{
|
||||
return typArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type()
|
||||
{
|
||||
return Type.BASE.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeCategory()
|
||||
{
|
||||
return TypeCategory.STRING.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsBinary(ByteBuf buffer, @Nonnull String value)
|
||||
{
|
||||
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
buffer.writeInt(bytes.length);
|
||||
buffer.writeBytes(bytes);
|
||||
return INT32_BYTE_SIZE + bytes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeAsText(ByteBuf buffer, @Nonnull String value)
|
||||
{
|
||||
return writeAsBinary(buffer, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeAsUTF8Text(@Nonnull String value)
|
||||
{
|
||||
return value.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readBinaryValue(ByteBuf buffer, int valueLength)
|
||||
{
|
||||
byte[] utf8 = new byte[valueLength];
|
||||
buffer.readBytes(utf8);
|
||||
return new String(utf8, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decodeUTF8Text(byte[] bytes)
|
||||
{
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEmptyValue()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public static class NameType
|
||||
{
|
||||
static final int OID = 19;
|
||||
private static final int ARRAY_OID = -1;
|
||||
private static final int TYPE_LEN = 64;
|
||||
|
||||
public static final VarcharType NAME = new VarcharType(OID, ARRAY_OID, TYPE_LEN, "name");
|
||||
}
|
||||
|
||||
public static class TextType
|
||||
{
|
||||
static final int OID = 25;
|
||||
static final int TEXT_ARRAY_OID = 1009;
|
||||
public static final VarcharType TEXT = new VarcharType(OID, TEXT_ARRAY_OID, -1, "text");
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type.parser;
|
||||
|
||||
import com.carrotsearch.hppc.ByteArrayList;
|
||||
import io.wren.base.type.parser.antlr.v4.PgArrayBaseVisitor;
|
||||
import io.wren.base.type.parser.antlr.v4.PgArrayParser;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Function;
|
||||
|
||||
class PgArrayASTVisitor
|
||||
extends PgArrayBaseVisitor<Object>
|
||||
{
|
||||
private final Function<byte[], Object> convert;
|
||||
|
||||
PgArrayASTVisitor(Function<byte[], Object> convert)
|
||||
{
|
||||
this.convert = convert;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitArray(PgArrayParser.ArrayContext ctx)
|
||||
{
|
||||
ArrayList<Object> array = new ArrayList<>();
|
||||
for (PgArrayParser.ItemContext value : ctx.item()) {
|
||||
array.add(value.accept(this));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitUnquotedString(PgArrayParser.UnquotedStringContext ctx)
|
||||
{
|
||||
String text = ctx.getText();
|
||||
return convert.apply(text.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitNull(PgArrayParser.NullContext ctx)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitQuotedString(PgArrayParser.QuotedStringContext ctx)
|
||||
{
|
||||
String text = ctx.getText();
|
||||
String withoutQuotes = text.substring(1, text.length() - 1);
|
||||
return convert.apply(removeEscapes(withoutQuotes.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bytes {@code byte[]} that represent an array's item.
|
||||
*/
|
||||
private static byte[] removeEscapes(byte[] bytes)
|
||||
{
|
||||
ByteArrayList itemBytes = new ByteArrayList(bytes.length);
|
||||
int end = bytes.length - 1;
|
||||
for (int i = 0; i <= end; i++) {
|
||||
char c = (char) bytes[i];
|
||||
if (i < end) {
|
||||
char next = (char) bytes[i + 1];
|
||||
if (c == '\\' && (next == '\\' || next == '\"')) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
itemBytes.add(bytes[i]);
|
||||
}
|
||||
return itemBytes.toArray();
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type.parser;
|
||||
|
||||
import io.wren.base.WrenException;
|
||||
import io.wren.base.metadata.StandardErrorCode;
|
||||
import io.wren.base.type.parser.antlr.v4.PgArrayLexer;
|
||||
import io.wren.base.type.parser.antlr.v4.PgArrayParser;
|
||||
import org.antlr.v4.runtime.BaseErrorListener;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||
import org.antlr.v4.runtime.misc.ParseCancellationException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class PgArrayParserWrapper
|
||||
{
|
||||
private static final BaseErrorListener ERROR_LISTENER = new BaseErrorListener()
|
||||
{
|
||||
@Override
|
||||
public void syntaxError(Recognizer<?, ?> recognizer,
|
||||
Object offendingSymbol,
|
||||
int line,
|
||||
int charPositionInLine,
|
||||
String message,
|
||||
RecognitionException e)
|
||||
{
|
||||
throw new WrenException(StandardErrorCode.GENERIC_INTERNAL_ERROR, e);
|
||||
}
|
||||
};
|
||||
|
||||
private static final PgArrayParserWrapper INSTANCE = new PgArrayParserWrapper();
|
||||
|
||||
public static Object parse(byte[] bytes, Function<byte[], Object> convert)
|
||||
{
|
||||
return INSTANCE.invokeParser(
|
||||
new ByteArrayInputStream(bytes),
|
||||
PgArrayParser::array,
|
||||
convert);
|
||||
}
|
||||
|
||||
private Object invokeParser(InputStream inputStream,
|
||||
Function<PgArrayParser, ParserRuleContext> parseFunction,
|
||||
Function<byte[], Object> convert)
|
||||
{
|
||||
try {
|
||||
PgArrayLexer lexer = new PgArrayLexer(CharStreams.fromStream(
|
||||
inputStream,
|
||||
StandardCharsets.UTF_8));
|
||||
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
|
||||
PgArrayParser parser = new PgArrayParser(tokenStream);
|
||||
|
||||
lexer.removeErrorListeners();
|
||||
lexer.addErrorListener(ERROR_LISTENER);
|
||||
|
||||
parser.removeErrorListeners();
|
||||
parser.addErrorListener(ERROR_LISTENER);
|
||||
|
||||
ParserRuleContext tree;
|
||||
try {
|
||||
// first, try parsing with potentially faster SLL mode
|
||||
parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
|
||||
tree = parseFunction.apply(parser);
|
||||
}
|
||||
catch (ParseCancellationException ex) {
|
||||
// if we fail, parse with LL mode
|
||||
tokenStream.seek(0); // rewind input stream
|
||||
parser.reset();
|
||||
|
||||
parser.getInterpreter().setPredictionMode(PredictionMode.LL);
|
||||
tree = parseFunction.apply(parser);
|
||||
}
|
||||
return tree.accept(new PgArrayASTVisitor(convert));
|
||||
}
|
||||
catch (StackOverflowError e) {
|
||||
throw new WrenException(StandardErrorCode.GENERIC_INTERNAL_ERROR, "stack overflow while parsing: " + e.getLocalizedMessage());
|
||||
}
|
||||
catch (IOException e) {
|
||||
return new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type.parser;
|
||||
|
||||
import io.wren.base.WrenException;
|
||||
import io.wren.base.metadata.StandardErrorCode;
|
||||
import io.wren.base.type.parser.antlr.v4.PgDateTimeFormatBaseVisitor;
|
||||
import io.wren.base.type.parser.antlr.v4.PgDateTimeFormatLexer;
|
||||
import io.wren.base.type.parser.antlr.v4.PgDateTimeFormatParser;
|
||||
import org.antlr.v4.runtime.BaseErrorListener;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* this parser adapts patterns from PostgreSQL to Joda.
|
||||
* <p>
|
||||
* for more info about patterns of PostgreSQL, refer to https://www.postgresql.org/docs/13/functions-formatting.html
|
||||
* for more info about patterns of Joda, refer to https://help.gooddata.com/cloudconnect/manual/date-and-time-format.html
|
||||
*/
|
||||
public final class PgDateTimeFormatParserWrapper
|
||||
{
|
||||
private PgDateTimeFormatParserWrapper() {}
|
||||
|
||||
private static final BaseErrorListener ERROR_LISTENER = new BaseErrorListener()
|
||||
{
|
||||
@Override
|
||||
public void syntaxError(Recognizer<?, ?> recognizer,
|
||||
Object offendingSymbol,
|
||||
int line,
|
||||
int charPositionInLine,
|
||||
String message,
|
||||
RecognitionException e)
|
||||
{
|
||||
throw new WrenException(StandardErrorCode.GENERIC_INTERNAL_ERROR, e);
|
||||
}
|
||||
};
|
||||
|
||||
public static String parse(String origin)
|
||||
{
|
||||
Lexer lexer = new PgDateTimeFormatLexer(CharStreams.fromString(origin));
|
||||
lexer.removeErrorListeners();
|
||||
lexer.addErrorListener(ERROR_LISTENER);
|
||||
|
||||
PgDateTimeFormatParser parser = new PgDateTimeFormatParser(new CommonTokenStream(lexer));
|
||||
parser.removeErrorListeners();
|
||||
parser.addErrorListener(ERROR_LISTENER);
|
||||
|
||||
ParseTree tree = parser.format();
|
||||
return new AstBuilder().visit(tree);
|
||||
}
|
||||
|
||||
private static class AstBuilder
|
||||
extends PgDateTimeFormatBaseVisitor<String>
|
||||
{
|
||||
@Override
|
||||
public String visitFormat(PgDateTimeFormatParser.FormatContext ctx)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
ctx.symbol().forEach(symbol -> builder.append(visit(symbol)));
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitSeparator(PgDateTimeFormatParser.SeparatorContext ctx)
|
||||
{
|
||||
return ctx.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitHourLiteral(PgDateTimeFormatParser.HourLiteralContext ctx)
|
||||
{
|
||||
if (ctx.getText().endsWith("24")) {
|
||||
return "HH";
|
||||
}
|
||||
return "hh";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitMinuteLiteral(PgDateTimeFormatParser.MinuteLiteralContext ctx)
|
||||
{
|
||||
return "mm";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitSecondLiteral(PgDateTimeFormatParser.SecondLiteralContext ctx)
|
||||
{
|
||||
return "ss";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitMilliSecondLiteral(PgDateTimeFormatParser.MilliSecondLiteralContext ctx)
|
||||
{
|
||||
return "SSS";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitMeridiemMarkerLiteral(PgDateTimeFormatParser.MeridiemMarkerLiteralContext ctx)
|
||||
{
|
||||
return "a";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitEraDesignatorLiteral(PgDateTimeFormatParser.EraDesignatorLiteralContext ctx)
|
||||
{
|
||||
return "G";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitTimeZoneLiteral(PgDateTimeFormatParser.TimeZoneLiteralContext ctx)
|
||||
{
|
||||
return "z";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitYearLiteral(PgDateTimeFormatParser.YearLiteralContext ctx)
|
||||
{
|
||||
return ctx.getText().toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitMonthLiteral(PgDateTimeFormatParser.MonthLiteralContext ctx)
|
||||
{
|
||||
String month = ctx.getText();
|
||||
if (month.equals("Month")) {
|
||||
return "MMMM";
|
||||
}
|
||||
if (month.equals("Mon")) {
|
||||
return "MMM";
|
||||
}
|
||||
return month.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitWeekLiteral(PgDateTimeFormatParser.WeekLiteralContext ctx)
|
||||
{
|
||||
return ctx.getText().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitDayLiteral(PgDateTimeFormatParser.DayLiteralContext ctx)
|
||||
{
|
||||
String day = ctx.getText();
|
||||
if (day.equals("Day")) {
|
||||
return "EEEE";
|
||||
}
|
||||
if (day.equals("Dy")) {
|
||||
return "EEE";
|
||||
}
|
||||
if (day.length() == 2) {
|
||||
return "dd";
|
||||
}
|
||||
if (day.length() == 1) {
|
||||
return "e";
|
||||
}
|
||||
return day.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type.parser;
|
||||
|
||||
import io.wren.base.WrenException;
|
||||
import io.wren.base.metadata.StandardErrorCode;
|
||||
import io.wren.base.type.parser.antlr.v4.PgNumericFormatBaseVisitor;
|
||||
import io.wren.base.type.parser.antlr.v4.PgNumericFormatLexer;
|
||||
import io.wren.base.type.parser.antlr.v4.PgNumericFormatParser;
|
||||
import org.antlr.v4.runtime.BaseErrorListener;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
|
||||
public final class PgNumericFormatParserWrapper
|
||||
{
|
||||
private PgNumericFormatParserWrapper() {}
|
||||
|
||||
private static final BaseErrorListener ERROR_LISTENER = new BaseErrorListener()
|
||||
{
|
||||
@Override
|
||||
public void syntaxError(Recognizer<?, ?> recognizer,
|
||||
Object offendingSymbol,
|
||||
int line,
|
||||
int charPositionInLine,
|
||||
String message,
|
||||
RecognitionException e)
|
||||
{
|
||||
throw new WrenException(StandardErrorCode.GENERIC_INTERNAL_ERROR, e);
|
||||
}
|
||||
};
|
||||
|
||||
public static String parse(String origin)
|
||||
{
|
||||
Lexer lexer = new PgNumericFormatLexer(CharStreams.fromString(origin));
|
||||
lexer.removeErrorListeners();
|
||||
lexer.addErrorListener(ERROR_LISTENER);
|
||||
|
||||
PgNumericFormatParser parser = new PgNumericFormatParser(new CommonTokenStream(lexer));
|
||||
parser.removeErrorListeners();
|
||||
parser.addErrorListener(ERROR_LISTENER);
|
||||
|
||||
ParseTree tree = parser.format();
|
||||
return new AstBuilder().visit(tree);
|
||||
}
|
||||
|
||||
private static class AstBuilder
|
||||
extends PgNumericFormatBaseVisitor<String>
|
||||
{
|
||||
@Override
|
||||
public String visitFormat(PgNumericFormatParser.FormatContext ctx)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
ctx.pattern().forEach(pattern -> builder.append(visit(pattern)));
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitDigitPattern(PgNumericFormatParser.DigitPatternContext ctx)
|
||||
{
|
||||
if (ctx.getText().equals("9")) {
|
||||
return "#";
|
||||
}
|
||||
return "0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitDecimalPointPattern(PgNumericFormatParser.DecimalPointPatternContext ctx)
|
||||
{
|
||||
return ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitGroupSeparatorPattern(PgNumericFormatParser.GroupSeparatorPatternContext ctx)
|
||||
{
|
||||
return ",";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitCurrencySymbolPattern(PgNumericFormatParser.CurrencySymbolPatternContext ctx)
|
||||
{
|
||||
return "\u00A4";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitExponentPattern(PgNumericFormatParser.ExponentPatternContext ctx)
|
||||
{
|
||||
return "E00";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitNonReservedPattern(PgNumericFormatParser.NonReservedPatternContext ctx)
|
||||
{
|
||||
throw new WrenException(StandardErrorCode.GENERIC_INTERNAL_ERROR, String.format("we didn't support the pattern %s.", ctx.getText()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.wireprotocol;
|
||||
|
||||
import io.wren.base.ConnectorRecordIterator;
|
||||
import io.wren.base.Parameter;
|
||||
import io.wren.base.client.Client;
|
||||
import io.wren.base.sql.SqlConverter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PgMetastore
|
||||
{
|
||||
void directDDL(String sql);
|
||||
|
||||
ConnectorRecordIterator directQuery(String sql, List<Parameter> parameters);
|
||||
|
||||
String handlePgType(String type);
|
||||
|
||||
String getPgCatalogName();
|
||||
|
||||
boolean isSchemaExist(String schemaName);
|
||||
|
||||
void dropTableIfExists(String name);
|
||||
|
||||
Client getClient();
|
||||
|
||||
SqlConverter getSqlConverter();
|
||||
|
||||
void close();
|
||||
}
|
||||
@@ -15,13 +15,11 @@
|
||||
package io.wren.base.sqlrewrite;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.trino.sql.parser.SqlParser;
|
||||
import io.wren.base.SessionContext;
|
||||
import io.wren.base.client.AutoCloseableIterator;
|
||||
import io.wren.base.client.duckdb.DuckDBConfig;
|
||||
import io.wren.base.client.duckdb.DuckDBSettingSQL;
|
||||
import io.wren.base.client.duckdb.DuckdbClient;
|
||||
import io.wren.base.client.duckdb.DuckdbS3StyleStorageConfig;
|
||||
import io.wren.base.dto.Column;
|
||||
import io.wren.base.dto.Manifest;
|
||||
import io.wren.base.dto.Model;
|
||||
@@ -38,7 +36,6 @@ import static io.wren.base.sqlrewrite.Utils.parseSql;
|
||||
|
||||
public abstract class AbstractTestFramework
|
||||
{
|
||||
private static final SqlParser SQL_PARSER = new SqlParser();
|
||||
public static final SessionContext DEFAULT_SESSION_CONTEXT =
|
||||
SessionContext.builder().setCatalog("wren").setSchema("test").build();
|
||||
private DuckdbClient duckdbClient;
|
||||
@@ -70,7 +67,7 @@ public abstract class AbstractTestFramework
|
||||
@BeforeClass
|
||||
public void init()
|
||||
{
|
||||
duckdbClient = new DuckdbClient(new DuckDBConfig(), new DuckdbS3StyleStorageConfig(), new DuckDBSettingSQL());
|
||||
duckdbClient = new DuckdbClient(new DuckDBConfig(), new DuckDBSettingSQL());
|
||||
prepareData();
|
||||
}
|
||||
|
||||
|
||||
-161
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.sqlrewrite.analyzer;
|
||||
|
||||
import io.wren.base.CatalogSchemaTableName;
|
||||
import io.wren.base.WrenMDL;
|
||||
import io.wren.base.WrenTypes;
|
||||
import io.wren.base.dto.Column;
|
||||
import io.wren.base.dto.Model;
|
||||
import io.wren.base.sqlrewrite.AbstractTestFramework;
|
||||
import io.wren.base.type.BigIntType;
|
||||
import io.wren.base.type.BooleanType;
|
||||
import io.wren.base.type.ByteaType;
|
||||
import io.wren.base.type.DateType;
|
||||
import io.wren.base.type.DoubleType;
|
||||
import io.wren.base.type.IntegerType;
|
||||
import io.wren.base.type.IntervalType;
|
||||
import io.wren.base.type.PGArray;
|
||||
import io.wren.base.type.RealType;
|
||||
import io.wren.base.type.RecordType;
|
||||
import io.wren.base.type.TimestampType;
|
||||
import io.wren.base.type.VarcharType;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static io.wren.base.sqlrewrite.Utils.parseExpression;
|
||||
import static io.wren.base.sqlrewrite.analyzer.ExpressionTypeAnalyzer.analyze;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestExpressionTypeAnalyzer
|
||||
extends AbstractTestFramework
|
||||
{
|
||||
private static final Scope EMPTY_SCOPE = Scope.builder().build();
|
||||
private static final WrenMDL EMPTY_MDL = WrenMDL.fromManifest(withDefaultCatalogSchema().build());
|
||||
|
||||
private final Model customer;
|
||||
|
||||
public TestExpressionTypeAnalyzer()
|
||||
{
|
||||
customer = Model.model("Customer",
|
||||
"select * from main.customer",
|
||||
List.of(
|
||||
Column.column("custkey", WrenTypes.INTEGER, null, true),
|
||||
Column.column("name", WrenTypes.VARCHAR, null, true),
|
||||
Column.column("address", WrenTypes.VARCHAR, null, true),
|
||||
Column.column("nationkey", WrenTypes.INTEGER, null, true),
|
||||
Column.column("phone", WrenTypes.VARCHAR, null, true),
|
||||
Column.column("acctbal", WrenTypes.INTEGER, null, true),
|
||||
Column.column("mktsegment", WrenTypes.VARCHAR, null, true),
|
||||
Column.column("comment", WrenTypes.VARCHAR, null, true)),
|
||||
"custkey");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiteral()
|
||||
{
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("1"))).isEqualTo(BigIntType.BIGINT);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("'abc'"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("INTERVAL '1 month'"))).isEqualTo(IntervalType.INTERVAL);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("NULL"))).isEqualTo(null);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("TIMESTAMP '2023-10-29 00:00:00.000000'"))).isEqualTo(TimestampType.TIMESTAMP);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("REAL '3.5'"))).isEqualTo(RealType.REAL);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("x'65683F'"))).isEqualTo(ByteaType.BYTEA);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("1.1"))).isEqualTo(DoubleType.DOUBLE);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("10.3e0"))).isEqualTo(DoubleType.DOUBLE);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("false"))).isEqualTo(BooleanType.BOOLEAN);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("cast(1.1 as DOUBLE)"))).isEqualTo(DoubleType.DOUBLE);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("ROW(1, 2e0)"))).isEqualTo(RecordType.EMPTY_RECORD);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("array[1,2,3]"))).isEqualTo(PGArray.INT8_ARRAY);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("array['a','b','c']"))).isEqualTo(PGArray.VARCHAR_ARRAY);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("current_user"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("current_schema"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("current_catalog"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("current_path"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("current_date"))).isEqualTo(DateType.DATE);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("current_time"))).isEqualTo(TimestampType.TIMESTAMP);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("current_timestamp"))).isEqualTo(TimestampType.TIMESTAMP);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("localtime"))).isEqualTo(TimestampType.TIMESTAMP);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("localtimestamp"))).isEqualTo(TimestampType.TIMESTAMP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPredicate()
|
||||
{
|
||||
assertPredicate("x > 1");
|
||||
assertPredicate("x >= 1");
|
||||
assertPredicate("x < 1");
|
||||
assertPredicate("x <= 1");
|
||||
assertPredicate("x = 1");
|
||||
assertPredicate("x <> y");
|
||||
assertPredicate("x != INTERVAL '1 month'");
|
||||
assertPredicate("x IS NULL");
|
||||
assertPredicate("x IS NOT NULL");
|
||||
assertPredicate("x in (1, 2, 3)");
|
||||
assertPredicate("x like 'abc'");
|
||||
assertPredicate("x between 1 and 2");
|
||||
assertPredicate("x > 1 and y < 2");
|
||||
assertPredicate("x > 1 or y < 2");
|
||||
assertPredicate("not x > 1");
|
||||
assertPredicate("exists (select 1)");
|
||||
}
|
||||
|
||||
private void assertPredicate(String expression)
|
||||
{
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression(expression))).isEqualTo(BooleanType.BOOLEAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunction()
|
||||
{
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("date_trunc('day', create_date)"))).isEqualTo(DateType.DATE);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("now()"))).isEqualTo(TimestampType.TIMESTAMP);
|
||||
assertThat(analyze(EMPTY_MDL, EMPTY_SCOPE, parseExpression("now___timestamp()"))).isEqualTo(TimestampType.TIMESTAMP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testColumns()
|
||||
{
|
||||
WrenMDL mdl = WrenMDL.fromManifest(withDefaultCatalogSchema().setModels(List.of(customer)).build());
|
||||
List<Field> fields = customer.getColumns().stream()
|
||||
.map(column -> Field.builder()
|
||||
.tableName(new CatalogSchemaTableName(mdl.getCatalog(), mdl.getSchema(), customer.getName()))
|
||||
.columnName(column.getName())
|
||||
.name(column.getName())
|
||||
.build())
|
||||
.collect(toImmutableList());
|
||||
Scope scope = Scope.builder().relationType(new RelationType(fields)).build();
|
||||
|
||||
assertThat(analyze(mdl, scope, parseExpression("custkey"))).isEqualTo(IntegerType.INTEGER);
|
||||
assertThat(analyze(mdl, scope, parseExpression("name"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(mdl, scope, parseExpression("address"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(mdl, scope, parseExpression("nationkey"))).isEqualTo(IntegerType.INTEGER);
|
||||
assertThat(analyze(mdl, scope, parseExpression("phone"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(mdl, scope, parseExpression("acctbal"))).isEqualTo(IntegerType.INTEGER);
|
||||
assertThat(analyze(mdl, scope, parseExpression("mktsegment"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(mdl, scope, parseExpression("comment"))).isEqualTo(VarcharType.VARCHAR);
|
||||
|
||||
assertThat(analyze(mdl, scope, parseExpression("Customer.custkey"))).isEqualTo(IntegerType.INTEGER);
|
||||
assertThat(analyze(mdl, scope, parseExpression("Customer.name"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(mdl, scope, parseExpression("Customer.address"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(mdl, scope, parseExpression("Customer.nationkey"))).isEqualTo(IntegerType.INTEGER);
|
||||
assertThat(analyze(mdl, scope, parseExpression("Customer.phone"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(mdl, scope, parseExpression("Customer.acctbal"))).isEqualTo(IntegerType.INTEGER);
|
||||
assertThat(analyze(mdl, scope, parseExpression("Customer.mktsegment"))).isEqualTo(VarcharType.VARCHAR);
|
||||
assertThat(analyze(mdl, scope, parseExpression("Customer.comment"))).isEqualTo(VarcharType.VARCHAR);
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.base.type;
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
|
||||
public class TestTimestampWithTimeZoneTypeParsing
|
||||
{
|
||||
@DataProvider
|
||||
public Object[][] timestampWithTimeZone()
|
||||
{
|
||||
return new Object[][] {
|
||||
{"2004-10-19 10:23:54 +02"},
|
||||
{"1999-01-08 04:05:06 PST"},
|
||||
{"1999-01-08 04:05:06 PST8PDT"},
|
||||
{"1999-01-08 04:05:06 zulu"},
|
||||
{"1999-01-08 04:05:06 z"},
|
||||
{"1999-01-08 04:05:06 America/New_York"},
|
||||
{"2004-10-19 10:23:54+02"},
|
||||
{"1999-01-08 04:05:06PST"},
|
||||
{"1999-01-08 04:05:06PST8PDT"},
|
||||
{"1999-01-08 04:05:06zulu"},
|
||||
{"1999-01-08 04:05:06z"},
|
||||
|
||||
// TODO: unsupported pg pattern
|
||||
// {"1999-01-08 04:05:06 -8:00"},
|
||||
// {"1999-01-08 04:05:06-8:00"},
|
||||
// {"1999-01-08 04:05:06 -8:00:00"},
|
||||
// {"1999-01-08 04:05:06 -8:00"},
|
||||
// {"1999-01-08 04:05:06 -800"},
|
||||
// {"1999-01-08 04:05:06-8:00:00"},
|
||||
// {"1999-01-08 04:05:06-8:00"},
|
||||
// {"1999-01-08 04:05:06-800"},
|
||||
// {"1999-01-08 04:05:06-8"},
|
||||
// {"1999-01-08 04:05:06 -8"},
|
||||
// {"1999-01-08 04:05:06Americ/New_York"},
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "timestampWithTimeZone")
|
||||
public void testParsing(String timestampString)
|
||||
{
|
||||
assertThatNoException()
|
||||
.isThrownBy(() -> TimestampWithTimeZoneType.PG_TIMESTAMP.parse(timestampString));
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.wren</groupId>
|
||||
<artifactId>wren-root</artifactId>
|
||||
<version>0.5.3-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>wren-cache</artifactId>
|
||||
<name>wren-cache</name>
|
||||
<description>WrenEngine - Cache</description>
|
||||
|
||||
<properties>
|
||||
<air.main.basedir>${project.parent.basedir}</air.main.basedir>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.airlift</groupId>
|
||||
<artifactId>concurrent</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.airlift</groupId>
|
||||
<artifactId>configuration</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.airlift</groupId>
|
||||
<artifactId>log</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.airlift</groupId>
|
||||
<artifactId>units</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.wren</groupId>
|
||||
<artifactId>trino-parser</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.wren</groupId>
|
||||
<artifactId>wren-base</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.duckdb</groupId>
|
||||
<artifactId>duckdb_jdbc</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.cache;
|
||||
|
||||
import io.wren.base.WrenException;
|
||||
import io.wren.base.dto.CacheInfo;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static io.wren.base.metadata.StandardErrorCode.GENERIC_USER_ERROR;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class CacheInfoPair
|
||||
{
|
||||
private final CacheInfo cacheInfo;
|
||||
private final Optional<String> tableName;
|
||||
private final Optional<String> errorMessage;
|
||||
private final long createTime;
|
||||
|
||||
protected CacheInfoPair(CacheInfo cacheInfo, String tableName, long createTime)
|
||||
{
|
||||
this(cacheInfo, Optional.of(tableName), Optional.empty(), createTime);
|
||||
}
|
||||
|
||||
protected CacheInfoPair(CacheInfo cacheInfo, Optional<String> tableName, Optional<String> errorMessage, long createTime)
|
||||
{
|
||||
this.cacheInfo = requireNonNull(cacheInfo, "cacheInfo is null");
|
||||
this.tableName = requireNonNull(tableName, "tableName is null");
|
||||
this.errorMessage = requireNonNull(errorMessage, "errorMessage is null");
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
public CacheInfo getCacheInfo()
|
||||
{
|
||||
return cacheInfo;
|
||||
}
|
||||
|
||||
public String getRequiredTableName()
|
||||
{
|
||||
return tableName.orElseThrow(() -> new WrenException(GENERIC_USER_ERROR, "Mapping table name is refreshing or not exists"));
|
||||
}
|
||||
|
||||
public Optional<String> getTableName()
|
||||
{
|
||||
return tableName;
|
||||
}
|
||||
|
||||
public Optional<String> getErrorMessage()
|
||||
{
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
public long getCreateTime()
|
||||
{
|
||||
return createTime;
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.cache;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.wren.base.AnalyzedMDL;
|
||||
import io.wren.base.CatalogSchemaTableName;
|
||||
import io.wren.base.ConnectorRecordIterator;
|
||||
import io.wren.base.Parameter;
|
||||
import io.wren.base.WrenException;
|
||||
import io.wren.base.dto.CacheInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static io.wren.base.metadata.StandardErrorCode.GENERIC_INTERNAL_ERROR;
|
||||
|
||||
public interface CacheManager
|
||||
{
|
||||
default ConnectorRecordIterator query(String sql, List<Parameter> parameters)
|
||||
{
|
||||
throw new WrenException(GENERIC_INTERNAL_ERROR, "Enable Wren Protocol to use this feature");
|
||||
}
|
||||
|
||||
default void removeCacheIfExist(String catalogName, String schemaName) {}
|
||||
|
||||
default void removeCacheIfExist(CatalogSchemaTableName catalogSchemaTableName) {}
|
||||
|
||||
default boolean cacheScheduledFutureExists(CatalogSchemaTableName catalogSchemaTableName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
default boolean retryScheduledFutureExists(CatalogSchemaTableName catalogSchemaTableName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
default CompletableFuture<List<TaskInfo>> createTask(AnalyzedMDL analyzedMDL)
|
||||
{
|
||||
return CompletableFuture.completedFuture(List.of());
|
||||
}
|
||||
|
||||
default CompletableFuture<TaskInfo> createTask(AnalyzedMDL analyzedMDL, CacheInfo cacheInfo)
|
||||
{
|
||||
throw new WrenException(GENERIC_INTERNAL_ERROR, "Enable Wren Protocol to use this feature");
|
||||
}
|
||||
|
||||
default CompletableFuture<List<TaskInfo>> listTaskInfo(String catalogName, String schemaName)
|
||||
{
|
||||
throw new WrenException(GENERIC_INTERNAL_ERROR, "Enable Wren Protocol to use this feature");
|
||||
}
|
||||
|
||||
default CompletableFuture<Optional<TaskInfo>> getTaskInfo(CatalogSchemaTableName catalogSchemaTableName)
|
||||
{
|
||||
throw new WrenException(GENERIC_INTERNAL_ERROR, "Enable Wren Protocol to use this feature");
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
default void untilTaskDone(CatalogSchemaTableName name) {}
|
||||
|
||||
default List<Object> getDuckDBSettings()
|
||||
{
|
||||
throw new WrenException(GENERIC_INTERNAL_ERROR, "Enable Wren Protocol to use this feature");
|
||||
}
|
||||
}
|
||||
@@ -1,434 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.cache;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.inject.Inject;
|
||||
import io.airlift.log.Logger;
|
||||
import io.trino.sql.parser.SqlParser;
|
||||
import io.trino.sql.tree.Statement;
|
||||
import io.wren.base.AnalyzedMDL;
|
||||
import io.wren.base.CatalogSchemaTableName;
|
||||
import io.wren.base.ConnectorRecordIterator;
|
||||
import io.wren.base.Parameter;
|
||||
import io.wren.base.SessionContext;
|
||||
import io.wren.base.WrenException;
|
||||
import io.wren.base.WrenMDL;
|
||||
import io.wren.base.client.duckdb.CacheStorageConfig;
|
||||
import io.wren.base.client.duckdb.DuckDBConfig;
|
||||
import io.wren.base.config.ConfigManager;
|
||||
import io.wren.base.dto.CacheInfo;
|
||||
import io.wren.base.sql.SqlConverter;
|
||||
import io.wren.base.sqlrewrite.WrenPlanner;
|
||||
import io.wren.base.wireprotocol.PgMetastore;
|
||||
import io.wren.cache.dto.CachedTable;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static io.airlift.concurrent.Threads.daemonThreadsNamed;
|
||||
import static io.airlift.concurrent.Threads.threadsNamed;
|
||||
import static io.trino.execution.sql.SqlFormatterUtil.getFormattedSql;
|
||||
import static io.wren.base.CatalogSchemaTableName.catalogSchemaTableName;
|
||||
import static io.wren.base.metadata.StandardErrorCode.EXCEEDED_GLOBAL_MEMORY_LIMIT;
|
||||
import static io.wren.base.metadata.StandardErrorCode.GENERIC_INTERNAL_ERROR;
|
||||
import static io.wren.base.metadata.StandardErrorCode.GENERIC_USER_ERROR;
|
||||
import static io.wren.base.sqlrewrite.Utils.parseSql;
|
||||
import static io.wren.cache.EventLogger.Level.ERROR;
|
||||
import static io.wren.cache.EventLogger.Level.INFO;
|
||||
import static io.wren.cache.TaskInfo.TaskStatus.DONE;
|
||||
import static io.wren.cache.TaskInfo.TaskStatus.QUEUED;
|
||||
import static io.wren.cache.TaskInfo.TaskStatus.RUNNING;
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.UUID.randomUUID;
|
||||
import static java.util.concurrent.CompletableFuture.supplyAsync;
|
||||
import static java.util.concurrent.Executors.newCachedThreadPool;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
public class CacheManagerImpl
|
||||
implements CacheManager, Closeable
|
||||
{
|
||||
private static final Logger LOG = Logger.get(CacheManager.class);
|
||||
private final ExtraRewriter extraRewriter;
|
||||
private final CacheService cacheService;
|
||||
private final SqlParser sqlParser;
|
||||
private final SqlConverter sqlConverter;
|
||||
private final PgMetastore pgMetastore;
|
||||
private final ConcurrentLinkedQueue<PathInfo> tempFileLocations = new ConcurrentLinkedQueue<>();
|
||||
private final CachedTableMapping cachedTableMapping;
|
||||
private final ConcurrentMap<CatalogSchemaTableName, ScheduledFuture<?>> cacheScheduledFutures = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<CatalogSchemaTableName, ScheduledFuture<?>> retryScheduledFutures = new ConcurrentHashMap<>();
|
||||
private final ScheduledThreadPoolExecutor refreshExecutor = new ScheduledThreadPoolExecutor(5, daemonThreadsNamed("cache-refresh-%s"));
|
||||
private final ScheduledThreadPoolExecutor retryExecutor = new ScheduledThreadPoolExecutor(5, daemonThreadsNamed("cache-retry-%s"));
|
||||
private final ExecutorService executorService = newCachedThreadPool(threadsNamed("cache-manager-%s"));
|
||||
private final ConcurrentHashMap<CatalogSchemaTableName, Task> tasks = new ConcurrentHashMap<>();
|
||||
private final EventLogger eventLogger;
|
||||
private final CacheTaskManager cacheTaskManager;
|
||||
private final ConfigManager configManager;
|
||||
|
||||
@Inject
|
||||
public CacheManagerImpl(
|
||||
SqlConverter sqlConverter,
|
||||
CacheService cacheService,
|
||||
ExtraRewriter extraRewriter,
|
||||
PgMetastore pgMetastore,
|
||||
CachedTableMapping cachedTableMapping,
|
||||
EventLogger eventLogger,
|
||||
CacheTaskManager cacheTaskManager,
|
||||
ConfigManager configManager)
|
||||
{
|
||||
this.sqlParser = new SqlParser();
|
||||
this.sqlConverter = requireNonNull(sqlConverter, "sqlConverter is null");
|
||||
this.cacheService = requireNonNull(cacheService, "cacheService is null");
|
||||
this.extraRewriter = requireNonNull(extraRewriter, "extraRewriter is null");
|
||||
this.pgMetastore = requireNonNull(pgMetastore, "pgMetastore is null");
|
||||
this.cacheTaskManager = requireNonNull(cacheTaskManager, "cacheTaskManager is null");
|
||||
this.cachedTableMapping = requireNonNull(cachedTableMapping, "cachedTableMapping is null");
|
||||
this.eventLogger = requireNonNull(eventLogger, "eventLogger is null");
|
||||
this.configManager = requireNonNull(configManager, "configManager is null");
|
||||
refreshExecutor.setRemoveOnCancelPolicy(true);
|
||||
}
|
||||
|
||||
private synchronized CompletableFuture<Void> refreshCache(AnalyzedMDL analyzedMDL, CacheInfo cacheInfo, TaskInfo taskInfo)
|
||||
{
|
||||
WrenMDL mdl = analyzedMDL.getWrenMDL();
|
||||
CatalogSchemaTableName catalogSchemaTableName = catalogSchemaTableName(mdl.getCatalog(), mdl.getSchema(), cacheInfo.getName());
|
||||
Optional<Task> taskOptional = Optional.ofNullable(tasks.get(catalogSchemaTableName));
|
||||
if (taskOptional.isPresent() && taskOptional.get().getTaskInfo().inProgress()) {
|
||||
throw new WrenException(GENERIC_USER_ERROR, format("cache is already running; catalogName: %s, schemaName: %s, tableName: %s", mdl.getCatalog(), mdl.getSchema(), cacheInfo.getName()));
|
||||
}
|
||||
removeCacheIfExist(catalogSchemaTableName);
|
||||
return doCache(analyzedMDL, cacheInfo, taskInfo);
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> handleCache(AnalyzedMDL analyzedMDL, CacheInfo cacheInfo, TaskInfo taskInfo)
|
||||
{
|
||||
WrenMDL mdl = analyzedMDL.getWrenMDL();
|
||||
CatalogSchemaTableName catalogSchemaTableName = new CatalogSchemaTableName(mdl.getCatalog(), mdl.getSchema(), cacheInfo.getName());
|
||||
String duckdbTableName = format("%s_%s", cacheInfo.getName(), randomUUID().toString().replace("-", ""));
|
||||
long createTime = currentTimeMillis();
|
||||
return refreshCache(analyzedMDL, cacheInfo, taskInfo)
|
||||
.thenRun(() -> {
|
||||
if (cacheInfo.getRefreshTime().toMillis() > 0) {
|
||||
cacheScheduledFutures.put(
|
||||
catalogSchemaTableName,
|
||||
refreshExecutor.scheduleWithFixedDelay(
|
||||
() -> createTask(analyzedMDL, cacheInfo).join(),
|
||||
cacheInfo.getRefreshTime().toMillis(),
|
||||
cacheInfo.getRefreshTime().toMillis(),
|
||||
MILLISECONDS));
|
||||
}
|
||||
})
|
||||
.exceptionally(e -> {
|
||||
String errMsg = format("Failed to do cache for cacheInfo %s; caused by %s", cacheInfo.getName(), e.getMessage());
|
||||
// If the cache fails because DuckDB doesn't have sufficient memory, we'll attempt to retry it later.
|
||||
if (e.getCause() instanceof WrenException && EXCEEDED_GLOBAL_MEMORY_LIMIT.toErrorCode().equals(((WrenException) e.getCause()).getErrorCode())) {
|
||||
long delay = configManager.getConfig(DuckDBConfig.class).getCacheTaskRetryDelay();
|
||||
retryScheduledFutures.put(
|
||||
catalogSchemaTableName,
|
||||
retryExecutor.schedule(
|
||||
() -> createTask(analyzedMDL, cacheInfo).join(),
|
||||
delay,
|
||||
SECONDS));
|
||||
errMsg += "; will retry after " + delay + " seconds";
|
||||
}
|
||||
pgMetastore.dropTableIfExists(duckdbTableName);
|
||||
LOG.error(e, errMsg);
|
||||
cachedTableMapping.putCachedTableMapping(catalogSchemaTableName, new CacheInfoPair(cacheInfo, Optional.empty(), Optional.of(errMsg), createTime));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectorRecordIterator query(String sql, List<Parameter> parameters)
|
||||
{
|
||||
return cacheTaskManager.addCacheQueryTask(() -> DuckdbRecordIterator.of(pgMetastore.getClient(), sql, parameters.stream().collect(toImmutableList())));
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> doCache(AnalyzedMDL analyzedMDL, CacheInfo cacheInfo, TaskInfo taskInfo)
|
||||
{
|
||||
WrenMDL mdl = analyzedMDL.getWrenMDL();
|
||||
CatalogSchemaTableName catalogSchemaTableName = new CatalogSchemaTableName(mdl.getCatalog(), mdl.getSchema(), cacheInfo.getName());
|
||||
String duckdbTableName = format("%s_%s", cacheInfo.getName(), randomUUID().toString().replace("-", ""));
|
||||
long createTime = currentTimeMillis();
|
||||
return cacheTaskManager.addCacheTask(() -> {
|
||||
cacheTaskManager.checkCacheMemoryLimit();
|
||||
taskInfo.setTaskStatus(RUNNING);
|
||||
SessionContext sessionContext = SessionContext.builder()
|
||||
.setCatalog(mdl.getCatalog())
|
||||
.setSchema(mdl.getSchema())
|
||||
.build();
|
||||
String wrenRewritten = WrenPlanner.rewrite(
|
||||
format("select * from %s", cacheInfo.getName()),
|
||||
sessionContext,
|
||||
analyzedMDL);
|
||||
Statement parsedStatement = parseSql(wrenRewritten);
|
||||
Statement rewrittenStatement = extraRewriter.rewrite(parsedStatement);
|
||||
|
||||
createCache(mdl, cacheInfo, sessionContext, rewrittenStatement, duckdbTableName);
|
||||
cachedTableMapping.putCachedTableMapping(catalogSchemaTableName, new CacheInfoPair(cacheInfo, duckdbTableName, createTime));
|
||||
});
|
||||
}
|
||||
|
||||
private void createCache(
|
||||
WrenMDL mdl,
|
||||
CacheInfo cacheInfo,
|
||||
SessionContext sessionContext,
|
||||
Statement rewrittenStatement,
|
||||
String duckdbTableName)
|
||||
{
|
||||
cacheService.createCache(
|
||||
mdl.getCatalog(),
|
||||
mdl.getSchema(),
|
||||
cacheInfo.getName(),
|
||||
sqlConverter.convert(getFormattedSql(rewrittenStatement, sqlParser), sessionContext))
|
||||
.ifPresent(pathInfo -> {
|
||||
try {
|
||||
tempFileLocations.add(pathInfo);
|
||||
refreshCacheInDuckDB(pathInfo.getPath() + "/" + pathInfo.getFilePattern(), duckdbTableName);
|
||||
}
|
||||
finally {
|
||||
removeTempFile(pathInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshCacheInDuckDB(String path, String tableName)
|
||||
{
|
||||
pgMetastore.directDDL(configManager.getConfig(CacheStorageConfig.class).generateDuckdbParquetStatement(path, tableName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCacheIfExist(String catalogName, String schemaName)
|
||||
{
|
||||
requireNonNull(catalogName, "catalogName is null");
|
||||
requireNonNull(schemaName, "schemaName is null");
|
||||
|
||||
cacheScheduledFutures.keySet().stream()
|
||||
.filter(catalogSchemaTableName -> catalogSchemaTableName.getCatalogName().equals(catalogName)
|
||||
&& catalogSchemaTableName.getSchemaTableName().getSchemaName().equals(schemaName))
|
||||
.forEach(catalogSchemaTableName -> {
|
||||
cacheScheduledFutures.get(catalogSchemaTableName).cancel(true);
|
||||
cacheScheduledFutures.remove(catalogSchemaTableName);
|
||||
});
|
||||
|
||||
retryScheduledFutures.keySet().stream()
|
||||
.filter(catalogSchemaTableName -> catalogSchemaTableName.getCatalogName().equals(catalogName)
|
||||
&& catalogSchemaTableName.getSchemaTableName().getSchemaName().equals(schemaName))
|
||||
.forEach(catalogSchemaTableName -> {
|
||||
retryScheduledFutures.get(catalogSchemaTableName).cancel(true);
|
||||
retryScheduledFutures.remove(catalogSchemaTableName);
|
||||
});
|
||||
|
||||
cachedTableMapping.entrySet().stream()
|
||||
.filter(entry -> entry.getKey().getCatalogName().equals(catalogName)
|
||||
&& entry.getKey().getSchemaTableName().getSchemaName().equals(schemaName))
|
||||
.forEach(entry -> {
|
||||
entry.getValue().getTableName().ifPresent(pgMetastore::dropTableIfExists);
|
||||
cachedTableMapping.remove(entry.getKey());
|
||||
});
|
||||
|
||||
tasks.keySet().stream()
|
||||
.filter(catalogSchemaTableName -> catalogSchemaTableName.getCatalogName().equals(catalogName)
|
||||
&& catalogSchemaTableName.getSchemaTableName().getSchemaName().equals(schemaName))
|
||||
.forEach(tasks::remove);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCacheIfExist(CatalogSchemaTableName catalogSchemaTableName)
|
||||
{
|
||||
if (cacheScheduledFutures.containsKey(catalogSchemaTableName)) {
|
||||
cacheScheduledFutures.get(catalogSchemaTableName).cancel(true);
|
||||
cacheScheduledFutures.remove(catalogSchemaTableName);
|
||||
}
|
||||
|
||||
if (retryScheduledFutures.containsKey(catalogSchemaTableName)) {
|
||||
retryScheduledFutures.get(catalogSchemaTableName).cancel(true);
|
||||
retryScheduledFutures.remove(catalogSchemaTableName);
|
||||
}
|
||||
|
||||
Optional.ofNullable(cachedTableMapping.get(catalogSchemaTableName)).ifPresent(cacheInfoPair -> {
|
||||
cacheInfoPair.getTableName().ifPresent(pgMetastore::dropTableIfExists);
|
||||
cachedTableMapping.remove(catalogSchemaTableName);
|
||||
});
|
||||
|
||||
Task task = tasks.remove(catalogSchemaTableName);
|
||||
if (task != null) {
|
||||
eventLogger.logEvent(INFO, "REMOVE_TASK", "Remove cache: " + catalogSchemaTableName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cacheScheduledFutureExists(CatalogSchemaTableName catalogSchemaTableName)
|
||||
{
|
||||
return cacheScheduledFutures.containsKey(catalogSchemaTableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retryScheduledFutureExists(CatalogSchemaTableName catalogSchemaTableName)
|
||||
{
|
||||
return retryScheduledFutures.containsKey(catalogSchemaTableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
refreshExecutor.shutdownNow();
|
||||
retryExecutor.shutdownNow();
|
||||
executorService.shutdownNow();
|
||||
cleanTempFiles();
|
||||
}
|
||||
|
||||
private void cleanTempFiles()
|
||||
{
|
||||
try {
|
||||
List<PathInfo> locations = ImmutableList.copyOf(tempFileLocations);
|
||||
locations.forEach(this::removeTempFile);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error(e, "Failed to clean temp file");
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTempFile(PathInfo pathInfo)
|
||||
{
|
||||
if (tempFileLocations.contains(pathInfo)) {
|
||||
cacheService.deleteTarget(pathInfo);
|
||||
tempFileLocations.remove(pathInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<TaskInfo>> createTask(AnalyzedMDL analyzedMDL)
|
||||
{
|
||||
return supplyAsync(() ->
|
||||
analyzedMDL.getWrenMDL().listCached().stream().map(cacheInfo -> createTask(analyzedMDL, cacheInfo).join()).collect(toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<TaskInfo> createTask(AnalyzedMDL analyzedMDL, CacheInfo cacheInfo)
|
||||
{
|
||||
return supplyAsync(() -> {
|
||||
WrenMDL mdl = analyzedMDL.getWrenMDL();
|
||||
CatalogSchemaTableName catalogSchemaTableName = new CatalogSchemaTableName(mdl.getCatalog(), mdl.getSchema(), cacheInfo.getName());
|
||||
TaskInfo taskInfo = new TaskInfo(mdl.getCatalog(), mdl.getSchema(), cacheInfo.getName(), QUEUED, Instant.now());
|
||||
// To fix flaky test, we pass value to tasks instead of a reference;
|
||||
Task task = new Task(TaskInfo.copyFrom(taskInfo), analyzedMDL, cacheInfo);
|
||||
tasks.put(catalogSchemaTableName, task);
|
||||
return taskInfo;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<TaskInfo>> listTaskInfo(String catalogName, String schemaName)
|
||||
{
|
||||
Predicate<TaskInfo> catalogNamePred = catalogName.isEmpty() ?
|
||||
(t) -> true :
|
||||
(t) -> catalogName.equals(t.getCatalogName());
|
||||
|
||||
Predicate<TaskInfo> schemaNamePred = schemaName.isEmpty() ?
|
||||
(t) -> true :
|
||||
(t) -> schemaName.equals(t.getSchemaName());
|
||||
|
||||
return supplyAsync(
|
||||
() -> tasks.values().stream()
|
||||
.map(Task::getTaskInfo)
|
||||
.filter(catalogNamePred.and(schemaNamePred))
|
||||
.collect(toList()),
|
||||
executorService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<TaskInfo>> getTaskInfo(CatalogSchemaTableName catalogSchemaTableName)
|
||||
{
|
||||
requireNonNull(catalogSchemaTableName);
|
||||
return supplyAsync(
|
||||
() -> Optional.ofNullable(tasks.get(catalogSchemaTableName)).map(Task::getTaskInfo),
|
||||
executorService);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public void untilTaskDone(CatalogSchemaTableName name)
|
||||
{
|
||||
Optional.ofNullable(tasks.get(name)).ifPresent(Task::waitUntilDone);
|
||||
}
|
||||
|
||||
public List<Object> getDuckDBSettings()
|
||||
{
|
||||
try (ConnectorRecordIterator iter = query("SELECT * FROM duckdb_settings()", List.of())) {
|
||||
return ImmutableList.copyOf(iter);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error(e, "Failed to get duckdb settings");
|
||||
throw new WrenException(GENERIC_INTERNAL_ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
private class Task
|
||||
{
|
||||
private final TaskInfo taskInfo;
|
||||
private final CompletableFuture<?> completableFuture;
|
||||
|
||||
public Task(TaskInfo taskInfo, AnalyzedMDL analyzedMDL, CacheInfo cacheInfo)
|
||||
{
|
||||
this.taskInfo = taskInfo;
|
||||
this.completableFuture = handleCache(analyzedMDL, cacheInfo, taskInfo)
|
||||
.thenRun(() -> {
|
||||
CacheInfoPair cacheInfoPair = cachedTableMapping.getCacheInfoPair(
|
||||
taskInfo.getCatalogName(),
|
||||
taskInfo.getSchemaName(), taskInfo.getTableName());
|
||||
taskInfo.setCachedTable(new CachedTable(
|
||||
cacheInfoPair.getCacheInfo().getName(),
|
||||
cacheInfoPair.getErrorMessage(),
|
||||
cacheInfoPair.getCacheInfo().getRefreshTime(),
|
||||
Instant.ofEpochMilli(cacheInfoPair.getCreateTime())));
|
||||
taskInfo.setTaskStatus(DONE);
|
||||
if (cacheInfoPair.getErrorMessage().isPresent()) {
|
||||
eventLogger.logEvent(ERROR, "CREATE_TASK", taskInfo);
|
||||
}
|
||||
else {
|
||||
eventLogger.logEvent(INFO, "CREATE_TASK", taskInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public TaskInfo getTaskInfo()
|
||||
{
|
||||
return taskInfo;
|
||||
}
|
||||
|
||||
public void waitUntilDone()
|
||||
{
|
||||
completableFuture.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.cache;
|
||||
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Scopes;
|
||||
import io.airlift.configuration.AbstractConfigurationAwareModule;
|
||||
|
||||
public class CacheModule
|
||||
extends AbstractConfigurationAwareModule
|
||||
{
|
||||
@Override
|
||||
protected void setup(Binder binder)
|
||||
{
|
||||
binder.bind(CacheManager.class).to(CacheManagerImpl.class).in(Scopes.SINGLETON);
|
||||
binder.bind(CacheTaskManager.class).in(Scopes.SINGLETON);
|
||||
binder.bind(EventLogger.class).to(Log4jEventLogger.class).in(Scopes.SINGLETON);
|
||||
binder.bind(CachedTableMapping.class).to(DefaultCachedTableMapping.class).in(Scopes.SINGLETON);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.cache;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface CacheService
|
||||
{
|
||||
Optional<PathInfo> createCache(String catalog, String schema, String name, String statement);
|
||||
|
||||
void deleteTarget(PathInfo pathInfo);
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.cache;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.inject.Inject;
|
||||
import io.wren.base.ConnectorRecordIterator;
|
||||
import io.wren.base.WrenException;
|
||||
import io.wren.base.client.duckdb.DuckDBConfig;
|
||||
import io.wren.base.wireprotocol.PgMetastore;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import static io.airlift.concurrent.Threads.threadsNamed;
|
||||
import static io.wren.base.client.duckdb.DuckdbUtil.convertDuckDBUnits;
|
||||
import static io.wren.base.metadata.StandardErrorCode.EXCEEDED_GLOBAL_MEMORY_LIMIT;
|
||||
import static io.wren.base.metadata.StandardErrorCode.EXCEEDED_TIME_LIMIT;
|
||||
import static io.wren.base.metadata.StandardErrorCode.GENERIC_INTERNAL_ERROR;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.concurrent.CompletableFuture.runAsync;
|
||||
import static java.util.concurrent.Executors.newFixedThreadPool;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
public class CacheTaskManager
|
||||
implements Closeable
|
||||
{
|
||||
private final PgMetastore pgMetastore;
|
||||
private final ExecutorService taskExecutorService;
|
||||
private final DuckDBConfig duckDBConfig;
|
||||
private final double cacheMemoryLimit;
|
||||
|
||||
@Inject
|
||||
public CacheTaskManager(DuckDBConfig duckDBConfig, PgMetastore pgMetastore)
|
||||
{
|
||||
this.duckDBConfig = requireNonNull(duckDBConfig, "duckDBConfig is null");
|
||||
this.pgMetastore = requireNonNull(pgMetastore, "pgMetastore is null");
|
||||
this.taskExecutorService = newFixedThreadPool(duckDBConfig.getMaxConcurrentTasks(), threadsNamed("duckdb-task-%s"));
|
||||
this.cacheMemoryLimit = duckDBConfig.getMaxCacheTableSizeRatio() * duckDBConfig.getMemoryLimit().toBytes();
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> addCacheTask(Runnable runnable)
|
||||
{
|
||||
return runAsync(runnable, taskExecutorService);
|
||||
}
|
||||
|
||||
public <T> T addCacheQueryTask(Callable<T> callable)
|
||||
{
|
||||
try {
|
||||
return taskExecutorService.submit(callable).get(duckDBConfig.getMaxCacheQueryTimeout(), SECONDS);
|
||||
}
|
||||
catch (TimeoutException e) {
|
||||
throw new WrenException(EXCEEDED_TIME_LIMIT, "Query time limit exceeded", e);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e) {
|
||||
throw new WrenException(GENERIC_INTERNAL_ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
// for canner use
|
||||
public void addCacheQueryDDLTask(Runnable runnable)
|
||||
{
|
||||
try {
|
||||
taskExecutorService.submit(runnable).get(duckDBConfig.getMaxCacheQueryTimeout(), SECONDS);
|
||||
}
|
||||
catch (TimeoutException e) {
|
||||
throw new WrenException(EXCEEDED_TIME_LIMIT, "Query time limit exceeded", e);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e) {
|
||||
throw new WrenException(GENERIC_INTERNAL_ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
public long getMemoryUsageBytes()
|
||||
{
|
||||
try (ConnectorRecordIterator result = pgMetastore.directQuery("SELECT memory_usage FROM pragma_database_size()", ImmutableList.of())) {
|
||||
Object[] row = result.next();
|
||||
return convertDuckDBUnits(row[0].toString()).toBytes();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new WrenException(GENERIC_INTERNAL_ERROR, "Failed to get memory usage", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkCacheMemoryLimit()
|
||||
{
|
||||
long usage = getMemoryUsageBytes();
|
||||
if (usage >= cacheMemoryLimit) {
|
||||
throw new WrenException(EXCEEDED_GLOBAL_MEMORY_LIMIT, "Cache memory limit exceeded. Usage: " + usage + " bytes, Limit: " + cacheMemoryLimit + " bytes");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
throws IOException
|
||||
{
|
||||
taskExecutorService.shutdownNow();
|
||||
pgMetastore.close();
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.cache;
|
||||
|
||||
import io.wren.base.CatalogSchemaTableName;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public interface CachedTableMapping
|
||||
{
|
||||
void putCachedTableMapping(CatalogSchemaTableName catalogSchemaTableName, CacheInfoPair cacheInfoPair);
|
||||
|
||||
CacheInfoPair get(CatalogSchemaTableName cachedTable);
|
||||
|
||||
void remove(CatalogSchemaTableName cachedTable);
|
||||
|
||||
CacheInfoPair getCacheInfoPair(String catalog, String schema, String table);
|
||||
|
||||
Optional<String> convertToCachedTable(CatalogSchemaTableName catalogSchemaTableName);
|
||||
|
||||
Set<Map.Entry<CatalogSchemaTableName, CacheInfoPair>> entrySet();
|
||||
|
||||
List<CacheInfoPair> getCacheInfoPairs(String catalogName, String schemaName);
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.cache;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.wren.base.CatalogSchemaTableName;
|
||||
import io.wren.base.wireprotocol.PgMetastore;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class DefaultCachedTableMapping
|
||||
implements CachedTableMapping
|
||||
{
|
||||
private final PgMetastore pgMetastore;
|
||||
private final ConcurrentMap<CatalogSchemaTableName, CacheInfoPair> cachedTableMapping = new ConcurrentHashMap<>();
|
||||
|
||||
@Inject
|
||||
public DefaultCachedTableMapping(PgMetastore pgMetastore)
|
||||
{
|
||||
this.pgMetastore = requireNonNull(pgMetastore, "duckdbClient is null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putCachedTableMapping(CatalogSchemaTableName catalogSchemaTableName, CacheInfoPair cacheInfoPair)
|
||||
{
|
||||
synchronized (cachedTableMapping) {
|
||||
if (cachedTableMapping.containsKey(catalogSchemaTableName)) {
|
||||
CacheInfoPair existedCacheInfoPair = cachedTableMapping.get(catalogSchemaTableName);
|
||||
if (existedCacheInfoPair.getCreateTime() > cacheInfoPair.getCreateTime()) {
|
||||
cacheInfoPair.getTableName().ifPresent(pgMetastore::dropTableIfExists);
|
||||
return;
|
||||
}
|
||||
existedCacheInfoPair.getTableName().ifPresent(pgMetastore::dropTableIfExists);
|
||||
}
|
||||
cachedTableMapping.put(catalogSchemaTableName, cacheInfoPair);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheInfoPair get(CatalogSchemaTableName cachedTable)
|
||||
{
|
||||
return cachedTableMapping.get(cachedTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(CatalogSchemaTableName cachedTable)
|
||||
{
|
||||
cachedTableMapping.remove(cachedTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheInfoPair getCacheInfoPair(String catalog, String schema, String table)
|
||||
{
|
||||
return cachedTableMapping.get(new CatalogSchemaTableName(catalog, schema, table));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> convertToCachedTable(CatalogSchemaTableName catalogSchemaTableName)
|
||||
{
|
||||
return Optional.ofNullable(cachedTableMapping.get(catalogSchemaTableName))
|
||||
.flatMap(CacheInfoPair::getTableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<CatalogSchemaTableName, CacheInfoPair>> entrySet()
|
||||
{
|
||||
return cachedTableMapping.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CacheInfoPair> getCacheInfoPairs(String catalogName, String schemaName)
|
||||
{
|
||||
requireNonNull(catalogName, "catalogName is null");
|
||||
requireNonNull(schemaName, "schemaName is null");
|
||||
return cachedTableMapping.entrySet()
|
||||
.stream()
|
||||
.filter(entry ->
|
||||
entry.getKey().getCatalogName().equals(catalogName)
|
||||
&& entry.getKey().getSchemaTableName().getSchemaName().equals(schemaName))
|
||||
.map(Map.Entry::getValue)
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.wren.cache;
|
||||
|
||||
public interface EventLogger
|
||||
{
|
||||
enum Level
|
||||
{
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR
|
||||
}
|
||||
|
||||
void logEvent(Level level, String eventName, TaskInfo event);
|
||||
|
||||
void logEvent(Level level, String eventName, String description);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.wren.cache;
|
||||
|
||||
import io.trino.sql.tree.Statement;
|
||||
|
||||
public interface ExtraRewriter
|
||||
{
|
||||
default Statement rewrite(Statement statement)
|
||||
{
|
||||
return statement;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user