http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/protobuf/common.proto ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/protobuf/common.proto b/avatica/core/src/main/protobuf/common.proto new file mode 100644 index 0000000..bd116c3 --- /dev/null +++ b/avatica/core/src/main/protobuf/common.proto @@ -0,0 +1,275 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto3"; + +option java_package = "org.apache.calcite.avatica.proto"; + +// Details about a connection +message ConnectionProperties { + bool is_dirty = 1; + bool auto_commit = 2; + bool has_auto_commit = 7; // field is a Boolean, need to discern null and default value + bool read_only = 3; + bool has_read_only = 8; // field is a Boolean, need to discern null and default value + uint32 transaction_isolation = 4; + string catalog = 5; + string schema = 6; +} + +// Statement handle +message StatementHandle { + string connection_id = 1; + uint32 id = 2; + Signature signature = 3; +} + +// Results of preparing a statement +message Signature { + repeated ColumnMetaData columns = 1; + string sql = 2; + repeated AvaticaParameter parameters = 3; + CursorFactory cursor_factory = 4; + StatementType statementType = 5; +} + +// Has to be consistent with Meta.StatementType +enum StatementType { + SELECT = 0; + INSERT = 1; + UPDATE = 2; + DELETE = 3; + UPSERT = 4; + MERGE = 5; + OTHER_DML = 6; + CREATE = 7; + DROP = 8; + ALTER = 9; + OTHER_DDL = 10; + CALL = 11; +} + +message ColumnMetaData { + uint32 ordinal = 1; + bool auto_increment = 2; + bool case_sensitive = 3; + bool searchable = 4; + bool currency = 5; + uint32 nullable = 6; + bool signed = 7; + uint32 display_size = 8; + string label = 9; + string column_name = 10; + string schema_name = 11; + uint32 precision = 12; + uint32 scale = 13; + string table_name = 14; + string catalog_name = 15; + bool read_only = 16; + bool writable = 17; + bool definitely_writable = 18; + string column_class_name = 19; + AvaticaType type = 20; +} + +enum Rep { + PRIMITIVE_BOOLEAN = 0; + PRIMITIVE_BYTE = 1; + PRIMITIVE_CHAR = 2; + PRIMITIVE_SHORT = 3; + PRIMITIVE_INT = 4; + PRIMITIVE_LONG = 5; + PRIMITIVE_FLOAT = 6; + PRIMITIVE_DOUBLE = 7; + BOOLEAN = 8; + BYTE = 9; + CHARACTER = 10; + SHORT = 11; + INTEGER = 12; + LONG = 13; + FLOAT = 14; + DOUBLE = 15; + BIG_INTEGER = 25; + BIG_DECIMAL = 26; + JAVA_SQL_TIME = 16; + JAVA_SQL_TIMESTAMP = 17; + JAVA_SQL_DATE = 18; + JAVA_UTIL_DATE = 19; + BYTE_STRING = 20; + STRING = 21; + NUMBER = 22; + OBJECT = 23; + NULL = 24; + ARRAY = 27; + STRUCT = 28; + MULTISET = 29; +} + +// Base class for a column type +message AvaticaType { + uint32 id = 1; + string name = 2; + Rep rep = 3; + + repeated ColumnMetaData columns = 4; // Only present when name = STRUCT + AvaticaType component = 5; // Only present when name = ARRAY +} + +// Metadata for a parameter +message AvaticaParameter { + bool signed = 1; + uint32 precision = 2; + uint32 scale = 3; + uint32 parameter_type = 4; + string type_name = 5; + string class_name = 6; + string name = 7; +} + +// Information necessary to convert an Iterable into a Calcite Cursor +message CursorFactory { + enum Style { + OBJECT = 0; + RECORD = 1; + RECORD_PROJECTION = 2; + ARRAY = 3; + LIST = 4; + MAP = 5; + } + + Style style = 1; + string class_name = 2; + repeated string field_names = 3; +} + +// A collection of rows +message Frame { + uint64 offset = 1; + bool done = 2; + repeated Row rows = 3; +} + +// A row is a collection of values +message Row { + repeated ColumnValue value = 1; +} + +// Database property, list of functions the database provides for a certain operation +message DatabaseProperty { + string name = 1; + repeated string functions = 2; +} + +// Message which encapsulates another message to support a single RPC endpoint +message WireMessage { + string name = 1; + bytes wrapped_message = 2; +} + +// A value might be a TypedValue or an Array of TypedValue's +message ColumnValue { + repeated TypedValue value = 1; // deprecated, use array_value or scalar_value + repeated TypedValue array_value = 2; + bool has_array_value = 3; // Is an array value set? + TypedValue scalar_value = 4; +} + +// Generic wrapper to support any SQL type. Struct-like to work around no polymorphism construct. +message TypedValue { + Rep type = 1; // The actual type that was serialized in the general attribute below + + bool bool_value = 2; // boolean + string string_value = 3; // char/varchar + sint64 number_value = 4; // var-len encoding lets us shove anything from byte to long + // includes numeric types and date/time types. + bytes bytes_values = 5; // binary/varbinary + double double_value = 6; // big numbers + bool null = 7; // a null object +} + +// The severity of some unexpected outcome to an operation. +// Protobuf enum values must be unique across all other enums +enum Severity { + UNKNOWN_SEVERITY = 0; + FATAL_SEVERITY = 1; + ERROR_SEVERITY = 2; + WARNING_SEVERITY = 3; +} + +// Enumeration corresponding to DatabaseMetaData operations +enum MetaDataOperation { + GET_ATTRIBUTES = 0; + GET_BEST_ROW_IDENTIFIER = 1; + GET_CATALOGS = 2; + GET_CLIENT_INFO_PROPERTIES = 3; + GET_COLUMN_PRIVILEGES = 4; + GET_COLUMNS = 5; + GET_CROSS_REFERENCE = 6; + GET_EXPORTED_KEYS = 7; + GET_FUNCTION_COLUMNS = 8; + GET_FUNCTIONS = 9; + GET_IMPORTED_KEYS = 10; + GET_INDEX_INFO = 11; + GET_PRIMARY_KEYS = 12; + GET_PROCEDURE_COLUMNS = 13; + GET_PROCEDURES = 14; + GET_PSEUDO_COLUMNS = 15; + GET_SCHEMAS = 16; + GET_SCHEMAS_WITH_ARGS = 17; + GET_SUPER_TABLES = 18; + GET_SUPER_TYPES = 19; + GET_TABLE_PRIVILEGES = 20; + GET_TABLES = 21; + GET_TABLE_TYPES = 22; + GET_TYPE_INFO = 23; + GET_UDTS = 24; + GET_VERSION_COLUMNS = 25; +} + +// Represents the breadth of arguments to DatabaseMetaData functions +message MetaDataOperationArgument { + enum ArgumentType { + STRING = 0; + BOOL = 1; + INT = 2; + REPEATED_STRING = 3; + REPEATED_INT = 4; + NULL = 5; + } + + string string_value = 1; + bool bool_value = 2; + sint32 int_value = 3; + repeated string string_array_values = 4; + repeated sint32 int_array_values = 5; + ArgumentType type = 6; +} + +enum StateType { + SQL = 0; + METADATA = 1; +} + +message QueryState { + StateType type = 1; + string sql = 2; + MetaDataOperation op = 3; + repeated MetaDataOperationArgument args = 4; + bool has_args = 5; + bool has_sql = 6; + bool has_op = 7; +}
http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/protobuf/requests.proto ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/protobuf/requests.proto b/avatica/core/src/main/protobuf/requests.proto new file mode 100644 index 0000000..31b0941 --- /dev/null +++ b/avatica/core/src/main/protobuf/requests.proto @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; + +option java_package = "org.apache.calcite.avatica.proto"; + +import "common.proto"; + +// Request for Meta#getCatalogs() +message CatalogsRequest { + string connection_id = 1; +} + +// Request for Meta#getDatabaseProperties() +message DatabasePropertyRequest { + string connection_id = 1; +} + +// Request for Meta#getSchemas(String, org.apache.calcite.avatica.Meta.Pat)} +message SchemasRequest { + string catalog = 1; + string schema_pattern = 2; + string connection_id = 3; +} + +// Request for Request for Meta#getTables(String, org.apache.calcite.avatica.Meta.Pat, +// org.apache.calcite.avatica.Meta.Pat, java.util.List) +message TablesRequest { + string catalog = 1; + string schema_pattern = 2; + string table_name_pattern = 3; + repeated string type_list = 4; + bool has_type_list = 6; // Having an empty type_list is distinct from a null type_list + string connection_id = 7; +} + +// Request for Meta#getTableTypes() +message TableTypesRequest { + string connection_id = 1; +} + +// Request for Meta#getColumns(String, org.apache.calcite.avatica.Meta.Pat, +// org.apache.calcite.avatica.Meta.Pat, org.apache.calcite.avatica.Meta.Pat). +message ColumnsRequest { + string catalog = 1; + string schema_pattern = 2; + string table_name_pattern = 3; + string column_name_pattern = 4; + string connection_id = 5; +} + +// Request for Meta#getTypeInfo() +message TypeInfoRequest { + string connection_id = 1; +} + +// Request for Meta#prepareAndExecute(Meta.StatementHandle, String, long, Meta.PrepareCallback) +message PrepareAndExecuteRequest { + string connection_id = 1; + string sql = 2; + uint64 max_row_count = 3; + uint32 statement_id = 4; +} + +// Request for Meta.prepare(Meta.ConnectionHandle, String, long) +message PrepareRequest { + string connection_id = 1; + string sql = 2; + uint64 max_row_count = 3; +} + +// Request for Meta#fetch(Meta.StatementHandle, List, long, int) +message FetchRequest { + string connection_id = 1; + uint32 statement_id = 2; + uint64 offset = 3; + uint32 fetch_max_row_count = 4; // Maximum number of rows to be returned in the frame. Negative means no limit. +} + +// Request for Meta#createStatement(Meta.ConnectionHandle) +message CreateStatementRequest { + string connection_id = 1; +} + +// Request for Meta#closeStatement(Meta.StatementHandle) +message CloseStatementRequest { + string connection_id = 1; + uint32 statement_id = 2; +} + +// Request for Meta#openConnection(Meta.ConnectionHandle, Map<String, String>) +message OpenConnectionRequest { + string connection_id = 1; + map<string, string> info = 2; +} + +// Request for Meta#closeConnection(Meta.ConnectionHandle) +message CloseConnectionRequest { + string connection_id = 1; +} + +message ConnectionSyncRequest { + string connection_id = 1; + ConnectionProperties conn_props = 2; +} + +// Request for Meta#execute(Meta.ConnectionHandle, list, long) +message ExecuteRequest { + StatementHandle statementHandle = 1; + repeated TypedValue parameter_values = 2; + uint64 max_row_count = 3; + bool has_parameter_values = 4; +} + + +message SyncResultsRequest { + string connection_id = 1; + uint32 statement_id = 2; + QueryState state = 3; + uint64 offset = 4; +} + +// Request to invoke a commit on a Connection +message CommitRequest { + string connection_id = 1; +} + +// Request to invoke rollback on a Connection +message RollbackRequest { + string connection_id = 1; +} http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/protobuf/responses.proto ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/protobuf/responses.proto b/avatica/core/src/main/protobuf/responses.proto new file mode 100644 index 0000000..01a62ed --- /dev/null +++ b/avatica/core/src/main/protobuf/responses.proto @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; + +option java_package = "org.apache.calcite.avatica.proto"; + +import "common.proto"; + +// Response that contains a result set. +message ResultSetResponse { + string connection_id = 1; + uint32 statement_id = 2; + bool own_statement = 3; + Signature signature = 4; + Frame first_frame = 5; + uint64 update_count = 6; // -1 for normal result sets, else this response contains a dummy result set + // with no signature nor other data. + RpcMetadata metadata = 7; +} + +// Response to PrepareAndExecuteRequest +message ExecuteResponse { + repeated ResultSetResponse results = 1; + bool missing_statement = 2; // Did the request fail because of no-cached statement + RpcMetadata metadata = 3; +} + +// Response to PrepareRequest +message PrepareResponse { + StatementHandle statement = 1; + RpcMetadata metadata = 2; +} + +// Response to FetchRequest +message FetchResponse { + Frame frame = 1; + bool missing_statement = 2; // Did the request fail because of no-cached statement + bool missing_results = 3; // Did the request fail because of a cached-statement w/o ResultSet + RpcMetadata metadata = 4; +} + +// Response to CreateStatementRequest +message CreateStatementResponse { + string connection_id = 1; + uint32 statement_id = 2; + RpcMetadata metadata = 3; +} + +// Response to CloseStatementRequest +message CloseStatementResponse { + RpcMetadata metadata = 1; +} + +// Response to OpenConnectionRequest { +message OpenConnectionResponse { + RpcMetadata metadata = 1; +} + +// Response to CloseConnectionRequest { +message CloseConnectionResponse { + RpcMetadata metadata = 1; +} + +// Response to ConnectionSyncRequest +message ConnectionSyncResponse { + ConnectionProperties conn_props = 1; + RpcMetadata metadata = 2; +} + +message DatabasePropertyElement { + DatabaseProperty key = 1; + TypedValue value = 2; + RpcMetadata metadata = 3; +} + +// Response for Meta#getDatabaseProperties() +message DatabasePropertyResponse { + repeated DatabasePropertyElement props = 1; + RpcMetadata metadata = 2; +} + +// Send contextual information about some error over the wire from the server. +message ErrorResponse { + repeated string exceptions = 1; // exception stacktraces, many for linked exceptions. + bool has_exceptions = 7; // are there stacktraces contained? + string error_message = 2; // human readable description + Severity severity = 3; + uint32 error_code = 4; // numeric identifier for error + string sql_state = 5; // five-character standard-defined value + RpcMetadata metadata = 6; +} + +message SyncResultsResponse { + bool missing_statement = 1; // Server doesn't have the statement with the ID from the request + bool more_results = 2; // Should the client fetch() to get more results + RpcMetadata metadata = 3; +} + +// Generic metadata for the server to return with each response. +message RpcMetadata { + string server_address = 1; // The host:port of the server +} + +// Response to a commit request +message CommitResponse { + +} + +// Response to a rollback request +message RollbackResponse { + +} http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/resources/META-INF/LICENSE ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/resources/META-INF/LICENSE b/avatica/core/src/main/resources/META-INF/LICENSE new file mode 100644 index 0000000..7764d41 --- /dev/null +++ b/avatica/core/src/main/resources/META-INF/LICENSE @@ -0,0 +1,257 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + + + +----------------------------------------------------------------------- + +APACHE CALCITE SUBCOMPONENTS: + +The Apache Calcite project contains subcomponents with separate copyright +notices and license terms. Your use of the source code for the these +subcomponents is subject to the terms and conditions of the following +licenses. + +----------------------------------------------------------------------- + 3-clause BSD license +----------------------------------------------------------------------- + +The Apache Calcite Avatica project bundles Protocol Buffers, which is available +under the following "3-clause BSD" license: + + Copyright 2014, Google Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Code generated by the Protocol Buffer compiler is owned by the owner + of the input file used when generating it. This code is not + standalone and requires a support library to be linked with it. This + support library is itself covered by the above license. http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/resources/META-INF/services/java.sql.Driver ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/resources/META-INF/services/java.sql.Driver b/avatica/core/src/main/resources/META-INF/services/java.sql.Driver new file mode 100644 index 0000000..beb1ac0 --- /dev/null +++ b/avatica/core/src/main/resources/META-INF/services/java.sql.Driver @@ -0,0 +1 @@ +org.apache.calcite.avatica.remote.Driver http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/scripts/generate-protobuf.sh ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/scripts/generate-protobuf.sh b/avatica/core/src/main/scripts/generate-protobuf.sh new file mode 100755 index 0000000..c4d3abe --- /dev/null +++ b/avatica/core/src/main/scripts/generate-protobuf.sh @@ -0,0 +1,99 @@ +#! /usr/bin/env bash + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to you under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script will regenerate the protobuf code for Avatica. Slightly +# modified script over one in Accumulo. + +# NOTES: +# To support this script being called by other modules, only edit the right side. +# In other scripts, set the variables that diverge from the defaults below, then call this script. +# Leave the BUILD_DIR and FINAL_DIR alone for Maven builds. +# ======================================================================================================================== +[[ -z $REQUIRED_PROTOC_VERSION ]] && REQUIRED_PROTOC_VERSION='libprotoc 3.0.0' +[[ -z $BUILD_DIR ]] && BUILD_DIR='target/proto-tmp' +[[ -z $FINAL_DIR ]] && FINAL_DIR='src/main' +# ======================================================================================================================== + +fail() { + echo "$@" + exit 1 +} + +# Test to see if we have protoc installed +VERSION=$(protoc --version 2>/dev/null | grep -F "${REQUIRED_PROTOC_VERSION}" | wc -l) +if [[ $VERSION -ne 1 ]] ; then + # Nope: bail + echo "****************************************************" + echo "*** protoc is not available" + echo "*** expecting 'protoc --version' to return ${REQUIRED_PROTOC_VERSION}" + echo "*** generated code will not be updated" + fail "****************************************************" +fi + +# Ensure output directories are created +PROTOC_ARGS="-I src/main/protobuf --java_out=$BUILD_DIR" +rm -rf $BUILD_DIR +mkdir -p $BUILD_DIR + +protoc ${PROTOC_ARGS} src/main/protobuf/*.proto || fail unable to generate Java protocol buffer classes + +# For all generated protobuf code, suppress all warnings and add the LICENSE header +s='@SuppressWarnings({"unused", "rawtypes"})' +find $BUILD_DIR -name '*.java' -print0 | xargs -0 sed -i.orig -e 's/\(public final class \)/'"$s"' \1/' + +PREFIX="/* +" +LINE_NOTATION=" *" +SUFFIX=" + */" +FILE_SUFFIX=(.java) + +for file in "${FILE_SUFFIX[@]}"; do + for f in $(find $BUILD_DIR/ -name "*$file"); do + cat - "$f" > "${f}-with-license" <<EOF +${PREFIX}${LINE_NOTATION} Licensed to the Apache Software Foundation (ASF) under one or more +${LINE_NOTATION} contributor license agreements. See the NOTICE file distributed with +${LINE_NOTATION} this work for additional information regarding copyright ownership. +${LINE_NOTATION} The ASF licenses this file to you under the Apache License, Version 2.0 +${LINE_NOTATION} (the "License"); you may not use this file except in compliance with +${LINE_NOTATION} the License. You may obtain a copy of the License at +${LINE_NOTATION} +${LINE_NOTATION} http://www.apache.org/licenses/LICENSE-2.0 +${LINE_NOTATION} +${LINE_NOTATION} Unless required by applicable law or agreed to in writing, software +${LINE_NOTATION} distributed under the License is distributed on an "AS IS" BASIS, +${LINE_NOTATION} WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +${LINE_NOTATION} See the License for the specific language governing permissions and +${LINE_NOTATION} limitations under the License.${SUFFIX} +EOF + done +done + +# For every generated java file, compare it with the version-controlled one, and copy the ones that have changed into place +SDIR="${BUILD_DIR}/org/apache/calcite/avatica/proto" +DDIR="${FINAL_DIR}/java/org/apache/calcite/avatica/proto" +FILE_SUFFIX=(.java) +mkdir -p "$DDIR" +for file in "${FILE_SUFFIX[@]}"; do + for f in $(find $SDIR -name *$file); do + DEST=$DDIR/$(basename "$f") + if ! cmp -s "${f}-with-license" "${DEST}" ; then + echo cp -f "${f}-with-license" "${DEST}" + cp -f "${f}-with-license" "${DEST}" || fail unable to copy files to java workspace + fi + done +done http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/AvaticaConnectionTest.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/AvaticaConnectionTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/AvaticaConnectionTest.java new file mode 100644 index 0000000..a1414c3 --- /dev/null +++ b/avatica/core/src/test/java/org/apache/calcite/avatica/AvaticaConnectionTest.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Properties; + +/** + * Tests for AvaticaConnection + */ +public class AvaticaConnectionTest { + + @Test + public void testNumExecuteRetries() { + AvaticaConnection statement = Mockito.mock(AvaticaConnection.class); + + Mockito.when(statement.getNumStatementRetries(Mockito.any(Properties.class))) + .thenCallRealMethod(); + + // Bad argument should throw an exception + try { + statement.getNumStatementRetries(null); + Assert.fail("Calling getNumStatementRetries with a null object should throw an exception"); + } catch (NullPointerException e) { + // Pass + } + + Properties props = new Properties(); + + // Verify the default value + Assert.assertEquals(Long.valueOf(AvaticaConnection.NUM_EXECUTE_RETRIES_DEFAULT).longValue(), + statement.getNumStatementRetries(props)); + + // Set a non-default value + props.setProperty(AvaticaConnection.NUM_EXECUTE_RETRIES_KEY, "10"); + + // Verify that we observe that value + Assert.assertEquals(10, statement.getNumStatementRetries(props)); + } + +} + +// End AvaticaConnectionTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/FrameTest.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/FrameTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/FrameTest.java new file mode 100644 index 0000000..bdd989b --- /dev/null +++ b/avatica/core/src/test/java/org/apache/calcite/avatica/FrameTest.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica; + +import org.apache.calcite.avatica.Meta.Frame; +import org.apache.calcite.avatica.proto.Common; +import org.apache.calcite.avatica.proto.Common.ColumnValue; +import org.apache.calcite.avatica.proto.Common.TypedValue; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Tests serialization of {@link Frame}. + */ +public class FrameTest { + + private static final TypedValue NUMBER_VALUE = TypedValue.newBuilder().setNumberValue(1) + .setType(Common.Rep.LONG).build(); + + private void serializeAndTestEquality(Frame frame) { + Frame frameCopy = Frame.fromProto(frame.toProto()); + + assertEquals(frame.done, frameCopy.done); + assertEquals(frame.offset, frameCopy.offset); + + Iterable<Object> origRows = frame.rows; + Iterable<Object> copiedRows = frameCopy.rows; + + assertEquals("Expected rows to both be null, or both be non-null", + origRows == null, copiedRows == null); + + Iterator<Object> origIter = origRows.iterator(); + Iterator<Object> copiedIter = copiedRows.iterator(); + while (origIter.hasNext() && copiedIter.hasNext()) { + Object orig = origIter.next(); + Object copy = copiedIter.next(); + + assertEquals(orig == null, copy == null); + + // This is goofy, but it seems like an Array comes from the JDBC implementation but then + // the resulting Frame has a List to support the Avatica typed Accessors + assertEquals(Object[].class, orig.getClass()); + assertTrue("Expected List but got " + copy.getClass(), copy instanceof List); + + @SuppressWarnings("unchecked") + List<Object> copyList = (List<Object>) copy; + + assertArrayEquals((Object[]) orig, copyList.toArray(new Object[0])); + } + + assertEquals(origIter.hasNext(), copiedIter.hasNext()); + } + + @Test + public void testEmpty() { + serializeAndTestEquality(Frame.EMPTY); + } + + @Test + public void testSingleRow() { + ArrayList<Object> rows = new ArrayList<>(); + rows.add(new Object[] {"string", Integer.MAX_VALUE, new Date().getTime()}); + + Frame singleRow = new Frame(0, true, rows); + + serializeAndTestEquality(singleRow); + } + + @Test + public void testMultipleRows() { + ArrayList<Object> rows = new ArrayList<>(); + rows.add(new Object[] {"string", Integer.MAX_VALUE, new Date().getTime()}); + rows.add(new Object[] {"gnirts", 0, Long.MIN_VALUE}); + rows.add(new Object[] {"", null, Long.MAX_VALUE}); + + Frame singleRow = new Frame(0, true, rows); + + serializeAndTestEquality(singleRow); + } + + @Test public void testMalformedColumnValue() { + // Invalid ColumnValue: has an array and scalar + final ColumnValue bothAttributesColumnValue = ColumnValue.newBuilder().setHasArrayValue(true) + .setScalarValue(NUMBER_VALUE).build(); + // Note omission of setScalarValue(TypedValue). + final ColumnValue neitherAttributeColumnValue = ColumnValue.newBuilder().setHasArrayValue(false) + .build(); + + try { + Frame.validateColumnValue(bothAttributesColumnValue); + fail("Validating the ColumnValue should have failed as it has an array and scalar"); + } catch (IllegalArgumentException e) { + // Pass + } + + try { + Frame.validateColumnValue(neitherAttributeColumnValue); + fail("Validating the ColumnValue should have failed as it has neither an array nor scalar"); + } catch (IllegalArgumentException e) { + // Pass + } + } + + @Test public void testColumnValueBackwardsCompatibility() { + // 1 + final ColumnValue oldStyleScalarValue = ColumnValue.newBuilder().addValue(NUMBER_VALUE).build(); + // [1, 1] + final ColumnValue oldStyleArrayValue = ColumnValue.newBuilder().addValue(NUMBER_VALUE) + .addValue(NUMBER_VALUE).build(); + + assertFalse(Frame.isNewStyleColumn(oldStyleScalarValue)); + assertFalse(Frame.isNewStyleColumn(oldStyleArrayValue)); + + Object scalar = Frame.parseOldStyleColumn(oldStyleScalarValue); + assertEquals(1L, scalar); + + Object array = Frame.parseOldStyleColumn(oldStyleArrayValue); + assertEquals(Arrays.asList(1L, 1L), array); + } + + @Test public void testColumnValueParsing() { + // 1 + final ColumnValue scalarValue = ColumnValue.newBuilder().setScalarValue(NUMBER_VALUE).build(); + // [1, 1] + final ColumnValue arrayValue = ColumnValue.newBuilder().addArrayValue(NUMBER_VALUE) + .addArrayValue(NUMBER_VALUE).setHasArrayValue(true).build(); + + assertTrue(Frame.isNewStyleColumn(scalarValue)); + assertTrue(Frame.isNewStyleColumn(arrayValue)); + + Object scalar = Frame.parseColumn(scalarValue); + assertEquals(1L, scalar); + + Object array = Frame.parseColumn(arrayValue); + assertEquals(Arrays.asList(1L, 1L), array); + } +} + +// End FrameTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/QueryStateTest.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/QueryStateTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/QueryStateTest.java new file mode 100644 index 0000000..d97bfa2 --- /dev/null +++ b/avatica/core/src/test/java/org/apache/calcite/avatica/QueryStateTest.java @@ -0,0 +1,513 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica; + +import org.apache.calcite.avatica.remote.MetaDataOperation; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.Statement; + +import static org.junit.Assert.assertEquals; + +/** + * Tests that {@link QueryState} properly retains the necessary state to recreate + * a {@link ResultSet}. + */ +public class QueryStateTest { + + private Connection conn; + private DatabaseMetaData metadata; + private Statement statement; + + + @Before + public void setup() throws Exception { + conn = Mockito.mock(Connection.class); + metadata = Mockito.mock(DatabaseMetaData.class); + statement = Mockito.mock(Statement.class); + + Mockito.when(conn.getMetaData()).thenReturn(metadata); + } + + @Test + public void testMetadataGetAttributes() throws Exception { + final String catalog = "catalog"; + final String schemaPattern = null; + final String typeNamePattern = "%"; + final String attributeNamePattern = "%"; + + QueryState state = new QueryState(MetaDataOperation.GET_ATTRIBUTES, catalog, schemaPattern, + typeNamePattern, attributeNamePattern); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getAttributes(catalog, schemaPattern, typeNamePattern, + attributeNamePattern); + } + + @Test + public void testMetadataGetBestRowIdentifier() throws Exception { + final String catalog = "catalog"; + final String schema = null; + final String table = "table"; + final int scope = 1; + final boolean nullable = true; + + QueryState state = new QueryState(MetaDataOperation.GET_BEST_ROW_IDENTIFIER, new Object[] { + catalog, + schema, + table, + scope, + nullable + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getBestRowIdentifier(catalog, schema, table, scope, nullable); + } + + @Test + public void testMetadataGetCatalogs() throws Exception { + QueryState state = new QueryState(MetaDataOperation.GET_CATALOGS, new Object[0]); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getCatalogs(); + } + + @Test + public void testMetadataGetColumnPrivileges() throws Exception { + final String catalog = null; + final String schema = "schema"; + final String table = "table"; + final String columnNamePattern = "%"; + + QueryState state = new QueryState(MetaDataOperation.GET_COLUMN_PRIVILEGES, new Object[] { + catalog, + schema, + table, + columnNamePattern + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getColumnPrivileges(catalog, schema, table, columnNamePattern); + } + + @Test + public void testMetadataGetColumns() throws Exception { + final String catalog = null; + final String schemaPattern = "%"; + final String tableNamePattern = "%"; + final String columnNamePattern = "%"; + + QueryState state = new QueryState(MetaDataOperation.GET_COLUMNS, new Object[] { + catalog, + schemaPattern, + tableNamePattern, + columnNamePattern + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getColumns(catalog, schemaPattern, tableNamePattern, + columnNamePattern); + } + + @Test + public void testMetadataGetCrossReference() throws Exception { + final String parentCatalog = null; + final String parentSchema = null; + final String parentTable = "%"; + final String foreignCatalog = null; + final String foreignSchema = null; + final String foreignTable = "%"; + + QueryState state = new QueryState(MetaDataOperation.GET_CROSS_REFERENCE, new Object[] { + parentCatalog, + parentSchema, + parentTable, + foreignCatalog, + foreignSchema, + foreignTable + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getCrossReference(parentCatalog, parentSchema, parentTable, + foreignCatalog, foreignSchema, foreignTable); + } + + @Test + public void testMetadataGetExportedKeys() throws Exception { + final String catalog = ""; + final String schema = null; + final String table = "mytable"; + + QueryState state = new QueryState(MetaDataOperation.GET_EXPORTED_KEYS, new Object[] { + catalog, + schema, + table + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getExportedKeys(catalog, schema, table); + } + + @Test + public void testMetadataGetFunctionColumns() throws Exception { + final String catalog = null; + final String schemaPattern = "%"; + final String functionNamePattern = "%"; + final String columnNamePattern = "%"; + + QueryState state = new QueryState(MetaDataOperation.GET_FUNCTION_COLUMNS, new Object[] { + catalog, + schemaPattern, + functionNamePattern, + columnNamePattern + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getFunctionColumns(catalog, schemaPattern, functionNamePattern, + columnNamePattern); + } + + @Test + public void testMetadataGetFunctions() throws Exception { + final String catalog = null; + final String schemaPattern = "%"; + final String functionNamePattern = "%"; + + QueryState state = new QueryState(MetaDataOperation.GET_FUNCTIONS, new Object[] { + catalog, + schemaPattern, + functionNamePattern + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getFunctions(catalog, schemaPattern, functionNamePattern); + } + + @Test + public void testMetadataGetImportedKeys() throws Exception { + final String catalog = ""; + final String schema = null; + final String table = "my_table"; + + QueryState state = new QueryState(MetaDataOperation.GET_IMPORTED_KEYS, new Object[] { + catalog, + schema, + table + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getImportedKeys(catalog, schema, table); + } + + @Test + public void testMetadataGetIndexInfo() throws Exception { + final String catalog = ""; + final String schema = null; + final String table = "my_table"; + final boolean unique = true; + final boolean approximate = true; + + QueryState state = new QueryState(MetaDataOperation.GET_INDEX_INFO, new Object[] { + catalog, + schema, + table, + unique, + approximate + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getIndexInfo(catalog, schema, table, unique, approximate); + } + + @Test + public void testMetadataGetPrimaryKeys() throws Exception { + final String catalog = ""; + final String schema = null; + final String table = "my_table"; + + QueryState state = new QueryState(MetaDataOperation.GET_PRIMARY_KEYS, new Object[] { + catalog, + schema, + table + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getPrimaryKeys(catalog, schema, table); + } + + @Test + public void testMetadataGetProcedureColumns() throws Exception { + final String catalog = ""; + final String schemaPattern = null; + final String procedureNamePattern = "%"; + final String columnNamePattern = "%"; + + QueryState state = new QueryState(MetaDataOperation.GET_PROCEDURE_COLUMNS, new Object[] { + catalog, + schemaPattern, + procedureNamePattern, + columnNamePattern + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getProcedureColumns(catalog, schemaPattern, procedureNamePattern, + columnNamePattern); + } + + @Test + public void testMetadataGetProcedures() throws Exception { + final String catalog = ""; + final String schemaPattern = null; + final String procedureNamePattern = "%"; + + QueryState state = new QueryState(MetaDataOperation.GET_PROCEDURES, new Object[] { + catalog, + schemaPattern, + procedureNamePattern, + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getProcedures(catalog, schemaPattern, procedureNamePattern); + } + + @Test + public void testMetadataGetPseudoColumns() throws Exception { + final String catalog = ""; + final String schemaPattern = null; + final String tableNamePattern = "%"; + final String columnNamePattern = "%"; + + QueryState state = new QueryState(MetaDataOperation.GET_PSEUDO_COLUMNS, new Object[] { + catalog, + schemaPattern, + tableNamePattern, + columnNamePattern + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getPseudoColumns(catalog, schemaPattern, tableNamePattern, + columnNamePattern); + } + + @Test + public void testMetadataGetSchemas() throws Exception { + QueryState state = new QueryState(MetaDataOperation.GET_SCHEMAS, new Object[0]); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getSchemas(); + } + + @Test + public void testMetadataGetSchemasWithArgs() throws Exception { + final String catalog = ""; + final String schemaPattern = null; + + QueryState state = new QueryState(MetaDataOperation.GET_SCHEMAS_WITH_ARGS, new Object[] { + catalog, + schemaPattern + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getSchemas(catalog, schemaPattern); + } + + @Test + public void testMetadataGetSuperTables() throws Exception { + final String catalog = ""; + final String schemaPattern = null; + final String tableNamePattern = "%"; + + QueryState state = new QueryState(MetaDataOperation.GET_SUPER_TABLES, new Object[] { + catalog, + schemaPattern, + tableNamePattern + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getSuperTables(catalog, schemaPattern, tableNamePattern); + } + + @Test + public void testMetadataGetSuperTypes() throws Exception { + final String catalog = ""; + final String schemaPattern = null; + final String tableNamePattern = "%"; + + QueryState state = new QueryState(MetaDataOperation.GET_SUPER_TYPES, new Object[] { + catalog, + schemaPattern, + tableNamePattern + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getSuperTypes(catalog, schemaPattern, tableNamePattern); + } + + @Test + public void testMetadataGetTablePrivileges() throws Exception { + final String catalog = ""; + final String schemaPattern = null; + final String tableNamePattern = "%"; + + QueryState state = new QueryState(MetaDataOperation.GET_TABLE_PRIVILEGES, new Object[] { + catalog, + schemaPattern, + tableNamePattern + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getTablePrivileges(catalog, schemaPattern, tableNamePattern); + } + + @Test + public void testMetadataGetTables() throws Exception { + final String catalog = ""; + final String schemaPattern = null; + final String tableNamePattern = "%"; + final String[] types = new String[] {"VIEW", "TABLE"}; + + QueryState state = new QueryState(MetaDataOperation.GET_TABLES, new Object[] { + catalog, + schemaPattern, + tableNamePattern, + types + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getTables(catalog, schemaPattern, tableNamePattern, types); + } + + @Test + public void testMetadataGetTableTypes() throws Exception { + QueryState state = new QueryState(MetaDataOperation.GET_TABLE_TYPES, new Object[0]); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getTableTypes(); + } + + @Test + public void testMetadataGetTypeInfo() throws Exception { + QueryState state = new QueryState(MetaDataOperation.GET_TYPE_INFO, new Object[0]); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getTypeInfo(); + } + + @Test + public void testMetadataGetUDTs() throws Exception { + final String catalog = ""; + final String schemaPattern = null; + final String typeNamePattern = "%"; + final int[] types = new int[] {1, 2}; + + QueryState state = new QueryState(MetaDataOperation.GET_UDTS, new Object[] { + catalog, + schemaPattern, + typeNamePattern, + types + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getUDTs(catalog, schemaPattern, typeNamePattern, types); + } + + @Test + public void testMetadataGetVersionColumns() throws Exception { + final String catalog = ""; + final String schemaPattern = null; + final String table = "my_table"; + + QueryState state = new QueryState(MetaDataOperation.GET_VERSION_COLUMNS, new Object[] { + catalog, + schemaPattern, + table + }); + + state.invoke(conn, statement); + + Mockito.verify(metadata).getVersionColumns(catalog, schemaPattern, table); + } + + @Test + public void testSerialization() throws Exception { + final String catalog = "catalog"; + final String schema = null; + final String table = "table"; + final int scope = 1; + final boolean nullable = true; + + QueryState state = new QueryState(MetaDataOperation.GET_BEST_ROW_IDENTIFIER, new Object[] { + catalog, + schema, + table, + scope, + nullable + }); + + assertEquals(state, QueryState.fromProto(state.toProto())); + + final String schemaPattern = null; + final String typeNamePattern = "%"; + final int[] types = new int[] {1, 2}; + + state = new QueryState(MetaDataOperation.GET_UDTS, new Object[] { + catalog, + schemaPattern, + typeNamePattern, + types + }); + + assertEquals(state, QueryState.fromProto(state.toProto())); + + state = new QueryState("SELECT * FROM foo"); + + assertEquals(state, QueryState.fromProto(state.toProto())); + } + +} + +// End QueryStateTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/metrics/MetricsHelperTest.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/metrics/MetricsHelperTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/metrics/MetricsHelperTest.java new file mode 100644 index 0000000..c85312d --- /dev/null +++ b/avatica/core/src/test/java/org/apache/calcite/avatica/metrics/MetricsHelperTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica.metrics; + +import org.apache.calcite.avatica.remote.MetricsHelper; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Test class for {@link MetricsHelper}. + */ +public class MetricsHelperTest { + + @Test(expected = NullPointerException.class) public void testNullConcat() { + MetricsHelper.concat(null, "foo"); + } + + @Test public void testConcat() { + String suffix = "suffix"; + String finalName = getClass().getName() + "." + suffix; + assertEquals(finalName, MetricsHelper.concat(getClass(), suffix)); + } + +} + +// End MetricsHelperTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AbstractHandlerTest.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AbstractHandlerTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AbstractHandlerTest.java new file mode 100644 index 0000000..012cccc --- /dev/null +++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AbstractHandlerTest.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica.remote; + +import org.apache.calcite.avatica.AvaticaSeverity; +import org.apache.calcite.avatica.remote.Handler.HandlerResponse; +import org.apache.calcite.avatica.remote.Service.ErrorResponse; +import org.apache.calcite.avatica.remote.Service.Request; +import org.apache.calcite.avatica.remote.Service.Response; + +import org.junit.Test; +import org.mockito.Mockito; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Objects; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Test for common functionality across {@link Handler} implementations. + */ +public class AbstractHandlerTest { + + private String exceptionToString(Exception e) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + Objects.requireNonNull(e).printStackTrace(pw); + return sw.toString(); + } + + @Test public void testExceptionUnwrappingWithoutContext() { + @SuppressWarnings("unchecked") + AbstractHandler<String> handler = Mockito.mock(AbstractHandler.class); + + Mockito.when(handler.unwrapException(Mockito.any(Exception.class))).thenCallRealMethod(); + + Exception e = new RuntimeException(); + Response resp = handler.unwrapException(e); + assertTrue("Response should be ErrorResponse, but was " + resp.getClass(), + resp instanceof ErrorResponse); + ErrorResponse errorResp = (ErrorResponse) resp; + assertEquals(ErrorResponse.UNKNOWN_ERROR_CODE, errorResp.errorCode); + assertEquals(AvaticaSeverity.UNKNOWN, errorResp.severity); + assertEquals(Arrays.asList(exceptionToString(e)), errorResp.exceptions); + + e = new AvaticaRuntimeException(); + resp = handler.unwrapException(e); + assertTrue("Response should be ErrorResponse, but was " + resp.getClass(), + resp instanceof ErrorResponse); + errorResp = (ErrorResponse) resp; + assertEquals(ErrorResponse.UNKNOWN_ERROR_CODE, errorResp.errorCode); + assertEquals(AvaticaSeverity.UNKNOWN, errorResp.severity); + assertEquals(Arrays.asList(exceptionToString(e)), errorResp.exceptions); + } + + @Test public void testExceptionUnwrappingWithContext() { + @SuppressWarnings("unchecked") + AbstractHandler<String> handler = Mockito.mock(AbstractHandler.class); + + Mockito.when(handler.unwrapException(Mockito.any(Exception.class))).thenCallRealMethod(); + + final String msg = "Something failed!"; + AvaticaRuntimeException e = new AvaticaRuntimeException(msg, + ErrorResponse.UNKNOWN_ERROR_CODE, ErrorResponse.UNKNOWN_SQL_STATE, AvaticaSeverity.FATAL); + Response resp = handler.unwrapException(e); + assertTrue("Response should be ErrorResponse, but was " + resp.getClass(), + resp instanceof ErrorResponse); + ErrorResponse errorResp = (ErrorResponse) resp; + assertEquals(ErrorResponse.UNKNOWN_ERROR_CODE, errorResp.errorCode); + assertEquals(AvaticaSeverity.FATAL, errorResp.severity); + assertEquals(Arrays.asList(exceptionToString(e)), errorResp.exceptions); + assertEquals(msg, errorResp.errorMessage); + } + + @Test public void testFailedResponseSerialization() throws IOException { + @SuppressWarnings("unchecked") + final AbstractHandler<String> handler = Mockito.mock(AbstractHandler.class); + final Request request = Mockito.mock(Request.class); + final Response response = Mockito.mock(Response.class); + final IOException exception = new IOException(); + final ErrorResponse errorResponse = Mockito.mock(ErrorResponse.class); + final String serializedErrorResponse = "An ErrorResponse"; + + // Accept a serialized request + Mockito.when(handler.apply(Mockito.anyString())).thenCallRealMethod(); + // Deserialize it back into a POJO + Mockito.when(handler.decode(Mockito.anyString())).thenReturn(request); + // Construct the Response for that Request + Mockito.when(request.accept(Mockito.any(Service.class))).thenReturn(response); + // Throw an IOException when serializing the Response. + Mockito.when(handler.encode(response)).thenThrow(exception); + // Convert the IOException into an ErrorResponse + Mockito.when(handler.unwrapException(exception)).thenReturn(errorResponse); + Mockito.when(handler.encode(errorResponse)).thenReturn(serializedErrorResponse); + + HandlerResponse<String> handlerResp = handler.apply("this is mocked out"); + assertEquals(500, handlerResp.getStatusCode()); + assertEquals(serializedErrorResponse, handlerResp.getResponse()); + } + + @Test public void testFailedErrorResponseSerialization() throws IOException { + @SuppressWarnings("unchecked") + final AbstractHandler<String> handler = Mockito.mock(AbstractHandler.class); + final Request request = Mockito.mock(Request.class); + final Response response = Mockito.mock(Response.class); + final IOException exception = new IOException(); + final ErrorResponse errorResponse = Mockito.mock(ErrorResponse.class); + + // Accept a serialized request + Mockito.when(handler.apply(Mockito.anyString())).thenCallRealMethod(); + // Deserialize it back into a POJO + Mockito.when(handler.decode(Mockito.anyString())).thenReturn(request); + // Construct the Response for that Request + Mockito.when(request.accept(Mockito.any(Service.class))).thenReturn(response); + // Throw an IOException when serializing the Response. + Mockito.when(handler.encode(response)).thenThrow(exception); + // Convert the IOException into an ErrorResponse + Mockito.when(handler.unwrapException(exception)).thenReturn(errorResponse); + // Fail to serialize the ErrorResponse + Mockito.when(handler.encode(errorResponse)).thenThrow(exception); + + try { + handler.apply("this is mocked out"); + } catch (RuntimeException e) { + assertEquals(exception, e.getCause()); + } + } + + @Test public void testFailedRequestDeserialization() throws IOException { + @SuppressWarnings("unchecked") + final AbstractHandler<String> handler = Mockito.mock(AbstractHandler.class); + final IOException exception = new IOException(); + + // Accept a serialized request + Mockito.when(handler.apply(Mockito.anyString())).thenCallRealMethod(); + // Throw an Exception trying to convert it back into a POJO + Mockito.when(handler.decode(Mockito.anyString())).thenThrow(exception); + + try { + handler.apply("this is mocked out"); + } catch (RuntimeException e) { + assertEquals(exception, e.getCause()); + } + } +} + +// End AbstractHandlerTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryTest.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryTest.java new file mode 100644 index 0000000..cd64329 --- /dev/null +++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryTest.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica.remote; + +import org.apache.calcite.avatica.BuiltInConnectionProperty; +import org.apache.calcite.avatica.ConnectionConfig; +import org.apache.calcite.avatica.ConnectionConfigImpl; + +import org.junit.Test; + +import java.net.URL; +import java.util.Properties; + +import static org.junit.Assert.assertTrue; + +/** + * Tests for the factory that creates http clients. + */ +public class AvaticaHttpClientFactoryTest { + + @Test public void testDefaultHttpClient() throws Exception { + Properties props = new Properties(); + URL url = new URL("http://localhost:8765"); + ConnectionConfig config = new ConnectionConfigImpl(props); + AvaticaHttpClientFactory httpClientFactory = new AvaticaHttpClientFactoryImpl(); + + AvaticaHttpClient client = httpClientFactory.getClient(url, config); + assertTrue("Client was an instance of " + client.getClass(), + client instanceof AvaticaCommonsHttpClientImpl); + } + + @Test public void testOverridenHttpClient() throws Exception { + Properties props = new Properties(); + props.setProperty(BuiltInConnectionProperty.HTTP_CLIENT_IMPL.name(), + AvaticaHttpClientImpl.class.getName()); + URL url = new URL("http://localhost:8765"); + ConnectionConfig config = new ConnectionConfigImpl(props); + AvaticaHttpClientFactory httpClientFactory = new AvaticaHttpClientFactoryImpl(); + + AvaticaHttpClient client = httpClientFactory.getClient(url, config); + assertTrue("Client was an instance of " + client.getClass(), + client instanceof AvaticaHttpClientImpl); + } +} + +// End AvaticaHttpClientFactoryTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientTest.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientTest.java new file mode 100644 index 0000000..33369d2 --- /dev/null +++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaHttpClientTest.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica.remote; + +import org.junit.Test; +import org.mockito.Mockito; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +import static org.junit.Assert.assertArrayEquals; + +/** + * Tests for the HTTP transport. + */ +public class AvaticaHttpClientTest { + private static final String REQUEST = + "{\"request\":\"createStatement\",\"connectionId\":\"8f3f28ee-d0bb-4cdb-a4b1-8f6e8476c534\"}"; + private static final String RESPONSE = + "{\"response\":\"createStatement\",\"connectionId\":" + + "\"8f3f28ee-d0bb-4cdb-a4b1-8f6e8476c534\",\"statementId\":1608176856}"; + + @Test + public void testRetryOnUnavailable() throws Exception { + // HTTP-503, try again + URL url = new URL("http://127.0.0.1:8765"); + final HttpURLConnection cnxn = Mockito.mock(HttpURLConnection.class); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ByteArrayInputStream bais = new ByteArrayInputStream(RESPONSE.getBytes(StandardCharsets.UTF_8)); + + // Create the HTTP client + AvaticaHttpClientImpl client = new AvaticaHttpClientImpl(url) { + @Override HttpURLConnection openConnection() throws IOException { + return cnxn; + } + }; + + // HTTP 503 then 200 + Mockito.when(cnxn.getResponseCode()).thenReturn(HttpURLConnection.HTTP_UNAVAILABLE, + HttpURLConnection.HTTP_OK); + + Mockito.when(cnxn.getOutputStream()).thenReturn(baos); + Mockito.when(cnxn.getInputStream()).thenReturn(bais); + + byte[] response = client.send(REQUEST.getBytes(StandardCharsets.UTF_8)); + + assertArrayEquals(RESPONSE.getBytes(StandardCharsets.UTF_8), response); + } + + @Test(expected = RuntimeException.class) + public void testServerError() throws Exception { + // HTTP 500 should error out + URL url = new URL("http://127.0.0.1:8765"); + final HttpURLConnection cnxn = Mockito.mock(HttpURLConnection.class); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // Create the HTTP client + AvaticaHttpClientImpl client = new AvaticaHttpClientImpl(url) { + @Override HttpURLConnection openConnection() throws IOException { + return cnxn; + } + }; + + // HTTP 500 + Mockito.when(cnxn.getResponseCode()).thenReturn(HttpURLConnection.HTTP_INTERNAL_ERROR); + + Mockito.when(cnxn.getOutputStream()).thenReturn(baos); + + // Should throw an RTE + client.send(REQUEST.getBytes(StandardCharsets.UTF_8)); + } + +} + +// End AvaticaHttpClientTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java new file mode 100644 index 0000000..f66cb26 --- /dev/null +++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ErrorResponseTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica.remote; + +import org.apache.calcite.avatica.AvaticaClientRuntimeException; +import org.apache.calcite.avatica.AvaticaSeverity; +import org.apache.calcite.avatica.remote.Service.ErrorResponse; +import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * A test class for ErrorResponse. + */ +public class ErrorResponseTest { + + @Test public void testEquality() { + final String message = "There was an error"; + final int code = 23; + final String state = "a1b2c"; + final AvaticaSeverity severity = AvaticaSeverity.ERROR; + final List<String> exceptions = Arrays.asList("Server Stacktrace 1", "Server Stacktace 2"); + final RpcMetadataResponse metadata = new RpcMetadataResponse("localhost:8765"); + assertEquals(new ErrorResponse(message, code, state, severity, exceptions, metadata), + new ErrorResponse(message, code, state, severity, exceptions, metadata)); + } + + @Test public void testToClientRTE() { + final String message = "There was an error"; + final int code = 23; + final String state = "a1b2c"; + final AvaticaSeverity severity = AvaticaSeverity.ERROR; + final List<String> exceptions = Arrays.asList("Server Stacktrace 1", "Server Stacktace 2"); + final RpcMetadataResponse metadata = new RpcMetadataResponse("localhost:8765"); + final ErrorResponse resp = new ErrorResponse(message, code, state, severity, exceptions, + metadata); + AvaticaClientRuntimeException exception = resp.toException(); + assertTrue("Expected error message to end with '" + resp.errorMessage + "', but was '" + + exception.getMessage() + "'", exception.getMessage().endsWith(resp.errorMessage)); + assertEquals(resp.errorCode, exception.getErrorCode()); + assertEquals(resp.severity, exception.getSeverity()); + assertEquals(resp.sqlState, exception.getSqlState()); + assertEquals(resp.exceptions, exception.getServerExceptions()); + assertEquals(resp.rpcMetadata, exception.getRpcMetadata()); + } +} + +// End ErrorResponseTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/MetaDataOperationTest.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/MetaDataOperationTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/MetaDataOperationTest.java new file mode 100644 index 0000000..c64b32c --- /dev/null +++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/MetaDataOperationTest.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica.remote; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Tests for {@link MetaDataOperation} + */ +public class MetaDataOperationTest { + + @Test + public void testProtobufSerialization() { + for (MetaDataOperation metadataOp : MetaDataOperation.values()) { + assertEquals(metadataOp, MetaDataOperation.fromProto(metadataOp.toProto())); + } + } + +} + +// End MetaDataOperationTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java new file mode 100644 index 0000000..afb15c3 --- /dev/null +++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica.remote; + +import org.apache.calcite.avatica.Meta; +import org.apache.calcite.avatica.Meta.Frame; +import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystem; +import org.apache.calcite.avatica.proto.Common; +import org.apache.calcite.avatica.proto.Common.ColumnValue; +import org.apache.calcite.avatica.proto.Requests; +import org.apache.calcite.avatica.proto.Responses; +import org.apache.calcite.avatica.remote.Handler.HandlerResponse; +import org.apache.calcite.avatica.remote.Service.FetchRequest; +import org.apache.calcite.avatica.remote.Service.FetchResponse; +import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +/** + * Test basic serialization of objects with protocol buffers. + */ +public class ProtobufHandlerTest { + + // Mocks + private Service service; + private ProtobufTranslation translation; + + // Real objects + private ProtobufHandler handler; + + @Before + public void setupMocks() { + // Mocks + service = Mockito.mock(Service.class); + translation = Mockito.mock(ProtobufTranslation.class); + + // Real objects + handler = new ProtobufHandler(service, translation, NoopMetricsSystem.getInstance()); + } + + @Test + public void testFetch() throws Exception { + final String connectionId = "cnxn1"; + final int statementId = 30; + final long offset = 10; + final int fetchMaxRowCount = 100; + final List<Common.TypedValue> values = new ArrayList<>(); + + values.add(Common.TypedValue.newBuilder().setType(Common.Rep.BOOLEAN).setBoolValue(true) + .build()); + values.add(Common.TypedValue.newBuilder().setType(Common.Rep.STRING) + .setStringValue("my_string").build()); + + Requests.FetchRequest protoRequest = Requests.FetchRequest.newBuilder() + .setConnectionId(connectionId).setStatementId(statementId) + .setOffset(offset).setFetchMaxRowCount(fetchMaxRowCount) + .build(); + byte[] serializedRequest = protoRequest.toByteArray(); + + FetchRequest request = new FetchRequest().deserialize(protoRequest); + + List<Object> frameRows = new ArrayList<>(); + frameRows.add(new Object[] {true, "my_string"}); + + Meta.Frame frame = Frame.create(0, true, frameRows); + RpcMetadataResponse metadata = new RpcMetadataResponse("localhost:8765"); + FetchResponse response = new FetchResponse(frame, false, false, metadata); + + when(translation.parseRequest(serializedRequest)).thenReturn(request); + when(service.apply(request)).thenReturn(response); + when(translation.serializeResponse(response)) + .thenReturn(response.serialize().toByteArray()); + + HandlerResponse<byte[]> handlerResponse = handler.apply(serializedRequest); + byte[] serializedResponse = handlerResponse.getResponse(); + assertEquals(200, handlerResponse.getStatusCode()); + + Responses.FetchResponse protoResponse = Responses.FetchResponse.parseFrom(serializedResponse); + + Common.Frame protoFrame = protoResponse.getFrame(); + + assertEquals(frame.offset, protoFrame.getOffset()); + assertEquals(frame.done, protoFrame.getDone()); + + List<Common.Row> rows = protoFrame.getRowsList(); + assertEquals(1, rows.size()); + Common.Row row = rows.get(0); + List<Common.ColumnValue> columnValues = row.getValueList(); + assertEquals(2, columnValues.size()); + + Iterator<Common.ColumnValue> iter = columnValues.iterator(); + assertTrue(iter.hasNext()); + Common.ColumnValue column = iter.next(); + assertTrue("The Column should have contained a scalar: " + column, + column.hasField(ColumnValue.getDescriptor() + .findFieldByNumber(ColumnValue.SCALAR_VALUE_FIELD_NUMBER))); + + Common.TypedValue value = column.getScalarValue(); + assertEquals(Common.Rep.BOOLEAN, value.getType()); + assertEquals(true, value.getBoolValue()); + + assertTrue(iter.hasNext()); + column = iter.next(); + assertTrue("The Column should have contained a scalar: " + column, + column.hasField(ColumnValue.getDescriptor() + .findFieldByNumber(ColumnValue.SCALAR_VALUE_FIELD_NUMBER))); + value = column.getScalarValue(); + assertEquals(Common.Rep.STRING, value.getType()); + assertEquals("my_string", value.getStringValue()); + } + +} + +// End ProtobufHandlerTest.java
