├── .gitignore ├── README.md ├── build.gradle ├── driver └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── intellij │ │ │ ├── ClusterConnection.java │ │ │ ├── ContainerUtil.java │ │ │ ├── CouchbaseBaseStatement.java │ │ │ ├── CouchbaseClientURI.java │ │ │ ├── CouchbaseConnection.java │ │ │ ├── CouchbaseDocumentsSampler.java │ │ │ ├── CouchbaseError.java │ │ │ ├── CouchbaseJdbcDriver.java │ │ │ ├── CouchbaseMetaData.java │ │ │ ├── CouchbaseNoParamsPrepared.java │ │ │ ├── CouchbasePreparedStatement.java │ │ │ ├── CouchbaseSqlLikePattern.java │ │ │ ├── CouchbaseStatement.java │ │ │ ├── DateUtil.java │ │ │ ├── DriverPropertyInfoHelper.java │ │ │ ├── EscapingUtil.java │ │ │ ├── ObjectUtil.java │ │ │ ├── SslKeyStoreConfig.java │ │ │ ├── StringUtil.java │ │ │ ├── executor │ │ │ ├── BucketSettingsDto.java │ │ │ ├── CouchbaseCustomStatementExecutor.java │ │ │ ├── CreateBucketExecutor.java │ │ │ ├── CustomDdlExecutor.java │ │ │ ├── DescribeBucketExecutor.java │ │ │ ├── DescribeIndexExecutor.java │ │ │ ├── DropBucketExecutor.java │ │ │ ├── ExecutionResult.java │ │ │ ├── GetBucketKeysExecutor.java │ │ │ └── IndexCommons.java │ │ │ ├── meta │ │ │ ├── ClusterInfo.java │ │ │ ├── ColumnInfo.java │ │ │ ├── IndexInfo.java │ │ │ └── TableInfo.java │ │ │ ├── resultset │ │ │ ├── CouchbaseListResultSet.java │ │ │ ├── CouchbaseReactiveResultSet.java │ │ │ ├── CouchbaseResultSetMetaData.java │ │ │ ├── CouchbaseSimpleResultSet.java │ │ │ ├── ReactiveRows.java │ │ │ └── ResultSetRows.java │ │ │ └── types │ │ │ ├── ArrayImpl.java │ │ │ └── ColumnTypeHelper.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── java.sql.Driver │ └── test │ └── com │ └── intellij │ └── CouchbaseClientURITest.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── license.txt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | out/ 3 | build/ 4 | .gradle/ 5 | *.iml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Couchbase N1QL JDBC Driver 2 | 3 | This driver supports the Couchbase N1QL query language with some extensions for basic bucket management. 4 | 5 | ## How to build jar 6 | ``` 7 | # Linux, MacOs 8 | ./gradlew jar 9 | 10 | # Windows 11 | gradlew.bat jar 12 | ``` 13 | 14 | You'll find it in build/libs. 15 | 16 | ## JDBC connection string 17 | 18 | ``` 19 | jdbc:couchbase:[/defaultBucket][?=&=&...] 20 | ``` 21 | 22 | The driver supports a custom Couchbase port mapping, and the specified port should be a key-value service port [11210 by default]. 23 | 24 | If you are connecting to a pre Couchbase 6.5 cluster, a `defaultBucket` must be specified in order to properly initialize the connection. 25 | 26 | Recognized properties are the following: 27 | * Recognized by the driver itself 28 | * `user=` [required parameter] 29 | * `password=` [required parameter] 30 | * `sslenabled=true/false` 31 | * `meta.sampling.size=` specifies a number of documents fetched in order to infer a database schema 32 | * `query.scan.consistency=not_bounded/request_plus` specifies a query scan consistency (RYW consistency) [default value is `not_bounded`] 33 | * `loggingLevel` [default value is `severe`] 34 | * Propagated to a Couchbase cluster 35 | * The full list of recognized parameters is documented in the Couchbase [Client-Settings Documentation](https://docs.couchbase.com/java-sdk/current/ref/client-settings.html). 36 | Any client setting with a system property name may also be specified as a connection string parameter (without the com.couchbase.env. prefix). 37 | 38 | 39 | ## Extended N1QL statements 40 | 41 | This driver supports some extra statements for the N1QL query language. These are for a bucket (keyspace) management (create and drop). 42 | Important note: create bucket statement is valid for Couchbase Enterprise edition only. 43 | 44 | #### Create bucket statement 45 | ``` 46 | create-bucket ::= CREATE ( BUCKET | TABLE ) [ WITH PRIMARY INDEX ] keyspace-ref [ bucket-with ] 47 | keyspace-ref ::= [ namespace ':' ] keyspace 48 | bucket-with ::= WITH expr 49 | ``` 50 | Where `expr` is a json object with optional bucket settings: 51 | * `flushEnabled` true/false [default is false] 52 | * `ramQuotaMB` number [default is 100] 53 | * `replicaNumber` number [default is 1] 54 | * `replicaIndexes` true/false [default is false] 55 | * `maxTTL` number of seconds [default is 0] 56 | * `compressionMode` string, one of "off"/"passive"/"active" [default is passive] 57 | * `bucketType` string, one of "membase"/"memcached"/"ephemeral" [default is membase] 58 | * `conflictResolutionType` string, one of "lww"/"seqno" [default is seqno] 59 | * `evictionPolicy` string, one of "fullEviction"/"valueOnly"/"nruEviction"/"noEviction" [default is based on a bucket type] 60 | 61 | If `WITH PRIMARY INDEX` clause is present, a default primary index will be built for the newly created bucket. 62 | 63 | Example: 64 | ``` 65 | create bucket with primary index bucket_name 66 | with { ramQuotaMB: 128, bucketType: "ephemeral" } 67 | ``` 68 | #### Drop bucket statement 69 | ``` 70 | drop-bucket ::= DROP ( BUCKET | TABLE ) keyspace-ref 71 | keyspace-ref ::= [ namespace ':' ] keyspace 72 | ``` 73 | 74 | Example: 75 | ``` 76 | drop bucket bucket_name 77 | ``` 78 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | version '0.6.0' 6 | 7 | sourceCompatibility = 11 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | sourceSets { 14 | main { 15 | java { 16 | srcDirs = ['driver/src/main/java'] 17 | } 18 | resources { 19 | srcDirs = ['driver/src/main/resources'] 20 | } 21 | } 22 | test { 23 | java { 24 | srcDirs = ['driver/src/test'] 25 | } 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation 'org.jetbrains:annotations:19.0.0' 31 | compile 'com.couchbase.client:java-client:3.4.0' 32 | testCompile group: 'junit', name: 'junit', version: '4.12' 33 | } 34 | 35 | jar { 36 | from { 37 | configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } 38 | } 39 | } 40 | 41 | task sourcesJar(type: Jar, dependsOn: classes) { 42 | classifier = 'sources' 43 | from sourceSets.main.allSource 44 | } 45 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/ClusterConnection.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import com.couchbase.client.java.Cluster; 4 | import com.couchbase.client.java.env.ClusterEnvironment; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public class ClusterConnection { 9 | private final Cluster cluster; 10 | private final ClusterEnvironment clusterEnvironment; 11 | 12 | public ClusterConnection(@NotNull Cluster cluster, @NotNull ClusterEnvironment clusterEnvironment) { 13 | this.cluster = cluster; 14 | this.clusterEnvironment = clusterEnvironment; 15 | } 16 | 17 | public Cluster getCluster() { 18 | return cluster; 19 | } 20 | 21 | public void close() { 22 | cluster.disconnect(); 23 | clusterEnvironment.shutdown(); 24 | } 25 | 26 | void initConnection(@Nullable String defaultBucket) { 27 | if (defaultBucket != null && !defaultBucket.isEmpty()) { 28 | cluster.bucket(defaultBucket); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/ContainerUtil.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import org.jetbrains.annotations.Contract; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.Collection; 9 | import java.util.List; 10 | import java.util.function.Function; 11 | 12 | public final class ContainerUtil { 13 | private ContainerUtil() { 14 | } 15 | 16 | @Contract(pure = true) 17 | public static @NotNull List map(@NotNull Collection collection, @NotNull Function mapping) { 18 | if (collection.isEmpty()) return new ArrayList<>(); 19 | List list = new ArrayList<>(collection.size()); 20 | for (final T t : collection) { 21 | list.add(mapping.apply(t)); 22 | } 23 | return list; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/CouchbaseBaseStatement.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import com.couchbase.client.java.Cluster; 4 | import com.couchbase.client.java.json.JsonArray; 5 | import com.couchbase.client.java.query.QueryOptions; 6 | import com.couchbase.client.java.query.ReactiveQueryResult; 7 | import com.intellij.resultset.CouchbaseReactiveResultSet; 8 | import com.intellij.resultset.CouchbaseSimpleResultSet; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | import reactor.core.publisher.Mono; 12 | import reactor.util.concurrent.Queues; 13 | 14 | import java.sql.*; 15 | import java.util.List; 16 | import java.util.Locale; 17 | import java.util.Objects; 18 | import java.util.Properties; 19 | 20 | import static com.intellij.DriverPropertyInfoHelper.ScanConsistency.getQueryScanConsistency; 21 | 22 | @SuppressWarnings("RedundantThrows") 23 | public abstract class CouchbaseBaseStatement implements Statement { 24 | protected final Cluster cluster; 25 | protected final Properties properties; 26 | protected final boolean isReadOnly; 27 | protected CouchbaseConnection connection; 28 | protected ResultSet result; 29 | private int fetchSize = Queues.SMALL_BUFFER_SIZE; 30 | private boolean isClosed = false; 31 | private int updateCount = -1; 32 | 33 | CouchbaseBaseStatement(@NotNull CouchbaseConnection connection) { 34 | this.connection = connection; 35 | this.properties = connection.getProperties(); 36 | this.cluster = connection.getCluster(); 37 | this.isReadOnly = connection.isReadOnly(); 38 | } 39 | 40 | protected QueryOptions makeQueryOptions() { 41 | return QueryOptions.queryOptions() 42 | .scanConsistency(getQueryScanConsistency(properties)) 43 | .readonly(isReadOnly) 44 | .metrics(true); 45 | } 46 | 47 | @Override 48 | public void close() throws SQLException { 49 | if (result != null) { 50 | result.close(); 51 | } 52 | result = null; 53 | connection = null; 54 | isClosed = true; 55 | } 56 | 57 | void checkClosed() throws SQLException { 58 | if (isClosed) { 59 | throw new SQLException("Statement was previously closed."); 60 | } 61 | } 62 | 63 | @Override 64 | public boolean isClosed() throws SQLException { 65 | return isClosed; 66 | } 67 | 68 | protected boolean executeInner(@NotNull String sql, @NotNull Mono resultMono) throws SQLException { 69 | try { 70 | ReactiveQueryResult result = Objects.requireNonNull(resultMono.block(), "Query did not return result"); 71 | ResultSet resultSet; 72 | if (sql.toLowerCase(Locale.ENGLISH).startsWith("infer")) resultSet = listResultSet(result); 73 | else { 74 | resultSet = new CouchbaseReactiveResultSet(this, result); 75 | long mutationCount = ((CouchbaseReactiveResultSet) resultSet).getMutationCount(); 76 | if (mutationCount != -1) { 77 | resultSet.close(); 78 | setNewResultSet(resultSet, mutationCount); 79 | return false; 80 | } 81 | } 82 | setNewResultSet(resultSet); 83 | return true; 84 | } catch (Throwable t) { 85 | throw new SQLException(t); 86 | } 87 | } 88 | 89 | @Nullable 90 | private ResultSet listResultSet(@NotNull ReactiveQueryResult result) { 91 | List list = result.rowsAs(JsonArray.class) 92 | .map(JsonArray::toList) 93 | .blockFirst(); 94 | return list == null ? null : new CouchbaseSimpleResultSet(list); 95 | } 96 | 97 | protected void setNewResultSet(@Nullable ResultSet resultSet) throws SQLException { 98 | this.setNewResultSet(resultSet, -1); 99 | } 100 | 101 | protected void setNewResultSet(@Nullable ResultSet resultSet, long updateCount) throws SQLException { 102 | if (result != null) { 103 | result.close(); 104 | } 105 | result = resultSet; 106 | this.updateCount = coalesceInt(updateCount); 107 | } 108 | 109 | private int coalesceInt(long value) { 110 | if (value < Integer.MIN_VALUE) { 111 | return Integer.MIN_VALUE; 112 | } 113 | if (value > Integer.MAX_VALUE) { 114 | return Integer.MAX_VALUE; 115 | } 116 | return (int)value; 117 | } 118 | 119 | @Override 120 | public boolean getMoreResults() throws SQLException { 121 | return false; 122 | } 123 | 124 | @Override 125 | public int getUpdateCount() throws SQLException { 126 | checkClosed(); 127 | int returnCount = updateCount; 128 | updateCount = -1; 129 | return returnCount; 130 | } 131 | 132 | @Override 133 | public ResultSet getResultSet() throws SQLException { 134 | checkClosed(); 135 | return result; 136 | } 137 | 138 | @Override 139 | public int[] executeBatch() throws SQLException { 140 | throw new SQLFeatureNotSupportedException(); 141 | } 142 | 143 | @Override 144 | public T unwrap(final Class iface) throws SQLException { 145 | throw new SQLFeatureNotSupportedException(); 146 | } 147 | 148 | @Override 149 | public boolean isWrapperFor(final Class iface) throws SQLException { 150 | throw new SQLFeatureNotSupportedException(); 151 | } 152 | 153 | @Override 154 | public int getResultSetHoldability() throws SQLException { 155 | throw new SQLFeatureNotSupportedException(); 156 | } 157 | 158 | @Override 159 | public void setPoolable(boolean poolable) throws SQLException { 160 | throw new SQLFeatureNotSupportedException(); 161 | } 162 | 163 | @Override 164 | public boolean isPoolable() throws SQLException { 165 | throw new SQLFeatureNotSupportedException(); 166 | } 167 | 168 | @Override 169 | public void closeOnCompletion() throws SQLException { 170 | throw new SQLFeatureNotSupportedException(); 171 | } 172 | 173 | @Override 174 | public boolean isCloseOnCompletion() throws SQLException { 175 | throw new SQLFeatureNotSupportedException(); 176 | } 177 | 178 | @Override 179 | public int getMaxFieldSize() throws SQLException { 180 | throw new SQLFeatureNotSupportedException(); 181 | } 182 | 183 | @Override 184 | public void setMaxFieldSize(int max) throws SQLException { 185 | throw new SQLFeatureNotSupportedException(); 186 | } 187 | 188 | @Override 189 | public int getMaxRows() throws SQLException { 190 | throw new SQLFeatureNotSupportedException(); 191 | } 192 | 193 | @Override 194 | public void setMaxRows(int max) throws SQLException { 195 | // todo 196 | } 197 | 198 | @Override 199 | public void setEscapeProcessing(boolean enable) throws SQLException { 200 | throw new SQLFeatureNotSupportedException(); 201 | } 202 | 203 | @Override 204 | public int getQueryTimeout() throws SQLException { 205 | throw new SQLFeatureNotSupportedException(); 206 | } 207 | 208 | @Override 209 | public void setQueryTimeout(int seconds) throws SQLException { 210 | throw new SQLFeatureNotSupportedException(); 211 | } 212 | 213 | @Override 214 | public void cancel() throws SQLException { 215 | throw new SQLFeatureNotSupportedException("Couchbase provides no support for interrupting an operation."); 216 | } 217 | 218 | @Override 219 | public SQLWarning getWarnings() throws SQLException { 220 | return null; // todo 221 | } 222 | 223 | @Override 224 | public void clearWarnings() throws SQLException { 225 | // todo 226 | } 227 | 228 | @Override 229 | public void setCursorName(final String name) throws SQLException { 230 | checkClosed(); 231 | // Driver doesn't support positioned updates for now, so no-op. 232 | } 233 | 234 | @Override 235 | public void setFetchDirection(int direction) throws SQLException { 236 | throw new SQLFeatureNotSupportedException(); 237 | } 238 | 239 | @Override 240 | public int getFetchDirection() throws SQLException { 241 | return ResultSet.FETCH_FORWARD; 242 | } 243 | 244 | @Override 245 | public void setFetchSize(int rows) throws SQLException { 246 | this.fetchSize = rows; 247 | } 248 | 249 | @Override 250 | public int getFetchSize() { 251 | return this.fetchSize; 252 | } 253 | 254 | @Override 255 | public int getResultSetConcurrency() throws SQLException { 256 | throw new SQLFeatureNotSupportedException(); 257 | } 258 | 259 | @Override 260 | public int getResultSetType() throws SQLException { 261 | throw new SQLFeatureNotSupportedException(); 262 | } 263 | 264 | @Override 265 | public Connection getConnection() throws SQLException { 266 | throw new SQLFeatureNotSupportedException(); 267 | } 268 | 269 | @Override 270 | public boolean getMoreResults(int current) throws SQLException { 271 | throw new SQLFeatureNotSupportedException(); 272 | } 273 | 274 | @Override 275 | public ResultSet getGeneratedKeys() throws SQLException { 276 | throw new SQLFeatureNotSupportedException(); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/CouchbaseClientURI.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import com.couchbase.client.core.deps.io.netty.handler.ssl.util.InsecureTrustManagerFactory; 4 | import com.couchbase.client.core.env.*; 5 | import com.couchbase.client.java.Cluster; 6 | import com.couchbase.client.java.ClusterOptions; 7 | import com.couchbase.client.java.env.ClusterEnvironment; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.sql.SQLException; 12 | import java.util.*; 13 | import java.util.logging.ConsoleHandler; 14 | import java.util.logging.Handler; 15 | import java.util.logging.Level; 16 | import java.util.logging.Logger; 17 | import java.util.stream.Collectors; 18 | 19 | import static com.intellij.DriverPropertyInfoHelper.*; 20 | 21 | class CouchbaseClientURI { 22 | static final String PREFIX = "jdbc:couchbase:"; 23 | private static final String HTTP_SCHEMA = "couchbase://"; 24 | private static final String HTTPS_SCHEMA = "couchbases://"; 25 | 26 | private static final Set JDBC_KEYS = new HashSet<>(ContainerUtil.map( 27 | Arrays.asList(USER, PASSWORD, ENABLE_SSL, VERIFY_SERVER_CERTIFICATE, VERIFY_HOSTNAMES, DEFAULT_BUCKET), 28 | key -> key.toLowerCase(Locale.ENGLISH))); 29 | 30 | private final String connectionString; 31 | private final String uri; 32 | private final String hosts; 33 | private final String userName; 34 | private final String password; 35 | private final String defaultBucket; 36 | private final boolean sslEnabled; 37 | private final boolean verifyServerCert; 38 | private final boolean verifyHostnames; 39 | 40 | public CouchbaseClientURI(@NotNull String uri, @Nullable Properties info) { 41 | this.uri = uri; 42 | if (!uri.startsWith(PREFIX)) { 43 | throw new IllegalArgumentException("URI needs to start with " + PREFIX); 44 | } 45 | 46 | String trimmedUri = uri.substring(PREFIX.length()); 47 | Map> options = null; 48 | String serverPart; 49 | String nsPart = null; 50 | 51 | int optionsStartIndex = trimmedUri.indexOf("?"); 52 | if (optionsStartIndex >= 0) { 53 | serverPart = trimmedUri.substring(0, optionsStartIndex); 54 | options = parseOptions(trimmedUri.substring(optionsStartIndex + 1)); 55 | } else { 56 | serverPart = trimmedUri; 57 | } 58 | 59 | int lastSlashIndex = serverPart.lastIndexOf("/"); 60 | if (lastSlashIndex >= 0) { 61 | nsPart = serverPart.substring(lastSlashIndex + 1); 62 | serverPart = serverPart.substring(0, lastSlashIndex); 63 | } 64 | 65 | setLoggingLevel(info, options); 66 | 67 | this.userName = getOption(info, options, USER, null); 68 | this.password = getOption(info, options, PASSWORD, null); 69 | this.sslEnabled = isTrue(getOption(info, options, ENABLE_SSL, ENABLE_SSL_DEFAULT)); 70 | this.verifyServerCert = isTrue(getOption(info, options, VERIFY_SERVER_CERTIFICATE, VERIFY_SERVER_CERTIFICATE_DEFAULT)); 71 | this.verifyHostnames = isTrue(getOption(info, options, VERIFY_HOSTNAMES, VERIFY_HOSTNAMES_DEFAULT)); 72 | this.hosts = serverPart; 73 | this.defaultBucket = nsPart != null && !nsPart.isEmpty() ? nsPart : getOption(info, options, DEFAULT_BUCKET, null); 74 | this.connectionString = createConnectionString(serverPart, options); 75 | } 76 | 77 | private void setLoggingLevel(Properties info, Map> options) { 78 | Logger logger = Logger.getLogger("com.couchbase.client"); 79 | String logLevel = getOption(info, options, LOGGING_LEVEL, LOGGING_LEVEL_DEFAULT); 80 | if (logLevel == null) return; 81 | Level level; 82 | try { 83 | level = Level.parse(logLevel.toUpperCase(Locale.ENGLISH)); 84 | } catch (IllegalArgumentException e) { 85 | e.printStackTrace(); 86 | return; 87 | } 88 | logger.setLevel(level); 89 | for (Handler h : logger.getParent().getHandlers()) { 90 | if (h instanceof ConsoleHandler) { 91 | h.setLevel(level); 92 | } 93 | } 94 | } 95 | 96 | /** 97 | * @return option from properties or from uri if it is not found in properties. 98 | * null if options was not found. 99 | */ 100 | @Nullable 101 | private String getOption(@Nullable Properties properties, @Nullable Map> options, 102 | @NotNull String optionName, @Nullable String defaultValue) { 103 | if (properties != null) { 104 | String option = (String) properties.get(optionName); 105 | if (option != null) { 106 | return option; 107 | } 108 | } 109 | String value = getLastValue(options, optionName); 110 | return value != null ? value : defaultValue; 111 | } 112 | 113 | ClusterConnection createClusterConnection() throws SQLException { 114 | String connectionStringWithSchema = (sslEnabled ? HTTPS_SCHEMA : HTTP_SCHEMA) + connectionString; 115 | ClusterEnvironment.Builder builder = ClusterEnvironment.builder() 116 | .load(new ConnectionStringPropertyLoader(connectionStringWithSchema)); 117 | Authenticator authenticator = authenticate(builder); 118 | ClusterEnvironment environment = builder.build(); 119 | ClusterConnection clusterConnection = new ClusterConnection( 120 | Cluster.connect(connectionStringWithSchema, ClusterOptions 121 | .clusterOptions(authenticator) 122 | .environment(environment) 123 | ), environment); 124 | clusterConnection.initConnection(defaultBucket); 125 | return clusterConnection; 126 | } 127 | 128 | private Authenticator authenticate(ClusterEnvironment.Builder envBuilder) throws SQLException { 129 | if (sslEnabled) { 130 | SecurityConfig.Builder securityConfig = SecurityConfig.enableTls(true); 131 | securityConfig.enableHostnameVerification(verifyHostnames); 132 | if (verifyServerCert) { 133 | SslKeyStoreConfig trustStore = SslKeyStoreConfig.create(SslKeyStoreConfig.Type.TRUST_STORE); 134 | envBuilder.securityConfig(securityConfig.trustStore(trustStore.getPath(), trustStore.getPassword(), 135 | trustStore.getType())); 136 | } else { 137 | envBuilder.securityConfig(securityConfig.trustManagerFactory(InsecureTrustManagerFactory.INSTANCE)); 138 | } 139 | 140 | if (userName == null || userName.isEmpty()) { 141 | SslKeyStoreConfig keyStore = SslKeyStoreConfig.create(SslKeyStoreConfig.Type.KEY_STORE); 142 | return CertificateAuthenticator.fromKeyStore( 143 | keyStore.getPath(), keyStore.getPassword(), keyStore.getType()); 144 | } 145 | } 146 | 147 | if (userName == null || userName.isEmpty() || password == null) { 148 | throw new SQLException("Username or password is not provided"); 149 | } 150 | return PasswordAuthenticator.create(userName, password); 151 | } 152 | 153 | @Nullable 154 | private String getLastValue(@Nullable Map> optionsMap, @NotNull String key) { 155 | if (optionsMap == null) return null; 156 | String normalizedKey = key.toLowerCase(Locale.ENGLISH); 157 | List valueList = optionsMap.get(normalizedKey); 158 | if (valueList == null || valueList.size() == 0) return null; 159 | return valueList.get(valueList.size() - 1); 160 | } 161 | 162 | @NotNull 163 | private Map> parseOptions(@NotNull String optionsPart) { 164 | Map> optionsMap = new HashMap<>(); 165 | 166 | for (String _part : optionsPart.split("&")) { 167 | int idx = _part.indexOf("="); 168 | if (idx >= 0) { 169 | String key = _part.substring(0, idx).toLowerCase(Locale.ENGLISH); 170 | String value = _part.substring(idx + 1); 171 | List valueList = optionsMap.get(key); 172 | if (valueList == null) { 173 | valueList = new ArrayList<>(1); 174 | } 175 | valueList.add(value); 176 | optionsMap.put(key, valueList); 177 | } 178 | } 179 | 180 | return optionsMap; 181 | } 182 | 183 | @NotNull 184 | private String createConnectionString(@NotNull String hosts, @Nullable Map> optionsMap) { 185 | if (optionsMap == null) { 186 | return hosts; 187 | } 188 | return optionsMap.keySet().stream() 189 | .filter(key -> !JDBC_KEYS.contains(key)) 190 | .map(key -> key + "=" + getLastValue(optionsMap, key)) 191 | .collect(Collectors.joining("&", hosts + "?", "")); 192 | } 193 | 194 | // --------------------------------- 195 | 196 | /** 197 | * Gets the username 198 | * 199 | * @return the username 200 | */ 201 | public String getUsername() { 202 | return userName; 203 | } 204 | 205 | /** 206 | * Gets the password 207 | * 208 | * @return the password 209 | */ 210 | public String getPassword() { 211 | return password; 212 | } 213 | 214 | /** 215 | * Gets the ssl enabled property 216 | * 217 | * @return the ssl enabled property 218 | */ 219 | public Boolean getSslEnabled() { 220 | return sslEnabled; 221 | } 222 | 223 | /** 224 | * @return verifyServerCertificate property 225 | */ 226 | public Boolean getVerifyServerCertificate() { 227 | return verifyServerCert; 228 | } 229 | 230 | /** 231 | * Gets the list of hosts and params sent directly to Java SDK 232 | * 233 | * @return the host list 234 | */ 235 | public String getConnectionString() { 236 | return connectionString; 237 | } 238 | 239 | /** 240 | * Gets the list of hosts 241 | * 242 | * @return the host list 243 | */ 244 | public String getHosts() { 245 | return hosts; 246 | } 247 | 248 | /** 249 | * Get the unparsed URI. 250 | * 251 | * @return the URI 252 | */ 253 | public String getURI() { 254 | return uri; 255 | } 256 | 257 | /** 258 | * Gets the default bucket 259 | * 260 | * @return the default bucket 261 | */ 262 | public String getDefaultBucket() { 263 | return defaultBucket; 264 | } 265 | 266 | @Override 267 | public String toString() { 268 | return uri; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/CouchbaseConnection.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import com.couchbase.client.java.Cluster; 4 | import com.intellij.executor.CouchbaseCustomStatementExecutor; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.sql.Array; 8 | import java.sql.Blob; 9 | import java.sql.CallableStatement; 10 | import java.sql.Clob; 11 | import java.sql.Connection; 12 | import java.sql.DatabaseMetaData; 13 | import java.sql.NClob; 14 | import java.sql.PreparedStatement; 15 | import java.sql.ResultSet; 16 | import java.sql.SQLException; 17 | import java.sql.SQLFeatureNotSupportedException; 18 | import java.sql.SQLWarning; 19 | import java.sql.SQLXML; 20 | import java.sql.Savepoint; 21 | import java.sql.Statement; 22 | import java.sql.Struct; 23 | import java.util.Map; 24 | import java.util.Properties; 25 | import java.util.concurrent.Executor; 26 | 27 | public class CouchbaseConnection implements Connection { 28 | private static final String DEFAULT_SCHEMA = "default"; 29 | private final ClusterConnection cluster; 30 | private final CouchbaseJdbcDriver driver; 31 | private final CouchbaseClientURI uri; 32 | private final Properties properties; 33 | private boolean isClosed = false; 34 | private boolean isReadOnly = false; 35 | 36 | CouchbaseConnection(@NotNull ClusterConnection cluster, @NotNull CouchbaseJdbcDriver couchbaseJdbcDriver, 37 | @NotNull CouchbaseClientURI uri, @NotNull Properties properties) { 38 | this.cluster = cluster; 39 | this.driver = couchbaseJdbcDriver; 40 | this.uri = uri; 41 | this.properties = properties; 42 | } 43 | 44 | public String getCatalog() throws SQLException { 45 | checkClosed(); 46 | return null; 47 | } 48 | 49 | public Cluster getCluster() { 50 | return cluster.getCluster(); 51 | } 52 | 53 | Properties getProperties() { 54 | return properties; 55 | } 56 | 57 | CouchbaseClientURI getUri() { 58 | return uri; 59 | } 60 | 61 | @Override 62 | public T unwrap(Class iface) throws SQLException { 63 | checkClosed(); 64 | return null; 65 | } 66 | 67 | @Override 68 | public boolean isWrapperFor(Class iface) throws SQLException { 69 | checkClosed(); 70 | return false; 71 | } 72 | 73 | @Override 74 | public CouchbaseStatement createStatement() throws SQLException { 75 | checkClosed(); 76 | try { 77 | return new CouchbaseStatement(this); 78 | } catch (Throwable t) { 79 | throw new SQLException(t.getMessage(), t); 80 | } 81 | } 82 | 83 | @Override 84 | public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { 85 | throw new SQLFeatureNotSupportedException(); 86 | } 87 | 88 | @Override 89 | public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) 90 | throws SQLException { 91 | throw new SQLFeatureNotSupportedException(); 92 | } 93 | 94 | @Override 95 | public PreparedStatement prepareStatement(String sql) throws SQLException { 96 | checkClosed(); 97 | try { 98 | if (CouchbaseCustomStatementExecutor.mayExecute(sql)) { 99 | return new CouchbaseNoParamsPrepared(sql, 100 | new CouchbaseStatement(this)); 101 | } 102 | return new CouchbasePreparedStatement(this, sql); 103 | } catch (Throwable t) { 104 | throw new SQLException(t.getMessage(), t); 105 | } 106 | } 107 | 108 | @Override 109 | public CallableStatement prepareCall(String sql) throws SQLException { 110 | throw new SQLFeatureNotSupportedException(); 111 | } 112 | 113 | @Override 114 | public String nativeSQL(String sql) throws SQLException { 115 | checkClosed(); 116 | throw new SQLFeatureNotSupportedException("Couchbase does not support SQL natively."); 117 | } 118 | 119 | @Override 120 | public void setAutoCommit(boolean autoCommit) throws SQLException { 121 | checkClosed(); 122 | } 123 | 124 | @Override 125 | public boolean getAutoCommit() throws SQLException { 126 | checkClosed(); 127 | return true; 128 | } 129 | 130 | @Override 131 | public void commit() throws SQLException { 132 | checkClosed(); 133 | } 134 | 135 | @Override 136 | public void rollback() throws SQLException { 137 | checkClosed(); 138 | } 139 | 140 | @Override 141 | public void close() { 142 | if (!isClosed) { 143 | cluster.close(); 144 | } 145 | isClosed = true; 146 | } 147 | 148 | @Override 149 | public boolean isClosed() { 150 | return isClosed; 151 | } 152 | 153 | @Override 154 | public CouchbaseMetaData getMetaData() throws SQLException { 155 | checkClosed(); 156 | return new CouchbaseMetaData(this, driver); 157 | } 158 | 159 | @Override 160 | public void setReadOnly(boolean readOnly) throws SQLException { 161 | checkClosed(); 162 | isReadOnly = readOnly; 163 | } 164 | 165 | @Override 166 | public boolean isReadOnly() { 167 | return isReadOnly; 168 | } 169 | 170 | @Override 171 | public void setCatalog(String catalog) { 172 | 173 | } 174 | 175 | @Override 176 | public void setTransactionIsolation(int level) throws SQLException { 177 | checkClosed(); 178 | // Since the only valid value for MongDB is Connection.TRANSACTION_NONE, and the javadoc for this method 179 | // indicates that this is not a valid value for level here, throw unsupported operation exception. 180 | throw new UnsupportedOperationException("Couchbase provides no support for transactions."); 181 | } 182 | 183 | @Override 184 | public int getTransactionIsolation() throws SQLException { 185 | checkClosed(); 186 | return Connection.TRANSACTION_NONE; 187 | } 188 | 189 | @Override 190 | public SQLWarning getWarnings() throws SQLException { 191 | checkClosed(); 192 | return null; 193 | } 194 | 195 | @Override 196 | public void clearWarnings() throws SQLException { 197 | checkClosed(); 198 | } 199 | 200 | 201 | @Override 202 | public PreparedStatement prepareStatement(String sql, int resultSetType, 203 | int resultSetConcurrency) throws SQLException { 204 | checkClosed(); 205 | if (resultSetType != ResultSet.TYPE_FORWARD_ONLY || resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) { 206 | throw new SQLFeatureNotSupportedException(); 207 | } 208 | return prepareStatement(sql); 209 | } 210 | 211 | @Override 212 | public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { 213 | throw new SQLFeatureNotSupportedException(); 214 | } 215 | 216 | @Override 217 | public Map> getTypeMap() throws SQLException { 218 | throw new SQLFeatureNotSupportedException(); 219 | } 220 | 221 | @Override 222 | public void setTypeMap(Map> map) throws SQLException { 223 | throw new SQLFeatureNotSupportedException(); 224 | } 225 | 226 | @Override 227 | public void setHoldability(int holdability) throws SQLException { 228 | throw new SQLFeatureNotSupportedException(); 229 | } 230 | 231 | @Override 232 | public int getHoldability() throws SQLException { 233 | throw new SQLFeatureNotSupportedException(); 234 | } 235 | 236 | @Override 237 | public Savepoint setSavepoint() throws SQLException { 238 | throw new SQLFeatureNotSupportedException(); 239 | } 240 | 241 | @Override 242 | public Savepoint setSavepoint(String name) throws SQLException { 243 | throw new SQLFeatureNotSupportedException(); 244 | } 245 | 246 | @Override 247 | public void rollback(Savepoint savepoint) throws SQLException { 248 | throw new SQLFeatureNotSupportedException(); 249 | } 250 | 251 | @Override 252 | public void releaseSavepoint(Savepoint savepoint) throws SQLException { 253 | throw new SQLFeatureNotSupportedException(); 254 | } 255 | 256 | 257 | @Override 258 | public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, 259 | int resultSetHoldability) throws SQLException { 260 | throw new SQLFeatureNotSupportedException(); 261 | } 262 | 263 | @Override 264 | public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, 265 | int resultSetHoldability) throws SQLException { 266 | throw new SQLFeatureNotSupportedException(); 267 | } 268 | 269 | @Override 270 | public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { 271 | throw new SQLFeatureNotSupportedException(); 272 | } 273 | 274 | @Override 275 | public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { 276 | throw new SQLFeatureNotSupportedException(); 277 | } 278 | 279 | @Override 280 | public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { 281 | throw new SQLFeatureNotSupportedException(); 282 | } 283 | 284 | @Override 285 | public Clob createClob() throws SQLException { 286 | throw new SQLFeatureNotSupportedException(); 287 | } 288 | 289 | @Override 290 | public Blob createBlob() throws SQLException { 291 | throw new SQLFeatureNotSupportedException(); 292 | } 293 | 294 | @Override 295 | public NClob createNClob() throws SQLException { 296 | checkClosed(); 297 | return null; 298 | } 299 | 300 | @Override 301 | public SQLXML createSQLXML() throws SQLException { 302 | checkClosed(); 303 | return null; 304 | } 305 | 306 | @Override 307 | public boolean isValid(int timeout) throws SQLException { 308 | checkClosed(); 309 | return true; 310 | } 311 | 312 | @Override 313 | public void setClientInfo(String name, String value) { 314 | } 315 | 316 | @Override 317 | public void setClientInfo(Properties properties) { 318 | } 319 | 320 | @Override 321 | public String getClientInfo(String name) throws SQLException { 322 | checkClosed(); 323 | return null; 324 | } 325 | 326 | @Override 327 | public Properties getClientInfo() throws SQLException { 328 | checkClosed(); 329 | return null; 330 | } 331 | 332 | @Override 333 | public Array createArrayOf(String typeName, Object[] elements) throws SQLException { 334 | checkClosed(); 335 | 336 | return null; 337 | } 338 | 339 | @Override 340 | public Struct createStruct(String typeName, Object[] attributes) throws SQLException { 341 | checkClosed(); 342 | return null; 343 | } 344 | 345 | 346 | private void checkClosed() throws SQLException { 347 | if (isClosed) { 348 | throw new SQLException("Statement was previously closed."); 349 | } 350 | } 351 | 352 | @Override 353 | public void setSchema(String schema) { 354 | setCatalog(schema); 355 | } 356 | 357 | @Override 358 | public String getSchema() { 359 | return DEFAULT_SCHEMA; 360 | } 361 | 362 | @Override 363 | public void abort(Executor executor) throws SQLException { 364 | throw new SQLFeatureNotSupportedException(); 365 | } 366 | 367 | @Override 368 | public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { 369 | throw new SQLFeatureNotSupportedException(); 370 | } 371 | 372 | @Override 373 | public int getNetworkTimeout() throws SQLException { 374 | throw new SQLFeatureNotSupportedException(); 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/CouchbaseDocumentsSampler.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import com.couchbase.client.core.error.CouchbaseException; 4 | import com.intellij.meta.ColumnInfo; 5 | import com.intellij.meta.TableInfo; 6 | import com.intellij.types.ColumnTypeHelper; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.sql.ResultSet; 10 | import java.sql.SQLException; 11 | import java.sql.Types; 12 | import java.util.Collection; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.logging.Level; 17 | import java.util.logging.Logger; 18 | 19 | import static com.intellij.DriverPropertyInfoHelper.META_SAMPLING_SIZE; 20 | import static com.intellij.DriverPropertyInfoHelper.META_SAMPLING_SIZE_DEFAULT; 21 | import static com.intellij.EscapingUtil.escapeChars; 22 | import static com.intellij.EscapingUtil.wrapInBackquotes; 23 | 24 | public class CouchbaseDocumentsSampler { 25 | private static final String INTRO_QUERY = "SELECT t.* FROM %s t LIMIT %d;"; 26 | private static final Logger LOG = Logger.getLogger(CouchbaseDocumentsSampler.class.getName()); 27 | 28 | private final CouchbaseConnection connection; 29 | private final int sampleSize; 30 | 31 | public CouchbaseDocumentsSampler(@NotNull CouchbaseConnection connection) { 32 | this.connection = connection; 33 | int sampleSize = META_SAMPLING_SIZE_DEFAULT; 34 | try { 35 | sampleSize = Integer.parseInt(connection.getProperties().getProperty(META_SAMPLING_SIZE)); 36 | } catch (NumberFormatException ignore) { } 37 | this.sampleSize = sampleSize; 38 | } 39 | 40 | public Collection sample(@NotNull TableInfo table) throws SQLException { 41 | Map columns = new HashMap<>(); 42 | String sql = String.format(INTRO_QUERY, qualifyTablePath(table), sampleSize); 43 | try (CouchbaseStatement statement = connection.createStatement()) { 44 | try (ResultSet resultSet = statement.executeQuery(sql)) { 45 | doIterations(columns, resultSet); 46 | } catch (CouchbaseException ex) { 47 | List entries = CouchbaseError.create(ex).getErrorEntries(); 48 | if (!(entries.size() == 1 && entries.get(0).getErrorCode().startsWith("13"))) { 49 | LOG.log(Level.WARNING, "Error while sampling bucket: ", ex); 50 | } 51 | } 52 | } 53 | return columns.values(); 54 | } 55 | 56 | @SuppressWarnings({"unchecked"}) 57 | private void doIterations(Map columns, ResultSet resultSet) throws SQLException { 58 | int iteration = 0; 59 | while (iteration < sampleSize && resultSet.next()) { 60 | iteration++; 61 | Map resultMap = (Map) resultSet.getObject(1); 62 | extractColumns(columns, resultMap, null); 63 | } 64 | } 65 | 66 | @SuppressWarnings("unchecked") 67 | private void extractColumns(Map columns, Map row, String parentName) { 68 | row.forEach((key, value) -> { 69 | String name = fullyQualifyName(parentName, key); 70 | columns.merge(name, createColumn(name, value), (oldValue, newValue) -> { 71 | if (oldValue.getType() == Types.NULL) { 72 | return newValue; 73 | } 74 | return oldValue; 75 | }); 76 | if (value instanceof Map) { 77 | extractColumns(columns, (Map) value, name); 78 | } 79 | }); 80 | } 81 | 82 | private static String qualifyTablePath(TableInfo table) { 83 | return table.getSchema() + ":" + wrapInBackquotes(table.getName()); 84 | } 85 | 86 | private static String fullyQualifyName(String parentName, String name) { 87 | String qualifier = parentName != null ? (parentName + ".") : ""; 88 | return qualifier + escapeChars(name, '\\', '.'); 89 | } 90 | 91 | private static ColumnInfo createColumn(String name, Object object) { 92 | String typeName = "string"; 93 | if (object == null) { 94 | typeName = "null"; 95 | } else if (object instanceof Map) { 96 | typeName = "object"; 97 | } else if (object instanceof List) { 98 | typeName = "array"; 99 | } else if (object instanceof Boolean) { 100 | typeName = "boolean"; 101 | } else if (object instanceof Float) { 102 | typeName = "float"; 103 | } else if (object instanceof Double) { 104 | typeName = "double"; 105 | } else if (object instanceof Long) { 106 | typeName = "long"; 107 | } else if (object instanceof Integer) { 108 | typeName = "integer"; 109 | } else if (object instanceof Number) { 110 | typeName = "numeric"; 111 | } 112 | return new ColumnInfo(name, ColumnTypeHelper.getJavaType(typeName), typeName); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/CouchbaseError.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import com.couchbase.client.core.error.CouchbaseException; 4 | import com.couchbase.client.core.error.context.ErrorContext; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public class CouchbaseError { 14 | private static final String UNKNOWN = "UNKNOWN"; 15 | 16 | private final List errorEntries; 17 | 18 | public CouchbaseError(List errorEntries) { 19 | this.errorEntries = errorEntries; 20 | } 21 | 22 | public static CouchbaseError create(@NotNull CouchbaseException exception) { 23 | ErrorContext context = exception.context(); 24 | if (context == null) { 25 | return unknown(exception); 26 | } 27 | Map exported = new HashMap<>(); 28 | context.injectExportableParams(exported); 29 | Object errors = exported.get("errors"); 30 | if (!(errors instanceof List)) { 31 | return unknown(exception); 32 | } 33 | List entries = new ArrayList<>(); 34 | for (Object errorObject : (List) errors) { 35 | if (!(errorObject instanceof Map)) { 36 | return unknown(exception); 37 | } 38 | Map errorMap = (Map) errorObject; 39 | Object errorCode = errorMap.get("code"); 40 | if (errorCode == null) { 41 | continue; 42 | } 43 | entries.add(new ErrorEntry(errorCode.toString(), String.valueOf(errorMap.get("message")))); 44 | } 45 | return new CouchbaseError(entries); 46 | } 47 | 48 | private static CouchbaseError unknown(@NotNull CouchbaseException exception) { 49 | return new CouchbaseError(Collections.singletonList(new ErrorEntry(UNKNOWN, exception.toString()))); 50 | } 51 | 52 | public List getErrorEntries() { 53 | return errorEntries; 54 | } 55 | 56 | public ErrorEntry getFirstError() { 57 | return errorEntries.stream().findFirst().orElse(null); 58 | } 59 | 60 | public static class ErrorEntry { 61 | private final String errorCode; 62 | private final String message; 63 | 64 | private ErrorEntry(String errorCode, String message) { 65 | this.errorCode = errorCode; 66 | this.message = message; 67 | } 68 | 69 | public String getMessage() { 70 | return message; 71 | } 72 | 73 | public String getErrorCode() { 74 | return errorCode; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/CouchbaseJdbcDriver.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.sql.Connection; 7 | import java.sql.Driver; 8 | import java.sql.DriverManager; 9 | import java.sql.DriverPropertyInfo; 10 | import java.sql.SQLException; 11 | import java.sql.SQLFeatureNotSupportedException; 12 | import java.util.Properties; 13 | import java.util.logging.Logger; 14 | 15 | import static com.intellij.CouchbaseClientURI.PREFIX; 16 | 17 | 18 | /** 19 | * Minimal implementation of the JDBC standards for the Couchbase database. 20 | */ 21 | public class CouchbaseJdbcDriver implements Driver { 22 | 23 | static { 24 | try { 25 | DriverManager.registerDriver(new CouchbaseJdbcDriver()); 26 | } catch (SQLException ex) { 27 | ex.printStackTrace(); 28 | } 29 | } 30 | 31 | /** 32 | * Connect to the database using a URL like : 33 | * jdbc:couchbase:host1[:port1][,host2[:port2],...][?option=value[&option=value&...]] 34 | * The URL's hosts and ports configuration is passed as it is to the Couchbase native Java driver. 35 | */ 36 | public Connection connect(@NotNull String url, @Nullable Properties info) throws SQLException { 37 | if (acceptsURL(url)) { 38 | try { 39 | CouchbaseClientURI clientURI = new CouchbaseClientURI(url, info); 40 | ClusterConnection cluster = clientURI.createClusterConnection(); 41 | if (info == null) { 42 | info = new Properties(); 43 | } 44 | return new CouchbaseConnection(cluster, this, clientURI, info); 45 | } catch (Exception e) { 46 | throw new SQLException(e.getMessage(), e); 47 | } 48 | } 49 | return null; 50 | } 51 | 52 | /** 53 | * URLs accepted are of the form: 54 | * jdbc:couchbase:host1[:port1][,host2[:port2],...][?option=value[&option=value&...]] 55 | */ 56 | @Override 57 | public boolean acceptsURL(@NotNull String url) { 58 | return url.startsWith(PREFIX); 59 | } 60 | 61 | @Override 62 | public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) { 63 | return DriverPropertyInfoHelper.getPropertyInfo(); 64 | } 65 | 66 | String getVersion() { 67 | return "0.6.0"; 68 | } 69 | 70 | @Override 71 | public int getMajorVersion() { 72 | return 0; 73 | } 74 | 75 | @Override 76 | public int getMinorVersion() { 77 | return 5; 78 | } 79 | 80 | @Override 81 | public boolean jdbcCompliant() { 82 | return false; 83 | } 84 | 85 | @Override 86 | public Logger getParentLogger() throws SQLFeatureNotSupportedException { 87 | throw new SQLFeatureNotSupportedException(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/CouchbaseNoParamsPrepared.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.io.InputStream; 6 | import java.io.Reader; 7 | import java.math.BigDecimal; 8 | import java.net.URL; 9 | import java.sql.Array; 10 | import java.sql.Blob; 11 | import java.sql.Clob; 12 | import java.sql.Connection; 13 | import java.sql.Date; 14 | import java.sql.NClob; 15 | import java.sql.ParameterMetaData; 16 | import java.sql.PreparedStatement; 17 | import java.sql.Ref; 18 | import java.sql.ResultSet; 19 | import java.sql.ResultSetMetaData; 20 | import java.sql.RowId; 21 | import java.sql.SQLException; 22 | import java.sql.SQLFeatureNotSupportedException; 23 | import java.sql.SQLWarning; 24 | import java.sql.SQLXML; 25 | import java.sql.Time; 26 | import java.sql.Timestamp; 27 | import java.util.Calendar; 28 | 29 | public class CouchbaseNoParamsPrepared implements PreparedStatement { 30 | private final String sql; 31 | private final CouchbaseStatement delegateStatement; 32 | 33 | public CouchbaseNoParamsPrepared(@NotNull String sql, @NotNull CouchbaseStatement statement) { 34 | this.sql = sql; 35 | this.delegateStatement = statement; 36 | } 37 | 38 | @Override 39 | public ResultSet executeQuery() throws SQLException { 40 | return delegateStatement.executeQuery(sql); 41 | } 42 | 43 | @Override 44 | public int executeUpdate() throws SQLException { 45 | return delegateStatement.executeUpdate(sql); 46 | } 47 | 48 | @Override 49 | public boolean execute() throws SQLException { 50 | return delegateStatement.execute(sql); 51 | } 52 | 53 | @Override 54 | public void setNull(int parameterIndex, int sqlType) throws SQLException { 55 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 56 | } 57 | 58 | @Override 59 | public void setBoolean(int parameterIndex, boolean x) throws SQLException { 60 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 61 | } 62 | 63 | @Override 64 | public void setByte(int parameterIndex, byte x) throws SQLException { 65 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 66 | } 67 | 68 | @Override 69 | public void setShort(int parameterIndex, short x) throws SQLException { 70 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 71 | } 72 | 73 | @Override 74 | public void setInt(int parameterIndex, int x) throws SQLException { 75 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 76 | } 77 | 78 | @Override 79 | public void setLong(int parameterIndex, long x) throws SQLException { 80 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 81 | } 82 | 83 | @Override 84 | public void setFloat(int parameterIndex, float x) throws SQLException { 85 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 86 | } 87 | 88 | @Override 89 | public void setDouble(int parameterIndex, double x) throws SQLException { 90 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 91 | } 92 | 93 | @Override 94 | public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { 95 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 96 | } 97 | 98 | @Override 99 | public void setString(int parameterIndex, String x) throws SQLException { 100 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 101 | } 102 | 103 | @Override 104 | public void setBytes(int parameterIndex, byte[] x) throws SQLException { 105 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 106 | } 107 | 108 | @Override 109 | public void setDate(int parameterIndex, Date x) throws SQLException { 110 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 111 | } 112 | 113 | @Override 114 | public void setTime(int parameterIndex, Time x) throws SQLException { 115 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 116 | } 117 | 118 | @Override 119 | public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { 120 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 121 | } 122 | 123 | @Override 124 | public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { 125 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 126 | } 127 | 128 | @Override 129 | public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { 130 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 131 | } 132 | 133 | @Override 134 | public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { 135 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 136 | } 137 | 138 | @Override 139 | public void clearParameters() throws SQLException { 140 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 141 | } 142 | 143 | @Override 144 | public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { 145 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 146 | } 147 | 148 | @Override 149 | public void setObject(int parameterIndex, Object x) throws SQLException { 150 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 151 | } 152 | 153 | @Override 154 | public void addBatch() throws SQLException { 155 | throw new SQLFeatureNotSupportedException(); 156 | } 157 | 158 | @Override 159 | public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { 160 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 161 | } 162 | 163 | @Override 164 | public void setRef(int parameterIndex, Ref x) throws SQLException { 165 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 166 | } 167 | 168 | @Override 169 | public void setBlob(int parameterIndex, Blob x) throws SQLException { 170 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 171 | } 172 | 173 | @Override 174 | public void setClob(int parameterIndex, Clob x) throws SQLException { 175 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 176 | } 177 | 178 | @Override 179 | public void setArray(int parameterIndex, Array x) throws SQLException { 180 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 181 | } 182 | 183 | @Override 184 | public ResultSetMetaData getMetaData() throws SQLException { 185 | throw new SQLFeatureNotSupportedException(); 186 | } 187 | 188 | @Override 189 | public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { 190 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 191 | } 192 | 193 | @Override 194 | public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { 195 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 196 | } 197 | 198 | @Override 199 | public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { 200 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 201 | } 202 | 203 | @Override 204 | public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { 205 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 206 | } 207 | 208 | @Override 209 | public void setURL(int parameterIndex, URL x) throws SQLException { 210 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 211 | } 212 | 213 | @Override 214 | public ParameterMetaData getParameterMetaData() throws SQLException { 215 | throw new SQLFeatureNotSupportedException(); 216 | } 217 | 218 | @Override 219 | public void setRowId(int parameterIndex, RowId x) throws SQLException { 220 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 221 | } 222 | 223 | @Override 224 | public void setNString(int parameterIndex, String value) throws SQLException { 225 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 226 | } 227 | 228 | @Override 229 | public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { 230 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 231 | } 232 | 233 | @Override 234 | public void setNClob(int parameterIndex, NClob value) throws SQLException { 235 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 236 | } 237 | 238 | @Override 239 | public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { 240 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 241 | } 242 | 243 | @Override 244 | public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { 245 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 246 | } 247 | 248 | @Override 249 | public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { 250 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 251 | } 252 | 253 | @Override 254 | public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { 255 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 256 | } 257 | 258 | @Override 259 | public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { 260 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 261 | } 262 | 263 | @Override 264 | public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { 265 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 266 | } 267 | 268 | @Override 269 | public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { 270 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 271 | } 272 | 273 | @Override 274 | public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { 275 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 276 | } 277 | 278 | @Override 279 | public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { 280 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 281 | } 282 | 283 | @Override 284 | public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { 285 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 286 | } 287 | 288 | @Override 289 | public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { 290 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 291 | } 292 | 293 | @Override 294 | public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { 295 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 296 | } 297 | 298 | @Override 299 | public void setClob(int parameterIndex, Reader reader) throws SQLException { 300 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 301 | } 302 | 303 | @Override 304 | public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { 305 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 306 | } 307 | 308 | @Override 309 | public void setNClob(int parameterIndex, Reader reader) throws SQLException { 310 | throw new SQLFeatureNotSupportedException("Parameters are not supported for this statements"); 311 | } 312 | 313 | @Override 314 | public ResultSet executeQuery(String sql) throws SQLException { 315 | throw new SQLException("Method should not be called on prepared statement"); 316 | } 317 | 318 | @Override 319 | public int executeUpdate(String sql) throws SQLException { 320 | throw new SQLException("Method should not be called on prepared statement"); 321 | } 322 | 323 | @Override 324 | public void close() throws SQLException { 325 | delegateStatement.close(); 326 | } 327 | 328 | @Override 329 | public int getMaxFieldSize() throws SQLException { 330 | return delegateStatement.getMaxFieldSize(); 331 | } 332 | 333 | @Override 334 | public void setMaxFieldSize(int max) throws SQLException { 335 | delegateStatement.setMaxFieldSize(max); 336 | } 337 | 338 | @Override 339 | public int getMaxRows() throws SQLException { 340 | return delegateStatement.getMaxRows(); 341 | } 342 | 343 | @Override 344 | public void setMaxRows(int max) throws SQLException { 345 | delegateStatement.setMaxRows(max); 346 | } 347 | 348 | @Override 349 | public void setEscapeProcessing(boolean enable) throws SQLException { 350 | delegateStatement.setEscapeProcessing(enable); 351 | } 352 | 353 | @Override 354 | public int getQueryTimeout() throws SQLException { 355 | return delegateStatement.getQueryTimeout(); 356 | } 357 | 358 | @Override 359 | public void setQueryTimeout(int seconds) throws SQLException { 360 | delegateStatement.setQueryTimeout(seconds); 361 | } 362 | 363 | @Override 364 | public void cancel() throws SQLException { 365 | delegateStatement.cancel(); 366 | } 367 | 368 | @Override 369 | public SQLWarning getWarnings() throws SQLException { 370 | return delegateStatement.getWarnings(); 371 | } 372 | 373 | @Override 374 | public void clearWarnings() throws SQLException { 375 | delegateStatement.clearWarnings(); 376 | } 377 | 378 | @Override 379 | public void setCursorName(String name) throws SQLException { 380 | delegateStatement.setCursorName(name); 381 | } 382 | 383 | @Override 384 | public boolean execute(String sql) throws SQLException { 385 | throw new SQLException("Method should not be called on prepared statement"); 386 | } 387 | 388 | @Override 389 | public ResultSet getResultSet() throws SQLException { 390 | return delegateStatement.getResultSet(); 391 | } 392 | 393 | @Override 394 | public int getUpdateCount() throws SQLException { 395 | return delegateStatement.getUpdateCount(); 396 | } 397 | 398 | @Override 399 | public boolean getMoreResults() throws SQLException { 400 | return delegateStatement.getMoreResults(); 401 | } 402 | 403 | @Override 404 | public void setFetchDirection(int direction) throws SQLException { 405 | delegateStatement.setFetchDirection(direction); 406 | } 407 | 408 | @Override 409 | public int getFetchDirection() throws SQLException { 410 | return delegateStatement.getFetchDirection(); 411 | } 412 | 413 | @Override 414 | public void setFetchSize(int rows) throws SQLException { 415 | delegateStatement.setFetchSize(rows); 416 | } 417 | 418 | @Override 419 | public int getFetchSize() throws SQLException { 420 | return delegateStatement.getFetchSize(); 421 | } 422 | 423 | @Override 424 | public int getResultSetConcurrency() throws SQLException { 425 | return delegateStatement.getResultSetConcurrency(); 426 | } 427 | 428 | @Override 429 | public int getResultSetType() throws SQLException { 430 | return delegateStatement.getResultSetType(); 431 | } 432 | 433 | @Override 434 | public void addBatch(String sql) throws SQLException { 435 | throw new SQLFeatureNotSupportedException(); 436 | } 437 | 438 | @Override 439 | public void clearBatch() throws SQLException { 440 | throw new SQLFeatureNotSupportedException(); 441 | } 442 | 443 | @Override 444 | public int[] executeBatch() throws SQLException { 445 | throw new SQLFeatureNotSupportedException(); 446 | } 447 | 448 | @Override 449 | public Connection getConnection() throws SQLException { 450 | return delegateStatement.getConnection(); 451 | } 452 | 453 | @Override 454 | public boolean getMoreResults(int current) throws SQLException { 455 | return delegateStatement.getMoreResults(current); 456 | } 457 | 458 | @Override 459 | public ResultSet getGeneratedKeys() throws SQLException { 460 | return delegateStatement.getGeneratedKeys(); 461 | } 462 | 463 | @Override 464 | public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { 465 | throw new SQLException("Method should not be called on prepared statement"); 466 | } 467 | 468 | @Override 469 | public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { 470 | throw new SQLException("Method should not be called on prepared statement"); 471 | } 472 | 473 | @Override 474 | public int executeUpdate(String sql, String[] columnNames) throws SQLException { 475 | throw new SQLException("Method should not be called on prepared statement"); 476 | } 477 | 478 | @Override 479 | public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { 480 | throw new SQLException("Method should not be called on prepared statement"); 481 | } 482 | 483 | @Override 484 | public boolean execute(String sql, int[] columnIndexes) throws SQLException { 485 | throw new SQLException("Method should not be called on prepared statement"); 486 | } 487 | 488 | @Override 489 | public boolean execute(String sql, String[] columnNames) throws SQLException { 490 | throw new SQLException("Method should not be called on prepared statement"); 491 | } 492 | 493 | @Override 494 | public int getResultSetHoldability() throws SQLException { 495 | return delegateStatement.getResultSetHoldability(); 496 | } 497 | 498 | @Override 499 | public boolean isClosed() throws SQLException { 500 | return delegateStatement.isClosed(); 501 | } 502 | 503 | @Override 504 | public void setPoolable(boolean poolable) throws SQLException { 505 | delegateStatement.setPoolable(poolable); 506 | } 507 | 508 | @Override 509 | public boolean isPoolable() throws SQLException { 510 | return delegateStatement.isPoolable(); 511 | } 512 | 513 | @Override 514 | public void closeOnCompletion() throws SQLException { 515 | delegateStatement.closeOnCompletion(); 516 | } 517 | 518 | @Override 519 | public boolean isCloseOnCompletion() throws SQLException { 520 | return delegateStatement.isCloseOnCompletion(); 521 | } 522 | 523 | @Override 524 | public T unwrap(Class iface) throws SQLException { 525 | throw new SQLFeatureNotSupportedException(); 526 | } 527 | 528 | @Override 529 | public boolean isWrapperFor(Class iface) throws SQLException { 530 | throw new SQLFeatureNotSupportedException(); 531 | } 532 | } 533 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/CouchbasePreparedStatement.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import com.couchbase.client.java.json.JsonArray; 4 | import com.couchbase.client.java.query.QueryOptions; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.io.InputStream; 8 | import java.io.Reader; 9 | import java.math.BigDecimal; 10 | import java.net.URL; 11 | import java.sql.Array; 12 | import java.sql.Blob; 13 | import java.sql.Clob; 14 | import java.sql.Date; 15 | import java.sql.NClob; 16 | import java.sql.ParameterMetaData; 17 | import java.sql.PreparedStatement; 18 | import java.sql.Ref; 19 | import java.sql.ResultSet; 20 | import java.sql.ResultSetMetaData; 21 | import java.sql.RowId; 22 | import java.sql.SQLException; 23 | import java.sql.SQLFeatureNotSupportedException; 24 | import java.sql.SQLXML; 25 | import java.sql.Time; 26 | import java.sql.Timestamp; 27 | import java.util.Arrays; 28 | import java.util.Calendar; 29 | 30 | import static com.intellij.DateUtil.Direction; 31 | import static com.intellij.DateUtil.considerTimeZone; 32 | import static java.lang.Math.max; 33 | 34 | public class CouchbasePreparedStatement extends CouchbaseBaseStatement implements PreparedStatement { 35 | private final String sql; 36 | private final Object[] params; 37 | 38 | CouchbasePreparedStatement(@NotNull CouchbaseConnection connection, @NotNull String sql) { 39 | super(connection); 40 | this.sql = sql; 41 | this.params = new Object[countPossibleParametersNumber(sql)]; 42 | } 43 | 44 | @Override 45 | public ResultSet executeQuery() throws SQLException { 46 | checkClosed(); 47 | execute(); 48 | if (result == null) { 49 | throw new SQLException("No result set"); 50 | } 51 | return result; 52 | } 53 | 54 | @Override 55 | public ResultSet executeQuery(String sql) throws SQLException { 56 | throw new SQLException("Method should not be called on prepared statement"); 57 | } 58 | 59 | @Override 60 | public boolean execute(final String sql) throws SQLException { 61 | throw new SQLException("Method should not be called on prepared statement"); 62 | } 63 | 64 | @Override 65 | public void setObject(int parameterIndex, Object value) { 66 | int idx = parameterIndex - 1; 67 | params[idx] = value; 68 | } 69 | 70 | private static int countPossibleParametersNumber(@NotNull String sql) { 71 | int size = 0; 72 | for (char character : sql.toCharArray()) { 73 | if (character == '?') { 74 | size++; 75 | } 76 | } 77 | return size; 78 | } 79 | 80 | @Override 81 | public void addBatch(String sql) throws SQLException { 82 | throw new SQLException("Method should not be called on prepared statement"); 83 | } 84 | 85 | @Override 86 | public void clearBatch() throws SQLException { 87 | throw new SQLFeatureNotSupportedException(); 88 | } 89 | 90 | @Override 91 | public int executeUpdate() throws SQLException { 92 | checkClosed(); 93 | execute(); 94 | return max(0, getUpdateCount()); 95 | } 96 | 97 | @Override 98 | public boolean execute() throws SQLException { 99 | checkClosed(); 100 | try { 101 | return executeInner(sql, cluster.reactive().query(sql, bindParameters())); 102 | } catch (Throwable t) { 103 | throw new SQLException(t.getMessage(), t); 104 | } 105 | } 106 | 107 | private QueryOptions bindParameters() { 108 | try { 109 | QueryOptions options = makeQueryOptions() 110 | .adhoc(false); 111 | if (params != null && params.length > 0) { 112 | options = options.parameters(JsonArray.from(params)); 113 | } 114 | return options; 115 | } finally { 116 | clearParams(); 117 | } 118 | } 119 | 120 | private void clearParams() { 121 | if (params == null) return; 122 | Arrays.fill(params, null); 123 | } 124 | 125 | @Override 126 | public int executeUpdate(String sql) throws SQLException { 127 | throw new SQLException("Method should not be called on prepared statement"); 128 | } 129 | 130 | @Override 131 | public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { 132 | throw new SQLException("Method should not be called on prepared statement"); 133 | } 134 | 135 | @Override 136 | public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { 137 | throw new SQLException("Method should not be called on prepared statement"); 138 | } 139 | 140 | @Override 141 | public int executeUpdate(String sql, String[] columnNames) throws SQLException { 142 | throw new SQLException("Method should not be called on prepared statement"); 143 | } 144 | 145 | @Override 146 | public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { 147 | throw new SQLException("Method should not be called on prepared statement"); 148 | } 149 | 150 | @Override 151 | public boolean execute(String sql, int[] columnIndexes) throws SQLException { 152 | throw new SQLException("Method should not be called on prepared statement"); 153 | } 154 | 155 | @Override 156 | public boolean execute(String sql, String[] columnNames) throws SQLException { 157 | throw new SQLException("Method should not be called on prepared statement"); 158 | } 159 | 160 | @Override 161 | public void setNull(int parameterIndex, int sqlType) throws SQLException { 162 | checkClosed(); 163 | setObject(parameterIndex, null); 164 | } 165 | 166 | @Override 167 | public void setBoolean(int parameterIndex, boolean x) throws SQLException { 168 | checkClosed(); 169 | setObject(parameterIndex, x); 170 | } 171 | 172 | @Override 173 | public void setByte(int parameterIndex, byte x) throws SQLException { 174 | checkClosed(); 175 | setObject(parameterIndex, x); 176 | } 177 | 178 | @Override 179 | public void setShort(int parameterIndex, short x) throws SQLException { 180 | checkClosed(); 181 | setObject(parameterIndex, x); 182 | } 183 | 184 | @Override 185 | public void setInt(int parameterIndex, int x) throws SQLException { 186 | checkClosed(); 187 | setObject(parameterIndex, x); 188 | } 189 | 190 | @Override 191 | public void setLong(int parameterIndex, long x) throws SQLException { 192 | checkClosed(); 193 | setObject(parameterIndex, x); 194 | } 195 | 196 | @Override 197 | public void setFloat(int parameterIndex, float x) throws SQLException { 198 | checkClosed(); 199 | setObject(parameterIndex, x); 200 | } 201 | 202 | @Override 203 | public void setDouble(int parameterIndex, double x) throws SQLException { 204 | checkClosed(); 205 | setObject(parameterIndex, x); 206 | } 207 | 208 | @Override 209 | public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { 210 | checkClosed(); 211 | setObject(parameterIndex, x); 212 | } 213 | 214 | @Override 215 | public void setString(int parameterIndex, String x) throws SQLException { 216 | checkClosed(); 217 | setObject(parameterIndex, x); 218 | } 219 | 220 | @Override 221 | public void setBytes(int parameterIndex, byte[] x) throws SQLException { 222 | checkClosed(); 223 | setObject(parameterIndex, x); 224 | } 225 | 226 | @Override 227 | public void setDate(int parameterIndex, Date x) throws SQLException { 228 | checkClosed(); 229 | setObject(parameterIndex, x); 230 | } 231 | 232 | @Override 233 | public void setTime(int parameterIndex, Time x) throws SQLException { 234 | checkClosed(); 235 | setObject(parameterIndex, x); 236 | } 237 | 238 | @Override 239 | public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { 240 | checkClosed(); 241 | setObject(parameterIndex, x); 242 | } 243 | 244 | @Override 245 | public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { 246 | checkClosed(); 247 | setObject(parameterIndex, x); 248 | } 249 | 250 | @Override 251 | public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { 252 | checkClosed(); 253 | setObject(parameterIndex, x); 254 | } 255 | 256 | @Override 257 | public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { 258 | checkClosed(); 259 | setObject(parameterIndex, x); 260 | } 261 | 262 | @Override 263 | public void clearParameters() throws SQLException { 264 | checkClosed(); 265 | clearParams(); 266 | } 267 | 268 | @Override 269 | public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { 270 | throw new SQLFeatureNotSupportedException(); 271 | } 272 | 273 | @Override 274 | public void addBatch() throws SQLException { 275 | throw new SQLFeatureNotSupportedException(); 276 | } 277 | 278 | @Override 279 | public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { 280 | throw new SQLFeatureNotSupportedException(); 281 | } 282 | 283 | @Override 284 | public void setRef(int parameterIndex, Ref x) throws SQLException { 285 | throw new SQLFeatureNotSupportedException(); 286 | } 287 | 288 | @Override 289 | public void setBlob(int parameterIndex, Blob x) throws SQLException { 290 | throw new SQLFeatureNotSupportedException(); 291 | } 292 | 293 | @Override 294 | public void setClob(int parameterIndex, Clob x) throws SQLException { 295 | throw new SQLFeatureNotSupportedException(); 296 | } 297 | 298 | @Override 299 | public void setArray(int parameterIndex, Array x) throws SQLException { 300 | checkClosed(); 301 | setObject(parameterIndex, x); 302 | } 303 | 304 | @Override 305 | public ResultSetMetaData getMetaData() throws SQLException { 306 | throw new SQLFeatureNotSupportedException(); 307 | } 308 | 309 | @Override 310 | public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { 311 | checkClosed(); 312 | Date result = considerTimeZone(x, cal, Direction.TO_UTC); 313 | setObject(parameterIndex, result); 314 | } 315 | 316 | @Override 317 | public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { 318 | checkClosed(); 319 | Time result = considerTimeZone(x, cal, Direction.TO_UTC); 320 | setObject(parameterIndex, result); 321 | } 322 | 323 | @Override 324 | public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { 325 | checkClosed(); 326 | Timestamp result = considerTimeZone(x, cal, Direction.TO_UTC); 327 | setObject(parameterIndex, result); 328 | } 329 | 330 | @Override 331 | public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { 332 | throw new SQLFeatureNotSupportedException(); 333 | 334 | } 335 | 336 | @Override 337 | public void setURL(int parameterIndex, URL x) throws SQLException { 338 | throw new SQLFeatureNotSupportedException(); 339 | } 340 | 341 | @Override 342 | public ParameterMetaData getParameterMetaData() throws SQLException { 343 | throw new SQLFeatureNotSupportedException(); 344 | } 345 | 346 | @Override 347 | public void setRowId(int parameterIndex, RowId x) throws SQLException { 348 | throw new SQLFeatureNotSupportedException(); 349 | } 350 | 351 | @Override 352 | public void setNString(int parameterIndex, String value) throws SQLException { 353 | throw new SQLFeatureNotSupportedException(); 354 | } 355 | 356 | @Override 357 | public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { 358 | throw new SQLFeatureNotSupportedException(); 359 | } 360 | 361 | @Override 362 | public void setNClob(int parameterIndex, NClob value) throws SQLException { 363 | throw new SQLFeatureNotSupportedException(); 364 | } 365 | 366 | @Override 367 | public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { 368 | throw new SQLFeatureNotSupportedException(); 369 | } 370 | 371 | @Override 372 | public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { 373 | throw new SQLFeatureNotSupportedException(); 374 | } 375 | 376 | @Override 377 | public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { 378 | throw new SQLFeatureNotSupportedException(); 379 | } 380 | 381 | @Override 382 | public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { 383 | throw new SQLFeatureNotSupportedException(); 384 | } 385 | 386 | @Override 387 | public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) { 388 | setObject(parameterIndex, x); 389 | } 390 | 391 | @Override 392 | public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { 393 | throw new SQLFeatureNotSupportedException(); 394 | } 395 | 396 | @Override 397 | public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { 398 | throw new SQLFeatureNotSupportedException(); 399 | } 400 | 401 | @Override 402 | public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { 403 | throw new SQLFeatureNotSupportedException(); 404 | } 405 | 406 | @Override 407 | public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { 408 | throw new SQLFeatureNotSupportedException(); 409 | } 410 | 411 | @Override 412 | public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { 413 | throw new SQLFeatureNotSupportedException(); 414 | } 415 | 416 | @Override 417 | public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { 418 | throw new SQLFeatureNotSupportedException(); 419 | } 420 | 421 | @Override 422 | public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { 423 | throw new SQLFeatureNotSupportedException(); 424 | } 425 | 426 | @Override 427 | public void setClob(int parameterIndex, Reader reader) throws SQLException { 428 | throw new SQLFeatureNotSupportedException(); 429 | } 430 | 431 | @Override 432 | public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { 433 | throw new SQLFeatureNotSupportedException(); 434 | } 435 | 436 | @Override 437 | public void setNClob(int parameterIndex, Reader reader) throws SQLException { 438 | throw new SQLFeatureNotSupportedException(); 439 | } 440 | } 441 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/CouchbaseSqlLikePattern.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | import java.util.regex.Pattern; 9 | 10 | public class CouchbaseSqlLikePattern { 11 | private static final Set SPECIAL_REGEX_CHARS = new HashSet<>(); 12 | 13 | private final String plain; 14 | private final Pattern pattern; 15 | 16 | static { 17 | SPECIAL_REGEX_CHARS.add('['); 18 | SPECIAL_REGEX_CHARS.add(']'); 19 | SPECIAL_REGEX_CHARS.add('{'); 20 | SPECIAL_REGEX_CHARS.add('}'); 21 | SPECIAL_REGEX_CHARS.add('('); 22 | SPECIAL_REGEX_CHARS.add(')'); 23 | SPECIAL_REGEX_CHARS.add('\\'); 24 | SPECIAL_REGEX_CHARS.add('.'); 25 | SPECIAL_REGEX_CHARS.add('*'); 26 | SPECIAL_REGEX_CHARS.add('+'); 27 | SPECIAL_REGEX_CHARS.add('?'); 28 | SPECIAL_REGEX_CHARS.add('^'); 29 | SPECIAL_REGEX_CHARS.add('$'); 30 | SPECIAL_REGEX_CHARS.add('|'); 31 | } 32 | 33 | private CouchbaseSqlLikePattern() { 34 | this.plain = null; 35 | this.pattern = null; 36 | } 37 | 38 | private CouchbaseSqlLikePattern(@NotNull Pattern pattern) { 39 | this.plain = null; 40 | this.pattern = pattern; 41 | } 42 | 43 | private CouchbaseSqlLikePattern(@NotNull String plain) { 44 | this.plain = plain; 45 | this.pattern = null; 46 | } 47 | 48 | @Nullable 49 | public String asPlain() { 50 | return plain; 51 | } 52 | 53 | public boolean matches(@NotNull String name) { 54 | return plain != null ? plain.equals(name) : 55 | pattern == null || pattern.matcher(name).matches(); 56 | } 57 | 58 | /** 59 | * Given an inputPattern using SQL syntax (e.g. % for wildcard, and '_' for single character) 60 | * generate a Java Pattern that can be used to validate input. 61 | * 62 | * @param inputPattern The String representing the SQL pattern. 63 | * @return A suitable Pattern if the input has wildcard characters in it, or NULL if no pattern matching required. 64 | */ 65 | @NotNull 66 | public static CouchbaseSqlLikePattern create(@Nullable String inputPattern) throws IllegalArgumentException { 67 | if (inputPattern == null) return new CouchbaseSqlLikePattern(); 68 | String plain = toPlain(inputPattern); 69 | if (plain != null) return new CouchbaseSqlLikePattern(plain); 70 | 71 | boolean escaped = false; 72 | StringBuilder sb = new StringBuilder(); 73 | for (int i = 0; i < inputPattern.length(); i++) { 74 | char c = inputPattern.charAt(i); 75 | if (c == '\\') { 76 | if (escaped) sb.append("\\\\"); 77 | escaped = !escaped; 78 | } 79 | else if (escaped) { 80 | escaped = false; 81 | if (c == '_' || c == '%') sb.append(c); 82 | else 83 | throw new IllegalArgumentException("Illegal char escape " + c + " at position " + i + " string: " + inputPattern); 84 | } 85 | else { 86 | if (c == '_') sb.append("."); 87 | else if (c == '%') sb.append(".*"); 88 | else if (SPECIAL_REGEX_CHARS.contains(c)) sb.append('\\').append(c); 89 | else sb.append(c); 90 | } 91 | } 92 | 93 | String pattern = sb.toString(); 94 | return pattern.equals(".*") ? new CouchbaseSqlLikePattern() : new CouchbaseSqlLikePattern(Pattern.compile(pattern)); 95 | } 96 | 97 | @Nullable 98 | private static String toPlain(@NotNull String inputPattern) { 99 | boolean escaped = false; 100 | StringBuilder plain = new StringBuilder(); 101 | for (int i = 0; i < inputPattern.length(); i++) { 102 | char c = inputPattern.charAt(i); 103 | if (c == '\\') { 104 | if (escaped) plain.append("\\"); 105 | escaped = !escaped; 106 | } 107 | else if (escaped) { 108 | escaped = false; 109 | if (c == '_') plain.append("_"); 110 | else if (c == '%') plain.append("%"); 111 | else 112 | throw new IllegalArgumentException("Illegal char escape " + c + " at position " + i + " string: " + inputPattern); 113 | } 114 | else { 115 | if (c == '_' || c == '%') return null; // unescaped pattern entities 116 | plain.append(c); 117 | } 118 | } 119 | return plain.toString(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/CouchbaseStatement.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import com.intellij.executor.ExecutionResult; 4 | import com.intellij.resultset.CouchbaseListResultSet; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.sql.SQLFeatureNotSupportedException; 10 | 11 | import static com.intellij.executor.CouchbaseCustomStatementExecutor.tryExecuteDdlStatement; 12 | import static java.lang.Math.max; 13 | 14 | public class CouchbaseStatement extends CouchbaseBaseStatement { 15 | CouchbaseStatement(@NotNull CouchbaseConnection connection) { 16 | super(connection); 17 | } 18 | 19 | @Override 20 | public ResultSet executeQuery(@NotNull String sql) throws SQLException { 21 | checkClosed(); 22 | execute(sql); 23 | if (result == null) { 24 | throw new SQLException("No result set"); 25 | } 26 | return result; 27 | } 28 | 29 | CouchbaseListResultSet executeMetaQuery(@NotNull String sql) throws SQLException { 30 | checkClosed(); 31 | try { 32 | return new CouchbaseListResultSet(cluster.query(sql, makeQueryOptions())); 33 | } catch (Throwable t) { 34 | throw new SQLException(t.getMessage(), t); 35 | } 36 | } 37 | 38 | @Override 39 | public int executeUpdate(@NotNull String sql) throws SQLException { 40 | checkClosed(); 41 | execute(sql); 42 | return max(0, getUpdateCount()); 43 | } 44 | 45 | @Override 46 | public boolean execute(@NotNull String sql) throws SQLException { 47 | checkClosed(); 48 | try { 49 | ExecutionResult executionResult = tryExecuteDdlStatement(connection, sql); 50 | if (executionResult.isSuccess()) { 51 | ResultSet resultSet = executionResult.getResultSet(); 52 | setNewResultSet(resultSet); 53 | return resultSet != null; 54 | } 55 | return executeInner(sql, cluster.reactive().query(sql, makeQueryOptions())); 56 | } catch (Throwable t) { 57 | throw new SQLException(t); 58 | } 59 | } 60 | 61 | @Override 62 | public void addBatch(String sql) throws SQLException { 63 | throw new SQLFeatureNotSupportedException(); 64 | } 65 | 66 | @Override 67 | public void clearBatch() throws SQLException { 68 | throw new SQLFeatureNotSupportedException(); 69 | } 70 | 71 | @Override 72 | public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { 73 | throw new SQLFeatureNotSupportedException(); 74 | } 75 | 76 | @Override 77 | public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { 78 | throw new SQLFeatureNotSupportedException(); 79 | } 80 | 81 | @Override 82 | public int executeUpdate(String sql, String[] columnNames) throws SQLException { 83 | throw new SQLFeatureNotSupportedException(); 84 | } 85 | 86 | @Override 87 | public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { 88 | throw new SQLFeatureNotSupportedException(); 89 | } 90 | 91 | @Override 92 | public boolean execute(String sql, int[] columnIndexes) throws SQLException { 93 | throw new SQLFeatureNotSupportedException(); 94 | } 95 | 96 | @Override 97 | public boolean execute(String sql, String[] columnNames) throws SQLException { 98 | throw new SQLFeatureNotSupportedException(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/DateUtil.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import java.sql.SQLException; 4 | import java.sql.Time; 5 | import java.sql.Timestamp; 6 | import java.text.DateFormat; 7 | import java.text.ParseException; 8 | import java.text.SimpleDateFormat; 9 | import java.util.Calendar; 10 | import java.util.Date; 11 | import java.util.TimeZone; 12 | import java.util.function.Function; 13 | 14 | /** 15 | * @author Liudmila Kornilova 16 | **/ 17 | class DateUtil { 18 | private static final SimpleDateFormat utcDateFormat = new SimpleDateFormat("yyyy-MM-dd"); 19 | private static final SimpleDateFormat utcTimeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); 20 | private static final SimpleDateFormat utcDateTimeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); 21 | private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); 22 | 23 | static { 24 | utcDateFormat.setTimeZone(UTC); 25 | utcTimeFormat.setTimeZone(UTC); 26 | utcDateTimeFormat.setTimeZone(UTC); 27 | } 28 | 29 | private static SimpleDateFormat getDateFormat(TimeZone timeZone) { 30 | if (timeZone.equals(UTC)) return utcDateFormat; 31 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 32 | dateFormat.setTimeZone(timeZone); 33 | return dateFormat; 34 | } 35 | 36 | private static SimpleDateFormat getTimeFormat(TimeZone timeZone) { 37 | if (timeZone.equals(UTC)) return utcTimeFormat; 38 | SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); 39 | timeFormat.setTimeZone(timeZone); 40 | return timeFormat; 41 | } 42 | 43 | private static SimpleDateFormat getDateTimeFormat(TimeZone timeZone) { 44 | if (timeZone.equals(UTC)) return utcDateTimeFormat; 45 | SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); 46 | dateTimeFormat.setTimeZone(timeZone); 47 | return dateTimeFormat; 48 | } 49 | 50 | static Timestamp considerTimeZone(Timestamp timestamp, Calendar calendar, Direction direction) throws SQLException { 51 | long time = considerTimeZone(timestamp, calendar.getTimeZone(), direction, DateUtil::getDateTimeFormat); 52 | Timestamp result = new Timestamp(time); 53 | result.setNanos(timestamp.getNanos()); 54 | return result; 55 | } 56 | 57 | static java.sql.Date considerTimeZone(java.sql.Date timestamp, Calendar calendar, Direction direction) throws SQLException { 58 | long time = considerTimeZone(timestamp, calendar.getTimeZone(), direction, DateUtil::getDateFormat); 59 | return new java.sql.Date(time); 60 | } 61 | 62 | static Time considerTimeZone(Time timestamp, Calendar calendar, Direction direction) throws SQLException { 63 | long time = considerTimeZone(timestamp, calendar.getTimeZone(), direction, DateUtil::getTimeFormat); 64 | return new Time(time); 65 | } 66 | 67 | private static long considerTimeZone(T date, TimeZone timeZone, Direction direction, 68 | Function formatSupplier) throws SQLException { 69 | String startValue = direction.formatter(timeZone, formatSupplier).format(date); 70 | try { 71 | Date resultDate = direction.parser(timeZone, formatSupplier).parse(startValue); 72 | return resultDate.getTime(); 73 | } catch (ParseException e) { 74 | throw new SQLException(e); 75 | } 76 | } 77 | 78 | enum Direction { 79 | FROM_UTC { 80 | @Override 81 | DateFormat formatter(TimeZone timeZone, Function formatSupplier) { 82 | return formatSupplier.apply(UTC); 83 | } 84 | 85 | @Override 86 | DateFormat parser(TimeZone timeZone, Function formatSupplier) { 87 | return formatSupplier.apply(timeZone); 88 | } 89 | }, 90 | TO_UTC { 91 | @Override 92 | DateFormat formatter(TimeZone timeZone, Function formatSupplier) { 93 | return FROM_UTC.parser(timeZone, formatSupplier); 94 | } 95 | 96 | @Override 97 | DateFormat parser(TimeZone timeZone, Function formatSupplier) { 98 | return FROM_UTC.formatter(timeZone, formatSupplier); 99 | } 100 | }; 101 | 102 | abstract DateFormat formatter(TimeZone timeZone, Function formatSupplier); 103 | 104 | abstract DateFormat parser(TimeZone timeZone, Function formatSupplier); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/DriverPropertyInfoHelper.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import com.couchbase.client.java.query.QueryScanConsistency; 4 | 5 | import java.sql.DriverPropertyInfo; 6 | import java.util.AbstractMap; 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.Locale; 10 | import java.util.Map; 11 | import java.util.Properties; 12 | import java.util.stream.Collectors; 13 | import java.util.stream.Stream; 14 | 15 | public class DriverPropertyInfoHelper { 16 | public static final String ENABLE_SSL = "sslenabled"; 17 | public static final String ENABLE_SSL_DEFAULT = "false"; 18 | public static final String VERIFY_SERVER_CERTIFICATE = "verifyServerCertificate"; 19 | public static final String VERIFY_SERVER_CERTIFICATE_DEFAULT = "true"; 20 | public static final String VERIFY_HOSTNAMES = "verifyHostnames"; 21 | public static final String VERIFY_HOSTNAMES_DEFAULT = "true"; 22 | private static final String[] BOOL_CHOICES = new String[]{"true", "false"}; 23 | 24 | public static final String USER = "user"; 25 | public static final String PASSWORD = "password"; 26 | 27 | public static final String META_SAMPLING_SIZE = "meta.sampling.size"; 28 | public static final int META_SAMPLING_SIZE_DEFAULT = 1000; 29 | 30 | public static final String DEFAULT_BUCKET = "defaultBucket"; 31 | 32 | public static final String LOGGING_LEVEL = "loggingLevel"; 33 | public static final String LOGGING_LEVEL_DEFAULT = "severe"; 34 | private static final String[] LOGGING_LEVEL_CHOICES = new String[]{"off", "severe", "warning", "info", "fine", "all"}; 35 | 36 | 37 | public static DriverPropertyInfo[] getPropertyInfo() { 38 | ArrayList propInfos = new ArrayList<>(); 39 | 40 | addPropInfo(propInfos, DEFAULT_BUCKET, "", "If you are connecting to a pre Couchbase 6.5 cluster, a default bucket must be specified in order to properly initialize the connection.", null); 41 | addPropInfo(propInfos, ENABLE_SSL, ENABLE_SSL_DEFAULT, "Enable ssl.", BOOL_CHOICES); 42 | addPropInfo(propInfos, VERIFY_SERVER_CERTIFICATE, VERIFY_SERVER_CERTIFICATE_DEFAULT, 43 | "Configure a connection that uses SSL but does not verify the identity of the server.", 44 | BOOL_CHOICES); 45 | addPropInfo(propInfos, VERIFY_HOSTNAMES, VERIFY_HOSTNAMES_DEFAULT, 46 | "Verifies the server host to ensure that it matches the name stored in the server certificate.", 47 | BOOL_CHOICES); 48 | addPropInfo(propInfos, USER, "", "Username.", null); 49 | addPropInfo(propInfos, PASSWORD, "", "Password.", null); 50 | addPropInfo(propInfos, META_SAMPLING_SIZE, Integer.toString(META_SAMPLING_SIZE_DEFAULT), 51 | "Number of documents that will be fetched per collection in order " + 52 | "to return meta information from DatabaseMetaData.getColumns method.", null); 53 | addPropInfo(propInfos, ScanConsistency.QUERY_SCAN_CONSISTENCY, 54 | ScanConsistency.QUERY_SCAN_CONSISTENCY_DEFAULT.toString(), 55 | "Query scan consistency.", 56 | ScanConsistency.CHOICES); 57 | 58 | addPropInfo(propInfos, LOGGING_LEVEL, LOGGING_LEVEL_DEFAULT, "", LOGGING_LEVEL_CHOICES); 59 | 60 | return propInfos.toArray(new DriverPropertyInfo[0]); 61 | } 62 | 63 | private static void addPropInfo(final ArrayList propInfos, final String propName, 64 | final String defaultVal, final String description, final String[] choices) { 65 | DriverPropertyInfo newProp = new DriverPropertyInfo(propName, defaultVal); 66 | newProp.description = description; 67 | if (choices != null) { 68 | newProp.choices = choices; 69 | } 70 | propInfos.add(newProp); 71 | } 72 | 73 | public static boolean isTrue(String value) { 74 | return value != null && (value.equals("1") || value.toLowerCase(Locale.ENGLISH).equals("true")); 75 | } 76 | 77 | public static class ScanConsistency { 78 | private ScanConsistency() { 79 | // empty 80 | } 81 | 82 | public static final String QUERY_SCAN_CONSISTENCY = "query.scan.consistency"; 83 | public static final QueryScanConsistency QUERY_SCAN_CONSISTENCY_DEFAULT = QueryScanConsistency.NOT_BOUNDED; 84 | private static final Map MAPPING = Collections.unmodifiableMap(Stream 85 | .of(new AbstractMap.SimpleEntry<>("not_bounded", QueryScanConsistency.NOT_BOUNDED), 86 | new AbstractMap.SimpleEntry<>("request_plus", QueryScanConsistency.REQUEST_PLUS)) 87 | .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); 88 | private static final String[] CHOICES = MAPPING.keySet().toArray(new String[0]); 89 | 90 | public static QueryScanConsistency getQueryScanConsistency(Properties properties) { 91 | return getQueryScanConsistency(properties.getProperty(QUERY_SCAN_CONSISTENCY)); 92 | } 93 | 94 | public static QueryScanConsistency getQueryScanConsistency(String scanConsistency) { 95 | if (scanConsistency == null) { 96 | return QUERY_SCAN_CONSISTENCY_DEFAULT; 97 | } 98 | return MAPPING.getOrDefault(scanConsistency, QUERY_SCAN_CONSISTENCY_DEFAULT); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/EscapingUtil.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import org.jetbrains.annotations.Contract; 4 | import org.jetbrains.annotations.NotNull; 5 | import reactor.util.annotation.Nullable; 6 | 7 | public class EscapingUtil { 8 | private static final String BACKQUOTE = "`"; 9 | 10 | private EscapingUtil() { 11 | //empty 12 | } 13 | 14 | @Nullable 15 | @Contract("null -> null") 16 | public static String stripBackquotes(@Nullable String s) { 17 | if (s == null) { 18 | return null; 19 | } 20 | if (s.startsWith(BACKQUOTE) && s.endsWith(BACKQUOTE)) { 21 | return s.substring(1, s.length() - 1); 22 | } 23 | return s; 24 | } 25 | 26 | @Nullable 27 | @Contract("null -> null") 28 | public static String wrapInBackquotes(@Nullable String s) { 29 | if (s == null) { 30 | return null; 31 | } 32 | return BACKQUOTE + s + BACKQUOTE; 33 | } 34 | 35 | @NotNull 36 | public static String escapeChars(@NotNull final String str, final char... character) { 37 | final StringBuilder buf = new StringBuilder(str); 38 | for (char c : character) { 39 | escapeChar(buf, c); 40 | } 41 | return buf.toString(); 42 | } 43 | 44 | public static void escapeChar(@NotNull final StringBuilder buf, final char character) { 45 | int idx = 0; 46 | while ((idx = indexOf(buf, character, idx)) >= 0) { 47 | buf.insert(idx, "\\"); 48 | idx += 2; 49 | } 50 | } 51 | 52 | @Contract(pure = true) 53 | public static int indexOf(@NotNull CharSequence s, char c, int start) { 54 | return indexOf(s, c, start, s.length()); 55 | } 56 | 57 | @Contract(pure = true) 58 | public static int indexOf(@NotNull CharSequence s, char c, int start, int end) { 59 | end = Math.min(end, s.length()); 60 | for (int i = Math.max(start, 0); i < end; i++) { 61 | if (s.charAt(i) == c) return i; 62 | } 63 | return -1; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/ObjectUtil.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import org.jetbrains.annotations.Contract; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public final class ObjectUtil { 8 | private ObjectUtil() {} 9 | 10 | @Contract(value = "null, _ -> null", pure = true) 11 | public static @Nullable T tryCast(@Nullable Object obj, @NotNull Class clazz) { 12 | if (clazz.isInstance(obj)) { 13 | return clazz.cast(obj); 14 | } 15 | return null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/SslKeyStoreConfig.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import java.nio.file.Path; 4 | import java.nio.file.Paths; 5 | import java.security.KeyStore; 6 | import java.sql.SQLException; 7 | import java.util.Optional; 8 | import java.util.logging.Logger; 9 | 10 | class SslKeyStoreConfig { 11 | private final String storeType; 12 | private final String storePassword; 13 | private final String storeUrl; 14 | 15 | private SslKeyStoreConfig(String storeType, String storePassword, String storeUrl) { 16 | this.storeType = storeType; 17 | this.storePassword = storePassword; 18 | this.storeUrl = storeUrl; 19 | } 20 | 21 | public static SslKeyStoreConfig create(Type type) throws SQLException { 22 | String storeType = System.getProperty("javax.net.ssl." + type + "StoreType", KeyStore.getDefaultType()); 23 | String storePassword = System.getProperty("javax.net.ssl." + type + "StorePassword"); 24 | String storeUrl = System.getProperty("javax.net.ssl." + type + "Store", ""); 25 | if (storeUrl == null || storeUrl.isEmpty()) { 26 | throw new SQLException(type + "Store url is not provided"); 27 | } 28 | return new SslKeyStoreConfig(storeType, storePassword, storeUrl); 29 | } 30 | 31 | public Optional getType() { 32 | return Optional.ofNullable(storeType); 33 | } 34 | 35 | public String getPassword() { 36 | return storePassword; 37 | } 38 | 39 | public Path getPath() { 40 | return Paths.get(storeUrl); 41 | } 42 | 43 | public enum Type { 44 | KEY_STORE("key"), TRUST_STORE("trust"); 45 | 46 | private final String key; 47 | 48 | Type(String key) { 49 | this.key = key; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return key; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/StringUtil.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import org.jetbrains.annotations.Contract; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public class StringUtil { 8 | @Contract(pure = true) 9 | public static @NotNull String trimEnd(@NotNull String s, char suffix) { 10 | if (endsWithChar(s, suffix)) { 11 | return s.substring(0, s.length() - 1); 12 | } 13 | return s; 14 | } 15 | 16 | @Contract(pure = true) 17 | public static boolean endsWithChar(@Nullable CharSequence s, char suffix) { 18 | return s != null && s.length() != 0 && s.charAt(s.length() - 1) == suffix; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/executor/BucketSettingsDto.java: -------------------------------------------------------------------------------- 1 | package com.intellij.executor; 2 | 3 | import com.couchbase.client.java.manager.bucket.*; 4 | 5 | import java.time.Duration; 6 | 7 | class BucketSettingsDto { 8 | public Boolean flushEnabled; 9 | public Long ramQuotaMB; 10 | public Integer replicaNumber; 11 | public Boolean replicaIndexes; 12 | public Integer maxTTL; 13 | public CompressionMode compressionMode; 14 | public BucketType bucketType; 15 | public ConflictResolutionType conflictResolutionType; 16 | public EvictionPolicyType evictionPolicy; 17 | 18 | BucketSettings injectToBucketSettings(BucketSettings bucketSettings) { 19 | if (flushEnabled != null) { 20 | bucketSettings.flushEnabled(flushEnabled); 21 | } 22 | if (ramQuotaMB != null) { 23 | bucketSettings.ramQuotaMB(ramQuotaMB); 24 | } 25 | if (replicaNumber != null) { 26 | bucketSettings.numReplicas(replicaNumber); 27 | } 28 | if (replicaIndexes != null) { 29 | bucketSettings.replicaIndexes(replicaIndexes); 30 | } 31 | if (maxTTL != null) { 32 | bucketSettings.maxExpiry(Duration.ofSeconds(maxTTL)); 33 | } 34 | if (compressionMode != null) { 35 | bucketSettings.compressionMode(compressionMode); 36 | } 37 | if (bucketType != null) { 38 | bucketSettings.bucketType(bucketType); 39 | } 40 | if (conflictResolutionType != null) { 41 | bucketSettings.conflictResolutionType(conflictResolutionType); 42 | } 43 | if (evictionPolicy != null) { 44 | bucketSettings.evictionPolicy(evictionPolicy); 45 | } 46 | return bucketSettings; 47 | } 48 | 49 | static BucketSettingsDto extractBucketSettings(BucketSettings bucketSettings) { 50 | BucketSettingsDto dto = new BucketSettingsDto(); 51 | dto.flushEnabled = bucketSettings.flushEnabled(); 52 | dto.ramQuotaMB = bucketSettings.ramQuotaMB(); 53 | dto.replicaNumber = bucketSettings.numReplicas(); 54 | dto.replicaIndexes = bucketSettings.replicaIndexes(); 55 | dto.maxTTL = (int) bucketSettings.maxExpiry().getSeconds(); 56 | dto.compressionMode = bucketSettings.compressionMode(); 57 | dto.bucketType = bucketSettings.bucketType(); 58 | dto.conflictResolutionType = bucketSettings.conflictResolutionType(); 59 | dto.evictionPolicy = bucketSettings.evictionPolicy(); 60 | return dto; 61 | } 62 | 63 | @Override 64 | public String toString() { 65 | return "CreateBucketSettings{" + 66 | "flushEnabled=" + flushEnabled + 67 | ", ramQuotaMB=" + ramQuotaMB + 68 | ", replicaNumber=" + replicaNumber + 69 | ", replicaIndexes=" + replicaIndexes + 70 | ", maxTTL=" + maxTTL + 71 | ", compressionMode=" + compressionMode + 72 | ", bucketType=" + bucketType + 73 | ", conflictResolutionType=" + conflictResolutionType + 74 | ", evictionPolicy=" + evictionPolicy + 75 | '}'; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/executor/CouchbaseCustomStatementExecutor.java: -------------------------------------------------------------------------------- 1 | package com.intellij.executor; 2 | 3 | import com.intellij.CouchbaseConnection; 4 | import com.intellij.StringUtil; 5 | 6 | import java.sql.SQLException; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | public class CouchbaseCustomStatementExecutor { 11 | private static final List CUSTOM_EXECUTORS = 12 | Arrays.asList(new CreateBucketExecutor(), new DropBucketExecutor(), new DescribeBucketExecutor(), 13 | new DescribeIndexExecutor(), new GetBucketKeysExecutor()); 14 | 15 | public static boolean mayExecute(String sql) { 16 | String trimmedSql = trim(sql); 17 | return CUSTOM_EXECUTORS.stream() 18 | .anyMatch(executor -> executor.mayAccept(trimmedSql)); 19 | } 20 | 21 | private static String trim(String sql) { 22 | return StringUtil.trimEnd(sql.trim(), ';').trim(); 23 | } 24 | 25 | public static ExecutionResult tryExecuteDdlStatement(CouchbaseConnection connection, String sql) 26 | throws SQLException { 27 | sql = trim(sql); 28 | for (CustomDdlExecutor executor : CUSTOM_EXECUTORS) { 29 | if (executor.mayAccept(sql)) { 30 | if (connection.isReadOnly() && executor.isRequireWriteAccess()) { 31 | throw new SQLException( 32 | "The server or request is read-only and cannot accept this write statement."); 33 | } 34 | return executor.execute(connection, sql); 35 | } 36 | } 37 | return new ExecutionResult(false); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/executor/CreateBucketExecutor.java: -------------------------------------------------------------------------------- 1 | package com.intellij.executor; 2 | 3 | import com.couchbase.client.core.deps.com.fasterxml.jackson.core.JsonParser; 4 | import com.couchbase.client.core.deps.com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.couchbase.client.core.deps.com.fasterxml.jackson.databind.MapperFeature; 6 | import com.couchbase.client.core.deps.com.fasterxml.jackson.databind.json.JsonMapper; 7 | import com.couchbase.client.core.error.BucketExistsException; 8 | import com.couchbase.client.core.error.CouchbaseException; 9 | import com.couchbase.client.core.error.IndexNotFoundException; 10 | import com.couchbase.client.core.error.InternalServerFailureException; 11 | import com.couchbase.client.core.retry.reactor.Retry; 12 | import com.couchbase.client.java.Cluster; 13 | import com.couchbase.client.java.http.HttpPath; 14 | import com.couchbase.client.java.http.HttpResponse; 15 | import com.couchbase.client.java.http.HttpTarget; 16 | import com.couchbase.client.java.manager.bucket.BucketSettings; 17 | import com.couchbase.client.java.manager.query.CreatePrimaryQueryIndexOptions; 18 | import com.couchbase.client.java.manager.query.WatchQueryIndexesOptions; 19 | import com.intellij.CouchbaseConnection; 20 | import com.intellij.CouchbaseError; 21 | import com.intellij.EscapingUtil; 22 | import org.jetbrains.annotations.NotNull; 23 | import reactor.core.publisher.Mono; 24 | 25 | import java.sql.SQLException; 26 | import java.time.Duration; 27 | import java.util.Collections; 28 | import java.util.regex.Matcher; 29 | import java.util.regex.Pattern; 30 | 31 | import static com.couchbase.client.core.util.CbThrowables.findCause; 32 | import static com.couchbase.client.core.util.CbThrowables.hasCause; 33 | import static com.intellij.CouchbaseMetaData.SYSTEM_SCHEMA; 34 | import static java.util.regex.Pattern.CASE_INSENSITIVE; 35 | import static java.util.regex.Pattern.DOTALL; 36 | 37 | public class CreateBucketExecutor implements CustomDdlExecutor { 38 | private static final String WITH_INDEX = "(?\\s+WITH\\s+PRIMARY\\s+INDEX)?"; 39 | private static final String WAIT_UNTIL_READY = "(?\\s+WAIT\\s+UNTIL\\s+READY)?"; 40 | private static final Pattern CREATE_BUCKET_PATTERN = Pattern.compile( 41 | "CREATE\\s+(BUCKET|TABLE)" + WITH_INDEX + "\\s+" + BUCKET_NAME + WAIT_UNTIL_READY + 42 | "(?(?:\\s+WITH\\s+\\{.*})?)", CASE_INSENSITIVE | DOTALL); 43 | private static final WatchQueryIndexesOptions WATCH_PRIMARY = WatchQueryIndexesOptions 44 | .watchQueryIndexesOptions() 45 | .watchPrimary(true); 46 | private static final JsonMapper MAPPER = JsonMapper.builder() 47 | .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) 48 | .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) 49 | .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true) 50 | .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS, true) 51 | .build(); 52 | private static final String DEFAULT_INDEX_NAME = "#primary"; 53 | 54 | @Override 55 | public boolean mayAccept(@NotNull String sql) { 56 | return CREATE_BUCKET_PATTERN.matcher(sql).matches(); 57 | } 58 | 59 | @Override 60 | public boolean isRequireWriteAccess() { 61 | return true; 62 | } 63 | 64 | @Override 65 | public ExecutionResult execute(@NotNull CouchbaseConnection connection, @NotNull String sql) throws SQLException { 66 | Matcher matcher = CREATE_BUCKET_PATTERN.matcher(sql); 67 | if (matcher.matches()) { 68 | Cluster cluster = connection.getCluster(); 69 | String name = EscapingUtil.stripBackquotes(matcher.group("bucket")); 70 | String schema = matcher.group("schema"); 71 | if (SYSTEM_SCHEMA.equals(schema)) { 72 | throw new SQLException("Cannot create bucket in system schema"); 73 | } 74 | try { 75 | BucketSettings bucketSettings = createBucketSettings(matcher, name); 76 | cluster.buckets().createBucket(bucketSettings); 77 | } catch (BucketExistsException ignore) { 78 | // ignore 79 | } 80 | if (matcher.group("index") != null) { 81 | Mono.fromRunnable(() -> createIndex(cluster, name)) 82 | .retryWhen(Retry.onlyIf(ctx -> 83 | findCause(ctx.exception(), InternalServerFailureException.class) 84 | .filter(exception -> CouchbaseError.create(exception) 85 | .getErrorEntries().stream() 86 | .anyMatch(err -> err.getMessage().contains("GSI"))) 87 | .isPresent()) 88 | .exponentialBackoff(Duration.ofMillis(50), Duration.ofSeconds(3)) 89 | .timeout(Duration.ofSeconds(60)) 90 | .toReactorRetry()) 91 | .block(); 92 | Mono.fromRunnable(() -> waitForIndex(cluster, name)) 93 | .retryWhen(Retry.onlyIf(ctx -> hasCause(ctx.exception(), IndexNotFoundException.class)) 94 | .exponentialBackoff(Duration.ofMillis(50), Duration.ofSeconds(3)) 95 | .timeout(Duration.ofSeconds(30)) 96 | .toReactorRetry()) 97 | .block(); 98 | IndexCommons.waitUntilReady(cluster, name, Duration.ofSeconds(60)); 99 | } else if (matcher.group("wait") != null) { 100 | waitForBucketSetup(name, connection.getCluster()); 101 | } 102 | return new ExecutionResult(true); 103 | } 104 | return new ExecutionResult(false); 105 | } 106 | 107 | private void waitForBucketSetup(String name, Cluster cluster) { 108 | Mono.fromRunnable(() -> { 109 | HttpResponse response; 110 | try { 111 | response = cluster.httpClient().get(HttpTarget.manager(), HttpPath.of("/pools/default/buckets/{}/docs?include_docs=false", name)); 112 | } catch (Exception e) { 113 | throw new CouchbaseException("Failed to request keys from " + name, e); 114 | } 115 | if (!response.success()) { 116 | throw new GetKeysException("Failed to to request keys from " + name + ": " 117 | + "Response status=" + response.statusCode() + " " 118 | + "Response body=" + response.contentAsString()); 119 | } 120 | }).retryWhen(Retry.any() 121 | .exponentialBackoff(Duration.ofMillis(50), Duration.ofSeconds(3)) 122 | .timeout(Duration.ofSeconds(30)) 123 | .toReactorRetry()) 124 | .block(); 125 | } 126 | 127 | private void createIndex(Cluster cluster, String bucketName) { 128 | cluster.queryIndexes().createPrimaryIndex(bucketName, 129 | CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions() 130 | .ignoreIfExists(true) 131 | .numReplicas(0)); 132 | } 133 | 134 | private void waitForIndex(Cluster cluster, String bucketName) { 135 | cluster.queryIndexes().watchIndexes(bucketName, Collections.singletonList(DEFAULT_INDEX_NAME), 136 | Duration.ofSeconds(10), WATCH_PRIMARY); 137 | } 138 | 139 | private static BucketSettings createBucketSettings(Matcher matcher, String name) throws SQLException { 140 | BucketSettings bucketSettings = BucketSettings.create(name); 141 | String paramsGroup = matcher.group("params"); 142 | if (!paramsGroup.isEmpty()) { 143 | String params = paramsGroup.substring(paramsGroup.indexOf("{"), paramsGroup.lastIndexOf("}") + 1); 144 | try { 145 | MAPPER.readValue(params, BucketSettingsDto.class) 146 | .injectToBucketSettings(bucketSettings); 147 | } catch (JsonProcessingException e) { 148 | throw new SQLException("Could not decode from JSON: " + params, e); 149 | } 150 | } 151 | return bucketSettings; 152 | } 153 | 154 | private static class GetKeysException extends RuntimeException { 155 | GetKeysException(String message) { 156 | super(message); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/executor/CustomDdlExecutor.java: -------------------------------------------------------------------------------- 1 | package com.intellij.executor; 2 | 3 | import com.intellij.CouchbaseConnection; 4 | import org.intellij.lang.annotations.Language; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.sql.SQLException; 8 | 9 | interface CustomDdlExecutor { 10 | @Language("regexp") String IDENTIFIER = "`[0-9a-zA-Z_.%\\-]+`|[a-zA-Z_][a-zA-Z_\\d]*"; 11 | @Language("regexp") String BUCKET_NAME = "(?:(?[a-zA-Z]+):)?(?" + IDENTIFIER + ")"; 12 | 13 | boolean mayAccept(@NotNull String sql); 14 | boolean isRequireWriteAccess(); 15 | ExecutionResult execute(@NotNull CouchbaseConnection connection, @NotNull String sql) throws SQLException; 16 | } 17 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/executor/DescribeBucketExecutor.java: -------------------------------------------------------------------------------- 1 | package com.intellij.executor; 2 | 3 | import com.couchbase.client.core.deps.com.fasterxml.jackson.core.type.TypeReference; 4 | import com.couchbase.client.core.json.Mapper; 5 | import com.couchbase.client.java.Cluster; 6 | import com.couchbase.client.java.manager.bucket.BucketSettings; 7 | import com.intellij.CouchbaseConnection; 8 | import com.intellij.CouchbaseDocumentsSampler; 9 | import com.intellij.EscapingUtil; 10 | import com.intellij.meta.TableInfo; 11 | import com.intellij.resultset.CouchbaseListResultSet; 12 | import com.intellij.resultset.CouchbaseResultSetMetaData; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.sql.SQLException; 16 | import java.util.List; 17 | import java.util.Map; 18 | import java.util.regex.Matcher; 19 | import java.util.regex.Pattern; 20 | import java.util.stream.Collectors; 21 | 22 | import static com.intellij.resultset.CouchbaseResultSetMetaData.createColumn; 23 | import static java.util.Collections.singletonList; 24 | import static java.util.Collections.singletonMap; 25 | import static java.util.regex.Pattern.CASE_INSENSITIVE; 26 | 27 | public class DescribeBucketExecutor implements CustomDdlExecutor { 28 | private static final Pattern DESCRIBE_BUCKET_PATTERN = Pattern.compile( 29 | "DESCRIBE\\s+(BUCKET|TABLE)\\s+(?(?:(COLUMNS|SETTINGS)\\s+)?)" + BUCKET_NAME, 30 | CASE_INSENSITIVE); 31 | private static final TypeReference> MAP_TYPE_REFERENCE = new TypeReference<>() {}; 32 | private static final String ROW_NAME = "result"; 33 | 34 | @Override 35 | public boolean mayAccept(@NotNull String sql) { 36 | return DESCRIBE_BUCKET_PATTERN.matcher(sql).matches(); 37 | } 38 | 39 | @Override 40 | public boolean isRequireWriteAccess() { 41 | return false; 42 | } 43 | 44 | @Override 45 | public ExecutionResult execute(@NotNull CouchbaseConnection connection, @NotNull String sql) throws SQLException { 46 | Matcher matcher = DESCRIBE_BUCKET_PATTERN.matcher(sql); 47 | if (matcher.matches()) { 48 | String name = EscapingUtil.stripBackquotes(matcher.group("bucket")); 49 | String type = matcher.group("type").trim(); 50 | if (type.isEmpty() || type.equalsIgnoreCase("SETTINGS")) { 51 | return describeSettings(connection.getCluster(), name); 52 | } else if (type.equalsIgnoreCase("COLUMNS")) { 53 | String schema = matcher.group("schema"); 54 | if (schema.isEmpty()) { 55 | schema = "default:"; 56 | } 57 | return describeColumns(connection, schema.substring(0, schema.length() - 1), name); 58 | } else { 59 | throw new SQLException("Unknown describe type: " + type); 60 | } 61 | } 62 | return new ExecutionResult(false); 63 | } 64 | 65 | private static ExecutionResult describeSettings(Cluster cluster, String name) { 66 | BucketSettings bucketSettings = cluster.buckets().getBucket(name); 67 | Map map = Mapper.convertValue( 68 | BucketSettingsDto.extractBucketSettings(bucketSettings), MAP_TYPE_REFERENCE); 69 | CouchbaseListResultSet resultSet = new CouchbaseListResultSet(singletonList(singletonMap(ROW_NAME, map))); 70 | resultSet.setMetadata(new CouchbaseResultSetMetaData(singletonList(createColumn(ROW_NAME, "map")))); 71 | return new ExecutionResult(true, resultSet); 72 | } 73 | 74 | private static ExecutionResult describeColumns(CouchbaseConnection connection, String schema, String name) 75 | throws SQLException { 76 | CouchbaseDocumentsSampler documentsSampler = new CouchbaseDocumentsSampler(connection); 77 | List> infos = documentsSampler.sample(new TableInfo(name, schema)) 78 | .stream() 79 | .map(it -> Mapper.convertValue(it, MAP_TYPE_REFERENCE)) 80 | .map(it -> singletonMap(ROW_NAME, (Object) it)) 81 | .collect(Collectors.toList()); 82 | CouchbaseListResultSet resultSet = new CouchbaseListResultSet(infos); 83 | resultSet.setMetadata(new CouchbaseResultSetMetaData(singletonList(createColumn(ROW_NAME, "map")))); 84 | return new ExecutionResult(true, resultSet); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/executor/DescribeIndexExecutor.java: -------------------------------------------------------------------------------- 1 | package com.intellij.executor; 2 | 3 | import com.couchbase.client.java.http.CouchbaseHttpClient; 4 | import com.intellij.CouchbaseConnection; 5 | import com.intellij.resultset.CouchbaseListResultSet; 6 | import com.intellij.resultset.CouchbaseResultSetMetaData; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.sql.ResultSet; 10 | import java.sql.SQLException; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | import java.util.stream.Collectors; 17 | 18 | import static com.intellij.executor.IndexCommons.getIndexes; 19 | import static com.intellij.resultset.CouchbaseResultSetMetaData.createColumn; 20 | import static java.util.Collections.singletonList; 21 | import static java.util.regex.Pattern.CASE_INSENSITIVE; 22 | 23 | class DescribeIndexExecutor implements CustomDdlExecutor { 24 | private static final Pattern DESCRIBE_INDEX_PATTERN = Pattern.compile( 25 | "DESCRIBE\\s+INDEXES", CASE_INSENSITIVE); 26 | private static final String ROW_NAME = "result"; 27 | 28 | @Override 29 | public boolean mayAccept(@NotNull String sql) { 30 | return DESCRIBE_INDEX_PATTERN.matcher(sql).matches(); 31 | } 32 | 33 | @Override 34 | public boolean isRequireWriteAccess() { 35 | return false; 36 | } 37 | 38 | @Override 39 | public ExecutionResult execute(@NotNull CouchbaseConnection connection, @NotNull String sql) throws SQLException { 40 | Matcher matcher = DESCRIBE_INDEX_PATTERN.matcher(sql); 41 | if (matcher.matches()) { 42 | return new ExecutionResult(true, doRequest(connection.getCluster().httpClient())); 43 | } 44 | return new ExecutionResult(false); 45 | } 46 | 47 | private static ResultSet doRequest(CouchbaseHttpClient httpClient) throws SQLException { 48 | List> rows = getIndexes(httpClient).stream() 49 | .map(item -> Collections.singletonMap(ROW_NAME, item)) 50 | .collect(Collectors.toList()); 51 | CouchbaseListResultSet resultSet = new CouchbaseListResultSet(rows); 52 | resultSet.setMetadata(new CouchbaseResultSetMetaData(singletonList(createColumn(ROW_NAME, "map")))); 53 | return resultSet; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/executor/DropBucketExecutor.java: -------------------------------------------------------------------------------- 1 | package com.intellij.executor; 2 | 3 | import com.couchbase.client.core.error.BucketNotFoundException; 4 | import com.couchbase.client.core.error.IndexFailureException; 5 | import com.couchbase.client.java.Cluster; 6 | import com.couchbase.client.java.manager.query.DropPrimaryQueryIndexOptions; 7 | import com.intellij.CouchbaseConnection; 8 | import com.intellij.CouchbaseError; 9 | import com.intellij.EscapingUtil; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.sql.SQLException; 13 | import java.time.Duration; 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | import static com.intellij.CouchbaseMetaData.SYSTEM_SCHEMA; 18 | import static java.util.regex.Pattern.CASE_INSENSITIVE; 19 | 20 | class DropBucketExecutor implements CustomDdlExecutor { 21 | private static final Pattern DROP_BUCKET_PATTERN = Pattern.compile( 22 | "DROP\\s+(BUCKET|TABLE)\\s+" + BUCKET_NAME, CASE_INSENSITIVE); 23 | 24 | public boolean mayAccept(@NotNull String sql) { 25 | return DROP_BUCKET_PATTERN.matcher(sql).matches(); 26 | } 27 | 28 | public ExecutionResult execute(@NotNull CouchbaseConnection connection, @NotNull String sql) throws SQLException { 29 | Matcher matcher = DROP_BUCKET_PATTERN.matcher(sql); 30 | if (matcher.matches()) { 31 | Cluster cluster = connection.getCluster(); 32 | String name = EscapingUtil.stripBackquotes(matcher.group("bucket")); 33 | String schema = matcher.group("schema"); 34 | if (SYSTEM_SCHEMA.equals(schema)) { 35 | throw new SQLException("Cannot drop bucket in system schema"); 36 | } 37 | try { 38 | try { 39 | cluster.queryIndexes().dropPrimaryIndex(name, 40 | DropPrimaryQueryIndexOptions.dropPrimaryQueryIndexOptions().ignoreIfNotExists(true)); 41 | } catch (IndexFailureException ex) { 42 | if (CouchbaseError.create(ex).getErrorEntries().stream() 43 | .noneMatch(e -> "12003".equals(e.getErrorCode()))) { 44 | throw ex; // Bucket not found error 45 | } 46 | } 47 | cluster.buckets().dropBucket(name); 48 | IndexCommons.waitUntilOffline(cluster, name, Duration.ofSeconds(30)); 49 | } catch (BucketNotFoundException ignore) { } 50 | return new ExecutionResult(true); 51 | } 52 | return new ExecutionResult(false); 53 | } 54 | 55 | @Override 56 | public boolean isRequireWriteAccess() { 57 | return true; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/executor/ExecutionResult.java: -------------------------------------------------------------------------------- 1 | package com.intellij.executor; 2 | 3 | import java.sql.ResultSet; 4 | 5 | public class ExecutionResult { 6 | private final boolean isSuccess; 7 | private final ResultSet resultSet; 8 | private final int updateCount; 9 | 10 | public ExecutionResult(boolean isSuccess) { 11 | this(isSuccess, null, -1); 12 | } 13 | 14 | public ExecutionResult(boolean isSuccess, ResultSet resultSet) { 15 | this(isSuccess, resultSet, -1); 16 | } 17 | 18 | public ExecutionResult(boolean isSuccess, ResultSet resultSet, int updateCount) { 19 | this.isSuccess = isSuccess; 20 | this.resultSet = resultSet; 21 | this.updateCount = updateCount; 22 | } 23 | 24 | public boolean isSuccess() { 25 | return isSuccess; 26 | } 27 | 28 | public ResultSet getResultSet() { 29 | return resultSet; 30 | } 31 | 32 | public int getUpdateCount() { 33 | return updateCount; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/executor/GetBucketKeysExecutor.java: -------------------------------------------------------------------------------- 1 | package com.intellij.executor; 2 | 3 | import com.couchbase.client.core.error.BucketNotFoundException; 4 | import com.couchbase.client.core.service.ServiceType; 5 | import com.couchbase.client.java.json.JsonArray; 6 | import com.couchbase.client.java.json.JsonObject; 7 | import com.couchbase.client.java.manager.raw.RawManager; 8 | import com.couchbase.client.java.manager.raw.RawManagerRequest; 9 | import com.couchbase.client.java.manager.raw.RawManagerResponse; 10 | import com.intellij.CouchbaseConnection; 11 | import com.intellij.EscapingUtil; 12 | import com.intellij.resultset.CouchbaseSimpleResultSet; 13 | import org.intellij.lang.annotations.Language; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | import java.net.URLEncoder; 17 | import java.nio.charset.StandardCharsets; 18 | import java.sql.SQLException; 19 | import java.util.regex.Matcher; 20 | import java.util.regex.Pattern; 21 | 22 | import static com.intellij.CouchbaseMetaData.SYSTEM_SCHEMA; 23 | import static java.util.regex.Pattern.CASE_INSENSITIVE; 24 | 25 | class GetBucketKeysExecutor implements CustomDdlExecutor { 26 | private static final @Language("regexp") 27 | String LIMIT = "(\\s*limit (?\\d+))?"; 28 | private static final @Language("regexp") 29 | String OFFSET = "(\\s*offset (?\\d+))?"; 30 | private static final Pattern GET_KEYS_PATTERN = Pattern.compile( 31 | "GET\\s+KEYS\\s+FROM\\s+" + BUCKET_NAME + LIMIT + OFFSET, CASE_INSENSITIVE); 32 | 33 | @Override 34 | public boolean mayAccept(@NotNull String sql) { 35 | return GET_KEYS_PATTERN.matcher(sql).matches(); 36 | } 37 | 38 | @Override 39 | public boolean isRequireWriteAccess() { 40 | return false; 41 | } 42 | 43 | @Override 44 | public ExecutionResult execute(@NotNull CouchbaseConnection connection, @NotNull String sql) throws SQLException { 45 | Matcher matcher = GET_KEYS_PATTERN.matcher(sql); 46 | if (!matcher.matches()) { 47 | return new ExecutionResult(false); 48 | } 49 | String name = EscapingUtil.stripBackquotes(matcher.group("name")); 50 | if (name == null) throw new SQLException("Bucket name is not specified"); 51 | String schema = matcher.group("schema"); 52 | if (SYSTEM_SCHEMA.equals(schema)) { 53 | throw new SQLException("Cannot get keys from bucket in system schema"); 54 | } 55 | String limit = matcher.group("limit"); 56 | String offset = matcher.group("offset"); 57 | String url = "/pools/default/buckets/" + URLEncoder.encode(name, StandardCharsets.UTF_8) + "/docs?include_docs=false"; 58 | if (limit != null) url += "&limit=" + limit; 59 | if (offset != null) url += "&skip=" + offset; 60 | RawManagerResponse response = RawManager.call(connection.getCluster(), RawManagerRequest.get(ServiceType.MANAGER, url)).block(); 61 | if (response != null && response.httpStatus() == 404) throw BucketNotFoundException.forBucket(name); 62 | else if (response == null || response.httpStatus() != 200) throw new SQLException("Request did not succeed. Http status: " + (response == null ? null : response.httpStatus())); 63 | JsonObject jsonObject = response.contentAs(JsonObject.class); 64 | Object rows = jsonObject.get("rows"); 65 | if (!(rows instanceof JsonArray)) { 66 | throw new SQLException("Result does not contain rows: " + jsonObject.toString()); 67 | } 68 | return new ExecutionResult(true, new CouchbaseSimpleResultSet(((JsonArray) rows).toList())); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/executor/IndexCommons.java: -------------------------------------------------------------------------------- 1 | package com.intellij.executor; 2 | 3 | import com.couchbase.client.core.error.IndexesNotReadyException; 4 | import com.couchbase.client.core.retry.reactor.Retry; 5 | import com.couchbase.client.core.retry.reactor.RetryExhaustedException; 6 | import com.couchbase.client.java.Cluster; 7 | import com.couchbase.client.java.http.CouchbaseHttpClient; 8 | import com.couchbase.client.java.http.HttpPath; 9 | import com.couchbase.client.java.http.HttpResponse; 10 | import com.couchbase.client.java.http.HttpTarget; 11 | import com.couchbase.client.java.json.JsonObject; 12 | import com.intellij.meta.IndexInfo; 13 | import reactor.core.publisher.Mono; 14 | 15 | import java.sql.SQLException; 16 | import java.time.Duration; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.concurrent.TimeoutException; 20 | 21 | import static com.couchbase.client.core.logging.RedactableArgument.redactMeta; 22 | import static com.couchbase.client.core.util.CbThrowables.findCause; 23 | import static com.couchbase.client.core.util.CbThrowables.hasCause; 24 | import static java.util.Collections.singletonMap; 25 | import static java.util.stream.Collectors.toList; 26 | import static java.util.stream.Collectors.toMap; 27 | 28 | class IndexCommons { 29 | static void waitUntilReady(Cluster cluster, String bucketName, Duration timeout) { 30 | waitInner(timeout, () -> failIfIndexesOffline(cluster.httpClient(), bucketName)); 31 | } 32 | 33 | static void waitUntilOffline(Cluster cluster, String bucketName, Duration timeout) { 34 | waitInner(timeout, () -> failIfIndexesPresent(cluster.httpClient(), bucketName)); 35 | } 36 | 37 | private static void waitInner(Duration timeout, Runnable runnable) { 38 | Mono.fromRunnable(runnable) 39 | .retryWhen(Retry.onlyIf(ctx -> hasCause(ctx.exception(), IndexesNotReadyException.class)) 40 | .exponentialBackoff(Duration.ofMillis(50), Duration.ofSeconds(3)) 41 | .timeout(timeout) 42 | .toReactorRetry()) 43 | .onErrorMap(t -> t instanceof RetryExhaustedException ? toWatchTimeoutException(t, timeout) : t) 44 | .block(); 45 | } 46 | 47 | private static TimeoutException toWatchTimeoutException(Throwable t, Duration timeout) { 48 | final StringBuilder msg = new StringBuilder("A requested index is still not ready after " + timeout + "."); 49 | 50 | findCause(t, IndexesNotReadyException.class).ifPresent(cause -> 51 | msg.append(" Unready index name -> state: ").append(redactMeta(cause.indexNameToState()))); 52 | 53 | return new TimeoutException(msg.toString()); 54 | } 55 | 56 | private static void failIfIndexesPresent(CouchbaseHttpClient httpClient, String bucketName) { 57 | Map matchingIndexes = getMatchingIndexInfo(httpClient, bucketName) 58 | .stream() 59 | .collect(toMap(IndexInfo::getQualified, IndexInfo::getStatus)); 60 | 61 | if (!matchingIndexes.isEmpty()) { 62 | throw new IndexesNotReadyException(matchingIndexes); 63 | } 64 | } 65 | 66 | private static void failIfIndexesOffline(CouchbaseHttpClient httpClient, String bucketName) 67 | throws IndexesNotReadyException { 68 | List matchingIndexes = getMatchingIndexInfo(httpClient, bucketName); 69 | if (matchingIndexes.isEmpty()) { 70 | throw new IndexesNotReadyException(singletonMap("#primary", "notFound")); 71 | } 72 | 73 | final Map offlineIndexNameToState = matchingIndexes.stream() 74 | .filter(idx -> !"Ready".equals(idx.status)) 75 | .collect(toMap(IndexInfo::getQualified, IndexInfo::getStatus)); 76 | 77 | if (!offlineIndexNameToState.isEmpty()) { 78 | throw new IndexesNotReadyException(offlineIndexNameToState); 79 | } 80 | } 81 | 82 | static List getIndexes(CouchbaseHttpClient httpClient) throws SQLException { 83 | try { 84 | HttpResponse response = httpClient.get(HttpTarget.manager(), HttpPath.of("/indexStatus")); 85 | if (!response.success()) { 86 | throw new SQLException("Failed to retrieve index information: " 87 | + "Response status=" + response.statusCode() + " " 88 | + "Response body=" + response.contentAsString()); 89 | } 90 | return JsonObject.fromJson(response.content()) 91 | .getArray("indexes") 92 | .toList(); 93 | } catch (SQLException ex) { 94 | throw ex; 95 | } catch (Exception ex) { 96 | throw new SQLException(ex); 97 | } 98 | } 99 | 100 | private static List getMatchingIndexInfo(CouchbaseHttpClient httpClient, String bucketName) { 101 | List list; 102 | try { 103 | list = getIndexes(httpClient); 104 | } catch (SQLException e) { 105 | throw new RuntimeException(e); 106 | } 107 | 108 | return list.stream() 109 | .filter(i -> i instanceof Map) 110 | .map(i -> IndexInfo.create((Map) i)) 111 | .filter(i -> bucketName.equals(i.bucket) && "#primary".equals(i.name)) 112 | .collect(toList()); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/meta/ClusterInfo.java: -------------------------------------------------------------------------------- 1 | package com.intellij.meta; 2 | 3 | import com.couchbase.client.java.Cluster; 4 | import com.couchbase.client.java.http.HttpPath; 5 | import com.couchbase.client.java.http.HttpResponse; 6 | import com.couchbase.client.java.http.HttpTarget; 7 | import com.couchbase.client.java.json.JsonObject; 8 | 9 | import java.sql.SQLException; 10 | 11 | public class ClusterInfo { 12 | private final Cluster cluster; 13 | private volatile JsonObject lazyClusterInfo = null; 14 | 15 | public ClusterInfo(Cluster cluster) { 16 | this.cluster = cluster; 17 | } 18 | 19 | public JsonObject getClusterInfo() throws SQLException { 20 | if (lazyClusterInfo == null) { 21 | try { 22 | HttpResponse response = cluster.httpClient().get(HttpTarget.manager(), HttpPath.of("/pools")); 23 | if (!response.success()) { 24 | throw new SQLException("Failed to retrieve cluster information: " 25 | + "Response status=" + response.statusCode() + " " 26 | + "Response body=" + response.contentAsString()); 27 | } 28 | lazyClusterInfo = JsonObject.fromJson(response.content()); 29 | } catch (SQLException e) { 30 | throw e; 31 | } catch (Exception e) { 32 | throw new SQLException("Failed to retrieve cluster information", e); 33 | } 34 | } 35 | return lazyClusterInfo; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/meta/ColumnInfo.java: -------------------------------------------------------------------------------- 1 | package com.intellij.meta; 2 | 3 | import java.util.Objects; 4 | 5 | public class ColumnInfo { 6 | private final int type; 7 | private final String typeName; 8 | private final String name; 9 | 10 | public ColumnInfo(String name, int type, String typeName) { 11 | this.type = type; 12 | this.typeName = typeName; 13 | this.name = name; 14 | } 15 | 16 | public int getType() { 17 | return type; 18 | } 19 | 20 | public String getTypeName() { 21 | return typeName; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | @Override 29 | public boolean equals(Object o) { 30 | if (this == o) { 31 | return true; 32 | } 33 | if (!(o instanceof ColumnInfo)) { 34 | return false; 35 | } 36 | ColumnInfo that = (ColumnInfo) o; 37 | return Objects.equals(getName(), that.getName()); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Objects.hash(getName()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/meta/IndexInfo.java: -------------------------------------------------------------------------------- 1 | package com.intellij.meta; 2 | 3 | import java.util.Map; 4 | 5 | import static com.intellij.ObjectUtil.tryCast; 6 | 7 | public class IndexInfo { 8 | public final String name; 9 | public final String bucket; 10 | public final String status; 11 | public final String lastScanTime; 12 | 13 | public IndexInfo(String name, String bucket, String status, String lastScanTime) { 14 | this.name = name; 15 | this.bucket = bucket; 16 | this.status = status; 17 | this.lastScanTime = lastScanTime; 18 | } 19 | 20 | public static IndexInfo create(Map asMap) { 21 | String name = tryCast(asMap.get("index"), String.class); 22 | String bucket = tryCast(asMap.get("bucket"), String.class); 23 | String status = tryCast(asMap.get("status"), String.class); 24 | String lastScanTime = tryCast(asMap.get("lastScanTime"), String.class); 25 | return new IndexInfo(name, bucket, status, lastScanTime); 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public String getBucket() { 33 | return bucket; 34 | } 35 | 36 | public String getStatus() { 37 | return status; 38 | } 39 | 40 | public String getQualified() { 41 | return bucket + ":" + name; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "IndexInfo{" + 47 | "name='" + name + '\'' + 48 | ", bucket='" + bucket + '\'' + 49 | ", status='" + status + '\'' + 50 | ", lastScanTime='" + lastScanTime + '\'' + 51 | '}'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/meta/TableInfo.java: -------------------------------------------------------------------------------- 1 | package com.intellij.meta; 2 | 3 | public class TableInfo { 4 | private final String name; 5 | private final String schema; 6 | 7 | public TableInfo(String name, String schema) { 8 | this.name = name; 9 | this.schema = schema; 10 | } 11 | 12 | public String getSchema() { 13 | return schema; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/resultset/CouchbaseResultSetMetaData.java: -------------------------------------------------------------------------------- 1 | package com.intellij.resultset; 2 | 3 | import com.intellij.types.ColumnTypeHelper; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.sql.ResultSetMetaData; 7 | import java.sql.SQLException; 8 | import java.sql.SQLFeatureNotSupportedException; 9 | import java.util.List; 10 | 11 | public class CouchbaseResultSetMetaData implements ResultSetMetaData { 12 | 13 | private static final String NOT_APPLICABLE_TABLE_NAME = ""; 14 | private static final String NOT_APPLICABLE_CATALOG = ""; 15 | private static final int NOT_APPLICABLE_SCALE = 0; 16 | private static final int NOT_APPLICABLE_PRECISION = 0; 17 | 18 | private final List columnMetaData; 19 | 20 | public CouchbaseResultSetMetaData(@NotNull List columnMetaData) { 21 | this.columnMetaData = columnMetaData; 22 | } 23 | 24 | public static ColumnMetaData createColumn(String name, String typeName) { 25 | return new ColumnMetaData(name, typeName); 26 | } 27 | 28 | public int findColumn(String columnLabel) { 29 | for (int i = 0; i < columnMetaData.size(); i++) { 30 | if (columnMetaData.get(i).name.equals(columnLabel)) { 31 | return i + 1; 32 | } 33 | } 34 | return -1; 35 | } 36 | 37 | @Override 38 | public T unwrap(Class iface) throws SQLException { 39 | throw new SQLFeatureNotSupportedException(); 40 | } 41 | 42 | @Override 43 | public boolean isWrapperFor(Class iface) throws SQLException { 44 | throw new SQLFeatureNotSupportedException(); 45 | } 46 | 47 | @Override 48 | public int getColumnCount() { 49 | return this.columnMetaData.size(); 50 | } 51 | 52 | @Override 53 | public boolean isAutoIncrement(int column) { 54 | return false; 55 | } 56 | 57 | @Override 58 | public boolean isCaseSensitive(int column) throws SQLException { 59 | throw new SQLFeatureNotSupportedException(); 60 | } 61 | 62 | @Override 63 | public boolean isSearchable(int column) throws SQLException { 64 | throw new SQLFeatureNotSupportedException(); 65 | } 66 | 67 | @Override 68 | public boolean isCurrency(int column) throws SQLException { 69 | throw new SQLFeatureNotSupportedException(); 70 | } 71 | 72 | @Override 73 | public int isNullable(int column) { 74 | return ResultSetMetaData.columnNullable; 75 | } 76 | 77 | @Override 78 | public boolean isSigned(int column) throws SQLException { 79 | throw new SQLFeatureNotSupportedException(); 80 | } 81 | 82 | @Override 83 | public int getColumnDisplaySize(int column) throws SQLException { 84 | throw new SQLFeatureNotSupportedException(); 85 | } 86 | 87 | @Override 88 | public String getColumnLabel(int column) { 89 | return columnMetaData.get(column - 1).name; 90 | } 91 | 92 | @Override 93 | public String getColumnName(int column) { 94 | return columnMetaData.get(column - 1).name; 95 | } 96 | 97 | @Override 98 | public String getSchemaName(int column) { 99 | return getCatalogName(column); 100 | } 101 | 102 | @Override 103 | public int getPrecision(int column) { 104 | return NOT_APPLICABLE_PRECISION; 105 | } 106 | 107 | @Override 108 | public int getScale(int column) { 109 | return NOT_APPLICABLE_SCALE; 110 | } 111 | 112 | @Override 113 | public String getTableName(int column) { 114 | return NOT_APPLICABLE_TABLE_NAME; 115 | } 116 | 117 | @Override 118 | public String getCatalogName(int column) { 119 | return NOT_APPLICABLE_CATALOG; 120 | } 121 | 122 | @Override 123 | public int getColumnType(int column) { 124 | return columnMetaData.get(column - 1).getJavaType(); 125 | } 126 | 127 | @Override 128 | public String getColumnTypeName(int column) { 129 | return columnMetaData.get(column - 1).typeName; 130 | } 131 | 132 | @Override 133 | public boolean isReadOnly(int column) throws SQLException { 134 | throw new SQLFeatureNotSupportedException(); 135 | } 136 | 137 | @Override 138 | public boolean isWritable(int column) throws SQLException { 139 | throw new SQLFeatureNotSupportedException(); 140 | } 141 | 142 | @Override 143 | public boolean isDefinitelyWritable(int column) throws SQLException { 144 | throw new SQLFeatureNotSupportedException(); 145 | } 146 | 147 | @Override 148 | public String getColumnClassName(int column) { 149 | return columnMetaData.get(column - 1).getClassName(); 150 | } 151 | 152 | public static class ColumnMetaData { 153 | private final String name; 154 | private final String typeName; 155 | 156 | private ColumnMetaData(String name, String typeName) { 157 | this.name = name; 158 | this.typeName = typeName; 159 | } 160 | 161 | public int getJavaType() { 162 | return ColumnTypeHelper.getJavaType(typeName); 163 | } 164 | 165 | public String getClassName() { 166 | return ColumnTypeHelper.getClassName(typeName); 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/resultset/CouchbaseSimpleResultSet.java: -------------------------------------------------------------------------------- 1 | package com.intellij.resultset; 2 | 3 | import java.io.InputStream; 4 | import java.io.Reader; 5 | import java.math.BigDecimal; 6 | import java.net.URL; 7 | import java.sql.*; 8 | import java.util.Calendar; 9 | import java.util.Collections; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | import static com.intellij.resultset.CouchbaseResultSetMetaData.createColumn; 14 | 15 | public class CouchbaseSimpleResultSet implements ResultSet { 16 | private final static CouchbaseResultSetMetaData DEFAULT_METADATA = new CouchbaseResultSetMetaData(Collections.singletonList(createColumn("result", "map"))); 17 | private final List data; 18 | private int currentRow = -1; 19 | private boolean isClosed = false; 20 | 21 | public CouchbaseSimpleResultSet(List data) { 22 | this.data = data; 23 | } 24 | 25 | public boolean next() { 26 | if (data == null) { 27 | return false; 28 | } 29 | if (currentRow < data.size() - 1) { 30 | currentRow++; 31 | return true; 32 | } 33 | return false; 34 | } 35 | 36 | public void close() throws SQLException { 37 | checkClosed(); 38 | this.isClosed = true; 39 | } 40 | 41 | private void checkClosed() throws SQLException { 42 | if (isClosed) throw new SQLException("ResultSet was previously closed."); 43 | } 44 | 45 | public boolean isClosed() { 46 | return isClosed; 47 | } 48 | 49 | public boolean wasNull() { 50 | return false; 51 | } 52 | 53 | public String getString(int columnIndex) throws SQLException { 54 | if (currentRow >= data.size()) { 55 | throw new SQLException("ResultSet exhausted, request currentRow = " + currentRow); 56 | } 57 | final Object val = data.get(currentRow); 58 | return val != null ? val.toString() : null; 59 | } 60 | 61 | public boolean getBoolean(int columnIndex) throws SQLException { 62 | checkClosed(); 63 | throw new SQLFeatureNotSupportedException(); 64 | } 65 | 66 | public byte getByte(int columnIndex) throws SQLException { 67 | checkClosed(); 68 | throw new SQLFeatureNotSupportedException(); 69 | } 70 | 71 | public short getShort(int columnIndex) throws SQLException { 72 | checkClosed(); 73 | throw new SQLFeatureNotSupportedException(); 74 | } 75 | 76 | public int getInt(int columnIndex) throws SQLException { 77 | checkClosed(); 78 | throw new SQLFeatureNotSupportedException(); 79 | } 80 | 81 | public long getLong(int columnIndex) throws SQLException { 82 | checkClosed(); 83 | throw new SQLFeatureNotSupportedException(); 84 | } 85 | 86 | public float getFloat(int columnIndex) throws SQLException { 87 | checkClosed(); 88 | throw new SQLFeatureNotSupportedException(); 89 | } 90 | 91 | public double getDouble(int columnIndex) throws SQLException { 92 | checkClosed(); 93 | throw new SQLFeatureNotSupportedException(); 94 | } 95 | 96 | public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { 97 | checkClosed(); 98 | throw new SQLFeatureNotSupportedException(); 99 | } 100 | 101 | public byte[] getBytes(int columnIndex) throws SQLException { 102 | checkClosed(); 103 | throw new SQLFeatureNotSupportedException(); 104 | } 105 | 106 | public Date getDate(int columnIndex) throws SQLException { 107 | checkClosed(); 108 | throw new SQLFeatureNotSupportedException(); 109 | } 110 | 111 | public Time getTime(int columnIndex) throws SQLException { 112 | checkClosed(); 113 | throw new SQLFeatureNotSupportedException(); 114 | } 115 | 116 | public Timestamp getTimestamp(int columnIndex) throws SQLException { 117 | checkClosed(); 118 | throw new SQLFeatureNotSupportedException(); 119 | } 120 | 121 | public InputStream getAsciiStream(int columnIndex) throws SQLException { 122 | checkClosed(); 123 | throw new SQLFeatureNotSupportedException(); 124 | } 125 | 126 | public InputStream getUnicodeStream(int columnIndex) throws SQLException { 127 | checkClosed(); 128 | throw new SQLFeatureNotSupportedException(); 129 | } 130 | 131 | public InputStream getBinaryStream(int columnIndex) throws SQLException { 132 | checkClosed(); 133 | throw new SQLFeatureNotSupportedException(); 134 | } 135 | 136 | public String getString(String columnLabel) throws SQLException { 137 | checkClosed(); 138 | throw new SQLFeatureNotSupportedException(); 139 | } 140 | 141 | public boolean getBoolean(String columnLabel) throws SQLException { 142 | checkClosed(); 143 | throw new SQLFeatureNotSupportedException(); 144 | } 145 | 146 | public byte getByte(String columnLabel) throws SQLException { 147 | checkClosed(); 148 | throw new SQLFeatureNotSupportedException(); 149 | } 150 | 151 | public short getShort(String columnLabel) throws SQLException { 152 | checkClosed(); 153 | throw new SQLFeatureNotSupportedException(); 154 | } 155 | 156 | public int getInt(String columnLabel) throws SQLException { 157 | checkClosed(); 158 | throw new SQLFeatureNotSupportedException(); 159 | } 160 | 161 | public long getLong(String columnLabel) throws SQLException { 162 | checkClosed(); 163 | throw new SQLFeatureNotSupportedException(); 164 | } 165 | 166 | public float getFloat(String columnLabel) throws SQLException { 167 | checkClosed(); 168 | throw new SQLFeatureNotSupportedException(); 169 | } 170 | 171 | public double getDouble(String columnLabel) throws SQLException { 172 | checkClosed(); 173 | throw new SQLFeatureNotSupportedException(); 174 | } 175 | 176 | public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { 177 | checkClosed(); 178 | throw new SQLFeatureNotSupportedException(); 179 | } 180 | 181 | public byte[] getBytes(String columnLabel) throws SQLException { 182 | checkClosed(); 183 | throw new SQLFeatureNotSupportedException(); 184 | } 185 | 186 | public Date getDate(String columnLabel) throws SQLException { 187 | checkClosed(); 188 | throw new SQLFeatureNotSupportedException(); 189 | } 190 | 191 | public Time getTime(String columnLabel) throws SQLException { 192 | checkClosed(); 193 | throw new SQLFeatureNotSupportedException(); 194 | } 195 | 196 | public Timestamp getTimestamp(String columnLabel) throws SQLException { 197 | checkClosed(); 198 | throw new SQLFeatureNotSupportedException(); 199 | } 200 | 201 | public InputStream getAsciiStream(String columnLabel) throws SQLException { 202 | checkClosed(); 203 | throw new SQLFeatureNotSupportedException(); 204 | } 205 | 206 | public InputStream getUnicodeStream(String columnLabel) { 207 | return null; 208 | } 209 | 210 | public InputStream getBinaryStream(String columnLabel) { 211 | return null; 212 | } 213 | 214 | public SQLWarning getWarnings() { 215 | return null; 216 | } 217 | 218 | public void clearWarnings() { 219 | } 220 | 221 | public String getCursorName() { 222 | return null; 223 | } 224 | 225 | public ResultSetMetaData getMetaData() { 226 | return DEFAULT_METADATA; 227 | } 228 | 229 | public Object getObject(int columnIndex) throws SQLException { 230 | if (currentRow >= data.size()) { 231 | throw new SQLException("ResultSet exhausted, request currentRow = " + currentRow); 232 | } 233 | return data.get(currentRow); 234 | } 235 | 236 | public Object getObject(String columnLabel) throws SQLException { 237 | return getObject(1); 238 | } 239 | 240 | public int findColumn(String columnLabel) { 241 | return 0; 242 | } 243 | 244 | public Reader getCharacterStream(int columnIndex) { 245 | return null; 246 | } 247 | 248 | public Reader getCharacterStream(String columnLabel) { 249 | return null; 250 | } 251 | 252 | public BigDecimal getBigDecimal(int columnIndex) { 253 | return null; 254 | } 255 | 256 | public BigDecimal getBigDecimal(String columnLabel) { 257 | return null; 258 | } 259 | 260 | public boolean isBeforeFirst() { 261 | return false; 262 | } 263 | 264 | public boolean isAfterLast() { 265 | return false; 266 | } 267 | 268 | public boolean isFirst() { 269 | return false; 270 | } 271 | 272 | public boolean isLast() { 273 | return false; 274 | } 275 | 276 | public void beforeFirst() { 277 | 278 | } 279 | 280 | public void afterLast() { 281 | 282 | } 283 | 284 | public boolean first() { 285 | return false; 286 | } 287 | 288 | public boolean last() { 289 | return false; 290 | } 291 | 292 | public int getRow() { 293 | return 0; 294 | } 295 | 296 | public boolean absolute(int row) { 297 | return false; 298 | } 299 | 300 | public boolean relative(int rows) { 301 | return false; 302 | } 303 | 304 | public boolean previous() { 305 | return false; 306 | } 307 | 308 | public void setFetchDirection(int direction) throws SQLFeatureNotSupportedException { 309 | throw new SQLFeatureNotSupportedException(); 310 | } 311 | 312 | public int getFetchDirection() { 313 | return ResultSet.FETCH_FORWARD; 314 | } 315 | 316 | public void setFetchSize(int rows) { 317 | 318 | } 319 | 320 | public int getFetchSize() { 321 | return 0; 322 | } 323 | 324 | public int getType() { 325 | return ResultSet.TYPE_FORWARD_ONLY; 326 | } 327 | 328 | public int getConcurrency() throws SQLFeatureNotSupportedException { 329 | throw new SQLFeatureNotSupportedException(); 330 | } 331 | 332 | public boolean rowUpdated() { 333 | return false; 334 | } 335 | 336 | public boolean rowInserted() { 337 | return false; 338 | } 339 | 340 | public boolean rowDeleted() { 341 | return false; 342 | } 343 | 344 | public void updateNull(int columnIndex) { 345 | 346 | } 347 | 348 | public void updateBoolean(int columnIndex, boolean x) { 349 | } 350 | 351 | public void updateByte(int columnIndex, byte x) { 352 | } 353 | 354 | public void updateShort(int columnIndex, short x) { 355 | } 356 | 357 | public void updateInt(int columnIndex, int x) { 358 | } 359 | 360 | public void updateLong(int columnIndex, long x) { 361 | } 362 | 363 | public void updateFloat(int columnIndex, float x) { 364 | } 365 | 366 | public void updateDouble(int columnIndex, double x) { 367 | } 368 | 369 | public void updateBigDecimal(int columnIndex, BigDecimal x) { 370 | } 371 | 372 | public void updateString(int columnIndex, String x) { 373 | } 374 | 375 | public void updateBytes(int columnIndex, byte[] x) { 376 | } 377 | 378 | public void updateDate(int columnIndex, Date x) { 379 | } 380 | 381 | public void updateTime(int columnIndex, Time x) { 382 | } 383 | 384 | public void updateTimestamp(int columnIndex, Timestamp x) { 385 | } 386 | 387 | public void updateAsciiStream(int columnIndex, InputStream x, int length) { 388 | } 389 | 390 | public void updateBinaryStream(int columnIndex, InputStream x, int length) { 391 | } 392 | 393 | public void updateCharacterStream(int columnIndex, Reader x, int length) { 394 | } 395 | 396 | public void updateObject(int columnIndex, Object x, int scaleOrLength) { 397 | } 398 | 399 | public void updateObject(int columnIndex, Object x) { 400 | } 401 | 402 | public void updateNull(String columnLabel) { 403 | } 404 | 405 | public void updateBoolean(String columnLabel, boolean x) { 406 | } 407 | 408 | public void updateByte(String columnLabel, byte x) { 409 | } 410 | 411 | public void updateShort(String columnLabel, short x) { 412 | } 413 | 414 | public void updateInt(String columnLabel, int x) { 415 | } 416 | 417 | public void updateLong(String columnLabel, long x) { 418 | } 419 | 420 | public void updateFloat(String columnLabel, float x) { 421 | } 422 | 423 | public void updateDouble(String columnLabel, double x) { 424 | } 425 | 426 | public void updateBigDecimal(String columnLabel, BigDecimal x) { 427 | } 428 | 429 | public void updateString(String columnLabel, String x) { 430 | } 431 | 432 | public void updateBytes(String columnLabel, byte[] x) { 433 | } 434 | 435 | public void updateDate(String columnLabel, Date x) { 436 | } 437 | 438 | public void updateTime(String columnLabel, Time x) { 439 | } 440 | 441 | public void updateTimestamp(String columnLabel, Timestamp x) { 442 | } 443 | 444 | public void updateAsciiStream(String columnLabel, InputStream x, int length) { 445 | } 446 | 447 | public void updateBinaryStream(String columnLabel, InputStream x, int length) { 448 | } 449 | 450 | public void updateCharacterStream(String columnLabel, Reader reader, int length) { 451 | } 452 | 453 | public void updateObject(String columnLabel, Object x, int scaleOrLength) { 454 | } 455 | 456 | public void updateObject(String columnLabel, Object x) { 457 | } 458 | 459 | public void insertRow() { 460 | } 461 | 462 | public void updateRow() { 463 | } 464 | 465 | public void deleteRow() { 466 | } 467 | 468 | public void refreshRow() { 469 | } 470 | 471 | public void cancelRowUpdates() { 472 | } 473 | 474 | public void moveToInsertRow() { 475 | } 476 | 477 | public void moveToCurrentRow() { 478 | } 479 | 480 | public Statement getStatement() { 481 | return null; 482 | } 483 | 484 | public Object getObject(int columnIndex, Map> map) { 485 | return null; 486 | } 487 | 488 | public Ref getRef(int columnIndex) { 489 | return null; 490 | } 491 | 492 | public Blob getBlob(int columnIndex) { 493 | return null; 494 | } 495 | 496 | public Clob getClob(int columnIndex) { 497 | return null; 498 | } 499 | 500 | public Array getArray(int columnIndex) { 501 | return null; 502 | } 503 | 504 | public Object getObject(String columnLabel, Map> map) { 505 | return null; 506 | } 507 | 508 | public Ref getRef(String columnLabel) { 509 | return null; 510 | } 511 | 512 | public Blob getBlob(String columnLabel) { 513 | return null; 514 | } 515 | 516 | public Clob getClob(String columnLabel) { 517 | return null; 518 | } 519 | 520 | public Array getArray(String columnLabel) { 521 | return null; 522 | } 523 | 524 | public Date getDate(int columnIndex, Calendar cal) { 525 | return null; 526 | } 527 | 528 | public Date getDate(String columnLabel, Calendar cal) { 529 | return null; 530 | } 531 | 532 | public Time getTime(int columnIndex, Calendar cal) { 533 | return null; 534 | } 535 | 536 | public Time getTime(String columnLabel, Calendar cal) { 537 | return null; 538 | } 539 | 540 | public Timestamp getTimestamp(int columnIndex, Calendar cal) { 541 | return null; 542 | } 543 | 544 | public Timestamp getTimestamp(String columnLabel, Calendar cal) { 545 | return null; 546 | } 547 | 548 | public URL getURL(int columnIndex) { 549 | return null; 550 | } 551 | 552 | public URL getURL(String columnLabel) { 553 | return null; 554 | } 555 | 556 | public void updateRef(int columnIndex, Ref x) { 557 | } 558 | 559 | public void updateRef(String columnLabel, Ref x) { 560 | } 561 | 562 | public void updateBlob(int columnIndex, Blob x) { 563 | } 564 | 565 | public void updateBlob(String columnLabel, Blob x) { 566 | } 567 | 568 | public void updateClob(int columnIndex, Clob x) { 569 | } 570 | 571 | public void updateClob(String columnLabel, Clob x) { 572 | } 573 | 574 | public void updateArray(int columnIndex, Array x) { 575 | } 576 | 577 | public void updateArray(String columnLabel, Array x) { 578 | } 579 | 580 | public RowId getRowId(int columnIndex) { 581 | return null; 582 | } 583 | 584 | public RowId getRowId(String columnLabel) { 585 | return null; 586 | } 587 | 588 | public void updateRowId(int columnIndex, RowId x) { 589 | } 590 | 591 | public void updateRowId(String columnLabel, RowId x) { 592 | } 593 | 594 | public int getHoldability() throws SQLFeatureNotSupportedException { 595 | throw new SQLFeatureNotSupportedException(); 596 | } 597 | 598 | public void updateNString(int columnIndex, String nString) throws SQLException { 599 | checkClosed(); 600 | } 601 | 602 | public void updateNString(String columnLabel, String nString) { 603 | } 604 | 605 | public void updateNClob(int columnIndex, NClob nClob) { 606 | } 607 | 608 | public void updateNClob(String columnLabel, NClob nClob) { 609 | } 610 | 611 | public NClob getNClob(int columnIndex) { 612 | return null; 613 | } 614 | 615 | public NClob getNClob(String columnLabel) { 616 | return null; 617 | } 618 | 619 | public SQLXML getSQLXML(int columnIndex) { 620 | return null; 621 | } 622 | 623 | public SQLXML getSQLXML(String columnLabel) { 624 | return null; 625 | } 626 | 627 | public void updateSQLXML(int columnIndex, SQLXML xmlObject) { 628 | } 629 | 630 | public void updateSQLXML(String columnLabel, SQLXML xmlObject) { 631 | } 632 | 633 | public String getNString(int columnIndex) { 634 | return null; 635 | } 636 | 637 | public String getNString(String columnLabel) { 638 | return null; 639 | } 640 | 641 | public Reader getNCharacterStream(int columnIndex) { 642 | return null; 643 | } 644 | 645 | public Reader getNCharacterStream(String columnLabel) { 646 | return null; 647 | } 648 | 649 | public void updateNCharacterStream(int columnIndex, Reader x, long length) { 650 | } 651 | 652 | public void updateNCharacterStream(String columnLabel, Reader reader, long length) { 653 | } 654 | 655 | public void updateAsciiStream(int columnIndex, InputStream x, long length) { 656 | } 657 | 658 | public void updateBinaryStream(int columnIndex, InputStream x, long length) { 659 | } 660 | 661 | public void updateCharacterStream(int columnIndex, Reader x, long length) { 662 | } 663 | 664 | public void updateAsciiStream(String columnLabel, InputStream x, long length) { 665 | } 666 | 667 | public void updateBinaryStream(String columnLabel, InputStream x, long length) { 668 | } 669 | 670 | public void updateCharacterStream(String columnLabel, Reader reader, long length) { 671 | } 672 | 673 | public void updateBlob(int columnIndex, InputStream inputStream, long length) { 674 | } 675 | 676 | public void updateBlob(String columnLabel, InputStream inputStream, long length) { 677 | } 678 | 679 | public void updateClob(int columnIndex, Reader reader, long length) { 680 | } 681 | 682 | public void updateClob(String columnLabel, Reader reader, long length) { 683 | } 684 | 685 | public void updateNClob(int columnIndex, Reader reader, long length) { 686 | } 687 | 688 | public void updateNClob(String columnLabel, Reader reader, long length) { 689 | } 690 | 691 | public void updateNCharacterStream(int columnIndex, Reader x) { 692 | } 693 | 694 | public void updateNCharacterStream(String columnLabel, Reader reader) { 695 | } 696 | 697 | public void updateAsciiStream(int columnIndex, InputStream x) { 698 | } 699 | 700 | public void updateBinaryStream(int columnIndex, InputStream x) { 701 | } 702 | 703 | public void updateCharacterStream(int columnIndex, Reader x) { 704 | } 705 | 706 | public void updateAsciiStream(String columnLabel, InputStream x) { 707 | } 708 | 709 | public void updateBinaryStream(String columnLabel, InputStream x) { 710 | } 711 | 712 | public void updateCharacterStream(String columnLabel, Reader reader) { 713 | } 714 | 715 | public void updateBlob(int columnIndex, InputStream inputStream) { 716 | } 717 | 718 | public void updateBlob(String columnLabel, InputStream inputStream) { 719 | } 720 | 721 | public void updateClob(int columnIndex, Reader reader) { 722 | } 723 | 724 | public void updateClob(String columnLabel, Reader reader) { 725 | } 726 | 727 | public void updateNClob(int columnIndex, Reader reader) { 728 | 729 | } 730 | 731 | public void updateNClob(String columnLabel, Reader reader) { 732 | } 733 | 734 | @Override 735 | public T getObject(int columnIndex, Class type) { 736 | return null; 737 | } 738 | 739 | @Override 740 | public T getObject(String columnLabel, Class type) { 741 | return null; 742 | } 743 | 744 | public T unwrap(Class iface) { 745 | return null; 746 | } 747 | 748 | public boolean isWrapperFor(Class iface) { 749 | return false; 750 | } 751 | } 752 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/resultset/ReactiveRows.java: -------------------------------------------------------------------------------- 1 | package com.intellij.resultset; 2 | 3 | import com.couchbase.client.java.json.JsonObject; 4 | import com.couchbase.client.java.query.ReactiveQueryResult; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.Collections; 8 | import java.util.Iterator; 9 | import java.util.stream.Stream; 10 | 11 | public class ReactiveRows implements ResultSetRows { 12 | private Stream stream; 13 | private Iterator iterator; 14 | 15 | public ReactiveRows(@NotNull ReactiveQueryResult queryResult, int fetchSize) { 16 | this.stream = queryResult.rowsAsObject() 17 | .toStream(fetchSize); 18 | this.iterator = stream.iterator(); 19 | } 20 | 21 | @Override 22 | public boolean hasNext() { 23 | return iterator.hasNext(); 24 | } 25 | 26 | @Override 27 | public JsonObject next() { 28 | return iterator.next(); 29 | } 30 | 31 | @Override 32 | public void close() { 33 | if (stream != null) { 34 | stream.close(); 35 | iterator = Collections.emptyIterator(); 36 | stream = null; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/resultset/ResultSetRows.java: -------------------------------------------------------------------------------- 1 | package com.intellij.resultset; 2 | 3 | import java.util.Iterator; 4 | 5 | public interface ResultSetRows extends Iterator { 6 | void close(); 7 | } 8 | -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/types/ArrayImpl.java: -------------------------------------------------------------------------------- 1 | package com.intellij.types; 2 | 3 | import java.sql.Array; 4 | import java.sql.ResultSet; 5 | import java.sql.SQLException; 6 | import java.sql.SQLFeatureNotSupportedException; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | 11 | public class ArrayImpl implements Array { 12 | private Object[] array; 13 | 14 | public ArrayImpl(Object[] array) { 15 | this.array = array; 16 | } 17 | 18 | public ArrayImpl(List array) { 19 | this.array = array.toArray(); 20 | } 21 | 22 | @Override 23 | public String getBaseTypeName() throws SQLException { 24 | throw new SQLFeatureNotSupportedException(); 25 | } 26 | 27 | @Override 28 | public int getBaseType() throws SQLException { 29 | throw new SQLFeatureNotSupportedException(); 30 | } 31 | 32 | @Override 33 | public Object[] getArray() throws SQLException { 34 | if (array == null) { 35 | throw new SQLException("Array was freed"); 36 | } 37 | return array; 38 | } 39 | 40 | @Override 41 | public Object getArray(Map> map) throws SQLException { 42 | throw new SQLFeatureNotSupportedException(); 43 | } 44 | 45 | @Override 46 | public Object getArray(long index, int count) throws SQLException { 47 | throw new SQLFeatureNotSupportedException(); 48 | } 49 | 50 | @Override 51 | public Object getArray(long index, int count, Map> map) throws SQLException { 52 | throw new SQLFeatureNotSupportedException(); 53 | } 54 | 55 | @Override 56 | public ResultSet getResultSet() throws SQLException { 57 | throw new SQLFeatureNotSupportedException(); 58 | } 59 | 60 | @Override 61 | public ResultSet getResultSet(Map> map) throws SQLException { 62 | throw new SQLFeatureNotSupportedException(); 63 | } 64 | 65 | @Override 66 | public ResultSet getResultSet(long index, int count) throws SQLException { 67 | throw new SQLFeatureNotSupportedException(); 68 | } 69 | 70 | @Override 71 | public ResultSet getResultSet(long index, int count, Map> map) throws SQLException { 72 | throw new SQLFeatureNotSupportedException(); 73 | } 74 | 75 | @Override 76 | public void free() { 77 | if (array != null) { 78 | array = null; 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /driver/src/main/java/com/intellij/types/ColumnTypeHelper.java: -------------------------------------------------------------------------------- 1 | package com.intellij.types; 2 | 3 | import java.sql.Types; 4 | import java.util.HashMap; 5 | import java.util.Locale; 6 | import java.util.Map; 7 | 8 | public class ColumnTypeHelper { 9 | private static final Map javaTypeMap = new HashMap<>(); 10 | private static final Map typeNameMap = new HashMap<>(); 11 | 12 | static { 13 | javaTypeMap.put("map", Types.JAVA_OBJECT); 14 | javaTypeMap.put("object", Types.JAVA_OBJECT); 15 | javaTypeMap.put("array", Types.ARRAY); 16 | javaTypeMap.put("numeric", Types.NUMERIC); 17 | javaTypeMap.put("int", Types.INTEGER); 18 | javaTypeMap.put("integer", Types.INTEGER); 19 | javaTypeMap.put("long", Types.BIGINT); 20 | javaTypeMap.put("short", Types.INTEGER); 21 | javaTypeMap.put("boolean", Types.BOOLEAN); 22 | javaTypeMap.put("string", Types.VARCHAR); 23 | javaTypeMap.put("null", Types.NULL); 24 | javaTypeMap.put("float", Types.FLOAT); 25 | javaTypeMap.put("double", Types.DOUBLE); 26 | 27 | 28 | typeNameMap.put("map", "java.util.Map"); 29 | typeNameMap.put("object", "java.util.Map"); 30 | typeNameMap.put("array", "java.util.List"); 31 | typeNameMap.put("numeric", "java.lang.Long"); 32 | typeNameMap.put("int", "java.lang.Integer"); 33 | typeNameMap.put("integer", "java.lang.Integer"); 34 | typeNameMap.put("long", "java.lang.Long"); 35 | typeNameMap.put("short", "java.lang.Short"); 36 | typeNameMap.put("boolean", "java.lang.Boolean"); 37 | typeNameMap.put("string", "java.lang.String"); 38 | typeNameMap.put("null", "java.lang.Object"); 39 | typeNameMap.put("float", "java.lang.Float"); 40 | typeNameMap.put("double", "java.lang.Double"); 41 | } 42 | 43 | public static int getJavaType(String typeName) { 44 | String lower = toLowerCase(typeName); 45 | if (javaTypeMap.containsKey(lower)) { 46 | return javaTypeMap.get(lower); 47 | } 48 | throw new IllegalArgumentException("Type name is not known: " + lower); 49 | } 50 | 51 | public static String getClassName(String typeName) { 52 | String lower = toLowerCase(typeName); 53 | if (typeNameMap.containsKey(lower)) { 54 | return typeNameMap.get(lower); 55 | } 56 | throw new IllegalArgumentException("Type name is not known: " + lower); 57 | } 58 | 59 | private static String toLowerCase(String value) { 60 | return value.toLowerCase(Locale.ENGLISH); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /driver/src/main/resources/META-INF/services/java.sql.Driver: -------------------------------------------------------------------------------- 1 | com.intellij.CouchbaseJdbcDriver -------------------------------------------------------------------------------- /driver/src/test/com/intellij/CouchbaseClientURITest.java: -------------------------------------------------------------------------------- 1 | package com.intellij; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Properties; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | /** 12 | * @author Liudmila Kornilova 13 | **/ 14 | public class CouchbaseClientURITest { 15 | 16 | @Test(expected = IllegalArgumentException.class) 17 | public void testUriForDifferentDb() { 18 | new CouchbaseClientURI("jdbc:postgresql://localhost:54332/guest", null); 19 | } 20 | 21 | @Test 22 | public void testSimpleUri() { 23 | CouchbaseClientURI uri = new CouchbaseClientURI("jdbc:couchbase:localhost:9042", null); 24 | List hosts = Arrays.asList(uri.getHosts().split(",")); 25 | assertEquals(1, hosts.size()); 26 | assertEquals("localhost:9042", hosts.get(0)); 27 | } 28 | 29 | @Test 30 | public void testUriWithUserName() { 31 | CouchbaseClientURI uri = new CouchbaseClientURI("jdbc:couchbase:localhost:9042?user=cassandra", null); 32 | List hosts = Arrays.asList(uri.getHosts().split(",")); 33 | assertEquals(1, hosts.size()); 34 | assertEquals("localhost:9042", hosts.get(0)); 35 | assertEquals("cassandra", uri.getUsername()); 36 | } 37 | 38 | @Test 39 | public void testUriConnectionString() { 40 | String uriSting = "jdbc:couchbase:localhost:9042?user=cass&kv.timeout=123s&retry=exponential&password=pass"; 41 | CouchbaseClientURI uri = new CouchbaseClientURI(uriSting, null); 42 | assertEquals("cass", uri.getUsername()); 43 | assertEquals("pass", uri.getPassword()); 44 | assertEquals("localhost:9042?kv.timeout=123s&retry=exponential", uri.getConnectionString()); 45 | } 46 | 47 | @Test 48 | public void testUriConnectionStringWithoutOptionsPart() { 49 | String uriSting = "jdbc:couchbase:127.0.0.1:9042?user=p&password=p"; 50 | CouchbaseClientURI uri = new CouchbaseClientURI(uriSting, null); 51 | assertEquals("127.0.0.1:9042?", uri.getConnectionString()); 52 | } 53 | 54 | @Test 55 | public void testOptionsInProperties() { 56 | Properties properties = new Properties(); 57 | properties.put("user", "NameFromProperties"); 58 | properties.put("password", "PasswordFromProperties"); 59 | CouchbaseClientURI uri = new CouchbaseClientURI( 60 | "jdbc:couchbase:localhost:9042?user=cassandra&password=cassandra", 61 | properties); 62 | List hosts = Arrays.asList(uri.getHosts().split(",")); 63 | assertEquals(1, hosts.size()); 64 | assertEquals("localhost:9042", hosts.get(0)); 65 | assertEquals("NameFromProperties", uri.getUsername()); 66 | assertEquals("PasswordFromProperties", uri.getPassword()); 67 | } 68 | 69 | 70 | @Test 71 | public void testSslEnabledOptionTrue() { 72 | Properties properties = new Properties(); 73 | properties.put("sslenabled", "true"); 74 | CouchbaseClientURI uri = new CouchbaseClientURI( 75 | "jdbc:couchbase:localhost:9042?name=cassandra&password=cassandra", 76 | properties); 77 | assertTrue(uri.getSslEnabled()); 78 | } 79 | 80 | @Test 81 | public void testSslEnabledOptionFalse() { 82 | Properties properties = new Properties(); 83 | properties.put("sslenabled", "false"); 84 | CouchbaseClientURI uri = new CouchbaseClientURI( 85 | "jdbc:couchbase:localhost:9042?name=cassandra&password=cassandra", 86 | properties); 87 | assertFalse(uri.getSslEnabled()); 88 | } 89 | 90 | @Test 91 | public void testVerifyServerCertificateOptionFalse() { 92 | Properties properties = new Properties(); 93 | properties.put("sslenabled", "true"); 94 | CouchbaseClientURI uri = new CouchbaseClientURI( 95 | "jdbc:couchbase:localhost:9042?verifyServerCertificate=false", 96 | properties); 97 | assertFalse(uri.getVerifyServerCertificate()); 98 | assertEquals("localhost:9042?", uri.getConnectionString()); 99 | } 100 | 101 | @Test 102 | public void testNullSslEnabledOptionFalse() { 103 | Properties properties = new Properties(); 104 | CouchbaseClientURI uri = new CouchbaseClientURI( 105 | "jdbc:couchbase:localhost:9042?name=cassandra&password=cassandra", 106 | properties); 107 | assertFalse(uri.getSslEnabled()); 108 | } 109 | 110 | @Test 111 | public void testDefaultBucket() { 112 | Properties properties = new Properties(); 113 | CouchbaseClientURI uri = new CouchbaseClientURI( 114 | "jdbc:couchbase:localhost:9042/default", 115 | properties); 116 | assertEquals("default", uri.getDefaultBucket()); 117 | } 118 | 119 | @Test 120 | public void testDefaultBucketWithOptions() { 121 | Properties properties = new Properties(); 122 | CouchbaseClientURI uri = new CouchbaseClientURI( 123 | "jdbc:couchbase:localhost:9042/default?kv.timeout=123s&retry=exponential", 124 | properties); 125 | assertEquals("default", uri.getDefaultBucket()); 126 | assertEquals("localhost:9042?kv.timeout=123s&retry=exponential", uri.getConnectionString()); 127 | } 128 | 129 | @Test 130 | public void testDefaultBucketInProperties() { 131 | Properties properties = new Properties(); 132 | properties.put("defaultBucket", "default"); 133 | CouchbaseClientURI uri = new CouchbaseClientURI( 134 | "jdbc:couchbase:localhost:9042", 135 | properties); 136 | assertEquals("default", uri.getDefaultBucket()); 137 | assertEquals("localhost:9042", uri.getConnectionString()); 138 | } 139 | 140 | @Test 141 | public void testDefaultBucketInUrlParameters() { 142 | Properties properties = new Properties(); 143 | CouchbaseClientURI uri = new CouchbaseClientURI( 144 | "jdbc:couchbase:localhost:9042?defaultBucket=default", 145 | properties); 146 | assertEquals("default", uri.getDefaultBucket()); 147 | assertEquals("localhost:9042?", uri.getConnectionString()); 148 | } 149 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataGrip/couchbase-jdbc-driver/3826bf807442b63a85eb341ab97493ab7fba9dc5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | 88 | @rem Execute Gradle 89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 90 | 91 | :end 92 | @rem End local scope for the variables with windows NT shell 93 | if "%ERRORLEVEL%"=="0" goto mainEnd 94 | 95 | :fail 96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 97 | rem the _cmd.exe /c_ return code! 98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 99 | exit /b 1 100 | 101 | :mainEnd 102 | if "%OS%"=="Windows_NT" endlocal 103 | 104 | :omega 105 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2023 JetBrains s.r.o. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'couchbase-jdbc-driver' 2 | 3 | --------------------------------------------------------------------------------