/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.sql.pg; import com.foundationdb.server.service.tree.KeyCreator; import com.foundationdb.sql.optimizer.NestedResultSetTypeComputer; import com.foundationdb.sql.optimizer.plan.PhysicalSelect.PhysicalResultColumn; import com.foundationdb.sql.optimizer.plan.PhysicalSelect; import com.foundationdb.sql.optimizer.plan.PhysicalUpdate; import com.foundationdb.sql.optimizer.plan.ResultSet.ResultField; import com.foundationdb.sql.types.DataTypeDescriptor; import com.foundationdb.sql.types.TypeId; import com.foundationdb.ais.model.AkibanInformationSchema; import com.foundationdb.qp.operator.Operator; import com.foundationdb.server.types.service.TypesRegistryService; import com.foundationdb.server.types.TInstance; import java.util.*; public class PostgresJsonCompiler extends PostgresOperatorCompiler { protected PostgresJsonCompiler() { } @Override protected void initAIS(AkibanInformationSchema ais, String defaultSchemaName) { super.initAIS(ais, defaultSchemaName); binder.setAllowSubqueryMultipleColumns(true); } @Override protected void initTypesRegistry(TypesRegistryService typesRegistry) { super.initTypesRegistry(typesRegistry); typeComputer = new NestedResultSetTypeComputer(typesRegistry); } public static PostgresJsonCompiler create(PostgresServerSession server, KeyCreator keyCreator) { PostgresJsonCompiler compiler = new PostgresJsonCompiler(); compiler.initServer(server, keyCreator); compiler.initDone(); return compiler; } public static class JsonResultColumn extends PhysicalResultColumn { private DataTypeDescriptor sqlType; private TInstance type; private PostgresType pgType; private List<JsonResultColumn> nestedResultColumns; public JsonResultColumn(String name, DataTypeDescriptor sqlType, TInstance type, PostgresType pgType, List<JsonResultColumn> nestedResultColumns) { super(name); this.sqlType = sqlType; this.type = type; this.pgType = pgType; this.nestedResultColumns = nestedResultColumns; } public DataTypeDescriptor getSqlType() { return sqlType; } public TInstance getType() { return type; } public PostgresType getPostgresType() { return pgType; } public List<JsonResultColumn> getNestedResultColumns() { return nestedResultColumns; } } @Override public PhysicalResultColumn getResultColumn(ResultField field) { return getJsonResultColumn(field.getName(), field.getSQLtype(), field.getType()); } protected JsonResultColumn getJsonResultColumn(String name, DataTypeDescriptor sqlType, TInstance type) { PostgresType pgType = null; List<JsonResultColumn> nestedResultColumns = null; if (sqlType == null) { } else if (sqlType.getTypeId().isRowMultiSet()) { TypeId.RowMultiSetTypeId typeId = (TypeId.RowMultiSetTypeId)sqlType.getTypeId(); String[] columnNames = typeId.getColumnNames(); DataTypeDescriptor[] columnTypes = typeId.getColumnTypes(); nestedResultColumns = new ArrayList<>(columnNames.length); for (int i = 0; i < columnNames.length; i++) { nestedResultColumns.add(getJsonResultColumn(columnNames[i], columnTypes[i], getTypesTranslator().typeForSQLType(columnTypes[i]))); } } else { pgType = PostgresType.fromDerby(sqlType, type); } return new JsonResultColumn(name, sqlType, type, pgType, nestedResultColumns); } @Override protected PostgresBaseOperatorStatement generateSelect() { return new PostgresJsonStatement(this); } @Override protected PostgresBaseOperatorStatement generateSelect(PostgresStatement pstmt, PhysicalSelect select, PostgresType[] parameterTypes) { PostgresJsonStatement pjstmt = (PostgresJsonStatement)pstmt; int ncols = select.getResultColumns().size(); List<JsonResultColumn> resultColumns = new ArrayList<>(ncols); for (PhysicalResultColumn physColumn : select.getResultColumns()) { JsonResultColumn resultColumn = (JsonResultColumn)physColumn; resultColumns.add(resultColumn); } pjstmt.init(select.getResultOperator(), select.getResultRowType(), resultColumns, parameterTypes, select.getCostEstimate()); return pjstmt; } @Override protected PostgresBaseOperatorStatement generateUpdate() { return super.generateUpdate(); // To handle !returning, see below } @Override protected PostgresBaseOperatorStatement generateUpdate(PostgresStatement pstmt, PhysicalUpdate update, String statementType, PostgresType[] parameterTypes) { if (!update.isReturning()) { return super.generateUpdate(pstmt, update, statementType, parameterTypes); } else { int ncols = update.getResultColumns().size(); List<JsonResultColumn> resultColumns = new ArrayList<>(ncols); for (PhysicalResultColumn physColumn : update.getResultColumns()) { JsonResultColumn resultColumn = (JsonResultColumn)physColumn; resultColumns.add(resultColumn); } PostgresJsonModifyStatement pjmstmt = new PostgresJsonModifyStatement(this); pjmstmt.init(statementType, (Operator)update.getPlannable(), update.getResultRowType(), resultColumns, parameterTypes, update.getCostEstimate(), update.putInCache()); return pjmstmt; } } }