├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ ├── other.md │ └── question.md ├── pull_request_template.md └── workflows │ ├── maven.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── CHANGELOG_LAGACY.md ├── CHANGLOG_CN.md ├── LICENSE ├── README.md ├── build.sh ├── eclipse-java-google-style.xml ├── example ├── pom.xml └── src │ └── main │ ├── java │ ├── DataDumpBenchmark.java │ ├── HikariCP.java │ ├── OdpsJdbcClient.java │ └── Pagination.java │ └── resources │ └── conf.properties.example ├── hooks └── pre-commit ├── intellij-java-google-style.xml ├── jdbc_test.sh ├── logback ├── logback-classic-1.2.3.jar ├── logback-core-1.2.3.jar └── logback.xml ├── mindmap-thumb.png ├── mindmap.pdf ├── pom.xml ├── secret.key.asc.enc ├── settings.xml └── src ├── main ├── java │ └── com │ │ └── aliyun │ │ └── odps │ │ └── jdbc │ │ ├── AbstractOdpsPreparedStatement.java │ │ ├── AcidTableUploader.java │ │ ├── BasicTableUploader.java │ │ ├── DataUploader.java │ │ ├── JdbcRunner.java │ │ ├── JdbcSessionTest.java │ │ ├── JdbcTest.java │ │ ├── OdpsAsyncPreparedStatement.java │ │ ├── OdpsAsyncStatement.java │ │ ├── OdpsCallableStatement.java │ │ ├── OdpsConnection.java │ │ ├── OdpsDatabaseMetaData.java │ │ ├── OdpsDriver.java │ │ ├── OdpsForwardResultSet.java │ │ ├── OdpsPreparedStatement.java │ │ ├── OdpsResultSet.java │ │ ├── OdpsResultSetMetaData.java │ │ ├── OdpsScollResultSet.java │ │ ├── OdpsSessionForwardResultSet.java │ │ ├── OdpsStatement.java │ │ ├── OdpsStaticResultSet.java │ │ ├── PublicMethodAspect.aj │ │ ├── WrapperAdapter.java │ │ └── utils │ │ ├── CalendarCache.java │ │ ├── ConnectionResource.java │ │ ├── InstanceDataIterator.java │ │ ├── JdbcColumn.java │ │ ├── LoggerFactory.java │ │ ├── OdpsFormatter.java │ │ ├── OdpsLogger.java │ │ ├── RecordConverterCache.java │ │ ├── SettingParser.java │ │ ├── TimeUtils.java │ │ ├── Utils.java │ │ └── transformer │ │ └── to │ │ ├── jdbc │ │ ├── AbstractToJdbcDateTypeTransformer.java │ │ ├── AbstractToJdbcTransformer.java │ │ ├── ToJdbcBigDecimalTransformer.java │ │ ├── ToJdbcBooleanTransformer.java │ │ ├── ToJdbcByteArrayTransformer.java │ │ ├── ToJdbcByteTransformer.java │ │ ├── ToJdbcDateTransformer.java │ │ ├── ToJdbcDoubleTransformer.java │ │ ├── ToJdbcFloatTransformer.java │ │ ├── ToJdbcIntTransformer.java │ │ ├── ToJdbcLongTransformer.java │ │ ├── ToJdbcShortTransformer.java │ │ ├── ToJdbcStringTransformer.java │ │ ├── ToJdbcTimeTransfomer.java │ │ ├── ToJdbcTimestampTransformer.java │ │ └── ToJdbcTransformerFactory.java │ │ └── odps │ │ ├── AbstractToOdpsTransformer.java │ │ ├── ToOdpsBigIntTransformer.java │ │ ├── ToOdpsBinaryTransformer.java │ │ ├── ToOdpsBooleanTransformer.java │ │ ├── ToOdpsCharTransformer.java │ │ ├── ToOdpsDateTransformer.java │ │ ├── ToOdpsDatetimeTransformer.java │ │ ├── ToOdpsDecimalTransformer.java │ │ ├── ToOdpsDoubleTransformer.java │ │ ├── ToOdpsFloatTransformer.java │ │ ├── ToOdpsIntTransformer.java │ │ ├── ToOdpsJsonTransformer.java │ │ ├── ToOdpsSmallintTransformer.java │ │ ├── ToOdpsStringTransformer.java │ │ ├── ToOdpsTimeStampTransformer.java │ │ ├── ToOdpsTinyintTransformer.java │ │ ├── ToOdpsTransformerFactory.java │ │ └── ToOdpsVarcharTransformer.java └── resources │ ├── META-INF │ └── services │ │ └── java.sql.Driver │ └── maxcompute-version.properties └── test ├── java └── com │ └── aliyun │ └── odps │ └── jdbc │ ├── ImplicitTypeConversionTest.java │ ├── InstanceTunnelTest.java │ ├── MaxQATest.java │ ├── OdpsDatabaseMetaDataTest.java │ ├── OdpsJdbcDateTest.java │ ├── OdpsJdbcDateTimeTest.java │ ├── OdpsNewTypeTest.java │ ├── OdpsPreparedStatementTest.java │ ├── OdpsResultSetMetaDataTest.java │ ├── OdpsResultSetTest.java │ ├── OdpsScollResultSetTest.java │ ├── OdpsSessionScollResultSetTest.java │ ├── OdpsSessionStatementTest.java │ ├── OdpsStatementCommandApiTest.java │ ├── OdpsStatementTest.java │ ├── SpecialSQLTest.java │ ├── TestManager.java │ ├── TimezoneTest.java │ ├── WarningLogTest.java │ └── utils │ ├── ConnectionResourceTest.java │ ├── JdbcTransformerTest.java │ ├── SettingParserTest.java │ ├── TestUtils.java │ └── UtilsTest.java └── resources ├── conf.properties.example ├── logback.xml └── odps_config.ini /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: cornmonster, lyman 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | To help us reproducing this bug, please provide information below: 15 | 1. Your Java version 16 | 2. Your MaxCompute JDBC Driver version 17 | 3. Full stack of the error. 18 | 4. Minimized code to reproduce the error. 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: cornmonster, lyman 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other 3 | about: Submit other issues here. 4 | title: "[OTHER]" 5 | labels: '' 6 | assignees: lyman, cornmonster 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: A question about MaxCompute JDBC Driver 4 | title: "[QUESTION]" 5 | labels: question 6 | assignees: cornmonster, lyman 7 | 8 | --- 9 | 10 | A clear and concise description of what the problem is. 11 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## What do these changes do? 2 | 3 | 4 | 5 | ## Related issue number 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Java CI with Maven 5 | 6 | on: 7 | push: 8 | pull_request: 9 | branches: [ master ] 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up JDK 11 17 | uses: actions/setup-java@v2 18 | with: 19 | java-version: '8' 20 | distribution: 'adopt' 21 | - name: Build with Maven 22 | run: mvn -U -B package --file pom.xml -DskipTests 23 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | jobs: 12 | release: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check out Git repository 16 | uses: actions/checkout@v2 17 | 18 | - name: Install Java and Maven 19 | uses: actions/setup-java@v1 20 | with: 21 | java-version: 8 22 | 23 | - name: Setup gpg 24 | run: | 25 | mkdir ~/.gnupg 26 | echo use-agent >> ~/.gnupg/gpg.conf 27 | echo pinentry-mode loopback >> ~/.gnupg/gpg.conf 28 | echo allow-loopback-pinentry >> ~/.gnupg/gpg-agent.conf 29 | 30 | - name: Release Maven package 31 | uses: samuelmeuli/action-maven-publish@v1 32 | with: 33 | gpg_private_key: ${{ secrets.gpg_private_key }} 34 | gpg_passphrase: ${{ secrets.gpg_passphrase }} 35 | nexus_username: ${{ secrets.nexus_token_username }} 36 | nexus_password: ${{ secrets.nexus_token_password }} 37 | server_id: ossrh 38 | maven_args: -DskipTests -X 39 | 40 | - name: Publish to GitHub Packages 41 | run: mvn clean package -DskipTests 42 | 43 | - name: Upload Release 44 | uses: ncipollo/release-action@v1 45 | with: 46 | allowUpdates: true 47 | omitName: true 48 | artifacts: 'target/odps-jdbc-*.jar' 49 | token: ${{ secrets.RELEASE_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /bin 3 | /.classpath 4 | /.project 5 | /.settings 6 | 7 | # Intellij Idea 8 | .idea 9 | *.iml 10 | *.class 11 | 12 | 13 | /example/target 14 | /example/src/main/resources/* 15 | !/example/src/main/resources/conf.properties.example 16 | 17 | /src/test/resources/* 18 | !/src/test/resources/conf.properties.example 19 | 20 | logback/jdbc.log 21 | test.sh 22 | -------------------------------------------------------------------------------- /CHANGELOG_LAGACY.md: -------------------------------------------------------------------------------- 1 | ### v2.4 2 | 3 | 1. fallback to limited mode instance tunnel if project is protection 4 | 1. fix bug when sql statement contains semi-colon 5 | 1. fix bug of no column info when accessing table from other project 6 | 7 | ### v2.3.1 (2018-02-17) 8 | 9 | 1. update odps sdk and fastjson for security reason 10 | 11 | ### v1.9.2 (2018-02-17) 12 | 13 | 1. update odps sdk and fastjson for security reason 14 | 1. add test tool `jdbc_test.sh` 15 | 16 | ### v2.3 (2018-01-22) 17 | 18 | 1. support customizing tunnel endpoint 19 | 1. add test tool `jdbc_test.sh` with log conf sample 20 | 21 | ### v2.2 (2017-02-24) 22 | 23 | 1. Support Hive data type. 24 | 25 | ### v1.9.1 (2016-11-11) 26 | 27 | 1. Minor update to support hive proxy. 28 | 29 | ### v2.0beta (2016-08-25) 30 | 31 | 1. Support passing `logconffile` to customize logging 32 | 2. Support passing `odps_config` with a file to specify JDBC configuration in order to avoid encoding/decoding the params which contains special characters. If `odps_config` is passed , neither `URL` nor `PROP` config will be ignored. 33 | 3. Unify the configuration effects of both `URL_KEY` and `PROP_KEY`.`PROP_KEY`'s priority would be greater than `URL_KEY`'s when they both exist. 34 | 4. `executeUpdate` will return non-negative value. 35 | 5. Add `synchronized` to some methods to protect the safety of concurrent calling. 36 | 37 | ### v2.0beta (2016-06-15) 38 | 39 | 1. Use instance tunnel instead of table tunnel, so that temporary table is no longer created. Demands MaxCompute service >= Sprint27. 40 | 41 | ### v1.9 (2016-06-14) 42 | 43 | 1. Support set sql properties in `statement.execute()`. 44 | 45 | ### v1.8 (2016-03-25) 46 | 47 | 1. AutoCommit default is true, and always is true. 48 | 49 | ### v1.7 (2016-03-18) 50 | 51 | 1. Log for SQLFeatureNotSupportedException on the critical path. 52 | 1. Refactor the unitest 53 | 54 | ### v1.6 (2015-12-31) 55 | 56 | 1. Put logview in the warning chain to let be captured in **SQLWorkbench/j**. 57 | 1. Remove the hardcode when retrieve version 58 | 1. Clear javadoc warnings. 59 | 1. Put all the utils to `/utils` folder. 60 | 61 | ### v1.5 (2015-12-30) 62 | 63 | 1. Set temp table lifecycle in create SQL (eliminate the round trip cost). 64 | 1. Accept `VALUES(?, ?, ?)` in prepareStatement. 65 | 1. Reuse one tunnel upload session in `PrepareStatement.executeBatch()`. Commit the session on the close of the PrepareStatement. 66 | 1. Catch the NPE of `instance.getTaskStatus().get("SQL")`. 67 | 1. Support `supportsSchemasInTableDefinitions` to adpat to **Kettle**. 68 | 69 | ### v1.4 (2015-12-22) 70 | 71 | 1. Support `isValid()` to adpat to **HikariCP**. 72 | 1. Support `setReadOnly()` to adapt to **HikariCP** (only allow for setting it to false). 73 | 1. Add an example to use **HikariCP**. 74 | 75 | ### v1.3 (2015-12-07) 76 | 77 | 1. Support `supportsResultSetType()` to adapt to **Pentaho**. 78 | 1. Support `PreparedStatement.setBytes()`. 79 | 1. Support `getMaxTableNameLength()` to adapt to **SQL Workbench/J**. 80 | 1. Support `getIdentifierQuoteString()` to adapt to **SQL Workbench/J**. 81 | 1. Fix the `getUpdateCount()` bug. See Issue \#15. 82 | 83 | ### v1.2 (2015-11-29) 84 | 85 | 1. Support batch insert in PreparedStatement. 86 | 1. Add an example to migrate data using batch functionality. The example splits the source and uses more than 1 thread to upload. 87 | 1. `DatabaseMetaData.getColumns()` Support accoss schema query. 88 | 1. Per-connection logger bug fixed. 89 | 1. Display uuid in loggers' console handler. 90 | 1. Use travis to deploy. 91 | 1. `user_agent` looks like odps-jdbc-1.1. 92 | 1. Rename OdpsQueryResultSet to ScollResultSet. 93 | 1. Use project name in log string. 94 | 1. Throw NPE if `instance.getStatus() == null`. 95 | 96 | 97 | ### v1.1 (2015-11-17) 98 | 99 | 1. SQLWorkbench's wbcopy command supported. 100 | 1. Compress data when downloading the result set. 101 | 1. No longer use log4j. 102 | 1. `Driver.getParentLogger()` supported. 103 | 1. Per-connection logger (support two connections with different log level.) 104 | 1. More debug logs for profiling. 105 | 1. Change the driver name to 'ODPS'. 106 | 1. Optimize the performance of getting tables. 107 | 1. No longer supports the concept of catalog (all related functions return null). 108 | -------------------------------------------------------------------------------- /CHANGLOG_CN.md: -------------------------------------------------------------------------------- 1 | # 更新日志 2 | 3 | ## [3.8.5] - 2025-03-18 4 | ### 改进 5 | - **依赖升级** 6 | 升级 `odps-sdk` 版本到 `0.51.11-public`。新版本 SDK 针对 `MCQA 2.0 ResultDescriptor` 实体进行了重构,以确保与 MCQA 2.0 的兼容性。**强烈建议 MCQA 2.0 用户升级到该版本**。详情请参考 [SDK 更新日志](https://github.com/aliyun/aliyun-odps-java-sdk/releases/tag/v0.51.11-public)。 7 | 8 | --- 9 | 10 | ## [3.8.3] - 2025-02-11 11 | ### 优化 12 | - **SQL 设置提取** 13 | 使用状态机替代正则表达式进行 SQL 设置提取,提升解析准确性,规避了因 JDBC 侧 SQL 重写导致的执行失败问题。 14 | - **依赖升级** 15 | 升级 `odps-sdk` 版本到 `0.51.6-public`。 16 | 17 | ---- 18 | 19 | ## [3.8.2] - 2025-01-15 20 | ### 功能 21 | - **执行模式** 22 | 在 `OdpsStatement` 类中新增 `getExecuteMode` 方法,用于获取当前执行模式(`INTERACTIVE`、`INTERACTIVE_V2` 或 `OFFLINE`)。 23 | - **依赖升级** 24 | 升级 `odps-sdk` 版本到 `0.51.5-public`。 25 | ---- 26 | ## [3.8.1] - 2024-11-27 27 | ### 功能 28 | - **Logview** 新增对 Logview V2 的支持,V2 版本保障了数据安全,更多信息参考 [2024年11月14日-MaxCompute Logview安全升级](https://help.aliyun.com/zh/maxcompute/product-overview/2024-service-notices) 。通过url参数 `logviewVersion` 方法指定。 29 | ---- 30 | 31 | ## [3.8.0] - 2024-10-9 32 | **欢迎进入 MCQA 2.0 时代!** 33 | 34 | ### 新功能 35 | - 支持提交 MCQA 2.0 作业。通过指定 `quotaName` 为交互式 quota,并开启 `interactiveMode=true` 即可开启 MCQA 2.0 模式。 36 | - 新增参数 `useInstanceTunnel`,用于指定是否使用 Tunnel 来获取数据,默认值为 true,与原行为一致。 37 | 38 | 如关闭 instanceTunnel,则会使用 Restful API 来获取数据,在这个模式下 39 | - 执行速度会更快。 40 | - 只能返回 10000 条结果,如超出这个限制,数据会被截断。 41 | - 返回值均为String类型,仅适合屏显型作业。 42 | - 新增 `JdbcRunner` 可执行类,可以通过链接串和SQL文件,执行JDBC任务。使用方式如下 43 | ```bash 44 | java -cp odps-jdbc-version-jar-with-dependencies.jar com.aliyun.odps.jdbc.JdbcRunner 45 | ``` 46 | - 新增一些调试用 settings,这些 setting 会修改当前 `Connection` 的配置(相当于重新获取一次 Connection 并替代当前的 Connection)。 47 | - `set tunnelEndpoint = xxx;` 修改当前 Connection 的 tunnelEndpoint 参数 48 | - `set useTunnel = true/false;` 修改当前 Connection 的 useInstanceTunnel 参数 49 | - `set interactiveMode = true/false;` 修改当前 Connection 的 interactiveMode 参数 50 | 51 | ### 变更 52 | - 移除 removeComment 方法中的 # 注释处理:现已不再去除 # 后的注释,这一改动解决了许多正常 SQL 语句因 # 而被误删的问题。 53 | - 更新 odps-sdk 版本到 [0.50.0-public](https://github.com/aliyun/aliyun-odps-java-sdk/blob/release/0.50.x/CHANGELOG_CN.md#0500-public---2024-10-09) 54 | 55 | 56 | ## [3.7.0] - 2024-08-29 57 | **兼容 Metabase!** 58 | 59 | [了解如何连接 MaxCompute 与 Metabase](https://help.aliyun.com/zh/maxcompute/user-guide/connect-metabase-to-maxcompute) 60 | 61 | ### 重要变更 62 | - **优化 `DatabaseMetadata.getColumns` 逻辑**:增强了三层模型和两层模型使用者的兼容性,具体如下: 63 | - **三层模型(project.schema.table)**: 64 | - 忽略传入的 `catalog`,使用 JDBC 链接中的项目名,以及参数中的 `schemaPattern` 和 `tableNamePattern` 组合表引用。 65 | - **两层模型(project.table)**: 66 | - 忽略 `schemaPattern`,将 `catalog` 视为项目名,并结合参数中的 `tableNamePattern` 组成表引用。 67 | 68 | ```text 69 | 三层模型:catalog.schemaPattern.tableNamePattern -> project.schemaPattern.tableNamePattern 70 | 两层模型:schemaPattern.tableNamePattern -> catalog.tableNamePattern 71 | ``` 72 | 73 | ### 变更 74 | - **增强 SQLException 异常信息**:异常信息中新增了正确的 ErrorMessage。 75 | - **更新 odps-sdk 版本**:已更新至 [0.48.8-public](https://github.com/aliyun/aliyun-odps-java-sdk/blob/release/0.50.x/CHANGELOG_CN.md#0488-public---2024-08-12)。 76 | 77 | 78 | ## [3.6.0] - 2024-08-07 79 | **拒绝 SQLFeatureNotSupportedException!** 80 | 81 | ### 新增功能 82 | - **LocalDate 支持**:`PrepareStatement.setObject` 现支持 LocalDate 类型,用于 MaxCompute 的 DATE 类型。 83 | - **SQL 注入检查**:新增参数 `skipSqlInjectCheck`,可跳过对 SQL 注入的检查,适用于设置 String 类型字段时。 84 | - **TIMESTAMP_NTZ 类型支持**:`DatabaseMetadata` 现增加对 MaxCompute `TIMESTAMP_NTZ` 类型字段的支持。 85 | - **扩展 `PrepareStatement` 方法**: 86 | - 实现了 `setObject(int, Object, int)`、`setObject(int, Object, int, int)`、`setObject(int, Object, SQLType)` 和 `setObject(int, Object, SQLType, int)` 方法,现不再抛出异常,而是返回 `setObject(int, Object)` 的结果,忽略多余字段。 87 | - **扩展 `ResultSet` 方法**: 88 | - 新增 `getObject(int, Map)` 和 `getObject(String, Map)` 方法,现不抛出异常,而是忽略 `map`,调用 `ResultSet.getObject(int)` 返回结果。 89 | - 新增 `getObject(int, Class)` 和 `getObject(String, Class)` 方法,首先调用 `ResultSet.getObject(int)`,然后尝试将结果转换为指定类型。 90 | - **优化 `Statement` 方法**:`createStatement(int, int, int)` 方法现在不再抛出异常,而是忽略 `resultSetHoldability`,返回 `Statement.createStatement(int, int)` 的结果。 91 | 92 | ### 变更 93 | - **增强 `DatabaseMetadata.getColumns`**:接口返回值新增 `IS_AUTOINCREMENT` 和 `IS_GENERATEDCOLUMN` 字段,以符合 JDBC 规范。 94 | - **更新 odps-sdk 版本**:已更新至 [0.48.7-public](https://github.com/aliyun/aliyun-odps-java-sdk/blob/release/0.50.x/CHANGELOG_CN.md#0487-public---2024-08-07)。 95 | - 新版本应用了 key-path-end 优化,提高了在执行离线作业时的效率,在较复杂query中提升明显。 96 | 97 | ## [3.5.8] - 2024-07-22 98 | 99 | ### 新增功能 100 | - **Delta Table 写入支持**:`PrepareStatement` 现支持对 Delta Table 的写入操作。 101 | - **跳过 SQL 重写**:新增参数 `skipSqlRewrite`,默认值为`true`,可在去除注释过程中跳过对 SQL 语句的重写。启用该参数可能导致与 SQL 一起提交的 settings 失效。 102 | 103 | ### 变更 104 | - **优化去除注释逻辑**:在处理非常长的查询时,已优化逻辑以避免抛出异常。修复了在去除注释的过程中,可能将 SQL hints 也去掉的问题。 105 | - **SDK 更新**:odps-sdk 已更新至 [0.48.6-public](https://github.com/aliyun/aliyun-odps-java-sdk/blob/release/0.50.x/CHANGELOG_CN.md#0486-public---2024-07-17)。 106 | - 新版本减少了网络开销,略微提高在执行离线作业时的效率。 107 | 108 | 109 | ## [3.5.7] - 2024-04-29 110 | 111 | ### 新增功能 112 | - **新增参数 `tunnelDownloadUseSingleReader`**:默认值为 `false`。开启该参数后,每个 ResultSet 将仅使用单个 Reader 进行数据下载。 113 | 此改动适用于一次性读取大量数据的场景,以优化性能。原行为是在每次调用 ResultSet 的 `hasNext` 方法时开启一个 Reader,以防止连接中断。 114 | 115 | 116 | ## [3.5.6] - 2023-10-27 117 | ### 新增功能 118 | - 支持 `insert into tablename (co1,co2) values(?,?)` 语法 119 | - `DatabaseMetadata.getColumns` 适配使用三层模型的情况 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 1999-2015 Alibaba Group Holding Ltd. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec mvn clean package -DskipTests 4 | -------------------------------------------------------------------------------- /example/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | com.aliyun.odps 6 | odps-jdbc-example 7 | 1.0-SNAPSHOT 8 | odps-jdbc-example 9 | 10 | 11 | 12 | com.aliyun.odps 13 | odps-jdbc 14 | 3.0.1 15 | 16 | 17 | com.zaxxer 18 | HikariCP 19 | 2.4.2 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /example/src/main/java/HikariCP.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | import java.io.InputStream; 3 | import java.sql.SQLException; 4 | import java.sql.Connection; 5 | import java.sql.ResultSet; 6 | import java.sql.Statement; 7 | import java.util.Properties; 8 | import com.zaxxer.hikari.HikariDataSource; 9 | 10 | 11 | public class HikariCP { 12 | 13 | private static String driverName = "com.aliyun.odps.jdbc.OdpsDriver"; 14 | 15 | /** 16 | * @param args 17 | * @throws SQLException 18 | */ 19 | public static void main(String[] args) throws SQLException { 20 | 21 | try { 22 | Class.forName(driverName); 23 | } catch (ClassNotFoundException e) { 24 | e.printStackTrace(); 25 | System.exit(1); 26 | } 27 | 28 | 29 | // fill in the information string 30 | Properties odpsConfig = new Properties(); 31 | InputStream 32 | is = 33 | Thread.currentThread().getContextClassLoader().getResourceAsStream("conf.properties"); 34 | try { 35 | odpsConfig.load(is); 36 | } catch (IOException e) { 37 | e.printStackTrace(); 38 | System.exit(1); 39 | } 40 | 41 | 42 | try { 43 | 44 | HikariDataSource ds = new HikariDataSource(); 45 | ds.setDriverClassName(driverName); 46 | ds.setJdbcUrl(odpsConfig.getProperty("connection_string")); 47 | ds.setUsername(odpsConfig.getProperty("username")); 48 | ds.setPassword(odpsConfig.getProperty("password")); 49 | ds.setMaximumPoolSize(5); 50 | ds.setConnectionTimeout(3000); 51 | ds.setAutoCommit(false); 52 | ds.setReadOnly(false); 53 | 54 | 55 | Connection conn = ds.getConnection(); 56 | Statement stmt = conn.createStatement(); 57 | String tableName = "testOdpsDriverTable"; 58 | stmt.execute("drop table if exists " + tableName); 59 | stmt.execute("create table " + tableName + " (key int, value string)"); 60 | 61 | String sql; 62 | ResultSet res; 63 | 64 | // insert a record 65 | sql = 66 | String.format( 67 | "insert into table %s select 24 key, 'hours' value from (select count(1) from %s) a", 68 | tableName, tableName); 69 | System.out.println("Running: " + sql); 70 | int count = stmt.executeUpdate(sql); 71 | System.out.println("updated records: " + count); 72 | 73 | // select * query 74 | sql = "select * from " + tableName; 75 | System.out.println("Running: " + sql); 76 | res = stmt.executeQuery(sql); 77 | while (res.next()) { 78 | System.out.println(String.valueOf(res.getInt(1)) + "\t" + res.getString(2)); 79 | } 80 | 81 | // regular query 82 | sql = "select count(1) from " + tableName; 83 | System.out.println("Running: " + sql); 84 | res = stmt.executeQuery(sql); 85 | while (res.next()) { 86 | System.out.println(res.getString(1)); 87 | } 88 | 89 | ds.close(); 90 | 91 | } catch (Exception e) { 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /example/src/main/java/OdpsJdbcClient.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | import java.io.InputStream; 3 | import java.sql.SQLException; 4 | import java.sql.Connection; 5 | import java.sql.ResultSet; 6 | import java.sql.Statement; 7 | import java.sql.DriverManager; 8 | import java.util.Properties; 9 | 10 | public class OdpsJdbcClient { 11 | private static String driverName = "com.aliyun.odps.jdbc.OdpsDriver"; 12 | 13 | /** 14 | * @param args 15 | * @throws SQLException 16 | */ 17 | public static void main(String[] args) throws SQLException { 18 | try { 19 | Class.forName(driverName); 20 | } catch (ClassNotFoundException e) { 21 | e.printStackTrace(); 22 | System.exit(1); 23 | } 24 | 25 | // fill in the information string 26 | Properties odpsConfig = new Properties(); 27 | InputStream is = 28 | Thread.currentThread().getContextClassLoader().getResourceAsStream("conf.properties"); 29 | try { 30 | odpsConfig.load(is); 31 | } catch (IOException e) { 32 | e.printStackTrace(); 33 | System.exit(1); 34 | } 35 | 36 | String accessId = odpsConfig.getProperty("access_id"); 37 | String accessKey = odpsConfig.getProperty("access_key"); 38 | Connection conn = DriverManager.getConnection(odpsConfig.getProperty("endpoint"), accessId, accessKey); 39 | 40 | Statement stmt = conn.createStatement(); 41 | String tableName = "testOdpsDriverTable"; 42 | stmt.execute("drop table if exists " + tableName); 43 | stmt.execute("create table " + tableName + " (key int, value string)"); 44 | 45 | String sql; 46 | ResultSet res; 47 | 48 | // insert a record 49 | sql = String.format("insert into table %s select 24 key, 'hours' value from (select count(1) from %s) a", tableName, tableName); 50 | System.out.println("Running: " + sql); 51 | int count = stmt.executeUpdate(sql); 52 | System.out.println("updated records: " + count); 53 | 54 | // select * query 55 | sql = "select * from " + tableName; 56 | System.out.println("Running: " + sql); 57 | res = stmt.executeQuery(sql); 58 | while (res.next()) { 59 | System.out.println(String.valueOf(res.getInt(1)) + "\t" + res.getString(2)); 60 | } 61 | 62 | // regular query 63 | sql = "select count(1) from " + tableName; 64 | System.out.println("Running: " + sql); 65 | res = stmt.executeQuery(sql); 66 | while (res.next()) { 67 | System.out.println(res.getString(1)); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /example/src/main/java/Pagination.java: -------------------------------------------------------------------------------- 1 | import java.sql.Connection; 2 | import java.sql.DriverManager; 3 | import java.sql.ResultSet; 4 | import java.sql.SQLException; 5 | import java.sql.Statement; 6 | import java.util.Scanner; 7 | 8 | public class Pagination { 9 | 10 | private static void printPage(ResultSet res, int start, int count) throws SQLException { 11 | // 表头 12 | int columnCount = res.getMetaData().getColumnCount(); 13 | for (int i = 0; i < columnCount; i++) { 14 | System.out.print(res.getMetaData().getColumnName(i + 1)); 15 | if (i < columnCount - 1) { 16 | System.out.print(" | "); 17 | } else { 18 | System.out.print("\n"); 19 | } 20 | } 21 | // 表内容 22 | res.absolute(start - 1); 23 | int c = 0; 24 | while (res.next()) { 25 | for (int i = 0; i < columnCount; i++) { 26 | System.out.print(res.getString(i + 1)); 27 | if (i < columnCount - 1) { 28 | System.out.print(" | "); 29 | } else { 30 | System.out.print("\n"); 31 | } 32 | } 33 | c++; 34 | if (c == count) { 35 | break; 36 | } 37 | } 38 | } 39 | 40 | public static void main(String[] args) throws SQLException { 41 | if (args.length < 3) { 42 | System.out.println("Usage: Pagination connection_string sql record_per_page"); 43 | System.out.println( 44 | " eg. Pagination 'jdbc:odps:http://service.odps.aliyun.com/api?project=odpsdemo?accessId=...&accessKey=...&charset=UTF-8' 'select * from dual' 10"); 45 | System.exit(1); 46 | } 47 | String connectionString = args[0]; 48 | String sql = args[1]; 49 | int recordPerPage = Integer.parseInt(args[2]); 50 | 51 | try { 52 | String driverName = "com.aliyun.odps.jdbc.OdpsDriver"; 53 | Class.forName(driverName); 54 | } catch (ClassNotFoundException e) { 55 | e.printStackTrace(); 56 | System.exit(1); 57 | } 58 | 59 | System.out.println("Connection: " + connectionString); 60 | Connection conn = DriverManager.getConnection(connectionString); 61 | ResultSet res; 62 | 63 | // SQL 只需运行一次,不要每显式一页都运行一个全新的 SQL 64 | System.out.println("Running : " + sql); 65 | if (sql.trim().equalsIgnoreCase("show tables")) { 66 | res = conn.getMetaData().getTables(null, null, null, null); 67 | } else { 68 | Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); 69 | res = stmt.executeQuery(sql); 70 | } 71 | 72 | // 获得 ResultSet 的记录数 73 | res.last(); 74 | int recordCount = res.getRow(); 75 | // 计算页数 76 | int pageCount = (int) Math.ceil(1.0 * recordCount / recordPerPage); 77 | 78 | while (true) { 79 | System.out.print("Page Count " + pageCount + ", please input page number (0 to exit): "); 80 | Scanner scanner = new Scanner(System.in); 81 | int p = scanner.nextInt(); 82 | if (p == 0) { 83 | break; 84 | } 85 | // 根据输入的页号 p 显式页 86 | printPage(res, (p - 1) * recordPerPage + 1, recordPerPage); 87 | } 88 | 89 | // 退出分页时关闭 ResultSet 和 Connection 90 | res.close(); 91 | conn.close(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /example/src/main/resources/conf.properties.example: -------------------------------------------------------------------------------- 1 | access_id= 2 | access_key= 3 | connection_string= 4 | -------------------------------------------------------------------------------- /hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | STAGE_FILES=$(git diff --cached --name-only --diff-filter=ACM) 4 | 5 | echo 'check sensitive information ...' 6 | FAIL=0 7 | for FILE in $STAGE_FILES 8 | do 9 | grep --color -Hni -E "(ssh-rsa|authorized_keys|id_dsa|ssh-keygen)" $FILE && FAIL=1 10 | grep --color -Hni -E "(private key|secret|signature|accessid|access_id|access_key|accesskey|access_|password)(.*?)(\=|\:)(\s*)(\'|\")[^\$^%][^)]+(\'|\")[^)]*$" $FILE && FAIL=1 11 | grep --color -Hni -E "jdbc\:odps\:.*?accessId\=[^\.]+)" $FILE && FAIL=1 12 | done 13 | 14 | if [ ${FAIL} == 0 ]; then 15 | echo 'check sensitive information ... passed' 16 | exit 0 17 | else 18 | echo 'check sensitive information ... failed' 19 | exit 1 20 | fi 21 | -------------------------------------------------------------------------------- /jdbc_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | JAR=`find . -maxdepth 2 -name 'odps-jdbc-*-jar-with-dependencies.jar'` 4 | echo "JDBC Jar : $JAR" 5 | # exec java -cp "$JAR:logback/logback-core-1.2.3.jar:logback/logback-classic-1.2.3.jar" com.aliyun.odps.jdbc.JdbcTest "$@" 6 | exec java -cp "$JAR" com.aliyun.odps.jdbc.JdbcTest "$@" 7 | -------------------------------------------------------------------------------- /logback/logback-classic-1.2.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-odps-jdbc/30fee8ea9d98f7c1114c21bf9ed536ae3e938115/logback/logback-classic-1.2.3.jar -------------------------------------------------------------------------------- /logback/logback-core-1.2.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-odps-jdbc/30fee8ea9d98f7c1114c21bf9ed536ae3e938115/logback/logback-core-1.2.3.jar -------------------------------------------------------------------------------- /logback/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ${LOG_HOME}/${LOG_NAME}.log 6 | 7 | 8 | %date %level [%thread] %logger{10} %X{connectionId} [%file:%line] %msg%n 9 | 10 | 11 | ${LOG_HOME}/${LOG_NAME}.%d{yyyy-MM-dd}.log 12 | 30 13 | 100MB 14 | 15 | 16 | 17 | 18 | %msg%n 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /mindmap-thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-odps-jdbc/30fee8ea9d98f7c1114c21bf9ed536ae3e938115/mindmap-thumb.png -------------------------------------------------------------------------------- /mindmap.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-odps-jdbc/30fee8ea9d98f7c1114c21bf9ed536ae3e938115/mindmap.pdf -------------------------------------------------------------------------------- /secret.key.asc.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliyun/aliyun-odps-jdbc/30fee8ea9d98f7c1114c21bf9ed536ae3e938115/secret.key.asc.enc -------------------------------------------------------------------------------- /settings.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | ossrh 7 | ${env.nexus_token_username} 8 | ${env.nexus_token_password} 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/AcidTableUploader.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import static java.lang.String.format; 4 | 5 | import java.io.IOException; 6 | import java.sql.SQLException; 7 | import java.util.List; 8 | 9 | import com.aliyun.odps.OdpsException; 10 | import com.aliyun.odps.tunnel.TableTunnel; 11 | import com.aliyun.odps.tunnel.TunnelException; 12 | import com.aliyun.odps.tunnel.impl.UpsertRecord; 13 | import com.aliyun.odps.tunnel.streams.UpsertStream; 14 | 15 | public class AcidTableUploader extends DataUploader { 16 | 17 | private TableTunnel.UpsertSession upsertSession; 18 | 19 | public AcidTableUploader(String projectName, 20 | String schemaName, 21 | String tableName, 22 | String partitionSpec, 23 | List specificColumns, 24 | OdpsConnection conn) throws OdpsException, IOException { 25 | super(projectName, schemaName, tableName, partitionSpec, specificColumns, conn); 26 | } 27 | 28 | protected void setUpSession() throws OdpsException, IOException { 29 | TableTunnel.UpsertSession.Builder builder = tunnel.buildUpsertSession(projectName, tableName); 30 | 31 | if (null != partitionSpec) { 32 | builder.setPartitionSpec(partitionSpec); 33 | } 34 | 35 | upsertSession = builder.build(); 36 | conn.log.info("create upsert session id=" + upsertSession.getId()); 37 | reuseRecord = (UpsertRecord) upsertSession.newRecord(); 38 | tableSchema = upsertSession.getSchema(); 39 | } 40 | 41 | protected void upload(List data, int batchSize, int[] updateCounts) 42 | throws OdpsException, IOException, SQLException { 43 | UpsertStream stream = upsertSession.buildUpsertStream().build(); 44 | 45 | for (int i = 0; i < data.size(); i++) { 46 | Object[] row = data.get(i); 47 | setReusedRecord(row, tableSchema); 48 | stream.upsert(reuseRecord); 49 | updateCounts[i] = 1; 50 | } 51 | 52 | stream.close(); 53 | } 54 | 55 | public void commit() throws TunnelException, IOException { 56 | if (upsertSession != null) { 57 | upsertSession.commit(false); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/BasicTableUploader.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import static java.lang.String.format; 4 | 5 | import java.io.IOException; 6 | import java.sql.SQLException; 7 | import java.util.List; 8 | 9 | import com.aliyun.odps.OdpsException; 10 | import com.aliyun.odps.data.ArrayRecord; 11 | import com.aliyun.odps.tunnel.TableTunnel; 12 | import com.aliyun.odps.tunnel.TunnelException; 13 | import com.aliyun.odps.tunnel.io.TunnelRecordWriter; 14 | 15 | public class BasicTableUploader extends DataUploader { 16 | 17 | private TableTunnel.UploadSession uploadSession; 18 | private int blocks = 0; 19 | 20 | public BasicTableUploader(String projectName, String schemaName, String tableName, 21 | String partitionSpec, List specificColumns, OdpsConnection conn) 22 | throws OdpsException, IOException { 23 | super(projectName, schemaName, tableName, partitionSpec, specificColumns, conn); 24 | } 25 | 26 | 27 | public void setUpSession() throws OdpsException { 28 | if (null != partitionSpec) { 29 | uploadSession = tunnel.createUploadSession(projectName, tableName, partitionSpec); 30 | } else { 31 | uploadSession = tunnel.createUploadSession(projectName, tableName); 32 | } 33 | 34 | conn.log.info("create upload session id=" + uploadSession.getId()); 35 | reuseRecord = (ArrayRecord) uploadSession.newRecord(); 36 | tableSchema = uploadSession.getSchema(); 37 | } 38 | 39 | 40 | protected void upload(List batchedRows, int batchedSize, int[] updateCounts) 41 | throws OdpsException, IOException, SQLException { 42 | 43 | long startTime = System.currentTimeMillis(); 44 | 45 | try(TunnelRecordWriter recordWriter = (TunnelRecordWriter) uploadSession.openRecordWriter(blocks, true)) { 46 | for (int i = 0; i < batchedSize; i++) { 47 | Object[] row = batchedRows.get(i); 48 | setReusedRecord(row, tableSchema); 49 | recordWriter.write(reuseRecord); 50 | updateCounts[i] = 1; 51 | } 52 | 53 | long duration = System.currentTimeMillis() - startTime; 54 | float megaBytesPerSec = (float) recordWriter.getTotalBytes() / 1024 / 1024 / duration * 1000; 55 | conn.log.info(format("It took me %d ms to insert %d records [%d], %.2f MiB/s", duration, 56 | batchedSize, 57 | blocks, megaBytesPerSec)); 58 | blocks += 1; 59 | } 60 | } 61 | 62 | 63 | public void commit() throws TunnelException, IOException { 64 | if (uploadSession != null && blocks > 0) { 65 | Long[] blockList = new Long[blocks]; 66 | conn.log.info("commit session: " + blocks + " blocks"); 67 | for (int i = 0; i < blocks; i++) { 68 | blockList[i] = Long.valueOf(i); 69 | } 70 | uploadSession.commit(blockList); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/DataUploader.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.io.IOException; 4 | import java.sql.SQLException; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.stream.Collectors; 8 | 9 | import com.aliyun.odps.Column; 10 | import com.aliyun.odps.OdpsException; 11 | import com.aliyun.odps.PartitionSpec; 12 | import com.aliyun.odps.Table; 13 | import com.aliyun.odps.TableSchema; 14 | import com.aliyun.odps.data.ArrayRecord; 15 | import com.aliyun.odps.jdbc.utils.transformer.to.odps.AbstractToOdpsTransformer; 16 | import com.aliyun.odps.jdbc.utils.transformer.to.odps.ToOdpsTransformerFactory; 17 | import com.aliyun.odps.tunnel.TableTunnel; 18 | import com.aliyun.odps.tunnel.TunnelException; 19 | import com.aliyun.odps.utils.StringUtils; 20 | 21 | public abstract class DataUploader { 22 | 23 | protected String projectName; 24 | protected String schemaName; 25 | protected String tableName; 26 | protected PartitionSpec partitionSpec; 27 | protected List specificColumns; 28 | protected OdpsConnection conn; 29 | 30 | protected TableTunnel tunnel; 31 | protected Table table; 32 | protected TableSchema tableSchema; 33 | protected ArrayRecord reuseRecord; 34 | 35 | public DataUploader(String projectName, 36 | String schemaName, 37 | String tableName, 38 | String partitionSpec, 39 | List specificColumns, 40 | OdpsConnection conn) 41 | throws OdpsException, IOException { 42 | this.projectName = projectName; 43 | this.schemaName = schemaName; 44 | this.tableName = tableName; 45 | if (!StringUtils.isNullOrEmpty(partitionSpec)) { 46 | this.partitionSpec = new PartitionSpec(partitionSpec); 47 | } 48 | this.specificColumns = specificColumns; 49 | this.conn = conn; 50 | 51 | tunnel = new TableTunnel(conn.getOdps()); 52 | Table table = conn.getOdps().tables().get(projectName, tableName); 53 | if (partitionSpec != null && !table.hasPartition(this.partitionSpec)) { 54 | table.createPartition(this.partitionSpec); 55 | } 56 | setUpSession(); 57 | 58 | if (specificColumns == null) { 59 | this.specificColumns = tableSchema.getColumns().stream().map(Column::getName).collect(Collectors.toList()); 60 | } 61 | } 62 | 63 | public static DataUploader build(String projectName, 64 | String schemaName, 65 | String tableName, 66 | String partitionSpec, 67 | List specificColumns, 68 | OdpsConnection conn) throws OdpsException, IOException { 69 | Table table = conn.getOdps().tables().get(projectName, tableName); 70 | if (table.isTransactional() && table.getPrimaryKey() != null && !table.getPrimaryKey().isEmpty()) { 71 | return new AcidTableUploader(projectName, schemaName, tableName, partitionSpec, specificColumns, conn); 72 | } else { 73 | return new BasicTableUploader(projectName, schemaName, tableName, partitionSpec, specificColumns, conn); 74 | } 75 | } 76 | 77 | protected abstract void setUpSession() throws OdpsException, IOException; 78 | 79 | 80 | public int[] upload(List batchedRows) throws SQLException { 81 | 82 | int batchedSize = batchedRows.size(); 83 | if (batchedSize == 0) { 84 | return new int[0]; 85 | } 86 | 87 | conn.log.info(batchedSize + " records are going to be uploaded to table " 88 | + projectName + "." + tableName + " in batch"); 89 | 90 | int[] updateCounts = new int[batchedSize]; 91 | Arrays.fill(updateCounts, -1); 92 | 93 | try { 94 | upload(batchedRows, batchedSize, updateCounts); 95 | } catch (Exception e) { 96 | throw new SQLException(e.getMessage(), e); 97 | //TODO 98 | } 99 | 100 | return updateCounts; 101 | } 102 | 103 | protected abstract void upload(List batchedRows, int batchedSize, int[] updateCounts) 104 | throws OdpsException, IOException, SQLException; 105 | 106 | protected void setReusedRecord(Object[] row, TableSchema schema) throws SQLException { 107 | for (int i = 0; i < specificColumns.size(); i++) { 108 | String columnName = specificColumns.get(i); 109 | AbstractToOdpsTransformer transformer = ToOdpsTransformerFactory.getTransformer( 110 | schema.getColumn(columnName).getTypeInfo().getOdpsType()); 111 | reuseRecord.set(columnName, transformer.transform(row[i], conn.getCharset())); 112 | } 113 | } 114 | 115 | public abstract void commit() throws TunnelException, IOException; 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/JdbcRunner.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileReader; 5 | import java.io.IOException; 6 | import java.sql.DriverManager; 7 | import java.sql.SQLException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | import com.aliyun.odps.OdpsException; 13 | 14 | public class JdbcRunner { 15 | 16 | public static void main(String[] args) throws SQLException, OdpsException { 17 | if (args.length < 2) { 18 | System.err.println( 19 | "Usage: java -cp odps-jdbc-version-jar-with-dependencies.jar com.aliyun.odps.jdbc.JdbcRunner "); 20 | System.exit(1); 21 | } 22 | 23 | String jdbcUrl = args[0]; 24 | String sqlFilePath = args[1]; 25 | String sqlContent = readFile(sqlFilePath); 26 | 27 | System.out.println("JDBC URL: " + jdbcUrl); 28 | 29 | OdpsConnection conn = (OdpsConnection) DriverManager.getConnection(jdbcUrl); 30 | OdpsStatement stmt = conn.createStatement(); 31 | if (sqlContent == null) { 32 | System.out.println("no sql content."); 33 | System.exit(0); 34 | } 35 | 36 | long start = System.currentTimeMillis(); 37 | stmt.execute(sqlContent); 38 | System.out.println(stmt.getSqlExecutor().getLogView()); 39 | OdpsResultSet odpsResultSet = (OdpsResultSet) stmt.getResultSet(); 40 | 41 | long cost = System.currentTimeMillis() - start; 42 | 43 | List columnNames = new ArrayList<>(); 44 | for (int i = 1; i <= odpsResultSet.getMetaData().getColumnCount(); i++) { 45 | columnNames.add(odpsResultSet.getMetaData().getColumnName(i)); 46 | } 47 | 48 | System.out.println(columnNames.stream().collect(Collectors.joining("\t"))); 49 | while (odpsResultSet.next()) { 50 | StringBuilder sb = new StringBuilder(); 51 | for (int i = 1; i <= columnNames.size(); i++) { 52 | Object object = odpsResultSet.getObject(i); 53 | sb.append(object == null ? "NULL" : object.toString()); 54 | if (i != columnNames.size()) { 55 | sb.append("\t"); 56 | } 57 | } 58 | System.out.println(sb); 59 | } 60 | 61 | System.out.println("Summary:\n" + stmt.getSqlExecutor().getSummary()); 62 | System.out.println("Execute cost " + cost + " ms;"); 63 | System.exit(0); 64 | } 65 | 66 | private static String readFile(String filePath) { 67 | StringBuilder contentBuilder = new StringBuilder(); 68 | try (BufferedReader br = new BufferedReader(new FileReader(filePath))) { 69 | String line; 70 | while ((line = br.readLine()) != null) { 71 | contentBuilder.append(line).append("\n"); 72 | } 73 | } catch (IOException e) { 74 | System.err.println("Error reading file: " + e.getMessage()); 75 | return null; 76 | } 77 | return contentBuilder.toString(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/JdbcSessionTest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.ResultSet; 6 | import java.sql.SQLException; 7 | import java.sql.Statement; 8 | 9 | public class JdbcSessionTest { 10 | 11 | // res will be closed in this function 12 | private static void printResultSet(ResultSet res) throws SQLException { 13 | int columnCount = res.getMetaData().getColumnCount(); 14 | for (int i = 0; i < columnCount; i++) { 15 | System.out.print(res.getMetaData().getColumnName(i + 1)); 16 | if (i < columnCount - 1) { 17 | System.out.print(" | "); 18 | } else { 19 | System.out.print("\n"); 20 | } 21 | } 22 | while (res.next()) { 23 | for (int i = 0; i < columnCount; i++) { 24 | System.out.print(res.getString(i + 1)); 25 | if (i < columnCount - 1) { 26 | System.out.print(" | "); 27 | } else { 28 | System.out.print("\n"); 29 | } 30 | } 31 | } 32 | res.close(); 33 | } 34 | 35 | public static void main(String[] args) throws SQLException { 36 | if (args.length < 2) { 37 | System.out.println( 38 | "Usage: java -cp odps-jdbc-...-jar-with-dependencies.jar com.aliyun.odps.jdbc.JdbcTest connection_string sql"); 39 | System.out.println( 40 | " eg. JdbcTest 'jdbc:odps:http://service.odps.aliyun.com/api?project=odpsdemo&accessId=..." 41 | + 42 | "&accessKey=...&charset=UTF-8&interactiveMode=true&interactiveServiceName=public.default&majorVersion=default&longPolling=false' 'select * from dual'"); 43 | System.exit(1); 44 | } 45 | String connectionString = args[0]; 46 | String sql = args[1]; 47 | try { 48 | String driverName = "com.aliyun.odps.jdbc.OdpsDriver"; 49 | Class.forName(driverName); 50 | } catch (ClassNotFoundException e) { 51 | e.printStackTrace(); 52 | System.exit(1); 53 | } 54 | System.out.println("Connection: " + connectionString); 55 | Connection conn = DriverManager.getConnection(connectionString); 56 | ResultSet res; 57 | try { 58 | System.out.println("Running : " + sql); 59 | if (sql.trim().equalsIgnoreCase("show tables")) { 60 | res = conn.getMetaData().getTables(null, null, null, null); 61 | } else { 62 | Statement stmt = conn.createStatement(); 63 | res = stmt.executeQuery(sql); 64 | } 65 | 66 | System.out.println("Result :"); 67 | printResultSet(res); 68 | } catch (Exception e) { 69 | throw e; 70 | } finally { 71 | conn.close(); 72 | } 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/JdbcTest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.ResultSet; 6 | import java.sql.SQLException; 7 | import java.sql.Statement; 8 | 9 | public class JdbcTest { 10 | 11 | // res will be closed in this function 12 | private static void printResultSet(ResultSet res) throws SQLException { 13 | int columnCount = res.getMetaData().getColumnCount(); 14 | for (int i = 0; i < columnCount; i++) { 15 | System.out.print(res.getMetaData().getColumnName(i + 1)); 16 | if (i < columnCount - 1) { 17 | System.out.print(" | "); 18 | } else { 19 | System.out.print("\n"); 20 | } 21 | } 22 | while (res.next()) { 23 | for (int i = 0; i < columnCount; i++) { 24 | System.out.print(res.getString(i + 1)); 25 | if (i < columnCount - 1) { 26 | System.out.print(" | "); 27 | } else { 28 | System.out.print("\n"); 29 | } 30 | } 31 | } 32 | res.close(); 33 | } 34 | 35 | public static void main(String[] args) throws SQLException { 36 | if (args.length < 2) { 37 | System.out.println( 38 | "Usage: java -cp odps-jdbc-...-jar-with-dependencies.jar com.aliyun.odps.jdbc.JdbcTest connection_string sql"); 39 | System.out.println( 40 | " eg. JdbcTest 'jdbc:odps:http://service.odps.aliyun.com/api?project=odpsdemo&accessId=...&accessKey=...&charset=UTF-8' 'select * from dual'"); 41 | System.exit(1); 42 | } 43 | String connectionString = args[0]; 44 | String sql = args[1]; 45 | 46 | try { 47 | String driverName = "com.aliyun.odps.jdbc.OdpsDriver"; 48 | Class.forName(driverName); 49 | } catch (ClassNotFoundException e) { 50 | e.printStackTrace(); 51 | System.exit(1); 52 | } 53 | 54 | System.out.println("Connection: " + connectionString); 55 | Connection conn = DriverManager.getConnection(connectionString); 56 | ResultSet res; 57 | 58 | System.out.println("Running : " + sql); 59 | if (sql.trim().equalsIgnoreCase("show tables")) { 60 | res = conn.getMetaData().getTables(null, null, null, null); 61 | } else { 62 | Statement stmt = conn.createStatement(); 63 | res = stmt.executeQuery(sql); 64 | } 65 | 66 | System.out.println("Result :"); 67 | printResultSet(res); 68 | 69 | conn.close(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/OdpsAsyncPreparedStatement.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.io.IOException; 4 | import java.sql.ResultSet; 5 | import java.sql.SQLException; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Properties; 10 | 11 | import com.aliyun.odps.OdpsException; 12 | import com.aliyun.odps.jdbc.utils.SettingParser; 13 | import com.aliyun.odps.sqa.SQLExecutor; 14 | import com.aliyun.odps.utils.StringUtils; 15 | 16 | /** 17 | * @author dingxin (zhangdingxin.zdx@alibaba-inc.com) 18 | */ 19 | public class OdpsAsyncPreparedStatement extends OdpsPreparedStatement { 20 | 21 | OdpsAsyncPreparedStatement(OdpsConnection conn, String sql) { 22 | super(conn, sql); 23 | } 24 | 25 | OdpsAsyncPreparedStatement(OdpsConnection conn, String sql, boolean isResultSetScrollable) { 26 | super(conn, sql, isResultSetScrollable); 27 | } 28 | 29 | 30 | @Override 31 | public synchronized boolean execute(String query) throws SQLException { 32 | // short cut for SET clause 33 | Properties properties = new Properties(); 34 | if (!connHandle.isSkipSqlCheck()) { 35 | SettingParser.ParseResult parseResult = SettingParser.parse(query); 36 | query = parseResult.getRemainingQuery(); 37 | properties.putAll(parseResult.getSettings()); 38 | } 39 | if (StringUtils.isBlank(query)) { 40 | // only settings, just set properties 41 | processSetClause(properties); 42 | return false; 43 | } else { 44 | try { 45 | processSetClauseExtra(properties); 46 | } catch (OdpsException e) { 47 | throw new SQLException(e.getMessage(), e); 48 | } 49 | } 50 | // otherwise those properties is just for this query 51 | if (processUseClause(query)) { 52 | return false; 53 | } 54 | checkClosed(); 55 | beforeExecute(); 56 | runSQL(query, properties); 57 | return hasResultSet(); 58 | } 59 | 60 | private void runSQL(String sql, Properties properties) throws SQLException { 61 | SQLExecutor executor = this.sqlExecutor; 62 | try { 63 | // If the client forget to end with a semi-colon, append it. 64 | if (!sql.endsWith(";")) { 65 | sql += ";"; 66 | } 67 | Map settings = new HashMap<>(); 68 | for (String key : sqlTaskProperties.stringPropertyNames()) { 69 | settings.put(key, sqlTaskProperties.getProperty(key)); 70 | } 71 | 72 | inputProperties = new Properties(); 73 | if (properties != null && !properties.isEmpty()) { 74 | for (String key : properties.stringPropertyNames()) { 75 | settings.put(key, properties.getProperty(key)); 76 | inputProperties.put(key, properties.getProperty(key)); 77 | } 78 | } 79 | if (!settings.isEmpty()) { 80 | connHandle.log.info("Enabled SQL task properties: " + settings); 81 | } 82 | long begin = System.currentTimeMillis(); 83 | if (queryTimeout != -1 && !settings.containsKey("odps.sql.session.query.timeout")) { 84 | settings.put("odps.sql.session.query.timeout", String.valueOf(queryTimeout)); 85 | } 86 | Long autoSelectLimit = connHandle.getAutoSelectLimit(); 87 | if (autoSelectLimit != null && autoSelectLimit > 0) { 88 | settings.put("odps.sql.select.auto.limit", autoSelectLimit.toString()); 89 | } 90 | executor.run(sql, settings); 91 | connHandle.log.info("Run SQL: [" + sql + "],submit cost: " + (System.currentTimeMillis() - begin) + "ms"); 92 | logviewUrl = executor.getLogView(); 93 | connHandle.log.info("LogView: " + logviewUrl); 94 | executeInstance = executor.getInstance(); 95 | if (executeInstance != null) { 96 | connHandle.log.info("InstanceId: " + executeInstance.getId()); 97 | } 98 | } catch (OdpsException e) { 99 | throwSQLException(e, sql, executor.getInstance(), executor.getLogView()); 100 | } 101 | } 102 | 103 | @Override 104 | public synchronized ResultSet getResultSet() throws SQLException { 105 | SQLExecutor executor = this.sqlExecutor; 106 | try { 107 | long startTime = System.currentTimeMillis(); 108 | setResultSetInternal(); 109 | connHandle.log.info("Get result set, cost time: " + (System.currentTimeMillis() - startTime) + "ms"); 110 | List exeLog = executor.getExecutionLog(); 111 | if (!exeLog.isEmpty()) { 112 | for (String log : exeLog) { 113 | connHandle.log.info("Session execution log: " + log); 114 | } 115 | } 116 | return super.getResultSet(); 117 | } catch (OdpsException | IOException e) { 118 | throwSQLException(e, "unknown", executor.getInstance(), executor.getLogView()); 119 | return null; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/OdpsAsyncStatement.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.io.IOException; 4 | import java.sql.ResultSet; 5 | import java.sql.SQLException; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Properties; 10 | 11 | import com.aliyun.odps.OdpsException; 12 | import com.aliyun.odps.jdbc.utils.SettingParser; 13 | import com.aliyun.odps.sqa.SQLExecutor; 14 | import com.aliyun.odps.utils.StringUtils; 15 | 16 | /** 17 | * @author dingxin (zhangdingxin.zdx@alibaba-inc.com) 18 | */ 19 | public class OdpsAsyncStatement extends OdpsStatement { 20 | 21 | OdpsAsyncStatement(OdpsConnection conn) { 22 | super(conn); 23 | } 24 | 25 | OdpsAsyncStatement(OdpsConnection conn, boolean isResultSetScrollable) { 26 | super(conn, isResultSetScrollable); 27 | } 28 | 29 | @Override 30 | public synchronized boolean execute(String query) throws SQLException { 31 | // short cut for SET clause 32 | Properties properties = new Properties(); 33 | if (!connHandle.isSkipSqlCheck()) { 34 | SettingParser.ParseResult parseResult = SettingParser.parse(query); 35 | query = parseResult.getRemainingQuery(); 36 | properties.putAll(parseResult.getSettings()); 37 | } 38 | if (StringUtils.isBlank(query)) { 39 | // only settings, just set properties 40 | processSetClause(properties); 41 | return false; 42 | } else { 43 | try { 44 | processSetClauseExtra(properties); 45 | } catch (OdpsException e) { 46 | throw new SQLException(e.getMessage(), e); 47 | } 48 | } 49 | // otherwise those properties is just for this query 50 | if (processUseClause(query)) { 51 | return false; 52 | } 53 | checkClosed(); 54 | beforeExecute(); 55 | runSQL(query, properties); 56 | return hasResultSet(); 57 | } 58 | 59 | private void runSQL(String sql, Properties properties) throws SQLException { 60 | SQLExecutor executor = this.sqlExecutor; 61 | try { 62 | // If the client forget to end with a semi-colon, append it. 63 | if (!sql.endsWith(";")) { 64 | sql += ";"; 65 | } 66 | Map settings = new HashMap<>(); 67 | for (String key : sqlTaskProperties.stringPropertyNames()) { 68 | settings.put(key, sqlTaskProperties.getProperty(key)); 69 | } 70 | 71 | inputProperties = new Properties(); 72 | if (properties != null && !properties.isEmpty()) { 73 | for (String key : properties.stringPropertyNames()) { 74 | settings.put(key, properties.getProperty(key)); 75 | inputProperties.put(key, properties.getProperty(key)); 76 | } 77 | } 78 | if (!settings.isEmpty()) { 79 | connHandle.log.info("Enabled SQL task properties: " + settings); 80 | } 81 | long begin = System.currentTimeMillis(); 82 | if (queryTimeout != -1 && !settings.containsKey("odps.sql.session.query.timeout")) { 83 | settings.put("odps.sql.session.query.timeout", String.valueOf(queryTimeout)); 84 | } 85 | Long autoSelectLimit = connHandle.getAutoSelectLimit(); 86 | if (autoSelectLimit != null && autoSelectLimit > 0) { 87 | settings.put("odps.sql.select.auto.limit", autoSelectLimit.toString()); 88 | } 89 | executor.run(sql, settings); 90 | connHandle.log.info("Run SQL: [" + sql + "],submit cost: " + (System.currentTimeMillis() - begin) + "ms"); 91 | logviewUrl = executor.getLogView(); 92 | connHandle.log.info("LogView: " + logviewUrl); 93 | executeInstance = executor.getInstance(); 94 | if (executeInstance != null) { 95 | connHandle.log.info("InstanceId: " + executeInstance.getId()); 96 | } 97 | } catch (OdpsException e) { 98 | throwSQLException(e, sql, executor.getInstance(), executor.getLogView()); 99 | } 100 | } 101 | 102 | @Override 103 | public synchronized ResultSet getResultSet() throws SQLException { 104 | SQLExecutor executor = this.sqlExecutor; 105 | try { 106 | long startTime = System.currentTimeMillis(); 107 | setResultSetInternal(); 108 | connHandle.log.info("Get result set, cost time: " + (System.currentTimeMillis() - startTime) + "ms"); 109 | List exeLog = executor.getExecutionLog(); 110 | if (!exeLog.isEmpty()) { 111 | for (String log : exeLog) { 112 | connHandle.log.info("Session execution log: " + log); 113 | } 114 | } 115 | return super.getResultSet(); 116 | } catch (OdpsException | IOException e) { 117 | throwSQLException(e, "unknown", executor.getInstance(), executor.getLogView()); 118 | return null; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/OdpsDriver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc; 22 | 23 | import java.sql.Connection; 24 | import java.sql.Driver; 25 | import java.sql.DriverManager; 26 | import java.sql.DriverPropertyInfo; 27 | import java.sql.SQLException; 28 | import java.sql.SQLFeatureNotSupportedException; 29 | import java.util.Properties; 30 | import java.util.logging.Logger; 31 | 32 | import com.aliyun.odps.jdbc.utils.ConnectionResource; 33 | import com.aliyun.odps.jdbc.utils.Utils; 34 | 35 | public class OdpsDriver implements Driver { 36 | 37 | static { 38 | try { 39 | DriverManager.registerDriver(new OdpsDriver()); 40 | } catch (SQLException e) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | 45 | /** 46 | * Is this driver JDBC compliant? 47 | */ 48 | private static final boolean JDBC_COMPLIANT = false; 49 | 50 | public OdpsDriver() { 51 | SecurityManager security = System.getSecurityManager(); 52 | if (security != null) { 53 | security.checkWrite("odps"); 54 | } 55 | } 56 | 57 | @Override 58 | public Connection connect(String url, Properties info) throws SQLException { 59 | return acceptsURL(url) ? new OdpsConnection(url, info) : null; 60 | } 61 | 62 | @Override 63 | public boolean acceptsURL(String url) throws SQLException { 64 | return ConnectionResource.acceptURL(url); 65 | } 66 | 67 | // each element is a DriverPropertyInfo object representing a connection URL attribute 68 | // that has not already been specified. 69 | @Override 70 | public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) 71 | throws SQLException { 72 | 73 | ConnectionResource connRes = new ConnectionResource(url, info); 74 | 75 | DriverPropertyInfo 76 | accessIdProp = 77 | new DriverPropertyInfo(ConnectionResource.ACCESS_ID_PROP_KEY, connRes.getAccessId()); 78 | accessIdProp.required = true; 79 | accessIdProp.description = "ODPS access id"; 80 | 81 | DriverPropertyInfo 82 | accessKeyProp = 83 | new DriverPropertyInfo(ConnectionResource.ACCESS_KEY_PROP_KEY, connRes.getAccessKey()); 84 | accessKeyProp.required = true; 85 | accessKeyProp.description = "ODPS access key"; 86 | 87 | DriverPropertyInfo 88 | projectProp = 89 | new DriverPropertyInfo(ConnectionResource.PROJECT_PROP_KEY, connRes.getProject()); 90 | projectProp.required = true; 91 | projectProp.description = "ODPS default project"; 92 | 93 | DriverPropertyInfo 94 | charsetProp = 95 | new DriverPropertyInfo(ConnectionResource.CHARSET_PROP_KEY, connRes.getCharset()); 96 | charsetProp.required = false; 97 | charsetProp.description = "character set for the string type"; 98 | charsetProp.choices = new String[]{"UTF-8", "GBK"}; 99 | 100 | DriverPropertyInfo 101 | logviewProp = 102 | new DriverPropertyInfo(ConnectionResource.LOGVIEW_HOST_PROP_KEY, connRes.getLogview()); 103 | logviewProp.required = false; 104 | logviewProp.description = "logview host"; 105 | 106 | return new DriverPropertyInfo[]{accessIdProp, accessKeyProp, projectProp, charsetProp}; 107 | } 108 | 109 | @Override 110 | public int getMajorVersion() { 111 | try { 112 | return Integer.parseInt(Utils.retrieveVersion("driver.version").split("\\.")[0]); 113 | } catch (Exception e) { 114 | e.printStackTrace(); 115 | return 1; 116 | } 117 | } 118 | 119 | @Override 120 | public int getMinorVersion() { 121 | try { 122 | return Integer.parseInt(Utils.retrieveVersion("driver.version").split("\\.")[1]); 123 | } catch (Exception e) { 124 | e.printStackTrace(); 125 | return 0; 126 | } 127 | } 128 | 129 | @Override 130 | public boolean jdbcCompliant() { 131 | return JDBC_COMPLIANT; 132 | } 133 | 134 | public Logger getParentLogger() throws SQLFeatureNotSupportedException { 135 | return Logger.getLogger("com.aliyun.odps.jdbc"); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/OdpsSessionForwardResultSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc; 22 | 23 | import java.sql.ResultSet; 24 | import java.sql.SQLException; 25 | 26 | import com.aliyun.odps.data.Record; 27 | 28 | public class OdpsSessionForwardResultSet extends OdpsResultSet implements ResultSet { 29 | 30 | private Object[] currentRow; 31 | com.aliyun.odps.data.ResultSet resultSet; 32 | private int fetchedRows = 0; 33 | // max row count can be read 34 | private int totalRows = Integer.MAX_VALUE; 35 | private boolean isClosed = false; 36 | 37 | private long startTime; 38 | 39 | 40 | OdpsSessionForwardResultSet(OdpsStatement stmt, OdpsResultSetMetaData meta, 41 | com.aliyun.odps.data.ResultSet resultSet, long startTime) 42 | throws SQLException { 43 | super(stmt.getConnection(), stmt, meta); 44 | 45 | // maxRows take effect only if it > 0 46 | if (stmt.resultSetMaxRows > 0) { 47 | totalRows = stmt.resultSetMaxRows; 48 | } 49 | 50 | this.resultSet = resultSet; 51 | this.startTime = startTime; 52 | } 53 | 54 | protected void checkClosed() throws SQLException { 55 | if (isClosed) { 56 | throw new SQLException("The result set has been closed"); 57 | } 58 | } 59 | 60 | @Override 61 | public int getRow() throws SQLException { 62 | checkClosed(); 63 | return (int) fetchedRows; 64 | } 65 | 66 | @Override 67 | public int getType() throws SQLException { 68 | return ResultSet.TYPE_FORWARD_ONLY; 69 | } 70 | 71 | @Override 72 | public boolean isClosed() throws SQLException { 73 | return isClosed; 74 | } 75 | 76 | @Override 77 | public void close() throws SQLException { 78 | if (isClosed) { 79 | return; 80 | } 81 | 82 | isClosed = true; 83 | conn.log.debug("the result set has been closed"); 84 | } 85 | 86 | @Override 87 | public boolean next() throws SQLException { 88 | checkClosed(); 89 | 90 | if (fetchedRows == totalRows || !resultSet.hasNext()) { 91 | conn.log.info("It took me " + (System.currentTimeMillis() - startTime) 92 | + " ms to fetch all records, count:" + fetchedRows); 93 | return false; 94 | } 95 | Record record = resultSet.next(); 96 | int columns = record.getColumnCount(); 97 | currentRow = new Object[columns]; 98 | for (int i = 0; i < columns; i++) { 99 | currentRow[i] = record.get(i); 100 | } 101 | fetchedRows++; 102 | return true; 103 | } 104 | 105 | @Override 106 | protected Object[] rowAtCursor() throws SQLException { 107 | if (currentRow == null) { 108 | throw new SQLException("the row should be not-null, row=" + fetchedRows); 109 | } 110 | 111 | if (currentRow.length == 0) { 112 | throw new SQLException("the row should have more than 1 column , row=" + fetchedRows); 113 | } 114 | 115 | return currentRow; 116 | } 117 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/OdpsStaticResultSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc; 22 | 23 | import java.sql.ResultSet; 24 | import java.sql.SQLException; 25 | import java.util.Iterator; 26 | 27 | 28 | class OdpsStaticResultSet extends OdpsResultSet implements ResultSet { 29 | 30 | private Iterator iterator; 31 | private boolean isClosed = false; 32 | Object[] row; 33 | 34 | /** 35 | * For some meta query (like procedures) we need to return an empty query result. 36 | */ 37 | private boolean isEmptyResultSet = false; 38 | 39 | OdpsStaticResultSet(OdpsConnection conn, OdpsResultSetMetaData meta) throws SQLException { 40 | super(conn, null, meta); 41 | // Construct an empty result set 42 | isEmptyResultSet = true; 43 | } 44 | 45 | /** 46 | * For non-empty result set, its data is passed via parameter 47 | */ 48 | OdpsStaticResultSet(OdpsConnection conn, OdpsResultSetMetaData meta, Iterator iter) 49 | throws SQLException { 50 | super(conn, null, meta); 51 | iterator = iter; 52 | isEmptyResultSet = false; 53 | } 54 | 55 | @Override 56 | public void close() throws SQLException { 57 | iterator = null; 58 | isClosed = true; 59 | } 60 | 61 | @Override 62 | public boolean isClosed() { 63 | return isClosed; 64 | } 65 | 66 | @Override 67 | public boolean next() throws SQLException { 68 | if (isEmptyResultSet) { 69 | return false; 70 | } 71 | if (iterator.hasNext()) { 72 | row = iterator.next(); 73 | return true; 74 | } else { 75 | return false; 76 | } 77 | } 78 | 79 | @Override 80 | protected Object[] rowAtCursor() throws SQLException { 81 | return row; 82 | } 83 | } 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/PublicMethodAspect.aj: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.io.IOException; 4 | import java.net.URISyntaxException; 5 | 6 | import org.aspectj.lang.JoinPoint; 7 | 8 | import com.aliyun.odps.jdbc.utils.OdpsLogger; 9 | 10 | public aspect PublicMethodAspect { 11 | pointcut Include(): execution(public * com.aliyun.odps.jdbc.Odps*.*(..)); 12 | 13 | private OdpsLogger logger; 14 | 15 | public PublicMethodAspect() throws IOException, URISyntaxException { 16 | logger = new OdpsLogger(getClass().getName(), null, null, null, false, false); 17 | } 18 | 19 | before(): Include() { 20 | int lineNumber = getCurrentLineNumber(thisJoinPoint); 21 | String classname = getCurrentClassname(thisJoinPoint); 22 | String methodName = getCurrentMethodName(thisJoinPoint); 23 | String args = getCurrentArguments(thisJoinPoint).replaceAll("accessKey=\\w+", "accessKey=***"); 24 | String msg = String.format( 25 | "Enter: [line %d] [%s] [%s] [%s]", lineNumber, classname, methodName, args); 26 | logger.debug(msg); 27 | } 28 | 29 | after() returning(Object ret): Include() { 30 | int lineNumber = getCurrentLineNumber(thisJoinPoint); 31 | String classname = getCurrentClassname(thisJoinPoint); 32 | String methodName = getCurrentMethodName(thisJoinPoint); 33 | String msg = String.format( 34 | "Leave: [line %d] [%s] [%s] [%s]", lineNumber, classname, methodName, ret); 35 | logger.debug(msg); 36 | } 37 | 38 | after() throwing(Exception e): Include() { 39 | logger.error("exception happened: ", e); 40 | } 41 | 42 | private int getCurrentLineNumber(JoinPoint joinPoint) { 43 | try { 44 | return joinPoint.getSourceLocation().getLine(); 45 | } catch (Exception e) { 46 | return -1; 47 | } 48 | } 49 | 50 | private String getCurrentClassname(JoinPoint joinPoint) { 51 | try { 52 | return joinPoint.getThis().getClass().getName(); 53 | } catch (Exception e) { 54 | return "N/A"; 55 | } 56 | } 57 | 58 | private String getCurrentMethodName(JoinPoint joinPoint) { 59 | try { 60 | return joinPoint.getSignature().getName(); 61 | } catch (Exception e) { 62 | return "N/A"; 63 | } 64 | } 65 | 66 | private String formatObject(Object o) { 67 | // null 68 | if (o == null) { 69 | return "null"; 70 | } 71 | // object[] 72 | StringBuilder sb = new StringBuilder(); 73 | if (o instanceof Object[]) { 74 | sb.append("["); 75 | int l = ((Object[]) o).length; 76 | for (int i = 0; i < l; i++) { 77 | Object oi = ((Object[]) o)[i]; 78 | sb.append(formatObject(oi)); 79 | if (i != l - 1) { 80 | sb.append(", "); 81 | } 82 | } 83 | sb.append("]"); 84 | return sb.toString(); 85 | } 86 | // default 87 | sb.append("'"); 88 | sb.append(o.toString()); 89 | sb.append("'"); 90 | return sb.toString(); 91 | } 92 | 93 | private String getCurrentArguments(JoinPoint joinPoint) { 94 | try { 95 | return formatObject(joinPoint.getArgs()); 96 | } catch (Exception e) { 97 | return "?"; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/WrapperAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc; 22 | 23 | import java.sql.Wrapper; 24 | 25 | public class WrapperAdapter implements Wrapper { 26 | 27 | public WrapperAdapter() { 28 | } 29 | 30 | @Override 31 | public boolean isWrapperFor(Class iface) { 32 | return iface != null && iface.isInstance(this); 33 | 34 | } 35 | 36 | @SuppressWarnings("unchecked") 37 | @Override 38 | public T unwrap(Class iface) { 39 | if (iface == null) { 40 | return null; 41 | } 42 | 43 | if (iface.isInstance(this)) { 44 | return (T) this; 45 | } 46 | 47 | return null; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/CalendarCache.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc.utils; 2 | 3 | 4 | import java.util.Date; 5 | import java.util.GregorianCalendar; 6 | import java.util.HashMap; 7 | import java.util.TimeZone; 8 | 9 | public class CalendarCache { 10 | 11 | private static final ThreadLocal localState = 12 | ThreadLocal.withInitial(CalendarCacheState::new); 13 | 14 | public static GregorianCalendar get(TimeZone timezone, String tzId, String calId) { 15 | CalendarCacheState state = localState.get(); 16 | if (state.enabled) { 17 | GregorianCalendar res = state.map.get(calId); 18 | if (res == null) { 19 | res = createCalendar(timezone, tzId); 20 | state.map.put(calId, res); 21 | } else { 22 | if (timezone != null && !res.getTimeZone().hasSameRules(timezone)) { 23 | throw new IllegalArgumentException("Cached timezone is not equivalent to the requested one:" + timezone + " VS " + res.getTimeZone()); 24 | } 25 | 26 | if (state.alwaysGregorian && res.getGregorianChange().getTime() != Long.MIN_VALUE) { 27 | throw new IllegalArgumentException("Cached calendar gregorian offset is not set as expected:" + res.getGregorianChange().getTime() + " VS " + Long.MIN_VALUE); 28 | } 29 | } 30 | 31 | if (state.produceClones) { 32 | res = (GregorianCalendar)res.clone(); 33 | } else { 34 | res.clear(); 35 | } 36 | 37 | return res; 38 | } else { 39 | return createCalendar(timezone, tzId); 40 | } 41 | } 42 | 43 | private static GregorianCalendar createCalendar(TimeZone timezone, String tzId) { 44 | CalendarCacheState state = localState.get(); 45 | if (timezone == null) { 46 | timezone = state.timeZoneFactory.getTimeZone(tzId); 47 | } 48 | 49 | GregorianCalendar cal = new GregorianCalendar(timezone); 50 | cal.clear(); 51 | if (state.alwaysGregorian) { 52 | cal.setGregorianChange(new Date(Long.MIN_VALUE)); 53 | } 54 | 55 | return cal; 56 | } 57 | 58 | public static GregorianCalendar get(TimeZone timezone, String calId) { 59 | return get(timezone, null, calId); 60 | } 61 | 62 | public static GregorianCalendar get(TimeZone timezone) { 63 | return get(timezone, null, timezone.getID()); 64 | } 65 | 66 | public static GregorianCalendar get(String tzId) { 67 | return get(null, tzId, tzId); 68 | } 69 | 70 | private static class CalendarCacheState { 71 | HashMap map = new HashMap(); 72 | boolean enabled = false; 73 | boolean produceClones = true; 74 | boolean alwaysGregorian = false; 75 | TimeZoneFactory timeZoneFactory = new DefaultTimeZoneFactory(); 76 | 77 | CalendarCacheState() { 78 | } 79 | } 80 | 81 | private static class DefaultTimeZoneFactory implements TimeZoneFactory { 82 | private DefaultTimeZoneFactory() { 83 | } 84 | 85 | public TimeZone getTimeZone(String id) { 86 | return TimeZone.getTimeZone(id); 87 | } 88 | } 89 | 90 | public interface TimeZoneFactory { 91 | TimeZone getTimeZone(String var1); 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/LoggerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more contributor license 3 | * agreements. See the NOTICE file distributed with this work for additional information regarding 4 | * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the 5 | * "License"); you may not use this file except in compliance with the License. You may obtain a 6 | * copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License 11 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 12 | * or implied. See the License for the specific language governing permissions and limitations under 13 | * the License. 14 | */ 15 | 16 | package com.aliyun.odps.jdbc.utils; 17 | 18 | import java.io.File; 19 | import java.lang.reflect.Method; 20 | import java.net.URL; 21 | import java.util.Map; 22 | import java.util.concurrent.ConcurrentHashMap; 23 | 24 | import org.slf4j.Logger; 25 | 26 | public class LoggerFactory { 27 | 28 | private static Map cache = new ConcurrentHashMap(); 29 | 30 | @SuppressWarnings({"rawtypes", "unchecked"}) 31 | public static Logger getLogger(String logConfFile, String name) { 32 | if (cache.containsKey(name)) { 33 | return cache.get(name); 34 | } 35 | Logger logger = null; 36 | if (logConfFile != null) { 37 | try { 38 | Class loggerContextClazz = Class.forName("ch.qos.logback.classic.LoggerContext"); 39 | Class contextInitializerClazz = 40 | Class.forName("ch.qos.logback.classic.util.ContextInitializer"); 41 | Object loggerContext = loggerContextClazz.newInstance(); 42 | Object contextInitializer = 43 | contextInitializerClazz.getConstructor(loggerContextClazz).newInstance(loggerContext); 44 | Method configureByResourceMethod = 45 | contextInitializerClazz.getMethod("configureByResource", URL.class); 46 | URL url = new File(logConfFile).toURI().toURL(); 47 | configureByResourceMethod.invoke(contextInitializer, url); 48 | Method getLoggerMethod = loggerContextClazz.getMethod("getLogger", String.class); 49 | logger = (Logger) getLoggerMethod.invoke(loggerContext, name); 50 | logger.debug("Configure logConf Successfully : {}", url); 51 | } catch (Throwable e) { 52 | org.slf4j.LoggerFactory.getLogger(name).error( 53 | "Configure logConf failed: " + logConfFile 54 | + " , replace with application's default conf ~ ", e); 55 | logger = org.slf4j.LoggerFactory.getLogger(name); 56 | } 57 | } else { 58 | logger = org.slf4j.LoggerFactory.getLogger(name); 59 | } 60 | cache.put(name, logger); 61 | return logger; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/OdpsFormatter.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc.utils; 2 | 3 | import java.sql.Timestamp; 4 | import java.util.logging.Formatter; 5 | import java.util.logging.LogRecord; 6 | 7 | public class OdpsFormatter extends Formatter { 8 | 9 | OdpsFormatter() { 10 | super(); 11 | } 12 | 13 | @Override 14 | public synchronized String format(LogRecord record) { 15 | Timestamp timestamp = new Timestamp(record.getMillis()); 16 | String format = "[%s] [%s] [%s] %s\n"; 17 | return String.format( 18 | format, 19 | record.getLevel(), 20 | record.getLoggerName(), 21 | timestamp.toString(), 22 | record.getMessage()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/OdpsLogger.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc.utils; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.PrintWriter; 6 | import java.io.StringWriter; 7 | import java.nio.file.Paths; 8 | import java.util.Map; 9 | import java.util.Objects; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | import java.util.logging.ConsoleHandler; 12 | import java.util.logging.FileHandler; 13 | import java.util.logging.Handler; 14 | import java.util.logging.Level; 15 | import java.util.logging.Logger; 16 | 17 | import com.aliyun.odps.jdbc.OdpsDriver; 18 | 19 | public class OdpsLogger { 20 | 21 | private static final String DEFAULT_OUTPUT_DIR = "/tmp"; 22 | private static Map pathToFileHandler = new ConcurrentHashMap<>(); 23 | 24 | private boolean enableOdpsLogger = false; 25 | private Logger odpsLogger; 26 | private org.slf4j.Logger sl4jLogger; 27 | private String connectionId; 28 | private boolean toConsole = false; 29 | 30 | public OdpsLogger(String name, 31 | String connectionId, 32 | String outputPath, 33 | String configFilePath, 34 | boolean toConsole, 35 | boolean enableOdpsLogger) { 36 | this(name, connectionId, outputPath, configFilePath, toConsole, enableOdpsLogger, 37 | Level.INFO); 38 | } 39 | 40 | /** 41 | * Constructor 42 | * 43 | * @param name For both odps and sl4j logger, name of the logger 44 | * @param outputPath For odps logger, output path for file handler 45 | * @param toConsole For odps logger, output to console or not 46 | * @param enableOdpsLogger For odps logger, enable or not 47 | * @param configFilePath For sl4j logger, config file path 48 | */ 49 | public OdpsLogger(String name, 50 | String connectionId, 51 | String outputPath, 52 | String configFilePath, 53 | boolean toConsole, 54 | boolean enableOdpsLogger, 55 | Level level) { 56 | 57 | this.connectionId = connectionId; 58 | 59 | this.enableOdpsLogger = enableOdpsLogger; 60 | 61 | Objects.requireNonNull(name); 62 | 63 | // Init odps logger 64 | if (outputPath == null) { 65 | outputPath = getDefaultOutputPath(); 66 | } 67 | if (enableOdpsLogger) { 68 | odpsLogger = Logger.getLogger(name); 69 | odpsLogger.setLevel(level); 70 | if (toConsole) { 71 | if (!this.toConsole) { 72 | Handler consoleHandler = new ConsoleHandler(); 73 | consoleHandler.setFormatter(new OdpsFormatter()); 74 | consoleHandler.setLevel(level); 75 | odpsLogger.addHandler(consoleHandler); 76 | this.toConsole = true; 77 | } 78 | } 79 | 80 | try { 81 | FileHandler fileHandler; 82 | if (!pathToFileHandler.containsKey(outputPath)) { 83 | fileHandler = new FileHandler(outputPath, true); 84 | fileHandler.setFormatter(new OdpsFormatter()); 85 | fileHandler.setLevel(level); 86 | pathToFileHandler.put(outputPath, fileHandler); 87 | odpsLogger.addHandler(fileHandler); 88 | } 89 | } catch (IOException e) { 90 | // ignore 91 | } 92 | } 93 | 94 | // Init sl4j logger 95 | sl4jLogger = LoggerFactory.getLogger(configFilePath, name); 96 | } 97 | 98 | public synchronized void debug(String msg) { 99 | if (enableOdpsLogger) { 100 | odpsLogger.fine(String.format("[connection-%s] %s", connectionId, msg)); 101 | } 102 | sl4jLogger.debug(msg); 103 | } 104 | 105 | public synchronized void info(String msg) { 106 | if (enableOdpsLogger) { 107 | odpsLogger.info(String.format("[connection-%s] %s", connectionId, msg)); 108 | } 109 | sl4jLogger.info(msg); 110 | } 111 | 112 | public synchronized void warn(String msg) { 113 | if (enableOdpsLogger) { 114 | odpsLogger.warning(String.format("[connection-%s] %s", connectionId, msg)); 115 | } 116 | sl4jLogger.warn(msg); 117 | } 118 | 119 | public synchronized void error(String msg) { 120 | if (enableOdpsLogger) { 121 | odpsLogger.severe(String.format("[connection-%s] %s", connectionId, msg)); 122 | } 123 | sl4jLogger.error(msg); 124 | } 125 | 126 | public synchronized void error(String msg, Throwable e) { 127 | StringWriter sw = new StringWriter(); 128 | PrintWriter pw = new PrintWriter(sw); 129 | e.printStackTrace(pw); 130 | if (enableOdpsLogger) { 131 | odpsLogger.severe(String.format("[connection-%s] %s", connectionId, msg)); 132 | odpsLogger.severe(String.format("[connection-%s] %s", connectionId, sw.toString())); 133 | } 134 | sl4jLogger.error(msg, e); 135 | } 136 | 137 | /** 138 | * Return the default output path. This method tries to return the dir of source code. If it is 139 | * not allowed due to security reason, return "/tmp" 140 | * 141 | * @return default output path 142 | */ 143 | public static String getDefaultOutputPath() { 144 | String outputPath; 145 | try { 146 | outputPath = new File(OdpsDriver.class.getProtectionDomain().getCodeSource() 147 | .getLocation().toURI()).getParent(); 148 | } catch (Exception e) { 149 | outputPath = DEFAULT_OUTPUT_DIR; 150 | } 151 | return Paths.get(outputPath, "jdbc.log").toString(); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/RecordConverterCache.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc.utils; 2 | 3 | import java.util.Map; 4 | import java.util.TimeZone; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | import com.aliyun.odps.data.converter.OdpsRecordConverter; 8 | 9 | /** 10 | * @author dingxin (zhangdingxin.zdx@alibaba-inc.com) 11 | */ 12 | public class RecordConverterCache { 13 | 14 | static Map> recordConverters; 15 | 16 | public static OdpsRecordConverter get(TimeZone timeZone) { 17 | if (timeZone == null) { 18 | timeZone = TimeZone.getDefault(); 19 | } 20 | if (recordConverters == null) { 21 | recordConverters = new ConcurrentHashMap<>(); 22 | } 23 | if (recordConverters.get(timeZone.getID()) == null) { 24 | TimeZone finalTimeZone = timeZone; 25 | recordConverters.put(timeZone.getID(), ThreadLocal.withInitial(() -> { 26 | return OdpsRecordConverter.builder() 27 | .setStrictMode(false) 28 | .enableParseNull() 29 | .timezone(finalTimeZone.getID()) 30 | .build(); 31 | })); 32 | } 33 | return recordConverters.get(timeZone.getID()).get(); 34 | } 35 | 36 | /** 37 | * clear all record converter, should be called when odps driver is de-registered 38 | * TODO: not called now 39 | */ 40 | public static void cleanup() { 41 | for (ThreadLocal converter : recordConverters.values()) { 42 | converter.remove(); 43 | } 44 | recordConverters.clear(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/SettingParser.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedHashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public class SettingParser { 9 | public static ParseResult parse(String query) { 10 | if (!query.trim().endsWith(";")) { 11 | query += ";"; 12 | } 13 | SettingParser parser = new SettingParser(); 14 | return parser.extractSetStatements(query); 15 | } 16 | 17 | enum State { 18 | DEFAULT, 19 | SINGLE_LINE_COMMENT, 20 | MULTI_LINE_COMMENT, 21 | IN_SET, 22 | IN_KEY_VALUE, 23 | STOP 24 | } 25 | 26 | public static class ParseResult { 27 | 28 | private final Map settings; 29 | private final String remainingQuery; 30 | private final List errors; 31 | 32 | public ParseResult(Map settings, String remainingQuery, List errors) { 33 | this.settings = settings; 34 | this.remainingQuery = remainingQuery; 35 | this.errors = errors; 36 | } 37 | 38 | public Map getSettings() { 39 | return settings; 40 | } 41 | 42 | public String getRemainingQuery() { 43 | return remainingQuery; 44 | } 45 | 46 | public List getErrors() { 47 | return errors; 48 | } 49 | } 50 | 51 | public ParseResult extractSetStatements(String s) { 52 | Map settings = new LinkedHashMap<>(); 53 | List errors = new ArrayList<>(); 54 | List excludeRanges = new ArrayList<>(); 55 | State currentState = State.DEFAULT; 56 | // use for ignore the case of 'set' 57 | String lowerS = s.toLowerCase(); 58 | int i = 0; 59 | int currentStartIndex = -1; 60 | 61 | while (i < s.length()) { 62 | switch (currentState) { 63 | case DEFAULT: 64 | if (i <= s.length() - 2 && s.startsWith("--", i)) { 65 | currentState = State.SINGLE_LINE_COMMENT; 66 | i += 2; 67 | } else if (i <= s.length() - 2 && s.startsWith("/*", i)) { 68 | currentState = State.MULTI_LINE_COMMENT; 69 | i += 2; 70 | } else if (i <= s.length() - 3 && lowerS.startsWith("set", i)) { 71 | if (i + 3 < s.length() && Character.isWhitespace(s.charAt(i + 3))) { 72 | currentState = State.IN_SET; 73 | currentStartIndex = i; 74 | i += 4; // Skip 'set' and the following whitespace 75 | } else { 76 | i++; 77 | } 78 | } else { 79 | if (!Character.isWhitespace(s.charAt(i))) { 80 | currentState = State.STOP; 81 | } 82 | i++; 83 | } 84 | break; 85 | case SINGLE_LINE_COMMENT: 86 | while (i < s.length() && s.charAt(i) != '\n') { 87 | i++; 88 | } 89 | if (i < s.length()) { 90 | i++; 91 | } 92 | currentState = State.DEFAULT; 93 | break; 94 | case MULTI_LINE_COMMENT: 95 | while (i < s.length()) { 96 | if (i + 1 < s.length() && s.startsWith("*/", i)) { 97 | i += 2; 98 | currentState = State.DEFAULT; 99 | break; 100 | } else { 101 | i++; 102 | } 103 | } 104 | break; 105 | case IN_SET: 106 | while (i < s.length() && Character.isWhitespace(s.charAt(i))) { 107 | i++; 108 | } 109 | if (i < s.length()) { 110 | currentState = State.IN_KEY_VALUE; 111 | } else { 112 | errors.add("Invalid SET statement: missing key-value after 'set'"); 113 | currentStartIndex = -1; 114 | currentState = State.DEFAULT; 115 | } 116 | break; 117 | case IN_KEY_VALUE: 118 | int keyValueStart = i; 119 | boolean foundSemicolon = false; 120 | while (i < s.length()) { 121 | // Allows escape of semicolons to place semicolons in settings 122 | if (s.charAt(i) == ';' && s.charAt(i - 1) != '\\') { 123 | foundSemicolon = true; 124 | i++; 125 | break; 126 | } 127 | i++; 128 | } 129 | if (foundSemicolon) { 130 | String kv = s.substring(keyValueStart, i - 1).trim(); 131 | if (parseKeyValue(kv, settings, errors)) { 132 | excludeRanges.add(new int[]{currentStartIndex, i}); 133 | } 134 | } else { 135 | errors.add("Invalid SET statement: missing semicolon"); 136 | } 137 | currentState = State.DEFAULT; 138 | currentStartIndex = -1; 139 | break; 140 | case STOP: 141 | i = s.length(); 142 | break; 143 | default: 144 | } 145 | } 146 | 147 | // Build remaining query 148 | StringBuilder remainingQuery = new StringBuilder(); 149 | int currentPos = 0; 150 | for (int[] range : excludeRanges) { 151 | if (currentPos < range[0]) { 152 | remainingQuery.append(s, currentPos, range[0]); 153 | } 154 | currentPos = range[1]; 155 | } 156 | if (currentPos < s.length()) { 157 | remainingQuery.append(s, currentPos, s.length()); 158 | } 159 | 160 | return new ParseResult(settings, remainingQuery.toString(), errors); 161 | } 162 | 163 | private boolean parseKeyValue(String kv, Map settings, List errors) { 164 | int eqIdx = kv.indexOf('='); 165 | if (eqIdx == -1) { 166 | errors.add("Invalid key-value pair '" + kv + "': missing '='"); 167 | return false; 168 | } 169 | String key = kv.substring(0, eqIdx).trim(); 170 | if (key.isEmpty()) { 171 | errors.add("Invalid key-value pair '" + kv + "': empty key"); 172 | return false; 173 | } 174 | String value = eqIdx < kv.length() - 1 ? kv.substring(eqIdx + 1).trim() : ""; 175 | value = value.replace("\\;", ";"); 176 | settings.put(key, value); 177 | return true; 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/TimeUtils.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc.utils; 2 | 3 | import java.sql.Date; 4 | import java.sql.SQLException; 5 | import java.sql.Timestamp; 6 | import java.time.Instant; 7 | import java.time.LocalDateTime; 8 | import java.time.ZoneOffset; 9 | import java.util.Calendar; 10 | import java.util.TimeZone; 11 | 12 | 13 | /** 14 | *

JDBC Time Zone Logic Description

15 | * 16 | *

17 | * The default time zone used by JDBC is UTC. This is generally sufficient in most cases, 18 | * as Java's {@link java.sql.Timestamp} and {@link java.util.Date} types will print using the local time zone. 19 | *

20 | * 21 | *

22 | * However, we can modify the time zone behavior of JDBC using the following methods: 23 | *

24 | * 25 | *
    26 | *
  1. 27 | * timezone parameter: 28 | *

    29 | * This parameter has the highest priority. If the timezone parameter is specified in the connection string, 30 | * JDBC will use that time zone. Additionally, this parameter will also configure the time zone used 31 | * for executing SQL on the server side. 32 | *

    33 | *
  2. 34 | *
  3. 35 | * useProjectTimeZone parameter: 36 | *

    37 | * This parameter has a lower priority than the timezone parameter. If the timezone parameter is not specified, 38 | * JDBC will attempt to retrieve the time zone configuration from the {@link com.aliyun.odps.Project} 39 | * and use that project's time zone. 40 | *

    41 | *
  4. 42 | *
43 | * 44 | *

45 | * By configuring these parameters appropriately, users can flexibly manage the JDBC time zone settings 46 | * according to business requirements, ensuring the consistency and accuracy of time data. 47 | *

48 | * 49 | * @see java.sql.Timestamp 50 | * @see java.util.Date 51 | * @see com.aliyun.odps.Project 52 | */ 53 | public class TimeUtils { 54 | 55 | public static final TimeZone UTC = TimeZone.getTimeZone("UTC"); 56 | 57 | public static Date getDate(Date originDate, TimeZone targetTimezone) throws SQLException { 58 | try { 59 | long millis = originDate.getTime(); 60 | long milliSecsSinceEpochNew = 61 | millis + moveToTimeZoneOffset(millis, UTC, TimeZone.getDefault()) + moveToTimeZoneOffset(millis, UTC, targetTimezone); 62 | return new Date(milliSecsSinceEpochNew); 63 | } catch (NumberFormatException ex) { 64 | throw new SQLException("Invalid date value: " + originDate); 65 | } 66 | } 67 | 68 | public static Date getDate(Instant originDate, TimeZone targetTimezone) throws SQLException { 69 | try { 70 | long millis = originDate.getEpochSecond() * 1000 + originDate.getNano() / 1000000; 71 | long milliSecsSinceEpochNew = 72 | millis + moveToTimeZoneOffset(millis, UTC, TimeZone.getDefault()) + moveToTimeZoneOffset(millis, UTC, targetTimezone); 73 | return new Date(milliSecsSinceEpochNew); 74 | } catch (NumberFormatException ex) { 75 | throw new SQLException("Invalid date value: " + originDate); 76 | } 77 | } 78 | 79 | public static Timestamp getTimestamp(Timestamp originTimestamp, TimeZone targetTimezone) throws SQLException { 80 | try { 81 | long millis = originTimestamp.getTime(); 82 | long milliSecsSinceEpochNew = 83 | millis + moveToTimeZoneOffset(millis, UTC, targetTimezone); 84 | Timestamp res = new Timestamp(milliSecsSinceEpochNew); 85 | res.setNanos(originTimestamp.getNanos()); 86 | return res; 87 | } catch (NumberFormatException ex) { 88 | throw new SQLException("Invalid date value: " + originTimestamp); 89 | } 90 | } 91 | 92 | public static Timestamp getTimestamp(LocalDateTime originTimestamp, TimeZone targetTimezone) throws SQLException { 93 | try { 94 | long millis = originTimestamp.toEpochSecond(ZoneOffset.UTC) * 1000 + originTimestamp.getNano() / 1000000; 95 | long milliSecsSinceEpochNew = 96 | millis + moveToTimeZoneOffset(millis, UTC, targetTimezone); 97 | Timestamp res = new Timestamp(milliSecsSinceEpochNew); 98 | res.setNanos(originTimestamp.getNano()); 99 | return res; 100 | } catch (NumberFormatException ex) { 101 | throw new SQLException("Invalid date value: " + originTimestamp); 102 | } 103 | } 104 | 105 | public static Timestamp getTimestamp(Instant originTimestamp, TimeZone targetTimezone) throws SQLException { 106 | try { 107 | long millis = originTimestamp.getEpochSecond() * 1000 + originTimestamp.getNano() / 1000000; 108 | long milliSecsSinceEpochNew = 109 | millis + moveToTimeZoneOffset(millis, UTC, targetTimezone); 110 | Timestamp res = new Timestamp(milliSecsSinceEpochNew); 111 | res.setNanos(originTimestamp.getNano()); 112 | return res; 113 | } catch (NumberFormatException ex) { 114 | throw new SQLException("Invalid date value: " + originTimestamp); 115 | } 116 | } 117 | 118 | /** 119 | * simplified moveToTimeZone method 120 | * 121 | * @param milliSecsSinceEpoch 122 | * @param oldTZ 123 | * @param newTZ 124 | * @return offset 125 | */ 126 | private static long moveToTimeZoneOffset( 127 | long milliSecsSinceEpoch, TimeZone oldTZ, TimeZone newTZ) { 128 | if (oldTZ.hasSameRules(newTZ)) { 129 | // same time zone 130 | return 0; 131 | } 132 | int offsetMillisInOldTZ = oldTZ.getOffset(milliSecsSinceEpoch); 133 | 134 | Calendar calendar = CalendarCache.get(oldTZ); 135 | calendar.setTimeInMillis(milliSecsSinceEpoch); 136 | 137 | int millisecondWithinDay = 138 | ((calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE)) * 60 139 | + calendar.get(Calendar.SECOND)) 140 | * 1000 141 | + calendar.get(Calendar.MILLISECOND); 142 | 143 | int era = calendar.get(Calendar.ERA); 144 | int year = calendar.get(Calendar.YEAR); 145 | int month = calendar.get(Calendar.MONTH); 146 | int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); 147 | int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); 148 | 149 | int offsetMillisInNewTZ = 150 | newTZ.getOffset(era, year, month, dayOfMonth, dayOfWeek, millisecondWithinDay); 151 | 152 | return offsetMillisInNewTZ - offsetMillisInOldTZ; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/AbstractToJdbcDateTypeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 2 | 3 | import java.sql.SQLException; 4 | import java.text.SimpleDateFormat; 5 | import java.time.ZoneId; 6 | import java.time.format.DateTimeFormatter; 7 | import java.util.Calendar; 8 | import java.util.TimeZone; 9 | 10 | import com.aliyun.odps.jdbc.utils.JdbcColumn; 11 | import com.aliyun.odps.type.TypeInfo; 12 | 13 | public abstract class AbstractToJdbcDateTypeTransformer extends AbstractToJdbcTransformer { 14 | static ThreadLocal 15 | TIME_FORMAT = 16 | ThreadLocal.withInitial(() -> new SimpleDateFormat(JdbcColumn.ODPS_TIME_FORMAT)); 17 | 18 | @Override 19 | public Object transform(Object o, String charset) throws SQLException { 20 | return this.transform(o, charset, null, null); 21 | } 22 | 23 | @Override 24 | public Object transform(Object o, String charset, TypeInfo odpsType) throws SQLException { 25 | return this.transform(o, charset, null, null, odpsType); 26 | } 27 | 28 | /** 29 | * Transform ODPS SDK object to an instance of java.util.Date subclass 30 | * 31 | * @param o java object from ODPS SDK 32 | * @param charset charset to encode byte array 33 | * @param cal a calendar object to construct java.util.Date object 34 | * @return JDBC object 35 | * @throws SQLException 36 | */ 37 | public abstract Object transform( 38 | Object o, 39 | String charset, 40 | Calendar cal, 41 | TimeZone timeZone) throws SQLException; 42 | 43 | public Object transform( 44 | Object o, 45 | String charset, 46 | Calendar cal, 47 | TimeZone timeZone, 48 | TypeInfo odpsType) throws SQLException { 49 | //default implement 50 | return transform(o, charset, cal, timeZone); 51 | } 52 | 53 | void restoreToDefaultCalendar() { 54 | TIME_FORMAT.get().setCalendar(Calendar.getInstance()); 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/AbstractToJdbcTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.io.UnsupportedEncodingException; 24 | import java.sql.SQLException; 25 | 26 | import com.aliyun.odps.type.TypeInfo; 27 | 28 | 29 | public abstract class AbstractToJdbcTransformer { 30 | 31 | static final String INVALID_TRANSFORMATION_ERROR_MSG = 32 | "Cannot transform ODPS-SDK Java class %s to %s"; 33 | static final String ENCODING_ERR_MSG = 34 | "Error happened during encoding, please check the charset"; 35 | static final String TRANSFORMATION_ERR_MSG = 36 | "Error happened when transforming %s into %s : %s"; 37 | 38 | /** 39 | * Transform ODPS SDK object to JDBC object 40 | * 41 | * @param o java object from ODPS SDK 42 | * @param charset charset to encode byte array 43 | * @return JDBC object 44 | * @throws SQLException 45 | */ 46 | public abstract Object transform(Object o, String charset) throws SQLException; 47 | 48 | public Object transform(Object o, String charset, TypeInfo odpsType) throws SQLException { 49 | // default implement 50 | return transform(o, charset); 51 | } 52 | 53 | static String getInvalidTransformationErrorMsg(Class odpsCls, Class jdbcCls) { 54 | return String.format(INVALID_TRANSFORMATION_ERROR_MSG, odpsCls.getName(), jdbcCls.getName()); 55 | } 56 | 57 | static String getTransformationErrMsg(Object o, Class jdbcCls, String errorMsg) { 58 | return String.format(TRANSFORMATION_ERR_MSG, o.toString(), jdbcCls.getName(), errorMsg); 59 | } 60 | 61 | public static String encodeBytes(byte[] bytes, String charset) throws SQLException { 62 | if (charset != null) { 63 | try { 64 | return new String(bytes, charset); 65 | } catch (UnsupportedEncodingException e) { 66 | throw new SQLException(ENCODING_ERR_MSG, e); 67 | } 68 | } 69 | return new String(bytes); 70 | } 71 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcBigDecimalTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.math.BigDecimal; 24 | import java.sql.SQLException; 25 | import java.util.Objects; 26 | 27 | import com.aliyun.odps.data.AbstractChar; 28 | import com.aliyun.odps.data.Binary; 29 | 30 | 31 | public class ToJdbcBigDecimalTransformer extends AbstractToJdbcTransformer { 32 | 33 | @Override 34 | public Object transform(Object o, String charset) throws SQLException { 35 | if (o == null) { 36 | return null; 37 | } 38 | 39 | try { 40 | if (o instanceof BigDecimal) { 41 | return o; 42 | } else if (o instanceof Number || o instanceof String || o instanceof AbstractChar) { 43 | return new BigDecimal(o.toString().trim()); 44 | } else if (o instanceof Binary) { 45 | String str = encodeBytes(((Binary) o).data(), charset); 46 | return new BigDecimal(str); 47 | } else if (o instanceof byte[]) { 48 | String str = encodeBytes((byte[]) o, charset); 49 | return new BigDecimal(str); 50 | } else { 51 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), BigDecimal.class); 52 | throw new SQLException(errorMsg); 53 | } 54 | } catch (SQLException e) { 55 | throw e; 56 | } catch (Exception e) { 57 | String 58 | errorMsg = 59 | getTransformationErrMsg(Objects.toString(o), BigDecimal.class, e.getMessage()); 60 | throw new SQLException(errorMsg, e); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcBooleanTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.sql.SQLException; 24 | 25 | /** 26 | * Mapping of Java Types to ODPS Types for {@link java.sql.ResultSet#getBoolean(int)} usage. 27 | * A transformer is applied to convert ODPS native types to match the Java byte requirement. 28 | * Following table show which ODPS types can be converted. 29 | * Incompatible types or conversion errors will result in a SQLException being thrown. 30 | *

31 | * | JAVA\ODPS | TINYINT | SMALLINT | INT | BIGINT | FLOAT | DOUBLE | DECIMAL | CHAR | VARCHAR | STRING | DATE | DATETIME | TIMESTAMP | TIMESTAMP_NTZ | BOOLEAN | BINARY | 32 | * |:----------:|:-------:|:--------:|:---:|:------:|:-----:|:------:|:-------:|:----:|:-------:|:------:|:----:|:--------:|:---------:|:-------------:|:-------:|:------:| 33 | * | boolean | Y | Y | Y | Y | Y | Y | Y | | | Y | | | | | Y | | 34 | *

35 | * Note: The 'Y' marks indicate compatible types for the transformation. 36 | */ 37 | public class ToJdbcBooleanTransformer extends AbstractToJdbcTransformer { 38 | 39 | @Override 40 | public Object transform(Object o, String charset) throws SQLException { 41 | if (o == null) { 42 | return false; 43 | } 44 | 45 | if (o instanceof Boolean) { 46 | return o; 47 | } else if (o instanceof Number) { 48 | return ((Number) o).intValue() != 0; 49 | } else if (o instanceof byte[]) { 50 | return !"0".equals(encodeBytes((byte[]) o, charset)); 51 | } else { 52 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), boolean.class); 53 | throw new SQLException(errorMsg); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcByteArrayTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.sql.SQLException; 24 | import java.util.Calendar; 25 | import java.util.Objects; 26 | import java.util.TimeZone; 27 | 28 | import com.aliyun.odps.data.Binary; 29 | import com.aliyun.odps.data.converter.OdpsRecordConverter; 30 | import com.aliyun.odps.jdbc.utils.RecordConverterCache; 31 | import com.aliyun.odps.type.TypeInfo; 32 | import com.aliyun.odps.utils.OdpsCommonUtils; 33 | 34 | 35 | /** 36 | * Mapping of Java Types to ODPS Types for {@link java.sql.ResultSet#getBytes(int)} usage. 37 | * A transformer is applied to convert ODPS native types to match the Java byte requirement. 38 | * All ODPS types can be converted. 39 | * use {@link OdpsRecordConverter} to convert object to string and use {@link RecordConverterCache} to cache the record converter. 40 | */ 41 | public class ToJdbcByteArrayTransformer extends AbstractToJdbcDateTypeTransformer { 42 | 43 | @Override 44 | public Object transform( 45 | Object o, 46 | String charset, 47 | Calendar cal, 48 | TimeZone timeZone, 49 | TypeInfo odpsType) throws SQLException { 50 | if (o == null) { 51 | return null; 52 | } 53 | if (o instanceof byte[]) { 54 | return o; 55 | } 56 | if (o instanceof Binary) { 57 | return ((Binary) o).data(); 58 | } 59 | OdpsRecordConverter odpsRecordConverter = RecordConverterCache.get(timeZone); 60 | try { 61 | return odpsRecordConverter.formatObject(o, odpsType).getBytes(charset); 62 | } catch (Exception e) { 63 | String errorMsg = getTransformationErrMsg(Objects.toString(o), byte[].class, e.getMessage()); 64 | throw new SQLException(errorMsg, e); 65 | } 66 | } 67 | 68 | @Override 69 | public Object transform(Object o, String charset, Calendar cal, TimeZone timeZone) 70 | throws SQLException { 71 | return transform(o, charset, cal, timeZone, OdpsCommonUtils.indicateTypeFromClass(o)); 72 | } 73 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcByteTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.sql.SQLException; 24 | import java.util.Objects; 25 | 26 | import com.aliyun.odps.data.AbstractChar; 27 | import com.aliyun.odps.data.Binary; 28 | 29 | /** 30 | * Mapping of Java Types to ODPS Types for {@link java.sql.ResultSet#getByte(int)} usage. 31 | * A transformer is applied to convert ODPS native types to match the Java byte requirement. 32 | * Following table show which ODPS types can be converted. 33 | * Incompatible types or conversion errors will result in a SQLException being thrown. 34 | *

35 | * | JAVA\ODPS | TINYINT | SMALLINT | INT | BIGINT | FLOAT | DOUBLE | DECIMAL | CHAR | VARCHAR | STRING | DATE | DATETIME | TIMESTAMP | TIMESTAMP_NTZ | BOOLEAN | BINARY | 36 | * |:----------:|:-------:|:--------:|:---:|:------:|:-----:|:------:|:-------:|:----:|:-------:|:------:|:----:|:--------:|:---------:|:-------------:|:-------:|:------:| 37 | * | byte | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | | | | | Y | Y | 38 | *

39 | * Note: The 'Y' marks indicate compatible types for the transformation. 40 | */ 41 | 42 | public class ToJdbcByteTransformer extends AbstractToJdbcTransformer { 43 | 44 | @Override 45 | public Object transform(Object o, String charset) throws SQLException { 46 | if (o == null) { 47 | return (byte) 0; 48 | } 49 | try { 50 | if (o instanceof Number) { 51 | return ((Number) o).byteValue(); 52 | } else if (o instanceof Boolean) { 53 | return (Boolean) o ? (byte) 1 : (byte) 0; 54 | } else if (o instanceof byte[]) { 55 | String str = encodeBytes((byte[]) o, charset); 56 | return Byte.parseByte(str); 57 | } else if (o instanceof String) { 58 | String str = (String) o; 59 | return Byte.parseByte(str); 60 | } else if (o instanceof AbstractChar) { 61 | return Byte.parseByte(((AbstractChar) o).getValue()); 62 | } else if (o instanceof Binary) { 63 | String str = encodeBytes(((Binary) o).data(), charset); 64 | return Byte.parseByte(str); 65 | } else { 66 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), byte.class); 67 | throw new SQLException(errorMsg); 68 | } 69 | } catch (SQLException e) { 70 | throw e; 71 | } catch (Exception e) { 72 | String errorMsg = getTransformationErrMsg(Objects.toString(o), byte.class, e.getMessage()); 73 | throw new SQLException(errorMsg, e); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcDateTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.sql.Date; 24 | import java.sql.SQLException; 25 | import java.time.Instant; 26 | import java.time.LocalDate; 27 | import java.time.LocalDateTime; 28 | import java.time.ZoneId; 29 | import java.time.ZonedDateTime; 30 | import java.util.Calendar; 31 | import java.util.Objects; 32 | import java.util.TimeZone; 33 | 34 | import com.aliyun.odps.data.Binary; 35 | import com.aliyun.odps.jdbc.utils.RecordConverterCache; 36 | import com.aliyun.odps.jdbc.utils.TimeUtils; 37 | import com.aliyun.odps.type.TypeInfoFactory; 38 | 39 | 40 | /** 41 | * Mapping of Java Types to ODPS Types for {@link java.sql.ResultSet#getDate(int)} usage. 42 | * A transformer is applied to convert ODPS native types to match the Java byte requirement. 43 | * Following show which ODPS types can be converted. 44 | * Incompatible types or conversion errors will result in a SQLException being thrown. 45 | *

46 | * LocalDate (DATE), ZonedDateTime (DATETIME), Instant (TIMESTAMP), LocalDateTime (TIMESTAMP_NTZ), byte[] (String), Binary (Binary) 47 | *

48 | */ 49 | public class ToJdbcDateTransformer extends AbstractToJdbcDateTypeTransformer { 50 | 51 | @Override 52 | public Object transform( 53 | Object o, 54 | String charset, 55 | Calendar cal, 56 | TimeZone timeZone) throws SQLException { 57 | if (o == null) { 58 | return null; 59 | } 60 | try { 61 | if (o instanceof byte[]) { 62 | String str = encodeBytes((byte[]) o, charset); 63 | // convert to local date 64 | o = RecordConverterCache.get(timeZone).parseObject(str, TypeInfoFactory.DATE); 65 | } 66 | if (o instanceof Binary) { 67 | String str = encodeBytes(((Binary) o).data(), charset); 68 | // convert to local date 69 | o = RecordConverterCache.get(timeZone).parseObject(str, TypeInfoFactory.DATE); 70 | } 71 | if (o instanceof LocalDate) { 72 | return TimeUtils.getDate(java.sql.Date.valueOf((LocalDate) o), timeZone); 73 | } else if (o instanceof ZonedDateTime) { 74 | Date date = Date.valueOf(((ZonedDateTime) o).toLocalDate()); 75 | return TimeUtils.getDate(date, timeZone); 76 | } else if (o instanceof Instant) { 77 | return TimeUtils.getDate((Instant) o, timeZone); 78 | } else if (o instanceof LocalDateTime) { 79 | Date date = Date.valueOf(((LocalDateTime) o).toLocalDate()); 80 | return TimeUtils.getDate(date, timeZone); 81 | } else { 82 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Date.class); 83 | throw new SQLException(errorMsg); 84 | } 85 | } catch (SQLException e) { 86 | throw e; 87 | } catch (Exception e) { 88 | String errorMsg = getTransformationErrMsg(Objects.toString(o), Date.class, e.getMessage()); 89 | throw new SQLException(errorMsg, e); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcDoubleTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.sql.SQLException; 24 | import java.util.Objects; 25 | 26 | import com.aliyun.odps.data.AbstractChar; 27 | import com.aliyun.odps.data.Binary; 28 | 29 | /** 30 | * Mapping of Java Types to ODPS Types for {@link java.sql.ResultSet#getDouble(int)} usage. 31 | * A transformer is applied to convert ODPS native types to match the Java byte requirement. 32 | * Following table show which ODPS types can be converted. 33 | * Incompatible types or conversion errors will result in a SQLException being thrown. 34 | *

35 | * | JAVA\ODPS | TINYINT | SMALLINT | INT | BIGINT | FLOAT | DOUBLE | DECIMAL | CHAR | VARCHAR | STRING | DATE | DATETIME | TIMESTAMP | TIMESTAMP_NTZ | BOOLEAN | BINARY | 36 | * |:----------:|:-------:|:--------:|:---:|:------:|:-----:|:------:|:-------:|:----:|:-------:|:------:|:----:|:--------:|:---------:|:-------------:|:-------:|:------:| 37 | * | double | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | | | | | Y | Y | 38 | *

39 | * Note: The 'Y' marks indicate compatible types for the transformation. 40 | */ 41 | public class ToJdbcDoubleTransformer extends AbstractToJdbcTransformer { 42 | 43 | @Override 44 | public Object transform(Object o, String charset) throws SQLException { 45 | if (o == null) { 46 | return (double) 0; 47 | } 48 | 49 | try { 50 | if (o instanceof Number) { 51 | return ((Number) o).doubleValue(); 52 | } else if (o instanceof Boolean) { 53 | return (Boolean) o ? (Double) 1d : (Double) 0d; 54 | } else if (o instanceof byte[]) { 55 | String str = encodeBytes((byte[]) o, charset); 56 | return Double.parseDouble(str); 57 | } else if (o instanceof String) { 58 | String str = (String) o; 59 | return Double.parseDouble(str); 60 | } else if (o instanceof AbstractChar) { 61 | return Double.parseDouble(((AbstractChar) o).getValue()); 62 | } else if (o instanceof Binary) { 63 | String str = encodeBytes(((Binary) o).data(), charset); 64 | return Double.parseDouble(str); 65 | } else { 66 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Double.class); 67 | throw new SQLException(errorMsg); 68 | } 69 | } catch (SQLException e) { 70 | throw e; 71 | } catch (Exception e) { 72 | String errorMsg = getTransformationErrMsg(Objects.toString(o), Double.class, e.getMessage()); 73 | throw new SQLException(errorMsg, e); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcFloatTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.sql.SQLException; 24 | import java.util.Objects; 25 | 26 | import com.aliyun.odps.data.AbstractChar; 27 | import com.aliyun.odps.data.Binary; 28 | 29 | /** 30 | * Mapping of Java Types to ODPS Types for {@link java.sql.ResultSet#getFloat(int)} usage. 31 | * A transformer is applied to convert ODPS native types to match the Java byte requirement. 32 | * Following table show which ODPS types can be converted. 33 | * Incompatible types or conversion errors will result in a SQLException being thrown. 34 | *

35 | * | JAVA\ODPS | TINYINT | SMALLINT | INT | BIGINT | FLOAT | DOUBLE | DECIMAL | CHAR | VARCHAR | STRING | DATE | DATETIME | TIMESTAMP | TIMESTAMP_NTZ | BOOLEAN | BINARY | 36 | * |:----------:|:-------:|:--------:|:---:|:------:|:-----:|:------:|:-------:|:----:|:-------:|:------:|:----:|:--------:|:---------:|:-------------:|:-------:|:------:| 37 | * | float | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | | | | | Y | Y | 38 | *

39 | * Note: The 'Y' marks indicate compatible types for the transformation. 40 | */ 41 | public class ToJdbcFloatTransformer extends AbstractToJdbcTransformer { 42 | 43 | @Override 44 | public Object transform(Object o, String charset) throws SQLException { 45 | if (o == null) { 46 | return (float) 0; 47 | } 48 | 49 | try { 50 | if (o instanceof Number) { 51 | return ((Number) o).floatValue(); 52 | } else if (o instanceof Boolean) { 53 | return (Boolean) o ? (Float) 1f : (Float) 0f; 54 | } else if (o instanceof byte[]) { 55 | String str = encodeBytes((byte[]) o, charset); 56 | return Float.parseFloat(str); 57 | } else if (o instanceof String) { 58 | String str = (String) o; 59 | return Float.parseFloat(str); 60 | } else if (o instanceof AbstractChar) { 61 | return Float.parseFloat(((AbstractChar) o).getValue()); 62 | } else if (o instanceof Binary) { 63 | String str = encodeBytes(((Binary) o).data(), charset); 64 | return Float.parseFloat(str); 65 | } else { 66 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Float.class); 67 | throw new SQLException(errorMsg); 68 | } 69 | } catch (SQLException e) { 70 | throw e; 71 | } catch (Exception e) { 72 | String errorMsg = getTransformationErrMsg(Objects.toString(o), Float.class, e.getMessage()); 73 | throw new SQLException(errorMsg, e); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcIntTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.sql.SQLException; 24 | import java.util.Objects; 25 | 26 | import com.aliyun.odps.data.AbstractChar; 27 | import com.aliyun.odps.data.Binary; 28 | 29 | /** 30 | * Mapping of Java Types to ODPS Types for {@link java.sql.ResultSet#getInt(int)} usage. 31 | * A transformer is applied to convert ODPS native types to match the Java byte requirement. 32 | * Following table show which ODPS types can be converted. 33 | * Incompatible types or conversion errors will result in a SQLException being thrown. 34 | *

35 | * | JAVA\ODPS | TINYINT | SMALLINT | INT | BIGINT | FLOAT | DOUBLE | DECIMAL | CHAR | VARCHAR | STRING | DATE | DATETIME | TIMESTAMP | TIMESTAMP_NTZ | BOOLEAN | BINARY | 36 | * |:----------:|:-------:|:--------:|:---:|:------:|:-----:|:------:|:-------:|:----:|:-------:|:------:|:----:|:--------:|:---------:|:-------------:|:-------:|:------:| 37 | * | int | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | | | | | Y | Y | 38 | *

39 | * Note: The 'Y' marks indicate compatible types for the transformation. 40 | */ 41 | public class ToJdbcIntTransformer extends AbstractToJdbcTransformer { 42 | 43 | @Override 44 | public Object transform(Object o, String charset) throws SQLException { 45 | if (o == null) { 46 | return 0; 47 | } 48 | try { 49 | if (o instanceof Number) { 50 | return ((Number) o).intValue(); 51 | } else if (o instanceof Boolean) { 52 | return (Boolean) o ? (Integer) 1 : (Integer) 0; 53 | } else if (o instanceof byte[]) { 54 | String str = encodeBytes((byte[]) o, charset); 55 | return Integer.parseInt(str); 56 | } else if (o instanceof String) { 57 | String str = (String) o; 58 | return Integer.parseInt(str); 59 | } else if (o instanceof AbstractChar) { 60 | return Integer.parseInt(((AbstractChar) o).getValue()); 61 | } else if (o instanceof Binary) { 62 | String str = encodeBytes(((Binary) o).data(), charset); 63 | return Integer.parseInt(str); 64 | } else { 65 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Integer.class); 66 | throw new SQLException(errorMsg); 67 | } 68 | } catch (SQLException e) { 69 | throw e; 70 | } catch (Exception e) { 71 | String errorMsg = getTransformationErrMsg(Objects.toString(o), Integer.class, e.getMessage()); 72 | throw new SQLException(errorMsg, e); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcLongTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.sql.SQLException; 24 | import java.util.Objects; 25 | 26 | import com.aliyun.odps.data.AbstractChar; 27 | import com.aliyun.odps.data.Binary; 28 | 29 | 30 | /** 31 | * Mapping of Java Types to ODPS Types for {@link java.sql.ResultSet#getLong(int)} usage. 32 | * A transformer is applied to convert ODPS native types to match the Java byte requirement. 33 | * Following table show which ODPS types can be converted. 34 | * Incompatible types or conversion errors will result in a SQLException being thrown. 35 | *

36 | * | JAVA\ODPS | TINYINT | SMALLINT | INT | BIGINT | FLOAT | DOUBLE | DECIMAL | CHAR | VARCHAR | STRING | DATE | DATETIME | TIMESTAMP | TIMESTAMP_NTZ | BOOLEAN | BINARY | 37 | * |:----------:|:-------:|:--------:|:---:|:------:|:-----:|:------:|:-------:|:----:|:-------:|:------:|:----:|:--------:|:---------:|:-------------:|:-------:|:------:| 38 | * | long | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | | | | | Y | Y | 39 | *

40 | * Note: The 'Y' marks indicate compatible types for the transformation. 41 | */ 42 | public class ToJdbcLongTransformer extends AbstractToJdbcTransformer { 43 | 44 | @Override 45 | public Object transform(Object o, String charset) throws SQLException { 46 | if (o == null) { 47 | return (long) 0; 48 | } 49 | 50 | try { 51 | if (o instanceof Number) { 52 | return ((Number) o).longValue(); 53 | } else if (o instanceof Boolean) { 54 | return (Boolean) o ? (Long) 1L : (Long) 0L; 55 | } else if (o instanceof byte[]) { 56 | String str = encodeBytes((byte[]) o, charset); 57 | return Long.parseLong(str); 58 | } else if (o instanceof String) { 59 | String str = (String) o; 60 | return Long.parseLong(str); 61 | } else if (o instanceof AbstractChar) { 62 | return Long.parseLong(((AbstractChar) o).getValue()); 63 | } else if (o instanceof Binary) { 64 | String str = encodeBytes(((Binary) o).data(), charset); 65 | return Long.parseLong(str); 66 | } else { 67 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Long.class); 68 | throw new SQLException(errorMsg); 69 | } 70 | } catch (SQLException e) { 71 | throw e; 72 | } catch (Exception e) { 73 | String errorMsg = getTransformationErrMsg(Objects.toString(o), Long.class, e.getMessage()); 74 | throw new SQLException(errorMsg, e); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcShortTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.sql.SQLException; 24 | import java.util.Objects; 25 | 26 | import com.aliyun.odps.data.AbstractChar; 27 | import com.aliyun.odps.data.Binary; 28 | 29 | /** 30 | * Mapping of Java Types to ODPS Types for {@link java.sql.ResultSet#getShort(int)} usage. 31 | * A transformer is applied to convert ODPS native types to match the Java byte requirement. 32 | * Following table show which ODPS types can be converted. 33 | * Incompatible types or conversion errors will result in a SQLException being thrown. 34 | *

35 | * | JAVA\ODPS | TINYINT | SMALLINT | INT | BIGINT | FLOAT | DOUBLE | DECIMAL | CHAR | VARCHAR | STRING | DATE | DATETIME | TIMESTAMP | TIMESTAMP_NTZ | BOOLEAN | BINARY | 36 | * |:----------:|:-------:|:--------:|:---:|:------:|:-----:|:------:|:-------:|:----:|:-------:|:------:|:----:|:--------:|:---------:|:-------------:|:-------:|:------:| 37 | * | short | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | | | | | Y | Y | 38 | *

39 | * Note: The 'Y' marks indicate compatible types for the transformation. 40 | */ 41 | public class ToJdbcShortTransformer extends AbstractToJdbcTransformer { 42 | 43 | @Override 44 | public Object transform(Object o, String charset) throws SQLException { 45 | if (o == null) { 46 | return (short) 0; 47 | } 48 | try { 49 | if (o instanceof Number) { 50 | return ((Number) o).shortValue(); 51 | } else if (o instanceof Boolean) { 52 | return (Boolean) o ? (short) 1 : (short) 0; 53 | } else if (o instanceof byte[]) { 54 | String str = encodeBytes((byte[]) o, charset); 55 | return Short.parseShort(str); 56 | } else if (o instanceof String) { 57 | String str = (String) o; 58 | return Short.parseShort(str); 59 | } else if (o instanceof AbstractChar) { 60 | return Short.parseShort(((AbstractChar) o).getValue()); 61 | } else if (o instanceof Binary) { 62 | String str = encodeBytes(((Binary) o).data(), charset); 63 | return Short.parseShort(str); 64 | } else { 65 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), short.class); 66 | throw new SQLException(errorMsg); 67 | } 68 | } catch (SQLException e) { 69 | throw e; 70 | } catch (Exception e) { 71 | String errorMsg = getTransformationErrMsg(Objects.toString(o), short.class, e.getMessage()); 72 | throw new SQLException(errorMsg, e); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcStringTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.nio.charset.StandardCharsets; 24 | import java.sql.SQLException; 25 | import java.util.Calendar; 26 | import java.util.Objects; 27 | import java.util.TimeZone; 28 | 29 | import com.aliyun.odps.data.converter.OdpsRecordConverter; 30 | import com.aliyun.odps.jdbc.utils.RecordConverterCache; 31 | import com.aliyun.odps.type.TypeInfo; 32 | import com.aliyun.odps.utils.OdpsCommonUtils; 33 | 34 | 35 | /** 36 | * Mapping of Java Types to ODPS Types for {@link java.sql.ResultSet#getString(int)} usage. 37 | * A transformer is applied to convert ODPS native types to match the Java byte requirement. 38 | * All ODPS types can be converted. 39 | * use {@link OdpsRecordConverter} to convert object to string and use {@link RecordConverterCache} to cache the record converter. 40 | */ 41 | public class ToJdbcStringTransformer extends AbstractToJdbcDateTypeTransformer { 42 | 43 | @Override 44 | public Object transform( 45 | Object o, 46 | String charset, 47 | Calendar cal, 48 | TimeZone timeZone, 49 | TypeInfo odpsType) throws SQLException { 50 | if (o == null) { 51 | return null; 52 | } 53 | if (o instanceof byte[]) { 54 | return encodeBytes((byte[]) o, charset); 55 | } 56 | OdpsRecordConverter odpsRecordConverter = RecordConverterCache.get(timeZone); 57 | 58 | try { 59 | // odps record converter always use utf-8 encoding. 60 | if (charset != null && !charset.equals(StandardCharsets.UTF_8.name())) { 61 | return new java.lang.String( 62 | (odpsRecordConverter.formatObject(o, odpsType).getBytes(StandardCharsets.UTF_8)), 63 | charset); 64 | } 65 | return odpsRecordConverter.formatObject(o, odpsType); 66 | } catch (Exception e) { 67 | String errorMsg = getTransformationErrMsg(Objects.toString(o), byte[].class, e.getMessage()); 68 | throw new SQLException(errorMsg, e); 69 | } 70 | } 71 | 72 | @Override 73 | public Object transform(Object o, String charset, Calendar cal, TimeZone timeZone) 74 | throws SQLException { 75 | return transform(o, charset, cal, timeZone, OdpsCommonUtils.indicateTypeFromClass(o)); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcTimeTransfomer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.sql.SQLException; 24 | import java.text.SimpleDateFormat; 25 | import java.time.Instant; 26 | import java.time.LocalDateTime; 27 | import java.time.ZoneId; 28 | import java.time.ZonedDateTime; 29 | import java.util.Calendar; 30 | import java.util.Objects; 31 | import java.util.TimeZone; 32 | 33 | /** 34 | * Mapping of Java Types to ODPS Types for {@link java.sql.ResultSet#getTimestamp(int)} usage. 35 | * A transformer is applied to convert ODPS native types to match the Java byte requirement. 36 | * Following show which ODPS types can be converted. 37 | * Incompatible types or conversion errors will result in a SQLException being thrown. 38 | *

39 | * ZonedDateTime (DATETIME), Instant (TIMESTAMP), LocalDateTime (TIMESTAMP_NTZ), byte[] (String), Binary (Binary) 40 | *

41 | */ 42 | public class ToJdbcTimeTransfomer extends AbstractToJdbcDateTypeTransformer { 43 | 44 | @Override 45 | public Object transform( 46 | Object o, 47 | String charset, 48 | Calendar cal, 49 | TimeZone timeZone) throws SQLException { 50 | 51 | if (o == null) { 52 | return null; 53 | } 54 | try { 55 | if (o instanceof ZonedDateTime) { 56 | if (timeZone != null) { 57 | o = ((ZonedDateTime) o).withZoneSameInstant(timeZone.toZoneId()); 58 | } 59 | return java.sql.Time.valueOf(((ZonedDateTime) o).toLocalTime()); 60 | } else if (o instanceof Instant) { 61 | ZonedDateTime 62 | zonedDateTime = 63 | ZonedDateTime.ofInstant((Instant) o, 64 | timeZone == null ? ZoneId.systemDefault() 65 | : timeZone.toZoneId()); 66 | return java.sql.Time.valueOf(zonedDateTime.toLocalTime()); 67 | } else if (o instanceof LocalDateTime) { 68 | return ((LocalDateTime) o).toLocalTime(); 69 | } else if (o instanceof byte[]) { 70 | o = encodeBytes((byte[]) o, charset); 71 | try { 72 | SimpleDateFormat timeFormat = TIME_FORMAT.get(); 73 | if (cal != null) { 74 | timeFormat.setCalendar(cal); 75 | } 76 | return new java.sql.Time(timeFormat.parse((String) o).getTime()); 77 | } finally { 78 | restoreToDefaultCalendar(); 79 | } 80 | } else { 81 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), java.sql.Timestamp.class); 82 | throw new SQLException(errorMsg); 83 | } 84 | } catch (SQLException e) { 85 | throw e; 86 | } catch (Exception e) { 87 | String 88 | errorMsg = 89 | getTransformationErrMsg(Objects.toString(o), java.sql.Time.class, e.getMessage()); 90 | throw new SQLException(errorMsg, e); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcTimestampTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.sql.SQLException; 24 | import java.sql.Timestamp; 25 | import java.time.Instant; 26 | import java.time.LocalDateTime; 27 | import java.time.ZoneId; 28 | import java.time.ZonedDateTime; 29 | import java.util.Calendar; 30 | import java.util.Objects; 31 | import java.util.TimeZone; 32 | 33 | import com.aliyun.odps.OdpsType; 34 | import com.aliyun.odps.data.Binary; 35 | import com.aliyun.odps.jdbc.utils.RecordConverterCache; 36 | import com.aliyun.odps.jdbc.utils.TimeUtils; 37 | import com.aliyun.odps.type.TypeInfo; 38 | import com.aliyun.odps.type.TypeInfoFactory; 39 | import com.aliyun.odps.utils.OdpsCommonUtils; 40 | 41 | 42 | /** 43 | * Mapping of Java Types to ODPS Types for {@link java.sql.ResultSet#getTimestamp(int)} usage. 44 | * A transformer is applied to convert ODPS native types to match the Java byte requirement. 45 | * Following show which ODPS types can be converted. 46 | * Incompatible types or conversion errors will result in a SQLException being thrown. 47 | *

48 | * ZonedDateTime (DATETIME), Instant (TIMESTAMP), LocalDateTime (TIMESTAMP_NTZ), byte[] (String), Binary (Binary) 49 | *

50 | */ 51 | public class ToJdbcTimestampTransformer extends AbstractToJdbcDateTypeTransformer { 52 | 53 | @Override 54 | public Object transform( 55 | Object o, 56 | String charset, 57 | Calendar cal, 58 | TimeZone timeZone) throws SQLException { 59 | return transform(o, charset, cal, timeZone, OdpsCommonUtils.indicateTypeFromClass(o)); 60 | } 61 | 62 | @Override 63 | public Object transform( 64 | Object o, 65 | String charset, 66 | Calendar cal, 67 | TimeZone timeZone, 68 | TypeInfo typeInfo) throws SQLException { 69 | if (o == null) { 70 | return null; 71 | } 72 | // if typeInfo is null or not time type, use default (TIMESTAMP) 73 | if (typeInfo == null || (typeInfo.getOdpsType() != OdpsType.DATETIME 74 | && typeInfo.getOdpsType() != OdpsType.TIMESTAMP 75 | && typeInfo.getOdpsType() != OdpsType.TIMESTAMP_NTZ)) { 76 | typeInfo = TypeInfoFactory.TIMESTAMP; 77 | } 78 | try { 79 | if (o instanceof byte[]) { 80 | String str = encodeBytes((byte[]) o, charset); 81 | // convert to local date 82 | o = RecordConverterCache.get(timeZone).parseObject(str, typeInfo); 83 | } 84 | if (o instanceof Binary) { 85 | String str = encodeBytes(((Binary) o).data(), charset); 86 | // convert to local date 87 | o = RecordConverterCache.get(timeZone).parseObject(str, typeInfo); 88 | } 89 | 90 | if (o instanceof ZonedDateTime) { 91 | return TimeUtils.getTimestamp(((ZonedDateTime) o).toInstant(), timeZone); 92 | } else if (o instanceof Instant) { 93 | return TimeUtils.getTimestamp((Instant) o, timeZone); 94 | } else if (o instanceof LocalDateTime) { 95 | // LocalDatetime is not time zone aware, so we always convert it to UTC 96 | return TimeUtils.getTimestamp((LocalDateTime) o, TimeUtils.UTC); 97 | } else { 98 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), java.sql.Timestamp.class); 99 | throw new SQLException(errorMsg); 100 | } 101 | } catch (SQLException e) { 102 | throw e; 103 | } catch (Exception e) { 104 | String 105 | errorMsg = 106 | getTransformationErrMsg(Objects.toString(o), java.sql.Timestamp.class, e.getMessage()); 107 | throw new SQLException(errorMsg, e); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/jdbc/ToJdbcTransformerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.jdbc; 22 | 23 | import java.math.BigDecimal; 24 | import java.sql.SQLException; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | 29 | public class ToJdbcTransformerFactory { 30 | 31 | private ToJdbcTransformerFactory() { 32 | } 33 | 34 | private static final ToJdbcByteTransformer BYTE_TRANSFORMER = new ToJdbcByteTransformer(); 35 | private static final ToJdbcShortTransformer SHORT_TRANSFORMER = new ToJdbcShortTransformer(); 36 | private static final ToJdbcIntTransformer INT_TRANSFORMER = new ToJdbcIntTransformer(); 37 | private static final ToJdbcLongTransformer LONG_TRANSFORMER = new ToJdbcLongTransformer(); 38 | private static final ToJdbcFloatTransformer FLOAT_TRANSFORMER = new ToJdbcFloatTransformer(); 39 | private static final ToJdbcDoubleTransformer DOUBLE_TRANSFORMER = new ToJdbcDoubleTransformer(); 40 | private static final ToJdbcBigDecimalTransformer 41 | BIG_DECIMAL_TRANSFORMER = 42 | new ToJdbcBigDecimalTransformer(); 43 | private static final ToJdbcStringTransformer STRING_TRANSFORMER = new ToJdbcStringTransformer(); 44 | private static final ToJdbcByteArrayTransformer 45 | BYTE_ARRAY_TRANSFORMER = 46 | new ToJdbcByteArrayTransformer(); 47 | private static final ToJdbcDateTransformer DATE_TRANSFORMER = new ToJdbcDateTransformer(); 48 | private static final ToJdbcTimeTransfomer TIME_TRANSFORMER = new ToJdbcTimeTransfomer(); 49 | private static final ToJdbcTimestampTransformer 50 | TIMESTAMP_TRANSFORMER = 51 | new ToJdbcTimestampTransformer(); 52 | private static final ToJdbcBooleanTransformer 53 | BOOLEAN_TRANSFORMER = 54 | new ToJdbcBooleanTransformer(); 55 | 56 | private static final Map JDBC_CLASS_TO_TRANSFORMER = 57 | new HashMap(); 58 | 59 | static { 60 | JDBC_CLASS_TO_TRANSFORMER.put(byte.class, BYTE_TRANSFORMER); 61 | JDBC_CLASS_TO_TRANSFORMER.put(short.class, SHORT_TRANSFORMER); 62 | JDBC_CLASS_TO_TRANSFORMER.put(int.class, INT_TRANSFORMER); 63 | JDBC_CLASS_TO_TRANSFORMER.put(long.class, LONG_TRANSFORMER); 64 | JDBC_CLASS_TO_TRANSFORMER.put(float.class, FLOAT_TRANSFORMER); 65 | JDBC_CLASS_TO_TRANSFORMER.put(double.class, DOUBLE_TRANSFORMER); 66 | JDBC_CLASS_TO_TRANSFORMER.put(BigDecimal.class, BIG_DECIMAL_TRANSFORMER); 67 | JDBC_CLASS_TO_TRANSFORMER.put(String.class, STRING_TRANSFORMER); 68 | JDBC_CLASS_TO_TRANSFORMER.put(byte[].class, BYTE_ARRAY_TRANSFORMER); 69 | JDBC_CLASS_TO_TRANSFORMER.put(java.sql.Date.class, DATE_TRANSFORMER); 70 | JDBC_CLASS_TO_TRANSFORMER.put(java.sql.Time.class, TIME_TRANSFORMER); 71 | JDBC_CLASS_TO_TRANSFORMER.put(java.sql.Timestamp.class, TIMESTAMP_TRANSFORMER); 72 | JDBC_CLASS_TO_TRANSFORMER.put(boolean.class, BOOLEAN_TRANSFORMER); 73 | } 74 | 75 | public static AbstractToJdbcTransformer getTransformer(Class jdbcCls) throws SQLException { 76 | AbstractToJdbcTransformer transformer = JDBC_CLASS_TO_TRANSFORMER.get(jdbcCls); 77 | if (transformer == null) { 78 | throw new SQLException("Not supported JDBC class: " + jdbcCls.getName()); 79 | } 80 | return transformer; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/AbstractToOdpsTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.io.UnsupportedEncodingException; 24 | import java.sql.SQLException; 25 | 26 | public abstract class AbstractToOdpsTransformer { 27 | 28 | static final String INVALID_TRANSFORMATION_ERROR_MSG = 29 | "Cannot transform JDBC java class %s to %s"; 30 | static final String ENCODING_ERR_MSG = 31 | "Error happened during encoding, please check the charset"; 32 | static final String TRANSFORMATION_ERR_MSG = 33 | "Error happened when transforming %s into %s"; 34 | 35 | /** 36 | * Transform JDBC object to ODPS SDK object 37 | * 38 | * @param o JDBC object 39 | * @return ODPS SDK object 40 | * @throws SQLException 41 | */ 42 | public abstract Object transform(Object o, String charset) throws SQLException; 43 | 44 | static String getInvalidTransformationErrorMsg(Class jdbcCls, Class odpsCls) { 45 | return String.format(INVALID_TRANSFORMATION_ERROR_MSG, jdbcCls.getName(), odpsCls.getName()); 46 | } 47 | 48 | static String getTransformationErrMsg(Object o, Class jdbcCls) { 49 | return String.format(TRANSFORMATION_ERR_MSG, o.toString(), jdbcCls.getName()); 50 | } 51 | 52 | public static String encodeBytes(byte[] bytes, String charset) throws SQLException { 53 | if (charset != null) { 54 | try { 55 | return new String(bytes, charset); 56 | } catch (UnsupportedEncodingException e) { 57 | throw new SQLException(ENCODING_ERR_MSG, e); 58 | } 59 | } 60 | return new String(bytes); 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsBigIntTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | 25 | public class ToOdpsBigIntTransformer extends AbstractToOdpsTransformer { 26 | 27 | @Override 28 | public Object transform(Object o, String charset) throws SQLException { 29 | if (o == null) { 30 | return null; 31 | } 32 | 33 | if (Long.class.isInstance(o)) { 34 | return o; 35 | } else { 36 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Long.class); 37 | throw new SQLException(errorMsg); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsBinaryTransformer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 2 | 3 | import java.sql.SQLException; 4 | 5 | import com.aliyun.odps.data.Binary; 6 | 7 | public class ToOdpsBinaryTransformer extends AbstractToOdpsTransformer { 8 | 9 | @Override 10 | public Object transform(Object o, String charset) throws SQLException { 11 | if (o == null) { 12 | return null; 13 | } 14 | 15 | if (Binary.class.isInstance(o)) { 16 | return o; 17 | } else if (String.class.isInstance(o)) { 18 | return new Binary(((String) o).getBytes()); 19 | } else if (byte[].class.isInstance(o)) { 20 | return new Binary((byte[]) o); 21 | } else { 22 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Binary.class); 23 | throw new SQLException(errorMsg); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsBooleanTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | 25 | public class ToOdpsBooleanTransformer extends AbstractToOdpsTransformer { 26 | 27 | @Override 28 | public Object transform(Object o, String charset) throws SQLException { 29 | if (o == null) { 30 | return null; 31 | } 32 | 33 | if (Boolean.class.isInstance(o)) { 34 | return o; 35 | } else { 36 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Boolean.class); 37 | throw new SQLException(errorMsg); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsCharTransformer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 2 | 3 | import java.sql.SQLException; 4 | 5 | import com.aliyun.odps.data.Char; 6 | 7 | public class ToOdpsCharTransformer extends AbstractToOdpsTransformer { 8 | 9 | @Override 10 | public Object transform(Object o, String charset) throws SQLException { 11 | if (o == null) { 12 | return null; 13 | } 14 | 15 | if (Char.class.isInstance(o)) { 16 | return o; 17 | } else if (String.class.isInstance(o)) { 18 | return new Char((String) o); 19 | } else if (byte[].class.isInstance(o)) { 20 | return new Char(encodeBytes((byte[]) o, charset)); 21 | } else { 22 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Char.class); 23 | throw new SQLException(errorMsg); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsDateTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | import java.time.ZoneId; 25 | 26 | public class ToOdpsDateTransformer extends AbstractToOdpsTransformer { 27 | 28 | @Override 29 | public Object transform(Object o, String charset) throws SQLException { 30 | if (o == null) { 31 | return null; 32 | } 33 | 34 | if (java.sql.Date.class.isInstance(o)) { 35 | return new java.util.Date(((java.sql.Date) o).getTime()).toInstant() 36 | .atZone(ZoneId.systemDefault()).toLocalDate(); 37 | } else if (java.util.Date.class.isInstance(o)) { 38 | return ((java.util.Date) o).toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); 39 | } else { 40 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), java.sql.Date.class); 41 | throw new SQLException(errorMsg); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsDatetimeTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | import java.sql.Time; 25 | import java.sql.Timestamp; 26 | import java.time.ZoneId; 27 | import java.time.ZonedDateTime; 28 | import java.util.Date; 29 | 30 | public class ToOdpsDatetimeTransformer extends AbstractToOdpsTransformer { 31 | 32 | @Override 33 | public Object transform(Object o, String charset) throws SQLException { 34 | if (o == null) { 35 | return null; 36 | } 37 | 38 | if (Timestamp.class.isInstance(o) 39 | || java.sql.Date.class.isInstance(o) 40 | || Time.class.isInstance(o)) { 41 | return new java.util.Date(((Date) o).getTime()).toInstant().atZone(ZoneId.systemDefault()); 42 | } else if (Date.class.isInstance(o)) { 43 | return ((java.util.Date) o).toInstant().atZone(ZoneId.systemDefault()); 44 | } else if (ZonedDateTime.class.isInstance(o)) { 45 | return o; 46 | } else { 47 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Date.class); 48 | throw new SQLException(errorMsg); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsDecimalTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.math.BigDecimal; 24 | import java.sql.SQLException; 25 | 26 | public class ToOdpsDecimalTransformer extends AbstractToOdpsTransformer { 27 | 28 | @Override 29 | public Object transform(Object o, String charset) throws SQLException { 30 | if (o == null) { 31 | return null; 32 | } 33 | 34 | if (BigDecimal.class.isInstance(o)) { 35 | return o; 36 | } else { 37 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), BigDecimal.class); 38 | throw new SQLException(errorMsg); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsDoubleTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | 25 | public class ToOdpsDoubleTransformer extends AbstractToOdpsTransformer { 26 | 27 | @Override 28 | public Object transform(Object o, String charset) throws SQLException { 29 | if (o == null) { 30 | return null; 31 | } 32 | 33 | if (Double.class.isInstance(o)) { 34 | return o; 35 | } else { 36 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Double.class); 37 | throw new SQLException(errorMsg); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsFloatTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | 25 | public class ToOdpsFloatTransformer extends AbstractToOdpsTransformer { 26 | 27 | @Override 28 | public Object transform(Object o, String charset) throws SQLException { 29 | if (o == null) { 30 | return null; 31 | } 32 | 33 | if (Float.class.isInstance(o)) { 34 | return o; 35 | } else { 36 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Float.class); 37 | throw new SQLException(errorMsg); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsIntTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | 25 | public class ToOdpsIntTransformer extends AbstractToOdpsTransformer { 26 | 27 | @Override 28 | public Object transform(Object o, String charset) throws SQLException { 29 | if (o == null) { 30 | return null; 31 | } 32 | 33 | if (Integer.class.isInstance(o)) { 34 | return o; 35 | } else { 36 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Integer.class); 37 | throw new SQLException(errorMsg); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsJsonTransformer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 2 | 3 | import java.sql.SQLException; 4 | 5 | import com.aliyun.odps.data.SimpleJsonValue; 6 | 7 | public class ToOdpsJsonTransformer extends AbstractToOdpsTransformer { 8 | 9 | @Override 10 | public Object transform(Object o, String charset) throws SQLException { 11 | if (o == null) { 12 | return null; 13 | } 14 | 15 | if (String.class.isInstance(o)) { 16 | return new SimpleJsonValue((String) o); 17 | } else if (byte[].class.isInstance(o)) { 18 | return new SimpleJsonValue(encodeBytes((byte[]) o, charset)); 19 | } else { 20 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), String.class); 21 | throw new SQLException(errorMsg); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsSmallintTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | 25 | public class ToOdpsSmallintTransformer extends AbstractToOdpsTransformer { 26 | 27 | @Override 28 | public Object transform(Object o, String charset) throws SQLException { 29 | if (o == null) { 30 | return null; 31 | } 32 | 33 | if (Short.class.isInstance(o)) { 34 | return o; 35 | } else { 36 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Short.class); 37 | throw new SQLException(errorMsg); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsStringTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | 25 | public class ToOdpsStringTransformer extends AbstractToOdpsTransformer { 26 | 27 | @Override 28 | public Object transform(Object o, String charset) throws SQLException { 29 | if (o == null) { 30 | return null; 31 | } 32 | 33 | if (String.class.isInstance(o)) { 34 | return o; 35 | } else if (byte[].class.isInstance(o)) { 36 | return encodeBytes((byte[]) o, charset); 37 | } else { 38 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), String.class); 39 | throw new SQLException(errorMsg); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsTimeStampTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | import java.sql.Timestamp; 25 | import java.time.Instant; 26 | import java.time.ZoneId; 27 | 28 | public class ToOdpsTimeStampTransformer extends AbstractToOdpsTransformer { 29 | 30 | @Override 31 | public Object transform(Object o, String charset) throws SQLException { 32 | if (o == null) { 33 | return null; 34 | } 35 | 36 | if (Timestamp.class.isInstance(o)) { 37 | return ((Timestamp) o).toInstant().atZone(ZoneId.systemDefault()).toInstant(); 38 | } else if (Instant.class.isInstance(o)) { 39 | return o; 40 | } else { 41 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Timestamp.class); 42 | throw new SQLException(errorMsg); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsTinyintTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | 25 | public class ToOdpsTinyintTransformer extends AbstractToOdpsTransformer { 26 | 27 | @Override 28 | public Object transform(Object o, String charset) throws SQLException { 29 | if (o == null) { 30 | return null; 31 | } 32 | 33 | if (Byte.class.isInstance(o)) { 34 | return o; 35 | } else { 36 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Byte.class); 37 | throw new SQLException(errorMsg); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsTransformerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | import java.util.HashMap; 25 | import java.util.Map; 26 | 27 | import com.aliyun.odps.OdpsType; 28 | 29 | public class ToOdpsTransformerFactory { 30 | 31 | private ToOdpsTransformerFactory() { 32 | } 33 | 34 | private static ToOdpsTinyintTransformer TINYINT_TRANSFORMER = new ToOdpsTinyintTransformer(); 35 | private static ToOdpsSmallintTransformer SMALLINT_TRANSFORMER = new ToOdpsSmallintTransformer(); 36 | private static ToOdpsIntTransformer INT_TRANSFORMER = new ToOdpsIntTransformer(); 37 | private static ToOdpsBigIntTransformer BIGINT_TRANSFORMER = new ToOdpsBigIntTransformer(); 38 | private static ToOdpsFloatTransformer FLOAT_TRANSFORMER = new ToOdpsFloatTransformer(); 39 | private static ToOdpsDoubleTransformer DOUBLE_TRANSFORMER = new ToOdpsDoubleTransformer(); 40 | private static ToOdpsDecimalTransformer DECIMAL_TRANSFORMER = new ToOdpsDecimalTransformer(); 41 | private static ToOdpsVarcharTransformer VARCHAR_TRANSFORMER = new ToOdpsVarcharTransformer(); 42 | private static ToOdpsStringTransformer STRING_TRANSFORMER = new ToOdpsStringTransformer(); 43 | private static ToOdpsDatetimeTransformer DATETIME_TRANSFORMER = new ToOdpsDatetimeTransformer(); 44 | private static ToOdpsTimeStampTransformer 45 | TIMESTAMP_TRANSFORMER = 46 | new ToOdpsTimeStampTransformer(); 47 | private static ToOdpsDateTransformer DATE_TRANSFORMER = new ToOdpsDateTransformer(); 48 | private static ToOdpsBooleanTransformer BOOLEAN_TRANSFORMER = new ToOdpsBooleanTransformer(); 49 | private static ToOdpsBinaryTransformer BINARY_TRANSFORMER = new ToOdpsBinaryTransformer(); 50 | private static ToOdpsCharTransformer CHAR_TRANSFORMER = new ToOdpsCharTransformer(); 51 | private static ToOdpsJsonTransformer JSON_TRANSFORMER = new ToOdpsJsonTransformer(); 52 | 53 | private static final Map ODPS_TYPE_TO_TRANSFORMER = 54 | new HashMap<>(); 55 | 56 | static { 57 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.TINYINT, TINYINT_TRANSFORMER); 58 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.SMALLINT, SMALLINT_TRANSFORMER); 59 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.INT, INT_TRANSFORMER); 60 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.BIGINT, BIGINT_TRANSFORMER); 61 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.FLOAT, FLOAT_TRANSFORMER); 62 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.DOUBLE, DOUBLE_TRANSFORMER); 63 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.DECIMAL, DECIMAL_TRANSFORMER); 64 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.VARCHAR, VARCHAR_TRANSFORMER); 65 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.STRING, STRING_TRANSFORMER); 66 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.DATETIME, DATETIME_TRANSFORMER); 67 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.TIMESTAMP, TIMESTAMP_TRANSFORMER); 68 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.DATE, DATE_TRANSFORMER); 69 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.BOOLEAN, BOOLEAN_TRANSFORMER); 70 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.BINARY, BINARY_TRANSFORMER); 71 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.CHAR, CHAR_TRANSFORMER); 72 | ODPS_TYPE_TO_TRANSFORMER.put(OdpsType.JSON, JSON_TRANSFORMER); 73 | } 74 | 75 | public static AbstractToOdpsTransformer getTransformer(OdpsType odpsType) throws SQLException { 76 | AbstractToOdpsTransformer transformer = ODPS_TYPE_TO_TRANSFORMER.get(odpsType); 77 | if (transformer == null) { 78 | throw new SQLException("Not supported ODPS type: " + odpsType); 79 | } 80 | return transformer; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/odps/jdbc/utils/transformer/to/odps/ToOdpsVarcharTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc.utils.transformer.to.odps; 22 | 23 | import java.sql.SQLException; 24 | 25 | import com.aliyun.odps.data.Varchar; 26 | 27 | public class ToOdpsVarcharTransformer extends AbstractToOdpsTransformer { 28 | 29 | @Override 30 | public Object transform(Object o, String charset) throws SQLException { 31 | if (o == null) { 32 | return null; 33 | } 34 | 35 | if (String.class.isInstance(o)) { 36 | return new Varchar((String) o); 37 | } else if (byte[].class.isInstance(o)) { 38 | return new Varchar(encodeBytes((byte[]) o, charset)); 39 | } else { 40 | String errorMsg = getInvalidTransformationErrorMsg(o.getClass(), Varchar.class); 41 | throw new SQLException(errorMsg); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/java.sql.Driver: -------------------------------------------------------------------------------- 1 | com.aliyun.odps.jdbc.OdpsDriver 2 | -------------------------------------------------------------------------------- /src/main/resources/maxcompute-version.properties: -------------------------------------------------------------------------------- 1 | driver.version=${project.version} 2 | sdk.version=${sdk.version} 3 | -------------------------------------------------------------------------------- /src/test/java/com/aliyun/odps/jdbc/InstanceTunnelTest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.sql.Connection; 6 | import java.sql.DriverManager; 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.sql.Statement; 10 | import java.util.Properties; 11 | 12 | import org.junit.Test; 13 | 14 | /** 15 | * @author dingxin (zhangdingxin.zdx@alibaba-inc.com) 16 | */ 17 | public class InstanceTunnelTest { 18 | private static Connection conn; 19 | 20 | public static void prepare() { 21 | try { 22 | String driverName = "com.aliyun.odps.jdbc.OdpsDriver"; 23 | Class.forName(driverName); 24 | } catch (ClassNotFoundException e) { 25 | e.printStackTrace(); 26 | System.exit(1); 27 | } 28 | 29 | // fill in the information string 30 | Properties odpsConfig = new Properties(); 31 | InputStream is = 32 | Thread.currentThread().getContextClassLoader().getResourceAsStream("conf.properties"); 33 | try { 34 | odpsConfig.load(is); 35 | } catch (IOException e) { 36 | e.printStackTrace(); 37 | System.exit(1); 38 | } 39 | 40 | String accessId = odpsConfig.getProperty("access_id"); 41 | String accessKey = odpsConfig.getProperty("access_key"); 42 | String endpoint = odpsConfig.getProperty("end_point"); 43 | String pj = odpsConfig.getProperty("project_name"); 44 | conn = null; 45 | try { 46 | conn = 47 | DriverManager.getConnection( 48 | "jdbc:odps:" + endpoint + "?project=" + pj + "&enableOdpsLogger=true&enableLimit=false", accessId, 49 | accessKey); 50 | } catch (SQLException e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | 55 | @Test 56 | public void testShowCreateTable() throws Exception { 57 | prepare(); 58 | Statement statement = conn.createStatement(); 59 | ResultSet resultSet = statement.executeQuery("show create table aaa;"); 60 | boolean next = resultSet.next(); 61 | while (next) { 62 | System.out.println(resultSet.getString(1)); 63 | next = resultSet.next(); 64 | } 65 | } 66 | 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/com/aliyun/odps/jdbc/MaxQATest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.sql.Connection; 4 | 5 | import org.junit.Test; 6 | 7 | import com.aliyun.odps.jdbc.utils.TestUtils; 8 | import com.google.common.collect.ImmutableMap; 9 | 10 | /** 11 | * @author dingxin (zhangdingxin.zdx@alibaba-inc.com) 12 | */ 13 | public class MaxQATest { 14 | 15 | @Test 16 | public void testMaxQA() throws Exception { 17 | Connection 18 | connection = 19 | TestUtils.getConnection( 20 | ImmutableMap.of("interactiveMode", "MaxQA", "quotaName", "maxqa_tpcds_test_32cu")); 21 | boolean execute = connection.createStatement().execute("select 1;"); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/aliyun/odps/jdbc/OdpsJdbcDateTest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.sql.Connection; 6 | import java.sql.Date; 7 | import java.sql.DriverManager; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.time.LocalDate; 11 | import java.util.Properties; 12 | import java.util.TimeZone; 13 | 14 | import org.junit.Assert; 15 | import org.junit.Test; 16 | 17 | import com.aliyun.odps.jdbc.utils.TimeUtils; 18 | 19 | public class OdpsJdbcDateTest { 20 | 21 | private static Connection conn; 22 | 23 | public static void prepare() { 24 | try { 25 | String driverName = "com.aliyun.odps.jdbc.OdpsDriver"; 26 | Class.forName(driverName); 27 | } catch (ClassNotFoundException e) { 28 | e.printStackTrace(); 29 | System.exit(1); 30 | } 31 | 32 | // fill in the information string 33 | Properties odpsConfig = new Properties(); 34 | InputStream is = 35 | Thread.currentThread().getContextClassLoader().getResourceAsStream("conf.properties"); 36 | try { 37 | odpsConfig.load(is); 38 | } catch (IOException e) { 39 | e.printStackTrace(); 40 | System.exit(1); 41 | } 42 | 43 | String accessId = odpsConfig.getProperty("access_id"); 44 | String accessKey = odpsConfig.getProperty("access_key"); 45 | String endpoint = odpsConfig.getProperty("end_point"); 46 | String pj = odpsConfig.getProperty("project_name"); 47 | conn = null; 48 | try { 49 | conn = 50 | DriverManager.getConnection( 51 | "jdbc:odps:" + endpoint + "?project=" + pj + "&enableOdpsLogger=true&useProjectTimeZone=true&timezone=UTC", accessId, 52 | accessKey); 53 | } catch (SQLException e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | 58 | /** 59 | * 2024-0930 UTC 60 | * 1727625600000 at UTC 61 | * 1727654400000 at Asia/Shanghai 62 | */ 63 | @Test 64 | public void testTimezoneE2E() { 65 | prepare(); 66 | // mock jdbc timezone is Asia/Shanghai 67 | TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); 68 | try { 69 | ResultSet 70 | resultSet = 71 | conn.createStatement().executeQuery("SELECT `tableausql`.`t1` AS `t1`,\n" 72 | + " `tableausql`.`t2` AS `t2`,\n" 73 | + " `tableausql`.`t3` AS `t3`\n" 74 | + "FROM (\n" 75 | + " select\n" 76 | + " '2024-09-30' t1\n" 77 | + " ,to_date('2024-09-30') t2\n" 78 | + " ,date(to_date('2024-09-30 23:59:59') ) t3\n" 79 | + ") `tableausql`\n" 80 | + "LIMIT 200;"); 81 | resultSet.next(); 82 | Object date1 = resultSet.getObject(2); 83 | Object date2 = resultSet.getObject(3); 84 | 85 | LocalDate localDate = (LocalDate) date1; 86 | Date origin = Date.valueOf(localDate); 87 | Date afterBugfix = resultSet.getDate(2); 88 | 89 | Assert.assertEquals(date1, date2); 90 | 91 | // mock client timezone is utc 92 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); 93 | System.out.println(afterBugfix); 94 | System.out.println(origin); 95 | 96 | Assert.assertEquals("2024-09-30", afterBugfix.toString()); 97 | } catch (SQLException e) { 98 | e.printStackTrace(); 99 | } 100 | } 101 | 102 | @Test 103 | public void testTimezone() throws SQLException { 104 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); 105 | LocalDate localDate = LocalDate.of(2025, 5, 1); 106 | Date date = Date.valueOf(localDate); 107 | System.out.println(date.getTime()); // 1746057600000 2025-05-01 08:00:00 Shanghai 108 | System.out.println(date.getTimezoneOffset()); 109 | System.out.println(date); 110 | 111 | TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); 112 | date = Date.valueOf(localDate); 113 | System.out.println(date.getTime()); // 1746028800000 2025-05-01 00:00:00 Shanghai 114 | 115 | Date afterConvert = TimeUtils.getDate(date, TimeZone.getTimeZone("UTC")); 116 | System.out.println(afterConvert.getTime()); // 1746057600000 2025-05-01 08:00:00 Shanghai 117 | 118 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")); 119 | System.out.println(date); // 2025-04-30 120 | System.out.println(afterConvert); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/test/java/com/aliyun/odps/jdbc/OdpsResultSetMetaDataTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc; 22 | 23 | import java.sql.ResultSet; 24 | import java.sql.ResultSetMetaData; 25 | import java.sql.Statement; 26 | import java.sql.Types; 27 | 28 | import org.junit.AfterClass; 29 | import org.junit.Assert; 30 | import org.junit.BeforeClass; 31 | import org.junit.Test; 32 | 33 | import com.aliyun.odps.data.Record; 34 | import com.aliyun.odps.data.RecordWriter; 35 | import com.aliyun.odps.tunnel.TableTunnel; 36 | 37 | public class OdpsResultSetMetaDataTest { 38 | 39 | static Statement stmt; 40 | static ResultSet rs; 41 | static ResultSetMetaData rsmd; 42 | 43 | @BeforeClass 44 | public static void setUp() throws Exception { 45 | stmt = TestManager.getInstance().conn.createStatement(); 46 | stmt.executeUpdate("drop table if exists dual;"); 47 | stmt.executeUpdate("create table if not exists dual(id bigint);"); 48 | 49 | TableTunnel.UploadSession upload = TestManager.getInstance().tunnel.createUploadSession( 50 | TestManager.getInstance().odps.getDefaultProject(), "dual"); 51 | RecordWriter writer = upload.openRecordWriter(0); 52 | Record r = upload.newRecord(); 53 | r.setBigint(0, 42L); 54 | writer.write(r); 55 | writer.close(); 56 | upload.commit(new Long[]{0L}); 57 | 58 | String sql = "select 'yichao' name, true male, 25 age, 173.5 height, " 59 | + "cast('2015-07-09 11:11:11' as datetime) day, " 60 | + "cast('2.1234567890123' as decimal) volume from dual;"; 61 | rs = stmt.executeQuery(sql); 62 | rsmd = rs.getMetaData(); 63 | } 64 | 65 | @AfterClass 66 | public static void tearDown() throws Exception { 67 | rs.close(); 68 | stmt.close(); 69 | } 70 | 71 | @Test 72 | public void testGetColumnCount() throws Exception { 73 | Assert.assertEquals(6, rsmd.getColumnCount()); 74 | } 75 | 76 | @Test 77 | public void testGetColumnName() throws Exception { 78 | Assert.assertEquals("name", rsmd.getColumnName(1)); 79 | Assert.assertEquals("male", rsmd.getColumnName(2)); 80 | Assert.assertEquals("age", rsmd.getColumnName(3)); 81 | Assert.assertEquals("height", rsmd.getColumnName(4)); 82 | Assert.assertEquals("day", rsmd.getColumnName(5)); 83 | Assert.assertEquals("volume", rsmd.getColumnName(6)); 84 | } 85 | 86 | @Test 87 | public void testGetColumnType() throws Exception { 88 | Assert.assertEquals(Types.VARCHAR, rsmd.getColumnType(1)); 89 | Assert.assertEquals(Types.BOOLEAN, rsmd.getColumnType(2)); 90 | Assert.assertEquals(Types.INTEGER, rsmd.getColumnType(3)); 91 | Assert.assertEquals(Types.DOUBLE, rsmd.getColumnType(4)); 92 | Assert.assertEquals(Types.TIMESTAMP, rsmd.getColumnType(5)); 93 | Assert.assertEquals(Types.DECIMAL, rsmd.getColumnType(6)); 94 | } 95 | 96 | @Test 97 | public void testGetColumnTypeName() throws Exception { 98 | Assert.assertEquals("STRING", rsmd.getColumnTypeName(1)); 99 | Assert.assertEquals("BOOLEAN", rsmd.getColumnTypeName(2)); 100 | Assert.assertEquals("INT", rsmd.getColumnTypeName(3)); 101 | Assert.assertEquals("DOUBLE", rsmd.getColumnTypeName(4)); 102 | Assert.assertEquals("DATETIME", rsmd.getColumnTypeName(5)); 103 | Assert.assertTrue(rsmd.getColumnTypeName(6).contains("DECIMAL")); 104 | } 105 | 106 | @Test 107 | public void testGetColumnMeta() throws Exception { 108 | for (int i = 0; i < rsmd.getColumnCount(); i++) { 109 | Assert.assertEquals(ResultSetMetaData.columnNullable, rsmd.isNullable(i + 1)); 110 | int scale = rsmd.getScale(i + 1); 111 | int precision = rsmd.getPrecision(i + 1); 112 | int displaySize = rsmd.getColumnDisplaySize(i + 1); 113 | boolean caseSensive = rsmd.isCaseSensitive(i + 1); 114 | boolean signed = rsmd.isSigned(i + 1); 115 | System.out.printf("%d: %d %d %d %b %b\n", i + 1, scale, precision, displaySize, caseSensive, 116 | signed); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/test/java/com/aliyun/odps/jdbc/OdpsStatementCommandApiTest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.sql.Connection; 6 | import java.sql.DriverManager; 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.sql.Statement; 10 | import java.util.Properties; 11 | 12 | import org.junit.AfterClass; 13 | import org.junit.BeforeClass; 14 | import org.junit.Test; 15 | 16 | public class OdpsStatementCommandApiTest { 17 | 18 | static final String tableName = "commandapi_test"; 19 | 20 | static Connection conn; 21 | 22 | @BeforeClass 23 | public static void prepare() throws SQLException { 24 | try { 25 | String driverName = "com.aliyun.odps.jdbc.OdpsDriver"; 26 | Class.forName(driverName); 27 | } catch (ClassNotFoundException e) { 28 | e.printStackTrace(); 29 | System.exit(1); 30 | } 31 | 32 | // fill in the information string 33 | Properties odpsConfig = new Properties(); 34 | InputStream is = 35 | Thread.currentThread().getContextClassLoader().getResourceAsStream("conf.properties"); 36 | try { 37 | odpsConfig.load(is); 38 | } catch (IOException e) { 39 | e.printStackTrace(); 40 | System.exit(1); 41 | } 42 | 43 | String accessId = odpsConfig.getProperty("access_id"); 44 | String accessKey = odpsConfig.getProperty("access_key"); 45 | String endpoint = odpsConfig.getProperty("end_point"); 46 | String pj = odpsConfig.getProperty("project_name"); 47 | conn = null; 48 | try { 49 | conn = 50 | DriverManager.getConnection("jdbc:odps:" + endpoint + "?project=" + pj 51 | + "&enableOdpsLogger=true&enableCommandApi=true&interactiveMode=true", 52 | accessId, accessKey); 53 | } catch (SQLException e) { 54 | e.printStackTrace(); 55 | } 56 | 57 | Statement statement = conn.createStatement(); 58 | statement.execute("drop table if exists " + tableName); 59 | statement.execute("create table " + tableName + " (key string, value datetime);"); 60 | } 61 | 62 | @AfterClass 63 | public static void after() throws SQLException { 64 | Statement stmt = conn.createStatement(); 65 | stmt.execute("drop table if exists " + tableName); 66 | } 67 | 68 | @Test 69 | public void showCreateTableTest() throws SQLException { 70 | Statement statement = conn.createStatement(); 71 | 72 | String sql = "show create table " + tableName; 73 | ResultSet resultSet; 74 | 75 | resultSet = statement.executeQuery(sql); 76 | while (resultSet.next()) { 77 | int count = resultSet.getMetaData().getColumnCount(); 78 | for (int i = 1; i <= count; i++) { 79 | System.out.println(resultSet.getMetaData().getColumnName(i) + " : " + resultSet.getString(i)); 80 | } 81 | } 82 | } 83 | 84 | @Test 85 | public void showSchemaTest() throws SQLException { 86 | Statement statement = conn.createStatement(); 87 | 88 | String sql = "show schemas ;"; 89 | ResultSet resultSet; 90 | 91 | resultSet = statement.executeQuery(sql); 92 | while (resultSet.next()) { 93 | int count = resultSet.getMetaData().getColumnCount(); 94 | for (int i = 1; i <= count; i++) { 95 | System.out.println(resultSet.getMetaData().getColumnName(i) + " : " + resultSet.getString(i)); 96 | } 97 | } 98 | } 99 | 100 | @Test 101 | public void commandApiTest() throws SQLException { 102 | Statement stmt = conn.createStatement(); 103 | 104 | String sql; 105 | ResultSet res; 106 | 107 | res = stmt.executeQuery("desc " + tableName + ";"); 108 | while (res.next()) { 109 | int count = res.getMetaData().getColumnCount(); 110 | for (int i = 1; i <= count; i++) { 111 | System.out.println(res.getMetaData().getColumnName(i) + " : " + res.getString(i)); 112 | } 113 | } 114 | 115 | sql = "whoami;"; 116 | stmt.execute(sql); 117 | res = stmt.getResultSet(); 118 | 119 | while (res.next()) { 120 | int count = res.getMetaData().getColumnCount(); 121 | for (int i = 1; i <= count; i++) { 122 | System.out.println(res.getMetaData().getColumnName(i) + " : " + res.getString(i)); 123 | } 124 | } 125 | 126 | sql = 127 | String.format("insert into %s values ('testnow' , datetime('2022-07-10 10:10:00'));", 128 | tableName); 129 | int updateCount = stmt.executeUpdate(sql); 130 | System.out.println(updateCount); 131 | System.out.println("update sql resultSet: " + stmt.getResultSet()); 132 | System.out.println(((OdpsStatement) stmt).getLogViewUrl()); 133 | 134 | 135 | sql = "select * from " + tableName; 136 | res = stmt.executeQuery(sql); 137 | while (res.next()) { 138 | int count = res.getMetaData().getColumnCount(); 139 | for (int i = 1; i <= count; i++) { 140 | System.out.println(res.getMetaData().getColumnName(i) + " : " + res.getString(i)); 141 | } 142 | } 143 | System.out.println(((OdpsStatement) stmt).getLogViewUrl()); 144 | 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /src/test/java/com/aliyun/odps/jdbc/SpecialSQLTest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.sql.Statement; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * @author dingxin (zhangdingxin.zdx@alibaba-inc.com) 9 | */ 10 | public class SpecialSQLTest { 11 | 12 | @Test 13 | public void testEndWithSpace() throws Exception { 14 | String sql = "select * from test; "; 15 | TestManager instance = TestManager.getInstance(); 16 | Statement statement = instance.conn.createStatement(); 17 | try { 18 | statement.executeQuery(sql); 19 | } catch (Exception e) { 20 | e.printStackTrace(); 21 | } 22 | statement.close(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/aliyun/odps/jdbc/TestManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | */ 20 | 21 | package com.aliyun.odps.jdbc; 22 | 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.sql.Connection; 26 | import java.sql.DriverManager; 27 | import java.sql.Statement; 28 | import java.util.Properties; 29 | 30 | import org.junit.Assert; 31 | 32 | import com.aliyun.odps.Odps; 33 | import com.aliyun.odps.account.Account; 34 | import com.aliyun.odps.account.AliyunAccount; 35 | import com.aliyun.odps.tunnel.TableTunnel; 36 | 37 | /** 38 | * This class manage a global JDBC connection and multiple testing instances can access it 39 | * simultaneously. It will also close the connection automatically. 40 | */ 41 | public class TestManager { 42 | 43 | public Connection conn; 44 | public Connection sessionConn; 45 | public Odps odps; 46 | public TableTunnel tunnel; 47 | 48 | private static final TestManager cf = new TestManager(); 49 | 50 | protected void finalize() throws Throwable { 51 | if (sessionConn != null) { 52 | sessionConn.close(); 53 | } 54 | } 55 | 56 | private TestManager() { 57 | try { 58 | Properties odpsConfig = new Properties(); 59 | 60 | InputStream is = 61 | Thread.currentThread().getContextClassLoader().getResourceAsStream("conf.properties"); 62 | odpsConfig.load(is); 63 | 64 | Class.forName("com.aliyun.odps.jdbc.OdpsDriver"); 65 | 66 | String endpoint = odpsConfig.getProperty("end_point"); 67 | String project = odpsConfig.getProperty("project_name"); 68 | String username = odpsConfig.getProperty("access_id"); 69 | String password = odpsConfig.getProperty("access_key"); 70 | String loglevel = odpsConfig.getProperty("log_level"); 71 | String logview = odpsConfig.getProperty("logview_host"); 72 | String enableLimit = odpsConfig.getProperty("enable_limit"); 73 | String autoSelectLimit = odpsConfig.getProperty("auto_select_limit"); 74 | String 75 | url = 76 | String 77 | .format("jdbc:odps:%s?project=%s&loglevel=%s&enableOdpsLogger=true&logview=%s&skipSqlInjectCheck=true&settings={\"odps.sql.groupby.orderby. position.alias\":\"true\"}", endpoint, project, loglevel, 78 | logview); 79 | 80 | // pass project name via url 81 | conn = DriverManager.getConnection(url, username, password); 82 | Assert.assertNotNull(conn); 83 | Statement stmt = conn.createStatement(); 84 | stmt.execute("set odps.sql.hive.compatible=true;"); 85 | stmt.execute("set odps.sql.preparse.odps2=lot;"); 86 | stmt.execute("set odps.sql.planner.mode=lot;"); 87 | stmt.execute("set odps.sql.planner.parser.odps2=true;"); 88 | stmt.execute("set odps.sql.ddl.odps2=true;"); 89 | stmt.execute("set odps.sql.runtime.mode=executionengine;"); 90 | stmt.execute("set odps.compiler.verify=true;"); 91 | stmt.execute("set odps.compiler.output.format=lot,pot;"); 92 | 93 | String 94 | urlSession = 95 | String.format("jdbc:odps:%s?project=%s&interactiveMode=true&enableCommandApi=true&settings={\"odps.sql.groupby.orderby.position.alias\":\"true\"}", endpoint, project); 96 | 97 | // pass project name via url 98 | sessionConn = DriverManager.getConnection(urlSession, username, password); 99 | Assert.assertNotNull(sessionConn); 100 | Statement sessionConnStatement = sessionConn.createStatement(); 101 | sessionConnStatement.execute("set odps.sql.hive.compatible=true;"); 102 | sessionConnStatement.execute("set odps.sql.preparse.odps2=lot;"); 103 | sessionConnStatement.execute("set odps.sql.planner.mode=lot;"); 104 | sessionConnStatement.execute("set odps.sql.planner.parser.odps2=true;"); 105 | sessionConnStatement.execute("set odps.sql.ddl.odps2=true;"); 106 | sessionConnStatement.execute("set odps.sql.runtime.mode=executionengine;"); 107 | sessionConnStatement.execute("set odps.compiler.verify=true;"); 108 | sessionConnStatement.execute("set odps.compiler.output.format=lot,pot;"); 109 | 110 | Account account = new AliyunAccount(username, password); 111 | odps = new Odps(account); 112 | odps.setEndpoint(endpoint); 113 | odps.setDefaultProject(project); 114 | Assert.assertNotNull(odps); 115 | 116 | tunnel = new TableTunnel(odps); 117 | Assert.assertNotNull(tunnel); 118 | } catch (ClassNotFoundException e) { 119 | e.printStackTrace(); 120 | } catch (java.sql.SQLException e) { 121 | e.printStackTrace(); 122 | } catch (IOException e) { 123 | e.printStackTrace(); 124 | } 125 | } 126 | 127 | public static TestManager getInstance() { 128 | return cf; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/test/java/com/aliyun/odps/jdbc/TimezoneTest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import java.sql.ResultSet; 4 | 5 | /** 6 | * @author dingxin (zhangdingxin.zdx@alibaba-inc.com) 7 | */ 8 | public class TimezoneTest { 9 | 10 | public static void main(String[] args) throws Exception { 11 | TestManager instance = TestManager.getInstance(); 12 | ResultSet resultSet = instance.conn.createStatement().executeQuery("select now();"); 13 | if (resultSet.next()) { 14 | Object o = resultSet.getTimestamp(1); 15 | System.out.println(o); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/com/aliyun/odps/jdbc/WarningLogTest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc; 2 | 3 | import org.junit.After; 4 | import org.junit.AfterClass; 5 | import org.junit.Assert; 6 | import org.junit.Before; 7 | import org.junit.BeforeClass; 8 | import org.junit.Test; 9 | 10 | import ch.qos.logback.classic.Level; 11 | import ch.qos.logback.classic.Logger; 12 | import ch.qos.logback.classic.spi.ILoggingEvent; 13 | import ch.qos.logback.core.read.ListAppender; 14 | 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.sql.Connection; 18 | import java.sql.ResultSet; 19 | import java.sql.Statement; 20 | 21 | import com.aliyun.odps.jdbc.utils.OdpsLogger; 22 | import com.aliyun.odps.jdbc.utils.TestUtils; 23 | import com.google.common.collect.ImmutableMap; 24 | 25 | /** 26 | * @author dingxin (zhangdingxin.zdx@alibaba-inc.com) 27 | */ 28 | public class WarningLogTest { 29 | 30 | Logger logger = (Logger) LoggerFactory.getLogger("ROOT"); 31 | ListAppender listAppender = new ListAppender<>(); 32 | 33 | @Before 34 | public void setUp() { 35 | listAppender.start(); 36 | logger.addAppender(listAppender); 37 | } 38 | 39 | @After 40 | public void tearDown() { 41 | logger.detachAppender(listAppender); 42 | } 43 | 44 | @Test 45 | public void testLongSQLLog() throws Exception { 46 | StringBuilder sb = new StringBuilder(); 47 | for (int i = 0; i < 10000; i++) { 48 | sb.append(" "); 49 | } 50 | String longSql = "select" + sb.toString() + "1;"; 51 | 52 | try (Connection connection = TestUtils.getConnection(); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery( 53 | longSql)) { 54 | while (resultSet.next()) { 55 | // do nothing 56 | } 57 | } 58 | Assert.assertTrue(listAppender.list.stream().filter(event -> event.getLevel() == Level.WARN) 59 | .anyMatch(event -> event.getFormattedMessage() 60 | .contains( 61 | "The length of sql is too long, it may cause performance issues."))); 62 | } 63 | 64 | @Test 65 | public void testLongJobWarningThreshold() throws Exception { 66 | // set longJobWarningThreshold=100ms to ensure the warning log is triggered 67 | try (Connection connection = TestUtils.getConnection(ImmutableMap.of("longJobWarningThreshold", "100")); 68 | Statement statement = connection.createStatement(); 69 | ResultSet resultSet = statement.executeQuery( 70 | "select 1;")) { 71 | while (resultSet.next()) { 72 | // do nothing 73 | } 74 | } 75 | Assert.assertTrue(listAppender.list.stream().filter(event -> event.getLevel() == Level.WARN) 76 | .anyMatch(event -> event.getFormattedMessage() 77 | .contains( 78 | "SQL execution time exceeds long job warning threshold."))); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/com/aliyun/odps/jdbc/utils/JdbcTransformerTest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc.utils; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.sql.Connection; 6 | import java.sql.DriverManager; 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.sql.Statement; 10 | import java.util.Properties; 11 | import java.util.TimeZone; 12 | 13 | import org.junit.Assert; 14 | import org.junit.BeforeClass; 15 | import org.junit.Test; 16 | 17 | /** 18 | * @author dingxin (zhangdingxin.zdx@alibaba-inc.com) 19 | */ 20 | public class JdbcTransformerTest { 21 | 22 | private static Connection conn; 23 | 24 | @BeforeClass 25 | public static void prepare() { 26 | try { 27 | String driverName = "com.aliyun.odps.jdbc.OdpsDriver"; 28 | Class.forName(driverName); 29 | } catch (ClassNotFoundException e) { 30 | e.printStackTrace(); 31 | System.exit(1); 32 | } 33 | 34 | // fill in the information string 35 | Properties odpsConfig = new Properties(); 36 | InputStream is = 37 | Thread.currentThread().getContextClassLoader().getResourceAsStream("conf.properties"); 38 | try { 39 | odpsConfig.load(is); 40 | } catch (IOException e) { 41 | e.printStackTrace(); 42 | System.exit(1); 43 | } 44 | 45 | String accessId = odpsConfig.getProperty("access_id"); 46 | String accessKey = odpsConfig.getProperty("access_key"); 47 | String endpoint = odpsConfig.getProperty("end_point"); 48 | String pj = odpsConfig.getProperty("project_name"); 49 | conn = null; 50 | try { 51 | conn = 52 | DriverManager.getConnection( 53 | "jdbc:odps:" + endpoint + "?project=" + pj 54 | + "&enableOdpsLogger=true&useProjectTimeZone=true", accessId, 55 | accessKey); 56 | } catch (SQLException e) { 57 | e.printStackTrace(); 58 | } 59 | } 60 | 61 | @Test 62 | public void testDate() throws Exception { 63 | TimeZone.setDefault(TimeZone.getTimeZone("GMT+8")); 64 | 65 | Statement statement = conn.createStatement(); 66 | ResultSet 67 | resultSet = 68 | statement.executeQuery( 69 | "set odps.sql.timezone=UTC;" + 70 | "select DATE'2024-09-30', DATETIME'2024-09-30 00:00:00', TIMESTAMP'2024-09-30 00:00:00.123', TIMESTAMP_NTZ'2024-09-30 00:00:00.123';"); 71 | 72 | resultSet.next(); 73 | for (int i = 1; i <= 4; i++) { 74 | Assert.assertEquals(1727654400000L, resultSet.getDate(i).getTime()); 75 | } 76 | 77 | resultSet = 78 | statement.executeQuery( 79 | "set odps.sql.timezone=Asia/Shanghai;" + 80 | "select DATE'2024-09-30', DATETIME'2024-09-30 00:00:00', TIMESTAMP'2024-09-30 00:00:00.123', TIMESTAMP_NTZ'2024-09-30 00:00:00.123';"); 81 | resultSet.next(); 82 | for (int i = 1; i <= 4; i++) { 83 | Assert.assertEquals(1727625600000L, resultSet.getDate(i).getTime()); 84 | } 85 | } 86 | 87 | @Test 88 | public void testTimestamp() throws Exception { 89 | TimeZone.setDefault(TimeZone.getTimeZone("GMT+8")); 90 | 91 | Statement statement = conn.createStatement(); 92 | ResultSet 93 | resultSet = 94 | statement.executeQuery( 95 | "set odps.sql.timezone=UTC;" + 96 | "select DATETIME'2024-09-30 00:00:00', TIMESTAMP'2024-09-30 00:00:00.000', TIMESTAMP_NTZ'2024-09-30 00:00:00.000';"); 97 | 98 | resultSet.next(); 99 | for (int i = 1; i <= 3; i++) { 100 | System.out.println(resultSet.getTimestamp(i).getTime()); 101 | Assert.assertEquals(1727654400000L, resultSet.getDate(i).getTime()); 102 | } 103 | 104 | resultSet = 105 | statement.executeQuery( 106 | "set odps.sql.timezone=Asia/Shanghai;" + 107 | "select DATETIME'2024-09-30 00:00:00', TIMESTAMP'2024-09-30 00:00:00.000', TIMESTAMP_NTZ'2024-09-30 00:00:00.000';"); 108 | 109 | resultSet.next(); 110 | for (int i = 1; i <= 3; i++) { 111 | Assert.assertEquals(1727625600000L, resultSet.getTimestamp(i).getTime()); 112 | } 113 | } 114 | 115 | @Test 116 | public void testTimestampWithHighNanos() throws Exception { 117 | TimeZone.setDefault(TimeZone.getTimeZone("GMT+8")); 118 | 119 | Statement statement = conn.createStatement(); 120 | ResultSet 121 | resultSet = 122 | statement.executeQuery( 123 | "set odps.sql.timezone=UTC;" + 124 | "select TIMESTAMP'2024-09-30 00:00:00.123456789', TIMESTAMP_NTZ'2024-09-30 00:00:00.123456789';"); 125 | 126 | resultSet.next(); 127 | for (int i = 1; i <= 2; i++) { 128 | Assert.assertEquals(1727654400123L, resultSet.getTimestamp(i).getTime()); 129 | Assert.assertEquals(123456789L, resultSet.getTimestamp(i).getNanos()); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/test/java/com/aliyun/odps/jdbc/utils/SettingParserTest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.odps.jdbc.utils; 2 | 3 | 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | import java.util.Map; 7 | 8 | public class SettingParserTest { 9 | // 基础测试用例 10 | @Test 11 | public void testStandardScenario() { 12 | String sql = "/* pre comment */\n" 13 | + "SET key1 = value1; -- inline comment\n" 14 | + "SeT key2=value2;\n" 15 | + "SELECT 1;"; 16 | 17 | SettingParser parser = new SettingParser(); 18 | SettingParser.ParseResult result = parser.extractSetStatements(sql); 19 | 20 | // 验证设置项 21 | Map settings = result.getSettings(); 22 | assertEquals(2, settings.size()); 23 | assertEquals("value1", settings.get("key1")); 24 | assertEquals("value2", settings.get("key2")); 25 | 26 | // 验证剩余SQL 27 | String expectedRemaining = "/* pre comment */\n -- inline comment\n\nSELECT 1;"; 28 | assertEquals(expectedRemaining, result.getRemainingQuery()); 29 | 30 | // 验证错误信息 31 | assertTrue(result.getErrors().isEmpty()); 32 | } 33 | 34 | // 注释嵌套测试 35 | @Test 36 | public void testNestedComments() { 37 | String sql = "/**//*! SET invalid1=1 */\n" 38 | + "-- SET invalid2=2\n" 39 | + "SET /*inner*/valid3=3; -- end\n" 40 | + "SELECT '--string'/*, \"SET\"*/;"; 41 | 42 | SettingParser.ParseResult result = new SettingParser().extractSetStatements(sql); 43 | 44 | assertEquals(1, result.getSettings().size()); 45 | assertEquals("3", result.getSettings().get("/*inner*/valid3")); 46 | 47 | String expected = "/**//*! SET invalid1=1 */\n-- SET invalid2=2\n -- end\nSELECT '--string'/*, \"SET\"*/;"; 48 | assertEquals(expected, result.getRemainingQuery()); 49 | } 50 | 51 | // 错误场景测试 52 | @Test 53 | public void testErrorScenarios() { 54 | SettingParser parser = new SettingParser(); 55 | 56 | // 缺少分号 57 | String sql1 = "SET key=value"; 58 | SettingParser.ParseResult r1 = parser.extractSetStatements(sql1); 59 | assertEquals(1, r1.getErrors().size()); 60 | assertTrue(r1.getErrors().get(0).contains("missing semicolon")); 61 | 62 | // 无效键值对 63 | String sql2 = "SET invalid;"; 64 | SettingParser.ParseResult r2 = parser.extractSetStatements(sql2); 65 | assertEquals(1, r2.getErrors().size()); 66 | assertTrue(r2.getErrors().get(0).contains("missing '='")); 67 | } 68 | 69 | // 字符串保护测试 70 | @Test 71 | public void testStringProtection() { 72 | String sql = "SET key = 'semi\\;colon';\n" 73 | + "SELECT '--fake_comment', \"/*fake*/\";"; 74 | 75 | SettingParser.ParseResult result = new SettingParser().extractSetStatements(sql); 76 | 77 | assertEquals("'semi;colon'", result.getSettings().get("key")); 78 | assertEquals("\nSELECT '--fake_comment', \"/*fake*/\";", result.getRemainingQuery()); 79 | } 80 | 81 | 82 | @Test 83 | public void testSetLabel() { 84 | String sql = "SET odps.namespace.schema=true;\n" 85 | + " SET LABEL 1 TO TABLE default.wrk_gh_events(`repo_id`, `repo_name`, `org_id`, `org_login`);"; 86 | 87 | SettingParser.ParseResult result = new SettingParser().extractSetStatements(sql); 88 | System.out.println(result.getSettings()); 89 | System.out.println(result.getErrors()); 90 | System.out.println(result.getRemainingQuery()); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/test/resources/conf.properties.example: -------------------------------------------------------------------------------- 1 | access_id= 2 | access_key= 3 | end_point= 4 | project_name= 5 | logview_host= 6 | charset=UTF-8 7 | interactive_mode=false 8 | interactive_service_name= 9 | interactive_timeout=30 10 | major_version=default 11 | log_level=INFO 12 | -------------------------------------------------------------------------------- /src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ${HOME:-${USERPROFILE}}/logs/odps.log 4 | 5 | 6 | %date %level [%thread] %logger{10} %X{connectionId} [%file:%line] %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | %msg%n 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/test/resources/odps_config.ini: -------------------------------------------------------------------------------- 1 | project_name=p2 2 | access_id=345 3 | access_key=456= 4 | end_point=http://2.2.2.2:8100/api 5 | dt_end_point=http://10.101.202.159:8066 6 | log_conf_file=logback1.xml 7 | lifecycle=2 8 | tunnel_endpoint=http://1.1.1.1:8066 9 | interactive_mode=false 10 | interactive_service_name=sn 11 | interactive_timeout=11 12 | major_version=default1 13 | table_list=project1.table1,project1.table2,project2.table1,project2.table2,project2.table3 14 | read_timeout=21 --------------------------------------------------------------------------------