├── .gitignore ├── LICENSE ├── README.md ├── examples ├── CompoundElements.java ├── CrudOperations.java ├── PreparedStatements.java ├── README.md ├── SimpleSelects.java ├── UseCredentials.java └── UseSQLJSON.java ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── couchbase │ │ ├── jdbc │ │ ├── CBArray.java │ │ ├── CBConnection.java │ │ ├── CBDatabaseMetaData.java │ │ ├── CBDriver.java │ │ ├── CBParameterMetaData.java │ │ ├── CBPreparedResult.java │ │ ├── CBPreparedStatement.java │ │ ├── CBResultSet.java │ │ ├── CBResultSetMetaData.java │ │ ├── CBStatement.java │ │ ├── ConnectionParameters.java │ │ ├── connect │ │ │ ├── Cluster.java │ │ │ ├── Instance.java │ │ │ └── Protocol.java │ │ ├── core │ │ │ ├── CouchBaseSQLException.java │ │ │ ├── CouchError.java │ │ │ ├── CouchMetrics.java │ │ │ ├── CouchResponse.java │ │ │ ├── EscapedFunctions.java │ │ │ ├── Field.java │ │ │ ├── Parser.java │ │ │ ├── ProtocolImpl.java │ │ │ └── SqlJsonImplementation.java │ │ └── util │ │ │ ├── Credentials.java │ │ │ ├── JSONTypes.java │ │ │ ├── SqlParser.java │ │ │ ├── StringUtil.java │ │ │ └── TimestampUtils.java │ │ └── json │ │ └── SQLJSON.java └── resources │ └── META-INF │ └── services │ └── java.sql.Driver └── test ├── java └── com │ └── couchbase │ └── jdbc │ ├── BigDataJDBCDriverTests.java │ ├── BucketInfo.java │ ├── CBDatabaseMetaDataTest.java │ ├── ClusterAwareTests.java │ ├── ClusterInfo.java │ ├── ClusterSetupUtils.java │ ├── ClusterTest.java │ ├── ConnectionTest.java │ ├── CouchBaseTestCase.java │ ├── DriverTest.java │ ├── JDBCTestUtils.java │ ├── N1QLErrorJDBCDriverHandlingTests.java │ ├── NodeInfo.java │ ├── PreparedStatementTest.java │ ├── ProjectionJDBCDriverTests.java │ ├── QueryInfo.java │ ├── RQGJDBCTests.java │ ├── ResultSetMetaDataTest.java │ ├── ResultSetTest.java │ ├── SSLConnectionTest.java │ ├── SqlParserTest.java │ ├── StatementTest.java │ ├── Test16017.java │ ├── TestBoon.java │ ├── TestResultAnalysis.java │ ├── TestSQLJson.java │ ├── TestUser.java │ ├── TestUtil.java │ └── WrapperTest.java └── resources ├── config.json ├── dev_config.json ├── dev_environment.properties ├── dev_rqg_config.json ├── environment.properties ├── example1.json ├── local_config.json ├── local_environment.properties ├── logback-test.xml ├── rqg_config.json └── rqg_config.properties /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.idea/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Couchbase JDBC Driver 2 | 3 | This project is a JDBC driver for accessing the Couchbase NoSQL database. Couchbase stores data as JSON documents. 4 | The N1QL query language is based on SQL, but designed for querying JSON data sources, such as Couchbase. 5 | This driver lets the user run N1QL queries against Couchbase, and retrieve JSON data through the standard 6 | JDBC interfaces. 7 | 8 | Both simple JSON values (string/numbers/booleans) and compound JSON values (objects/arrays) are supported. 9 | Both types of values can be retrieved as fields and supplied as parameters. 10 | 11 | Users who do not have a pre-existing investment in JDBC but still want to access Couchbase through Java 12 | may find the [Couchbase Java SDK](http://developer.couchbase.com/documentation/server/current/sdk/java/start-using-sdk.html) 13 | a more convenient solution. 14 | 15 | ## Brief Setup Instructions 16 | 17 | The Maven coordinates of the driver are 18 | 19 | 20 | com.couchbase.jdbc 21 | jdbc-n1ql 22 | 1.0-BETA 23 | 24 | 25 | The driver enables communication with Couchbase through the standard JDBC interfaces. Use connect string `jdbc:couchbase://localhost:8093` to connect to the Couchbase instance on your local machine. 26 | 27 | If you have the *beer-sample* sample bucket installed, this query returns a manageable set of ten rows with two fields per row: 28 | 29 | select name, code from `beer-sample` where city = 'San Francisco' and type = 'brewery' 30 | 31 | To run the query, you will need to create a primary index on *beer-sample*. 32 | 33 | ## Detailed Setup Instructions 34 | 35 | These instructions assume you are conversant with the Java ecosystem, and therefore 36 | know your way around Maven. Use Maven 3 and Java 8. 37 | 38 | Install Couchbase on your local machine. Instructions are [here](http://developer.couchbase.com/documentation/server/current/getting-started/installing.html). 39 | 40 | Go to to Administration Console at *http://localhost:8091* and log in using the Administrator password you supplied during installation. In the *Settings* tab you will find the *Sample Buckets* section, with a set of available sample buckets. Check the *beer-sample* box to create this set of sample data. 41 | 42 | CBQ is the tool for running N1QL queries against Couchbase. Instructions for running it are [here](http://developer.couchbase.com/documentation/server/current/n1ql/n1ql-intro/cbq.html). Create a primary index on the *beer-sample* bucket by running this command in CBQ: 43 | 44 | create primary index on `beer-sample`; 45 | 46 | Create a new Maven project. Add this dependency to the *pom.xml* file, which should already exist in your new project: 47 | 48 | 49 | 50 | com.couchbase.jdbc 51 | jdbc-n1ql 52 | 1.0-BETA/version> 53 | 54 | 55 | 56 | Add a new Java class to the src/main/java/trial directory of the project: 57 | 58 | package com.couchbase.jdbc.examples; 59 | import java.sql.Connection; 60 | import java.sql.DriverManager; 61 | import java.sql.ResultSet; 62 | import java.sql.Statement; 63 | public class SimpleVerification { 64 | static String ConnectionURL = "jdbc:couchbase://localhost:8093"; 65 | public static void main(String[] args) throws Exception { 66 | Connection con = DriverManager.getConnection(ConnectionURL); 67 | Statement stmt = con.createStatement(); 68 | ResultSet rs = stmt.executeQuery("select name,code from `beer-sample` where city = 'San Francisco' and type = 'brewery'"); 69 | while (rs.next()) { 70 | String name = rs.getString("name"); 71 | String code = rs.getString("code"); 72 | System.out.println("name: " + name + ", code: " + code); 73 | } 74 | } 75 | } 76 | 77 | Run the new class. You should see this output: 78 | 79 | 12:35:19.121 [main] DEBUG com.couchbase.CBResultSet - Loaded result set 80 | name: 21st Amendment Brewery Cafe, code: 94107 81 | name: Anchor Brewing, code: 94107 82 | name: Beach Chalet Brewery, code: 94121 83 | name: Big Bang Brewery (Closed), code: 94107 84 | name: Golden Gate Park Brewery, code: 85 | name: Magnolia Pub and Brewery, code: 94117 86 | name: Shmaltz Brewing Company, code: 87 | name: Shmaltz Enterprises, code: 94110 88 | name: Speakeasy Ales and Lagers, code: 94124 89 | name: ThirstyBear Brewing, code: 94105 90 | 91 | The */examples* directory contains additional example files, showing how to use the driver. 92 | 93 | ## Additional Usage Notes 94 | 95 | The connection string, used in the `DriverManager.getConnection()` call, should be of the form `jdbc:couchbase://:`. 96 | 97 | If you are working with an instance of Couchbase on your local machine, use the string `jdbc:couchbase://localhost:8093`. 98 | 99 | If you are working with a Couchbase cluster, the host should be the name or ip address of a Couchbase node running the Query service. 100 | The driver will then distribute the queries around the nodes of the cluster in round-robin fashion. You can check which nodes are 101 | running the Query service on the "Server Nodes" tab of the Couchbase Admin Console. The "Services" column shows which nodes are 102 | running which service. The port is the port of the query service; 8093 by default. 103 | 104 | Couchbase supports access over SSL-protected connections, but only Enterprise Edition supports this, not Community Edition. 105 | To use SSL, connect on the 18093 port rather than the standard 8093 port. For a more detailed example of how to use SSL, consult 106 | the SSL connection test at *src/test/java/com/couchbase/jdbc/SSLConnectionTest.java*. 107 | 108 | ## Building the Driver from Source 109 | 110 | You need to have Git, Maven 3 and Java 8 installed on your machine. 111 | 112 | Run the following commands: 113 | 114 | git clone https://github.com/jdbc-json/jdbc-cb 115 | cd jdbc-cb 116 | mvn -Dmaven.test.skip=true package 117 | 118 | The JAR file will be in the */target* directory. 119 | 120 | By default, `mvn package` runs the unit tests before creating the JAR. 121 | These directions specifically omit running the unit tests, because the tests require additional setup, as explained in the next section. 122 | After the setup is complete, you can run `mvn package` without the extra flag. 123 | 124 | ## Running the Unit Tests 125 | 126 | (Assuming you have downloaded and built the driver from source, as described above.) 127 | 128 | The unit tests assume an instance of Couchbase Enterprise Edition is set up and accessible on 129 | the local machine. If the Couchbase instance is Community Edition, the SSLConnectionTest will fail because 130 | Community Edition instances are not accessible over SSL. 131 | The administrator and password should be "Administrator" and "password", 132 | respectively. The *beer-sample* and *default* data buckets 133 | should be present. The *default* bucket is always present; *beer-sample* is created on request at installation time. 134 | 135 | The beer-sample and default buckets must be indexed for the tests to run correctly. 136 | You can index them by running these two commands in the CBQ: 137 | 138 | create primary index on default; 139 | create primary index on `beer-sample`; 140 | 141 | Note the back-ticks in the second line. 142 | 143 | Run all the tests with this command: 144 | 145 | mvn test 146 | 147 | Run a specific test with a command like this: 148 | 149 | mvn test -Dtest=com.couchbase.jdbc.PreparedStatementTest 150 | -------------------------------------------------------------------------------- /examples/CompoundElements.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc.examples; 2 | 3 | import java.sql.Array; 4 | import java.sql.Connection; 5 | import java.sql.DriverManager; 6 | import java.sql.PreparedStatement; 7 | import java.sql.ResultSet; 8 | import java.sql.Statement; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /* 13 | * This example shows how work with JSON objects and arrays using the Couchbase JDBC driver. 14 | * It creates a value with two fields, received as parameters, one a JSON object and the 15 | * other a JSON array. 16 | * It then retrieves these two fields, and makes the accessible as a map and an array. 17 | */ 18 | 19 | public class CompoundElements { 20 | static String ConnectionURL = "jdbc:couchbase://localhost:8093"; 21 | 22 | @SuppressWarnings("unchecked") 23 | public static void main(String[] args) throws Exception { 24 | Connection con = DriverManager.getConnection(ConnectionURL); 25 | PreparedStatement ps = con.prepareStatement("insert into default(key, value) values ('comp1', { 'obj': ?, 'arr': ?, 'type': 'compound'})"); 26 | 27 | // Set a JSON object parameter. 28 | HashMap m = new HashMap(); 29 | m.put("length", "234"); 30 | m.put("width", "12"); 31 | ps.setObject(1, m); 32 | 33 | // Set a JSON array parameter. 34 | Object[] arrElems = new Object[]{ "one", "two", "three" }; 35 | Array arr = con.createArrayOf(null, arrElems); 36 | ps.setArray(2, arr); 37 | 38 | int numUpdate = ps.executeUpdate(); 39 | System.out.println("Created " + numUpdate); 40 | System.out.println(); 41 | Thread.sleep(1000); 42 | 43 | // Retrieve the object. 44 | Statement stmt = con.createStatement(); 45 | ResultSet rs = stmt.executeQuery("select obj, arr from default where type = 'compound'"); 46 | rs.next(); 47 | 48 | // Retrieve the JSON object field. 49 | System.out.println("RETRIEVED OBJECT FIELDS"); 50 | Map objResult = (Map) rs.getObject("obj"); 51 | System.out.println("length: " + objResult.get("length")); 52 | System.out.println("width: " + objResult.get("width")); 53 | System.out.println(); 54 | 55 | // Retrieve the JSON array field. 56 | System.out.println("RETRIEVED ARRAY ELEMENTS"); 57 | Array arrResult= rs.getArray("arr"); 58 | Object[] elements = (Object[]) arrResult.getArray(); 59 | for (Object o : elements) { 60 | System.out.println(o.toString()); 61 | } 62 | System.out.println(); 63 | 64 | // Clean up. 65 | numUpdate = stmt.executeUpdate("delete from default where type = 'compound'"); 66 | System.out.println("Deleted " + numUpdate); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/CrudOperations.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc.examples; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.ResultSet; 6 | import java.sql.Statement; 7 | 8 | /* 9 | * This example shows how to insert, update, select, and delete values 10 | * using the Couchbase JDBC driver. 11 | * 12 | * This example adds, modifies, retrieves, and deletes a value from the "default" bucket. 13 | */ 14 | public class CrudOperations { 15 | 16 | static String ConnectionURL = "jdbc:couchbase://localhost:8093"; 17 | 18 | public static void main(String[] args) throws Exception { 19 | 20 | Connection con = DriverManager.getConnection(ConnectionURL); 21 | Statement stmt = con.createStatement(); 22 | 23 | int numUpdates; 24 | 25 | // Create a value. 26 | numUpdates = stmt.executeUpdate("insert into default (key, value) values ('crud-test1', { 'type': 'crud', 'num': 55 }), ('crud-test2', { 'type': 'crud', 'num': 65 }) "); 27 | System.out.println("Created " + numUpdates + " values."); 28 | Thread.sleep(1000); // Wait for the update to propagate. 29 | 30 | // Modify a value. 31 | numUpdates = stmt.executeUpdate("update default set name = 'apple' where type = 'crud' and num = 55"); 32 | System.out.println("Modified " + numUpdates + " values."); 33 | Thread.sleep(1000); // Wait for the update to propagate. 34 | 35 | 36 | // Retrieve the values. 37 | ResultSet rs = stmt.executeQuery("select * from default where type = 'crud'"); 38 | while (rs.next()) { 39 | System.out.println("Value: " + rs.getString(1)); 40 | } 41 | 42 | // Delete the values. 43 | numUpdates = stmt.executeUpdate("delete from default where type = 'crud'"); 44 | System.out.println("Deleted " + numUpdates + " values."); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/PreparedStatements.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc.examples; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.PreparedStatement; 6 | import java.sql.ResultSet; 7 | import java.sql.Statement; 8 | 9 | /* 10 | * This example shows how to use prepared statements and set parameters 11 | * through the Couchbase JDBC driver. 12 | */ 13 | public class PreparedStatements { 14 | static String ConnectionURL = "jdbc:couchbase://localhost:8093"; 15 | 16 | public static void main(String[] args) throws Exception { 17 | Connection con = DriverManager.getConnection(ConnectionURL); 18 | PreparedStatement ps = con.prepareStatement("insert into default(key, value) values (?, [?, ?, ?, ?])"); 19 | int numUpdates; 20 | 21 | // Insert first array. 22 | ps.setString(1, "ps1"); 23 | ps.setInt(2, 33); 24 | ps.setDouble(3, 950.3); 25 | ps.setBoolean(4, false); 26 | ps.setString(5, "ps"); 27 | numUpdates = ps.executeUpdate(); 28 | System.out.println("Inserted " + numUpdates); 29 | 30 | // Insert second array. 31 | ps.setString(1, "ps2"); 32 | ps.setString(2, "second"); 33 | ps.setString(3, "second"); 34 | ps.setString(4, "second"); 35 | ps.setString(5, "ps"); 36 | numUpdates = ps.executeUpdate(); 37 | System.out.println("Inserted " + numUpdates); 38 | 39 | // Insert third array. 40 | ps.setString(1, "ps3"); 41 | ps.setInt(2, 3); 42 | ps.setInt(3, 3); 43 | ps.setInt(4, 3); 44 | ps.setString(5, "ps"); 45 | numUpdates = ps.executeUpdate(); 46 | System.out.println("Inserted " + numUpdates); 47 | 48 | // Retrieve what we inserted. 49 | Thread.sleep(1000); // Wait for the updates to propagate. 50 | Statement stmt = con.createStatement(); 51 | ResultSet rs = stmt.executeQuery("select meta(d).id, * from default d where d[3] = 'ps'"); 52 | while (rs.next()) { 53 | System.out.println("key: " + rs.getString("id") + ", value: " + rs.getString("d")); 54 | } 55 | 56 | // Clean up. 57 | numUpdates = stmt.executeUpdate("delete from default d where d[3] = 'ps'"); 58 | System.out.println("Deleted " + numUpdates); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # How to Use the Driver 2 | 3 | This directory contains example files that show how to use the driver. 4 | All five files have `main()` functions, so you can run them directly; 5 | they are set up to connect to the Couchbase instance running on the local machine. 6 | 7 | The first four require *beer-sample* and *default* buckets to exist and have primary indexes. 8 | The last one has more specific set-up instructions in the comments of the file itself. 9 | 10 | * *SimpleSelects.java* shows how to retrieve simple JSON values -- strings, numbers, and booleans -- from Couchbase. 11 | * *CrudOperations.java* shows how to create, read, update, and delete entries in Couchbase using the N1QL query languge, 12 | through the driver. 13 | * *PreparedStatements.java* shows how to use prepared statements and parameters. There queries are readied once, and can then 14 | be executed multiple times with varying parameters. 15 | * *CompoundElements.java* shows how to insert and retrieve JSON object and array values. 16 | * *UseSQLJSON.java* shows how to use SQLJSON, an extended interface for results, available only in this driver. 17 | * In Couchbase, buckets can be protected with passwords. *UseCredentials.java* shows how to provide such passwords to the driver 18 | to allow access to these buckets. 19 | -------------------------------------------------------------------------------- /examples/SimpleSelects.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc.examples; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.ResultSet; 6 | import java.sql.Statement; 7 | 8 | /* 9 | * This example shows 10 | * a) How to run a simple SELECT statement using the Couchbase JDBC driver. 11 | * b) How to retrieve scalar field values from the rows. 12 | */ 13 | public class SimpleSelects { 14 | static String ConnectionURL = "jdbc:couchbase://localhost:8093"; 15 | 16 | public static void main(String[] args) throws Exception { 17 | Connection con = DriverManager.getConnection(ConnectionURL); 18 | Statement stmt = con.createStatement(); 19 | 20 | ResultSet rs = stmt 21 | .executeQuery("select name, abv, (abv > 5) as strong from `beer-sample` where type = 'beer' limit 10"); 22 | System.out.println("TEN BEERS"); 23 | while (rs.next()) { 24 | String name = rs.getString("name"); 25 | double abv = rs.getDouble("abv"); 26 | boolean strong = rs.getBoolean("strong"); 27 | System.out.println("name: " + name + ", abv: " + abv + ", strong: " + strong); 28 | } 29 | System.out.println(); 30 | 31 | rs = stmt.executeQuery("select category, count(*) as num from `beer-sample` where type = 'beer' group by category"); 32 | System.out.println("BEER CATEGORIES"); 33 | while (rs.next()) { 34 | System.out.println(rs.getString("category") + ": " + rs.getInt("num")); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/UseCredentials.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc.examples; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.Statement; 6 | import java.util.Properties; 7 | 8 | import com.couchbase.jdbc.util.Credentials; 9 | 10 | /* 11 | * This example shows how to provide per-bucket credentials (passwords) 12 | * through the Couchbase JDBC driver. 13 | * 14 | * Initially, this code will not work. It requires some setup. 15 | * First create two buckets, rb1 and rb2. Then use the CBQ tool to create primary indexes on both 16 | * buckets. Finally add passwords rb1p and rb2p on the two buckets. 17 | * The code will then run correctly. 18 | * 19 | * Instead of supplying both per-bucket passwords, you can supply the Administrator 20 | * and admin password instead. That overrides the per-bucket passwords. 21 | */ 22 | public class UseCredentials { 23 | static String ConnectionURL = "jdbc:couchbase://localhost:8093"; 24 | 25 | public static void main(String[] args) throws Exception { 26 | Credentials cred = new Credentials(); 27 | cred.add("rb1", "rb1p"); 28 | cred.add("rb2", "rb2p"); 29 | // cred.add("Administrator", "password"); // Alternate credential. 30 | 31 | Properties prop = new Properties(); 32 | prop.setProperty("credentials", cred.toString()); 33 | 34 | Connection con = DriverManager.getConnection(ConnectionURL, prop); 35 | Statement stmt = con.createStatement(); 36 | stmt.executeQuery("select count(*) from rb1 union select count(*) from rb2"); 37 | System.out.println("Success."); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /examples/UseSQLJSON.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc.examples; 2 | 3 | import java.sql.DriverManager; 4 | import java.sql.Statement; 5 | import java.util.Arrays; 6 | import java.util.HashMap; 7 | 8 | import com.couchbase.jdbc.CBConnection; 9 | import com.couchbase.jdbc.CBPreparedStatement; 10 | import com.couchbase.jdbc.CBResultSet; 11 | import com.couchbase.json.SQLJSON; 12 | 13 | /* 14 | * This example shows how work with SQLJSON objects and arrays using the Couchbase JDBC driver. 15 | * It creates a value with two fields, received as parameters, one a JSON object and the 16 | * other a JSON array. 17 | * It then retrieves these two fields, and makes the accessible as a map and a list. 18 | */ 19 | 20 | public class UseSQLJSON { 21 | static String ConnectionURL = "jdbc:couchbase://localhost:8093"; 22 | 23 | public static void main(String[] args) throws Exception { 24 | // Note the use of specialized versions of Connection and PreparedStatement, 25 | // so we can use to the SQLJSON access functions. 26 | CBConnection con = (CBConnection)DriverManager.getConnection(ConnectionURL); 27 | CBPreparedStatement ps = (CBPreparedStatement)con.prepareStatement("insert into default(key, value) values ('comp1', { 'obj': ?, 'arr': ?, 'type': 'sqljson-compound'})"); 28 | 29 | // Set a JSON object parameter. 30 | HashMap m = new HashMap<>(); 31 | m.put("length", "234"); 32 | m.put("width", "12"); 33 | SQLJSON json = con.createSQLJSON(); 34 | json.setMap(m); 35 | ps.setSQLJSON(1, json); 36 | 37 | // Set a JSON array parameter. 38 | SQLJSON arr = con.createSQLJSON(); 39 | arr.setArray(Arrays.asList("one", "two", "three" )); 40 | ps.setSQLJSON(2, arr); 41 | 42 | int numUpdate = ps.executeUpdate(); 43 | System.out.println("Created " + numUpdate); 44 | System.out.println(); 45 | Thread.sleep(1000); 46 | 47 | // Retrieve the object. 48 | Statement stmt = con.createStatement(); 49 | CBResultSet rs = (CBResultSet)stmt.executeQuery("select obj, arr from default where type = 'sqljson-compound'"); 50 | rs.next(); 51 | 52 | // Retrieve the JSON object field. 53 | System.out.println("RETRIEVED OBJECT FIELDS"); 54 | SQLJSON objField = rs.getSQLJSON("obj"); 55 | System.out.println("length: " + objField.getMap().get("length")); 56 | System.out.println("width: " + objField.getMap().get("width")); 57 | System.out.println(); 58 | 59 | // Retrieve the JSON array field. 60 | System.out.println("RETRIEVED ARRAY ELEMENTS"); 61 | SQLJSON arrField = rs.getSQLJSON("arr"); 62 | for (Object o : arrField.getArray()) { 63 | System.out.println(o); 64 | } 65 | System.out.println(); 66 | 67 | // Clean up. 68 | numUpdate = stmt.executeUpdate("delete from default where type = 'sqljson-compound'"); 69 | System.out.println("Deleted " + numUpdate); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.couchbase.jdbc 6 | jdbc-n1ql 7 | 1.0-SNAPSHOT 8 | 9 | jar 10 | 11 | 12 | org.sonatype.oss 13 | oss-parent 14 | 7 15 | 16 | 17 | Couchbase JDBC-JSON Driver 18 | A JDBC driver for the Couchbase database, based on the N1QL query language. 19 | http://maven.apache.org 20 | 21 | 22 | 23 | The Apache License, Version 2.0 24 | http://www.apache.org/licenses/LICENSE-2.0.txt 25 | 26 | 27 | 28 | 29 | 30 | Johan Larson 31 | johan.larson@couchbase.com 32 | Couchbase 33 | http://www.couchbase.com 34 | 35 | 36 | David Cramer 37 | davecramer@gmail.com 38 | 39 | 40 | 41 | 42 | scm:git:git@github.com:jdbc-json/jdbc-cb.git 43 | scm:git:git@github.com:jdbc-json/jdbc-cb.git 44 | git@github.com:jdbc-json/jdbc-cb.git 45 | 46 | 47 | 48 | UTF-8 49 | /var/www/html 50 | 51 | 52 | 53 | 54 | junit 55 | junit 56 | 4.11 57 | test 58 | 59 | 60 | org.apache.httpcomponents 61 | httpclient 62 | 4.4 63 | 64 | 65 | com.googlecode.json-simple 66 | json-simple 67 | 1.1 68 | 69 | 70 | org.slf4j 71 | slf4j-api 72 | 1.7.12 73 | 74 | 75 | ch.qos.logback 76 | logback-classic 77 | 1.1.2 78 | 79 | 80 | io.fastjson 81 | boon 82 | 0.33 83 | 84 | 85 | 86 | 87 | 88 | ossrh 89 | https://oss.sonatype.org/content/repositories/snapshots 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.apache.maven.plugins 98 | maven-compiler-plugin 99 | 3.3 100 | 101 | 1.7 102 | 1.7 103 | 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-shade-plugin 108 | 2.4 109 | 110 | 111 | package 112 | 113 | shade 114 | 115 | 116 | 117 | 118 | junit:junit 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | release 133 | 134 | 135 | 136 | org.apache.maven.plugins 137 | maven-source-plugin 138 | 2.2.1 139 | 140 | 141 | attach-sources 142 | 143 | jar-no-fork 144 | 145 | 146 | 147 | 148 | 149 | org.apache.maven.plugins 150 | maven-javadoc-plugin 151 | 2.9.1 152 | 153 | 154 | attach-javadocs 155 | 156 | jar 157 | 158 | 159 | 160 | 161 | 162 | org.sonatype.plugins 163 | nexus-staging-maven-plugin 164 | 1.6.3 165 | true 166 | 167 | ossrh 168 | https://oss.sonatype.org/ 169 | true 170 | 171 | 172 | 173 | org.apache.maven.plugins 174 | maven-gpg-plugin 175 | 1.5 176 | 177 | 178 | sign-artifacts 179 | verify 180 | 181 | sign 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/CBDriver.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * // Copyright (c) 2015 Couchbase, Inc. 4 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * // except in compliance with the License. You may obtain a copy of the License at 6 | * // http://www.apache.org/licenses/LICENSE-2.0 7 | * // Unless required by applicable law or agreed to in writing, software distributed under the 8 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 9 | * // either express or implied. See the License for the specific language governing permissions 10 | * // and limitations under the License. 11 | */ 12 | 13 | package com.couchbase.jdbc; 14 | 15 | 16 | import ch.qos.logback.classic.Level; 17 | import ch.qos.logback.classic.Logger; 18 | 19 | import org.slf4j.LoggerFactory; 20 | 21 | import java.sql.*; 22 | import java.text.MessageFormat; 23 | import java.util.Properties; 24 | import java.util.concurrent.ConcurrentLinkedQueue; 25 | 26 | 27 | public class CBDriver implements java.sql.Driver 28 | { 29 | public static final org.slf4j.Logger logger = LoggerFactory.getLogger(CBDriver.class.getName()); 30 | 31 | public static final int MAJOR_VERSION = 1; 32 | 33 | public static final int MINOR_VERSION = 1; 34 | 35 | public static final String DRIVER_NAME = "n1ql_jdbc"; 36 | 37 | static CBDriver registered; 38 | 39 | final Thread houseKeepingThread; 40 | final ClusterThread ct; 41 | static 42 | { 43 | try 44 | { 45 | registered = new CBDriver(); 46 | java.sql.DriverManager.registerDriver(registered); 47 | 48 | } 49 | catch (SQLException e) 50 | { 51 | logger.error("Error registering driver", e); 52 | } 53 | 54 | 55 | } 56 | 57 | public CBDriver() throws SQLException 58 | { 59 | ct = new ClusterThread(); 60 | houseKeepingThread = new Thread(ct, "Couchbase housekeeping thread"); 61 | houseKeepingThread.setDaemon(true); 62 | houseKeepingThread.start(); 63 | } 64 | /** 65 | * Attempts to make a database connection to the given URL. 66 | * The driver should return "null" if it realizes it is the wrong kind 67 | * of driver to connect to the given URL. This will be common, as when 68 | * the JDBC driver manager is asked to connect to a given URL it passes 69 | * the URL to each loaded driver in turn. 70 | * 71 | *

The driver should throw an SQLException if it is the right 72 | * driver to connect to the given URL but has trouble connecting to 73 | * the database. 74 | * 75 | *

The java.util.Properties argument can be used to pass 76 | * arbitrary string tag/value pairs as connection arguments. 77 | * Normally at least "user" and "password" properties should be 78 | * included in the Properties object. 79 | * 80 | * @param url the URL of the database to which to connect 81 | * @param info a list of arbitrary string tag/value pairs as 82 | * connection arguments. Normally at least a "user" and 83 | * "password" property should be included. 84 | * @return a Connection object that represents a 85 | * connection to the URL 86 | * @throws java.sql.SQLException if a database access error occurs 87 | */ 88 | @Override 89 | public Connection connect(String url, Properties info) throws SQLException 90 | { 91 | 92 | if (acceptsURL(url)) 93 | { 94 | CBConnection con = new CBConnection(url, info); 95 | ct.addConnection(con); 96 | return con; 97 | } 98 | else 99 | { 100 | return null; 101 | } 102 | } 103 | 104 | /** 105 | * Retrieves whether the driver thinks that it can open a connection 106 | * to the given URL. Typically drivers will return true if they 107 | * understand the subprotocol specified in the URL and false if 108 | * they do not. 109 | * 110 | * @param url the URL of the database 111 | * @return true if this driver understands the given URL; 112 | * false otherwise 113 | * @throws java.sql.SQLException if a database access error occurs 114 | */ 115 | @Override 116 | public boolean acceptsURL(String url) throws SQLException 117 | { 118 | return url.startsWith("jdbc:couchbase:"); 119 | } 120 | 121 | /** 122 | * Gets information about the possible properties for this driver. 123 | * 124 | * The getPropertyInfo method is intended to allow a generic 125 | * GUI tool to discover what properties it should prompt 126 | * a human for in order to get 127 | * enough information to connect to a database. Note that depending on 128 | * the values the human has supplied so far, additional values may become 129 | * necessary, so it may be necessary to iterate though several calls 130 | * to the getPropertyInfo method. 131 | * 132 | * @param url the URL of the database to which to connect 133 | * @param info a proposed list of tag/value pairs that will be sent on 134 | * connect open 135 | * @return an array of DriverPropertyInfo objects describing 136 | * possible properties. This array may be an empty array if 137 | * no properties are required. 138 | * @throws java.sql.SQLException if a database access error occurs 139 | */ 140 | @Override 141 | public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException 142 | { 143 | return new DriverPropertyInfo[0]; 144 | } 145 | 146 | /** 147 | * Retrieves the driver's major version number. Initially this should be 1. 148 | * 149 | * @return this driver's major version number 150 | */ 151 | @Override 152 | public int getMajorVersion() 153 | { 154 | return 0; 155 | } 156 | 157 | /** 158 | * Gets the driver's minor version number. Initially this should be 0. 159 | * 160 | * @return this driver's minor version number 161 | */ 162 | @Override 163 | public int getMinorVersion() 164 | { 165 | return 0; 166 | } 167 | 168 | /** 169 | * Reports whether this driver is a genuine JDBC 170 | * Compliant(TM) driver. 171 | * A driver may only report true here if it passes the JDBC 172 | * compliance tests; otherwise it is required to return false. 173 | * 174 | * JDBC compliance requires full support for the JDBC API and full support 175 | * for SQL 92 Entry Level. It is expected that JDBC compliant drivers will 176 | * be available for all the major commercial databases. 177 | * 178 | * This method is not intended to encourage the development of non-JDBC 179 | * compliant drivers, but is a recognition of the fact that some vendors 180 | * are interested in using the JDBC API and framework for lightweight 181 | * databases that do not support full database functionality, or for 182 | * special databases such as document information retrieval where a SQL 183 | * implementation may not be feasible. 184 | * 185 | * @return true if this driver is JDBC Compliant; false 186 | * otherwise 187 | */ 188 | @Override 189 | public boolean jdbcCompliant() 190 | { 191 | return false; 192 | } 193 | 194 | private static final MessageFormat mf = new MessageFormat("Method {0}.{1} is not yet implemented."); 195 | 196 | public static java.sql.SQLFeatureNotSupportedException notImplemented(Class callClass, String functionName) 197 | { 198 | 199 | return new java.sql.SQLFeatureNotSupportedException(mf.format(new Object [] {callClass.getName(),functionName})); 200 | } 201 | 202 | public static void setLogLevel(Level logLevel) 203 | { 204 | synchronized (CBDriver.class) 205 | { 206 | Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.couchbase"); 207 | logger.setLevel(logLevel); 208 | //logLevelSet = true; 209 | } 210 | } 211 | 212 | public static Level getLogLevel() 213 | { 214 | synchronized (CBDriver.class) 215 | { 216 | Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.couchbase"); 217 | return logger.getLevel(); 218 | } 219 | } 220 | 221 | /** 222 | * Return the parent Logger of all the Loggers used by this driver. This 223 | * should be the Logger farthest from the root Logger that is 224 | * still an ancestor of all of the Loggers used by this driver. Configuring 225 | * this Logger will affect all of the log messages generated by the driver. 226 | * In the worst case, this may be the root Logger. 227 | * 228 | * @return the parent Logger for this driver 229 | * @throws java.sql.SQLFeatureNotSupportedException if the driver does not use java.util.logging. 230 | * @since 1.7 231 | */ 232 | @Override 233 | public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException 234 | { 235 | throw notImplemented(CBDriver.class, "getParentLogger"); 236 | } 237 | 238 | public static void cleanup() 239 | { 240 | if (registered != null) 241 | { 242 | try 243 | { 244 | DriverManager.deregisterDriver(registered); 245 | 246 | //stop the thread below 247 | runCluster=false; 248 | Thread.currentThread().interrupt(); 249 | 250 | 251 | } 252 | catch (SQLException e) 253 | { 254 | logger.warn("Error deregistering driver", e); 255 | } 256 | } 257 | } 258 | public void cleanup(CBConnection con) 259 | { 260 | ct.removeConnection(con); 261 | } 262 | 263 | static boolean runCluster=true; 264 | 265 | private static class ClusterThread implements Runnable 266 | { 267 | 268 | ConcurrentLinkedQueue connections; 269 | ClusterThread() 270 | { 271 | connections = new ConcurrentLinkedQueue(); 272 | } 273 | @Override 274 | public void run() 275 | { 276 | while(runCluster) 277 | { 278 | CBConnection connection = connections.poll(); 279 | if ( connection != null ) 280 | { 281 | try 282 | { 283 | connection.pollCluster(); 284 | } catch (SQLException e) 285 | { 286 | logger.error("Error polling cluster", e); 287 | } 288 | } 289 | try 290 | { 291 | Thread.sleep(1000); 292 | } 293 | catch (InterruptedException e) 294 | { 295 | // ignore it 296 | } 297 | } 298 | } 299 | public void addConnection(CBConnection connection) 300 | { 301 | connections.add(connection); 302 | } 303 | public void removeConnection(CBConnection connection) 304 | { 305 | connections.remove(connection); 306 | } 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/CBParameterMetaData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | 15 | import java.sql.ParameterMetaData; 16 | import java.sql.SQLException; 17 | 18 | /** 19 | * Created by davec on 2015-07-28. 20 | */ 21 | public class CBParameterMetaData implements ParameterMetaData 22 | { 23 | final CBPreparedResult preparedResult; 24 | 25 | public CBParameterMetaData(CBPreparedResult preparedResult) 26 | { 27 | this.preparedResult = preparedResult; 28 | } 29 | /** 30 | * Retrieves the number of parameters in the PreparedStatement 31 | * object for which this ParameterMetaData object contains 32 | * information. 33 | * 34 | * @return the number of parameters 35 | * @throws SQLException if a database access error occurs 36 | * @since 1.4 37 | */ 38 | @Override 39 | public int getParameterCount() throws SQLException 40 | { 41 | return 0; 42 | } 43 | 44 | /** 45 | * Retrieves whether null values are allowed in the designated parameter. 46 | * 47 | * @param param the first parameter is 1, the second is 2, ... 48 | * @return the nullability status of the given parameter; one of 49 | * ParameterMetaData.parameterNoNulls, 50 | * ParameterMetaData.parameterNullable, or 51 | * ParameterMetaData.parameterNullableUnknown 52 | * @throws SQLException if a database access error occurs 53 | * @since 1.4 54 | */ 55 | @Override 56 | public int isNullable(int param) throws SQLException 57 | { 58 | return parameterNullable; 59 | } 60 | 61 | /** 62 | * Retrieves whether values for the designated parameter can be signed numbers. 63 | * 64 | * @param param the first parameter is 1, the second is 2, ... 65 | * @return true if so; false otherwise 66 | * @throws SQLException if a database access error occurs 67 | * @since 1.4 68 | */ 69 | @Override 70 | public boolean isSigned(int param) throws SQLException 71 | { 72 | return false; 73 | } 74 | 75 | /** 76 | * Retrieves the designated parameter's specified column size. 77 | * 78 | *

The returned value represents the maximum column size for the given parameter. 79 | * For numeric data, this is the maximum precision. For character data, this is the length in characters. 80 | * For datetime datatypes, this is the length in characters of the String representation (assuming the 81 | * maximum allowed precision of the fractional seconds component). For binary data, this is the length in bytes. For the ROWID datatype, 82 | * this is the length in bytes. 0 is returned for data types where the 83 | * column size is not applicable. 84 | * 85 | * @param param the first parameter is 1, the second is 2, ... 86 | * @return precision 87 | * @throws SQLException if a database access error occurs 88 | * @since 1.4 89 | */ 90 | @Override 91 | public int getPrecision(int param) throws SQLException 92 | { 93 | return 0; 94 | } 95 | 96 | /** 97 | * Retrieves the designated parameter's number of digits to right of the decimal point. 98 | * 0 is returned for data types where the scale is not applicable. 99 | * 100 | * @param param the first parameter is 1, the second is 2, ... 101 | * @return scale 102 | * @throws SQLException if a database access error occurs 103 | * @since 1.4 104 | */ 105 | @Override 106 | public int getScale(int param) throws SQLException 107 | { 108 | return 0; 109 | } 110 | 111 | /** 112 | * Retrieves the designated parameter's SQL type. 113 | * 114 | * @param param the first parameter is 1, the second is 2, ... 115 | * @return SQL type from java.sql.Types 116 | * @throws SQLException if a database access error occurs 117 | * @see java.sql.Types 118 | * @since 1.4 119 | */ 120 | @Override 121 | public int getParameterType(int param) throws SQLException 122 | { 123 | return 0; 124 | } 125 | 126 | /** 127 | * Retrieves the designated parameter's database-specific type name. 128 | * 129 | * @param param the first parameter is 1, the second is 2, ... 130 | * @return type the name used by the database. If the parameter type is 131 | * a user-defined type, then a fully-qualified type name is returned. 132 | * @throws SQLException if a database access error occurs 133 | * @since 1.4 134 | */ 135 | @Override 136 | public String getParameterTypeName(int param) throws SQLException 137 | { 138 | return null; 139 | } 140 | 141 | /** 142 | * Retrieves the fully-qualified name of the Java class whose instances 143 | * should be passed to the method PreparedStatement.setObject. 144 | * 145 | * @param param the first parameter is 1, the second is 2, ... 146 | * @return the fully-qualified name of the class in the Java programming 147 | * language that would be used by the method 148 | * PreparedStatement.setObject to set the value 149 | * in the specified parameter. This is the class name used 150 | * for custom mapping. 151 | * @throws SQLException if a database access error occurs 152 | * @since 1.4 153 | */ 154 | @Override 155 | public String getParameterClassName(int param) throws SQLException 156 | { 157 | return null; 158 | } 159 | 160 | /** 161 | * Retrieves the designated parameter's mode. 162 | * 163 | * @param param the first parameter is 1, the second is 2, ... 164 | * @return mode of the parameter; one of 165 | * ParameterMetaData.parameterModeIn, 166 | * ParameterMetaData.parameterModeOut, or 167 | * ParameterMetaData.parameterModeInOut 168 | * ParameterMetaData.parameterModeUnknown. 169 | * @throws SQLException if a database access error occurs 170 | * @since 1.4 171 | */ 172 | @Override 173 | public int getParameterMode(int param) throws SQLException 174 | { 175 | return parameterModeIn; 176 | } 177 | 178 | /** 179 | * Returns an object that implements the given interface to allow access to 180 | * non-standard methods, or standard methods not exposed by the proxy. 181 | * 182 | * If the receiver implements the interface then the result is the receiver 183 | * or a proxy for the receiver. If the receiver is a wrapper 184 | * and the wrapped object implements the interface then the result is the 185 | * wrapped object or a proxy for the wrapped object. Otherwise return the 186 | * the result of calling unwrap recursively on the wrapped object 187 | * or a proxy for that result. If the receiver is not a 188 | * wrapper and does not implement the interface, then an SQLException is thrown. 189 | * 190 | * @param iface A Class defining an interface that the result must implement. 191 | * @return an object that implements the interface. May be a proxy for the actual implementing object. 192 | * @throws SQLException If no object found that implements the interface 193 | * @since 1.6 194 | */ 195 | @Override 196 | public T unwrap(Class iface) throws SQLException 197 | { 198 | if (iface.isAssignableFrom(getClass())) 199 | { 200 | return iface.cast(this); 201 | } 202 | throw new SQLException("Cannot unwrap to " + iface.getName()); 203 | } 204 | 205 | /** 206 | * Returns true if this either implements the interface argument or is directly or indirectly a wrapper 207 | * for an object that does. Returns false otherwise. If this implements the interface then return true, 208 | * else if this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped 209 | * object. If this does not implement the interface and is not a wrapper, return false. 210 | * This method should be implemented as a low-cost operation compared to unwrap so that 211 | * callers can use this method to avoid expensive unwrap calls that may fail. If this method 212 | * returns true then calling unwrap with the same argument should succeed. 213 | * 214 | * @param iface a Class defining an interface. 215 | * @return true if this implements the interface or directly or indirectly wraps an object that does. 216 | * @throws SQLException if an error occurs while determining whether this is a wrapper 217 | * for an object with the given interface. 218 | * @since 1.6 219 | */ 220 | @Override 221 | public boolean isWrapperFor(Class iface) throws SQLException 222 | { 223 | return iface.isAssignableFrom(getClass()); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/CBPreparedResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.util.Map; 18 | 19 | /** 20 | * Created by davec on 2015-07-28. 21 | */ 22 | public class CBPreparedResult 23 | { 24 | private static final Logger logger = LoggerFactory.getLogger(CBPreparedResult.class); 25 | 26 | String name; 27 | String encodedPlan; 28 | Map operator; 29 | Map signature; 30 | 31 | public CBPreparedResult( Map preparedResult ) 32 | { 33 | name = (String)preparedResult.get("name"); 34 | encodedPlan = (String)preparedResult.get("encoded_plan"); 35 | 36 | operator = (Map)preparedResult.get("operator"); 37 | signature = (Map)preparedResult.get("signature"); 38 | 39 | logger.trace("Prepared statement {}\nencoded_plan {}\noperator {}\nsignature {}", name, encodedPlan, operator, signature); 40 | } 41 | public String getName() 42 | { 43 | return name; 44 | } 45 | public String getEncodedPlan() { return encodedPlan; } 46 | public Map getSignature() 47 | { 48 | return signature; 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/CBResultSetMetaData.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * // Copyright (c) 2015 Couchbase, Inc. 4 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * // except in compliance with the License. You may obtain a copy of the License at 6 | * // http://www.apache.org/licenses/LICENSE-2.0 7 | * // Unless required by applicable law or agreed to in writing, software distributed under the 8 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 9 | * // either express or implied. See the License for the specific language governing permissions 10 | * // and limitations under the License. 11 | */ 12 | 13 | package com.couchbase.jdbc; 14 | 15 | import java.sql.ResultSetMetaData; 16 | import java.sql.SQLException; 17 | 18 | /** 19 | * Created by davec on 2015-02-20. 20 | */ 21 | public class CBResultSetMetaData implements ResultSetMetaData 22 | { 23 | final CBResultSet resultSet; 24 | 25 | CBResultSetMetaData(CBResultSet resultSet) 26 | { 27 | this.resultSet = resultSet; 28 | } 29 | /** 30 | * Returns the number of columns in this ResultSet object. 31 | * 32 | * @return the number of columns 33 | * @throws java.sql.SQLException if a database access error occurs 34 | */ 35 | @Override 36 | public int getColumnCount() throws SQLException 37 | { 38 | return resultSet.fields.size(); 39 | } 40 | 41 | /** 42 | * Indicates whether the designated column is automatically numbered. 43 | * 44 | * @param column the first column is 1, the second is 2, ... 45 | * @return true if so; false otherwise 46 | * @throws java.sql.SQLException if a database access error occurs 47 | */ 48 | @Override 49 | public boolean isAutoIncrement(int column) throws SQLException 50 | { 51 | return false; 52 | } 53 | 54 | /** 55 | * Indicates whether a column's case matters. 56 | * 57 | * @param column the first column is 1, the second is 2, ... 58 | * @return true if so; false otherwise 59 | * @throws java.sql.SQLException if a database access error occurs 60 | */ 61 | @Override 62 | public boolean isCaseSensitive(int column) throws SQLException 63 | { 64 | return false; 65 | } 66 | 67 | /** 68 | * Indicates whether the designated column can be used in a where clause. 69 | * 70 | * @param column the first column is 1, the second is 2, ... 71 | * @return true if so; false otherwise 72 | * @throws java.sql.SQLException if a database access error occurs 73 | */ 74 | @Override 75 | public boolean isSearchable(int column) throws SQLException 76 | { 77 | return false; 78 | } 79 | 80 | /** 81 | * Indicates whether the designated column is a cash value. 82 | * 83 | * @param column the first column is 1, the second is 2, ... 84 | * @return true if so; false otherwise 85 | * @throws java.sql.SQLException if a database access error occurs 86 | */ 87 | @Override 88 | public boolean isCurrency(int column) throws SQLException 89 | { 90 | return false; 91 | } 92 | 93 | /** 94 | * Indicates the nullability of values in the designated column. 95 | * 96 | * @param column the first column is 1, the second is 2, ... 97 | * @return the nullability status of the given column; one of columnNoNulls, 98 | * columnNullable or columnNullableUnknown 99 | * @throws java.sql.SQLException if a database access error occurs 100 | */ 101 | @Override 102 | public int isNullable(int column) throws SQLException 103 | { 104 | return columnNullable; 105 | } 106 | 107 | /** 108 | * Indicates whether values in the designated column are signed numbers. 109 | * 110 | * @param column the first column is 1, the second is 2, ... 111 | * @return true if so; false otherwise 112 | * @throws java.sql.SQLException if a database access error occurs 113 | */ 114 | @Override 115 | public boolean isSigned(int column) throws SQLException 116 | { 117 | return false; 118 | } 119 | 120 | /** 121 | * Indicates the designated column's normal maximum width in characters. 122 | * 123 | * @param column the first column is 1, the second is 2, ... 124 | * @return the normal maximum number of characters allowed as the width 125 | * of the designated column 126 | * @throws java.sql.SQLException if a database access error occurs 127 | */ 128 | @Override 129 | public int getColumnDisplaySize(int column) throws SQLException 130 | { 131 | return 0; 132 | } 133 | 134 | /** 135 | * Gets the designated column's suggested title for use in printouts and 136 | * displays. The suggested title is usually specified by the SQL AS 137 | * clause. If a SQL AS is not specified, the value returned from 138 | * getColumnLabel will be the same as the value returned by the 139 | * getColumnName method. 140 | * 141 | * @param column the first column is 1, the second is 2, ... 142 | * @return the suggested column title 143 | * @throws java.sql.SQLException if a database access error occurs 144 | */ 145 | @Override 146 | public String getColumnLabel(int column) throws SQLException 147 | { 148 | 149 | return resultSet.getField(column).getName(); 150 | } 151 | 152 | /** 153 | * Get the designated column's name. 154 | * 155 | * @param column the first column is 1, the second is 2, ... 156 | * @return column name 157 | * @throws java.sql.SQLException if a database access error occurs 158 | */ 159 | @Override 160 | public String getColumnName(int column) throws SQLException 161 | { 162 | return resultSet.getField(column).getName(); 163 | } 164 | 165 | /** 166 | * Get the designated column's table's schema. 167 | * 168 | * @param column the first column is 1, the second is 2, ... 169 | * @return schema name or "" if not applicable 170 | * @throws java.sql.SQLException if a database access error occurs 171 | */ 172 | @Override 173 | public String getSchemaName(int column) throws SQLException 174 | { 175 | return null; 176 | } 177 | 178 | /** 179 | * Get the designated column's specified column size. 180 | * For numeric data, this is the maximum precision. For character data, this is the length in characters. 181 | * For datetime datatypes, this is the length in characters of the String representation (assuming the 182 | * maximum allowed precision of the fractional seconds component). For binary data, this is the length in bytes. For the ROWID datatype, 183 | * this is the length in bytes. 0 is returned for data types where the 184 | * column size is not applicable. 185 | * 186 | * @param column the first column is 1, the second is 2, ... 187 | * @return precision 188 | * @throws java.sql.SQLException if a database access error occurs 189 | */ 190 | @Override 191 | public int getPrecision(int column) throws SQLException 192 | { 193 | return 0; 194 | } 195 | 196 | /** 197 | * Gets the designated column's number of digits to right of the decimal point. 198 | * 0 is returned for data types where the scale is not applicable. 199 | * 200 | * @param column the first column is 1, the second is 2, ... 201 | * @return scale 202 | * @throws java.sql.SQLException if a database access error occurs 203 | */ 204 | @Override 205 | public int getScale(int column) throws SQLException 206 | { 207 | return 0; 208 | } 209 | 210 | /** 211 | * Gets the designated column's table name. 212 | * 213 | * @param column the first column is 1, the second is 2, ... 214 | * @return table name or "" if not applicable 215 | * @throws java.sql.SQLException if a database access error occurs 216 | */ 217 | @Override 218 | public String getTableName(int column) throws SQLException 219 | { 220 | return null; 221 | } 222 | 223 | /** 224 | * Gets the designated column's table's catalog name. 225 | * 226 | * @param column the first column is 1, the second is 2, ... 227 | * @return the name of the catalog for the table in which the given column 228 | * appears or "" if not applicable 229 | * @throws java.sql.SQLException if a database access error occurs 230 | */ 231 | @Override 232 | public String getCatalogName(int column) throws SQLException 233 | { 234 | return null; 235 | } 236 | 237 | /** 238 | * Retrieves the designated column's SQL type. 239 | * 240 | * @param column the first column is 1, the second is 2, ... 241 | * @return SQL type from java.sql.Types 242 | * @throws java.sql.SQLException if a database access error occurs 243 | * @see java.sql.Types 244 | */ 245 | @Override 246 | public int getColumnType(int column) throws SQLException 247 | { 248 | return resultSet.getField(column).getSqlType(); 249 | } 250 | 251 | /** 252 | * Retrieves the designated column's database-specific type name. 253 | * 254 | * @param column the first column is 1, the second is 2, ... 255 | * @return type name used by the database. If the column type is 256 | * a user-defined type, then a fully-qualified type name is returned. 257 | * @throws java.sql.SQLException if a database access error occurs 258 | */ 259 | @Override 260 | public String getColumnTypeName(int column) throws SQLException 261 | { 262 | return resultSet.getField(column).getType(); 263 | } 264 | 265 | /** 266 | * Indicates whether the designated column is definitely not writable. 267 | * 268 | * @param column the first column is 1, the second is 2, ... 269 | * @return true if so; false otherwise 270 | * @throws java.sql.SQLException if a database access error occurs 271 | */ 272 | @Override 273 | public boolean isReadOnly(int column) throws SQLException 274 | { 275 | return false; 276 | } 277 | 278 | /** 279 | * Indicates whether it is possible for a write on the designated column to succeed. 280 | * 281 | * @param column the first column is 1, the second is 2, ... 282 | * @return true if so; false otherwise 283 | * @throws java.sql.SQLException if a database access error occurs 284 | */ 285 | @Override 286 | public boolean isWritable(int column) throws SQLException 287 | { 288 | return false; 289 | } 290 | 291 | /** 292 | * Indicates whether a write on the designated column will definitely succeed. 293 | * 294 | * @param column the first column is 1, the second is 2, ... 295 | * @return true if so; false otherwise 296 | * @throws java.sql.SQLException if a database access error occurs 297 | */ 298 | @Override 299 | public boolean isDefinitelyWritable(int column) throws SQLException 300 | { 301 | return false; 302 | } 303 | 304 | /** 305 | *

Returns the fully-qualified name of the Java class whose instances 306 | * are manufactured if the method ResultSet.getObject 307 | * is called to retrieve a value 308 | * from the column. ResultSet.getObject may return a subclass of the 309 | * class returned by this method. 310 | * 311 | * @param column the first column is 1, the second is 2, ... 312 | * @return the fully-qualified name of the class in the Java programming 313 | * language that would be used by the method 314 | * ResultSet.getObject to retrieve the value in the specified 315 | * column. This is the class name used for custom mapping. 316 | * @throws java.sql.SQLException if a database access error occurs 317 | * @since 1.2 318 | */ 319 | @Override 320 | public String getColumnClassName(int column) throws SQLException 321 | { 322 | return null; 323 | } 324 | 325 | /** 326 | * Returns an object that implements the given interface to allow access to 327 | * non-standard methods, or standard methods not exposed by the proxy. 328 | * 329 | * If the receiver implements the interface then the result is the receiver 330 | * or a proxy for the receiver. If the receiver is a wrapper 331 | * and the wrapped object implements the interface then the result is the 332 | * wrapped object or a proxy for the wrapped object. Otherwise return the 333 | * the result of calling unwrap recursively on the wrapped object 334 | * or a proxy for that result. If the receiver is not a 335 | * wrapper and does not implement the interface, then an SQLException is thrown. 336 | * 337 | * @param iface A Class defining an interface that the result must implement. 338 | * @return an object that implements the interface. May be a proxy for the actual implementing object. 339 | * @throws java.sql.SQLException If no object found that implements the interface 340 | * @since 1.6 341 | */ 342 | @Override 343 | public T unwrap(Class iface) throws SQLException 344 | { 345 | return null; 346 | } 347 | 348 | /** 349 | * Returns true if this either implements the interface argument or is directly or indirectly a wrapper 350 | * for an object that does. Returns false otherwise. If this implements the interface then return true, 351 | * else if this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped 352 | * object. If this does not implement the interface and is not a wrapper, return false. 353 | * This method should be implemented as a low-cost operation compared to unwrap so that 354 | * callers can use this method to avoid expensive unwrap calls that may fail. If this method 355 | * returns true then calling unwrap with the same argument should succeed. 356 | * 357 | * @param iface a Class defining an interface. 358 | * @return true if this implements the interface or directly or indirectly wraps an object that does. 359 | * @throws java.sql.SQLException if an error occurs while determining whether this is a wrapper 360 | * for an object with the given interface. 361 | * @since 1.6 362 | */ 363 | @Override 364 | public boolean isWrapperFor(Class iface) throws SQLException 365 | { 366 | return false; 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/ConnectionParameters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | /** 15 | * Created by davec on 2015-03-04. 16 | */ 17 | public class ConnectionParameters 18 | { 19 | public final static String CONNECTION_TIMEOUT="connectionTimeout"; 20 | public final static String USER="user"; 21 | public final static String PASSWORD="password"; 22 | public final static String SCAN_CONSISTENCY="ScanConsistency"; 23 | public final static String ENABLE_SSL="EnableSSL"; 24 | public final static String REDUNDANCY="Redundancy"; 25 | public final static String SSL_CERTIFICATE="SSLCertificate"; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/connect/Cluster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc.connect; 13 | 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.sql.SQLException; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.Map; 21 | import java.util.concurrent.atomic.AtomicInteger; 22 | 23 | /** 24 | * Created by davec on 2015-05-22. 25 | */ 26 | public class Cluster 27 | { 28 | private static final Logger logger = LoggerFactory.getLogger(Cluster.class); 29 | 30 | Integer instanceIndex = new Integer(0); 31 | AtomicInteger numInstances = new AtomicInteger(0); 32 | List endpoints = new ArrayList(); 33 | 34 | /* 35 | [{"cluster":"default","name":"10.168.209.119","queryEndpoint":"http://10.168.209.119:8093/query/service","adminEndpoint":"http://10.168.209.119:8093/admin","options":null}, 36 | {"cluster":"default","name":"10.169.93.182","queryEndpoint":"http://10.169.93.182:8093/query/service","adminEndpoint":"http://10.169.93.182:8093/admin","options":null}, 37 | {"cluster":"default","name":"10.181.71.84","queryEndpoint":"http://10.181.71.84:8093/query/service","adminEndpoint":"http://10.181.71.84:8093/admin","options":null}, 38 | {"cluster":"default","name":"10.30.210.238","queryEndpoint":"http://10.30.210.238:8093/query/service","adminEndpoint":"http://10.30.210.238:8093/admin","options":null}] 39 | */ 40 | 41 | public Cluster( List jsonArray, boolean ssl ) 42 | { 43 | int num = jsonArray.size(); 44 | for(int i=0; i < num ;i++) 45 | { 46 | try 47 | { 48 | //noinspection unchecked 49 | endpoints.add(new Instance(jsonArray.get(i))); 50 | numInstances.incrementAndGet(); 51 | } 52 | catch( SQLException ex) 53 | { 54 | logger.debug("Invalid endpoint ", ex.getCause().getMessage()); 55 | } 56 | } 57 | } 58 | public Instance getNextEndpoint() 59 | { 60 | 61 | /* 62 | Map jsonObject = new HashMap(); 63 | jsonObject.put("queryEndpoint","http://54.237.32.30:8093/query/service" ); 64 | jsonObject.put("adminEndpoint","http://54.237.32.30:8093/query/admin" ); 65 | 66 | jsonObject.put("querySecure", "https://54.237.32.30:18093/query/service"); 67 | jsonObject.put("adminSecure", "https://54.237.32.30:18093/query/admin"); 68 | 69 | try 70 | { 71 | Instance instance = new Instance(jsonObject); 72 | return instance; 73 | } 74 | catch (SQLException ex ) 75 | { 76 | return null; 77 | } 78 | */ 79 | 80 | 81 | int i; 82 | synchronized (instanceIndex) 83 | { 84 | i = instanceIndex++; 85 | 86 | if (i >= numInstances.get()) 87 | { 88 | instanceIndex = 0; 89 | i=0; 90 | } 91 | } 92 | 93 | logger.trace( "Endpoint {} of {}",i,numInstances); 94 | 95 | return endpoints.get(i); 96 | 97 | } 98 | public void addEndPoint(Map endpoint) 99 | { 100 | try 101 | { 102 | //noinspection unchecked 103 | endpoints.add(new Instance(endpoint)); 104 | numInstances.incrementAndGet(); 105 | } 106 | catch( SQLException ex) 107 | { 108 | logger.debug("Invalid endpoint ", ex.getCause().getMessage()); 109 | } 110 | } 111 | public void invalidateEndpoint(Instance instance) 112 | { 113 | logger.trace("Invalidating instance {}", instance); 114 | synchronized (instanceIndex) 115 | { 116 | endpoints.remove(instance); 117 | numInstances.decrementAndGet(); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/connect/Instance.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc.connect; 13 | 14 | import java.net.URI; 15 | import java.net.URISyntaxException; 16 | import java.sql.SQLException; 17 | import java.util.Map; 18 | 19 | /** 20 | * Created by davec on 2015-05-22. 21 | */ 22 | public class Instance 23 | { 24 | String cluster, 25 | name, 26 | queryEndPoint, 27 | adminEndPoint, 28 | querySecure, 29 | adminSecure; 30 | 31 | String options; 32 | 33 | public Instance( Map jsonObject ) throws SQLException 34 | { 35 | this.cluster = jsonObject.get("cluster"); 36 | this.name = jsonObject.get("name"); 37 | 38 | this.adminEndPoint = jsonObject.get("adminEndpoint"); 39 | this.adminSecure = jsonObject.get("adminSecure"); 40 | 41 | isValidURI( adminEndPoint ); 42 | 43 | this.queryEndPoint = jsonObject.get("queryEndpoint"); 44 | this.querySecure = jsonObject.get("querySecure"); 45 | 46 | isValidURI(queryEndPoint); 47 | } 48 | public String getCluster() 49 | { 50 | return cluster; 51 | } 52 | 53 | public void setCluster(String cluster) 54 | { 55 | this.cluster = cluster; 56 | } 57 | 58 | public String getName() 59 | { 60 | return name; 61 | } 62 | 63 | public void setName(String name) 64 | { 65 | this.name = name; 66 | } 67 | 68 | public String getQueryEndPoint() 69 | { 70 | return queryEndPoint; 71 | } 72 | public String getSecureQueryEndPoint() { return querySecure; } 73 | 74 | public void setQueryEndPoint(String queryEndPoint) 75 | { 76 | this.queryEndPoint = queryEndPoint; 77 | } 78 | 79 | public String getAdminEndPoint() 80 | { 81 | return adminEndPoint; 82 | } 83 | 84 | public void setAdminEndPoint(String adminEndPoint) 85 | { 86 | this.adminEndPoint = adminEndPoint; 87 | } 88 | 89 | public String getOptions() 90 | { 91 | return options; 92 | } 93 | 94 | public void setOptions(String options) 95 | { 96 | this.options = options; 97 | } 98 | 99 | private boolean isValidURI(String uriStr) throws SQLException 100 | { 101 | try 102 | { 103 | URI uri = new URI(uriStr); 104 | return true; 105 | } 106 | catch (URISyntaxException e) 107 | { 108 | throw new SQLException("Endpoint " + uriStr + " is invalid", e); 109 | } 110 | } 111 | public String getEndpointURL(boolean ssl) 112 | { 113 | return ssl?querySecure:queryEndPoint; 114 | } 115 | 116 | public String toString() 117 | { 118 | return name; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/connect/Protocol.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc.connect; 13 | 14 | import com.couchbase.jdbc.CBResultSet; 15 | import com.couchbase.jdbc.CBStatement; 16 | import com.couchbase.jdbc.core.CouchResponse; 17 | 18 | import java.sql.SQLException; 19 | import java.sql.SQLWarning; 20 | import java.util.Map; 21 | 22 | /** 23 | * Created by davec on 2015-02-26. 24 | */ 25 | public interface Protocol 26 | { 27 | void connect() throws Exception; 28 | void close() throws Exception; 29 | 30 | CBResultSet query(CBStatement statement, String sql) throws SQLException; 31 | int executeUpdate(CBStatement statement, String sql) throws SQLException; 32 | boolean execute(CBStatement statement, String sql) throws SQLException; 33 | void addBatch(String sql) throws SQLException; 34 | void clearBatch() throws SQLException; 35 | int [] executeBatch() throws SQLException; 36 | 37 | CouchResponse prepareStatement(String sql, String[] returning) throws SQLException; 38 | CouchResponse doQuery(String query, Map queryParameters) throws SQLException; 39 | 40 | long getUpdateCount(); 41 | CBResultSet getResultSet(); 42 | 43 | String getURL(); 44 | String getUserName(); 45 | String getPassword(); 46 | 47 | void setConnectionTimeout(String timeout); 48 | void setConnectionTimeout(int timeout); 49 | void setReadOnly(boolean readOnly); 50 | boolean getReadOnly(); 51 | 52 | void setQueryTimeout(int seconds) throws SQLException; 53 | int getQueryTimeout() throws SQLException; 54 | SQLWarning getWarnings() throws SQLException; 55 | void clearWarning() throws SQLException; 56 | void setSchema(String schema) throws SQLException; 57 | String getSchema() throws SQLException; 58 | boolean isValid(int timeout) throws SQLException; 59 | void pollCluster() throws SQLException; 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/core/CouchBaseSQLException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc.core; 13 | 14 | import java.sql.SQLException; 15 | 16 | /** 17 | * Created by davec on 2015-07-08. 18 | */ 19 | public class CouchBaseSQLException extends SQLException 20 | { 21 | public CouchBaseSQLException() 22 | { 23 | super(); 24 | } 25 | public CouchBaseSQLException(String msg) 26 | { 27 | super(msg); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/core/CouchError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc.core; 13 | 14 | public class CouchError 15 | { 16 | Integer code; 17 | String msg; 18 | public Integer getCode() {return code;} 19 | public String getMsg() { return msg;} 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/core/CouchMetrics.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc.core; 13 | 14 | /** 15 | * Created by davec on 2015-06-23. 16 | */ 17 | public class CouchMetrics 18 | { 19 | String executionTime; 20 | String elapsedTime; 21 | 22 | long resultCount; 23 | long errorCount; 24 | long resultSize; 25 | long mutationCount; 26 | long warningCount; 27 | 28 | public String getExecutionTime() 29 | { 30 | return executionTime; 31 | } 32 | 33 | public String getElapsedTime() 34 | { 35 | return elapsedTime; 36 | } 37 | 38 | public long getResultCount() 39 | { 40 | return resultCount; 41 | } 42 | 43 | public long getErrorCount() 44 | { 45 | return errorCount; 46 | } 47 | 48 | public long getResultSize() 49 | { 50 | return resultSize; 51 | } 52 | 53 | public long getMutationCount() 54 | { 55 | return mutationCount; 56 | } 57 | 58 | public long getWarningCount() 59 | { 60 | return warningCount; 61 | } 62 | public void setResultCount(int count) 63 | { 64 | resultCount = count; 65 | } 66 | public void setResultSize(int size) 67 | { 68 | resultSize = size; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/core/CouchResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc.core; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.Set; 18 | import java.util.concurrent.atomic.AtomicBoolean; 19 | 20 | /** 21 | * Created by davec on 2015-06-23. 22 | */ 23 | public class CouchResponse 24 | { 25 | AtomicBoolean fieldsInitialized = new AtomicBoolean(false); 26 | 27 | Map signature=null; 28 | ArrayList fields; 29 | 30 | List errors; 31 | List warnings; 32 | CouchMetrics metrics; 33 | String requestId; 34 | String status; 35 | List > results; 36 | 37 | 38 | public CouchMetrics getMetrics() 39 | { 40 | return metrics; 41 | } 42 | 43 | 44 | // we don't know which will get called first so set the fields in getFields() 45 | 46 | public void setSignature(Map signature) 47 | { 48 | this.signature = signature; 49 | } 50 | 51 | public ArrayListgetFields() 52 | { 53 | // check to make sure we haven't set these yet 54 | if (!fieldsInitialized.getAndSet(true)) 55 | { 56 | if (signature != null) 57 | { 58 | fields = new ArrayList(signature.size()); 59 | } 60 | 61 | if (signature.containsKey("*") ) 62 | { 63 | if (metrics.getResultSize() > 0) { 64 | Map firstRow = results.get(0); 65 | Set keySet = firstRow.keySet(); 66 | 67 | for (String key : keySet) 68 | { 69 | Object object = firstRow.get(key); 70 | if (object == null ) 71 | { 72 | fields.add( new Field(key, "null")); 73 | } 74 | else 75 | { 76 | Object type = firstRow.get(key); 77 | String jsonType="json"; 78 | 79 | if (type instanceof Number) 80 | jsonType = "number"; 81 | else if ( type instanceof Boolean) 82 | jsonType = "boolean"; 83 | else if ( type instanceof String) 84 | jsonType = "string"; 85 | else if (type instanceof Map ) 86 | jsonType = "json"; 87 | else if ( type instanceof List ) 88 | jsonType = "json"; 89 | 90 | fields.add(new Field(key, jsonType)); 91 | } 92 | } 93 | } 94 | } 95 | else 96 | { 97 | for (String key : signature.keySet()) 98 | { 99 | fields.add(new Field(key, signature.get(key))); 100 | } 101 | } 102 | } 103 | return fields; 104 | } 105 | public List > getResults() 106 | { 107 | return results; 108 | } 109 | public void setResults(List results) 110 | { 111 | //noinspection unchecked 112 | this.results = results; 113 | } 114 | public void setMetrics(CouchMetrics metrics) 115 | { 116 | this.metrics = metrics; 117 | } 118 | public List getWarnings() {return warnings;} 119 | public List getErrors(){return errors;} 120 | 121 | public Map getFirstResult() 122 | { 123 | return (Map)results.get(0); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/core/Field.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc.core; 13 | 14 | import com.couchbase.jdbc.util.JSONTypes; 15 | 16 | public class Field 17 | { 18 | String name; 19 | String type; 20 | 21 | public Field(String name, String type ) 22 | { 23 | this.name = name; 24 | this.type = type; 25 | } 26 | 27 | public String getType() 28 | { 29 | return type; 30 | } 31 | 32 | public void setType(String type) 33 | { 34 | if (type.equalsIgnoreCase("true") || type.equalsIgnoreCase("false") ) 35 | { 36 | this.type="boolean"; 37 | } 38 | else if (type.equalsIgnoreCase("null")) 39 | { 40 | this.type="unknown"; 41 | } 42 | else 43 | { 44 | this.type = type; 45 | } 46 | } 47 | 48 | public String getName() 49 | { 50 | return name; 51 | } 52 | 53 | public void setName(String name) 54 | { 55 | this.name = name; 56 | } 57 | 58 | 59 | public int getSqlType() 60 | { 61 | return JSONTypes.jdbcTypes.get(type); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/core/Parser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc.core; 13 | /*------------------------------------------------------------------------- 14 | * 15 | * Copyright (c) 2006, PostgreSQL Global Development Group 16 | * 17 | * 18 | *------------------------------------------------------------------------- 19 | */ 20 | 21 | 22 | /** 23 | * Basic query parser infrastructure. 24 | * 25 | * @author Michael Paesold (mpaesold@gmx.at) 26 | */ 27 | public class Parser { 28 | 29 | /* 30 | * Find the end of the single-quoted string starting at the given offset. 31 | * 32 | * Note: for 'single '' quote in string', this method currently 33 | * returns the offset of first ' character after the initial 34 | * one. The caller must call the method a second time for the second 35 | * part of the quoted string. 36 | * @param query 37 | * @param offset 38 | * @return 39 | */ 40 | public static int parseSingleQuotes(final char[] query, int offset) 41 | { 42 | 43 | // treat backslashes as escape characters 44 | while (++offset < query.length) 45 | { 46 | switch (query[offset]) 47 | { 48 | case '\\': 49 | ++offset; 50 | break; 51 | case '\'': 52 | return offset; 53 | default: 54 | break; 55 | } 56 | } 57 | 58 | return query.length; 59 | } 60 | 61 | /* 62 | * Find the end of the double-quoted string starting at the given offset. 63 | * 64 | * Note: for "double "" quote in string", 65 | * this method currently returns the offset of first " 66 | * character after the initial one. The caller must call the method a 67 | * second time for the second part of the quoted string. 68 | * @param query 69 | * @param offset 70 | * @return 71 | */ 72 | public static int parseDoubleQuotes(final char[] query, int offset) 73 | { 74 | while (++offset < query.length && query[offset] != '"') ; 75 | return offset; 76 | } 77 | 78 | /* 79 | * Test if the dollar character ($) at the given offset starts 80 | * a dollar-quoted string and return the offset of the ending dollar 81 | * character. 82 | * @param query 83 | * @param offset 84 | * @return 85 | */ 86 | public static int parseDollarQuotes(final char[] query, int offset) { 87 | if (offset + 1 < query.length 88 | && (offset == 0 || !isIdentifierContChar(query[offset-1]))) 89 | { 90 | int endIdx = -1; 91 | if (query[offset + 1] == '$') 92 | endIdx = offset + 1; 93 | else if (isDollarQuoteStartChar(query[offset + 1])) 94 | { 95 | for (int d = offset + 2; d < query.length; ++d) 96 | { 97 | if (query[d] == '$') 98 | { 99 | endIdx = d; 100 | break; 101 | } 102 | else if (!isDollarQuoteContChar(query[d])) 103 | break; 104 | } 105 | } 106 | if (endIdx > 0) 107 | { 108 | // found; note: tag includes start and end $ character 109 | int tagIdx = offset, tagLen = endIdx - offset + 1; 110 | offset = endIdx; // loop continues at endIdx + 1 111 | for (++offset; offset < query.length; ++offset) 112 | { 113 | if (query[offset] == '$' && 114 | subArraysEqual(query, tagIdx, offset, tagLen)) 115 | { 116 | offset += tagLen - 1; 117 | break; 118 | } 119 | } 120 | } 121 | } 122 | return offset; 123 | } 124 | 125 | /* 126 | * Test if the - character at offset starts a 127 | * -- style line comment, and return the position of the first 128 | * \r or \n character. 129 | * @param query 130 | * @param offset 131 | * @return 132 | */ 133 | public static int parseLineComment(final char[] query, int offset) { 134 | if (offset + 1 < query.length && query[offset + 1] == '-') 135 | { 136 | while (offset + 1 < query.length) 137 | { 138 | offset++; 139 | if (query[offset] == '\r' || query[offset] == '\n') 140 | break; 141 | } 142 | } 143 | return offset; 144 | } 145 | 146 | /* 147 | * Test if the / character at offset starts a block 148 | * comment, and return the position of the last / character. 149 | * @param query 150 | * @param offset 151 | * @return 152 | */ 153 | public static int parseBlockComment(final char[] query, int offset) { 154 | if (offset + 1 < query.length && query[offset + 1] == '*') 155 | { 156 | // /* /* */ */ nest, according to SQL spec 157 | int level = 1; 158 | for (offset += 2; offset < query.length; ++offset) 159 | { 160 | switch (query[offset-1]) 161 | { 162 | case '*': 163 | if (query[offset] == '/') 164 | { 165 | --level; 166 | ++offset; // don't parse / in */* twice 167 | } 168 | break; 169 | case '/': 170 | if (query[offset] == '*') 171 | { 172 | ++level; 173 | ++offset; // don't parse * in /*/ twice 174 | } 175 | break; 176 | default: 177 | break; 178 | } 179 | 180 | if (level == 0) 181 | { 182 | --offset; // reset position to last '/' char 183 | break; 184 | } 185 | } 186 | } 187 | return offset; 188 | } 189 | 190 | /** 191 | * unmark '??' in query back to '?' 192 | * @param query query to remove question marks from 193 | * @return modified query 194 | */ 195 | public static String unmarkDoubleQuestion( String query ) 196 | { 197 | if (query == null) return query; 198 | 199 | char[] aChars = query.toCharArray(); 200 | StringBuilder buf = new StringBuilder(aChars.length); 201 | for(int i=0, j; i< aChars.length; i++) 202 | { 203 | switch (aChars[i]) 204 | { 205 | case '\'': // single-quotes 206 | j = Parser.parseSingleQuotes(aChars, i); 207 | buf.append(aChars, i, j-i+1); 208 | i = j; 209 | break; 210 | 211 | case '"': // double-quotes 212 | j = Parser.parseDoubleQuotes(aChars, i); 213 | buf.append(aChars, i, j-i+1); 214 | i = j; 215 | break; 216 | 217 | case '-': // possibly -- style comment 218 | j = Parser.parseLineComment(aChars, i); 219 | buf.append(aChars, i, j-i+1); 220 | i = j; 221 | break; 222 | 223 | case '/': // possibly /* */ style comment 224 | j = Parser.parseBlockComment(aChars, i); 225 | buf.append(aChars, i, j-i+1); 226 | i = j; 227 | break; 228 | 229 | case '$': // possibly dollar quote start 230 | j = Parser.parseDollarQuotes(aChars, i); 231 | buf.append(aChars, i, j-i+1); 232 | i = j; 233 | break; 234 | 235 | case '?': // unescape '??' back to '?' 236 | if (i+1 < aChars.length && aChars[i+1] == '?') { 237 | buf.append("?"); 238 | i = i+1; 239 | } else buf.append("?"); 240 | 241 | break; 242 | 243 | default: 244 | buf.append(aChars[i]); 245 | } 246 | } 247 | 248 | return buf.toString(); 249 | } 250 | 251 | /* 252 | * @param c 253 | * @return true if the character is a whitespace character as defined 254 | * in the backend's parser 255 | */ 256 | public static boolean isSpace(char c) { 257 | return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'; 258 | } 259 | 260 | /* 261 | * @param c 262 | * @return true if the given character is a valid character for an 263 | * operator in the backend's parser 264 | */ 265 | public static boolean isOperatorChar(char c) { 266 | /* 267 | * Extracted from operators defined by {self} and {op_chars} 268 | * in pgsql/src/backend/parser/scan.l. 269 | */ 270 | return ",()[].;:+-*/%^<>=~!@#&|`?".indexOf(c) != -1; 271 | } 272 | 273 | /** 274 | * Checks if a character is valid as the start of an identifier. 275 | * 276 | * @param c the character to check 277 | * @return true if valid as first character of an identifier; false if not 278 | */ 279 | public static boolean isIdentifierStartChar(char c) { 280 | /* 281 | * Extracted from {ident_start} and {ident_cont} in 282 | * pgsql/src/backend/parser/scan.l: 283 | * ident_start [A-Za-z\200-\377_] 284 | * ident_cont [A-Za-z\200-\377_0-9\$] 285 | */ 286 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') 287 | || c == '_' || c > 127 ; 288 | } 289 | 290 | /** 291 | * Checks if a character is valid as the second or later character of an 292 | * identifier. 293 | * 294 | * @param c the character to check 295 | * @return true if valid as second or later character of an identifier; false if not 296 | */ 297 | public static boolean isIdentifierContChar(char c) { 298 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') 299 | || c == '_' || c > 127 300 | || (c >= '0' && c <= '9') 301 | || c == '$'; 302 | } 303 | 304 | /* 305 | * @param c 306 | * @return true if the character terminates an identifier 307 | */ 308 | public static boolean charTerminatesIdentifier(char c) { 309 | return c == '"' || isSpace(c) || isOperatorChar(c); 310 | } 311 | 312 | /** 313 | * Checks if a character is valid as the start of a dollar quoting tag. 314 | * 315 | * @param c the character to check 316 | * @return true if valid as first character of a dollar quoting tag; false if not 317 | */ 318 | public static boolean isDollarQuoteStartChar(char c) { 319 | /* 320 | * The allowed dollar quote start and continuation characters 321 | * must stay in sync with what the backend defines in 322 | * pgsql/src/backend/parser/scan.l 323 | */ 324 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') 325 | || c == '_' || c > 127; 326 | } 327 | 328 | /** 329 | * Checks if a character is valid as the second or later character of a 330 | * dollar quoting tag. 331 | * 332 | * @param c the character to check 333 | * @return true if valid as second or later character of a dollar quoting tag; 334 | * false if not 335 | */ 336 | public static boolean isDollarQuoteContChar(char c) { 337 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') 338 | || c == '_' || c > 127 339 | || (c >= '0' && c <= '9'); 340 | } 341 | 342 | /** 343 | * Compares two sub-arrays of the given character array for equalness. 344 | * If the length is zero, the result is true if and only if the offsets 345 | * are within the bounds of the array. 346 | * 347 | * @param arr a char array 348 | * @param offA first sub-array start offset 349 | * @param offB second sub-array start offset 350 | * @param len length of the sub arrays to compare 351 | * @return true if the sub-arrays are equal; false if not 352 | */ 353 | private static boolean subArraysEqual(final char[] arr, 354 | final int offA, final int offB, 355 | final int len) { 356 | if (offA < 0 || offB < 0 357 | || offA >= arr.length || offB >= arr.length 358 | || offA + len > arr.length || offB + len > arr.length) 359 | return false; 360 | 361 | for (int i = 0; i < len; ++i) 362 | { 363 | if (arr[offA + i] != arr[offB + i]) 364 | return false; 365 | } 366 | 367 | return true; 368 | } 369 | } 370 | 371 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/util/Credentials.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc.util; 13 | 14 | import org.boon.json.JsonFactory; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | /** 20 | * Created by davec on 2015-05-12. 21 | */ 22 | public class Credentials 23 | { 24 | 25 | List credentials = new ArrayList(); 26 | 27 | public Credentials() 28 | { 29 | } 30 | 31 | public Credentials add(String user, String password) 32 | { 33 | this.credentials.add(new Credential(user,password)); 34 | return this; 35 | } 36 | 37 | private class Credential 38 | { 39 | String user, pass; 40 | 41 | Credential(String user, String password) 42 | { 43 | this.user = user; 44 | this.pass = password; 45 | } 46 | 47 | } 48 | public String toString() 49 | { 50 | return JsonFactory.toJson(credentials); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/util/JSONTypes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc.util; 13 | 14 | import java.sql.Types; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | /** 19 | * Created by davec on 2015-09-10. 20 | */ 21 | public class JSONTypes 22 | { 23 | final static public int JSON_NUMBER = 0; 24 | final static public int JSON_STRING = 1; 25 | final static public int JSON_BOOLEAN = 2; 26 | final static public int JSON_ARRAY = 3; 27 | final static public int JSON_MAP = 4; 28 | final static public int JSON_OBJECT = 5; 29 | final static public int JSON_NULL = 6; 30 | 31 | static public Map jsonTypes = new HashMap(); 32 | 33 | static { 34 | jsonTypes.put("number",JSON_NUMBER); 35 | jsonTypes.put("string",JSON_STRING); 36 | jsonTypes.put("boolean",JSON_BOOLEAN); 37 | jsonTypes.put("array",JSON_ARRAY); 38 | jsonTypes.put("map",JSON_MAP); 39 | jsonTypes.put("object",JSON_OBJECT); 40 | jsonTypes.put("json",JSON_OBJECT); 41 | jsonTypes.put("null",JSON_NULL); 42 | } 43 | 44 | static public Map jdbcTypes = new HashMap(); 45 | 46 | static { 47 | jdbcTypes.put("number", Types.NUMERIC); 48 | jdbcTypes.put("string", Types.VARCHAR); 49 | jdbcTypes.put("boolean", Types.BOOLEAN); 50 | jdbcTypes.put("array", Types.ARRAY); 51 | jdbcTypes.put("map", Types.JAVA_OBJECT); //?? 52 | jdbcTypes.put("object", Types.JAVA_OBJECT); 53 | jdbcTypes.put("json", Types.JAVA_OBJECT); 54 | jdbcTypes.put("null", Types.NULL); 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/jdbc/util/SqlParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc.util; 13 | 14 | import com.couchbase.jdbc.core.CouchBaseSQLException; 15 | import com.couchbase.jdbc.core.EscapedFunctions; 16 | import com.couchbase.jdbc.core.Parser; 17 | 18 | import java.lang.reflect.InvocationTargetException; 19 | import java.lang.reflect.Method; 20 | import java.sql.SQLException; 21 | import java.util.ArrayList; 22 | 23 | /** 24 | * Created by davec on 2015-03-12. 25 | */ 26 | public class SqlParser 27 | { 28 | final ArrayList statementList; 29 | final ArrayList fragmentList; 30 | 31 | 32 | String query; 33 | 34 | public SqlParser(String sql) 35 | { 36 | statementList = new ArrayList(); 37 | //noinspection unchecked 38 | fragmentList = new ArrayList(15); 39 | query=sql; 40 | } 41 | 42 | public int getNumFields() 43 | { 44 | return fragmentList.size()-1; 45 | } 46 | public void parse() 47 | { 48 | int parameterIndex = 1; 49 | int fragmentStart = 0; 50 | int inParen = 0; 51 | 52 | 53 | char[] aChars = query.toCharArray(); 54 | 55 | for (int i = 0; i < aChars.length; ++i) 56 | { 57 | switch (aChars[i]) 58 | { 59 | case '\'': // single-quotes 60 | i = Parser.parseSingleQuotes(aChars, i); 61 | break; 62 | 63 | case '"': // double-quotes 64 | i = Parser.parseDoubleQuotes(aChars, i); 65 | break; 66 | 67 | case '-': // possibly -- style comment 68 | i = Parser.parseLineComment(aChars, i); 69 | break; 70 | 71 | case '/': // possibly /* */ style comment 72 | i = Parser.parseBlockComment(aChars, i); 73 | break; 74 | 75 | case '$': // possibly dollar quote start 76 | i = Parser.parseDollarQuotes(aChars, i); 77 | break; 78 | 79 | case '(': 80 | inParen++; 81 | break; 82 | 83 | case ')': 84 | inParen--; 85 | break; 86 | 87 | case '?': 88 | fragmentList.add(query.substring(fragmentStart, i)+"$"+parameterIndex++); 89 | fragmentStart = i + 1; 90 | break; 91 | 92 | case ';': 93 | if (inParen == 0) 94 | { 95 | fragmentList.add(query.substring(fragmentStart, i)); 96 | fragmentStart = i + 1; 97 | if (fragmentList.size() > 1 || fragmentList.get(0).trim().length() > 0) 98 | //noinspection unchecked 99 | statementList.add(fragmentList.toArray(new String[fragmentList.size()])); 100 | fragmentList.clear(); 101 | } 102 | break; 103 | 104 | default: 105 | break; 106 | } 107 | } 108 | fragmentList.add(query.substring(fragmentStart)); 109 | 110 | } 111 | public String replaceProcessing(String p_sql, boolean replaceProcessingEnabled) throws SQLException 112 | { 113 | if (replaceProcessingEnabled) 114 | { 115 | // Since escape codes can only appear in SQL CODE, we keep track 116 | // of if we enter a string or not. 117 | int len = p_sql.length(); 118 | StringBuilder newsql = new StringBuilder(len); 119 | int i=0; 120 | while (i 0)) ORDER BY a 124 | // We can't ending replacing after the extra closing paren 125 | // because that changes a syntax error to a valid query 126 | // that isn't what the user specified. 127 | if (i < len) { 128 | newsql.append(p_sql.charAt(i)); 129 | i++; 130 | } 131 | } 132 | return newsql.toString(); 133 | } 134 | else 135 | { 136 | return p_sql; 137 | } 138 | } 139 | 140 | private static final short IN_SQLCODE = 0; 141 | private static final short IN_STRING = 1; 142 | private static final short IN_IDENTIFIER = 6; 143 | private static final short BACKSLASH = 2; 144 | private static final short ESC_TIMEDATE = 3; 145 | private static final short ESC_FUNCTION = 4; 146 | private static final short ESC_OUTERJOIN = 5; 147 | private static final short ESC_ESCAPECHAR = 7; 148 | 149 | /* 150 | * parse the given sql from index i, appending it to the gven buffer 151 | * until we hit an unmatched right parentheses or end of string. When 152 | * the stopOnComma flag is set we also stop processing when a comma is 153 | * found in sql text that isn't inside nested parenthesis. 154 | * 155 | * @param p_sql the original query text 156 | * @param i starting position for replacing 157 | * @param newsql where to write the replaced output 158 | * @param stopOnComma should we stop after hitting the first comma in sql text? 159 | * @return the position we stopped processing at 160 | */ 161 | protected int parseSql(String p_sql,int i,StringBuilder newsql, boolean stopOnComma ) throws SQLException 162 | { 163 | short state = IN_SQLCODE; 164 | int len = p_sql.length(); 165 | int nestedParenthesis=0; 166 | boolean endOfNested=false; 167 | 168 | // because of the ++i loop 169 | i--; 170 | while (!endOfNested && ++i < len) 171 | { 172 | char c = p_sql.charAt(i); 173 | switch (state) 174 | { 175 | case IN_SQLCODE: 176 | if (c == '\'') // start of a string? 177 | state = IN_STRING; 178 | else if (c == '"') // start of a identifer? 179 | state = IN_IDENTIFIER; 180 | else if (c=='(') { // begin nested sql 181 | nestedParenthesis++; 182 | } else if (c==')') { // end of nested sql 183 | nestedParenthesis--; 184 | if (nestedParenthesis<0){ 185 | endOfNested=true; 186 | break; 187 | } 188 | } else if (stopOnComma && c==',' && nestedParenthesis==0) { 189 | endOfNested=true; 190 | break; 191 | } else if (c == '{') { // start of an escape code? 192 | if (i + 1 < len) 193 | { 194 | char next = p_sql.charAt(i + 1); 195 | char nextnext = (i + 2 < len) ? p_sql.charAt(i + 2) : '\0'; 196 | if (next == 'd' || next == 'D') 197 | { 198 | state = ESC_TIMEDATE; 199 | i++; 200 | newsql.append("DATE "); 201 | break; 202 | } 203 | else if (next == 't' || next == 'T') 204 | { 205 | state = ESC_TIMEDATE; 206 | if (nextnext == 's' || nextnext == 'S'){ 207 | // timestamp constant 208 | i+=2; 209 | //newsql.append("TIMESTAMP "); 210 | }else{ 211 | // time constant 212 | i++; 213 | //newsql.append("TIME "); 214 | } 215 | break; 216 | } 217 | else if ( next == 'f' || next == 'F' ) 218 | { 219 | state = ESC_FUNCTION; 220 | i += (nextnext == 'n' || nextnext == 'N') ? 2 : 1; 221 | break; 222 | } 223 | else if ( next == 'o' || next == 'O' ) 224 | { 225 | state = ESC_OUTERJOIN; 226 | i += (nextnext == 'j' || nextnext == 'J') ? 2 : 1; 227 | break; 228 | } 229 | else if ( next == 'e' || next == 'E' ) 230 | { // we assume that escape is the only escape sequence beginning with e 231 | state = ESC_ESCAPECHAR; 232 | break; 233 | } 234 | } 235 | } 236 | newsql.append(c); 237 | break; 238 | 239 | case IN_STRING: 240 | if (c == '\'') // end of string? 241 | state = IN_SQLCODE; 242 | else if (c == '\\' ) // a backslash? 243 | state = BACKSLASH; 244 | 245 | newsql.append(c); 246 | break; 247 | 248 | case IN_IDENTIFIER: 249 | if (c == '"') // end of identifier 250 | state = IN_SQLCODE; 251 | newsql.append(c); 252 | break; 253 | 254 | case BACKSLASH: 255 | state = IN_STRING; 256 | 257 | newsql.append(c); 258 | break; 259 | 260 | case ESC_FUNCTION: 261 | // extract function name 262 | String functionName; 263 | int posArgs = p_sql.indexOf('(',i); 264 | if (posArgs!=-1){ 265 | functionName=p_sql.substring(i,posArgs).trim(); 266 | // extract arguments 267 | i= posArgs+1;// we start the scan after the first ( 268 | StringBuilder args=new StringBuilder(); 269 | i = parseSql(p_sql,i,args,false); 270 | // translate the function and parse arguments 271 | newsql.append(escapeFunction(functionName,args.toString())); 272 | } 273 | // go to the end of the function copying anything found 274 | i++; 275 | while (i2 ) 103 | { 104 | int nanos = Integer.parseInt(parts[2]); 105 | 106 | 107 | for (int numlength = parts[2].length(); numlength < 9; ++numlength) 108 | nanos *= 10; 109 | ts.setNanos(nanos); 110 | } 111 | 112 | return ts; 113 | } 114 | 115 | public Time applyCalendar( Calendar cal, Time time) 116 | { 117 | // check to see if there is a calendar and that it is different than the one used to parse 118 | if ( !cal.getTimeZone().hasSameRules(tf.getTimeZone())) 119 | { 120 | Calendar convertCal = Calendar.getInstance(); 121 | convertCal.setTime(time); 122 | TimeZone toTimeZone = cal.getTimeZone(); 123 | TimeZone fromTimeZone = tf.getTimeZone(); 124 | 125 | convertCal.setTimeZone(fromTimeZone); 126 | convertCal.add(Calendar.MILLISECOND, fromTimeZone.getRawOffset() * -1); 127 | if (fromTimeZone.inDaylightTime(convertCal.getTime())) { 128 | convertCal.add(Calendar.MILLISECOND, convertCal.getTimeZone().getDSTSavings() * -1); 129 | } 130 | 131 | convertCal.add(Calendar.MILLISECOND, toTimeZone.getRawOffset()); 132 | if (toTimeZone.inDaylightTime(convertCal.getTime())) { 133 | convertCal.add(Calendar.MILLISECOND, toTimeZone.getDSTSavings()); 134 | } 135 | 136 | return new Time(convertCal.getTime().getTime()); 137 | } 138 | return time; 139 | } 140 | 141 | public Date applyCalendar(Calendar cal, Date date) 142 | { 143 | // check to see if there is a calendar and that it is different than the one used to parse 144 | if ( !cal.getTimeZone().hasSameRules(df.getTimeZone())) 145 | { 146 | Calendar convertCal = Calendar.getInstance(); 147 | convertCal.setTime(date); 148 | TimeZone toTimeZone = cal.getTimeZone(); 149 | TimeZone fromTimeZone = df.getTimeZone(); 150 | 151 | convertCal.setTimeZone(fromTimeZone); 152 | convertCal.add(Calendar.MILLISECOND, fromTimeZone.getRawOffset() * -1); 153 | if (fromTimeZone.inDaylightTime(convertCal.getTime())) { 154 | convertCal.add(Calendar.MILLISECOND, convertCal.getTimeZone().getDSTSavings() * -1); 155 | } 156 | 157 | convertCal.add(Calendar.MILLISECOND, toTimeZone.getRawOffset()); 158 | if (toTimeZone.inDaylightTime(convertCal.getTime())) { 159 | convertCal.add(Calendar.MILLISECOND, toTimeZone.getDSTSavings()); 160 | } 161 | 162 | return new Date(convertCal.getTime().getTime()); 163 | } 164 | return date; 165 | } 166 | 167 | public Timestamp applyCalendar(Calendar cal, Timestamp timestamp) 168 | { 169 | if ( !cal.getTimeZone().hasSameRules(tsf.getTimeZone())) 170 | { 171 | Calendar convertCal = Calendar.getInstance(); 172 | convertCal.setTime(timestamp); 173 | TimeZone toTimeZone = cal.getTimeZone(); 174 | TimeZone fromTimeZone = tsf.getTimeZone(); 175 | 176 | convertCal.setTimeZone(fromTimeZone); 177 | convertCal.add(Calendar.MILLISECOND, fromTimeZone.getRawOffset() * -1); 178 | if (fromTimeZone.inDaylightTime(convertCal.getTime())) { 179 | convertCal.add(Calendar.MILLISECOND, convertCal.getTimeZone().getDSTSavings() * -1); 180 | } 181 | 182 | convertCal.add(Calendar.MILLISECOND, toTimeZone.getRawOffset()); 183 | if (toTimeZone.inDaylightTime(convertCal.getTime())) { 184 | convertCal.add(Calendar.MILLISECOND, toTimeZone.getDSTSavings()); 185 | } 186 | 187 | return new Timestamp(convertCal.getTime().getTime()); 188 | } 189 | return timestamp; 190 | } 191 | private static void appendDate(StringBuffer sb, Calendar cal) 192 | { 193 | int l_year = cal.get(Calendar.YEAR); 194 | // always use at least four digits for the year so very 195 | // early years, like 2, don't get misinterpreted 196 | // 197 | int l_yearlen = String.valueOf(l_year).length(); 198 | for (int i = 4; i > l_yearlen; i--) 199 | { 200 | sb.append("0"); 201 | } 202 | 203 | sb.append(l_year); 204 | sb.append('-'); 205 | int l_month = cal.get(Calendar.MONTH) + 1; 206 | if (l_month < 10) 207 | sb.append('0'); 208 | sb.append(l_month); 209 | sb.append('-'); 210 | int l_day = cal.get(Calendar.DAY_OF_MONTH); 211 | if (l_day < 10) 212 | sb.append('0'); 213 | sb.append(l_day); 214 | } 215 | 216 | private static void appendTime(StringBuffer sb, Calendar cal, int nanos) 217 | { 218 | int hours = cal.get(Calendar.HOUR_OF_DAY); 219 | if (hours < 10) 220 | sb.append('0'); 221 | sb.append(hours); 222 | 223 | sb.append(':'); 224 | int minutes = cal.get(Calendar.MINUTE); 225 | if (minutes < 10) 226 | sb.append('0'); 227 | sb.append(minutes); 228 | 229 | sb.append(':'); 230 | int seconds = cal.get(Calendar.SECOND); 231 | if (seconds < 10) 232 | sb.append('0'); 233 | sb.append(seconds); 234 | 235 | // Add nanoseconds. 236 | // This won't work for server versions < 7.2 which only want 237 | // a two digit fractional second, but we don't need to support 7.1 238 | // anymore and getting the version number here is difficult. 239 | // 240 | char[] decimalStr = {'0', '0', '0', '0', '0', '0', '0', '0', '0'}; 241 | char[] nanoStr = Integer.toString(nanos).toCharArray(); 242 | System.arraycopy(nanoStr, 0, decimalStr, decimalStr.length - nanoStr.length, nanoStr.length); 243 | sb.append('.'); 244 | sb.append(decimalStr, 0, 6); 245 | } 246 | 247 | private void appendTimeZone(StringBuffer sb, java.util.Calendar cal) 248 | { 249 | int offset = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / 1000; 250 | 251 | int absoff = Math.abs(offset); 252 | int hours = absoff / 60 / 60; 253 | int mins = (absoff - hours * 60 * 60) / 60; 254 | int secs = absoff - hours * 60 * 60 - mins * 60; 255 | 256 | sb.append((offset >= 0) ? " +" : " -"); 257 | 258 | if (hours < 10) 259 | sb.append('0'); 260 | sb.append(hours); 261 | 262 | sb.append(':'); 263 | 264 | if (mins < 10) 265 | sb.append('0'); 266 | sb.append(mins); 267 | 268 | sb.append(':'); 269 | if (secs < 10) 270 | sb.append('0'); 271 | sb.append(secs); 272 | } 273 | 274 | private static void appendEra(StringBuffer sb, Calendar cal) 275 | { 276 | if (cal.get(Calendar.ERA) == GregorianCalendar.BC) { 277 | sb.append(" BC"); 278 | } 279 | } 280 | 281 | } 282 | -------------------------------------------------------------------------------- /src/main/java/com/couchbase/json/SQLJSON.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.json; 13 | 14 | import java.io.InputStream; 15 | import java.io.Reader; 16 | import java.math.BigDecimal; 17 | import java.sql.*; 18 | import java.util.Calendar; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | /** 23 | * Created by davec on 2015-06-26. 24 | */ 25 | public interface SQLJSON 26 | { 27 | void free(); 28 | 29 | InputStream getBinaryStream() throws SQLException; 30 | void setBinaryStream() throws SQLException; 31 | 32 | Reader getCharacterStream() throws SQLException; 33 | void setCharacterStream(Reader stream) throws SQLException; 34 | 35 | String getString() throws SQLException; 36 | void setString(String str) throws SQLException; 37 | 38 | boolean getBoolean() throws SQLException; 39 | void setBoolean(boolean val) throws SQLException; 40 | 41 | byte getByte() throws SQLException; 42 | void setByte(byte val) throws SQLException; 43 | 44 | short getShort() throws SQLException; 45 | void setShort(short val) throws SQLException; 46 | 47 | int getInt() throws SQLException; 48 | void setInt(int val) throws SQLException; 49 | 50 | long getLong() throws SQLException; 51 | void setLong(long val) throws SQLException; 52 | 53 | void setFloat(float val)throws SQLException; 54 | float getFloat()throws SQLException; 55 | 56 | void setDouble(double val)throws SQLException; 57 | double getDouble()throws SQLException; 58 | 59 | void setBytes(byte[] val)throws SQLException; 60 | byte[] getBytes()throws SQLException; 61 | 62 | void setDate(Date val, Calendar cal)throws SQLException; 63 | Date getDate(Calendar cal)throws SQLException; 64 | 65 | void setTime(Time val, Calendar cal)throws SQLException; 66 | Time getTime(Calendar cal)throws SQLException; 67 | 68 | void setTimestamp(Timestamp val, Calendar cal)throws SQLException; 69 | Timestamp getTimestamp(Calendar cal)throws SQLException; 70 | 71 | BigDecimal getBigDecimal() throws SQLException; 72 | void setBigDecimal(BigDecimal val) throws SQLException; 73 | 74 | Map getMap() throws SQLException; 75 | void setMap(Map map) throws SQLException; 76 | 77 | List getArray() throws SQLException; 78 | void setArray(Object[] array) throws SQLException; 79 | void setArray(List array) throws SQLException; 80 | 81 | Object getObject() throws SQLException; 82 | void setObject(Object val) throws SQLException; 83 | 84 | /* 85 | * 86 | * @param columnName 87 | * @return if the SQLJSON object is a JSON object. 88 | Get the given object with the given fieldName, 89 | * @throws SQLException Throw not valid exception if the SQLJSON object is not a JSON object. 90 | */ 91 | Object getObject(String columnName) throws SQLException; 92 | 93 | 94 | /* 95 | * 96 | * Set the given object with the given fieldName, if the SQLJSON object is a JSON object. 97 | * @param columnName 98 | * @param val 99 | * @throws SQLException Throw not valid exception if the SQLJSON object is not a JSON object. 100 | */ 101 | void setObject(String columnName, Object val) throws SQLException; 102 | 103 | 104 | /* 105 | * Return the object at the given index, if the SQLJSON object is a JSON array. 106 | * Return NULL if the SQLJSON object is not a JSON array 107 | * or if the SQLJSON object is a JSON array and does not have an element at the given index. 108 | * 109 | * @param index 110 | * @return 111 | */ 112 | Object get(int index) ; 113 | 114 | /* 115 | * Set the given index with the given element, if the SQLJSON object is a JSON array. 116 | * 117 | * @param index 118 | */ 119 | void set(int index, Object object) throws SQLException; 120 | boolean isNull() throws SQLException; 121 | 122 | int getJDBCType(); 123 | Object parameterValue(); 124 | 125 | 126 | Object parse(Class clazz) throws SQLException; 127 | Map parse() throws SQLException; 128 | 129 | int compareTo(SQLJSON sqljson); 130 | } 131 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/java.sql.Driver: -------------------------------------------------------------------------------- 1 | com.couchbase.jdbc.CBDriver -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/BigDataJDBCDriverTests.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.HashMap; 6 | 7 | import org.json.simple.JSONArray; 8 | import org.json.simple.JSONObject; 9 | import org.junit.BeforeClass; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.junit.runners.JUnit4; 13 | 14 | @RunWith(JUnit4.class) 15 | public class BigDataJDBCDriverTests { 16 | @BeforeClass 17 | public static void initializeCluster() throws Exception 18 | { 19 | TestUtil.resetEnvironmentProperties(null); 20 | } 21 | 22 | @SuppressWarnings("unchecked") 23 | @Test 24 | public void testLargeDataSize() throws Exception 25 | { 26 | JDBCTestUtils.setConnection(null); 27 | JSONObject obj = new JSONObject(); 28 | String deleteData = "delete from default"; 29 | JDBCTestUtils.runQueryWithoutResult(deleteData); 30 | HashMap map = new HashMap(); 31 | StringBuffer outputBuffer = new StringBuffer(); 32 | for (int i = 0; i < 1000; i++){ 33 | outputBuffer.append("xx"); 34 | } 35 | map.put("name", outputBuffer.toString()); 36 | map.put("id", 12345); 37 | obj.putAll(map); 38 | JSONArray expectedArray = new JSONArray(); 39 | HashMap objMap = new HashMap(); 40 | objMap.put("1", obj); 41 | expectedArray.add(obj); 42 | JDBCTestUtils.insertData(objMap, "default"); 43 | String query = "select * from default"; 44 | JSONArray actualArray = JDBCTestUtils.runQueryAndExtractMap(query); 45 | assertEquals(expectedArray, actualArray); 46 | } 47 | 48 | @SuppressWarnings("unchecked") 49 | @Test 50 | public void testLargeNumberOfFields() throws Exception 51 | { 52 | JDBCTestUtils.setConnection(null); 53 | JSONObject obj = new JSONObject(); 54 | String deleteData = "delete from default"; 55 | JDBCTestUtils.runQueryWithoutResult(deleteData); 56 | HashMap map = new HashMap(); 57 | for (int i = 1; i < 10000; i++){ 58 | map.put(i+"", 12345); 59 | } 60 | obj.putAll(map); 61 | JSONArray expectedArray = new JSONArray(); 62 | HashMap objMap = new HashMap(); 63 | objMap.put("1", obj); 64 | expectedArray.add(obj); 65 | JDBCTestUtils.insertData(objMap, "default"); 66 | String query = "select * from default"; 67 | JSONArray actualArray = JDBCTestUtils.runQueryAndExtractMap(query); 68 | assertEquals(expectedArray, actualArray); 69 | } 70 | 71 | @SuppressWarnings("unchecked") 72 | // FIXME: Fix this test. 73 | public void testLargeNumberOfObjects() throws Exception 74 | { 75 | JDBCTestUtils.setConnection(null); 76 | JSONObject obj = new JSONObject(); 77 | String deleteData = "delete from default"; 78 | JDBCTestUtils.runQueryWithoutResult(deleteData); 79 | JSONArray expectedArray = new JSONArray(); 80 | for(int m=1;m<100;++m){ 81 | HashMap objMap = new HashMap(); 82 | for(int j=0;j<100;++j){ 83 | HashMap map = new HashMap(); 84 | StringBuffer outputBuffer = new StringBuffer(); 85 | for (int i = 0; i < 1000; i++){ 86 | outputBuffer.append("xx"); 87 | } 88 | map.put("name", outputBuffer.toString()); 89 | map.put("id", 12345); 90 | obj.putAll(map); 91 | objMap.put(m+j+"", obj); 92 | expectedArray.add(obj); 93 | } 94 | JDBCTestUtils.bulkInsertData(objMap, "default"); 95 | JDBCTestUtils.bulkUpsertData(objMap, "default"); 96 | } 97 | String query = "select * from default"; 98 | JSONArray actualArray = JDBCTestUtils.runQueryAndExtractMap(query); 99 | assertEquals(expectedArray, actualArray); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/BucketInfo.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc; 2 | 3 | import org.json.simple.JSONObject; 4 | 5 | public class BucketInfo { 6 | public String name; 7 | public int port; 8 | public int ramQuotaMB; 9 | public int replicaNumber; 10 | public Integer proxyPort; 11 | public String authType; 12 | public static int proxy_port = 21000; 13 | 14 | /*** 15 | * Constructor 16 | */ 17 | public BucketInfo(){ 18 | this.name = "default"; 19 | this.port = 12000; 20 | this.ramQuotaMB = 1000; 21 | this.replicaNumber = 0; 22 | this.proxyPort = BucketInfo.proxy_port++; 23 | this.authType = "none"; 24 | } 25 | 26 | /*** 27 | * Constructor 28 | * @param name 29 | * @param port 30 | * @param ramQuotaMB 31 | * @param replicaNumber 32 | * @param proxyPort 33 | * @param authType 34 | */ 35 | public BucketInfo( 36 | String name, 37 | int port, 38 | int ramQuotaMB, 39 | int replicaNumber, 40 | int proxyPort, 41 | String authType) { 42 | this.name = name; 43 | this.port = port; 44 | this.ramQuotaMB = ramQuotaMB; 45 | this.replicaNumber = replicaNumber; 46 | this.proxyPort = proxyPort; 47 | this.authType = authType; 48 | } 49 | 50 | @SuppressWarnings("unchecked") 51 | public JSONObject createJSONObject(){ 52 | JSONObject obj = new JSONObject(); 53 | obj.put("name", this.name); 54 | obj.put("authType", this.authType); 55 | obj.put("proxyPort", this.proxyPort); 56 | obj.put("ramQuotaMB", this.ramQuotaMB); 57 | obj.put("replicaNumber", this.replicaNumber); 58 | return obj; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return "\n [name=" + name + ", port=" + port + ", ramQuotaMB=" + ramQuotaMB + ", replicaNumber=" 64 | + replicaNumber + ", proxyPort=" + proxyPort + ", authType=" + authType + "]"; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/ClusterAwareTests.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc; 2 | 3 | import com.couchbase.json.SQLJSON; 4 | import com.couchbase.jdbc.CBResultSet; 5 | 6 | import org.junit.After; 7 | import org.junit.AfterClass; 8 | import org.junit.BeforeClass; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.junit.runners.JUnit4; 12 | import static org.junit.Assert.*; 13 | import java.util.Map; 14 | import java.sql.Connection; 15 | import java.sql.ResultSet; 16 | import java.sql.Statement; 17 | import java.sql.Types; 18 | import java.util.HashMap; 19 | import org.json.simple.JSONArray; 20 | import org.json.simple.JSONObject; 21 | 22 | @RunWith(JUnit4.class) 23 | public class ClusterAwareTests { 24 | 25 | @BeforeClass 26 | public static void initializeCluster() throws Exception 27 | { 28 | TestUtil.resetEnvironmentProperties(null); 29 | TestUtil.setRebalancePermission(); 30 | TestUtil.initializeCluster(true); 31 | } 32 | 33 | @AfterClass 34 | public static void cleanupCluster() throws Exception 35 | { 36 | TestUtil.destroyCluster(); 37 | } 38 | 39 | @After 40 | public void cleanupBucket() throws Exception 41 | { 42 | JDBCTestUtils.deleteDataFromBucket("default"); 43 | Thread.sleep(1000); 44 | } 45 | 46 | @SuppressWarnings("unchecked") 47 | @Test 48 | public void testRemoveService() throws Exception 49 | { 50 | 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/ClusterInfo.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc; 2 | 3 | import java.util.HashMap; 4 | import org.json.simple.JSONArray; 5 | import org.json.simple.JSONObject; 6 | 7 | public class ClusterInfo { 8 | NodeInfo global ; 9 | NodeInfo masterNodeInfo ; 10 | public HashMap nodeInformation; 11 | public HashMap bucketInformation; 12 | 13 | 14 | 15 | /*** 16 | * Constructor 17 | */ 18 | public ClusterInfo() { 19 | global = new NodeInfo(); 20 | masterNodeInfo = null; 21 | this.nodeInformation = new HashMap(); 22 | this.bucketInformation = new HashMap(); 23 | } 24 | 25 | 26 | /*** 27 | * Add Node information 28 | * @param nodeInfo 29 | */ 30 | public void addNodeInfo(NodeInfo nodeInfo){ 31 | if(nodeInformation == null){ 32 | nodeInformation = new HashMap(); 33 | } 34 | nodeInformation.put(String.format("%s:%s", nodeInfo.ip,nodeInfo.port), nodeInfo); 35 | } 36 | 37 | /*** 38 | * Add Bucket Information 39 | * @param bucketInfo 40 | */ 41 | public void addBucketInfo(BucketInfo bucketInfo){ 42 | this.bucketInformation.put(bucketInfo.name, bucketInfo); 43 | } 44 | 45 | /*** 46 | * Reset Bucket Information 47 | */ 48 | public void resetBucketInformation(){ 49 | new HashMap(); 50 | } 51 | 52 | @SuppressWarnings("unchecked") 53 | public JSONObject createJSONObject(){ 54 | JSONObject jsonObject = new JSONObject(); 55 | JSONArray nodeInformationObjectArray = new JSONArray(); 56 | for(NodeInfo nodeInfo:this.nodeInformation.values()){ 57 | nodeInformationObjectArray.add(nodeInfo.createJSONObject()); 58 | } 59 | jsonObject.put("node_info", nodeInformationObjectArray); 60 | JSONArray bucketInformationObjectArray = new JSONArray(); 61 | for(BucketInfo bucketInfo:this.bucketInformation.values()){ 62 | bucketInformationObjectArray.add(bucketInfo.createJSONObject()); 63 | } 64 | jsonObject.put("bucket_info", bucketInformationObjectArray); 65 | jsonObject.put("global", global.createJSONObject()); 66 | return jsonObject; 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | StringBuffer sb = new StringBuffer(); 72 | sb.append("\n ________________ CLUSTER INFORMATION _____________________"); 73 | sb.append("\n ________________ Bucket Information _____________________"); 74 | for(BucketInfo info:bucketInformation.values()){ 75 | sb.append("\n "+info.toString()); 76 | } 77 | sb.append("\n ________________ Cluster Information _____________________"); 78 | for(NodeInfo info:nodeInformation.values()){ 79 | sb.append("\n "+info.toString()); 80 | } 81 | return sb.toString(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/ClusterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | import com.couchbase.jdbc.CBConnection; 15 | import com.couchbase.jdbc.ConnectionParameters; 16 | import com.couchbase.jdbc.connect.Cluster; 17 | import com.couchbase.jdbc.core.ProtocolImpl; 18 | import junit.framework.TestCase; 19 | 20 | import org.boon.json.JsonFactory; 21 | import org.boon.json.ObjectMapper; 22 | import org.junit.After; 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | import org.junit.runners.JUnit4; 27 | 28 | import java.sql.Connection; 29 | import java.sql.DriverManager; 30 | import java.sql.Statement; 31 | import java.util.Map; 32 | import java.util.Properties; 33 | 34 | /** 35 | * Created by davec on 2015-09-02. 36 | */ 37 | @RunWith(JUnit4.class) 38 | public class ClusterTest extends TestCase 39 | { 40 | Connection con; 41 | @Before 42 | public void openConnection() throws Exception 43 | { 44 | Properties properties = new Properties(); 45 | TestUtil.resetEnvironmentProperties(null); 46 | properties.put(ConnectionParameters.CONNECTION_TIMEOUT,"1000"); 47 | properties.put(ConnectionParameters.USER,TestUtil.getUser()); 48 | properties.put(ConnectionParameters.PASSWORD,TestUtil.getPassword()); 49 | con = DriverManager.getConnection(TestUtil.getURL(), properties); 50 | assertNotNull(con); 51 | } 52 | @After 53 | public void closeConnection() throws Exception 54 | { 55 | assertNotNull(con); 56 | if( con.isClosed()) return; 57 | con.createStatement().executeUpdate("delete from default"); 58 | con.close(); 59 | } 60 | @Test 61 | public void testBadInstance() throws Exception 62 | { 63 | 64 | Cluster cluster = ((ProtocolImpl)((CBConnection) con).protocol).getCluster(); 65 | 66 | String endpoint = "{\"cluster\":\"default\",\"name\":\"10.168.209.119\",\"queryEndpoint\":\"http://10.168.209.119:8093/query/service\"," + 67 | "\"adminEndpoint\":\"http://10.168.209.119:8093/admin\",\"options\":null}"; 68 | 69 | ObjectMapper mapper = JsonFactory.create(); 70 | Map instanceEndpoint = (Map)mapper.fromJson(endpoint); 71 | 72 | cluster.addEndPoint(instanceEndpoint); 73 | 74 | assertNotNull(con); 75 | Statement statement = con.createStatement(); 76 | assertNotNull(statement); 77 | 78 | // there are only 4 endpoints we added 1 which is 5 79 | for (int i = 0; i++< 6;) 80 | { 81 | 82 | int inserted = statement.executeUpdate("INSERT INTO default (KEY, VALUE) VALUES ( 'K" + i +"'," + i +")"); 83 | assertEquals(1, inserted); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/CouchBaseTestCase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | import com.couchbase.jdbc.ConnectionParameters; 15 | 16 | import junit.framework.TestCase; 17 | import org.junit.BeforeClass; 18 | import org.junit.Ignore; 19 | import org.junit.Before; 20 | import java.sql.Connection; 21 | import java.sql.DriverManager; 22 | import java.util.Properties; 23 | 24 | /** 25 | * Created by davec on 2015-09-08. 26 | */ 27 | 28 | @Ignore // Do not run this class, since it has no test methods of its own. Run tests derived from it, instead. 29 | public class CouchBaseTestCase extends TestCase 30 | { 31 | 32 | public Connection con; 33 | public static Properties properties; 34 | 35 | @BeforeClass 36 | public static void initialize() throws Exception 37 | { 38 | CouchBaseTestCase.properties = new Properties(); 39 | properties.put(ConnectionParameters.SCAN_CONSISTENCY,"request_plus"); 40 | properties.put(ConnectionParameters.USER,TestUtil.getUser()); 41 | properties.put(ConnectionParameters.PASSWORD,TestUtil.getPassword()); 42 | TestUtil.resetEnvironmentProperties(null); 43 | } 44 | 45 | @Before 46 | public void openConnection() throws Exception 47 | { 48 | con = DriverManager.getConnection(TestUtil.getURL(), CouchBaseTestCase.properties); 49 | assertNotNull(con); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/DriverTest.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * // Copyright (c) 2015 Couchbase, Inc. 4 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * // except in compliance with the License. You may obtain a copy of the License at 6 | * // http://www.apache.org/licenses/LICENSE-2.0 7 | * // Unless required by applicable law or agreed to in writing, software distributed under the 8 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 9 | * // either express or implied. See the License for the specific language governing permissions 10 | * // and limitations under the License. 11 | */ 12 | 13 | package com.couchbase.jdbc; 14 | 15 | import ch.qos.logback.classic.Level; 16 | 17 | import com.couchbase.jdbc.CBDriver; 18 | 19 | import junit.framework.TestCase; 20 | import org.junit.Before; 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | import org.junit.runners.JUnit4; 24 | 25 | import java.sql.Connection; 26 | import java.sql.DatabaseMetaData; 27 | import java.sql.DriverManager; 28 | import java.sql.SQLException; 29 | 30 | /** 31 | * Unit test for CouchBase JDBC Driver 32 | */ 33 | @RunWith(JUnit4.class) 34 | 35 | public class DriverTest extends TestCase 36 | { 37 | @Before 38 | public void setUp() throws Exception 39 | { 40 | TestUtil.resetEnvironmentProperties(null); 41 | TestUtil.initializeCluster(false); 42 | } 43 | 44 | /* 45 | * Tests parseURL (internal) 46 | */ 47 | /* 48 | * Tests the connect method by connecting to the test database 49 | */ 50 | @org.junit.Test 51 | public void testConnect() throws Exception 52 | { 53 | 54 | Connection con = DriverManager.getConnection(TestUtil.getURL(), TestUtil.getUser(), TestUtil.getPassword()); 55 | assertNotNull(con); 56 | con.close(); 57 | try 58 | { 59 | con.createStatement(); 60 | assertEquals("Connection should be unusable", false,true); 61 | } 62 | catch (SQLException ex) 63 | { 64 | assertEquals("Connection is closed", ex.getMessage()); 65 | } 66 | } 67 | 68 | @org.junit.Test 69 | public void testBadConnect() throws Exception 70 | { 71 | try 72 | { 73 | DriverManager.getConnection(TestUtil.getBadURL()+"/?connectionTimeout=1000", TestUtil.getUser(), TestUtil.getPassword()); 74 | assertFalse("Should not get here",true); 75 | } 76 | catch(SQLException ex) 77 | { 78 | assertEquals("Error opening connection",ex.getMessage()); 79 | } 80 | } 81 | public void getMetaData() throws Exception 82 | { 83 | Connection con = DriverManager.getConnection(TestUtil.getBadURL(), TestUtil.getUser(), TestUtil.getPassword()); 84 | assertNotNull(con); 85 | DatabaseMetaData databaseMetaData = con.getMetaData(); 86 | assertEquals(CBDriver.MAJOR_VERSION,databaseMetaData.getDriverMajorVersion()); 87 | assertEquals(CBDriver.MINOR_VERSION,databaseMetaData.getDatabaseMinorVersion()); 88 | assertEquals(4,databaseMetaData.getJDBCMajorVersion()); 89 | assertEquals(1,databaseMetaData.getJDBCMinorVersion()); 90 | } 91 | @Test 92 | public void setLogLevel() 93 | { 94 | CBDriver.setLogLevel(Level.DEBUG); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/NodeInfo.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc; 2 | 3 | import java.util.ArrayList; 4 | 5 | import org.json.simple.JSONArray; 6 | import org.json.simple.JSONObject; 7 | 8 | public class NodeInfo { 9 | public String ip; 10 | public int port; 11 | public boolean isMasterNode; 12 | public int n1qlPort; 13 | public int indexPort; 14 | public String membaseUserId; 15 | public String membasePassword; 16 | public String machineUserId; 17 | public String machinePassword; 18 | public ArrayList services; 19 | 20 | /*** 21 | * Constructor 22 | */ 23 | public NodeInfo(){ 24 | this.ip = "127.0.0.1"; 25 | this.indexPort = 9102; 26 | this.isMasterNode = false; 27 | this.machineUserId = "root"; 28 | this.machinePassword = "couchbase"; 29 | this.membaseUserId = "Administrator"; 30 | this.membasePassword = "password"; 31 | this.n1qlPort = 8093; 32 | this.port = 8091; 33 | services = new ArrayList(); 34 | services.add("kv"); 35 | services.add("n1ql"); 36 | services.add("index"); 37 | } 38 | 39 | /*** 40 | * Constructor 41 | * @param ip 42 | * @param port 43 | * @param n1qlPort 44 | * @param indexPort 45 | * @param membaseUserId 46 | * @param membasePassword 47 | * @param machineUserId 48 | * @param machinePassword 49 | * @param services 50 | */ 51 | public NodeInfo( 52 | String ip, 53 | int port, 54 | int n1qlPort, 55 | int indexPort, 56 | String membaseUserId, 57 | String membasePassword, 58 | String machineUserId, 59 | String machinePassword, 60 | ArrayList services) { 61 | this.ip = ip; 62 | this.port = port; 63 | this.n1qlPort = n1qlPort; 64 | this.indexPort = indexPort; 65 | this.membaseUserId = membaseUserId; 66 | this.membasePassword = membasePassword; 67 | this.machineUserId = machineUserId; 68 | this.machinePassword = machinePassword; 69 | this.services = services; 70 | } 71 | 72 | public String getServerURL(){ 73 | return String.format("http://%s:%s",this.ip,this.port); 74 | } 75 | 76 | public String getQueryServerURL(){ 77 | return String.format("jdbc:couchbase://%s:%s",this.ip,this.n1qlPort); 78 | } 79 | 80 | /*** 81 | * Create JSON Object from the current object 82 | * @return 83 | */ 84 | @SuppressWarnings("unchecked") 85 | public JSONObject createJSONObject(){ 86 | JSONObject obj = new JSONObject(); 87 | obj.put("ip", this.ip); 88 | obj.put("port", this.port); 89 | obj.put("n1qlPort", this.n1qlPort); 90 | obj.put("indexPort", this.indexPort); 91 | obj.put("membaseUserId", this.membaseUserId); 92 | obj.put("membasePassword", this.membasePassword); 93 | obj.put("machineUserId", this.machineUserId); 94 | obj.put("machinePassword", this.machinePassword); 95 | JSONArray array = new JSONArray(); 96 | for(String service:services){ 97 | array.add(service); 98 | } 99 | obj.put("services", array); 100 | return obj; 101 | } 102 | 103 | /*** 104 | * Method to return comma delimited services 105 | * @return 106 | */ 107 | public String getServices(){ 108 | StringBuffer sb = new StringBuffer(); 109 | if(this.services.size() > 0){ 110 | sb.append(this.services.toString().replace("[", "").replace("]", "")); 111 | }else{ 112 | sb.append("kv"); 113 | } 114 | return sb.toString().replace(" ", ""); 115 | } 116 | 117 | @Override 118 | public String toString() { 119 | return "\n [ip=" + ip + ", port=" + port + ", n1qlPort=" 120 | + n1qlPort + ", indexPort=" + indexPort + ", membaseUserId=" + membaseUserId + ", membasePassword=" 121 | + membasePassword + ", machineUserId=" + machineUserId + ", machinePassword=" + machinePassword 122 | + ", services=" + services + "]"; 123 | } 124 | 125 | 126 | 127 | 128 | 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/QueryInfo.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc; 2 | import org.json.simple.JSONArray; 3 | 4 | public class QueryInfo { 5 | public String n1ql; 6 | public String sql; 7 | public JSONArray expectedResult; 8 | public QueryInfo(String n1ql, String sql, JSONArray expectedResult) { 9 | this.n1ql = n1ql; 10 | this.sql = sql; 11 | this.expectedResult = expectedResult; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/RQGJDBCTests.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc; 2 | import org.junit.After; 3 | import org.junit.AfterClass; 4 | 5 | import junit.framework.TestCase; 6 | import org.junit.After; 7 | import org.junit.Before; 8 | import org.junit.BeforeClass; 9 | import org.junit.Rule; 10 | import org.junit.Test; 11 | import org.junit.rules.ExpectedException; 12 | import org.junit.runner.RunWith; 13 | import org.junit.runners.JUnit4; 14 | 15 | import junit.framework.TestCase; 16 | import java.sql.*; 17 | import static org.junit.Assert.*; 18 | 19 | @RunWith(JUnit4.class) 20 | public class RQGJDBCTests extends TestCase { 21 | 22 | @BeforeClass 23 | public static void initializeCluster() throws Exception 24 | { 25 | TestUtil.resetEnvironmentProperties(null); 26 | TestUtil.loadRQGProperties(); 27 | } 28 | 29 | @After 30 | public void cleanupData() throws Exception 31 | { 32 | TestUtil.clusterInfo.resetBucketInformation(); 33 | } 34 | 35 | @Test 36 | public void testAggregateMethods() throws Exception 37 | { 38 | String inputPath = TestUtil.getRQGAggregateDir(); 39 | TestResultAnalysis analysis = JDBCTestUtils.runTestsWithAggregateFunctionQueries(TestUtil.clusterInfo, inputPath); 40 | assertTrue(analysis.isTestPassing()); 41 | } 42 | 43 | @Test 44 | public void testFields() throws Exception 45 | { 46 | String inputPath = TestUtil.getRQGFieldsDir(); 47 | TestResultAnalysis analysis = JDBCTestUtils.runTestsWithFieldProjectionQueries(TestUtil.clusterInfo,inputPath); 48 | assertTrue(analysis.isTestPassing()); 49 | } 50 | 51 | @Test 52 | public void testJoins() throws Exception 53 | { 54 | String inputPath = TestUtil.getRQGJOINSDir(); 55 | TestResultAnalysis analysis = JDBCTestUtils.runTestsWithFieldProjectionQueries(TestUtil.clusterInfo, inputPath); 56 | assertTrue(analysis.isTestPassing()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/ResultSetMetaDataTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | import org.junit.After; 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | import org.junit.runner.RunWith; 18 | import org.junit.runners.JUnit4; 19 | import org.junit.Test; 20 | 21 | import java.sql.*; 22 | 23 | @RunWith(JUnit4.class) 24 | public class ResultSetMetaDataTest extends CouchBaseTestCase 25 | { 26 | static ResultSet resultSet; 27 | static ResultSetMetaData resultSetMetaData; 28 | 29 | @Before 30 | public void open() throws Exception 31 | { 32 | Statement statement = con.createStatement(); 33 | assertNotNull(statement); 34 | 35 | resultSet = statement.executeQuery("SELECT true as c1, 1 as c2, 3.14 as c3, 'Hello World!' as c4, [1,2,3,5,8] as c5, { 'a1': 'Object' } as c6"); 36 | assertTrue(resultSet.next()); 37 | resultSetMetaData = resultSet.getMetaData(); 38 | 39 | } 40 | 41 | @After 42 | public void closeConnection() throws Exception 43 | { 44 | assertNotNull(resultSet); 45 | resultSet.close(); 46 | assertNotNull(con); 47 | con.close(); 48 | } 49 | 50 | @Test 51 | public void testGetColumnCount() throws Exception 52 | { 53 | assertEquals(6,resultSetMetaData.getColumnCount()); 54 | } 55 | 56 | @Test 57 | public void testIsAutoIncrement() throws Exception 58 | { 59 | for (int i=1;i<=6;i++) 60 | { 61 | assertFalse( resultSetMetaData.isAutoIncrement(i) ); 62 | } 63 | } 64 | 65 | @Test 66 | public void testGetColumnLabel() throws Exception 67 | { 68 | for (int i=1;i<=6;i++) 69 | { 70 | assertEquals("c"+i,resultSetMetaData.getColumnLabel(i)); 71 | } 72 | } 73 | 74 | @Test 75 | public void testGetColumnName() throws Exception 76 | { 77 | for (int i=1;i<=6;i++) 78 | { 79 | assertEquals("c"+i,resultSetMetaData.getColumnName(i)); 80 | } 81 | 82 | } 83 | 84 | @Test 85 | public void testGetColumnType() throws Exception 86 | { 87 | 88 | int [] types = {Types.BOOLEAN, Types.NUMERIC,Types.NUMERIC, Types.VARCHAR, Types.ARRAY, Types.JAVA_OBJECT}; 89 | for (int i=1;i<=6;i++) 90 | { 91 | assertEquals(types[i-1],resultSetMetaData.getColumnType(i)); 92 | } 93 | 94 | } 95 | 96 | @Test 97 | public void testGetColumnTypeName() throws Exception 98 | { 99 | String [] types = {"boolean", "number", "number", "string", "array", "object" }; 100 | for (int i=1;i<=6;i++) 101 | { 102 | assertEquals(types[i-1],resultSetMetaData.getColumnTypeName(i)); 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/SSLConnectionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | import com.couchbase.jdbc.ConnectionParameters; 15 | 16 | import junit.framework.TestCase; 17 | import org.junit.Test; 18 | import org.junit.runner.RunWith; 19 | import org.junit.runners.JUnit4; 20 | 21 | import java.sql.ResultSet; 22 | import java.sql.Statement; 23 | import java.sql.Connection; 24 | import java.sql.DriverManager; 25 | import java.util.Properties; 26 | 27 | /** 28 | * Created by davec on 2015-09-10. 29 | */ 30 | @RunWith(JUnit4.class) 31 | public class SSLConnectionTest extends TestCase 32 | { 33 | @Test 34 | public void openConnection() throws Exception 35 | { 36 | Properties properties = new Properties(); 37 | 38 | properties.put(ConnectionParameters.SCAN_CONSISTENCY,"request_plus"); 39 | properties.put(ConnectionParameters.USER, TestUtil.getUser()); 40 | properties.put(ConnectionParameters.PASSWORD,TestUtil.getPassword()); 41 | properties.put(ConnectionParameters.ENABLE_SSL,"true"); 42 | Connection con = DriverManager.getConnection(TestUtil.getSSLUrl(), properties); 43 | 44 | assertNotNull(con); 45 | try(Statement statement = con.createStatement()) 46 | { 47 | try(ResultSet rs = statement.executeQuery("select 1")) 48 | { 49 | assertTrue(rs.next()); 50 | assertEquals(1, rs.getInt(1)); 51 | } 52 | 53 | } 54 | 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/SqlParserTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | import com.couchbase.jdbc.CouchBaseTestCase; 15 | import com.couchbase.jdbc.util.SqlParser; 16 | import org.junit.Test; 17 | import org.junit.runner.RunWith; 18 | import org.junit.runners.JUnit4; 19 | 20 | import java.sql.*; 21 | import java.util.Calendar; 22 | import java.util.Date; 23 | 24 | import static org.junit.Assert.*; 25 | import static org.junit.Assert.assertNotNull; 26 | 27 | /** 28 | * Created by davec on 2015-09-18. 29 | */ 30 | @RunWith(JUnit4.class) 31 | public class SqlParserTest extends CouchBaseTestCase 32 | { 33 | 34 | @Test 35 | public void testReplaceProcessing() throws Exception 36 | { 37 | SqlParser sqlParser = new SqlParser("select {fn acos(-0.6)} as acos "); 38 | String replaced = sqlParser.replaceProcessing("select {fn acos(-0.6)} as acos",true); 39 | assertEquals("select acos(-0.6) as acos", replaced); 40 | 41 | replaced = sqlParser.replaceProcessing("insert into escapetest (ts) values ({ts '1900-01-01 00:00:00'})", true); 42 | assertEquals("insert into escapetest (ts) values ( '1900-01-01 00:00:00')", replaced); 43 | 44 | replaced = sqlParser.replaceProcessing("insert into escapetest (d) values ({d '1900-01-01'})",true); 45 | assertEquals("insert into escapetest (d) values (DATE '1900-01-01')", replaced); 46 | 47 | replaced = sqlParser.replaceProcessing("insert into escapetest (t) values ({t '00:00:00'})",true); 48 | assertEquals("insert into escapetest (t) values ( '00:00:00')", replaced); 49 | 50 | replaced = sqlParser.replaceProcessing("select {fn version()} as version", true); 51 | assertEquals("select version() as version",replaced); 52 | 53 | replaced = sqlParser.replaceProcessing("select {fn version()} as version, {fn log({fn log(3.0)})} as log", true); 54 | assertEquals("select version() as version, ln(ln(3.0)) as log",replaced); 55 | 56 | replaced = sqlParser.replaceProcessing("select * from {oj test_statement a left outer join b on (a.i=b.i)} ", true); 57 | assertEquals("select * from test_statement a left outer join b on (a.i=b.i) ",replaced); 58 | 59 | } 60 | 61 | @Test 62 | public void testNumericFunctions() throws Exception 63 | { 64 | Statement stmt = con.createStatement(); 65 | 66 | ResultSet rs = stmt.executeQuery("select {fn abs(-2.3)} as abs "); 67 | assertTrue(rs.next()); 68 | assertEquals(2.3f, rs.getFloat(1), 0.00001); 69 | 70 | rs = stmt.executeQuery("select {fn acos(-0.6)} as acos "); 71 | assertTrue(rs.next()); 72 | assertEquals(Math.acos(-0.6), rs.getDouble(1), 0.00001); 73 | 74 | rs = stmt.executeQuery("select {fn asin(-0.6)} as asin "); 75 | assertTrue(rs.next()); 76 | assertEquals(Math.asin(-0.6), rs.getDouble(1), 0.00001); 77 | 78 | rs = stmt.executeQuery("select {fn atan(-0.6)} as atan "); 79 | assertTrue(rs.next()); 80 | assertEquals(Math.atan(-0.6), rs.getDouble(1), 0.00001); 81 | 82 | rs = stmt.executeQuery("select {fn atan2(-2.3,7)} as atan2 "); 83 | assertTrue(rs.next()); 84 | assertEquals(Math.atan2(-2.3,7), rs.getDouble(1), 0.00001); 85 | 86 | rs = stmt.executeQuery("select {fn ceiling(-2.3)} as ceiling "); 87 | assertTrue(rs.next()); 88 | assertEquals(-2, rs.getDouble(1), 0.00001); 89 | 90 | rs = stmt.executeQuery("select {fn cos(-2.3)} as cos, {fn cot(-2.3)} as cot "); 91 | assertTrue(rs.next()); 92 | assertEquals(Math.cos(-2.3), rs.getDouble(1), 0.00001); 93 | assertEquals(1/Math.tan(-2.3), rs.getDouble(2), 0.00001); 94 | 95 | rs = stmt.executeQuery("select {fn degrees({fn pi()})} as degrees "); 96 | assertTrue(rs.next()); 97 | assertEquals(180, rs.getDouble(1), 0.00001); 98 | 99 | rs = stmt.executeQuery("select {fn exp(-2.3)}, {fn floor(-2.3)}," + 100 | " {fn log(2.3)},{fn log10(2.3)},{fn mod(3,2)}"); 101 | assertTrue(rs.next()); 102 | assertEquals(Math.exp(-2.3), rs.getDouble(1), 0.00001); 103 | assertEquals(-3, rs.getDouble(2), 0.00001); 104 | assertEquals(Math.log(2.3), rs.getDouble(3), 0.00001); 105 | assertEquals(Math.log(2.3)/Math.log(10), rs.getDouble(4), 0.00001); 106 | assertEquals(1, rs.getDouble(5), 0.00001); 107 | 108 | rs = stmt.executeQuery("select {fn pi()}, {fn power(7,-2.3)}," + 109 | " {fn radians(-180)},{fn round(3.1294,2)}"); 110 | assertTrue(rs.next()); 111 | assertEquals(Math.PI, rs.getDouble(1), 0.00001); 112 | assertEquals(Math.pow(7,-2.3), rs.getDouble(2), 0.00001); 113 | assertEquals(-Math.PI, rs.getDouble(3), 0.00001); 114 | assertEquals(3.13, rs.getDouble(4), 0.00001); 115 | 116 | rs = stmt.executeQuery("select {fn sign(-2.3)}, {fn sin(-2.3)}," + 117 | " {fn sqrt(2.3)},{fn tan(-2.3)},{fn truncate(3.1294,2)}"); 118 | assertTrue(rs.next()); 119 | assertEquals(-1, rs.getInt(1)); 120 | assertEquals(Math.sin(-2.3), rs.getDouble(2), 0.00001); 121 | assertEquals(Math.sqrt(2.3), rs.getDouble(3), 0.00001); 122 | assertEquals(Math.tan(-2.3), rs.getDouble(4), 0.00001); 123 | assertEquals(3.12, rs.getDouble(5), 0.00001); 124 | } 125 | 126 | @Test 127 | public void testStringFunctions() throws SQLException 128 | { 129 | Statement stmt = con.createStatement(); 130 | ResultSet rs = stmt.executeQuery("select {fn concat('ab','cd')}" + 131 | ",{fn lcase('aBcD')},{fn left('1234',2)},{fn length('123 ')}" + 132 | ",{fn locate('bc','abc')},{fn locate('bc','abc',3)}"); 133 | assertTrue(rs.next()); 134 | assertEquals("abcd",rs.getString(1)); 135 | assertEquals("abcd",rs.getString(2)); 136 | assertEquals("12",rs.getString(3)); 137 | assertEquals(3,rs.getInt(4)); 138 | assertEquals(2,rs.getInt(5)); 139 | assertEquals(0,rs.getInt(6)); 140 | 141 | rs = stmt.executeQuery("SELECT {fn insert('abcdef',3,2,'xxxx')}" + 142 | ",{fn replace('abcdbc','bc','x')}"); 143 | assertTrue(rs.next()); 144 | assertEquals("abxxxxef",rs.getString(1)); 145 | assertEquals("axdx",rs.getString(2)); 146 | 147 | rs = stmt.executeQuery("select {fn ltrim(' ab')},{fn repeat('ab',2)}" + 148 | ",{fn right('abcde',2)},{fn rtrim('ab ')}" + 149 | ",{fn space(3)},{fn substring('abcd',2,2)}" + 150 | ",{fn ucase('aBcD')}"); 151 | assertTrue(rs.next()); 152 | assertEquals("ab",rs.getString(1)); 153 | assertEquals("abab",rs.getString(2)); 154 | assertEquals("de",rs.getString(3)); 155 | assertEquals("ab",rs.getString(4)); 156 | assertEquals(" ",rs.getString(5)); 157 | assertEquals("bc",rs.getString(6)); 158 | assertEquals("ABCD",rs.getString(7)); 159 | } 160 | 161 | @Test 162 | public void testDateFunctions() throws SQLException 163 | { 164 | Calendar expected = Calendar.getInstance(), actual=Calendar.getInstance(); 165 | 166 | Statement stmt = con.createStatement(); 167 | ResultSet rs = stmt.executeQuery("select {fn curdate()} as curdate,{fn curtime()} as curtime" + 168 | ",{fn dayname('2015-02-22 12:00:00')} as dayname , {fn dayofmonth('2015-02-22 12:00:00')} as dayofmonth" + 169 | ",{fn dayofweek({ts '2015-02-22 12:00:00'})} as dayofweek,{fn dayofyear('2015-02-22 12:00:00')} as dayofyear" + 170 | ",{fn hour('2015-02-22 12:00:00')} as hour,{fn minute('2015-02-22 12:00:00')} as minute" + 171 | ",{fn month('2015-02-22 12:00:00')} as month" + 172 | ",{fn monthname('2015-02-22 12:00:00')} as monthname,{fn quarter('2015-02-22 12:00:00')} as quarter" + 173 | ",{fn second('2015-02-22 12:00:00')} as second,{fn week('2015-02-22 12:00:00')} as week" + 174 | ",{fn year('2015-02-22 12:00:00')} as year"); 175 | assertTrue(rs.next()); 176 | 177 | Date date = rs.getDate("curdate"); 178 | actual.setTime(date); 179 | 180 | assertEquals(expected.get(Calendar.DAY_OF_MONTH),actual.get(Calendar.DAY_OF_MONTH)); 181 | assertEquals(expected.get(Calendar.YEAR), actual.get(Calendar.YEAR)); 182 | assertEquals(expected.get(Calendar.MONTH),actual.get(Calendar.MONTH)); 183 | 184 | Time time = rs.getTime("curtime"); 185 | assertNotNull(time); 186 | 187 | String dayName = rs.getString("dayname"); 188 | assertEquals("SUN", dayName); 189 | 190 | int dayOfMonth = rs.getInt("dayofmonth"); 191 | assertEquals(22, dayOfMonth); 192 | 193 | int dayOfWeek = rs.getInt("dayofweek"); 194 | assertEquals(7,dayOfWeek); 195 | 196 | int dayOfYear = rs.getInt("dayofyear"); 197 | assertEquals(53, dayOfYear); 198 | 199 | int hour = rs.getInt("hour"); 200 | assertEquals(12,hour); 201 | 202 | int minute = rs.getInt("minute"); 203 | assertEquals(0,minute); 204 | 205 | int month = rs.getInt("month"); 206 | assertEquals(2,month); 207 | 208 | String monthName = rs.getString("monthname"); 209 | assertEquals("FEB", monthName); 210 | 211 | int quarter = rs.getInt("quarter"); 212 | assertEquals(1,quarter); 213 | 214 | int second = rs.getInt("second"); 215 | assertEquals(0,second); 216 | 217 | int week = rs.getInt("week"); 218 | assertEquals(8, week); 219 | 220 | int year = rs.getInt("year"); 221 | assertEquals(2015,year); 222 | 223 | rs = stmt.executeQuery("select {fn timestampadd(SQL_TSI_SECOND,30, '2015-02-22 12:00:00'"); 224 | assertTrue(rs.next()); 225 | Timestamp ts= rs.getTimestamp(1); 226 | 227 | actual.setTime(ts); 228 | assertEquals(30, actual.get(Calendar.SECOND)); 229 | 230 | rs = stmt.executeQuery("select {fn timestampadd(SQL_TSI_MINUTE,25, '2015-02-22 12:00:00'"); 231 | assertTrue(rs.next()); 232 | ts= rs.getTimestamp(1); 233 | 234 | actual.setTime(ts); 235 | assertEquals(25, actual.get(Calendar.MINUTE)); 236 | 237 | rs = stmt.executeQuery("select {fn timestampadd(SQL_TSI_HOUR,1, '2015-02-22 12:00:00'"); 238 | assertTrue(rs.next()); 239 | ts= rs.getTimestamp(1); 240 | 241 | actual.setTime(ts); 242 | assertEquals(13, actual.get(Calendar.HOUR_OF_DAY)); 243 | 244 | rs = stmt.executeQuery("select {fn timestampadd(SQL_TSI_DAY,1, '2015-02-22 12:00:00'"); 245 | assertTrue(rs.next()); 246 | ts= rs.getTimestamp(1); 247 | 248 | actual.setTime(ts); 249 | assertEquals(23, actual.get(Calendar.DAY_OF_MONTH)); 250 | 251 | rs = stmt.executeQuery("select {fn timestampadd(SQL_TSI_WEEK,1, '2015-02-22 12:00:00'"); 252 | assertTrue(rs.next()); 253 | ts= rs.getTimestamp(1); 254 | 255 | actual.setTime(ts); 256 | assertEquals(10, actual.get(Calendar.WEEK_OF_YEAR)); 257 | 258 | rs = stmt.executeQuery("select {fn timestampadd(SQL_TSI_MONTH,1, '2015-02-22 12:00:00'"); 259 | assertTrue(rs.next()); 260 | ts= rs.getTimestamp(1); 261 | 262 | actual.setTime(ts); 263 | assertEquals(2, actual.get(Calendar.MONTH)); 264 | 265 | 266 | // second 267 | rs = stmt.executeQuery("select {fn timestampdiff(SQL_TSI_SECOND,{fn now()},{fn timestampadd(SQL_TSI_SECOND,3,{fn now()})})} "); 268 | assertTrue(rs.next()); 269 | assertEquals(3,rs.getInt(1)); 270 | // MINUTE 271 | rs = stmt.executeQuery("select {fn timestampdiff(SQL_TSI_MINUTE,{fn now()},{fn timestampadd(SQL_TSI_MINUTE,3,{fn now()})})} "); 272 | assertTrue(rs.next()); 273 | assertEquals(3,rs.getInt(1)); 274 | // HOUR 275 | rs = stmt.executeQuery("select {fn timestampdiff(SQL_TSI_HOUR,{fn now()},{fn timestampadd(SQL_TSI_HOUR,3,{fn now()})})} "); 276 | assertTrue(rs.next()); 277 | assertEquals(3,rs.getInt(1)); 278 | // day 279 | rs = stmt.executeQuery("select {fn timestampdiff(SQL_TSI_DAY,{fn now()},{fn timestampadd(SQL_TSI_DAY,-3,{fn now()})})} "); 280 | assertTrue(rs.next()); 281 | assertEquals(-3,rs.getInt(1)); 282 | // WEEK => extract week from interval is not supported by backend 283 | rs = stmt.executeQuery("select {fn timestampdiff(SQL_TSI_WEEK,{fn now()},{fn timestampadd(SQL_TSI_WEEK,3,{fn now()})})} "); 284 | assertTrue(rs.next()); 285 | assertEquals(3,rs.getInt(1)); 286 | // YEAR 287 | rs = stmt.executeQuery("select {fn timestampdiff(SQL_TSI_YEAR,{fn now()},{fn timestampadd(SQL_TSI_YEAR,3,{fn now()})})} "); 288 | assertTrue(rs.next()); 289 | assertEquals(3,rs.getInt(1)); 290 | 291 | // QUARTER => backend assume there are 1 quarter even in 270 days... 292 | rs = stmt.executeQuery("select {fn timestampdiff(SQL_TSI_QUARTER,{fn now()},{fn timestampadd(SQL_TSI_QUARTER,3,{fn now()})})} "); 293 | assertTrue(rs.next()); 294 | assertEquals(3,rs.getInt(1)); 295 | 296 | // MONTH => backend assume there are 0 month in an interval of 92 days... 297 | rs = stmt.executeQuery("select {fn timestampdiff(SQL_TSI_MONTH,{fn now()},{fn timestampadd(SQL_TSI_MONTH,3,{fn now()})})} "); 298 | assertTrue(rs.next()); 299 | assertEquals(3,rs.getInt(1)); 300 | } 301 | 302 | } -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/StatementTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | import org.junit.Test; 15 | import org.junit.After; 16 | import org.junit.runner.RunWith; 17 | import org.junit.runners.JUnit4; 18 | 19 | import java.sql.ResultSet; 20 | import java.sql.Statement; 21 | import java.util.Arrays; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | /** 26 | * Created by davec on 2015-02-26. 27 | */ 28 | @RunWith(JUnit4.class) 29 | public class StatementTest extends CouchBaseTestCase 30 | { 31 | @After 32 | public void cleanupBucket() throws Exception 33 | { 34 | JDBCTestUtils.deleteDataFromBucket("default"); 35 | } 36 | 37 | @Test 38 | public void createStatement() throws Exception 39 | { 40 | Statement statement = con.createStatement(); 41 | assertNotNull(statement); 42 | 43 | } 44 | @Test 45 | public void emptyResult() throws Exception 46 | { 47 | Statement statement = con.createStatement(); 48 | assertNotNull(statement); 49 | 50 | ResultSet rs = statement.executeQuery("select * from default"); 51 | assertFalse(rs.next()); 52 | 53 | } 54 | @Test 55 | public void simpleSelect() throws Exception 56 | { 57 | Statement statement = con.createStatement(); 58 | assertNotNull(statement); 59 | 60 | ResultSet rs = statement.executeQuery("select 1"); 61 | 62 | assertTrue(rs.next()); 63 | assertEquals(1,rs.getInt(1)); 64 | 65 | } 66 | 67 | @Test 68 | public void simpleInsert() throws Exception 69 | { 70 | assertNotNull(con); 71 | Statement statement = con.createStatement(); 72 | assertNotNull(statement); 73 | 74 | for (int i = 0; i++< 100;) 75 | { 76 | 77 | int inserted = statement.executeUpdate("INSERT INTO default (KEY, VALUE) VALUES ( 'K" + i + "', { 'id': " + i + ", 'val': " + i + " })"); 78 | assertEquals(1, inserted); 79 | } 80 | Thread.sleep(5000); 81 | 82 | ResultSet resultSet = statement.executeQuery("select count(1) as test_count from default"); 83 | assertTrue(resultSet.next()); 84 | assertEquals(100,resultSet.getInt("test_count")); 85 | 86 | resultSet = statement.executeQuery("select val from default order by val"); 87 | for (int i=0; resultSet.next(); i++) 88 | { 89 | assertEquals(i+1, resultSet.getInt(1)); 90 | } 91 | 92 | resultSet = statement.executeQuery("select raw val from default order by val"); 93 | for (int i=0; resultSet.next(); i++) 94 | { 95 | assertTrue(resultSet.getInt(1)>0); 96 | } 97 | 98 | // A known problem, assigned issue #16. 99 | boolean hasResultSet = statement.execute("update default set val=0 returning val"); 100 | if ( hasResultSet ) 101 | { 102 | resultSet = statement.getResultSet(); 103 | for (int i=0; resultSet.next(); i++) 104 | { 105 | assertEquals(0, resultSet.getInt(1)); 106 | } 107 | 108 | } 109 | 110 | statement.executeUpdate("delete from default"); 111 | Thread.sleep(5000); 112 | 113 | resultSet = statement.executeQuery("select count(1) as count from default"); 114 | assertTrue(resultSet.next()); 115 | assertEquals(0, resultSet.getInt(1)); 116 | 117 | } 118 | @Test 119 | public void getAllTypes() throws Exception 120 | { 121 | Integer []foo = new Integer[]{1, 2, 3, 5, 8}; 122 | Object array = Arrays.asList(foo); 123 | 124 | 125 | Map object = new HashMap(); 126 | object.put("a1","Object"); 127 | 128 | Statement statement = con.createStatement(); 129 | assertNotNull(statement); 130 | 131 | ResultSet resultSet = statement.executeQuery("SELECT true as c1, 1 as c2, 3.14 as c3, 'Hello World!' as c4, [1,2,3,5,8] as c5, { 'a1': 'Object' } as c6"); 132 | 133 | assertTrue(resultSet.next()); 134 | 135 | assertTrue(resultSet.getBoolean(1)); 136 | assertTrue(resultSet.getBoolean("c1")); 137 | 138 | assertEquals(1,resultSet.getInt(2)); 139 | assertEquals(1,resultSet.getInt("c2")); 140 | 141 | 142 | assertEquals(3.14F, resultSet.getFloat(3), 0.0f); 143 | assertEquals(3.14F, resultSet.getFloat("c3"), 0.0f); 144 | 145 | assertEquals("Hello World!",resultSet.getString(4)); 146 | assertEquals("Hello World!",resultSet.getString("c4")); 147 | 148 | assertTrue(Arrays.equals(foo, (Object[]) resultSet.getArray(5).getArray())); 149 | assertTrue(Arrays.equals(foo, (Object[]) resultSet.getArray("c5").getArray())); 150 | 151 | Object foo2 = resultSet.getObject(6); 152 | assertEquals(object, resultSet.getObject(6)); 153 | assertEquals(object, resultSet.getObject("c6")); 154 | 155 | 156 | } 157 | 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/Test16017.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | import org.junit.runners.JUnit4; 17 | 18 | import java.sql.*; 19 | 20 | /** 21 | * Created by davec on 2015-08-11. 22 | */ 23 | @RunWith(JUnit4.class) 24 | public class Test16017 extends CouchBaseTestCase 25 | { 26 | @Test 27 | public void testValues() throws Exception 28 | { 29 | Statement statement = con.createStatement(); 30 | assertNotNull(statement); 31 | statement.executeUpdate("insert into default (key,value) values ('1',{\n" + 32 | "                    \"bool_field1\": false,\n" + 33 | "                    \"char_field1\": \"N\",\n" + 34 | "                    \"datetime_field1\": \"2009-05-19 00:00:00\",\n" + 35 | "                    \"decimal_field1\": 4763,\n" + 36 | "                    \"int_field1\": 1790,\n" + 37 | "                    \"primary_key_id\": \"1\",\n" + 38 | "                    \"varchar_field1\": \"EpTpDkFseV\"\n" + 39 | "                })"); 40 | ResultSet rs = statement.executeQuery("select * from default"); 41 | String columnName; 42 | Object columnValue; 43 | while (rs.next()) { 44 | int total_rows = rs.getMetaData().getColumnCount(); 45 | for (int i = 0; i < total_rows; i++) { 46 | columnName = rs.getMetaData().getColumnLabel(i + 1).toLowerCase(); 47 | columnValue = rs.getObject(i + 1); 48 | 49 | if (columnValue == null){ 50 | columnValue = "null"; 51 | } 52 | System.out.println(columnName +':' + columnValue); 53 | 54 | } 55 | 56 | } 57 | 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/TestBoon.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | import junit.framework.TestCase; 15 | import org.boon.core.value.ValueList; 16 | import org.boon.core.value.ValueMap; 17 | import org.boon.json.JsonSerializer; 18 | import org.boon.json.JsonSerializerFactory; 19 | import org.boon.json.JsonSlurper; 20 | import org.junit.Test; 21 | 22 | import java.io.File; 23 | import java.net.URL; 24 | 25 | /** 26 | * Created by davec on 2015-05-18. 27 | */ 28 | public class TestBoon extends TestCase 29 | { 30 | @Test 31 | public void testFlatten1() 32 | { 33 | ClassLoader classLoader = getClass().getClassLoader(); 34 | URL url = classLoader.getResource("example1.json"); 35 | String path = url.getFile(); 36 | File file = new File(path); 37 | 38 | ValueMap jsonObject = (ValueMap)new JsonSlurper().parse(file); 39 | 40 | ValueList results = (ValueList)jsonObject.get("results"); 41 | 42 | JsonSerializer jsonSerializer = new JsonSerializerFactory().outputType().create(); 43 | 44 | 45 | System.out.println(jsonSerializer.serialize(jsonObject)); 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/TestResultAnalysis.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc; 2 | 3 | import java.util.LinkedList; 4 | 5 | public class TestResultAnalysis { 6 | public int totalQueries =0; 7 | public int totalPassResults = 0; 8 | public double passPercentage = 0; 9 | public LinkedList failureResults = new LinkedList(); 10 | 11 | public TestResultAnalysis(){ 12 | 13 | } 14 | public void addFailureResult(String data){ 15 | this.failureResults.add(data); 16 | } 17 | 18 | public boolean isTestPassing(){ 19 | if(this.totalQueries == this.totalPassResults){ 20 | return true; 21 | } 22 | return false; 23 | } 24 | 25 | public void publishResult(){ 26 | System.out.println("\n ++++++++++++++++++++++ START RESULT +++++++++++++++++++++++"); 27 | this.publishFailureResult(); 28 | System.out.printf("\n total queries run %d", totalQueries); 29 | System.out.printf("\n Pass %d", totalPassResults); 30 | System.out.println("\n ++++++++++++++++++++++ END RESULT +++++++++++++++++++++++"); 31 | } 32 | 33 | public void publishFailureResult(){ 34 | for(String data:failureResults){ 35 | System.out.println("_________________________________________________________________"); 36 | System.out.println(data); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/TestUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | /** 15 | * Created by davec on 2015-07-22. 16 | */ 17 | public class TestUser 18 | { 19 | public String first; 20 | public String last; 21 | public int age; 22 | public boolean active; 23 | 24 | @Override 25 | public boolean equals(Object o) 26 | { 27 | if (this == o) return true; 28 | if (o == null || getClass() != o.getClass()) return false; 29 | 30 | TestUser testUser = (TestUser) o; 31 | 32 | if (age != testUser.age) return false; 33 | if (active != testUser.active) return false; 34 | if (first != null ? !first.equals(testUser.first) : testUser.first != null) return false; 35 | return !(last != null ? !last.equals(testUser.last) : testUser.last != null); 36 | 37 | } 38 | 39 | @Override 40 | public int hashCode() 41 | { 42 | int result = first != null ? first.hashCode() : 0; 43 | result = 31 * result + (last != null ? last.hashCode() : 0); 44 | result = 31 * result + age; 45 | result = 31 * result + (active ? 1 : 0); 46 | return result; 47 | } 48 | 49 | public TestUser(String first, String last, int age, boolean active) 50 | { 51 | this.first = first; 52 | this.last = last; 53 | this.age = age; 54 | this.active= active; 55 | 56 | } 57 | public String getFirst() 58 | { 59 | return first; 60 | } 61 | 62 | public void setFirst(String first) 63 | { 64 | this.first = first; 65 | } 66 | 67 | public String getLast() 68 | { 69 | return last; 70 | } 71 | 72 | public void setLast(String last) 73 | { 74 | this.last = last; 75 | } 76 | 77 | public int getAge() 78 | { 79 | return age; 80 | } 81 | 82 | public void setAge(int age) 83 | { 84 | this.age = age; 85 | } 86 | 87 | public boolean isActive() 88 | { 89 | return active; 90 | } 91 | 92 | public void setActive(boolean active) 93 | { 94 | this.active = active; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/TestUtil.java: -------------------------------------------------------------------------------- 1 | package com.couchbase.jdbc; 2 | 3 | import com.couchbase.jdbc.util.Credentials; 4 | 5 | import java.io.InputStream; 6 | import java.util.*; 7 | 8 | /** 9 | * Created by davec on 2015-02-22. 10 | */ 11 | public class TestUtil 12 | { 13 | public static Properties environment=new Properties(); 14 | public static Properties rqg_environment=new Properties(); 15 | public static ClusterInfo clusterInfo; 16 | public static boolean isRQGEnabled = false; 17 | public static boolean rebalanceInAllowed = false; 18 | 19 | static { 20 | resetEnvironmentProperties("environment.properties"); 21 | } 22 | 23 | public static void resetEnvironmentProperties(String propertPath){ 24 | environment=new Properties(); 25 | InputStream stream = null; 26 | if (propertPath == null){ 27 | propertPath = "environment.properties"; 28 | } 29 | if(propertPath != null){ 30 | stream = ClassLoader.getSystemClassLoader().getResourceAsStream(propertPath); 31 | } 32 | try 33 | { 34 | if( stream != null ){ 35 | environment.load( stream ); 36 | } 37 | } 38 | catch (Exception ex ) 39 | { 40 | ex.printStackTrace(System.err); 41 | } 42 | } 43 | 44 | public static String getRQGConfig() { return environment.getProperty("couchbasedb.test.rqg_config", "do_not_use");} 45 | 46 | public static String getConfig() { return environment.getProperty("couchbasedb.test.config", "config.json");} 47 | 48 | public static String getURL() { return environment.getProperty("couchbasedb.test.url", "jdbc:couchbase://127.0.0.1:8093");} 49 | 50 | public static String getSSLUrl() { return environment.getProperty("couchbasedb.test.sslurl", "jdbc:couchbase://127.0.0.1:18093");} 51 | 52 | public static String getBadURL() {return environment.getProperty("couchbasedb.test.url", "jdbc:couchbase://127.0.0.1:8093");} 53 | 54 | public static String getRQGCofigJSON(){return rqg_environment.getProperty("couchbasedb.test.rqg.config_json", "rqg_config.json");} 55 | 56 | public static String getRQGAggregateDir(){return rqg_environment.getProperty("couchbasedb.test.rqg.dir.aggregate", "/tmp/rqg/aggregate");} 57 | 58 | public static String getRQGJOINSDir(){return rqg_environment.getProperty("couchbasedb.test.rqg.dir.joins", "/tmp/rqg/joins");} 59 | 60 | public static String getRQGFieldsDir(){return rqg_environment.getProperty("couchbasedb.test.rqg.dir.fields", "/tmp/rqg/fields");} 61 | 62 | public static void loadRQGProperties(){ 63 | isRQGEnabled = true; 64 | String propertPath = getRQGConfig(); 65 | rqg_environment=new Properties(); 66 | InputStream stream = null; 67 | if (propertPath == null){ 68 | propertPath = "rqg_config.properties"; 69 | } 70 | if(propertPath != null){ 71 | stream = ClassLoader.getSystemClassLoader().getResourceAsStream(propertPath); 72 | } 73 | try 74 | { 75 | if( stream != null ){ 76 | rqg_environment.load( stream ); 77 | System.out.print(rqg_environment.toString()); 78 | } 79 | } 80 | catch (Exception ex ) 81 | { 82 | ex.printStackTrace(System.err); 83 | } 84 | } 85 | 86 | public static void rebalanceInAdditionalNodes(){ 87 | try{ 88 | ClusterSetupUtils.rebalanceIn(TestUtil.clusterInfo); 89 | }catch(Exception e){ 90 | e.printStackTrace(); 91 | } 92 | } 93 | 94 | public static void initializeCluster(boolean createPrimaryIndex){ 95 | try{ 96 | String clusterConfigPath = TestUtil.getConfig(); 97 | if(isRQGEnabled){ 98 | clusterConfigPath = TestUtil.getRQGCofigJSON(); 99 | } 100 | TestUtil.clusterInfo = ClusterSetupUtils.readConfigFile(clusterConfigPath); 101 | ClusterSetupUtils.initializeCluster(TestUtil.clusterInfo); 102 | System.out.println(TestUtil.clusterInfo.toString()); 103 | Thread.sleep(5000); 104 | if(! isRQGEnabled){ 105 | if(rebalanceInAllowed){ 106 | rebalanceInAdditionalNodes(); 107 | } 108 | ClusterSetupUtils.createBuckets(TestUtil.clusterInfo); 109 | Thread.sleep(10000); 110 | JDBCTestUtils.setConnection(TestUtil.getURL()); 111 | if(createPrimaryIndex){ 112 | JDBCTestUtils.createPrimaryIndexes(TestUtil.clusterInfo.bucketInformation.keySet()); 113 | } 114 | }else{ 115 | JDBCTestUtils.setConnection(TestUtil.getURL()); 116 | } 117 | 118 | 119 | }catch(Exception e){ 120 | e.printStackTrace(); 121 | } 122 | } 123 | 124 | public static void destroyCluster(){ 125 | try{ 126 | ClusterSetupUtils.deleteBuckets(TestUtil.clusterInfo); 127 | ClusterSetupUtils.rebalanceOut(TestUtil.clusterInfo); 128 | }catch(Exception e){ 129 | e.printStackTrace(); 130 | } 131 | } 132 | 133 | 134 | 135 | public static String getServer() { 136 | return System.getProperty("couchbasedb.test.server", "127.0.0.1:8091"); 137 | } 138 | 139 | public static String getPort() { 140 | return environment.getProperty("couchbasedb.test.port", "8093"); 141 | } 142 | 143 | public static String getDatabase() { 144 | return environment.getProperty("couchbasedb.test.db", "test"); 145 | } 146 | 147 | public static Properties getProperties() { 148 | 149 | Properties props = new Properties(); 150 | 151 | props.setProperty("user", getUser()); 152 | props.setProperty("password", getPassword()); 153 | 154 | return props; 155 | } 156 | 157 | public static void setRebalancePermission(){ 158 | rebalanceInAllowed = true; 159 | } 160 | public static String getUser() { 161 | return environment.getProperty("couchbasedb.test.user", "Administrator"); 162 | } 163 | 164 | public static String getPassword() { 165 | return environment.getProperty("couchbasedb.test.password", "password"); 166 | } 167 | 168 | public static Credentials getCredentials() { 169 | Credentials credentials = new Credentials(); 170 | String auth = environment.getProperty("couchbasedb.test.auth",""); 171 | String [] parts = auth.split(","); 172 | for (String part : parts ) 173 | { 174 | String []creds = part.split(":"); 175 | credentials.add(creds[0],creds[1]); 176 | } 177 | return credentials; 178 | 179 | } 180 | static String []resultSetGetters = { 181 | "getArray","getAsciiStream", 182 | "getString","getBigDecimal", 183 | "getBinaryStream", 184 | "getBoolean", "getByte", 185 | "getBytes", "getCharacterStream", 186 | "getDate", 187 | "getDouble","getInt", 188 | "getLong", "getObject", 189 | "getShort", 190 | "getString", "getTime", 191 | "getTimestamp", "getUnicodeStream", 192 | "getURL" 193 | }; 194 | static String []notImplemented = { 195 | "getClob","getBlob" 196 | 197 | } ; 198 | public static List getSuppportedResultSetGetters() 199 | { 200 | return Arrays.asList(resultSetGetters); 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /src/test/java/com/couchbase/jdbc/WrapperTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * // Copyright (c) 2015 Couchbase, Inc. 3 | * // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | * // except in compliance with the License. You may obtain a copy of the License at 5 | * // http://www.apache.org/licenses/LICENSE-2.0 6 | * // Unless required by applicable law or agreed to in writing, software distributed under the 7 | * // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | * // either express or implied. See the License for the specific language governing permissions 9 | * // and limitations under the License. 10 | */ 11 | 12 | package com.couchbase.jdbc; 13 | 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | import org.junit.runner.RunWith; 17 | import org.junit.runners.JUnit4; 18 | 19 | import com.couchbase.jdbc.CBConnection; 20 | import com.couchbase.jdbc.CBStatement; 21 | 22 | import java.sql.Connection; 23 | import java.sql.SQLException; 24 | import java.sql.Statement; 25 | 26 | /** 27 | * Created by davec on 2015-09-16. 28 | */ 29 | @RunWith(JUnit4.class) 30 | public class WrapperTest extends CouchBaseTestCase 31 | { 32 | Statement statement; 33 | /** 34 | * This interface is private, and so cannot be supported by any wrapper 35 | * 36 | */ 37 | private interface PrivateInterface { 38 | } 39 | 40 | 41 | @Before 42 | public void setup() throws Exception 43 | { 44 | statement=con.createStatement(); 45 | } 46 | 47 | 48 | @Test 49 | public void testConnectionIsWrapperForPrivate() throws SQLException { 50 | assertFalse(con.isWrapperFor(PrivateInterface.class)); 51 | } 52 | 53 | @Test 54 | public void testConnectionIsWrapperForConnection() throws SQLException { 55 | assertTrue(con.isWrapperFor(Connection.class)); 56 | } 57 | 58 | @Test 59 | public void testConnectionIsWrapperForCBConnection() throws SQLException { 60 | assertTrue(con.isWrapperFor(CBConnection.class)); 61 | } 62 | 63 | @Test 64 | public void testConnectionUnwrapPrivate() throws SQLException { 65 | try { 66 | con.unwrap(PrivateInterface.class); 67 | fail("unwrap of non-wrapped interface should fail"); 68 | } 69 | catch (SQLException e) { 70 | // Ok 71 | } 72 | } 73 | 74 | @Test 75 | public void testConnectionUnwrapConnection() throws SQLException { 76 | Object v = con.unwrap(Connection.class); 77 | assertNotNull(v); 78 | assertTrue(v instanceof Connection); 79 | } 80 | 81 | @Test 82 | public void testConnectionUnwrapCBConnection() throws SQLException { 83 | Object v = con.unwrap(CBConnection.class); 84 | assertNotNull(v); 85 | assertTrue(v instanceof CBConnection); 86 | } 87 | 88 | @Test 89 | public void testStatementIsWrapperForPrivate() throws SQLException { 90 | assertFalse(statement.isWrapperFor(PrivateInterface.class)); 91 | } 92 | 93 | @Test 94 | public void testStatementIsWrapperForStatement() throws SQLException { 95 | assertTrue(statement.isWrapperFor(Statement.class)); 96 | } 97 | 98 | @Test 99 | public void testStatementIsWrapperForCBStatement() throws SQLException { 100 | assertTrue(statement.isWrapperFor(CBStatement.class)); 101 | } 102 | 103 | @Test 104 | public void testStatementUnwrapPrivate() throws SQLException { 105 | try { 106 | statement.unwrap(PrivateInterface.class); 107 | fail("unwrap of non-wrapped interface should fail"); 108 | } 109 | catch (SQLException e) { 110 | // Ok 111 | } 112 | } 113 | 114 | @Test 115 | public void testStatementUnwrapStatement() throws SQLException { 116 | Object v = statement.unwrap(Statement.class); 117 | assertNotNull(v); 118 | assertTrue(v instanceof Statement); 119 | } 120 | 121 | @Test 122 | public void testStatementUnwrapCBStatement() throws SQLException { 123 | Object v = statement.unwrap(CBStatement.class); 124 | assertNotNull(v); 125 | assertTrue(v instanceof CBStatement); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/test/resources/config.json: -------------------------------------------------------------------------------- 1 | {"node_info":[{"isMasterNode":"true","membasePassword":"password","n1qlPort":9499,"membaseUserId":"Administrator","port":9000,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102},{"isMasterNode":"false","membasePassword":"password","n1qlPort":9498,"membaseUserId":"Administrator","port":9001,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102}], 2 | "bucket_info":[{"proxyPort":21000,"name":"default","replicaNumber":0,"ramQuotaMB":1000,"authType":"none"}],"global":{"membasePassword":"password","n1qlPort":9949,"membaseUserId":"Administrator","port":8091,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102}} -------------------------------------------------------------------------------- /src/test/resources/dev_config.json: -------------------------------------------------------------------------------- 1 | {"node_info":[{"isMasterNode":"true","membasePassword":"password","n1qlPort":9499,"membaseUserId":"Administrator","port":9000,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102},{"isMasterNode":"false","membasePassword":"password","n1qlPort":9498,"membaseUserId":"Administrator","port":9001,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102}], 2 | "bucket_info":[{"proxyPort":21000,"name":"default","replicaNumber":0,"ramQuotaMB":1000,"authType":"none"}],"global":{"membasePassword":"password","n1qlPort":9949,"membaseUserId":"Administrator","port":8091,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102}} -------------------------------------------------------------------------------- /src/test/resources/dev_environment.properties: -------------------------------------------------------------------------------- 1 | # 2 | # // Copyright (c) 2015 Couchbase, Inc. 3 | # // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | # // except in compliance with the License. You may obtain a copy of the License at 5 | # // http://www.apache.org/licenses/LICENSE-2.0 6 | # // Unless required by applicable law or agreed to in writing, software distributed under the 7 | # // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | # // either express or implied. See the License for the specific language governing permissions 9 | # // and limitations under the License. 10 | # 11 | couchbasedb.test.url=jdbc:couchbase://127.0.0.1:9499 12 | couchbasedb.test.sslurl=jdbc:couchbase://127.0.0.1:19499 13 | couchbasedb.test.url=jdbc:couchbase://127.0.0.1:9499 14 | couchbasedb.test.auth=product:Pr0dct,purchases:Prch3s3s 15 | couchbasedb.test.config=dev_config.json 16 | couchbasedb.test.rqg_config=rqg_config.properties -------------------------------------------------------------------------------- /src/test/resources/dev_rqg_config.json: -------------------------------------------------------------------------------- 1 | {"node_info":[{"isMasterNode":"true","membasePassword":"password","n1qlPort":9499,"membaseUserId":"Administrator","port":9000,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102},{"isMasterNode":"false","membasePassword":"password","n1qlPort":9500,"membaseUserId":"Administrator","port":9001,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102}], 2 | "bucket_info":[],"global":{"membasePassword":"password","n1qlPort":9499,"membaseUserId":"Administrator","port":8091,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102}} -------------------------------------------------------------------------------- /src/test/resources/environment.properties: -------------------------------------------------------------------------------- 1 | # 2 | # // Copyright (c) 2015 Couchbase, Inc. 3 | # // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | # // except in compliance with the License. You may obtain a copy of the License at 5 | # // http://www.apache.org/licenses/LICENSE-2.0 6 | # // Unless required by applicable law or agreed to in writing, software distributed under the 7 | # // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | # // either express or implied. See the License for the specific language governing permissions 9 | # // and limitations under the License. 10 | # 11 | 12 | couchbasedb.test.url=jdbc:couchbase://localhost:8093 13 | couchbasedb.test.sslurl=jdbc:couchbase://localhost:18093 14 | couchbasedb.test.auth=product:Pr0dct,purchases:Prch3s3s 15 | couchbasedb.test.config=config.json 16 | couchbasedb.test.rqg_config=rqg_config.properties 17 | -------------------------------------------------------------------------------- /src/test/resources/example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestID": "bf1a57e9-e240-4c1d-91af-ead85c5488fb", 3 | "signature": { 4 | "emp": "json" 5 | }, 6 | "results": [ 7 | { 8 | "emp": { 9 | "ENO": 1234, 10 | "address": { 11 | "city": "Raleigh", 12 | "state": "NC", 13 | "street": "123 Doe Lane" 14 | }, 15 | "emails": [ 16 | "fred@firm.co", 17 | "fred.regis@gmail.com" 18 | ], 19 | "name": { 20 | "fname": "Fred", 21 | "lname": "Regis" 22 | } 23 | } 24 | } 25 | ], 26 | "status": "success", 27 | "metrics": { 28 | "elapsedTime": "17.521371ms", 29 | "executionTime": "17.402437ms", 30 | "resultCount": 1, 31 | "resultSize": 486 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/resources/local_config.json: -------------------------------------------------------------------------------- 1 | {"node_info":[{"isMasterNode":"true","membasePassword":"password","n1qlPort":8093,"membaseUserId":"Administrator","port":8091,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102}], 2 | "bucket_info":[{"proxyPort":21000,"name":"default","replicaNumber":0,"ramQuotaMB":1000,"authType":"none"}],"global":{"membasePassword":"password","n1qlPort":8093,"membaseUserId":"Administrator","port":8091,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102}} -------------------------------------------------------------------------------- /src/test/resources/local_environment.properties: -------------------------------------------------------------------------------- 1 | # 2 | # // Copyright (c) 2015 Couchbase, Inc. 3 | # // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | # // except in compliance with the License. You may obtain a copy of the License at 5 | # // http://www.apache.org/licenses/LICENSE-2.0 6 | # // Unless required by applicable law or agreed to in writing, software distributed under the 7 | # // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 8 | # // either express or implied. See the License for the specific language governing permissions 9 | # // and limitations under the License. 10 | # 11 | couchbasedb.test.url=jdbc:couchbase://127.0.0.1:8093 12 | couchbasedb.test.sslurl=jdbc:couchbase://127.0.0.1:18093 13 | couchbasedb.test.url=jdbc:couchbase://127.0.0.1:8093 14 | couchbasedb.test.auth=product:Pr0dct,purchases:Prch3s3s 15 | couchbasedb.test.config=local_config.json 16 | couchbasedb.test.rqg_config=rqg_config.properties -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 16 | 17 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/test/resources/rqg_config.json: -------------------------------------------------------------------------------- 1 | {"node_info":[{"isMasterNode":"true","membasePassword":"password","n1qlPort":8093,"membaseUserId":"Administrator","port":8091,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102}], 2 | "bucket_info":[],"global":{"membasePassword":"password","n1qlPort":8093,"membaseUserId":"Administrator","port":8091,"machineUserId":"root","ip":"localhost","services":["kv","n1ql","index"],"machinePassword":"couchbase","indexPort":9102}} -------------------------------------------------------------------------------- /src/test/resources/rqg_config.properties: -------------------------------------------------------------------------------- 1 | couchbasedb.test.rqg.config_json=rqg_config.json 2 | couchbasedb.test.rqg.dir.aggregate=/tmp/aggregate 3 | couchbasedb.test.rqg.dir.joins=/tmp/joins 4 | couchbasedb.test.rqg.dir.fields=/tmp/fields --------------------------------------------------------------------------------