├── LICENSE ├── Makefile ├── README.md ├── execQual.c ├── execScan.c ├── execTuples.c ├── execTuples.h ├── executor.h ├── expected └── vectorize_engine.out ├── nodeAgg.c ├── nodeAgg.h ├── nodeSeqscan.c ├── nodeSeqscan.h ├── nodeUnbatch.c ├── nodeUnbatch.h ├── plan.c ├── plan.h ├── sql └── vectorize_engine.sql ├── utils.c ├── utils.h ├── vectorEngine.c ├── vectorTupleSlot.c ├── vectorTupleSlot.h ├── vectorize_engine--1.0.sql ├── vectorize_engine.control └── vtype ├── vdate.c ├── vdate.h ├── vfloat.c ├── vfloat.h ├── vint.c ├── vint.h ├── vpseudotypes.c ├── vpseudotypes.h ├── vtimestamp.c ├── vtimestamp.h ├── vtype.c ├── vtype.h ├── vvarchar.c └── vvarchar.h /LICENSE: -------------------------------------------------------------------------------- 1 | PostgreSQL Database Management System 2 | (formerly known as Postgres, then as Postgres95) 3 | 4 | Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group 5 | 6 | Portions Copyright (c) 1994, The Regents of the University of California 7 | 8 | Permission to use, copy, modify, and distribute this software and its 9 | documentation for any purpose, without fee, and without a written agreement 10 | is hereby granted, provided that the above copyright notice and this 11 | paragraph and the following two paragraphs appear in all copies. 12 | 13 | IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR 14 | DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING 15 | LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS 16 | DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE 17 | POSSIBILITY OF SUCH DAMAGE. 18 | 19 | THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 20 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 | AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 22 | ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO 23 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MODULE_big = vectorize_engine 2 | 3 | EXTENSION = vectorize_engine 4 | DATA = vectorize_engine--1.0.sql 5 | 6 | PGFILEDESC = "vectorize engine for PostgreSQL" 7 | 8 | REGRESS = vectorize_engine 9 | 10 | OBJS += vectorEngine.o nodeSeqscan.o nodeAgg.o nodeUnbatch.o execScan.o plan.o utils.o execTuples.o execQual.o vectorTupleSlot.o 11 | OBJS += vtype/vtype.o vtype/vtimestamp.o vtype/vint.o vtype/vfloat.o vtype/vpseudotypes.o vtype/vvarchar.o vtype/vdate.o 12 | 13 | # print vectorize info when compile 14 | # PG_CFLAGS = -fopt-info-vec 15 | 16 | PG_CFLAGS = -Wno-int-in-bool-context 17 | PG_CONFIG = pg_config 18 | PGXS := $(shell $(PG_CONFIG) --pgxs) 19 | include $(PGXS) 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | Vectorize Execution is an extension for Postgres which utilize vectorized technique to accelerate query execution. 3 | 4 | Vectorize Execution is based on Postgres 9.6 now, and will support newer Postgres version soon. 5 | 6 | ## Design 7 | Below are features in our design. 8 | 1. Pure extension. We didn't hack any code into postgres kernel. 9 | 2. CustomScan node. We use CustomScan framework to replace original executor node such as SeqScan, Agg etc. 10 | Based on CustomScan, we could extend the CustomScanState, BeginCustomScan(), ExecCustomScan(), EndCustomScan() 11 | interface and implement vectorize executor node. 12 | 3. Post planner hook. After plan is generated, we use plan_tree_walker to traverse the plan tree and check whether 13 | it could be vectorized. If yes, we will replace non-vectorized node(seqscan, agg etc.) with vectorized node(in 14 | form of customscan node) and use vectorized executor. If no, we will revert to the original plan and use non-vectorized executor. 15 | 4. Inherit original executor code. Instead of rewriting the whole executor, we choose a more smooth method to modify 16 | current Postgres executor node and make it vectorized. We copy the current executor node into our extension, and 17 | add vectorize logic based on it. When Postgres enchance its executor, we could relatively easily merge them back. 18 | 5. Pluggable storage. Postgres has supported pluggable storage now. TupleTableSlot is refactored as abstract struct 19 | TupleTableSlotOps. We will implement VectorTupleTableSlot in our extension when we upgrade the extension to latest PG. 20 | 21 | ## Usage 22 | 1. Add GCC SIMD support option in configure when building Postgres. `-march=native` 23 | 2. Build & Install. `cd vectorize_engine; make install` 24 | 3. Config postgres.conf & Restart database. `shared_preload_libraries = 'vectorize_engine'` 25 | 4. Run test. `make installcheck` 26 | 5. Initialize at database level. `create extension vectorize_engine;` 27 | 6. Enable by GUC(default off). `set enable_vectorize_engine to on;` 28 | 29 | ## Performance 30 | We run TPC-H 10G Q1 on machine at GCP(24G memory, 8 Core Intel(R) Xeon(R) CPU @ 2.20GHz). 31 | 32 | standard PG run 50s and PG with vectorize engine version run 28s. 33 | 34 | lineitem is stored as heap table with schema is as follows 35 | ``` 36 | Table "public.lineitem" 37 | Column | Type | Modifiers 38 | -----------------+-----------------------+----------- 39 | l_orderkey | bigint | not null 40 | l_partkey | integer | not null 41 | l_suppkey | integer | not null 42 | l_linenumber | integer | not null 43 | l_quantity | double precision | not null 44 | l_extendedprice | double precision | not null 45 | l_discount | double precision | not null 46 | l_tax | double precision | not null 47 | l_returnflag | character(1) | not null 48 | l_linestatus | character(1) | not null 49 | l_shipdate | date | not null 50 | l_commitdate | date | not null 51 | l_receiptdate | date | not null 52 | l_shipinstruct | character(25) | not null 53 | l_shipmode | character(10) | not null 54 | l_comment | character varying(44) | not null 55 | ``` 56 | 57 | 58 | TPC-H Q1 is 59 | ``` 60 | select 61 | l_returnflag, 62 | l_linestatus, 63 | sum(l_quantity) as sum_qty, 64 | sum(l_extendedprice) as sum_base_price, 65 | sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, 66 | sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, 67 | avg(l_quantity) as avg_qty, 68 | avg(l_extendedprice) as avg_price, 69 | avg(l_discount) as avg_disc, 70 | count(l_discount) as count_order 71 | from 72 | lineitem1 73 | where 74 | l_shipdate <= date '1998-12-01' - interval '106 day' 75 | group by 76 | l_returnflag, 77 | l_linestatus; 78 | ``` 79 | 80 | -------------------------------------------------------------------------------- /execQual.c: -------------------------------------------------------------------------------- 1 | #include "postgres.h" 2 | 3 | #include "access/htup_details.h" 4 | #include "access/nbtree.h" 5 | #include "access/tupconvert.h" 6 | #include "catalog/objectaccess.h" 7 | #include "catalog/pg_type.h" 8 | #include "executor/execdebug.h" 9 | #include "executor/nodeSubplan.h" 10 | #include "funcapi.h" 11 | #include "miscadmin.h" 12 | #include "nodes/makefuncs.h" 13 | #include "nodes/nodeFuncs.h" 14 | #include "optimizer/planner.h" 15 | #include "parser/parse_coerce.h" 16 | #include "parser/parsetree.h" 17 | #include "pgstat.h" 18 | #include "utils/acl.h" 19 | #include "utils/builtins.h" 20 | #include "utils/lsyscache.h" 21 | #include "utils/memutils.h" 22 | #include "utils/typcache.h" 23 | #include "utils/xml.h" 24 | 25 | #include "executor.h" 26 | #include "execTuples.h" 27 | #include "vectorTupleSlot.h" 28 | 29 | /* ---------------------------------------------------------------- 30 | * ExecQual 31 | * 32 | * Evaluates a conjunctive boolean expression (qual list) and 33 | * returns true iff none of the subexpressions are false. 34 | * (We also return true if the list is empty.) 35 | * 36 | * If some of the subexpressions yield NULL but none yield FALSE, 37 | * then the result of the conjunction is NULL (ie, unknown) 38 | * according to three-valued boolean logic. In this case, 39 | * we return the value specified by the "resultForNull" parameter. 40 | * 41 | * Callers evaluating WHERE clauses should pass resultForNull=FALSE, 42 | * since SQL specifies that tuples with null WHERE results do not 43 | * get selected. On the other hand, callers evaluating constraint 44 | * conditions should pass resultForNull=TRUE, since SQL also specifies 45 | * that NULL constraint conditions are not failures. 46 | * 47 | * NOTE: it would not be correct to use this routine to evaluate an 48 | * AND subclause of a boolean expression; for that purpose, a NULL 49 | * result must be returned as NULL so that it can be properly treated 50 | * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). 51 | * This routine is only used in contexts where a complete expression 52 | * is being evaluated and we know that NULL can be treated the same 53 | * as one boolean result or the other. 54 | * 55 | * ---------------------------------------------------------------- 56 | */ 57 | bool 58 | VExecScanQual(List *qual, ExprContext *econtext, bool resultForNull) 59 | { 60 | MemoryContext oldContext; 61 | TupleTableSlot *slot; 62 | VectorTupleSlot *vslot; 63 | ListCell *l; 64 | int row; 65 | 66 | /* 67 | * debugging stuff 68 | */ 69 | EV_printf("ExecQual: qual is "); 70 | EV_nodeDisplay(qual); 71 | EV_printf("\n"); 72 | 73 | /* 74 | * Run in short-lived per-tuple context while computing expressions. 75 | */ 76 | oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); 77 | 78 | /* 79 | * Evaluate the qual conditions one at a time. If we find a FALSE result, 80 | * we can stop evaluating and return FALSE --- the AND result must be 81 | * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we 82 | * can stop and return FALSE --- the AND result must be FALSE or NULL in 83 | * that case, and the caller doesn't care which. 84 | * 85 | * If we get to the end of the list, we can return TRUE. This will happen 86 | * when the AND result is indeed TRUE, or when the AND result is NULL (one 87 | * or more NULL subresult, with all the rest TRUE) and the caller has 88 | * specified resultForNull = TRUE. 89 | */ 90 | 91 | slot = econtext->ecxt_scantuple; 92 | vslot = (VectorTupleSlot *)slot; 93 | foreach(l, qual) 94 | { 95 | ExprState *clause = (ExprState *) lfirst(l); 96 | Datum expr_value; 97 | bool isNull; 98 | vbool *expr_val_bools; 99 | 100 | /* take a batch as input to evaluate quals */ 101 | expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); 102 | 103 | expr_val_bools = (vbool *)DatumGetPointer(expr_value); 104 | 105 | /* using skip array to indicated row which didn't pass the qual */ 106 | for(row = 0; row < BATCHSIZE; row++) 107 | if((!expr_val_bools->isnull[row] || !resultForNull) && 108 | !DatumGetBool(expr_val_bools->values[row]) && 109 | !vslot->skip[row]) 110 | vslot->skip[row] = true; 111 | /* TODO: opt: add skipped count for vslot to support skipping the whole batch?*/ 112 | } 113 | 114 | MemoryContextSwitchTo(oldContext); 115 | 116 | /* return true if any tuple in batch pass the qual. */ 117 | for(row = 0; row < BATCHSIZE; row++) 118 | if (!vslot->skip[row]) 119 | return true; 120 | 121 | return false; 122 | } 123 | 124 | -------------------------------------------------------------------------------- /execScan.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * execScan.c 4 | * This code provides support for generalized relation scans. ExecScan 5 | * is passed a node and a pointer to a function to "do the right thing" 6 | * and return a tuple from the relation. ExecScan then does the tedious 7 | * stuff - checking the qualification and projecting the tuple 8 | * appropriately. 9 | * 10 | * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group 11 | * Portions Copyright (c) 1994, Regents of the University of California 12 | * 13 | * 14 | * IDENTIFICATION 15 | * src/backend/executor/execScan.c 16 | * 17 | *------------------------------------------------------------------------- 18 | */ 19 | #include "postgres.h" 20 | 21 | #include "executor/executor.h" 22 | #include "miscadmin.h" 23 | #include "utils/memutils.h" 24 | 25 | #include "executor.h" 26 | #include "nodeSeqscan.h" 27 | #include "vectorTupleSlot.h" 28 | 29 | /* 30 | * ExecScanFetch -- fetch next potential tuple 31 | * 32 | * This routine is concerned with substituting a test tuple if we are 33 | * inside an EvalPlanQual recheck. If we aren't, just execute 34 | * the access method's next-tuple routine. 35 | */ 36 | static inline TupleTableSlot * 37 | ExecScanFetch(VectorScanState *vss, 38 | VExecScanAccessMtd accessMtd, 39 | VExecScanRecheckMtd recheckMtd) 40 | { 41 | EState *estate; 42 | SeqScanState *node = vss->seqstate; 43 | 44 | estate = node->ss.ps.state; 45 | 46 | if (estate->es_epqTuple != NULL) 47 | { 48 | /* 49 | * We are inside an EvalPlanQual recheck. Return the test tuple if 50 | * one is available, after rechecking any access-method-specific 51 | * conditions. 52 | */ 53 | Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid; 54 | 55 | if (scanrelid == 0) 56 | { 57 | TupleTableSlot *slot = node->ss.ss_ScanTupleSlot; 58 | 59 | /* 60 | * This is a ForeignScan or CustomScan which has pushed down a 61 | * join to the remote side. The recheck method is responsible not 62 | * only for rechecking the scan/join quals but also for storing 63 | * the correct tuple in the slot. 64 | */ 65 | if (!(*recheckMtd) (vss, slot)) 66 | ExecClearTuple(slot); /* would not be returned by scan */ 67 | return slot; 68 | } 69 | else if (estate->es_epqTupleSet[scanrelid - 1]) 70 | { 71 | TupleTableSlot *slot = node->ss.ss_ScanTupleSlot; 72 | 73 | /* Return empty slot if we already returned a tuple */ 74 | if (estate->es_epqScanDone[scanrelid - 1]) 75 | return ExecClearTuple(slot); 76 | /* Else mark to remember that we shouldn't return more */ 77 | estate->es_epqScanDone[scanrelid - 1] = true; 78 | 79 | /* Return empty slot if we haven't got a test tuple */ 80 | if (estate->es_epqTuple[scanrelid - 1] == NULL) 81 | return ExecClearTuple(slot); 82 | 83 | /* Store test tuple in the plan node's scan slot */ 84 | ExecStoreTuple(estate->es_epqTuple[scanrelid - 1], 85 | slot, InvalidBuffer, false); 86 | 87 | /* Check if it meets the access-method conditions */ 88 | if (!(*recheckMtd) (vss, slot)) 89 | ExecClearTuple(slot); /* would not be returned by scan */ 90 | 91 | return slot; 92 | } 93 | } 94 | 95 | /* 96 | * Run the node-type-specific access method function to get the next tuple 97 | */ 98 | return (*accessMtd) (vss); 99 | } 100 | 101 | /* ---------------------------------------------------------------- 102 | * ExecScan 103 | * 104 | * Scans the relation using the 'access method' indicated and 105 | * returns the next qualifying tuple in the direction specified 106 | * in the global variable ExecDirection. 107 | * The access method returns the next tuple and ExecScan() is 108 | * responsible for checking the tuple returned against the qual-clause. 109 | * 110 | * A 'recheck method' must also be provided that can check an 111 | * arbitrary tuple of the relation against any qual conditions 112 | * that are implemented internal to the access method. 113 | * 114 | * Conditions: 115 | * -- the "cursor" maintained by the AMI is positioned at the tuple 116 | * returned previously. 117 | * 118 | * Initial States: 119 | * -- the relation indicated is opened for scanning so that the 120 | * "cursor" is positioned before the first qualifying tuple. 121 | * ---------------------------------------------------------------- 122 | */ 123 | TupleTableSlot * 124 | VExecScan(VectorScanState *vss, 125 | VExecScanAccessMtd accessMtd, /* function returning a tuple */ 126 | VExecScanRecheckMtd recheckMtd) 127 | { 128 | ExprContext *econtext; 129 | List *qual; 130 | ProjectionInfo *projInfo; 131 | ExprDoneCond isDone; 132 | TupleTableSlot *resultSlot; 133 | ScanState *node; 134 | 135 | node = &vss->seqstate->ss; 136 | 137 | /* 138 | * Fetch data from node 139 | */ 140 | qual = node->ps.qual; 141 | projInfo = node->ps.ps_ProjInfo; 142 | econtext = node->ps.ps_ExprContext; 143 | 144 | /* 145 | * If we have neither a qual to check nor a projection to do, just skip 146 | * all the overhead and return the raw scan tuple. 147 | */ 148 | if (!qual && !projInfo) 149 | { 150 | ResetExprContext(econtext); 151 | return ExecScanFetch(vss, accessMtd, recheckMtd); 152 | } 153 | 154 | /* 155 | * Check to see if we're still projecting out tuples from a previous scan 156 | * tuple (because there is a function-returning-set in the projection 157 | * expressions). If so, try to project another one. 158 | */ 159 | if (node->ps.ps_TupFromTlist) 160 | { 161 | Assert(projInfo); /* can't get here if not projecting */ 162 | resultSlot = ExecProject(projInfo, &isDone); 163 | if (isDone == ExprMultipleResult) 164 | return resultSlot; 165 | /* Done with that source tuple... */ 166 | node->ps.ps_TupFromTlist = false; 167 | } 168 | 169 | /* 170 | * Reset per-tuple memory context to free any expression evaluation 171 | * storage allocated in the previous tuple cycle. Note this can't happen 172 | * until we're done projecting out tuples from a scan tuple. 173 | */ 174 | ResetExprContext(econtext); 175 | 176 | /* 177 | * get a tuple from the access method. Loop until we obtain a tuple that 178 | * passes the qualification. 179 | */ 180 | for (;;) 181 | { 182 | TupleTableSlot *slot; 183 | 184 | CHECK_FOR_INTERRUPTS(); 185 | 186 | slot = ExecScanFetch(vss, accessMtd, recheckMtd); 187 | 188 | /* 189 | * if the slot returned by the accessMtd contains NULL, then it means 190 | * there is nothing more to scan so we just return an empty slot, 191 | * being careful to use the projection result slot so it has correct 192 | * tupleDesc. 193 | */ 194 | if (TupIsNull(slot)) 195 | { 196 | if (projInfo) 197 | return ExecClearTuple(projInfo->pi_slot); 198 | else 199 | return slot; 200 | } 201 | 202 | /* 203 | * place the current tuple into the expr context 204 | */ 205 | econtext->ecxt_scantuple = slot; 206 | 207 | /* 208 | * check that the current tuple satisfies the qual-clause 209 | * 210 | * check for non-nil qual here to avoid a function call to ExecQual() 211 | * when the qual is nil ... saves only a few cycles, but they add up 212 | * ... 213 | */ 214 | if (!qual || VExecScanQual(qual, econtext, false)) 215 | { 216 | /* 217 | * Found a satisfactory scan tuple. 218 | */ 219 | if (projInfo) 220 | { 221 | /* 222 | * Form a projection tuple, store it in the result tuple slot 223 | * and return it --- unless we find we can project no tuples 224 | * from this scan tuple, in which case continue scan. 225 | */ 226 | resultSlot = ExecProject(projInfo, &isDone); 227 | memcpy(((VectorTupleSlot*)resultSlot)->skip, ((VectorTupleSlot*)slot)->skip, sizeof(bool) * BATCHSIZE); 228 | if (isDone != ExprEndResult) 229 | { 230 | node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); 231 | return resultSlot; 232 | } 233 | } 234 | else 235 | { 236 | /* 237 | * Here, we aren't projecting, so just return scan tuple. 238 | */ 239 | return slot; 240 | } 241 | } 242 | else 243 | InstrCountFiltered1(node, 1); 244 | 245 | /* 246 | * Tuple fails qual, so free per-tuple memory and try again. 247 | */ 248 | ResetExprContext(econtext); 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /execTuples.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * execTuples.c 4 | * Routines dealing with TupleTableSlots. These are used for resource 5 | * management associated with tuples (eg, releasing buffer pins for 6 | * tuples in disk buffers, or freeing the memory occupied by transient 7 | * tuples). Slots also provide access abstraction that lets us implement 8 | * "virtual" tuples to reduce data-copying overhead. 9 | * 10 | * Routines dealing with the type information for tuples. Currently, 11 | * the type information for a tuple is an array of FormData_pg_attribute. 12 | * This information is needed by routines manipulating tuples 13 | * (getattribute, formtuple, etc.). 14 | * 15 | * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group 16 | * Portions Copyright (c) 1994, Regents of the University of California 17 | * 18 | * 19 | * IDENTIFICATION 20 | * src/backend/executor/execTuples.c 21 | * 22 | *------------------------------------------------------------------------- 23 | */ 24 | /* 25 | * INTERFACE ROUTINES 26 | * 27 | * SLOT CREATION/DESTRUCTION 28 | * MakeTupleTableSlot - create an empty slot 29 | * ExecAllocTableSlot - create a slot within a tuple table 30 | * ExecResetTupleTable - clear and optionally delete a tuple table 31 | * MakeSingleTupleTableSlot - make a standalone slot, set its descriptor 32 | * ExecDropSingleTupleTableSlot - destroy a standalone slot 33 | * 34 | * SLOT ACCESSORS 35 | * ExecSetSlotDescriptor - set a slot's tuple descriptor 36 | * ExecStoreTuple - store a physical tuple in the slot 37 | * ExecStoreMinimalTuple - store a minimal physical tuple in the slot 38 | * ExecClearTuple - clear contents of a slot 39 | * ExecStoreVirtualTuple - mark slot as containing a virtual tuple 40 | * ExecCopySlotTuple - build a physical tuple from a slot 41 | * ExecCopySlotMinimalTuple - build a minimal physical tuple from a slot 42 | * ExecMaterializeSlot - convert virtual to physical storage 43 | * ExecCopySlot - copy one slot's contents to another 44 | * 45 | * CONVENIENCE INITIALIZATION ROUTINES 46 | * ExecInitResultTupleSlot \ convenience routines to initialize 47 | * ExecInitScanTupleSlot \ the various tuple slots for nodes 48 | * ExecInitExtraTupleSlot / which store copies of tuples. 49 | * ExecInitNullTupleSlot / 50 | * 51 | * Routines that probably belong somewhere else: 52 | * ExecTypeFromTL - form a TupleDesc from a target list 53 | * 54 | * EXAMPLE OF HOW TABLE ROUTINES WORK 55 | * Suppose we have a query such as SELECT emp.name FROM emp and we have 56 | * a single SeqScan node in the query plan. 57 | * 58 | * At ExecutorStart() 59 | * ---------------- 60 | * - ExecInitSeqScan() calls ExecInitScanTupleSlot() and 61 | * ExecInitResultTupleSlot() to construct TupleTableSlots 62 | * for the tuples returned by the access methods and the 63 | * tuples resulting from performing target list projections. 64 | * 65 | * During ExecutorRun() 66 | * ---------------- 67 | * - SeqNext() calls ExecStoreTuple() to place the tuple returned 68 | * by the access methods into the scan tuple slot. 69 | * 70 | * - ExecSeqScan() calls ExecStoreTuple() to take the result 71 | * tuple from ExecProject() and place it into the result tuple slot. 72 | * 73 | * - ExecutePlan() calls the output function. 74 | * 75 | * The important thing to watch in the executor code is how pointers 76 | * to the slots containing tuples are passed instead of the tuples 77 | * themselves. This facilitates the communication of related information 78 | * (such as whether or not a tuple should be pfreed, what buffer contains 79 | * this tuple, the tuple's tuple descriptor, etc). It also allows us 80 | * to avoid physically constructing projection tuples in many cases. 81 | */ 82 | #include "postgres.h" 83 | 84 | #include "access/htup_details.h" 85 | #include "access/tuptoaster.h" 86 | #include "funcapi.h" 87 | #include "catalog/pg_type.h" 88 | #include "nodes/nodeFuncs.h" 89 | #include "storage/bufmgr.h" 90 | #include "utils/builtins.h" 91 | #include "utils/lsyscache.h" 92 | #include "utils/typcache.h" 93 | 94 | #include "execTuples.h" 95 | #include "vtype/vtype.h" 96 | #include "utils.h" 97 | #include "vectorTupleSlot.h" 98 | 99 | /* static vectorized functions */ 100 | static void VExecAssignResultType(PlanState *planstate, TupleDesc tupDesc); 101 | 102 | 103 | /* ---------------- 104 | * VExecInitResultTupleSlot 105 | * ---------------- 106 | */ 107 | void 108 | VExecInitResultTupleSlot(EState *estate, PlanState *planstate) 109 | { 110 | planstate->ps_ResultTupleSlot = VExecAllocTableSlot(&estate->es_tupleTable); 111 | } 112 | 113 | /* ---------------- 114 | * VExecInitScanTupleSlot 115 | * ---------------- 116 | */ 117 | void 118 | VExecInitScanTupleSlot(EState *estate, ScanState *scanstate) 119 | { 120 | scanstate->ss_ScanTupleSlot = VExecAllocTableSlot(&estate->es_tupleTable); 121 | } 122 | 123 | /* ---------------- 124 | * VExecInitExtraTupleSlot 125 | * ---------------- 126 | */ 127 | TupleTableSlot * 128 | VExecInitExtraTupleSlot(EState *estate) 129 | { 130 | return VExecAllocTableSlot(&estate->es_tupleTable); 131 | } 132 | 133 | 134 | /* ---------------- 135 | * VExecAssignResultTypeFromTL 136 | * ---------------- 137 | */ 138 | void 139 | VExecAssignResultTypeFromTL(PlanState *planstate) 140 | { 141 | bool hasoid; 142 | TupleDesc tupDesc; 143 | 144 | if (ExecContextForcesOids(planstate, &hasoid)) 145 | { 146 | /* context forces OID choice; hasoid is now set correctly */ 147 | } 148 | else 149 | { 150 | /* given free choice, don't leave space for OIDs in result tuples */ 151 | hasoid = false; 152 | } 153 | 154 | /* 155 | * ExecTypeFromTL needs the parse-time representation of the tlist, not a 156 | * list of ExprStates. This is good because some plan nodes don't bother 157 | * to set up planstate->targetlist ... 158 | */ 159 | tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid); 160 | VExecAssignResultType(planstate, tupDesc); 161 | } 162 | 163 | 164 | /* ---------------- 165 | * VExecAssignResultType 166 | * ---------------- 167 | */ 168 | static void 169 | VExecAssignResultType(PlanState *planstate, TupleDesc tupDesc) 170 | { 171 | TupleTableSlot *slot; 172 | 173 | slot = planstate->ps_ResultTupleSlot; 174 | 175 | ExecSetSlotDescriptor(slot, tupDesc); 176 | 177 | InitializeVectorSlotColumn((VectorTupleSlot *)slot); 178 | } 179 | -------------------------------------------------------------------------------- /execTuples.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR_ENGINE_EXEC_TUPLES_H 2 | #define VECTOR_ENGINE_EXEC_TUPLES_H 3 | 4 | #include "postgres.h" 5 | 6 | #include "executor/execdesc.h" 7 | #include "nodes/parsenodes.h" 8 | #include "executor/tuptable.h" 9 | #include "storage/buf.h" 10 | #include "vtype/vtype.h" 11 | 12 | /* 13 | * prototypes from functions in execTuples.c 14 | */ 15 | extern void VExecInitResultTupleSlot(EState *estate, PlanState *planstate); 16 | extern void VExecInitScanTupleSlot(EState *estate, ScanState *scanstate); 17 | extern TupleTableSlot *VExecInitExtraTupleSlot(EState *estate); 18 | extern void VExecAssignResultTypeFromTL(PlanState *planstate); 19 | #endif 20 | -------------------------------------------------------------------------------- /executor.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR_ENGINE_EXECUTOR_H 2 | #define VECTOR_ENGINE_EXECUTOR_H 3 | 4 | #include "postgres.h" 5 | #include "executor/execdesc.h" 6 | #include "nodes/parsenodes.h" 7 | 8 | #include "nodeSeqscan.h" 9 | 10 | extern bool VExecScanQual(List *qual, ExprContext *econtext, bool resultForNull); 11 | /* 12 | * prototypes from functions in execScan.c 13 | */ 14 | typedef TupleTableSlot *(*VExecScanAccessMtd) (VectorScanState *node); 15 | typedef bool (*VExecScanRecheckMtd) (VectorScanState *node, TupleTableSlot *slot); 16 | 17 | TupleTableSlot * 18 | VExecScan(VectorScanState* node, VExecScanAccessMtd accessMtd, 19 | VExecScanRecheckMtd recheckMtd); 20 | #endif 21 | -------------------------------------------------------------------------------- /expected/vectorize_engine.out: -------------------------------------------------------------------------------- 1 | -- 2 | -- Regression Tests for Custom Plan APIs 3 | -- 4 | -- construction of test data 5 | CREATE TABLE t1 ( 6 | a int, 7 | b double precision 8 | ); 9 | INSERT INTO t1 SELECT generate_series(1,3), 2.3; 10 | INSERT INTO t1 SELECT generate_series(1,3), 3.3; 11 | INSERT INTO t1 SELECT generate_series(1,3), 4.3; 12 | VACUUM ANALYZE t1; 13 | create extension vectorize_engine; 14 | SET enable_vectorize_engine TO on; 15 | SELECT * FROM t1; 16 | a | b 17 | ---+----- 18 | 1 | 2.3 19 | 2 | 2.3 20 | 3 | 2.3 21 | 1 | 3.3 22 | 2 | 3.3 23 | 3 | 3.3 24 | 1 | 4.3 25 | 2 | 4.3 26 | 3 | 4.3 27 | (9 rows) 28 | 29 | SELECT b FROM t1; 30 | b 31 | ----- 32 | 2.3 33 | 2.3 34 | 2.3 35 | 3.3 36 | 3.3 37 | 3.3 38 | 4.3 39 | 4.3 40 | 4.3 41 | (9 rows) 42 | 43 | SELECT b+1 FROM t1; 44 | ?column? 45 | ---------- 46 | 3.3 47 | 3.3 48 | 3.3 49 | 4.3 50 | 4.3 51 | 4.3 52 | 5.3 53 | 5.3 54 | 5.3 55 | (9 rows) 56 | 57 | SELECT count(b) FROM t1; 58 | count 59 | ------- 60 | 9 61 | (1 row) 62 | 63 | SELECT a, sum(b), avg(b) FROM t1 group by a; 64 | a | sum | avg 65 | ---+-----+----- 66 | 1 | 9.9 | 3.3 67 | 2 | 9.9 | 3.3 68 | 3 | 9.9 | 3.3 69 | (3 rows) 70 | 71 | SELECT a, sum(b), avg(b) FROM t1 where a < 3 group by a; 72 | a | sum | avg 73 | ---+-----+----- 74 | 1 | 9.9 | 3.3 75 | 2 | 9.9 | 3.3 76 | (2 rows) 77 | 78 | drop extension vectorize_engine; 79 | -------------------------------------------------------------------------------- /nodeAgg.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * nodeAgg.h 4 | * TODO file description 5 | * 6 | * 7 | * Copyright (c) 2019-Present Pivotal Software, Inc. 8 | * 9 | * 10 | *------------------------------------------------------------------------- 11 | */ 12 | 13 | #ifndef VECTOR_ENGINE_NODE_AGG_H 14 | #define VECTOR_ENGINE_NODE_AGG_H 15 | 16 | #include "nodes/plannodes.h" 17 | 18 | /* 19 | * VectorAggState - state object of vectoragg on executor. 20 | */ 21 | typedef struct VectorAggState 22 | { 23 | CustomScanState css; 24 | 25 | /* Attributes for vectorization */ 26 | AggState *aggstate; 27 | TupleTableSlot *resultSlot; 28 | } VectorAggState; 29 | 30 | extern CustomScan *MakeCustomScanForAgg(void); 31 | extern void InitVectorAgg(void); 32 | 33 | #endif /* VECTOR_ENGINE_NODE_AGG_H */ 34 | -------------------------------------------------------------------------------- /nodeSeqscan.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * nodeSeqscan.c 4 | * Support routines for sequential scans of relations. 5 | * 6 | * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group 7 | * Portions Copyright (c) 1994, Regents of the University of California 8 | * 9 | * 10 | * IDENTIFICATION 11 | * src/backend/executor/nodeSeqscan.c 12 | * 13 | *------------------------------------------------------------------------- 14 | */ 15 | /* 16 | * INTERFACE ROUTINES 17 | * ExecSeqScan sequentially scans a relation. 18 | * ExecSeqNext retrieve next tuple in sequential order. 19 | * ExecInitSeqScan creates and initializes a seqscan node. 20 | * ExecEndSeqScan releases any storage allocated. 21 | * ExecReScanSeqScan rescans the relation 22 | * 23 | * ExecSeqScanEstimate estimates DSM space needed for parallel scan 24 | * ExecSeqScanInitializeDSM initialize DSM for parallel scan 25 | * ExecSeqScanInitializeWorker attach to DSM info in parallel worker 26 | */ 27 | #include "postgres.h" 28 | 29 | #include "access/relscan.h" 30 | #include "executor/execdebug.h" 31 | #include "executor/nodeSeqscan.h" 32 | #include "utils/rel.h" 33 | 34 | /*-------------------------- Vectorize part of nodeSeqScan ---------------------------------*/ 35 | #include "nodes/extensible.h" 36 | #include "executor/nodeCustom.h" 37 | #include "utils/memutils.h" 38 | 39 | #include "executor.h" 40 | #include "execTuples.h" 41 | #include "nodeSeqscan.h" 42 | #include "vectorTupleSlot.h" 43 | #include "utils.h" 44 | #include "vtype/vtype.h" 45 | 46 | 47 | /* CustomScanMethods */ 48 | static Node *CreateVectorScanState(CustomScan *custom_plan); 49 | 50 | /* CustomScanExecMethods */ 51 | static void BeginVectorScan(CustomScanState *node, EState *estate, int eflags); 52 | static void ReScanVectorScan(CustomScanState *node); 53 | static TupleTableSlot *ExecVectorScan(CustomScanState *node); 54 | static void EndVectorScan(CustomScanState *node); 55 | 56 | static SeqScanState *VExecInitSeqScan(SeqScan *node, EState *estate, int eflags); 57 | static TupleTableSlot *VExecSeqScan(VectorScanState *vss); 58 | static void VExecEndSeqScan(VectorScanState *vss); 59 | static void VExecReScanSeqScan(VectorScanState *vss); 60 | 61 | static void VInitScanRelation(SeqScanState *node, EState *estate, int eflags); 62 | static TupleTableSlot *VSeqNext(VectorScanState *vss); 63 | static bool VSeqRecheck(VectorScanState *node, TupleTableSlot *slot); 64 | 65 | static CustomScanMethods vectorscan_scan_methods = { 66 | "vectorscan", /* CustomName */ 67 | CreateVectorScanState, /* CreateCustomScanState */ 68 | }; 69 | 70 | static CustomExecMethods vectorscan_exec_methods = { 71 | "vectorscan", /* CustomName */ 72 | BeginVectorScan, /* BeginCustomScan */ 73 | ExecVectorScan, /* ExecCustomScan */ 74 | EndVectorScan, /* EndCustomScan */ 75 | ReScanVectorScan, /* ReScanCustomScan */ 76 | NULL, /* MarkPosCustomScan */ 77 | NULL, /* RestrPosCustomScan */ 78 | NULL, /* EstimateDSMCustomScan */ 79 | NULL, /* InitializeDSMCustomScan */ 80 | NULL, /* InitializeWorkerCustomScan */ 81 | NULL, /* ExplainCustomScan */ 82 | }; 83 | 84 | 85 | /* 86 | * CreateVectorScanState - A method of CustomScan; that populate a custom 87 | * object being delivered from CustomScanState type, according to the 88 | * supplied CustomPath object. 89 | * 90 | */ 91 | static Node * 92 | CreateVectorScanState(CustomScan *custom_plan) 93 | { 94 | VectorScanState *vss = MemoryContextAllocZero(CurTransactionContext, 95 | sizeof(VectorScanState)); 96 | /* Set tag and executor callbacks */ 97 | NodeSetTag(vss, T_CustomScanState); 98 | 99 | vss->css.methods = &vectorscan_exec_methods; 100 | 101 | return (Node *) vss; 102 | } 103 | 104 | /* 105 | * BeginVectorScan - A method of CustomScanState; that initializes 106 | * the supplied VectorScanState object, at beginning of the executor. 107 | * 108 | */ 109 | static void 110 | BeginVectorScan(CustomScanState *css, EState *estate, int eflags) 111 | { 112 | VectorScanState *vss; 113 | CustomScan *cscan; 114 | SeqScan *node; 115 | 116 | /* clear state initialized in ExecInitCustomScan */ 117 | ClearCustomScanState(css); 118 | 119 | cscan = (CustomScan *)css->ss.ps.plan; 120 | node = (SeqScan *)linitial(cscan->custom_plans); 121 | 122 | vss = (VectorScanState*)css; 123 | vss->scanFinish = false; 124 | 125 | vss->seqstate = VExecInitSeqScan(node, estate, eflags); 126 | 127 | vss->css.ss.ps.ps_ResultTupleSlot = vss->seqstate->ss.ps.ps_ResultTupleSlot; 128 | } 129 | 130 | /* 131 | * ReScanVectorScan - A method of CustomScanState; that rewind the current 132 | * seek position. 133 | * 134 | * Derived from ExecReScanSeqScan(). 135 | */ 136 | static void 137 | ReScanVectorScan(CustomScanState *node) 138 | { 139 | VExecReScanSeqScan((VectorScanState *)node); 140 | } 141 | 142 | 143 | 144 | /* 145 | * ExecVectorScan - A method of CustomScanState; that fetches a tuple 146 | * from the relation, if exist anymore. 147 | * 148 | * Derived from ExecSeqScan(). 149 | */ 150 | static TupleTableSlot * 151 | ExecVectorScan(CustomScanState *node) 152 | { 153 | return VExecSeqScan((VectorScanState *)node); 154 | } 155 | 156 | /* 157 | * CTidEndCustomScan - A method of CustomScanState; that closes heap and 158 | * scan descriptor, and release other related resources. 159 | * 160 | * Derived from ExecEndSeqScan(). 161 | */ 162 | static void 163 | EndVectorScan(CustomScanState *node) 164 | { 165 | VExecEndSeqScan((VectorScanState *)node); 166 | } 167 | 168 | /* 169 | * Interface to get the custom scan plan for vector scan 170 | */ 171 | CustomScan * 172 | MakeCustomScanForSeqScan(void) 173 | { 174 | CustomScan *cscan = (CustomScan *)makeNode(CustomScan); 175 | cscan->methods = &vectorscan_scan_methods; 176 | 177 | return cscan; 178 | } 179 | 180 | /* 181 | * Initialize vectorscan CustomScan node. 182 | */ 183 | void 184 | InitVectorScan(void) 185 | { 186 | /* Register a vscan type of custom scan node */ 187 | RegisterCustomScanMethods(&vectorscan_scan_methods); 188 | } 189 | 190 | /*---------------------- End of vectorized part of nodeSeqscan ---------------------------*/ 191 | 192 | 193 | /* ---------------------------------------------------------------- 194 | * Scan Support 195 | * ---------------------------------------------------------------- 196 | */ 197 | 198 | /* ---------------------------------------------------------------- 199 | * SeqNext 200 | * 201 | * This is a workhorse for ExecSeqScan 202 | * ---------------------------------------------------------------- 203 | */ 204 | static TupleTableSlot * 205 | VSeqNext(VectorScanState *vss) 206 | { 207 | HeapTuple tuple; 208 | HeapScanDesc scandesc; 209 | EState *estate; 210 | ScanDirection direction; 211 | TupleTableSlot *slot; 212 | VectorTupleSlot *vslot; 213 | int row; 214 | SeqScanState *node = vss->seqstate; 215 | 216 | /* 217 | * get information from the estate and scan state 218 | */ 219 | scandesc = node->ss.ss_currentScanDesc; 220 | estate = node->ss.ps.state; 221 | direction = estate->es_direction; 222 | slot = node->ss.ss_ScanTupleSlot; 223 | 224 | if (scandesc == NULL) 225 | { 226 | /* 227 | * We reach here if the scan is not parallel, or if we're serially 228 | * executing a scan that was planned to be parallel. 229 | */ 230 | scandesc = heap_beginscan(node->ss.ss_currentRelation, 231 | estate->es_snapshot, 232 | 0, NULL); 233 | node->ss.ss_currentScanDesc = scandesc; 234 | } 235 | 236 | vslot = (VectorTupleSlot *)slot; 237 | 238 | /* return the last batch. */ 239 | if (vss->scanFinish) 240 | { 241 | VExecClearTuple(slot); 242 | return slot; 243 | } 244 | VExecClearTuple(slot); 245 | 246 | /* fetch a batch of rows and fill them into VectorTupleSlot */ 247 | for (row = 0 ; row < BATCHSIZE; row++) 248 | { 249 | /* 250 | * get the next tuple from the table 251 | */ 252 | tuple = heap_getnext(scandesc, direction); 253 | 254 | /* 255 | * save the tuple and the buffer returned to us by the access methods in 256 | * our scan tuple slot and return the slot. Note: we pass 'false' because 257 | * tuples returned by heap_getnext() are pointers onto disk pages and were 258 | * not created with palloc() and so should not be pfree()'d. Note also 259 | * that ExecStoreTuple will increment the refcount of the buffer; the 260 | * refcount will not be dropped until the tuple table slot is cleared. 261 | */ 262 | if (tuple) 263 | VExecStoreTuple(tuple, /* tuple to store */ 264 | slot, /* slot to store in */ 265 | scandesc->rs_cbuf, /* buffer associated with this 266 | * tuple */ 267 | false); /* don't pfree this pointer */ 268 | else 269 | { 270 | /* scan finish, but we still need to emit current vslot */ 271 | vss->scanFinish = true; 272 | break; 273 | } 274 | } 275 | 276 | /* 277 | * deform and generate virtual tuple 278 | * TODO: late deform to avoid deform unneccessary columns. 279 | */ 280 | if (row > 0) 281 | { 282 | vslot->dim = row; 283 | memset(vslot->skip, false, sizeof(bool) * row); 284 | 285 | /* deform the vector slot now */ 286 | Vslot_getallattrs(slot); 287 | ExecStoreVirtualTuple(slot); 288 | } 289 | 290 | return slot; 291 | } 292 | 293 | /* 294 | * SeqRecheck -- access method routine to recheck a tuple in EvalPlanQual 295 | */ 296 | static bool 297 | VSeqRecheck(VectorScanState *node, TupleTableSlot *slot) 298 | { 299 | /* 300 | * Note that unlike IndexScan, SeqScan never use keys in heap_beginscan 301 | * (and this is very bad) - so, here we do not check are keys ok or not. 302 | */ 303 | return true; 304 | } 305 | 306 | /* ---------------------------------------------------------------- 307 | * ExecSeqScan(node) 308 | * 309 | * Scans the relation sequentially and returns the next qualifying 310 | * tuple. 311 | * We call the ExecScan() routine and pass it the appropriate 312 | * access method functions. 313 | * ---------------------------------------------------------------- 314 | */ 315 | static TupleTableSlot * 316 | VExecSeqScan(VectorScanState *node) 317 | { 318 | return VExecScan(node, 319 | (VExecScanAccessMtd) VSeqNext, 320 | (VExecScanRecheckMtd) VSeqRecheck); 321 | } 322 | 323 | /* ---------------------------------------------------------------- 324 | * InitScanRelation 325 | * 326 | * Set up to access the scan relation. 327 | * ---------------------------------------------------------------- 328 | */ 329 | static void 330 | VInitScanRelation(SeqScanState *node, EState *estate, int eflags) 331 | { 332 | Relation currentRelation; 333 | TupleDesc vdesc; 334 | TupleTableSlot *slot; 335 | int i; 336 | 337 | /* 338 | * get the relation object id from the relid'th entry in the range table, 339 | * open that relation and acquire appropriate lock on it. 340 | */ 341 | currentRelation = ExecOpenScanRelation(estate, 342 | ((SeqScan *) node->ss.ps.plan)->scanrelid, 343 | eflags); 344 | 345 | node->ss.ss_currentRelation = currentRelation; 346 | 347 | /* 348 | * since we will change the attr type of tuple desc to vector 349 | * type. we need to copy it to avoid dirty the relcache 350 | */ 351 | vdesc = CreateTupleDescCopyConstr(RelationGetDescr(currentRelation)); 352 | 353 | /* change the attr type of tuple desc to vector type */ 354 | for (i = 0; i < vdesc->natts; i++) 355 | { 356 | Form_pg_attribute attr = vdesc->attrs[i]; 357 | Oid vtypid = GetVtype(attr->atttypid); 358 | if (vtypid != InvalidOid) 359 | attr->atttypid = vtypid; 360 | else 361 | elog(ERROR, "cannot find vectorized type for type %d", 362 | attr->atttypid); 363 | } 364 | 365 | /* and report the scan tuple slot's rowtype */ 366 | ExecAssignScanType(&node->ss, vdesc); 367 | 368 | slot = node->ss.ss_ScanTupleSlot; 369 | InitializeVectorSlotColumn((VectorTupleSlot *)slot); 370 | } 371 | 372 | /* ---------------------------------------------------------------- 373 | * ExecInitSeqScan 374 | * ---------------------------------------------------------------- 375 | */ 376 | static SeqScanState * 377 | VExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 378 | { 379 | SeqScanState *scanstate; 380 | 381 | /* 382 | * Once upon a time it was possible to have an outerPlan of a SeqScan, but 383 | * not any more. 384 | */ 385 | Assert(outerPlan(node) == NULL); 386 | Assert(innerPlan(node) == NULL); 387 | 388 | /* 389 | * create state structure 390 | */ 391 | scanstate = makeNode(SeqScanState); 392 | scanstate->ss.ps.plan = (Plan *) node; 393 | scanstate->ss.ps.state = estate; 394 | 395 | /* 396 | * Miscellaneous initialization 397 | * 398 | * create expression context for node 399 | */ 400 | ExecAssignExprContext(estate, &scanstate->ss.ps); 401 | 402 | /* 403 | * initialize child expressions 404 | */ 405 | scanstate->ss.ps.targetlist = (List *) 406 | ExecInitExpr((Expr *) node->plan.targetlist, 407 | (PlanState *) scanstate); 408 | scanstate->ss.ps.qual = (List *) 409 | ExecInitExpr((Expr *) node->plan.qual, 410 | (PlanState *) scanstate); 411 | 412 | /* 413 | * tuple table initialization 414 | */ 415 | VExecInitResultTupleSlot(estate, &scanstate->ss.ps); 416 | VExecInitScanTupleSlot(estate, &scanstate->ss); 417 | 418 | /* 419 | * initialize scan relation 420 | */ 421 | VInitScanRelation(scanstate, estate, eflags); 422 | 423 | scanstate->ss.ps.ps_TupFromTlist = false; 424 | 425 | /* 426 | * Initialize result tuple type and projection info. 427 | */ 428 | VExecAssignResultTypeFromTL(&scanstate->ss.ps); 429 | ExecAssignScanProjectionInfo(&scanstate->ss); 430 | 431 | return scanstate; 432 | } 433 | 434 | /* ---------------------------------------------------------------- 435 | * ExecEndSeqScan 436 | * 437 | * frees any storage allocated through C routines. 438 | * ---------------------------------------------------------------- 439 | */ 440 | static void 441 | VExecEndSeqScan(VectorScanState *vss) 442 | { 443 | Relation relation; 444 | HeapScanDesc scanDesc; 445 | SeqScanState *node = vss->seqstate; 446 | 447 | /* 448 | * get information from node 449 | */ 450 | relation = node->ss.ss_currentRelation; 451 | scanDesc = node->ss.ss_currentScanDesc; 452 | 453 | /* 454 | * Free the exprcontext 455 | */ 456 | ExecFreeExprContext(&node->ss.ps); 457 | 458 | /* 459 | * clean out the tuple table 460 | */ 461 | VExecClearTuple(node->ss.ps.ps_ResultTupleSlot); 462 | VExecClearTuple(node->ss.ss_ScanTupleSlot); 463 | 464 | /* 465 | * close heap scan 466 | */ 467 | if (scanDesc != NULL) 468 | heap_endscan(scanDesc); 469 | 470 | /* 471 | * close the heap relation. 472 | */ 473 | ExecCloseScanRelation(relation); 474 | } 475 | 476 | /* ---------------------------------------------------------------- 477 | * Join Support 478 | * ---------------------------------------------------------------- 479 | */ 480 | 481 | /* ---------------------------------------------------------------- 482 | * ExecReScanSeqScan 483 | * 484 | * Rescans the relation. 485 | * ---------------------------------------------------------------- 486 | */ 487 | static void 488 | VExecReScanSeqScan(VectorScanState *vss) 489 | { 490 | elog(ERROR, "vectorize rescan not implemented yet."); 491 | } 492 | -------------------------------------------------------------------------------- /nodeSeqscan.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * nodescan.h 4 | * 5 | *------------------------------------------------------------------------- 6 | */ 7 | 8 | #ifndef VECTOR_ENGINE_NODE_SCAN_H 9 | #define VECTOR_ENGINE_NODE_SCAN_H 10 | 11 | #include "nodes/execnodes.h" 12 | #include "nodes/plannodes.h" 13 | 14 | /* 15 | * VectorScanState - state object of vectorscan on executor. 16 | */ 17 | typedef struct VectorScanState 18 | { 19 | CustomScanState css; 20 | 21 | /* Attributes for vectorization */ 22 | SeqScanState *seqstate; 23 | bool scanFinish; 24 | } VectorScanState; 25 | 26 | extern CustomScan *MakeCustomScanForSeqScan(void); 27 | extern void InitVectorScan(void); 28 | 29 | #endif /* VECTOR_ENGINE_SCAN_H */ 30 | -------------------------------------------------------------------------------- /nodeUnbatch.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * unbatch.c 4 | * 5 | * Copyright (c) 1996-2019, PostgreSQL Global Development Group 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #include "postgres.h" 11 | 12 | #include "fmgr.h" 13 | #include "optimizer/planner.h" 14 | #include "executor/nodeCustom.h" 15 | 16 | #include "nodeUnbatch.h" 17 | #include "execTuples.h" 18 | #include "vtype/vtype.h" 19 | #include "utils.h" 20 | #include "vectorTupleSlot.h" 21 | 22 | 23 | /* 24 | * UnbatchState - state object of vectorscan on executor. 25 | */ 26 | typedef struct UnbatchState 27 | { 28 | CustomScanState css; 29 | 30 | TupleTableSlot *ps_ResultVTupleSlot; /* slot for my result tuples */ 31 | int iter; 32 | 33 | /* Attributes for vectorization */ 34 | } UnbatchState; 35 | 36 | static Node *CreateUnbatchState(CustomScan *custom_plan); 37 | static TupleTableSlot *FetchRowFromBatch(UnbatchState *ubs); 38 | static bool ReadNextVectorSlot(UnbatchState *ubs); 39 | /* CustomScanExecMethods */ 40 | static void BeginUnbatch(CustomScanState *node, EState *estate, int eflags); 41 | static TupleTableSlot *ExecUnbatch(CustomScanState *node); 42 | static void EndUnbatch(CustomScanState *node); 43 | 44 | static CustomScanMethods unbatch_methods = { 45 | "unbatch", /* CustomName */ 46 | CreateUnbatchState, /* CreateCustomScanState */ 47 | }; 48 | 49 | static CustomExecMethods unbatch_exec_methods = { 50 | "unbatch", /* CustomName */ 51 | BeginUnbatch, /* BeginCustomScan */ 52 | ExecUnbatch, /* ExecCustomScan */ 53 | EndUnbatch, /* EndCustomScan */ 54 | NULL, /* ReScanCustomScan */ 55 | NULL, /* MarkPosCustomScan */ 56 | NULL, /* RestrPosCustomScan */ 57 | NULL, /* EstimateDSMCustomScan */ 58 | NULL, /* InitializeDSMCustomScan */ 59 | NULL, /* InitializeWorkerCustomScan */ 60 | NULL, /* ExplainCustomScan */ 61 | }; 62 | 63 | static void 64 | BeginUnbatch(CustomScanState *node, EState *estate, int eflags) 65 | { 66 | UnbatchState *vcs = (UnbatchState*) node; 67 | CustomScan *cscan = (CustomScan *) node->ss.ps.plan; 68 | TupleDesc tupdesc; 69 | 70 | outerPlanState(vcs) = ExecInitNode(outerPlan(cscan), estate, eflags); 71 | 72 | /* Convert Vtype in tupdesc to Ntype in unbatch Node */ 73 | { 74 | node->ss.ps.ps_ResultTupleSlot->tts_tupleDescriptor = CreateTupleDescCopy(outerPlanState(vcs)->ps_ResultTupleSlot->tts_tupleDescriptor); 75 | tupdesc = node->ss.ps.ps_ResultTupleSlot->tts_tupleDescriptor; 76 | 77 | for (int i = 0; i < tupdesc->natts; i++) 78 | { 79 | Form_pg_attribute attr = tupdesc->attrs[i]; 80 | Oid typid = GetNtype(attr->atttypid); 81 | if (typid != InvalidOid) 82 | attr->atttypid = typid; 83 | } 84 | ExecSetSlotDescriptor(node->ss.ps.ps_ResultTupleSlot, tupdesc); 85 | } 86 | 87 | vcs->ps_ResultVTupleSlot = VExecInitExtraTupleSlot(estate); 88 | vcs->ps_ResultVTupleSlot->tts_tupleDescriptor = CreateTupleDescCopy(outerPlanState(vcs)->ps_ResultTupleSlot->tts_tupleDescriptor); 89 | } 90 | 91 | static TupleTableSlot* 92 | FetchRowFromBatch(UnbatchState *ubs) 93 | { 94 | VectorTupleSlot *vslot; 95 | TupleTableSlot *slot; 96 | int iter; 97 | int natts; 98 | int i; 99 | 100 | 101 | slot = ubs->css.ss.ps.ps_ResultTupleSlot; 102 | vslot = (VectorTupleSlot *)ubs->ps_ResultVTupleSlot; 103 | iter = ubs->iter; 104 | 105 | while(iter < BATCHSIZE && vslot->skip[iter]) 106 | iter++; 107 | 108 | /* we have checked that natts is greater than zero */ 109 | if (iter == BATCHSIZE) 110 | return NULL; 111 | 112 | ExecClearTuple(slot); 113 | natts = slot->tts_tupleDescriptor->natts; 114 | for(i = 0; i < natts; i++) 115 | { 116 | slot->tts_values[i] = ((vtype*)(vslot->tts.tts_values[i]))->values[iter]; 117 | slot->tts_isnull[i] = false; 118 | } 119 | 120 | ubs->iter = ++iter; 121 | return ExecStoreVirtualTuple(slot); 122 | } 123 | 124 | /* 125 | * 126 | */ 127 | static TupleTableSlot * 128 | ExecUnbatch(CustomScanState *node) 129 | { 130 | UnbatchState *ubs; 131 | TupleTableSlot *slot; 132 | 133 | ubs = (UnbatchState*) node; 134 | /* find a non skip tuple and return to client */ 135 | while(true) 136 | { 137 | /* 138 | * iter = 0 indicate we finish unbatching the vector slot 139 | * and need to read next vector slot 140 | */ 141 | slot = FetchRowFromBatch(ubs); 142 | if(slot) 143 | break; 144 | 145 | /* finish current batch, read next batch */ 146 | if (!ReadNextVectorSlot(ubs)) 147 | return NULL; 148 | } 149 | 150 | return slot; 151 | } 152 | 153 | static bool 154 | ReadNextVectorSlot(UnbatchState *ubs) 155 | { 156 | TupleTableSlot *slot; 157 | 158 | slot = ExecProcNode(ubs->css.ss.ps.lefttree); 159 | if(TupIsNull(slot)) 160 | return false; 161 | 162 | /* Make sure the tuple is fully deconstructed */ 163 | slot_getallattrs(slot); 164 | 165 | ubs->ps_ResultVTupleSlot = slot; 166 | ubs->iter = 0; 167 | return true; 168 | } 169 | /* 170 | * 171 | */ 172 | static void 173 | EndUnbatch(CustomScanState *node) 174 | { 175 | PlanState *outerPlan; 176 | outerPlan = outerPlanState(node); 177 | ExecEndNode(outerPlan); 178 | } 179 | 180 | 181 | static Node * 182 | CreateUnbatchState(CustomScan *custom_plan) 183 | { 184 | UnbatchState *vss = palloc0(sizeof(UnbatchState)); 185 | 186 | NodeSetTag(vss, T_CustomScanState); 187 | vss->css.methods = &unbatch_exec_methods; 188 | 189 | return (Node *) &vss->css; 190 | } 191 | 192 | 193 | /* 194 | * Add unbatch Node at top to make batch to tuple 195 | */ 196 | Plan * 197 | AddUnbatchNodeAtTop(Plan *node) 198 | { 199 | CustomScan *convert = makeNode(CustomScan); 200 | convert->methods = &unbatch_methods; 201 | convert->scan.plan.lefttree = node; 202 | convert->scan.plan.righttree = NULL; 203 | return &convert->scan.plan; 204 | } 205 | 206 | /* 207 | * Initialize vectorscan CustomScan node. 208 | */ 209 | void 210 | InitUnbatch(void) 211 | { 212 | RegisterCustomScanMethods(&unbatch_methods); 213 | } 214 | -------------------------------------------------------------------------------- /nodeUnbatch.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * vconvert.h 4 | * TODO file description 5 | * 6 | * 7 | * Copyright (c) 2019-Present Pivotal Software, Inc. 8 | * 9 | * 10 | *------------------------------------------------------------------------- 11 | */ 12 | 13 | #ifndef VCONVERT_H 14 | #define VCONVERT_H 15 | 16 | extern Plan *AddUnbatchNodeAtTop(Plan *node); 17 | extern void InitUnbatch(void); 18 | 19 | #endif /* GPVECTOR_H */ 20 | -------------------------------------------------------------------------------- /plan.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * plan.c 4 | * 5 | * Copyright (c) 1996-2019, PostgreSQL Global Development Group 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | #include "postgres.h" 10 | #include "access/htup.h" 11 | #include "catalog/pg_collation.h" 12 | #include "catalog/pg_operator.h" 13 | #include "catalog/pg_type.h" 14 | #include "catalog/pg_proc.h" 15 | #include "miscadmin.h" 16 | #include "access/htup_details.h" 17 | #include "nodes/nodeFuncs.h" 18 | #include "optimizer/var.h" 19 | #include "parser/parse_oper.h" 20 | #include "parser/parse_func.h" 21 | #include "parser/parse_coerce.h" 22 | #include "nodes/makefuncs.h" 23 | #include "nodes/nodeFuncs.h" 24 | #include "nodes/primnodes.h" 25 | #include "nodes/plannodes.h" 26 | #include "nodes/relation.h" 27 | #include "nodes/nodes.h" 28 | #include "nodes/parsenodes.h" 29 | #include "utils/acl.h" 30 | #include "utils/lsyscache.h" 31 | #include "utils/syscache.h" 32 | 33 | #include "plan.h" 34 | #include "nodeSeqscan.h" 35 | #include "nodeAgg.h" 36 | #include "utils.h" 37 | 38 | static void mutate_plan_fields(Plan *newplan, Plan *oldplan, Node *(*mutator) (), void *context); 39 | static Node * plan_tree_mutator(Node *node, Node *(*mutator) (), void *context); 40 | 41 | /* 42 | * We check the expressions tree recursively becuase the args can be a sub expression, 43 | * we must check the return type of sub expression to fit the parent expressions. 44 | * so the retType in Vectorized is a temporary values, after we check on expression, 45 | * we set the retType of this expression, and transfer this value to his parent. 46 | */ 47 | typedef struct VectorizedContext 48 | { 49 | Oid retType; 50 | }VectorizedContext; 51 | 52 | static Oid getNodeReturnType(Node *node); 53 | 54 | 55 | static Oid 56 | getNodeReturnType(Node *node) 57 | { 58 | switch(nodeTag(node)) 59 | { 60 | case T_Var: 61 | return ((Var*)node)->vartype; 62 | case T_Const: 63 | return ((Const*)node)->consttype; 64 | case T_OpExpr: 65 | return ((OpExpr*)node)->opresulttype; 66 | default: 67 | { 68 | elog(ERROR, "Node return type %d not supported", nodeTag(node)); 69 | } 70 | } 71 | } 72 | 73 | /* 74 | * Check all the expressions if they can be vectorized 75 | * NOTE: if an expressions is vectorized, we return false...,because we should check 76 | * all the expressions in the Plan node, if we return true, then the walker will be 77 | * over... 78 | */ 79 | static Node* 80 | VectorizeMutator(Node *node, VectorizedContext *ctx) 81 | { 82 | if(NULL == node) 83 | return NULL; 84 | 85 | //check the type of Var if it can be vectorized 86 | switch (nodeTag(node)) 87 | { 88 | case T_Var: 89 | { 90 | Var *newnode; 91 | Oid vtype; 92 | 93 | newnode = (Var*)plan_tree_mutator(node, VectorizeMutator, ctx); 94 | vtype = GetVtype(newnode->vartype); 95 | if(InvalidOid == vtype) 96 | { 97 | elog(ERROR, "Cannot find vtype for type %d", newnode->vartype); 98 | } 99 | newnode->vartype = vtype; 100 | return (Node *)newnode; 101 | } 102 | 103 | case T_Aggref: 104 | { 105 | Aggref *newnode; 106 | Oid oldfnOid; 107 | Oid retype; 108 | HeapTuple proctup; 109 | Form_pg_proc procform; 110 | List *funcname = NULL; 111 | int i; 112 | Oid *argtypes; 113 | char *proname; 114 | bool retset; 115 | int nvargs; 116 | Oid vatype; 117 | Oid *true_oid_array; 118 | FuncDetailCode fdresult; 119 | 120 | newnode = (Aggref *)plan_tree_mutator(node, VectorizeMutator, ctx); 121 | oldfnOid = newnode->aggfnoid; 122 | 123 | proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(oldfnOid)); 124 | if (!HeapTupleIsValid(proctup)) 125 | elog(ERROR, "cache lookup failed for function %u", oldfnOid); 126 | procform = (Form_pg_proc) GETSTRUCT(proctup); 127 | proname = NameStr(procform->proname); 128 | funcname = lappend(funcname, makeString(proname)); 129 | 130 | argtypes = palloc(sizeof(Oid) * procform->pronargs); 131 | for (i = 0; i < procform->pronargs; i++) 132 | argtypes[i] = GetVtype(procform->proargtypes.values[i]); 133 | 134 | fdresult = func_get_detail(funcname, NIL, NIL, 135 | procform->pronargs, argtypes, false, false, 136 | &newnode->aggfnoid, &retype, &retset, 137 | &nvargs, &vatype, 138 | &true_oid_array, NULL); 139 | 140 | ReleaseSysCache(proctup); 141 | 142 | //TODO check validation of fdresult. 143 | if (fdresult != FUNCDETAIL_AGGREGATE || !OidIsValid(newnode->aggfnoid)) 144 | elog(ERROR, "aggreate function not defined"); 145 | return (Node *)newnode; 146 | } 147 | 148 | case T_OpExpr: 149 | { 150 | OpExpr *newnode; 151 | Oid ltype, rtype, rettype; 152 | Form_pg_operator voper; 153 | HeapTuple tuple; 154 | 155 | /* mutate OpExpr itself in plan_tree_mutator firstly. */ 156 | newnode = (OpExpr *)plan_tree_mutator(node, VectorizeMutator, ctx); 157 | rettype = GetVtype(newnode->opresulttype); 158 | if (InvalidOid == rettype) 159 | { 160 | elog(ERROR, "Cannot find vtype for type %d", newnode->opresulttype); 161 | } 162 | 163 | if (list_length(newnode->args) != 2) 164 | { 165 | elog(ERROR, "Unary operator not supported"); 166 | } 167 | ltype = getNodeReturnType(linitial(newnode->args)); 168 | rtype = getNodeReturnType(lsecond(newnode->args)); 169 | 170 | //get the vectorized operator functions 171 | tuple = oper(NULL, list_make1(makeString(get_opname(newnode->opno))), 172 | ltype, rtype, true, -1); 173 | if(NULL == tuple) 174 | { 175 | elog(ERROR, "Vectorized operator not found"); 176 | } 177 | 178 | voper = (Form_pg_operator)GETSTRUCT(tuple); 179 | if(voper->oprresult != rettype) 180 | { 181 | ReleaseSysCache(tuple); 182 | elog(ERROR, "Vectorize operator rettype not correct"); 183 | } 184 | 185 | newnode->opresulttype = rettype; 186 | newnode->opfuncid = voper->oprcode; 187 | 188 | ReleaseSysCache(tuple); 189 | return (Node *)newnode; 190 | } 191 | 192 | default: 193 | return plan_tree_mutator(node, VectorizeMutator, ctx); 194 | } 195 | } 196 | 197 | 198 | static Node * 199 | plan_tree_mutator(Node *node, 200 | Node *(*mutator) (), 201 | void *context) 202 | { 203 | /* 204 | * The mutator has already decided not to modify the current node, but we 205 | * must call the mutator for any sub-nodes. 206 | */ 207 | #define FLATCOPY(newnode, node, nodetype) \ 208 | ( (newnode) = makeNode(nodetype), \ 209 | memcpy((newnode), (node), sizeof(nodetype)) ) 210 | 211 | #define CHECKFLATCOPY(newnode, node, nodetype) \ 212 | ( AssertMacro(IsA((node), nodetype)), \ 213 | (newnode) = makeNode(nodetype), \ 214 | memcpy((newnode), (node), sizeof(nodetype)) ) 215 | 216 | #define MUTATE(newfield, oldfield, fieldtype) \ 217 | ( (newfield) = (fieldtype) mutator((Node *) (oldfield), context) ) 218 | 219 | #define PLANMUTATE(newplan, oldplan) \ 220 | mutate_plan_fields((Plan*)(newplan), (Plan*)(oldplan), mutator, context) 221 | 222 | /* This is just like PLANMUTATE because Scan adds only scalar fields. */ 223 | #define SCANMUTATE(newplan, oldplan) \ 224 | mutate_plan_fields((Plan*)(newplan), (Plan*)(oldplan), mutator, context) 225 | 226 | #define JOINMUTATE(newplan, oldplan) \ 227 | mutate_join_fields((Join*)(newplan), (Join*)(oldplan), mutator, context) 228 | 229 | #define COPYARRAY(dest,src,lenfld,datfld) \ 230 | do { \ 231 | (dest)->lenfld = (src)->lenfld; \ 232 | if ( (src)->lenfld > 0 && \ 233 | (src)->datfld != NULL) \ 234 | { \ 235 | Size _size = ((src)->lenfld*sizeof(*((src)->datfld))); \ 236 | (dest)->datfld = palloc(_size); \ 237 | memcpy((dest)->datfld, (src)->datfld, _size); \ 238 | } \ 239 | else \ 240 | { \ 241 | (dest)->datfld = NULL; \ 242 | } \ 243 | } while (0) 244 | 245 | 246 | if (node == NULL) 247 | return NULL; 248 | 249 | /* Guard against stack overflow due to overly complex expressions */ 250 | check_stack_depth(); 251 | 252 | switch (nodeTag(node)) 253 | { 254 | case T_SeqScan: 255 | { 256 | CustomScan *cscan; 257 | SeqScan *vscan; 258 | 259 | cscan = MakeCustomScanForSeqScan(); 260 | FLATCOPY(vscan, node, SeqScan); 261 | cscan->custom_plans = lappend(cscan->custom_plans, vscan); 262 | 263 | SCANMUTATE(vscan, node); 264 | return (Node *)cscan; 265 | } 266 | 267 | case T_Agg: 268 | { 269 | CustomScan *cscan; 270 | Agg *vagg; 271 | 272 | if (((Agg *)node)->aggstrategy != AGG_PLAIN && ((Agg *)node)->aggstrategy != AGG_HASHED) 273 | elog(ERROR, "Non plain agg is not supported"); 274 | 275 | cscan = MakeCustomScanForAgg(); 276 | FLATCOPY(vagg, node, Agg); 277 | cscan->custom_plans = lappend(cscan->custom_plans, vagg); 278 | 279 | SCANMUTATE(vagg, node); 280 | return (Node *)cscan; 281 | } 282 | case T_Const: 283 | { 284 | Const *oldnode = (Const *) node; 285 | Const *newnode; 286 | 287 | FLATCOPY(newnode, oldnode, Const); 288 | return (Node *) newnode; 289 | } 290 | 291 | case T_Var: 292 | { 293 | Var *var = (Var *)node; 294 | Var *newnode; 295 | 296 | FLATCOPY(newnode, var, Var); 297 | return (Node *)newnode; 298 | } 299 | 300 | case T_OpExpr: 301 | { 302 | OpExpr *expr = (OpExpr *)node; 303 | OpExpr *newnode; 304 | 305 | FLATCOPY(newnode, expr, OpExpr); 306 | MUTATE(newnode->args, expr->args, List *); 307 | return (Node *)newnode; 308 | } 309 | 310 | case T_FuncExpr: 311 | { 312 | FuncExpr *expr = (FuncExpr *)node; 313 | FuncExpr *newnode; 314 | 315 | FLATCOPY(newnode, expr, FuncExpr); 316 | MUTATE(newnode->args, expr->args, List *); 317 | return (Node *)newnode; 318 | } 319 | 320 | case T_List: 321 | { 322 | /* 323 | * We assume the mutator isn't interested in the list nodes 324 | * per se, so just invoke it on each list element. NOTE: this 325 | * would fail badly on a list with integer elements! 326 | */ 327 | List *resultlist; 328 | ListCell *temp; 329 | 330 | resultlist = NIL; 331 | foreach(temp, (List *) node) 332 | { 333 | resultlist = lappend(resultlist, 334 | mutator((Node *) lfirst(temp), 335 | context)); 336 | } 337 | return (Node *) resultlist; 338 | } 339 | 340 | case T_TargetEntry: 341 | { 342 | TargetEntry *targetentry = (TargetEntry *) node; 343 | TargetEntry *newnode; 344 | 345 | FLATCOPY(newnode, targetentry, TargetEntry); 346 | MUTATE(newnode->expr, targetentry->expr, Expr *); 347 | return (Node *) newnode; 348 | } 349 | case T_Aggref: 350 | { 351 | Aggref *aggref = (Aggref *) node; 352 | Aggref *newnode; 353 | 354 | FLATCOPY(newnode, aggref, Aggref); 355 | /* assume mutation doesn't change types of arguments */ 356 | newnode->aggargtypes = list_copy(aggref->aggargtypes); 357 | MUTATE(newnode->aggdirectargs, aggref->aggdirectargs, List *); 358 | MUTATE(newnode->args, aggref->args, List *); 359 | MUTATE(newnode->aggorder, aggref->aggorder, List *); 360 | MUTATE(newnode->aggdistinct, aggref->aggdistinct, List *); 361 | MUTATE(newnode->aggfilter, aggref->aggfilter, Expr *); 362 | return (Node *) newnode; 363 | } 364 | break; 365 | 366 | default: 367 | elog(ERROR, "node type %d not supported", nodeTag(node)); 368 | break; 369 | } 370 | } 371 | 372 | /* Function mutate_plan_fields() is a subroutine for plan_tree_mutator(). 373 | * It "hijacks" the macro MUTATE defined for use in that function, so don't 374 | * change the argument names "mutator" and "context" use in the macro 375 | * definition. 376 | * 377 | */ 378 | static void 379 | mutate_plan_fields(Plan *newplan, Plan *oldplan, Node *(*mutator) (), void *context) 380 | { 381 | /* 382 | * Scalar fields startup_cost total_cost plan_rows plan_width nParamExec 383 | * need no mutation. 384 | */ 385 | 386 | /* Node fields need mutation. */ 387 | MUTATE(newplan->targetlist, oldplan->targetlist, List *); 388 | MUTATE(newplan->qual, oldplan->qual, List *); 389 | MUTATE(newplan->lefttree, oldplan->lefttree, Plan *); 390 | MUTATE(newplan->righttree, oldplan->righttree, Plan *); 391 | MUTATE(newplan->initPlan, oldplan->initPlan, List *); 392 | 393 | /* Bitmapsets aren't nodes but need to be copied to palloc'd space. */ 394 | newplan->extParam = bms_copy(oldplan->extParam); 395 | newplan->allParam = bms_copy(oldplan->allParam); 396 | } 397 | 398 | /* 399 | * Replace the non-vectorirzed type to vectorized type 400 | */ 401 | Plan* 402 | ReplacePlanNodeWalker(Node *node) 403 | { 404 | return (Plan *)plan_tree_mutator(node, VectorizeMutator, NULL); 405 | } 406 | -------------------------------------------------------------------------------- /plan.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vwalkers.h 3 | */ 4 | 5 | #ifndef VECTOR_ENGINE_PLAN_H_ 6 | #define VECTOR_ENGINE_PLAN_H_ 7 | 8 | 9 | extern Plan* ReplacePlanNodeWalker(Node *node); 10 | 11 | #endif /* VECTOR_ENGINE_PLAN_H_ */ 12 | -------------------------------------------------------------------------------- /sql/vectorize_engine.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Regression Tests for Custom Plan APIs 3 | -- 4 | 5 | -- construction of test data 6 | 7 | CREATE TABLE t1 ( 8 | a int, 9 | b double precision 10 | ); 11 | INSERT INTO t1 SELECT generate_series(1,3), 2.3; 12 | INSERT INTO t1 SELECT generate_series(1,3), 3.3; 13 | INSERT INTO t1 SELECT generate_series(1,3), 4.3; 14 | VACUUM ANALYZE t1; 15 | 16 | create extension vectorize_engine; 17 | SET enable_vectorize_engine TO on; 18 | SELECT * FROM t1; 19 | SELECT b FROM t1; 20 | SELECT b+1 FROM t1; 21 | SELECT count(b) FROM t1; 22 | SELECT a, sum(b), avg(b) FROM t1 group by a; 23 | SELECT a, sum(b), avg(b) FROM t1 where a < 3 group by a; 24 | 25 | 26 | drop extension vectorize_engine; 27 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * utils.c 4 | * 5 | * Copyright (c) 1996-2019, PostgreSQL Global Development Group 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | #include "postgres.h" 10 | 11 | #include "catalog/namespace.h" 12 | #include "executor/executor.h" 13 | #include "utils/lsyscache.h" 14 | #include "utils/syscache.h" 15 | #include "utils/hsearch.h" 16 | #include "utils.h" 17 | 18 | typedef struct VecTypeHashEntry 19 | { 20 | Oid src; 21 | Oid dest; 22 | }VecTypeHashEntry; 23 | 24 | /* Map between the vectorized types and non-vectorized types */ 25 | static HTAB *hashMapN2V = NULL; 26 | static HTAB *hashMapV2N = NULL; 27 | 28 | #define BUILTIN_TYPE_NUM 12 29 | #define TYPE_HASH_TABLE_SIZE 64 30 | const char *typenames[] = { "any", "int2", "int4", "int8", "float4", "float8", 31 | "bool", "text", "date", "bpchar", "timestamp", "varchar"}; 32 | const char *vtypenames[] = { "vany", "vint2", "vint4", "vint8", "vfloat4", 33 | "vfloat8", "vbool", "vtext", "vdate", "vbpchar", 34 | "vtimestamp","vvarchar"}; 35 | 36 | 37 | /* 38 | * Clear common CustomScanState, since we would 39 | * use custom scan to do agg, hash etc. 40 | */ 41 | void 42 | ClearCustomScanState(CustomScanState *node) 43 | { 44 | /* Free the exprcontext */ 45 | ExecFreeExprContext(&node->ss.ps); 46 | 47 | /* Clean out the tuple table */ 48 | ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); 49 | ExecClearTuple(node->ss.ss_ScanTupleSlot); 50 | 51 | /* Close the heap relation */ 52 | if (node->ss.ss_currentRelation) 53 | ExecCloseScanRelation(node->ss.ss_currentRelation); 54 | } 55 | 56 | 57 | /* 58 | * map non-vectorized type to vectorized type. 59 | * To scan the PG_TYPE is inefficient, so we create a hashtable to map 60 | * the vectorized type and non-vectorized types. 61 | */ 62 | Oid GetVtype(Oid ntype) 63 | { 64 | VecTypeHashEntry *entry = NULL; 65 | bool found = false; 66 | 67 | /* construct the hash table */ 68 | if(NULL == hashMapN2V) 69 | { 70 | HASHCTL hash_ctl; 71 | 72 | MemSet(&hash_ctl, 0, sizeof(hash_ctl)); 73 | 74 | hash_ctl.keysize = sizeof(Oid); 75 | hash_ctl.entrysize = sizeof(VecTypeHashEntry); 76 | hash_ctl.hash = oid_hash; 77 | 78 | hashMapN2V = hash_create("vectorized_n2v",TYPE_HASH_TABLE_SIZE, 79 | &hash_ctl, HASH_ELEM | HASH_FUNCTION); 80 | } 81 | 82 | /* insert supported built-in type and vtypes */ 83 | { 84 | int i; 85 | Oid vtypid; 86 | Oid typid; 87 | for (i = 0; i < BUILTIN_TYPE_NUM; i++) 88 | { 89 | vtypid = TypenameGetTypid(vtypenames[i]); 90 | typid = TypenameGetTypid(typenames[i]); 91 | 92 | if (vtypid == InvalidOid) 93 | return InvalidOid; 94 | /* insert int4->vint4 mapping manually, may construct from catalog in future */ 95 | entry = hash_search(hashMapN2V, &typid, HASH_ENTER, &found); 96 | entry->dest = vtypid; 97 | } 98 | } 99 | 100 | /* find the vectorized type in hash table */ 101 | entry = hash_search(hashMapN2V, &ntype, HASH_FIND, &found); 102 | if(found) 103 | return entry->dest; 104 | 105 | return InvalidOid; 106 | } 107 | 108 | 109 | 110 | /* 111 | * map vectorized type to non-vectorized type. 112 | * To scan the PG_TYPE is inefficient, so we create a hashtable to map 113 | * the vectorized type and non-vectorized types. 114 | */ 115 | Oid GetNtype(Oid vtype) 116 | { 117 | VecTypeHashEntry *entry = NULL; 118 | bool found = false; 119 | 120 | /* construct the hash table */ 121 | if(NULL == hashMapV2N) 122 | { 123 | HASHCTL hash_ctl; 124 | 125 | MemSet(&hash_ctl, 0, sizeof(hash_ctl)); 126 | 127 | hash_ctl.keysize = sizeof(Oid); 128 | hash_ctl.entrysize = sizeof(VecTypeHashEntry); 129 | hash_ctl.hash = oid_hash; 130 | 131 | hashMapV2N = hash_create("vectorized_v2n", TYPE_HASH_TABLE_SIZE, 132 | &hash_ctl, HASH_ELEM | HASH_FUNCTION); 133 | } 134 | 135 | /* insert supported built-in type and vtypes */ 136 | { 137 | int i; 138 | Oid vtypid; 139 | Oid typid; 140 | for (i = 0; i < BUILTIN_TYPE_NUM; i++) 141 | { 142 | vtypid = TypenameGetTypid(vtypenames[i]); 143 | typid = TypenameGetTypid(typenames[i]); 144 | 145 | if (vtypid == InvalidOid) 146 | return InvalidOid; 147 | entry = hash_search(hashMapV2N, &vtypid, HASH_ENTER, &found); 148 | entry->dest = typid; 149 | } 150 | } 151 | 152 | /* find the vectorized type in hash table */ 153 | entry = hash_search(hashMapV2N, &vtype, HASH_FIND, &found); 154 | if(found) 155 | return entry->dest; 156 | 157 | return InvalidOid; 158 | } 159 | 160 | Oid 161 | GetTupDescAttVType(TupleDesc tupdesc, int i) 162 | { 163 | Form_pg_attribute att = tupdesc->attrs[i]; 164 | return GetVtype(att->atttypid); 165 | } 166 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include "access/tupdesc.h" 5 | #include "nodes/execnodes.h" 6 | 7 | extern void ClearCustomScanState(CustomScanState *node); 8 | extern Oid GetVtype(Oid ntype); 9 | extern Oid GetNtype(Oid vtype); 10 | extern Oid GetTupDescAttVType(TupleDesc tupdesc, int i); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /vectorEngine.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * VectorEngine.c 4 | * Portal of vertorized engine for Postgres. 5 | * 6 | * Copyright (c) 1996-2019, PostgreSQL Global Development Group 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #include "postgres.h" 11 | 12 | #include "fmgr.h" 13 | #include "optimizer/planner.h" 14 | #include "executor/nodeCustom.h" 15 | #include "utils/guc.h" 16 | 17 | #include "nodeUnbatch.h" 18 | #include "nodeSeqscan.h" 19 | #include "nodeAgg.h" 20 | #include "plan.h" 21 | 22 | PG_MODULE_MAGIC; 23 | 24 | /* static variables */ 25 | static bool enable_vectorize_engine; 26 | static bool enable_vectorize_notice; 27 | static planner_hook_type planner_hook_next; 28 | 29 | /* static functionss */ 30 | static PlannedStmt *vector_post_planner(Query *parse, int cursorOptions, 31 | ParamListInfo boundParams); 32 | 33 | void _PG_init(void); 34 | 35 | static PlannedStmt * 36 | vector_post_planner(Query *parse, 37 | int cursorOptions, 38 | ParamListInfo boundParams) 39 | { 40 | PlannedStmt *stmt; 41 | Plan *savedPlanTree; 42 | List *savedSubplan; 43 | 44 | if (planner_hook_next) 45 | stmt = planner_hook_next(parse, cursorOptions, boundParams); 46 | else 47 | stmt = standard_planner(parse, cursorOptions, boundParams); 48 | 49 | if (!enable_vectorize_engine) 50 | return stmt; 51 | 52 | /* modify plan by using vectorized nodes */ 53 | savedPlanTree = stmt->planTree; 54 | savedSubplan = stmt->subplans; 55 | 56 | PG_TRY(); 57 | { 58 | List *subplans = NULL; 59 | ListCell *cell; 60 | 61 | stmt->planTree = ReplacePlanNodeWalker((Node *) stmt->planTree); 62 | 63 | foreach(cell, stmt->subplans) 64 | { 65 | Plan *subplan = ReplacePlanNodeWalker((Node *)lfirst(cell)); 66 | subplans = lappend(subplans, subplan); 67 | } 68 | stmt->subplans = subplans; 69 | 70 | /* 71 | * vectorize executor exchange batch of tuples between plan nodes 72 | * add unbatch node at top to convert batch to row and send to client. 73 | */ 74 | stmt->planTree = AddUnbatchNodeAtTop(stmt->planTree); 75 | } 76 | PG_CATCH(); 77 | { 78 | ErrorData *edata; 79 | edata = CopyErrorData(); 80 | FlushErrorState(); 81 | if (enable_vectorize_notice) 82 | ereport(NOTICE, 83 | (errcode(ERRCODE_INTERNAL_ERROR), 84 | errmsg("query can't be vectorized"), 85 | errdetail("%s", edata->message))); 86 | stmt->planTree = savedPlanTree; 87 | stmt->subplans = savedSubplan; 88 | } 89 | PG_END_TRY(); 90 | 91 | return stmt; 92 | } 93 | 94 | void 95 | _PG_init(void) 96 | { 97 | elog(LOG, "Initialize vectorized extension"); 98 | 99 | /* Register customscan node for vectorized scan and agg */ 100 | InitVectorScan(); 101 | InitVectorAgg(); 102 | InitUnbatch(); 103 | 104 | /* planner hook registration */ 105 | planner_hook_next = planner_hook; 106 | planner_hook = vector_post_planner; 107 | 108 | DefineCustomBoolVariable("enable_vectorize_engine", 109 | "Enables vectorize engine.", 110 | NULL, 111 | &enable_vectorize_engine, 112 | false, 113 | PGC_USERSET, 114 | GUC_NOT_IN_SAMPLE, 115 | NULL, NULL, NULL); 116 | 117 | DefineCustomBoolVariable("enable_vectorize_notice", 118 | "Enables vectorize engine.", 119 | NULL, 120 | &enable_vectorize_notice, 121 | true, 122 | PGC_USERSET, 123 | GUC_NOT_IN_SAMPLE, 124 | NULL, NULL, NULL); 125 | } 126 | -------------------------------------------------------------------------------- /vectorTupleSlot.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * vectorTupleSlot.c 4 | * 5 | * Copyright (c) 1996-2019, PostgreSQL Global Development Group 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | #include "postgres.h" 10 | 11 | #include "access/sysattr.h" 12 | #include "access/tuptoaster.h" 13 | #include "executor/tuptable.h" 14 | #include "utils/expandeddatum.h" 15 | 16 | #include "vectorTupleSlot.h" 17 | 18 | 19 | static void Vslot_deform_tuple(TupleTableSlot *slot, int natts); 20 | 21 | /* -------------------------------- 22 | * VMakeTupleTableSlot 23 | * 24 | * Basic routine to make an empty VectorTupleTableSlot. 25 | * -------------------------------- 26 | */ 27 | TupleTableSlot * 28 | VMakeTupleTableSlot(void) 29 | { 30 | TupleTableSlot *slot; 31 | VectorTupleSlot *vslot; 32 | 33 | slot = palloc(sizeof(VectorTupleSlot)); 34 | NodeSetTag(slot, T_TupleTableSlot); 35 | 36 | slot->tts_isempty = true; 37 | slot->tts_shouldFree = false; 38 | slot->tts_shouldFreeMin = false; 39 | slot->tts_tuple = NULL; 40 | slot->tts_tupleDescriptor = NULL; 41 | slot->tts_mcxt = CurrentMemoryContext; 42 | slot->tts_buffer = InvalidBuffer; 43 | slot->tts_nvalid = 0; 44 | slot->tts_values = NULL; 45 | slot->tts_isnull = NULL; 46 | slot->tts_mintuple = NULL; 47 | 48 | /* vectorized fields */ 49 | vslot = (VectorTupleSlot*)slot; 50 | vslot->dim = 0; 51 | vslot->bufnum = 0; 52 | memset(vslot->tts_buffers, InvalidBuffer, sizeof(vslot->tts_buffers)); 53 | memset(vslot->tts_tuples, 0, sizeof(vslot->tts_tuples)); 54 | /* all tuples should be skipped in initialization */ 55 | memset(vslot->skip, true, sizeof(vslot->skip)); 56 | 57 | return slot; 58 | } 59 | 60 | /* -------------------------------- 61 | * VExecAllocTableSlot 62 | * 63 | * Create a vector tuple table slot within a tuple table (which is just a List). 64 | * -------------------------------- 65 | */ 66 | TupleTableSlot * 67 | VExecAllocTableSlot(List **tupleTable) 68 | { 69 | TupleTableSlot *slot = VMakeTupleTableSlot(); 70 | 71 | *tupleTable = lappend(*tupleTable, slot); 72 | 73 | return slot; 74 | } 75 | 76 | 77 | /* 78 | * slot_deform_tuple 79 | * Given a TupleTableSlot, extract data from the slot's physical tuple 80 | * into its Datum/isnull arrays. Data is extracted up through the 81 | * natts'th column (caller must ensure this is a legal column number). 82 | * 83 | * This is essentially an incremental version of heap_deform_tuple: 84 | * on each call we extract attributes up to the one needed, without 85 | * re-computing information about previously extracted attributes. 86 | * slot->tts_nvalid is the number of attributes already extracted. 87 | */ 88 | static void 89 | Vslot_deform_tuple(TupleTableSlot *slot, int natts) 90 | { 91 | VectorTupleSlot *vslot = (VectorTupleSlot *)slot; 92 | TupleDesc tupleDesc = slot->tts_tupleDescriptor; 93 | HeapTuple tuple; 94 | HeapTupleHeader tup; 95 | bool hasnulls; 96 | Form_pg_attribute *att = tupleDesc->attrs; 97 | int attnum; 98 | char *tp; /* ptr to tuple data */ 99 | long off; /* offset in tuple data */ 100 | bits8 *bp; /* ptr to null bitmap in tuple */ 101 | bool slow; /* can we use/set attcacheoff? */ 102 | int row; 103 | vtype *column; 104 | 105 | for (row = 0; row < vslot->dim; row++) 106 | { 107 | tuple = &vslot->tts_tuples[row]; 108 | tup = tuple->t_data; 109 | bp = tup->t_bits; 110 | hasnulls = HeapTupleHasNulls(tuple); 111 | 112 | attnum = slot->tts_nvalid; 113 | /* 114 | * Check whether the first call for this tuple, and initialize or restore 115 | * loop state. 116 | */ 117 | /* vectorize engine deform once for now */ 118 | off = 0; 119 | slow = false; 120 | 121 | tp = (char *) tup + tup->t_hoff; 122 | 123 | for (; attnum < natts; attnum++) 124 | { 125 | Form_pg_attribute thisatt = att[attnum]; 126 | column = (vtype *)slot->tts_values[attnum]; 127 | 128 | if (hasnulls && att_isnull(attnum, bp)) 129 | { 130 | column->values[row] = (Datum) 0; 131 | column->isnull[row] = true; 132 | slow = true; /* can't use attcacheoff anymore */ 133 | continue; 134 | } 135 | 136 | column->isnull[row] = false; 137 | 138 | if (!slow && thisatt->attcacheoff >= 0) 139 | off = thisatt->attcacheoff; 140 | else if (thisatt->attlen == -1) 141 | { 142 | /* 143 | * We can only cache the offset for a varlena attribute if the 144 | * offset is already suitably aligned, so that there would be no 145 | * pad bytes in any case: then the offset will be valid for either 146 | * an aligned or unaligned value. 147 | */ 148 | if (!slow && 149 | off == att_align_nominal(off, thisatt->attalign)) 150 | thisatt->attcacheoff = off; 151 | else 152 | { 153 | off = att_align_pointer(off, thisatt->attalign, -1, 154 | tp + off); 155 | slow = true; 156 | } 157 | } 158 | else 159 | { 160 | /* not varlena, so safe to use att_align_nominal */ 161 | off = att_align_nominal(off, thisatt->attalign); 162 | 163 | if (!slow) 164 | thisatt->attcacheoff = off; 165 | } 166 | 167 | column->values[row] = fetchatt(thisatt, tp + off); 168 | 169 | off = att_addlength_pointer(off, thisatt->attlen, tp + off); 170 | 171 | if (thisatt->attlen <= 0) 172 | slow = true; /* can't use attcacheoff anymore */ 173 | } 174 | } 175 | 176 | 177 | attnum = slot->tts_nvalid; 178 | for (; attnum < natts; attnum++) 179 | { 180 | column = (vtype *)slot->tts_values[attnum]; 181 | column->dim = vslot->dim; 182 | } 183 | 184 | /* 185 | * Save state for next execution 186 | */ 187 | slot->tts_nvalid = attnum; 188 | } 189 | 190 | 191 | /* 192 | * slot_getallattrs 193 | * This function forces all the entries of the slot's Datum/isnull 194 | * arrays to be valid. The caller may then extract data directly 195 | * from those arrays instead of using slot_getattr. 196 | */ 197 | void 198 | Vslot_getallattrs(TupleTableSlot *slot) 199 | { 200 | VectorTupleSlot *vslot = (VectorTupleSlot *)slot; 201 | int tdesc_natts = slot->tts_tupleDescriptor->natts; 202 | int attnum; 203 | HeapTuple tuple; 204 | int i; 205 | 206 | /* Quick out if we have 'em all already */ 207 | if (slot->tts_nvalid == tdesc_natts) 208 | return; 209 | 210 | if (vslot->dim == 0) 211 | return; 212 | /* 213 | * otherwise we had better have a physical tuple (tts_nvalid should equal 214 | * natts in all virtual-tuple cases) 215 | */ 216 | for (i = 0; i < vslot->dim; i++) 217 | { 218 | tuple = &vslot->tts_tuples[i]; 219 | if (tuple == NULL) /* internal error */ 220 | elog(ERROR, "cannot extract attribute from empty tuple slot"); 221 | } 222 | /* 223 | * load up any slots available from physical tuple 224 | */ 225 | attnum = HeapTupleHeaderGetNatts(vslot->tts_tuples[0].t_data); 226 | attnum = Min(attnum, tdesc_natts); 227 | 228 | Vslot_deform_tuple(slot, attnum); 229 | 230 | /* 231 | * If tuple doesn't have all the atts indicated by tupleDesc, read the 232 | * rest as null 233 | */ 234 | for (; attnum < tdesc_natts; attnum++) 235 | { 236 | slot->tts_values[attnum] = (Datum) 0; 237 | slot->tts_isnull[attnum] = true; 238 | } 239 | slot->tts_nvalid = tdesc_natts; 240 | } 241 | 242 | 243 | /* 244 | * slot_getsomeattrs 245 | * This function forces the entries of the slot's Datum/isnull 246 | * arrays to be valid at least up through the attnum'th entry. 247 | */ 248 | void 249 | Vslot_getsomeattrs(TupleTableSlot *slot, int attnum) 250 | { 251 | /* Quick out if we have 'em all already */ 252 | if (slot->tts_nvalid >= attnum) 253 | return; 254 | 255 | elog(ERROR, "slot should be deformed in scan for vectorize engine"); 256 | 257 | } 258 | 259 | 260 | /* -------------------------------- 261 | * VExecClearTuple 262 | * 263 | * This function is used to clear out a slot in the tuple table. 264 | * 265 | * NB: only the tuple is cleared, not the tuple descriptor (if any). 266 | * -------------------------------- 267 | */ 268 | TupleTableSlot * 269 | VExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */ 270 | { 271 | int i; 272 | vtype *column; 273 | VectorTupleSlot *vslot; 274 | /* 275 | * sanity checks 276 | */ 277 | Assert(slot != NULL); 278 | 279 | vslot = (VectorTupleSlot *)slot; 280 | /* 281 | * Free the old physical tuple if necessary. 282 | */ 283 | if (slot->tts_shouldFree) 284 | heap_freetuple(slot->tts_tuple); 285 | if (slot->tts_shouldFreeMin) 286 | heap_free_minimal_tuple(slot->tts_mintuple); 287 | 288 | slot->tts_tuple = NULL; 289 | slot->tts_mintuple = NULL; 290 | slot->tts_shouldFree = false; 291 | slot->tts_shouldFreeMin = false; 292 | 293 | /* 294 | * Drop the pin on the referenced buffer, if there is one. 295 | */ 296 | if (BufferIsValid(slot->tts_buffer)) 297 | ReleaseBuffer(slot->tts_buffer); 298 | 299 | slot->tts_buffer = InvalidBuffer; 300 | 301 | /* 302 | * Mark it empty. 303 | */ 304 | slot->tts_isempty = true; 305 | slot->tts_nvalid = 0; 306 | 307 | /* vectorize part */ 308 | for(i = 0; i < vslot->bufnum; i++) 309 | { 310 | if(BufferIsValid(vslot->tts_buffers[i])) 311 | { 312 | ReleaseBuffer(vslot->tts_buffers[i]); 313 | vslot->tts_buffers[i] = InvalidBuffer; 314 | } 315 | } 316 | vslot->dim = 0; 317 | vslot->bufnum = 0; 318 | 319 | for (i = 0; i < slot->tts_tupleDescriptor->natts; i++) 320 | { 321 | column = (vtype *)DatumGetPointer(slot->tts_values[i]); 322 | column->dim = 0; 323 | } 324 | 325 | memset(vslot->skip, true, sizeof(vslot->skip)); 326 | 327 | return slot; 328 | } 329 | 330 | 331 | /* -------------------------------- 332 | * ExecStoreTuple 333 | * 334 | * This function is used to store a physical tuple into a specified 335 | * slot in the tuple table. 336 | * 337 | * tuple: tuple to store 338 | * slot: slot to store it in 339 | * buffer: disk buffer if tuple is in a disk page, else InvalidBuffer 340 | * shouldFree: true if ExecClearTuple should pfree() the tuple 341 | * when done with it 342 | * 343 | * If 'buffer' is not InvalidBuffer, the tuple table code acquires a pin 344 | * on the buffer which is held until the slot is cleared, so that the tuple 345 | * won't go away on us. 346 | * 347 | * shouldFree is normally set 'true' for tuples constructed on-the-fly. 348 | * It must always be 'false' for tuples that are stored in disk pages, 349 | * since we don't want to try to pfree those. 350 | * 351 | * Another case where it is 'false' is when the referenced tuple is held 352 | * in a tuple table slot belonging to a lower-level executor Proc node. 353 | * In this case the lower-level slot retains ownership and responsibility 354 | * for eventually releasing the tuple. When this method is used, we must 355 | * be certain that the upper-level Proc node will lose interest in the tuple 356 | * sooner than the lower-level one does! If you're not certain, copy the 357 | * lower-level tuple with heap_copytuple and let the upper-level table 358 | * slot assume ownership of the copy! 359 | * 360 | * Return value is just the passed-in slot pointer. 361 | * 362 | * NOTE: before PostgreSQL 8.1, this function would accept a NULL tuple 363 | * pointer and effectively behave like ExecClearTuple (though you could 364 | * still specify a buffer to pin, which would be an odd combination). 365 | * This saved a couple lines of code in a few places, but seemed more likely 366 | * to mask logic errors than to be really useful, so it's now disallowed. 367 | * -------------------------------- 368 | */ 369 | TupleTableSlot * 370 | VExecStoreTuple(HeapTuple tuple, 371 | TupleTableSlot *slot, 372 | Buffer buffer, 373 | bool shouldFree) 374 | { 375 | VectorTupleSlot *vslot; 376 | /* 377 | * sanity checks 378 | */ 379 | Assert(tuple != NULL); 380 | Assert(slot != NULL); 381 | Assert(slot->tts_tupleDescriptor != NULL); 382 | /* passing shouldFree=true for a tuple on a disk page is not sane */ 383 | Assert(BufferIsValid(buffer) ? (!shouldFree) : true); 384 | 385 | vslot = (VectorTupleSlot *)slot; 386 | /* 387 | * Free any old physical tuple belonging to the slot. 388 | */ 389 | if (slot->tts_shouldFree) 390 | heap_freetuple(slot->tts_tuple); 391 | if (slot->tts_shouldFreeMin) 392 | heap_free_minimal_tuple(slot->tts_mintuple); 393 | 394 | /* 395 | * Store the new tuple into the specified slot. 396 | */ 397 | slot->tts_isempty = false; 398 | slot->tts_shouldFree = shouldFree; 399 | slot->tts_shouldFreeMin = false; 400 | memcpy(&vslot->tts_tuples[vslot->dim], tuple, sizeof(HeapTupleData)); 401 | slot->tts_mintuple = NULL; 402 | 403 | /* Mark extracted state invalid */ 404 | slot->tts_nvalid = 0; 405 | 406 | /* 407 | * If tuple is on a disk page, keep the page pinned as long as we hold a 408 | * pointer into it. We assume the caller already has such a pin. 409 | * 410 | * This is coded to optimize the case where the slot previously held a 411 | * tuple on the same disk page: in that case releasing and re-acquiring 412 | * the pin is a waste of cycles. This is a common situation during 413 | * seqscans, so it's worth troubling over. 414 | */ 415 | if (vslot->bufnum == 0 || vslot->tts_buffers[vslot->bufnum-1] != buffer) 416 | { 417 | if (BufferIsValid(vslot->tts_buffers[vslot->bufnum])) 418 | ReleaseBuffer(vslot->tts_buffers[vslot->bufnum]); 419 | vslot->tts_buffers[vslot->bufnum] = buffer; 420 | vslot->bufnum++; 421 | if (BufferIsValid(buffer)) 422 | IncrBufferRefCount(buffer); 423 | } 424 | vslot->dim++; 425 | 426 | return slot; 427 | } 428 | 429 | void 430 | InitializeVectorSlotColumn(VectorTupleSlot *vslot) 431 | { 432 | TupleDesc desc; 433 | Oid typid; 434 | vtype *column; 435 | int i; 436 | 437 | desc = vslot->tts.tts_tupleDescriptor; 438 | /* initailize column in vector slot */ 439 | for (i = 0; i < desc->natts; i++) 440 | { 441 | typid = desc->attrs[i]->atttypid; 442 | column = buildvtype(typid, BATCHSIZE, vslot->skip); 443 | column->dim = 0; 444 | vslot->tts.tts_values[i] = PointerGetDatum(column); 445 | /* tts_isnull not used yet */ 446 | vslot->tts.tts_isnull[i] = false; 447 | } 448 | } 449 | -------------------------------------------------------------------------------- /vectorTupleSlot.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * vectortTupleSlot.h 4 | * vector tuple table support stuff 5 | * 6 | * 7 | * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group 8 | * Portions Copyright (c) 1994, Regents of the University of California 9 | * 10 | * 11 | *------------------------------------------------------------------------- 12 | */ 13 | #ifndef VECTOR_TUPLE_SLOT_H 14 | #define VECTOR_TUPLE_SLOT_H 15 | 16 | #include "executor/tuptable.h" 17 | #include "storage/bufmgr.h" 18 | 19 | #include "vtype/vtype.h" 20 | /* 21 | * VectorTupleSlot store a batch of tuples in each slot. 22 | */ 23 | typedef struct VectorTupleSlot 24 | { 25 | TupleTableSlot tts; 26 | /* how many tuples does this slot contain */ 27 | int32 dim; 28 | int32 bufnum; 29 | 30 | /* batch of physical tuples */ 31 | HeapTupleData tts_tuples[BATCHSIZE]; 32 | /* 33 | * tuples in slot would across many heap blocks, 34 | * we need pin these buffers if needed. 35 | */ 36 | Buffer tts_buffers[BATCHSIZE]; 37 | /* skip array to represent filtered tuples */ 38 | bool skip[BATCHSIZE]; 39 | } VectorTupleSlot; 40 | 41 | /* vector tuple slot related interface */ 42 | 43 | extern TupleTableSlot *VMakeTupleTableSlot(void); 44 | extern TupleTableSlot *VExecAllocTableSlot(List **tupleTable); 45 | extern void InitializeVectorSlotColumn(VectorTupleSlot *vslot); 46 | 47 | extern TupleTableSlot *VExecStoreTuple(HeapTuple tuple, 48 | TupleTableSlot *slot, 49 | Buffer buffer, 50 | bool shouldFree); 51 | extern TupleTableSlot *VExecClearTuple(TupleTableSlot *slot); 52 | 53 | extern void Vslot_getsomeattrs(TupleTableSlot *slot, int attnum); 54 | extern void Vslot_getallattrs(TupleTableSlot *slot); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /vectorize_engine.control: -------------------------------------------------------------------------------- 1 | # vectorize_engine extension 2 | comment = 'vectorize_engine' 3 | default_version = '1.0' 4 | module_pathname = '$libdir/vectorize_engine' 5 | relocatable = true 6 | -------------------------------------------------------------------------------- /vtype/vdate.c: -------------------------------------------------------------------------------- 1 | #include "vdate.h" 2 | #include "vtype/vtimestamp.h" 3 | 4 | #include "catalog/pg_type.h" 5 | #include "utils/date.h" 6 | 7 | PG_FUNCTION_INFO_V1(vdate_le_timestamp); 8 | PG_FUNCTION_INFO_V1(vdate_mi_interval); 9 | PG_FUNCTION_INFO_V1(vdate_le); 10 | PG_FUNCTION_INFO_V1(vdate_in); 11 | PG_FUNCTION_INFO_V1(vdate_out); 12 | 13 | 14 | static vtimestamp* vdate2vtimestamp(vdate* vdateVal); 15 | /* 16 | * Internal routines for promoting date to timestamp and timestamp with 17 | * time zone 18 | */ 19 | 20 | vdate* buildvdate(int dim, bool *skip) 21 | { 22 | return (vdate *)buildvtype(DATEOID, dim, skip); 23 | } 24 | 25 | 26 | static vtimestamp* 27 | vdate2vtimestamp(vdate* vdateVal) 28 | { 29 | vtimestamp *result; 30 | int i; 31 | DateADT dateVal; 32 | Timestamp tmp; 33 | 34 | result = buildvtimestamp(vdateVal->dim, vdateVal->skipref); 35 | for (i = 0; i < BATCHSIZE; i++) 36 | { 37 | if (vdateVal->skipref[i]) 38 | continue; 39 | dateVal = DatumGetDateADT(vdateVal->values[i]); 40 | #ifdef HAVE_INT64_TIMESTAMP 41 | /* date is days since 2000, timestamp is microseconds since same... */ 42 | tmp = dateVal * USECS_PER_DAY; 43 | 44 | /* Date's range is wider than timestamp's, so must check for overflow */ 45 | if (tmp / USECS_PER_DAY != dateVal) 46 | ereport(ERROR, 47 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), 48 | errmsg("date out of range for timestamp"))); 49 | 50 | result->values[i] = TimestampGetDatum(tmp); 51 | #else 52 | /* date is days since 2000, timestamp is seconds since same... */ 53 | result->values[i] = TimestampGetDatum(dateVal * (double) SECS_PER_DAY); 54 | #endif 55 | } 56 | return result; 57 | } 58 | Datum 59 | vdate_le_timestamp(PG_FUNCTION_ARGS) 60 | { 61 | vdate *vdateVal = (vdate *)PG_GETARG_POINTER(0); 62 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); 63 | vtimestamp *vdt1; 64 | int i; 65 | vbool *result; 66 | Timestamp dt1; 67 | 68 | vdt1 = vdate2vtimestamp(vdateVal); 69 | 70 | result = buildvbool(vdt1->dim, vdt1->skipref); 71 | #ifdef HAVE_INT64_TIMESTAMP 72 | for (i = 0; i < BATCHSIZE; i++ ) 73 | { 74 | if (vdt1->skipref[i]) 75 | continue; 76 | dt1 = DatumGetTimestamp(vdt1->values[i]); 77 | result->values[i] = BoolGetDatum((dt1 <= dt2) ? true :false); 78 | } 79 | return PointerGetDatum(result); 80 | #else 81 | elog(ERROR, "HAVE_INT64_TIMESTAMP must be enabled in vectorize executor."); 82 | #endif 83 | } 84 | 85 | 86 | Datum vdate_mi_interval(PG_FUNCTION_ARGS) 87 | { 88 | vdate *vdateVal = (vdate *)PG_GETARG_POINTER(0); 89 | Interval *span = PG_GETARG_INTERVAL_P(1); 90 | vtimestamp *vdateStamp; 91 | 92 | vdateStamp = vdate2vtimestamp(vdateVal); 93 | 94 | return DirectFunctionCall2(vtimestamp_mi_interval, 95 | PointerGetDatum(vdateStamp), 96 | PointerGetDatum(span)); 97 | } 98 | 99 | Datum 100 | vdate_le(PG_FUNCTION_ARGS) 101 | { 102 | vdate *vdt1 = (vdate *)PG_GETARG_POINTER(0); 103 | DateADT dateVal2 = PG_GETARG_DATEADT(1); 104 | vbool *result; 105 | int i; 106 | 107 | result = buildvbool(vdt1->dim, vdt1->skipref); 108 | for (i = 0; i < BATCHSIZE; i++ ) 109 | { 110 | if (vdt1->skipref[i]) 111 | continue; 112 | result->values[i] = BoolGetDatum(DatumGetDateADT(vdt1->values[i]) <= dateVal2); 113 | } 114 | PG_RETURN_POINTER(result); 115 | } 116 | 117 | Datum vdate_in(PG_FUNCTION_ARGS) 118 | { 119 | elog(ERROR, "vdate_in not supported"); 120 | } 121 | 122 | Datum vdate_out(PG_FUNCTION_ARGS) 123 | { 124 | elog(ERROR, "vdate_out not supported"); 125 | } 126 | -------------------------------------------------------------------------------- /vtype/vdate.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR_ENGINE_VTYPE_VDATE_H 2 | #define VECTOR_ENGINE_VTYPE_VDATE_H 3 | #include "postgres.h" 4 | #include "fmgr.h" 5 | typedef struct vtype vdate; 6 | extern vdate* buildvdate(int dim, bool *skip); 7 | 8 | /* vdate oper op const */ 9 | extern Datum vdate_mi_interval(PG_FUNCTION_ARGS); 10 | 11 | /* vdate oper cmp const */ 12 | extern Datum vdate_le_timestamp(PG_FUNCTION_ARGS); 13 | extern Datum vdate_le(PG_FUNCTION_ARGS); 14 | 15 | extern Datum vdate_in(PG_FUNCTION_ARGS); 16 | extern Datum vdate_out(PG_FUNCTION_ARGS); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /vtype/vfloat.c: -------------------------------------------------------------------------------- 1 | #include "vfloat.h" 2 | #include "vtype.h" 3 | #include "math.h" 4 | #include "utils/array.h" 5 | #include "catalog/pg_type.h" 6 | 7 | PG_FUNCTION_INFO_V1(vfloat8vfloat8mul2); 8 | PG_FUNCTION_INFO_V1(vfloat8pl); 9 | PG_FUNCTION_INFO_V1(vfloat8_accum); 10 | PG_FUNCTION_INFO_V1(vfloat8_avg); 11 | 12 | static float8 * 13 | check_float8_array(ArrayType *transarray, const char *caller, int n); 14 | 15 | #define CHECKFLOATVAL(val, inf_is_valid, zero_is_valid) \ 16 | do { \ 17 | if (isinf(val) && !(inf_is_valid)) \ 18 | ereport(ERROR, \ 19 | (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \ 20 | errmsg("value out of range: overflow"))); \ 21 | \ 22 | if ((val) == 0.0 && !(zero_is_valid)) \ 23 | ereport(ERROR, \ 24 | (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \ 25 | errmsg("value out of range: underflow"))); \ 26 | } while(0) 27 | 28 | 29 | Datum vfloat8vfloat8mul2(PG_FUNCTION_ARGS) 30 | { 31 | vtype *arg1 = (vtype *)PG_GETARG_POINTER(0); 32 | vtype *arg2 = (vtype *)PG_GETARG_POINTER(1); 33 | vtype *result; 34 | float8 mul; 35 | int i; 36 | 37 | result = buildvtype(FLOAT8OID, BATCHSIZE, arg1->skipref); 38 | 39 | for (i = 0; i < BATCHSIZE; i++ ) 40 | { 41 | if (arg1->skipref[i]) 42 | continue; 43 | mul = DatumGetFloat8(arg1->values[i]) * DatumGetFloat8(arg2->values[i]); 44 | result->values[i] = Float8GetDatum(mul); 45 | } 46 | 47 | PG_RETURN_POINTER(result); 48 | } 49 | 50 | Datum vfloat8pl(PG_FUNCTION_ARGS) 51 | { 52 | float8 result; 53 | float8 arg1; 54 | float8 arg2; 55 | int i; 56 | char **entries; 57 | vtype *batch; 58 | Datum *transVal; 59 | int32 groupOffset = PG_GETARG_INT32(1); 60 | 61 | if (groupOffset < 0) 62 | elog(ERROR, "Not implemented"); 63 | 64 | entries = (char **)PG_GETARG_POINTER(0); 65 | batch = (vtype *) PG_GETARG_POINTER(2); 66 | for (i = 0; i < BATCHSIZE; i++) 67 | { 68 | if (batch->skipref[i]) 69 | continue; 70 | 71 | transVal = (Datum *)(entries[i] + groupOffset); 72 | arg1 = DatumGetFloat8(*transVal); 73 | arg2 = DatumGetFloat8(batch->values[i]); 74 | result = arg1 + arg2; 75 | 76 | CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true); 77 | *transVal = Float8GetDatum(result); 78 | } 79 | 80 | PG_RETURN_INT64(0); 81 | } 82 | 83 | 84 | Datum 85 | vfloat8_accum(PG_FUNCTION_ARGS) 86 | { 87 | ArrayType *transarray; 88 | float8 newval; 89 | float8 *transvalues; 90 | float8 N, 91 | sumX, 92 | sumX2; 93 | 94 | Datum *transDatum; 95 | int i; 96 | char **entries; 97 | vtype *batch; 98 | int32 groupOffset = PG_GETARG_INT32(1); 99 | 100 | if (groupOffset < 0) 101 | elog(ERROR, "Not implemented"); 102 | 103 | entries = (char **)PG_GETARG_POINTER(0); 104 | batch = (vtype *) PG_GETARG_POINTER(2); 105 | 106 | for (i = 0; i < BATCHSIZE; i++) 107 | { 108 | if (batch->skipref[i]) 109 | continue; 110 | transDatum = (Datum *)(entries[i] + groupOffset); 111 | transarray = DatumGetArrayTypeP(*transDatum); 112 | transvalues = check_float8_array(transarray, "float8_accum", 3); 113 | N = transvalues[0]; 114 | sumX = transvalues[1]; 115 | sumX2 = transvalues[2]; 116 | newval = DatumGetFloat8(batch->values[i]); 117 | 118 | N += 1.0; 119 | sumX += newval; 120 | CHECKFLOATVAL(sumX, isinf(transvalues[1]) || isinf(newval), true); 121 | sumX2 += newval * newval; 122 | CHECKFLOATVAL(sumX2, isinf(transvalues[2]) || isinf(newval), true); 123 | 124 | /* 125 | * If we're invoked as an aggregate, we can cheat and modify our first 126 | * parameter in-place to reduce palloc overhead. Otherwise we construct a 127 | * new array with the updated transition data and return it. 128 | */ 129 | if (AggCheckCallContext(fcinfo, NULL)) 130 | { 131 | transvalues[0] = N; 132 | transvalues[1] = sumX; 133 | transvalues[2] = sumX2; 134 | } 135 | #if 0 136 | else 137 | { 138 | Datum transdatums[3]; 139 | ArrayType *result; 140 | 141 | transdatums[0] = Float8GetDatumFast(N); 142 | transdatums[1] = Float8GetDatumFast(sumX); 143 | transdatums[2] = Float8GetDatumFast(sumX2); 144 | 145 | result = construct_array(transdatums, 3, 146 | FLOAT8OID, 147 | sizeof(float8), FLOAT8PASSBYVAL, 'd'); 148 | } 149 | #endif 150 | } 151 | PG_RETURN_ARRAYTYPE_P(0); 152 | } 153 | 154 | 155 | Datum 156 | vfloat8_avg(PG_FUNCTION_ARGS) 157 | { 158 | ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); 159 | float8 *transvalues; 160 | float8 N, 161 | sumX; 162 | 163 | transvalues = check_float8_array(transarray, "float8_avg", 3); 164 | N = transvalues[0]; 165 | sumX = transvalues[1]; 166 | /* ignore sumX2 */ 167 | 168 | /* SQL defines AVG of no values to be NULL */ 169 | if (N == 0.0) 170 | PG_RETURN_NULL(); 171 | 172 | PG_RETURN_FLOAT8(sumX / N); 173 | } 174 | 175 | static float8 * 176 | check_float8_array(ArrayType *transarray, const char *caller, int n) 177 | { 178 | /* 179 | * We expect the input to be an N-element float array; verify that. We 180 | * don't need to use deconstruct_array() since the array data is just 181 | * going to look like a C array of N float8 values. 182 | */ 183 | if (ARR_NDIM(transarray) != 1 || 184 | ARR_DIMS(transarray)[0] != n || 185 | ARR_HASNULL(transarray) || 186 | ARR_ELEMTYPE(transarray) != FLOAT8OID) 187 | elog(ERROR, "%s: expected %d-element float8 array", caller, n); 188 | return (float8 *) ARR_DATA_PTR(transarray); 189 | } 190 | -------------------------------------------------------------------------------- /vtype/vfloat.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR_ENGINE_VTYPE_VFLOAT_H 2 | #define VECTOR_ENGINE_VTYPE_VFLOAT_H 3 | #include "postgres.h" 4 | #include "fmgr.h" 5 | 6 | extern Datum vfloat8pl(PG_FUNCTION_ARGS); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /vtype/vint.c: -------------------------------------------------------------------------------- 1 | #include "vint.h" 2 | #include "vtype.h" 3 | 4 | PG_FUNCTION_INFO_V1(vint8inc_any); 5 | PG_FUNCTION_INFO_V1(vint4_sum); 6 | PG_FUNCTION_INFO_V1(vint8inc); 7 | 8 | Datum vint8inc_any(PG_FUNCTION_ARGS) 9 | { 10 | int64 result; 11 | int64 arg; 12 | int i; 13 | char **entries; 14 | vtype *batch; 15 | Datum *transVal; 16 | 17 | int32 groupOffset = PG_GETARG_INT32(1); 18 | 19 | if (groupOffset < 0) 20 | { 21 | /* Not called as an aggregate, so just do it the dumb way */ 22 | arg = PG_GETARG_INT64(0); 23 | batch = (vtype *) PG_GETARG_POINTER(2); 24 | 25 | result = arg; 26 | 27 | for (i = 0; i < BATCHSIZE; i++) 28 | { 29 | if (batch->skipref[i]) 30 | continue; 31 | result++; 32 | } 33 | 34 | /* Overflow check */ 35 | if (result < 0 && arg > 0) 36 | ereport(ERROR, 37 | (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), 38 | errmsg("bigint out of range"))); 39 | 40 | PG_RETURN_INT64(result); 41 | } 42 | 43 | entries = (char **)PG_GETARG_POINTER(0); 44 | batch = (vtype *) PG_GETARG_POINTER(2); 45 | 46 | for (i = 0; i < BATCHSIZE; i++) 47 | { 48 | if (batch->skipref[i]) 49 | continue; 50 | 51 | transVal = (Datum *)(entries[i] + groupOffset); 52 | 53 | arg = DatumGetInt64(*transVal); 54 | result = arg + 1; 55 | /* Overflow check */ 56 | if (result < 0 && arg > 0) 57 | ereport(ERROR, 58 | (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), 59 | errmsg("bigint out of range"))); 60 | 61 | *transVal = Int64GetDatum(result); 62 | } 63 | 64 | PG_RETURN_INT64(0); 65 | } 66 | 67 | Datum 68 | vint4_sum(PG_FUNCTION_ARGS) 69 | { 70 | char **entries; 71 | vtype *batch; 72 | int i; 73 | int64 result; 74 | Datum *transVal; 75 | int32 groupOffset = PG_GETARG_INT32(1); 76 | 77 | #if 0 78 | if (PG_ARGISNULL(0)) 79 | { 80 | /* No non-null input seen so far... */ 81 | if (PG_ARGISNULL(1)) 82 | PG_RETURN_NULL(); /* still no non-null */ 83 | /* This is the first non-null input. */ 84 | newval = (int64) PG_GETARG_INT32(1); 85 | PG_RETURN_INT64(newval); 86 | } 87 | #endif 88 | 89 | 90 | if (groupOffset < 0) 91 | { 92 | /* Not called as an aggregate, so just do it the dumb way */ 93 | result = PG_GETARG_INT64(0); 94 | batch = (vtype *) PG_GETARG_POINTER(2); 95 | 96 | for (i = 0; i < BATCHSIZE; i++) 97 | { 98 | if (batch->skipref[i]) 99 | continue; 100 | 101 | result += DatumGetInt32(batch->values[i]); 102 | } 103 | 104 | PG_RETURN_INT64(result); 105 | } 106 | 107 | entries = (char **)PG_GETARG_POINTER(0); 108 | batch = (vtype *) PG_GETARG_POINTER(2); 109 | for (i = 0; i < BATCHSIZE; i++) 110 | { 111 | if (batch->skipref[i]) 112 | continue; 113 | 114 | transVal = (Datum *)(entries[i] + groupOffset); 115 | 116 | result = DatumGetInt64(*transVal); 117 | result += DatumGetInt32(batch->values[i]); 118 | 119 | *transVal = Int64GetDatum(result); 120 | } 121 | 122 | PG_RETURN_INT64(0); 123 | } 124 | 125 | Datum vint8inc(PG_FUNCTION_ARGS) 126 | { 127 | int64 result; 128 | int64 arg; 129 | int i; 130 | char **entries; 131 | vtype *batch; 132 | Datum *transVal; 133 | int32 groupOffset = PG_GETARG_INT32(1); 134 | 135 | if (groupOffset < 0) 136 | { 137 | /* Not called as an aggregate, so just do it the dumb way */ 138 | result = arg = PG_GETARG_INT64(0); 139 | batch = (vtype *) PG_GETARG_POINTER(2); 140 | 141 | for (i = 0; i < BATCHSIZE; i++) 142 | { 143 | if (batch->skipref[i]) 144 | continue; 145 | result++; 146 | } 147 | /* Overflow check */ 148 | if (result < 0 && arg > 0) 149 | ereport(ERROR, 150 | (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), 151 | errmsg("bigint out of range"))); 152 | 153 | PG_RETURN_INT64(result); 154 | } 155 | 156 | entries = (char **)PG_GETARG_POINTER(0); 157 | batch = (vtype *) PG_GETARG_POINTER(2); 158 | for (i = 0; i < BATCHSIZE; i++) 159 | { 160 | if (batch->skipref[i]) 161 | continue; 162 | 163 | transVal = (Datum *)(entries[i] + groupOffset); 164 | arg = DatumGetInt64(*transVal); 165 | result = arg + 1; 166 | /* Overflow check */ 167 | if (result < 0 && arg > 0) 168 | ereport(ERROR, 169 | (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), 170 | errmsg("bigint out of range"))); 171 | 172 | *transVal = Int64GetDatum(result); 173 | } 174 | 175 | PG_RETURN_INT64(0); 176 | } 177 | -------------------------------------------------------------------------------- /vtype/vint.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR_ENGINE_VTYPE_VINT_H 2 | #define VECTOR_ENGINE_VTYPE_VINT_H 3 | #include "postgres.h" 4 | #include "fmgr.h" 5 | 6 | extern Datum vint8inc_any(PG_FUNCTION_ARGS); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /vtype/vpseudotypes.c: -------------------------------------------------------------------------------- 1 | #include "postgres.h" 2 | #include "catalog/pg_type.h" 3 | #include "vpseudotypes.h" 4 | #include "vtype.h" 5 | 6 | PG_FUNCTION_INFO_V1(vany_in); 7 | PG_FUNCTION_INFO_V1(vany_out); 8 | 9 | 10 | vany* buildvany(int dim, bool *skip) 11 | { 12 | return (vany *)buildvtype(ANYOID, dim, skip); 13 | } 14 | 15 | Datum vany_in(PG_FUNCTION_ARGS) 16 | { 17 | elog(ERROR, "vany_in not supported"); 18 | } 19 | 20 | Datum vany_out(PG_FUNCTION_ARGS) 21 | { 22 | elog(ERROR, "vany_out not supported"); 23 | } 24 | -------------------------------------------------------------------------------- /vtype/vpseudotypes.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR_ENGINE_VTYPE_VPSEUDO_H 2 | #define VECTOR_ENGINE_VTYPE_VPSEUDO_H 3 | #include "postgres.h" 4 | #include "fmgr.h" 5 | typedef struct vtype vany; 6 | extern vany *buildvany(int dim, bool *skip); 7 | 8 | extern Datum vany_in(PG_FUNCTION_ARGS); 9 | extern Datum vany_out(PG_FUNCTION_ARGS); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /vtype/vtimestamp.c: -------------------------------------------------------------------------------- 1 | #include "vtimestamp.h" 2 | #include "vtype.h" 3 | 4 | #include "datatype/timestamp.h" 5 | #include "catalog/pg_type.h" 6 | 7 | PG_FUNCTION_INFO_V1(vtimestamp_mi_interval); 8 | PG_FUNCTION_INFO_V1(vtimestamp_pl_interval); 9 | PG_FUNCTION_INFO_V1(vtimestamp_in); 10 | PG_FUNCTION_INFO_V1(vtimestamp_out); 11 | 12 | vtimestamp* buildvtimestamp(int dim, bool *skip) 13 | { 14 | return (vtimestamp*)buildvtype(TIMESTAMPOID, dim, skip); 15 | } 16 | /* 17 | * We are currently sharing some code between timestamp and timestamptz. 18 | * The comparison functions are among them. - thomas 2001-09-25 19 | * 20 | * timestamp_relop - is timestamp1 relop timestamp2 21 | * 22 | * collate invalid timestamp at the end 23 | */ 24 | Datum 25 | vtimestamp_timestamp_cmp_internal(vtimestamp *vdt1, Timestamp dt2) 26 | { 27 | int i; 28 | vint4 *result; 29 | Timestamp dt1; 30 | 31 | result = buildvtype(INT4OID,vdt1->dim, vdt1->skipref); 32 | #ifdef HAVE_INT64_TIMESTAMP 33 | for (i = 0; i < BATCHSIZE; i++ ) 34 | { 35 | if (vdt1->skipref[i]) 36 | continue; 37 | dt1 = DatumGetTimestamp(vdt1->values[i]); 38 | result->values[i] = Int32GetDatum((dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0)); 39 | } 40 | return PointerGetDatum(result); 41 | #else 42 | elog(ERROR, "HAVE_INT64_TIMESTAMP must be enabled in vectorize executor."); 43 | #endif 44 | } 45 | 46 | 47 | /* timestamp_pl_interval() 48 | * Add an interval to a timestamp data type. 49 | * Note that interval has provisions for qualitative year/month and day 50 | * units, so try to do the right thing with them. 51 | * To add a month, increment the month, and use the same day of month. 52 | * Then, if the next month has fewer days, set the day of month 53 | * to the last day of month. 54 | * To add a day, increment the mday, and use the same time of day. 55 | * Lastly, add in the "quantitative time". 56 | */ 57 | Datum 58 | vtimestamp_pl_interval(PG_FUNCTION_ARGS) 59 | { 60 | vtimestamp *vts = (vtimestamp *)PG_GETARG_POINTER(0); 61 | Interval *span = PG_GETARG_INTERVAL_P(1); 62 | Timestamp timestamp; 63 | vtimestamp *result; 64 | int i; 65 | 66 | result = buildvtimestamp(vts->dim, vts->skipref); 67 | 68 | for(i = 0; i< vts->dim; i++) 69 | { 70 | if (vts->skipref[i]) 71 | continue; 72 | timestamp = DatumGetTimestamp(vts->values[i]); 73 | if (TIMESTAMP_NOT_FINITE(timestamp)) 74 | result->values[i] = TimestampGetDatum(timestamp); 75 | else 76 | { 77 | if (span->month != 0) 78 | { 79 | struct pg_tm tt, 80 | *tm = &tt; 81 | fsec_t fsec; 82 | 83 | if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) 84 | ereport(ERROR, 85 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), 86 | errmsg("timestamp out of range"))); 87 | 88 | tm->tm_mon += span->month; 89 | if (tm->tm_mon > MONTHS_PER_YEAR) 90 | { 91 | tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR; 92 | tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1; 93 | } 94 | else if (tm->tm_mon < 1) 95 | { 96 | tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1; 97 | tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR; 98 | } 99 | 100 | /* adjust for end of month boundary problems... */ 101 | if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) 102 | tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]); 103 | 104 | if (tm2timestamp(tm, fsec, NULL, ×tamp) != 0) 105 | ereport(ERROR, 106 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), 107 | errmsg("timestamp out of range"))); 108 | } 109 | 110 | if (span->day != 0) 111 | { 112 | struct pg_tm tt, 113 | *tm = &tt; 114 | fsec_t fsec; 115 | int julian; 116 | 117 | if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) 118 | ereport(ERROR, 119 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), 120 | errmsg("timestamp out of range"))); 121 | 122 | /* Add days by converting to and from Julian */ 123 | julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day; 124 | j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); 125 | 126 | if (tm2timestamp(tm, fsec, NULL, ×tamp) != 0) 127 | ereport(ERROR, 128 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), 129 | errmsg("timestamp out of range"))); 130 | } 131 | 132 | timestamp += span->time; 133 | 134 | if (!IS_VALID_TIMESTAMP(timestamp)) 135 | ereport(ERROR, 136 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), 137 | errmsg("timestamp out of range"))); 138 | 139 | result->values[i] = TimestampGetDatum(timestamp); 140 | } 141 | } 142 | PG_RETURN_POINTER(result); 143 | } 144 | 145 | Datum 146 | vtimestamp_mi_interval(PG_FUNCTION_ARGS) 147 | { 148 | vtimestamp *vts = (vtimestamp *)PG_GETARG_POINTER(0); 149 | Interval *span = PG_GETARG_INTERVAL_P(1); 150 | Interval tspan; 151 | 152 | tspan.month = -span->month; 153 | tspan.day = -span->day; 154 | tspan.time = -span->time; 155 | 156 | return DirectFunctionCall2(vtimestamp_pl_interval, 157 | PointerGetDatum(vts), 158 | PointerGetDatum(&tspan)); 159 | } 160 | 161 | 162 | Datum vtimestamp_in(PG_FUNCTION_ARGS) 163 | { 164 | elog(ERROR, "vtimestamp_in not supported"); 165 | } 166 | Datum vtimestamp_out(PG_FUNCTION_ARGS) 167 | { 168 | elog(ERROR, "vtimestamp_out not supported"); 169 | } 170 | -------------------------------------------------------------------------------- /vtype/vtimestamp.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR_ENGINE_VTYPE_VTIMESTAMP_H 2 | #define VECTOR_ENGINE_VTYPE_VTIMESTAMP_H 3 | #include "postgres.h" 4 | 5 | #include "fmgr.h" 6 | #include "utils/datetime.h" 7 | #include "utils/timestamp.h" 8 | 9 | #include "vtype.h" 10 | 11 | 12 | typedef vtype vtimestamp; 13 | 14 | extern vtimestamp* buildvtimestamp(int dim, bool *skip); 15 | 16 | extern Datum vtimestamp_timestamp_cmp_internal(vtimestamp *vdt1, Timestamp dt2); 17 | 18 | extern Datum vtimestamp_mi_interval(PG_FUNCTION_ARGS); 19 | 20 | 21 | extern Datum vtimestamp_pl_interval(PG_FUNCTION_ARGS); 22 | 23 | extern Datum vtimestamp_in(PG_FUNCTION_ARGS); 24 | extern Datum vtimestamp_out(PG_FUNCTION_ARGS); 25 | #endif 26 | -------------------------------------------------------------------------------- /vtype/vtype.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #include "postgres.h" 21 | #include "catalog/pg_type.h" 22 | #include "utils/builtins.h" 23 | #include "utils/int8.h" 24 | #include "utils/date.h" 25 | #include "vtype.h" 26 | 27 | 28 | #define MAX_NUM_LEN 64 29 | 30 | const char canary = 0xe7; 31 | 32 | vtype* buildvtype(Oid elemtype,int dim,bool *skip) 33 | { 34 | vtype *res; 35 | res = palloc0(sizeof(vtype)); 36 | res->dim = dim; 37 | res->elemtype = elemtype; 38 | res->skipref = skip; 39 | 40 | return res; 41 | } 42 | 43 | void destroyvtype(vtype** vt) 44 | { 45 | pfree((*vt)); 46 | *vt = NULL; 47 | } 48 | 49 | #define _FUNCTION_BUILD(type, typeoid) \ 50 | v##type* buildv##type(int dim, bool *skip) \ 51 | { \ 52 | return buildvtype(typeoid, dim, skip); \ 53 | } 54 | 55 | /* 56 | * IN function for the abstract data types 57 | * e.g. Datum vint2in(PG_FUNCTION_ARGS) 58 | */ 59 | #define _FUNCTION_IN(type, fname, typeoid) \ 60 | PG_FUNCTION_INFO_V1(v##fname##in); \ 61 | Datum \ 62 | v##fname##in(PG_FUNCTION_ARGS) \ 63 | { \ 64 | char *intString = PG_GETARG_CSTRING(0); \ 65 | vtype *res = NULL; \ 66 | char tempstr[MAX_NUM_LEN] = {0}; \ 67 | int n = 0; \ 68 | res = buildvtype(typeoid,BATCHSIZE,NULL);\ 69 | for (n = 0; *intString && n < BATCHSIZE; n++) \ 70 | { \ 71 | char *start = NULL;\ 72 | while (*intString && isspace((unsigned char) *intString)) \ 73 | intString++; \ 74 | if (*intString == '\0') \ 75 | break; \ 76 | start = intString; \ 77 | while ((*intString && !isspace((unsigned char) *intString)) && *intString != '\0') \ 78 | intString++; \ 79 | Assert(intString - start < MAX_NUM_LEN); \ 80 | strncpy(tempstr, start, intString - start); \ 81 | tempstr[intString - start] = 0; \ 82 | res->values[n] = DirectFunctionCall1(fname##in, CStringGetDatum(tempstr)); \ 83 | while (*intString && !isspace((unsigned char) *intString)) \ 84 | intString++; \ 85 | } \ 86 | while (*intString && isspace((unsigned char) *intString)) \ 87 | intString++; \ 88 | if (*intString) \ 89 | ereport(ERROR, \ 90 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ 91 | errmsg("int2vector has too many elements"))); \ 92 | res->elemtype = typeoid; \ 93 | res->dim = n; \ 94 | SET_VARSIZE(res, VTYPESIZE(n)); \ 95 | PG_RETURN_POINTER(res); \ 96 | } 97 | 98 | /* 99 | * OUT function for the abstract data types 100 | * e.g. Datum vint2out(PG_FUNCTION_ARGS) 101 | */ 102 | #define _FUNCTION_OUT(type, fname, typeoid) \ 103 | PG_FUNCTION_INFO_V1(v##fname##out); \ 104 | Datum \ 105 | v##fname##out(PG_FUNCTION_ARGS) \ 106 | { \ 107 | vtype * arg1 = (v##type *) PG_GETARG_POINTER(0); \ 108 | int len = arg1->dim; \ 109 | int i = 0; \ 110 | char *rp; \ 111 | char *result; \ 112 | rp = result = (char *) palloc0(len * MAX_NUM_LEN + 1); \ 113 | for (i = 0; i < len; i++) \ 114 | { \ 115 | if (i != 0) \ 116 | *rp++ = ' '; \ 117 | strcat(rp, DatumGetCString(DirectFunctionCall1(fname##out, arg1->values[i])));\ 118 | while (*++rp != '\0'); \ 119 | } \ 120 | *rp = '\0'; \ 121 | PG_RETURN_CSTRING(result); \ 122 | } 123 | 124 | /* 125 | * Operator function for the abstract data types, this MACRO is used for the 126 | * V-types OP V-types. 127 | * e.g. extern Datum vint2vint2pl(PG_FUNCTION_ARGS); 128 | * NOTE:we assum that return type is same with the type of arg1, 129 | * we have not processed the overflow so far. 130 | */ 131 | #define __FUNCTION_OP(type1, XTYPE1, type2, XTYPE2, opsym, opstr) \ 132 | PG_FUNCTION_INFO_V1(v##type1##v##type2##opstr); \ 133 | Datum \ 134 | v##type1##v##type2##opstr(PG_FUNCTION_ARGS) \ 135 | { \ 136 | int size = 0; \ 137 | int i = 0; \ 138 | v##type1 *arg1 = (v##type1*)PG_GETARG_POINTER(0); \ 139 | v##type2 *arg2 = (v##type2*)PG_GETARG_POINTER(1); \ 140 | v##type1 *res = buildv##type1(BATCHSIZE, arg1->skipref); \ 141 | Assert(arg1->dim == arg2->dim); \ 142 | size = arg1->dim; \ 143 | while(i < size) \ 144 | { \ 145 | res->isnull[i] = arg1->isnull[i] || arg2->isnull[i]; \ 146 | i++; \ 147 | } \ 148 | i=0; \ 149 | while(i < size) \ 150 | { \ 151 | if(!res->isnull[i]) \ 152 | res->values[i] = XTYPE1##GetDatum((DatumGet##XTYPE1(arg1->values[i])) opsym (DatumGet##XTYPE2(arg2->values[i]))); \ 153 | i++; \ 154 | } \ 155 | res->dim = arg1->dim; \ 156 | PG_RETURN_POINTER(res); \ 157 | } 158 | 159 | /* 160 | * Operator function for the abstract data types, this MACRO is used for the 161 | * V-types OP Consts. 162 | * e.g. extern Datum vint2int2pl(PG_FUNCTION_ARGS); 163 | */ 164 | #define __FUNCTION_OP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, opsym, opstr) \ 165 | PG_FUNCTION_INFO_V1(v##type##const_type##opstr); \ 166 | Datum \ 167 | v##type##const_type##opstr(PG_FUNCTION_ARGS) \ 168 | { \ 169 | int size = 0; \ 170 | int i = 0; \ 171 | v##type *arg1 = (v##type*)PG_GETARG_POINTER(0); \ 172 | const_type arg2 = CONST_ARG_MACRO(1); \ 173 | v##type *res = buildv##type(BATCHSIZE, arg1->skipref); \ 174 | size = arg1->dim;\ 175 | while(i < size) \ 176 | { \ 177 | res->isnull[i] = arg1->isnull[i]; \ 178 | if(!res->isnull[i]) \ 179 | res->values[i] = XTYPE##GetDatum((DatumGet##XTYPE(arg1->values[i])) opsym ((type)arg2)); \ 180 | i ++ ;\ 181 | } \ 182 | res->dim = arg1->dim; \ 183 | PG_RETURN_POINTER(res); \ 184 | } 185 | 186 | /* 187 | * Operator function for the abstract data types, this MACRO is used for the 188 | * Consts OP V-types. 189 | * e.g. extern Datum int2vint2pl(PG_FUNCTION_ARGS); 190 | */ 191 | #define __FUNCTION_OP_LCONST(type, XTYPE, const_type, CONST_ARG_MACRO, opsym, opstr) \ 192 | PG_FUNCTION_INFO_V1(const_type##v##type##opstr); \ 193 | Datum \ 194 | const_type##v##type##opstr(PG_FUNCTION_ARGS) \ 195 | { \ 196 | int size = 0; \ 197 | int i = 0; \ 198 | const_type arg1 = CONST_ARG_MACRO(0); \ 199 | v##type *arg2 = (v##type*)PG_GETARG_POINTER(1); \ 200 | v##type *res = buildv##type(BATCHSIZE, arg2->skipref); \ 201 | size = arg2->dim;\ 202 | while(i < size) \ 203 | { \ 204 | res->isnull[i] = arg2->isnull[i]; \ 205 | i++; \ 206 | } \ 207 | i=0; \ 208 | while(i < size) \ 209 | { \ 210 | if(!res->isnull[i]) \ 211 | res->values[i] = XTYPE##GetDatum(((type)arg1) opsym (DatumGet##XTYPE(arg2->values[i]))); \ 212 | i ++ ;\ 213 | } \ 214 | res->dim = arg2->dim; \ 215 | PG_RETURN_POINTER(res); \ 216 | } 217 | 218 | 219 | /* 220 | * Comparision function for the abstract data types, this MACRO is used for the 221 | * V-types OP V-types. 222 | * e.g. extern Datum vint2vint2eq(PG_FUNCTION_ARGS); 223 | */ 224 | #define __FUNCTION_CMP(type1, XTYPE1, type2, XTYPE2, cmpsym, cmpstr) \ 225 | PG_FUNCTION_INFO_V1(v##type1##v##type2##cmpstr); \ 226 | Datum \ 227 | v##type1##v##type2##cmpstr(PG_FUNCTION_ARGS) \ 228 | { \ 229 | vbool *res; \ 230 | int size = 0; \ 231 | int i = 0; \ 232 | v##type1 *arg1 = (v##type1*)PG_GETARG_POINTER(0); \ 233 | v##type2 *arg2 = (v##type2*)PG_GETARG_POINTER(1); \ 234 | Assert(arg1->dim == arg2->dim); \ 235 | res = buildvtype(BOOLOID, BATCHSIZE, arg1->skipref); \ 236 | size = arg1->dim; \ 237 | while(i < size) \ 238 | { \ 239 | res->isnull[i] = arg1->isnull[i] || arg2->isnull[i]; \ 240 | i++; \ 241 | } \ 242 | i=0; \ 243 | while(i < size) \ 244 | { \ 245 | if(!res->isnull[i]) \ 246 | res->values[i] = BoolGetDatum(DatumGet##XTYPE1(arg1->values[i]) cmpsym (DatumGet##XTYPE2(arg2->values[i]))); \ 247 | i++; \ 248 | } \ 249 | res->dim = arg1->dim; \ 250 | PG_RETURN_POINTER(res); \ 251 | } 252 | 253 | /* 254 | * Comparision function for the abstract data types, this MACRO is used for the 255 | * V-types OP Consts. 256 | * e.g. extern Datum vint2int2eq(PG_FUNCTION_ARGS); 257 | */ 258 | #define __FUNCTION_CMP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, cmpsym, cmpstr) \ 259 | PG_FUNCTION_INFO_V1(v##type##const_type##cmpstr); \ 260 | Datum \ 261 | v##type##const_type##cmpstr(PG_FUNCTION_ARGS) \ 262 | { \ 263 | int size = 0; \ 264 | int i = 0; \ 265 | v##type *arg1 = (v##type*)PG_GETARG_POINTER(0); \ 266 | const_type arg2 = CONST_ARG_MACRO(1); \ 267 | vbool *res = buildvtype(BOOLOID, BATCHSIZE, arg1->skipref); \ 268 | size = arg1->dim; \ 269 | while(i < size) \ 270 | { \ 271 | res->isnull[i] = arg1->isnull[i]; \ 272 | i++; \ 273 | } \ 274 | i=0; \ 275 | while(i < size) \ 276 | { \ 277 | if(!res->isnull[i]) \ 278 | res->values[i] = BoolGetDatum((DatumGet##XTYPE(arg1->values[i])) cmpsym arg2); \ 279 | i++; \ 280 | } \ 281 | res->dim = arg1->dim; \ 282 | PG_RETURN_POINTER(res); \ 283 | } 284 | 285 | //Macro Level 3 286 | /* These MACRO will be expanded when the code is compiled. */ 287 | #define _FUNCTION_OP(type1, XTYPE1, type2, XTYPE2) \ 288 | __FUNCTION_OP(type1, XTYPE1, type2, XTYPE2, +, pl) \ 289 | __FUNCTION_OP(type1, XTYPE1, type2, XTYPE2, -, mi) \ 290 | __FUNCTION_OP(type1, XTYPE1, type2, XTYPE2, *, mul) \ 291 | __FUNCTION_OP(type1, XTYPE1, type2, XTYPE2, /, div) 292 | 293 | #define _FUNCTION_DATE_OP_CONST(type, XTYPE, const_type, CONST_ARG_MACRO) \ 294 | __FUNCTION_OP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, +, pl) \ 295 | __FUNCTION_OP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, -, mi) \ 296 | __FUNCTION_OP_LCONST(type, XTYPE, const_type, CONST_ARG_MACRO, +, pl) \ 297 | __FUNCTION_OP_LCONST(type, XTYPE, const_type, CONST_ARG_MACRO, -, mi) 298 | 299 | #define _FUNCTION_OP_CONST(type, XTYPE, const_type, CONST_ARG_MACRO) \ 300 | __FUNCTION_OP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, +, pl) \ 301 | __FUNCTION_OP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, -, mi) \ 302 | __FUNCTION_OP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, *, mul) \ 303 | __FUNCTION_OP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, /, div) \ 304 | __FUNCTION_OP_LCONST(type, XTYPE, const_type, CONST_ARG_MACRO, +, pl) \ 305 | __FUNCTION_OP_LCONST(type, XTYPE, const_type, CONST_ARG_MACRO, -, mi) \ 306 | __FUNCTION_OP_LCONST(type, XTYPE, const_type, CONST_ARG_MACRO, *, mul) \ 307 | __FUNCTION_OP_LCONST(type, XTYPE, const_type, CONST_ARG_MACRO, /, div) 308 | 309 | #define _FUNCTION_CMP(type1, XTYPE1, type2, XTYPE2) \ 310 | __FUNCTION_CMP(type1, XTYPE1, type2, XTYPE2, ==, eq) \ 311 | __FUNCTION_CMP(type1, XTYPE1, type2, XTYPE2, !=, ne) \ 312 | __FUNCTION_CMP(type1, XTYPE1, type2, XTYPE2, >, gt) \ 313 | __FUNCTION_CMP(type1, XTYPE1, type2, XTYPE2, >=, ge) \ 314 | __FUNCTION_CMP(type1, XTYPE1, type2, XTYPE2, <, lt) \ 315 | __FUNCTION_CMP(type1, XTYPE1, type2, XTYPE2, <=, le) 316 | 317 | #define _FUNCTION_CMP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO) \ 318 | __FUNCTION_CMP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, ==, eq) \ 319 | __FUNCTION_CMP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, !=, ne) \ 320 | __FUNCTION_CMP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, >, gt) \ 321 | __FUNCTION_CMP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, >=, ge) \ 322 | __FUNCTION_CMP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, <, lt) \ 323 | __FUNCTION_CMP_RCONST(type, XTYPE, const_type, CONST_ARG_MACRO, <=, le) \ 324 | 325 | //Macro Level 2 326 | #define FUNCTION_OP(type, XTYPE1) \ 327 | _FUNCTION_OP(type, XTYPE1, int2, Int16) \ 328 | _FUNCTION_OP(type, XTYPE1, int4, Int32) \ 329 | _FUNCTION_OP(type, XTYPE1, int8, Int64) \ 330 | _FUNCTION_OP(type, XTYPE1, float4, Float4) \ 331 | _FUNCTION_OP(type, XTYPE1, float8, Float8) 332 | 333 | #define FUNCTION_DATE_OP_RCONST(type, XTYPE) \ 334 | _FUNCTION_DATE_OP_CONST(type, XTYPE, DateADT, PG_GETARG_DATEADT) \ 335 | _FUNCTION_DATE_OP_CONST(type, XTYPE, Interval, PG_GETARG_INTERVAL_P) \ 336 | _FUNCTION_DATE_OP_CONST(type, XTYPE, int4, PG_GETARG_INT32) 337 | 338 | #define FUNCTION_OP_RCONST(type, XTYPE) \ 339 | _FUNCTION_OP_CONST(type, XTYPE, int2, PG_GETARG_INT16) \ 340 | _FUNCTION_OP_CONST(type, XTYPE, int4, PG_GETARG_INT32) \ 341 | _FUNCTION_OP_CONST(type, XTYPE, int8, PG_GETARG_INT64) \ 342 | _FUNCTION_OP_CONST(type, XTYPE, float4, PG_GETARG_FLOAT4) \ 343 | _FUNCTION_OP_CONST(type, XTYPE, float8, PG_GETARG_FLOAT8) 344 | 345 | #define FUNCTION_CMP(type1, XTYPE1) \ 346 | _FUNCTION_CMP(type1, XTYPE1, int2, Int16) \ 347 | _FUNCTION_CMP(type1, XTYPE1, int4, Int32) \ 348 | _FUNCTION_CMP(type1, XTYPE1, int8, Int64) \ 349 | _FUNCTION_CMP(type1, XTYPE1, float4, Float4) \ 350 | _FUNCTION_CMP(type1, XTYPE1, float8, Float8) 351 | 352 | #define FUNCTION_CMP_RCONST(type, XTYPE) \ 353 | _FUNCTION_CMP_RCONST(type, XTYPE, int2, PG_GETARG_INT16) \ 354 | _FUNCTION_CMP_RCONST(type, XTYPE, int4, PG_GETARG_INT32) \ 355 | _FUNCTION_CMP_RCONST(type, XTYPE, int8, PG_GETARG_INT64) \ 356 | _FUNCTION_CMP_RCONST(type, XTYPE, float4, PG_GETARG_FLOAT4) \ 357 | _FUNCTION_CMP_RCONST(type, XTYPE, float8, PG_GETARG_FLOAT8) 358 | 359 | //Macro Level 1 360 | #define FUNCTION_OP_ALL(type, XTYPE1) \ 361 | FUNCTION_OP(type, XTYPE1) \ 362 | FUNCTION_OP_RCONST(type, XTYPE1) \ 363 | FUNCTION_CMP(type, XTYPE1) \ 364 | FUNCTION_CMP_RCONST(type, XTYPE1) 365 | 366 | #define FUNCTION_BUILD(type,fname, typeoid) \ 367 | _FUNCTION_BUILD(type, typeoid) \ 368 | _FUNCTION_IN(type,fname, typeoid) \ 369 | _FUNCTION_OUT(type, fname, typeoid) 370 | 371 | //Macro Level 0 372 | FUNCTION_BUILD(int2, int2, INT2OID) 373 | FUNCTION_BUILD(int4, int4, INT4OID) 374 | FUNCTION_BUILD(int8, int8, INT8OID) 375 | FUNCTION_BUILD(float4, float4, FLOAT4OID) 376 | FUNCTION_BUILD(float8, float8, FLOAT8OID) 377 | FUNCTION_BUILD(bool, bool, BOOLOID) 378 | FUNCTION_BUILD(text, text, TEXTOID) 379 | FUNCTION_BUILD(bpchar, bpchar, BPCHAROID) 380 | 381 | FUNCTION_OP_ALL(int2, Int16) 382 | FUNCTION_OP_ALL(int4, Int32) 383 | FUNCTION_OP_ALL(int8, Int64) 384 | FUNCTION_OP_ALL(float4, Float4) 385 | FUNCTION_OP_ALL(float8, Float8) 386 | FUNCTION_OP_ALL(bool, Bool) 387 | -------------------------------------------------------------------------------- /vtype/vtype.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #ifndef __VTYPE_H___ 21 | #define __VTYPE_H___ 22 | 23 | #include "postgres.h" 24 | #include "fmgr.h" 25 | 26 | typedef int16 int2; 27 | typedef int32 int4; 28 | 29 | #define BATCHSIZE 1024 30 | typedef struct vtype { 31 | Oid elemtype; 32 | int dim; 33 | bool isnull[BATCHSIZE]; 34 | Datum values[BATCHSIZE]; 35 | bool *skipref; 36 | }vtype; 37 | 38 | 39 | #define CANARYSIZE sizeof(char) 40 | #define VTYPEHEADERSZ (sizeof(vtype)) 41 | #define VDATUMSZ(dim) (sizeof(Datum) * dim) 42 | #define ISNULLSZ(dim) (sizeof(bool) * dim) 43 | #define VTYPESIZE(dim) (VTYPEHEADERSZ + VDATUMSZ(dim) + CANARYSIZE + ISNULLSZ(dim)) 44 | #define CANARYOFFSET(vtype) ((char*)((unsigned char*)vtype + VTYPEHEADERSZ + VDATUMSZ(dim))) 45 | #define ISNULLOFFSET(vtype) ((bool*)((unsigned char*)vtype + VTYPEHEADERSZ + VDATUMSZ(vtype->dim) + CANARYSIZE)) 46 | #define VTYPE_STURCTURE(type) typedef struct vtype v##type; 47 | 48 | #define FUNCTION_BUILD_HEADER(type) \ 49 | v##type* buildv##type(int dim, bool *skip); 50 | 51 | /* 52 | * Operator function for the abstract data types, this MACRO is used for the 53 | * V-types OP V-types. 54 | * e.g. extern Datum vint2vint2pl(PG_FUNCTION_ARGS); 55 | */ 56 | #define __FUNCTION_OP_HEADER(type1, type2, opsym, opstr) \ 57 | extern Datum v##type1##v##type2##opstr(PG_FUNCTION_ARGS); 58 | 59 | /* 60 | * Operator function for the abstract data types, this MACRO is used for the 61 | * V-types OP Consts. 62 | * e.g. extern Datum vint2int2pl(PG_FUNCTION_ARGS); 63 | */ 64 | #define __FUNCTION_OP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO, opsym, opstr) \ 65 | extern Datum v##type##const_type##opstr(PG_FUNCTION_ARGS); 66 | #define __FUNCTION_OP_LCONST_HEADER(type, const_type, CONST_ARG_MACRO, opsym, opstr) \ 67 | extern Datum \ 68 | const_type##v##type##opstr(PG_FUNCTION_ARGS); 69 | /* 70 | * Comparision function for the abstract data types, this MACRO is used for the 71 | * V-types OP V-types. 72 | * e.g. extern Datum vint2vint2eq(PG_FUNCTION_ARGS); 73 | */ 74 | #define __FUNCTION_CMP_HEADER(type1, type2, cmpsym, cmpstr) \ 75 | extern Datum v##type1##v##type2##cmpstr(PG_FUNCTION_ARGS); 76 | 77 | /* 78 | * Comparision function for the abstract data types, this MACRO is used for the 79 | * V-types OP Consts. 80 | * e.g. extern Datum vint2int2eq(PG_FUNCTION_ARGS); 81 | */ 82 | #define __FUNCTION_CMP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO, cmpsym, cmpstr) \ 83 | extern Datum v##type##const_type##cmpstr(PG_FUNCTION_ARGS); 84 | 85 | #define _FUNCTION_OP_HEADER(type1, type2) \ 86 | __FUNCTION_OP_HEADER(type1, type2, +, pl) \ 87 | __FUNCTION_OP_HEADER(type1, type2, -, mi) \ 88 | __FUNCTION_OP_HEADER(type1, type2, *, mul) \ 89 | __FUNCTION_OP_HEADER(type1, type2, /, div) 90 | 91 | #define _FUNCTION_OP_CONST_HEADER(type, const_type, CONST_ARG_MACRO) \ 92 | __FUNCTION_OP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO, +, pl) \ 93 | __FUNCTION_OP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO, -, mi) \ 94 | __FUNCTION_OP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO, *, mul) \ 95 | __FUNCTION_OP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO, /, div) \ 96 | __FUNCTION_OP_LCONST_HEADER(type, const_type, CONST_ARG_MACRO, +, pl) \ 97 | __FUNCTION_OP_LCONST_HEADER(type, const_type, CONST_ARG_MACRO, -, mi) \ 98 | __FUNCTION_OP_LCONST_HEADER(type, const_type, CONST_ARG_MACRO, *, mul) \ 99 | __FUNCTION_OP_LCONST_HEADER(type, const_type, CONST_ARG_MACRO, /, div) 100 | 101 | #define _FUNCTION_CMP_HEADER(type1, type2) \ 102 | __FUNCTION_CMP_HEADER(type1, type2, ==, eq) \ 103 | __FUNCTION_CMP_HEADER(type1, type2, !=, ne) \ 104 | __FUNCTION_CMP_HEADER(type1, type2, >, gt) \ 105 | __FUNCTION_CMP_HEADER(type1, type2, >=, ge) \ 106 | __FUNCTION_CMP_HEADER(type1, type2, <, lt) \ 107 | __FUNCTION_CMP_HEADER(type1, type2, <=, le) 108 | 109 | #define _FUNCTION_CMP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO) \ 110 | __FUNCTION_CMP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO, ==, eq) \ 111 | __FUNCTION_CMP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO, !=, ne) \ 112 | __FUNCTION_CMP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO, >, gt) \ 113 | __FUNCTION_CMP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO, >=, ge) \ 114 | __FUNCTION_CMP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO, <, lt) \ 115 | __FUNCTION_CMP_RCONST_HEADER(type, const_type, CONST_ARG_MACRO, <=, le) 116 | 117 | /* 118 | * IN function for the abstract data types 119 | * e.g. Datum vint2in(PG_FUNCTION_ARGS) 120 | */ 121 | #define FUNCTION_IN_HEADER(type, typeoid) \ 122 | extern Datum v##type##in(PG_FUNCTION_ARGS); 123 | 124 | /* 125 | * OUT function for the abstract data types 126 | * e.g. Datum vint2out(PG_FUNCTION_ARGS) 127 | */ 128 | #define FUNCTION_OUT_HEADER(type, typeoid) \ 129 | extern Datum v##type##out(PG_FUNCTION_ARGS); 130 | 131 | #define FUNCTION_OP_HEADER(type) \ 132 | _FUNCTION_OP_HEADER(type, int2) \ 133 | _FUNCTION_OP_HEADER(type, int4) \ 134 | _FUNCTION_OP_HEADER(type, int8) \ 135 | _FUNCTION_OP_HEADER(type, float4) \ 136 | _FUNCTION_OP_HEADER(type, float8) 137 | 138 | #define FUNCTION_OP_CONST_HEADER(type) \ 139 | _FUNCTION_OP_CONST_HEADER(type, int2, PG_GETARG_INT16) \ 140 | _FUNCTION_OP_CONST_HEADER(type, int4, PG_GETARG_INT32) \ 141 | _FUNCTION_OP_CONST_HEADER(type, int8, PG_GETARG_INT64) \ 142 | _FUNCTION_OP_CONST_HEADER(type, float4, PG_GETARG_FLOAT4) \ 143 | _FUNCTION_OP_CONST_HEADER(type, float8, PG_GETARG_FLOAT8) 144 | 145 | #define FUNCTION_CMP_HEADER(type1) \ 146 | _FUNCTION_CMP_HEADER(type1, int2) \ 147 | _FUNCTION_CMP_HEADER(type1, int4) \ 148 | _FUNCTION_CMP_HEADER(type1, int8) \ 149 | _FUNCTION_CMP_HEADER(type1, float4) \ 150 | _FUNCTION_CMP_HEADER(type1, float8) 151 | 152 | #define FUNCTION_CMP_RCONST_HEADER(type) \ 153 | _FUNCTION_CMP_RCONST_HEADER(type, int2, PG_GETARG_INT16) \ 154 | _FUNCTION_CMP_RCONST_HEADER(type, int4, PG_GETARG_INT32) \ 155 | _FUNCTION_CMP_RCONST_HEADER(type, int8, PG_GETARG_INT64) \ 156 | _FUNCTION_CMP_RCONST_HEADER(type, float4, PG_GETARG_FLOAT4) \ 157 | _FUNCTION_CMP_RCONST_HEADER(type, float8, PG_GETARG_FLOAT8) 158 | 159 | #define FUNCTION_OP_ALL_HEADER(type) \ 160 | FUNCTION_OP_HEADER(type) \ 161 | FUNCTION_OP_CONST_HEADER(type) \ 162 | FUNCTION_CMP_HEADER(type) \ 163 | FUNCTION_CMP_RCONST_HEADER(type) \ 164 | 165 | #define TYPE_HEADER(type,oid) \ 166 | VTYPE_STURCTURE(type) \ 167 | FUNCTION_BUILD_HEADER(type) \ 168 | FUNCTION_OP_ALL_HEADER(type) \ 169 | FUNCTION_IN_HEADER(type, typeoid) \ 170 | FUNCTION_OUT_HEADER(type, typeoid) \ 171 | 172 | TYPE_HEADER(int2, INT2OID) 173 | TYPE_HEADER(int4, INT4OID) 174 | TYPE_HEADER(int8, INT8OID) 175 | TYPE_HEADER(float4, FLOAT4OID) 176 | TYPE_HEADER(float8, FLOAT8OID) 177 | TYPE_HEADER(bool, BOOLOID) 178 | TYPE_HEADER(text, TEXTOID) 179 | TYPE_HEADER(date, DATEOID) 180 | TYPE_HEADER(bpchar, BPCHAROID) 181 | 182 | extern vtype* buildvtype(Oid elemtype,int dim,bool *skip); 183 | extern void destroyvtype(vtype** vt); 184 | #endif 185 | -------------------------------------------------------------------------------- /vtype/vvarchar.c: -------------------------------------------------------------------------------- 1 | #include "vvarchar.h" 2 | #include "vtype.h" 3 | 4 | PG_FUNCTION_INFO_V1(vvarchar_in); 5 | PG_FUNCTION_INFO_V1(vvarchar_out); 6 | 7 | 8 | Datum vvarcharin(PG_FUNCTION_ARGS) 9 | { 10 | elog(ERROR, "vvarchar_in not supported"); 11 | } 12 | 13 | Datum vvarcharout(PG_FUNCTION_ARGS) 14 | { 15 | elog(ERROR, "vvarchar_out not supported"); 16 | } 17 | -------------------------------------------------------------------------------- /vtype/vvarchar.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR_ENGINE_VTYPE_VVARCHAR_H 2 | #define VECTOR_ENGINE_VTYPE_VVARCHAR_H 3 | #include "postgres.h" 4 | #include "fmgr.h" 5 | typedef struct vtype vvarchar; 6 | extern vvarchar *buildvvarchar(int dim, bool *skip); 7 | 8 | 9 | extern Datum vvarcharin(PG_FUNCTION_ARGS); 10 | extern Datum vvarcharout(PG_FUNCTION_ARGS); 11 | 12 | #endif 13 | --------------------------------------------------------------------------------