forked from hugegraph/hugegraph-sync
Compare commits
4 Commits
master
...
release-0.
| Author | SHA1 | Date |
|---|---|---|
|
|
81c209d3ea | |
|
|
ed70e00c2e | |
|
|
55166c7460 | |
|
|
6fdbebf6c0 |
|
|
@ -1,8 +1,8 @@
|
|||
# HugeGraph
|
||||
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[](https://travis-ci.org/hugegraph/hugegraph)
|
||||
[](https://codecov.io/gh/hugegraph/hugegraph)
|
||||
[](https://travis-ci.org/hugegraph/hugegraph)
|
||||
[](https://codecov.io/gh/hugegraph/hugegraph)
|
||||
|
||||
HugeGraph is a fast-speed and highly-scalable [graph database](https://en.wikipedia.org/wiki/Graph_database). Billions of vertices and edges can be easily stored into and queried from HugeGraph due to its excellent OLTP ability. As compliance to [Apache TinkerPop 3](https://tinkerpop.apache.org/) framework, various complicated graph queries can be accomplished through [Gremlin](https://tinkerpop.apache.org/gremlin.html)(a powerful graph traversal language).
|
||||
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ import com.baidu.hugegraph.metrics.MetricsUtil;
|
|||
import com.baidu.hugegraph.server.RestServer;
|
||||
import com.baidu.hugegraph.type.HugeType;
|
||||
import com.baidu.hugegraph.util.E;
|
||||
import com.baidu.hugegraph.util.JsonUtil;
|
||||
import com.baidu.hugegraph.util.Log;
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public class API {
|
||||
|
|
@ -172,13 +172,15 @@ public class API {
|
|||
if (properties == null || properties.isEmpty()) {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
Map<String, Object> props = null;
|
||||
try {
|
||||
props = mapper.readValue(properties, Map.class);
|
||||
props = JsonUtil.fromJson(properties, Map.class);
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
// If properties is the string "null", props will be null
|
||||
E.checkArgument(props != null, "Invalid request with none properties");
|
||||
E.checkArgument(props != null,
|
||||
"Invalid request with properties: %s", properties);
|
||||
return props;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -276,13 +276,9 @@ public class EdgeAPI extends BatchAPI {
|
|||
|
||||
Map<String, Object> props = parseProperties(properties);
|
||||
if (page != null) {
|
||||
E.checkArgument(vertexId == null && direction == null &&
|
||||
offset == 0,
|
||||
E.checkArgument(offset == 0,
|
||||
"Not support querying edges based on paging " +
|
||||
"and [vertex, direction, offset] together");
|
||||
E.checkArgument(props.size() <= 1,
|
||||
"Not support querying edges based on paging " +
|
||||
"and more than one property");
|
||||
"and offset together");
|
||||
}
|
||||
|
||||
Id vertex = VertexAPI.checkAndParseVertexId(vertexId);
|
||||
|
|
|
|||
|
|
@ -230,9 +230,6 @@ public class VertexAPI extends BatchAPI {
|
|||
E.checkArgument(offset == 0,
|
||||
"Not support querying vertices based on paging " +
|
||||
"and offset together");
|
||||
E.checkArgument(props.size() <= 1,
|
||||
"Not support querying vertices based on paging " +
|
||||
"and more than one property");
|
||||
}
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
|
|
|||
|
|
@ -209,21 +209,27 @@ public class CassandraSessionPool extends BackendSessionPool {
|
|||
} catch (InvalidQueryException ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
assert this.session == null;
|
||||
this.session = cluster().connect(keyspace());
|
||||
this.opened = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean opened() {
|
||||
return this.session != null;
|
||||
if (this.opened && this.session == null) {
|
||||
this.tryOpen();
|
||||
}
|
||||
return this.opened && this.session != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closed() {
|
||||
if (this.session == null) {
|
||||
this.tryOpen();
|
||||
if (!this.opened || this.session == null) {
|
||||
return true;
|
||||
}
|
||||
return this.session == null ? true : this.session.isClosed();
|
||||
return this.session.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -233,6 +239,7 @@ public class CassandraSessionPool extends BackendSessionPool {
|
|||
return;
|
||||
}
|
||||
this.session.close();
|
||||
this.session = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -123,7 +123,8 @@ public abstract class CassandraStore
|
|||
this.store);
|
||||
}
|
||||
|
||||
if (this.sessions.opened()) {
|
||||
assert this.sessions != null;
|
||||
if (!this.sessions.closed()) {
|
||||
// TODO: maybe we should throw an exception here instead of ignore
|
||||
LOG.debug("Store {} has been opened before", this.store);
|
||||
this.sessions.useSession();
|
||||
|
|
@ -169,7 +170,7 @@ public abstract class CassandraStore
|
|||
@Override
|
||||
public boolean opened() {
|
||||
this.checkClusterConnected();
|
||||
return !this.sessions.session().closed();
|
||||
return this.sessions.session().opened();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -259,15 +260,16 @@ public abstract class CassandraStore
|
|||
public void init() {
|
||||
this.checkClusterConnected();
|
||||
|
||||
// Create keyspace if needed
|
||||
if (!this.existsKeyspace()) {
|
||||
this.initKeyspace();
|
||||
}
|
||||
|
||||
if (this.sessions.session().opened()) {
|
||||
// Session has ever been opened.
|
||||
LOG.warn("Session has ever been opened(exist keyspace '{}' before)",
|
||||
this.keyspace);
|
||||
} else {
|
||||
// Create keyspace if needed
|
||||
if (!this.existsKeyspace()) {
|
||||
this.initKeyspace();
|
||||
}
|
||||
// Open session explicitly to get the exception when it fails
|
||||
this.sessions.session().open();
|
||||
}
|
||||
|
|
@ -280,13 +282,16 @@ public abstract class CassandraStore
|
|||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
public void clear(boolean clearSpace) {
|
||||
this.checkClusterConnected();
|
||||
|
||||
if (this.existsKeyspace()) {
|
||||
this.checkOpened();
|
||||
this.clearTables();
|
||||
this.clearKeyspace();
|
||||
if (!clearSpace) {
|
||||
this.checkOpened();
|
||||
this.clearTables();
|
||||
} else {
|
||||
this.clearKeyspace();
|
||||
}
|
||||
}
|
||||
|
||||
LOG.debug("Store cleared: {}", this.store);
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ public abstract class CassandraTable
|
|||
Query query) {
|
||||
ExtendableIterator<BackendEntry> rs = new ExtendableIterator<>();
|
||||
|
||||
if (query.limit() == 0 && query.limit() != Query.NO_LIMIT) {
|
||||
if (query.limit() == 0L && !query.nolimit()) {
|
||||
LOG.debug("Return empty result(limit=0) for query {}", query);
|
||||
return rs;
|
||||
}
|
||||
|
|
@ -159,7 +159,7 @@ public abstract class CassandraTable
|
|||
}
|
||||
|
||||
protected void setPageState(Query query, List<Select> selects) {
|
||||
if (query.limit() == Query.NO_LIMIT) {
|
||||
if (query.nolimit()) {
|
||||
return;
|
||||
}
|
||||
for (Select select : selects) {
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@ public class CachedBackendStore implements BackendStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.store.clear();
|
||||
public void clear(boolean clearSpace) {
|
||||
this.store.clear(clearSpace);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -26,18 +26,42 @@ import com.baidu.hugegraph.util.E;
|
|||
|
||||
public class IdHolderList extends ArrayList<IdHolder> {
|
||||
|
||||
private static final IdHolderList EMPTY_P = new IdHolderList(true);
|
||||
private static final IdHolderList EMPTY_NP = new IdHolderList(false);
|
||||
|
||||
private static final long serialVersionUID = -738694176552424990L;
|
||||
|
||||
private final boolean paging;
|
||||
private final boolean needSkipOffset;
|
||||
|
||||
public static IdHolderList empty(boolean paging) {
|
||||
IdHolderList empty = paging ? EMPTY_P : EMPTY_NP;
|
||||
empty.clear();
|
||||
return empty;
|
||||
}
|
||||
|
||||
public IdHolderList(boolean paging) {
|
||||
this(paging, true);
|
||||
}
|
||||
|
||||
public IdHolderList(boolean paging, boolean needSkipOffset) {
|
||||
this.paging = paging;
|
||||
this.needSkipOffset = needSkipOffset;
|
||||
}
|
||||
|
||||
public boolean paging() {
|
||||
return this.paging;
|
||||
}
|
||||
|
||||
public boolean needSkipOffset() {
|
||||
return this.needSkipOffset;
|
||||
}
|
||||
|
||||
public boolean sameParameters(IdHolderList other) {
|
||||
return this.paging == other.paging &&
|
||||
this.needSkipOffset == other.needSkipOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(IdHolder holder) {
|
||||
E.checkArgument(this.paging == holder.paging(),
|
||||
|
|
|
|||
|
|
@ -62,11 +62,12 @@ public final class QueryList {
|
|||
return this.fetcher;
|
||||
}
|
||||
|
||||
public void add(List<IdHolder> holders) {
|
||||
public void add(IdHolderList holders) {
|
||||
// IdHolderList is results of one index query, the query is flattened
|
||||
if (!this.parent.paging()) {
|
||||
for (QueryHolder q : this.queries) {
|
||||
if (q instanceof IndexQuery) {
|
||||
((IndexQuery) q).holders.addAll(holders);
|
||||
((IndexQuery) q).merge(holders);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -124,7 +125,6 @@ public final class QueryList {
|
|||
return query.iterator(offset - current, pageInfo.page(), pageSize);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static Set<Id> limit(Set<Id> ids, Query query) {
|
||||
long fromIndex = query.offset();
|
||||
E.checkArgument(fromIndex <= Integer.MAX_VALUE,
|
||||
|
|
@ -134,11 +134,11 @@ public final class QueryList {
|
|||
if (query.offset() >= ids.size()) {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
if (query.limit() == Query.NO_LIMIT && query.offset() == 0) {
|
||||
if (query.nolimit() && query.offset() == 0) {
|
||||
return ids;
|
||||
}
|
||||
long toIndex = query.offset() + query.limit();
|
||||
if (query.limit() == Query.NO_LIMIT || toIndex > ids.size()) {
|
||||
long toIndex = query.total();
|
||||
if (query.nolimit() || toIndex > ids.size()) {
|
||||
toIndex = ids.size();
|
||||
}
|
||||
assert fromIndex < ids.size();
|
||||
|
|
@ -191,14 +191,13 @@ public final class QueryList {
|
|||
Query query = this.query.copy();
|
||||
query.page(page);
|
||||
// Not set limit to pageSize due to PageEntryIterator.remaining
|
||||
if (this.query.limit() == Query.NO_LIMIT) {
|
||||
if (this.query.nolimit()) {
|
||||
query.limit(pageSize);
|
||||
}
|
||||
QueryResults results = fetcher().apply(query);
|
||||
QueryResults rs = fetcher().apply(query);
|
||||
// Must iterate all entries before get the next page
|
||||
return new PageIterator(results.list().iterator(),
|
||||
results.queries(),
|
||||
PageInfo.pageState(results.iterator()));
|
||||
return new PageIterator(rs.list().iterator(), rs.queries(),
|
||||
PageInfo.pageState(rs.iterator()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -212,11 +211,21 @@ public final class QueryList {
|
|||
*/
|
||||
private class IndexQuery implements QueryHolder {
|
||||
|
||||
// Actual is an instance of IdHolderList
|
||||
private final List<IdHolder> holders;
|
||||
// An IdHolder each sub-query
|
||||
private final IdHolderList holders;
|
||||
// To skip the offset in parent query
|
||||
private long offsetToSkip;
|
||||
|
||||
public IndexQuery(List<IdHolder> holders) {
|
||||
public IndexQuery(IdHolderList holders) {
|
||||
this.holders = holders;
|
||||
this.offsetToSkip = this.holders.needSkipOffset() ?
|
||||
parent().offset() : -1L;
|
||||
}
|
||||
|
||||
public void merge(IdHolderList holders) {
|
||||
E.checkState(holders.sameParameters(this.holders),
|
||||
"Can't merge IdHolderList with different parameters");
|
||||
this.holders.addAll(holders);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -229,18 +238,23 @@ public final class QueryList {
|
|||
|
||||
private QueryResults each(IdHolder holder) {
|
||||
Set<Id> ids = holder.ids();
|
||||
if (ids.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (parent().limit() != Query.NO_LIMIT &&
|
||||
ids.size() > parent().limit()) {
|
||||
Query parent = parent();
|
||||
if (this.offsetToSkip > 0L) {
|
||||
// Skip offset when needed
|
||||
this.offsetToSkip -= ids.size();
|
||||
ids = limit(ids, parent);
|
||||
} else if (!parent.nolimit() && ids.size() > parent.total()) {
|
||||
/*
|
||||
* Avoid too many ids in one time query,
|
||||
* Assume it will get one result by each id
|
||||
*/
|
||||
ids = CollectionUtil.subSet(ids, 0, (int) parent().limit());
|
||||
ids = CollectionUtil.subSet(ids, 0, (int) parent.total());
|
||||
}
|
||||
IdQuery query = new IdQuery(parent(), ids);
|
||||
|
||||
if (ids.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
IdQuery query = new IdQuery(parent, ids);
|
||||
return fetcher().apply(query);
|
||||
}
|
||||
|
||||
|
|
@ -252,8 +266,8 @@ public final class QueryList {
|
|||
return PageIterator.EMPTY;
|
||||
}
|
||||
IdQuery query = new IdQuery(parent(), pageIds.ids());
|
||||
QueryResults results = fetcher().apply(query);
|
||||
return new PageIterator(results.iterator(), results.queries(),
|
||||
QueryResults rs = fetcher().apply(query);
|
||||
return new PageIterator(rs.iterator(), rs.queries(),
|
||||
pageIds.pageState());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -156,6 +156,10 @@ public class Query implements Cloneable {
|
|||
this.limit = limit;
|
||||
}
|
||||
|
||||
public boolean nolimit() {
|
||||
return this.limit() == NO_LIMIT;
|
||||
}
|
||||
|
||||
public boolean reachLimit(long count) {
|
||||
long limit = this.limit();
|
||||
if (limit == NO_LIMIT) {
|
||||
|
|
@ -170,7 +174,7 @@ public class Query implements Cloneable {
|
|||
* @param start the range start, include it
|
||||
* @param end the range end, exclude it
|
||||
*/
|
||||
public void range(long start, long end) {
|
||||
public long range(long start, long end) {
|
||||
// Update offset
|
||||
long offset = this.offset();
|
||||
start = Math.max(start, offset);
|
||||
|
|
@ -178,7 +182,7 @@ public class Query implements Cloneable {
|
|||
|
||||
// Update limit
|
||||
if (end != -1L) {
|
||||
if (this.limit() != Query.NO_LIMIT) {
|
||||
if (!this.nolimit()) {
|
||||
end = Math.min(end, offset + this.limit());
|
||||
} else {
|
||||
assert end < Query.NO_LIMIT;
|
||||
|
|
@ -190,6 +194,7 @@ public class Query implements Cloneable {
|
|||
// Keep the origin limit
|
||||
assert this.limit() <= Query.NO_LIMIT;
|
||||
}
|
||||
return this.limit;
|
||||
}
|
||||
|
||||
public String page() {
|
||||
|
|
|
|||
|
|
@ -178,8 +178,8 @@ public class QueryResults {
|
|||
return (Iterator<T>) EMPTY_ITERATOR;
|
||||
}
|
||||
|
||||
private static class EmptyIterator<T> implements Iterator<T>,
|
||||
Metadatable {
|
||||
private static class EmptyIterator<T> implements Iterator<T>, Metadatable {
|
||||
|
||||
@Override
|
||||
public Object metadata(String meta, Object... args) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -112,7 +112,12 @@ public abstract class AbstractBackendStoreProvider
|
|||
public void clear() throws BackendException {
|
||||
this.checkOpened();
|
||||
for (BackendStore store : this.stores.values()) {
|
||||
store.clear();
|
||||
// Just clear tables of store, not clear space
|
||||
store.clear(false);
|
||||
}
|
||||
for (BackendStore store : this.stores.values()) {
|
||||
// Only clear space of store
|
||||
store.clear(true);
|
||||
}
|
||||
this.notifyAndWaitEvent(Events.STORE_CLEAR);
|
||||
|
||||
|
|
|
|||
|
|
@ -26,12 +26,14 @@ import com.baidu.hugegraph.backend.store.BackendStore.TxState;
|
|||
*/
|
||||
public abstract class BackendSession {
|
||||
|
||||
protected boolean opened;
|
||||
private int refs;
|
||||
private TxState txState;
|
||||
private final long created;
|
||||
private long updated;
|
||||
|
||||
public BackendSession() {
|
||||
this.opened = true;
|
||||
this.refs = 1;
|
||||
this.txState = TxState.CLEAN;
|
||||
this.created = System.currentTimeMillis();
|
||||
|
|
@ -50,10 +52,9 @@ public abstract class BackendSession {
|
|||
this.updated = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public abstract void open();
|
||||
public abstract void close();
|
||||
|
||||
public abstract boolean closed();
|
||||
|
||||
public abstract Object commit();
|
||||
|
||||
public abstract void rollback();
|
||||
|
|
@ -64,6 +65,18 @@ public abstract class BackendSession {
|
|||
// pass
|
||||
}
|
||||
|
||||
protected void reset() {
|
||||
// pass
|
||||
}
|
||||
|
||||
public boolean opened() {
|
||||
return this.opened;
|
||||
}
|
||||
|
||||
public boolean closed() {
|
||||
return !this.opened;
|
||||
}
|
||||
|
||||
protected int attach() {
|
||||
return ++this.refs;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package com.baidu.hugegraph.backend.store;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
|
|
@ -37,12 +39,14 @@ public abstract class BackendSessionPool {
|
|||
private final String name;
|
||||
private final ThreadLocal<BackendSession> threadLocalSession;
|
||||
private final AtomicInteger sessionCount;
|
||||
private final Map<Long, BackendSession> sessions;
|
||||
|
||||
public BackendSessionPool(HugeConfig config, String name) {
|
||||
this.config = config;
|
||||
this.name = name;
|
||||
this.threadLocalSession = new ThreadLocal<>();
|
||||
this.sessionCount = new AtomicInteger(0);
|
||||
this.sessions = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public HugeConfig config() {
|
||||
|
|
@ -55,6 +59,8 @@ public abstract class BackendSessionPool {
|
|||
session = this.newSession();
|
||||
assert session != null;
|
||||
this.threadLocalSession.set(session);
|
||||
assert !this.sessions.containsKey(Thread.currentThread().getId());
|
||||
this.sessions.put(Thread.currentThread().getId(), session);
|
||||
int sessionCount = this.sessionCount.incrementAndGet();
|
||||
LOG.debug("Now(after connect({})) session count is: {}",
|
||||
this, sessionCount);
|
||||
|
|
@ -113,9 +119,18 @@ public abstract class BackendSessionPool {
|
|||
throw e;
|
||||
}
|
||||
this.threadLocalSession.remove();
|
||||
assert this.sessions.containsKey(Thread.currentThread().getId());
|
||||
this.sessions.remove(Thread.currentThread().getId());
|
||||
|
||||
return Pair.of(this.sessionCount.decrementAndGet(), ref);
|
||||
}
|
||||
|
||||
protected void forceResetSessions() {
|
||||
for (BackendSession session : this.sessions.values()) {
|
||||
session.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
Pair<Integer, Integer> result = Pair.of(-1, -1);
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public interface BackendStore {
|
|||
|
||||
// Initialize/clear database
|
||||
public void init();
|
||||
public void clear();
|
||||
public void clear(boolean clearSpace);
|
||||
public boolean initialized();
|
||||
|
||||
// Delete all data of database (keep table structure)
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ public abstract class InMemoryDBStore
|
|||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
public void clear(boolean clearSpace) {
|
||||
for (InMemoryDBTable table : this.tables()) {
|
||||
table.clear(null);
|
||||
}
|
||||
|
|
@ -263,9 +263,9 @@ public abstract class InMemoryDBStore
|
|||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
public void clear(boolean clearSpace) {
|
||||
this.counter.reset();
|
||||
super.clear();
|
||||
super.clear(clearSpace);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -147,8 +147,7 @@ public class InMemoryDBTable extends BackendTable<BackendSession,
|
|||
}
|
||||
iterator = this.skipOffset(iterator, query.offset());
|
||||
|
||||
if (query.limit() != Query.NO_LIMIT &&
|
||||
query.offset() + query.limit() < rs.size()) {
|
||||
if (!query.nolimit() && query.total() < rs.size()) {
|
||||
iterator = this.dropTails(iterator, query.limit());
|
||||
}
|
||||
return iterator;
|
||||
|
|
|
|||
|
|
@ -154,8 +154,7 @@ public abstract class AbstractTransaction implements Transaction {
|
|||
RateLimiter rateLimiter = this.graph.rateLimiter();
|
||||
if (rateLimiter != null) {
|
||||
int size = this.mutationSize();
|
||||
assert size > 0;
|
||||
double time = rateLimiter.acquire(size);
|
||||
double time = size > 0 ? rateLimiter.acquire(size) : 0.0;
|
||||
if (time > 0) {
|
||||
LOG.debug("Waited for {}s to mutate {} item(s)", time, size);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ public class GraphIndexTransaction extends AbstractTransaction {
|
|||
* @return converted id query
|
||||
*/
|
||||
@Watched(prefix = "index")
|
||||
public List<IdHolder> queryIndex(ConditionQuery query) {
|
||||
public IdHolderList queryIndex(ConditionQuery query) {
|
||||
// Index query must have been flattened in Graph tx
|
||||
query.checkFlattened();
|
||||
|
||||
|
|
@ -355,7 +355,7 @@ public class GraphIndexTransaction extends AbstractTransaction {
|
|||
}
|
||||
|
||||
@Watched(prefix = "index")
|
||||
private List<IdHolder> queryByLabel(ConditionQuery query) {
|
||||
private IdHolderList queryByLabel(ConditionQuery query) {
|
||||
HugeType queryType = query.resultType();
|
||||
IndexLabel il = IndexLabel.label(queryType);
|
||||
Id label = query.condition(HugeKeys.LABEL);
|
||||
|
|
@ -390,13 +390,15 @@ public class GraphIndexTransaction extends AbstractTransaction {
|
|||
indexQuery.capacity(query.capacity());
|
||||
|
||||
IdHolder idHolder = this.doIndexQuery(il, indexQuery);
|
||||
List<IdHolder> holders = new IdHolderList(query.paging());
|
||||
|
||||
// NOTE: the backend itself will skip the offset
|
||||
IdHolderList holders = new IdHolderList(query.paging(), false);
|
||||
holders.add(idHolder);
|
||||
return holders;
|
||||
}
|
||||
|
||||
@Watched(prefix = "index")
|
||||
private List<IdHolder> queryByUserprop(ConditionQuery query) {
|
||||
private IdHolderList queryByUserprop(ConditionQuery query) {
|
||||
// Get user applied label or collect all qualified labels with
|
||||
// related index labels
|
||||
Set<MatchedIndex> indexes = this.collectMatchedIndexes(query);
|
||||
|
|
@ -406,12 +408,12 @@ public class GraphIndexTransaction extends AbstractTransaction {
|
|||
}
|
||||
|
||||
// Value type of Condition not matched
|
||||
boolean paging = query.paging();
|
||||
if (!validQueryConditionValues(this.graph(), query)) {
|
||||
return ImmutableList.of();
|
||||
return IdHolderList.empty(paging);
|
||||
}
|
||||
|
||||
// Do index query
|
||||
boolean paging = query.paging();
|
||||
IdHolderList holders = new IdHolderList(paging);
|
||||
long idsSize = 0;
|
||||
for (MatchedIndex index : indexes) {
|
||||
|
|
@ -430,6 +432,12 @@ public class GraphIndexTransaction extends AbstractTransaction {
|
|||
holders.add(holder);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish early if records exceeds required.
|
||||
* NOTE: need to skip the offset if offset > 0, but can't handle
|
||||
* it here because the query may a sub-query after flatten,
|
||||
* so the offset will be handle in QueryList.IndexQuery
|
||||
*/
|
||||
idsSize += holders.idsSize();
|
||||
if (query.reachLimit(idsSize)) {
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ import com.baidu.hugegraph.backend.BackendException;
|
|||
import com.baidu.hugegraph.backend.id.EdgeId;
|
||||
import com.baidu.hugegraph.backend.id.Id;
|
||||
import com.baidu.hugegraph.backend.id.SplicingIdGenerator;
|
||||
import com.baidu.hugegraph.backend.page.IdHolder;
|
||||
import com.baidu.hugegraph.backend.page.IdHolderList;
|
||||
import com.baidu.hugegraph.backend.page.PageInfo;
|
||||
import com.baidu.hugegraph.backend.page.QueryList;
|
||||
import com.baidu.hugegraph.backend.query.Condition;
|
||||
|
|
@ -553,8 +553,7 @@ public class GraphTransaction extends IndexableTransaction {
|
|||
}
|
||||
|
||||
public Iterator<Vertex> queryVertices(Query query) {
|
||||
E.checkArgument(this.removedVertices.isEmpty() ||
|
||||
query.limit() == Query.NO_LIMIT,
|
||||
E.checkArgument(this.removedVertices.isEmpty() || query.nolimit(),
|
||||
"It's not allowed to query with limit when " +
|
||||
"there are uncommitted delete records.");
|
||||
|
||||
|
|
@ -700,8 +699,7 @@ public class GraphTransaction extends IndexableTransaction {
|
|||
}
|
||||
|
||||
public Iterator<Edge> queryEdges(Query query) {
|
||||
E.checkArgument(this.removedEdges.isEmpty() ||
|
||||
query.limit() == Query.NO_LIMIT,
|
||||
E.checkArgument(this.removedEdges.isEmpty() || query.nolimit(),
|
||||
"It's not allowed to query with limit when " +
|
||||
"there are uncommitted delete records.");
|
||||
|
||||
|
|
@ -1103,7 +1101,7 @@ public class GraphTransaction extends IndexableTransaction {
|
|||
return null;
|
||||
}
|
||||
|
||||
private List<IdHolder> indexQuery(ConditionQuery query) {
|
||||
private IdHolderList indexQuery(ConditionQuery query) {
|
||||
/*
|
||||
* Optimize by index-query
|
||||
* It will return a list of id (maybe empty) if success,
|
||||
|
|
|
|||
|
|
@ -409,6 +409,17 @@ public class HugeGraphSONModule extends TinkerPopJacksonModule {
|
|||
|
||||
generator.writeEndObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWithType(HugeVertex value, JsonGenerator generator,
|
||||
SerializerProvider provider,
|
||||
TypeSerializer typeSer)
|
||||
throws IOException {
|
||||
WritableTypeId typeId = typeSer.typeId(value, JsonToken.VALUE_STRING);
|
||||
typeSer.writeTypePrefix(generator, typeId);
|
||||
this.serialize(value, generator, provider);
|
||||
typeSer.writeTypeSuffix(generator, typeId);
|
||||
}
|
||||
}
|
||||
|
||||
private static class HugeEdgeSerializer
|
||||
|
|
@ -441,6 +452,17 @@ public class HugeGraphSONModule extends TinkerPopJacksonModule {
|
|||
|
||||
generator.writeEndObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWithType(HugeEdge value, JsonGenerator generator,
|
||||
SerializerProvider provider,
|
||||
TypeSerializer typeSer)
|
||||
throws IOException {
|
||||
WritableTypeId typeId = typeSer.typeId(value, JsonToken.VALUE_STRING);
|
||||
typeSer.writeTypePrefix(generator, typeId);
|
||||
this.serialize(value, generator, provider);
|
||||
typeSer.writeTypeSuffix(generator, typeId);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ShardSerializer extends StdSerializer<Shard> {
|
||||
|
|
|
|||
|
|
@ -29,13 +29,14 @@ import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer;
|
|||
import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer;
|
||||
|
||||
import com.baidu.hugegraph.HugeException;
|
||||
import com.baidu.hugegraph.HugeFactory;
|
||||
import com.baidu.hugegraph.util.ReflectionUtil;
|
||||
import com.google.common.reflect.ClassPath;
|
||||
|
||||
public class HugeGraphGremlinPlugin extends AbstractGremlinPlugin {
|
||||
|
||||
private static final String PACKAGE = "com.baidu.hugegraph";
|
||||
private static final String NAME = "com.baidu.hugegraph";
|
||||
private static final String PACKAGE = "com.baidu.hugegraph.type.define";
|
||||
private static final String NAME = "HugeGraph";
|
||||
|
||||
private static final HugeGraphGremlinPlugin instance;
|
||||
private static final ImportCustomizer imports;
|
||||
|
|
@ -54,6 +55,9 @@ public class HugeGraphGremlinPlugin extends AbstractGremlinPlugin {
|
|||
@SuppressWarnings("rawtypes")
|
||||
Set<Class> classes = new HashSet<>();
|
||||
classInfos.forEachRemaining(classInfo -> classes.add(classInfo.load()));
|
||||
// Add entrance class: graph = HugeFactory.open("hugegraph.properties")
|
||||
classes.add(HugeFactory.class);
|
||||
|
||||
imports = DefaultImportCustomizer.build()
|
||||
.addClassImports(classes)
|
||||
.create();
|
||||
|
|
|
|||
|
|
@ -22,12 +22,19 @@ package com.baidu.hugegraph.security;
|
|||
import java.io.FileDescriptor;
|
||||
import java.net.InetAddress;
|
||||
import java.security.Permission;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.baidu.hugegraph.util.Log;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
public class HugeSecurityManager extends SecurityManager {
|
||||
|
||||
private static final String USER_DIR = System.getProperty("user.dir");
|
||||
|
||||
private static final String GREMLIN_SERVER_WORKER = "gremlin-server-exec";
|
||||
private static final String TASK_WORKER = "task-worker";
|
||||
private static final Set<String> GREMLIN_EXECUTOR_CLASS = ImmutableSet.of(
|
||||
|
|
@ -42,7 +49,8 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
"groovy.lang.GroovyClassLoader",
|
||||
"sun.reflect.DelegatingClassLoader",
|
||||
"org.codehaus.groovy.reflection.SunClassLoader",
|
||||
"org.codehaus.groovy.runtime.callsite.CallSiteClassLoader"
|
||||
"org.codehaus.groovy.runtime.callsite.CallSiteClassLoader",
|
||||
"org.apache.hadoop.hbase.util.DynamicClassLoader"
|
||||
);
|
||||
|
||||
private static final Set<String> CAFFEINE_CLASSES = ImmutableSet.of(
|
||||
|
|
@ -51,15 +59,49 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
|
||||
private static final Set<String> WHITE_SYSTEM_PROPERTYS = ImmutableSet.of(
|
||||
"line.separator",
|
||||
"file.separator"
|
||||
"file.separator",
|
||||
"socksProxyHost", // MySQL
|
||||
"file.encoding" // PostgreSQL
|
||||
);
|
||||
|
||||
private static final Map<String, Set<String>> ASYNC_TASKS = ImmutableMap.of(
|
||||
// Fixed https://github.com/hugegraph/hugegraph/pull/892#issue-387202362
|
||||
"com.baidu.hugegraph.backend.tx.SchemaTransaction",
|
||||
ImmutableSet.of("removeVertexLabel", "removeEdgeLabel",
|
||||
"removeIndexLabel", "rebuildIndex"),
|
||||
"com.baidu.hugegraph.backend.tx.GraphIndexTransaction",
|
||||
ImmutableSet.of("asyncRemoveIndexLeft")
|
||||
);
|
||||
|
||||
private static final Map<String, Set<String>> BACKEND_SOCKET = ImmutableMap.of(
|
||||
// Fixed #758
|
||||
"com.baidu.hugegraph.backend.store.mysql.MysqlStore",
|
||||
ImmutableSet.of("open", "init", "clear", "opened", "initialized")
|
||||
);
|
||||
|
||||
private static final Map<String, Set<String>> BACKEND_THREAD = ImmutableMap.of(
|
||||
// Fixed #758
|
||||
"com.baidu.hugegraph.backend.store.cassandra.CassandraStore",
|
||||
ImmutableSet.of("open", "opened", "init"),
|
||||
// Fixed https://github.com/hugegraph/hugegraph/pull/892#issuecomment-598545072
|
||||
"com.datastax.driver.core.AbstractSession",
|
||||
ImmutableSet.of("execute")
|
||||
);
|
||||
|
||||
private static final Set<String> HBASE_CLASSES = ImmutableSet.of(
|
||||
// Fixed #758
|
||||
"com.baidu.hugegraph.backend.store.hbase.HbaseStore",
|
||||
"com.baidu.hugegraph.backend.store.hbase.HbaseStore$HbaseSchemaStore",
|
||||
"com.baidu.hugegraph.backend.store.hbase.HbaseStore$HbaseGraphStore",
|
||||
"com.baidu.hugegraph.backend.store.hbase.HbaseSessions$RowIterator"
|
||||
);
|
||||
|
||||
@Override
|
||||
public void checkPermission(Permission permission) {
|
||||
if (DENIED_PERMISSIONS.contains(permission.getName()) &&
|
||||
callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to access denied permission via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to access denied permission via Gremlin");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,16 +109,16 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
public void checkPermission(Permission permission, Object context) {
|
||||
if (DENIED_PERMISSIONS.contains(permission.getName()) &&
|
||||
callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to access denied permission via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to access denied permission via Gremlin");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCreateClassLoader() {
|
||||
if (!callFromAcceptClassLoaders() && callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to create class loader via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to create class loader via Gremlin");
|
||||
}
|
||||
super.checkCreateClassLoader();
|
||||
}
|
||||
|
|
@ -84,26 +126,30 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkLink(String lib) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to link library via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to link library via Gremlin");
|
||||
}
|
||||
super.checkLink(lib);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(Thread thread) {
|
||||
if (callFromGremlin() && !callFromCaffeine()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to access thread via Gremlin");
|
||||
if (callFromGremlin() && !callFromCaffeine() &&
|
||||
!callFromAsyncTasks() && !callFromEventHubNotify() &&
|
||||
!callFromBackendThread() && !callFromBackendHbase()) {
|
||||
throw newSecurityException(
|
||||
"Not allowed to access thread via Gremlin");
|
||||
}
|
||||
super.checkAccess(thread);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(ThreadGroup threadGroup) {
|
||||
if (callFromGremlin() && !callFromCaffeine()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to access thread group via Gremlin");
|
||||
if (callFromGremlin() && !callFromCaffeine() &&
|
||||
!callFromAsyncTasks() && !callFromEventHubNotify() &&
|
||||
!callFromBackendThread() && !callFromBackendHbase()) {
|
||||
throw newSecurityException(
|
||||
"Not allowed to access thread group via Gremlin");
|
||||
}
|
||||
super.checkAccess(threadGroup);
|
||||
}
|
||||
|
|
@ -111,8 +157,8 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkExit(int status) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to call System.exit() via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to call System.exit() via Gremlin");
|
||||
}
|
||||
super.checkExit(status);
|
||||
}
|
||||
|
|
@ -120,24 +166,26 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkExec(String cmd) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to execute command via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to execute command via Gremlin");
|
||||
}
|
||||
super.checkExec(cmd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkRead(FileDescriptor fd) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException("Not allowed to read fd via Gremlin");
|
||||
if (callFromGremlin() && !callFromBackendSocket()) {
|
||||
throw newSecurityException("Not allowed to read fd via Gremlin");
|
||||
}
|
||||
super.checkRead(fd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkRead(String file) {
|
||||
if (callFromGremlin() && !callFromCaffeine()) {
|
||||
throw new SecurityException("Not allowed to read file via Gremlin");
|
||||
if (callFromGremlin() && !callFromCaffeine() &&
|
||||
!readGroovyInCurrentDir(file) && !callFromBackendHbase()) {
|
||||
throw newSecurityException(
|
||||
"Not allowed to read file via Gremlin: %s", file);
|
||||
}
|
||||
super.checkRead(file);
|
||||
}
|
||||
|
|
@ -145,15 +193,16 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkRead(String file, Object context) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException("Not allowed to read file via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to read file via Gremlin: %s", file);
|
||||
}
|
||||
super.checkRead(file, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkWrite(FileDescriptor fd) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException("Not allowed to write fd via Gremlin");
|
||||
if (callFromGremlin() && !callFromBackendSocket()) {
|
||||
throw newSecurityException("Not allowed to write fd via Gremlin");
|
||||
}
|
||||
super.checkWrite(fd);
|
||||
}
|
||||
|
|
@ -161,8 +210,7 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkWrite(String file) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to write file via Gremlin");
|
||||
throw newSecurityException("Not allowed to write file via Gremlin");
|
||||
}
|
||||
super.checkWrite(file);
|
||||
}
|
||||
|
|
@ -170,8 +218,8 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkDelete(String file) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to delete file via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to delete file via Gremlin");
|
||||
}
|
||||
super.checkDelete(file);
|
||||
}
|
||||
|
|
@ -179,8 +227,8 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkListen(int port) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to listen socket via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to listen socket via Gremlin");
|
||||
}
|
||||
super.checkListen(port);
|
||||
}
|
||||
|
|
@ -188,17 +236,18 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkAccept(String host, int port) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to accept socket via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to accept socket via Gremlin");
|
||||
}
|
||||
super.checkAccept(host, port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkConnect(String host, int port) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to connect socket via Gremlin");
|
||||
if (callFromGremlin() && !callFromBackendSocket() &&
|
||||
!callFromBackendHbase()) {
|
||||
throw newSecurityException(
|
||||
"Not allowed to connect socket via Gremlin");
|
||||
}
|
||||
super.checkConnect(host, port);
|
||||
}
|
||||
|
|
@ -206,8 +255,8 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkConnect(String host, int port, Object context) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to connect socket via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to connect socket via Gremlin");
|
||||
}
|
||||
super.checkConnect(host, port, context);
|
||||
}
|
||||
|
|
@ -215,7 +264,7 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkMulticast(InetAddress maddr) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException("Not allowed to multicast via Gremlin");
|
||||
throw newSecurityException("Not allowed to multicast via Gremlin");
|
||||
}
|
||||
super.checkMulticast(maddr);
|
||||
}
|
||||
|
|
@ -224,7 +273,7 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@SuppressWarnings("deprecation")
|
||||
public void checkMulticast(InetAddress maddr, byte ttl) {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException("Not allowed to multicast via Gremlin");
|
||||
throw newSecurityException("Not allowed to multicast via Gremlin");
|
||||
}
|
||||
super.checkMulticast(maddr, ttl);
|
||||
}
|
||||
|
|
@ -232,8 +281,8 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkSetFactory() {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to set socket factory via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to set socket factory via Gremlin");
|
||||
}
|
||||
super.checkSetFactory();
|
||||
}
|
||||
|
|
@ -241,8 +290,8 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkPropertiesAccess() {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to access system properties via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to access system properties via Gremlin");
|
||||
}
|
||||
super.checkPropertiesAccess();
|
||||
}
|
||||
|
|
@ -250,10 +299,9 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkPropertyAccess(String key) {
|
||||
if (!callFromAcceptClassLoaders() && callFromGremlin() &&
|
||||
!WHITE_SYSTEM_PROPERTYS.contains(key)) {
|
||||
throw new SecurityException(String.format(
|
||||
"Not allowed to access system property(%s) via Gremlin",
|
||||
key));
|
||||
!WHITE_SYSTEM_PROPERTYS.contains(key) && !callFromBackendHbase()) {
|
||||
throw newSecurityException(
|
||||
"Not allowed to access system property(%s) via Gremlin", key);
|
||||
}
|
||||
super.checkPropertyAccess(key);
|
||||
}
|
||||
|
|
@ -261,7 +309,7 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@Override
|
||||
public void checkPrintJobAccess() {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException("Not allowed to print job via Gremlin");
|
||||
throw newSecurityException("Not allowed to print job via Gremlin");
|
||||
}
|
||||
super.checkPrintJobAccess();
|
||||
}
|
||||
|
|
@ -270,8 +318,8 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
@SuppressWarnings("deprecation")
|
||||
public void checkSystemClipboardAccess() {
|
||||
if (callFromGremlin()) {
|
||||
throw new SecurityException(
|
||||
"Not allowed to access system clipboard via Gremlin");
|
||||
throw newSecurityException(
|
||||
"Not allowed to access system clipboard via Gremlin");
|
||||
}
|
||||
super.checkSystemClipboardAccess();
|
||||
}
|
||||
|
|
@ -309,6 +357,29 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
super.checkAwtEventQueueAccess();
|
||||
}
|
||||
|
||||
private static SecurityException newSecurityException(String message,
|
||||
Object... args) {
|
||||
if (args.length > 0) {
|
||||
message = String.format(message, args);
|
||||
}
|
||||
/*
|
||||
* use dynamic logger here because "static final logger" can't be
|
||||
* initialized: the logger is not initialized when HugeSecurityManager
|
||||
* class is loaded
|
||||
*/
|
||||
Logger log = Log.logger(HugeSecurityManager.class);
|
||||
log.warn("SecurityException: {}", message);
|
||||
return new SecurityException(message);
|
||||
}
|
||||
|
||||
private static boolean readGroovyInCurrentDir(String file) {
|
||||
if (USER_DIR != null && file != null && file.startsWith(USER_DIR)
|
||||
&& (file.endsWith(".class") || file.endsWith(".groovy"))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean callFromGremlin() {
|
||||
return callFromWorkerWithClass(GREMLIN_EXECUTOR_CLASS);
|
||||
}
|
||||
|
|
@ -321,6 +392,32 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
return callFromWorkerWithClass(CAFFEINE_CLASSES);
|
||||
}
|
||||
|
||||
private static boolean callFromBackendSocket() {
|
||||
// Fixed issue #758
|
||||
return callFromMethods(BACKEND_SOCKET);
|
||||
}
|
||||
|
||||
private static boolean callFromBackendThread() {
|
||||
// Fixed issue #758
|
||||
return callFromMethods(BACKEND_THREAD);
|
||||
}
|
||||
|
||||
private static boolean callFromEventHubNotify() {
|
||||
// Fixed issue #758
|
||||
// notify() will create thread when submit task to executor
|
||||
return callFromMethod("com.baidu.hugegraph.event.EventHub", "notify");
|
||||
}
|
||||
|
||||
private static boolean callFromAsyncTasks() {
|
||||
// Async tasks will create thread when submitted to executor
|
||||
return callFromMethods(ASYNC_TASKS);
|
||||
}
|
||||
|
||||
private static boolean callFromBackendHbase() {
|
||||
// TODO: remove this unsafe entrance
|
||||
return callFromWorkerWithClass(HBASE_CLASSES);
|
||||
}
|
||||
|
||||
private static boolean callFromWorkerWithClass(Set<String> classes) {
|
||||
Thread curThread = Thread.currentThread();
|
||||
if (curThread.getName().startsWith(GREMLIN_SERVER_WORKER) ||
|
||||
|
|
@ -336,7 +433,19 @@ public class HugeSecurityManager extends SecurityManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean callFromMethods(Map<String, Set<String>> methods) {
|
||||
Thread curThread = Thread.currentThread();
|
||||
StackTraceElement[] elements = curThread.getStackTrace();
|
||||
for (StackTraceElement element : elements) {
|
||||
Set<String> clazzMethods = methods.get(element.getClassName());
|
||||
if (clazzMethods != null &&
|
||||
clazzMethods.contains(element.getMethodName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean callFromMethod(String clazz, String method) {
|
||||
Thread curThread = Thread.currentThread();
|
||||
StackTraceElement[] elements = curThread.getStackTrace();
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public final class HugeGraphStep<S, E extends Element>
|
|||
// Store limit/order-by
|
||||
private final Query queryInfo = new Query(HugeType.UNKNOWN);
|
||||
|
||||
private Iterator<E> lastTimeResults = null;
|
||||
private Iterator<E> lastTimeResults = QueryResults.emptyIterator();
|
||||
|
||||
public HugeGraphStep(final GraphStep<S, E> originGraphStep) {
|
||||
super(originGraphStep.getTraversal(),
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import com.baidu.hugegraph.HugeGraph;
|
|||
import com.baidu.hugegraph.backend.id.Id;
|
||||
import com.baidu.hugegraph.backend.query.ConditionQuery;
|
||||
import com.baidu.hugegraph.backend.query.Query;
|
||||
import com.baidu.hugegraph.backend.query.QueryResults;
|
||||
import com.baidu.hugegraph.backend.tx.GraphTransaction;
|
||||
import com.baidu.hugegraph.type.define.Directions;
|
||||
import com.baidu.hugegraph.util.Log;
|
||||
|
|
@ -54,7 +55,7 @@ public final class HugeVertexStep<E extends Element>
|
|||
// Store limit/order-by
|
||||
private final Query queryInfo = new Query(null);
|
||||
|
||||
private Iterator<E> lastTimeResults = null;
|
||||
private Iterator<E> lastTimeResults = QueryResults.emptyIterator();
|
||||
|
||||
public HugeVertexStep(final VertexStep<E> originVertexStep) {
|
||||
super(originVertexStep.getTraversal(),
|
||||
|
|
|
|||
|
|
@ -50,8 +50,7 @@ public interface QueryHolder extends HasContainerHolder, Metadatable {
|
|||
}
|
||||
|
||||
public default long setRange(long start, long end) {
|
||||
this.queryInfo().range(start, end);
|
||||
return this.queryInfo().limit();
|
||||
return this.queryInfo().range(start, end);
|
||||
}
|
||||
|
||||
public default void setPage(String page) {
|
||||
|
|
|
|||
|
|
@ -570,9 +570,9 @@ public final class TraversalUtil {
|
|||
private static <V> V validPredicateValue(V value, PropertyKey pkey) {
|
||||
V validValue = pkey.convValue(value, false);
|
||||
E.checkArgumentNotNull(validValue,
|
||||
"Invalid data type of query value, " +
|
||||
"expect '%s', actual '%s'",
|
||||
pkey.dataType().clazz(),
|
||||
"Invalid data type of query value '%s', " +
|
||||
"expect '%s' for '%s', actual '%s'",
|
||||
value, pkey.dataType().clazz(), pkey.name(),
|
||||
value == null ? null : value.getClass());
|
||||
return validValue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,10 +57,10 @@ check_port "$REST_SERVER_URL"
|
|||
echo "Starting HugeGraphServer..."
|
||||
if [ -n "$VERBOSE" ]; then
|
||||
"$BIN"/hugegraph-server.sh "$TOP"/conf/gremlin-server.yaml \
|
||||
"$TOP"/conf/rest-server.properties "$OPEN_SECURITY_CHECK" $USER_OPTION $GC_OPTION &
|
||||
"$TOP"/conf/rest-server.properties "$OPEN_SECURITY_CHECK" "$USER_OPTION" "$GC_OPTION" &
|
||||
else
|
||||
"$BIN"/hugegraph-server.sh "$TOP"/conf/gremlin-server.yaml \
|
||||
"$TOP"/conf/rest-server.properties "$OPEN_SECURITY_CHECK" $USER_OPTION $GC_OPTION >/dev/null 2>&1 &
|
||||
"$TOP"/conf/rest-server.properties "$OPEN_SECURITY_CHECK" "$USER_OPTION" "$GC_OPTION" >/dev/null 2>&1 &
|
||||
fi
|
||||
|
||||
PID="$!"
|
||||
|
|
@ -69,7 +69,7 @@ echo "$PID" > $PID_FILE
|
|||
|
||||
trap 'kill $PID; exit' SIGHUP SIGINT SIGQUIT SIGTERM
|
||||
|
||||
wait_for_startup 'HugeGraphServer' "$REST_SERVER_URL/graphs" $SERVER_STARTUP_TIMEOUT_S || {
|
||||
wait_for_startup ${PID} 'HugeGraphServer' "$REST_SERVER_URL/graphs" ${SERVER_STARTUP_TIMEOUT_S} || {
|
||||
echo "See $TOP/logs/hugegraph-server.log for HugeGraphServer log output." >&2
|
||||
exit 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,9 +107,10 @@ function crontab_remove() {
|
|||
|
||||
# wait_for_startup friendly_name host port timeout_s
|
||||
function wait_for_startup() {
|
||||
local server_name="$1"
|
||||
local server_url="$2"
|
||||
local timeout_s="$3"
|
||||
local pid="$1"
|
||||
local server_name="$2"
|
||||
local server_url="$3"
|
||||
local timeout_s="$4"
|
||||
|
||||
local now_s=`date '+%s'`
|
||||
local stop_s=$(( $now_s + $timeout_s ))
|
||||
|
|
@ -119,8 +120,14 @@ function wait_for_startup() {
|
|||
echo -n "Connecting to $server_name ($server_url)"
|
||||
while [ $now_s -le $stop_s ]; do
|
||||
echo -n .
|
||||
process_status "$server_name" "$pid" >/dev/null
|
||||
if [ $? -eq 1 ]; then
|
||||
echo "Starting $server_name failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
status=`curl -o /dev/null -s -w %{http_code} $server_url`
|
||||
if [ $status -eq 200 ]; then
|
||||
if [[ $status -eq 200 || $status -eq 401 ]]; then
|
||||
echo "OK"
|
||||
return 0
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
hosts: [localhost]
|
||||
port: 8182
|
||||
serializer: {
|
||||
className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0,
|
||||
className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0,
|
||||
config: {
|
||||
serializeResultToString: false
|
||||
serializeResultToString: false,
|
||||
ioRegistries: [com.baidu.hugegraph.io.HugeGraphIoRegistry]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# host and port of gremlin server
|
||||
# host and port of gremlin server, need to be consistent with host and port in rest-server.properties
|
||||
#host: 127.0.0.1
|
||||
#port: 8182
|
||||
|
||||
|
|
@ -31,12 +31,6 @@ scriptEngines: {
|
|||
}
|
||||
}
|
||||
serializers:
|
||||
- { className: org.apache.tinkerpop.gremlin.driver.ser.GryoLiteMessageSerializerV1d0,
|
||||
config: {
|
||||
serializeResultToString: false,
|
||||
ioRegistries: [com.baidu.hugegraph.io.HugeGraphIoRegistry]
|
||||
}
|
||||
}
|
||||
- { className: org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1,
|
||||
config: {
|
||||
serializeResultToString: false,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ serializer=binary
|
|||
|
||||
store=hugegraph
|
||||
|
||||
search.text_analyzer=jieba
|
||||
search.text_analyzer_mode=INDEX
|
||||
|
||||
# rocksdb backend config
|
||||
#rocksdb.data_path=/path/to/disk
|
||||
#rocksdb.wal_path=/path/to/disk
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
hosts: [localhost]
|
||||
port: 8182
|
||||
serializer: { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { ioRegistries: [com.baidu.hugegraph.graphdb.tinkerpop.hugegraphIoRegistry] }}
|
||||
serializer: {
|
||||
className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0,
|
||||
config: {
|
||||
serializeResultToString: false,
|
||||
ioRegistries: [com.baidu.hugegraph.io.HugeGraphIoRegistry]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
hosts: [localhost]
|
||||
port: 8182
|
||||
serializer: {
|
||||
className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0,
|
||||
className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0,
|
||||
config: {
|
||||
serializeResultToString: true,
|
||||
serializeResultToString: false,
|
||||
ioRegistries: [com.baidu.hugegraph.io.HugeGraphIoRegistry]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# bind url
|
||||
restserver.url=http://127.0.0.1:8080
|
||||
# gremlin server url, need to be consistent with host and port in gremlin-server.yaml
|
||||
#gremlinserver.url=http://127.0.0.1:8182
|
||||
|
||||
# graphs list with pair NAME:CONF_PATH
|
||||
graphs=[hugegraph:conf/hugegraph.properties]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
com.baidu.hugegraph.plugin.HugeGraphGremlinPlugin
|
||||
org.apache.tinkerpop.gremlin.console.plugin.DriverGremlinPlugin
|
||||
org.apache.tinkerpop.gremlin.console.groovy.plugin.DriverGremlinPlugin
|
||||
org.apache.tinkerpop.gremlin.tinkergraph.groovy.plugin.TinkerGraphGremlinPlugin
|
||||
org.apache.tinkerpop.gremlin.hadoop.groovy.plugin.HadoopGremlinPlugin
|
||||
org.apache.tinkerpop.gremlin.giraph.groovy.plugin.GiraphGremlinPlugin
|
||||
org.apache.tinkerpop.gremlin.spark.groovy.plugin.SparkGremlinPlugin
|
||||
org.apache.tinkerpop.gremlin.console.groovy.plugin.UtilitiesGremlinPlugin
|
||||
org.apache.tinkerpop.gremlin.console.jsr223.DriverGremlinPlugin
|
||||
org.apache.tinkerpop.gremlin.console.jsr223.UtilitiesGremlinPlugin
|
||||
org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ set -ev
|
|||
|
||||
TRAVIS_DIR=`dirname $0`
|
||||
|
||||
if [ ! -d $HOME/downloads ]; then
|
||||
mkdir $HOME/downloads
|
||||
fi
|
||||
|
||||
if [[ "$BACKEND" == "cassandra" || "$BACKEND" == "scylladb" ]]; then
|
||||
$TRAVIS_DIR/install-cassandra.sh
|
||||
elif [ "$BACKEND" == "hbase" ]; then
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
set -ev
|
||||
|
||||
TRAVIS_DIR=`dirname $0`
|
||||
VERSION=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||
SERVER_DIR=hugegraph-$VERSION
|
||||
|
||||
mvn package -DskipTests
|
||||
$TRAVIS_DIR/start-server.sh
|
||||
mvn test -P api-test,$BACKEND
|
||||
$TRAVIS_DIR/start-server.sh $SERVER_DIR
|
||||
mvn test -P api-test,$BACKEND || (cat $SERVER_DIR/logs/hugegraph-server.log && exit 1)
|
||||
$TRAVIS_DIR/build-report.sh
|
||||
$TRAVIS_DIR/stop-server.sh
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ set -ev
|
|||
|
||||
HOME_DIR=`pwd`
|
||||
TRAVIS_DIR=`dirname $0`
|
||||
VERSION=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||
BASE_DIR=hugegraph-$VERSION
|
||||
BASE_DIR=$1
|
||||
BIN=$BASE_DIR/bin
|
||||
CONF=$BASE_DIR/conf/hugegraph.properties
|
||||
REST_CONF=$BASE_DIR/conf/rest-server.properties
|
||||
GREMLIN_CONF=$BASE_DIR/conf/gremlin-server.yaml
|
||||
|
||||
# PostgreSQL configurations
|
||||
POSTGRESQL_DRIVER=org.postgresql.Driver
|
||||
|
|
@ -32,6 +33,13 @@ if [ "$BACKEND" == "postgresql" ]; then
|
|||
sed -i "s/#jdbc.username=.*/jdbc.username=$POSTGRESQL_USERNAME/" $CONF
|
||||
fi
|
||||
|
||||
# Set timeout for hbase
|
||||
if [ "$BACKEND" == "hbase" ]; then
|
||||
sed -i '$arestserver.request_timeout=200' $REST_CONF
|
||||
sed -i '$agremlinserver.timeout=200' $REST_CONF
|
||||
sed -i 's/scriptEvaluationTimeout.*/scriptEvaluationTimeout: 200000/' $GREMLIN_CONF
|
||||
fi
|
||||
|
||||
# Append schema.sync_deletion=true to config file
|
||||
echo "schema.sync_deletion=true" >> $CONF
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
package com.baidu.hugegraph.backend.store.hbase;
|
||||
|
||||
import static com.baidu.hugegraph.config.OptionChecker.disallowEmpty;
|
||||
import static com.baidu.hugegraph.config.OptionChecker.positiveInt;
|
||||
import static com.baidu.hugegraph.config.OptionChecker.rangeInt;
|
||||
|
||||
import com.baidu.hugegraph.config.ConfigOption;
|
||||
|
|
@ -80,4 +81,12 @@ public class HbaseOptions extends OptionHolder {
|
|||
rangeInt(1, 1000),
|
||||
64
|
||||
);
|
||||
|
||||
public static final ConfigOption<Long> TRUNCATE_TIMEOUT =
|
||||
new ConfigOption<>(
|
||||
"hbase.truncate_timeout",
|
||||
"The timeout in seconds of waiting for store truncate.",
|
||||
positiveInt(),
|
||||
30L
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ public class HbaseSessions extends BackendSessionPool {
|
|||
|
||||
@Override
|
||||
protected synchronized boolean opened() {
|
||||
// NOTE: isClosed() seems to always return true even if not connected
|
||||
return this.hbase != null && !this.hbase.isClosed();
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +206,7 @@ public class HbaseSessions extends BackendSessionPool {
|
|||
} catch (TableNotEnabledException ignored) {
|
||||
// pass
|
||||
}
|
||||
return admin.truncateTableAsync(tableName, false);
|
||||
return admin.truncateTableAsync(tableName, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,11 +233,9 @@ public class HbaseSessions extends BackendSessionPool {
|
|||
*/
|
||||
public final class Session extends BackendSession {
|
||||
|
||||
private boolean closed;
|
||||
private final Map<String, List<Row>> batch;
|
||||
|
||||
public Session() {
|
||||
this.closed = false;
|
||||
this.batch = new HashMap<>();
|
||||
}
|
||||
|
||||
|
|
@ -272,15 +271,20 @@ public class HbaseSessions extends BackendSessionPool {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
this.opened = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
assert this.closeable();
|
||||
this.closed = true;
|
||||
this.opened = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closed() {
|
||||
return this.closed;
|
||||
return !this.opened || !HbaseSessions.this.opened();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ import java.util.HashMap;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.hadoop.hbase.NamespaceExistException;
|
||||
|
|
@ -128,8 +132,8 @@ public abstract class HbaseStore extends AbstractBackendStore<Session> {
|
|||
this.sessions = new HbaseSessions(config, this.namespace, this.store);
|
||||
}
|
||||
|
||||
// NOTE: seems to always return true even if not connected
|
||||
if (this.sessions.opened()) {
|
||||
assert this.sessions != null;
|
||||
if (!this.sessions.closed()) {
|
||||
LOG.debug("Store {} has been opened before", this.store);
|
||||
this.sessions.useSession();
|
||||
return;
|
||||
|
|
@ -162,7 +166,7 @@ public abstract class HbaseStore extends AbstractBackendStore<Session> {
|
|||
@Override
|
||||
public boolean opened() {
|
||||
this.checkConnectionOpened();
|
||||
return !this.sessions.session().closed();
|
||||
return this.sessions.session().opened();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -243,7 +247,7 @@ public abstract class HbaseStore extends AbstractBackendStore<Session> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
public void clear(boolean clearSpace) {
|
||||
this.checkConnectionOpened();
|
||||
|
||||
// Return if not exists namespace
|
||||
|
|
@ -257,29 +261,34 @@ public abstract class HbaseStore extends AbstractBackendStore<Session> {
|
|||
e, this.namespace);
|
||||
}
|
||||
|
||||
// Drop tables
|
||||
for (String table : this.tableNames()) {
|
||||
try {
|
||||
this.sessions.dropTable(table);
|
||||
} catch (TableNotFoundException e) {
|
||||
continue;
|
||||
} catch (IOException e) {
|
||||
throw new BackendException("Failed to drop table '%s' for '%s'",
|
||||
e, table, this.store);
|
||||
if (!clearSpace) {
|
||||
// Drop tables
|
||||
for (String table : this.tableNames()) {
|
||||
try {
|
||||
this.sessions.dropTable(table);
|
||||
} catch (TableNotFoundException e) {
|
||||
LOG.warn("The table '{}' for '{}' does not exist " +
|
||||
"when trying to drop", table, this.store);
|
||||
} catch (IOException e) {
|
||||
throw new BackendException(
|
||||
"Failed to drop table '%s' for '%s'",
|
||||
e, table, this.store);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Drop namespace
|
||||
try {
|
||||
this.sessions.dropNamespace();
|
||||
} catch (IOException e) {
|
||||
String notEmpty = "Only empty namespaces can be removed";
|
||||
if (e.getCause().getMessage().contains(notEmpty)) {
|
||||
LOG.debug("Can't drop namespace '{}': {}", this.namespace, e);
|
||||
} else {
|
||||
throw new BackendException(
|
||||
"Failed to drop namespace '%s' for '%s'",
|
||||
e, this.namespace, this.store);
|
||||
} else {
|
||||
// Drop namespace
|
||||
try {
|
||||
this.sessions.dropNamespace();
|
||||
} catch (IOException e) {
|
||||
String notEmpty = "Only empty namespaces can be removed";
|
||||
if (e.getCause().getMessage().contains(notEmpty)) {
|
||||
LOG.debug("Can't drop namespace '{}': {}",
|
||||
this.namespace, e);
|
||||
} else {
|
||||
throw new BackendException(
|
||||
"Failed to drop namespace '%s' for '%s'",
|
||||
e, this.namespace, this.store);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -310,14 +319,25 @@ public abstract class HbaseStore extends AbstractBackendStore<Session> {
|
|||
this.checkOpened();
|
||||
|
||||
// Truncate tables
|
||||
for (String table : this.tableNames()) {
|
||||
try {
|
||||
this.sessions.truncateTable(table);
|
||||
} catch (IOException e) {
|
||||
throw new BackendException(
|
||||
"Failed to truncate table '%s' for '%s'",
|
||||
e, table, this.store);
|
||||
List<String> tables = this.tableNames();
|
||||
Map<String, Future<Void>> futures = new HashMap<>(tables.size());
|
||||
String currentTable = null;
|
||||
try {
|
||||
for (String table : tables) {
|
||||
currentTable = table;
|
||||
futures.put(table, this.sessions.truncateTable(table));
|
||||
}
|
||||
long timeout = this.sessions.config()
|
||||
.get(HbaseOptions.TRUNCATE_TIMEOUT);
|
||||
for (Map.Entry<String, Future<Void>> entry : futures.entrySet()) {
|
||||
currentTable = entry.getKey();
|
||||
entry.getValue().get(timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
} catch (IOException | InterruptedException |
|
||||
ExecutionException | TimeoutException e) {
|
||||
throw new BackendException(
|
||||
"Failed to truncate table '%s' for '%s'",
|
||||
e, currentTable, this.store);
|
||||
}
|
||||
|
||||
LOG.debug("Store truncated: {}", this.store);
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ public class HbaseTable extends BackendTable<Session, BackendEntry> {
|
|||
|
||||
@Override
|
||||
public Iterator<BackendEntry> query(Session session, Query query) {
|
||||
if (query.limit() == 0 && query.limit() != Query.NO_LIMIT) {
|
||||
if (query.limit() == 0L && !query.nolimit()) {
|
||||
LOG.debug("Return empty result(limit=0) for query {}", query);
|
||||
return ImmutableList.<BackendEntry>of().iterator();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ public class MysqlSessions extends BackendSessionPool {
|
|||
if (e.getCause() instanceof SocketTimeoutException) {
|
||||
LOG.warn("Drop database '{}' timeout", this.database());
|
||||
} else {
|
||||
throw new BackendException("Failed to drop database '%s'",
|
||||
throw new BackendException("Failed to drop database '%s'", e,
|
||||
this.database());
|
||||
}
|
||||
}
|
||||
|
|
@ -156,6 +156,11 @@ public class MysqlSessions extends BackendSessionPool {
|
|||
}
|
||||
}
|
||||
|
||||
public void resetConnections() {
|
||||
// Close the under layer connections owned by each thread
|
||||
this.forceResetSessions();
|
||||
}
|
||||
|
||||
protected String buildCreateDatabase(String database) {
|
||||
return String.format("CREATE DATABASE IF NOT EXISTS %s " +
|
||||
"DEFAULT CHARSET utf8 COLLATE utf8_bin;",
|
||||
|
|
@ -178,15 +183,11 @@ public class MysqlSessions extends BackendSessionPool {
|
|||
* Connect DB without specified database
|
||||
*/
|
||||
protected Connection openWithoutDB(int timeout) {
|
||||
String jdbcUrl = this.config.get(MysqlOptions.JDBC_URL);
|
||||
String url = new URIBuilder().setPath(jdbcUrl)
|
||||
.setParameter("socketTimeout",
|
||||
String.valueOf(timeout))
|
||||
.toString();
|
||||
String url = this.buildUri(false, false, false, timeout);
|
||||
try {
|
||||
return this.connect(url);
|
||||
} catch (SQLException e) {
|
||||
throw new BackendException("Failed to access %s", jdbcUrl);
|
||||
throw new BackendException("Failed to access %s", e, url);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -194,20 +195,11 @@ public class MysqlSessions extends BackendSessionPool {
|
|||
* Connect DB with specified database, but won't auto reconnect
|
||||
*/
|
||||
protected Connection openWithDB(int timeout) {
|
||||
String jdbcUrl = this.config.get(MysqlOptions.JDBC_URL);
|
||||
if (jdbcUrl.endsWith("/")) {
|
||||
jdbcUrl = String.format("%s%s", jdbcUrl, this.database());
|
||||
} else {
|
||||
jdbcUrl = String.format("%s/%s", jdbcUrl, this.database());
|
||||
}
|
||||
String url = new URIBuilder().setPath(jdbcUrl)
|
||||
.setParameter("socketTimeout",
|
||||
String.valueOf(timeout))
|
||||
.toString();
|
||||
String url = this.buildUri(false, true, false, timeout);
|
||||
try {
|
||||
return this.connect(url);
|
||||
} catch (SQLException e) {
|
||||
throw new BackendException("Failed to access %s", jdbcUrl);
|
||||
throw new BackendException("Failed to access %s", e, url);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,27 +207,39 @@ public class MysqlSessions extends BackendSessionPool {
|
|||
* Connect DB with specified database
|
||||
*/
|
||||
private Connection open(boolean autoReconnect) throws SQLException {
|
||||
String url = this.buildUri(true, true, autoReconnect, null);
|
||||
return this.connect(url);
|
||||
}
|
||||
|
||||
protected String buildUri(boolean withConnParams, boolean withDB,
|
||||
boolean autoReconnect, Integer timeout) {
|
||||
String url = this.config.get(MysqlOptions.JDBC_URL);
|
||||
if (url.endsWith("/")) {
|
||||
url = String.format("%s%s", url, this.database());
|
||||
} else {
|
||||
url = String.format("%s/%s", url, this.database());
|
||||
if (withDB) {
|
||||
if (url.endsWith("/")) {
|
||||
url = String.format("%s%s", url, this.database());
|
||||
} else {
|
||||
url = String.format("%s/%s", url, this.database());
|
||||
}
|
||||
}
|
||||
|
||||
int maxTimes = this.config.get(MysqlOptions.JDBC_RECONNECT_MAX_TIMES);
|
||||
int interval = this.config.get(MysqlOptions.JDBC_RECONNECT_INTERVAL);
|
||||
String sslMode = this.config.get(MysqlOptions.JDBC_SSL_MODE);
|
||||
|
||||
URIBuilder uriBuilder = this.newConnectionURIBuilder();
|
||||
uriBuilder.setPath(url)
|
||||
.setParameter("useSSL", sslMode)
|
||||
.setParameter("characterEncoding", "utf-8")
|
||||
.setParameter("rewriteBatchedStatements", "true")
|
||||
.setParameter("useServerPrepStmts", "false")
|
||||
.setParameter("autoReconnect", String.valueOf(autoReconnect))
|
||||
.setParameter("maxReconnects", String.valueOf(maxTimes))
|
||||
.setParameter("initialTimeout", String.valueOf(interval));
|
||||
return this.connect(uriBuilder.toString());
|
||||
URIBuilder builder = this.newConnectionURIBuilder();
|
||||
builder.setPath(url).setParameter("useSSL", sslMode);
|
||||
if (withConnParams) {
|
||||
builder.setParameter("characterEncoding", "utf-8")
|
||||
.setParameter("rewriteBatchedStatements", "true")
|
||||
.setParameter("useServerPrepStmts", "false")
|
||||
.setParameter("autoReconnect", String.valueOf(autoReconnect))
|
||||
.setParameter("maxReconnects", String.valueOf(maxTimes))
|
||||
.setParameter("initialTimeout", String.valueOf(interval));
|
||||
}
|
||||
if (timeout != null) {
|
||||
builder.setParameter("socketTimeout", String.valueOf(timeout));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected URIBuilder newConnectionURIBuilder() {
|
||||
|
|
@ -260,26 +264,36 @@ public class MysqlSessions extends BackendSessionPool {
|
|||
|
||||
private Connection conn;
|
||||
private Map<String, PreparedStatement> statements;
|
||||
private boolean opened;
|
||||
private int count;
|
||||
|
||||
public Session() {
|
||||
this.conn = null;
|
||||
this.statements = new HashMap<>();
|
||||
this.opened = false;
|
||||
this.count = 0;
|
||||
try {
|
||||
this.open();
|
||||
} catch (SQLException ignored) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
public HugeConfig config() {
|
||||
return MysqlSessions.this.config();
|
||||
}
|
||||
|
||||
public void open() throws SQLException {
|
||||
@Override
|
||||
public void open() {
|
||||
try {
|
||||
this.doOpen();
|
||||
} catch (SQLException e) {
|
||||
throw new BackendException("Failed to open connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryOpen() {
|
||||
try {
|
||||
this.doOpen();
|
||||
} catch (SQLException ignored) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
private void doOpen() throws SQLException {
|
||||
if (this.conn != null && !this.conn.isClosed()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -294,6 +308,11 @@ public class MysqlSessions extends BackendSessionPool {
|
|||
return;
|
||||
}
|
||||
|
||||
this.opened = false;
|
||||
this.doClose();
|
||||
}
|
||||
|
||||
private void doClose() {
|
||||
SQLException exception = null;
|
||||
for (PreparedStatement statement : this.statements.values()) {
|
||||
try {
|
||||
|
|
@ -302,23 +321,42 @@ public class MysqlSessions extends BackendSessionPool {
|
|||
exception = e;
|
||||
}
|
||||
}
|
||||
this.statements.clear();
|
||||
|
||||
try {
|
||||
this.conn.close();
|
||||
} catch (SQLException e) {
|
||||
exception = e;
|
||||
} finally {
|
||||
this.conn = null;
|
||||
}
|
||||
|
||||
this.opened = false;
|
||||
if (exception != null) {
|
||||
throw new BackendException("Failed to close connection",
|
||||
exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean opened() {
|
||||
if (this.opened && this.conn == null) {
|
||||
// Reconnect if the connection is reset
|
||||
tryOpen();
|
||||
}
|
||||
return this.opened && this.conn != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closed() {
|
||||
return !this.opened;
|
||||
if (!this.opened || this.conn == null) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
return this.conn.isClosed();
|
||||
} catch (SQLException ignored) {
|
||||
// Assume closed here
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
|
|
@ -398,6 +436,19 @@ public class MysqlSessions extends BackendSessionPool {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset() {
|
||||
// NOTE: this method may be called by other threads
|
||||
if (this.conn == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.doClose();
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Failed to reset connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
public ResultSet select(String sql) throws SQLException {
|
||||
assert this.conn.getAutoCommit();
|
||||
return this.conn.createStatement().executeQuery(sql);
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ public abstract class MysqlStore extends AbstractBackendStore<Session> {
|
|||
@Override
|
||||
public boolean opened() {
|
||||
this.checkClusterConnected();
|
||||
return !this.sessions.session().closed();
|
||||
return this.sessions.session().opened();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -157,7 +157,7 @@ public abstract class MysqlStore extends AbstractBackendStore<Session> {
|
|||
try {
|
||||
// Open a new session connected with specified database
|
||||
this.sessions.session().open();
|
||||
} catch (SQLException e) {
|
||||
} catch (Exception e) {
|
||||
throw new BackendException("Failed to connect database '%s'",
|
||||
this.database);
|
||||
}
|
||||
|
|
@ -168,14 +168,28 @@ public abstract class MysqlStore extends AbstractBackendStore<Session> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
public void clear(boolean clearSpace) {
|
||||
// Check connected
|
||||
this.checkClusterConnected();
|
||||
|
||||
if (this.sessions.existsDatabase()) {
|
||||
this.checkOpened();
|
||||
this.clearTables();
|
||||
this.sessions.dropDatabase();
|
||||
if (!clearSpace) {
|
||||
this.checkOpened();
|
||||
this.clearTables();
|
||||
/*
|
||||
* Disconnect connections for following database drop.
|
||||
* Connections will be auto reconnected if not drop database
|
||||
* in next step, but never do this operation because database
|
||||
* might be blocked in mysql or throw 'terminating' exception.
|
||||
* we can't resetConnections() when dropDatabase(), because
|
||||
* there are 3 stores(schema,system,graph), which are shared
|
||||
* one database, other stores may keep connected with the
|
||||
* database when one store doing clear(clearSpace=false).
|
||||
*/
|
||||
this.sessions.resetConnections();
|
||||
} else {
|
||||
this.sessions.dropDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
LOG.debug("Store cleared: {}", this.store);
|
||||
|
|
|
|||
|
|
@ -19,14 +19,10 @@
|
|||
|
||||
package com.baidu.hugegraph.backend.store.mysql;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.baidu.hugegraph.backend.BackendException;
|
||||
import com.baidu.hugegraph.backend.store.AbstractBackendStoreProvider;
|
||||
import com.baidu.hugegraph.backend.store.BackendStore;
|
||||
import com.baidu.hugegraph.backend.store.mysql.MysqlStore.MysqlGraphStore;
|
||||
import com.baidu.hugegraph.backend.store.mysql.MysqlStore.MysqlSchemaStore;
|
||||
import com.baidu.hugegraph.util.Events;
|
||||
|
||||
public class MysqlStoreProvider extends AbstractBackendStoreProvider {
|
||||
|
||||
|
|
@ -44,20 +40,6 @@ public class MysqlStoreProvider extends AbstractBackendStoreProvider {
|
|||
return new MysqlGraphStore(this, this.database(), store);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() throws BackendException {
|
||||
this.checkOpened();
|
||||
/*
|
||||
* We should drop database once only with stores(schema/graph/system),
|
||||
* otherwise it will easily lead to blocking when drop tables
|
||||
*/
|
||||
Iterator<BackendStore> iter = this.stores.values().iterator();
|
||||
if (iter.hasNext()) {
|
||||
iter.next().clear();
|
||||
}
|
||||
this.notifyAndWaitEvent(Events.STORE_CLEAR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "mysql";
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ public abstract class MysqlTable
|
|||
public Iterator<BackendEntry> query(Session session, Query query) {
|
||||
ExtendableIterator<BackendEntry> rs = new ExtendableIterator<>();
|
||||
|
||||
if (query.limit() == 0 && query.limit() != Query.NO_LIMIT) {
|
||||
if (query.limit() == 0L && !query.nolimit()) {
|
||||
LOG.debug("Return empty result(limit=0) for query {}", query);
|
||||
return rs;
|
||||
}
|
||||
|
|
@ -344,7 +344,7 @@ public abstract class MysqlTable
|
|||
}
|
||||
if (query.paging()) {
|
||||
this.wrapPage(selection, query);
|
||||
} else if (query.limit() != Query.NO_LIMIT || query.offset() > 0) {
|
||||
} else if (!query.nolimit() || query.offset() > 0) {
|
||||
this.wrapOffset(selection, query);
|
||||
}
|
||||
}
|
||||
|
|
@ -512,7 +512,7 @@ public abstract class MysqlTable
|
|||
|
||||
select.append(this.orderByKeys());
|
||||
|
||||
if (query.limit() != Query.NO_LIMIT) {
|
||||
if (!query.nolimit()) {
|
||||
// Fetch `limit + 1` rows for judging whether reached the last page
|
||||
select.append(" limit ");
|
||||
select.append(query.limit() + 1);
|
||||
|
|
|
|||
|
|
@ -536,14 +536,10 @@ public class RocksDBStdSessions extends RocksDBSessions {
|
|||
*/
|
||||
private final class StdSession extends RocksDBSessions.Session {
|
||||
|
||||
private boolean closed;
|
||||
|
||||
private WriteBatch batch;
|
||||
private WriteOptions writeOptions;
|
||||
|
||||
public StdSession(HugeConfig conf) {
|
||||
this.closed = false;
|
||||
|
||||
boolean bulkload = conf.get(RocksDBOptions.BULKLOAD_MODE);
|
||||
this.batch = new WriteBatch();
|
||||
this.writeOptions = new WriteOptions();
|
||||
|
|
@ -551,15 +547,20 @@ public class RocksDBStdSessions extends RocksDBSessions {
|
|||
//this.writeOptions.setSync(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
this.opened = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
assert this.closeable();
|
||||
this.closed = true;
|
||||
this.opened = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closed() {
|
||||
return this.closed;
|
||||
return !this.opened || !RocksDBStdSessions.this.opened();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@ public abstract class RocksDBStore extends AbstractBackendStore<Session> {
|
|||
@Override
|
||||
public boolean opened() {
|
||||
this.checkDbOpened();
|
||||
return !this.sessions.session().closed();
|
||||
return this.sessions.session().opened();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -412,14 +412,15 @@ public abstract class RocksDBStore extends AbstractBackendStore<Session> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public synchronized void clear() {
|
||||
public synchronized void clear(boolean clearSpace) {
|
||||
this.checkDbOpened();
|
||||
|
||||
// Drop tables with main disk
|
||||
for (String table : this.tableNames()) {
|
||||
this.dropTable(this.sessions, table);
|
||||
}
|
||||
|
||||
// Drop table with optimized disk
|
||||
// Drop tables with optimized disk
|
||||
Map<String, RocksDBSessions> tableDBMap = this.tableDBMapping();
|
||||
for (Map.Entry<String, RocksDBSessions> e : tableDBMap.entrySet()) {
|
||||
this.dropTable(e.getValue(), e.getKey());
|
||||
|
|
@ -461,7 +462,7 @@ public abstract class RocksDBStore extends AbstractBackendStore<Session> {
|
|||
public synchronized void truncate() {
|
||||
this.checkOpened();
|
||||
|
||||
this.clear();
|
||||
this.clear(false);
|
||||
this.init();
|
||||
|
||||
LOG.debug("Store truncated: {}", this.store);
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ public class RocksDBTable extends BackendTable<Session, BackendEntry> {
|
|||
|
||||
@Override
|
||||
public Iterator<BackendEntry> query(Session session, Query query) {
|
||||
if (query.limit() == 0 && query.limit() != Query.NO_LIMIT) {
|
||||
if (query.limit() == 0L && !query.nolimit()) {
|
||||
LOG.debug("Return empty result(limit=0) for query {}", query);
|
||||
return ImmutableList.<BackendEntry>of().iterator();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,23 +172,21 @@ public class RocksDBSstSessions extends RocksDBSessions {
|
|||
*/
|
||||
private final class SstSession extends Session {
|
||||
|
||||
private boolean closed;
|
||||
private Map<String, Changes> batch;
|
||||
|
||||
public SstSession() {
|
||||
this.closed = false;
|
||||
this.batch = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
this.opened = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
assert this.closeable();
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closed() {
|
||||
return this.closed;
|
||||
this.opened = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public class GremlinApiTest extends BaseApiTest {
|
|||
+ "\"bindings\":{},"
|
||||
+ "\"language\":\"gremlin-groovy\","
|
||||
+ "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
|
||||
Assert.assertEquals(200, client().post(path, body).getStatus());
|
||||
assertResponseStatus(200, client().post(path, body));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -49,4 +49,63 @@ public class GremlinApiTest extends BaseApiTest {
|
|||
Response r = client().get(path, params);
|
||||
Assert.assertEquals(r.readEntity(String.class), 200, r.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScript() {
|
||||
String bodyTemplate = "{"
|
||||
+ "\"gremlin\":\"%s\","
|
||||
+ "\"bindings\":{},"
|
||||
+ "\"language\":\"gremlin-groovy\","
|
||||
+ "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
|
||||
|
||||
String script = "schema=hugegraph.schema();"
|
||||
+ "schema.propertyKey('name').asText().ifNotExist().create();"
|
||||
+ "schema.propertyKey('age').asInt().ifNotExist().create();"
|
||||
+ "schema.propertyKey('city').asUUID().ifNotExist().create();"
|
||||
+ "schema.propertyKey('lang').asText().ifNotExist().create();"
|
||||
+ "schema.propertyKey('date').asText().ifNotExist().create();"
|
||||
+ "schema.propertyKey('price').asInt().ifNotExist().create();"
|
||||
+ "person=schema.vertexLabel('person').properties('name','age','city').useCustomizeUuidId().ifNotExist().create();"
|
||||
+ "knows=schema.edgeLabel('knows').sourceLabel('person').targetLabel('person').properties('date').ifNotExist().create();"
|
||||
+ "marko=hugegraph.addVertex(T.id, '835e1153928149578691cf79258e90eb', T.label,'person','name','marko','age',29,'city','135e1153928149578691cf79258e90eb');"
|
||||
+ "vadas=hugegraph.addVertex(T.id, '935e1153928149578691cf79258e90eb', T.label,'person','name','vadas','age',27,'city','235e1153928149578691cf79258e90eb');"
|
||||
+ "marko.addEdge('knows',vadas,'date','20160110');";
|
||||
String body = String.format(bodyTemplate, script);
|
||||
assertResponseStatus(200, client().post(path, body));
|
||||
|
||||
String queryV = "g.V()";
|
||||
body = String.format(bodyTemplate, queryV);
|
||||
assertResponseStatus(200, client().post(path, body));
|
||||
|
||||
String queryE = "g.E()";
|
||||
body = String.format(bodyTemplate, queryE);
|
||||
assertResponseStatus(200, client().post(path, body));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearAndInit() {
|
||||
String body = "{"
|
||||
+ "\"gremlin\":\"hugegraph.clearBackend()\","
|
||||
+ "\"bindings\":{},"
|
||||
+ "\"language\":\"gremlin-groovy\","
|
||||
+ "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
|
||||
assertResponseStatus(200, client().post(path, body));
|
||||
|
||||
body = "{"
|
||||
+ "\"gremlin\":\"hugegraph.initBackend()\","
|
||||
+ "\"bindings\":{},"
|
||||
+ "\"language\":\"gremlin-groovy\","
|
||||
+ "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
|
||||
assertResponseStatus(200, client().post(path, body));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTruncate() {
|
||||
String body = "{"
|
||||
+ "\"gremlin\":\"hugegraph.truncateBackend()\","
|
||||
+ "\"bindings\":{},"
|
||||
+ "\"language\":\"gremlin-groovy\","
|
||||
+ "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
|
||||
assertResponseStatus(200, client().post(path, body));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5768,14 +5768,14 @@ public class VertexCoreTest extends BaseCoreTest {
|
|||
.has("city", "Beijing").toList();
|
||||
Assert.assertEquals(3, vertices.size());
|
||||
|
||||
List<Vertex> vertices1 = graph().traversal().V()
|
||||
.has("city", "Beijing")
|
||||
.range(0, 2).toList();
|
||||
Set<Vertex> vertices1 = graph().traversal().V()
|
||||
.has("city", "Beijing")
|
||||
.range(0, 2).toSet();
|
||||
Assert.assertEquals(2, vertices1.size());
|
||||
|
||||
List<Vertex> vertices2 = graph().traversal().V()
|
||||
.has("city", "Beijing")
|
||||
.range(2, 3).toList();
|
||||
Set<Vertex> vertices2 = graph().traversal().V()
|
||||
.has("city", "Beijing")
|
||||
.range(2, 3).toSet();
|
||||
Assert.assertEquals(1, vertices2.size());
|
||||
|
||||
vertices1.addAll(vertices2);
|
||||
|
|
@ -5791,13 +5791,13 @@ public class VertexCoreTest extends BaseCoreTest {
|
|||
List<Vertex> vertices = graph().traversal().V()
|
||||
.has("age", P.between(5, 22)).toList();
|
||||
Assert.assertEquals(4, vertices.size());
|
||||
List<Vertex> vertices1 = graph().traversal().V()
|
||||
.has("age", P.between(5, 22))
|
||||
.range(0, 3).toList();
|
||||
Set<Vertex> vertices1 = graph().traversal().V()
|
||||
.has("age", P.between(5, 22))
|
||||
.range(0, 3).toSet();
|
||||
Assert.assertEquals(3, vertices1.size());
|
||||
List<Vertex> vertices2 = graph().traversal().V()
|
||||
.has("age", P.between(5, 22))
|
||||
.range(3, 4).toList();
|
||||
Set<Vertex> vertices2 = graph().traversal().V()
|
||||
.has("age", P.between(5, 22))
|
||||
.range(3, 4).toSet();
|
||||
Assert.assertEquals(1, vertices2.size());
|
||||
|
||||
vertices1.addAll(vertices2);
|
||||
|
|
@ -5813,19 +5813,19 @@ public class VertexCoreTest extends BaseCoreTest {
|
|||
.toList();
|
||||
Assert.assertEquals(5, vertices.size());
|
||||
|
||||
List<Vertex> vertices1 = graph().traversal().V()
|
||||
.hasLabel("person")
|
||||
.range(0, 3).toList();
|
||||
Set<Vertex> vertices1 = graph().traversal().V()
|
||||
.hasLabel("person")
|
||||
.range(0, 3).toSet();
|
||||
Assert.assertEquals(3, vertices1.size());
|
||||
|
||||
List<Vertex> vertices2 = graph().traversal().V()
|
||||
.hasLabel("person")
|
||||
.range(3, 5).toList();
|
||||
Set<Vertex> vertices2 = graph().traversal().V()
|
||||
.hasLabel("person")
|
||||
.range(3, 5).toSet();
|
||||
Assert.assertEquals(2, vertices2.size());
|
||||
|
||||
List<Vertex> vertices3 = graph().traversal().V()
|
||||
.hasLabel("person")
|
||||
.limit(4).toList();
|
||||
Set<Vertex> vertices3 = graph().traversal().V()
|
||||
.hasLabel("person")
|
||||
.limit(4).toSet();
|
||||
Assert.assertEquals(4, vertices3.size());
|
||||
|
||||
vertices1.addAll(vertices2);
|
||||
|
|
|
|||
|
|
@ -119,6 +119,14 @@ public class SecurityManagerTest {
|
|||
String result = runGremlinJob("new FileInputStream(new File(\"\"))");
|
||||
assertError(result, "Not allowed to read file via Gremlin");
|
||||
|
||||
// read file
|
||||
String pom = System.getProperty("user.dir") + "/a.groovy";
|
||||
try (FileInputStream fis = new FileInputStream(new File(pom))) {
|
||||
} catch (IOException ignored) {}
|
||||
result = runGremlinJob(String.format(
|
||||
"new FileInputStream(new File(\"%s\"))", pom));
|
||||
assertError(result, "(No such file or directory)");
|
||||
|
||||
// read file fd
|
||||
@SuppressWarnings({ "unused", "resource" })
|
||||
FileInputStream fis = new FileInputStream(FileDescriptor.in);
|
||||
|
|
@ -278,7 +286,8 @@ public class SecurityManagerTest {
|
|||
}
|
||||
|
||||
private static void assertError(String result, String message) {
|
||||
Assert.assertTrue(result, result.endsWith(message));
|
||||
Assert.assertTrue(result, result.endsWith(message) ||
|
||||
result.contains(message));
|
||||
}
|
||||
|
||||
private static String runGremlinJob(String gremlin) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue