未验证 提交 86062c80 编写于 作者: J Joan Augsburger 提交者: GitHub

chore(sql): column names are no longer case-sensitive

上级 f1fc3935
......@@ -25,15 +25,16 @@
package io.questdb.cairo;
import io.questdb.cairo.sql.RecordMetadata;
import io.questdb.std.CharSequenceIntHashMap;
import io.questdb.std.LowerCaseCharSequenceIntHashMap;
import io.questdb.std.ObjList;
public class GenericRecordMetadata extends BaseRecordMetadata {
public static final GenericRecordMetadata EMPTY = new GenericRecordMetadata();
private final LowerCaseCharSequenceIntHashMap columnNameIndexMap;
public GenericRecordMetadata() {
this.columnMetadata = new ObjList<>();
this.columnNameIndexMap = new CharSequenceIntHashMap();
this.columnNameIndexMap = new LowerCaseCharSequenceIntHashMap();
this.timestampIndex = -1;
}
......@@ -49,6 +50,15 @@ public class GenericRecordMetadata extends BaseRecordMetadata {
}
}
@Override
public int getColumnIndexQuiet(CharSequence columnName, int lo, int hi) {
final int index = columnNameIndexMap.keyIndex(columnName, lo, hi);
if (index < 0) {
return columnNameIndexMap.valueAt(index);
}
return -1;
}
public static GenericRecordMetadata copyOf(RecordMetadata that) {
GenericRecordMetadata metadata = copyOfSansTimestamp(that);
metadata.setTimestampIndex(that.getTimestampIndex());
......
......@@ -1050,7 +1050,7 @@ public class SqlCodeGenerator implements Mutable {
return recordCursorFactory;
}
try {
final CharSequenceIntHashMap orderBy = model.getOrderHash();
final LowerCaseCharSequenceIntHashMap orderBy = model.getOrderHash();
final ObjList<CharSequence> columnNames = orderBy.keys();
final int size = columnNames.size();
......
......@@ -668,7 +668,7 @@ class SqlOptimiser {
// order hash is used to determine redundant order when parsing analytic function definition
private void createOrderHash(QueryModel model) {
CharSequenceIntHashMap hash = model.getOrderHash();
LowerCaseCharSequenceIntHashMap hash = model.getOrderHash();
hash.clear();
final ObjList<ExpressionNode> orderBy = model.getOrderBy();
......@@ -687,7 +687,7 @@ class SqlOptimiser {
if (nestedModel != null) {
createOrderHash(nestedModel);
if (m > 0) {
CharSequenceIntHashMap thatHash = nestedModel.getOrderHash();
LowerCaseCharSequenceIntHashMap thatHash = nestedModel.getOrderHash();
if (thatHash.size() > 0) {
for (int i = 0; i < m; i++) {
QueryColumn column = columns.getQuick(i);
......@@ -729,7 +729,7 @@ class SqlOptimiser {
// taking into account that column is pre-aliased, e.g.
// "col, col" will look like "col, col col1"
CharSequenceObjHashMap<CharSequence> translatingAliasMap = translatingModel.getColumnNameToAliasMap();
LowerCaseCharSequenceObjHashMap<CharSequence> translatingAliasMap = translatingModel.getColumnNameToAliasMap();
int index = translatingAliasMap.keyIndex(columnAst.token);
if (index < 0) {
// column is already being referenced by translating model
......@@ -914,7 +914,7 @@ class SqlOptimiser {
}
private ExpressionNode doReplaceLiteral(@Transient ExpressionNode node, QueryModel translatingModel, QueryModel innerModel, QueryModel validatingModel) throws SqlException {
final CharSequenceObjHashMap<CharSequence> map = translatingModel.getColumnNameToAliasMap();
final LowerCaseCharSequenceObjHashMap<CharSequence> map = translatingModel.getColumnNameToAliasMap();
int index = map.keyIndex(node.token);
if (index > -1) {
// there is a possibility that column references join table, but in a different way
......@@ -1210,7 +1210,7 @@ class SqlOptimiser {
return orderByAdvice;
}
CharSequenceObjHashMap<QueryColumn> map = model.getAliasToColumnMap();
LowerCaseCharSequenceObjHashMap<QueryColumn> map = model.getAliasToColumnMap();
for (int i = 0; i < len; i++) {
QueryColumn queryColumn = map.get(orderBy.getQuick(i).token);
if (queryColumn.getAst().type == ExpressionNode.LITERAL) {
......@@ -2277,7 +2277,7 @@ class SqlOptimiser {
if (ascendColumns && base != model) {
// check if column is aliased as either
// "x y" or "tab.x y" or "t.x y", where "t" is alias of table "tab"
final CharSequenceObjHashMap<CharSequence> map = baseParent.getColumnNameToAliasMap();
final LowerCaseCharSequenceObjHashMap<CharSequence> map = baseParent.getColumnNameToAliasMap();
CharSequence alias = null;
int index = map.keyIndex(column);
if (index > -1 && dot > -1) {
......@@ -2496,7 +2496,7 @@ class SqlOptimiser {
if (flatModel) {
if (flatParent && m.getSampleBy() != null) {
throw SqlException.$(m.getSampleBy().position, "'sample by' must be used with 'select' clause, which contains aggerate expression(s)");
throw SqlException.$(m.getSampleBy().position, "'sample by' must be used with 'select' clause, which contains aggregate expression(s)");
}
} else {
model.replaceJoinModel(i, rewriteSelectClause0(m));
......@@ -2828,7 +2828,7 @@ class SqlOptimiser {
}
private static class LiteralCheckingVisitor implements PostOrderTreeTraversalAlgo.Visitor {
private CharSequenceObjHashMap<QueryColumn> nameTypeMap;
private LowerCaseCharSequenceObjHashMap<QueryColumn> nameTypeMap;
@Override
public void visit(ExpressionNode node) {
......@@ -2846,14 +2846,14 @@ class SqlOptimiser {
}
}
PostOrderTreeTraversalAlgo.Visitor of(CharSequenceObjHashMap<QueryColumn> nameTypeMap) {
PostOrderTreeTraversalAlgo.Visitor of(LowerCaseCharSequenceObjHashMap<QueryColumn> nameTypeMap) {
this.nameTypeMap = nameTypeMap;
return this;
}
}
private static class LiteralRewritingVisitor implements PostOrderTreeTraversalAlgo.Visitor {
private CharSequenceObjHashMap<CharSequence> aliasToColumnMap;
private LowerCaseCharSequenceObjHashMap<CharSequence> aliasToColumnMap;
@Override
public void visit(ExpressionNode node) {
......@@ -2873,7 +2873,7 @@ class SqlOptimiser {
}
}
PostOrderTreeTraversalAlgo.Visitor of(CharSequenceObjHashMap<CharSequence> aliasToColumnMap) {
PostOrderTreeTraversalAlgo.Visitor of(LowerCaseCharSequenceObjHashMap<CharSequence> aliasToColumnMap) {
this.aliasToColumnMap = aliasToColumnMap;
return this;
}
......
......@@ -328,7 +328,7 @@ public final class SqlParser {
return parseSelect(lexer);
}
QueryModel parseAsSubQuery(GenericLexer lexer, @Nullable CharSequenceObjHashMap<WithClauseModel> withClauses) throws SqlException {
QueryModel parseAsSubQuery(GenericLexer lexer, @Nullable LowerCaseCharSequenceObjHashMap<WithClauseModel> withClauses) throws SqlException {
QueryModel model;
this.subQueryMode = true;
try {
......@@ -339,7 +339,7 @@ public final class SqlParser {
return model;
}
private QueryModel parseAsSubQueryAndExpectClosingBrace(GenericLexer lexer, CharSequenceObjHashMap<WithClauseModel> withClauses) throws SqlException {
private QueryModel parseAsSubQueryAndExpectClosingBrace(GenericLexer lexer, LowerCaseCharSequenceObjHashMap<WithClauseModel> withClauses) throws SqlException {
final QueryModel model = parseAsSubQuery(lexer, withClauses);
expectTok(lexer, ')');
return model;
......@@ -655,7 +655,7 @@ public final class SqlParser {
return null;
}
private QueryModel parseDml(GenericLexer lexer, @Nullable CharSequenceObjHashMap<WithClauseModel> withClauses) throws SqlException {
private QueryModel parseDml(GenericLexer lexer, @Nullable LowerCaseCharSequenceObjHashMap<WithClauseModel> withClauses) throws SqlException {
QueryModel model = null;
QueryModel prevModel = null;
while (true) {
......@@ -823,7 +823,7 @@ public final class SqlParser {
}
@NotNull
private QueryModel parseDml0(GenericLexer lexer, @Nullable CharSequenceObjHashMap<WithClauseModel> parentWithClauses) throws SqlException {
private QueryModel parseDml0(GenericLexer lexer, @Nullable LowerCaseCharSequenceObjHashMap<WithClauseModel> parentWithClauses) throws SqlException {
CharSequence tok;
final int modelPosition = lexer.getPosition();
......@@ -1274,7 +1274,7 @@ public final class SqlParser {
return null;
}
private QueryModel parseWith(GenericLexer lexer, WithClauseModel wcm, CharSequenceObjHashMap<WithClauseModel> withClauses) throws SqlException {
private QueryModel parseWith(GenericLexer lexer, WithClauseModel wcm, LowerCaseCharSequenceObjHashMap<WithClauseModel> withClauses) throws SqlException {
QueryModel m = wcm.popModel();
if (m != null) {
return m;
......
......@@ -81,7 +81,7 @@ public class SqlUtil {
CharacterStore store,
CharSequence base,
int indexOfDot,
CharSequenceObjHashMap<QueryColumn> aliasToColumnMap
LowerCaseCharSequenceObjHashMap<QueryColumn> aliasToColumnMap
) {
final boolean disallowed = disallowedAliases.contains(base);
......
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.griffin.engine.functions.catalogue;
import io.questdb.cairo.*;
import io.questdb.cairo.sql.*;
import io.questdb.griffin.FunctionFactory;
import io.questdb.griffin.SqlExecutionContext;
import io.questdb.griffin.engine.functions.CursorFunction;
import io.questdb.std.*;
import io.questdb.std.str.NativeLPSZ;
import io.questdb.std.str.Path;
public class AttributeCatalogueFunctionFactory implements FunctionFactory {
private static final RecordMetadata METADATA;
@Override
public String getSignature() {
return "pg_catalog.pg_attribute()";
}
@Override
public Function newInstance(ObjList<Function> args, int position, CairoConfiguration configuration) {
return new CursorFunction(
position,
new AttributeCatalogueCursorFactory(
configuration,
METADATA
)
);
}
private static class AttributeCatalogueCursorFactory extends AbstractRecordCursorFactory {
private final Path path = new Path();
private final ReadOnlyColumn metaMem = new OnePageMemory();
private final AttributeClassCatalogueCursor cursor;
public AttributeCatalogueCursorFactory(CairoConfiguration configuration, RecordMetadata metadata) {
super(metadata);
this.cursor = new AttributeClassCatalogueCursor(configuration, path, metaMem);
}
@Override
public void close() {
Misc.free(path);
Misc.free(metaMem);
}
@Override
public RecordCursor getCursor(SqlExecutionContext executionContext) {
cursor.toTop();
return cursor;
}
@Override
public boolean recordCursorSupportsRandomAccess() {
return false;
}
}
private static class AttributeClassCatalogueCursor implements NoRandomAccessRecordCursor {
private final Path path;
private final FilesFacade ff;
private final DiskReadingRecord diskReadingRecord = new DiskReadingRecord();
private final NativeLPSZ nativeLPSZ = new NativeLPSZ();
private final int plimit;
private final ReadOnlyColumn metaMem;
private long findFileStruct = 0;
private int columnIndex = 0;
private int tableId = 1000;
private boolean readNextFileFromDisk = true;
private int columnCount;
private boolean hasNextFile = true;
private boolean foundMetadataFile = false;
public AttributeClassCatalogueCursor(CairoConfiguration configuration, Path path, ReadOnlyColumn metaMem) {
this.ff = configuration.getFilesFacade();
this.path = path;
this.path.of(configuration.getRoot()).$();
this.plimit = this.path.length();
this.metaMem = metaMem;
}
@Override
public void close() {
if (findFileStruct != 0) {
ff.findClose(findFileStruct);
findFileStruct = 0;
}
metaMem.close();
}
@Override
public Record getRecord() {
return diskReadingRecord;
}
@Override
public boolean hasNext() {
if (findFileStruct == 0) {
findFileStruct = ff.findFirst(path.trimTo(plimit).$());
if (findFileStruct > 0) {
return next0();
}
findFileStruct = 0;
return false;
}
return next0();
}
@Override
public void toTop() {
if (findFileStruct != 0) {
ff.findClose(findFileStruct);
findFileStruct = 0;
}
}
@Override
public long size() {
return -1;
}
private boolean next0() {
do {
if (readNextFileFromDisk) {
foundMetadataFile = false;
final long pname = ff.findName(findFileStruct);
if (hasNextFile) {
nativeLPSZ.of(pname);
if (
ff.findType(findFileStruct) == Files.DT_DIR && Chars.notDots(nativeLPSZ)
) {
path.trimTo(plimit);
path.concat(pname);
if (ff.exists(path.concat(TableUtils.META_FILE_NAME).$())) {
foundMetadataFile = true;
metaMem.of(ff, path, ff.getPageSize(), ff.length(path));
columnCount = metaMem.getInt(TableUtils.META_OFFSET_COUNT);
tableId = metaMem.getInt(TableUtils.META_OFFSET_TABLE_ID);
}
}
hasNextFile = ff.findNext(findFileStruct) > 0;
}
}
if (foundMetadataFile) {
long offset = TableUtils.getColumnNameOffset(columnCount);
for (int i = 0; i < columnCount; i++) {
CharSequence name = metaMem.getStr(offset);
if (columnIndex == i) {
diskReadingRecord.name = name;
diskReadingRecord.columnNumber = (short) (i + 1);
diskReadingRecord.tableId = tableId;
columnIndex++;
if (columnIndex == columnCount) {
readNextFileFromDisk = true;
columnIndex = 0;
} else {
readNextFileFromDisk = false;
}
return true;
}
offset += ReadOnlyMemory.getStorageLength(name);
}
}
} while (hasNextFile);
ff.findClose(findFileStruct);
findFileStruct = 0;
hasNextFile = true;
foundMetadataFile = false;
return false;
}
private static class DiskReadingRecord implements Record {
public CharSequence name = null;
public short columnNumber = 0;
public int tableId = 0;
@Override
public short getShort(int col) {
return columnNumber;
}
@Override
public int getInt(int col) {
return tableId;
}
@Override
public CharSequence getStr(int col) {
return name;
}
@Override
public CharSequence getStrB(int col) {
return name;
}
@Override
public int getStrLen(int col) {
return getStr(col).length();
}
}
}
static {
final GenericRecordMetadata metadata = new GenericRecordMetadata();
metadata.add(new TableColumnMetadata("attrelid", ColumnType.INT));
metadata.add(new TableColumnMetadata("attname", ColumnType.STRING));
metadata.add(new TableColumnMetadata("attnum", ColumnType.SHORT));
METADATA = metadata;
}
}
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.griffin.engine.functions.catalogue;
import io.questdb.cairo.ColumnType;
import io.questdb.cairo.GenericRecordMetadata;
import io.questdb.cairo.TableColumnMetadata;
import io.questdb.cairo.sql.NoRandomAccessRecordCursor;
import io.questdb.cairo.sql.Record;
import io.questdb.cairo.sql.RecordMetadata;
class IndexCatalogueCursor implements NoRandomAccessRecordCursor {
static final RecordMetadata METADATA;
private static final IndexCatalogueRecord record = new IndexCatalogueRecord();
@Override
public void close() {
}
@Override
public Record getRecord() {
return record;
}
@Override
public boolean hasNext() {
return false;
}
@Override
public void toTop() {
}
@Override
public long size() {
return 0;
}
private static class IndexCatalogueRecord implements Record {
}
static {
final GenericRecordMetadata metadata = new GenericRecordMetadata();
metadata.add(new TableColumnMetadata("indkey", ColumnType.INT));
metadata.add(new TableColumnMetadata("indrelid", ColumnType.INT));
metadata.add(new TableColumnMetadata("indexrelid", ColumnType.INT));
metadata.add(new TableColumnMetadata("indisprimary", ColumnType.BOOLEAN));
METADATA = metadata;
}
}
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.griffin.engine.functions.catalogue;
import io.questdb.cairo.CairoConfiguration;
import io.questdb.cairo.sql.Function;
import io.questdb.griffin.FunctionFactory;
import io.questdb.griffin.engine.functions.CursorFunction;
import io.questdb.griffin.engine.functions.GenericRecordCursorFactory;
import io.questdb.std.ObjList;
public class IndexCatalogueFunctionFactory implements FunctionFactory {
@Override
public String getSignature() {
return "pg_catalog.pg_index()";
}
public Function newInstance(ObjList<Function> args, int position, CairoConfiguration configuration) {
return new CursorFunction(
position,
new GenericRecordCursorFactory(
IndexCatalogueCursor.METADATA,
new IndexCatalogueCursor(),
false
)
);
}
}
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.griffin.engine.functions.catalogue;
import io.questdb.cairo.ColumnType;
import io.questdb.cairo.GenericRecordMetadata;
import io.questdb.cairo.TableColumnMetadata;
import io.questdb.cairo.sql.NoRandomAccessRecordCursor;
import io.questdb.cairo.sql.Record;
import io.questdb.cairo.sql.RecordMetadata;
class InformationSchemaCursor implements NoRandomAccessRecordCursor {
static final RecordMetadata METADATA;
private static final InformationSchemaRecord record = new InformationSchemaRecord();
@Override
public void close() {
}
@Override
public Record getRecord() {
return record;
}
@Override
public boolean hasNext() {
return false;
}
@Override
public void toTop() {
}
@Override
public long size() {
return 0;
}
private static class InformationSchemaRecord implements Record {
}
static {
final GenericRecordMetadata metadata = new GenericRecordMetadata();
metadata.add(new TableColumnMetadata("x", ColumnType.INT));
metadata.add(new TableColumnMetadata("n", ColumnType.INT));
METADATA = metadata;
}
}
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.griffin.engine.functions.catalogue;
import io.questdb.cairo.CairoConfiguration;
import io.questdb.cairo.sql.Function;
import io.questdb.griffin.FunctionFactory;
import io.questdb.griffin.engine.functions.CursorFunction;
import io.questdb.griffin.engine.functions.GenericRecordCursorFactory;
import io.questdb.std.ObjList;
public class InformationSchemaFunctionFactory implements FunctionFactory {
@Override
public String getSignature() {
return "information_schema._pg_expandarray(I)";
}
public Function newInstance(ObjList<Function> args, int position, CairoConfiguration configuration) {
return new CursorFunction(
position,
new GenericRecordCursorFactory(
InformationSchemaCursor.METADATA,
new InformationSchemaCursor(),
false
)
);
}
}
......@@ -56,11 +56,11 @@ public class QueryModel implements Mutable, ExecutionModel, AliasTranslator, Sin
public static final int SET_OPERATION_INTERSECT = 3;
private static final ObjList<String> modelTypeName = new ObjList<>();
private final ObjList<QueryColumn> bottomUpColumns = new ObjList<>();
private final CharSequenceHashSet topDownNameSet = new CharSequenceHashSet();
private final LowerCaseCharSequenceHashSet topDownNameSet = new LowerCaseCharSequenceHashSet();
private final ObjList<QueryColumn> topDownColumns = new ObjList<>();
private final CharSequenceObjHashMap<CharSequence> aliasToColumnNameMap = new CharSequenceObjHashMap<>();
private final CharSequenceObjHashMap<CharSequence> columnNameToAliasMap = new CharSequenceObjHashMap<>();
private final CharSequenceObjHashMap<QueryColumn> aliasToColumnMap = new CharSequenceObjHashMap<>();
private final LowerCaseCharSequenceObjHashMap<CharSequence> aliasToColumnNameMap = new LowerCaseCharSequenceObjHashMap<>();
private final LowerCaseCharSequenceObjHashMap<CharSequence> columnNameToAliasMap = new LowerCaseCharSequenceObjHashMap<>();
private final LowerCaseCharSequenceObjHashMap<QueryColumn> aliasToColumnMap = new LowerCaseCharSequenceObjHashMap<>();
private final ObjList<CharSequence> bottomUpColumnNames = new ObjList<>();
private final ObjList<QueryModel> joinModels = new ObjList<>();
private final ObjList<ExpressionNode> orderBy = new ObjList<>();
......@@ -69,7 +69,7 @@ public class QueryModel implements Mutable, ExecutionModel, AliasTranslator, Sin
private final IntHashSet dependencies = new IntHashSet();
private final IntList orderedJoinModels1 = new IntList();
private final IntList orderedJoinModels2 = new IntList();
private final CharSequenceIntHashMap aliasIndexes = new CharSequenceIntHashMap();
private final LowerCaseCharSequenceIntHashMap aliasIndexes = new LowerCaseCharSequenceIntHashMap();
private final ObjList<ExpressionNode> expressionModels = new ObjList<>();
// collect frequency of column names from each join model
// and check if any of columns with frequency > 0 are selected
......@@ -79,9 +79,9 @@ public class QueryModel implements Mutable, ExecutionModel, AliasTranslator, Sin
private final ObjList<ExpressionNode> parsedWhere = new ObjList<>();
private final IntHashSet parsedWhereConsts = new IntHashSet();
private final ArrayDeque<ExpressionNode> sqlNodeStack = new ArrayDeque<>();
private final CharSequenceIntHashMap orderHash = new CharSequenceIntHashMap(4, 0.5, -1);
private final LowerCaseCharSequenceIntHashMap orderHash = new LowerCaseCharSequenceIntHashMap(4, 0.5, -1);
private final ObjList<ExpressionNode> joinColumns = new ObjList<>(4);
private final CharSequenceObjHashMap<WithClauseModel> withClauses = new CharSequenceObjHashMap<>();
private final LowerCaseCharSequenceObjHashMap<WithClauseModel> withClauses = new LowerCaseCharSequenceObjHashMap<>();
private final ObjList<ExpressionNode> sampleByFill = new ObjList<>();
private final ObjList<ExpressionNode> latestBy = new ObjList<>();
private final ObjList<ExpressionNode> orderByAdvice = new ObjList<>();
......@@ -182,7 +182,7 @@ public class QueryModel implements Mutable, ExecutionModel, AliasTranslator, Sin
withClauses.put(name, model);
}
public void addWithClauses(CharSequenceObjHashMap<WithClauseModel> parentWithClauses) {
public void addWithClauses(LowerCaseCharSequenceObjHashMap<WithClauseModel> parentWithClauses) {
withClauses.putAll(parentWithClauses);
}
......@@ -288,11 +288,11 @@ public class QueryModel implements Mutable, ExecutionModel, AliasTranslator, Sin
return -1;
}
public CharSequenceObjHashMap<QueryColumn> getAliasToColumnMap() {
public LowerCaseCharSequenceObjHashMap<QueryColumn> getAliasToColumnMap() {
return aliasToColumnMap;
}
public CharSequenceObjHashMap<CharSequence> getAliasToColumnNameMap() {
public LowerCaseCharSequenceObjHashMap<CharSequence> getAliasToColumnNameMap() {
return aliasToColumnNameMap;
}
......@@ -304,7 +304,7 @@ public class QueryModel implements Mutable, ExecutionModel, AliasTranslator, Sin
return bottomUpColumns;
}
public CharSequenceObjHashMap<CharSequence> getColumnNameToAliasMap() {
public LowerCaseCharSequenceObjHashMap<CharSequence> getColumnNameToAliasMap() {
return columnNameToAliasMap;
}
......@@ -441,7 +441,7 @@ public class QueryModel implements Mutable, ExecutionModel, AliasTranslator, Sin
return orderByDirectionAdvice;
}
public CharSequenceIntHashMap getOrderHash() {
public LowerCaseCharSequenceIntHashMap getOrderHash() {
return orderHash;
}
......@@ -550,7 +550,7 @@ public class QueryModel implements Mutable, ExecutionModel, AliasTranslator, Sin
return withClauses.get(name);
}
public CharSequenceObjHashMap<WithClauseModel> getWithClauses() {
public LowerCaseCharSequenceObjHashMap<WithClauseModel> getWithClauses() {
return withClauses;
}
......
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.std;
import java.util.Arrays;
public abstract class AbstractLowerCaseCharSequenceHashSet implements Mutable {
protected static final CharSequence noEntryKey = null;
protected static final int MIN_INITIAL_CAPACITY = 16;
protected final double loadFactor;
protected int mask;
protected int free;
protected int capacity;
// exposed for testing only
protected CharSequence[] keys;
public AbstractLowerCaseCharSequenceHashSet(int initialCapacity, double loadFactor) {
if (loadFactor <= 0d || loadFactor >= 1d) {
throw new IllegalArgumentException("0 < loadFactor < 1");
}
free = this.capacity = Math.max(initialCapacity, MIN_INITIAL_CAPACITY);
this.loadFactor = loadFactor;
keys = new CharSequence[Numbers.ceilPow2((int) (this.capacity / loadFactor))];
mask = keys.length - 1;
}
@Override
public void clear() {
Arrays.fill(keys, noEntryKey);
free = this.capacity;
}
public boolean excludes(CharSequence key) {
return keyIndex(key) > -1;
}
public boolean excludes(CharSequence key, int lo, int hi) {
return keyIndex(key, lo, hi) > -1;
}
public int keyIndex(CharSequence key) {
int index = Chars.lowerCaseHashCode(key) & mask;
if (keys[index] == noEntryKey) {
return index;
}
if (Chars.equalsLowerCase(key, keys[index])) {
return -index - 1;
}
return probe(key, index);
}
public int keyIndex(CharSequence key, int lo, int hi) {
int index = Chars.lowerCaseHashCode(key, lo, hi) & mask;
if (keys[index] == noEntryKey) {
return index;
}
CharSequence cs = keys[index];
if (Chars.equalsLowerCase(key, lo, hi, cs, 0, cs.length())) {
return -index - 1;
}
return probe(key, lo, hi, index);
}
public int remove(CharSequence key) {
int index = keyIndex(key);
if (index < 0) {
removeAt(index);
return -index - 1;
}
return -1;
}
public void removeAt(int index) {
if (index < 0) {
int from = -index - 1;
erase(from);
free++;
// after we have freed up a slot
// consider non-empty keys directly below
// they may have been a direct hit but because
// directly hit slot wasn't empty these keys would
// have moved.
//
// After slot if freed these keys require re-hash
from = (from + 1) & mask;
for (
CharSequence key = keys[from];
key != noEntryKey;
from = (from + 1) & mask, key = keys[from]
) {
int idealHit = Chars.lowerCaseHashCode(key) & mask;
if (idealHit != from) {
int to;
if (keys[idealHit] != noEntryKey) {
to = probe(key, idealHit);
} else {
to = idealHit;
}
if (to > -1) {
move(from, to);
}
}
}
}
}
public int size() {
return capacity - free;
}
/**
* Erases entry in array.
*
* @param index always positive, no arithmetic required.
*/
abstract protected void erase(int index);
abstract protected void move(int from, int to);
private int probe(CharSequence key, int index) {
do {
index = (index + 1) & mask;
if (keys[index] == noEntryKey) {
return index;
}
if (Chars.equalsLowerCase(key, keys[index])) {
return -index - 1;
}
} while (true);
}
private int probe(CharSequence key, int lo, int hi, int index) {
do {
index = (index + 1) & mask;
if (keys[index] == noEntryKey) {
return index;
}
CharSequence cs = keys[index];
if (Chars.equalsLowerCase(key, lo, hi, cs, 0, cs.length())) {
return -index - 1;
}
} while (true);
}
}
......@@ -230,6 +230,39 @@ public final class Chars {
return l != null && equalsIgnoreCase(l, r);
}
public static boolean equalsLowerCase(CharSequence l, int lLo, int lHi, CharSequence r, int rLo, int rHi) {
if (l == r) {
return true;
}
int ll = lHi - lLo;
if (ll != rHi - rLo) {
return false;
}
for (int i = 0; i < ll; i++) {
if (Character.toLowerCase(l.charAt(i + lLo)) != Character.toLowerCase(r.charAt(i + rLo))) {
return false;
}
}
return true;
}
public static boolean equalsLowerCase(@NotNull CharSequence l, CharSequence r) {
int ll;
if ((ll = l.length()) != r.length()) {
return false;
}
for (int i = 0; i < ll; i++) {
if (Character.toLowerCase(l.charAt(i)) != Character.toLowerCase(r.charAt(i))) {
return false;
}
}
return true;
}
public static boolean equalsLowerCaseAscii(CharSequence l, int lLo, int lHi, CharSequence r, int rLo, int rHi) {
if (l == r) {
return true;
......@@ -241,7 +274,7 @@ public final class Chars {
}
for (int i = 0; i < ll; i++) {
if (toLowerCaseAscii(l.charAt(i + lLo)) != r.charAt(i + rLo)) {
if (toLowerCaseAscii(l.charAt(i + lLo)) != toLowerCaseAscii(r.charAt(i + rLo))) {
return false;
}
}
......@@ -255,7 +288,7 @@ public final class Chars {
}
for (int i = 0; i < ll; i++) {
if (toLowerCaseAscii(l.charAt(i)) != r.charAt(i)) {
if (toLowerCaseAscii(l.charAt(i)) != toLowerCaseAscii(r.charAt(i))) {
return false;
}
}
......@@ -389,6 +422,31 @@ public final class Chars {
return h;
}
public static int lowerCaseHashCode(CharSequence value, int lo, int hi) {
if (hi == lo) {
return 0;
}
int h = 0;
for (int p = lo; p < hi; p++) {
h = 31 * h + Character.toLowerCase(value.charAt(p));
}
return h;
}
public static int lowerCaseHashCode(CharSequence value) {
int len = value.length();
if (len == 0) {
return 0;
}
int h = 0;
for (int p = 0; p < len; p++) {
h = 31 * h + Character.toLowerCase(value.charAt(p));
}
return h;
}
public static boolean noMatch(CharSequence l, int llo, int lhi, CharSequence r, int rlo, int rhi) {
int lp = llo;
int rp = rlo;
......@@ -498,8 +556,6 @@ public final class Chars {
b.put(toLowerCaseAscii(value.charAt(i)));
}
return b.toString();
}
public static char toLowerCaseAscii(char character) {
......@@ -765,4 +821,15 @@ public final class Chars {
sink.put((char) (b1 << 6 ^ b2 ^ 3968));
return 2;
}
public static String toLowerCase(CharSequence str) {
final CharSink sink = Misc.getThreadLocalBuilder();
if (str != null) {
final int len = str.length();
for (int i = 0; i < len; i++) {
sink.put(Character.toLowerCase(str.charAt(i)));
}
}
return sink.toString();
}
}
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.std;
public class LowerCaseCharSequenceHashSet extends AbstractLowerCaseCharSequenceHashSet {
private static final int MIN_INITIAL_CAPACITY = 16;
public LowerCaseCharSequenceHashSet() {
this(MIN_INITIAL_CAPACITY);
}
private LowerCaseCharSequenceHashSet(int initialCapacity) {
this(initialCapacity, 0.4);
}
private LowerCaseCharSequenceHashSet(int initialCapacity, double loadFactor) {
super(initialCapacity, loadFactor);
clear();
}
/**
* Adds key to hash set preserving key uniqueness.
*
* @param key immutable sequence of characters.
* @return false if key is already in the set and true otherwise.
*/
public boolean add(CharSequence key) {
int index = keyIndex(key);
if (index < 0) {
return false;
}
addAt(index, key);
return true;
}
public void addAt(int index, CharSequence key) {
keys[index] = key;
if (--free < 1) {
rehash();
}
}
public boolean contains(CharSequence key) {
return keyIndex(key) < 0;
}
public CharSequence keyAt(int index) {
return keys[-index - 1];
}
@Override
protected void erase(int index) {
keys[index] = noEntryKey;
}
@Override
protected void move(int from, int to) {
keys[to] = keys[from];
erase(from);
}
private void rehash() {
int newCapacity = capacity * 2;
final int size = size();
free = capacity = newCapacity;
int len = Numbers.ceilPow2((int) (newCapacity / loadFactor));
CharSequence[] newKeys = new CharSequence[len];
CharSequence[] oldKeys = keys;
mask = len - 1;
this.keys = newKeys;
free -= size;
for (int i = 0, n = oldKeys.length; i < n; i++) {
CharSequence key = oldKeys[i];
if (key != null) {
keys[keyIndex(key)] = key;
}
}
}
}
\ No newline at end of file
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.std;
import java.util.Arrays;
public class LowerCaseCharSequenceIntHashMap extends AbstractLowerCaseCharSequenceHashSet {
private static final int NO_ENTRY_VALUE = -1;
private final int noEntryValue;
protected int[] values;
private final ObjList<CharSequence> list;
public LowerCaseCharSequenceIntHashMap() {
this(8);
}
public LowerCaseCharSequenceIntHashMap(int initialCapacity) {
this(initialCapacity, 0.5, NO_ENTRY_VALUE);
}
public LowerCaseCharSequenceIntHashMap(int initialCapacity, double loadFactor, int noEntryValue) {
super(initialCapacity, loadFactor);
this.noEntryValue = noEntryValue;
this.list = new ObjList<>(capacity);
values = new int[keys.length];
clear();
}
public void clear() {
super.clear();
list.clear();
Arrays.fill(values, noEntryValue);
}
@Override
protected void erase(int index) {
keys[index] = noEntryKey;
values[index] = noEntryValue;
}
public void removeAt(int index) {
if (index < 0) {
int index1 = -index - 1;
CharSequence key = keys[index1];
super.removeAt(index);
list.remove(key);
}
}
public int valueAt(int index) {
return index < 0 ? values[-index - 1] : noEntryValue;
}
public boolean contains(CharSequence key) {
return keyIndex(key) < 0;
}
public int get(CharSequence key) {
return valueAt(keyIndex(key));
}
public boolean put(CharSequence key, int value) {
return putAt(keyIndex(key), key, value);
}
public boolean putAt(int index, CharSequence key, int value) {
if (index < 0) {
values[-index - 1] = value;
return false;
}
putAt0(index, key, value);
list.add(key);
return true;
}
public void putIfAbsent(CharSequence key, int value) {
int index = keyIndex(key);
if (index > -1) {
putAt0(index, key, value);
}
}
@Override
protected void move(int from, int to) {
keys[to] = keys[from];
values[to] = values[from];
erase(from);
}
protected void putAt0(int index, CharSequence key, int value) {
keys[index] = key;
values[index] = value;
if (--free == 0) {
rehash();
}
}
private void rehash() {
int size = size();
int newCapacity = capacity * 2;
free = capacity = newCapacity;
int len = Numbers.ceilPow2((int) (newCapacity / loadFactor));
int[] oldValues = values;
CharSequence[] oldKeys = keys;
this.keys = new CharSequence[len];
this.values = new int[len];
Arrays.fill(keys, null);
mask = len - 1;
free -= size;
for (int i = oldKeys.length; i-- > 0; ) {
CharSequence key = oldKeys[i];
if (key != null) {
final int index = keyIndex(key);
keys[index] = key;
values[index] = oldValues[i];
}
}
}
public ObjList<CharSequence> keys() {
return list;
}
}
\ No newline at end of file
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.std;
import java.util.Arrays;
public class LowerCaseCharSequenceObjHashMap<T> extends AbstractLowerCaseCharSequenceHashSet {
private final ObjList<CharSequence> list;
private T[] values;
public LowerCaseCharSequenceObjHashMap() {
this(8);
}
public LowerCaseCharSequenceObjHashMap(int initialCapacity) {
this(initialCapacity, 0.5);
}
@SuppressWarnings("unchecked")
public LowerCaseCharSequenceObjHashMap(int initialCapacity, double loadFactor) {
super(initialCapacity, loadFactor);
values = (T[]) new Object[keys.length];
this.list = new ObjList<>(capacity);
clear();
}
public final void clear() {
super.clear();
list.clear();
Arrays.fill(values, null);
}
public ObjList<CharSequence> keys() {
return list;
}
@Override
protected void erase(int index) {
keys[index] = noEntryKey;
values[index] = null;
}
@Override
public void removeAt(int index) {
if (index < 0) {
CharSequence key = keys[-index - 1];
super.removeAt(index);
list.remove(key);
}
}
public boolean contains(CharSequence key) {
return keyIndex(key) < 0;
}
public T get(CharSequence key) {
return valueAt(keyIndex(key));
}
@Override
protected void move(int from, int to) {
keys[to] = keys[from];
values[to] = values[from];
erase(from);
}
public boolean put(CharSequence key, T value) {
return putAt(keyIndex(key), key, value);
}
public boolean putAt(int index, CharSequence key, T value) {
if (index < 0) {
values[-index - 1] = value;
return false;
}
putAt0(index, key, value);
list.add(key);
return true;
}
public void putIfAbsent(CharSequence key, T value) {
int index = keyIndex(key);
if (index > -1) {
putAt0(index, key, value);
}
}
public T valueAt(int index) {
return index < 0 ? valueAtQuick(index) : null;
}
public T valueAtQuick(int index) {
return values[-index - 1];
}
private void putAt0(int index, CharSequence key, T value) {
keys[index] = key;
values[index] = value;
if (--free == 0) {
rehash();
}
}
@SuppressWarnings("unchecked")
private void rehash() {
int size = size();
int newCapacity = capacity * 2;
free = capacity = newCapacity;
int arrayCapacity = Numbers.ceilPow2((int) (newCapacity / loadFactor));
T[] oldValues = values;
CharSequence[] oldKeys = keys;
this.keys = new CharSequence[arrayCapacity];
this.values = (T[]) new Object[arrayCapacity];
Arrays.fill(keys, null);
mask = arrayCapacity - 1;
free -= size;
for (int i = oldKeys.length; i-- > 0; ) {
CharSequence key = oldKeys[i];
if (key != null) {
final int index = keyIndex(key);
keys[index] = key;
values[index] = oldValues[i];
}
}
}
public void putAll(LowerCaseCharSequenceObjHashMap<T> other) {
CharSequence[] otherKeys = other.keys;
T[] otherValues = other.values;
for (int i = 0, n = otherKeys.length; i < n; i++) {
if (otherKeys[i] != noEntryKey) {
put(otherKeys[i], otherValues[i]);
}
}
}
}
\ No newline at end of file
......@@ -455,7 +455,10 @@ open module io.questdb {
io.questdb.griffin.engine.functions.conditional.CaseFunctionFactory,
io.questdb.griffin.engine.functions.conditional.SwitchFunctionFactory,
// PostgeSQL catalogue functions
io.questdb.griffin.engine.functions.catalogue.AttributeCatalogueFunctionFactory,
io.questdb.griffin.engine.functions.catalogue.ClassCatalogueFunctionFactory,
io.questdb.griffin.engine.functions.catalogue.IndexCatalogueFunctionFactory,
io.questdb.griffin.engine.functions.catalogue.InformationSchemaFunctionFactory,
io.questdb.griffin.engine.functions.catalogue.PrefixedNamespaceCatalogueFunctionFactory,
io.questdb.griffin.engine.functions.catalogue.NamespaceCatalogueFunctionFactory,
io.questdb.griffin.engine.functions.catalogue.IsTableVisibleCatalogueFunctionFactory,
......
......@@ -22,6 +22,30 @@
#
################################################################################
################################################################################
# ___ _ ____ ____
# / _ \ _ _ ___ ___| |_| _ \| __ )
# | | | | | | |/ _ \/ __| __| | | | _ \
# | |_| | |_| | __/\__ \ |_| |_| | |_) |
# \__\_\\__,_|\___||___/\__|____/|____/
#
# Copyright (c) 2014-2019 Appsicle
# Copyright (c) 2019-2020 QuestDB
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
################################################################################
# ___ _ ____ ____
......@@ -463,8 +487,11 @@ io.questdb.griffin.engine.functions.math.RoundHalfEvenDoubleFunctionFactory
io.questdb.griffin.engine.functions.conditional.CaseFunctionFactory
io.questdb.griffin.engine.functions.conditional.SwitchFunctionFactory
# PostgeSQL catalogue functions
# PostgreSQL catalogue functions
io.questdb.griffin.engine.functions.catalogue.AttributeCatalogueFunctionFactory
io.questdb.griffin.engine.functions.catalogue.ClassCatalogueFunctionFactory
io.questdb.griffin.engine.functions.catalogue.IndexCatalogueFunctionFactory
io.questdb.griffin.engine.functions.catalogue.InformationSchemaFunctionFactory
io.questdb.griffin.engine.functions.catalogue.PrefixedNamespaceCatalogueFunctionFactory
io.questdb.griffin.engine.functions.catalogue.IsTableVisibleCatalogueFunctionFactory
io.questdb.griffin.engine.functions.catalogue.UserByIdCatalogueFunctionFactory
......
......@@ -78,6 +78,31 @@ public class GenericRecordMetadataTest {
TestUtils.assertEquals(expected, sink);
}
@Test
public void testDuplicateColumn2() {
final String expected = "{\"columnCount\":2,\"columns\":[{\"index\":0,\"name\":\"abc\",\"type\":\"INT\"},{\"index\":1,\"name\":\"cde\",\"type\":\"INT\"}],\"timestampIndex\":-1}";
GenericRecordMetadata metadata = new GenericRecordMetadata();
metadata.add(new TableColumnMetadata("abc", ColumnType.INT));
metadata.add(new TableColumnMetadata("cde", ColumnType.INT));
sink.clear();
metadata.toJson(sink);
TestUtils.assertEquals(expected, sink);
try {
metadata.add(new TableColumnMetadata("ABC", ColumnType.FLOAT));
Assert.fail();
} catch (CairoException e) {
TestUtils.assertContains(e.getMessage(), "Duplicate column");
}
sink.clear();
metadata.toJson(sink);
TestUtils.assertEquals(expected, sink);
}
@Test
public void testReuse() {
String expected1 = "{\"columnCount\":3,\"columns\":[{\"index\":0,\"name\":\"abc\",\"type\":\"INT\"},{\"index\":1,\"name\":\"cde\",\"type\":\"INT\"},{\"index\":2,\"name\":\"timestamp\",\"type\":\"TIMESTAMP\"}],\"timestampIndex\":2}";
......
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.griffin.engine.functions.catalogue;
import io.questdb.griffin.AbstractGriffinTest;
import org.junit.Test;
public class AttributeCatalogueFunctionFactoryTest extends AbstractGriffinTest {
@Test
public void testPgAttributeFunc() throws Exception {
assertQuery(
"attrelid\tattname\tattnum\n" +
"1\ta\t1\n",
"pg_catalog.pg_attribute;",
"create table x(a int)",
null,
false,
false
);
}
@Test
public void testPgAttributeFuncNoTables() throws Exception {
assertQuery(
"attrelid\tattname\tattnum\n",
"pg_catalog.pg_attribute;",
null,
null,
false,
false
);
}
@Test
public void testPgAttributeFuncWith2Tables() throws Exception {
assertQuery(
"attrelid\tattname\tattnum\n" +
"1\ta\t1\n",
"pg_catalog.pg_attribute order by 1;",
"create table x(a int)",
null,
"create table y(a double, b string)",
"attrelid\tattname\tattnum\n" +
"1\ta\t1\n" +
"2\ta\t1\n" +
"2\tb\t2\n",
true,
false,
false
);
}
@Test
public void testPgAttributeFuncWith2TablesLimit1() throws Exception {
assertQuery(
"attrelid\tattname\tattnum\n" +
"1\ta\t1\n",
"pg_catalog.pg_attribute order by 1 limit 1;",
"create table x(a int)",
null,
"create table y(a double, b string)",
"attrelid\tattname\tattnum\n" +
"1\ta\t1\n",
true,
false,
true
);
}
@Test
public void testKafkaMetadataQuery() throws Exception {
String query = "\n" +
"SELECT\n" +
" result.TABLE_CAT, \n" +
" result.TABLE_SCHEM, \n" +
" result.TABLE_NAME, \n" +
" result.COLUMN_NAME, \n" +
" result.KEY_SEQ, \n" +
" result.PK_NAME,\n" +
" result.KEYS,\n" +
" result.A_ATTNUM,\n" +
" RAW \n" +
"FROM\n" +
" (SELECT \n" +
" NULL AS TABLE_CAT, \n" +
" n.nspname AS TABLE_SCHEM, \n" +
" ct.relname AS TABLE_NAME, \n" +
" a.attname AS COLUMN_NAME, \n" +
" (information_schema._pg_expandarray(i.indkey)).n AS KEY_SEQ, \n" +
" ci.relname AS PK_NAME, \n" +
" information_schema._pg_expandarray(i.indkey) AS KEYS, \n" +
" a.attnum AS A_ATTNUM,\n" +
" i.indkey AS RAW \n" +
" FROM pg_catalog.pg_class ct\n" +
" JOIN pg_catalog.pg_attribute a ON (ct.oid = a.attrelid) \n" +
" JOIN pg_catalog.pg_namespace n ON (ct.relnamespace = n.oid) \n" +
" JOIN pg_catalog.pg_index i ON ( a.attrelid = i.indrelid) \n" +
" JOIN pg_catalog.pg_class ci ON (ci.oid = i.indexrelid) \n" +
" WHERE \n" +
" true \n" +
" AND ct.relname = E'po_items' \n" +
" AND i.indisprimary \n" +
" ) result; \n";
assertQuery(
"TABLE_CAT\tTABLE_SCHEM\tTABLE_NAME\tCOLUMN_NAME\tKEY_SEQ\tPK_NAME\tKEYS\tA_ATTNUM\tRAW\n",
query,
"create table x(a int)",
null,
false,
false
);
}
@Test
public void testKafkaMetadataQueryCaseInsensitivity1() throws Exception {
String query = "SELECT\n" +
" result.TABLE_CAT, \n" +
" result.TABLE_SCHEM, \n" +
" result.TABLE_NAME, \n" +
" result.COLUMN_NAME, \n" +
" result.KEY_SEQ, \n" +
" result.PK_NAME,\n" +
" -- result.KEYS,\n" +
" result.A_ATTNUM,\n" +
" RAW \n" +
"FROM\n" +
" (SELECT \n" +
" NULL AS TABLE_CAT, \n" +
" n.nspname AS TABLE_SCHEM, \n" +
" ct.relname AS TABLE_NAME, \n" +
" a.attname AS COLUMN_NAME, \n" +
" (information_schema._pg_expandarray(i.indkey)).n AS KEY_SEQ, \n" +
" ci.relname AS PK_NAME, \n" +
" -- information_schema._pg_expandarray(i.indkey) AS KEYS, \n" +
" a.attnum AS A_ATTNUM,\n" +
" i.indkey AS RAW \n" +
" FROM pg_catalog.pg_class ct\n" +
" JOIN pg_catalog.pg_attribute a ON (ct.oid = a.attrelid) \n" +
" JOIN pg_catalog.pg_namespace n ON (ct.relnamespace = n.oid) \n" +
" JOIN pg_catalog.pg_index i ON ( a.attrelid = i.indrelid) \n" +
" JOIN pg_catalog.pg_class ci ON (ci.oid = i.indexrelid) \n" +
" WHERE \n" +
" true \n" +
" AND ct.relname = E'po_items' \n" +
" AND i.indisprimary \n" +
" ) result \n" +
"--WHERE A_ATTNUM = (result.KEYS).x \n" +
"ORDER BY result.table_name, result.PK_NAME, result.KEY_SEQ;";
assertQuery(
"TABLE_CAT\tTABLE_SCHEM\tTABLE_NAME\tCOLUMN_NAME\tKEY_SEQ\tPK_NAME\tA_ATTNUM\tRAW\n",
query,
"create table x(a int)",
null,
true,
false
);
}
@Test
public void testKafkaMetadataQueryCaseInsensitivity2() throws Exception {
String query = "SELECT\n" +
" result.TABLE_CAT, \n" +
" result.TABLE_SCHEM, \n" +
" result.TABLE_NAME, \n" +
" result.COLUMN_NAME, \n" +
" result.KEY_SEQ, \n" +
" result.PK_NAME,\n" +
" -- result.KEYS,\n" +
" result.A_ATTNUM,\n" +
" RAW \n" +
"FROM\n" +
" (SELECT \n" +
" NULL AS TABLE_CAT, \n" +
" n.nspname AS TABLE_SCHEM, \n" +
" ct.relname AS TABLE_NAME, \n" +
" a.attname AS COLUMN_NAME, \n" +
" (information_schema._pg_expandarray(i.indkey)).n AS KEY_SEQ, \n" +
" ci.relname AS PK_NAME, \n" +
" -- information_schema._pg_expandarray(i.indkey) AS KEYS, \n" +
" a.attnum AS A_ATTNUM,\n" +
" i.indkey AS RAW \n" +
" FROM pg_catalog.pg_class ct\n" +
" JOIN pg_catalog.pg_attribute a ON (ct.oid = a.attrelid) \n" +
" JOIN pg_catalog.pg_namespace n ON (ct.relnamespace = n.oid) \n" +
" JOIN pg_catalog.pg_index i ON ( a.attrelid = i.indrelid) \n" +
" JOIN pg_catalog.pg_class ci ON (ci.oid = i.indexrelid) \n" +
" WHERE \n" +
" true \n" +
" AND ct.relname = E'po_items' \n" +
" AND i.indisprimary \n" +
" ) result \n" +
"--WHERE A_ATTNUM = (result.KEYS).x \n" +
"ORDER BY result.TABLE_NAME, result.pk_name, result.KEY_SEQ;";
assertQuery(
"TABLE_CAT\tTABLE_SCHEM\tTABLE_NAME\tCOLUMN_NAME\tKEY_SEQ\tPK_NAME\tA_ATTNUM\tRAW\n",
query,
"create table x(a int)",
null,
true,
false
);
}
}
\ No newline at end of file
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.griffin.engine.functions.catalogue;
import io.questdb.griffin.AbstractGriffinTest;
import org.junit.Test;
public class IndexCatalogueFunctionFactoryTest extends AbstractGriffinTest {
@Test
public void testPgIndexFunc() throws Exception {
assertQuery(
"indkey\tindrelid\tindexrelid\tindisprimary\n",
"pg_catalog.pg_index;",
"create table x(a int)",
null,
false,
false,
true
);
}
}
\ No newline at end of file
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.griffin.engine.functions.catalogue;
import io.questdb.griffin.AbstractGriffinTest;
import org.junit.Test;
public class InformationSchemaFunctionFactoryTest extends AbstractGriffinTest {
@Test
public void testInformationSchemaPivotFunc() throws Exception {
assertQuery(
"x\tn\n",
"information_schema._pg_expandarray(5);",
"create table x(a int)",
null,
false,
false,
true
);
}
}
\ No newline at end of file
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.std;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashSet;
public class LowerCaseCharSequenceHashSetTest {
@Test
public void testSaturation() {
final int N = 10_000;
final Rnd rnd = new Rnd();
final LowerCaseCharSequenceHashSet lowerCaseSet = new LowerCaseCharSequenceHashSet();
final HashSet<String> referenceSet = new HashSet<>();
for (int i = 0; i < N; i++) {
String str = rnd.nextString(4);
int keyIndex = lowerCaseSet.keyIndex(str, 0, str.length());
if (lowerCaseSet.add(str)) {
Assert.assertTrue("at " + i, referenceSet.add(str));
Assert.assertTrue(keyIndex > -1);
} else {
Assert.assertTrue(keyIndex < 0);
}
}
// verify
for (String s : referenceSet) {
Assert.assertTrue(lowerCaseSet.contains(s));
Assert.assertFalse(lowerCaseSet.excludes(s));
Assert.assertFalse(lowerCaseSet.excludes(s, 0, s.length()));
int keyIndex = lowerCaseSet.keyIndex(s);
Assert.assertTrue(keyIndex < 0);
Assert.assertTrue(Chars.equals(s, lowerCaseSet.keyAt(keyIndex)));
keyIndex = lowerCaseSet.keyIndex(s, 0, s.length());
Assert.assertTrue(Chars.equals(s, lowerCaseSet.keyAt(keyIndex)));
}
for (int i = 0, n = lowerCaseSet.keys.length; i < n; i++) {
CharSequence v = lowerCaseSet.keys[i];
if (v != null) {
Assert.assertTrue(referenceSet.contains(v.toString()));
}
}
// remove every forth key
// make sure rnd generates the same keys again
rnd.reset();
for (int i = 0; i < N; i++) {
String s = rnd.nextString(4);
if (i % 4 == 0) {
lowerCaseSet.remove(s);
referenceSet.remove(s);
}
}
// verify
for (String s : referenceSet) {
Assert.assertTrue(s, lowerCaseSet.contains(s));
}
for (int i = 0, n = lowerCaseSet.keys.length; i < n; i++) {
CharSequence v = lowerCaseSet.keys[i];
if (v != null) {
Assert.assertTrue(referenceSet.contains(v.toString()));
}
}
}
}
\ No newline at end of file
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.std;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
public class LowerCaseCharSequenceIntHashMapTest {
@Test
public void testSaturation() {
final int N = 10_000;
final Rnd rnd = new Rnd();
final LowerCaseCharSequenceIntHashMap lowerCaseMap = new LowerCaseCharSequenceIntHashMap();
final HashMap<String, Integer> referenceMap = new HashMap<>();
for (int i = 0; i < N; i++) {
String str = rnd.nextString(4);
int value = rnd.nextInt();
int keyIndex = lowerCaseMap.keyIndex(str, 0, str.length());
if (lowerCaseMap.put(str, value)) {
Assert.assertNull("at " + i, referenceMap.put(str, value));
Assert.assertTrue(keyIndex > -1);
} else {
Assert.assertTrue(keyIndex < 0);
// this should fail to put
lowerCaseMap.putIfAbsent(str, value);
lowerCaseMap.putAt(keyIndex, str, referenceMap.get(str));
}
}
// verify
for (Map.Entry<String, Integer> e : referenceMap.entrySet()) {
Assert.assertTrue(lowerCaseMap.contains(e.getKey()));
Assert.assertFalse(lowerCaseMap.excludes(e.getKey()));
Assert.assertFalse(lowerCaseMap.excludes(e.getKey(), 0, e.getKey().length()));
int keyIndex = lowerCaseMap.keyIndex(e.getKey());
Assert.assertTrue(keyIndex < 0);
Assert.assertEquals(e.getKey(), (int) e.getValue(), lowerCaseMap.valueAt(keyIndex));
keyIndex = lowerCaseMap.keyIndex(e.getKey(), 0, e.getKey().length());
Assert.assertEquals((int) e.getValue(), lowerCaseMap.valueAt(keyIndex));
}
for (int i = 0, n = lowerCaseMap.keys.length; i < n; i++) {
CharSequence v = lowerCaseMap.keys[i];
if (v != null) {
Assert.assertTrue(referenceMap.containsKey(v.toString()));
}
}
// remove every forth key
// make sure rnd generates the same keys again
rnd.reset();
for (int i = 0; i < N; i++) {
String s = rnd.nextString(4);
if (i % 4 == 0) {
lowerCaseMap.remove(s);
referenceMap.remove(s);
}
}
// verify
for (Map.Entry<String, Integer> e : referenceMap.entrySet()) {
Assert.assertTrue(e.getKey(), lowerCaseMap.contains(e.getKey()));
}
for (int i = 0, n = lowerCaseMap.keys.length; i < n; i++) {
CharSequence v = lowerCaseMap.keys[i];
if (v != null) {
Assert.assertTrue(referenceMap.containsKey(v.toString()));
}
}
}
}
\ No newline at end of file
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package io.questdb.std;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
public class LowerCaseCharSequenceObjHashMapTest {
@Test
public void testSaturation() {
final int N = 10_000;
final Rnd rnd = new Rnd();
final LowerCaseCharSequenceObjHashMap<Integer> lowerCaseMap = new LowerCaseCharSequenceObjHashMap<>();
final HashMap<String, Integer> referenceMap = new HashMap<>();
for (int i = 0; i < N; i++) {
String str = rnd.nextString(4);
int value = rnd.nextInt();
int keyIndex = lowerCaseMap.keyIndex(str, 0, str.length());
if (lowerCaseMap.put(str, value)) {
Assert.assertNull("at " + i, referenceMap.put(str, value));
Assert.assertTrue(keyIndex > -1);
} else {
Assert.assertTrue(keyIndex < 0);
// this should fail to put
lowerCaseMap.putIfAbsent(str, value);
lowerCaseMap.putAt(keyIndex, str, referenceMap.get(str));
}
}
// verify
for (Map.Entry<String, Integer> e : referenceMap.entrySet()) {
Assert.assertTrue(lowerCaseMap.contains(e.getKey()));
Assert.assertFalse(lowerCaseMap.excludes(e.getKey()));
Assert.assertFalse(lowerCaseMap.excludes(e.getKey(), 0, e.getKey().length()));
int keyIndex = lowerCaseMap.keyIndex(e.getKey());
Assert.assertTrue(keyIndex < 0);
Assert.assertEquals(e.getKey(), e.getValue(), lowerCaseMap.valueAt(keyIndex));
keyIndex = lowerCaseMap.keyIndex(e.getKey(), 0, e.getKey().length());
Assert.assertEquals(e.getValue(), lowerCaseMap.valueAt(keyIndex));
}
for (int i = 0, n = lowerCaseMap.keys.length; i < n; i++) {
CharSequence v = lowerCaseMap.keys[i];
if (v != null) {
Assert.assertTrue(referenceMap.containsKey(v.toString()));
}
}
// remove every forth key
// make sure rnd generates the same keys again
rnd.reset();
for (int i = 0; i < N; i++) {
String s = rnd.nextString(4);
if (i % 4 == 0) {
lowerCaseMap.remove(s);
referenceMap.remove(s);
}
}
// verify
for (Map.Entry<String, Integer> e : referenceMap.entrySet()) {
Assert.assertTrue(e.getKey(), lowerCaseMap.contains(e.getKey()));
}
for (int i = 0, n = lowerCaseMap.keys.length; i < n; i++) {
CharSequence v = lowerCaseMap.keys[i];
if (v != null) {
Assert.assertTrue(referenceMap.containsKey(v.toString()));
}
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册