/** * 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.rest.dml; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; import java.util.regex.Pattern; import com.foundationdb.ais.model.Column; import com.foundationdb.ais.model.Table; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.foundationdb.ais.model.TableName; import com.foundationdb.qp.operator.Operator; import com.foundationdb.server.explain.ExplainContext; import com.foundationdb.server.explain.format.DefaultFormatter; import com.foundationdb.server.service.session.Session; import com.foundationdb.server.test.it.ITBase; import com.foundationdb.server.types.common.types.TypesTranslator; import com.foundationdb.server.types.mcompat.mtypes.MTypesTranslator; import com.foundationdb.server.types.service.TypesRegistryService; public class InsertGeneratorIT extends ITBase { public static final String SCHEMA = "test"; private InsertGenerator insertGenerator; protected TypesTranslator typesTranslator() { return MTypesTranslator.INSTANCE; } @After public void commit() { this.txnService().commitTransaction(this.session()); } @Before public void start() { Session session = this.session(); this.txnService().beginTransaction(session); } @Test public void testCInsert() { createTable(SCHEMA, "c", "cid INT PRIMARY KEY NOT NULL", "name VARCHAR(32)"); TableName table = new TableName (SCHEMA, "c"); this.insertGenerator = new InsertGenerator (this.ais()); insertGenerator.setTypesRegistry(this.serviceManager().getServiceByClass(TypesRegistryService.class)); insertGenerator.setTypesTranslator(this.typesTranslator()); Operator insert = insertGenerator.create(table); assertEquals( "Project_Default(Field(0))\n" + " Insert_Returning(INTO c)\n" + " Project_Default(NULL, NULL)\n" + " ValuesScan_Default([])", getExplain(insert, table.getSchemaName()) ); } @Test public void testNoPKInsert() { createTable (SCHEMA, "c", "cid INT NOT NULL", "name VARCHAR(32)"); TableName table = new TableName (SCHEMA, "c"); this.insertGenerator = new InsertGenerator (this.ais()); insertGenerator.setTypesRegistry(this.serviceManager().getServiceByClass(TypesRegistryService.class)); insertGenerator.setTypesTranslator(this.typesTranslator()); Operator insert = insertGenerator.create(table); assertEquals( "Insert_Returning(INTO c)\n" + " Project_Default(NULL, NULL, _SEQ_NEXT(test, c___row_id_seq))\n" + " ValuesScan_Default([])", getExplain(insert, table.getSchemaName()) ); } @Test public void testIdentityDefault() { createTable (SCHEMA, "c", "cid int NOT NULL PRIMARY KEY generated by default as identity", "name varchar(32) NOT NULL"); TableName table = new TableName (SCHEMA, "c"); this.insertGenerator = new InsertGenerator (this.ais()); insertGenerator.setTypesRegistry(this.serviceManager().getServiceByClass(TypesRegistryService.class)); insertGenerator.setTypesTranslator(this.typesTranslator()); Operator insert = insertGenerator.create(table); assertEquals( "Project_Default(Field(0))\n" + " Insert_Returning(INTO c)\n" + " Project_Default(_SEQ_NEXT(test, c_cid_seq), NULL)\n" + " ValuesScan_Default([])", getExplain(insert, table.getSchemaName()) ); } @Test public void testIdentityAlways() { createTable (SCHEMA, "c", "cid int NOT NULL PRIMARY KEY generated always as identity", "name varchar(32) NOT NULL"); TableName table = new TableName (SCHEMA, "c"); this.insertGenerator = new InsertGenerator (this.ais()); insertGenerator.setTypesRegistry(this.serviceManager().getServiceByClass(TypesRegistryService.class)); insertGenerator.setTypesTranslator(this.typesTranslator()); Operator insert = insertGenerator.create(valueMap("c", "cid", "10"), table); assertEquals( "Project_Default(Field(0))\n" + " Insert_Returning(INTO c)\n" + " Project_Default(_SEQ_NEXT(test, c_cid_seq), NULL)\n" + " ValuesScan_Default(['10'])", getExplain(insert, table.getSchemaName()) ); } @Test public void testDefaults() { createTable (SCHEMA, "c", "cid int not null primary key default 0", "name varchar(32) not null default ''", "taxes double not null default '0.0'", "cdate date default current_date" ); TableName table = new TableName (SCHEMA, "c"); this.insertGenerator = new InsertGenerator (this.ais()); insertGenerator.setTypesRegistry(this.serviceManager().getServiceByClass(TypesRegistryService.class)); insertGenerator.setTypesTranslator(this.typesTranslator()); Operator insert = insertGenerator.create(table); assertEquals( "Project_Default(Field(0))\n" + " Insert_Returning(INTO c)\n" + " Project_Default(0, '', 0.000000e+00, CURRENT_DATE())\n" + " ValuesScan_Default([])", getExplain(insert, table.getSchemaName()) ); insert = insertGenerator.create(valueMap("c", "name", "foo", "cdate", "2014-02-03"), table); assertEquals( "Project_Default(Field(0))\n" + " Insert_Returning(INTO c)\n" + " Project_Default(0, Field(0), 0.000000e+00, Field(1))\n" + " ValuesScan_Default(['foo', '2014-02-03'])", getExplain(insert, table.getSchemaName()) ); } @Test public void testPKNotFirst() { createTable (SCHEMA, "c", "name varchar(32) not null", "address varchar(64) not null", "cid int not null primary key"); TableName table = new TableName (SCHEMA, "c"); this.insertGenerator = new InsertGenerator (this.ais()); insertGenerator.setTypesRegistry(this.serviceManager().getServiceByClass(TypesRegistryService.class)); insertGenerator.setTypesTranslator(this.typesTranslator()); Operator insert = insertGenerator.create(table); assertEquals( "Project_Default(Field(2))\n" + " Insert_Returning(INTO c)\n" + " Project_Default(NULL, NULL, NULL)\n" + " ValuesScan_Default([])", getExplain(insert, table.getSchemaName()) ); } @Test public void testPKMultiColumn() { createTable(SCHEMA, "o", "cid int not null", "oid int not null", "items int not null", "primary key (cid, oid)"); TableName table = new TableName (SCHEMA, "o"); this.insertGenerator = new InsertGenerator (this.ais()); insertGenerator.setTypesRegistry(this.serviceManager().getServiceByClass(TypesRegistryService.class)); insertGenerator.setTypesTranslator(this.typesTranslator()); Operator insert = insertGenerator.create(table); assertEquals( "Project_Default(Field(0), Field(1))\n" + " Insert_Returning(INTO o)\n" + " Project_Default(NULL, NULL, NULL)\n" + " ValuesScan_Default([])", getExplain(insert, table.getSchemaName()) ); } @Test public void testJoinedTable() { createTable(SCHEMA, "c", "cid int not null", "fist_name varchar(32)", "PRIMARY KEY(cid)"); createTable (SCHEMA, "a", "aid int not null", "cid int not null", "state char(2)", "PRIMARY KEY (aid)", "GROUPING FOREIGN KEY (cid) REFERENCES c(cid)"); TableName table = new TableName (SCHEMA, "a"); this.insertGenerator = new InsertGenerator (this.ais()); insertGenerator.setTypesRegistry(this.serviceManager().getServiceByClass(TypesRegistryService.class)); insertGenerator.setTypesTranslator(this.typesTranslator()); Operator insert = insertGenerator.create(table); assertEquals( "Project_Default(Field(0))\n" + " Insert_Returning(INTO a)\n" + " Project_Default(NULL, NULL, NULL)\n" + " ValuesScan_Default([])", getExplain(insert, table.getSchemaName()) ); } @Test public void testOrdersTable() { createTable(SCHEMA, "customers", "cid int not null", "first_name varchar(32)", "primary key (cid)"); createTable (SCHEMA, "orders", "oid int not null", "cid int not null", "odate datetime", "primary key (oid)", "grouping foreign key (cid) references customers(cid)"); TableName table = new TableName (SCHEMA, "orders"); this.insertGenerator = new InsertGenerator (this.ais()); insertGenerator.setTypesRegistry(this.serviceManager().getServiceByClass(TypesRegistryService.class)); insertGenerator.setTypesTranslator(this.typesTranslator()); Operator insert = insertGenerator.create(table); assertEquals( "Project_Default(Field(0))\n" + " Insert_Returning(INTO orders)\n" + " Project_Default(NULL, NULL, NULL)\n" + " ValuesScan_Default([])", getExplain(insert, table.getSchemaName()) ); } @Test public void testAllTypes() { createTable (SCHEMA, "all_types", "year_field year", "bigint_field bigint", "bigint_unsigned_field bigint unsigned", "blob_field blob", "boolean_field boolean", "char_field char", "char_multi_field char(32)", "clob_field clob", "date_field date", "decimal_field decimal(10,0)", "double_field double", "float_field float", "integer_field integer", "numeric_field numeric(10,0)", "real_field real", "smallint_field smallint", "time_field time", "timestamp_field timestamp", "varchar_field varchar(32)", "datetime_field datetime"); TableName table = new TableName (SCHEMA, "all_types"); this.insertGenerator = new InsertGenerator (this.ais()); insertGenerator.setTypesRegistry(this.serviceManager().getServiceByClass(TypesRegistryService.class)); insertGenerator.setTypesTranslator(this.typesTranslator()); Operator insert = insertGenerator.create(table); assertEquals( "Insert_Returning(INTO all_types)\n" + " Project_Default(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, _SEQ_NEXT(test, all_types___row_id_seq))\n" + " ValuesScan_Default([])", getExplain(insert, table.getSchemaName()) ); } protected String getExplain (Operator plannable, String defaultSchemaName) { StringBuilder str = new StringBuilder(); ExplainContext context = new ExplainContext(); // Empty DefaultFormatter f = new DefaultFormatter(defaultSchemaName); for (String operator : f.format(plannable.getExplainer(context))) { if(str.length() > 0) { str.append("\n"); } str.append(operator); } return str.toString(); } private Map<Column, String> valueMap(String tableName, String... colsAndValues) { assert (colsAndValues.length % 2) == 0; Table table = ais().getTable(SCHEMA, tableName); Map<Column, String> outMap = new LinkedHashMap<>(); for(int i = 0; i < colsAndValues.length; i += 2) { String col = colsAndValues[i]; String value = colsAndValues[i + 1]; outMap.put(table.getColumn(col), value); } return outMap; } }