1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-30 11:03:19 +03:00

Massive commit to run PGINDENT on all *.c and *.h files.

This commit is contained in:
Bruce Momjian
1997-09-07 05:04:48 +00:00
parent 8fecd4febf
commit 1ccd423235
687 changed files with 150775 additions and 136888 deletions

View File

@ -8,8 +8,8 @@
* For example array_int4eq returns true if some of the elements * For example array_int4eq returns true if some of the elements
* of an array of int4 is equal to the given value: * of an array of int4 is equal to the given value:
* *
* array_int4eq({1,2,3}, 1) --> true * array_int4eq({1,2,3}, 1) --> true
* array_int4eq({1,2,3}, 4) --> false * array_int4eq({1,2,3}, 4) --> false
* *
* If we have defined T array types and O scalar operators * If we have defined T array types and O scalar operators
* we can define T x O array operators, each of them has a name * we can define T x O array operators, each of them has a name
@ -19,10 +19,10 @@
* the array_int4_like because there is no like operator for int4. * the array_int4_like because there is no like operator for int4.
* It is now possible to write queries which look inside the arrays: * It is now possible to write queries which look inside the arrays:
* *
* create table t(id int4[], txt text[]); * create table t(id int4[], txt text[]);
* select * from t where t.id *= 123; * select * from t where t.id *= 123;
* select * from t where t.txt *~ '[a-z]'; * select * from t where t.txt *~ '[a-z]';
* select * from t where t.txt[1:3] **~ '[a-z]'; * select * from t where t.txt[1:3] **~ '[a-z]';
* *
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it> * Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
*/ */
@ -40,93 +40,116 @@
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/elog.h" #include "utils/elog.h"
static int32 static int32
array_iterator(Oid elemtype, Oid proc, int and, ArrayType *array, Datum value) array_iterator(Oid elemtype, Oid proc, int and, ArrayType * array, Datum value)
{ {
HeapTuple typ_tuple; HeapTuple typ_tuple;
TypeTupleForm typ_struct; TypeTupleForm typ_struct;
bool typbyval; bool typbyval;
int typlen; int typlen;
func_ptr proc_fn; func_ptr proc_fn;
int pronargs; int pronargs;
int nitems, i, result; int nitems,
int ndim, *dim; i,
char *p; result;
int ndim,
*dim;
char *p;
/* Sanity checks */ /* Sanity checks */
if ((array == (ArrayType *) NULL) if ((array == (ArrayType *) NULL)
|| (ARR_IS_LO(array) == true)) { || (ARR_IS_LO(array) == true))
/* elog(NOTICE, "array_iterator: array is null"); */ {
return (0); /* elog(NOTICE, "array_iterator: array is null"); */
} return (0);
ndim = ARR_NDIM(array); }
dim = ARR_DIMS(array); ndim = ARR_NDIM(array);
nitems = getNitems(ndim, dim); dim = ARR_DIMS(array);
if (nitems == 0) { nitems = getNitems(ndim, dim);
/* elog(NOTICE, "array_iterator: nitems = 0"); */ if (nitems == 0)
return (0); {
} /* elog(NOTICE, "array_iterator: nitems = 0"); */
/* Lookup element type information */
typ_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(elemtype),0,0,0);
if (!HeapTupleIsValid(typ_tuple)) {
elog(WARN,"array_iterator: cache lookup failed for type %d", elemtype);
return 0;
}
typ_struct = (TypeTupleForm) GETSTRUCT(typ_tuple);
typlen = typ_struct->typlen;
typbyval = typ_struct->typbyval;
/* Lookup the function entry point */
proc_fn == (func_ptr) NULL;
fmgr_info(proc, &proc_fn, &pronargs);
if ((proc_fn == NULL) || (pronargs != 2)) {
elog(WARN, "array_iterator: fmgr_info lookup failed for oid %d", proc);
return (0);
}
/* Scan the array and apply the operator to each element */
result = 0;
p = ARR_DATA_PTR(array);
for (i = 0; i < nitems; i++) {
if (typbyval) {
switch(typlen) {
case 1:
result = (int) (*proc_fn)(*p, value);
break;
case 2:
result = (int) (*proc_fn)(* (int16 *) p, value);
break;
case 3:
case 4:
result = (int) (*proc_fn)(* (int32 *) p, value);
break;
}
p += typlen;
} else {
result = (int) (*proc_fn)(p, value);
if (typlen > 0) {
p += typlen;
} else {
p += INTALIGN(* (int32 *) p);
}
}
if (result) {
if (!and) {
return (1);
}
} else {
if (and) {
return (0); return (0);
}
} }
}
if (and && result) { /* Lookup element type information */
return (1); typ_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(elemtype), 0, 0, 0);
} else { if (!HeapTupleIsValid(typ_tuple))
return (0); {
} elog(WARN, "array_iterator: cache lookup failed for type %d", elemtype);
return 0;
}
typ_struct = (TypeTupleForm) GETSTRUCT(typ_tuple);
typlen = typ_struct->typlen;
typbyval = typ_struct->typbyval;
/* Lookup the function entry point */
proc_fn == (func_ptr) NULL;
fmgr_info(proc, &proc_fn, &pronargs);
if ((proc_fn == NULL) || (pronargs != 2))
{
elog(WARN, "array_iterator: fmgr_info lookup failed for oid %d", proc);
return (0);
}
/* Scan the array and apply the operator to each element */
result = 0;
p = ARR_DATA_PTR(array);
for (i = 0; i < nitems; i++)
{
if (typbyval)
{
switch (typlen)
{
case 1:
result = (int) (*proc_fn) (*p, value);
break;
case 2:
result = (int) (*proc_fn) (*(int16 *) p, value);
break;
case 3:
case 4:
result = (int) (*proc_fn) (*(int32 *) p, value);
break;
}
p += typlen;
}
else
{
result = (int) (*proc_fn) (p, value);
if (typlen > 0)
{
p += typlen;
}
else
{
p += INTALIGN(*(int32 *) p);
}
}
if (result)
{
if (!and)
{
return (1);
}
}
else
{
if (and)
{
return (0);
}
}
}
if (and && result)
{
return (1);
}
else
{
return (0);
}
} }
/* /*
@ -134,39 +157,39 @@ array_iterator(Oid elemtype, Oid proc, int and, ArrayType *array, Datum value)
*/ */
int32 int32
array_texteq(ArrayType *array, char* value) array_texteq(ArrayType * array, char *value)
{ {
return array_iterator((Oid) 25, /* text */ return array_iterator((Oid) 25, /* text */
(Oid) 67, /* texteq */ (Oid) 67, /* texteq */
0, /* logical or */ 0, /* logical or */
array, (Datum)value); array, (Datum) value);
} }
int32 int32
array_all_texteq(ArrayType *array, char* value) array_all_texteq(ArrayType * array, char *value)
{ {
return array_iterator((Oid) 25, /* text */ return array_iterator((Oid) 25, /* text */
(Oid) 67, /* texteq */ (Oid) 67, /* texteq */
1, /* logical and */ 1, /* logical and */
array, (Datum)value); array, (Datum) value);
} }
int32 int32
array_textregexeq(ArrayType *array, char* value) array_textregexeq(ArrayType * array, char *value)
{ {
return array_iterator((Oid) 25, /* text */ return array_iterator((Oid) 25, /* text */
(Oid) 81, /* textregexeq */ (Oid) 81, /* textregexeq */
0, /* logical or */ 0, /* logical or */
array, (Datum)value); array, (Datum) value);
} }
int32 int32
array_all_textregexeq(ArrayType *array, char* value) array_all_textregexeq(ArrayType * array, char *value)
{ {
return array_iterator((Oid) 25, /* text */ return array_iterator((Oid) 25, /* text */
(Oid) 81, /* textregexeq */ (Oid) 81, /* textregexeq */
1, /* logical and */ 1, /* logical and */
array, (Datum)value); array, (Datum) value);
} }
/* /*
@ -175,39 +198,39 @@ array_all_textregexeq(ArrayType *array, char* value)
*/ */
int32 int32
array_char16eq(ArrayType *array, char* value) array_char16eq(ArrayType * array, char *value)
{ {
return array_iterator((Oid) 20, /* char16 */ return array_iterator((Oid) 20, /* char16 */
(Oid) 490, /* char16eq */ (Oid) 490, /* char16eq */
0, /* logical or */ 0, /* logical or */
array, (Datum)value); array, (Datum) value);
} }
int32 int32
array_all_char16eq(ArrayType *array, char* value) array_all_char16eq(ArrayType * array, char *value)
{ {
return array_iterator((Oid) 20, /* char16 */ return array_iterator((Oid) 20, /* char16 */
(Oid) 490, /* char16eq */ (Oid) 490, /* char16eq */
1, /* logical and */ 1, /* logical and */
array, (Datum)value); array, (Datum) value);
} }
int32 int32
array_char16regexeq(ArrayType *array, char* value) array_char16regexeq(ArrayType * array, char *value)
{ {
return array_iterator((Oid) 20, /* char16 */ return array_iterator((Oid) 20, /* char16 */
(Oid) 700, /* char16regexeq */ (Oid) 700, /* char16regexeq */
0, /* logical or */ 0, /* logical or */
array, (Datum)value); array, (Datum) value);
} }
int32 int32
array_all_char16regexeq(ArrayType *array, char* value) array_all_char16regexeq(ArrayType * array, char *value)
{ {
return array_iterator((Oid) 20, /* char16 */ return array_iterator((Oid) 20, /* char16 */
(Oid) 700, /* char16regexeq */ (Oid) 700, /* char16regexeq */
1, /* logical and */ 1, /* logical and */
array, (Datum)value); array, (Datum) value);
} }
/* /*
@ -215,37 +238,37 @@ array_all_char16regexeq(ArrayType *array, char* value)
*/ */
int32 int32
array_int4eq(ArrayType *array, int4 value) array_int4eq(ArrayType * array, int4 value)
{ {
return array_iterator((Oid) 23, /* int4 */ return array_iterator((Oid) 23, /* int4 */
(Oid) 65, /* int4eq */ (Oid) 65, /* int4eq */
0, /* logical or */ 0, /* logical or */
array, (Datum)value); array, (Datum) value);
} }
int32 int32
array_all_int4eq(ArrayType *array, int4 value) array_all_int4eq(ArrayType * array, int4 value)
{ {
return array_iterator((Oid) 23, /* int4 */ return array_iterator((Oid) 23, /* int4 */
(Oid) 65, /* int4eq */ (Oid) 65, /* int4eq */
1, /* logical and */ 1, /* logical and */
array, (Datum)value); array, (Datum) value);
} }
int32 int32
array_int4gt(ArrayType *array, int4 value) array_int4gt(ArrayType * array, int4 value)
{ {
return array_iterator((Oid) 23, /* int4 */ return array_iterator((Oid) 23, /* int4 */
(Oid) 147, /* int4gt */ (Oid) 147, /* int4gt */
0, /* logical or */ 0, /* logical or */
array, (Datum)value); array, (Datum) value);
} }
int32 int32
array_all_int4gt(ArrayType *array, int4 value) array_all_int4gt(ArrayType * array, int4 value)
{ {
return array_iterator((Oid) 23, /* int4 */ return array_iterator((Oid) 23, /* int4 */
(Oid) 147, /* int4gt */ (Oid) 147, /* int4gt */
1, /* logical and */ 1, /* logical and */
array, (Datum)value); array, (Datum) value);
} }

View File

@ -13,86 +13,99 @@
#include "utils/datetime.h" #include "utils/datetime.h"
TimeADT *time_difference(TimeADT * time1, TimeADT * time2) TimeADT *
time_difference(TimeADT * time1, TimeADT * time2)
{ {
TimeADT *result = (TimeADT *) palloc(sizeof(TimeADT)); TimeADT *result = (TimeADT *) palloc(sizeof(TimeADT));
*result = *time1 - *time2;
return (result); *result = *time1 - *time2;
return (result);
} }
TimeADT *currenttime() TimeADT *
currenttime()
{ {
time_t current_time; time_t current_time;
struct tm *tm; struct tm *tm;
TimeADT *result = (TimeADT *) palloc(sizeof(TimeADT)); TimeADT *result = (TimeADT *) palloc(sizeof(TimeADT));
current_time = time(NULL); current_time = time(NULL);
tm = localtime(&current_time); tm = localtime(&current_time);
*result = ((((tm->tm_hour*60)+tm->tm_min)*60)+tm->tm_sec); *result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec);
return (result); return (result);
} }
DateADT currentdate() DateADT
currentdate()
{ {
time_t current_time; time_t current_time;
struct tm *tm; struct tm *tm;
DateADT result; DateADT result;
current_time = time(NULL);
tm = localtime(&current_time);
result = date2j(tm->tm_year,tm->tm_mon + 1,tm->tm_mday) - current_time = time(NULL);
date2j(100,1,1); tm = localtime(&current_time);
return (result);
result = date2j(tm->tm_year, tm->tm_mon + 1, tm->tm_mday) -
date2j(100, 1, 1);
return (result);
} }
int4 hours(TimeADT * time) int4
hours(TimeADT * time)
{ {
return(*time / (60*60)); return (*time / (60 * 60));
} }
int4 minutes(TimeADT * time) int4
minutes(TimeADT * time)
{ {
return(((int) (*time / 60)) % 60); return (((int) (*time / 60)) % 60);
} }
int4 seconds(TimeADT * time) int4
seconds(TimeADT * time)
{ {
return(((int) *time) % 60); return (((int) *time) % 60);
} }
int4 day(DateADT *date) int4
day(DateADT * date)
{ {
struct tm tm; struct tm tm;
j2date( (*date + date2j(2000,1,1)), j2date((*date + date2j(2000, 1, 1)),
&tm.tm_year, &tm.tm_mon, &tm.tm_mday); &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
return (tm.tm_mday); return (tm.tm_mday);
} }
int4 month(DateADT *date) int4
month(DateADT * date)
{ {
struct tm tm; struct tm tm;
j2date( (*date + date2j(2000,1,1)), j2date((*date + date2j(2000, 1, 1)),
&tm.tm_year, &tm.tm_mon, &tm.tm_mday); &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
return (tm.tm_mon); return (tm.tm_mon);
} }
int4 year(DateADT *date) int4
year(DateADT * date)
{ {
struct tm tm; struct tm tm;
j2date( (*date + date2j(2000,1,1)), j2date((*date + date2j(2000, 1, 1)),
&tm.tm_year, &tm.tm_mon, &tm.tm_mday); &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
return (tm.tm_year); return (tm.tm_year);
} }
int4 asminutes(TimeADT * time) int4
asminutes(TimeADT * time)
{ {
int seconds = (int) *time; int seconds = (int) *time;
return (seconds / 60); return (seconds / 60);
} }
int4 asseconds(TimeADT * time) int4
asseconds(TimeADT * time)
{ {
int seconds = (int) *time; int seconds = (int) *time;
return (seconds); return (seconds);
} }

View File

@ -1,12 +1,12 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* int8.c-- * int8.c--
* Internal 64-bit integer operations * Internal 64-bit integer operations
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include <stdio.h> /* for sprintf proto, etc. */ #include <stdio.h> /* for sprintf proto, etc. */
#include <stdlib.h> /* for strtod, etc. */ #include <stdlib.h> /* for strtod, etc. */
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <time.h> #include <time.h>
@ -17,7 +17,7 @@
#include "postgres.h" #include "postgres.h"
#include "utils/palloc.h" #include "utils/palloc.h"
#define MAXINT8LEN 25 #define MAXINT8LEN 25
#define USE_LOCAL_CODE 1 #define USE_LOCAL_CODE 1
@ -26,53 +26,58 @@
#endif #endif
#ifndef HAVE_64BIT_INTS #ifndef HAVE_64BIT_INTS
typedef char[8] int64; typedef char [8] int64;
#elif defined(__alpha) #elif defined(__alpha)
typedef long int int64; typedef long int int64;
#define INT64_FORMAT "%ld" #define INT64_FORMAT "%ld"
#elif defined(__GNUC__) #elif defined(__GNUC__)
typedef long long int int64; typedef long long int int64;
#define INT64_FORMAT "%Ld" #define INT64_FORMAT "%Ld"
#else #else
typedef long int int64; typedef long int int64;
#define INT64_FORMAT "%ld" #define INT64_FORMAT "%ld"
#endif #endif
int64 *int8in(char *str); int64 *int8in(char *str);
char *int8out(int64 *val); char *int8out(int64 * val);
bool int8eq(int64 *val1, int64 *val2); bool int8eq(int64 * val1, int64 * val2);
bool int8ne(int64 *val1, int64 *val2); bool int8ne(int64 * val1, int64 * val2);
bool int8lt(int64 *val1, int64 *val2); bool int8lt(int64 * val1, int64 * val2);
bool int8gt(int64 *val1, int64 *val2); bool int8gt(int64 * val1, int64 * val2);
bool int8le(int64 *val1, int64 *val2); bool int8le(int64 * val1, int64 * val2);
bool int8ge(int64 *val1, int64 *val2); bool int8ge(int64 * val1, int64 * val2);
bool int84eq(int64 *val1, int32 val2); bool int84eq(int64 * val1, int32 val2);
bool int84ne(int64 *val1, int32 val2); bool int84ne(int64 * val1, int32 val2);
bool int84lt(int64 *val1, int32 val2); bool int84lt(int64 * val1, int32 val2);
bool int84gt(int64 *val1, int32 val2); bool int84gt(int64 * val1, int32 val2);
bool int84le(int64 *val1, int32 val2); bool int84le(int64 * val1, int32 val2);
bool int84ge(int64 *val1, int32 val2); bool int84ge(int64 * val1, int32 val2);
int64 *int8um(int64 *val); int64 *int8um(int64 * val);
int64 *int8pl(int64 *val1, int64 *val2); int64 *int8pl(int64 * val1, int64 * val2);
int64 *int8mi(int64 *val1, int64 *val2); int64 *int8mi(int64 * val1, int64 * val2);
int64 *int8mul(int64 *val1, int64 *val2); int64 *int8mul(int64 * val1, int64 * val2);
int64 *int8div(int64 *val1, int64 *val2); int64 *int8div(int64 * val1, int64 * val2);
int64 *int48(int32 val);
int32 int84(int64 * val);
int64 *int48(int32 val);
int32 int84(int64 *val);
#if FALSE #if FALSE
int64 *int28(int16 val); int64 *int28(int16 val);
int16 int82(int64 *val); int16 int82(int64 * val);
#endif #endif
float64 i8tod(int64 *val); float64 i8tod(int64 * val);
int64 *dtoi8(float64 val); int64 *dtoi8(float64 val);
#if USE_LOCAL_CODE #if USE_LOCAL_CODE
@ -88,7 +93,7 @@ int64 *dtoi8(float64 val);
/*********************************************************************** /***********************************************************************
** **
** Routines for 64-bit integers. ** Routines for 64-bit integers.
** **
***********************************************************************/ ***********************************************************************/
@ -98,264 +103,289 @@ int64 *dtoi8(float64 val);
/* int8in() /* int8in()
*/ */
int64 *int8in(char *str) int64 *
int8in(char *str)
{ {
int64 *result = PALLOCTYPE(int64); int64 *result = PALLOCTYPE(int64);
#if HAVE_64BIT_INTS #if HAVE_64BIT_INTS
if (!PointerIsValid(str)) if (!PointerIsValid(str))
elog (WARN,"Bad (null) int8 external representation",NULL); elog(WARN, "Bad (null) int8 external representation", NULL);
if (sscanf(str, INT64_FORMAT, result) != 1) if (sscanf(str, INT64_FORMAT, result) != 1)
elog(WARN,"Bad int8 external representation '%s'",str); elog(WARN, "Bad int8 external representation '%s'", str);
#else #else
elog(WARN,"64-bit integers are not supported",NULL); elog(WARN, "64-bit integers are not supported", NULL);
result = NULL; result = NULL;
#endif #endif
return(result); return (result);
} /* int8in() */ } /* int8in() */
/* int8out() /* int8out()
*/ */
char *int8out(int64 *val) char *
int8out(int64 * val)
{ {
char *result; char *result;
int len; int len;
char buf[MAXINT8LEN+1]; char buf[MAXINT8LEN + 1];
#if HAVE_64BIT_INTS #if HAVE_64BIT_INTS
if (!PointerIsValid(val)) if (!PointerIsValid(val))
return(NULL); return (NULL);
if ((len = snprintf( buf, MAXINT8LEN, INT64_FORMAT, *val)) < 0) if ((len = snprintf(buf, MAXINT8LEN, INT64_FORMAT, *val)) < 0)
elog (WARN,"Unable to format int8",NULL); elog(WARN, "Unable to format int8", NULL);
result = PALLOC(len+1); result = PALLOC(len + 1);
strcpy(result, buf); strcpy(result, buf);
#else #else
elog(WARN,"64-bit integers are not supported",NULL); elog(WARN, "64-bit integers are not supported", NULL);
result = NULL; result = NULL;
#endif #endif
return( result); return (result);
} /* int8out() */ } /* int8out() */
/*---------------------------------------------------------- /*----------------------------------------------------------
* Relational operators for int8s. * Relational operators for int8s.
*---------------------------------------------------------*/ *---------------------------------------------------------*/
/* int8relop() /* int8relop()
* Is val1 relop val2? * Is val1 relop val2?
*/ */
bool int8eq(int64 *val1, int64 *val2) bool
int8eq(int64 * val1, int64 * val2)
{ {
return(*val1 == *val2); return (*val1 == *val2);
} /* int8eq() */ } /* int8eq() */
bool int8ne(int64 *val1, int64 *val2) bool
int8ne(int64 * val1, int64 * val2)
{ {
return(*val1 != *val2); return (*val1 != *val2);
} /* int8ne() */ } /* int8ne() */
bool int8lt(int64 *val1, int64 *val2) bool
int8lt(int64 * val1, int64 * val2)
{ {
return(*val1 < *val2); return (*val1 < *val2);
} /* int8lt() */ } /* int8lt() */
bool int8gt(int64 *val1, int64 *val2) bool
int8gt(int64 * val1, int64 * val2)
{ {
return(*val1 > *val2); return (*val1 > *val2);
} /* int8gt() */ } /* int8gt() */
bool int8le(int64 *val1, int64 *val2) bool
int8le(int64 * val1, int64 * val2)
{ {
return(*val1 <= *val2); return (*val1 <= *val2);
} /* int8le() */ } /* int8le() */
bool int8ge(int64 *val1, int64 *val2) bool
int8ge(int64 * val1, int64 * val2)
{ {
return(*val1 >= *val2); return (*val1 >= *val2);
} /* int8ge() */ } /* int8ge() */
/* int84relop() /* int84relop()
* Is 64-bit val1 relop 32-bit val2? * Is 64-bit val1 relop 32-bit val2?
*/ */
bool int84eq(int64 *val1, int32 val2) bool
int84eq(int64 * val1, int32 val2)
{ {
return(*val1 == val2); return (*val1 == val2);
} /* int84eq() */ } /* int84eq() */
bool int84ne(int64 *val1, int32 val2) bool
int84ne(int64 * val1, int32 val2)
{ {
return(*val1 != val2); return (*val1 != val2);
} /* int84ne() */ } /* int84ne() */
bool int84lt(int64 *val1, int32 val2) bool
int84lt(int64 * val1, int32 val2)
{ {
return(*val1 < val2); return (*val1 < val2);
} /* int84lt() */ } /* int84lt() */
bool int84gt(int64 *val1, int32 val2) bool
int84gt(int64 * val1, int32 val2)
{ {
return(*val1 > val2); return (*val1 > val2);
} /* int84gt() */ } /* int84gt() */
bool int84le(int64 *val1, int32 val2) bool
int84le(int64 * val1, int32 val2)
{ {
return(*val1 <= val2); return (*val1 <= val2);
} /* int84le() */ } /* int84le() */
bool int84ge(int64 *val1, int32 val2) bool
int84ge(int64 * val1, int32 val2)
{ {
return(*val1 >= val2); return (*val1 >= val2);
} /* int84ge() */ } /* int84ge() */
/*---------------------------------------------------------- /*----------------------------------------------------------
* Arithmetic operators on 64-bit integers. * Arithmetic operators on 64-bit integers.
*---------------------------------------------------------*/ *---------------------------------------------------------*/
int64 *int8um(int64 *val) int64 *
int8um(int64 * val)
{ {
int64 *result = PALLOCTYPE(int64); int64 *result = PALLOCTYPE(int64);
if (!PointerIsValid(val)) if (!PointerIsValid(val))
return NULL; return NULL;
*result = (- *val); *result = (-*val);
return(result); return (result);
} /* int8um() */ } /* int8um() */
int64 *int8pl(int64 *val1, int64 *val2) int64 *
int8pl(int64 * val1, int64 * val2)
{ {
int64 *result = PALLOCTYPE(int64); int64 *result = PALLOCTYPE(int64);
if ((!PointerIsValid(val1)) || (!PointerIsValid(val2))) if ((!PointerIsValid(val1)) || (!PointerIsValid(val2)))
return NULL; return NULL;
*result = *val1 + *val2; *result = *val1 + *val2;
return(result); return (result);
} /* int8pl() */ } /* int8pl() */
int64 *int8mi(int64 *val1, int64 *val2) int64 *
int8mi(int64 * val1, int64 * val2)
{ {
int64 *result = PALLOCTYPE(int64); int64 *result = PALLOCTYPE(int64);
if ((!PointerIsValid(val1)) || (!PointerIsValid(val2))) if ((!PointerIsValid(val1)) || (!PointerIsValid(val2)))
return NULL; return NULL;
*result = *val1 - *val2; *result = *val1 - *val2;
return(result); return (result);
} /* int8mi() */ } /* int8mi() */
int64 *int8mul(int64 *val1, int64 *val2) int64 *
int8mul(int64 * val1, int64 * val2)
{ {
int64 *result = PALLOCTYPE(int64); int64 *result = PALLOCTYPE(int64);
if ((!PointerIsValid(val1)) || (!PointerIsValid(val2))) if ((!PointerIsValid(val1)) || (!PointerIsValid(val2)))
return NULL; return NULL;
*result = *val1 * *val2; *result = *val1 * *val2;
return(result); return (result);
} /* int8mul() */ } /* int8mul() */
int64 *int8div(int64 *val1, int64 *val2) int64 *
int8div(int64 * val1, int64 * val2)
{ {
int64 *result = PALLOCTYPE(int64); int64 *result = PALLOCTYPE(int64);
if ((!PointerIsValid(val1)) || (!PointerIsValid(val2))) if ((!PointerIsValid(val1)) || (!PointerIsValid(val2)))
return NULL; return NULL;
*result = *val1 / *val2; *result = *val1 / *val2;
return(result); return (result);
} /* int8div() */ } /* int8div() */
/*---------------------------------------------------------- /*----------------------------------------------------------
* Conversion operators. * Conversion operators.
*---------------------------------------------------------*/ *---------------------------------------------------------*/
int64 *int48(int32 val) int64 *
int48(int32 val)
{ {
int64 *result = PALLOCTYPE(int64); int64 *result = PALLOCTYPE(int64);
*result = val; *result = val;
return(result); return (result);
} /* int48() */ } /* int48() */
int32 int84(int64 *val) int32
int84(int64 * val)
{ {
int32 result; int32 result;
if (!PointerIsValid(val)) if (!PointerIsValid(val))
elog(WARN,"Invalid (null) int64, can't convert int8 to int4",NULL); elog(WARN, "Invalid (null) int64, can't convert int8 to int4", NULL);
if ((*val < INT_MIN) || (*val > INT_MAX)) if ((*val < INT_MIN) || (*val > INT_MAX))
elog(WARN,"int8 conversion to int4 is out of range",NULL); elog(WARN, "int8 conversion to int4 is out of range", NULL);
result = *val; result = *val;
return(result); return (result);
} /* int84() */ } /* int84() */
#if FALSE #if FALSE
int64 *int28(int16 val) int64 *
int28(int16 val)
{ {
int64 *result; int64 *result;
if (!PointerIsValid(result = PALLOCTYPE(int64))) if (!PointerIsValid(result = PALLOCTYPE(int64)))
elog(WARN,"Memory allocation failed, can't convert int8 to int2",NULL); elog(WARN, "Memory allocation failed, can't convert int8 to int2", NULL);
*result = val; *result = val;
return(result); return (result);
} /* int28() */ } /* int28() */
int16 int82(int64 *val) int16
int82(int64 * val)
{ {
int16 result; int16 result;
if (!PointerIsValid(val)) if (!PointerIsValid(val))
elog(WARN,"Invalid (null) int8, can't convert to int2",NULL); elog(WARN, "Invalid (null) int8, can't convert to int2", NULL);
result = *val; result = *val;
return (result);
} /* int82() */
return(result);
} /* int82() */
#endif #endif
float64 i8tod(int64 *val) float64
i8tod(int64 * val)
{ {
float64 result = PALLOCTYPE(float64data); float64 result = PALLOCTYPE(float64data);
*result = *val; *result = *val;
return(result); return (result);
} /* i8tod() */ } /* i8tod() */
int64 *dtoi8(float64 val) int64 *
dtoi8(float64 val)
{ {
int64 *result = PALLOCTYPE(int64); int64 *result = PALLOCTYPE(int64);
if ((*val < (-pow(2,64)+1)) || (*val > (pow(2,64)-1))) if ((*val < (-pow(2, 64) + 1)) || (*val > (pow(2, 64) - 1)))
elog(WARN,"Floating point conversion to int64 is out of range",NULL); elog(WARN, "Floating point conversion to int64 is out of range", NULL);
*result = *val; *result = *val;
return(result);
} /* dtoi8() */
return (result);
} /* dtoi8() */

View File

@ -1,8 +1,8 @@
/* /*
** **
** halt.c ** halt.c
** **
** This is used to print out error messages and exit ** This is used to print out error messages and exit
*/ */
#include <varargs.h> #include <varargs.h>
@ -15,44 +15,46 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
** **
** halt - print error message, and call clean up routine or exit ** halt - print error message, and call clean up routine or exit
** **
**------------------------------------------------------------------------*/ **------------------------------------------------------------------------*/
/*VARARGS*/ /*VARARGS*/
void halt(va_alist) void
halt(va_alist)
va_dcl va_dcl
{ {
va_list arg_ptr; va_list arg_ptr;
char *format, *pstr; char *format,
void (*sig_func)(); *pstr;
void (*sig_func) ();
va_start(arg_ptr); va_start(arg_ptr);
format = va_arg(arg_ptr,char *); format = va_arg(arg_ptr, char *);
if (strncmp(format,"PERROR", 6) != 0) if (strncmp(format, "PERROR", 6) != 0)
vfprintf(stderr,format,arg_ptr); vfprintf(stderr, format, arg_ptr);
else else
{ {
for (pstr=format+6; *pstr == ' ' || *pstr == ':'; pstr++) for (pstr = format + 6; *pstr == ' ' || *pstr == ':'; pstr++)
; ;
vfprintf(stderr,pstr,arg_ptr); vfprintf(stderr, pstr, arg_ptr);
perror(""); perror("");
} }
va_end(arg_ptr); va_end(arg_ptr);
fflush(stderr); fflush(stderr);
/* call one clean up function if defined */ /* call one clean up function if defined */
if ( (sig_func = signal(SIGTERM, SIG_DFL)) != SIG_DFL && if ((sig_func = signal(SIGTERM, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN) sig_func != SIG_IGN)
(*sig_func)(0); (*sig_func) (0);
else if ( (sig_func = signal(SIGHUP, SIG_DFL)) != SIG_DFL && else if ((sig_func = signal(SIGHUP, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN) sig_func != SIG_IGN)
(*sig_func)(0); (*sig_func) (0);
else if ( (sig_func = signal(SIGINT, SIG_DFL)) != SIG_DFL && else if ((sig_func = signal(SIGINT, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN) sig_func != SIG_IGN)
(*sig_func)(0); (*sig_func) (0);
else if ( (sig_func = signal(SIGQUIT, SIG_DFL)) != SIG_DFL && else if ((sig_func = signal(SIGQUIT, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN) sig_func != SIG_IGN)
(*sig_func)(0); (*sig_func) (0);
exit(1); exit(1);
} }

View File

@ -3,5 +3,4 @@
** **
*/ */
void halt(); void halt();

View File

@ -10,20 +10,25 @@
#include "halt.h" #include "halt.h"
#include "pginterface.h" #include "pginterface.h"
int main(int argc, char **argv) int
main(int argc, char **argv)
{ {
char query[4000]; char query[4000];
int row =1; int row = 1;
int aint; int aint;
float afloat; float afloat;
double adouble; double adouble;
char achar[11], achar16[17], abpchar[11], avarchar[51], atext[51]; char achar[11],
time_t aabstime; achar16[17],
abpchar[11],
avarchar[51],
atext[51];
time_t aabstime;
if (argc != 2) if (argc != 2)
halt("Usage: %s database\n",argv[0]); halt("Usage: %s database\n", argv[0]);
connectdb(argv[1],NULL,NULL,NULL,NULL); connectdb(argv[1], NULL, NULL, NULL, NULL);
on_error_continue(); on_error_continue();
doquery("DROP TABLE testfetch"); doquery("DROP TABLE testfetch");
@ -42,9 +47,9 @@ int main(int argc, char **argv)
aabstime abstime) \ aabstime abstime) \
"); ");
while(1) while (1)
{ {
sprintf(query,"INSERT INTO testfetch VALUES ( \ sprintf(query, "INSERT INTO testfetch VALUES ( \
%d, \ %d, \
2322.12, \ 2322.12, \
'923121.0323'::float8, \ '923121.0323'::float8, \
@ -57,37 +62,37 @@ int main(int argc, char **argv)
doquery(query); doquery(query);
doquery("BEGIN WORK"); doquery("BEGIN WORK");
doquery("DECLARE c_testfetch BINARY CURSOR FOR \ doquery("DECLARE c_testfetch BINARY CURSOR FOR \
SELECT * FROM testfetch"); SELECT * FROM testfetch");
doquery("FETCH ALL IN c_testfetch"); doquery("FETCH ALL IN c_testfetch");
while (fetch( while (fetch(
&aint, &aint,
&afloat, &afloat,
&adouble, &adouble,
achar, achar,
achar16, achar16,
abpchar, abpchar,
avarchar, avarchar,
atext, atext,
&aabstime) != END_OF_TUPLES) &aabstime) != END_OF_TUPLES)
printf("int %d\nfloat %f\ndouble %f\nchar %s\nchar16 %s\n\ printf("int %d\nfloat %f\ndouble %f\nchar %s\nchar16 %s\n\
bpchar %s\nvarchar %s\ntext %s\nabstime %s", bpchar %s\nvarchar %s\ntext %s\nabstime %s",
aint, aint,
afloat, afloat,
adouble, adouble,
achar, achar,
achar16, achar16,
abpchar, abpchar,
avarchar, avarchar,
atext, atext,
ctime(&aabstime)); ctime(&aabstime));
doquery("CLOSE c_testfetch"); doquery("CLOSE c_testfetch");
doquery("COMMIT WORK"); doquery("COMMIT WORK");
printf("--- %-d rows inserted so far\n",row); printf("--- %-d rows inserted so far\n", row);
row++; row++;
} }
@ -95,4 +100,3 @@ bpchar %s\nvarchar %s\ntext %s\nabstime %s",
disconnectdb(); disconnectdb();
return 0; return 0;
} }

View File

@ -12,77 +12,82 @@
#include "halt.h" #include "halt.h"
#include "pginterface.h" #include "pginterface.h"
static void sig_disconnect(); static void sig_disconnect();
static void set_signals(); static void set_signals();
#define NUL '\0' #define NUL '\0'
/* GLOBAL VARIABLES */ /* GLOBAL VARIABLES */
static PGconn* conn; static PGconn *conn;
static PGresult* res = NULL; static PGresult *res = NULL;
#define ON_ERROR_STOP 0 #define ON_ERROR_STOP 0
#define ON_ERROR_CONTINUE 1 #define ON_ERROR_CONTINUE 1
static int on_error_state = ON_ERROR_STOP; static int on_error_state = ON_ERROR_STOP;
/* LOCAL VARIABLES */ /* LOCAL VARIABLES */
static sigset_t block_sigs, unblock_sigs; static sigset_t block_sigs,
static int tuple; unblock_sigs;
static int tuple;
/* /*
** **
** connectdb - returns PGconn structure ** connectdb - returns PGconn structure
** **
*/ */
PGconn *connectdb( char *dbName, PGconn *
char *pghost, connectdb(char *dbName,
char *pgport, char *pghost,
char *pgoptions, char *pgport,
char *pgtty) char *pgoptions,
char *pgtty)
{ {
/* make a connection to the database */ /* make a connection to the database */
conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
if (PQstatus(conn) == CONNECTION_BAD) if (PQstatus(conn) == CONNECTION_BAD)
halt("Connection to database '%s' failed.\n%s\n", dbName, halt("Connection to database '%s' failed.\n%s\n", dbName,
PQerrorMessage(conn)); PQerrorMessage(conn));
set_signals(); set_signals();
return conn; return conn;
} }
/* /*
** **
** disconnectdb ** disconnectdb
** **
*/ */
void disconnectdb() void
disconnectdb()
{ {
PQfinish(conn); PQfinish(conn);
} }
/* /*
** **
** doquery - returns PGresult structure ** doquery - returns PGresult structure
** **
*/ */
PGresult *doquery(char *query) PGresult *
doquery(char *query)
{ {
if (res != NULL) if (res != NULL)
PQclear(res); PQclear(res);
sigprocmask(SIG_SETMASK,&block_sigs,NULL); sigprocmask(SIG_SETMASK, &block_sigs, NULL);
res = PQexec(conn, query); res = PQexec(conn, query);
sigprocmask(SIG_SETMASK,&unblock_sigs,NULL); sigprocmask(SIG_SETMASK, &unblock_sigs, NULL);
if (on_error_state == ON_ERROR_STOP && if (on_error_state == ON_ERROR_STOP &&
(res == NULL || (res == NULL ||
PQresultStatus(res) == PGRES_BAD_RESPONSE || PQresultStatus(res) == PGRES_BAD_RESPONSE ||
PQresultStatus(res) == PGRES_NONFATAL_ERROR || PQresultStatus(res) == PGRES_NONFATAL_ERROR ||
PQresultStatus(res) == PGRES_FATAL_ERROR)) PQresultStatus(res) == PGRES_FATAL_ERROR))
{ {
if (res != NULL) if (res != NULL)
fprintf(stderr,"query error: %s\n",PQcmdStatus(res)); fprintf(stderr, "query error: %s\n", PQcmdStatus(res));
else fprintf(stderr,"connection error: %s\n",PQerrorMessage(conn)); else
fprintf(stderr, "connection error: %s\n", PQerrorMessage(conn));
PQfinish(conn); PQfinish(conn);
halt("failed request: %s\n", query); halt("failed request: %s\n", query);
} }
@ -92,14 +97,16 @@ PGresult *doquery(char *query)
/* /*
** **
** fetch - returns tuple number (starts at 0), or the value END_OF_TUPLES ** fetch - returns tuple number (starts at 0), or the value END_OF_TUPLES
** NULL pointers are skipped ** NULL pointers are skipped
** **
*/ */
int fetch(void *param, ...) int
fetch(void *param,...)
{ {
va_list ap; va_list ap;
int arg, num_fields; int arg,
num_fields;
num_fields = PQnfields(res); num_fields = PQnfields(res);
@ -113,11 +120,11 @@ int fetch(void *param, ...)
{ {
if (PQfsize(res, arg) == -1) if (PQfsize(res, arg) == -1)
{ {
memcpy(param,PQgetvalue(res,tuple,arg),PQgetlength(res,tuple,arg)); memcpy(param, PQgetvalue(res, tuple, arg), PQgetlength(res, tuple, arg));
((char *)param)[PQgetlength(res,tuple,arg)] = NUL; ((char *) param)[PQgetlength(res, tuple, arg)] = NUL;
} }
else else
memcpy(param,PQgetvalue(res,tuple,arg),PQfsize(res,arg)); memcpy(param, PQgetvalue(res, tuple, arg), PQfsize(res, arg));
} }
param = va_arg(ap, char *); param = va_arg(ap, char *);
} }
@ -127,15 +134,17 @@ int fetch(void *param, ...)
/* /*
** **
** fetchwithnulls - returns tuple number (starts at 0), ** fetchwithnulls - returns tuple number (starts at 0),
** or the value END_OF_TUPLES ** or the value END_OF_TUPLES
** Returns true or false into null indicator variables ** Returns true or false into null indicator variables
** NULL pointers are skipped ** NULL pointers are skipped
*/ */
int fetchwithnulls(void *param, ...) int
fetchwithnulls(void *param,...)
{ {
va_list ap; va_list ap;
int arg, num_fields; int arg,
num_fields;
num_fields = PQnfields(res); num_fields = PQnfields(res);
@ -149,17 +158,17 @@ int fetchwithnulls(void *param, ...)
{ {
if (PQfsize(res, arg) == -1) if (PQfsize(res, arg) == -1)
{ {
memcpy(param,PQgetvalue(res,tuple,arg),PQgetlength(res,tuple,arg)); memcpy(param, PQgetvalue(res, tuple, arg), PQgetlength(res, tuple, arg));
((char *)param)[PQgetlength(res,tuple,arg)] = NUL; ((char *) param)[PQgetlength(res, tuple, arg)] = NUL;
} }
else else
memcpy(param,PQgetvalue(res,tuple,arg),PQfsize(res,arg)); memcpy(param, PQgetvalue(res, tuple, arg), PQfsize(res, arg));
} }
param = va_arg(ap, char *); param = va_arg(ap, char *);
if (PQgetisnull(res,tuple,arg) != 0) if (PQgetisnull(res, tuple, arg) != 0)
*(int *)param = 1; *(int *) param = 1;
else else
*(int *)param = 0; *(int *) param = 0;
param = va_arg(ap, char *); param = va_arg(ap, char *);
} }
va_end(ap); va_end(ap);
@ -168,52 +177,56 @@ int fetchwithnulls(void *param, ...)
/* /*
** **
** on_error_stop ** on_error_stop
** **
*/ */
void on_error_stop() void
on_error_stop()
{ {
on_error_state = ON_ERROR_STOP; on_error_state = ON_ERROR_STOP;
} }
/* /*
** **
** on_error_continue ** on_error_continue
** **
*/ */
void on_error_continue() void
on_error_continue()
{ {
on_error_state = ON_ERROR_CONTINUE; on_error_state = ON_ERROR_CONTINUE;
} }
/* /*
** **
** sig_disconnect ** sig_disconnect
** **
*/ */
static void sig_disconnect() static void
sig_disconnect()
{ {
fprintf(stderr,"exiting...\n"); fprintf(stderr, "exiting...\n");
PQfinish(conn); PQfinish(conn);
exit(1); exit(1);
} }
/* /*
** **
** set_signals ** set_signals
** **
*/ */
static void set_signals() static void
set_signals()
{ {
sigemptyset(&block_sigs); sigemptyset(&block_sigs);
sigemptyset(&unblock_sigs); sigemptyset(&unblock_sigs);
sigaddset(&block_sigs,SIGTERM); sigaddset(&block_sigs, SIGTERM);
sigaddset(&block_sigs,SIGHUP); sigaddset(&block_sigs, SIGHUP);
sigaddset(&block_sigs,SIGINT); sigaddset(&block_sigs, SIGINT);
/* sigaddset(&block_sigs,SIGQUIT); no block */ /* sigaddset(&block_sigs,SIGQUIT); no block */
sigprocmask(SIG_SETMASK,&unblock_sigs,NULL); sigprocmask(SIG_SETMASK, &unblock_sigs, NULL);
signal(SIGTERM,sig_disconnect); signal(SIGTERM, sig_disconnect);
signal(SIGHUP,sig_disconnect); signal(SIGHUP, sig_disconnect);
signal(SIGINT,sig_disconnect); signal(SIGINT, sig_disconnect);
signal(SIGQUIT,sig_disconnect); signal(SIGQUIT, sig_disconnect);
} }

View File

@ -3,12 +3,12 @@
* *
*/ */
PGresult *doquery(char *query); PGresult *doquery(char *query);
PGconn *connectdb(); PGconn *connectdb();
void disconnectdb(); void disconnectdb();
int fetch(void *param, ...); int fetch(void *param,...);
int fetchwithnulls(void *param, ...); int fetchwithnulls(void *param,...);
void on_error_continue(); void on_error_continue();
void on_error_stop(); void on_error_stop();
#define END_OF_TUPLES (-1) #define END_OF_TUPLES (-1)

View File

@ -12,29 +12,34 @@
#include <libpq-fe.h> #include <libpq-fe.h>
#include <pginterface.h> #include <pginterface.h>
int main(int argc, char **argv) int
main(int argc, char **argv)
{ {
char query[4000]; char query[4000];
int row =1; int row = 1;
int aint; int aint;
float afloat; float afloat;
double adouble; double adouble;
char achar[11], achar16[17], abpchar[11], avarchar[51], atext[51]; char achar[11],
time_t aabstime; achar16[17],
int aint_null, abpchar[11],
afloat_null, avarchar[51],
adouble_null, atext[51];
achar_null, time_t aabstime;
achar16_null, int aint_null,
abpchar_null, afloat_null,
avarchar_null, adouble_null,
atext_null, achar_null,
aabstime_null; achar16_null,
abpchar_null,
avarchar_null,
atext_null,
aabstime_null;
if (argc != 2) if (argc != 2)
halt("Usage: %s database\n",argv[0]); halt("Usage: %s database\n", argv[0]);
connectdb(argv[1],NULL,NULL,NULL,NULL); connectdb(argv[1], NULL, NULL, NULL, NULL);
on_error_continue(); on_error_continue();
doquery("DROP TABLE testfetch"); doquery("DROP TABLE testfetch");
@ -54,7 +59,7 @@ int main(int argc, char **argv)
"); ");
#ifdef TEST_NON_NULLS #ifdef TEST_NON_NULLS
sprintf(query,"INSERT INTO testfetch VALUES ( \ sprintf(query, "INSERT INTO testfetch VALUES ( \
0, \ 0, \
0, \ 0, \
0, \ 0, \
@ -65,7 +70,7 @@ int main(int argc, char **argv)
'', \ '', \
'');"); '');");
#else #else
sprintf(query,"INSERT INTO testfetch VALUES ( \ sprintf(query, "INSERT INTO testfetch VALUES ( \
NULL, \ NULL, \
NULL, \ NULL, \
NULL, \ NULL, \
@ -85,55 +90,54 @@ int main(int argc, char **argv)
doquery("FETCH ALL IN c_testfetch"); doquery("FETCH ALL IN c_testfetch");
if (fetchwithnulls( if (fetchwithnulls(
&aint, &aint,
&aint_null, &aint_null,
&afloat, &afloat,
&afloat_null, &afloat_null,
&adouble, &adouble,
&adouble_null, &adouble_null,
achar, achar,
&achar_null, &achar_null,
achar16, achar16,
&achar16_null, &achar16_null,
abpchar, abpchar,
&abpchar_null, &abpchar_null,
avarchar, avarchar,
&avarchar_null, &avarchar_null,
atext, atext,
&atext_null, &atext_null,
&aabstime, &aabstime,
&aabstime_null) != END_OF_TUPLES) &aabstime_null) != END_OF_TUPLES)
printf("int %d\nfloat %f\ndouble %f\nchar %s\nchar16 %s\n\ printf("int %d\nfloat %f\ndouble %f\nchar %s\nchar16 %s\n\
bpchar %s\nvarchar %s\ntext %s\nabstime %s\n", bpchar %s\nvarchar %s\ntext %s\nabstime %s\n",
aint, aint,
afloat, afloat,
adouble, adouble,
achar, achar,
achar16, achar16,
abpchar, abpchar,
avarchar, avarchar,
atext, atext,
ctime(&aabstime)); ctime(&aabstime));
printf("NULL:\nint %d\nfloat %d\ndouble %d\nchar %d\nchar16 %d\n\ printf("NULL:\nint %d\nfloat %d\ndouble %d\nchar %d\nchar16 %d\n\
bpchar %d\nvarchar %d\ntext %d\nabstime %d\n", bpchar %d\nvarchar %d\ntext %d\nabstime %d\n",
aint_null, aint_null,
afloat_null, afloat_null,
adouble_null, adouble_null,
achar_null, achar_null,
achar16_null, achar16_null,
abpchar_null, abpchar_null,
avarchar_null, avarchar_null,
atext_null, atext_null,
aabstime_null); aabstime_null);
doquery("CLOSE c_testfetch"); doquery("CLOSE c_testfetch");
doquery("COMMIT WORK"); doquery("COMMIT WORK");
printf("--- %-d rows inserted so far\n",row); printf("--- %-d rows inserted so far\n", row);
row++; row++;
disconnectdb(); disconnectdb();
return 0; return 0;
} }

View File

@ -10,17 +10,18 @@
#include <libpq-fe.h> #include <libpq-fe.h>
#include "pginterface.h" #include "pginterface.h"
int main(int argc, char **argv) int
main(int argc, char **argv)
{ {
char query[4000]; char query[4000];
int row = 0; int row = 0;
int count; int count;
char line[4000]; char line[4000];
if (argc != 2) if (argc != 2)
halt("Usage: %s database\n",argv[0]); halt("Usage: %s database\n", argv[0]);
connectdb(argv[1],NULL,NULL,NULL,NULL); connectdb(argv[1], NULL, NULL, NULL, NULL);
on_error_continue(); on_error_continue();
doquery("DROP TABLE words"); doquery("DROP TABLE words");
on_error_stop(); on_error_stop();
@ -35,12 +36,12 @@ int main(int argc, char **argv)
word text_ops )\ word text_ops )\
"); ");
while(1) while (1)
{ {
if (scanf("%s",line) != 1) if (scanf("%s", line) != 1)
break; break;
doquery("BEGIN WORK"); doquery("BEGIN WORK");
sprintf(query,"\ sprintf(query, "\
DECLARE c_words BINARY CURSOR FOR \ DECLARE c_words BINARY CURSOR FOR \
SELECT count(*) \ SELECT count(*) \
FROM words \ FROM words \
@ -54,14 +55,14 @@ int main(int argc, char **argv)
doquery("COMMIT WORK"); doquery("COMMIT WORK");
if (count == 0) if (count == 0)
sprintf(query,"\ sprintf(query, "\
INSERT INTO words \ INSERT INTO words \
VALUES (1, '%s')", line); VALUES (1, '%s')", line);
else else
sprintf(query,"\ sprintf(query, "\
UPDATE words \ UPDATE words \
SET matches = matches + 1 \ SET matches = matches + 1 \
WHERE word = '%s'", line); WHERE word = '%s'", line);
doquery(query); doquery(query);
row++; row++;
} }
@ -69,4 +70,3 @@ int main(int argc, char **argv)
disconnectdb(); disconnectdb();
return 0; return 0;
} }

View File

@ -4,80 +4,86 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include "postgres.h" /* for char16, etc. */ #include "postgres.h" /* for char16, etc. */
#include "utils/palloc.h" /* for palloc */ #include "utils/palloc.h" /* for palloc */
#include "libpq-fe.h" /* for TUPLE */ #include "libpq-fe.h" /* for TUPLE */
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
/* prototype for soundex function */ /* prototype for soundex function */
char *soundex(char *instr, char *outstr); char *soundex(char *instr, char *outstr);
text *text_soundex(text *t) text *
text_soundex(text * t)
{ {
/* ABCDEFGHIJKLMNOPQRSTUVWXYZ */ /* ABCDEFGHIJKLMNOPQRSTUVWXYZ */
char *table = "01230120022455012623010202"; char *table = "01230120022455012623010202";
int count = 0; int count = 0;
text *new_t; text *new_t;
char outstr[6+1]; /* max length of soundex is 6 */ char outstr[6 + 1]; /* max length of soundex is 6 */
char *instr; char *instr;
/* make a null-terminated string */ /* make a null-terminated string */
instr=palloc(VARSIZE(t)+1); instr = palloc(VARSIZE(t) + 1);
memcpy(instr,VARDATA(t),VARSIZE(t)-VARHDRSZ); memcpy(instr, VARDATA(t), VARSIZE(t) - VARHDRSZ);
instr[VARSIZE(t)-VARHDRSZ] = (char)0; instr[VARSIZE(t) - VARHDRSZ] = (char) 0;
/* load soundex into outstr */ /* load soundex into outstr */
soundex(instr, outstr); soundex(instr, outstr);
/* Now the outstr contains the soundex of instr */ /* Now the outstr contains the soundex of instr */
/* copy outstr to new_t */ /* copy outstr to new_t */
new_t = (text *) palloc(strlen(outstr)+VARHDRSZ); new_t = (text *) palloc(strlen(outstr) + VARHDRSZ);
memset(new_t, 0, strlen(outstr)+1); memset(new_t, 0, strlen(outstr) + 1);
VARSIZE(new_t) = strlen(outstr)+VARHDRSZ; VARSIZE(new_t) = strlen(outstr) + VARHDRSZ;
memcpy((void *) VARDATA(new_t), memcpy((void *) VARDATA(new_t),
(void *) outstr, (void *) outstr,
strlen(outstr)); strlen(outstr));
/* free instr */ /* free instr */
pfree(instr); pfree(instr);
return(new_t); return (new_t);
} }
char *soundex(char *instr, char *outstr) char *
{ /* ABCDEFGHIJKLMNOPQRSTUVWXYZ */ soundex(char *instr, char *outstr)
char *table = "01230120022455012623010202"; { /* ABCDEFGHIJKLMNOPQRSTUVWXYZ */
int count = 0; char *table = "01230120022455012623010202";
int count = 0;
while(!isalpha(instr[0]) && instr[0]) while (!isalpha(instr[0]) && instr[0])
++instr; ++instr;
if(!instr[0]) { /* Hey! Where'd the string go? */ if (!instr[0])
outstr[0]=(char)0; { /* Hey! Where'd the string go? */
return outstr; outstr[0] = (char) 0;
} return outstr;
}
if(toupper(instr[0]) == 'P' && toupper(instr[1]) == 'H') { if (toupper(instr[0]) == 'P' && toupper(instr[1]) == 'H')
instr[0] = 'F'; {
instr[1] = 'A'; instr[0] = 'F';
} instr[1] = 'A';
}
*outstr++ = (char)toupper(*instr++); *outstr++ = (char) toupper(*instr++);
while(*instr && count < 5) { while (*instr && count < 5)
if(isalpha(*instr) && *instr != *(instr-1)) { {
*outstr = table[toupper(instr[0]) - 'A']; if (isalpha(*instr) && *instr != *(instr - 1))
if(*outstr != '0') { {
++outstr; *outstr = table[toupper(instr[0]) - 'A'];
++count; if (*outstr != '0')
} {
} ++outstr;
++instr; ++count;
} }
}
++instr;
}
*outstr = '\0'; *outstr = '\0';
return(outstr); return (outstr);
} }

View File

@ -17,14 +17,14 @@
/* define this if you want to see iso-8859 characters */ /* define this if you want to see iso-8859 characters */
#define ISO8859 #define ISO8859
#define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MIN(x, y) ((x) < (y) ? (x) : (y))
#define VALUE(char) ((char) - '0') #define VALUE(char) ((char) - '0')
#define DIGIT(val) ((val) + '0') #define DIGIT(val) ((val) + '0')
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7')) #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
#ifndef ISO8859 #ifndef ISO8859
#define NOTPRINTABLE(c) (!isprint(c)) #define NOTPRINTABLE(c) (!isprint(c))
#else #else
#define NOTPRINTABLE(c) (!isprint(c) && ((c) < 0xa0)) #define NOTPRINTABLE(c) (!isprint(c) && ((c) < 0xa0))
#endif #endif
/* /*
@ -36,109 +36,123 @@
* The function is used by output methods of various string types. * The function is used by output methods of various string types.
* *
* Arguments: * Arguments:
* data - input data (can be NULL) * data - input data (can be NULL)
* size - optional size of data. A negative value indicates * size - optional size of data. A negative value indicates
* that data is a null terminated string. * that data is a null terminated string.
* *
* Returns: * Returns:
* a pointer to a new string containing the printable * a pointer to a new string containing the printable
* representation of data. * representation of data.
*/ */
char * char *
string_output(char *data, int size) string_output(char *data, int size)
{ {
register unsigned char c, *p, *r, *result; register unsigned char c,
register int l, len; *p,
*r,
*result;
register int l,
len;
if (data == NULL) { if (data == NULL)
result = (char *) palloc(2); {
result[0] = '-'; result = (char *) palloc(2);
result[1] = '\0'; result[0] = '-';
return (result); result[1] = '\0';
} return (result);
if (size < 0) {
size = strlen(data);
}
/* adjust string length for escapes */
len = size;
for (p=data,l=size; l>0; p++,l--) {
switch (*p) {
case '\\':
case '"' :
case '{':
case '}':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
case '\v':
len++;
break;
default:
if (NOTPRINTABLE(*p)) {
len += 3;
}
} }
}
len++;
result = (char *) palloc(len); if (size < 0)
{
for (p=data,r=result,l=size; (l > 0) && (c = *p); p++,l--) { size = strlen(data);
switch (c) {
case '\\':
case '"' :
case '{':
case '}':
*r++ = '\\';
*r++ = c;
break;
case '\b':
*r++ = '\\';
*r++ = 'b';
break;
case '\f':
*r++ = '\\';
*r++ = 'f';
break;
case '\n':
*r++ = '\\';
*r++ = 'n';
break;
case '\r':
*r++ = '\\';
*r++ = 'r';
break;
case '\t':
*r++ = '\\';
*r++ = 't';
break;
case '\v':
*r++ = '\\';
*r++ = 'v';
break;
default:
if (NOTPRINTABLE(c)) {
*r = '\\';
r += 3;
*r-- = DIGIT(c & 07);
c >>= 3;
*r-- = DIGIT(c & 07);
c >>= 3;
*r = DIGIT(c & 03);
r += 3;
} else {
*r++ = c;
}
} }
}
*r = '\0';
return((char *) result); /* adjust string length for escapes */
len = size;
for (p = data, l = size; l > 0; p++, l--)
{
switch (*p)
{
case '\\':
case '"':
case '{':
case '}':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
case '\v':
len++;
break;
default:
if (NOTPRINTABLE(*p))
{
len += 3;
}
}
}
len++;
result = (char *) palloc(len);
for (p = data, r = result, l = size; (l > 0) && (c = *p); p++, l--)
{
switch (c)
{
case '\\':
case '"':
case '{':
case '}':
*r++ = '\\';
*r++ = c;
break;
case '\b':
*r++ = '\\';
*r++ = 'b';
break;
case '\f':
*r++ = '\\';
*r++ = 'f';
break;
case '\n':
*r++ = '\\';
*r++ = 'n';
break;
case '\r':
*r++ = '\\';
*r++ = 'r';
break;
case '\t':
*r++ = '\\';
*r++ = 't';
break;
case '\v':
*r++ = '\\';
*r++ = 'v';
break;
default:
if (NOTPRINTABLE(c))
{
*r = '\\';
r += 3;
*r-- = DIGIT(c & 07);
c >>= 3;
*r-- = DIGIT(c & 07);
c >>= 3;
*r = DIGIT(c & 03);
r += 3;
}
else
{
*r++ = c;
}
}
}
*r = '\0';
return ((char *) result);
} }
/* /*
@ -153,209 +167,231 @@ string_output(char *data, int size)
* receive strings in internal form. * receive strings in internal form.
* *
* Arguments: * Arguments:
* str - input string possibly with escapes * str - input string possibly with escapes
* size - the required size of new data. A value of 0 * size - the required size of new data. A value of 0
* indicates a variable size string, while a * indicates a variable size string, while a
* negative value indicates a variable size string * negative value indicates a variable size string
* of size not greater than this absolute value. * of size not greater than this absolute value.
* hdrsize - size of an optional header to be allocated before * hdrsize - size of an optional header to be allocated before
* the data. It must then be filled by the caller. * the data. It must then be filled by the caller.
* rtn_size - an optional pointer to an int variable where the * rtn_size - an optional pointer to an int variable where the
* size of the new string is stored back. * size of the new string is stored back.
* *
* Returns: * Returns:
* a pointer to the new string or the header. * a pointer to the new string or the header.
*/ */
char * char *
string_input(char *str, int size, int hdrsize, int *rtn_size) string_input(char *str, int size, int hdrsize, int *rtn_size)
{ {
register unsigned char *p, *r; register unsigned char *p,
unsigned char *result; *r;
int len; unsigned char *result;
int len;
if ((str == NULL) || (hdrsize < 0)) { if ((str == NULL) || (hdrsize < 0))
return (char *) NULL; {
} return (char *) NULL;
/* Compute result size */
len = strlen(str);
for (p=str; *p; ) {
if (*p++ == '\\') {
if (ISOCTAL(*p)) {
if (ISOCTAL(*(p+1))) {
p++;
len--;
}
if (ISOCTAL(*(p+1))) {
p++;
len--;
}
}
if (*p) p++;
len--;
} }
}
/* result has variable length */ /* Compute result size */
if (size == 0) { len = strlen(str);
size = len+1; for (p = str; *p;)
} else {
if (*p++ == '\\')
/* result has variable length with maximum size */ {
if (size < 0) { if (ISOCTAL(*p))
size = MIN(len, - size)+1; {
} if (ISOCTAL(*(p + 1)))
{
result = (char *) palloc(hdrsize+size); p++;
memset(result, 0, hdrsize+size); len--;
if (rtn_size) { }
*rtn_size = size; if (ISOCTAL(*(p + 1)))
} {
p++;
r = result + hdrsize; len--;
for (p=str; *p; ) { }
register unsigned char c; }
if ((c = *p++) == '\\') { if (*p)
switch (c = *p++) { p++;
case '\0': len--;
p--;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
c = VALUE(c);
if (isdigit(*p)) {
c = (c<<3) + VALUE(*p++);
} }
if (isdigit(*p)) {
c = (c<<3) + VALUE(*p++);
}
*r++ = c;
break;
case 'b':
*r++ = '\b';
break;
case 'f':
*r++ = '\f';
break;
case 'n':
*r++ = '\n';
break;
case 'r':
*r++ = '\r';
break;
case 't':
*r++ = '\t';
break;
case 'v':
*r++ = '\v';
break;
default:
*r++ = c;
}
} else {
*r++ = c;
} }
}
return((char *) result); /* result has variable length */
if (size == 0)
{
size = len + 1;
}
else
/* result has variable length with maximum size */
if (size < 0)
{
size = MIN(len, -size) + 1;
}
result = (char *) palloc(hdrsize + size);
memset(result, 0, hdrsize + size);
if (rtn_size)
{
*rtn_size = size;
}
r = result + hdrsize;
for (p = str; *p;)
{
register unsigned char c;
if ((c = *p++) == '\\')
{
switch (c = *p++)
{
case '\0':
p--;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
c = VALUE(c);
if (isdigit(*p))
{
c = (c << 3) + VALUE(*p++);
}
if (isdigit(*p))
{
c = (c << 3) + VALUE(*p++);
}
*r++ = c;
break;
case 'b':
*r++ = '\b';
break;
case 'f':
*r++ = '\f';
break;
case 'n':
*r++ = '\n';
break;
case 'r':
*r++ = '\r';
break;
case 't':
*r++ = '\t';
break;
case 'v':
*r++ = '\v';
break;
default:
*r++ = c;
}
}
else
{
*r++ = c;
}
}
return ((char *) result);
} }
char * char *
c_charout(int32 c) c_charout(int32 c)
{ {
char str[2]; char str[2];
str[0] = (char) c; str[0] = (char) c;
str[1] = '\0'; str[1] = '\0';
return (string_output(str, 1)); return (string_output(str, 1));
} }
char * char *
c_char2out(uint16 s) c_char2out(uint16 s)
{ {
return (string_output((char *) &s, 2)); return (string_output((char *) &s, 2));
} }
char * char *
c_char4out(uint32 s) c_char4out(uint32 s)
{ {
return (string_output((char *) &s, 4)); return (string_output((char *) &s, 4));
} }
char * char *
c_char8out(char *s) c_char8out(char *s)
{ {
return (string_output(s, 8)); return (string_output(s, 8));
} }
char * char *
c_char16out(char *s) c_char16out(char *s)
{ {
return (string_output(s, 16)); return (string_output(s, 16));
} }
/* /*
* This can be used for text, bytea, SET and unknown data types * This can be used for text, bytea, SET and unknown data types
*/ */
char * char *
c_textout(struct varlena *vlena) c_textout(struct varlena * vlena)
{ {
int len = 0; int len = 0;
char *s = NULL; char *s = NULL;
if (vlena) { if (vlena)
len = VARSIZE(vlena) - VARHDRSZ; {
s = VARDATA(vlena); len = VARSIZE(vlena) - VARHDRSZ;
} s = VARDATA(vlena);
return (string_output(s, len)); }
return (string_output(s, len));
} }
/* /*
* This can be used for varchar and bpchar strings * This can be used for varchar and bpchar strings
*/ */
char * char *
c_varcharout(char *s) c_varcharout(char *s)
{ {
int len; int len;
if (s) { if (s)
len = *(int32*)s - 4; {
s += 4; len = *(int32 *) s - 4;
} s += 4;
return (string_output(s, len)); }
return (string_output(s, len));
} }
#ifdef 0 #ifdef 0
struct varlena * struct varlena *
c_textin(char *str) c_textin(char *str)
{ {
struct varlena *result; struct varlena *result;
int len; int len;
if (str == NULL) { if (str == NULL)
return ((struct varlena *) NULL); {
} return ((struct varlena *) NULL);
}
result = (struct varlena *) string_input(str, 0, VARHDRSZ, &len); result = (struct varlena *) string_input(str, 0, VARHDRSZ, &len);
VARSIZE(result) = len; VARSIZE(result) = len;
return (result); return (result);
} }
char * char *
c_char16in(char *str) c_char16in(char *str)
{ {
return (string_input(str, 16, 0, NULL)); return (string_input(str, 16, 0, NULL));
} }
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* heapvalid.c-- * heapvalid.c--
* heap tuple qualification validity checking code * heap tuple qualification validity checking code
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/common/Attic/heapvalid.c,v 1.16 1997/08/29 09:12:20 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/access/common/Attic/heapvalid.c,v 1.17 1997/09/07 04:37:36 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -25,128 +25,138 @@
#include <utils/builtins.h> #include <utils/builtins.h>
/* ---------------- /* ----------------
* heap_keytest * heap_keytest
* *
* Test a heap tuple with respect to a scan key. * Test a heap tuple with respect to a scan key.
* ---------------- * ----------------
*/ */
bool bool
heap_keytest(HeapTuple t, heap_keytest(HeapTuple t,
TupleDesc tupdesc, TupleDesc tupdesc,
int nkeys, int nkeys,
ScanKey keys) ScanKey keys)
{ {
bool isnull; bool isnull;
Datum atp; Datum atp;
int test; int test;
for (; nkeys--; keys++) { for (; nkeys--; keys++)
atp = (Datum)heap_getattr(t, InvalidBuffer, {
keys->sk_attno, atp = (Datum) heap_getattr(t, InvalidBuffer,
tupdesc, keys->sk_attno,
&isnull); tupdesc,
&isnull);
if (isnull) if (isnull)
/* XXX eventually should check if SK_ISNULL */ /* XXX eventually should check if SK_ISNULL */
return false; return false;
if (keys->sk_flags & SK_ISNULL) { if (keys->sk_flags & SK_ISNULL)
return (false); {
return (false);
}
if (keys->sk_func == (func_ptr) oideq) /* optimization */
test = (keys->sk_argument == atp);
else if (keys->sk_flags & SK_COMMUTE)
test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure,
keys->sk_argument, atp);
else
test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure,
atp, keys->sk_argument);
if (!test == !(keys->sk_flags & SK_NEGATE))
return false;
} }
if (keys->sk_func == (func_ptr)oideq) /* optimization */ return true;
test = (keys->sk_argument == atp);
else if (keys->sk_flags & SK_COMMUTE)
test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure,
keys->sk_argument, atp);
else
test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure,
atp, keys->sk_argument);
if (!test == !(keys->sk_flags & SK_NEGATE))
return false;
}
return true;
} }
/* ---------------- /* ----------------
* heap_tuple_satisfies * heap_tuple_satisfies
* *
* Returns a valid HeapTuple if it satisfies the timequal and keytest. * Returns a valid HeapTuple if it satisfies the timequal and keytest.
* Returns NULL otherwise. Used to be heap_satisifies (sic) which * Returns NULL otherwise. Used to be heap_satisifies (sic) which
* returned a boolean. It now returns a tuple so that we can avoid doing two * returned a boolean. It now returns a tuple so that we can avoid doing two
* PageGetItem's per tuple. * PageGetItem's per tuple.
* *
* Complete check of validity including LP_CTUP and keytest. * Complete check of validity including LP_CTUP and keytest.
* This should perhaps be combined with valid somehow in the * This should perhaps be combined with valid somehow in the
* future. (Also, additional rule tests/time range tests.) * future. (Also, additional rule tests/time range tests.)
* *
* on 8/21/92 mao says: i rearranged the tests here to do keytest before * on 8/21/92 mao says: i rearranged the tests here to do keytest before
* SatisfiesTimeQual. profiling indicated that even for vacuumed relations, * SatisfiesTimeQual. profiling indicated that even for vacuumed relations,
* time qual checking was more expensive than key testing. time qual is * time qual checking was more expensive than key testing. time qual is
* least likely to fail, too. we should really add the time qual test to * least likely to fail, too. we should really add the time qual test to
* the restriction and optimize it in the normal way. this has interactions * the restriction and optimize it in the normal way. this has interactions
* with joey's expensive function work. * with joey's expensive function work.
* ---------------- * ----------------
*/ */
HeapTuple HeapTuple
heap_tuple_satisfies(ItemId itemId, heap_tuple_satisfies(ItemId itemId,
Relation relation, Relation relation,
Buffer buffer, Buffer buffer,
PageHeader disk_page, PageHeader disk_page,
TimeQual qual, TimeQual qual,
int nKeys, int nKeys,
ScanKey key) ScanKey key)
{ {
HeapTuple tuple, result; HeapTuple tuple,
bool res; result;
TransactionId old_tmin, old_tmax; bool res;
TransactionId old_tmin,
old_tmax;
if (! ItemIdIsUsed(itemId)) if (!ItemIdIsUsed(itemId))
return NULL; return NULL;
tuple = (HeapTuple) PageGetItem((Page) disk_page, itemId); tuple = (HeapTuple) PageGetItem((Page) disk_page, itemId);
if (key != NULL) if (key != NULL)
res = heap_keytest(tuple, RelationGetTupleDescriptor(relation), res = heap_keytest(tuple, RelationGetTupleDescriptor(relation),
nKeys, key); nKeys, key);
else else
res = TRUE; res = TRUE;
result = (HeapTuple)NULL; result = (HeapTuple) NULL;
if (res) { if (res)
if(relation->rd_rel->relkind == RELKIND_UNCATALOGED) { {
result = tuple; if (relation->rd_rel->relkind == RELKIND_UNCATALOGED)
} else { {
old_tmin = tuple->t_tmin; result = tuple;
old_tmax = tuple->t_tmax; }
res = HeapTupleSatisfiesTimeQual(tuple,qual); else
if(tuple->t_tmin != old_tmin || {
tuple->t_tmax != old_tmax) { old_tmin = tuple->t_tmin;
SetBufferCommitInfoNeedsSave(buffer); old_tmax = tuple->t_tmax;
} res = HeapTupleSatisfiesTimeQual(tuple, qual);
if(res) { if (tuple->t_tmin != old_tmin ||
result = tuple; tuple->t_tmax != old_tmax)
} {
SetBufferCommitInfoNeedsSave(buffer);
}
if (res)
{
result = tuple;
}
}
} }
}
return result; return result;
} }
/* /*
* TupleUpdatedByCurXactAndCmd() -- Returns true if this tuple has * TupleUpdatedByCurXactAndCmd() -- Returns true if this tuple has
* already been updated once by the current transaction/command * already been updated once by the current transaction/command
* pair. * pair.
*/ */
bool bool
TupleUpdatedByCurXactAndCmd(HeapTuple t) TupleUpdatedByCurXactAndCmd(HeapTuple t)
{ {
if (TransactionIdEquals(t->t_xmax, if (TransactionIdEquals(t->t_xmax,
GetCurrentTransactionId()) && GetCurrentTransactionId()) &&
CommandIdGEScanCommandId (t->t_cmax)) CommandIdGEScanCommandId(t->t_cmax))
return true; return true;
return false; return false;
} }

View File

@ -1,14 +1,14 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* indextuple.c-- * indextuple.c--
* This file contains index tuple accessor and mutator routines, * This file contains index tuple accessor and mutator routines,
* as well as a few various tuple utilities. * as well as a few various tuple utilities.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/common/indextuple.c,v 1.15 1997/08/19 21:28:50 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/common/indextuple.c,v 1.16 1997/09/07 04:37:37 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -21,402 +21,438 @@
#include <access/tupmacs.h> #include <access/tupmacs.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
static Size IndexInfoFindDataOffset(unsigned short t_info); static Size IndexInfoFindDataOffset(unsigned short t_info);
static char *fastgetiattr(IndexTuple tup, int attnum, static char *
TupleDesc att, bool *isnull); fastgetiattr(IndexTuple tup, int attnum,
TupleDesc att, bool * isnull);
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* index_ tuple interface routines * index_ tuple interface routines
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
/* ---------------- /* ----------------
* index_formtuple * index_formtuple
* ---------------- * ----------------
*/ */
IndexTuple IndexTuple
index_formtuple(TupleDesc tupleDescriptor, index_formtuple(TupleDesc tupleDescriptor,
Datum value[], Datum value[],
char null[]) char null[])
{ {
register char *tp; /* tuple pointer */ register char *tp; /* tuple pointer */
IndexTuple tuple; /* return tuple */ IndexTuple tuple; /* return tuple */
Size size, hoff; Size size,
int i; hoff;
unsigned short infomask = 0; int i;
bool hasnull = false; unsigned short infomask = 0;
char tupmask = 0; bool hasnull = false;
int numberOfAttributes = tupleDescriptor->natts; char tupmask = 0;
int numberOfAttributes = tupleDescriptor->natts;
if (numberOfAttributes > MaxIndexAttributeNumber) if (numberOfAttributes > MaxIndexAttributeNumber)
elog(WARN, "index_formtuple: numberOfAttributes of %d > %d", elog(WARN, "index_formtuple: numberOfAttributes of %d > %d",
numberOfAttributes, MaxIndexAttributeNumber); numberOfAttributes, MaxIndexAttributeNumber);
for (i = 0; i < numberOfAttributes && !hasnull; i++) { for (i = 0; i < numberOfAttributes && !hasnull; i++)
if (null[i] != ' ') hasnull = true; {
} if (null[i] != ' ')
hasnull = true;
}
if (hasnull) infomask |= INDEX_NULL_MASK; if (hasnull)
infomask |= INDEX_NULL_MASK;
hoff = IndexInfoFindDataOffset(infomask); hoff = IndexInfoFindDataOffset(infomask);
size = hoff size = hoff
+ ComputeDataSize(tupleDescriptor, + ComputeDataSize(tupleDescriptor,
value, null); value, null);
size = DOUBLEALIGN(size); /* be conservative */ size = DOUBLEALIGN(size); /* be conservative */
tp = (char *) palloc(size); tp = (char *) palloc(size);
tuple = (IndexTuple) tp; tuple = (IndexTuple) tp;
memset(tp,0,(int)size); memset(tp, 0, (int) size);
DataFill((char *)tp + hoff, DataFill((char *) tp + hoff,
tupleDescriptor, tupleDescriptor,
value, value,
null, null,
&tupmask, &tupmask,
(hasnull ? (bits8*)tp + sizeof(*tuple) : NULL)); (hasnull ? (bits8 *) tp + sizeof(*tuple) : NULL));
/* /*
* We do this because DataFill wants to initialize a "tupmask" which * We do this because DataFill wants to initialize a "tupmask" which
* is used for HeapTuples, but we want an indextuple infomask. The only * is used for HeapTuples, but we want an indextuple infomask. The
* "relevent" info is the "has variable attributes" field, which is in * only "relevent" info is the "has variable attributes" field, which
* mask position 0x02. We have already set the null mask above. * is in mask position 0x02. We have already set the null mask above.
*/ */
if (tupmask & 0x02) infomask |= INDEX_VAR_MASK; if (tupmask & 0x02)
infomask |= INDEX_VAR_MASK;
/* /*
* Here we make sure that we can actually hold the size. We also want * Here we make sure that we can actually hold the size. We also want
* to make sure that size is not aligned oddly. This actually is a * to make sure that size is not aligned oddly. This actually is a
* rather odd way to make sure the size is not too large overall. * rather odd way to make sure the size is not too large overall.
*/ */
if (size & 0xE000) if (size & 0xE000)
elog(WARN, "index_formtuple: data takes %d bytes: too big", size); elog(WARN, "index_formtuple: data takes %d bytes: too big", size);
infomask |= size; infomask |= size;
/* ---------------- /* ----------------
* initialize metadata * initialize metadata
* ---------------- * ----------------
*/ */
tuple->t_info = infomask; tuple->t_info = infomask;
return (tuple); return (tuple);
} }
/* ---------------- /* ----------------
* fastgetiattr * fastgetiattr
* *
* This is a newer version of fastgetiattr which attempts to be * This is a newer version of fastgetiattr which attempts to be
* faster by caching attribute offsets in the attribute descriptor. * faster by caching attribute offsets in the attribute descriptor.
* *
* an alternate way to speed things up would be to cache offsets * an alternate way to speed things up would be to cache offsets
* with the tuple, but that seems more difficult unless you take * with the tuple, but that seems more difficult unless you take
* the storage hit of actually putting those offsets into the * the storage hit of actually putting those offsets into the
* tuple you send to disk. Yuck. * tuple you send to disk. Yuck.
* *
* This scheme will be slightly slower than that, but should * This scheme will be slightly slower than that, but should
* preform well for queries which hit large #'s of tuples. After * preform well for queries which hit large #'s of tuples. After
* you cache the offsets once, examining all the other tuples using * you cache the offsets once, examining all the other tuples using
* the same attribute descriptor will go much quicker. -cim 5/4/91 * the same attribute descriptor will go much quicker. -cim 5/4/91
* ---------------- * ----------------
*/ */
static char * static char *
fastgetiattr(IndexTuple tup, fastgetiattr(IndexTuple tup,
int attnum, int attnum,
TupleDesc tupleDesc, TupleDesc tupleDesc,
bool *isnull) bool * isnull)
{ {
register char *tp; /* ptr to att in tuple */ register char *tp; /* ptr to att in tuple */
register char *bp = NULL; /* ptr to att in tuple */ register char *bp = NULL; /* ptr to att in tuple */
int slow; /* do we have to walk nulls? */ int slow; /* do we have to walk nulls? */
register int data_off; /* tuple data offset */ register int data_off; /* tuple data offset */
AttributeTupleForm *att = tupleDesc->attrs; AttributeTupleForm *att = tupleDesc->attrs;
/* ----------------
* sanity checks
* ----------------
*/
Assert(PointerIsValid(isnull));
Assert(attnum > 0);
/* ----------------
* Three cases:
*
* 1: No nulls and no variable length attributes.
* 2: Has a null or a varlena AFTER att.
* 3: Has nulls or varlenas BEFORE att.
* ----------------
*/
*isnull = false;
data_off = IndexTupleHasMinHeader(tup) ? sizeof *tup :
IndexInfoFindDataOffset(tup->t_info);
if (IndexTupleNoNulls(tup)) {
/* first attribute is always at position zero */
if (attnum == 1) {
return(fetchatt(&(att[0]), (char *) tup + data_off));
}
attnum--;
if (att[attnum]->attcacheoff > 0) {
return(fetchatt(&(att[attnum]),
(char *) tup + data_off +
att[attnum]->attcacheoff));
}
tp = (char *) tup + data_off;
slow = 0;
}else { /* there's a null somewhere in the tuple */
bp = (char *) tup + sizeof(*tup); /* "knows" t_bits are here! */
slow = 0;
/* ---------------- /* ----------------
* check to see if desired att is null * sanity checks
* ---------------- * ----------------
*/ */
attnum--; Assert(PointerIsValid(isnull));
{ Assert(attnum > 0);
if (att_isnull(attnum, bp)) {
*isnull = true;
return NULL;
}
}
/* ---------------- /* ----------------
* Now check to see if any preceeding bits are null... * Three cases:
*
* 1: No nulls and no variable length attributes.
* 2: Has a null or a varlena AFTER att.
* 3: Has nulls or varlenas BEFORE att.
* ---------------- * ----------------
*/ */
*isnull = false;
data_off = IndexTupleHasMinHeader(tup) ? sizeof *tup :
IndexInfoFindDataOffset(tup->t_info);
if (IndexTupleNoNulls(tup))
{ {
register int i = 0; /* current offset in bp */
register int mask; /* bit in byte we're looking at */
register char n; /* current byte in bp */
register int byte, finalbit;
byte = attnum >> 3; /* first attribute is always at position zero */
finalbit = attnum & 0x07;
for (; i <= byte; i++) { if (attnum == 1)
n = bp[i]; {
if (i < byte) { return (fetchatt(&(att[0]), (char *) tup + data_off));
/* check for nulls in any "earlier" bytes */ }
if ((~n) != 0) { attnum--;
slow++;
if (att[attnum]->attcacheoff > 0)
{
return (fetchatt(&(att[attnum]),
(char *) tup + data_off +
att[attnum]->attcacheoff));
}
tp = (char *) tup + data_off;
slow = 0;
}
else
{ /* there's a null somewhere in the tuple */
bp = (char *) tup + sizeof(*tup); /* "knows" t_bits are
* here! */
slow = 0;
/* ----------------
* check to see if desired att is null
* ----------------
*/
attnum--;
{
if (att_isnull(attnum, bp))
{
*isnull = true;
return NULL;
}
}
/* ----------------
* Now check to see if any preceeding bits are null...
* ----------------
*/
{
register int i = 0; /* current offset in bp */
register int mask; /* bit in byte we're looking at */
register char n; /* current byte in bp */
register int byte,
finalbit;
byte = attnum >> 3;
finalbit = attnum & 0x07;
for (; i <= byte; i++)
{
n = bp[i];
if (i < byte)
{
/* check for nulls in any "earlier" bytes */
if ((~n) != 0)
{
slow++;
break;
}
}
else
{
/* check for nulls "before" final bit of last byte */
mask = (finalbit << 1) - 1;
if ((~n) & mask)
slow++;
}
}
}
tp = (char *) tup + data_off;
}
/* now check for any non-fixed length attrs before our attribute */
if (!slow)
{
if (att[attnum]->attcacheoff > 0)
{
return (fetchatt(&(att[attnum]),
tp + att[attnum]->attcacheoff));
}
else if (!IndexTupleAllFixed(tup))
{
register int j = 0;
for (j = 0; j < attnum && !slow; j++)
if (att[j]->attlen < 1)
slow = 1;
}
}
/*
* if slow is zero, and we got here, we know that we have a tuple with
* no nulls. We also know that we have to initialize the remainder of
* the attribute cached offset values.
*/
if (!slow)
{
register int j = 1;
register long off;
/*
* need to set cache for some atts
*/
att[0]->attcacheoff = 0;
while (att[j]->attcacheoff > 0)
j++;
off = att[j - 1]->attcacheoff +
att[j - 1]->attlen;
for (; j < attnum + 1; j++)
{
/*
* Fix me when going to a machine with more than a four-byte
* word!
*/
switch (att[j]->attlen)
{
case -1:
off = (att[j]->attalign == 'd') ?
DOUBLEALIGN(off) : INTALIGN(off);
break;
case sizeof(char):
break;
case sizeof(short):
off = SHORTALIGN(off);
break;
case sizeof(int32):
off = INTALIGN(off);
break;
default:
if (att[j]->attlen > sizeof(int32))
off = (att[j]->attalign == 'd') ?
DOUBLEALIGN(off) : LONGALIGN(off);
else
elog(WARN, "fastgetiattr: attribute %d has len %d",
j, att[j]->attlen);
break;
}
att[j]->attcacheoff = off;
off += att[j]->attlen;
}
return (fetchatt(&(att[attnum]),
tp + att[attnum]->attcacheoff));
}
else
{
register bool usecache = true;
register int off = 0;
register int i;
/*
* Now we know that we have to walk the tuple CAREFULLY.
*/
for (i = 0; i < attnum; i++)
{
if (!IndexTupleNoNulls(tup))
{
if (att_isnull(i, bp))
{
usecache = false;
continue;
}
}
if (usecache && att[i]->attcacheoff > 0)
{
off = att[i]->attcacheoff;
if (att[i]->attlen == -1)
usecache = false;
else
continue;
}
if (usecache)
att[i]->attcacheoff = off;
switch (att[i]->attlen)
{
case sizeof(char):
off++;
break;
case sizeof(short):
off = SHORTALIGN(off) +sizeof(short);
break;
case sizeof(int32):
off = INTALIGN(off) + sizeof(int32);
break;
case -1:
usecache = false;
off = (att[i]->attalign == 'd') ?
DOUBLEALIGN(off) : INTALIGN(off);
off += VARSIZE(tp + off);
break;
default:
if (att[i]->attlen > sizeof(int32))
off = (att[i]->attalign == 'd') ?
DOUBLEALIGN(off) + att[i]->attlen :
LONGALIGN(off) + att[i]->attlen;
else
elog(WARN, "fastgetiattr2: attribute %d has len %d",
i, att[i]->attlen);
break;
}
}
/*
* I don't know why this code was missed here! I've got it from
* heaptuple.c:fastgetattr(). - vadim 06/12/97
*/
switch (att[attnum]->attlen)
{
case -1:
off = (att[attnum]->attalign == 'd') ?
DOUBLEALIGN(off) : INTALIGN(off);
break; break;
}
} else {
/* check for nulls "before" final bit of last byte*/
mask = (finalbit << 1) - 1;
if ((~n) & mask)
slow++;
}
}
}
tp = (char *) tup + data_off;
}
/* now check for any non-fixed length attrs before our attribute */
if (!slow) {
if (att[attnum]->attcacheoff > 0) {
return(fetchatt(&(att[attnum]),
tp + att[attnum]->attcacheoff));
}else if (!IndexTupleAllFixed(tup)) {
register int j = 0;
for (j = 0; j < attnum && !slow; j++)
if (att[j]->attlen < 1) slow = 1;
}
}
/*
* if slow is zero, and we got here, we know that we have a tuple with
* no nulls. We also know that we have to initialize the remainder of
* the attribute cached offset values.
*/
if (!slow) {
register int j = 1;
register long off;
/*
* need to set cache for some atts
*/
att[0]->attcacheoff = 0;
while (att[j]->attcacheoff > 0) j++;
off = att[j-1]->attcacheoff +
att[j-1]->attlen;
for (; j < attnum + 1; j++) {
/*
* Fix me when going to a machine with more than a four-byte
* word!
*/
switch(att[j]->attlen)
{
case -1:
off = (att[j]->attalign=='d')?
DOUBLEALIGN(off):INTALIGN(off);
break;
case sizeof(char): case sizeof(char):
break; break;
case sizeof(short): case sizeof(short):
off = SHORTALIGN(off); off = SHORTALIGN(off);
break; break;
case sizeof(int32): case sizeof(int32):
off = INTALIGN(off); off = INTALIGN(off);
break; break;
default: default:
if (att[j]->attlen > sizeof(int32)) if (att[attnum]->attlen < sizeof(int32))
off = (att[j]->attalign=='d')? elog(WARN, "fastgetattr3: attribute %d has len %d",
DOUBLEALIGN(off) : LONGALIGN(off); attnum, att[attnum]->attlen);
else if (att[attnum]->attalign == 'd')
elog(WARN, "fastgetiattr: attribute %d has len %d", off = DOUBLEALIGN(off);
j, att[j]->attlen); else
break; off = LONGALIGN(off);
break;
} }
att[j]->attcacheoff = off; return (fetchatt(&att[attnum], tp + off));
off += att[j]->attlen;
} }
return(fetchatt( &(att[attnum]),
tp + att[attnum]->attcacheoff));
}else {
register bool usecache = true;
register int off = 0;
register int i;
/*
* Now we know that we have to walk the tuple CAREFULLY.
*/
for (i = 0; i < attnum; i++) {
if (!IndexTupleNoNulls(tup)) {
if (att_isnull(i, bp)) {
usecache = false;
continue;
}
}
if (usecache && att[i]->attcacheoff > 0) {
off = att[i]->attcacheoff;
if (att[i]->attlen == -1)
usecache = false;
else
continue;
}
if (usecache) att[i]->attcacheoff = off;
switch(att[i]->attlen)
{
case sizeof(char):
off++;
break;
case sizeof(short):
off = SHORTALIGN(off) + sizeof(short);
break;
case sizeof(int32):
off = INTALIGN(off) + sizeof(int32);
break;
case -1:
usecache = false;
off = (att[i]->attalign=='d')?
DOUBLEALIGN(off):INTALIGN(off);
off += VARSIZE(tp + off);
break;
default:
if (att[i]->attlen > sizeof(int32))
off = (att[i]->attalign=='d') ?
DOUBLEALIGN(off) + att[i]->attlen :
LONGALIGN(off) + att[i]->attlen;
else
elog(WARN, "fastgetiattr2: attribute %d has len %d",
i, att[i]->attlen);
break;
}
}
/*
* I don't know why this code was missed here!
* I've got it from heaptuple.c:fastgetattr().
* - vadim 06/12/97
*/
switch (att[attnum]->attlen) {
case -1:
off = (att[attnum]->attalign=='d')?
DOUBLEALIGN(off) : INTALIGN(off);
break;
case sizeof(char):
break;
case sizeof(short):
off = SHORTALIGN(off);
break;
case sizeof(int32):
off = INTALIGN(off);
break;
default:
if (att[attnum]->attlen < sizeof(int32))
elog(WARN, "fastgetattr3: attribute %d has len %d",
attnum, att[attnum]->attlen);
if (att[attnum]->attalign == 'd')
off = DOUBLEALIGN(off);
else
off = LONGALIGN(off);
break;
}
return(fetchatt(&att[attnum], tp + off));
}
} }
/* ---------------- /* ----------------
* index_getattr * index_getattr
* ---------------- * ----------------
*/ */
Datum Datum
index_getattr(IndexTuple tuple, index_getattr(IndexTuple tuple,
AttrNumber attNum, AttrNumber attNum,
TupleDesc tupDesc, TupleDesc tupDesc,
bool *isNullOutP) bool * isNullOutP)
{ {
Assert (attNum > 0); Assert(attNum > 0);
return (Datum) return (Datum)
fastgetiattr(tuple, attNum, tupDesc, isNullOutP); fastgetiattr(tuple, attNum, tupDesc, isNullOutP);
} }
RetrieveIndexResult RetrieveIndexResult
FormRetrieveIndexResult(ItemPointer indexItemPointer, FormRetrieveIndexResult(ItemPointer indexItemPointer,
ItemPointer heapItemPointer) ItemPointer heapItemPointer)
{ {
RetrieveIndexResult result; RetrieveIndexResult result;
Assert(ItemPointerIsValid(indexItemPointer)); Assert(ItemPointerIsValid(indexItemPointer));
Assert(ItemPointerIsValid(heapItemPointer)); Assert(ItemPointerIsValid(heapItemPointer));
result = (RetrieveIndexResult) palloc(sizeof *result); result = (RetrieveIndexResult) palloc(sizeof *result);
result->index_iptr = *indexItemPointer; result->index_iptr = *indexItemPointer;
result->heap_iptr = *heapItemPointer; result->heap_iptr = *heapItemPointer;
return (result); return (result);
} }
/* /*
@ -425,19 +461,21 @@ FormRetrieveIndexResult(ItemPointer indexItemPointer,
* *
* Change me if adding an attribute to IndexTuples!!!!!!!!!!! * Change me if adding an attribute to IndexTuples!!!!!!!!!!!
*/ */
static Size static Size
IndexInfoFindDataOffset(unsigned short t_info) IndexInfoFindDataOffset(unsigned short t_info)
{ {
if (!(t_info & INDEX_NULL_MASK)) if (!(t_info & INDEX_NULL_MASK))
return((Size) sizeof(IndexTupleData)); return ((Size) sizeof(IndexTupleData));
else { else
Size size = sizeof(IndexTupleData); {
Size size = sizeof(IndexTupleData);
if (t_info & INDEX_NULL_MASK) { if (t_info & INDEX_NULL_MASK)
size += sizeof(IndexAttributeBitMapData); {
size += sizeof(IndexAttributeBitMapData);
}
return DOUBLEALIGN(size); /* be conservative */
} }
return DOUBLEALIGN(size); /* be conservative */
}
} }
/* /*
@ -445,17 +483,17 @@ IndexInfoFindDataOffset(unsigned short t_info)
* we assume we have space that is already palloc'ed. * we assume we have space that is already palloc'ed.
*/ */
void void
CopyIndexTuple(IndexTuple source, IndexTuple *target) CopyIndexTuple(IndexTuple source, IndexTuple * target)
{ {
Size size; Size size;
IndexTuple ret; IndexTuple ret;
size = IndexTupleSize(source); size = IndexTupleSize(source);
if (*target == NULL) { if (*target == NULL)
*target = (IndexTuple) palloc(size); {
} *target = (IndexTuple) palloc(size);
}
ret = *target; ret = *target;
memmove((char*)ret, (char*)source, size); memmove((char *) ret, (char *) source, size);
} }

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* indexvalid.c-- * indexvalid.c--
* index tuple qualification validity checking code * index tuple qualification validity checking code
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/common/Attic/indexvalid.c,v 1.14 1997/03/18 18:38:19 scrappy Exp $ * $Header: /cvsroot/pgsql/src/backend/access/common/Attic/indexvalid.c,v 1.15 1997/09/07 04:37:38 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -21,64 +21,70 @@
#include <executor/execdebug.h> #include <executor/execdebug.h>
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* index scan key qualification code * index scan key qualification code
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
int NIndexTupleProcessed; int NIndexTupleProcessed;
/* ---------------- /* ----------------
* index_keytest * index_keytest
* *
* old comments * old comments
* May eventually combine with other tests (like timeranges)? * May eventually combine with other tests (like timeranges)?
* Should have Buffer buffer; as an argument and pass it to amgetattr. * Should have Buffer buffer; as an argument and pass it to amgetattr.
* ---------------- * ----------------
*/ */
bool bool
index_keytest(IndexTuple tuple, index_keytest(IndexTuple tuple,
TupleDesc tupdesc, TupleDesc tupdesc,
int scanKeySize, int scanKeySize,
ScanKey key) ScanKey key)
{ {
bool isNull; bool isNull;
Datum datum; Datum datum;
int test; int test;
IncrIndexProcessed(); IncrIndexProcessed();
while (scanKeySize > 0) { while (scanKeySize > 0)
datum = index_getattr(tuple, {
key[0].sk_attno, datum = index_getattr(tuple,
tupdesc, key[0].sk_attno,
&isNull); tupdesc,
&isNull);
if (isNull) { if (isNull)
/* XXX eventually should check if SK_ISNULL */ {
return (false); /* XXX eventually should check if SK_ISNULL */
return (false);
}
if (key[0].sk_flags & SK_ISNULL)
{
return (false);
}
if (key[0].sk_flags & SK_COMMUTE)
{
test = (*(key[0].sk_func))
(DatumGetPointer(key[0].sk_argument),
datum) ? 1 : 0;
}
else
{
test = (*(key[0].sk_func))
(datum,
DatumGetPointer(key[0].sk_argument)) ? 1 : 0;
}
if (!test == !(key[0].sk_flags & SK_NEGATE))
{
return (false);
}
scanKeySize -= 1;
key++;
} }
if (key[0].sk_flags & SK_ISNULL) { return (true);
return (false);
}
if (key[0].sk_flags & SK_COMMUTE) {
test = (*(key[0].sk_func))
(DatumGetPointer(key[0].sk_argument),
datum) ? 1 : 0;
} else {
test = (*(key[0].sk_func))
(datum,
DatumGetPointer(key[0].sk_argument)) ? 1 : 0;
}
if (!test == !(key[0].sk_flags & SK_NEGATE)) {
return (false);
}
scanKeySize -= 1;
key++;
}
return (true);
} }

View File

@ -1,14 +1,14 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* printtup.c-- * printtup.c--
* Routines to print out tuples to the destination (binary or non-binary * Routines to print out tuples to the destination (binary or non-binary
* portals, frontend/interactive backend, etc.). * portals, frontend/interactive backend, etc.).
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.15 1997/08/26 23:31:23 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.16 1997/09/07 04:37:39 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -24,271 +24,296 @@
#include <utils/syscache.h> #include <utils/syscache.h>
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* printtup / debugtup support * printtup / debugtup support
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
/* ---------------- /* ----------------
* typtoout - used by printtup and debugtup * typtoout - used by printtup and debugtup
* ---------------- * ----------------
*/ */
Oid Oid
typtoout(Oid type) typtoout(Oid type)
{ {
HeapTuple typeTuple; HeapTuple typeTuple;
typeTuple = SearchSysCacheTuple(TYPOID, typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type), ObjectIdGetDatum(type),
0, 0, 0); 0, 0, 0);
if (HeapTupleIsValid(typeTuple)) if (HeapTupleIsValid(typeTuple))
return((Oid) return ((Oid)
((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput); ((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);
elog(WARN, "typtoout: Cache lookup of type %d failed", type); elog(WARN, "typtoout: Cache lookup of type %d failed", type);
return(InvalidOid); return (InvalidOid);
} }
Oid Oid
gettypelem(Oid type) gettypelem(Oid type)
{ {
HeapTuple typeTuple; HeapTuple typeTuple;
typeTuple = SearchSysCacheTuple(TYPOID, typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type), ObjectIdGetDatum(type),
0,0,0); 0, 0, 0);
if (HeapTupleIsValid(typeTuple)) if (HeapTupleIsValid(typeTuple))
return((Oid) return ((Oid)
((TypeTupleForm) GETSTRUCT(typeTuple))->typelem); ((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);
elog(WARN, "typtoout: Cache lookup of type %d failed", type); elog(WARN, "typtoout: Cache lookup of type %d failed", type);
return(InvalidOid); return (InvalidOid);
} }
/* ---------------- /* ----------------
* printtup * printtup
* ---------------- * ----------------
*/ */
void void
printtup(HeapTuple tuple, TupleDesc typeinfo) printtup(HeapTuple tuple, TupleDesc typeinfo)
{ {
int i, j, k; int i,
char *outputstr, *attr; j,
bool isnull; k;
Oid typoutput; char *outputstr,
*attr;
bool isnull;
Oid typoutput;
/* ---------------- /* ----------------
* tell the frontend to expect new tuple data * tell the frontend to expect new tuple data
* ---------------- * ----------------
*/ */
pq_putnchar("D", 1); pq_putnchar("D", 1);
/* ---------------- /* ----------------
* send a bitmap of which attributes are null * send a bitmap of which attributes are null
* ---------------- * ----------------
*/ */
j = 0; j = 0;
k = 1 << 7; k = 1 << 7;
for (i = 0; i < tuple->t_natts; ) { for (i = 0; i < tuple->t_natts;)
i++; /* heap_getattr is a macro, so no increment */ {
attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull); i++; /* heap_getattr is a macro, so no
if (!isnull) * increment */
j |= k; attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull);
k >>= 1; if (!isnull)
if (!(i & 7)) { j |= k;
pq_putint(j, 1); k >>= 1;
j = 0; if (!(i & 7))
k = 1 << 7; {
pq_putint(j, 1);
j = 0;
k = 1 << 7;
}
} }
} if (i & 7)
if (i & 7) pq_putint(j, 1);
pq_putint(j, 1);
/* ---------------- /* ----------------
* send the attributes of this tuple * send the attributes of this tuple
* ---------------- * ----------------
*/ */
for (i = 0; i < tuple->t_natts; ++i) { for (i = 0; i < tuple->t_natts; ++i)
attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull); {
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid); attr = heap_getattr(tuple, InvalidBuffer, i + 1, typeinfo, &isnull);
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
if (!isnull && OidIsValid(typoutput)) { if (!isnull && OidIsValid(typoutput))
outputstr = fmgr(typoutput, attr, {
gettypelem(typeinfo->attrs[i]->atttypid)); outputstr = fmgr(typoutput, attr,
pq_putint(strlen(outputstr)+4, 4); gettypelem(typeinfo->attrs[i]->atttypid));
pq_putnchar(outputstr, strlen(outputstr)); pq_putint(strlen(outputstr) + 4, 4);
pfree(outputstr); pq_putnchar(outputstr, strlen(outputstr));
pfree(outputstr);
}
} }
}
} }
/* ---------------- /* ----------------
* printatt * printatt
* ---------------- * ----------------
*/ */
static void static void
printatt(unsigned attributeId, printatt(unsigned attributeId,
AttributeTupleForm attributeP, AttributeTupleForm attributeP,
char *value) char *value)
{ {
printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, byval = %c)\n", printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, byval = %c)\n",
attributeId, attributeId,
attributeP->attname.data, attributeP->attname.data,
value != NULL ? " = \"" : "", value != NULL ? " = \"" : "",
value != NULL ? value : "", value != NULL ? value : "",
value != NULL ? "\"" : "", value != NULL ? "\"" : "",
(unsigned int) (attributeP->atttypid), (unsigned int) (attributeP->atttypid),
attributeP->attlen, attributeP->attlen,
attributeP->attbyval ? 't' : 'f'); attributeP->attbyval ? 't' : 'f');
} }
/* ---------------- /* ----------------
* showatts * showatts
* ---------------- * ----------------
*/ */
void void
showatts(char *name, TupleDesc tupleDesc) showatts(char *name, TupleDesc tupleDesc)
{ {
int i; int i;
int natts = tupleDesc->natts; int natts = tupleDesc->natts;
AttributeTupleForm *attinfo = tupleDesc->attrs; AttributeTupleForm *attinfo = tupleDesc->attrs;
puts(name); puts(name);
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
printatt((unsigned) i+1, attinfo[i], (char *) NULL); printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
printf("\t----\n"); printf("\t----\n");
} }
/* ---------------- /* ----------------
* debugtup * debugtup
* ---------------- * ----------------
*/ */
void void
debugtup(HeapTuple tuple, TupleDesc typeinfo) debugtup(HeapTuple tuple, TupleDesc typeinfo)
{ {
register int i; register int i;
char *attr, *value; char *attr,
bool isnull; *value;
Oid typoutput; bool isnull;
Oid typoutput;
for (i = 0; i < tuple->t_natts; ++i) { for (i = 0; i < tuple->t_natts; ++i)
attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull); {
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid); attr = heap_getattr(tuple, InvalidBuffer, i + 1, typeinfo, &isnull);
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
if (!isnull && OidIsValid(typoutput)) { if (!isnull && OidIsValid(typoutput))
value = fmgr(typoutput, attr, {
gettypelem(typeinfo->attrs[i]->atttypid)); value = fmgr(typoutput, attr,
printatt((unsigned) i+1, typeinfo->attrs[i], value); gettypelem(typeinfo->attrs[i]->atttypid));
pfree(value); printatt((unsigned) i + 1, typeinfo->attrs[i], value);
pfree(value);
}
} }
} printf("\t----\n");
printf("\t----\n");
} }
/* ---------------- /* ----------------
* printtup_internal * printtup_internal
* Protocol expects either T, D, C, E, or N. * Protocol expects either T, D, C, E, or N.
* We use a different data prefix, e.g. 'B' instead of 'D' to * We use a different data prefix, e.g. 'B' instead of 'D' to
* indicate a tuple in internal (binary) form. * indicate a tuple in internal (binary) form.
* *
* This is same as printtup, except we don't use the typout func. * This is same as printtup, except we don't use the typout func.
* ---------------- * ----------------
*/ */
void void
printtup_internal(HeapTuple tuple, TupleDesc typeinfo) printtup_internal(HeapTuple tuple, TupleDesc typeinfo)
{ {
int i, j, k; int i,
char *attr; j,
bool isnull; k;
char *attr;
bool isnull;
/* ---------------- /* ----------------
* tell the frontend to expect new tuple data * tell the frontend to expect new tuple data
* ---------------- * ----------------
*/ */
pq_putnchar("B", 1); pq_putnchar("B", 1);
/* ---------------- /* ----------------
* send a bitmap of which attributes are null * send a bitmap of which attributes are null
* ---------------- * ----------------
*/ */
j = 0; j = 0;
k = 1 << 7; k = 1 << 7;
for (i = 0; i < tuple->t_natts; ) { for (i = 0; i < tuple->t_natts;)
i++; /* heap_getattr is a macro, so no increment */ {
attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull); i++; /* heap_getattr is a macro, so no
if (!isnull) * increment */
j |= k; attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull);
k >>= 1; if (!isnull)
if (!(i & 7)) { j |= k;
pq_putint(j, 1); k >>= 1;
j = 0; if (!(i & 7))
k = 1 << 7;
}
}
if (i & 7)
pq_putint(j, 1);
/* ----------------
* send the attributes of this tuple
* ----------------
*/
#ifdef IPORTAL_DEBUG
fprintf(stderr, "sending tuple with %d atts\n", tuple->t_natts);
#endif
for (i = 0; i < tuple->t_natts; ++i) {
int32 len = typeinfo->attrs[i]->attlen;
attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
if (!isnull) {
/* # of bytes, and opaque data */
if (len == -1) {
/* variable length, assume a varlena structure */
len = VARSIZE(attr) - VARHDRSZ;
pq_putint(len, sizeof(int32));
pq_putnchar(VARDATA(attr), len);
#ifdef IPORTAL_DEBUG
{ {
char *d = VARDATA(attr); pq_putint(j, 1);
j = 0;
fprintf(stderr, "length %d data %x%x%x%x\n", k = 1 << 7;
len, *d, *(d+1), *(d+2), *(d+3)); }
}
if (i & 7)
pq_putint(j, 1);
/* ----------------
* send the attributes of this tuple
* ----------------
*/
#ifdef IPORTAL_DEBUG
fprintf(stderr, "sending tuple with %d atts\n", tuple->t_natts);
#endif
for (i = 0; i < tuple->t_natts; ++i)
{
int32 len = typeinfo->attrs[i]->attlen;
attr = heap_getattr(tuple, InvalidBuffer, i + 1, typeinfo, &isnull);
if (!isnull)
{
/* # of bytes, and opaque data */
if (len == -1)
{
/* variable length, assume a varlena structure */
len = VARSIZE(attr) - VARHDRSZ;
pq_putint(len, sizeof(int32));
pq_putnchar(VARDATA(attr), len);
#ifdef IPORTAL_DEBUG
{
char *d = VARDATA(attr);
fprintf(stderr, "length %d data %x%x%x%x\n",
len, *d, *(d + 1), *(d + 2), *(d + 3));
}
#endif
}
else
{
/* fixed size */
if (typeinfo->attrs[i]->attbyval)
{
int8 i8;
int16 i16;
int32 i32;
pq_putint(len, sizeof(int32));
switch (len)
{
case sizeof(int8):
i8 = DatumGetChar(attr);
pq_putnchar((char *) &i8, len);
break;
case sizeof(int16):
i16 = DatumGetInt16(attr);
pq_putnchar((char *) &i16, len);
break;
case sizeof(int32):
i32 = DatumGetInt32(attr);
pq_putnchar((char *) &i32, len);
break;
}
#ifdef IPORTAL_DEBUG
fprintf(stderr, "byval length %d data %d\n", len, attr);
#endif
}
else
{
pq_putint(len, sizeof(int32));
pq_putnchar(attr, len);
#ifdef IPORTAL_DEBUG
fprintf(stderr, "byref length %d data %x\n", len, attr);
#endif
}
}
} }
#endif
} else {
/* fixed size */
if (typeinfo->attrs[i]->attbyval) {
int8 i8;
int16 i16;
int32 i32;
pq_putint(len, sizeof(int32));
switch (len) {
case sizeof(int8):
i8 = DatumGetChar(attr);
pq_putnchar((char *) &i8, len);
break;
case sizeof(int16):
i16 = DatumGetInt16(attr);
pq_putnchar((char *) &i16, len);
break;
case sizeof(int32):
i32 = DatumGetInt32(attr);
pq_putnchar((char *) &i32, len);
break;
}
#ifdef IPORTAL_DEBUG
fprintf(stderr, "byval length %d data %d\n", len, attr);
#endif
} else {
pq_putint(len, sizeof(int32));
pq_putnchar(attr, len);
#ifdef IPORTAL_DEBUG
fprintf(stderr, "byref length %d data %x\n", len, attr);
#endif
}
}
} }
}
} }

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* scan.c-- * scan.c--
* scan direction and key code * scan direction and key code
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.9 1996/11/05 07:42:45 scrappy Exp $ * $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.10 1997/09/07 04:37:39 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -19,49 +19,49 @@
/* /*
* ScanKeyEntryIsLegal -- * ScanKeyEntryIsLegal --
* True iff the scan key entry is legal. * True iff the scan key entry is legal.
*/ */
#define ScanKeyEntryIsLegal(entry) \ #define ScanKeyEntryIsLegal(entry) \
((bool) (AssertMacro(PointerIsValid(entry)) && \ ((bool) (AssertMacro(PointerIsValid(entry)) && \
AttributeNumberIsValid(entry->sk_attno))) AttributeNumberIsValid(entry->sk_attno)))
/* /*
* ScanKeyEntrySetIllegal -- * ScanKeyEntrySetIllegal --
* Marks a scan key entry as illegal. * Marks a scan key entry as illegal.
*/ */
void void
ScanKeyEntrySetIllegal(ScanKey entry) ScanKeyEntrySetIllegal(ScanKey entry)
{ {
Assert(PointerIsValid(entry)); Assert(PointerIsValid(entry));
entry->sk_flags = 0; /* just in case... */ entry->sk_flags = 0; /* just in case... */
entry->sk_attno = InvalidAttrNumber; entry->sk_attno = InvalidAttrNumber;
entry->sk_procedure = 0; /* should be InvalidRegProcedure */ entry->sk_procedure = 0; /* should be InvalidRegProcedure */
} }
/* /*
* ScanKeyEntryInitialize -- * ScanKeyEntryInitialize --
* Initializes an scan key entry. * Initializes an scan key entry.
* *
* Note: * Note:
* Assumes the scan key entry is valid. * Assumes the scan key entry is valid.
* Assumes the intialized scan key entry will be legal. * Assumes the intialized scan key entry will be legal.
*/ */
void void
ScanKeyEntryInitialize(ScanKey entry, ScanKeyEntryInitialize(ScanKey entry,
bits16 flags, bits16 flags,
AttrNumber attributeNumber, AttrNumber attributeNumber,
RegProcedure procedure, RegProcedure procedure,
Datum argument) Datum argument)
{ {
Assert(PointerIsValid(entry)); Assert(PointerIsValid(entry));
entry->sk_flags = flags; entry->sk_flags = flags;
entry->sk_attno = attributeNumber; entry->sk_attno = attributeNumber;
entry->sk_procedure = procedure; entry->sk_procedure = procedure;
entry->sk_argument = argument; entry->sk_argument = argument;
fmgr_info(procedure, &entry->sk_func, &entry->sk_nargs); fmgr_info(procedure, &entry->sk_func, &entry->sk_nargs);
Assert(ScanKeyEntryIsLegal(entry)); Assert(ScanKeyEntryIsLegal(entry));
} }

View File

@ -1,17 +1,17 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* tupdesc.c-- * tupdesc.c--
* POSTGRES tuple descriptor support code * POSTGRES tuple descriptor support code
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.19 1997/08/22 02:55:39 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.20 1997/09/07 04:37:41 momjian Exp $
* *
* NOTES * NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be * some of the executor utility code such as "ExecTypeFromTL" should be
* moved here. * moved here.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -28,518 +28,534 @@
#include <utils/syscache.h> #include <utils/syscache.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* CreateTemplateTupleDesc * CreateTemplateTupleDesc
* *
* This function allocates and zeros a tuple descriptor structure. * This function allocates and zeros a tuple descriptor structure.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
TupleDesc TupleDesc
CreateTemplateTupleDesc(int natts) CreateTemplateTupleDesc(int natts)
{ {
uint32 size; uint32 size;
TupleDesc desc; TupleDesc desc;
/* ---------------- /* ----------------
* sanity checks * sanity checks
* ---------------- * ----------------
*/ */
AssertArg(natts >= 1); AssertArg(natts >= 1);
/* ---------------- /* ----------------
* allocate enough memory for the tuple descriptor and * allocate enough memory for the tuple descriptor and
* zero it as TupleDescInitEntry assumes that the descriptor * zero it as TupleDescInitEntry assumes that the descriptor
* is filled with NULL pointers. * is filled with NULL pointers.
* ---------------- * ----------------
*/ */
size = natts * sizeof (AttributeTupleForm); size = natts * sizeof(AttributeTupleForm);
desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->attrs = (AttributeTupleForm*) palloc(size); desc->attrs = (AttributeTupleForm *) palloc(size);
desc->constr = NULL; desc->constr = NULL;
memset(desc->attrs, 0, size); memset(desc->attrs, 0, size);
desc->natts = natts; desc->natts = natts;
return (desc); return (desc);
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* CreateTupleDesc * CreateTupleDesc
* *
* This function allocates a new TupleDesc from AttributeTupleForm array * This function allocates a new TupleDesc from AttributeTupleForm array
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
TupleDesc TupleDesc
CreateTupleDesc(int natts, AttributeTupleForm* attrs) CreateTupleDesc(int natts, AttributeTupleForm * attrs)
{ {
TupleDesc desc; TupleDesc desc;
/* ---------------- /* ----------------
* sanity checks * sanity checks
* ---------------- * ----------------
*/ */
AssertArg(natts >= 1); AssertArg(natts >= 1);
desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->attrs = attrs; desc->attrs = attrs;
desc->natts = natts; desc->natts = natts;
desc->constr = NULL; desc->constr = NULL;
return (desc); return (desc);
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* CreateTupleDescCopy * CreateTupleDescCopy
* *
* This function creates a new TupleDesc by copying from an existing * This function creates a new TupleDesc by copying from an existing
* TupleDesc * TupleDesc
* *
* !!! Constraints are not copied !!! * !!! Constraints are not copied !!!
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
TupleDesc TupleDesc
CreateTupleDescCopy(TupleDesc tupdesc) CreateTupleDescCopy(TupleDesc tupdesc)
{ {
TupleDesc desc; TupleDesc desc;
int i, size; int i,
size;
desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->natts = tupdesc->natts; desc->natts = tupdesc->natts;
size = desc->natts * sizeof (AttributeTupleForm); size = desc->natts * sizeof(AttributeTupleForm);
desc->attrs = (AttributeTupleForm*) palloc(size); desc->attrs = (AttributeTupleForm *) palloc(size);
for (i=0;i<desc->natts;i++) { for (i = 0; i < desc->natts; i++)
desc->attrs[i] = {
(AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE); desc->attrs[i] =
memmove(desc->attrs[i], (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
tupdesc->attrs[i], memmove(desc->attrs[i],
ATTRIBUTE_TUPLE_SIZE); tupdesc->attrs[i],
desc->attrs[i]->attnotnull = false; ATTRIBUTE_TUPLE_SIZE);
desc->attrs[i]->atthasdef = false; desc->attrs[i]->attnotnull = false;
} desc->attrs[i]->atthasdef = false;
desc->constr = NULL; }
desc->constr = NULL;
return desc; return desc;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* CreateTupleDescCopyConstr * CreateTupleDescCopyConstr
* *
* This function creates a new TupleDesc by copying from an existing * This function creates a new TupleDesc by copying from an existing
* TupleDesc (with Constraints) * TupleDesc (with Constraints)
* *
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
TupleDesc TupleDesc
CreateTupleDescCopyConstr(TupleDesc tupdesc) CreateTupleDescCopyConstr(TupleDesc tupdesc)
{ {
TupleDesc desc; TupleDesc desc;
TupleConstr *constr = tupdesc->constr; TupleConstr *constr = tupdesc->constr;
int i, size; int i,
size;
desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->natts = tupdesc->natts; desc->natts = tupdesc->natts;
size = desc->natts * sizeof (AttributeTupleForm); size = desc->natts * sizeof(AttributeTupleForm);
desc->attrs = (AttributeTupleForm*) palloc(size); desc->attrs = (AttributeTupleForm *) palloc(size);
for (i=0;i<desc->natts;i++) { for (i = 0; i < desc->natts; i++)
desc->attrs[i] = {
(AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE); desc->attrs[i] =
memmove(desc->attrs[i], (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
tupdesc->attrs[i], memmove(desc->attrs[i],
ATTRIBUTE_TUPLE_SIZE); tupdesc->attrs[i],
} ATTRIBUTE_TUPLE_SIZE);
if (constr) }
{ if (constr)
TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr)); {
TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr));
cpy->has_not_null = constr->has_not_null; cpy->has_not_null = constr->has_not_null;
if ( ( cpy->num_defval = constr->num_defval ) > 0 ) if ((cpy->num_defval = constr->num_defval) > 0)
{ {
cpy->defval = (AttrDefault *) palloc (cpy->num_defval * sizeof (AttrDefault)); cpy->defval = (AttrDefault *) palloc(cpy->num_defval * sizeof(AttrDefault));
memcpy (cpy->defval, constr->defval, cpy->num_defval * sizeof (AttrDefault)); memcpy(cpy->defval, constr->defval, cpy->num_defval * sizeof(AttrDefault));
for (i = cpy->num_defval - 1; i >= 0; i--) for (i = cpy->num_defval - 1; i >= 0; i--)
{ {
if ( constr->defval[i].adbin ) if (constr->defval[i].adbin)
cpy->defval[i].adbin = pstrdup (constr->defval[i].adbin); cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin);
if ( constr->defval[i].adsrc ) if (constr->defval[i].adsrc)
cpy->defval[i].adsrc = pstrdup (constr->defval[i].adsrc); cpy->defval[i].adsrc = pstrdup(constr->defval[i].adsrc);
} }
} }
if ( ( cpy->num_check = constr->num_check ) > 0 ) if ((cpy->num_check = constr->num_check) > 0)
{ {
cpy->check = (ConstrCheck *) palloc (cpy->num_check * sizeof (ConstrCheck)); cpy->check = (ConstrCheck *) palloc(cpy->num_check * sizeof(ConstrCheck));
memcpy (cpy->check, constr->check, cpy->num_check * sizeof (ConstrCheck)); memcpy(cpy->check, constr->check, cpy->num_check * sizeof(ConstrCheck));
for (i = cpy->num_check - 1; i >= 0; i--) for (i = cpy->num_check - 1; i >= 0; i--)
{ {
if ( constr->check[i].ccname ) if (constr->check[i].ccname)
cpy->check[i].ccname = pstrdup (constr->check[i].ccname); cpy->check[i].ccname = pstrdup(constr->check[i].ccname);
if ( constr->check[i].ccbin ) if (constr->check[i].ccbin)
cpy->check[i].ccbin = pstrdup (constr->check[i].ccbin); cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin);
if ( constr->check[i].ccsrc ) if (constr->check[i].ccsrc)
cpy->check[i].ccsrc = pstrdup (constr->check[i].ccsrc); cpy->check[i].ccsrc = pstrdup(constr->check[i].ccsrc);
} }
} }
desc->constr = cpy; desc->constr = cpy;
} }
else else
desc->constr = NULL; desc->constr = NULL;
return desc; return desc;
} }
void void
FreeTupleDesc (TupleDesc tupdesc) FreeTupleDesc(TupleDesc tupdesc)
{ {
int i; int i;
for (i = 0; i < tupdesc->natts; i++) for (i = 0; i < tupdesc->natts; i++)
pfree (tupdesc->attrs[i]); pfree(tupdesc->attrs[i]);
pfree (tupdesc->attrs); pfree(tupdesc->attrs);
if ( tupdesc->constr ) if (tupdesc->constr)
{ {
if ( tupdesc->constr->num_defval > 0 ) if (tupdesc->constr->num_defval > 0)
{ {
AttrDefault *attrdef = tupdesc->constr->defval; AttrDefault *attrdef = tupdesc->constr->defval;
for (i = tupdesc->constr->num_defval - 1; i >= 0; i--) for (i = tupdesc->constr->num_defval - 1; i >= 0; i--)
{ {
if ( attrdef[i].adbin ) if (attrdef[i].adbin)
pfree (attrdef[i].adbin); pfree(attrdef[i].adbin);
if ( attrdef[i].adsrc ) if (attrdef[i].adsrc)
pfree (attrdef[i].adsrc); pfree(attrdef[i].adsrc);
} }
pfree (attrdef); pfree(attrdef);
} }
if ( tupdesc->constr->num_check > 0 ) if (tupdesc->constr->num_check > 0)
{ {
ConstrCheck *check = tupdesc->constr->check; ConstrCheck *check = tupdesc->constr->check;
for (i = tupdesc->constr->num_check - 1; i >= 0; i--) for (i = tupdesc->constr->num_check - 1; i >= 0; i--)
{ {
if ( check[i].ccname ) if (check[i].ccname)
pfree (check[i].ccname); pfree(check[i].ccname);
if ( check[i].ccbin ) if (check[i].ccbin)
pfree (check[i].ccbin); pfree(check[i].ccbin);
if ( check[i].ccsrc ) if (check[i].ccsrc)
pfree (check[i].ccsrc); pfree(check[i].ccsrc);
} }
pfree (check); pfree(check);
} }
pfree (tupdesc->constr); pfree(tupdesc->constr);
} }
pfree (tupdesc); pfree(tupdesc);
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* TupleDescInitEntry * TupleDescInitEntry
* *
* This function initializes a single attribute structure in * This function initializes a single attribute structure in
* a preallocated tuple descriptor. * a preallocated tuple descriptor.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
bool bool
TupleDescInitEntry(TupleDesc desc, TupleDescInitEntry(TupleDesc desc,
AttrNumber attributeNumber, AttrNumber attributeNumber,
char *attributeName, char *attributeName,
char *typeName, char *typeName,
int attdim, int attdim,
bool attisset) bool attisset)
{ {
HeapTuple tuple; HeapTuple tuple;
TypeTupleForm typeForm; TypeTupleForm typeForm;
AttributeTupleForm att; AttributeTupleForm att;
/* ----------------
* sanity checks
* ----------------
*/
AssertArg(PointerIsValid(desc));
AssertArg(attributeNumber >= 1);
/* attributeName's are sometimes NULL,
from resdom's. I don't know why that is, though -- Jolly */
/* AssertArg(NameIsValid(attributeName));*/
/* AssertArg(NameIsValid(typeName));*/
AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1]));
/* ----------------
* allocate storage for this attribute
* ----------------
*/
att = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
desc->attrs[attributeNumber - 1] = att;
/* ----------------
* initialize some of the attribute fields
* ----------------
*/
att->attrelid = 0; /* dummy value */
if (attributeName != NULL)
namestrcpy(&(att->attname), attributeName);
else
memset(att->attname.data,0,NAMEDATALEN);
att->attdisbursion = 0; /* dummy value */
att->attcacheoff = -1;
att->attnum = attributeNumber;
att->attnelems = attdim;
att->attisset = attisset;
att->attnotnull = false;
att->atthasdef = false;
/* ----------------
* search the system cache for the type tuple of the attribute
* we are creating so that we can get the typeid and some other
* stuff.
*
* Note: in the special case of
*
* create EMP (name = char16, manager = EMP)
*
* RelationNameCreateHeapRelation() calls BuildDesc() which
* calls this routine and since EMP does not exist yet, the
* system cache lookup below fails. That's fine, but rather
* then doing a elog(WARN) we just leave that information
* uninitialized, return false, then fix things up later.
* -cim 6/14/90
* ----------------
*/
tuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typeName),
0,0,0);
if (! HeapTupleIsValid(tuple)) {
/* ---------------- /* ----------------
* here type info does not exist yet so we just fill * sanity checks
* the attribute with dummy information and return false.
* ---------------- * ----------------
*/ */
att->atttypid = InvalidOid; AssertArg(PointerIsValid(desc));
att->attlen = (int16) 0; AssertArg(attributeNumber >= 1);
att->attbyval = (bool) 0;
att->attalign = 'i';
return false;
}
/* ---------------- /*
* type info exists so we initialize our attribute * attributeName's are sometimes NULL, from resdom's. I don't know
* information from the type tuple we found.. * why that is, though -- Jolly
* ---------------- */
*/ /* AssertArg(NameIsValid(attributeName));*/
typeForm = (TypeTupleForm) GETSTRUCT(tuple); /* AssertArg(NameIsValid(typeName));*/
att->atttypid = tuple->t_oid; AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1]));
att->attalign = typeForm->typalign;
/* ------------------------
If this attribute is a set, what is really stored in the
attribute is the OID of a tuple in the pg_proc catalog.
The pg_proc tuple contains the query string which defines
this set - i.e., the query to run to get the set.
So the atttypid (just assigned above) refers to the type returned
by this query, but the actual length of this attribute is the
length (size) of an OID.
Why not just make the atttypid point to the OID type, instead
of the type the query returns? Because the executor uses the atttypid
to tell the front end what type will be returned (in BeginCommand),
and in the end the type returned will be the result of the query, not
an OID.
Why not wait until the return type of the set is known (i.e., the
recursive call to the executor to execute the set has returned)
before telling the front end what the return type will be? Because
the executor is a delicate thing, and making sure that the correct
order of front-end commands is maintained is messy, especially
considering that target lists may change as inherited attributes
are considered, etc. Ugh.
-----------------------------------------
*/
if (attisset) {
Type t = type("oid");
att->attlen = tlen(t);
att->attbyval = tbyval(t);
} else {
att->attlen = typeForm->typlen;
att->attbyval = typeForm->typbyval;
}
return true; /* ----------------
* allocate storage for this attribute
* ----------------
*/
att = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
desc->attrs[attributeNumber - 1] = att;
/* ----------------
* initialize some of the attribute fields
* ----------------
*/
att->attrelid = 0; /* dummy value */
if (attributeName != NULL)
namestrcpy(&(att->attname), attributeName);
else
memset(att->attname.data, 0, NAMEDATALEN);
att->attdisbursion = 0; /* dummy value */
att->attcacheoff = -1;
att->attnum = attributeNumber;
att->attnelems = attdim;
att->attisset = attisset;
att->attnotnull = false;
att->atthasdef = false;
/* ----------------
* search the system cache for the type tuple of the attribute
* we are creating so that we can get the typeid and some other
* stuff.
*
* Note: in the special case of
*
* create EMP (name = char16, manager = EMP)
*
* RelationNameCreateHeapRelation() calls BuildDesc() which
* calls this routine and since EMP does not exist yet, the
* system cache lookup below fails. That's fine, but rather
* then doing a elog(WARN) we just leave that information
* uninitialized, return false, then fix things up later.
* -cim 6/14/90
* ----------------
*/
tuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typeName),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
/* ----------------
* here type info does not exist yet so we just fill
* the attribute with dummy information and return false.
* ----------------
*/
att->atttypid = InvalidOid;
att->attlen = (int16) 0;
att->attbyval = (bool) 0;
att->attalign = 'i';
return false;
}
/* ----------------
* type info exists so we initialize our attribute
* information from the type tuple we found..
* ----------------
*/
typeForm = (TypeTupleForm) GETSTRUCT(tuple);
att->atttypid = tuple->t_oid;
att->attalign = typeForm->typalign;
/* ------------------------
If this attribute is a set, what is really stored in the
attribute is the OID of a tuple in the pg_proc catalog.
The pg_proc tuple contains the query string which defines
this set - i.e., the query to run to get the set.
So the atttypid (just assigned above) refers to the type returned
by this query, but the actual length of this attribute is the
length (size) of an OID.
Why not just make the atttypid point to the OID type, instead
of the type the query returns? Because the executor uses the atttypid
to tell the front end what type will be returned (in BeginCommand),
and in the end the type returned will be the result of the query, not
an OID.
Why not wait until the return type of the set is known (i.e., the
recursive call to the executor to execute the set has returned)
before telling the front end what the return type will be? Because
the executor is a delicate thing, and making sure that the correct
order of front-end commands is maintained is messy, especially
considering that target lists may change as inherited attributes
are considered, etc. Ugh.
-----------------------------------------
*/
if (attisset)
{
Type t = type("oid");
att->attlen = tlen(t);
att->attbyval = tbyval(t);
}
else
{
att->attlen = typeForm->typlen;
att->attbyval = typeForm->typbyval;
}
return true;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* TupleDescMakeSelfReference * TupleDescMakeSelfReference
* *
* This function initializes a "self-referential" attribute like * This function initializes a "self-referential" attribute like
* manager in "create EMP (name=text, manager = EMP)". * manager in "create EMP (name=text, manager = EMP)".
* It calls TypeShellMake() which inserts a "shell" type * It calls TypeShellMake() which inserts a "shell" type
* tuple into pg_type. A self-reference is one kind of set, so * tuple into pg_type. A self-reference is one kind of set, so
* its size and byval are the same as for a set. See the comments * its size and byval are the same as for a set. See the comments
* above in TupleDescInitEntry. * above in TupleDescInitEntry.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
static void static void
TupleDescMakeSelfReference(TupleDesc desc, TupleDescMakeSelfReference(TupleDesc desc,
AttrNumber attnum, AttrNumber attnum,
char *relname) char *relname)
{ {
AttributeTupleForm att; AttributeTupleForm att;
Type t = type("oid"); Type t = type("oid");
att = desc->attrs[attnum-1]; att = desc->attrs[attnum - 1];
att->atttypid = TypeShellMake(relname); att->atttypid = TypeShellMake(relname);
att->attlen = tlen(t); att->attlen = tlen(t);
att->attbyval = tbyval(t); att->attbyval = tbyval(t);
att->attnelems = 0; att->attnelems = 0;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* BuildDescForRelation * BuildDescForRelation
* *
* This is a general purpose function identical to BuildDesc * This is a general purpose function identical to BuildDesc
* but is used by the DefineRelation() code to catch the * but is used by the DefineRelation() code to catch the
* special case where you * special case where you
* *
* create FOO ( ..., x = FOO ) * create FOO ( ..., x = FOO )
* *
* here, the initial type lookup for "x = FOO" will fail * here, the initial type lookup for "x = FOO" will fail
* because FOO isn't in the catalogs yet. But since we * because FOO isn't in the catalogs yet. But since we
* are creating FOO, instead of doing an elog() we add * are creating FOO, instead of doing an elog() we add
* a shell type tuple to pg_type and fix things later * a shell type tuple to pg_type and fix things later
* in amcreate(). * in amcreate().
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
TupleDesc TupleDesc
BuildDescForRelation(List *schema, char *relname) BuildDescForRelation(List * schema, char *relname)
{ {
int natts; int natts;
AttrNumber attnum; AttrNumber attnum;
List *p; List *p;
TupleDesc desc; TupleDesc desc;
AttrDefault *attrdef = NULL; AttrDefault *attrdef = NULL;
TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr)); TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr));
char *attname; char *attname;
char *typename; char *typename;
int attdim; int attdim;
int ndef = 0; int ndef = 0;
bool attisset; bool attisset;
/* ----------------
* allocate a new tuple descriptor
* ----------------
*/
natts = length(schema);
desc = CreateTemplateTupleDesc(natts);
constr->has_not_null = false;
attnum = 0;
typename = palloc(NAMEDATALEN);
foreach(p, schema) {
ColumnDef *entry;
List *arry;
/* ---------------- /* ----------------
* for each entry in the list, get the name and type * allocate a new tuple descriptor
* information from the list and have TupleDescInitEntry
* fill in the attribute information we need.
* ---------------- * ----------------
*/ */
attnum++; natts = length(schema);
desc = CreateTemplateTupleDesc(natts);
constr->has_not_null = false;
entry = lfirst(p); attnum = 0;
attname = entry->colname;
arry = entry->typename->arrayBounds;
attisset = entry->typename->setof;
strNcpy(typename, entry->typename->name,NAMEDATALEN-1); typename = palloc(NAMEDATALEN);
if (arry != NIL)
attdim = length(arry);
else
attdim = 0;
if (! TupleDescInitEntry(desc, attnum, attname, foreach(p, schema)
typename, attdim, attisset)) {
/* ----------------
* if TupleDescInitEntry() fails, it means there is
* no type in the system catalogs. So now we check if
* the type name equals the relation name. If so we
* have a self reference, otherwise it's an error.
* ----------------
*/
if (!strcmp(typename, relname)) {
TupleDescMakeSelfReference(desc, attnum, relname);
} else
elog(WARN, "DefineRelation: no such type %s",
typename);
}
/*
* this is for char() and varchar(). When an entry is of type
* char() or varchar(), typlen is set to the appropriate length,
* which we'll use here instead. (The catalog lookup only returns
* the length of bpchar and varchar which is not what we want!)
* - ay 6/95
*/
if (entry->typename->typlen > 0) {
desc->attrs[attnum - 1]->attlen = entry->typename->typlen;
}
/* This is for constraints */
if (entry->is_not_null)
constr->has_not_null = true;
desc->attrs[attnum-1]->attnotnull = entry->is_not_null;
if ( entry->defval != NULL )
{ {
if ( attrdef == NULL ) ColumnDef *entry;
attrdef = (AttrDefault*) palloc (natts * sizeof (AttrDefault)); List *arry;
attrdef[ndef].adnum = attnum;
attrdef[ndef].adbin = NULL; /* ----------------
attrdef[ndef].adsrc = entry->defval; * for each entry in the list, get the name and type
ndef++; * information from the list and have TupleDescInitEntry
desc->attrs[attnum-1]->atthasdef = true; * fill in the attribute information we need.
* ----------------
*/
attnum++;
entry = lfirst(p);
attname = entry->colname;
arry = entry->typename->arrayBounds;
attisset = entry->typename->setof;
strNcpy(typename, entry->typename->name, NAMEDATALEN - 1);
if (arry != NIL)
attdim = length(arry);
else
attdim = 0;
if (!TupleDescInitEntry(desc, attnum, attname,
typename, attdim, attisset))
{
/* ----------------
* if TupleDescInitEntry() fails, it means there is
* no type in the system catalogs. So now we check if
* the type name equals the relation name. If so we
* have a self reference, otherwise it's an error.
* ----------------
*/
if (!strcmp(typename, relname))
{
TupleDescMakeSelfReference(desc, attnum, relname);
}
else
elog(WARN, "DefineRelation: no such type %s",
typename);
}
/*
* this is for char() and varchar(). When an entry is of type
* char() or varchar(), typlen is set to the appropriate length,
* which we'll use here instead. (The catalog lookup only returns
* the length of bpchar and varchar which is not what we want!) -
* ay 6/95
*/
if (entry->typename->typlen > 0)
{
desc->attrs[attnum - 1]->attlen = entry->typename->typlen;
}
/* This is for constraints */
if (entry->is_not_null)
constr->has_not_null = true;
desc->attrs[attnum - 1]->attnotnull = entry->is_not_null;
if (entry->defval != NULL)
{
if (attrdef == NULL)
attrdef = (AttrDefault *) palloc(natts * sizeof(AttrDefault));
attrdef[ndef].adnum = attnum;
attrdef[ndef].adbin = NULL;
attrdef[ndef].adsrc = entry->defval;
ndef++;
desc->attrs[attnum - 1]->atthasdef = true;
}
} }
if (constr->has_not_null || ndef > 0)
{
desc->constr = constr;
} if (ndef > 0) /* DEFAULTs */
if ( constr->has_not_null || ndef > 0 ) {
{ if (ndef < natts)
desc->constr = constr; constr->defval = (AttrDefault *)
repalloc(attrdef, ndef * sizeof(AttrDefault));
if ( ndef > 0 ) /* DEFAULTs */ else
{ constr->defval = attrdef;
if ( ndef < natts ) constr->num_defval = ndef;
constr->defval = (AttrDefault*) }
repalloc (attrdef, ndef * sizeof (AttrDefault)); else
else constr->num_defval = 0;
constr->defval = attrdef; constr->num_check = 0;
constr->num_defval = ndef; }
} else
else {
constr->num_defval = 0; pfree(constr);
constr->num_check = 0; desc->constr = NULL;
} }
else return desc;
{
pfree (constr);
desc->constr = NULL;
}
return desc;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* gistget.c-- * gistget.c--
* fetch tuples from a GiST scan. * fetch tuples from a GiST scan.
* *
* *
* *
* IDENTIFICATION * IDENTIFICATION
* /usr/local/devel/pglite/cvs/src/backend/access/gisr/gistget.c,v 1.9.1 1996/11/21 01:00:00 vadim Exp * /usr/local/devel/pglite/cvs/src/backend/access/gisr/gistget.c,v 1.9.1 1996/11/21 01:00:00 vadim Exp
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -22,350 +22,392 @@
#include <storage/bufmgr.h> #include <storage/bufmgr.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
static OffsetNumber gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, static OffsetNumber
ScanDirection dir); gistfindnext(IndexScanDesc s, Page p, OffsetNumber n,
ScanDirection dir);
static RetrieveIndexResult gistscancache(IndexScanDesc s, ScanDirection dir); static RetrieveIndexResult gistscancache(IndexScanDesc s, ScanDirection dir);
static RetrieveIndexResult gistfirst(IndexScanDesc s, ScanDirection dir); static RetrieveIndexResult gistfirst(IndexScanDesc s, ScanDirection dir);
static RetrieveIndexResult gistnext(IndexScanDesc s, ScanDirection dir); static RetrieveIndexResult gistnext(IndexScanDesc s, ScanDirection dir);
static ItemPointer gistheapptr(Relation r, ItemPointer itemp); static ItemPointer gistheapptr(Relation r, ItemPointer itemp);
static bool gistindex_keytest(IndexTuple tuple, TupleDesc tupdesc, static bool
int scanKeySize, ScanKey key, GISTSTATE *giststate, gistindex_keytest(IndexTuple tuple, TupleDesc tupdesc,
Relation r, Page p, OffsetNumber offset); int scanKeySize, ScanKey key, GISTSTATE * giststate,
Relation r, Page p, OffsetNumber offset);
RetrieveIndexResult RetrieveIndexResult
gistgettuple(IndexScanDesc s, ScanDirection dir) gistgettuple(IndexScanDesc s, ScanDirection dir)
{ {
RetrieveIndexResult res; RetrieveIndexResult res;
/* if we have it cached in the scan desc, just return the value */ /* if we have it cached in the scan desc, just return the value */
if ((res = gistscancache(s, dir)) != (RetrieveIndexResult) NULL) if ((res = gistscancache(s, dir)) != (RetrieveIndexResult) NULL)
return (res);
/* not cached, so we'll have to do some work */
if (ItemPointerIsValid(&(s->currentItemData)))
{
res = gistnext(s, dir);
}
else
{
res = gistfirst(s, dir);
}
return (res); return (res);
/* not cached, so we'll have to do some work */
if (ItemPointerIsValid(&(s->currentItemData))) {
res = gistnext(s, dir);
} else {
res = gistfirst(s, dir);
}
return (res);
} }
static RetrieveIndexResult static RetrieveIndexResult
gistfirst(IndexScanDesc s, ScanDirection dir) gistfirst(IndexScanDesc s, ScanDirection dir)
{ {
Buffer b; Buffer b;
Page p; Page p;
OffsetNumber n; OffsetNumber n;
OffsetNumber maxoff; OffsetNumber maxoff;
RetrieveIndexResult res; RetrieveIndexResult res;
GISTPageOpaque po; GISTPageOpaque po;
GISTScanOpaque so; GISTScanOpaque so;
GISTSTACK *stk; GISTSTACK *stk;
BlockNumber blk; BlockNumber blk;
IndexTuple it; IndexTuple it;
b = ReadBuffer(s->relation, GISTP_ROOT); b = ReadBuffer(s->relation, GISTP_ROOT);
p = BufferGetPage(b); p = BufferGetPage(b);
po = (GISTPageOpaque) PageGetSpecialPointer(p); po = (GISTPageOpaque) PageGetSpecialPointer(p);
so = (GISTScanOpaque) s->opaque; so = (GISTScanOpaque) s->opaque;
for (;;) { for (;;)
maxoff = PageGetMaxOffsetNumber(p); {
if (ScanDirectionIsBackward(dir)) maxoff = PageGetMaxOffsetNumber(p);
n = gistfindnext(s, p, maxoff, dir); if (ScanDirectionIsBackward(dir))
else n = gistfindnext(s, p, maxoff, dir);
n = gistfindnext(s, p, FirstOffsetNumber, dir); else
n = gistfindnext(s, p, FirstOffsetNumber, dir);
while (n < FirstOffsetNumber || n > maxoff) { while (n < FirstOffsetNumber || n > maxoff)
{
ReleaseBuffer(b); ReleaseBuffer(b);
if (so->s_stack == (GISTSTACK *) NULL) if (so->s_stack == (GISTSTACK *) NULL)
return ((RetrieveIndexResult) NULL); return ((RetrieveIndexResult) NULL);
stk = so->s_stack; stk = so->s_stack;
b = ReadBuffer(s->relation, stk->gs_blk); b = ReadBuffer(s->relation, stk->gs_blk);
p = BufferGetPage(b); p = BufferGetPage(b);
po = (GISTPageOpaque) PageGetSpecialPointer(p); po = (GISTPageOpaque) PageGetSpecialPointer(p);
maxoff = PageGetMaxOffsetNumber(p); maxoff = PageGetMaxOffsetNumber(p);
if (ScanDirectionIsBackward(dir)) { if (ScanDirectionIsBackward(dir))
n = OffsetNumberPrev(stk->gs_child); {
} else { n = OffsetNumberPrev(stk->gs_child);
n = OffsetNumberNext(stk->gs_child); }
} else
so->s_stack = stk->gs_parent; {
pfree(stk); n = OffsetNumberNext(stk->gs_child);
}
so->s_stack = stk->gs_parent;
pfree(stk);
n = gistfindnext(s, p, n, dir); n = gistfindnext(s, p, n, dir);
}
if (po->flags & F_LEAF)
{
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
ReleaseBuffer(b);
return (res);
}
else
{
stk = (GISTSTACK *) palloc(sizeof(GISTSTACK));
stk->gs_child = n;
stk->gs_blk = BufferGetBlockNumber(b);
stk->gs_parent = so->s_stack;
so->s_stack = stk;
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
blk = ItemPointerGetBlockNumber(&(it->t_tid));
ReleaseBuffer(b);
b = ReadBuffer(s->relation, blk);
p = BufferGetPage(b);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
}
} }
if (po->flags & F_LEAF) {
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
ReleaseBuffer(b);
return (res);
} else {
stk = (GISTSTACK *) palloc(sizeof(GISTSTACK));
stk->gs_child = n;
stk->gs_blk = BufferGetBlockNumber(b);
stk->gs_parent = so->s_stack;
so->s_stack = stk;
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
blk = ItemPointerGetBlockNumber(&(it->t_tid));
ReleaseBuffer(b);
b = ReadBuffer(s->relation, blk);
p = BufferGetPage(b);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
}
}
} }
static RetrieveIndexResult static RetrieveIndexResult
gistnext(IndexScanDesc s, ScanDirection dir) gistnext(IndexScanDesc s, ScanDirection dir)
{ {
Buffer b; Buffer b;
Page p; Page p;
OffsetNumber n; OffsetNumber n;
OffsetNumber maxoff; OffsetNumber maxoff;
RetrieveIndexResult res; RetrieveIndexResult res;
GISTPageOpaque po; GISTPageOpaque po;
GISTScanOpaque so; GISTScanOpaque so;
GISTSTACK *stk; GISTSTACK *stk;
BlockNumber blk; BlockNumber blk;
IndexTuple it; IndexTuple it;
blk = ItemPointerGetBlockNumber(&(s->currentItemData)); blk = ItemPointerGetBlockNumber(&(s->currentItemData));
n = ItemPointerGetOffsetNumber(&(s->currentItemData)); n = ItemPointerGetOffsetNumber(&(s->currentItemData));
if (ScanDirectionIsForward(dir)) { if (ScanDirectionIsForward(dir))
n = OffsetNumberNext(n); {
} else { n = OffsetNumberNext(n);
n = OffsetNumberPrev(n);
}
b = ReadBuffer(s->relation, blk);
p = BufferGetPage(b);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
so = (GISTScanOpaque) s->opaque;
for (;;) {
maxoff = PageGetMaxOffsetNumber(p);
n = gistfindnext(s, p, n, dir);
while (n < FirstOffsetNumber || n > maxoff) {
ReleaseBuffer(b);
if (so->s_stack == (GISTSTACK *) NULL)
return ((RetrieveIndexResult) NULL);
stk = so->s_stack;
b = ReadBuffer(s->relation, stk->gs_blk);
p = BufferGetPage(b);
maxoff = PageGetMaxOffsetNumber(p);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
if (ScanDirectionIsBackward(dir)) {
n = OffsetNumberPrev(stk->gs_child);
} else {
n = OffsetNumberNext(stk->gs_child);
}
so->s_stack = stk->gs_parent;
pfree(stk);
n = gistfindnext(s, p, n, dir);
} }
if (po->flags & F_LEAF) { else
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n); {
n = OffsetNumberPrev(n);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); }
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid)); b = ReadBuffer(s->relation, blk);
p = BufferGetPage(b);
ReleaseBuffer(b); po = (GISTPageOpaque) PageGetSpecialPointer(p);
return (res); so = (GISTScanOpaque) s->opaque;
} else {
stk = (GISTSTACK *) palloc(sizeof(GISTSTACK)); for (;;)
stk->gs_child = n; {
stk->gs_blk = BufferGetBlockNumber(b); maxoff = PageGetMaxOffsetNumber(p);
stk->gs_parent = so->s_stack; n = gistfindnext(s, p, n, dir);
so->s_stack = stk;
while (n < FirstOffsetNumber || n > maxoff)
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); {
blk = ItemPointerGetBlockNumber(&(it->t_tid));
ReleaseBuffer(b);
ReleaseBuffer(b); if (so->s_stack == (GISTSTACK *) NULL)
b = ReadBuffer(s->relation, blk); return ((RetrieveIndexResult) NULL);
p = BufferGetPage(b);
po = (GISTPageOpaque) PageGetSpecialPointer(p); stk = so->s_stack;
b = ReadBuffer(s->relation, stk->gs_blk);
if (ScanDirectionIsBackward(dir)) { p = BufferGetPage(b);
n = PageGetMaxOffsetNumber(p); maxoff = PageGetMaxOffsetNumber(p);
} else { po = (GISTPageOpaque) PageGetSpecialPointer(p);
n = FirstOffsetNumber;
} if (ScanDirectionIsBackward(dir))
{
n = OffsetNumberPrev(stk->gs_child);
}
else
{
n = OffsetNumberNext(stk->gs_child);
}
so->s_stack = stk->gs_parent;
pfree(stk);
n = gistfindnext(s, p, n, dir);
}
if (po->flags & F_LEAF)
{
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
ReleaseBuffer(b);
return (res);
}
else
{
stk = (GISTSTACK *) palloc(sizeof(GISTSTACK));
stk->gs_child = n;
stk->gs_blk = BufferGetBlockNumber(b);
stk->gs_parent = so->s_stack;
so->s_stack = stk;
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
blk = ItemPointerGetBlockNumber(&(it->t_tid));
ReleaseBuffer(b);
b = ReadBuffer(s->relation, blk);
p = BufferGetPage(b);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
if (ScanDirectionIsBackward(dir))
{
n = PageGetMaxOffsetNumber(p);
}
else
{
n = FirstOffsetNumber;
}
}
} }
}
} }
/* Similar to index_keytest, but decompresses the key in the IndexTuple */ /* Similar to index_keytest, but decompresses the key in the IndexTuple */
static bool static bool
gistindex_keytest(IndexTuple tuple, gistindex_keytest(IndexTuple tuple,
TupleDesc tupdesc, TupleDesc tupdesc,
int scanKeySize, int scanKeySize,
ScanKey key, ScanKey key,
GISTSTATE *giststate, GISTSTATE * giststate,
Relation r, Relation r,
Page p, Page p,
OffsetNumber offset) OffsetNumber offset)
{ {
bool isNull; bool isNull;
Datum datum; Datum datum;
int test; int test;
GISTENTRY de; GISTENTRY de;
IncrIndexProcessed(); IncrIndexProcessed();
while (scanKeySize > 0) { while (scanKeySize > 0)
datum = index_getattr(tuple, {
1, datum = index_getattr(tuple,
tupdesc, 1,
&isNull); tupdesc,
gistdentryinit(giststate, &de, (char *)datum, r, p, offset, &isNull);
IndexTupleSize(tuple) - sizeof(IndexTupleData), gistdentryinit(giststate, &de, (char *) datum, r, p, offset,
FALSE); IndexTupleSize(tuple) - sizeof(IndexTupleData),
FALSE);
if (isNull) { if (isNull)
/* XXX eventually should check if SK_ISNULL */ {
return (false); /* XXX eventually should check if SK_ISNULL */
return (false);
}
if (key[0].sk_flags & SK_COMMUTE)
{
test = (*(key[0].sk_func))
(DatumGetPointer(key[0].sk_argument),
&de, key[0].sk_procedure) ? 1 : 0;
}
else
{
test = (*(key[0].sk_func))
(&de,
DatumGetPointer(key[0].sk_argument),
key[0].sk_procedure) ? 1 : 0;
}
if (!test == !(key[0].sk_flags & SK_NEGATE))
{
return (false);
}
scanKeySize -= 1;
key++;
} }
if (key[0].sk_flags & SK_COMMUTE) { return (true);
test = (*(key[0].sk_func))
(DatumGetPointer(key[0].sk_argument),
&de, key[0].sk_procedure) ? 1 : 0;
} else {
test = (*(key[0].sk_func))
(&de,
DatumGetPointer(key[0].sk_argument),
key[0].sk_procedure) ? 1 : 0;
}
if (!test == !(key[0].sk_flags & SK_NEGATE)) {
return (false);
}
scanKeySize -= 1;
key++;
}
return (true);
} }
static OffsetNumber static OffsetNumber
gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir) gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir)
{ {
OffsetNumber maxoff; OffsetNumber maxoff;
char *it; char *it;
GISTPageOpaque po; GISTPageOpaque po;
GISTScanOpaque so; GISTScanOpaque so;
GISTSTATE *giststate; GISTSTATE *giststate;
maxoff = PageGetMaxOffsetNumber(p); maxoff = PageGetMaxOffsetNumber(p);
po = (GISTPageOpaque) PageGetSpecialPointer(p); po = (GISTPageOpaque) PageGetSpecialPointer(p);
so = (GISTScanOpaque) s->opaque; so = (GISTScanOpaque) s->opaque;
giststate = so->giststate; giststate = so->giststate;
/* /*
* If we modified the index during the scan, we may have a pointer to * If we modified the index during the scan, we may have a pointer to
* a ghost tuple, before the scan. If this is the case, back up one. * a ghost tuple, before the scan. If this is the case, back up one.
*/ */
if (so->s_flags & GS_CURBEFORE) { if (so->s_flags & GS_CURBEFORE)
so->s_flags &= ~GS_CURBEFORE; {
n = OffsetNumberPrev(n); so->s_flags &= ~GS_CURBEFORE;
} n = OffsetNumberPrev(n);
while (n >= FirstOffsetNumber && n <= maxoff) {
it = (char *) PageGetItem(p, PageGetItemId(p, n));
if (gistindex_keytest((IndexTuple) it,
RelationGetTupleDescriptor(s->relation),
s->numberOfKeys, s->keyData, giststate,
s->relation, p, n))
break;
if (ScanDirectionIsBackward(dir)) {
n = OffsetNumberPrev(n);
} else {
n = OffsetNumberNext(n);
} }
}
return (n); while (n >= FirstOffsetNumber && n <= maxoff)
{
it = (char *) PageGetItem(p, PageGetItemId(p, n));
if (gistindex_keytest((IndexTuple) it,
RelationGetTupleDescriptor(s->relation),
s->numberOfKeys, s->keyData, giststate,
s->relation, p, n))
break;
if (ScanDirectionIsBackward(dir))
{
n = OffsetNumberPrev(n);
}
else
{
n = OffsetNumberNext(n);
}
}
return (n);
} }
static RetrieveIndexResult static RetrieveIndexResult
gistscancache(IndexScanDesc s, ScanDirection dir) gistscancache(IndexScanDesc s, ScanDirection dir)
{ {
RetrieveIndexResult res; RetrieveIndexResult res;
ItemPointer ip; ItemPointer ip;
if (!(ScanDirectionIsNoMovement(dir) if (!(ScanDirectionIsNoMovement(dir)
&& ItemPointerIsValid(&(s->currentItemData)))) { && ItemPointerIsValid(&(s->currentItemData))))
{
return ((RetrieveIndexResult) NULL); return ((RetrieveIndexResult) NULL);
} }
ip = gistheapptr(s->relation, &(s->currentItemData)); ip = gistheapptr(s->relation, &(s->currentItemData));
if (ItemPointerIsValid(ip)) if (ItemPointerIsValid(ip))
res = FormRetrieveIndexResult(&(s->currentItemData), ip); res = FormRetrieveIndexResult(&(s->currentItemData), ip);
else else
res = (RetrieveIndexResult) NULL; res = (RetrieveIndexResult) NULL;
pfree (ip); pfree(ip);
return (res); return (res);
} }
/* /*
* gistheapptr returns the item pointer to the tuple in the heap relation * gistheapptr returns the item pointer to the tuple in the heap relation
* for which itemp is the index relation item pointer. * for which itemp is the index relation item pointer.
*/ */
static ItemPointer static ItemPointer
gistheapptr(Relation r, ItemPointer itemp) gistheapptr(Relation r, ItemPointer itemp)
{ {
Buffer b; Buffer b;
Page p; Page p;
IndexTuple it; IndexTuple it;
ItemPointer ip; ItemPointer ip;
OffsetNumber n; OffsetNumber n;
ip = (ItemPointer) palloc(sizeof(ItemPointerData)); ip = (ItemPointer) palloc(sizeof(ItemPointerData));
if (ItemPointerIsValid(itemp)) { if (ItemPointerIsValid(itemp))
b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp)); {
p = BufferGetPage(b); b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp));
n = ItemPointerGetOffsetNumber(itemp); p = BufferGetPage(b);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); n = ItemPointerGetOffsetNumber(itemp);
memmove((char *) ip, (char *) &(it->t_tid), it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
sizeof(ItemPointerData)); memmove((char *) ip, (char *) &(it->t_tid),
ReleaseBuffer(b); sizeof(ItemPointerData));
} else { ReleaseBuffer(b);
ItemPointerSetInvalid(ip); }
} else
{
ItemPointerSetInvalid(ip);
}
return (ip); return (ip);
} }

View File

@ -1,11 +1,11 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* gistscan.c-- * gistscan.c--
* routines to manage scans on index relations * routines to manage scans on index relations
* *
* *
* IDENTIFICATION * IDENTIFICATION
* /usr/local/devel/pglite/cvs/src/backend/access/gist/gistscan.c,v 1.7 1995/06/14 00:10:05 jolly Exp * /usr/local/devel/pglite/cvs/src/backend/access/gist/gistscan.c,v 1.7 1995/06/14 00:10:05 jolly Exp
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -21,372 +21,408 @@
#include <storage/lmgr.h> #include <storage/lmgr.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
/* routines defined and used here */ /* routines defined and used here */
static void gistregscan(IndexScanDesc s); static void gistregscan(IndexScanDesc s);
static void gistdropscan(IndexScanDesc s); static void gistdropscan(IndexScanDesc s);
static void gistadjone(IndexScanDesc s, int op, BlockNumber blkno, static void
OffsetNumber offnum); gistadjone(IndexScanDesc s, int op, BlockNumber blkno,
static void adjuststack(GISTSTACK *stk, BlockNumber blkno, OffsetNumber offnum);
static void
adjuststack(GISTSTACK * stk, BlockNumber blkno,
OffsetNumber offnum); OffsetNumber offnum);
static void adjustiptr(IndexScanDesc s, ItemPointer iptr, static void
int op, BlockNumber blkno, OffsetNumber offnum); adjustiptr(IndexScanDesc s, ItemPointer iptr,
int op, BlockNumber blkno, OffsetNumber offnum);
/* /*
* Whenever we start a GiST scan in a backend, we register it in private * Whenever we start a GiST scan in a backend, we register it in private
* space. Then if the GiST index gets updated, we check all registered * space. Then if the GiST index gets updated, we check all registered
* scans and adjust them if the tuple they point at got moved by the * scans and adjust them if the tuple they point at got moved by the
* update. We only need to do this in private space, because when we update * update. We only need to do this in private space, because when we update
* an GiST we have a write lock on the tree, so no other process can have * an GiST we have a write lock on the tree, so no other process can have
* any locks at all on it. A single transaction can have write and read * any locks at all on it. A single transaction can have write and read
* locks on the same object, so that's why we need to handle this case. * locks on the same object, so that's why we need to handle this case.
*/ */
typedef struct GISTScanListData { typedef struct GISTScanListData
IndexScanDesc gsl_scan; {
struct GISTScanListData *gsl_next; IndexScanDesc gsl_scan;
} GISTScanListData; struct GISTScanListData *gsl_next;
} GISTScanListData;
typedef GISTScanListData *GISTScanList; typedef GISTScanListData *GISTScanList;
/* pointer to list of local scans on GiSTs */ /* pointer to list of local scans on GiSTs */
static GISTScanList GISTScans = (GISTScanList) NULL; static GISTScanList GISTScans = (GISTScanList) NULL;
IndexScanDesc IndexScanDesc
gistbeginscan(Relation r, gistbeginscan(Relation r,
bool fromEnd, bool fromEnd,
uint16 nkeys, uint16 nkeys,
ScanKey key) ScanKey key)
{ {
IndexScanDesc s; IndexScanDesc s;
RelationSetLockForRead(r); RelationSetLockForRead(r);
s = RelationGetIndexScan(r, fromEnd, nkeys, key); s = RelationGetIndexScan(r, fromEnd, nkeys, key);
gistregscan(s); gistregscan(s);
return (s); return (s);
} }
void void
gistrescan(IndexScanDesc s, bool fromEnd, ScanKey key) gistrescan(IndexScanDesc s, bool fromEnd, ScanKey key)
{ {
GISTScanOpaque p; GISTScanOpaque p;
int i; int i;
if (!IndexScanIsValid(s)) { if (!IndexScanIsValid(s))
elog(WARN, "gistrescan: invalid scan.");
return;
}
/*
* Clear all the pointers.
*/
ItemPointerSetInvalid(&s->previousItemData);
ItemPointerSetInvalid(&s->currentItemData);
ItemPointerSetInvalid(&s->nextItemData);
ItemPointerSetInvalid(&s->previousMarkData);
ItemPointerSetInvalid(&s->currentMarkData);
ItemPointerSetInvalid(&s->nextMarkData);
/*
* Set flags.
*/
if (RelationGetNumberOfBlocks(s->relation) == 0) {
s->flags = ScanUnmarked;
} else if (fromEnd) {
s->flags = ScanUnmarked | ScanUncheckedPrevious;
} else {
s->flags = ScanUnmarked | ScanUncheckedNext;
}
s->scanFromEnd = fromEnd;
if (s->numberOfKeys > 0) {
memmove(s->keyData,
key,
s->numberOfKeys * sizeof(ScanKeyData));
}
p = (GISTScanOpaque) s->opaque;
if (p != (GISTScanOpaque) NULL) {
gistfreestack(p->s_stack);
gistfreestack(p->s_markstk);
p->s_stack = p->s_markstk = (GISTSTACK *) NULL;
p->s_flags = 0x0;
for (i = 0; i < s->numberOfKeys; i++)
{ {
s->keyData[i].sk_procedure elog(WARN, "gistrescan: invalid scan.");
= RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno, return;
s->keyData[i].sk_procedure);
s->keyData[i].sk_func = p->giststate->consistentFn;
} }
} else {
/* initialize opaque data */ /*
p = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData)); * Clear all the pointers.
p->s_stack = p->s_markstk = (GISTSTACK *) NULL; */
p->s_flags = 0x0;
s->opaque = p; ItemPointerSetInvalid(&s->previousItemData);
p->giststate = (GISTSTATE *)palloc(sizeof(GISTSTATE)); ItemPointerSetInvalid(&s->currentItemData);
initGISTstate(p->giststate, s->relation); ItemPointerSetInvalid(&s->nextItemData);
ItemPointerSetInvalid(&s->previousMarkData);
ItemPointerSetInvalid(&s->currentMarkData);
ItemPointerSetInvalid(&s->nextMarkData);
/*
* Set flags.
*/
if (RelationGetNumberOfBlocks(s->relation) == 0)
{
s->flags = ScanUnmarked;
}
else if (fromEnd)
{
s->flags = ScanUnmarked | ScanUncheckedPrevious;
}
else
{
s->flags = ScanUnmarked | ScanUncheckedNext;
}
s->scanFromEnd = fromEnd;
if (s->numberOfKeys > 0) if (s->numberOfKeys > 0)
/* {
** Play games here with the scan key to use the Consistent memmove(s->keyData,
** function for all comparisons: key,
** 1) the sk_procedure field will now be used to hold the s->numberOfKeys * sizeof(ScanKeyData));
** strategy number }
** 2) the sk_func field will point to the Consistent function
*/ p = (GISTScanOpaque) s->opaque;
for (i = 0; i < s->numberOfKeys; i++) { if (p != (GISTScanOpaque) NULL)
/* s->keyData[i].sk_procedure {
= index_getprocid(s->relation, 1, GIST_CONSISTENT_PROC); */ gistfreestack(p->s_stack);
s->keyData[i].sk_procedure gistfreestack(p->s_markstk);
= RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno, p->s_stack = p->s_markstk = (GISTSTACK *) NULL;
s->keyData[i].sk_procedure); p->s_flags = 0x0;
s->keyData[i].sk_func = p->giststate->consistentFn; for (i = 0; i < s->numberOfKeys; i++)
} {
} s->keyData[i].sk_procedure
= RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno,
s->keyData[i].sk_procedure);
s->keyData[i].sk_func = p->giststate->consistentFn;
}
}
else
{
/* initialize opaque data */
p = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData));
p->s_stack = p->s_markstk = (GISTSTACK *) NULL;
p->s_flags = 0x0;
s->opaque = p;
p->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
initGISTstate(p->giststate, s->relation);
if (s->numberOfKeys > 0)
/*
* * Play games here with the scan key to use the Consistent *
* function for all comparisons: * 1) the sk_procedure field
* will now be used to hold the * strategy number * 2) the
* sk_func field will point to the Consistent function
*/
for (i = 0; i < s->numberOfKeys; i++)
{
/*
* s->keyData[i].sk_procedure =
* index_getprocid(s->relation, 1, GIST_CONSISTENT_PROC);
*/
s->keyData[i].sk_procedure
= RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno,
s->keyData[i].sk_procedure);
s->keyData[i].sk_func = p->giststate->consistentFn;
}
}
} }
void void
gistmarkpos(IndexScanDesc s) gistmarkpos(IndexScanDesc s)
{ {
GISTScanOpaque p; GISTScanOpaque p;
GISTSTACK *o, *n, *tmp; GISTSTACK *o,
*n,
*tmp;
s->currentMarkData = s->currentItemData; s->currentMarkData = s->currentItemData;
p = (GISTScanOpaque) s->opaque; p = (GISTScanOpaque) s->opaque;
if (p->s_flags & GS_CURBEFORE) if (p->s_flags & GS_CURBEFORE)
p->s_flags |= GS_MRKBEFORE; p->s_flags |= GS_MRKBEFORE;
else else
p->s_flags &= ~GS_MRKBEFORE; p->s_flags &= ~GS_MRKBEFORE;
o = (GISTSTACK *) NULL; o = (GISTSTACK *) NULL;
n = p->s_stack; n = p->s_stack;
/* copy the parent stack from the current item data */ /* copy the parent stack from the current item data */
while (n != (GISTSTACK *) NULL) { while (n != (GISTSTACK *) NULL)
tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK)); {
tmp->gs_child = n->gs_child; tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
tmp->gs_blk = n->gs_blk; tmp->gs_child = n->gs_child;
tmp->gs_parent = o; tmp->gs_blk = n->gs_blk;
o = tmp; tmp->gs_parent = o;
n = n->gs_parent; o = tmp;
} n = n->gs_parent;
}
gistfreestack(p->s_markstk); gistfreestack(p->s_markstk);
p->s_markstk = o; p->s_markstk = o;
} }
void void
gistrestrpos(IndexScanDesc s) gistrestrpos(IndexScanDesc s)
{ {
GISTScanOpaque p; GISTScanOpaque p;
GISTSTACK *o, *n, *tmp; GISTSTACK *o,
*n,
*tmp;
s->currentItemData = s->currentMarkData; s->currentItemData = s->currentMarkData;
p = (GISTScanOpaque) s->opaque; p = (GISTScanOpaque) s->opaque;
if (p->s_flags & GS_MRKBEFORE) if (p->s_flags & GS_MRKBEFORE)
p->s_flags |= GS_CURBEFORE; p->s_flags |= GS_CURBEFORE;
else else
p->s_flags &= ~GS_CURBEFORE; p->s_flags &= ~GS_CURBEFORE;
o = (GISTSTACK *) NULL; o = (GISTSTACK *) NULL;
n = p->s_markstk; n = p->s_markstk;
/* copy the parent stack from the current item data */ /* copy the parent stack from the current item data */
while (n != (GISTSTACK *) NULL) { while (n != (GISTSTACK *) NULL)
tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK)); {
tmp->gs_child = n->gs_child; tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
tmp->gs_blk = n->gs_blk; tmp->gs_child = n->gs_child;
tmp->gs_parent = o; tmp->gs_blk = n->gs_blk;
o = tmp; tmp->gs_parent = o;
n = n->gs_parent; o = tmp;
} n = n->gs_parent;
}
gistfreestack(p->s_stack); gistfreestack(p->s_stack);
p->s_stack = o; p->s_stack = o;
} }
void void
gistendscan(IndexScanDesc s) gistendscan(IndexScanDesc s)
{ {
GISTScanOpaque p; GISTScanOpaque p;
p = (GISTScanOpaque) s->opaque; p = (GISTScanOpaque) s->opaque;
if (p != (GISTScanOpaque) NULL) { if (p != (GISTScanOpaque) NULL)
gistfreestack(p->s_stack); {
gistfreestack(p->s_markstk); gistfreestack(p->s_stack);
pfree (s->opaque); gistfreestack(p->s_markstk);
} pfree(s->opaque);
}
gistdropscan(s); gistdropscan(s);
/* XXX don't unset read lock -- two-phase locking */ /* XXX don't unset read lock -- two-phase locking */
} }
static void static void
gistregscan(IndexScanDesc s) gistregscan(IndexScanDesc s)
{ {
GISTScanList l; GISTScanList l;
l = (GISTScanList) palloc(sizeof(GISTScanListData)); l = (GISTScanList) palloc(sizeof(GISTScanListData));
l->gsl_scan = s; l->gsl_scan = s;
l->gsl_next = GISTScans; l->gsl_next = GISTScans;
GISTScans = l; GISTScans = l;
} }
static void static void
gistdropscan(IndexScanDesc s) gistdropscan(IndexScanDesc s)
{ {
GISTScanList l; GISTScanList l;
GISTScanList prev; GISTScanList prev;
prev = (GISTScanList) NULL; prev = (GISTScanList) NULL;
for (l = GISTScans; for (l = GISTScans;
l != (GISTScanList) NULL && l->gsl_scan != s; l != (GISTScanList) NULL && l->gsl_scan != s;
l = l->gsl_next) { l = l->gsl_next)
prev = l; {
} prev = l;
}
if (l == (GISTScanList) NULL) if (l == (GISTScanList) NULL)
elog(WARN, "GiST scan list corrupted -- cannot find 0x%lx", s); elog(WARN, "GiST scan list corrupted -- cannot find 0x%lx", s);
if (prev == (GISTScanList) NULL) if (prev == (GISTScanList) NULL)
GISTScans = l->gsl_next; GISTScans = l->gsl_next;
else else
prev->gsl_next = l->gsl_next; prev->gsl_next = l->gsl_next;
pfree(l); pfree(l);
} }
void void
gistadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum) gistadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum)
{ {
GISTScanList l; GISTScanList l;
Oid relid; Oid relid;
relid = r->rd_id; relid = r->rd_id;
for (l = GISTScans; l != (GISTScanList) NULL; l = l->gsl_next) { for (l = GISTScans; l != (GISTScanList) NULL; l = l->gsl_next)
if (l->gsl_scan->relation->rd_id == relid) {
gistadjone(l->gsl_scan, op, blkno, offnum); if (l->gsl_scan->relation->rd_id == relid)
} gistadjone(l->gsl_scan, op, blkno, offnum);
}
} }
/* /*
* gistadjone() -- adjust one scan for update. * gistadjone() -- adjust one scan for update.
* *
* By here, the scan passed in is on a modified relation. Op tells * By here, the scan passed in is on a modified relation. Op tells
* us what the modification is, and blkno and offind tell us what * us what the modification is, and blkno and offind tell us what
* block and offset index were affected. This routine checks the * block and offset index were affected. This routine checks the
* current and marked positions, and the current and marked stacks, * current and marked positions, and the current and marked stacks,
* to see if any stored location needs to be changed because of the * to see if any stored location needs to be changed because of the
* update. If so, we make the change here. * update. If so, we make the change here.
*/ */
static void static void
gistadjone(IndexScanDesc s, gistadjone(IndexScanDesc s,
int op, int op,
BlockNumber blkno, BlockNumber blkno,
OffsetNumber offnum) OffsetNumber offnum)
{ {
GISTScanOpaque so; GISTScanOpaque so;
adjustiptr(s, &(s->currentItemData), op, blkno, offnum); adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum); adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
so = (GISTScanOpaque) s->opaque; so = (GISTScanOpaque) s->opaque;
if (op == GISTOP_SPLIT) { if (op == GISTOP_SPLIT)
adjuststack(so->s_stack, blkno, offnum); {
adjuststack(so->s_markstk, blkno, offnum); adjuststack(so->s_stack, blkno, offnum);
} adjuststack(so->s_markstk, blkno, offnum);
}
} }
/* /*
* adjustiptr() -- adjust current and marked item pointers in the scan * adjustiptr() -- adjust current and marked item pointers in the scan
* *
* Depending on the type of update and the place it happened, we * Depending on the type of update and the place it happened, we
* need to do nothing, to back up one record, or to start over on * need to do nothing, to back up one record, or to start over on
* the same page. * the same page.
*/ */
static void static void
adjustiptr(IndexScanDesc s, adjustiptr(IndexScanDesc s,
ItemPointer iptr, ItemPointer iptr,
int op, int op,
BlockNumber blkno, BlockNumber blkno,
OffsetNumber offnum) OffsetNumber offnum)
{ {
OffsetNumber curoff; OffsetNumber curoff;
GISTScanOpaque so; GISTScanOpaque so;
if (ItemPointerIsValid(iptr)) { if (ItemPointerIsValid(iptr))
if (ItemPointerGetBlockNumber(iptr) == blkno) { {
curoff = ItemPointerGetOffsetNumber(iptr); if (ItemPointerGetBlockNumber(iptr) == blkno)
so = (GISTScanOpaque) s->opaque; {
curoff = ItemPointerGetOffsetNumber(iptr);
so = (GISTScanOpaque) s->opaque;
switch (op) { switch (op)
case GISTOP_DEL: {
/* back up one if we need to */ case GISTOP_DEL:
if (curoff >= offnum) { /* back up one if we need to */
if (curoff >= offnum)
{
if (curoff > FirstOffsetNumber) { if (curoff > FirstOffsetNumber)
/* just adjust the item pointer */ {
ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff)); /* just adjust the item pointer */
} else { ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff));
/* remember that we're before the current tuple */ }
ItemPointerSet(iptr, blkno, FirstOffsetNumber); else
if (iptr == &(s->currentItemData)) {
so->s_flags |= GS_CURBEFORE; /* remember that we're before the current tuple */
else ItemPointerSet(iptr, blkno, FirstOffsetNumber);
so->s_flags |= GS_MRKBEFORE; if (iptr == &(s->currentItemData))
} so->s_flags |= GS_CURBEFORE;
else
so->s_flags |= GS_MRKBEFORE;
}
}
break;
case GISTOP_SPLIT:
/* back to start of page on split */
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
if (iptr == &(s->currentItemData))
so->s_flags &= ~GS_CURBEFORE;
else
so->s_flags &= ~GS_MRKBEFORE;
break;
default:
elog(WARN, "Bad operation in GiST scan adjust: %d", op);
}
} }
break;
case GISTOP_SPLIT:
/* back to start of page on split */
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
if (iptr == &(s->currentItemData))
so->s_flags &= ~GS_CURBEFORE;
else
so->s_flags &= ~GS_MRKBEFORE;
break;
default:
elog(WARN, "Bad operation in GiST scan adjust: %d", op);
}
} }
}
} }
/* /*
* adjuststack() -- adjust the supplied stack for a split on a page in * adjuststack() -- adjust the supplied stack for a split on a page in
* the index we're scanning. * the index we're scanning.
* *
* If a page on our parent stack has split, we need to back up to the * If a page on our parent stack has split, we need to back up to the
* beginning of the page and rescan it. The reason for this is that * beginning of the page and rescan it. The reason for this is that
* the split algorithm for GiSTs doesn't order tuples in any useful * the split algorithm for GiSTs doesn't order tuples in any useful
* way on a single page. This means on that a split, we may wind up * way on a single page. This means on that a split, we may wind up
* looking at some heap tuples more than once. This is handled in the * looking at some heap tuples more than once. This is handled in the
* access method update code for heaps; if we've modified the tuple we * access method update code for heaps; if we've modified the tuple we
* are looking at already in this transaction, we ignore the update * are looking at already in this transaction, we ignore the update
* request. * request.
*/ */
/*ARGSUSED*/ /*ARGSUSED*/
static void static void
adjuststack(GISTSTACK *stk, adjuststack(GISTSTACK * stk,
BlockNumber blkno, BlockNumber blkno,
OffsetNumber offnum) OffsetNumber offnum)
{ {
while (stk != (GISTSTACK *) NULL) { while (stk != (GISTSTACK *) NULL)
if (stk->gs_blk == blkno) {
stk->gs_child = FirstOffsetNumber; if (stk->gs_blk == blkno)
stk->gs_child = FirstOffsetNumber;
stk = stk->gs_parent; stk = stk->gs_parent;
} }
} }

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* giststrat.c-- * giststrat.c--
* strategy map data for GiSTs. * strategy map data for GiSTs.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* /usr/local/devel/pglite/cvs/src/backend/access/gist/giststrat.c,v 1.4 1995/06/14 00:10:05 jolly Exp * /usr/local/devel/pglite/cvs/src/backend/access/gist/giststrat.c,v 1.4 1995/06/14 00:10:05 jolly Exp
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -18,51 +18,51 @@
#include <access/istrat.h> #include <access/istrat.h>
/* /*
* Note: negate, commute, and negatecommute all assume that operators are * Note: negate, commute, and negatecommute all assume that operators are
* ordered as follows in the strategy map: * ordered as follows in the strategy map:
* *
* contains, contained-by * contains, contained-by
* *
* The negate, commute, and negatecommute arrays are used by the planner * The negate, commute, and negatecommute arrays are used by the planner
* to plan indexed scans over data that appears in the qualificiation in * to plan indexed scans over data that appears in the qualificiation in
* a boolean negation, or whose operands appear in the wrong order. For * a boolean negation, or whose operands appear in the wrong order. For
* example, if the operator "<%" means "contains", and the user says * example, if the operator "<%" means "contains", and the user says
* *
* where not rel.box <% "(10,10,20,20)"::box * where not rel.box <% "(10,10,20,20)"::box
* *
* the planner can plan an index scan by noting that GiST indices have * the planner can plan an index scan by noting that GiST indices have
* an operator in their operator class for negating <%. * an operator in their operator class for negating <%.
* *
* Similarly, if the user says something like * Similarly, if the user says something like
* *
* where "(10,10,20,20)"::box <% rel.box * where "(10,10,20,20)"::box <% rel.box
* *
* the planner can see that the GiST index on rel.box has an operator in * the planner can see that the GiST index on rel.box has an operator in
* its opclass for commuting <%, and plan the scan using that operator. * its opclass for commuting <%, and plan the scan using that operator.
* This added complexity in the access methods makes the planner a lot easier * This added complexity in the access methods makes the planner a lot easier
* to write. * to write.
*/ */
/* if a op b, what operator tells us if (not a op b)? */ /* if a op b, what operator tells us if (not a op b)? */
static StrategyNumber GISTNegate[GISTNStrategies] = { static StrategyNumber GISTNegate[GISTNStrategies] = {
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy InvalidStrategy
}; };
/* if a op_1 b, what is the operator op_2 such that b op_2 a? */ /* if a op_1 b, what is the operator op_2 such that b op_2 a? */
static StrategyNumber GISTCommute[GISTNStrategies] = { static StrategyNumber GISTCommute[GISTNStrategies] = {
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy InvalidStrategy
}; };
/* if a op_1 b, what is the operator op_2 such that (b !op_2 a)? */ /* if a op_1 b, what is the operator op_2 such that (b !op_2 a)? */
static StrategyNumber GISTNegateCommute[GISTNStrategies] = { static StrategyNumber GISTNegateCommute[GISTNStrategies] = {
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy InvalidStrategy
}; };
/* /*
* GiSTs do not currently support TermData (see rtree/rtstrat.c for * GiSTs do not currently support TermData (see rtree/rtstrat.c for
@ -71,46 +71,47 @@ static StrategyNumber GISTNegateCommute[GISTNStrategies] = {
*/ */
/* /*
* If you were sufficiently attentive to detail, you would go through * If you were sufficiently attentive to detail, you would go through
* the ExpressionData pain above for every one of the strategies * the ExpressionData pain above for every one of the strategies
* we defined. I am not. Now we declare the StrategyEvaluationData * we defined. I am not. Now we declare the StrategyEvaluationData
* structure that gets shipped around to help the planner and the access * structure that gets shipped around to help the planner and the access
* method decide what sort of scan it should do, based on (a) what the * method decide what sort of scan it should do, based on (a) what the
* user asked for, (b) what operators are defined for a particular opclass, * user asked for, (b) what operators are defined for a particular opclass,
* and (c) the reams of information we supplied above. * and (c) the reams of information we supplied above.
* *
* The idea of all of this initialized data is to make life easier on the * The idea of all of this initialized data is to make life easier on the
* user when he defines a new operator class to use this access method. * user when he defines a new operator class to use this access method.
* By filling in all the data, we let him get away with leaving holes in his * By filling in all the data, we let him get away with leaving holes in his
* operator class, and still let him use the index. The added complexity * operator class, and still let him use the index. The added complexity
* in the access methods just isn't worth the trouble, though. * in the access methods just isn't worth the trouble, though.
*/ */
static StrategyEvaluationData GISTEvaluationData = { static StrategyEvaluationData GISTEvaluationData = {
GISTNStrategies, /* # of strategies */ GISTNStrategies, /* # of strategies */
(StrategyTransformMap) GISTNegate, /* how to do (not qual) */ (StrategyTransformMap) GISTNegate, /* how to do (not qual) */
(StrategyTransformMap) GISTCommute, /* how to swap operands */ (StrategyTransformMap) GISTCommute, /* how to swap operands */
(StrategyTransformMap) GISTNegateCommute, /* how to do both */ (StrategyTransformMap) GISTNegateCommute, /* how to do both */
{ NULL } {NULL}
}; };
StrategyNumber StrategyNumber
RelationGetGISTStrategy(Relation r, RelationGetGISTStrategy(Relation r,
AttrNumber attnum, AttrNumber attnum,
RegProcedure proc) RegProcedure proc)
{ {
return (RelationGetStrategy(r, attnum, &GISTEvaluationData, proc)); return (RelationGetStrategy(r, attnum, &GISTEvaluationData, proc));
} }
#ifdef NOT_USED #ifdef NOT_USED
bool bool
RelationInvokeGISTStrategy(Relation r, RelationInvokeGISTStrategy(Relation r,
AttrNumber attnum, AttrNumber attnum,
StrategyNumber s, StrategyNumber s,
Datum left, Datum left,
Datum right) Datum right)
{ {
return (RelationInvokeStrategy(r, &GISTEvaluationData, attnum, s, return (RelationInvokeStrategy(r, &GISTEvaluationData, attnum, s,
left, right)); left, right));
} }
#endif #endif

View File

@ -1,16 +1,16 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* hash.c-- * hash.c--
* Implementation of Margo Seltzer's Hashing package for postgres. * Implementation of Margo Seltzer's Hashing package for postgres.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.12 1997/01/10 09:46:13 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.13 1997/09/07 04:37:49 momjian Exp $
* *
* NOTES * NOTES
* This file contains only the public interface routines. * This file contains only the public interface routines.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -26,452 +26,483 @@
#include <miscadmin.h> #include <miscadmin.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
bool BuildingHash = false; bool BuildingHash = false;
/* /*
* hashbuild() -- build a new hash index. * hashbuild() -- build a new hash index.
* *
* We use a global variable to record the fact that we're creating * We use a global variable to record the fact that we're creating
* a new index. This is used to avoid high-concurrency locking, * a new index. This is used to avoid high-concurrency locking,
* since the index won't be visible until this transaction commits * since the index won't be visible until this transaction commits
* and since building is guaranteed to be single-threaded. * and since building is guaranteed to be single-threaded.
*/ */
void void
hashbuild(Relation heap, hashbuild(Relation heap,
Relation index, Relation index,
int natts, int natts,
AttrNumber *attnum, AttrNumber * attnum,
IndexStrategy istrat, IndexStrategy istrat,
uint16 pcount, uint16 pcount,
Datum *params, Datum * params,
FuncIndexInfo *finfo, FuncIndexInfo * finfo,
PredInfo *predInfo) PredInfo * predInfo)
{ {
HeapScanDesc hscan; HeapScanDesc hscan;
Buffer buffer; Buffer buffer;
HeapTuple htup; HeapTuple htup;
IndexTuple itup; IndexTuple itup;
TupleDesc htupdesc, itupdesc; TupleDesc htupdesc,
Datum *attdata; itupdesc;
bool *nulls; Datum *attdata;
InsertIndexResult res; bool *nulls;
int nhtups, nitups; InsertIndexResult res;
int i; int nhtups,
HashItem hitem; nitups;
int i;
HashItem hitem;
#ifndef OMIT_PARTIAL_INDEX #ifndef OMIT_PARTIAL_INDEX
ExprContext *econtext; ExprContext *econtext;
TupleTable tupleTable; TupleTable tupleTable;
TupleTableSlot *slot; TupleTableSlot *slot;
#endif #endif
Oid hrelid, irelid; Oid hrelid,
Node *pred, *oldPred; irelid;
Node *pred,
*oldPred;
/* note that this is a new btree */ /* note that this is a new btree */
BuildingHash = true; BuildingHash = true;
pred = predInfo->pred; pred = predInfo->pred;
oldPred = predInfo->oldPred; oldPred = predInfo->oldPred;
/* initialize the hash index metadata page (if this is a new index) */ /* initialize the hash index metadata page (if this is a new index) */
if (oldPred == NULL) if (oldPred == NULL)
_hash_metapinit(index); _hash_metapinit(index);
/* get tuple descriptors for heap and index relations */ /* get tuple descriptors for heap and index relations */
htupdesc = RelationGetTupleDescriptor(heap); htupdesc = RelationGetTupleDescriptor(heap);
itupdesc = RelationGetTupleDescriptor(index); itupdesc = RelationGetTupleDescriptor(index);
/* get space for data items that'll appear in the index tuple */ /* get space for data items that'll appear in the index tuple */
attdata = (Datum *) palloc(natts * sizeof(Datum)); attdata = (Datum *) palloc(natts * sizeof(Datum));
nulls = (bool *) palloc(natts * sizeof(bool)); nulls = (bool *) palloc(natts * sizeof(bool));
/* /*
* If this is a predicate (partial) index, we will need to evaluate the * If this is a predicate (partial) index, we will need to evaluate
* predicate using ExecQual, which requires the current tuple to be in a * the predicate using ExecQual, which requires the current tuple to
* slot of a TupleTable. In addition, ExecQual must have an ExprContext * be in a slot of a TupleTable. In addition, ExecQual must have an
* referring to that slot. Here, we initialize dummy TupleTable and * ExprContext referring to that slot. Here, we initialize dummy
* ExprContext objects for this purpose. --Nels, Feb '92 * TupleTable and ExprContext objects for this purpose. --Nels, Feb
*/ * '92
*/
#ifndef OMIT_PARTIAL_INDEX #ifndef OMIT_PARTIAL_INDEX
if (pred != NULL || oldPred != NULL) { if (pred != NULL || oldPred != NULL)
tupleTable = ExecCreateTupleTable(1); {
slot = ExecAllocTableSlot(tupleTable); tupleTable = ExecCreateTupleTable(1);
econtext = makeNode(ExprContext); slot = ExecAllocTableSlot(tupleTable);
FillDummyExprContext(econtext, slot, htupdesc, buffer); econtext = makeNode(ExprContext);
} FillDummyExprContext(econtext, slot, htupdesc, buffer);
else /* quiet the compiler */ }
else
/* quiet the compiler */
{ {
econtext = NULL; econtext = NULL;
tupleTable = 0; tupleTable = 0;
slot = 0; slot = 0;
} }
#endif /* OMIT_PARTIAL_INDEX */ #endif /* OMIT_PARTIAL_INDEX */
/* start a heap scan */ /* start a heap scan */
hscan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL); hscan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL);
htup = heap_getnext(hscan, 0, &buffer); htup = heap_getnext(hscan, 0, &buffer);
/* build the index */ /* build the index */
nhtups = nitups = 0; nhtups = nitups = 0;
for (; HeapTupleIsValid(htup); htup = heap_getnext(hscan, 0, &buffer)) { for (; HeapTupleIsValid(htup); htup = heap_getnext(hscan, 0, &buffer))
nhtups++;
/*
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
* this tuple if it was already in the existing partial index
*/
if (oldPred != NULL) {
/*SetSlotContents(slot, htup); */
#ifndef OMIT_PARTIAL_INDEX
slot->val = htup;
if (ExecQual((List*)oldPred, econtext) == true) {
nitups++;
continue;
}
#endif /* OMIT_PARTIAL_INDEX */
}
/* Skip this tuple if it doesn't satisfy the partial-index predicate */
if (pred != NULL) {
#ifndef OMIT_PARTIAL_INDEX
/*SetSlotContents(slot, htup); */
slot->val = htup;
if (ExecQual((List*)pred, econtext) == false)
continue;
#endif /* OMIT_PARTIAL_INDEX */
}
nitups++;
/*
* For the current heap tuple, extract all the attributes
* we use in this index, and note which are null.
*/
for (i = 1; i <= natts; i++) {
int attoff;
bool attnull;
/*
* Offsets are from the start of the tuple, and are
* zero-based; indices are one-based. The next call
* returns i - 1. That's data hiding for you.
*/
/* attoff = i - 1 */
attoff = AttrNumberGetAttrOffset(i);
/* below, attdata[attoff] set to equal some datum &
* attnull is changed to indicate whether or not the attribute
* is null for this tuple
*/
attdata[attoff] = GetIndexValue(htup,
htupdesc,
attoff,
attnum,
finfo,
&attnull,
buffer);
nulls[attoff] = (attnull ? 'n' : ' ');
}
/* form an index tuple and point it at the heap tuple */
itup = index_formtuple(itupdesc, attdata, nulls);
/*
* If the single index key is null, we don't insert it into
* the index. Hash tables support scans on '='.
* Relational algebra says that A = B
* returns null if either A or B is null. This
* means that no qualification used in an index scan could ever
* return true on a null attribute. It also means that indices
* can't be used by ISNULL or NOTNULL scans, but that's an
* artifact of the strategy map architecture chosen in 1986, not
* of the way nulls are handled here.
*/
if (itup->t_info & INDEX_NULL_MASK) {
pfree(itup);
continue;
}
itup->t_tid = htup->t_ctid;
hitem = _hash_formitem(itup);
res = _hash_doinsert(index, hitem);
pfree(hitem);
pfree(itup);
pfree(res);
}
/* okay, all heap tuples are indexed */
heap_endscan(hscan);
if (pred != NULL || oldPred != NULL) {
#ifndef OMIT_PARTIAL_INDEX
ExecDestroyTupleTable(tupleTable, true);
pfree(econtext);
#endif /* OMIT_PARTIAL_INDEX */
}
/*
* Since we just counted the tuples in the heap, we update its
* stats in pg_class to guarantee that the planner takes advantage
* of the index we just created. Finally, only update statistics
* during normal index definitions, not for indices on system catalogs
* created during bootstrap processing. We must close the relations
* before updatings statistics to guarantee that the relcache entries
* are flushed when we increment the command counter in UpdateStats().
*/
if (IsNormalProcessingMode())
{ {
hrelid = heap->rd_id;
irelid = index->rd_id; nhtups++;
heap_close(heap);
index_close(index); /*
UpdateStats(hrelid, nhtups, true); * If oldPred != NULL, this is an EXTEND INDEX command, so skip
UpdateStats(irelid, nitups, false); * this tuple if it was already in the existing partial index
if (oldPred != NULL) { */
if (nitups == nhtups) pred = NULL; if (oldPred != NULL)
UpdateIndexPredicate(irelid, oldPred, pred); {
} /* SetSlotContents(slot, htup); */
#ifndef OMIT_PARTIAL_INDEX
slot->val = htup;
if (ExecQual((List *) oldPred, econtext) == true)
{
nitups++;
continue;
}
#endif /* OMIT_PARTIAL_INDEX */
}
/*
* Skip this tuple if it doesn't satisfy the partial-index
* predicate
*/
if (pred != NULL)
{
#ifndef OMIT_PARTIAL_INDEX
/* SetSlotContents(slot, htup); */
slot->val = htup;
if (ExecQual((List *) pred, econtext) == false)
continue;
#endif /* OMIT_PARTIAL_INDEX */
}
nitups++;
/*
* For the current heap tuple, extract all the attributes we use
* in this index, and note which are null.
*/
for (i = 1; i <= natts; i++)
{
int attoff;
bool attnull;
/*
* Offsets are from the start of the tuple, and are
* zero-based; indices are one-based. The next call returns i
* - 1. That's data hiding for you.
*/
/* attoff = i - 1 */
attoff = AttrNumberGetAttrOffset(i);
/*
* below, attdata[attoff] set to equal some datum & attnull is
* changed to indicate whether or not the attribute is null
* for this tuple
*/
attdata[attoff] = GetIndexValue(htup,
htupdesc,
attoff,
attnum,
finfo,
&attnull,
buffer);
nulls[attoff] = (attnull ? 'n' : ' ');
}
/* form an index tuple and point it at the heap tuple */
itup = index_formtuple(itupdesc, attdata, nulls);
/*
* If the single index key is null, we don't insert it into the
* index. Hash tables support scans on '='. Relational algebra
* says that A = B returns null if either A or B is null. This
* means that no qualification used in an index scan could ever
* return true on a null attribute. It also means that indices
* can't be used by ISNULL or NOTNULL scans, but that's an
* artifact of the strategy map architecture chosen in 1986, not
* of the way nulls are handled here.
*/
if (itup->t_info & INDEX_NULL_MASK)
{
pfree(itup);
continue;
}
itup->t_tid = htup->t_ctid;
hitem = _hash_formitem(itup);
res = _hash_doinsert(index, hitem);
pfree(hitem);
pfree(itup);
pfree(res);
} }
/* be tidy */ /* okay, all heap tuples are indexed */
pfree(nulls); heap_endscan(hscan);
pfree(attdata);
/* all done */ if (pred != NULL || oldPred != NULL)
BuildingHash = false; {
#ifndef OMIT_PARTIAL_INDEX
ExecDestroyTupleTable(tupleTable, true);
pfree(econtext);
#endif /* OMIT_PARTIAL_INDEX */
}
/*
* Since we just counted the tuples in the heap, we update its stats
* in pg_class to guarantee that the planner takes advantage of the
* index we just created. Finally, only update statistics during
* normal index definitions, not for indices on system catalogs
* created during bootstrap processing. We must close the relations
* before updatings statistics to guarantee that the relcache entries
* are flushed when we increment the command counter in UpdateStats().
*/
if (IsNormalProcessingMode())
{
hrelid = heap->rd_id;
irelid = index->rd_id;
heap_close(heap);
index_close(index);
UpdateStats(hrelid, nhtups, true);
UpdateStats(irelid, nitups, false);
if (oldPred != NULL)
{
if (nitups == nhtups)
pred = NULL;
UpdateIndexPredicate(irelid, oldPred, pred);
}
}
/* be tidy */
pfree(nulls);
pfree(attdata);
/* all done */
BuildingHash = false;
} }
/* /*
* hashinsert() -- insert an index tuple into a hash table. * hashinsert() -- insert an index tuple into a hash table.
* *
* Hash on the index tuple's key, find the appropriate location * Hash on the index tuple's key, find the appropriate location
* for the new tuple, put it there, and return an InsertIndexResult * for the new tuple, put it there, and return an InsertIndexResult
* to the caller. * to the caller.
*/ */
InsertIndexResult InsertIndexResult
hashinsert(Relation rel, Datum *datum, char *nulls, ItemPointer ht_ctid, Relation heapRel) hashinsert(Relation rel, Datum * datum, char *nulls, ItemPointer ht_ctid, Relation heapRel)
{ {
HashItem hitem; HashItem hitem;
IndexTuple itup; IndexTuple itup;
InsertIndexResult res; InsertIndexResult res;
/* generate an index tuple */ /* generate an index tuple */
itup = index_formtuple(RelationGetTupleDescriptor(rel), datum, nulls); itup = index_formtuple(RelationGetTupleDescriptor(rel), datum, nulls);
itup->t_tid = *ht_ctid; itup->t_tid = *ht_ctid;
if (itup->t_info & INDEX_NULL_MASK) if (itup->t_info & INDEX_NULL_MASK)
return ((InsertIndexResult) NULL); return ((InsertIndexResult) NULL);
hitem = _hash_formitem(itup); hitem = _hash_formitem(itup);
res = _hash_doinsert(rel, hitem); res = _hash_doinsert(rel, hitem);
pfree(hitem); pfree(hitem);
pfree(itup); pfree(itup);
return (res); return (res);
} }
/* /*
* hashgettuple() -- Get the next tuple in the scan. * hashgettuple() -- Get the next tuple in the scan.
*/ */
char * char *
hashgettuple(IndexScanDesc scan, ScanDirection dir) hashgettuple(IndexScanDesc scan, ScanDirection dir)
{ {
RetrieveIndexResult res; RetrieveIndexResult res;
/* /*
* If we've already initialized this scan, we can just advance it * If we've already initialized this scan, we can just advance it in
* in the appropriate direction. If we haven't done so yet, we * the appropriate direction. If we haven't done so yet, we call a
* call a routine to get the first item in the scan. * routine to get the first item in the scan.
*/ */
if (ItemPointerIsValid(&(scan->currentItemData))) if (ItemPointerIsValid(&(scan->currentItemData)))
res = _hash_next(scan, dir); res = _hash_next(scan, dir);
else else
res = _hash_first(scan, dir); res = _hash_first(scan, dir);
return ((char *) res); return ((char *) res);
} }
/* /*
* hashbeginscan() -- start a scan on a hash index * hashbeginscan() -- start a scan on a hash index
*/ */
char * char *
hashbeginscan(Relation rel, hashbeginscan(Relation rel,
bool fromEnd, bool fromEnd,
uint16 keysz, uint16 keysz,
ScanKey scankey) ScanKey scankey)
{ {
IndexScanDesc scan; IndexScanDesc scan;
HashScanOpaque so; HashScanOpaque so;
scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey); scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey);
so = (HashScanOpaque) palloc(sizeof(HashScanOpaqueData)); so = (HashScanOpaque) palloc(sizeof(HashScanOpaqueData));
so->hashso_curbuf = so->hashso_mrkbuf = InvalidBuffer; so->hashso_curbuf = so->hashso_mrkbuf = InvalidBuffer;
scan->opaque = so; scan->opaque = so;
scan->flags = 0x0; scan->flags = 0x0;
/* register scan in case we change pages it's using */ /* register scan in case we change pages it's using */
_hash_regscan(scan); _hash_regscan(scan);
return ((char *) scan); return ((char *) scan);
} }
/* /*
* hashrescan() -- rescan an index relation * hashrescan() -- rescan an index relation
*/ */
void void
hashrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey) hashrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey)
{ {
ItemPointer iptr; ItemPointer iptr;
HashScanOpaque so; HashScanOpaque so;
so = (HashScanOpaque) scan->opaque; so = (HashScanOpaque) scan->opaque;
/* we hold a read lock on the current page in the scan */ /* we hold a read lock on the current page in the scan */
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) { if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ); {
so->hashso_curbuf = InvalidBuffer; _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
ItemPointerSetInvalid(iptr); so->hashso_curbuf = InvalidBuffer;
} ItemPointerSetInvalid(iptr);
if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) { }
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ); if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
so->hashso_mrkbuf = InvalidBuffer; {
ItemPointerSetInvalid(iptr); _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
} so->hashso_mrkbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
/* reset the scan key */ /* reset the scan key */
if (scan->numberOfKeys > 0) { if (scan->numberOfKeys > 0)
memmove(scan->keyData, {
scankey, memmove(scan->keyData,
scan->numberOfKeys * sizeof(ScanKeyData)); scankey,
} scan->numberOfKeys * sizeof(ScanKeyData));
}
} }
/* /*
* hashendscan() -- close down a scan * hashendscan() -- close down a scan
*/ */
void void
hashendscan(IndexScanDesc scan) hashendscan(IndexScanDesc scan)
{ {
ItemPointer iptr; ItemPointer iptr;
HashScanOpaque so; HashScanOpaque so;
so = (HashScanOpaque) scan->opaque; so = (HashScanOpaque) scan->opaque;
/* release any locks we still hold */ /* release any locks we still hold */
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) { if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ); {
so->hashso_curbuf = InvalidBuffer; _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
ItemPointerSetInvalid(iptr); so->hashso_curbuf = InvalidBuffer;
} ItemPointerSetInvalid(iptr);
}
if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) { if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
if (BufferIsValid(so->hashso_mrkbuf)) {
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ); if (BufferIsValid(so->hashso_mrkbuf))
so->hashso_mrkbuf = InvalidBuffer; _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
ItemPointerSetInvalid(iptr); so->hashso_mrkbuf = InvalidBuffer;
} ItemPointerSetInvalid(iptr);
}
/* don't need scan registered anymore */ /* don't need scan registered anymore */
_hash_dropscan(scan); _hash_dropscan(scan);
/* be tidy */ /* be tidy */
pfree (scan->opaque); pfree(scan->opaque);
} }
/* /*
* hashmarkpos() -- save current scan position * hashmarkpos() -- save current scan position
* *
*/ */
void void
hashmarkpos(IndexScanDesc scan) hashmarkpos(IndexScanDesc scan)
{ {
ItemPointer iptr; ItemPointer iptr;
HashScanOpaque so; HashScanOpaque so;
/* see if we ever call this code. if we do, then so_mrkbuf a /*
* useful element in the scan->opaque structure. if this procedure * see if we ever call this code. if we do, then so_mrkbuf a useful
* is never called, so_mrkbuf should be removed from the scan->opaque * element in the scan->opaque structure. if this procedure is never
* structure. * called, so_mrkbuf should be removed from the scan->opaque
*/ * structure.
elog(NOTICE, "Hashmarkpos() called."); */
elog(NOTICE, "Hashmarkpos() called.");
so = (HashScanOpaque) scan->opaque; so = (HashScanOpaque) scan->opaque;
/* release lock on old marked data, if any */ /* release lock on old marked data, if any */
if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) { if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ); {
so->hashso_mrkbuf = InvalidBuffer; _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
ItemPointerSetInvalid(iptr); so->hashso_mrkbuf = InvalidBuffer;
} ItemPointerSetInvalid(iptr);
}
/* bump lock on currentItemData and copy to currentMarkData */ /* bump lock on currentItemData and copy to currentMarkData */
if (ItemPointerIsValid(&(scan->currentItemData))) { if (ItemPointerIsValid(&(scan->currentItemData)))
so->hashso_mrkbuf = _hash_getbuf(scan->relation, {
BufferGetBlockNumber(so->hashso_curbuf), so->hashso_mrkbuf = _hash_getbuf(scan->relation,
HASH_READ); BufferGetBlockNumber(so->hashso_curbuf),
scan->currentMarkData = scan->currentItemData; HASH_READ);
} scan->currentMarkData = scan->currentItemData;
}
} }
/* /*
* hashrestrpos() -- restore scan to last saved position * hashrestrpos() -- restore scan to last saved position
*/ */
void void
hashrestrpos(IndexScanDesc scan) hashrestrpos(IndexScanDesc scan)
{ {
ItemPointer iptr; ItemPointer iptr;
HashScanOpaque so; HashScanOpaque so;
/* see if we ever call this code. if we do, then so_mrkbuf a /*
* useful element in the scan->opaque structure. if this procedure * see if we ever call this code. if we do, then so_mrkbuf a useful
* is never called, so_mrkbuf should be removed from the scan->opaque * element in the scan->opaque structure. if this procedure is never
* structure. * called, so_mrkbuf should be removed from the scan->opaque
*/ * structure.
elog(NOTICE, "Hashrestrpos() called."); */
elog(NOTICE, "Hashrestrpos() called.");
so = (HashScanOpaque) scan->opaque; so = (HashScanOpaque) scan->opaque;
/* release lock on current data, if any */ /* release lock on current data, if any */
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) { if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ); {
so->hashso_curbuf = InvalidBuffer; _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
ItemPointerSetInvalid(iptr); so->hashso_curbuf = InvalidBuffer;
} ItemPointerSetInvalid(iptr);
}
/* bump lock on currentMarkData and copy to currentItemData */ /* bump lock on currentMarkData and copy to currentItemData */
if (ItemPointerIsValid(&(scan->currentMarkData))) { if (ItemPointerIsValid(&(scan->currentMarkData)))
so->hashso_curbuf = {
_hash_getbuf(scan->relation, so->hashso_curbuf =
BufferGetBlockNumber(so->hashso_mrkbuf), _hash_getbuf(scan->relation,
HASH_READ); BufferGetBlockNumber(so->hashso_mrkbuf),
HASH_READ);
scan->currentItemData = scan->currentMarkData; scan->currentItemData = scan->currentMarkData;
} }
} }
/* stubs */ /* stubs */
void void
hashdelete(Relation rel, ItemPointer tid) hashdelete(Relation rel, ItemPointer tid)
{ {
/* adjust any active scans that will be affected by this deletion */ /* adjust any active scans that will be affected by this deletion */
_hash_adjscans(rel, tid); _hash_adjscans(rel, tid);
/* delete the data from the page */ /* delete the data from the page */
_hash_pagedel(rel, tid); _hash_pagedel(rel, tid);
} }

View File

@ -1,17 +1,17 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* hashfunc.c-- * hashfunc.c--
* Comparison functions for hash access method. * Comparison functions for hash access method.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.3 1996/11/10 02:57:40 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.4 1997/09/07 04:37:53 momjian Exp $
* *
* NOTES * NOTES
* These functions are stored in pg_amproc. For each operator class * These functions are stored in pg_amproc. For each operator class
* defined on hash tables, they compute the hash value of the argument. * defined on hash tables, they compute the hash value of the argument.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -20,206 +20,223 @@
#include "access/hash.h" #include "access/hash.h"
uint32 hashint2(int16 key) uint32
hashint2(int16 key)
{ {
return ((uint32) ~key); return ((uint32) ~ key);
} }
uint32 hashint4(uint32 key) uint32
hashint4(uint32 key)
{ {
return (~key); return (~key);
} }
/* Hash function from Chris Torek. */ /* Hash function from Chris Torek. */
uint32 hashfloat4(float32 keyp) uint32
hashfloat4(float32 keyp)
{ {
int len; int len;
int loop; int loop;
uint32 h; uint32 h;
char *kp = (char *) keyp; char *kp = (char *) keyp;
len = sizeof(float32data); len = sizeof(float32data);
#define HASH4a h = (h << 5) - h + *kp++; #define HASH4a h = (h << 5) - h + *kp++;
#define HASH4b h = (h << 5) + h + *kp++; #define HASH4b h = (h << 5) + h + *kp++;
#define HASH4 HASH4b #define HASH4 HASH4b
h = 0; h = 0;
if (len > 0) { if (len > 0)
loop = (len + 8 - 1) >> 3; {
loop = (len + 8 - 1) >> 3;
switch (len & (8 - 1)) { switch (len & (8 - 1))
case 0: {
do { /* All fall throughs */ case 0:
HASH4; do
case 7: { /* All fall throughs */
HASH4; HASH4;
case 6: case 7:
HASH4; HASH4;
case 5: case 6:
HASH4; HASH4;
case 4: case 5:
HASH4; HASH4;
case 3: case 4:
HASH4; HASH4;
case 2: case 3:
HASH4; HASH4;
case 1: case 2:
HASH4; HASH4;
} while (--loop); case 1:
HASH4;
} while (--loop);
}
} }
} return (h);
return (h);
} }
uint32 hashfloat8(float64 keyp) uint32
hashfloat8(float64 keyp)
{ {
int len; int len;
int loop; int loop;
uint32 h; uint32 h;
char *kp = (char *) keyp; char *kp = (char *) keyp;
len = sizeof(float64data); len = sizeof(float64data);
#define HASH4a h = (h << 5) - h + *kp++; #define HASH4a h = (h << 5) - h + *kp++;
#define HASH4b h = (h << 5) + h + *kp++; #define HASH4b h = (h << 5) + h + *kp++;
#define HASH4 HASH4b #define HASH4 HASH4b
h = 0; h = 0;
if (len > 0) { if (len > 0)
loop = (len + 8 - 1) >> 3; {
loop = (len + 8 - 1) >> 3;
switch (len & (8 - 1)) { switch (len & (8 - 1))
case 0: {
do { /* All fall throughs */ case 0:
HASH4; do
case 7: { /* All fall throughs */
HASH4; HASH4;
case 6: case 7:
HASH4; HASH4;
case 5: case 6:
HASH4; HASH4;
case 4: case 5:
HASH4; HASH4;
case 3: case 4:
HASH4; HASH4;
case 2: case 3:
HASH4; HASH4;
case 1: case 2:
HASH4; HASH4;
} while (--loop); case 1:
HASH4;
} while (--loop);
}
} }
} return (h);
return (h);
} }
uint32 hashoid(Oid key) uint32
hashoid(Oid key)
{ {
return ((uint32) ~key); return ((uint32) ~ key);
} }
uint32 hashchar(char key) uint32
hashchar(char key)
{ {
int len; int len;
uint32 h; uint32 h;
len = sizeof(char); len = sizeof(char);
#define PRIME1 37 #define PRIME1 37
#define PRIME2 1048583 #define PRIME2 1048583
h = 0; h = 0;
/* Convert char to integer */ /* Convert char to integer */
h = h * PRIME1 ^ (key - ' '); h = h * PRIME1 ^ (key - ' ');
h %= PRIME2; h %= PRIME2;
return (h); return (h);
} }
uint32 hashchar2(uint16 intkey) uint32
hashchar2(uint16 intkey)
{ {
uint32 h; uint32 h;
int len; int len;
char *key = (char *) &intkey; char *key = (char *) &intkey;
h = 0; h = 0;
len = sizeof(uint16); len = sizeof(uint16);
/* Convert string to integer */ /* Convert string to integer */
while (len--) while (len--)
h = h * PRIME1 ^ (*key++ - ' '); h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2; h %= PRIME2;
return (h); return (h);
} }
uint32 hashchar4(uint32 intkey) uint32
hashchar4(uint32 intkey)
{ {
uint32 h; uint32 h;
int len; int len;
char *key = (char *) &intkey; char *key = (char *) &intkey;
h = 0; h = 0;
len = sizeof(uint32); len = sizeof(uint32);
/* Convert string to integer */ /* Convert string to integer */
while (len--) while (len--)
h = h * PRIME1 ^ (*key++ - ' '); h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2; h %= PRIME2;
return (h); return (h);
} }
uint32 hashchar8(char *key) uint32
hashchar8(char *key)
{ {
uint32 h; uint32 h;
int len; int len;
h = 0; h = 0;
len = sizeof(char8); len = sizeof(char8);
/* Convert string to integer */ /* Convert string to integer */
while (len--) while (len--)
h = h * PRIME1 ^ (*key++ - ' '); h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2; h %= PRIME2;
return (h); return (h);
} }
uint32 hashname(NameData *n) uint32
hashname(NameData * n)
{ {
uint32 h; uint32 h;
int len; int len;
char *key; char *key;
key = n->data; key = n->data;
h = 0; h = 0;
len = NAMEDATALEN; len = NAMEDATALEN;
/* Convert string to integer */ /* Convert string to integer */
while (len--) while (len--)
h = h * PRIME1 ^ (*key++ - ' '); h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2; h %= PRIME2;
return (h); return (h);
} }
uint32 hashchar16(char *key) uint32
hashchar16(char *key)
{ {
uint32 h; uint32 h;
int len; int len;
h = 0; h = 0;
len = sizeof(char16); len = sizeof(char16);
/* Convert string to integer */ /* Convert string to integer */
while (len--) while (len--)
h = h * PRIME1 ^ (*key++ - ' '); h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2; h %= PRIME2;
return (h); return (h);
} }
@ -234,45 +251,49 @@ uint32 hashchar16(char *key)
* *
* "OZ's original sdbm hash" * "OZ's original sdbm hash"
*/ */
uint32 hashtext(struct varlena *key) uint32
hashtext(struct varlena * key)
{ {
int keylen; int keylen;
char *keydata; char *keydata;
uint32 n; uint32 n;
int loop; int loop;
keydata = VARDATA(key); keydata = VARDATA(key);
keylen = VARSIZE(key); keylen = VARSIZE(key);
/* keylen includes the four bytes in which string keylength is stored */ /* keylen includes the four bytes in which string keylength is stored */
keylen -= sizeof(VARSIZE(key)); keylen -= sizeof(VARSIZE(key));
#define HASHC n = *keydata++ + 65599 * n #define HASHC n = *keydata++ + 65599 * n
n = 0; n = 0;
if (keylen > 0) { if (keylen > 0)
loop = (keylen + 8 - 1) >> 3; {
loop = (keylen + 8 - 1) >> 3;
switch (keylen & (8 - 1)) { switch (keylen & (8 - 1))
case 0: {
do { /* All fall throughs */ case 0:
HASHC; do
case 7: { /* All fall throughs */
HASHC; HASHC;
case 6: case 7:
HASHC; HASHC;
case 5: case 6:
HASHC; HASHC;
case 4: case 5:
HASHC; HASHC;
case 3: case 4:
HASHC; HASHC;
case 2: case 3:
HASHC; HASHC;
case 1: case 2:
HASHC; HASHC;
} while (--loop); case 1:
HASHC;
} while (--loop);
}
} }
} return (n);
return (n);
} }

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* hashinsert.c-- * hashinsert.c--
* Item insertion in hash tables for Postgres. * Item insertion in hash tables for Postgres.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashinsert.c,v 1.8 1997/08/12 22:51:30 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/hash/hashinsert.c,v 1.9 1997/09/07 04:37:56 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -22,211 +22,221 @@ static InsertIndexResult _hash_insertonpg(Relation rel, Buffer buf, int keysz, S
static OffsetNumber _hash_pgaddtup(Relation rel, Buffer buf, int keysz, ScanKey itup_scankey, Size itemsize, HashItem hitem); static OffsetNumber _hash_pgaddtup(Relation rel, Buffer buf, int keysz, ScanKey itup_scankey, Size itemsize, HashItem hitem);
/* /*
* _hash_doinsert() -- Handle insertion of a single HashItem in the table. * _hash_doinsert() -- Handle insertion of a single HashItem in the table.
* *
* This routine is called by the public interface routines, hashbuild * This routine is called by the public interface routines, hashbuild
* and hashinsert. By here, hashitem is filled in, and has a unique * and hashinsert. By here, hashitem is filled in, and has a unique
* (xid, seqno) pair. The datum to be used as a "key" is in the * (xid, seqno) pair. The datum to be used as a "key" is in the
* hashitem. * hashitem.
*/ */
InsertIndexResult InsertIndexResult
_hash_doinsert(Relation rel, HashItem hitem) _hash_doinsert(Relation rel, HashItem hitem)
{ {
Buffer buf; Buffer buf;
Buffer metabuf; Buffer metabuf;
BlockNumber blkno; BlockNumber blkno;
HashMetaPage metap; HashMetaPage metap;
IndexTuple itup; IndexTuple itup;
InsertIndexResult res; InsertIndexResult res;
ScanKey itup_scankey; ScanKey itup_scankey;
int natts; int natts;
Page page; Page page;
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ); metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
metap = (HashMetaPage) BufferGetPage(metabuf); metap = (HashMetaPage) BufferGetPage(metabuf);
_hash_checkpage((Page) metap, LH_META_PAGE); _hash_checkpage((Page) metap, LH_META_PAGE);
/* we need a scan key to do our search, so build one */ /* we need a scan key to do our search, so build one */
itup = &(hitem->hash_itup); itup = &(hitem->hash_itup);
if ((natts = rel->rd_rel->relnatts) != 1) if ((natts = rel->rd_rel->relnatts) != 1)
elog(WARN, "Hash indices valid for only one index key."); elog(WARN, "Hash indices valid for only one index key.");
itup_scankey = _hash_mkscankey(rel, itup, metap); itup_scankey = _hash_mkscankey(rel, itup, metap);
/* /*
* find the first page in the bucket chain containing this key and * find the first page in the bucket chain containing this key and
* place it in buf. _hash_search obtains a read lock for us. * place it in buf. _hash_search obtains a read lock for us.
*/ */
_hash_search(rel, natts, itup_scankey, &buf, metap); _hash_search(rel, natts, itup_scankey, &buf, metap);
page = BufferGetPage(buf); page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE); _hash_checkpage(page, LH_BUCKET_PAGE);
/* /*
* trade in our read lock for a write lock so that we can do the * trade in our read lock for a write lock so that we can do the
* insertion. * insertion.
*/ */
blkno = BufferGetBlockNumber(buf); blkno = BufferGetBlockNumber(buf);
_hash_relbuf(rel, buf, HASH_READ); _hash_relbuf(rel, buf, HASH_READ);
buf = _hash_getbuf(rel, blkno, HASH_WRITE); buf = _hash_getbuf(rel, blkno, HASH_WRITE);
/* /*
* XXX btree comment (haven't decided what to do in hash): don't * XXX btree comment (haven't decided what to do in hash): don't think
* think the bucket can be split while we're reading the metapage. * the bucket can be split while we're reading the metapage.
* *
* If the page was split between the time that we surrendered our * If the page was split between the time that we surrendered our read
* read lock and acquired our write lock, then this page may no * lock and acquired our write lock, then this page may no longer be
* longer be the right place for the key we want to insert. * the right place for the key we want to insert.
*/ */
/* do the insertion */ /* do the insertion */
res = _hash_insertonpg(rel, buf, natts, itup_scankey, res = _hash_insertonpg(rel, buf, natts, itup_scankey,
hitem, metabuf); hitem, metabuf);
/* be tidy */ /* be tidy */
_hash_freeskey(itup_scankey); _hash_freeskey(itup_scankey);
return (res); return (res);
} }
/* /*
* _hash_insertonpg() -- Insert a tuple on a particular page in the table. * _hash_insertonpg() -- Insert a tuple on a particular page in the table.
* *
* This recursive procedure does the following things: * This recursive procedure does the following things:
* *
* + if necessary, splits the target page. * + if necessary, splits the target page.
* + inserts the tuple. * + inserts the tuple.
* *
* On entry, we must have the right buffer on which to do the * On entry, we must have the right buffer on which to do the
* insertion, and the buffer must be pinned and locked. On return, * insertion, and the buffer must be pinned and locked. On return,
* we will have dropped both the pin and the write lock on the buffer. * we will have dropped both the pin and the write lock on the buffer.
* *
*/ */
static InsertIndexResult static InsertIndexResult
_hash_insertonpg(Relation rel, _hash_insertonpg(Relation rel,
Buffer buf, Buffer buf,
int keysz, int keysz,
ScanKey scankey, ScanKey scankey,
HashItem hitem, HashItem hitem,
Buffer metabuf) Buffer metabuf)
{ {
InsertIndexResult res; InsertIndexResult res;
Page page; Page page;
BlockNumber itup_blkno; BlockNumber itup_blkno;
OffsetNumber itup_off; OffsetNumber itup_off;
int itemsz; int itemsz;
HashPageOpaque pageopaque; HashPageOpaque pageopaque;
bool do_expand = false; bool do_expand = false;
Buffer ovflbuf; Buffer ovflbuf;
HashMetaPage metap; HashMetaPage metap;
Bucket bucket; Bucket bucket;
metap = (HashMetaPage) BufferGetPage(metabuf); metap = (HashMetaPage) BufferGetPage(metabuf);
_hash_checkpage((Page) metap, LH_META_PAGE); _hash_checkpage((Page) metap, LH_META_PAGE);
page = BufferGetPage(buf); page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
bucket = pageopaque->hasho_bucket;
itemsz = IndexTupleDSize(hitem->hash_itup)
+ (sizeof(HashItemData) - sizeof(IndexTupleData));
itemsz = DOUBLEALIGN(itemsz);
while (PageGetFreeSpace(page) < itemsz) {
/*
* no space on this page; check for an overflow page
*/
if (BlockNumberIsValid(pageopaque->hasho_nextblkno)) {
/*
* ovfl page exists; go get it. if it doesn't have room,
* we'll find out next pass through the loop test above.
*/
ovflbuf = _hash_getbuf(rel, pageopaque->hasho_nextblkno,
HASH_WRITE);
_hash_relbuf(rel, buf, HASH_WRITE);
buf = ovflbuf;
page = BufferGetPage(buf);
} else {
/*
* we're at the end of the bucket chain and we haven't
* found a page with enough room. allocate a new overflow
* page.
*/
do_expand = true;
ovflbuf = _hash_addovflpage(rel, &metabuf, buf);
_hash_relbuf(rel, buf, HASH_WRITE);
buf = ovflbuf;
page = BufferGetPage(buf);
if (PageGetFreeSpace(page) < itemsz) {
/* it doesn't fit on an empty page -- give up */
elog(WARN, "hash item too large");
}
}
_hash_checkpage(page, LH_OVERFLOW_PAGE);
pageopaque = (HashPageOpaque) PageGetSpecialPointer(page); pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
Assert(pageopaque->hasho_bucket == bucket); bucket = pageopaque->hasho_bucket;
}
itup_off = _hash_pgaddtup(rel, buf, keysz, scankey, itemsz, hitem); itemsz = IndexTupleDSize(hitem->hash_itup)
itup_blkno = BufferGetBlockNumber(buf); + (sizeof(HashItemData) - sizeof(IndexTupleData));
itemsz = DOUBLEALIGN(itemsz);
/* by here, the new tuple is inserted */ while (PageGetFreeSpace(page) < itemsz)
res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); {
ItemPointerSet(&(res->pointerData), itup_blkno, itup_off); /*
* no space on this page; check for an overflow page
*/
if (BlockNumberIsValid(pageopaque->hasho_nextblkno))
{
if (res != NULL) { /*
/* * ovfl page exists; go get it. if it doesn't have room,
* Increment the number of keys in the table. * we'll find out next pass through the loop test above.
* We switch lock access type just for a moment */
* to allow greater accessibility to the metapage. ovflbuf = _hash_getbuf(rel, pageopaque->hasho_nextblkno,
*/ HASH_WRITE);
metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, _hash_relbuf(rel, buf, HASH_WRITE);
HASH_READ, HASH_WRITE); buf = ovflbuf;
metap->hashm_nkeys += 1; page = BufferGetPage(buf);
metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, }
HASH_WRITE, HASH_READ); else
{
} /*
* we're at the end of the bucket chain and we haven't found a
* page with enough room. allocate a new overflow page.
*/
do_expand = true;
ovflbuf = _hash_addovflpage(rel, &metabuf, buf);
_hash_relbuf(rel, buf, HASH_WRITE);
buf = ovflbuf;
page = BufferGetPage(buf);
_hash_wrtbuf(rel, buf); if (PageGetFreeSpace(page) < itemsz)
{
/* it doesn't fit on an empty page -- give up */
elog(WARN, "hash item too large");
}
}
_hash_checkpage(page, LH_OVERFLOW_PAGE);
pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
Assert(pageopaque->hasho_bucket == bucket);
}
if (do_expand || itup_off = _hash_pgaddtup(rel, buf, keysz, scankey, itemsz, hitem);
(metap->hashm_nkeys / (metap->hashm_maxbucket + 1)) itup_blkno = BufferGetBlockNumber(buf);
> metap->hashm_ffactor) {
_hash_expandtable(rel, metabuf); /* by here, the new tuple is inserted */
} res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
_hash_relbuf(rel, metabuf, HASH_READ);
return (res); ItemPointerSet(&(res->pointerData), itup_blkno, itup_off);
if (res != NULL)
{
/*
* Increment the number of keys in the table. We switch lock
* access type just for a moment to allow greater accessibility to
* the metapage.
*/
metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf,
HASH_READ, HASH_WRITE);
metap->hashm_nkeys += 1;
metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf,
HASH_WRITE, HASH_READ);
}
_hash_wrtbuf(rel, buf);
if (do_expand ||
(metap->hashm_nkeys / (metap->hashm_maxbucket + 1))
> metap->hashm_ffactor)
{
_hash_expandtable(rel, metabuf);
}
_hash_relbuf(rel, metabuf, HASH_READ);
return (res);
} }
/* /*
* _hash_pgaddtup() -- add a tuple to a particular page in the index. * _hash_pgaddtup() -- add a tuple to a particular page in the index.
* *
* This routine adds the tuple to the page as requested, and keeps the * This routine adds the tuple to the page as requested, and keeps the
* write lock and reference associated with the page's buffer. It is * write lock and reference associated with the page's buffer. It is
* an error to call pgaddtup() without a write lock and reference. * an error to call pgaddtup() without a write lock and reference.
*/ */
static OffsetNumber static OffsetNumber
_hash_pgaddtup(Relation rel, _hash_pgaddtup(Relation rel,
Buffer buf, Buffer buf,
int keysz, int keysz,
ScanKey itup_scankey, ScanKey itup_scankey,
Size itemsize, Size itemsize,
HashItem hitem) HashItem hitem)
{ {
OffsetNumber itup_off; OffsetNumber itup_off;
Page page; Page page;
page = BufferGetPage(buf); page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page)); itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
PageAddItem(page, (Item) hitem, itemsize, itup_off, LP_USED); PageAddItem(page, (Item) hitem, itemsize, itup_off, LP_USED);
/* write the buffer, but hold our lock */ /* write the buffer, but hold our lock */
_hash_wrtnorelbuf(rel, buf); _hash_wrtnorelbuf(rel, buf);
return (itup_off); return (itup_off);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,28 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* hashscan.c-- * hashscan.c--
* manage scans on hash tables * manage scans on hash tables
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashscan.c,v 1.8 1996/11/15 18:36:31 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/hash/hashscan.c,v 1.9 1997/09/07 04:38:01 momjian Exp $
* *
* NOTES * NOTES
* Because we can be doing an index scan on a relation while we * Because we can be doing an index scan on a relation while we
* update it, we need to avoid missing data that moves around in * update it, we need to avoid missing data that moves around in
* the index. The routines and global variables in this file * the index. The routines and global variables in this file
* guarantee that all scans in the local address space stay * guarantee that all scans in the local address space stay
* correctly positioned. This is all we need to worry about, since * correctly positioned. This is all we need to worry about, since
* write locking guarantees that no one else will be on the same * write locking guarantees that no one else will be on the same
* page at the same time as we are. * page at the same time as we are.
* *
* The scheme is to manage a list of active scans in the current * The scheme is to manage a list of active scans in the current
* backend. Whenever we add or remove records from an index, we * backend. Whenever we add or remove records from an index, we
* check the list of active scans to see if any has been affected. * check the list of active scans to see if any has been affected.
* A scan is affected only if it is on the same relation, and the * A scan is affected only if it is on the same relation, and the
* same page, as the update. * same page, as the update.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -31,130 +31,137 @@
#include <access/hash.h> #include <access/hash.h>
static void _hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno); static void _hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
static bool _hash_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno); static bool _hash_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
typedef struct HashScanListData { typedef struct HashScanListData
IndexScanDesc hashsl_scan; {
struct HashScanListData *hashsl_next; IndexScanDesc hashsl_scan;
} HashScanListData; struct HashScanListData *hashsl_next;
} HashScanListData;
typedef HashScanListData *HashScanList; typedef HashScanListData *HashScanList;
static HashScanList HashScans = (HashScanList) NULL; static HashScanList HashScans = (HashScanList) NULL;
/* /*
* _Hash_regscan() -- register a new scan. * _Hash_regscan() -- register a new scan.
*/ */
void void
_hash_regscan(IndexScanDesc scan) _hash_regscan(IndexScanDesc scan)
{ {
HashScanList new_el; HashScanList new_el;
new_el = (HashScanList) palloc(sizeof(HashScanListData)); new_el = (HashScanList) palloc(sizeof(HashScanListData));
new_el->hashsl_scan = scan; new_el->hashsl_scan = scan;
new_el->hashsl_next = HashScans; new_el->hashsl_next = HashScans;
HashScans = new_el; HashScans = new_el;
} }
/* /*
* _hash_dropscan() -- drop a scan from the scan list * _hash_dropscan() -- drop a scan from the scan list
*/ */
void void
_hash_dropscan(IndexScanDesc scan) _hash_dropscan(IndexScanDesc scan)
{ {
HashScanList chk, last; HashScanList chk,
last;
last = (HashScanList) NULL; last = (HashScanList) NULL;
for (chk = HashScans; for (chk = HashScans;
chk != (HashScanList) NULL && chk->hashsl_scan != scan; chk != (HashScanList) NULL && chk->hashsl_scan != scan;
chk = chk->hashsl_next) { chk = chk->hashsl_next)
last = chk; {
} last = chk;
}
if (chk == (HashScanList) NULL) if (chk == (HashScanList) NULL)
elog(WARN, "hash scan list trashed; can't find 0x%lx", scan); elog(WARN, "hash scan list trashed; can't find 0x%lx", scan);
if (last == (HashScanList) NULL) if (last == (HashScanList) NULL)
HashScans = chk->hashsl_next; HashScans = chk->hashsl_next;
else else
last->hashsl_next = chk->hashsl_next; last->hashsl_next = chk->hashsl_next;
pfree (chk); pfree(chk);
} }
void void
_hash_adjscans(Relation rel, ItemPointer tid) _hash_adjscans(Relation rel, ItemPointer tid)
{ {
HashScanList l; HashScanList l;
Oid relid; Oid relid;
relid = rel->rd_id; relid = rel->rd_id;
for (l = HashScans; l != (HashScanList) NULL; l = l->hashsl_next) { for (l = HashScans; l != (HashScanList) NULL; l = l->hashsl_next)
if (relid == l->hashsl_scan->relation->rd_id) {
_hash_scandel(l->hashsl_scan, ItemPointerGetBlockNumber(tid), if (relid == l->hashsl_scan->relation->rd_id)
ItemPointerGetOffsetNumber(tid)); _hash_scandel(l->hashsl_scan, ItemPointerGetBlockNumber(tid),
} ItemPointerGetOffsetNumber(tid));
}
} }
static void static void
_hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno) _hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
{ {
ItemPointer current; ItemPointer current;
Buffer buf; Buffer buf;
Buffer metabuf; Buffer metabuf;
HashScanOpaque so; HashScanOpaque so;
if (!_hash_scantouched(scan, blkno, offno)) if (!_hash_scantouched(scan, blkno, offno))
return; return;
metabuf = _hash_getbuf(scan->relation, HASH_METAPAGE, HASH_READ); metabuf = _hash_getbuf(scan->relation, HASH_METAPAGE, HASH_READ);
so = (HashScanOpaque) scan->opaque; so = (HashScanOpaque) scan->opaque;
buf = so->hashso_curbuf; buf = so->hashso_curbuf;
current = &(scan->currentItemData); current = &(scan->currentItemData);
if (ItemPointerIsValid(current) if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) { && ItemPointerGetOffsetNumber(current) >= offno)
_hash_step(scan, &buf, BackwardScanDirection, metabuf); {
so->hashso_curbuf = buf; _hash_step(scan, &buf, BackwardScanDirection, metabuf);
} so->hashso_curbuf = buf;
}
current = &(scan->currentMarkData); current = &(scan->currentMarkData);
if (ItemPointerIsValid(current) if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) { && ItemPointerGetOffsetNumber(current) >= offno)
ItemPointerData tmp; {
tmp = *current; ItemPointerData tmp;
*current = scan->currentItemData;
scan->currentItemData = tmp; tmp = *current;
_hash_step(scan, &buf, BackwardScanDirection, metabuf); *current = scan->currentItemData;
so->hashso_mrkbuf = buf; scan->currentItemData = tmp;
tmp = *current; _hash_step(scan, &buf, BackwardScanDirection, metabuf);
*current = scan->currentItemData; so->hashso_mrkbuf = buf;
scan->currentItemData = tmp; tmp = *current;
} *current = scan->currentItemData;
scan->currentItemData = tmp;
}
} }
static bool static bool
_hash_scantouched(IndexScanDesc scan, _hash_scantouched(IndexScanDesc scan,
BlockNumber blkno, BlockNumber blkno,
OffsetNumber offno) OffsetNumber offno)
{ {
ItemPointer current; ItemPointer current;
current = &(scan->currentItemData); current = &(scan->currentItemData);
if (ItemPointerIsValid(current) if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) && ItemPointerGetOffsetNumber(current) >= offno)
return (true); return (true);
current = &(scan->currentMarkData); current = &(scan->currentMarkData);
if (ItemPointerIsValid(current) if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) && ItemPointerGetOffsetNumber(current) >= offno)
return (true); return (true);
return (false); return (false);
} }

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* hashsearch.c-- * hashsearch.c--
* search code for postgres hash tables * search code for postgres hash tables
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashsearch.c,v 1.10 1997/06/28 05:45:40 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/access/hash/hashsearch.c,v 1.11 1997/09/07 04:38:02 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -18,406 +18,450 @@
#include <storage/bufmgr.h> #include <storage/bufmgr.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include "regex/utils.h" #include "regex/utils.h"
#else #else
# include <string.h> #include <string.h>
#endif #endif
/* /*
* _hash_search() -- Finds the page/bucket that the contains the * _hash_search() -- Finds the page/bucket that the contains the
* scankey and loads it into *bufP. the buffer has a read lock. * scankey and loads it into *bufP. the buffer has a read lock.
*/ */
void void
_hash_search(Relation rel, _hash_search(Relation rel,
int keysz, int keysz,
ScanKey scankey, ScanKey scankey,
Buffer *bufP, Buffer * bufP,
HashMetaPage metap) HashMetaPage metap)
{ {
BlockNumber blkno; BlockNumber blkno;
Datum keyDatum; Datum keyDatum;
Bucket bucket; Bucket bucket;
if (scankey == (ScanKey) NULL || if (scankey == (ScanKey) NULL ||
(keyDatum = scankey[0].sk_argument) == (Datum) NULL) { (keyDatum = scankey[0].sk_argument) == (Datum) NULL)
/* {
* If the scankey argument is NULL, all tuples will satisfy
* the scan so we start the scan at the first bucket (bucket
* 0).
*/
bucket = 0;
} else {
bucket = _hash_call(rel, metap, keyDatum);
}
blkno = BUCKET_TO_BLKNO(bucket); /*
* If the scankey argument is NULL, all tuples will satisfy the
* scan so we start the scan at the first bucket (bucket 0).
*/
bucket = 0;
}
else
{
bucket = _hash_call(rel, metap, keyDatum);
}
*bufP = _hash_getbuf(rel, blkno, HASH_READ); blkno = BUCKET_TO_BLKNO(bucket);
*bufP = _hash_getbuf(rel, blkno, HASH_READ);
} }
/* /*
* _hash_next() -- Get the next item in a scan. * _hash_next() -- Get the next item in a scan.
* *
* On entry, we have a valid currentItemData in the scan, and a * On entry, we have a valid currentItemData in the scan, and a
* read lock on the page that contains that item. We do not have * read lock on the page that contains that item. We do not have
* the page pinned. We return the next item in the scan. On * the page pinned. We return the next item in the scan. On
* exit, we have the page containing the next item locked but not * exit, we have the page containing the next item locked but not
* pinned. * pinned.
*/ */
RetrieveIndexResult RetrieveIndexResult
_hash_next(IndexScanDesc scan, ScanDirection dir) _hash_next(IndexScanDesc scan, ScanDirection dir)
{ {
Relation rel; Relation rel;
Buffer buf; Buffer buf;
Buffer metabuf; Buffer metabuf;
Page page; Page page;
OffsetNumber offnum; OffsetNumber offnum;
RetrieveIndexResult res; RetrieveIndexResult res;
ItemPointer current; ItemPointer current;
HashItem hitem; HashItem hitem;
IndexTuple itup; IndexTuple itup;
HashScanOpaque so; HashScanOpaque so;
rel = scan->relation; rel = scan->relation;
so = (HashScanOpaque) scan->opaque; so = (HashScanOpaque) scan->opaque;
current = &(scan->currentItemData); current = &(scan->currentItemData);
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ); metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
/* /*
* XXX 10 may 91: somewhere there's a bug in our management of the * XXX 10 may 91: somewhere there's a bug in our management of the
* cached buffer for this scan. wei discovered it. the following * cached buffer for this scan. wei discovered it. the following is
* is a workaround so he can work until i figure out what's going on. * a workaround so he can work until i figure out what's going on.
*/ */
if (!BufferIsValid(so->hashso_curbuf)) { if (!BufferIsValid(so->hashso_curbuf))
so->hashso_curbuf = _hash_getbuf(rel, {
ItemPointerGetBlockNumber(current), so->hashso_curbuf = _hash_getbuf(rel,
HASH_READ); ItemPointerGetBlockNumber(current),
} HASH_READ);
}
/* we still have the buffer pinned and locked */ /* we still have the buffer pinned and locked */
buf = so->hashso_curbuf; buf = so->hashso_curbuf;
/* /*
* step to next valid tuple. note that _hash_step releases our * step to next valid tuple. note that _hash_step releases our lock
* lock on 'metabuf'; if we switch to a new 'buf' while looking * on 'metabuf'; if we switch to a new 'buf' while looking for the
* for the next tuple, we come back with a lock on that buffer. * next tuple, we come back with a lock on that buffer.
*/ */
if (!_hash_step(scan, &buf, dir, metabuf)) { if (!_hash_step(scan, &buf, dir, metabuf))
return ((RetrieveIndexResult) NULL); {
} return ((RetrieveIndexResult) NULL);
}
/* if we're here, _hash_step found a valid tuple */ /* if we're here, _hash_step found a valid tuple */
current = &(scan->currentItemData); current = &(scan->currentItemData);
offnum = ItemPointerGetOffsetNumber(current); offnum = ItemPointerGetOffsetNumber(current);
page = BufferGetPage(buf); page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum)); hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
itup = &hitem->hash_itup; itup = &hitem->hash_itup;
res = FormRetrieveIndexResult(current, &(itup->t_tid)); res = FormRetrieveIndexResult(current, &(itup->t_tid));
return (res); return (res);
} }
static void static void
_hash_readnext(Relation rel, _hash_readnext(Relation rel,
Buffer *bufp, Page *pagep, HashPageOpaque *opaquep) Buffer * bufp, Page * pagep, HashPageOpaque * opaquep)
{ {
BlockNumber blkno; BlockNumber blkno;
blkno = (*opaquep)->hasho_nextblkno; blkno = (*opaquep)->hasho_nextblkno;
_hash_relbuf(rel, *bufp, HASH_READ); _hash_relbuf(rel, *bufp, HASH_READ);
*bufp = InvalidBuffer; *bufp = InvalidBuffer;
if (BlockNumberIsValid(blkno)) { if (BlockNumberIsValid(blkno))
*bufp = _hash_getbuf(rel, blkno, HASH_READ); {
*pagep = BufferGetPage(*bufp); *bufp = _hash_getbuf(rel, blkno, HASH_READ);
_hash_checkpage(*pagep, LH_OVERFLOW_PAGE); *pagep = BufferGetPage(*bufp);
*opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep); _hash_checkpage(*pagep, LH_OVERFLOW_PAGE);
Assert(!PageIsEmpty(*pagep)); *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
} Assert(!PageIsEmpty(*pagep));
}
} }
static void static void
_hash_readprev(Relation rel, _hash_readprev(Relation rel,
Buffer *bufp, Page *pagep, HashPageOpaque *opaquep) Buffer * bufp, Page * pagep, HashPageOpaque * opaquep)
{ {
BlockNumber blkno; BlockNumber blkno;
blkno = (*opaquep)->hasho_prevblkno; blkno = (*opaquep)->hasho_prevblkno;
_hash_relbuf(rel, *bufp, HASH_READ); _hash_relbuf(rel, *bufp, HASH_READ);
*bufp = InvalidBuffer; *bufp = InvalidBuffer;
if (BlockNumberIsValid(blkno)) { if (BlockNumberIsValid(blkno))
*bufp = _hash_getbuf(rel, blkno, HASH_READ); {
*pagep = BufferGetPage(*bufp); *bufp = _hash_getbuf(rel, blkno, HASH_READ);
_hash_checkpage(*pagep, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); *pagep = BufferGetPage(*bufp);
*opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep); _hash_checkpage(*pagep, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
if (PageIsEmpty(*pagep)) { *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
Assert((*opaquep)->hasho_flag & LH_BUCKET_PAGE); if (PageIsEmpty(*pagep))
_hash_relbuf(rel, *bufp, HASH_READ); {
*bufp = InvalidBuffer; Assert((*opaquep)->hasho_flag & LH_BUCKET_PAGE);
_hash_relbuf(rel, *bufp, HASH_READ);
*bufp = InvalidBuffer;
}
} }
}
} }
/* /*
* _hash_first() -- Find the first item in a scan. * _hash_first() -- Find the first item in a scan.
* *
* Return the RetrieveIndexResult of the first item in the tree that * Return the RetrieveIndexResult of the first item in the tree that
* satisfies the qualificatin associated with the scan descriptor. On * satisfies the qualificatin associated with the scan descriptor. On
* exit, the page containing the current index tuple is read locked * exit, the page containing the current index tuple is read locked
* and pinned, and the scan's opaque data entry is updated to * and pinned, and the scan's opaque data entry is updated to
* include the buffer. * include the buffer.
*/ */
RetrieveIndexResult RetrieveIndexResult
_hash_first(IndexScanDesc scan, ScanDirection dir) _hash_first(IndexScanDesc scan, ScanDirection dir)
{ {
Relation rel; Relation rel;
Buffer buf; Buffer buf;
Buffer metabuf; Buffer metabuf;
Page page; Page page;
HashPageOpaque opaque; HashPageOpaque opaque;
HashMetaPage metap; HashMetaPage metap;
HashItem hitem; HashItem hitem;
IndexTuple itup; IndexTuple itup;
ItemPointer current; ItemPointer current;
OffsetNumber offnum; OffsetNumber offnum;
RetrieveIndexResult res; RetrieveIndexResult res;
HashScanOpaque so; HashScanOpaque so;
rel = scan->relation; rel = scan->relation;
so = (HashScanOpaque) scan->opaque; so = (HashScanOpaque) scan->opaque;
current = &(scan->currentItemData); current = &(scan->currentItemData);
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ); metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
metap = (HashMetaPage) BufferGetPage(metabuf); metap = (HashMetaPage) BufferGetPage(metabuf);
_hash_checkpage((Page) metap, LH_META_PAGE); _hash_checkpage((Page) metap, LH_META_PAGE);
/* /*
* XXX -- The attribute number stored in the scan key is the attno * XXX -- The attribute number stored in the scan key is the attno in
* in the heap relation. We need to transmogrify this into * the heap relation. We need to transmogrify this into the index
* the index relation attno here. For the moment, we have * relation attno here. For the moment, we have hardwired attno == 1.
* hardwired attno == 1. */
*/
/* find the correct bucket page and load it into buf */ /* find the correct bucket page and load it into buf */
_hash_search(rel, 1, scan->keyData, &buf, metap); _hash_search(rel, 1, scan->keyData, &buf, metap);
page = BufferGetPage(buf); page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE); _hash_checkpage(page, LH_BUCKET_PAGE);
opaque = (HashPageOpaque) PageGetSpecialPointer(page); opaque = (HashPageOpaque) PageGetSpecialPointer(page);
/* /*
* if we are scanning forward, we need to find the first non-empty * if we are scanning forward, we need to find the first non-empty
* page (if any) in the bucket chain. since overflow pages are * page (if any) in the bucket chain. since overflow pages are never
* never empty, this had better be either the bucket page or the * empty, this had better be either the bucket page or the first
* first overflow page. * overflow page.
* *
* if we are scanning backward, we always go all the way to the * if we are scanning backward, we always go all the way to the end of
* end of the bucket chain. * the bucket chain.
*/ */
if (PageIsEmpty(page)) { if (PageIsEmpty(page))
if (BlockNumberIsValid(opaque->hasho_nextblkno)) { {
_hash_readnext(rel, &buf, &page, &opaque); if (BlockNumberIsValid(opaque->hasho_nextblkno))
} else { {
ItemPointerSetInvalid(current); _hash_readnext(rel, &buf, &page, &opaque);
so->hashso_curbuf = InvalidBuffer; }
/* else
* If there is no scankeys, all tuples will satisfy {
* the scan - so we continue in _hash_step to get ItemPointerSetInvalid(current);
* tuples from all buckets. - vadim 04/29/97 so->hashso_curbuf = InvalidBuffer;
*/
if ( scan->numberOfKeys >= 1 ) /*
{ * If there is no scankeys, all tuples will satisfy the scan -
_hash_relbuf(rel, buf, HASH_READ); * so we continue in _hash_step to get tuples from all
_hash_relbuf(rel, metabuf, HASH_READ); * buckets. - vadim 04/29/97
return ((RetrieveIndexResult) NULL); */
} if (scan->numberOfKeys >= 1)
{
_hash_relbuf(rel, buf, HASH_READ);
_hash_relbuf(rel, metabuf, HASH_READ);
return ((RetrieveIndexResult) NULL);
}
}
} }
} if (ScanDirectionIsBackward(dir))
if (ScanDirectionIsBackward(dir)) { {
while (BlockNumberIsValid(opaque->hasho_nextblkno)) { while (BlockNumberIsValid(opaque->hasho_nextblkno))
_hash_readnext(rel, &buf, &page, &opaque); {
_hash_readnext(rel, &buf, &page, &opaque);
}
} }
}
if (!_hash_step(scan, &buf, dir, metabuf)) { if (!_hash_step(scan, &buf, dir, metabuf))
return ((RetrieveIndexResult) NULL); {
} return ((RetrieveIndexResult) NULL);
}
/* if we're here, _hash_step found a valid tuple */ /* if we're here, _hash_step found a valid tuple */
current = &(scan->currentItemData); current = &(scan->currentItemData);
offnum = ItemPointerGetOffsetNumber(current); offnum = ItemPointerGetOffsetNumber(current);
page = BufferGetPage(buf); page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum)); hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
itup = &hitem->hash_itup; itup = &hitem->hash_itup;
res = FormRetrieveIndexResult(current, &(itup->t_tid)); res = FormRetrieveIndexResult(current, &(itup->t_tid));
return (res); return (res);
} }
/* /*
* _hash_step() -- step to the next valid item in a scan in the bucket. * _hash_step() -- step to the next valid item in a scan in the bucket.
* *
* If no valid record exists in the requested direction, return * If no valid record exists in the requested direction, return
* false. Else, return true and set the CurrentItemData for the * false. Else, return true and set the CurrentItemData for the
* scan to the right thing. * scan to the right thing.
* *
* 'bufP' points to the buffer which contains the current page * 'bufP' points to the buffer which contains the current page
* that we'll step through. * that we'll step through.
* *
* 'metabuf' is released when this returns. * 'metabuf' is released when this returns.
*/ */
bool bool
_hash_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir, Buffer metabuf) _hash_step(IndexScanDesc scan, Buffer * bufP, ScanDirection dir, Buffer metabuf)
{ {
Relation rel; Relation rel;
ItemPointer current; ItemPointer current;
HashScanOpaque so; HashScanOpaque so;
int allbuckets; int allbuckets;
HashMetaPage metap; HashMetaPage metap;
Buffer buf; Buffer buf;
Page page; Page page;
HashPageOpaque opaque; HashPageOpaque opaque;
OffsetNumber maxoff; OffsetNumber maxoff;
OffsetNumber offnum; OffsetNumber offnum;
Bucket bucket; Bucket bucket;
BlockNumber blkno; BlockNumber blkno;
HashItem hitem; HashItem hitem;
IndexTuple itup; IndexTuple itup;
rel = scan->relation; rel = scan->relation;
current = &(scan->currentItemData); current = &(scan->currentItemData);
so = (HashScanOpaque) scan->opaque; so = (HashScanOpaque) scan->opaque;
allbuckets = (scan->numberOfKeys < 1); allbuckets = (scan->numberOfKeys < 1);
metap = (HashMetaPage) BufferGetPage(metabuf); metap = (HashMetaPage) BufferGetPage(metabuf);
_hash_checkpage((Page) metap, LH_META_PAGE); _hash_checkpage((Page) metap, LH_META_PAGE);
buf = *bufP; buf = *bufP;
page = BufferGetPage(buf); page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
opaque = (HashPageOpaque) PageGetSpecialPointer(page); opaque = (HashPageOpaque) PageGetSpecialPointer(page);
/* /*
* If _hash_step is called from _hash_first, current will not be * If _hash_step is called from _hash_first, current will not be
* valid, so we can't dereference it. However, in that case, we * valid, so we can't dereference it. However, in that case, we
* presumably want to start at the beginning/end of the page... * presumably want to start at the beginning/end of the page...
*/ */
maxoff = PageGetMaxOffsetNumber(page); maxoff = PageGetMaxOffsetNumber(page);
if (ItemPointerIsValid(current)) { if (ItemPointerIsValid(current))
offnum = ItemPointerGetOffsetNumber(current); {
} else { offnum = ItemPointerGetOffsetNumber(current);
offnum = InvalidOffsetNumber; }
} else
{
/* offnum = InvalidOffsetNumber;
* 'offnum' now points to the last tuple we have seen (if any).
*
* continue to step through tuples until:
* 1) we get to the end of the bucket chain or
* 2) we find a valid tuple.
*/
do {
bucket = opaque->hasho_bucket;
switch (dir) {
case ForwardScanDirection:
if (offnum != InvalidOffsetNumber) {
offnum = OffsetNumberNext(offnum); /* move forward */
} else {
offnum = FirstOffsetNumber; /* new page */
}
while (offnum > maxoff) {
/*
* either this page is empty (maxoff ==
* InvalidOffsetNumber) or we ran off the end.
*/
_hash_readnext(rel, &buf, &page, &opaque);
if (BufferIsInvalid(buf)) { /* end of chain */
if (allbuckets && bucket < metap->hashm_maxbucket) {
++bucket;
blkno = BUCKET_TO_BLKNO(bucket);
buf = _hash_getbuf(rel, blkno, HASH_READ);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE);
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
Assert(opaque->hasho_bucket == bucket);
while (PageIsEmpty(page) &&
BlockNumberIsValid(opaque->hasho_nextblkno)) {
_hash_readnext(rel, &buf, &page, &opaque);
}
maxoff = PageGetMaxOffsetNumber(page);
offnum = FirstOffsetNumber;
} else {
maxoff = offnum = InvalidOffsetNumber;
break; /* while */
}
} else {
/* _hash_readnext never returns an empty page */
maxoff = PageGetMaxOffsetNumber(page);
offnum = FirstOffsetNumber;
}
}
break;
case BackwardScanDirection:
if (offnum != InvalidOffsetNumber) {
offnum = OffsetNumberPrev(offnum); /* move back */
} else {
offnum = maxoff; /* new page */
}
while (offnum < FirstOffsetNumber) {
/*
* either this page is empty (offnum ==
* InvalidOffsetNumber) or we ran off the end.
*/
_hash_readprev(rel, &buf, &page, &opaque);
if (BufferIsInvalid(buf)) { /* end of chain */
if (allbuckets && bucket > 0) {
--bucket;
blkno = BUCKET_TO_BLKNO(bucket);
buf = _hash_getbuf(rel, blkno, HASH_READ);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE);
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
Assert(opaque->hasho_bucket == bucket);
while (BlockNumberIsValid(opaque->hasho_nextblkno)) {
_hash_readnext(rel, &buf, &page, &opaque);
}
maxoff = offnum = PageGetMaxOffsetNumber(page);
} else {
maxoff = offnum = InvalidOffsetNumber;
break; /* while */
}
} else {
/* _hash_readprev never returns an empty page */
maxoff = offnum = PageGetMaxOffsetNumber(page);
}
}
break;
default:
/* NoMovementScanDirection */
/* this should not be reached */
break;
} }
/* we ran off the end of the world without finding a match */ /*
if (offnum == InvalidOffsetNumber) { * 'offnum' now points to the last tuple we have seen (if any).
_hash_relbuf(rel, metabuf, HASH_READ); *
*bufP = so->hashso_curbuf = InvalidBuffer; * continue to step through tuples until: 1) we get to the end of the
ItemPointerSetInvalid(current); * bucket chain or 2) we find a valid tuple.
return(false); */
} do
{
bucket = opaque->hasho_bucket;
/* get ready to check this tuple */ switch (dir)
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum)); {
itup = &hitem->hash_itup; case ForwardScanDirection:
} while (!_hash_checkqual(scan, itup)); if (offnum != InvalidOffsetNumber)
{
offnum = OffsetNumberNext(offnum); /* move forward */
}
else
{
offnum = FirstOffsetNumber; /* new page */
}
while (offnum > maxoff)
{
/* if we made it to here, we've found a valid tuple */ /*
_hash_relbuf(rel, metabuf, HASH_READ); * either this page is empty (maxoff ==
blkno = BufferGetBlockNumber(buf); * InvalidOffsetNumber) or we ran off the end.
*bufP = so->hashso_curbuf = buf; */
ItemPointerSet(current, blkno, offnum); _hash_readnext(rel, &buf, &page, &opaque);
return(true); if (BufferIsInvalid(buf))
{ /* end of chain */
if (allbuckets && bucket < metap->hashm_maxbucket)
{
++bucket;
blkno = BUCKET_TO_BLKNO(bucket);
buf = _hash_getbuf(rel, blkno, HASH_READ);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE);
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
Assert(opaque->hasho_bucket == bucket);
while (PageIsEmpty(page) &&
BlockNumberIsValid(opaque->hasho_nextblkno))
{
_hash_readnext(rel, &buf, &page, &opaque);
}
maxoff = PageGetMaxOffsetNumber(page);
offnum = FirstOffsetNumber;
}
else
{
maxoff = offnum = InvalidOffsetNumber;
break; /* while */
}
}
else
{
/* _hash_readnext never returns an empty page */
maxoff = PageGetMaxOffsetNumber(page);
offnum = FirstOffsetNumber;
}
}
break;
case BackwardScanDirection:
if (offnum != InvalidOffsetNumber)
{
offnum = OffsetNumberPrev(offnum); /* move back */
}
else
{
offnum = maxoff;/* new page */
}
while (offnum < FirstOffsetNumber)
{
/*
* either this page is empty (offnum ==
* InvalidOffsetNumber) or we ran off the end.
*/
_hash_readprev(rel, &buf, &page, &opaque);
if (BufferIsInvalid(buf))
{ /* end of chain */
if (allbuckets && bucket > 0)
{
--bucket;
blkno = BUCKET_TO_BLKNO(bucket);
buf = _hash_getbuf(rel, blkno, HASH_READ);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE);
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
Assert(opaque->hasho_bucket == bucket);
while (BlockNumberIsValid(opaque->hasho_nextblkno))
{
_hash_readnext(rel, &buf, &page, &opaque);
}
maxoff = offnum = PageGetMaxOffsetNumber(page);
}
else
{
maxoff = offnum = InvalidOffsetNumber;
break; /* while */
}
}
else
{
/* _hash_readprev never returns an empty page */
maxoff = offnum = PageGetMaxOffsetNumber(page);
}
}
break;
default:
/* NoMovementScanDirection */
/* this should not be reached */
break;
}
/* we ran off the end of the world without finding a match */
if (offnum == InvalidOffsetNumber)
{
_hash_relbuf(rel, metabuf, HASH_READ);
*bufP = so->hashso_curbuf = InvalidBuffer;
ItemPointerSetInvalid(current);
return (false);
}
/* get ready to check this tuple */
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
itup = &hitem->hash_itup;
} while (!_hash_checkqual(scan, itup));
/* if we made it to here, we've found a valid tuple */
_hash_relbuf(rel, metabuf, HASH_READ);
blkno = BufferGetBlockNumber(buf);
*bufP = so->hashso_curbuf = buf;
ItemPointerSet(current, blkno, offnum);
return (true);
} }

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* btstrat.c-- * btstrat.c--
* Srategy map entries for the btree indexed access method * Srategy map entries for the btree indexed access method
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/hash/Attic/hashstrat.c,v 1.9 1997/08/20 02:01:42 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/access/hash/Attic/hashstrat.c,v 1.10 1997/09/07 04:38:03 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -18,63 +18,66 @@
#include <access/istrat.h> #include <access/istrat.h>
/* /*
* only one valid strategy for hash tables: equality. * only one valid strategy for hash tables: equality.
*/ */
#ifdef NOT_USED #ifdef NOT_USED
static StrategyNumber HTNegate[1] = { static StrategyNumber HTNegate[1] = {
InvalidStrategy InvalidStrategy
}; };
static StrategyNumber HTCommute[1] = { static StrategyNumber HTCommute[1] = {
HTEqualStrategyNumber HTEqualStrategyNumber
}; };
static StrategyNumber HTNegateCommute[1] = { static StrategyNumber HTNegateCommute[1] = {
InvalidStrategy InvalidStrategy
}; };
static StrategyEvaluationData HTEvaluationData = { static StrategyEvaluationData HTEvaluationData = {
/* XXX static for simplicity */ /* XXX static for simplicity */
HTMaxStrategyNumber, HTMaxStrategyNumber,
(StrategyTransformMap)HTNegate, (StrategyTransformMap) HTNegate,
(StrategyTransformMap)HTCommute, (StrategyTransformMap) HTCommute,
(StrategyTransformMap)HTNegateCommute, (StrategyTransformMap) HTNegateCommute,
{NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL} {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
}; };
#endif #endif
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* RelationGetHashStrategy * RelationGetHashStrategy
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
#ifdef NOT_USED #ifdef NOT_USED
static StrategyNumber static StrategyNumber
_hash_getstrat(Relation rel, _hash_getstrat(Relation rel,
AttrNumber attno, AttrNumber attno,
RegProcedure proc) RegProcedure proc)
{ {
StrategyNumber strat; StrategyNumber strat;
strat = RelationGetStrategy(rel, attno, &HTEvaluationData, proc); strat = RelationGetStrategy(rel, attno, &HTEvaluationData, proc);
Assert(StrategyNumberIsValid(strat)); Assert(StrategyNumberIsValid(strat));
return (strat); return (strat);
} }
#endif #endif
#ifdef NOT_USED #ifdef NOT_USED
static bool static bool
_hash_invokestrat(Relation rel, _hash_invokestrat(Relation rel,
AttrNumber attno, AttrNumber attno,
StrategyNumber strat, StrategyNumber strat,
Datum left, Datum left,
Datum right) Datum right)
{ {
return (RelationInvokeStrategy(rel, &HTEvaluationData, attno, strat, return (RelationInvokeStrategy(rel, &HTEvaluationData, attno, strat,
left, right)); left, right));
} }
#endif #endif

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* btutils.c-- * btutils.c--
* Utility code for Postgres btree implementation. * Utility code for Postgres btree implementation.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashutil.c,v 1.9 1997/08/14 05:01:32 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/access/hash/hashutil.c,v 1.10 1997/09/07 04:38:04 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -20,90 +20,91 @@
#include <access/iqual.h> #include <access/iqual.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
ScanKey ScanKey
_hash_mkscankey(Relation rel, IndexTuple itup, HashMetaPage metap) _hash_mkscankey(Relation rel, IndexTuple itup, HashMetaPage metap)
{ {
ScanKey skey; ScanKey skey;
TupleDesc itupdesc; TupleDesc itupdesc;
int natts; int natts;
AttrNumber i; AttrNumber i;
Datum arg; Datum arg;
RegProcedure proc; RegProcedure proc;
bool null; bool null;
natts = rel->rd_rel->relnatts; natts = rel->rd_rel->relnatts;
itupdesc = RelationGetTupleDescriptor(rel); itupdesc = RelationGetTupleDescriptor(rel);
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData)); skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
for (i = 0; i < natts; i++) { for (i = 0; i < natts; i++)
arg = index_getattr(itup, i + 1, itupdesc, &null); {
proc = metap->hashm_procid; arg = index_getattr(itup, i + 1, itupdesc, &null);
ScanKeyEntryInitialize(&skey[i], proc = metap->hashm_procid;
0x0, (AttrNumber) (i + 1), proc, arg); ScanKeyEntryInitialize(&skey[i],
} 0x0, (AttrNumber) (i + 1), proc, arg);
}
return (skey); return (skey);
} }
void void
_hash_freeskey(ScanKey skey) _hash_freeskey(ScanKey skey)
{ {
pfree(skey); pfree(skey);
} }
bool bool
_hash_checkqual(IndexScanDesc scan, IndexTuple itup) _hash_checkqual(IndexScanDesc scan, IndexTuple itup)
{ {
if (scan->numberOfKeys > 0) if (scan->numberOfKeys > 0)
return (index_keytest(itup, return (index_keytest(itup,
RelationGetTupleDescriptor(scan->relation), RelationGetTupleDescriptor(scan->relation),
scan->numberOfKeys, scan->keyData)); scan->numberOfKeys, scan->keyData));
else else
return (true); return (true);
} }
HashItem HashItem
_hash_formitem(IndexTuple itup) _hash_formitem(IndexTuple itup)
{ {
int nbytes_hitem; int nbytes_hitem;
HashItem hitem; HashItem hitem;
Size tuplen; Size tuplen;
/* disallow nulls in hash keys */ /* disallow nulls in hash keys */
if (itup->t_info & INDEX_NULL_MASK) if (itup->t_info & INDEX_NULL_MASK)
elog(WARN, "hash indices cannot include null keys"); elog(WARN, "hash indices cannot include null keys");
/* make a copy of the index tuple with room for the sequence number */ /* make a copy of the index tuple with room for the sequence number */
tuplen = IndexTupleSize(itup); tuplen = IndexTupleSize(itup);
nbytes_hitem = tuplen + nbytes_hitem = tuplen +
(sizeof(HashItemData) - sizeof(IndexTupleData)); (sizeof(HashItemData) - sizeof(IndexTupleData));
hitem = (HashItem) palloc(nbytes_hitem); hitem = (HashItem) palloc(nbytes_hitem);
memmove((char *) &(hitem->hash_itup), (char *) itup, tuplen); memmove((char *) &(hitem->hash_itup), (char *) itup, tuplen);
return (hitem); return (hitem);
} }
Bucket Bucket
_hash_call(Relation rel, HashMetaPage metap, Datum key) _hash_call(Relation rel, HashMetaPage metap, Datum key)
{ {
uint32 n; uint32 n;
Bucket bucket; Bucket bucket;
RegProcedure proc; RegProcedure proc;
proc = metap->hashm_procid; proc = metap->hashm_procid;
n = (uint32) fmgr(proc, key); n = (uint32) fmgr(proc, key);
bucket = n & metap->hashm_highmask; bucket = n & metap->hashm_highmask;
if (bucket > metap->hashm_maxbucket) if (bucket > metap->hashm_maxbucket)
bucket = bucket & metap->hashm_lowmask; bucket = bucket & metap->hashm_lowmask;
return (bucket); return (bucket);
} }
/* /*
@ -112,12 +113,13 @@ _hash_call(Relation rel, HashMetaPage metap, Datum key)
uint32 uint32
_hash_log2(uint32 num) _hash_log2(uint32 num)
{ {
uint32 i, limit; uint32 i,
limit;
limit = 1; limit = 1;
for (i = 0; limit < num; limit = limit << 1, i++) for (i = 0; limit < num; limit = limit << 1, i++)
; ;
return (i); return (i);
} }
/* /*
@ -126,19 +128,20 @@ _hash_log2(uint32 num)
void void
_hash_checkpage(Page page, int flags) _hash_checkpage(Page page, int flags)
{ {
HashPageOpaque opaque; HashPageOpaque opaque;
Assert(page); Assert(page);
Assert(((PageHeader)(page))->pd_lower >= (sizeof(PageHeaderData) - sizeof(ItemIdData))); Assert(((PageHeader) (page))->pd_lower >= (sizeof(PageHeaderData) - sizeof(ItemIdData)));
#if 1 #if 1
Assert(((PageHeader)(page))->pd_upper <= Assert(((PageHeader) (page))->pd_upper <=
(BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData)))); (BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData))));
Assert(((PageHeader)(page))->pd_special == Assert(((PageHeader) (page))->pd_special ==
(BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData)))); (BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData))));
Assert(((PageHeader)(page))->pd_opaque.od_pagesize == BLCKSZ); Assert(((PageHeader) (page))->pd_opaque.od_pagesize == BLCKSZ);
#endif #endif
if (flags) { if (flags)
opaque = (HashPageOpaque) PageGetSpecialPointer(page); {
Assert(opaque->hasho_flag & flags); opaque = (HashPageOpaque) PageGetSpecialPointer(page);
} Assert(opaque->hasho_flag & flags);
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* hio.c-- * hio.c--
* POSTGRES heap access method input/output code. * POSTGRES heap access method input/output code.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Id: hio.c,v 1.9 1996/11/05 09:53:02 scrappy Exp $ * $Id: hio.c,v 1.10 1997/09/07 04:38:11 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -21,64 +21,65 @@
/* /*
* amputunique - place tuple at tid * amputunique - place tuple at tid
* Currently on errors, calls elog. Perhaps should return -1? * Currently on errors, calls elog. Perhaps should return -1?
* Possible errors include the addition of a tuple to the page * Possible errors include the addition of a tuple to the page
* between the time the linep is chosen and the page is L_UP'd. * between the time the linep is chosen and the page is L_UP'd.
* *
* This should be coordinated with the B-tree code. * This should be coordinated with the B-tree code.
* Probably needs to have an amdelunique to allow for * Probably needs to have an amdelunique to allow for
* internal index records to be deleted and reordered as needed. * internal index records to be deleted and reordered as needed.
* For the heap AM, this should never be needed. * For the heap AM, this should never be needed.
*/ */
void void
RelationPutHeapTuple(Relation relation, RelationPutHeapTuple(Relation relation,
BlockNumber blockIndex, BlockNumber blockIndex,
HeapTuple tuple) HeapTuple tuple)
{ {
Buffer buffer; Buffer buffer;
Page pageHeader; Page pageHeader;
BlockNumber numberOfBlocks; BlockNumber numberOfBlocks;
OffsetNumber offnum; OffsetNumber offnum;
unsigned int len; unsigned int len;
ItemId itemId; ItemId itemId;
Item item; Item item;
/* ---------------- /* ----------------
* increment access statistics * increment access statistics
* ---------------- * ----------------
*/ */
IncrHeapAccessStat(local_RelationPutHeapTuple); IncrHeapAccessStat(local_RelationPutHeapTuple);
IncrHeapAccessStat(global_RelationPutHeapTuple); IncrHeapAccessStat(global_RelationPutHeapTuple);
Assert(RelationIsValid(relation)); Assert(RelationIsValid(relation));
Assert(HeapTupleIsValid(tuple)); Assert(HeapTupleIsValid(tuple));
numberOfBlocks = RelationGetNumberOfBlocks(relation); numberOfBlocks = RelationGetNumberOfBlocks(relation);
Assert(blockIndex < numberOfBlocks); Assert(blockIndex < numberOfBlocks);
buffer = ReadBuffer(relation, blockIndex); buffer = ReadBuffer(relation, blockIndex);
#ifndef NO_BUFFERISVALID #ifndef NO_BUFFERISVALID
if (!BufferIsValid(buffer)) { if (!BufferIsValid(buffer))
elog(WARN, "RelationPutHeapTuple: no buffer for %ld in %s", {
blockIndex, &relation->rd_rel->relname); elog(WARN, "RelationPutHeapTuple: no buffer for %ld in %s",
} blockIndex, &relation->rd_rel->relname);
}
#endif #endif
pageHeader = (Page)BufferGetPage(buffer); pageHeader = (Page) BufferGetPage(buffer);
len = (unsigned)DOUBLEALIGN(tuple->t_len); /* be conservative */ len = (unsigned) DOUBLEALIGN(tuple->t_len); /* be conservative */
Assert((int)len <= PageGetFreeSpace(pageHeader)); Assert((int) len <= PageGetFreeSpace(pageHeader));
offnum = PageAddItem((Page)pageHeader, (Item)tuple, offnum = PageAddItem((Page) pageHeader, (Item) tuple,
tuple->t_len, InvalidOffsetNumber, LP_USED); tuple->t_len, InvalidOffsetNumber, LP_USED);
itemId = PageGetItemId((Page)pageHeader, offnum); itemId = PageGetItemId((Page) pageHeader, offnum);
item = PageGetItem((Page)pageHeader, itemId); item = PageGetItem((Page) pageHeader, itemId);
ItemPointerSet(&((HeapTuple)item)->t_ctid, blockIndex, offnum); ItemPointerSet(&((HeapTuple) item)->t_ctid, blockIndex, offnum);
WriteBuffer(buffer); WriteBuffer(buffer);
/* return an accurate tuple */ /* return an accurate tuple */
ItemPointerSet(&tuple->t_ctid, blockIndex, offnum); ItemPointerSet(&tuple->t_ctid, blockIndex, offnum);
} }
/* /*
@ -107,70 +108,70 @@ RelationPutHeapTuple(Relation relation,
void void
RelationPutHeapTupleAtEnd(Relation relation, HeapTuple tuple) RelationPutHeapTupleAtEnd(Relation relation, HeapTuple tuple)
{ {
Buffer buffer; Buffer buffer;
Page pageHeader; Page pageHeader;
BlockNumber lastblock; BlockNumber lastblock;
OffsetNumber offnum; OffsetNumber offnum;
unsigned int len; unsigned int len;
ItemId itemId; ItemId itemId;
Item item; Item item;
Assert(RelationIsValid(relation)); Assert(RelationIsValid(relation));
Assert(HeapTupleIsValid(tuple)); Assert(HeapTupleIsValid(tuple));
/* /*
* XXX This does an lseek - VERY expensive - but at the moment it * XXX This does an lseek - VERY expensive - but at the moment it is
* is the only way to accurately determine how many blocks are in * the only way to accurately determine how many blocks are in a
* a relation. A good optimization would be to get this to actually * relation. A good optimization would be to get this to actually
* work properly. * work properly.
*/ */
lastblock = RelationGetNumberOfBlocks(relation); lastblock = RelationGetNumberOfBlocks(relation);
if (lastblock == 0) if (lastblock == 0)
{ {
buffer = ReadBuffer(relation, lastblock); buffer = ReadBuffer(relation, lastblock);
pageHeader = (Page)BufferGetPage(buffer); pageHeader = (Page) BufferGetPage(buffer);
if (PageIsNew((PageHeader) pageHeader)) if (PageIsNew((PageHeader) pageHeader))
{ {
buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW); buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
pageHeader = (Page)BufferGetPage(buffer); pageHeader = (Page) BufferGetPage(buffer);
PageInit(pageHeader, BufferGetPageSize(buffer), 0); PageInit(pageHeader, BufferGetPageSize(buffer), 0);
} }
} }
else else
buffer = ReadBuffer(relation, lastblock - 1); buffer = ReadBuffer(relation, lastblock - 1);
pageHeader = (Page)BufferGetPage(buffer); pageHeader = (Page) BufferGetPage(buffer);
len = (unsigned)DOUBLEALIGN(tuple->t_len); /* be conservative */ len = (unsigned) DOUBLEALIGN(tuple->t_len); /* be conservative */
/* /*
* Note that this is true if the above returned a bogus page, which * Note that this is true if the above returned a bogus page, which it
* it will do for a completely empty relation. * will do for a completely empty relation.
*/ */
if (len > PageGetFreeSpace(pageHeader)) if (len > PageGetFreeSpace(pageHeader))
{ {
buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW); buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
pageHeader = (Page)BufferGetPage(buffer); pageHeader = (Page) BufferGetPage(buffer);
PageInit(pageHeader, BufferGetPageSize(buffer), 0); PageInit(pageHeader, BufferGetPageSize(buffer), 0);
if (len > PageGetFreeSpace(pageHeader)) if (len > PageGetFreeSpace(pageHeader))
elog(WARN, "Tuple is too big: size %d", len); elog(WARN, "Tuple is too big: size %d", len);
} }
offnum = PageAddItem((Page)pageHeader, (Item)tuple, offnum = PageAddItem((Page) pageHeader, (Item) tuple,
tuple->t_len, InvalidOffsetNumber, LP_USED); tuple->t_len, InvalidOffsetNumber, LP_USED);
itemId = PageGetItemId((Page)pageHeader, offnum); itemId = PageGetItemId((Page) pageHeader, offnum);
item = PageGetItem((Page)pageHeader, itemId); item = PageGetItem((Page) pageHeader, itemId);
lastblock = BufferGetBlockNumber(buffer); lastblock = BufferGetBlockNumber(buffer);
ItemPointerSet(&((HeapTuple)item)->t_ctid, lastblock, offnum); ItemPointerSet(&((HeapTuple) item)->t_ctid, lastblock, offnum);
/* return an accurate tuple */ /* return an accurate tuple */
ItemPointerSet(&tuple->t_ctid, lastblock, offnum); ItemPointerSet(&tuple->t_ctid, lastblock, offnum);
WriteBuffer(buffer); WriteBuffer(buffer);
} }

View File

@ -1,16 +1,16 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* stats.c-- * stats.c--
* heap access method debugging statistic collection routines * heap access method debugging statistic collection routines
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/heap/Attic/stats.c,v 1.11 1997/08/19 21:29:21 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/heap/Attic/stats.c,v 1.12 1997/09/07 04:38:13 momjian Exp $
* *
* NOTES * NOTES
* initam should be moved someplace else. * initam should be moved someplace else.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -23,15 +23,15 @@
#include <utils/mcxt.h> #include <utils/mcxt.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
static void InitHeapAccessStatistics(void); static void InitHeapAccessStatistics(void);
/* ---------------- /* ----------------
* InitHeapAccessStatistics * InitHeapAccessStatistics
* ---------------- * ----------------
*/ */
HeapAccessStatistics heap_access_stats = (HeapAccessStatistics) NULL; HeapAccessStatistics heap_access_stats = (HeapAccessStatistics) NULL;
@ -39,306 +39,311 @@ HeapAccessStatistics heap_access_stats = (HeapAccessStatistics) NULL;
static void static void
InitHeapAccessStatistics() InitHeapAccessStatistics()
{ {
MemoryContext oldContext; MemoryContext oldContext;
HeapAccessStatistics stats; HeapAccessStatistics stats;
/* ---------------- /* ----------------
* make sure we don't initialize things twice * make sure we don't initialize things twice
* ---------------- * ----------------
*/ */
if (heap_access_stats != NULL) if (heap_access_stats != NULL)
return; return;
/* ---------------- /* ----------------
* allocate statistics structure from the top memory context * allocate statistics structure from the top memory context
* ---------------- * ----------------
*/ */
oldContext = MemoryContextSwitchTo(TopMemoryContext); oldContext = MemoryContextSwitchTo(TopMemoryContext);
stats = (HeapAccessStatistics) stats = (HeapAccessStatistics)
palloc(sizeof(HeapAccessStatisticsData)); palloc(sizeof(HeapAccessStatisticsData));
/* ---------------- /* ----------------
* initialize fields to default values * initialize fields to default values
* ---------------- * ----------------
*/ */
stats->global_open = 0; stats->global_open = 0;
stats->global_openr = 0; stats->global_openr = 0;
stats->global_close = 0; stats->global_close = 0;
stats->global_beginscan = 0; stats->global_beginscan = 0;
stats->global_rescan = 0; stats->global_rescan = 0;
stats->global_endscan = 0; stats->global_endscan = 0;
stats->global_getnext = 0; stats->global_getnext = 0;
stats->global_fetch = 0; stats->global_fetch = 0;
stats->global_insert = 0; stats->global_insert = 0;
stats->global_delete = 0; stats->global_delete = 0;
stats->global_replace = 0; stats->global_replace = 0;
stats->global_markpos = 0; stats->global_markpos = 0;
stats->global_restrpos = 0; stats->global_restrpos = 0;
stats->global_BufferGetRelation = 0; stats->global_BufferGetRelation = 0;
stats->global_RelationIdGetRelation = 0; stats->global_RelationIdGetRelation = 0;
stats->global_RelationIdGetRelation_Buf = 0; stats->global_RelationIdGetRelation_Buf = 0;
stats->global_getreldesc = 0; stats->global_getreldesc = 0;
stats->global_heapgettup = 0; stats->global_heapgettup = 0;
stats->global_RelationPutHeapTuple = 0; stats->global_RelationPutHeapTuple = 0;
stats->global_RelationPutLongHeapTuple = 0; stats->global_RelationPutLongHeapTuple = 0;
stats->local_open = 0; stats->local_open = 0;
stats->local_openr = 0; stats->local_openr = 0;
stats->local_close = 0; stats->local_close = 0;
stats->local_beginscan = 0; stats->local_beginscan = 0;
stats->local_rescan = 0; stats->local_rescan = 0;
stats->local_endscan = 0; stats->local_endscan = 0;
stats->local_getnext = 0; stats->local_getnext = 0;
stats->local_fetch = 0; stats->local_fetch = 0;
stats->local_insert = 0; stats->local_insert = 0;
stats->local_delete = 0; stats->local_delete = 0;
stats->local_replace = 0; stats->local_replace = 0;
stats->local_markpos = 0; stats->local_markpos = 0;
stats->local_restrpos = 0; stats->local_restrpos = 0;
stats->local_BufferGetRelation = 0; stats->local_BufferGetRelation = 0;
stats->local_RelationIdGetRelation = 0; stats->local_RelationIdGetRelation = 0;
stats->local_RelationIdGetRelation_Buf = 0; stats->local_RelationIdGetRelation_Buf = 0;
stats->local_getreldesc = 0; stats->local_getreldesc = 0;
stats->local_heapgettup = 0; stats->local_heapgettup = 0;
stats->local_RelationPutHeapTuple = 0; stats->local_RelationPutHeapTuple = 0;
stats->local_RelationPutLongHeapTuple = 0; stats->local_RelationPutLongHeapTuple = 0;
stats->local_RelationNameGetRelation = 0; stats->local_RelationNameGetRelation = 0;
stats->global_RelationNameGetRelation = 0; stats->global_RelationNameGetRelation = 0;
/* ---------------- /* ----------------
* record init times * record init times
* ---------------- * ----------------
*/ */
time(&stats->init_global_timestamp); time(&stats->init_global_timestamp);
time(&stats->local_reset_timestamp); time(&stats->local_reset_timestamp);
time(&stats->last_request_timestamp); time(&stats->last_request_timestamp);
/* ---------------- /* ----------------
* return to old memory context * return to old memory context
* ---------------- * ----------------
*/ */
MemoryContextSwitchTo(oldContext); MemoryContextSwitchTo(oldContext);
heap_access_stats = stats; heap_access_stats = stats;
} }
#ifdef NOT_USED #ifdef NOT_USED
/* ---------------- /* ----------------
* ResetHeapAccessStatistics * ResetHeapAccessStatistics
* ---------------- * ----------------
*/ */
void void
ResetHeapAccessStatistics() ResetHeapAccessStatistics()
{ {
HeapAccessStatistics stats; HeapAccessStatistics stats;
/* ---------------- /* ----------------
* do nothing if stats aren't initialized * do nothing if stats aren't initialized
* ---------------- * ----------------
*/ */
if (heap_access_stats == NULL) if (heap_access_stats == NULL)
return; return;
stats = heap_access_stats; stats = heap_access_stats;
/* ---------------- /* ----------------
* reset local counts * reset local counts
* ---------------- * ----------------
*/ */
stats->local_open = 0; stats->local_open = 0;
stats->local_openr = 0; stats->local_openr = 0;
stats->local_close = 0; stats->local_close = 0;
stats->local_beginscan = 0; stats->local_beginscan = 0;
stats->local_rescan = 0; stats->local_rescan = 0;
stats->local_endscan = 0; stats->local_endscan = 0;
stats->local_getnext = 0; stats->local_getnext = 0;
stats->local_fetch = 0; stats->local_fetch = 0;
stats->local_insert = 0; stats->local_insert = 0;
stats->local_delete = 0; stats->local_delete = 0;
stats->local_replace = 0; stats->local_replace = 0;
stats->local_markpos = 0; stats->local_markpos = 0;
stats->local_restrpos = 0; stats->local_restrpos = 0;
stats->local_BufferGetRelation = 0; stats->local_BufferGetRelation = 0;
stats->local_RelationIdGetRelation = 0; stats->local_RelationIdGetRelation = 0;
stats->local_RelationIdGetRelation_Buf = 0; stats->local_RelationIdGetRelation_Buf = 0;
stats->local_getreldesc = 0; stats->local_getreldesc = 0;
stats->local_heapgettup = 0; stats->local_heapgettup = 0;
stats->local_RelationPutHeapTuple = 0; stats->local_RelationPutHeapTuple = 0;
stats->local_RelationPutLongHeapTuple = 0; stats->local_RelationPutLongHeapTuple = 0;
/* ---------------- /* ----------------
* reset local timestamps * reset local timestamps
* ---------------- * ----------------
*/ */
time(&stats->local_reset_timestamp); time(&stats->local_reset_timestamp);
time(&stats->last_request_timestamp); time(&stats->last_request_timestamp);
} }
#endif #endif
#ifdef NOT_USED #ifdef NOT_USED
/* ---------------- /* ----------------
* GetHeapAccessStatistics * GetHeapAccessStatistics
* ---------------- * ----------------
*/ */
HeapAccessStatistics GetHeapAccessStatistics() HeapAccessStatistics
GetHeapAccessStatistics()
{ {
HeapAccessStatistics stats; HeapAccessStatistics stats;
/* ---------------- /* ----------------
* return nothing if stats aren't initialized * return nothing if stats aren't initialized
* ---------------- * ----------------
*/ */
if (heap_access_stats == NULL) if (heap_access_stats == NULL)
return NULL; return NULL;
/* ---------------- /* ----------------
* record the current request time * record the current request time
* ---------------- * ----------------
*/ */
time(&heap_access_stats->last_request_timestamp); time(&heap_access_stats->last_request_timestamp);
/* ---------------- /* ----------------
* allocate a copy of the stats and return it to the caller. * allocate a copy of the stats and return it to the caller.
* ---------------- * ----------------
*/ */
stats = (HeapAccessStatistics) stats = (HeapAccessStatistics)
palloc(sizeof(HeapAccessStatisticsData)); palloc(sizeof(HeapAccessStatisticsData));
memmove(stats, memmove(stats,
heap_access_stats, heap_access_stats,
sizeof(HeapAccessStatisticsData)); sizeof(HeapAccessStatisticsData));
return stats; return stats;
} }
#endif #endif
#ifdef NOT_USED #ifdef NOT_USED
/* ---------------- /* ----------------
* PrintHeapAccessStatistics * PrintHeapAccessStatistics
* ---------------- * ----------------
*/ */
void void
PrintHeapAccessStatistics(HeapAccessStatistics stats) PrintHeapAccessStatistics(HeapAccessStatistics stats)
{ {
/* ---------------- /* ----------------
* return nothing if stats aren't valid * return nothing if stats aren't valid
* ---------------- * ----------------
*/ */
if (stats == NULL) if (stats == NULL)
return; return;
printf("======== heap am statistics ========\n"); printf("======== heap am statistics ========\n");
printf("init_global_timestamp: %s", printf("init_global_timestamp: %s",
ctime(&(stats->init_global_timestamp))); ctime(&(stats->init_global_timestamp)));
printf("local_reset_timestamp: %s", printf("local_reset_timestamp: %s",
ctime(&(stats->local_reset_timestamp))); ctime(&(stats->local_reset_timestamp)));
printf("last_request_timestamp: %s", printf("last_request_timestamp: %s",
ctime(&(stats->last_request_timestamp))); ctime(&(stats->last_request_timestamp)));
printf("local/global_open: %6d/%6d\n", printf("local/global_open: %6d/%6d\n",
stats->local_open, stats->global_open); stats->local_open, stats->global_open);
printf("local/global_openr: %6d/%6d\n", printf("local/global_openr: %6d/%6d\n",
stats->local_openr, stats->global_openr); stats->local_openr, stats->global_openr);
printf("local/global_close: %6d/%6d\n", printf("local/global_close: %6d/%6d\n",
stats->local_close, stats->global_close); stats->local_close, stats->global_close);
printf("local/global_beginscan: %6d/%6d\n", printf("local/global_beginscan: %6d/%6d\n",
stats->local_beginscan, stats->global_beginscan); stats->local_beginscan, stats->global_beginscan);
printf("local/global_rescan: %6d/%6d\n", printf("local/global_rescan: %6d/%6d\n",
stats->local_rescan, stats->global_rescan); stats->local_rescan, stats->global_rescan);
printf("local/global_endscan: %6d/%6d\n", printf("local/global_endscan: %6d/%6d\n",
stats->local_endscan, stats->global_endscan); stats->local_endscan, stats->global_endscan);
printf("local/global_getnext: %6d/%6d\n", printf("local/global_getnext: %6d/%6d\n",
stats->local_getnext, stats->global_getnext); stats->local_getnext, stats->global_getnext);
printf("local/global_fetch: %6d/%6d\n", printf("local/global_fetch: %6d/%6d\n",
stats->local_fetch, stats->global_fetch); stats->local_fetch, stats->global_fetch);
printf("local/global_insert: %6d/%6d\n", printf("local/global_insert: %6d/%6d\n",
stats->local_insert, stats->global_insert); stats->local_insert, stats->global_insert);
printf("local/global_delete: %6d/%6d\n", printf("local/global_delete: %6d/%6d\n",
stats->local_delete, stats->global_delete); stats->local_delete, stats->global_delete);
printf("local/global_replace: %6d/%6d\n", printf("local/global_replace: %6d/%6d\n",
stats->local_replace, stats->global_replace); stats->local_replace, stats->global_replace);
printf("local/global_markpos: %6d/%6d\n", printf("local/global_markpos: %6d/%6d\n",
stats->local_markpos, stats->global_markpos); stats->local_markpos, stats->global_markpos);
printf("local/global_restrpos: %6d/%6d\n", printf("local/global_restrpos: %6d/%6d\n",
stats->local_restrpos, stats->global_restrpos); stats->local_restrpos, stats->global_restrpos);
printf("================\n"); printf("================\n");
printf("local/global_BufferGetRelation: %6d/%6d\n", printf("local/global_BufferGetRelation: %6d/%6d\n",
stats->local_BufferGetRelation, stats->local_BufferGetRelation,
stats->global_BufferGetRelation); stats->global_BufferGetRelation);
printf("local/global_RelationIdGetRelation: %6d/%6d\n", printf("local/global_RelationIdGetRelation: %6d/%6d\n",
stats->local_RelationIdGetRelation, stats->local_RelationIdGetRelation,
stats->global_RelationIdGetRelation); stats->global_RelationIdGetRelation);
printf("local/global_RelationIdGetRelation_Buf: %6d/%6d\n", printf("local/global_RelationIdGetRelation_Buf: %6d/%6d\n",
stats->local_RelationIdGetRelation_Buf, stats->local_RelationIdGetRelation_Buf,
stats->global_RelationIdGetRelation_Buf); stats->global_RelationIdGetRelation_Buf);
printf("local/global_getreldesc: %6d/%6d\n", printf("local/global_getreldesc: %6d/%6d\n",
stats->local_getreldesc, stats->global_getreldesc); stats->local_getreldesc, stats->global_getreldesc);
printf("local/global_heapgettup: %6d/%6d\n", printf("local/global_heapgettup: %6d/%6d\n",
stats->local_heapgettup, stats->global_heapgettup); stats->local_heapgettup, stats->global_heapgettup);
printf("local/global_RelationPutHeapTuple: %6d/%6d\n", printf("local/global_RelationPutHeapTuple: %6d/%6d\n",
stats->local_RelationPutHeapTuple, stats->local_RelationPutHeapTuple,
stats->global_RelationPutHeapTuple); stats->global_RelationPutHeapTuple);
printf("local/global_RelationPutLongHeapTuple: %6d/%6d\n", printf("local/global_RelationPutLongHeapTuple: %6d/%6d\n",
stats->local_RelationPutLongHeapTuple, stats->local_RelationPutLongHeapTuple,
stats->global_RelationPutLongHeapTuple); stats->global_RelationPutLongHeapTuple);
printf("===================================\n"); printf("===================================\n");
printf("\n"); printf("\n");
} }
#endif #endif
#ifdef NOT_USED #ifdef NOT_USED
/* ---------------- /* ----------------
* PrintAndFreeHeapAccessStatistics * PrintAndFreeHeapAccessStatistics
* ---------------- * ----------------
*/ */
void void
PrintAndFreeHeapAccessStatistics(HeapAccessStatistics stats) PrintAndFreeHeapAccessStatistics(HeapAccessStatistics stats)
{ {
PrintHeapAccessStatistics(stats); PrintHeapAccessStatistics(stats);
if (stats != NULL) if (stats != NULL)
pfree(stats); pfree(stats);
} }
#endif #endif
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* access method initialization * access method initialization
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
/* ---------------- /* ----------------
* initam should someday be moved someplace else. * initam should someday be moved someplace else.
* ---------------- * ----------------
*/ */
void void
initam(void) initam(void)
{ {
/* ---------------- /* ----------------
* initialize heap statistics. * initialize heap statistics.
* ---------------- * ----------------
*/ */
InitHeapAccessStatistics(); InitHeapAccessStatistics();
} }

View File

@ -1,17 +1,17 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* genam.c-- * genam.c--
* general index access method routines * general index access method routines
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.7 1997/08/19 21:29:26 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.8 1997/09/07 04:38:17 momjian Exp $
* *
* NOTES * NOTES
* many of the old access method routines have been turned into * many of the old access method routines have been turned into
* macros and moved to genam.h -cim 4/30/91 * macros and moved to genam.h -cim 4/30/91
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -29,18 +29,18 @@
* previous, current, next. Note that the case of reverse scans works * previous, current, next. Note that the case of reverse scans works
* identically. * identically.
* *
* State Result * State Result
* (1) + + - + 0 0 (if the next item pointer is invalid) * (1) + + - + 0 0 (if the next item pointer is invalid)
* (2) + X - (otherwise) * (2) + X - (otherwise)
* (3) * 0 0 * 0 0 (no change) * (3) * 0 0 * 0 0 (no change)
* (4) + X 0 X 0 0 (shift) * (4) + X 0 X 0 0 (shift)
* (5) * + X + X - (shift, add unknown) * (5) * + X + X - (shift, add unknown)
* *
* All other states cannot occur. * All other states cannot occur.
* *
* Note: * Note:
*It would be possible to cache the status of the previous and *It would be possible to cache the status of the previous and
* next item pointer using the flags. * next item pointer using the flags.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
@ -51,220 +51,234 @@
#include <storage/bufmgr.h> #include <storage/bufmgr.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* general access method routines * general access method routines
* *
* All indexed access methods use an identical scan structure. * All indexed access methods use an identical scan structure.
* We don't know how the various AMs do locking, however, so we don't * We don't know how the various AMs do locking, however, so we don't
* do anything about that here. * do anything about that here.
* *
* The intent is that an AM implementor will define a front-end routine * The intent is that an AM implementor will define a front-end routine
* that calls this one, to fill in the scan, and then does whatever kind * that calls this one, to fill in the scan, and then does whatever kind
* of locking he wants. * of locking he wants.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
/* ---------------- /* ----------------
* RelationGetIndexScan -- Create and fill an IndexScanDesc. * RelationGetIndexScan -- Create and fill an IndexScanDesc.
* *
* This routine creates an index scan structure and sets its contents * This routine creates an index scan structure and sets its contents
* up correctly. This routine calls AMrescan to set up the scan with * up correctly. This routine calls AMrescan to set up the scan with
* the passed key. * the passed key.
* *
* Parameters: * Parameters:
* relation -- index relation for scan. * relation -- index relation for scan.
* scanFromEnd -- if true, begin scan at one of the index's * scanFromEnd -- if true, begin scan at one of the index's
* endpoints. * endpoints.
* numberOfKeys -- count of scan keys (more than one won't * numberOfKeys -- count of scan keys (more than one won't
* necessarily do anything useful, yet). * necessarily do anything useful, yet).
* key -- the ScanKey for the starting position of the scan. * key -- the ScanKey for the starting position of the scan.
* *
* Returns: * Returns:
* An initialized IndexScanDesc. * An initialized IndexScanDesc.
* *
* Side Effects: * Side Effects:
* Bumps the ref count on the relation to keep it in the cache. * Bumps the ref count on the relation to keep it in the cache.
* *
* ---------------- * ----------------
*/ */
IndexScanDesc IndexScanDesc
RelationGetIndexScan(Relation relation, RelationGetIndexScan(Relation relation,
bool scanFromEnd, bool scanFromEnd,
uint16 numberOfKeys, uint16 numberOfKeys,
ScanKey key) ScanKey key)
{ {
IndexScanDesc scan; IndexScanDesc scan;
if (! RelationIsValid(relation)) if (!RelationIsValid(relation))
elog(WARN, "RelationGetIndexScan: relation invalid"); elog(WARN, "RelationGetIndexScan: relation invalid");
scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData)); scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData));
scan->relation = relation; scan->relation = relation;
scan->opaque = NULL; scan->opaque = NULL;
scan->numberOfKeys = numberOfKeys; scan->numberOfKeys = numberOfKeys;
ItemPointerSetInvalid(&scan->previousItemData); ItemPointerSetInvalid(&scan->previousItemData);
ItemPointerSetInvalid(&scan->currentItemData); ItemPointerSetInvalid(&scan->currentItemData);
ItemPointerSetInvalid(&scan->nextItemData); ItemPointerSetInvalid(&scan->nextItemData);
ItemPointerSetInvalid(&scan->previousMarkData); ItemPointerSetInvalid(&scan->previousMarkData);
ItemPointerSetInvalid(&scan->currentMarkData); ItemPointerSetInvalid(&scan->currentMarkData);
ItemPointerSetInvalid(&scan->nextMarkData); ItemPointerSetInvalid(&scan->nextMarkData);
if (numberOfKeys > 0) { if (numberOfKeys > 0)
scan->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * numberOfKeys); {
} else { scan->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * numberOfKeys);
scan->keyData = NULL; }
} else
{
scan->keyData = NULL;
}
index_rescan(scan, scanFromEnd, key); index_rescan(scan, scanFromEnd, key);
return (scan); return (scan);
} }
#ifdef NOT_USED #ifdef NOT_USED
/* ---------------- /* ----------------
* IndexScanRestart -- Restart an index scan. * IndexScanRestart -- Restart an index scan.
* *
* This routine isn't used by any existing access method. It's * This routine isn't used by any existing access method. It's
* appropriate if relation level locks are what you want. * appropriate if relation level locks are what you want.
* *
* Returns: * Returns:
* None. * None.
* *
* Side Effects: * Side Effects:
* None. * None.
* ---------------- * ----------------
*/ */
void void
IndexScanRestart(IndexScanDesc scan, IndexScanRestart(IndexScanDesc scan,
bool scanFromEnd, bool scanFromEnd,
ScanKey key) ScanKey key)
{ {
if (! IndexScanIsValid(scan)) if (!IndexScanIsValid(scan))
elog(WARN, "IndexScanRestart: invalid scan"); elog(WARN, "IndexScanRestart: invalid scan");
ItemPointerSetInvalid(&scan->previousItemData); ItemPointerSetInvalid(&scan->previousItemData);
ItemPointerSetInvalid(&scan->currentItemData); ItemPointerSetInvalid(&scan->currentItemData);
ItemPointerSetInvalid(&scan->nextItemData); ItemPointerSetInvalid(&scan->nextItemData);
if (RelationGetNumberOfBlocks(scan->relation) == 0) if (RelationGetNumberOfBlocks(scan->relation) == 0)
scan->flags = ScanUnmarked; scan->flags = ScanUnmarked;
else if (scanFromEnd) else if (scanFromEnd)
scan->flags = ScanUnmarked | ScanUncheckedPrevious; scan->flags = ScanUnmarked | ScanUncheckedPrevious;
else else
scan->flags = ScanUnmarked | ScanUncheckedNext; scan->flags = ScanUnmarked | ScanUncheckedNext;
scan->scanFromEnd = (bool) scanFromEnd; scan->scanFromEnd = (bool) scanFromEnd;
if (scan->numberOfKeys > 0) if (scan->numberOfKeys > 0)
memmove(scan->keyData, memmove(scan->keyData,
key, key,
scan->numberOfKeys * sizeof(ScanKeyData)); scan->numberOfKeys * sizeof(ScanKeyData));
} }
#endif #endif
#ifdef NOT_USED #ifdef NOT_USED
/* ---------------- /* ----------------
* IndexScanEnd -- End and index scan. * IndexScanEnd -- End and index scan.
* *
* This routine is not used by any existing access method, but is * This routine is not used by any existing access method, but is
* suitable for use if you don't want to do sophisticated locking. * suitable for use if you don't want to do sophisticated locking.
* *
* Returns: * Returns:
* None. * None.
* *
* Side Effects: * Side Effects:
* None. * None.
* ---------------- * ----------------
*/ */
void void
IndexScanEnd(IndexScanDesc scan) IndexScanEnd(IndexScanDesc scan)
{ {
if (! IndexScanIsValid(scan)) if (!IndexScanIsValid(scan))
elog(WARN, "IndexScanEnd: invalid scan"); elog(WARN, "IndexScanEnd: invalid scan");
pfree(scan); pfree(scan);
} }
#endif #endif
/* ---------------- /* ----------------
* IndexScanMarkPosition -- Mark current position in a scan. * IndexScanMarkPosition -- Mark current position in a scan.
* *
* This routine isn't used by any existing access method, but is the * This routine isn't used by any existing access method, but is the
* one that AM implementors should use, if they don't want to do any * one that AM implementors should use, if they don't want to do any
* special locking. If relation-level locking is sufficient, this is * special locking. If relation-level locking is sufficient, this is
* the routine for you. * the routine for you.
* *
* Returns: * Returns:
* None. * None.
* *
* Side Effects: * Side Effects:
* None. * None.
* ---------------- * ----------------
*/ */
void void
IndexScanMarkPosition(IndexScanDesc scan) IndexScanMarkPosition(IndexScanDesc scan)
{ {
RetrieveIndexResult result; RetrieveIndexResult result;
if (scan->flags & ScanUncheckedPrevious) { if (scan->flags & ScanUncheckedPrevious)
result = {
index_getnext(scan, BackwardScanDirection); result =
index_getnext(scan, BackwardScanDirection);
if (result != NULL) { if (result != NULL)
scan->previousItemData = result->index_iptr; {
} else { scan->previousItemData = result->index_iptr;
ItemPointerSetInvalid(&scan->previousItemData); }
else
{
ItemPointerSetInvalid(&scan->previousItemData);
}
}
else if (scan->flags & ScanUncheckedNext)
{
result = (RetrieveIndexResult)
index_getnext(scan, ForwardScanDirection);
if (result != NULL)
{
scan->nextItemData = result->index_iptr;
}
else
{
ItemPointerSetInvalid(&scan->nextItemData);
}
} }
} else if (scan->flags & ScanUncheckedNext) { scan->previousMarkData = scan->previousItemData;
result = (RetrieveIndexResult) scan->currentMarkData = scan->currentItemData;
index_getnext(scan, ForwardScanDirection); scan->nextMarkData = scan->nextItemData;
if (result != NULL) { scan->flags = 0x0; /* XXX should have a symbolic name */
scan->nextItemData = result->index_iptr;
} else {
ItemPointerSetInvalid(&scan->nextItemData);
}
}
scan->previousMarkData = scan->previousItemData;
scan->currentMarkData = scan->currentItemData;
scan->nextMarkData = scan->nextItemData;
scan->flags = 0x0; /* XXX should have a symbolic name */
} }
/* ---------------- /* ----------------
* IndexScanRestorePosition -- Restore position on a marked scan. * IndexScanRestorePosition -- Restore position on a marked scan.
* *
* This routine isn't used by any existing access method, but is the * This routine isn't used by any existing access method, but is the
* one that AM implementors should use if they don't want to do any * one that AM implementors should use if they don't want to do any
* special locking. If relation-level locking is sufficient, then * special locking. If relation-level locking is sufficient, then
* this is the one you want. * this is the one you want.
* *
* Returns: * Returns:
* None. * None.
* *
* Side Effects: * Side Effects:
* None. * None.
* ---------------- * ----------------
*/ */
void void
IndexScanRestorePosition(IndexScanDesc scan) IndexScanRestorePosition(IndexScanDesc scan)
{ {
if (scan->flags & ScanUnmarked) if (scan->flags & ScanUnmarked)
elog(WARN, "IndexScanRestorePosition: no mark to restore"); elog(WARN, "IndexScanRestorePosition: no mark to restore");
scan->previousItemData = scan->previousMarkData; scan->previousItemData = scan->previousMarkData;
scan->currentItemData = scan->currentMarkData; scan->currentItemData = scan->currentMarkData;
scan->nextItemData = scan->nextMarkData; scan->nextItemData = scan->nextMarkData;
scan->flags = 0x0; /* XXX should have a symbolic name */ scan->flags = 0x0; /* XXX should have a symbolic name */
} }

View File

@ -1,64 +1,64 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* indexam.c-- * indexam.c--
* general index access method routines * general index access method routines
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.13 1997/08/26 23:31:28 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.14 1997/09/07 04:38:26 momjian Exp $
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
* index_open - open an index relation by relationId * index_open - open an index relation by relationId
* index_openr - open a index relation by name * index_openr - open a index relation by name
* index_close - close a index relation * index_close - close a index relation
* index_beginscan - start a scan of an index * index_beginscan - start a scan of an index
* index_rescan - restart a scan of an index * index_rescan - restart a scan of an index
* index_endscan - end a scan * index_endscan - end a scan
* index_insert - insert an index tuple into a relation * index_insert - insert an index tuple into a relation
* index_delete - delete an item from an index relation * index_delete - delete an item from an index relation
* index_markpos - mark a scan position * index_markpos - mark a scan position
* index_restrpos - restore a scan position * index_restrpos - restore a scan position
* index_getnext - get the next tuple from a scan * index_getnext - get the next tuple from a scan
* ** index_fetch - retrieve tuple with tid * ** index_fetch - retrieve tuple with tid
* ** index_replace - replace a tuple * ** index_replace - replace a tuple
* ** index_getattr - get an attribute from an index tuple * ** index_getattr - get an attribute from an index tuple
* index_getprocid - get a support procedure id from the rel tuple * index_getprocid - get a support procedure id from the rel tuple
* *
* IndexScanIsValid - check index scan * IndexScanIsValid - check index scan
* *
* NOTES * NOTES
* This file contains the index_ routines which used * This file contains the index_ routines which used
* to be a scattered collection of stuff in access/genam. * to be a scattered collection of stuff in access/genam.
* *
* The ** routines: index_fetch, index_replace, and index_getattr * The ** routines: index_fetch, index_replace, and index_getattr
* have not yet been implemented. They may not be needed. * have not yet been implemented. They may not be needed.
* *
* old comments * old comments
* Scans are implemented as follows: * Scans are implemented as follows:
* *
* `0' represents an invalid item pointer. * `0' represents an invalid item pointer.
* `-' represents an unknown item pointer. * `-' represents an unknown item pointer.
* `X' represents a known item pointers. * `X' represents a known item pointers.
* `+' represents known or invalid item pointers. * `+' represents known or invalid item pointers.
* `*' represents any item pointers. * `*' represents any item pointers.
* *
* State is represented by a triple of these symbols in the order of * State is represented by a triple of these symbols in the order of
* previous, current, next. Note that the case of reverse scans works * previous, current, next. Note that the case of reverse scans works
* identically. * identically.
* *
* State Result * State Result
* (1) + + - + 0 0 (if the next item pointer is invalid) * (1) + + - + 0 0 (if the next item pointer is invalid)
* (2) + X - (otherwise) * (2) + X - (otherwise)
* (3) * 0 0 * 0 0 (no change) * (3) * 0 0 * 0 0 (no change)
* (4) + X 0 X 0 0 (shift) * (4) + X 0 X 0 0 (shift)
* (5) * + X + X - (shift, add unknown) * (5) * + X + X - (shift, add unknown)
* *
* All other states cannot occur. * All other states cannot occur.
* *
* Note: It would be possible to cache the status of the previous and * Note: It would be possible to cache the status of the previous and
* next item pointer using the flags. * next item pointer using the flags.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -72,9 +72,9 @@
#include <access/heapam.h> #include <access/heapam.h>
/* ---------------- /* ----------------
* undefine macros we aren't going to use that would otherwise * undefine macros we aren't going to use that would otherwise
* get in our way.. delete is defined in c.h and the am's are * get in our way.. delete is defined in c.h and the am's are
* defined in heapam.h * defined in heapam.h
* ---------------- * ----------------
*/ */
#undef delete #undef delete
@ -88,314 +88,320 @@
#undef amgettuple #undef amgettuple
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* macros used in index_ routines * macros used in index_ routines
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
#define RELATION_CHECKS \ #define RELATION_CHECKS \
Assert(RelationIsValid(relation)); \ Assert(RelationIsValid(relation)); \
Assert(PointerIsValid(relation->rd_am)) Assert(PointerIsValid(relation->rd_am))
#define SCAN_CHECKS \ #define SCAN_CHECKS \
Assert(IndexScanIsValid(scan)); \ Assert(IndexScanIsValid(scan)); \
Assert(RelationIsValid(scan->relation)); \ Assert(RelationIsValid(scan->relation)); \
Assert(PointerIsValid(scan->relation->rd_am)) Assert(PointerIsValid(scan->relation->rd_am))
#define GET_REL_PROCEDURE(x,y) \ #define GET_REL_PROCEDURE(x,y) \
procedure = relation->rd_am->y; \ procedure = relation->rd_am->y; \
if (! RegProcedureIsValid(procedure)) \ if (! RegProcedureIsValid(procedure)) \
elog(WARN, "index_%s: invalid %s regproc", \ elog(WARN, "index_%s: invalid %s regproc", \
CppAsString(x), CppAsString(y)) CppAsString(x), CppAsString(y))
#define GET_SCAN_PROCEDURE(x,y) \ #define GET_SCAN_PROCEDURE(x,y) \
procedure = scan->relation->rd_am->y; \ procedure = scan->relation->rd_am->y; \
if (! RegProcedureIsValid(procedure)) \ if (! RegProcedureIsValid(procedure)) \
elog(WARN, "index_%s: invalid %s regproc", \ elog(WARN, "index_%s: invalid %s regproc", \
CppAsString(x), CppAsString(y)) CppAsString(x), CppAsString(y))
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* index_ interface functions * index_ interface functions
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
/* ---------------- /* ----------------
* index_open - open an index relation by relationId * index_open - open an index relation by relationId
* *
* presently the relcache routines do all the work we need * presently the relcache routines do all the work we need
* to open/close index relations. * to open/close index relations.
* ---------------- * ----------------
*/ */
Relation Relation
index_open(Oid relationId) index_open(Oid relationId)
{ {
return RelationIdGetRelation(relationId); return RelationIdGetRelation(relationId);
} }
/* ---------------- /* ----------------
* index_openr - open a index relation by name * index_openr - open a index relation by name
* *
* presently the relcache routines do all the work we need * presently the relcache routines do all the work we need
* to open/close index relations. * to open/close index relations.
* ---------------- * ----------------
*/ */
Relation Relation
index_openr(char *relationName) index_openr(char *relationName)
{ {
return RelationNameGetRelation(relationName); return RelationNameGetRelation(relationName);
} }
/* ---------------- /* ----------------
* index_close - close a index relation * index_close - close a index relation
* *
* presently the relcache routines do all the work we need * presently the relcache routines do all the work we need
* to open/close index relations. * to open/close index relations.
* ---------------- * ----------------
*/ */
void void
index_close(Relation relation) index_close(Relation relation)
{ {
RelationClose(relation); RelationClose(relation);
} }
/* ---------------- /* ----------------
* index_insert - insert an index tuple into a relation * index_insert - insert an index tuple into a relation
* ---------------- * ----------------
*/ */
InsertIndexResult InsertIndexResult
index_insert(Relation relation, index_insert(Relation relation,
Datum *datum, Datum * datum,
char *nulls, char *nulls,
ItemPointer heap_t_ctid, ItemPointer heap_t_ctid,
Relation heapRel) Relation heapRel)
{ {
RegProcedure procedure; RegProcedure procedure;
InsertIndexResult specificResult; InsertIndexResult specificResult;
RELATION_CHECKS; RELATION_CHECKS;
GET_REL_PROCEDURE(insert,aminsert); GET_REL_PROCEDURE(insert, aminsert);
/* ---------------- /* ----------------
* have the am's insert proc do all the work. * have the am's insert proc do all the work.
* ---------------- * ----------------
*/ */
specificResult = (InsertIndexResult) specificResult = (InsertIndexResult)
fmgr(procedure, relation, datum, nulls, heap_t_ctid, heapRel, NULL); fmgr(procedure, relation, datum, nulls, heap_t_ctid, heapRel, NULL);
/* ---------------- /* ----------------
* the insert proc is supposed to return a "specific result" and * the insert proc is supposed to return a "specific result" and
* this routine has to return a "general result" so after we get * this routine has to return a "general result" so after we get
* something back from the insert proc, we allocate a * something back from the insert proc, we allocate a
* "general result" and copy some crap between the two. * "general result" and copy some crap between the two.
* *
* As far as I'm concerned all this result shit is needlessly c * As far as I'm concerned all this result shit is needlessly c
* omplicated and should be eliminated. -cim 1/19/91 * omplicated and should be eliminated. -cim 1/19/91
* *
* mao concurs. regardless of how we feel here, however, it is * mao concurs. regardless of how we feel here, however, it is
* important to free memory we don't intend to return to anyone. * important to free memory we don't intend to return to anyone.
* 2/28/91 * 2/28/91
* *
* this "general result" crap is now gone. -ay 3/6/95 * this "general result" crap is now gone. -ay 3/6/95
* ---------------- * ----------------
*/ */
return (specificResult); return (specificResult);
} }
/* ---------------- /* ----------------
* index_delete - delete an item from an index relation * index_delete - delete an item from an index relation
* ---------------- * ----------------
*/ */
void void
index_delete(Relation relation, ItemPointer indexItem) index_delete(Relation relation, ItemPointer indexItem)
{ {
RegProcedure procedure; RegProcedure procedure;
RELATION_CHECKS; RELATION_CHECKS;
GET_REL_PROCEDURE(delete,amdelete); GET_REL_PROCEDURE(delete, amdelete);
fmgr(procedure, relation, indexItem); fmgr(procedure, relation, indexItem);
} }
/* ---------------- /* ----------------
* index_beginscan - start a scan of an index * index_beginscan - start a scan of an index
* ---------------- * ----------------
*/ */
IndexScanDesc IndexScanDesc
index_beginscan(Relation relation, index_beginscan(Relation relation,
bool scanFromEnd, bool scanFromEnd,
uint16 numberOfKeys, uint16 numberOfKeys,
ScanKey key) ScanKey key)
{ {
IndexScanDesc scandesc; IndexScanDesc scandesc;
RegProcedure procedure; RegProcedure procedure;
RELATION_CHECKS; RELATION_CHECKS;
GET_REL_PROCEDURE(beginscan,ambeginscan); GET_REL_PROCEDURE(beginscan, ambeginscan);
RelationSetRIntentLock(relation); RelationSetRIntentLock(relation);
scandesc = (IndexScanDesc) scandesc = (IndexScanDesc)
fmgr(procedure, relation, scanFromEnd, numberOfKeys, key); fmgr(procedure, relation, scanFromEnd, numberOfKeys, key);
return scandesc; return scandesc;
} }
/* ---------------- /* ----------------
* index_rescan - restart a scan of an index * index_rescan - restart a scan of an index
* ---------------- * ----------------
*/ */
void void
index_rescan(IndexScanDesc scan, bool scanFromEnd, ScanKey key) index_rescan(IndexScanDesc scan, bool scanFromEnd, ScanKey key)
{ {
RegProcedure procedure; RegProcedure procedure;
SCAN_CHECKS; SCAN_CHECKS;
GET_SCAN_PROCEDURE(rescan,amrescan); GET_SCAN_PROCEDURE(rescan, amrescan);
fmgr(procedure, scan, scanFromEnd, key); fmgr(procedure, scan, scanFromEnd, key);
} }
/* ---------------- /* ----------------
* index_endscan - end a scan * index_endscan - end a scan
* ---------------- * ----------------
*/ */
void void
index_endscan(IndexScanDesc scan) index_endscan(IndexScanDesc scan)
{ {
RegProcedure procedure; RegProcedure procedure;
SCAN_CHECKS; SCAN_CHECKS;
GET_SCAN_PROCEDURE(endscan,amendscan); GET_SCAN_PROCEDURE(endscan, amendscan);
fmgr(procedure, scan); fmgr(procedure, scan);
RelationUnsetRIntentLock(scan->relation); RelationUnsetRIntentLock(scan->relation);
} }
#ifdef NOT_USED #ifdef NOT_USED
/* ---------------- /* ----------------
* index_markpos - mark a scan position * index_markpos - mark a scan position
* ---------------- * ----------------
*/ */
void void
index_markpos(IndexScanDesc scan) index_markpos(IndexScanDesc scan)
{ {
RegProcedure procedure; RegProcedure procedure;
SCAN_CHECKS; SCAN_CHECKS;
GET_SCAN_PROCEDURE(markpos,ammarkpos); GET_SCAN_PROCEDURE(markpos, ammarkpos);
fmgr(procedure, scan); fmgr(procedure, scan);
} }
#endif #endif
#ifdef NOT_USED #ifdef NOT_USED
/* ---------------- /* ----------------
* index_restrpos - restore a scan position * index_restrpos - restore a scan position
* ---------------- * ----------------
*/ */
void void
index_restrpos(IndexScanDesc scan) index_restrpos(IndexScanDesc scan)
{ {
RegProcedure procedure; RegProcedure procedure;
SCAN_CHECKS; SCAN_CHECKS;
GET_SCAN_PROCEDURE(restrpos,amrestrpos); GET_SCAN_PROCEDURE(restrpos, amrestrpos);
fmgr(procedure, scan); fmgr(procedure, scan);
} }
#endif #endif
/* ---------------- /* ----------------
* index_getnext - get the next tuple from a scan * index_getnext - get the next tuple from a scan
* *
* A RetrieveIndexResult is a index tuple/heap tuple pair * A RetrieveIndexResult is a index tuple/heap tuple pair
* ---------------- * ----------------
*/ */
RetrieveIndexResult RetrieveIndexResult
index_getnext(IndexScanDesc scan, index_getnext(IndexScanDesc scan,
ScanDirection direction) ScanDirection direction)
{ {
RegProcedure procedure; RegProcedure procedure;
RetrieveIndexResult result; RetrieveIndexResult result;
SCAN_CHECKS; SCAN_CHECKS;
GET_SCAN_PROCEDURE(getnext,amgettuple); GET_SCAN_PROCEDURE(getnext, amgettuple);
/* ---------------- /* ----------------
* have the am's gettuple proc do all the work. * have the am's gettuple proc do all the work.
* ---------------- * ----------------
*/ */
result = (RetrieveIndexResult) result = (RetrieveIndexResult)
fmgr(procedure, scan, direction); fmgr(procedure, scan, direction);
return result; return result;
} }
/* ---------------- /* ----------------
* index_getprocid * index_getprocid
* *
* Some indexed access methods may require support routines that are * Some indexed access methods may require support routines that are
* not in the operator class/operator model imposed by pg_am. These * not in the operator class/operator model imposed by pg_am. These
* access methods may store the OIDs of registered procedures they * access methods may store the OIDs of registered procedures they
* need in pg_amproc. These registered procedure OIDs are ordered in * need in pg_amproc. These registered procedure OIDs are ordered in
* a way that makes sense to the access method, and used only by the * a way that makes sense to the access method, and used only by the
* access method. The general index code doesn't know anything about * access method. The general index code doesn't know anything about
* the routines involved; it just builds an ordered list of them for * the routines involved; it just builds an ordered list of them for
* each attribute on which an index is defined. * each attribute on which an index is defined.
* *
* This routine returns the requested procedure OID for a particular * This routine returns the requested procedure OID for a particular
* indexed attribute. * indexed attribute.
* ---------------- * ----------------
*/ */
RegProcedure RegProcedure
index_getprocid(Relation irel, index_getprocid(Relation irel,
AttrNumber attnum, AttrNumber attnum,
uint16 procnum) uint16 procnum)
{ {
RegProcedure *loc; RegProcedure *loc;
int natts; int natts;
natts = irel->rd_rel->relnatts; natts = irel->rd_rel->relnatts;
loc = irel->rd_support; loc = irel->rd_support;
Assert(loc != NULL); Assert(loc != NULL);
return (loc[(natts * (procnum - 1)) + (attnum - 1)]); return (loc[(natts * (procnum - 1)) + (attnum - 1)]);
} }
Datum Datum
GetIndexValue(HeapTuple tuple, GetIndexValue(HeapTuple tuple,
TupleDesc hTupDesc, TupleDesc hTupDesc,
int attOff, int attOff,
AttrNumber attrNums[], AttrNumber attrNums[],
FuncIndexInfo *fInfo, FuncIndexInfo * fInfo,
bool *attNull, bool * attNull,
Buffer buffer) Buffer buffer)
{ {
Datum returnVal; Datum returnVal;
bool isNull; bool isNull;
if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid) { if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid)
int i; {
Datum *attData = (Datum *)palloc(FIgetnArgs(fInfo)*sizeof(Datum)); int i;
Datum *attData = (Datum *) palloc(FIgetnArgs(fInfo) * sizeof(Datum));
for (i = 0; i < FIgetnArgs(fInfo); i++) { for (i = 0; i < FIgetnArgs(fInfo); i++)
attData[i] = (Datum) heap_getattr(tuple, {
buffer, attData[i] = (Datum) heap_getattr(tuple,
attrNums[i], buffer,
hTupDesc, attrNums[i],
attNull); hTupDesc,
attNull);
}
returnVal = (Datum) fmgr_array_args(FIgetProcOid(fInfo),
FIgetnArgs(fInfo),
(char **) attData,
&isNull);
pfree(attData);
*attNull = FALSE;
} }
returnVal = (Datum)fmgr_array_args(FIgetProcOid(fInfo), else
FIgetnArgs(fInfo), {
(char **) attData, returnVal = (Datum) heap_getattr(tuple, buffer, attrNums[attOff],
&isNull); hTupDesc, attNull);
pfree(attData); }
*attNull = FALSE; return returnVal;
}else {
returnVal = (Datum) heap_getattr(tuple, buffer, attrNums[attOff],
hTupDesc, attNull);
}
return returnVal;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,22 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* nbtcompare.c-- * nbtcompare.c--
* Comparison functions for btree access method. * Comparison functions for btree access method.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.10 1997/06/11 05:20:05 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.11 1997/09/07 04:38:39 momjian Exp $
* *
* NOTES * NOTES
* These functions are stored in pg_amproc. For each operator class * These functions are stored in pg_amproc. For each operator class
* defined on btrees, they compute * defined on btrees, they compute
* *
* compare(a, b): * compare(a, b):
* < 0 if a < b, * < 0 if a < b,
* = 0 if a == b, * = 0 if a == b,
* > 0 if a > b. * > 0 if a > b.
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -30,168 +30,171 @@
int32 int32
btint2cmp(int16 a, int16 b) btint2cmp(int16 a, int16 b)
{ {
return ((int32) (a - b)); return ((int32) (a - b));
} }
int32 int32
btint4cmp(int32 a, int32 b) btint4cmp(int32 a, int32 b)
{ {
return (a - b); return (a - b);
} }
int32 int32
btint24cmp(int16 a, int32 b) btint24cmp(int16 a, int32 b)
{ {
return (((int32) a) - b); return (((int32) a) - b);
} }
int32 int32
btint42cmp(int32 a, int16 b) btint42cmp(int32 a, int16 b)
{ {
return (a - ((int32) b)); return (a - ((int32) b));
} }
int32 int32
btfloat4cmp(float32 a, float32 b) btfloat4cmp(float32 a, float32 b)
{ {
if (*a > *b) if (*a > *b)
return (1); return (1);
else if (*a == *b) else if (*a == *b)
return (0); return (0);
else else
return (-1); return (-1);
} }
int32 int32
btfloat8cmp(float64 a, float64 b) btfloat8cmp(float64 a, float64 b)
{ {
if (*a > *b) if (*a > *b)
return (1); return (1);
else if (*a == *b) else if (*a == *b)
return (0); return (0);
else else
return (-1); return (-1);
} }
int32 int32
btoidcmp(Oid a, Oid b) btoidcmp(Oid a, Oid b)
{ {
if (a > b) if (a > b)
return (1); return (1);
else if (a == b) else if (a == b)
return (0); return (0);
else else
return (-1); return (-1);
} }
int32 int32
btabstimecmp(AbsoluteTime a, AbsoluteTime b) btabstimecmp(AbsoluteTime a, AbsoluteTime b)
{ {
if (AbsoluteTimeIsBefore(a, b)) if (AbsoluteTimeIsBefore(a, b))
return (-1); return (-1);
else if (AbsoluteTimeIsBefore(b, a)) else if (AbsoluteTimeIsBefore(b, a))
return (1); return (1);
else else
return (0); return (0);
} }
int32 int32
btcharcmp(char a, char b) btcharcmp(char a, char b)
{ {
return ((int32) ((uint8)a - (uint8)b)); return ((int32) ((uint8) a - (uint8) b));
} }
int32 int32
btchar2cmp(uint16 a, uint16 b) btchar2cmp(uint16 a, uint16 b)
{ {
return (strncmp((char *) &a, (char *) &b, 2)); return (strncmp((char *) &a, (char *) &b, 2));
} }
int32 int32
btchar4cmp(uint32 a, uint32 b) btchar4cmp(uint32 a, uint32 b)
{ {
return (strncmp((char *) &a, (char *) &b, 4)); return (strncmp((char *) &a, (char *) &b, 4));
} }
int32 int32
btchar8cmp(char *a, char *b) btchar8cmp(char *a, char *b)
{ {
return (strncmp(a, b, 8)); return (strncmp(a, b, 8));
} }
int32 int32
btchar16cmp(char *a, char *b) btchar16cmp(char *a, char *b)
{ {
return (strncmp(a, b, 16)); return (strncmp(a, b, 16));
} }
int32 int32
btnamecmp(NameData *a, NameData *b) btnamecmp(NameData * a, NameData * b)
{ {
return (strncmp(a->data, b->data, NAMEDATALEN)); return (strncmp(a->data, b->data, NAMEDATALEN));
} }
int32 int32
bttextcmp(struct varlena *a, struct varlena *b) bttextcmp(struct varlena * a, struct varlena * b)
{ {
int res; int res;
unsigned char *ap, *bp; unsigned char *ap,
*bp;
#ifdef USE_LOCALE #ifdef USE_LOCALE
int la = VARSIZE(a) - VARHDRSZ; int la = VARSIZE(a) - VARHDRSZ;
int lb = VARSIZE(b) - VARHDRSZ; int lb = VARSIZE(b) - VARHDRSZ;
ap = (unsigned char *) palloc (la + 1); ap = (unsigned char *) palloc(la + 1);
bp = (unsigned char *) palloc (lb + 1); bp = (unsigned char *) palloc(lb + 1);
memcpy(ap, VARDATA(a), la); memcpy(ap, VARDATA(a), la);
*(ap + la) = '\0'; *(ap + la) = '\0';
memcpy(bp, VARDATA(b), lb); memcpy(bp, VARDATA(b), lb);
*(bp + lb) = '\0'; *(bp + lb) = '\0';
res = strcoll (ap, bp); res = strcoll(ap, bp);
pfree (ap); pfree(ap);
pfree (bp); pfree(bp);
#else #else
int len = VARSIZE(a); int len = VARSIZE(a);
/* len is the length of the shorter of the two strings */ /* len is the length of the shorter of the two strings */
if ( len > VARSIZE(b) ) if (len > VARSIZE(b))
len = VARSIZE(b); len = VARSIZE(b);
len -= VARHDRSZ; len -= VARHDRSZ;
ap = (unsigned char *) VARDATA(a); ap = (unsigned char *) VARDATA(a);
bp = (unsigned char *) VARDATA(b); bp = (unsigned char *) VARDATA(b);
/* /*
* If the two strings differ in the first len bytes, or if they're * If the two strings differ in the first len bytes, or if they're the
* the same in the first len bytes and they're both len bytes long, * same in the first len bytes and they're both len bytes long, we're
* we're done. * done.
*/ */
res = 0; res = 0;
if (len > 0) { if (len > 0)
do { {
res = (int) (*ap++ - *bp++); do
len--; {
} while (res == 0 && len != 0); res = (int) (*ap++ - *bp++);
} len--;
} while (res == 0 && len != 0);
}
#endif #endif
if (res != 0 || VARSIZE(a) == VARSIZE(b)) if (res != 0 || VARSIZE(a) == VARSIZE(b))
return (res); return (res);
/* /*
* The two strings are the same in the first len bytes, and they * The two strings are the same in the first len bytes, and they are
* are of different lengths. * of different lengths.
*/ */
if (VARSIZE(a) < VARSIZE(b)) if (VARSIZE(a) < VARSIZE(b))
return (-1); return (-1);
else else
return (1); return (1);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,28 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* btscan.c-- * btscan.c--
* manage scans on btrees. * manage scans on btrees.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtscan.c,v 1.7 1997/02/18 17:13:45 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtscan.c,v 1.8 1997/09/07 04:38:57 momjian Exp $
* *
* *
* NOTES * NOTES
* Because we can be doing an index scan on a relation while we update * Because we can be doing an index scan on a relation while we update
* it, we need to avoid missing data that moves around in the index. * it, we need to avoid missing data that moves around in the index.
* The routines and global variables in this file guarantee that all * The routines and global variables in this file guarantee that all
* scans in the local address space stay correctly positioned. This * scans in the local address space stay correctly positioned. This
* is all we need to worry about, since write locking guarantees that * is all we need to worry about, since write locking guarantees that
* no one else will be on the same page at the same time as we are. * no one else will be on the same page at the same time as we are.
* *
* The scheme is to manage a list of active scans in the current backend. * The scheme is to manage a list of active scans in the current backend.
* Whenever we add or remove records from an index, or whenever we * Whenever we add or remove records from an index, or whenever we
* split a leaf page, we check the list of active scans to see if any * split a leaf page, we check the list of active scans to see if any
* has been affected. A scan is affected only if it is on the same * has been affected. A scan is affected only if it is on the same
* relation, and the same page, as the update. * relation, and the same page, as the update.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -32,83 +32,87 @@
#include <storage/bufpage.h> #include <storage/bufpage.h>
#include <access/nbtree.h> #include <access/nbtree.h>
typedef struct BTScanListData { typedef struct BTScanListData
IndexScanDesc btsl_scan; {
struct BTScanListData *btsl_next; IndexScanDesc btsl_scan;
} BTScanListData; struct BTScanListData *btsl_next;
} BTScanListData;
typedef BTScanListData *BTScanList; typedef BTScanListData *BTScanList;
static BTScanList BTScans = (BTScanList) NULL; static BTScanList BTScans = (BTScanList) NULL;
static void _bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno); static void _bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno);
static bool _bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno); static bool _bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
/* /*
* _bt_regscan() -- register a new scan. * _bt_regscan() -- register a new scan.
*/ */
void void
_bt_regscan(IndexScanDesc scan) _bt_regscan(IndexScanDesc scan)
{ {
BTScanList new_el; BTScanList new_el;
new_el = (BTScanList) palloc(sizeof(BTScanListData)); new_el = (BTScanList) palloc(sizeof(BTScanListData));
new_el->btsl_scan = scan; new_el->btsl_scan = scan;
new_el->btsl_next = BTScans; new_el->btsl_next = BTScans;
BTScans = new_el; BTScans = new_el;
} }
/* /*
* _bt_dropscan() -- drop a scan from the scan list * _bt_dropscan() -- drop a scan from the scan list
*/ */
void void
_bt_dropscan(IndexScanDesc scan) _bt_dropscan(IndexScanDesc scan)
{ {
BTScanList chk, last; BTScanList chk,
last;
last = (BTScanList) NULL; last = (BTScanList) NULL;
for (chk = BTScans; for (chk = BTScans;
chk != (BTScanList) NULL && chk->btsl_scan != scan; chk != (BTScanList) NULL && chk->btsl_scan != scan;
chk = chk->btsl_next) { chk = chk->btsl_next)
last = chk; {
} last = chk;
}
if (chk == (BTScanList) NULL) if (chk == (BTScanList) NULL)
elog(WARN, "btree scan list trashed; can't find 0x%lx", scan); elog(WARN, "btree scan list trashed; can't find 0x%lx", scan);
if (last == (BTScanList) NULL) if (last == (BTScanList) NULL)
BTScans = chk->btsl_next; BTScans = chk->btsl_next;
else else
last->btsl_next = chk->btsl_next; last->btsl_next = chk->btsl_next;
pfree (chk); pfree(chk);
} }
/* /*
* _bt_adjscans() -- adjust all scans in the scan list to compensate * _bt_adjscans() -- adjust all scans in the scan list to compensate
* for a given deletion or insertion * for a given deletion or insertion
*/ */
void void
_bt_adjscans(Relation rel, ItemPointer tid, int op) _bt_adjscans(Relation rel, ItemPointer tid, int op)
{ {
BTScanList l; BTScanList l;
Oid relid; Oid relid;
relid = rel->rd_id; relid = rel->rd_id;
for (l = BTScans; l != (BTScanList) NULL; l = l->btsl_next) { for (l = BTScans; l != (BTScanList) NULL; l = l->btsl_next)
if (relid == l->btsl_scan->relation->rd_id) {
_bt_scandel(l->btsl_scan, op, if (relid == l->btsl_scan->relation->rd_id)
ItemPointerGetBlockNumber(tid), _bt_scandel(l->btsl_scan, op,
ItemPointerGetOffsetNumber(tid)); ItemPointerGetBlockNumber(tid),
} ItemPointerGetOffsetNumber(tid));
}
} }
/* /*
* _bt_scandel() -- adjust a single scan * _bt_scandel() -- adjust a single scan
* *
* because each index page is always maintained as an ordered array of * because each index page is always maintained as an ordered array of
* index tuples, the index tuples on a given page shift beneath any * index tuples, the index tuples on a given page shift beneath any
* given scan. an index modification "behind" a scan position (i.e., * given scan. an index modification "behind" a scan position (i.e.,
* same page, lower or equal offset number) will therefore force us to * same page, lower or equal offset number) will therefore force us to
* adjust the scan in the following ways: * adjust the scan in the following ways:
* *
@ -126,80 +130,85 @@ _bt_adjscans(Relation rel, ItemPointer tid, int op)
static void static void
_bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno) _bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno)
{ {
ItemPointer current; ItemPointer current;
Buffer buf; Buffer buf;
BTScanOpaque so; BTScanOpaque so;
if (!_bt_scantouched(scan, blkno, offno)) if (!_bt_scantouched(scan, blkno, offno))
return; return;
so = (BTScanOpaque) scan->opaque; so = (BTScanOpaque) scan->opaque;
buf = so->btso_curbuf; buf = so->btso_curbuf;
current = &(scan->currentItemData); current = &(scan->currentItemData);
if (ItemPointerIsValid(current) if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) { && ItemPointerGetOffsetNumber(current) >= offno)
switch (op) { {
case BT_INSERT: switch (op)
_bt_step(scan, &buf, ForwardScanDirection); {
break; case BT_INSERT:
case BT_DELETE: _bt_step(scan, &buf, ForwardScanDirection);
_bt_step(scan, &buf, BackwardScanDirection); break;
break; case BT_DELETE:
default: _bt_step(scan, &buf, BackwardScanDirection);
elog(WARN, "_bt_scandel: bad operation '%d'", op); break;
/*NOTREACHED*/ default:
elog(WARN, "_bt_scandel: bad operation '%d'", op);
/* NOTREACHED */
}
so->btso_curbuf = buf;
} }
so->btso_curbuf = buf;
}
current = &(scan->currentMarkData); current = &(scan->currentMarkData);
if (ItemPointerIsValid(current) if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) { && ItemPointerGetOffsetNumber(current) >= offno)
ItemPointerData tmp; {
tmp = *current; ItemPointerData tmp;
*current = scan->currentItemData;
scan->currentItemData = tmp; tmp = *current;
switch (op) { *current = scan->currentItemData;
case BT_INSERT: scan->currentItemData = tmp;
_bt_step(scan, &buf, ForwardScanDirection); switch (op)
break; {
case BT_DELETE: case BT_INSERT:
_bt_step(scan, &buf, BackwardScanDirection); _bt_step(scan, &buf, ForwardScanDirection);
break; break;
default: case BT_DELETE:
elog(WARN, "_bt_scandel: bad operation '%d'", op); _bt_step(scan, &buf, BackwardScanDirection);
/*NOTREACHED*/ break;
default:
elog(WARN, "_bt_scandel: bad operation '%d'", op);
/* NOTREACHED */
}
so->btso_mrkbuf = buf;
tmp = *current;
*current = scan->currentItemData;
scan->currentItemData = tmp;
} }
so->btso_mrkbuf = buf;
tmp = *current;
*current = scan->currentItemData;
scan->currentItemData = tmp;
}
} }
/* /*
* _bt_scantouched() -- check to see if a scan is affected by a given * _bt_scantouched() -- check to see if a scan is affected by a given
* change to the index * change to the index
*/ */
static bool static bool
_bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno) _bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
{ {
ItemPointer current; ItemPointer current;
current = &(scan->currentItemData); current = &(scan->currentItemData);
if (ItemPointerIsValid(current) if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) && ItemPointerGetOffsetNumber(current) >= offno)
return (true); return (true);
current = &(scan->currentMarkData); current = &(scan->currentMarkData);
if (ItemPointerIsValid(current) if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno && ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) && ItemPointerGetOffsetNumber(current) >= offno)
return (true); return (true);
return (false); return (false);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* btstrat.c-- * btstrat.c--
* Srategy map entries for the btree indexed access method * Srategy map entries for the btree indexed access method
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtstrat.c,v 1.4 1996/11/05 10:35:37 scrappy Exp $ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtstrat.c,v 1.5 1997/09/07 04:39:04 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -20,111 +20,111 @@
/* /*
* Note: * Note:
* StrategyNegate, StrategyCommute, and StrategyNegateCommute * StrategyNegate, StrategyCommute, and StrategyNegateCommute
* assume <, <=, ==, >=, > ordering. * assume <, <=, ==, >=, > ordering.
*/ */
static StrategyNumber BTNegate[5] = { static StrategyNumber BTNegate[5] = {
BTGreaterEqualStrategyNumber, BTGreaterEqualStrategyNumber,
BTGreaterStrategyNumber, BTGreaterStrategyNumber,
InvalidStrategy, InvalidStrategy,
BTLessStrategyNumber, BTLessStrategyNumber,
BTLessEqualStrategyNumber BTLessEqualStrategyNumber
}; };
static StrategyNumber BTCommute[5] = { static StrategyNumber BTCommute[5] = {
BTGreaterStrategyNumber, BTGreaterStrategyNumber,
BTGreaterEqualStrategyNumber, BTGreaterEqualStrategyNumber,
InvalidStrategy, InvalidStrategy,
BTLessEqualStrategyNumber, BTLessEqualStrategyNumber,
BTLessStrategyNumber BTLessStrategyNumber
}; };
static StrategyNumber BTNegateCommute[5] = { static StrategyNumber BTNegateCommute[5] = {
BTLessEqualStrategyNumber, BTLessEqualStrategyNumber,
BTLessStrategyNumber, BTLessStrategyNumber,
InvalidStrategy, InvalidStrategy,
BTGreaterStrategyNumber, BTGreaterStrategyNumber,
BTGreaterEqualStrategyNumber BTGreaterEqualStrategyNumber
}; };
static uint16 BTLessTermData[] = { /* XXX type clash */ static uint16 BTLessTermData[] = { /* XXX type clash */
2, 2,
BTLessStrategyNumber, BTLessStrategyNumber,
SK_NEGATE, SK_NEGATE,
BTLessStrategyNumber, BTLessStrategyNumber,
SK_NEGATE | SK_COMMUTE SK_NEGATE | SK_COMMUTE
}; };
static uint16 BTLessEqualTermData[] = { /* XXX type clash */ static uint16 BTLessEqualTermData[] = { /* XXX type clash */
2, 2,
BTLessEqualStrategyNumber, BTLessEqualStrategyNumber,
0x0, 0x0,
BTLessEqualStrategyNumber, BTLessEqualStrategyNumber,
SK_COMMUTE SK_COMMUTE
}; };
static uint16 BTGreaterEqualTermData[] = { /* XXX type clash */ static uint16 BTGreaterEqualTermData[] = { /* XXX type clash */
2, 2,
BTGreaterEqualStrategyNumber, BTGreaterEqualStrategyNumber,
0x0, 0x0,
BTGreaterEqualStrategyNumber, BTGreaterEqualStrategyNumber,
SK_COMMUTE SK_COMMUTE
};
static uint16 BTGreaterTermData[] = { /* XXX type clash */
2,
BTGreaterStrategyNumber,
SK_NEGATE,
BTGreaterStrategyNumber,
SK_NEGATE | SK_COMMUTE
}; };
static StrategyTerm BTEqualExpressionData[] = { static uint16 BTGreaterTermData[] = { /* XXX type clash */
(StrategyTerm)BTLessTermData, /* XXX */ 2,
(StrategyTerm)BTLessEqualTermData, /* XXX */ BTGreaterStrategyNumber,
(StrategyTerm)BTGreaterEqualTermData, /* XXX */ SK_NEGATE,
(StrategyTerm)BTGreaterTermData, /* XXX */ BTGreaterStrategyNumber,
NULL SK_NEGATE | SK_COMMUTE
}; };
static StrategyEvaluationData BTEvaluationData = { static StrategyTerm BTEqualExpressionData[] = {
/* XXX static for simplicity */ (StrategyTerm) BTLessTermData, /* XXX */
(StrategyTerm) BTLessEqualTermData, /* XXX */
(StrategyTerm) BTGreaterEqualTermData, /* XXX */
(StrategyTerm) BTGreaterTermData, /* XXX */
NULL
};
BTMaxStrategyNumber, static StrategyEvaluationData BTEvaluationData = {
(StrategyTransformMap)BTNegate, /* XXX */ /* XXX static for simplicity */
(StrategyTransformMap)BTCommute, /* XXX */
(StrategyTransformMap)BTNegateCommute, /* XXX */
{ NULL, NULL, (StrategyExpression)BTEqualExpressionData, NULL, NULL, BTMaxStrategyNumber,
NULL,NULL,NULL,NULL,NULL,NULL,NULL} (StrategyTransformMap) BTNegate, /* XXX */
(StrategyTransformMap) BTCommute, /* XXX */
(StrategyTransformMap) BTNegateCommute, /* XXX */
{NULL, NULL, (StrategyExpression) BTEqualExpressionData, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL}
}; };
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* RelationGetBTStrategy * RelationGetBTStrategy
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
StrategyNumber StrategyNumber
_bt_getstrat(Relation rel, _bt_getstrat(Relation rel,
AttrNumber attno, AttrNumber attno,
RegProcedure proc) RegProcedure proc)
{ {
StrategyNumber strat; StrategyNumber strat;
strat = RelationGetStrategy(rel, attno, &BTEvaluationData, proc); strat = RelationGetStrategy(rel, attno, &BTEvaluationData, proc);
Assert(StrategyNumberIsValid(strat)); Assert(StrategyNumberIsValid(strat));
return (strat); return (strat);
} }
bool bool
_bt_invokestrat(Relation rel, _bt_invokestrat(Relation rel,
AttrNumber attno, AttrNumber attno,
StrategyNumber strat, StrategyNumber strat,
Datum left, Datum left,
Datum right) Datum right)
{ {
return (RelationInvokeStrategy(rel, &BTEvaluationData, attno, strat, return (RelationInvokeStrategy(rel, &BTEvaluationData, attno, strat,
left, right)); left, right));
} }

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* btutils.c-- * btutils.c--
* Utility code for Postgres btree implementation. * Utility code for Postgres btree implementation.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.11 1997/08/19 21:29:47 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.12 1997/09/07 04:39:05 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -23,367 +23,384 @@
#include <catalog/pg_proc.h> #include <catalog/pg_proc.h>
#include <executor/execdebug.h> #include <executor/execdebug.h>
extern int NIndexTupleProcessed; extern int NIndexTupleProcessed;
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
ScanKey ScanKey
_bt_mkscankey(Relation rel, IndexTuple itup) _bt_mkscankey(Relation rel, IndexTuple itup)
{ {
ScanKey skey; ScanKey skey;
TupleDesc itupdesc; TupleDesc itupdesc;
int natts; int natts;
int i; int i;
Datum arg; Datum arg;
RegProcedure proc; RegProcedure proc;
bool null; bool null;
bits16 flag; bits16 flag;
natts = rel->rd_rel->relnatts; natts = rel->rd_rel->relnatts;
itupdesc = RelationGetTupleDescriptor(rel); itupdesc = RelationGetTupleDescriptor(rel);
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData)); skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
for (i = 0; i < natts; i++) { for (i = 0; i < natts; i++)
arg = index_getattr(itup, i + 1, itupdesc, &null);
if ( null )
{ {
proc = NullValueRegProcedure; arg = index_getattr(itup, i + 1, itupdesc, &null);
flag = SK_ISNULL; if (null)
{
proc = NullValueRegProcedure;
flag = SK_ISNULL;
}
else
{
proc = index_getprocid(rel, i + 1, BTORDER_PROC);
flag = 0x0;
}
ScanKeyEntryInitialize(&skey[i],
flag, (AttrNumber) (i + 1), proc, arg);
} }
else
{
proc = index_getprocid(rel, i + 1, BTORDER_PROC);
flag = 0x0;
}
ScanKeyEntryInitialize(&skey[i],
flag, (AttrNumber) (i + 1), proc, arg);
}
return (skey); return (skey);
} }
void void
_bt_freeskey(ScanKey skey) _bt_freeskey(ScanKey skey)
{ {
pfree(skey); pfree(skey);
} }
void void
_bt_freestack(BTStack stack) _bt_freestack(BTStack stack)
{ {
BTStack ostack; BTStack ostack;
while (stack != (BTStack) NULL) { while (stack != (BTStack) NULL)
ostack = stack; {
stack = stack->bts_parent; ostack = stack;
pfree(ostack->bts_btitem); stack = stack->bts_parent;
pfree(ostack); pfree(ostack->bts_btitem);
} pfree(ostack);
}
} }
/* /*
* _bt_orderkeys() -- Put keys in a sensible order for conjunctive quals. * _bt_orderkeys() -- Put keys in a sensible order for conjunctive quals.
* *
* The order of the keys in the qual match the ordering imposed by * The order of the keys in the qual match the ordering imposed by
* the index. This routine only needs to be called if there are * the index. This routine only needs to be called if there are
* more than one qual clauses using this index. * more than one qual clauses using this index.
*/ */
void void
_bt_orderkeys(Relation relation, BTScanOpaque so) _bt_orderkeys(Relation relation, BTScanOpaque so)
{ {
ScanKey xform; ScanKey xform;
ScanKeyData *cur; ScanKeyData *cur;
StrategyMap map; StrategyMap map;
int nbytes; int nbytes;
long test; long test;
int i, j; int i,
int init[BTMaxStrategyNumber+1]; j;
ScanKey key; int init[BTMaxStrategyNumber + 1];
uint16 numberOfKeys = so->numberOfKeys; ScanKey key;
uint16 new_numberOfKeys = 0; uint16 numberOfKeys = so->numberOfKeys;
AttrNumber attno = 1; uint16 new_numberOfKeys = 0;
AttrNumber attno = 1;
if ( numberOfKeys < 1 ) if (numberOfKeys < 1)
return; return;
key = so->keyData; key = so->keyData;
cur = &key[0]; cur = &key[0];
if ( cur->sk_attno != 1 ) if (cur->sk_attno != 1)
elog (WARN, "_bt_orderkeys: key(s) for attribute 1 missed"); elog(WARN, "_bt_orderkeys: key(s) for attribute 1 missed");
if ( numberOfKeys == 1 ) if (numberOfKeys == 1)
{
/*
* We don't use indices for 'A is null' and 'A is not null'
* currently and 'A < = > <> NULL' is non-sense' - so
* qual is not Ok. - vadim 03/21/97
*/
if ( cur->sk_flags & SK_ISNULL )
so->qual_ok = 0;
so->numberOfFirstKeys = 1;
return;
}
/* get space for the modified array of keys */
nbytes = BTMaxStrategyNumber * sizeof(ScanKeyData);
xform = (ScanKey) palloc(nbytes);
memset(xform, 0, nbytes);
map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
BTMaxStrategyNumber,
attno);
for (j = 0; j <= BTMaxStrategyNumber; j++)
init[j] = 0;
/* check each key passed in */
for (i = 0; ; )
{
if ( i < numberOfKeys )
cur = &key[i];
if ( cur->sk_flags & SK_ISNULL ) /* see comments above */
so->qual_ok = 0;
if ( i == numberOfKeys || cur->sk_attno != attno )
{ {
if ( cur->sk_attno != attno + 1 && i < numberOfKeys )
{
elog (WARN, "_bt_orderkeys: key(s) for attribute %d missed", attno + 1);
}
/*
* If = has been specified, no other key will be used.
* In case of key < 2 && key == 1 and so on
* we have to set qual_ok to 0
*/
if (init[BTEqualStrategyNumber - 1])
{
ScanKeyData *eq, *chk;
eq = &xform[BTEqualStrategyNumber - 1];
for (j = BTMaxStrategyNumber; --j >= 0; )
{
if ( j == (BTEqualStrategyNumber - 1) || init[j] == 0 )
continue;
chk = &xform[j];
test = (long) fmgr(chk->sk_procedure, eq->sk_argument, chk->sk_argument);
if (!test)
so->qual_ok = 0;
}
init[BTLessStrategyNumber - 1] = 0;
init[BTLessEqualStrategyNumber - 1] = 0;
init[BTGreaterEqualStrategyNumber - 1] = 0;
init[BTGreaterStrategyNumber - 1] = 0;
}
/* only one of <, <= */
if (init[BTLessStrategyNumber - 1]
&& init[BTLessEqualStrategyNumber - 1])
{
ScanKeyData *lt, *le;
lt = &xform[BTLessStrategyNumber - 1];
le = &xform[BTLessEqualStrategyNumber - 1];
/* /*
* DO NOT use the cached function stuff here -- this is key * We don't use indices for 'A is null' and 'A is not null'
* ordering, happens only when the user expresses a hokey * currently and 'A < = > <> NULL' is non-sense' - so qual is not
* qualification, and gets executed only once, anyway. The * Ok. - vadim 03/21/97
* transform maps are hard-coded, and can't be initialized
* in the correct way.
*/ */
test = (long) fmgr(le->sk_procedure, lt->sk_argument, le->sk_argument); if (cur->sk_flags & SK_ISNULL)
if (test) so->qual_ok = 0;
init[BTLessEqualStrategyNumber - 1] = 0; so->numberOfFirstKeys = 1;
else return;
init[BTLessStrategyNumber - 1] = 0; }
}
/* only one of >, >= */ /* get space for the modified array of keys */
if (init[BTGreaterStrategyNumber - 1] nbytes = BTMaxStrategyNumber * sizeof(ScanKeyData);
&& init[BTGreaterEqualStrategyNumber - 1]) xform = (ScanKey) palloc(nbytes);
{
ScanKeyData *gt, *ge;
gt = &xform[BTGreaterStrategyNumber - 1]; memset(xform, 0, nbytes);
ge = &xform[BTGreaterEqualStrategyNumber - 1]; map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
BTMaxStrategyNumber,
/* see note above on function cache */ attno);
test = (long) fmgr(ge->sk_procedure, gt->sk_argument, ge->sk_argument); for (j = 0; j <= BTMaxStrategyNumber; j++)
if (test)
init[BTGreaterEqualStrategyNumber - 1] = 0;
else
init[BTGreaterStrategyNumber - 1] = 0;
}
/* okay, reorder and count */
for (j = BTMaxStrategyNumber; --j >= 0; )
if (init[j])
key[new_numberOfKeys++] = xform[j];
if ( attno == 1 )
so->numberOfFirstKeys = new_numberOfKeys;
if ( i == numberOfKeys )
break;
/* initialization for new attno */
attno = cur->sk_attno;
memset(xform, 0, nbytes);
map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
BTMaxStrategyNumber,
attno);
/* haven't looked at any strategies yet */
for (j = 0; j <= BTMaxStrategyNumber; j++)
init[j] = 0; init[j] = 0;
/* check each key passed in */
for (i = 0;;)
{
if (i < numberOfKeys)
cur = &key[i];
if (cur->sk_flags & SK_ISNULL) /* see comments above */
so->qual_ok = 0;
if (i == numberOfKeys || cur->sk_attno != attno)
{
if (cur->sk_attno != attno + 1 && i < numberOfKeys)
{
elog(WARN, "_bt_orderkeys: key(s) for attribute %d missed", attno + 1);
}
/*
* If = has been specified, no other key will be used. In case
* of key < 2 && key == 1 and so on we have to set qual_ok to
* 0
*/
if (init[BTEqualStrategyNumber - 1])
{
ScanKeyData *eq,
*chk;
eq = &xform[BTEqualStrategyNumber - 1];
for (j = BTMaxStrategyNumber; --j >= 0;)
{
if (j == (BTEqualStrategyNumber - 1) || init[j] == 0)
continue;
chk = &xform[j];
test = (long) fmgr(chk->sk_procedure, eq->sk_argument, chk->sk_argument);
if (!test)
so->qual_ok = 0;
}
init[BTLessStrategyNumber - 1] = 0;
init[BTLessEqualStrategyNumber - 1] = 0;
init[BTGreaterEqualStrategyNumber - 1] = 0;
init[BTGreaterStrategyNumber - 1] = 0;
}
/* only one of <, <= */
if (init[BTLessStrategyNumber - 1]
&& init[BTLessEqualStrategyNumber - 1])
{
ScanKeyData *lt,
*le;
lt = &xform[BTLessStrategyNumber - 1];
le = &xform[BTLessEqualStrategyNumber - 1];
/*
* DO NOT use the cached function stuff here -- this is
* key ordering, happens only when the user expresses a
* hokey qualification, and gets executed only once,
* anyway. The transform maps are hard-coded, and can't
* be initialized in the correct way.
*/
test = (long) fmgr(le->sk_procedure, lt->sk_argument, le->sk_argument);
if (test)
init[BTLessEqualStrategyNumber - 1] = 0;
else
init[BTLessStrategyNumber - 1] = 0;
}
/* only one of >, >= */
if (init[BTGreaterStrategyNumber - 1]
&& init[BTGreaterEqualStrategyNumber - 1])
{
ScanKeyData *gt,
*ge;
gt = &xform[BTGreaterStrategyNumber - 1];
ge = &xform[BTGreaterEqualStrategyNumber - 1];
/* see note above on function cache */
test = (long) fmgr(ge->sk_procedure, gt->sk_argument, ge->sk_argument);
if (test)
init[BTGreaterEqualStrategyNumber - 1] = 0;
else
init[BTGreaterStrategyNumber - 1] = 0;
}
/* okay, reorder and count */
for (j = BTMaxStrategyNumber; --j >= 0;)
if (init[j])
key[new_numberOfKeys++] = xform[j];
if (attno == 1)
so->numberOfFirstKeys = new_numberOfKeys;
if (i == numberOfKeys)
break;
/* initialization for new attno */
attno = cur->sk_attno;
memset(xform, 0, nbytes);
map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
BTMaxStrategyNumber,
attno);
/* haven't looked at any strategies yet */
for (j = 0; j <= BTMaxStrategyNumber; j++)
init[j] = 0;
}
for (j = BTMaxStrategyNumber; --j >= 0;)
{
if (cur->sk_procedure == map->entry[j].sk_procedure)
break;
}
/* have we seen one of these before? */
if (init[j])
{
/* yup, use the appropriate value */
test =
(long) FMGR_PTR2(cur->sk_func, cur->sk_procedure,
cur->sk_argument, xform[j].sk_argument);
if (test)
xform[j].sk_argument = cur->sk_argument;
else if (j == (BTEqualStrategyNumber - 1))
so->qual_ok = 0;/* key == a && key == b, but a != b */
}
else
{
/* nope, use this value */
memmove(&xform[j], cur, sizeof(*cur));
init[j] = 1;
}
i++;
} }
for (j = BTMaxStrategyNumber; --j >= 0; ) so->numberOfKeys = new_numberOfKeys;
{
if (cur->sk_procedure == map->entry[j].sk_procedure)
break;
}
/* have we seen one of these before? */ pfree(xform);
if (init[j])
{
/* yup, use the appropriate value */
test =
(long) FMGR_PTR2(cur->sk_func, cur->sk_procedure,
cur->sk_argument, xform[j].sk_argument);
if (test)
xform[j].sk_argument = cur->sk_argument;
else if ( j == (BTEqualStrategyNumber - 1) )
so->qual_ok = 0; /* key == a && key == b, but a != b */
} else
{
/* nope, use this value */
memmove(&xform[j], cur, sizeof(*cur));
init[j] = 1;
}
i++;
}
so->numberOfKeys = new_numberOfKeys;
pfree(xform);
} }
BTItem BTItem
_bt_formitem(IndexTuple itup) _bt_formitem(IndexTuple itup)
{ {
int nbytes_btitem; int nbytes_btitem;
BTItem btitem; BTItem btitem;
Size tuplen; Size tuplen;
extern Oid newoid(); extern Oid newoid();
/* see comments in btbuild /*
* see comments in btbuild
*
* if (itup->t_info & INDEX_NULL_MASK) elog(WARN, "btree indices cannot
* include null keys");
*/
if (itup->t_info & INDEX_NULL_MASK) /* make a copy of the index tuple with room for the sequence number */
elog(WARN, "btree indices cannot include null keys"); tuplen = IndexTupleSize(itup);
*/ nbytes_btitem = tuplen +
(sizeof(BTItemData) - sizeof(IndexTupleData));
/* make a copy of the index tuple with room for the sequence number */ btitem = (BTItem) palloc(nbytes_btitem);
tuplen = IndexTupleSize(itup); memmove((char *) &(btitem->bti_itup), (char *) itup, tuplen);
nbytes_btitem = tuplen +
(sizeof(BTItemData) - sizeof(IndexTupleData));
btitem = (BTItem) palloc(nbytes_btitem);
memmove((char *) &(btitem->bti_itup), (char *) itup, tuplen);
#ifndef BTREE_VERSION_1 #ifndef BTREE_VERSION_1
btitem->bti_oid = newoid(); btitem->bti_oid = newoid();
#endif #endif
return (btitem); return (btitem);
} }
#ifdef NOT_USED #ifdef NOT_USED
bool bool
_bt_checkqual(IndexScanDesc scan, IndexTuple itup) _bt_checkqual(IndexScanDesc scan, IndexTuple itup)
{ {
BTScanOpaque so; BTScanOpaque so;
so = (BTScanOpaque) scan->opaque; so = (BTScanOpaque) scan->opaque;
if (so->numberOfKeys > 0) if (so->numberOfKeys > 0)
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation), return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
so->numberOfKeys, so->keyData)); so->numberOfKeys, so->keyData));
else else
return (true); return (true);
} }
#endif #endif
#ifdef NOT_USED #ifdef NOT_USED
bool bool
_bt_checkforkeys(IndexScanDesc scan, IndexTuple itup, Size keysz) _bt_checkforkeys(IndexScanDesc scan, IndexTuple itup, Size keysz)
{ {
BTScanOpaque so; BTScanOpaque so;
so = (BTScanOpaque) scan->opaque; so = (BTScanOpaque) scan->opaque;
if ( keysz > 0 && so->numberOfKeys >= keysz ) if (keysz > 0 && so->numberOfKeys >= keysz)
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation), return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
keysz, so->keyData)); keysz, so->keyData));
else else
return (true); return (true);
} }
#endif #endif
bool bool
_bt_checkkeys (IndexScanDesc scan, IndexTuple tuple, Size *keysok) _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, Size * keysok)
{ {
BTScanOpaque so = (BTScanOpaque) scan->opaque; BTScanOpaque so = (BTScanOpaque) scan->opaque;
Size keysz = so->numberOfKeys; Size keysz = so->numberOfKeys;
TupleDesc tupdesc; TupleDesc tupdesc;
ScanKey key; ScanKey key;
Datum datum; Datum datum;
bool isNull; bool isNull;
int test; int test;
*keysok = 0; *keysok = 0;
if ( keysz == 0 ) if (keysz == 0)
return (true); return (true);
key = so->keyData; key = so->keyData;
tupdesc = RelationGetTupleDescriptor(scan->relation); tupdesc = RelationGetTupleDescriptor(scan->relation);
IncrIndexProcessed(); IncrIndexProcessed();
while (keysz > 0) while (keysz > 0)
{
datum = index_getattr(tuple,
key[0].sk_attno,
tupdesc,
&isNull);
/* btree doesn't support 'A is null' clauses, yet */
if ( isNull || key[0].sk_flags & SK_ISNULL )
{ {
return (false); datum = index_getattr(tuple,
key[0].sk_attno,
tupdesc,
&isNull);
/* btree doesn't support 'A is null' clauses, yet */
if (isNull || key[0].sk_flags & SK_ISNULL)
{
return (false);
}
if (key[0].sk_flags & SK_COMMUTE)
{
test = (int) (*(key[0].sk_func))
(DatumGetPointer(key[0].sk_argument),
datum);
}
else
{
test = (int) (*(key[0].sk_func))
(datum,
DatumGetPointer(key[0].sk_argument));
}
if (!test == !(key[0].sk_flags & SK_NEGATE))
{
return (false);
}
keysz -= 1;
key++;
(*keysok)++;
} }
if (key[0].sk_flags & SK_COMMUTE) { return (true);
test = (int) (*(key[0].sk_func))
(DatumGetPointer(key[0].sk_argument),
datum);
} else {
test = (int) (*(key[0].sk_func))
(datum,
DatumGetPointer(key[0].sk_argument));
}
if (!test == !(key[0].sk_flags & SK_NEGATE)) {
return (false);
}
keysz -= 1;
key++;
(*keysok)++;
}
return (true);
} }

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* rtget.c-- * rtget.c--
* fetch tuples from an rtree scan. * fetch tuples from an rtree scan.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtget.c,v 1.7 1996/11/21 06:13:43 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtget.c,v 1.8 1997/09/07 04:39:11 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -21,14 +21,15 @@
#include <access/rtree.h> #include <access/rtree.h>
#include <storage/bufpage.h> #include <storage/bufpage.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
static OffsetNumber findnext(IndexScanDesc s, Page p, OffsetNumber n, static OffsetNumber
ScanDirection dir); findnext(IndexScanDesc s, Page p, OffsetNumber n,
ScanDirection dir);
static RetrieveIndexResult rtscancache(IndexScanDesc s, ScanDirection dir); static RetrieveIndexResult rtscancache(IndexScanDesc s, ScanDirection dir);
static RetrieveIndexResult rtfirst(IndexScanDesc s, ScanDirection dir); static RetrieveIndexResult rtfirst(IndexScanDesc s, ScanDirection dir);
static RetrieveIndexResult rtnext(IndexScanDesc s, ScanDirection dir); static RetrieveIndexResult rtnext(IndexScanDesc s, ScanDirection dir);
@ -38,278 +39,315 @@ static ItemPointer rtheapptr(Relation r, ItemPointer itemp);
RetrieveIndexResult RetrieveIndexResult
rtgettuple(IndexScanDesc s, ScanDirection dir) rtgettuple(IndexScanDesc s, ScanDirection dir)
{ {
RetrieveIndexResult res; RetrieveIndexResult res;
/* if we have it cached in the scan desc, just return the value */ /* if we have it cached in the scan desc, just return the value */
if ((res = rtscancache(s, dir)) != (RetrieveIndexResult) NULL) if ((res = rtscancache(s, dir)) != (RetrieveIndexResult) NULL)
return (res);
/* not cached, so we'll have to do some work */
if (ItemPointerIsValid(&(s->currentItemData)))
{
res = rtnext(s, dir);
}
else
{
res = rtfirst(s, dir);
}
return (res); return (res);
/* not cached, so we'll have to do some work */
if (ItemPointerIsValid(&(s->currentItemData))) {
res = rtnext(s, dir);
} else {
res = rtfirst(s, dir);
}
return (res);
} }
static RetrieveIndexResult static RetrieveIndexResult
rtfirst(IndexScanDesc s, ScanDirection dir) rtfirst(IndexScanDesc s, ScanDirection dir)
{ {
Buffer b; Buffer b;
Page p; Page p;
OffsetNumber n; OffsetNumber n;
OffsetNumber maxoff; OffsetNumber maxoff;
RetrieveIndexResult res; RetrieveIndexResult res;
RTreePageOpaque po; RTreePageOpaque po;
RTreeScanOpaque so; RTreeScanOpaque so;
RTSTACK *stk; RTSTACK *stk;
BlockNumber blk; BlockNumber blk;
IndexTuple it; IndexTuple it;
b = ReadBuffer(s->relation, P_ROOT); b = ReadBuffer(s->relation, P_ROOT);
p = BufferGetPage(b); p = BufferGetPage(b);
po = (RTreePageOpaque) PageGetSpecialPointer(p); po = (RTreePageOpaque) PageGetSpecialPointer(p);
so = (RTreeScanOpaque) s->opaque; so = (RTreeScanOpaque) s->opaque;
for (;;) { for (;;)
maxoff = PageGetMaxOffsetNumber(p); {
if (ScanDirectionIsBackward(dir)) maxoff = PageGetMaxOffsetNumber(p);
n = findnext(s, p, maxoff, dir); if (ScanDirectionIsBackward(dir))
else n = findnext(s, p, maxoff, dir);
n = findnext(s, p, FirstOffsetNumber, dir); else
n = findnext(s, p, FirstOffsetNumber, dir);
while (n < FirstOffsetNumber || n > maxoff) { while (n < FirstOffsetNumber || n > maxoff)
{
ReleaseBuffer(b); ReleaseBuffer(b);
if (so->s_stack == (RTSTACK *) NULL) if (so->s_stack == (RTSTACK *) NULL)
return ((RetrieveIndexResult) NULL); return ((RetrieveIndexResult) NULL);
stk = so->s_stack; stk = so->s_stack;
b = ReadBuffer(s->relation, stk->rts_blk); b = ReadBuffer(s->relation, stk->rts_blk);
p = BufferGetPage(b); p = BufferGetPage(b);
po = (RTreePageOpaque) PageGetSpecialPointer(p); po = (RTreePageOpaque) PageGetSpecialPointer(p);
maxoff = PageGetMaxOffsetNumber(p); maxoff = PageGetMaxOffsetNumber(p);
if (ScanDirectionIsBackward(dir)) { if (ScanDirectionIsBackward(dir))
n = OffsetNumberPrev(stk->rts_child); {
} else { n = OffsetNumberPrev(stk->rts_child);
n = OffsetNumberNext(stk->rts_child); }
} else
so->s_stack = stk->rts_parent; {
pfree(stk); n = OffsetNumberNext(stk->rts_child);
}
so->s_stack = stk->rts_parent;
pfree(stk);
n = findnext(s, p, n, dir); n = findnext(s, p, n, dir);
}
if (po->flags & F_LEAF)
{
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
ReleaseBuffer(b);
return (res);
}
else
{
stk = (RTSTACK *) palloc(sizeof(RTSTACK));
stk->rts_child = n;
stk->rts_blk = BufferGetBlockNumber(b);
stk->rts_parent = so->s_stack;
so->s_stack = stk;
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
blk = ItemPointerGetBlockNumber(&(it->t_tid));
ReleaseBuffer(b);
b = ReadBuffer(s->relation, blk);
p = BufferGetPage(b);
po = (RTreePageOpaque) PageGetSpecialPointer(p);
}
} }
if (po->flags & F_LEAF) {
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
ReleaseBuffer(b);
return (res);
} else {
stk = (RTSTACK *) palloc(sizeof(RTSTACK));
stk->rts_child = n;
stk->rts_blk = BufferGetBlockNumber(b);
stk->rts_parent = so->s_stack;
so->s_stack = stk;
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
blk = ItemPointerGetBlockNumber(&(it->t_tid));
ReleaseBuffer(b);
b = ReadBuffer(s->relation, blk);
p = BufferGetPage(b);
po = (RTreePageOpaque) PageGetSpecialPointer(p);
}
}
} }
static RetrieveIndexResult static RetrieveIndexResult
rtnext(IndexScanDesc s, ScanDirection dir) rtnext(IndexScanDesc s, ScanDirection dir)
{ {
Buffer b; Buffer b;
Page p; Page p;
OffsetNumber n; OffsetNumber n;
OffsetNumber maxoff; OffsetNumber maxoff;
RetrieveIndexResult res; RetrieveIndexResult res;
RTreePageOpaque po; RTreePageOpaque po;
RTreeScanOpaque so; RTreeScanOpaque so;
RTSTACK *stk; RTSTACK *stk;
BlockNumber blk; BlockNumber blk;
IndexTuple it; IndexTuple it;
blk = ItemPointerGetBlockNumber(&(s->currentItemData)); blk = ItemPointerGetBlockNumber(&(s->currentItemData));
n = ItemPointerGetOffsetNumber(&(s->currentItemData)); n = ItemPointerGetOffsetNumber(&(s->currentItemData));
if (ScanDirectionIsForward(dir)) { if (ScanDirectionIsForward(dir))
n = OffsetNumberNext(n); {
} else { n = OffsetNumberNext(n);
n = OffsetNumberPrev(n);
}
b = ReadBuffer(s->relation, blk);
p = BufferGetPage(b);
po = (RTreePageOpaque) PageGetSpecialPointer(p);
so = (RTreeScanOpaque) s->opaque;
for (;;) {
maxoff = PageGetMaxOffsetNumber(p);
n = findnext(s, p, n, dir);
while (n < FirstOffsetNumber || n > maxoff) {
ReleaseBuffer(b);
if (so->s_stack == (RTSTACK *) NULL)
return ((RetrieveIndexResult) NULL);
stk = so->s_stack;
b = ReadBuffer(s->relation, stk->rts_blk);
p = BufferGetPage(b);
maxoff = PageGetMaxOffsetNumber(p);
po = (RTreePageOpaque) PageGetSpecialPointer(p);
if (ScanDirectionIsBackward(dir)) {
n = OffsetNumberPrev(stk->rts_child);
} else {
n = OffsetNumberNext(stk->rts_child);
}
so->s_stack = stk->rts_parent;
pfree(stk);
n = findnext(s, p, n, dir);
} }
if (po->flags & F_LEAF) { else
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n); {
n = OffsetNumberPrev(n);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); }
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid)); b = ReadBuffer(s->relation, blk);
p = BufferGetPage(b);
ReleaseBuffer(b); po = (RTreePageOpaque) PageGetSpecialPointer(p);
return (res); so = (RTreeScanOpaque) s->opaque;
} else {
stk = (RTSTACK *) palloc(sizeof(RTSTACK)); for (;;)
stk->rts_child = n; {
stk->rts_blk = BufferGetBlockNumber(b); maxoff = PageGetMaxOffsetNumber(p);
stk->rts_parent = so->s_stack; n = findnext(s, p, n, dir);
so->s_stack = stk;
while (n < FirstOffsetNumber || n > maxoff)
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); {
blk = ItemPointerGetBlockNumber(&(it->t_tid));
ReleaseBuffer(b);
ReleaseBuffer(b); if (so->s_stack == (RTSTACK *) NULL)
b = ReadBuffer(s->relation, blk); return ((RetrieveIndexResult) NULL);
p = BufferGetPage(b);
po = (RTreePageOpaque) PageGetSpecialPointer(p); stk = so->s_stack;
b = ReadBuffer(s->relation, stk->rts_blk);
if (ScanDirectionIsBackward(dir)) { p = BufferGetPage(b);
n = PageGetMaxOffsetNumber(p); maxoff = PageGetMaxOffsetNumber(p);
} else { po = (RTreePageOpaque) PageGetSpecialPointer(p);
n = FirstOffsetNumber;
} if (ScanDirectionIsBackward(dir))
{
n = OffsetNumberPrev(stk->rts_child);
}
else
{
n = OffsetNumberNext(stk->rts_child);
}
so->s_stack = stk->rts_parent;
pfree(stk);
n = findnext(s, p, n, dir);
}
if (po->flags & F_LEAF)
{
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
ReleaseBuffer(b);
return (res);
}
else
{
stk = (RTSTACK *) palloc(sizeof(RTSTACK));
stk->rts_child = n;
stk->rts_blk = BufferGetBlockNumber(b);
stk->rts_parent = so->s_stack;
so->s_stack = stk;
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
blk = ItemPointerGetBlockNumber(&(it->t_tid));
ReleaseBuffer(b);
b = ReadBuffer(s->relation, blk);
p = BufferGetPage(b);
po = (RTreePageOpaque) PageGetSpecialPointer(p);
if (ScanDirectionIsBackward(dir))
{
n = PageGetMaxOffsetNumber(p);
}
else
{
n = FirstOffsetNumber;
}
}
} }
}
} }
static OffsetNumber static OffsetNumber
findnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir) findnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir)
{ {
OffsetNumber maxoff; OffsetNumber maxoff;
IndexTuple it; IndexTuple it;
RTreePageOpaque po; RTreePageOpaque po;
RTreeScanOpaque so; RTreeScanOpaque so;
maxoff = PageGetMaxOffsetNumber(p); maxoff = PageGetMaxOffsetNumber(p);
po = (RTreePageOpaque) PageGetSpecialPointer(p); po = (RTreePageOpaque) PageGetSpecialPointer(p);
so = (RTreeScanOpaque) s->opaque; so = (RTreeScanOpaque) s->opaque;
/* /*
* If we modified the index during the scan, we may have a pointer to * If we modified the index during the scan, we may have a pointer to
* a ghost tuple, before the scan. If this is the case, back up one. * a ghost tuple, before the scan. If this is the case, back up one.
*/ */
if (so->s_flags & RTS_CURBEFORE) { if (so->s_flags & RTS_CURBEFORE)
so->s_flags &= ~RTS_CURBEFORE; {
n = OffsetNumberPrev(n); so->s_flags &= ~RTS_CURBEFORE;
} n = OffsetNumberPrev(n);
while (n >= FirstOffsetNumber && n <= maxoff) {
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
if (po->flags & F_LEAF) {
if (index_keytest(it,
RelationGetTupleDescriptor(s->relation),
s->numberOfKeys, s->keyData))
break;
} else {
if (index_keytest(it,
RelationGetTupleDescriptor(s->relation),
so->s_internalNKey, so->s_internalKey))
break;
} }
if (ScanDirectionIsBackward(dir)) { while (n >= FirstOffsetNumber && n <= maxoff)
n = OffsetNumberPrev(n); {
} else { it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
n = OffsetNumberNext(n); if (po->flags & F_LEAF)
} {
} if (index_keytest(it,
RelationGetTupleDescriptor(s->relation),
s->numberOfKeys, s->keyData))
break;
}
else
{
if (index_keytest(it,
RelationGetTupleDescriptor(s->relation),
so->s_internalNKey, so->s_internalKey))
break;
}
return (n); if (ScanDirectionIsBackward(dir))
{
n = OffsetNumberPrev(n);
}
else
{
n = OffsetNumberNext(n);
}
}
return (n);
} }
static RetrieveIndexResult static RetrieveIndexResult
rtscancache(IndexScanDesc s, ScanDirection dir) rtscancache(IndexScanDesc s, ScanDirection dir)
{ {
RetrieveIndexResult res; RetrieveIndexResult res;
ItemPointer ip; ItemPointer ip;
if (!(ScanDirectionIsNoMovement(dir) if (!(ScanDirectionIsNoMovement(dir)
&& ItemPointerIsValid(&(s->currentItemData)))) { && ItemPointerIsValid(&(s->currentItemData))))
{
return ((RetrieveIndexResult) NULL); return ((RetrieveIndexResult) NULL);
} }
ip = rtheapptr(s->relation, &(s->currentItemData)); ip = rtheapptr(s->relation, &(s->currentItemData));
if (ItemPointerIsValid(ip)) if (ItemPointerIsValid(ip))
res = FormRetrieveIndexResult(&(s->currentItemData), ip); res = FormRetrieveIndexResult(&(s->currentItemData), ip);
else else
res = (RetrieveIndexResult) NULL; res = (RetrieveIndexResult) NULL;
pfree (ip); pfree(ip);
return (res); return (res);
} }
/* /*
* rtheapptr returns the item pointer to the tuple in the heap relation * rtheapptr returns the item pointer to the tuple in the heap relation
* for which itemp is the index relation item pointer. * for which itemp is the index relation item pointer.
*/ */
static ItemPointer static ItemPointer
rtheapptr(Relation r, ItemPointer itemp) rtheapptr(Relation r, ItemPointer itemp)
{ {
Buffer b; Buffer b;
Page p; Page p;
IndexTuple it; IndexTuple it;
ItemPointer ip; ItemPointer ip;
OffsetNumber n; OffsetNumber n;
ip = (ItemPointer) palloc(sizeof(ItemPointerData)); ip = (ItemPointer) palloc(sizeof(ItemPointerData));
if (ItemPointerIsValid(itemp)) { if (ItemPointerIsValid(itemp))
b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp)); {
p = BufferGetPage(b); b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp));
n = ItemPointerGetOffsetNumber(itemp); p = BufferGetPage(b);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); n = ItemPointerGetOffsetNumber(itemp);
memmove((char *) ip, (char *) &(it->t_tid), it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
sizeof(ItemPointerData)); memmove((char *) ip, (char *) &(it->t_tid),
ReleaseBuffer(b); sizeof(ItemPointerData));
} else { ReleaseBuffer(b);
ItemPointerSetInvalid(ip); }
} else
{
ItemPointerSetInvalid(ip);
}
return (ip); return (ip);
} }

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* rtproc.c-- * rtproc.c--
* pg_amproc entries for rtrees. * pg_amproc entries for rtrees.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtproc.c,v 1.7 1997/04/22 17:31:23 scrappy Exp $ * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtproc.c,v 1.8 1997/09/07 04:39:16 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -17,136 +17,139 @@
#include <utils/builtins.h> #include <utils/builtins.h>
#include <utils/geo_decls.h> #include <utils/geo_decls.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
BOX BOX
*rt_box_union(BOX *a, BOX *b) * rt_box_union(BOX * a, BOX * b)
{ {
BOX *n; BOX *n;
if ((n = (BOX *) palloc(sizeof (*n))) == (BOX *) NULL) if ((n = (BOX *) palloc(sizeof(*n))) == (BOX *) NULL)
elog(WARN, "Cannot allocate box for union"); elog(WARN, "Cannot allocate box for union");
n->high.x = Max(a->high.x, b->high.x); n->high.x = Max(a->high.x, b->high.x);
n->high.y = Max(a->high.y, b->high.y); n->high.y = Max(a->high.y, b->high.y);
n->low.x = Min(a->low.x, b->low.x); n->low.x = Min(a->low.x, b->low.x);
n->low.y = Min(a->low.y, b->low.y); n->low.y = Min(a->low.y, b->low.y);
return (n); return (n);
} }
BOX * BOX *
rt_box_inter(BOX *a, BOX *b) rt_box_inter(BOX * a, BOX * b)
{ {
BOX *n; BOX *n;
if ((n = (BOX *) palloc(sizeof (*n))) == (BOX *) NULL) if ((n = (BOX *) palloc(sizeof(*n))) == (BOX *) NULL)
elog(WARN, "Cannot allocate box for union"); elog(WARN, "Cannot allocate box for union");
n->high.x = Min(a->high.x, b->high.x); n->high.x = Min(a->high.x, b->high.x);
n->high.y = Min(a->high.y, b->high.y); n->high.y = Min(a->high.y, b->high.y);
n->low.x = Max(a->low.x, b->low.x); n->low.x = Max(a->low.x, b->low.x);
n->low.y = Max(a->low.y, b->low.y); n->low.y = Max(a->low.y, b->low.y);
if (n->high.x < n->low.x || n->high.y < n->low.y) { if (n->high.x < n->low.x || n->high.y < n->low.y)
pfree(n); {
return ((BOX *) NULL); pfree(n);
} return ((BOX *) NULL);
}
return (n); return (n);
} }
void void
rt_box_size(BOX *a, float *size) rt_box_size(BOX * a, float *size)
{ {
if (a == (BOX *) NULL || a->high.x <= a->low.x || a->high.y <= a->low.y) if (a == (BOX *) NULL || a->high.x <= a->low.x || a->high.y <= a->low.y)
*size = 0.0; *size = 0.0;
else else
*size = (float) ((a->high.x - a->low.x) * (a->high.y - a->low.y)); *size = (float) ((a->high.x - a->low.x) * (a->high.y - a->low.y));
return; return;
} }
/* /*
* rt_bigbox_size() -- Compute a size for big boxes. * rt_bigbox_size() -- Compute a size for big boxes.
* *
* In an earlier release of the system, this routine did something * In an earlier release of the system, this routine did something
* different from rt_box_size. We now use floats, rather than ints, * different from rt_box_size. We now use floats, rather than ints,
* as the return type for the size routine, so we no longer need to * as the return type for the size routine, so we no longer need to
* have a special return type for big boxes. * have a special return type for big boxes.
*/ */
void void
rt_bigbox_size(BOX *a, float *size) rt_bigbox_size(BOX * a, float *size)
{ {
rt_box_size(a, size); rt_box_size(a, size);
} }
POLYGON * POLYGON *
rt_poly_union(POLYGON *a, POLYGON *b) rt_poly_union(POLYGON * a, POLYGON * b)
{ {
POLYGON *p; POLYGON *p;
p = (POLYGON *)PALLOCTYPE(POLYGON); p = (POLYGON *) PALLOCTYPE(POLYGON);
if (!PointerIsValid(p)) if (!PointerIsValid(p))
elog(WARN, "Cannot allocate polygon for union"); elog(WARN, "Cannot allocate polygon for union");
memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */ memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */
p->size = sizeof(POLYGON); p->size = sizeof(POLYGON);
p->npts = 0; p->npts = 0;
p->boundbox.high.x = Max(a->boundbox.high.x, b->boundbox.high.x); p->boundbox.high.x = Max(a->boundbox.high.x, b->boundbox.high.x);
p->boundbox.high.y = Max(a->boundbox.high.y, b->boundbox.high.y); p->boundbox.high.y = Max(a->boundbox.high.y, b->boundbox.high.y);
p->boundbox.low.x = Min(a->boundbox.low.x, b->boundbox.low.x); p->boundbox.low.x = Min(a->boundbox.low.x, b->boundbox.low.x);
p->boundbox.low.y = Min(a->boundbox.low.y, b->boundbox.low.y); p->boundbox.low.y = Min(a->boundbox.low.y, b->boundbox.low.y);
return p; return p;
} }
void void
rt_poly_size(POLYGON *a, float *size) rt_poly_size(POLYGON * a, float *size)
{ {
double xdim, ydim; double xdim,
ydim;
size = (float *) palloc(sizeof(float)); size = (float *) palloc(sizeof(float));
if (a == (POLYGON *) NULL || if (a == (POLYGON *) NULL ||
a->boundbox.high.x <= a->boundbox.low.x || a->boundbox.high.x <= a->boundbox.low.x ||
a->boundbox.high.y <= a->boundbox.low.y) a->boundbox.high.y <= a->boundbox.low.y)
*size = 0.0; *size = 0.0;
else { else
xdim = (a->boundbox.high.x - a->boundbox.low.x);
ydim = (a->boundbox.high.y - a->boundbox.low.y);
*size = (float) (xdim * ydim);
}
return;
}
POLYGON *
rt_poly_inter(POLYGON *a, POLYGON *b)
{
POLYGON *p;
p = (POLYGON *) PALLOCTYPE(POLYGON);
if (!PointerIsValid(p))
elog(WARN, "Cannot allocate polygon for intersection");
memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */
p->size = sizeof(POLYGON);
p->npts = 0;
p->boundbox.high.x = Min(a->boundbox.high.x, b->boundbox.high.x);
p->boundbox.high.y = Min(a->boundbox.high.y, b->boundbox.high.y);
p->boundbox.low.x = Max(a->boundbox.low.x, b->boundbox.low.x);
p->boundbox.low.y = Max(a->boundbox.low.y, b->boundbox.low.y);
if (p->boundbox.high.x < p->boundbox.low.x || p->boundbox.high.y < p->boundbox.low.y)
{ {
pfree(p); xdim = (a->boundbox.high.x - a->boundbox.low.x);
return ((POLYGON *) NULL); ydim = (a->boundbox.high.y - a->boundbox.low.y);
*size = (float) (xdim * ydim);
} }
return (p); return;
}
POLYGON *
rt_poly_inter(POLYGON * a, POLYGON * b)
{
POLYGON *p;
p = (POLYGON *) PALLOCTYPE(POLYGON);
if (!PointerIsValid(p))
elog(WARN, "Cannot allocate polygon for intersection");
memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */
p->size = sizeof(POLYGON);
p->npts = 0;
p->boundbox.high.x = Min(a->boundbox.high.x, b->boundbox.high.x);
p->boundbox.high.y = Min(a->boundbox.high.y, b->boundbox.high.y);
p->boundbox.low.x = Max(a->boundbox.low.x, b->boundbox.low.x);
p->boundbox.low.y = Max(a->boundbox.low.y, b->boundbox.low.y);
if (p->boundbox.high.x < p->boundbox.low.x || p->boundbox.high.y < p->boundbox.low.y)
{
pfree(p);
return ((POLYGON *) NULL);
}
return (p);
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* rtscan.c-- * rtscan.c--
* routines to manage scans on index relations * routines to manage scans on index relations
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtscan.c,v 1.10 1997/05/20 10:29:30 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtscan.c,v 1.11 1997/09/07 04:39:24 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -21,377 +21,411 @@
#include <access/rtree.h> #include <access/rtree.h>
#include <access/rtstrat.h> #include <access/rtstrat.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
/* routines defined and used here */ /* routines defined and used here */
static void rtregscan(IndexScanDesc s); static void rtregscan(IndexScanDesc s);
static void rtdropscan(IndexScanDesc s); static void rtdropscan(IndexScanDesc s);
static void rtadjone(IndexScanDesc s, int op, BlockNumber blkno, static void
OffsetNumber offnum); rtadjone(IndexScanDesc s, int op, BlockNumber blkno,
static void adjuststack(RTSTACK *stk, BlockNumber blkno, OffsetNumber offnum);
static void
adjuststack(RTSTACK * stk, BlockNumber blkno,
OffsetNumber offnum); OffsetNumber offnum);
static void adjustiptr(IndexScanDesc s, ItemPointer iptr, static void
int op, BlockNumber blkno, OffsetNumber offnum); adjustiptr(IndexScanDesc s, ItemPointer iptr,
int op, BlockNumber blkno, OffsetNumber offnum);
/* /*
* Whenever we start an rtree scan in a backend, we register it in private * Whenever we start an rtree scan in a backend, we register it in private
* space. Then if the rtree index gets updated, we check all registered * space. Then if the rtree index gets updated, we check all registered
* scans and adjust them if the tuple they point at got moved by the * scans and adjust them if the tuple they point at got moved by the
* update. We only need to do this in private space, because when we update * update. We only need to do this in private space, because when we update
* an rtree we have a write lock on the tree, so no other process can have * an rtree we have a write lock on the tree, so no other process can have
* any locks at all on it. A single transaction can have write and read * any locks at all on it. A single transaction can have write and read
* locks on the same object, so that's why we need to handle this case. * locks on the same object, so that's why we need to handle this case.
*/ */
typedef struct RTScanListData { typedef struct RTScanListData
IndexScanDesc rtsl_scan; {
struct RTScanListData *rtsl_next; IndexScanDesc rtsl_scan;
} RTScanListData; struct RTScanListData *rtsl_next;
} RTScanListData;
typedef RTScanListData *RTScanList; typedef RTScanListData *RTScanList;
/* pointer to list of local scans on rtrees */ /* pointer to list of local scans on rtrees */
static RTScanList RTScans = (RTScanList) NULL; static RTScanList RTScans = (RTScanList) NULL;
IndexScanDesc IndexScanDesc
rtbeginscan(Relation r, rtbeginscan(Relation r,
bool fromEnd, bool fromEnd,
uint16 nkeys, uint16 nkeys,
ScanKey key) ScanKey key)
{ {
IndexScanDesc s; IndexScanDesc s;
RelationSetLockForRead(r); RelationSetLockForRead(r);
s = RelationGetIndexScan(r, fromEnd, nkeys, key); s = RelationGetIndexScan(r, fromEnd, nkeys, key);
rtregscan(s); rtregscan(s);
return (s); return (s);
} }
void void
rtrescan(IndexScanDesc s, bool fromEnd, ScanKey key) rtrescan(IndexScanDesc s, bool fromEnd, ScanKey key)
{ {
RTreeScanOpaque p; RTreeScanOpaque p;
RegProcedure internal_proc; RegProcedure internal_proc;
int i; int i;
if (!IndexScanIsValid(s)) { if (!IndexScanIsValid(s))
elog(WARN, "rtrescan: invalid scan.");
return;
}
/*
* Clear all the pointers.
*/
ItemPointerSetInvalid(&s->previousItemData);
ItemPointerSetInvalid(&s->currentItemData);
ItemPointerSetInvalid(&s->nextItemData);
ItemPointerSetInvalid(&s->previousMarkData);
ItemPointerSetInvalid(&s->currentMarkData);
ItemPointerSetInvalid(&s->nextMarkData);
/*
* Set flags.
*/
if (RelationGetNumberOfBlocks(s->relation) == 0) {
s->flags = ScanUnmarked;
} else if (fromEnd) {
s->flags = ScanUnmarked | ScanUncheckedPrevious;
} else {
s->flags = ScanUnmarked | ScanUncheckedNext;
}
s->scanFromEnd = fromEnd;
if (s->numberOfKeys > 0) {
memmove(s->keyData,
key,
s->numberOfKeys * sizeof(ScanKeyData));
}
p = (RTreeScanOpaque) s->opaque;
if (p != (RTreeScanOpaque) NULL) {
freestack(p->s_stack);
freestack(p->s_markstk);
p->s_stack = p->s_markstk = (RTSTACK *) NULL;
p->s_flags = 0x0;
for (i = 0; i < s->numberOfKeys; i++)
{ {
p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument; elog(WARN, "rtrescan: invalid scan.");
return;
} }
} else {
/* initialize opaque data */
p = (RTreeScanOpaque) palloc(sizeof(RTreeScanOpaqueData));
p->s_stack = p->s_markstk = (RTSTACK *) NULL;
p->s_internalNKey = s->numberOfKeys;
p->s_flags = 0x0;
s->opaque = p;
if (s->numberOfKeys > 0) {
p->s_internalKey =
(ScanKey) palloc(sizeof(ScanKeyData) * s->numberOfKeys);
/* /*
* Scans on internal pages use different operators than they * Clear all the pointers.
* do on leaf pages. For example, if the user wants all boxes */
* that exactly match (x1,y1,x2,y2), then on internal pages
* we need to find all boxes that contain (x1,y1,x2,y2).
*/
for (i = 0; i < s->numberOfKeys; i++) { ItemPointerSetInvalid(&s->previousItemData);
p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument; ItemPointerSetInvalid(&s->currentItemData);
internal_proc = RTMapOperator(s->relation, ItemPointerSetInvalid(&s->nextItemData);
s->keyData[i].sk_attno, ItemPointerSetInvalid(&s->previousMarkData);
s->keyData[i].sk_procedure); ItemPointerSetInvalid(&s->currentMarkData);
ScanKeyEntryInitialize(&(p->s_internalKey[i]), ItemPointerSetInvalid(&s->nextMarkData);
s->keyData[i].sk_flags,
s->keyData[i].sk_attno, /*
internal_proc, * Set flags.
s->keyData[i].sk_argument); */
} if (RelationGetNumberOfBlocks(s->relation) == 0)
{
s->flags = ScanUnmarked;
}
else if (fromEnd)
{
s->flags = ScanUnmarked | ScanUncheckedPrevious;
}
else
{
s->flags = ScanUnmarked | ScanUncheckedNext;
}
s->scanFromEnd = fromEnd;
if (s->numberOfKeys > 0)
{
memmove(s->keyData,
key,
s->numberOfKeys * sizeof(ScanKeyData));
}
p = (RTreeScanOpaque) s->opaque;
if (p != (RTreeScanOpaque) NULL)
{
freestack(p->s_stack);
freestack(p->s_markstk);
p->s_stack = p->s_markstk = (RTSTACK *) NULL;
p->s_flags = 0x0;
for (i = 0; i < s->numberOfKeys; i++)
{
p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument;
}
}
else
{
/* initialize opaque data */
p = (RTreeScanOpaque) palloc(sizeof(RTreeScanOpaqueData));
p->s_stack = p->s_markstk = (RTSTACK *) NULL;
p->s_internalNKey = s->numberOfKeys;
p->s_flags = 0x0;
s->opaque = p;
if (s->numberOfKeys > 0)
{
p->s_internalKey =
(ScanKey) palloc(sizeof(ScanKeyData) * s->numberOfKeys);
/*
* Scans on internal pages use different operators than they
* do on leaf pages. For example, if the user wants all boxes
* that exactly match (x1,y1,x2,y2), then on internal pages we
* need to find all boxes that contain (x1,y1,x2,y2).
*/
for (i = 0; i < s->numberOfKeys; i++)
{
p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument;
internal_proc = RTMapOperator(s->relation,
s->keyData[i].sk_attno,
s->keyData[i].sk_procedure);
ScanKeyEntryInitialize(&(p->s_internalKey[i]),
s->keyData[i].sk_flags,
s->keyData[i].sk_attno,
internal_proc,
s->keyData[i].sk_argument);
}
}
} }
}
} }
void void
rtmarkpos(IndexScanDesc s) rtmarkpos(IndexScanDesc s)
{ {
RTreeScanOpaque p; RTreeScanOpaque p;
RTSTACK *o, *n, *tmp; RTSTACK *o,
*n,
*tmp;
s->currentMarkData = s->currentItemData; s->currentMarkData = s->currentItemData;
p = (RTreeScanOpaque) s->opaque; p = (RTreeScanOpaque) s->opaque;
if (p->s_flags & RTS_CURBEFORE) if (p->s_flags & RTS_CURBEFORE)
p->s_flags |= RTS_MRKBEFORE; p->s_flags |= RTS_MRKBEFORE;
else else
p->s_flags &= ~RTS_MRKBEFORE; p->s_flags &= ~RTS_MRKBEFORE;
o = (RTSTACK *) NULL; o = (RTSTACK *) NULL;
n = p->s_stack; n = p->s_stack;
/* copy the parent stack from the current item data */ /* copy the parent stack from the current item data */
while (n != (RTSTACK *) NULL) { while (n != (RTSTACK *) NULL)
tmp = (RTSTACK *) palloc(sizeof(RTSTACK)); {
tmp->rts_child = n->rts_child; tmp = (RTSTACK *) palloc(sizeof(RTSTACK));
tmp->rts_blk = n->rts_blk; tmp->rts_child = n->rts_child;
tmp->rts_parent = o; tmp->rts_blk = n->rts_blk;
o = tmp; tmp->rts_parent = o;
n = n->rts_parent; o = tmp;
} n = n->rts_parent;
}
freestack(p->s_markstk); freestack(p->s_markstk);
p->s_markstk = o; p->s_markstk = o;
} }
void void
rtrestrpos(IndexScanDesc s) rtrestrpos(IndexScanDesc s)
{ {
RTreeScanOpaque p; RTreeScanOpaque p;
RTSTACK *o, *n, *tmp; RTSTACK *o,
*n,
*tmp;
s->currentItemData = s->currentMarkData; s->currentItemData = s->currentMarkData;
p = (RTreeScanOpaque) s->opaque; p = (RTreeScanOpaque) s->opaque;
if (p->s_flags & RTS_MRKBEFORE) if (p->s_flags & RTS_MRKBEFORE)
p->s_flags |= RTS_CURBEFORE; p->s_flags |= RTS_CURBEFORE;
else else
p->s_flags &= ~RTS_CURBEFORE; p->s_flags &= ~RTS_CURBEFORE;
o = (RTSTACK *) NULL; o = (RTSTACK *) NULL;
n = p->s_markstk; n = p->s_markstk;
/* copy the parent stack from the current item data */ /* copy the parent stack from the current item data */
while (n != (RTSTACK *) NULL) { while (n != (RTSTACK *) NULL)
tmp = (RTSTACK *) palloc(sizeof(RTSTACK)); {
tmp->rts_child = n->rts_child; tmp = (RTSTACK *) palloc(sizeof(RTSTACK));
tmp->rts_blk = n->rts_blk; tmp->rts_child = n->rts_child;
tmp->rts_parent = o; tmp->rts_blk = n->rts_blk;
o = tmp; tmp->rts_parent = o;
n = n->rts_parent; o = tmp;
} n = n->rts_parent;
}
freestack(p->s_stack); freestack(p->s_stack);
p->s_stack = o; p->s_stack = o;
} }
void void
rtendscan(IndexScanDesc s) rtendscan(IndexScanDesc s)
{ {
RTreeScanOpaque p; RTreeScanOpaque p;
p = (RTreeScanOpaque) s->opaque; p = (RTreeScanOpaque) s->opaque;
if (p != (RTreeScanOpaque) NULL) { if (p != (RTreeScanOpaque) NULL)
freestack(p->s_stack); {
freestack(p->s_markstk); freestack(p->s_stack);
pfree (s->opaque); freestack(p->s_markstk);
} pfree(s->opaque);
}
rtdropscan(s); rtdropscan(s);
/* XXX don't unset read lock -- two-phase locking */ /* XXX don't unset read lock -- two-phase locking */
} }
static void static void
rtregscan(IndexScanDesc s) rtregscan(IndexScanDesc s)
{ {
RTScanList l; RTScanList l;
l = (RTScanList) palloc(sizeof(RTScanListData)); l = (RTScanList) palloc(sizeof(RTScanListData));
l->rtsl_scan = s; l->rtsl_scan = s;
l->rtsl_next = RTScans; l->rtsl_next = RTScans;
RTScans = l; RTScans = l;
} }
static void static void
rtdropscan(IndexScanDesc s) rtdropscan(IndexScanDesc s)
{ {
RTScanList l; RTScanList l;
RTScanList prev; RTScanList prev;
prev = (RTScanList) NULL; prev = (RTScanList) NULL;
for (l = RTScans; for (l = RTScans;
l != (RTScanList) NULL && l->rtsl_scan != s; l != (RTScanList) NULL && l->rtsl_scan != s;
l = l->rtsl_next) { l = l->rtsl_next)
prev = l; {
} prev = l;
}
if (l == (RTScanList) NULL) if (l == (RTScanList) NULL)
elog(WARN, "rtree scan list corrupted -- cannot find 0x%lx", s); elog(WARN, "rtree scan list corrupted -- cannot find 0x%lx", s);
if (prev == (RTScanList) NULL) if (prev == (RTScanList) NULL)
RTScans = l->rtsl_next; RTScans = l->rtsl_next;
else else
prev->rtsl_next = l->rtsl_next; prev->rtsl_next = l->rtsl_next;
pfree(l); pfree(l);
} }
void void
rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum) rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum)
{ {
RTScanList l; RTScanList l;
Oid relid; Oid relid;
relid = r->rd_id; relid = r->rd_id;
for (l = RTScans; l != (RTScanList) NULL; l = l->rtsl_next) { for (l = RTScans; l != (RTScanList) NULL; l = l->rtsl_next)
if (l->rtsl_scan->relation->rd_id == relid) {
rtadjone(l->rtsl_scan, op, blkno, offnum); if (l->rtsl_scan->relation->rd_id == relid)
} rtadjone(l->rtsl_scan, op, blkno, offnum);
}
} }
/* /*
* rtadjone() -- adjust one scan for update. * rtadjone() -- adjust one scan for update.
* *
* By here, the scan passed in is on a modified relation. Op tells * By here, the scan passed in is on a modified relation. Op tells
* us what the modification is, and blkno and offind tell us what * us what the modification is, and blkno and offind tell us what
* block and offset index were affected. This routine checks the * block and offset index were affected. This routine checks the
* current and marked positions, and the current and marked stacks, * current and marked positions, and the current and marked stacks,
* to see if any stored location needs to be changed because of the * to see if any stored location needs to be changed because of the
* update. If so, we make the change here. * update. If so, we make the change here.
*/ */
static void static void
rtadjone(IndexScanDesc s, rtadjone(IndexScanDesc s,
int op, int op,
BlockNumber blkno, BlockNumber blkno,
OffsetNumber offnum) OffsetNumber offnum)
{ {
RTreeScanOpaque so; RTreeScanOpaque so;
adjustiptr(s, &(s->currentItemData), op, blkno, offnum); adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum); adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
so = (RTreeScanOpaque) s->opaque; so = (RTreeScanOpaque) s->opaque;
if (op == RTOP_SPLIT) { if (op == RTOP_SPLIT)
adjuststack(so->s_stack, blkno, offnum); {
adjuststack(so->s_markstk, blkno, offnum); adjuststack(so->s_stack, blkno, offnum);
} adjuststack(so->s_markstk, blkno, offnum);
}
} }
/* /*
* adjustiptr() -- adjust current and marked item pointers in the scan * adjustiptr() -- adjust current and marked item pointers in the scan
* *
* Depending on the type of update and the place it happened, we * Depending on the type of update and the place it happened, we
* need to do nothing, to back up one record, or to start over on * need to do nothing, to back up one record, or to start over on
* the same page. * the same page.
*/ */
static void static void
adjustiptr(IndexScanDesc s, adjustiptr(IndexScanDesc s,
ItemPointer iptr, ItemPointer iptr,
int op, int op,
BlockNumber blkno, BlockNumber blkno,
OffsetNumber offnum) OffsetNumber offnum)
{ {
OffsetNumber curoff; OffsetNumber curoff;
RTreeScanOpaque so; RTreeScanOpaque so;
if (ItemPointerIsValid(iptr)) { if (ItemPointerIsValid(iptr))
if (ItemPointerGetBlockNumber(iptr) == blkno) { {
curoff = ItemPointerGetOffsetNumber(iptr); if (ItemPointerGetBlockNumber(iptr) == blkno)
so = (RTreeScanOpaque) s->opaque; {
curoff = ItemPointerGetOffsetNumber(iptr);
so = (RTreeScanOpaque) s->opaque;
switch (op) { switch (op)
case RTOP_DEL: {
/* back up one if we need to */ case RTOP_DEL:
if (curoff >= offnum) { /* back up one if we need to */
if (curoff >= offnum)
{
if (curoff > FirstOffsetNumber) { if (curoff > FirstOffsetNumber)
/* just adjust the item pointer */ {
ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff)); /* just adjust the item pointer */
} else { ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff));
/* remember that we're before the current tuple */ }
ItemPointerSet(iptr, blkno, FirstOffsetNumber); else
if (iptr == &(s->currentItemData)) {
so->s_flags |= RTS_CURBEFORE; /* remember that we're before the current tuple */
else ItemPointerSet(iptr, blkno, FirstOffsetNumber);
so->s_flags |= RTS_MRKBEFORE; if (iptr == &(s->currentItemData))
} so->s_flags |= RTS_CURBEFORE;
else
so->s_flags |= RTS_MRKBEFORE;
}
}
break;
case RTOP_SPLIT:
/* back to start of page on split */
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
if (iptr == &(s->currentItemData))
so->s_flags &= ~RTS_CURBEFORE;
else
so->s_flags &= ~RTS_MRKBEFORE;
break;
default:
elog(WARN, "Bad operation in rtree scan adjust: %d", op);
}
} }
break;
case RTOP_SPLIT:
/* back to start of page on split */
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
if (iptr == &(s->currentItemData))
so->s_flags &= ~RTS_CURBEFORE;
else
so->s_flags &= ~RTS_MRKBEFORE;
break;
default:
elog(WARN, "Bad operation in rtree scan adjust: %d", op);
}
} }
}
} }
/* /*
* adjuststack() -- adjust the supplied stack for a split on a page in * adjuststack() -- adjust the supplied stack for a split on a page in
* the index we're scanning. * the index we're scanning.
* *
* If a page on our parent stack has split, we need to back up to the * If a page on our parent stack has split, we need to back up to the
* beginning of the page and rescan it. The reason for this is that * beginning of the page and rescan it. The reason for this is that
* the split algorithm for rtrees doesn't order tuples in any useful * the split algorithm for rtrees doesn't order tuples in any useful
* way on a single page. This means on that a split, we may wind up * way on a single page. This means on that a split, we may wind up
* looking at some heap tuples more than once. This is handled in the * looking at some heap tuples more than once. This is handled in the
* access method update code for heaps; if we've modified the tuple we * access method update code for heaps; if we've modified the tuple we
* are looking at already in this transaction, we ignore the update * are looking at already in this transaction, we ignore the update
* request. * request.
*/ */
/*ARGSUSED*/ /*ARGSUSED*/
static void static void
adjuststack(RTSTACK *stk, adjuststack(RTSTACK * stk,
BlockNumber blkno, BlockNumber blkno,
OffsetNumber offnum) OffsetNumber offnum)
{ {
while (stk != (RTSTACK *) NULL) { while (stk != (RTSTACK *) NULL)
if (stk->rts_blk == blkno) {
stk->rts_child = FirstOffsetNumber; if (stk->rts_blk == blkno)
stk->rts_child = FirstOffsetNumber;
stk = stk->rts_parent; stk = stk->rts_parent;
} }
} }

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* rtstrat.c-- * rtstrat.c--
* strategy map data for rtrees. * strategy map data for rtrees.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtstrat.c,v 1.6 1997/08/19 21:29:52 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtstrat.c,v 1.7 1997/09/07 04:39:26 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -18,224 +18,226 @@
#include <access/rtree.h> #include <access/rtree.h>
#include <access/istrat.h> #include <access/istrat.h>
static StrategyNumber RelationGetRTStrategy(Relation r, static StrategyNumber
AttrNumber attnum, RegProcedure proc); RelationGetRTStrategy(Relation r,
AttrNumber attnum, RegProcedure proc);
/* /*
* Note: negate, commute, and negatecommute all assume that operators are * Note: negate, commute, and negatecommute all assume that operators are
* ordered as follows in the strategy map: * ordered as follows in the strategy map:
* *
* left, left-or-overlap, overlap, right-or-overlap, right, same, * left, left-or-overlap, overlap, right-or-overlap, right, same,
* contains, contained-by * contains, contained-by
* *
* The negate, commute, and negatecommute arrays are used by the planner * The negate, commute, and negatecommute arrays are used by the planner
* to plan indexed scans over data that appears in the qualificiation in * to plan indexed scans over data that appears in the qualificiation in
* a boolean negation, or whose operands appear in the wrong order. For * a boolean negation, or whose operands appear in the wrong order. For
* example, if the operator "<%" means "contains", and the user says * example, if the operator "<%" means "contains", and the user says
* *
* where not rel.box <% "(10,10,20,20)"::box * where not rel.box <% "(10,10,20,20)"::box
* *
* the planner can plan an index scan by noting that rtree indices have * the planner can plan an index scan by noting that rtree indices have
* an operator in their operator class for negating <%. * an operator in their operator class for negating <%.
* *
* Similarly, if the user says something like * Similarly, if the user says something like
* *
* where "(10,10,20,20)"::box <% rel.box * where "(10,10,20,20)"::box <% rel.box
* *
* the planner can see that the rtree index on rel.box has an operator in * the planner can see that the rtree index on rel.box has an operator in
* its opclass for commuting <%, and plan the scan using that operator. * its opclass for commuting <%, and plan the scan using that operator.
* This added complexity in the access methods makes the planner a lot easier * This added complexity in the access methods makes the planner a lot easier
* to write. * to write.
*/ */
/* if a op b, what operator tells us if (not a op b)? */ /* if a op b, what operator tells us if (not a op b)? */
static StrategyNumber RTNegate[RTNStrategies] = { static StrategyNumber RTNegate[RTNStrategies] = {
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy InvalidStrategy
}; };
/* if a op_1 b, what is the operator op_2 such that b op_2 a? */ /* if a op_1 b, what is the operator op_2 such that b op_2 a? */
static StrategyNumber RTCommute[RTNStrategies] = { static StrategyNumber RTCommute[RTNStrategies] = {
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy InvalidStrategy
}; };
/* if a op_1 b, what is the operator op_2 such that (b !op_2 a)? */ /* if a op_1 b, what is the operator op_2 such that (b !op_2 a)? */
static StrategyNumber RTNegateCommute[RTNStrategies] = { static StrategyNumber RTNegateCommute[RTNStrategies] = {
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy, InvalidStrategy,
InvalidStrategy InvalidStrategy
};
/*
* Now do the TermData arrays. These exist in case the user doesn't give
* us a full set of operators for a particular operator class. The idea
* is that by making multiple comparisons using any one of the supplied
* operators, we can decide whether two n-dimensional polygons are equal.
* For example, if a contains b and b contains a, we may conclude that
* a and b are equal.
*
* The presence of the TermData arrays in all this is a historical accident.
* Early in the development of the POSTGRES access methods, it was believed
* that writing functions was harder than writing arrays. This is wrong;
* TermData is hard to understand and hard to get right. In general, when
* someone populates a new operator class, the populate it completely. If
* Mike Hirohama had forced Cimarron Taylor to populate the strategy map
* for btree int2_ops completely in 1988, you wouldn't have to deal with
* all this now. Too bad for you.
*
* Since you can't necessarily do this in all cases (for example, you can't
* do it given only "intersects" or "disjoint"), TermData arrays for some
* operators don't appear below.
*
* Note that if you DO supply all the operators required in a given opclass
* by inserting them into the pg_opclass system catalog, you can get away
* without doing all this TermData stuff. Since the rtree code is intended
* to be a reference for access method implementors, I'm doing TermData
* correctly here.
*
* Note on style: these are all actually of type StrategyTermData, but
* since those have variable-length data at the end of the struct we can't
* properly initialize them if we declare them to be what they are.
*/
/* if you only have "contained-by", how do you determine equality? */
static uint16 RTContainedByTermData[] = {
2, /* make two comparisons */
RTContainedByStrategyNumber, /* use "a contained-by b" */
0x0, /* without any magic */
RTContainedByStrategyNumber, /* then use contained-by, */
SK_COMMUTE /* swapping a and b */
};
/* if you only have "contains", how do you determine equality? */
static uint16 RTContainsTermData[] = {
2, /* make two comparisons */
RTContainsStrategyNumber, /* use "a contains b" */
0x0, /* without any magic */
RTContainsStrategyNumber, /* then use contains again, */
SK_COMMUTE /* swapping a and b */
};
/* now put all that together in one place for the planner */
static StrategyTerm RTEqualExpressionData[] = {
(StrategyTerm) RTContainedByTermData,
(StrategyTerm) RTContainsTermData,
NULL
};
/*
* If you were sufficiently attentive to detail, you would go through
* the ExpressionData pain above for every one of the seven strategies
* we defined. I am not. Now we declare the StrategyEvaluationData
* structure that gets shipped around to help the planner and the access
* method decide what sort of scan it should do, based on (a) what the
* user asked for, (b) what operators are defined for a particular opclass,
* and (c) the reams of information we supplied above.
*
* The idea of all of this initialized data is to make life easier on the
* user when he defines a new operator class to use this access method.
* By filling in all the data, we let him get away with leaving holes in his
* operator class, and still let him use the index. The added complexity
* in the access methods just isn't worth the trouble, though.
*/
static StrategyEvaluationData RTEvaluationData = {
RTNStrategies, /* # of strategies */
(StrategyTransformMap) RTNegate, /* how to do (not qual) */
(StrategyTransformMap) RTCommute, /* how to swap operands */
(StrategyTransformMap) RTNegateCommute, /* how to do both */
{
NULL, /* express left */
NULL, /* express overleft */
NULL, /* express over */
NULL, /* express overright */
NULL, /* express right */
(StrategyExpression) RTEqualExpressionData, /* express same */
NULL, /* express contains */
NULL, /* express contained-by */
NULL,
NULL,
NULL
}
}; };
/* /*
* Okay, now something peculiar to rtrees that doesn't apply to most other * Now do the TermData arrays. These exist in case the user doesn't give
* indexing structures: When we're searching a tree for a given value, we * us a full set of operators for a particular operator class. The idea
* can't do the same sorts of comparisons on internal node entries as we * is that by making multiple comparisons using any one of the supplied
* do at leaves. The reason is that if we're looking for (say) all boxes * operators, we can decide whether two n-dimensional polygons are equal.
* that are the same as (0,0,10,10), then we need to find all leaf pages * For example, if a contains b and b contains a, we may conclude that
* that overlap that region. So internally we search for overlap, and at * a and b are equal.
* the leaf we search for equality.
* *
* This array maps leaf search operators to the internal search operators. * The presence of the TermData arrays in all this is a historical accident.
* We assume the normal ordering on operators: * Early in the development of the POSTGRES access methods, it was believed
* that writing functions was harder than writing arrays. This is wrong;
* TermData is hard to understand and hard to get right. In general, when
* someone populates a new operator class, the populate it completely. If
* Mike Hirohama had forced Cimarron Taylor to populate the strategy map
* for btree int2_ops completely in 1988, you wouldn't have to deal with
* all this now. Too bad for you.
* *
* left, left-or-overlap, overlap, right-or-overlap, right, same, * Since you can't necessarily do this in all cases (for example, you can't
* contains, contained-by * do it given only "intersects" or "disjoint"), TermData arrays for some
* operators don't appear below.
*
* Note that if you DO supply all the operators required in a given opclass
* by inserting them into the pg_opclass system catalog, you can get away
* without doing all this TermData stuff. Since the rtree code is intended
* to be a reference for access method implementors, I'm doing TermData
* correctly here.
*
* Note on style: these are all actually of type StrategyTermData, but
* since those have variable-length data at the end of the struct we can't
* properly initialize them if we declare them to be what they are.
*/
/* if you only have "contained-by", how do you determine equality? */
static uint16 RTContainedByTermData[] = {
2, /* make two comparisons */
RTContainedByStrategyNumber,/* use "a contained-by b" */
0x0, /* without any magic */
RTContainedByStrategyNumber,/* then use contained-by, */
SK_COMMUTE /* swapping a and b */
};
/* if you only have "contains", how do you determine equality? */
static uint16 RTContainsTermData[] = {
2, /* make two comparisons */
RTContainsStrategyNumber, /* use "a contains b" */
0x0, /* without any magic */
RTContainsStrategyNumber, /* then use contains again, */
SK_COMMUTE /* swapping a and b */
};
/* now put all that together in one place for the planner */
static StrategyTerm RTEqualExpressionData[] = {
(StrategyTerm) RTContainedByTermData,
(StrategyTerm) RTContainsTermData,
NULL
};
/*
* If you were sufficiently attentive to detail, you would go through
* the ExpressionData pain above for every one of the seven strategies
* we defined. I am not. Now we declare the StrategyEvaluationData
* structure that gets shipped around to help the planner and the access
* method decide what sort of scan it should do, based on (a) what the
* user asked for, (b) what operators are defined for a particular opclass,
* and (c) the reams of information we supplied above.
*
* The idea of all of this initialized data is to make life easier on the
* user when he defines a new operator class to use this access method.
* By filling in all the data, we let him get away with leaving holes in his
* operator class, and still let him use the index. The added complexity
* in the access methods just isn't worth the trouble, though.
*/
static StrategyEvaluationData RTEvaluationData = {
RTNStrategies, /* # of strategies */
(StrategyTransformMap) RTNegate, /* how to do (not qual) */
(StrategyTransformMap) RTCommute, /* how to swap operands */
(StrategyTransformMap) RTNegateCommute, /* how to do both */
{
NULL, /* express left */
NULL, /* express overleft */
NULL, /* express over */
NULL, /* express overright */
NULL, /* express right */
(StrategyExpression) RTEqualExpressionData, /* express same */
NULL, /* express contains */
NULL, /* express contained-by */
NULL,
NULL,
NULL
}
};
/*
* Okay, now something peculiar to rtrees that doesn't apply to most other
* indexing structures: When we're searching a tree for a given value, we
* can't do the same sorts of comparisons on internal node entries as we
* do at leaves. The reason is that if we're looking for (say) all boxes
* that are the same as (0,0,10,10), then we need to find all leaf pages
* that overlap that region. So internally we search for overlap, and at
* the leaf we search for equality.
*
* This array maps leaf search operators to the internal search operators.
* We assume the normal ordering on operators:
*
* left, left-or-overlap, overlap, right-or-overlap, right, same,
* contains, contained-by
*/ */
static StrategyNumber RTOperMap[RTNStrategies] = { static StrategyNumber RTOperMap[RTNStrategies] = {
RTOverLeftStrategyNumber, RTOverLeftStrategyNumber,
RTOverLeftStrategyNumber, RTOverLeftStrategyNumber,
RTOverlapStrategyNumber, RTOverlapStrategyNumber,
RTOverRightStrategyNumber, RTOverRightStrategyNumber,
RTOverRightStrategyNumber, RTOverRightStrategyNumber,
RTContainsStrategyNumber, RTContainsStrategyNumber,
RTContainsStrategyNumber, RTContainsStrategyNumber,
RTOverlapStrategyNumber RTOverlapStrategyNumber
}; };
static StrategyNumber static StrategyNumber
RelationGetRTStrategy(Relation r, RelationGetRTStrategy(Relation r,
AttrNumber attnum, AttrNumber attnum,
RegProcedure proc) RegProcedure proc)
{ {
return (RelationGetStrategy(r, attnum, &RTEvaluationData, proc)); return (RelationGetStrategy(r, attnum, &RTEvaluationData, proc));
} }
#ifdef NOT_USED #ifdef NOT_USED
bool bool
RelationInvokeRTStrategy(Relation r, RelationInvokeRTStrategy(Relation r,
AttrNumber attnum, AttrNumber attnum,
StrategyNumber s, StrategyNumber s,
Datum left, Datum left,
Datum right) Datum right)
{ {
return (RelationInvokeStrategy(r, &RTEvaluationData, attnum, s, return (RelationInvokeStrategy(r, &RTEvaluationData, attnum, s,
left, right)); left, right));
} }
#endif #endif
RegProcedure RegProcedure
RTMapOperator(Relation r, RTMapOperator(Relation r,
AttrNumber attnum, AttrNumber attnum,
RegProcedure proc) RegProcedure proc)
{ {
StrategyNumber procstrat; StrategyNumber procstrat;
StrategyMap strategyMap; StrategyMap strategyMap;
procstrat = RelationGetRTStrategy(r, attnum, proc); procstrat = RelationGetRTStrategy(r, attnum, proc);
strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(r), strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(r),
RTNStrategies, RTNStrategies,
attnum); attnum);
return (strategyMap->entry[RTOperMap[procstrat - 1] - 1].sk_procedure); return (strategyMap->entry[RTOperMap[procstrat - 1] - 1].sk_procedure);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,18 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* xid.c-- * xid.c--
* POSTGRES transaction identifier code. * POSTGRES transaction identifier code.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/xid.c,v 1.7 1997/08/19 21:30:20 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/xid.c,v 1.8 1997/09/07 04:39:40 momjian Exp $
* *
* OLD COMMENTS * OLD COMMENTS
* XXX WARNING * XXX WARNING
* Much of this file will change when we change our representation * Much of this file will change when we change our representation
* of transaction ids -cim 3/23/90 * of transaction ids -cim 3/23/90
* *
* It is time to make the switch from 5 byte to 4 byte transaction ids * It is time to make the switch from 5 byte to 4 byte transaction ids
* This file was totally reworked. -mer 5/22/92 * This file was totally reworked. -mer 5/22/92
@ -31,127 +31,127 @@ extern TransactionId AmiTransactionId;
extern TransactionId FirstTransactionId; extern TransactionId FirstTransactionId;
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* TransactionIdIsValid * TransactionIdIsValid
* *
* Macro-ize me. * Macro-ize me.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
bool bool
TransactionIdIsValid(TransactionId transactionId) TransactionIdIsValid(TransactionId transactionId)
{ {
return ((bool) (transactionId != NullTransactionId) ); return ((bool) (transactionId != NullTransactionId));
} }
/* XXX char16 name for catalogs */ /* XXX char16 name for catalogs */
TransactionId TransactionId
xidin(char *representation) xidin(char *representation)
{ {
return (atol(representation)); return (atol(representation));
} }
/* XXX char16 name for catalogs */ /* XXX char16 name for catalogs */
char* char *
xidout(TransactionId transactionId) xidout(TransactionId transactionId)
{ {
/* return(TransactionIdFormString(transactionId)); */ /* return(TransactionIdFormString(transactionId)); */
char *representation; char *representation;
/* maximum 32 bit unsigned integer representation takes 10 chars */ /* maximum 32 bit unsigned integer representation takes 10 chars */
representation = palloc(11); representation = palloc(11);
sprintf(representation, "%u", transactionId); sprintf(representation, "%u", transactionId);
return (representation); return (representation);
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* StoreInvalidTransactionId * StoreInvalidTransactionId
* *
* Maybe do away with Pointer types in these routines. * Maybe do away with Pointer types in these routines.
* Macro-ize this one. * Macro-ize this one.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
StoreInvalidTransactionId(TransactionId *destination) StoreInvalidTransactionId(TransactionId * destination)
{ {
*destination = NullTransactionId; *destination = NullTransactionId;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* TransactionIdStore * TransactionIdStore
* *
* Macro-ize this one. * Macro-ize this one.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
TransactionIdStore(TransactionId transactionId, TransactionIdStore(TransactionId transactionId,
TransactionId *destination) TransactionId * destination)
{ {
*destination = transactionId; *destination = transactionId;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* TransactionIdEquals * TransactionIdEquals
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
bool bool
TransactionIdEquals(TransactionId id1, TransactionId id2) TransactionIdEquals(TransactionId id1, TransactionId id2)
{ {
return ((bool) (id1 == id2)); return ((bool) (id1 == id2));
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* TransactionIdIsLessThan * TransactionIdIsLessThan
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
bool bool
TransactionIdIsLessThan(TransactionId id1, TransactionId id2) TransactionIdIsLessThan(TransactionId id1, TransactionId id2)
{ {
return ((bool)(id1 < id2)); return ((bool) (id1 < id2));
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* xideq * xideq
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
/* /*
* xideq - returns 1, iff xid1 == xid2 * xideq - returns 1, iff xid1 == xid2
* 0 else; * 0 else;
*/ */
bool bool
xideq(TransactionId xid1, TransactionId xid2) xideq(TransactionId xid1, TransactionId xid2)
{ {
return( (bool) (xid1 == xid2) ); return ((bool) (xid1 == xid2));
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* TransactionIdIncrement * TransactionIdIncrement
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
#ifdef NOT_USED #ifdef NOT_USED
void void
TransactionIdIncrement(TransactionId *transactionId) TransactionIdIncrement(TransactionId * transactionId)
{ {
(*transactionId)++; (*transactionId)++;
if (*transactionId == DisabledTransactionId) if (*transactionId == DisabledTransactionId)
elog(FATAL, "TransactionIdIncrement: exhausted XID's"); elog(FATAL, "TransactionIdIncrement: exhausted XID's");
return; return;
} }
#endif #endif
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* TransactionIdAdd * TransactionIdAdd
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
TransactionIdAdd(TransactionId *xid, int value) TransactionIdAdd(TransactionId * xid, int value)
{ {
*xid += value; *xid += value;
return; return;
} }

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/catalog.c,v 1.7 1997/08/18 20:51:59 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/catalog.c,v 1.8 1997/09/07 04:40:00 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -15,7 +15,7 @@
#include <postgres.h> #include <postgres.h>
#include <miscadmin.h> /* for DataDir */ #include <miscadmin.h> /* for DataDir */
#include <utils/syscache.h> #include <utils/syscache.h>
#include <catalog/catname.h> /* NameIs{,Shared}SystemRelationName */ #include <catalog/catname.h> /* NameIs{,Shared}SystemRelationName */
#include <catalog/pg_type.h> #include <catalog/pg_type.h>
@ -23,175 +23,188 @@
#include <access/transam.h> #include <access/transam.h>
/* /*
* relpath - path to the relation * relpath - path to the relation
* Perhaps this should be in-line code in relopen(). * Perhaps this should be in-line code in relopen().
*/ */
char * char *
relpath(char relname[]) relpath(char relname[])
{ {
char *path; char *path;
if (IsSharedSystemRelationName(relname)) { if (IsSharedSystemRelationName(relname))
path = (char *) palloc(strlen(DataDir) + sizeof(NameData) + 2); {
sprintf(path, "%s/%s", DataDir, relname); path = (char *) palloc(strlen(DataDir) + sizeof(NameData) + 2);
return (path); sprintf(path, "%s/%s", DataDir, relname);
} return (path);
return(relname); }
return (relname);
} }
#ifdef NOT_USED #ifdef NOT_USED
/* /*
* issystem - returns non-zero iff relname is a system catalog * issystem - returns non-zero iff relname is a system catalog
* *
* We now make a new requirement where system catalog relns must begin * We now make a new requirement where system catalog relns must begin
* with pg_ while user relns are forbidden to do so. Make the test * with pg_ while user relns are forbidden to do so. Make the test
* trivial and instantaneous. * trivial and instantaneous.
* *
* XXX this is way bogus. -- pma * XXX this is way bogus. -- pma
*/ */
bool bool
issystem(char relname[]) issystem(char relname[])
{ {
if (relname[0] && relname[1] && relname[2]) if (relname[0] && relname[1] && relname[2])
return (relname[0] == 'p' && return (relname[0] == 'p' &&
relname[1] == 'g' && relname[1] == 'g' &&
relname[2] == '_'); relname[2] == '_');
else else
return FALSE; return FALSE;
} }
#endif #endif
/* /*
* IsSystemRelationName -- * IsSystemRelationName --
* True iff name is the name of a system catalog relation. * True iff name is the name of a system catalog relation.
* *
* We now make a new requirement where system catalog relns must begin * We now make a new requirement where system catalog relns must begin
* with pg_ while user relns are forbidden to do so. Make the test * with pg_ while user relns are forbidden to do so. Make the test
* trivial and instantaneous. * trivial and instantaneous.
* *
* XXX this is way bogus. -- pma * XXX this is way bogus. -- pma
*/ */
bool bool
IsSystemRelationName(char *relname) IsSystemRelationName(char *relname)
{ {
if (relname[0] && relname[1] && relname[2]) if (relname[0] && relname[1] && relname[2])
return (relname[0] == 'p' && return (relname[0] == 'p' &&
relname[1] == 'g' && relname[1] == 'g' &&
relname[2] == '_'); relname[2] == '_');
else else
return FALSE; return FALSE;
} }
/* /*
* IsSharedSystemRelationName -- * IsSharedSystemRelationName --
* True iff name is the name of a shared system catalog relation. * True iff name is the name of a shared system catalog relation.
*/ */
bool bool
IsSharedSystemRelationName(char *relname) IsSharedSystemRelationName(char *relname)
{ {
int i; int i;
/* /*
* Quick out: if it's not a system relation, it can't be a shared * Quick out: if it's not a system relation, it can't be a shared
* system relation. * system relation.
*/ */
if (!IsSystemRelationName(relname)) if (!IsSystemRelationName(relname))
return FALSE;
i = 0;
while (SharedSystemRelationNames[i] != NULL)
{
if (strcmp(SharedSystemRelationNames[i], relname) == 0)
return TRUE;
i++;
}
return FALSE; return FALSE;
i = 0;
while ( SharedSystemRelationNames[i] != NULL) {
if (strcmp(SharedSystemRelationNames[i],relname) == 0)
return TRUE;
i++;
}
return FALSE;
} }
/* /*
* newoid - returns a unique identifier across all catalogs. * newoid - returns a unique identifier across all catalogs.
* *
* Object Id allocation is now done by GetNewObjectID in * Object Id allocation is now done by GetNewObjectID in
* access/transam/varsup.c. oids are now allocated correctly. * access/transam/varsup.c. oids are now allocated correctly.
* *
* old comments: * old comments:
* This needs to change soon, it fails if there are too many more * This needs to change soon, it fails if there are too many more
* than one call per second when postgres restarts after it dies. * than one call per second when postgres restarts after it dies.
* *
* The distribution of OID's should be done by the POSTMASTER. * The distribution of OID's should be done by the POSTMASTER.
* Also there needs to be a facility to preallocate OID's. Ie., * Also there needs to be a facility to preallocate OID's. Ie.,
* for a block of OID's to be declared as invalid ones to allow * for a block of OID's to be declared as invalid ones to allow
* user programs to use them for temporary object identifiers. * user programs to use them for temporary object identifiers.
*/ */
Oid newoid() Oid
newoid()
{ {
Oid lastoid; Oid lastoid;
GetNewObjectId(&lastoid); GetNewObjectId(&lastoid);
if (! OidIsValid(lastoid)) if (!OidIsValid(lastoid))
elog(WARN, "newoid: GetNewObjectId returns invalid oid"); elog(WARN, "newoid: GetNewObjectId returns invalid oid");
return lastoid; return lastoid;
} }
/* /*
* fillatt - fills the ATTRIBUTE relation fields from the TYP * fillatt - fills the ATTRIBUTE relation fields from the TYP
* *
* Expects that the atttypid domain is set for each att[]. * Expects that the atttypid domain is set for each att[].
* Returns with the attnum, and attlen domains set. * Returns with the attnum, and attlen domains set.
* attnum, attproc, atttyparg, ... should be set by the user. * attnum, attproc, atttyparg, ... should be set by the user.
* *
* In the future, attnum may not be set?!? or may be passed as an arg?!? * In the future, attnum may not be set?!? or may be passed as an arg?!?
* *
* Current implementation is very inefficient--should cashe the * Current implementation is very inefficient--should cashe the
* information if this is at all possible. * information if this is at all possible.
* *
* Check to see if this is really needed, and especially in the case * Check to see if this is really needed, and especially in the case
* of index tuples. * of index tuples.
*/ */
void void
fillatt(TupleDesc tupleDesc) fillatt(TupleDesc tupleDesc)
{ {
AttributeTupleForm *attributeP; AttributeTupleForm *attributeP;
register TypeTupleForm typp; register TypeTupleForm typp;
HeapTuple tuple; HeapTuple tuple;
int i; int i;
int natts = tupleDesc->natts; int natts = tupleDesc->natts;
AttributeTupleForm *att = tupleDesc->attrs; AttributeTupleForm *att = tupleDesc->attrs;
if (natts < 0 || natts > MaxHeapAttributeNumber) if (natts < 0 || natts > MaxHeapAttributeNumber)
elog(WARN, "fillatt: %d attributes is too large", natts); elog(WARN, "fillatt: %d attributes is too large", natts);
if (natts == 0) { if (natts == 0)
elog(DEBUG, "fillatt: called with natts == 0"); {
return; elog(DEBUG, "fillatt: called with natts == 0");
} return;
}
attributeP = &att[0];
attributeP = &att[0];
for (i = 0; i < natts;) {
tuple = SearchSysCacheTuple(TYPOID, for (i = 0; i < natts;)
Int32GetDatum((*attributeP)->atttypid), {
0,0,0); tuple = SearchSysCacheTuple(TYPOID,
if (!HeapTupleIsValid(tuple)) { Int32GetDatum((*attributeP)->atttypid),
elog(WARN, "fillatt: unknown atttypid %ld", 0, 0, 0);
(*attributeP)->atttypid); if (!HeapTupleIsValid(tuple))
} else { {
(*attributeP)->attnum = (int16) ++i; elog(WARN, "fillatt: unknown atttypid %ld",
/* Check if the attr is a set before messing with the length (*attributeP)->atttypid);
and byval, since those were already set in }
TupleDescInitEntry. In fact, this seems redundant else
here, but who knows what I'll break if I take it out... {
(*attributeP)->attnum = (int16)++ i;
same for char() and varchar() stuff. I share the same
sentiments. This function is poorly written anyway. -ay 6/95 /*
*/ * Check if the attr is a set before messing with the length
if (!(*attributeP)->attisset && * and byval, since those were already set in
(*attributeP)->atttypid!=BPCHAROID && * TupleDescInitEntry. In fact, this seems redundant here,
(*attributeP)->atttypid!=VARCHAROID) { * but who knows what I'll break if I take it out...
*
typp = (TypeTupleForm) GETSTRUCT(tuple); /* XXX */ * same for char() and varchar() stuff. I share the same
(*attributeP)->attlen = typp->typlen; * sentiments. This function is poorly written anyway. -ay
(*attributeP)->attbyval = typp->typbyval; * 6/95
} */
if (!(*attributeP)->attisset &&
(*attributeP)->atttypid != BPCHAROID &&
(*attributeP)->atttypid != VARCHAROID)
{
typp = (TypeTupleForm) GETSTRUCT(tuple); /* XXX */
(*attributeP)->attlen = typp->typlen;
(*attributeP)->attbyval = typp->typbyval;
}
}
attributeP += 1;
} }
attributeP += 1;
}
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,14 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* indexing.c-- * indexing.c--
* This file contains routines to support indices defined on system * This file contains routines to support indices defined on system
* catalogs. * catalogs.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.11 1997/08/31 09:56:18 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.12 1997/09/07 04:40:21 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -34,36 +34,37 @@
/* /*
* Names of indices on the following system catalogs: * Names of indices on the following system catalogs:
* *
* pg_attribute * pg_attribute
* pg_proc * pg_proc
* pg_type * pg_type
* pg_naming * pg_naming
* pg_class * pg_class
* pg_attrdef * pg_attrdef
* pg_relcheck * pg_relcheck
* pg_trigger * pg_trigger
*/ */
char *Name_pg_attr_indices[Num_pg_attr_indices] = {AttributeNameIndex, char *Name_pg_attr_indices[Num_pg_attr_indices] = {AttributeNameIndex,
AttributeNumIndex, AttributeNumIndex,
AttributeRelidIndex}; AttributeRelidIndex};
char *Name_pg_proc_indices[Num_pg_proc_indices] = { ProcedureNameIndex, char *Name_pg_proc_indices[Num_pg_proc_indices] = {ProcedureNameIndex,
ProcedureOidIndex, ProcedureOidIndex,
ProcedureSrcIndex}; ProcedureSrcIndex};
char *Name_pg_type_indices[Num_pg_type_indices] = { TypeNameIndex, char *Name_pg_type_indices[Num_pg_type_indices] = {TypeNameIndex,
TypeOidIndex}; TypeOidIndex};
char *Name_pg_class_indices[Num_pg_class_indices]= { ClassNameIndex, char *Name_pg_class_indices[Num_pg_class_indices] = {ClassNameIndex,
ClassOidIndex}; ClassOidIndex};
char *Name_pg_attrdef_indices[Num_pg_attrdef_indices]= { AttrDefaultIndex }; char *Name_pg_attrdef_indices[Num_pg_attrdef_indices] = {AttrDefaultIndex};
char *Name_pg_relcheck_indices[Num_pg_relcheck_indices]= { RelCheckIndex }; char *Name_pg_relcheck_indices[Num_pg_relcheck_indices] = {RelCheckIndex};
char *Name_pg_trigger_indices[Num_pg_trigger_indices]= { TriggerRelidIndex }; char *Name_pg_trigger_indices[Num_pg_trigger_indices] = {TriggerRelidIndex};
static HeapTuple CatalogIndexFetchTuple(Relation heapRelation, static HeapTuple
Relation idesc, CatalogIndexFetchTuple(Relation heapRelation,
ScanKey skey); Relation idesc,
ScanKey skey);
/* /*
@ -75,11 +76,11 @@ static HeapTuple CatalogIndexFetchTuple(Relation heapRelation,
void void
CatalogOpenIndices(int nIndices, char *names[], Relation idescs[]) CatalogOpenIndices(int nIndices, char *names[], Relation idescs[])
{ {
int i; int i;
for (i=0; i<nIndices; i++) for (i = 0; i < nIndices; i++)
{ {
idescs[i] = index_openr(names[i]); idescs[i] = index_openr(names[i]);
} }
} }
@ -87,12 +88,12 @@ CatalogOpenIndices(int nIndices, char *names[], Relation idescs[])
* This is the inverse routine to CatalogOpenIndices() * This is the inverse routine to CatalogOpenIndices()
*/ */
void void
CatalogCloseIndices(int nIndices, Relation *idescs) CatalogCloseIndices(int nIndices, Relation * idescs)
{ {
int i; int i;
for (i=0; i<nIndices; i++) for (i = 0; i < nIndices; i++)
index_close(idescs[i]); index_close(idescs[i]);
} }
@ -102,68 +103,69 @@ CatalogCloseIndices(int nIndices, Relation *idescs)
* each catalog index. * each catalog index.
*/ */
void void
CatalogIndexInsert(Relation *idescs, CatalogIndexInsert(Relation * idescs,
int nIndices, int nIndices,
Relation heapRelation, Relation heapRelation,
HeapTuple heapTuple) HeapTuple heapTuple)
{ {
HeapTuple pgIndexTup; HeapTuple pgIndexTup;
TupleDesc heapDescriptor; TupleDesc heapDescriptor;
IndexTupleForm pgIndexP; IndexTupleForm pgIndexP;
Datum datum; Datum datum;
int natts; int natts;
AttrNumber *attnumP; AttrNumber *attnumP;
FuncIndexInfo finfo, *finfoP; FuncIndexInfo finfo,
char nulls[INDEX_MAX_KEYS]; *finfoP;
int i; char nulls[INDEX_MAX_KEYS];
int i;
heapDescriptor = RelationGetTupleDescriptor(heapRelation); heapDescriptor = RelationGetTupleDescriptor(heapRelation);
for (i=0; i<nIndices; i++) for (i = 0; i < nIndices; i++)
{ {
TupleDesc indexDescriptor; TupleDesc indexDescriptor;
InsertIndexResult indexRes; InsertIndexResult indexRes;
indexDescriptor = RelationGetTupleDescriptor(idescs[i]); indexDescriptor = RelationGetTupleDescriptor(idescs[i]);
pgIndexTup = SearchSysCacheTuple(INDEXRELID, pgIndexTup = SearchSysCacheTuple(INDEXRELID,
Int32GetDatum(idescs[i]->rd_id), Int32GetDatum(idescs[i]->rd_id),
0,0,0); 0, 0, 0);
Assert(pgIndexTup); Assert(pgIndexTup);
pgIndexP = (IndexTupleForm)GETSTRUCT(pgIndexTup); pgIndexP = (IndexTupleForm) GETSTRUCT(pgIndexTup);
/* /*
* Compute the number of attributes we are indexing upon. * Compute the number of attributes we are indexing upon. very
* very important - can't assume one if this is a functional * important - can't assume one if this is a functional index.
* index. */
*/ for (attnumP = (&pgIndexP->indkey[0]), natts = 0;
for (attnumP=(&pgIndexP->indkey[0]), natts=0; *attnumP != InvalidAttrNumber;
*attnumP != InvalidAttrNumber; attnumP++, natts++)
attnumP++, natts++) ;
;
if (pgIndexP->indproc != InvalidOid) if (pgIndexP->indproc != InvalidOid)
{ {
FIgetnArgs(&finfo) = natts; FIgetnArgs(&finfo) = natts;
natts = 1; natts = 1;
FIgetProcOid(&finfo) = pgIndexP->indproc; FIgetProcOid(&finfo) = pgIndexP->indproc;
*(FIgetname(&finfo)) = '\0'; *(FIgetname(&finfo)) = '\0';
finfoP = &finfo; finfoP = &finfo;
} }
else else
finfoP = (FuncIndexInfo *)NULL; finfoP = (FuncIndexInfo *) NULL;
FormIndexDatum(natts, FormIndexDatum(natts,
(AttrNumber *)&pgIndexP->indkey[0], (AttrNumber *) & pgIndexP->indkey[0],
heapTuple, heapTuple,
heapDescriptor, heapDescriptor,
InvalidBuffer, InvalidBuffer,
&datum, &datum,
nulls, nulls,
finfoP); finfoP);
indexRes = index_insert(idescs[i], &datum, nulls, indexRes = index_insert(idescs[i], &datum, nulls,
&(heapTuple->t_ctid), heapRelation); &(heapTuple->t_ctid), heapRelation);
if (indexRes) pfree(indexRes); if (indexRes)
pfree(indexRes);
} }
} }
@ -174,81 +176,88 @@ CatalogIndexInsert(Relation *idescs,
bool bool
CatalogHasIndex(char *catName, Oid catId) CatalogHasIndex(char *catName, Oid catId)
{ {
Relation pg_class; Relation pg_class;
HeapTuple htup; HeapTuple htup;
Form_pg_class pgRelP; Form_pg_class pgRelP;
int i; int i;
Assert(IsSystemRelationName(catName)); Assert(IsSystemRelationName(catName));
/* /*
* If we're bootstraping we don't have pg_class (or any indices). * If we're bootstraping we don't have pg_class (or any indices).
*/ */
if (IsBootstrapProcessingMode()) if (IsBootstrapProcessingMode())
return false; return false;
if (IsInitProcessingMode()) { if (IsInitProcessingMode())
for (i = 0; IndexedCatalogNames[i] != NULL; i++) { {
if ( strcmp(IndexedCatalogNames[i], catName) == 0) for (i = 0; IndexedCatalogNames[i] != NULL; i++)
return (true); {
if (strcmp(IndexedCatalogNames[i], catName) == 0)
return (true);
}
return (false);
} }
return (false);
}
pg_class = heap_openr(RelationRelationName); pg_class = heap_openr(RelationRelationName);
htup = ClassOidIndexScan(pg_class, catId); htup = ClassOidIndexScan(pg_class, catId);
heap_close(pg_class); heap_close(pg_class);
if (! HeapTupleIsValid(htup)) { if (!HeapTupleIsValid(htup))
elog(NOTICE, "CatalogHasIndex: no relation with oid %d", catId); {
return false; elog(NOTICE, "CatalogHasIndex: no relation with oid %d", catId);
} return false;
}
pgRelP = (Form_pg_class)GETSTRUCT(htup); pgRelP = (Form_pg_class) GETSTRUCT(htup);
return (pgRelP->relhasindex); return (pgRelP->relhasindex);
} }
/* /*
* CatalogIndexFetchTuple() -- Get a tuple that satisfies a scan key * CatalogIndexFetchTuple() -- Get a tuple that satisfies a scan key
* from a catalog relation. * from a catalog relation.
* *
* Since the index may contain pointers to dead tuples, we need to * Since the index may contain pointers to dead tuples, we need to
* iterate until we find a tuple that's valid and satisfies the scan * iterate until we find a tuple that's valid and satisfies the scan
* key. * key.
*/ */
static HeapTuple static HeapTuple
CatalogIndexFetchTuple(Relation heapRelation, CatalogIndexFetchTuple(Relation heapRelation,
Relation idesc, Relation idesc,
ScanKey skey) ScanKey skey)
{ {
IndexScanDesc sd; IndexScanDesc sd;
RetrieveIndexResult indexRes; RetrieveIndexResult indexRes;
HeapTuple tuple; HeapTuple tuple;
Buffer buffer; Buffer buffer;
sd = index_beginscan(idesc, false, 1, skey); sd = index_beginscan(idesc, false, 1, skey);
tuple = (HeapTuple)NULL; tuple = (HeapTuple) NULL;
do { do
indexRes = index_getnext(sd, ForwardScanDirection); {
if (indexRes) { indexRes = index_getnext(sd, ForwardScanDirection);
ItemPointer iptr; if (indexRes)
{
ItemPointer iptr;
iptr = &indexRes->heap_iptr; iptr = &indexRes->heap_iptr;
tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer); tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
pfree(indexRes); pfree(indexRes);
} else }
break; else
} while (!HeapTupleIsValid(tuple)); break;
} while (!HeapTupleIsValid(tuple));
if (HeapTupleIsValid(tuple)) { if (HeapTupleIsValid(tuple))
tuple = heap_copytuple(tuple); {
ReleaseBuffer(buffer); tuple = heap_copytuple(tuple);
} ReleaseBuffer(buffer);
}
index_endscan(sd); index_endscan(sd);
pfree(sd); pfree(sd);
return (tuple); return (tuple);
} }
/* /*
@ -259,276 +268,295 @@ CatalogIndexFetchTuple(Relation heapRelation,
*/ */
HeapTuple HeapTuple
AttributeNameIndexScan(Relation heapRelation, AttributeNameIndexScan(Relation heapRelation,
Oid relid, Oid relid,
char *attname) char *attname)
{ {
Relation idesc; Relation idesc;
ScanKeyData skey; ScanKeyData skey;
OidName keyarg; OidName keyarg;
HeapTuple tuple; HeapTuple tuple;
keyarg = mkoidname(relid, attname); keyarg = mkoidname(relid, attname);
ScanKeyEntryInitialize(&skey, ScanKeyEntryInitialize(&skey,
(bits16)0x0, (bits16) 0x0,
(AttrNumber)1, (AttrNumber) 1,
(RegProcedure)OidNameEqRegProcedure, (RegProcedure) OidNameEqRegProcedure,
(Datum)keyarg); (Datum) keyarg);
idesc = index_openr(AttributeNameIndex); idesc = index_openr(AttributeNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc); index_close(idesc);
pfree(keyarg); pfree(keyarg);
return tuple; return tuple;
} }
HeapTuple HeapTuple
AttributeNumIndexScan(Relation heapRelation, AttributeNumIndexScan(Relation heapRelation,
Oid relid, Oid relid,
AttrNumber attnum) AttrNumber attnum)
{ {
Relation idesc; Relation idesc;
ScanKeyData skey; ScanKeyData skey;
OidInt2 keyarg; OidInt2 keyarg;
HeapTuple tuple; HeapTuple tuple;
keyarg = mkoidint2(relid, (uint16)attnum); keyarg = mkoidint2(relid, (uint16) attnum);
ScanKeyEntryInitialize(&skey, ScanKeyEntryInitialize(&skey,
(bits16)0x0, (bits16) 0x0,
(AttrNumber)1, (AttrNumber) 1,
(RegProcedure)OidInt2EqRegProcedure, (RegProcedure) OidInt2EqRegProcedure,
(Datum)keyarg); (Datum) keyarg);
idesc = index_openr(AttributeNumIndex); idesc = index_openr(AttributeNumIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc); index_close(idesc);
pfree(keyarg); pfree(keyarg);
return tuple; return tuple;
} }
HeapTuple HeapTuple
ProcedureOidIndexScan(Relation heapRelation, Oid procId) ProcedureOidIndexScan(Relation heapRelation, Oid procId)
{ {
Relation idesc; Relation idesc;
ScanKeyData skey; ScanKeyData skey;
HeapTuple tuple; HeapTuple tuple;
ScanKeyEntryInitialize(&skey, ScanKeyEntryInitialize(&skey,
(bits16)0x0, (bits16) 0x0,
(AttrNumber)1, (AttrNumber) 1,
(RegProcedure)ObjectIdEqualRegProcedure, (RegProcedure) ObjectIdEqualRegProcedure,
(Datum)procId); (Datum) procId);
idesc = index_openr(ProcedureOidIndex); idesc = index_openr(ProcedureOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc); index_close(idesc);
return tuple; return tuple;
} }
HeapTuple HeapTuple
ProcedureNameIndexScan(Relation heapRelation, ProcedureNameIndexScan(Relation heapRelation,
char *procName, char *procName,
int nargs, int nargs,
Oid *argTypes) Oid * argTypes)
{ {
Relation idesc; Relation idesc;
ScanKeyData skey; ScanKeyData skey;
HeapTuple tuple; /* tuple being tested */ HeapTuple tuple; /* tuple being tested */
HeapTuple return_tuple; /* The tuple pointer we eventually return */ HeapTuple return_tuple; /* The tuple pointer we eventually
IndexScanDesc sd; * return */
RetrieveIndexResult indexRes; IndexScanDesc sd;
Buffer buffer; RetrieveIndexResult indexRes;
Form_pg_proc pgProcP; Buffer buffer;
bool ScanComplete; Form_pg_proc pgProcP;
/* The index scan is complete, i.e. we've scanned everything there bool ScanComplete;
is to scan.
*/
bool FoundMatch;
/* In scanning pg_proc, we have found a row that meets our search
criteria.
*/
ScanKeyEntryInitialize(&skey, /*
(bits16)0x0, * The index scan is complete, i.e. we've scanned everything there is
(AttrNumber)1, * to scan.
(RegProcedure)NameEqualRegProcedure, */
(Datum)procName); bool FoundMatch;
idesc = index_openr(ProcedureNameIndex); /*
* In scanning pg_proc, we have found a row that meets our search
* criteria.
*/
sd = index_beginscan(idesc, false, 1, &skey); ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) NameEqualRegProcedure,
(Datum) procName);
/* idesc = index_openr(ProcedureNameIndex);
* for now, we do the work usually done by CatalogIndexFetchTuple
* by hand, so that we can check that the other keys match. when
* multi-key indices are added, they will be used here.
*/
tuple = (HeapTuple) NULL; /* initial value */
ScanComplete = false; /* Scan hasn't begun yet */
FoundMatch = false; /* No match yet; haven't even looked. */
while (!FoundMatch && !ScanComplete) {
indexRes = index_getnext(sd, ForwardScanDirection);
if (indexRes) {
ItemPointer iptr;
iptr = &indexRes->heap_iptr; sd = index_beginscan(idesc, false, 1, &skey);
tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
pfree(indexRes);
if (HeapTupleIsValid(tuple)) {
/* Here's a row for a procedure that has the sought procedure
name. To be a match, though, we need it to have the
right number and type of arguments too, so we check that
now.
*/
pgProcP = (Form_pg_proc)GETSTRUCT(tuple);
if (pgProcP->pronargs == nargs &&
oid8eq(&(pgProcP->proargtypes[0]), argTypes))
FoundMatch = true;
else ReleaseBuffer(buffer);
}
} else ScanComplete = true;
}
if (FoundMatch) { /*
Assert(HeapTupleIsValid(tuple)); * for now, we do the work usually done by CatalogIndexFetchTuple by
return_tuple = heap_copytuple(tuple); * hand, so that we can check that the other keys match. when
ReleaseBuffer(buffer); * multi-key indices are added, they will be used here.
} else return_tuple = (HeapTuple)NULL; */
tuple = (HeapTuple) NULL; /* initial value */
ScanComplete = false; /* Scan hasn't begun yet */
FoundMatch = false; /* No match yet; haven't even looked. */
while (!FoundMatch && !ScanComplete)
{
indexRes = index_getnext(sd, ForwardScanDirection);
if (indexRes)
{
ItemPointer iptr;
index_endscan(sd); iptr = &indexRes->heap_iptr;
index_close(idesc); tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
pfree(indexRes);
if (HeapTupleIsValid(tuple))
{
return return_tuple; /*
* Here's a row for a procedure that has the sought
* procedure name. To be a match, though, we need it to
* have the right number and type of arguments too, so we
* check that now.
*/
pgProcP = (Form_pg_proc) GETSTRUCT(tuple);
if (pgProcP->pronargs == nargs &&
oid8eq(&(pgProcP->proargtypes[0]), argTypes))
FoundMatch = true;
else
ReleaseBuffer(buffer);
}
}
else
ScanComplete = true;
}
if (FoundMatch)
{
Assert(HeapTupleIsValid(tuple));
return_tuple = heap_copytuple(tuple);
ReleaseBuffer(buffer);
}
else
return_tuple = (HeapTuple) NULL;
index_endscan(sd);
index_close(idesc);
return return_tuple;
} }
HeapTuple HeapTuple
ProcedureSrcIndexScan(Relation heapRelation, text *procSrc) ProcedureSrcIndexScan(Relation heapRelation, text * procSrc)
{ {
Relation idesc; Relation idesc;
IndexScanDesc sd; IndexScanDesc sd;
ScanKeyData skey; ScanKeyData skey;
RetrieveIndexResult indexRes; RetrieveIndexResult indexRes;
HeapTuple tuple; HeapTuple tuple;
Buffer buffer; Buffer buffer;
ScanKeyEntryInitialize(&skey, ScanKeyEntryInitialize(&skey,
(bits16)0x0, (bits16) 0x0,
(AttrNumber)Anum_pg_proc_prosrc, (AttrNumber) Anum_pg_proc_prosrc,
(RegProcedure)TextEqualRegProcedure, (RegProcedure) TextEqualRegProcedure,
(Datum)procSrc); (Datum) procSrc);
idesc = index_openr(ProcedureSrcIndex); idesc = index_openr(ProcedureSrcIndex);
sd = index_beginscan(idesc, false, 1, &skey); sd = index_beginscan(idesc, false, 1, &skey);
indexRes = index_getnext(sd, ForwardScanDirection); indexRes = index_getnext(sd, ForwardScanDirection);
if (indexRes) { if (indexRes)
ItemPointer iptr; {
ItemPointer iptr;
iptr = &indexRes->heap_iptr; iptr = &indexRes->heap_iptr;
tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer); tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
pfree(indexRes); pfree(indexRes);
} else }
tuple = (HeapTuple)NULL; else
tuple = (HeapTuple) NULL;
if (HeapTupleIsValid(tuple)) { if (HeapTupleIsValid(tuple))
tuple = heap_copytuple(tuple); {
ReleaseBuffer(buffer); tuple = heap_copytuple(tuple);
} ReleaseBuffer(buffer);
}
index_endscan(sd); index_endscan(sd);
return tuple; return tuple;
} }
HeapTuple HeapTuple
TypeOidIndexScan(Relation heapRelation, Oid typeId) TypeOidIndexScan(Relation heapRelation, Oid typeId)
{ {
Relation idesc; Relation idesc;
ScanKeyData skey; ScanKeyData skey;
HeapTuple tuple; HeapTuple tuple;
ScanKeyEntryInitialize(&skey, ScanKeyEntryInitialize(&skey,
(bits16)0x0, (bits16) 0x0,
(AttrNumber)1, (AttrNumber) 1,
(RegProcedure)ObjectIdEqualRegProcedure, (RegProcedure) ObjectIdEqualRegProcedure,
(Datum)typeId); (Datum) typeId);
idesc = index_openr(TypeOidIndex); idesc = index_openr(TypeOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc); index_close(idesc);
return tuple; return tuple;
} }
HeapTuple HeapTuple
TypeNameIndexScan(Relation heapRelation, char *typeName) TypeNameIndexScan(Relation heapRelation, char *typeName)
{ {
Relation idesc; Relation idesc;
ScanKeyData skey; ScanKeyData skey;
HeapTuple tuple; HeapTuple tuple;
ScanKeyEntryInitialize(&skey, ScanKeyEntryInitialize(&skey,
(bits16)0x0, (bits16) 0x0,
(AttrNumber)1, (AttrNumber) 1,
(RegProcedure)NameEqualRegProcedure, (RegProcedure) NameEqualRegProcedure,
(Datum)typeName); (Datum) typeName);
idesc = index_openr(TypeNameIndex); idesc = index_openr(TypeNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc); index_close(idesc);
return tuple; return tuple;
} }
HeapTuple HeapTuple
ClassNameIndexScan(Relation heapRelation, char *relName) ClassNameIndexScan(Relation heapRelation, char *relName)
{ {
Relation idesc; Relation idesc;
ScanKeyData skey; ScanKeyData skey;
HeapTuple tuple; HeapTuple tuple;
ScanKeyEntryInitialize(&skey, ScanKeyEntryInitialize(&skey,
(bits16)0x0, (bits16) 0x0,
(AttrNumber)1, (AttrNumber) 1,
(RegProcedure)NameEqualRegProcedure, (RegProcedure) NameEqualRegProcedure,
(Datum)relName); (Datum) relName);
idesc = index_openr(ClassNameIndex); idesc = index_openr(ClassNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc); index_close(idesc);
return tuple; return tuple;
} }
HeapTuple HeapTuple
ClassOidIndexScan(Relation heapRelation, Oid relId) ClassOidIndexScan(Relation heapRelation, Oid relId)
{ {
Relation idesc; Relation idesc;
ScanKeyData skey; ScanKeyData skey;
HeapTuple tuple; HeapTuple tuple;
ScanKeyEntryInitialize(&skey, ScanKeyEntryInitialize(&skey,
(bits16)0x0, (bits16) 0x0,
(AttrNumber)1, (AttrNumber) 1,
(RegProcedure)ObjectIdEqualRegProcedure, (RegProcedure) ObjectIdEqualRegProcedure,
(Datum)relId); (Datum) relId);
idesc = index_openr(ClassOidIndex); idesc = index_openr(ClassOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc); index_close(idesc);
return tuple; return tuple;
} }

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* pg_aggregate.c-- * pg_aggregate.c--
* routines to support manipulation of the pg_aggregate relation * routines to support manipulation of the pg_aggregate relation
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.5 1997/07/24 20:11:47 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.6 1997/09/07 04:40:24 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -24,9 +24,9 @@
#include <catalog/pg_aggregate.h> #include <catalog/pg_aggregate.h>
#include <miscadmin.h> #include <miscadmin.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
/* ---------------- /* ----------------
@ -38,284 +38,299 @@
* the inheritance hierarchy * the inheritance hierarchy
* *
* OLD COMMENTS: * OLD COMMENTS:
* Currently, redefining aggregates using the same name is not * Currently, redefining aggregates using the same name is not
* supported. In such a case, a warning is printed that the * supported. In such a case, a warning is printed that the
* aggregate already exists. If such is not the case, a new tuple * aggregate already exists. If such is not the case, a new tuple
* is created and inserted in the aggregate relation. The fields * is created and inserted in the aggregate relation. The fields
* of this tuple are aggregate name, owner id, 2 transition functions * of this tuple are aggregate name, owner id, 2 transition functions
* (called aggtransfn1 and aggtransfn2), final function (aggfinalfn), * (called aggtransfn1 and aggtransfn2), final function (aggfinalfn),
* type of data on which aggtransfn1 operates (aggbasetype), return * type of data on which aggtransfn1 operates (aggbasetype), return
* types of the two transition functions (aggtranstype1 and * types of the two transition functions (aggtranstype1 and
* aggtranstype2), final return type (aggfinaltype), and initial values * aggtranstype2), final return type (aggfinaltype), and initial values
* for the two state transition functions (agginitval1 and agginitval2). * for the two state transition functions (agginitval1 and agginitval2).
* All types and functions must have been defined * All types and functions must have been defined
* prior to defining the aggregate. * prior to defining the aggregate.
* *
* --------------- * ---------------
*/ */
void void
AggregateCreate(char *aggName, AggregateCreate(char *aggName,
char *aggtransfn1Name, char *aggtransfn1Name,
char *aggtransfn2Name, char *aggtransfn2Name,
char *aggfinalfnName, char *aggfinalfnName,
char *aggbasetypeName, char *aggbasetypeName,
char *aggtransfn1typeName, char *aggtransfn1typeName,
char *aggtransfn2typeName, char *aggtransfn2typeName,
char *agginitval1, char *agginitval1,
char *agginitval2) char *agginitval2)
{ {
register i; register i;
Relation aggdesc; Relation aggdesc;
HeapTuple tup; HeapTuple tup;
char nulls[Natts_pg_aggregate]; char nulls[Natts_pg_aggregate];
Datum values[Natts_pg_aggregate]; Datum values[Natts_pg_aggregate];
Form_pg_proc proc; Form_pg_proc proc;
Oid xfn1 = InvalidOid; Oid xfn1 = InvalidOid;
Oid xfn2 = InvalidOid; Oid xfn2 = InvalidOid;
Oid ffn = InvalidOid; Oid ffn = InvalidOid;
Oid xbase = InvalidOid; Oid xbase = InvalidOid;
Oid xret1 = InvalidOid; Oid xret1 = InvalidOid;
Oid xret2 = InvalidOid; Oid xret2 = InvalidOid;
Oid fret = InvalidOid; Oid fret = InvalidOid;
Oid fnArgs[8]; Oid fnArgs[8];
TupleDesc tupDesc; TupleDesc tupDesc;
memset(fnArgs, 0, 8 * sizeof(Oid)); memset(fnArgs, 0, 8 * sizeof(Oid));
/* sanity checks */ /* sanity checks */
if (!aggName) if (!aggName)
elog(WARN, "AggregateCreate: no aggregate name supplied"); elog(WARN, "AggregateCreate: no aggregate name supplied");
if (!aggtransfn1Name && !aggtransfn2Name) if (!aggtransfn1Name && !aggtransfn2Name)
elog(WARN, "AggregateCreate: aggregate must have at least one transition function"); elog(WARN, "AggregateCreate: aggregate must have at least one transition function");
tup = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(aggbasetypeName),
0,0,0);
if(!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: Type '%s' undefined",aggbasetypeName);
xbase = tup->t_oid;
if (aggtransfn1Name) {
tup = SearchSysCacheTuple(TYPNAME, tup = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(aggtransfn1typeName), PointerGetDatum(aggbasetypeName),
0,0,0); 0, 0, 0);
if(!HeapTupleIsValid(tup)) if (!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: Type '%s' undefined", elog(WARN, "AggregateCreate: Type '%s' undefined", aggbasetypeName);
aggtransfn1typeName); xbase = tup->t_oid;
xret1 = tup->t_oid;
fnArgs[0] = xret1; if (aggtransfn1Name)
fnArgs[1] = xbase; {
tup = SearchSysCacheTuple(PRONAME, tup = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(aggtransfn1Name), PointerGetDatum(aggtransfn1typeName),
Int32GetDatum(2), 0, 0, 0);
PointerGetDatum(fnArgs), if (!HeapTupleIsValid(tup))
0); elog(WARN, "AggregateCreate: Type '%s' undefined",
if(!HeapTupleIsValid(tup)) aggtransfn1typeName);
elog(WARN, "AggregateCreate: '%s('%s', '%s') does not exist", xret1 = tup->t_oid;
aggtransfn1Name, aggtransfn1typeName, aggbasetypeName);
if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1)
elog(WARN, "AggregateCreate: return type of '%s' is not '%s'",
aggtransfn1Name,
aggtransfn1typeName);
xfn1 = tup->t_oid;
if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
!OidIsValid(xbase))
elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
}
if (aggtransfn2Name) { fnArgs[0] = xret1;
tup = SearchSysCacheTuple(TYPNAME, fnArgs[1] = xbase;
PointerGetDatum(aggtransfn2typeName), tup = SearchSysCacheTuple(PRONAME,
0,0,0); PointerGetDatum(aggtransfn1Name),
if(!HeapTupleIsValid(tup)) Int32GetDatum(2),
elog(WARN, "AggregateCreate: Type '%s' undefined", PointerGetDatum(fnArgs),
aggtransfn2typeName); 0);
xret2 = tup->t_oid; if (!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: '%s('%s', '%s') does not exist",
aggtransfn1Name, aggtransfn1typeName, aggbasetypeName);
if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1)
elog(WARN, "AggregateCreate: return type of '%s' is not '%s'",
aggtransfn1Name,
aggtransfn1typeName);
xfn1 = tup->t_oid;
if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
!OidIsValid(xbase))
elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
}
fnArgs[0] = xret2; if (aggtransfn2Name)
fnArgs[1] = 0; {
tup = SearchSysCacheTuple(PRONAME, tup = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(aggtransfn2Name), PointerGetDatum(aggtransfn2typeName),
Int32GetDatum(1), 0, 0, 0);
PointerGetDatum(fnArgs), if (!HeapTupleIsValid(tup))
0); elog(WARN, "AggregateCreate: Type '%s' undefined",
if(!HeapTupleIsValid(tup)) aggtransfn2typeName);
elog(WARN, "AggregateCreate: '%s'('%s') does not exist", xret2 = tup->t_oid;
aggtransfn2Name, aggtransfn2typeName);
if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2)
elog(WARN, "AggregateCreate: return type of '%s' is not '%s'",
aggtransfn2Name, aggtransfn2typeName);
xfn2 = tup->t_oid;
if (!OidIsValid(xfn2) || !OidIsValid(xret2))
elog(WARN, "AggregateCreate: bogus function '%s'",aggfinalfnName);
}
tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName), fnArgs[0] = xret2;
ObjectIdGetDatum(xbase), fnArgs[1] = 0;
0,0); tup = SearchSysCacheTuple(PRONAME,
if (HeapTupleIsValid(tup)) PointerGetDatum(aggtransfn2Name),
elog(WARN, Int32GetDatum(1),
"AggregateCreate: aggregate '%s' with base type '%s' already exists", PointerGetDatum(fnArgs),
aggName, aggbasetypeName); 0);
if (!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: '%s'('%s') does not exist",
aggtransfn2Name, aggtransfn2typeName);
if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2)
elog(WARN, "AggregateCreate: return type of '%s' is not '%s'",
aggtransfn2Name, aggtransfn2typeName);
xfn2 = tup->t_oid;
if (!OidIsValid(xfn2) || !OidIsValid(xret2))
elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
}
/* more sanity checks */ tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName),
if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName) ObjectIdGetDatum(xbase),
elog(WARN, "AggregateCreate: Aggregate must have final function with both transition functions"); 0, 0);
if (HeapTupleIsValid(tup))
elog(WARN,
"AggregateCreate: aggregate '%s' with base type '%s' already exists",
aggName, aggbasetypeName);
if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName) /* more sanity checks */
elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions"); if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
elog(WARN, "AggregateCreate: Aggregate must have final function with both transition functions");
if (aggfinalfnName) { if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
fnArgs[0] = xret1; elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions");
fnArgs[1] = xret2;
tup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(aggfinalfnName),
Int32GetDatum(2),
PointerGetDatum(fnArgs),
0);
if(!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: '%s'('%s','%s') does not exist",
aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName);
ffn = tup->t_oid;
proc = (Form_pg_proc) GETSTRUCT(tup);
fret = proc->prorettype;
if (!OidIsValid(ffn) || !OidIsValid(fret))
elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
}
/* if (aggfinalfnName)
* If transition function 2 is defined, it must have an initial value, {
* whereas transition function 1 does not, which allows man and min fnArgs[0] = xret1;
* aggregates to return NULL if they are evaluated on empty sets. fnArgs[1] = xret2;
*/ tup = SearchSysCacheTuple(PRONAME,
if (OidIsValid(xfn2) && !agginitval2) PointerGetDatum(aggfinalfnName),
elog(WARN, "AggregateCreate: transition function 2 MUST have an initial value"); Int32GetDatum(2),
PointerGetDatum(fnArgs),
0);
if (!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: '%s'('%s','%s') does not exist",
aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName);
ffn = tup->t_oid;
proc = (Form_pg_proc) GETSTRUCT(tup);
fret = proc->prorettype;
if (!OidIsValid(ffn) || !OidIsValid(fret))
elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
}
/* initialize nulls and values */ /*
for(i=0; i < Natts_pg_aggregate; i++) { * If transition function 2 is defined, it must have an initial value,
nulls[i] = ' '; * whereas transition function 1 does not, which allows man and min
values[i] = (Datum)NULL; * aggregates to return NULL if they are evaluated on empty sets.
} */
values[Anum_pg_aggregate_aggname-1] = PointerGetDatum(aggName); if (OidIsValid(xfn2) && !agginitval2)
values[Anum_pg_aggregate_aggowner-1] = elog(WARN, "AggregateCreate: transition function 2 MUST have an initial value");
Int32GetDatum(GetUserId());
values[Anum_pg_aggregate_aggtransfn1-1] =
ObjectIdGetDatum(xfn1);
values[Anum_pg_aggregate_aggtransfn2-1] =
ObjectIdGetDatum(xfn2);
values[Anum_pg_aggregate_aggfinalfn-1] =
ObjectIdGetDatum(ffn);
values[Anum_pg_aggregate_aggbasetype-1] = /* initialize nulls and values */
ObjectIdGetDatum(xbase); for (i = 0; i < Natts_pg_aggregate; i++)
if (!OidIsValid(xfn1)) { {
values[Anum_pg_aggregate_aggtranstype1-1] = nulls[i] = ' ';
ObjectIdGetDatum(InvalidOid); values[i] = (Datum) NULL;
values[Anum_pg_aggregate_aggtranstype2-1] = }
ObjectIdGetDatum(xret2); values[Anum_pg_aggregate_aggname - 1] = PointerGetDatum(aggName);
values[Anum_pg_aggregate_aggfinaltype-1] = values[Anum_pg_aggregate_aggowner - 1] =
ObjectIdGetDatum(xret2); Int32GetDatum(GetUserId());
} values[Anum_pg_aggregate_aggtransfn1 - 1] =
else if (!OidIsValid(xfn2)) { ObjectIdGetDatum(xfn1);
values[Anum_pg_aggregate_aggtranstype1-1] = values[Anum_pg_aggregate_aggtransfn2 - 1] =
ObjectIdGetDatum(xret1); ObjectIdGetDatum(xfn2);
values[Anum_pg_aggregate_aggtranstype2-1] = values[Anum_pg_aggregate_aggfinalfn - 1] =
ObjectIdGetDatum(InvalidOid); ObjectIdGetDatum(ffn);
values[Anum_pg_aggregate_aggfinaltype-1] =
ObjectIdGetDatum(xret1);
}
else {
values[Anum_pg_aggregate_aggtranstype1-1] =
ObjectIdGetDatum(xret1);
values[Anum_pg_aggregate_aggtranstype2-1] =
ObjectIdGetDatum(xret2);
values[Anum_pg_aggregate_aggfinaltype-1] =
ObjectIdGetDatum(fret);
}
if (agginitval1) values[Anum_pg_aggregate_aggbasetype - 1] =
values[Anum_pg_aggregate_agginitval1-1] = PointerGetDatum(textin(agginitval1)); ObjectIdGetDatum(xbase);
else if (!OidIsValid(xfn1))
nulls[Anum_pg_aggregate_agginitval1-1] = 'n'; {
values[Anum_pg_aggregate_aggtranstype1 - 1] =
ObjectIdGetDatum(InvalidOid);
values[Anum_pg_aggregate_aggtranstype2 - 1] =
ObjectIdGetDatum(xret2);
values[Anum_pg_aggregate_aggfinaltype - 1] =
ObjectIdGetDatum(xret2);
}
else if (!OidIsValid(xfn2))
{
values[Anum_pg_aggregate_aggtranstype1 - 1] =
ObjectIdGetDatum(xret1);
values[Anum_pg_aggregate_aggtranstype2 - 1] =
ObjectIdGetDatum(InvalidOid);
values[Anum_pg_aggregate_aggfinaltype - 1] =
ObjectIdGetDatum(xret1);
}
else
{
values[Anum_pg_aggregate_aggtranstype1 - 1] =
ObjectIdGetDatum(xret1);
values[Anum_pg_aggregate_aggtranstype2 - 1] =
ObjectIdGetDatum(xret2);
values[Anum_pg_aggregate_aggfinaltype - 1] =
ObjectIdGetDatum(fret);
}
if (agginitval2) if (agginitval1)
values[Anum_pg_aggregate_agginitval2-1] = PointerGetDatum(textin(agginitval2)); values[Anum_pg_aggregate_agginitval1 - 1] = PointerGetDatum(textin(agginitval1));
else else
nulls[Anum_pg_aggregate_agginitval2-1] = 'n'; nulls[Anum_pg_aggregate_agginitval1 - 1] = 'n';
if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName))) if (agginitval2)
elog(WARN, "AggregateCreate: could not open '%s'", values[Anum_pg_aggregate_agginitval2 - 1] = PointerGetDatum(textin(agginitval2));
AggregateRelationName); else
nulls[Anum_pg_aggregate_agginitval2 - 1] = 'n';
tupDesc = aggdesc->rd_att; if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName)))
if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc, elog(WARN, "AggregateCreate: could not open '%s'",
values, AggregateRelationName);
nulls)))
elog(WARN, "AggregateCreate: heap_formtuple failed"); tupDesc = aggdesc->rd_att;
if (!OidIsValid(heap_insert(aggdesc, tup))) if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
elog(WARN, "AggregateCreate: heap_insert failed"); values,
heap_close(aggdesc); nulls)))
elog(WARN, "AggregateCreate: heap_formtuple failed");
if (!OidIsValid(heap_insert(aggdesc, tup)))
elog(WARN, "AggregateCreate: heap_insert failed");
heap_close(aggdesc);
} }
char * char *
AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull) AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool * isNull)
{ {
HeapTuple tup; HeapTuple tup;
Relation aggRel; Relation aggRel;
int initValAttno; int initValAttno;
Oid transtype; Oid transtype;
text *textInitVal; text *textInitVal;
char *strInitVal, *initVal; char *strInitVal,
*initVal;
Assert(PointerIsValid(aggName)); Assert(PointerIsValid(aggName));
Assert(PointerIsValid(isNull)); Assert(PointerIsValid(isNull));
Assert(xfuncno == 1 || xfuncno == 2); Assert(xfuncno == 1 || xfuncno == 2);
tup = SearchSysCacheTuple(AGGNAME, tup = SearchSysCacheTuple(AGGNAME,
PointerGetDatum(aggName), PointerGetDatum(aggName),
PointerGetDatum(basetype), PointerGetDatum(basetype),
0,0); 0, 0);
if (!HeapTupleIsValid(tup)) if (!HeapTupleIsValid(tup))
elog(WARN, "AggNameGetInitVal: cache lookup failed for aggregate '%s'", elog(WARN, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
aggName); aggName);
if (xfuncno == 1) { if (xfuncno == 1)
transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1; {
initValAttno = Anum_pg_aggregate_agginitval1; transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1;
} initValAttno = Anum_pg_aggregate_agginitval1;
else /* can only be 1 or 2 */ { }
transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2; else
initValAttno = Anum_pg_aggregate_agginitval2; /* can only be 1 or 2 */
} {
transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2;
initValAttno = Anum_pg_aggregate_agginitval2;
}
aggRel = heap_openr(AggregateRelationName); aggRel = heap_openr(AggregateRelationName);
if (!RelationIsValid(aggRel)) if (!RelationIsValid(aggRel))
elog(WARN, "AggNameGetInitVal: could not open \"%-.*s\"", elog(WARN, "AggNameGetInitVal: could not open \"%-.*s\"",
AggregateRelationName); AggregateRelationName);
/*
* must use fastgetattr in case one or other of the init values is NULL /*
*/ * must use fastgetattr in case one or other of the init values is
textInitVal = (text *) fastgetattr(tup, initValAttno, * NULL
RelationGetTupleDescriptor(aggRel), */
isNull); textInitVal = (text *) fastgetattr(tup, initValAttno,
if (!PointerIsValid(textInitVal)) RelationGetTupleDescriptor(aggRel),
*isNull = true; isNull);
if (*isNull) { if (!PointerIsValid(textInitVal))
*isNull = true;
if (*isNull)
{
heap_close(aggRel);
return ((char *) NULL);
}
strInitVal = textout(textInitVal);
heap_close(aggRel); heap_close(aggRel);
return((char *) NULL);
}
strInitVal = textout(textInitVal);
heap_close(aggRel);
tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(transtype), tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(transtype),
0,0,0); 0, 0, 0);
if (!HeapTupleIsValid(tup)) { if (!HeapTupleIsValid(tup))
{
pfree(strInitVal);
elog(WARN, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type");
}
initVal = fmgr(((TypeTupleForm) GETSTRUCT(tup))->typinput, strInitVal, -1);
pfree(strInitVal); pfree(strInitVal);
elog(WARN, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type"); return (initVal);
}
initVal = fmgr(((TypeTupleForm) GETSTRUCT(tup))->typinput, strInitVal, -1);
pfree(strInitVal);
return(initVal);
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* pg_proc.c-- * pg_proc.c--
* routines to support manipulation of the pg_proc relation * routines to support manipulation of the pg_proc relation
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.5 1996/11/08 00:44:34 scrappy Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.6 1997/09/07 04:40:30 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -30,231 +30,252 @@
#include <utils/lsyscache.h> #include <utils/lsyscache.h>
#include <miscadmin.h> #include <miscadmin.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ProcedureDefine * ProcedureDefine
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
Oid Oid
ProcedureCreate(char *procedureName, ProcedureCreate(char *procedureName,
bool returnsSet, bool returnsSet,
char *returnTypeName, char *returnTypeName,
char *languageName, char *languageName,
char *prosrc, char *prosrc,
char *probin, char *probin,
bool canCache, bool canCache,
bool trusted, bool trusted,
int32 byte_pct, int32 byte_pct,
int32 perbyte_cpu, int32 perbyte_cpu,
int32 percall_cpu, int32 percall_cpu,
int32 outin_ratio, int32 outin_ratio,
List *argList, List * argList,
CommandDest dest) CommandDest dest)
{ {
register i; register i;
Relation rdesc; Relation rdesc;
HeapTuple tup; HeapTuple tup;
bool defined; bool defined;
uint16 parameterCount; uint16 parameterCount;
char nulls[ Natts_pg_proc ]; char nulls[Natts_pg_proc];
Datum values[ Natts_pg_proc ]; Datum values[Natts_pg_proc];
Oid languageObjectId; Oid languageObjectId;
Oid typeObjectId; Oid typeObjectId;
List *x; List *x;
QueryTreeList *querytree_list; QueryTreeList *querytree_list;
List *plan_list; List *plan_list;
Oid typev[8]; Oid typev[8];
Oid relid; Oid relid;
Oid toid; Oid toid;
text *prosrctext; text *prosrctext;
TupleDesc tupDesc; TupleDesc tupDesc;
/* ---------------- /* ----------------
* sanity checks * sanity checks
* ---------------- * ----------------
*/
Assert(PointerIsValid(prosrc));
Assert(PointerIsValid(probin));
parameterCount = 0;
memset(typev, 0, 8 * sizeof(Oid));
foreach (x, argList) {
Value *t = lfirst(x);
if (parameterCount == 8)
elog(WARN, "Procedures cannot take more than 8 arguments");
if (strcmp(strVal(t), "opaque") == 0) {
if (strcmp(languageName, "sql") == 0) {
elog(WARN, "ProcedureDefine: sql functions cannot take type \"opaque\"");
}
toid = 0;
} else {
toid = TypeGet(strVal(t), &defined);
if (!OidIsValid(toid)) {
elog(WARN, "ProcedureCreate: arg type '%s' is not defined",
strVal(t));
}
if (!defined) {
elog(NOTICE, "ProcedureCreate: arg type '%s' is only a shell",
strVal(t));
}
}
typev[parameterCount++] = toid;
}
tup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(procedureName),
UInt16GetDatum(parameterCount),
PointerGetDatum(typev),
0);
if (HeapTupleIsValid(tup))
elog(WARN, "ProcedureCreate: procedure %s already exists with same arguments",
procedureName);
if (!strcmp(languageName, "sql")) {
/* If this call is defining a set, check if the set is already
* defined by looking to see whether this call's function text
* matches a function already in pg_proc. If so just return the
* OID of the existing set.
*/ */
if (!strcmp(procedureName, GENERICSETNAME)) { Assert(PointerIsValid(prosrc));
prosrctext = textin(prosrc); Assert(PointerIsValid(probin));
tup = SearchSysCacheTuple(PROSRC,
PointerGetDatum(prosrctext),
0,0,0);
if (HeapTupleIsValid(tup))
return tup->t_oid;
}
}
tup = SearchSysCacheTuple(LANNAME, parameterCount = 0;
PointerGetDatum(languageName), memset(typev, 0, 8 * sizeof(Oid));
0,0,0); foreach(x, argList)
if (!HeapTupleIsValid(tup))
elog(WARN, "ProcedureCreate: no such language %s",
languageName);
languageObjectId = tup->t_oid;
if (strcmp(returnTypeName, "opaque") == 0) {
if (strcmp(languageName, "sql") == 0) {
elog(WARN, "ProcedureCreate: sql functions cannot return type \"opaque\"");
}
typeObjectId = 0;
}
else {
typeObjectId = TypeGet(returnTypeName, &defined);
if (!OidIsValid(typeObjectId)) {
elog(NOTICE, "ProcedureCreate: type '%s' is not yet defined",
returnTypeName);
#if 0
elog(NOTICE, "ProcedureCreate: creating a shell for type '%s'",
returnTypeName);
#endif
typeObjectId = TypeShellMake(returnTypeName);
if (!OidIsValid(typeObjectId)) {
elog(WARN, "ProcedureCreate: could not create type '%s'",
returnTypeName);
}
}
else if (!defined) {
elog(NOTICE, "ProcedureCreate: return type '%s' is only a shell",
returnTypeName);
}
}
/* don't allow functions of complex types that have the same name as
existing attributes of the type */
if (parameterCount == 1 &&
(toid = TypeGet(strVal(lfirst(argList)), &defined)) &&
defined &&
(relid = typeid_get_relid(toid)) != 0 &&
get_attnum(relid, procedureName) != InvalidAttrNumber)
elog(WARN, "method %s already an attribute of type %s",
procedureName, strVal(lfirst(argList)));
/*
* If this is a postquel procedure, we parse it here in order to
* be sure that it contains no syntax errors. We should store
* the plan in an Inversion file for use later, but for now, we
* just store the procedure's text in the prosrc attribute.
*/
if (strcmp(languageName, "sql") == 0) {
plan_list = pg_plan(prosrc, typev, parameterCount,
&querytree_list, dest);
/* typecheck return value */
pg_checkretval(typeObjectId, querytree_list);
}
for (i = 0; i < Natts_pg_proc; ++i) {
nulls[i] = ' ';
values[i] = (Datum)NULL;
}
i = 0;
values[i++] = PointerGetDatum(procedureName);
values[i++] = Int32GetDatum(GetUserId());
values[i++] = ObjectIdGetDatum(languageObjectId);
/* XXX isinherited is always false for now */
values[i++] = Int8GetDatum((bool) 0);
/* XXX istrusted is always false for now */
values[i++] = Int8GetDatum(trusted);
values[i++] = Int8GetDatum(canCache);
values[i++] = UInt16GetDatum(parameterCount);
values[i++] = Int8GetDatum(returnsSet);
values[i++] = ObjectIdGetDatum(typeObjectId);
values[i++] = (Datum) typev;
/*
* The following assignments of constants are made. The real values
* will have to be extracted from the arglist someday soon.
*/
values[i++] = Int32GetDatum(byte_pct); /* probyte_pct */
values[i++] = Int32GetDatum(perbyte_cpu); /* properbyte_cpu */
values[i++] = Int32GetDatum(percall_cpu); /* propercall_cpu */
values[i++] = Int32GetDatum(outin_ratio); /* prooutin_ratio */
values[i++] = (Datum)fmgr(TextInRegProcedure, prosrc); /* prosrc */
values[i++] = (Datum)fmgr(TextInRegProcedure, probin); /* probin */
rdesc = heap_openr(ProcedureRelationName);
tupDesc = rdesc->rd_att;
tup = heap_formtuple(tupDesc,
values,
nulls);
heap_insert(rdesc, tup);
if (RelationGetRelationTupleForm(rdesc)->relhasindex)
{ {
Relation idescs[Num_pg_proc_indices]; Value *t = lfirst(x);
CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs); if (parameterCount == 8)
CatalogIndexInsert(idescs, Num_pg_proc_indices, rdesc, tup); elog(WARN, "Procedures cannot take more than 8 arguments");
CatalogCloseIndices(Num_pg_proc_indices, idescs);
if (strcmp(strVal(t), "opaque") == 0)
{
if (strcmp(languageName, "sql") == 0)
{
elog(WARN, "ProcedureDefine: sql functions cannot take type \"opaque\"");
}
toid = 0;
}
else
{
toid = TypeGet(strVal(t), &defined);
if (!OidIsValid(toid))
{
elog(WARN, "ProcedureCreate: arg type '%s' is not defined",
strVal(t));
}
if (!defined)
{
elog(NOTICE, "ProcedureCreate: arg type '%s' is only a shell",
strVal(t));
}
}
typev[parameterCount++] = toid;
} }
heap_close(rdesc);
return tup->t_oid;
}
tup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(procedureName),
UInt16GetDatum(parameterCount),
PointerGetDatum(typev),
0);
if (HeapTupleIsValid(tup))
elog(WARN, "ProcedureCreate: procedure %s already exists with same arguments",
procedureName);
if (!strcmp(languageName, "sql"))
{
/*
* If this call is defining a set, check if the set is already
* defined by looking to see whether this call's function text
* matches a function already in pg_proc. If so just return the
* OID of the existing set.
*/
if (!strcmp(procedureName, GENERICSETNAME))
{
prosrctext = textin(prosrc);
tup = SearchSysCacheTuple(PROSRC,
PointerGetDatum(prosrctext),
0, 0, 0);
if (HeapTupleIsValid(tup))
return tup->t_oid;
}
}
tup = SearchSysCacheTuple(LANNAME,
PointerGetDatum(languageName),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(WARN, "ProcedureCreate: no such language %s",
languageName);
languageObjectId = tup->t_oid;
if (strcmp(returnTypeName, "opaque") == 0)
{
if (strcmp(languageName, "sql") == 0)
{
elog(WARN, "ProcedureCreate: sql functions cannot return type \"opaque\"");
}
typeObjectId = 0;
}
else
{
typeObjectId = TypeGet(returnTypeName, &defined);
if (!OidIsValid(typeObjectId))
{
elog(NOTICE, "ProcedureCreate: type '%s' is not yet defined",
returnTypeName);
#if 0
elog(NOTICE, "ProcedureCreate: creating a shell for type '%s'",
returnTypeName);
#endif
typeObjectId = TypeShellMake(returnTypeName);
if (!OidIsValid(typeObjectId))
{
elog(WARN, "ProcedureCreate: could not create type '%s'",
returnTypeName);
}
}
else if (!defined)
{
elog(NOTICE, "ProcedureCreate: return type '%s' is only a shell",
returnTypeName);
}
}
/*
* don't allow functions of complex types that have the same name as
* existing attributes of the type
*/
if (parameterCount == 1 &&
(toid = TypeGet(strVal(lfirst(argList)), &defined)) &&
defined &&
(relid = typeid_get_relid(toid)) != 0 &&
get_attnum(relid, procedureName) != InvalidAttrNumber)
elog(WARN, "method %s already an attribute of type %s",
procedureName, strVal(lfirst(argList)));
/*
* If this is a postquel procedure, we parse it here in order to be
* sure that it contains no syntax errors. We should store the plan
* in an Inversion file for use later, but for now, we just store the
* procedure's text in the prosrc attribute.
*/
if (strcmp(languageName, "sql") == 0)
{
plan_list = pg_plan(prosrc, typev, parameterCount,
&querytree_list, dest);
/* typecheck return value */
pg_checkretval(typeObjectId, querytree_list);
}
for (i = 0; i < Natts_pg_proc; ++i)
{
nulls[i] = ' ';
values[i] = (Datum) NULL;
}
i = 0;
values[i++] = PointerGetDatum(procedureName);
values[i++] = Int32GetDatum(GetUserId());
values[i++] = ObjectIdGetDatum(languageObjectId);
/* XXX isinherited is always false for now */
values[i++] = Int8GetDatum((bool) 0);
/* XXX istrusted is always false for now */
values[i++] = Int8GetDatum(trusted);
values[i++] = Int8GetDatum(canCache);
values[i++] = UInt16GetDatum(parameterCount);
values[i++] = Int8GetDatum(returnsSet);
values[i++] = ObjectIdGetDatum(typeObjectId);
values[i++] = (Datum) typev;
/*
* The following assignments of constants are made. The real values
* will have to be extracted from the arglist someday soon.
*/
values[i++] = Int32GetDatum(byte_pct); /* probyte_pct */
values[i++] = Int32GetDatum(perbyte_cpu); /* properbyte_cpu */
values[i++] = Int32GetDatum(percall_cpu); /* propercall_cpu */
values[i++] = Int32GetDatum(outin_ratio); /* prooutin_ratio */
values[i++] = (Datum) fmgr(TextInRegProcedure, prosrc); /* prosrc */
values[i++] = (Datum) fmgr(TextInRegProcedure, probin); /* probin */
rdesc = heap_openr(ProcedureRelationName);
tupDesc = rdesc->rd_att;
tup = heap_formtuple(tupDesc,
values,
nulls);
heap_insert(rdesc, tup);
if (RelationGetRelationTupleForm(rdesc)->relhasindex)
{
Relation idescs[Num_pg_proc_indices];
CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_proc_indices, rdesc, tup);
CatalogCloseIndices(Num_pg_proc_indices, idescs);
}
heap_close(rdesc);
return tup->t_oid;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +1,23 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* version.c-- * version.c--
* This file contains all the rules that govern all version semantics. * This file contains all the rules that govern all version semantics.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* The version stuff has not been tested under postgres95 and probably doesn't * The version stuff has not been tested under postgres95 and probably doesn't
* work! - jolly 8/19/95 * work! - jolly 8/19/95
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/_deadcode/Attic/version.c,v 1.5 1997/08/19 21:30:47 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/_deadcode/Attic/version.c,v 1.6 1997/09/07 04:41:04 momjian Exp $
* *
* NOTES * NOTES
* At the point the version is defined, 2 physical relations are created * At the point the version is defined, 2 physical relations are created
* <vname>_added and <vname>_deleted. * <vname>_added and <vname>_deleted.
* *
* In addition, 4 rules are defined which govern the semantics of versions * In addition, 4 rules are defined which govern the semantics of versions
* w.r.t retrieves, appends, replaces and deletes. * w.r.t retrieves, appends, replaces and deletes.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -34,19 +34,21 @@
#define MAX_QUERY_LEN 1024 #define MAX_QUERY_LEN 1024
char rule_buf[MAX_QUERY_LEN]; char rule_buf[MAX_QUERY_LEN];
#ifdef NOT_USED #ifdef NOT_USED
static char attr_list[MAX_QUERY_LEN]; static char attr_list[MAX_QUERY_LEN];
#endif #endif
/* /*
* problem: the version system assumes that the rules it declares will * problem: the version system assumes that the rules it declares will
* be fired in the order of declaration, it also assumes * be fired in the order of declaration, it also assumes
* goh's silly instead semantics. Unfortunately, it is a pain * goh's silly instead semantics. Unfortunately, it is a pain
* to make the version system work with the new semantics. * to make the version system work with the new semantics.
* However the whole problem can be solved, and some nice * However the whole problem can be solved, and some nice
* functionality can be achieved if we get multiple action rules * functionality can be achieved if we get multiple action rules
* to work. So thats what I did -- glass * to work. So thats what I did -- glass
* *
* Well, at least they've been working for about 20 minutes. * Well, at least they've been working for about 20 minutes.
* *
@ -55,8 +57,8 @@ static char attr_list[MAX_QUERY_LEN];
*/ */
/* /*
* This is needed because the rule system only allows * This is needed because the rule system only allows
* *1* rule to be defined per transaction. * *1* rule to be defined per transaction.
* *
* NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO * NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
@ -80,90 +82,96 @@ static char attr_list[MAX_QUERY_LEN];
* a strange memory bug instead of watching the "Get Smart" marathon * a strange memory bug instead of watching the "Get Smart" marathon
* in NICK ! * in NICK !
* DO NOT COMMIT THE XACT, just increase the Cid counter! * DO NOT COMMIT THE XACT, just increase the Cid counter!
* _sp. * _sp.
*/ */
#ifdef NOT_USED #ifdef NOT_USED
static void static void
eval_as_new_xact(char *query) eval_as_new_xact(char *query)
{ {
/* WARNING! do not uncomment the following lines WARNING!
* CommitTransactionCommand(); /*
* StartTransactionCommand(); * WARNING! do not uncomment the following lines WARNING!
*/ * CommitTransactionCommand(); StartTransactionCommand();
CommandCounterIncrement(); */
pg_eval(query, (char **) NULL, (Oid *) NULL, 0); CommandCounterIncrement();
pg_eval(query, (char **) NULL, (Oid *) NULL, 0);
} }
#endif #endif
/* /*
* Define a version. * Define a version.
*/ */
#ifdef NOT_USED #ifdef NOT_USED
void void
DefineVersion(char *name, char *fromRelname, char *date) DefineVersion(char *name, char *fromRelname, char *date)
{ {
char *bname; char *bname;
static char saved_basename[512]; static char saved_basename[512];
static char saved_snapshot[512]; static char saved_snapshot[512];
if (date == NULL) { if (date == NULL)
/* no time ranges */ {
bname = fromRelname; /* no time ranges */
strcpy(saved_basename, (char *) bname); bname = fromRelname;
*saved_snapshot = (char)NULL; strcpy(saved_basename, (char *) bname);
} else { *saved_snapshot = (char) NULL;
/* version is a snapshot */ }
bname = fromRelname; else
strcpy(saved_basename, (char *) bname); {
sprintf(saved_snapshot, "['%s']", date); /* version is a snapshot */
} bname = fromRelname;
strcpy(saved_basename, (char *) bname);
sprintf(saved_snapshot, "['%s']", date);
}
/* /*
* Calls the routine ``GetAttrList'' get the list of attributes * Calls the routine ``GetAttrList'' get the list of attributes from
* from the base relation. * the base relation. Code is put here so that we only need to look up
* Code is put here so that we only need to look up the attribute once for * the attribute once for both appends and replaces.
* both appends and replaces. */
*/ setAttrList(bname);
setAttrList(bname);
VersionCreate (name, saved_basename); VersionCreate(name, saved_basename);
VersionAppend (name, saved_basename); VersionAppend(name, saved_basename);
VersionDelete (name, saved_basename,saved_snapshot); VersionDelete(name, saved_basename, saved_snapshot);
VersionReplace (name, saved_basename,saved_snapshot); VersionReplace(name, saved_basename, saved_snapshot);
VersionRetrieve (name, saved_basename, saved_snapshot); VersionRetrieve(name, saved_basename, saved_snapshot);
} }
#endif #endif
/* /*
* Creates the deltas. * Creates the deltas.
*/ */
#ifdef NOT_USED #ifdef NOT_USED
void void
VersionCreate(char *vname, char *bname) VersionCreate(char *vname, char *bname)
{ {
static char query_buf [MAX_QUERY_LEN]; static char query_buf[MAX_QUERY_LEN];
/* /*
* Creating the dummy version relation for triggering rules. * Creating the dummy version relation for triggering rules.
*/ */
sprintf(query_buf, "SELECT * INTO TABLE %s from %s where 1 =2", sprintf(query_buf, "SELECT * INTO TABLE %s from %s where 1 =2",
vname, bname); vname, bname);
pg_eval (query_buf, (char **) NULL, (Oid *) NULL, 0); pg_eval(query_buf, (char **) NULL, (Oid *) NULL, 0);
/* /*
* Creating the ``v_added'' relation * Creating the ``v_added'' relation
*/ */
sprintf (query_buf, "SELECT * INTO TABLE %s_added from %s where 1 = 2", sprintf(query_buf, "SELECT * INTO TABLE %s_added from %s where 1 = 2",
vname, bname); vname, bname);
eval_as_new_xact (query_buf); eval_as_new_xact(query_buf);
/* /*
* Creating the ``v_deleted'' relation. * Creating the ``v_deleted'' relation.
*/ */
sprintf (query_buf, "CREATE TABLE %s_del (DOID oid)", vname); sprintf(query_buf, "CREATE TABLE %s_del (DOID oid)", vname);
eval_as_new_xact (query_buf); eval_as_new_xact(query_buf);
} }
#endif #endif
@ -176,38 +184,44 @@ VersionCreate(char *vname, char *bname)
static void static void
setAttrList(char *bname) setAttrList(char *bname)
{ {
Relation rdesc; Relation rdesc;
int i = 0; int i = 0;
int maxattrs = 0; int maxattrs = 0;
char *attrname; char *attrname;
char temp_buf[512]; char temp_buf[512];
int notfirst = 0; int notfirst = 0;
rdesc = heap_openr(bname); rdesc = heap_openr(bname);
if (rdesc == NULL ) { if (rdesc == NULL)
elog(WARN,"Unable to expand all -- amopenr failed "); {
return; elog(WARN, "Unable to expand all -- amopenr failed ");
} return;
maxattrs = RelationGetNumberOfAttributes(rdesc);
attr_list[0] = '\0';
for ( i = maxattrs-1 ; i > -1 ; --i ) {
attrname = (rdesc->rd_att->attrs[i]->attname).data;
if (notfirst == 1) {
sprintf(temp_buf, ", %s = new.%s", attrname, attrname);
} else {
sprintf(temp_buf, "%s = new.%s", attrname, attrname);
notfirst = 1;
} }
strcat(attr_list, temp_buf); maxattrs = RelationGetNumberOfAttributes(rdesc);
}
heap_close(rdesc); attr_list[0] = '\0';
return; for (i = maxattrs - 1; i > -1; --i)
{
attrname = (rdesc->rd_att->attrs[i]->attname).data;
if (notfirst == 1)
{
sprintf(temp_buf, ", %s = new.%s", attrname, attrname);
}
else
{
sprintf(temp_buf, "%s = new.%s", attrname, attrname);
notfirst = 1;
}
strcat(attr_list, temp_buf);
}
heap_close(rdesc);
return;
} }
#endif #endif
/* /*
@ -219,128 +233,131 @@ setAttrList(char *bname)
static void static void
VersionAppend(char *vname, char *bname) VersionAppend(char *vname, char *bname)
{ {
sprintf(rule_buf, sprintf(rule_buf,
"define rewrite rule %s_append is on INSERT to %s do instead append %s_added(%s)", "define rewrite rule %s_append is on INSERT to %s do instead append %s_added(%s)",
vname, vname, vname, attr_list); vname, vname, vname, attr_list);
eval_as_new_xact(rule_buf); eval_as_new_xact(rule_buf);
} }
#endif #endif
/* /*
* This routine defines the rule governing the retrieval semantics of * This routine defines the rule governing the retrieval semantics of
* versions. To retrieve tuples from a version , we need to: * versions. To retrieve tuples from a version , we need to:
* *
* 1. Retrieve all tuples in the <vname>_added relation. * 1. Retrieve all tuples in the <vname>_added relation.
* 2. Retrieve all tuples in the base relation which are not in * 2. Retrieve all tuples in the base relation which are not in
* the <vname>_del relation. * the <vname>_del relation.
*/ */
#ifdef NOT_USED #ifdef NOT_USED
void void
VersionRetrieve(char *vname, char *bname, char *snapshot) VersionRetrieve(char *vname, char *bname, char *snapshot)
{ {
sprintf(rule_buf, sprintf(rule_buf,
"define rewrite rule %s_retrieve is on SELECT to %s do instead\n\ "define rewrite rule %s_retrieve is on SELECT to %s do instead\n\
SELECT %s_1.oid, %s_1.* from _%s in %s%s, %s_1 in (%s_added | _%s) \ SELECT %s_1.oid, %s_1.* from _%s in %s%s, %s_1 in (%s_added | _%s) \
where _%s.oid !!= '%s_del.DOID'", where _%s.oid !!= '%s_del.DOID'",
vname, vname, vname, vname, bname, vname, vname, vname, vname, bname,
bname, snapshot, bname, snapshot,
vname, vname, bname, bname, vname); vname, vname, bname, bname, vname);
eval_as_new_xact(rule_buf); eval_as_new_xact(rule_buf);
/* printf("%s\n",rule_buf); */ /* printf("%s\n",rule_buf); */
} }
#endif #endif
/* /*
* This routine defines the rules that govern the delete semantics of * This routine defines the rules that govern the delete semantics of
* versions. Two things happens when we delete a tuple from a version: * versions. Two things happens when we delete a tuple from a version:
* *
* 1. If the tuple to be deleted was added to the version *after* * 1. If the tuple to be deleted was added to the version *after*
* the version was created, then we simply delete the tuple * the version was created, then we simply delete the tuple
* from the <vname>_added relation. * from the <vname>_added relation.
* 2. If the tuple to be deleted is actually in the base relation, * 2. If the tuple to be deleted is actually in the base relation,
* then we have to mark that tuple as being deleted by adding * then we have to mark that tuple as being deleted by adding
* it to the <vname>_del relation. * it to the <vname>_del relation.
*/ */
#ifdef NOT_USED #ifdef NOT_USED
void void
VersionDelete(char *vname, char *bname, char *snapshot) VersionDelete(char *vname, char *bname, char *snapshot)
{ {
sprintf(rule_buf, sprintf(rule_buf,
"define rewrite rule %s_delete1 is on delete to %s do instead\n \ "define rewrite rule %s_delete1 is on delete to %s do instead\n \
[delete %s_added where current.oid = %s_added.oid\n \ [delete %s_added where current.oid = %s_added.oid\n \
append %s_del(DOID = current.oid) from _%s in %s%s \ append %s_del(DOID = current.oid) from _%s in %s%s \
where current.oid = _%s.oid] \n", where current.oid = _%s.oid] \n",
vname,vname,vname,vname,vname, vname, vname, vname, vname, vname,
bname,bname,snapshot,bname); bname, bname, snapshot, bname);
eval_as_new_xact(rule_buf); eval_as_new_xact(rule_buf);
#ifdef OLD_REWRITE #ifdef OLD_REWRITE
sprintf(rule_buf, sprintf(rule_buf,
"define rewrite rule %s_delete2 is on delete to %s do instead \n \ "define rewrite rule %s_delete2 is on delete to %s do instead \n \
append %s_del(DOID = current.oid) from _%s in %s%s \ append %s_del(DOID = current.oid) from _%s in %s%s \
where current.oid = _%s.oid \n", where current.oid = _%s.oid \n",
vname,vname,vname,bname,bname,snapshot,bname); vname, vname, vname, bname, bname, snapshot, bname);
eval_as_new_xact(rule_buf); eval_as_new_xact(rule_buf);
#endif /* OLD_REWRITE */ #endif /* OLD_REWRITE */
} }
#endif #endif
/* /*
* This routine defines the rules that govern the update semantics * This routine defines the rules that govern the update semantics
* of versions. To update a tuple in a version: * of versions. To update a tuple in a version:
* *
* 1. If the tuple is in <vname>_added, we simply ``replace'' * 1. If the tuple is in <vname>_added, we simply ``replace''
* the tuple (as per postgres style). * the tuple (as per postgres style).
* 2. if the tuple is in the base relation, then two things have to * 2. if the tuple is in the base relation, then two things have to
* happen: * happen:
* 2.1 The tuple is marked ``deleted'' from the base relation by * 2.1 The tuple is marked ``deleted'' from the base relation by
* adding the tuple to the <vname>_del relation. * adding the tuple to the <vname>_del relation.
* 2.2 A copy of the tuple is appended to the <vname>_added relation * 2.2 A copy of the tuple is appended to the <vname>_added relation
*/ */
#ifdef NOT_USED #ifdef NOT_USED
void void
VersionReplace(char *vname, char *bname, char *snapshot) VersionReplace(char *vname, char *bname, char *snapshot)
{ {
sprintf(rule_buf, sprintf(rule_buf,
"define rewrite rule %s_replace1 is on replace to %s do instead \n\ "define rewrite rule %s_replace1 is on replace to %s do instead \n\
[replace %s_added(%s) where current.oid = %s_added.oid \n\ [replace %s_added(%s) where current.oid = %s_added.oid \n\
append %s_del(DOID = current.oid) from _%s in %s%s \ append %s_del(DOID = current.oid) from _%s in %s%s \
where current.oid = _%s.oid\n\ where current.oid = _%s.oid\n\
append %s_added(%s) from _%s in %s%s \ append %s_added(%s) from _%s in %s%s \
where current.oid !!= '%s_added.oid' and current.oid = _%s.oid]\n", where current.oid !!= '%s_added.oid' and current.oid = _%s.oid]\n",
vname,vname,vname,attr_list,vname, vname, vname, vname, attr_list, vname,
vname,bname,bname,snapshot,bname, vname, bname, bname, snapshot, bname,
vname,attr_list,bname,bname,snapshot,vname,bname); vname, attr_list, bname, bname, snapshot, vname, bname);
eval_as_new_xact(rule_buf); eval_as_new_xact(rule_buf);
/* printf("%s\n",rule_buf); */ /* printf("%s\n",rule_buf); */
#ifdef OLD_REWRITE #ifdef OLD_REWRITE
sprintf(rule_buf, sprintf(rule_buf,
"define rewrite rule %s_replace2 is on replace to %s do \n\ "define rewrite rule %s_replace2 is on replace to %s do \n\
append %s_del(DOID = current.oid) from _%s in %s%s \ append %s_del(DOID = current.oid) from _%s in %s%s \
where current.oid = _%s.oid\n", where current.oid = _%s.oid\n",
vname,vname,vname,bname,bname,snapshot,bname); vname, vname, vname, bname, bname, snapshot, bname);
eval_as_new_xact(rule_buf); eval_as_new_xact(rule_buf);
sprintf(rule_buf, sprintf(rule_buf,
"define rewrite rule %s_replace3 is on replace to %s do instead\n\ "define rewrite rule %s_replace3 is on replace to %s do instead\n\
append %s_added(%s) from _%s in %s%s \ append %s_added(%s) from _%s in %s%s \
where current.oid !!= '%s_added.oid' and current.oid = \ where current.oid !!= '%s_added.oid' and current.oid = \
_%s.oid\n", _%s.oid\n",
vname,vname, vname,attr_list,bname,bname,snapshot,vname,bname); vname, vname, vname, attr_list, bname, bname, snapshot, vname, bname);
eval_as_new_xact(rule_buf); eval_as_new_xact(rule_buf);
#endif /* OLD_REWRITE */ #endif /* OLD_REWRITE */
/* printf("%s\n",rule_buf); */ /* printf("%s\n",rule_buf); */
} }

View File

@ -1,35 +1,35 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* async.c-- * async.c--
* Asynchronous notification * Asynchronous notification
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.17 1997/08/19 21:30:42 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.18 1997/09/07 04:40:35 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
/* New Async Notification Model: /* New Async Notification Model:
* 1. Multiple backends on same machine. Multiple backends listening on * 1. Multiple backends on same machine. Multiple backends listening on
* one relation. * one relation.
* *
* 2. One of the backend does a 'notify <relname>'. For all backends that * 2. One of the backend does a 'notify <relname>'. For all backends that
* are listening to this relation (all notifications take place at the * are listening to this relation (all notifications take place at the
* end of commit), * end of commit),
* 2.a If the process is the same as the backend process that issued * 2.a If the process is the same as the backend process that issued
* notification (we are notifying something that we are listening), * notification (we are notifying something that we are listening),
* signal the corresponding frontend over the comm channel using the * signal the corresponding frontend over the comm channel using the
* out-of-band channel. * out-of-band channel.
* 2.b For all other listening processes, we send kill(2) to wake up * 2.b For all other listening processes, we send kill(2) to wake up
* the listening backend. * the listening backend.
* 3. Upon receiving a kill(2) signal from another backend process notifying * 3. Upon receiving a kill(2) signal from another backend process notifying
* that one of the relation that we are listening is being notified, * that one of the relation that we are listening is being notified,
* we can be in either of two following states: * we can be in either of two following states:
* 3.a We are sleeping, wake up and signal our frontend. * 3.a We are sleeping, wake up and signal our frontend.
* 3.b We are in middle of another transaction, wait until the end of * 3.b We are in middle of another transaction, wait until the end of
* of the current transaction and signal our frontend. * of the current transaction and signal our frontend.
* 4. Each frontend receives this notification and prcesses accordingly. * 4. Each frontend receives this notification and prcesses accordingly.
* *
* -- jw, 12/28/93 * -- jw, 12/28/93
@ -43,15 +43,15 @@
* 1. Multiple backends on same machine. * 1. Multiple backends on same machine.
* *
* 2. Query on one backend sends stuff over an asynchronous portal by * 2. Query on one backend sends stuff over an asynchronous portal by
* appending to a relation, and then doing an async. notification * appending to a relation, and then doing an async. notification
* (which takes place after commit) to all listeners on this relation. * (which takes place after commit) to all listeners on this relation.
* *
* 3. Async. notification results in all backends listening on relation * 3. Async. notification results in all backends listening on relation
* to be woken up, by a process signal kill(2), with name of relation * to be woken up, by a process signal kill(2), with name of relation
* passed in shared memory. * passed in shared memory.
* *
* 4. Each backend notifies its respective frontend over the comm * 4. Each backend notifies its respective frontend over the comm
* channel using the out-of-band channel. * channel using the out-of-band channel.
* *
* 5. Each frontend receives this notification and processes accordingly. * 5. Each frontend receives this notification and processes accordingly.
* *
@ -62,7 +62,7 @@
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <sys/types.h> /* Needed by in.h on Ultrix */ #include <sys/types.h> /* Needed by in.h on Ultrix */
#include <netinet/in.h> #include <netinet/in.h>
#include <postgres.h> #include <postgres.h>
@ -82,83 +82,86 @@
#include <commands/async.h> #include <commands/async.h>
#include <libpq/libpq.h> #include <libpq/libpq.h>
#include <port-protos.h> /* for strdup() */ #include <port-protos.h> /* for strdup() */
#include <storage/lmgr.h> #include <storage/lmgr.h>
static int notifyFrontEndPending = 0; static int notifyFrontEndPending = 0;
static int notifyIssued = 0; static int notifyIssued = 0;
static Dllist *pendingNotifies = NULL; static Dllist *pendingNotifies = NULL;
static int AsyncExistsPendingNotify(char *); static int AsyncExistsPendingNotify(char *);
static void ClearPendingNotify(void); static void ClearPendingNotify(void);
static void Async_NotifyFrontEnd(void); static void Async_NotifyFrontEnd(void);
static void Async_Unlisten(char *relname, int pid); static void Async_Unlisten(char *relname, int pid);
static void Async_UnlistenOnExit(int code, char *relname); static void Async_UnlistenOnExit(int code, char *relname);
/* /*
*-------------------------------------------------------------- *--------------------------------------------------------------
* Async_NotifyHandler -- * Async_NotifyHandler --
* *
* This is the signal handler for SIGUSR2. When the backend * This is the signal handler for SIGUSR2. When the backend
* is signaled, the backend can be in two states. * is signaled, the backend can be in two states.
* 1. If the backend is in the middle of another transaction, * 1. If the backend is in the middle of another transaction,
* we set the flag, notifyFrontEndPending, and wait until * we set the flag, notifyFrontEndPending, and wait until
* the end of the transaction to notify the front end. * the end of the transaction to notify the front end.
* 2. If the backend is not in the middle of another transaction, * 2. If the backend is not in the middle of another transaction,
* we notify the front end immediately. * we notify the front end immediately.
* *
* -- jw, 12/28/93 * -- jw, 12/28/93
* Results: * Results:
* none * none
* *
* Side effects: * Side effects:
* none * none
*/ */
void void
Async_NotifyHandler(SIGNAL_ARGS) Async_NotifyHandler(SIGNAL_ARGS)
{ {
extern TransactionState CurrentTransactionState; extern TransactionState CurrentTransactionState;
if ((CurrentTransactionState->state == TRANS_DEFAULT) && if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
(CurrentTransactionState->blockState == TRANS_DEFAULT)) { (CurrentTransactionState->blockState == TRANS_DEFAULT))
{
#ifdef ASYNC_DEBUG #ifdef ASYNC_DEBUG
elog(DEBUG, "Waking up sleeping backend process"); elog(DEBUG, "Waking up sleeping backend process");
#endif #endif
Async_NotifyFrontEnd(); Async_NotifyFrontEnd();
}else { }
else
{
#ifdef ASYNC_DEBUG #ifdef ASYNC_DEBUG
elog(DEBUG, "Process is in the middle of another transaction, state = %d, block state = %d", elog(DEBUG, "Process is in the middle of another transaction, state = %d, block state = %d",
CurrentTransactionState->state, CurrentTransactionState->state,
CurrentTransactionState->blockState); CurrentTransactionState->blockState);
#endif #endif
notifyFrontEndPending = 1; notifyFrontEndPending = 1;
} }
} }
/* /*
*-------------------------------------------------------------- *--------------------------------------------------------------
* Async_Notify -- * Async_Notify --
* *
* Adds the relation to the list of pending notifies. * Adds the relation to the list of pending notifies.
* All notification happens at end of commit. * All notification happens at end of commit.
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* *
* All notification of backend processes happens here, * All notification of backend processes happens here,
* then each backend notifies its corresponding front end at * then each backend notifies its corresponding front end at
* the end of commit. * the end of commit.
* *
* This correspond to 'notify <relname>' command * This correspond to 'notify <relname>' command
* -- jw, 12/28/93 * -- jw, 12/28/93
* *
* Results: * Results:
* XXX * XXX
* *
* Side effects: * Side effects:
* All tuples for relname in pg_listener are updated. * All tuples for relname in pg_listener are updated.
* *
*-------------------------------------------------------------- *--------------------------------------------------------------
*/ */
@ -166,455 +169,491 @@ void
Async_Notify(char *relname) Async_Notify(char *relname)
{ {
HeapTuple lTuple, rTuple; HeapTuple lTuple,
Relation lRel; rTuple;
HeapScanDesc sRel; Relation lRel;
TupleDesc tdesc; HeapScanDesc sRel;
ScanKeyData key; TupleDesc tdesc;
Buffer b; ScanKeyData key;
Datum d, value[3]; Buffer b;
bool isnull; Datum d,
char repl[3], nulls[3]; value[3];
bool isnull;
char repl[3],
nulls[3];
char *notifyName; char *notifyName;
#ifdef ASYNC_DEBUG #ifdef ASYNC_DEBUG
elog(DEBUG,"Async_Notify: %s",relname); elog(DEBUG, "Async_Notify: %s", relname);
#endif #endif
if (!pendingNotifies) if (!pendingNotifies)
pendingNotifies = DLNewList(); pendingNotifies = DLNewList();
/* /*
* Allocate memory from the global malloc pool because it needs to be * Allocate memory from the global malloc pool because it needs to be
* referenced also when the transaction is finished. DZ - 26-08-1996 * referenced also when the transaction is finished. DZ - 26-08-1996
*/ */
notifyName = strdup(relname); notifyName = strdup(relname);
DLAddHead(pendingNotifies, DLNewElem(notifyName)); DLAddHead(pendingNotifies, DLNewElem(notifyName));
ScanKeyEntryInitialize(&key, 0, ScanKeyEntryInitialize(&key, 0,
Anum_pg_listener_relname, Anum_pg_listener_relname,
NameEqualRegProcedure, NameEqualRegProcedure,
PointerGetDatum(notifyName)); PointerGetDatum(notifyName));
lRel = heap_openr(ListenerRelationName); lRel = heap_openr(ListenerRelationName);
tdesc = RelationGetTupleDescriptor(lRel); tdesc = RelationGetTupleDescriptor(lRel);
RelationSetLockForWrite(lRel); RelationSetLockForWrite(lRel);
sRel = heap_beginscan(lRel, 0, NowTimeQual, 1, &key); sRel = heap_beginscan(lRel, 0, NowTimeQual, 1, &key);
nulls[0] = nulls[1] = nulls[2] = ' '; nulls[0] = nulls[1] = nulls[2] = ' ';
repl[0] = repl[1] = repl[2] = ' '; repl[0] = repl[1] = repl[2] = ' ';
repl[Anum_pg_listener_notify - 1] = 'r'; repl[Anum_pg_listener_notify - 1] = 'r';
value[0] = value[1] = value[2] = (Datum) 0; value[0] = value[1] = value[2] = (Datum) 0;
value[Anum_pg_listener_notify - 1] = Int32GetDatum(1); value[Anum_pg_listener_notify - 1] = Int32GetDatum(1);
while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0, &b))) { while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0, &b)))
d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_notify, {
tdesc, &isnull); d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_notify,
if (!DatumGetInt32(d)) { tdesc, &isnull);
rTuple = heap_modifytuple(lTuple, b, lRel, value, nulls, repl); if (!DatumGetInt32(d))
heap_replace(lRel, &lTuple->t_ctid, rTuple); {
rTuple = heap_modifytuple(lTuple, b, lRel, value, nulls, repl);
heap_replace(lRel, &lTuple->t_ctid, rTuple);
}
ReleaseBuffer(b);
} }
ReleaseBuffer(b); heap_endscan(sRel);
} RelationUnsetLockForWrite(lRel);
heap_endscan(sRel); heap_close(lRel);
RelationUnsetLockForWrite(lRel); notifyIssued = 1;
heap_close(lRel);
notifyIssued = 1;
} }
/* /*
*-------------------------------------------------------------- *--------------------------------------------------------------
* Async_NotifyAtCommit -- * Async_NotifyAtCommit --
* *
* Signal our corresponding frontend process on relations that * Signal our corresponding frontend process on relations that
* were notified. Signal all other backend process that * were notified. Signal all other backend process that
* are listening also. * are listening also.
* *
* -- jw, 12/28/93 * -- jw, 12/28/93
* *
* Results: * Results:
* XXX * XXX
* *
* Side effects: * Side effects:
* Tuples in pg_listener that has our listenerpid are updated so * Tuples in pg_listener that has our listenerpid are updated so
* that the notification is 0. We do not want to notify frontend * that the notification is 0. We do not want to notify frontend
* more than once. * more than once.
* *
* -- jw, 12/28/93 * -- jw, 12/28/93
* *
*-------------------------------------------------------------- *--------------------------------------------------------------
*/ */
void void
Async_NotifyAtCommit() Async_NotifyAtCommit()
{ {
HeapTuple lTuple; HeapTuple lTuple;
Relation lRel; Relation lRel;
HeapScanDesc sRel; HeapScanDesc sRel;
TupleDesc tdesc; TupleDesc tdesc;
ScanKeyData key; ScanKeyData key;
Datum d; Datum d;
int ourpid; int ourpid;
bool isnull; bool isnull;
Buffer b; Buffer b;
extern TransactionState CurrentTransactionState; extern TransactionState CurrentTransactionState;
if (!pendingNotifies) if (!pendingNotifies)
pendingNotifies = DLNewList(); pendingNotifies = DLNewList();
if ((CurrentTransactionState->state == TRANS_DEFAULT) && if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
(CurrentTransactionState->blockState == TRANS_DEFAULT)) { (CurrentTransactionState->blockState == TRANS_DEFAULT))
{
if (notifyIssued) { /* 'notify <relname>' issued by us */ if (notifyIssued)
notifyIssued = 0; { /* 'notify <relname>' issued by us */
StartTransactionCommand(); notifyIssued = 0;
StartTransactionCommand();
#ifdef ASYNC_DEBUG #ifdef ASYNC_DEBUG
elog(DEBUG, "Async_NotifyAtCommit."); elog(DEBUG, "Async_NotifyAtCommit.");
#endif #endif
ScanKeyEntryInitialize(&key, 0, ScanKeyEntryInitialize(&key, 0,
Anum_pg_listener_notify, Anum_pg_listener_notify,
Integer32EqualRegProcedure, Integer32EqualRegProcedure,
Int32GetDatum(1)); Int32GetDatum(1));
lRel = heap_openr(ListenerRelationName); lRel = heap_openr(ListenerRelationName);
RelationSetLockForWrite(lRel); RelationSetLockForWrite(lRel);
sRel = heap_beginscan(lRel, 0, NowTimeQual, 1, &key); sRel = heap_beginscan(lRel, 0, NowTimeQual, 1, &key);
tdesc = RelationGetTupleDescriptor(lRel); tdesc = RelationGetTupleDescriptor(lRel);
ourpid = getpid(); ourpid = getpid();
while (HeapTupleIsValid(lTuple = heap_getnext(sRel,0, &b))) { while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0, &b)))
d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_relname, {
tdesc, &isnull); d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_relname,
tdesc, &isnull);
if (AsyncExistsPendingNotify((char *) DatumGetPointer(d))) { if (AsyncExistsPendingNotify((char *) DatumGetPointer(d)))
d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_pid, {
tdesc, &isnull); d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_pid,
tdesc, &isnull);
if (ourpid == DatumGetInt32(d)) { if (ourpid == DatumGetInt32(d))
{
#ifdef ASYNC_DEBUG #ifdef ASYNC_DEBUG
elog(DEBUG, "Notifying self, setting notifyFronEndPending to 1"); elog(DEBUG, "Notifying self, setting notifyFronEndPending to 1");
#endif #endif
notifyFrontEndPending = 1; notifyFrontEndPending = 1;
} else { }
else
{
#ifdef ASYNC_DEBUG #ifdef ASYNC_DEBUG
elog(DEBUG, "Notifying others"); elog(DEBUG, "Notifying others");
#endif #endif
#ifdef HAVE_KILL #ifdef HAVE_KILL
if (kill(DatumGetInt32(d), SIGUSR2) < 0) { if (kill(DatumGetInt32(d), SIGUSR2) < 0)
if (errno == ESRCH) { {
heap_delete(lRel, &lTuple->t_ctid); if (errno == ESRCH)
} {
} heap_delete(lRel, &lTuple->t_ctid);
}
}
#endif #endif
} }
}
ReleaseBuffer(b);
}
heap_endscan(sRel);
RelationUnsetLockForWrite(lRel);
heap_close(lRel);
CommitTransactionCommand();
ClearPendingNotify();
} }
ReleaseBuffer(b);
}
heap_endscan(sRel);
RelationUnsetLockForWrite(lRel);
heap_close(lRel);
CommitTransactionCommand(); if (notifyFrontEndPending)
ClearPendingNotify(); { /* we need to notify the frontend of all
* pending notifies. */
notifyFrontEndPending = 1;
Async_NotifyFrontEnd();
}
} }
if (notifyFrontEndPending) { /* we need to notify the frontend of
all pending notifies. */
notifyFrontEndPending = 1;
Async_NotifyFrontEnd();
}
}
} }
/* /*
*-------------------------------------------------------------- *--------------------------------------------------------------
* Async_NotifyAtAbort -- * Async_NotifyAtAbort --
* *
* Gets rid of pending notifies. List elements are automatically * Gets rid of pending notifies. List elements are automatically
* freed through memory context. * freed through memory context.
* *
* *
* Results: * Results:
* XXX * XXX
* *
* Side effects: * Side effects:
* XXX * XXX
* *
*-------------------------------------------------------------- *--------------------------------------------------------------
*/ */
void void
Async_NotifyAtAbort() Async_NotifyAtAbort()
{ {
extern TransactionState CurrentTransactionState; extern TransactionState CurrentTransactionState;
if (notifyIssued) { if (notifyIssued)
ClearPendingNotify(); {
} ClearPendingNotify();
notifyIssued = 0; }
if (pendingNotifies) notifyIssued = 0;
DLFreeList(pendingNotifies); if (pendingNotifies)
pendingNotifies = DLNewList(); DLFreeList(pendingNotifies);
pendingNotifies = DLNewList();
if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
(CurrentTransactionState->blockState == TRANS_DEFAULT)) { if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
if (notifyFrontEndPending) { /* don't forget to notify front end */ (CurrentTransactionState->blockState == TRANS_DEFAULT))
Async_NotifyFrontEnd(); {
if (notifyFrontEndPending)
{ /* don't forget to notify front end */
Async_NotifyFrontEnd();
}
} }
}
} }
/* /*
*-------------------------------------------------------------- *--------------------------------------------------------------
* Async_Listen -- * Async_Listen --
* *
* Register a backend (identified by its Unix PID) as listening * Register a backend (identified by its Unix PID) as listening
* on the specified relation. * on the specified relation.
* *
* This corresponds to the 'listen <relation>' command in SQL * This corresponds to the 'listen <relation>' command in SQL
* *
* One listener per relation, pg_listener relation is keyed * One listener per relation, pg_listener relation is keyed
* on (relname,pid) to provide multiple listeners in future. * on (relname,pid) to provide multiple listeners in future.
* *
* Results: * Results:
* pg_listeners is updated. * pg_listeners is updated.
* *
* Side effects: * Side effects:
* XXX * XXX
* *
*-------------------------------------------------------------- *--------------------------------------------------------------
*/ */
void void
Async_Listen(char *relname, int pid) Async_Listen(char *relname, int pid)
{ {
Datum values[Natts_pg_listener]; Datum values[Natts_pg_listener];
char nulls[Natts_pg_listener]; char nulls[Natts_pg_listener];
TupleDesc tdesc; TupleDesc tdesc;
HeapScanDesc s; HeapScanDesc s;
HeapTuple htup,tup; HeapTuple htup,
Relation lDesc; tup;
Buffer b; Relation lDesc;
Datum d; Buffer b;
int i; Datum d;
bool isnull; int i;
int alreadyListener = 0; bool isnull;
int ourPid = getpid(); int alreadyListener = 0;
char *relnamei; int ourPid = getpid();
TupleDesc tupDesc; char *relnamei;
TupleDesc tupDesc;
#ifdef ASYNC_DEBUG #ifdef ASYNC_DEBUG
elog(DEBUG,"Async_Listen: %s",relname); elog(DEBUG, "Async_Listen: %s", relname);
#endif #endif
for (i = 0 ; i < Natts_pg_listener; i++) { for (i = 0; i < Natts_pg_listener; i++)
nulls[i] = ' '; {
values[i] = PointerGetDatum(NULL); nulls[i] = ' ';
} values[i] = PointerGetDatum(NULL);
i = 0;
values[i++] = (Datum) relname;
values[i++] = (Datum) pid;
values[i++] = (Datum) 0; /* no notifies pending */
lDesc = heap_openr(ListenerRelationName);
RelationSetLockForWrite(lDesc);
/* is someone already listening. One listener per relation */
tdesc = RelationGetTupleDescriptor(lDesc);
s = heap_beginscan(lDesc,0,NowTimeQual,0,(ScanKey)NULL);
while (HeapTupleIsValid(htup = heap_getnext(s,0,&b))) {
d = (Datum) heap_getattr(htup,b,Anum_pg_listener_relname,tdesc,
&isnull);
relnamei = DatumGetPointer(d);
if (!strncmp(relnamei,relname, NAMEDATALEN)) {
d = (Datum) heap_getattr(htup,b,Anum_pg_listener_pid,tdesc,&isnull);
pid = DatumGetInt32(d);
if (pid == ourPid) {
alreadyListener = 1;
}
} }
ReleaseBuffer(b);
}
heap_endscan(s);
if (alreadyListener) { i = 0;
elog(NOTICE, "Async_Listen: We are already listening on %s", values[i++] = (Datum) relname;
relname); values[i++] = (Datum) pid;
return; values[i++] = (Datum) 0; /* no notifies pending */
}
tupDesc = lDesc->rd_att; lDesc = heap_openr(ListenerRelationName);
tup = heap_formtuple(tupDesc, RelationSetLockForWrite(lDesc);
values,
nulls);
heap_insert(lDesc, tup);
pfree(tup); /* is someone already listening. One listener per relation */
/* if (alreadyListener) { tdesc = RelationGetTupleDescriptor(lDesc);
elog(NOTICE,"Async_Listen: already one listener on %s (possibly dead)",relname); s = heap_beginscan(lDesc, 0, NowTimeQual, 0, (ScanKey) NULL);
}*/ while (HeapTupleIsValid(htup = heap_getnext(s, 0, &b)))
{
d = (Datum) heap_getattr(htup, b, Anum_pg_listener_relname, tdesc,
&isnull);
relnamei = DatumGetPointer(d);
if (!strncmp(relnamei, relname, NAMEDATALEN))
{
d = (Datum) heap_getattr(htup, b, Anum_pg_listener_pid, tdesc, &isnull);
pid = DatumGetInt32(d);
if (pid == ourPid)
{
alreadyListener = 1;
}
}
ReleaseBuffer(b);
}
heap_endscan(s);
RelationUnsetLockForWrite(lDesc); if (alreadyListener)
heap_close(lDesc); {
elog(NOTICE, "Async_Listen: We are already listening on %s",
relname);
return;
}
/* tupDesc = lDesc->rd_att;
* now that we are listening, we should make a note to ourselves tup = heap_formtuple(tupDesc,
* to unlisten prior to dying. values,
*/ nulls);
relnamei = malloc(NAMEDATALEN); /* persists to process exit */ heap_insert(lDesc, tup);
strNcpy(relnamei, relname, NAMEDATALEN-1);
on_exitpg(Async_UnlistenOnExit, (caddr_t) relnamei); pfree(tup);
/*
* if (alreadyListener) { elog(NOTICE,"Async_Listen: already one
* listener on %s (possibly dead)",relname); }
*/
RelationUnsetLockForWrite(lDesc);
heap_close(lDesc);
/*
* now that we are listening, we should make a note to ourselves to
* unlisten prior to dying.
*/
relnamei = malloc(NAMEDATALEN); /* persists to process exit */
strNcpy(relnamei, relname, NAMEDATALEN - 1);
on_exitpg(Async_UnlistenOnExit, (caddr_t) relnamei);
} }
/* /*
*-------------------------------------------------------------- *--------------------------------------------------------------
* Async_Unlisten -- * Async_Unlisten --
* *
* Remove the backend from the list of listening backends * Remove the backend from the list of listening backends
* for the specified relation. * for the specified relation.
* *
* This would correspond to the 'unlisten <relation>' * This would correspond to the 'unlisten <relation>'
* command, but there isn't one yet. * command, but there isn't one yet.
* *
* Results: * Results:
* pg_listeners is updated. * pg_listeners is updated.
* *
* Side effects: * Side effects:
* XXX * XXX
* *
*-------------------------------------------------------------- *--------------------------------------------------------------
*/ */
static void static void
Async_Unlisten(char *relname, int pid) Async_Unlisten(char *relname, int pid)
{ {
Relation lDesc; Relation lDesc;
HeapTuple lTuple; HeapTuple lTuple;
lTuple = SearchSysCacheTuple(LISTENREL, PointerGetDatum(relname), lTuple = SearchSysCacheTuple(LISTENREL, PointerGetDatum(relname),
Int32GetDatum(pid), Int32GetDatum(pid),
0,0); 0, 0);
lDesc = heap_openr(ListenerRelationName); lDesc = heap_openr(ListenerRelationName);
RelationSetLockForWrite(lDesc); RelationSetLockForWrite(lDesc);
if (lTuple != NULL) { if (lTuple != NULL)
heap_delete(lDesc,&lTuple->t_ctid); {
} heap_delete(lDesc, &lTuple->t_ctid);
}
RelationUnsetLockForWrite(lDesc); RelationUnsetLockForWrite(lDesc);
heap_close(lDesc); heap_close(lDesc);
} }
static void static void
Async_UnlistenOnExit(int code, /* from exitpg */ Async_UnlistenOnExit(int code, /* from exitpg */
char *relname) char *relname)
{ {
Async_Unlisten((char *) relname, getpid()); Async_Unlisten((char *) relname, getpid());
} }
/* /*
* -------------------------------------------------------------- * --------------------------------------------------------------
* Async_NotifyFrontEnd -- * Async_NotifyFrontEnd --
* *
* Perform an asynchronous notification to front end over * Perform an asynchronous notification to front end over
* portal comm channel. The name of the relation which contains the * portal comm channel. The name of the relation which contains the
* data is sent to the front end. * data is sent to the front end.
* *
* We remove the notification flag from the pg_listener tuple * We remove the notification flag from the pg_listener tuple
* associated with our process. * associated with our process.
* *
* Results: * Results:
* XXX * XXX
* *
* Side effects: * Side effects:
* *
* We make use of the out-of-band channel to transmit the * We make use of the out-of-band channel to transmit the
* notification to the front end. The actual data transfer takes * notification to the front end. The actual data transfer takes
* place at the front end's request. * place at the front end's request.
* *
* -------------------------------------------------------------- * --------------------------------------------------------------
*/ */
GlobalMemory notifyContext = NULL; GlobalMemory notifyContext = NULL;
static void static void
Async_NotifyFrontEnd() Async_NotifyFrontEnd()
{ {
extern CommandDest whereToSendOutput; extern CommandDest whereToSendOutput;
HeapTuple lTuple, rTuple; HeapTuple lTuple,
Relation lRel; rTuple;
HeapScanDesc sRel; Relation lRel;
TupleDesc tdesc; HeapScanDesc sRel;
ScanKeyData key[2]; TupleDesc tdesc;
Datum d, value[3]; ScanKeyData key[2];
char repl[3], nulls[3]; Datum d,
Buffer b; value[3];
int ourpid; char repl[3],
bool isnull; nulls[3];
Buffer b;
int ourpid;
bool isnull;
notifyFrontEndPending = 0; notifyFrontEndPending = 0;
#ifdef ASYNC_DEBUG #ifdef ASYNC_DEBUG
elog(DEBUG, "Async_NotifyFrontEnd: notifying front end."); elog(DEBUG, "Async_NotifyFrontEnd: notifying front end.");
#endif #endif
StartTransactionCommand(); StartTransactionCommand();
ourpid = getpid(); ourpid = getpid();
ScanKeyEntryInitialize(&key[0], 0, ScanKeyEntryInitialize(&key[0], 0,
Anum_pg_listener_notify, Anum_pg_listener_notify,
Integer32EqualRegProcedure, Integer32EqualRegProcedure,
Int32GetDatum(1)); Int32GetDatum(1));
ScanKeyEntryInitialize(&key[1], 0, ScanKeyEntryInitialize(&key[1], 0,
Anum_pg_listener_pid, Anum_pg_listener_pid,
Integer32EqualRegProcedure, Integer32EqualRegProcedure,
Int32GetDatum(ourpid)); Int32GetDatum(ourpid));
lRel = heap_openr(ListenerRelationName); lRel = heap_openr(ListenerRelationName);
RelationSetLockForWrite(lRel); RelationSetLockForWrite(lRel);
tdesc = RelationGetTupleDescriptor(lRel); tdesc = RelationGetTupleDescriptor(lRel);
sRel = heap_beginscan(lRel, 0, NowTimeQual, 2, key); sRel = heap_beginscan(lRel, 0, NowTimeQual, 2, key);
nulls[0] = nulls[1] = nulls[2] = ' '; nulls[0] = nulls[1] = nulls[2] = ' ';
repl[0] = repl[1] = repl[2] = ' '; repl[0] = repl[1] = repl[2] = ' ';
repl[Anum_pg_listener_notify - 1] = 'r'; repl[Anum_pg_listener_notify - 1] = 'r';
value[0] = value[1] = value[2] = (Datum) 0; value[0] = value[1] = value[2] = (Datum) 0;
value[Anum_pg_listener_notify - 1] = Int32GetDatum(0); value[Anum_pg_listener_notify - 1] = Int32GetDatum(0);
while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0,&b))) { while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0, &b)))
d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_relname, {
tdesc, &isnull); d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_relname,
rTuple = heap_modifytuple(lTuple, b, lRel, value, nulls, repl); tdesc, &isnull);
heap_replace(lRel, &lTuple->t_ctid, rTuple); rTuple = heap_modifytuple(lTuple, b, lRel, value, nulls, repl);
heap_replace(lRel, &lTuple->t_ctid, rTuple);
/* notifying the front end */ /* notifying the front end */
if (whereToSendOutput == Remote) { if (whereToSendOutput == Remote)
pq_putnchar("A", 1); {
pq_putint(ourpid, 4); pq_putnchar("A", 1);
pq_putstr(DatumGetName(d)->data); pq_putint(ourpid, 4);
pq_flush(); pq_putstr(DatumGetName(d)->data);
} else { pq_flush();
elog(NOTICE, "Async_NotifyFrontEnd: no asynchronous notification to frontend on interactive sessions"); }
else
{
elog(NOTICE, "Async_NotifyFrontEnd: no asynchronous notification to frontend on interactive sessions");
}
ReleaseBuffer(b);
} }
ReleaseBuffer(b); CommitTransactionCommand();
}
CommitTransactionCommand();
} }
static int static int
AsyncExistsPendingNotify(char *relname) AsyncExistsPendingNotify(char *relname)
{ {
Dlelem* p; Dlelem *p;
for (p = DLGetHead(pendingNotifies);
p != NULL;
p = DLGetSucc(p)) {
/* Use NAMEDATALEN for relname comparison. DZ - 26-08-1996 */
if (!strncmp(DLE_VAL(p), relname, NAMEDATALEN))
return 1;
}
return 0; for (p = DLGetHead(pendingNotifies);
p != NULL;
p = DLGetSucc(p))
{
/* Use NAMEDATALEN for relname comparison. DZ - 26-08-1996 */
if (!strncmp(DLE_VAL(p), relname, NAMEDATALEN))
return 1;
}
return 0;
} }
static void static void
ClearPendingNotify() ClearPendingNotify()
{ {
Dlelem* p; Dlelem *p;
while ( (p = DLRemHead(pendingNotifies)) != NULL)
free(DLE_VAL(p));
}
while ((p = DLRemHead(pendingNotifies)) != NULL)
free(DLE_VAL(p));
}

View File

@ -1,20 +1,20 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* cluster.c-- * cluster.c--
* Paul Brown's implementation of cluster index. * Paul Brown's implementation of cluster index.
* *
* I am going to use the rename function as a model for this in the * I am going to use the rename function as a model for this in the
* parser and executor, and the vacuum code as an example in this * parser and executor, and the vacuum code as an example in this
* file. As I go - in contrast to the rest of postgres - there will * file. As I go - in contrast to the rest of postgres - there will
* be BUCKETS of comments. This is to allow reviewers to understand * be BUCKETS of comments. This is to allow reviewers to understand
* my (probably bogus) assumptions about the way this works. * my (probably bogus) assumptions about the way this works.
* [pbrown '94] * [pbrown '94]
* *
* Copyright (c) 1994-5, Regents of the University of California * Copyright (c) 1994-5, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.13 1997/08/19 21:30:45 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.14 1997/09/07 04:40:36 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -47,307 +47,323 @@
#include <optimizer/internal.h> #include <optimizer/internal.h>
#ifndef NO_SECURITY #ifndef NO_SECURITY
#include <utils/acl.h> #include <utils/acl.h>
#endif /* !NO_SECURITY */ #endif /* !NO_SECURITY */
static Relation copy_heap(Oid OIDOldHeap); static Relation copy_heap(Oid OIDOldHeap);
static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap); static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap);
static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex); static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
/* /*
* cluster * cluster
* *
* Check that the relation is a relation in the appropriate user * Check that the relation is a relation in the appropriate user
* ACL. I will use the same security that limits users on the * ACL. I will use the same security that limits users on the
* renamerel() function. * renamerel() function.
* *
* Check that the index specified is appropriate for the task * Check that the index specified is appropriate for the task
* ( ie it's an index over this relation ). This is trickier. * ( ie it's an index over this relation ). This is trickier.
* *
* Create a list of all the other indicies on this relation. Because * Create a list of all the other indicies on this relation. Because
* the cluster will wreck all the tids, I'll need to destroy bogus * the cluster will wreck all the tids, I'll need to destroy bogus
* indicies. The user will have to re-create them. Not nice, but * indicies. The user will have to re-create them. Not nice, but
* I'm not a nice guy. The alternative is to try some kind of post * I'm not a nice guy. The alternative is to try some kind of post
* destroy re-build. This may be possible. I'll check out what the * destroy re-build. This may be possible. I'll check out what the
* index create functiond want in the way of paramaters. On the other * index create functiond want in the way of paramaters. On the other
* hand, re-creating n indicies may blow out the space. * hand, re-creating n indicies may blow out the space.
* *
* Create new (temporary) relations for the base heap and the new * Create new (temporary) relations for the base heap and the new
* index. * index.
* *
* Exclusively lock the relations. * Exclusively lock the relations.
* *
* Create new clustered index and base heap relation. * Create new clustered index and base heap relation.
* *
*/ */
void void
cluster(char oldrelname[], char oldindexname[]) cluster(char oldrelname[], char oldindexname[])
{ {
Oid OIDOldHeap, OIDOldIndex, OIDNewHeap; Oid OIDOldHeap,
OIDOldIndex,
OIDNewHeap;
Relation OldHeap, OldIndex; Relation OldHeap,
Relation NewHeap; OldIndex;
Relation NewHeap;
char NewIndexName[NAMEDATALEN]; char NewIndexName[NAMEDATALEN];
char NewHeapName[NAMEDATALEN]; char NewHeapName[NAMEDATALEN];
char saveoldrelname[NAMEDATALEN]; char saveoldrelname[NAMEDATALEN];
char saveoldindexname[NAMEDATALEN]; char saveoldindexname[NAMEDATALEN];
/* Save the old names because they will get lost when the old relations /*
* are destroyed. * Save the old names because they will get lost when the old
*/ * relations are destroyed.
strcpy(saveoldrelname, oldrelname); */
strcpy(saveoldindexname, oldindexname); strcpy(saveoldrelname, oldrelname);
strcpy(saveoldindexname, oldindexname);
/* /*
* * I'm going to force all checking back into the commands.c function.
* I'm going to force all checking back into the commands.c function. *
* * Get the list if indicies for this relation. If the index we want is
* Get the list if indicies for this relation. If the index we want * among them, do not add it to the 'kill' list, as it will be handled
* is among them, do not add it to the 'kill' list, as it will be * by the 'clean up' code which commits this transaction.
* handled by the 'clean up' code which commits this transaction. *
* * I'm not using the SysCache, because this will happen but once, and the
* I'm not using the SysCache, because this will happen but * slow way is the sure way in this case.
* once, and the slow way is the sure way in this case. *
* */
*/
/*
* Like vacuum, cluster spans transactions, so I'm going to handle it in
* the same way.
*/
/* matches the StartTransaction in PostgresMain() */ /*
* Like vacuum, cluster spans transactions, so I'm going to handle it
* in the same way.
*/
OldHeap = heap_openr(oldrelname); /* matches the StartTransaction in PostgresMain() */
if (!RelationIsValid(OldHeap)) {
elog(WARN, "cluster: unknown relation: \"%s\"",
oldrelname);
}
OIDOldHeap = OldHeap->rd_id; /* Get OID for the index scan */
OldIndex=index_openr(oldindexname);/* Open old index relation */ OldHeap = heap_openr(oldrelname);
if (!RelationIsValid(OldIndex)) { if (!RelationIsValid(OldHeap))
elog(WARN, "cluster: unknown index: \"%s\"", {
oldindexname); elog(WARN, "cluster: unknown relation: \"%s\"",
} oldrelname);
OIDOldIndex = OldIndex->rd_id; /* OID for the index scan */ }
OIDOldHeap = OldHeap->rd_id;/* Get OID for the index scan */
heap_close(OldHeap); OldIndex = index_openr(oldindexname); /* Open old index relation */
index_close(OldIndex); if (!RelationIsValid(OldIndex))
{
elog(WARN, "cluster: unknown index: \"%s\"",
oldindexname);
}
OIDOldIndex = OldIndex->rd_id; /* OID for the index scan */
/* heap_close(OldHeap);
* I need to build the copies of the heap and the index. The Commit() index_close(OldIndex);
* between here is *very* bogus. If someone is appending stuff, they will
* get the lock after being blocked and add rows which won't be present in /*
* the new table. Bleagh! I'd be best to try and ensure that no-one's * I need to build the copies of the heap and the index. The Commit()
* in the tables for the entire duration of this process with a pg_vlock. * between here is *very* bogus. If someone is appending stuff, they
*/ * will get the lock after being blocked and add rows which won't be
NewHeap = copy_heap(OIDOldHeap); * present in the new table. Bleagh! I'd be best to try and ensure
OIDNewHeap = NewHeap->rd_id; * that no-one's in the tables for the entire duration of this process
strcpy(NewHeapName,NewHeap->rd_rel->relname.data); * with a pg_vlock.
*/
NewHeap = copy_heap(OIDOldHeap);
OIDNewHeap = NewHeap->rd_id;
strcpy(NewHeapName, NewHeap->rd_rel->relname.data);
/* To make the new heap visible (which is until now empty). */ /* To make the new heap visible (which is until now empty). */
CommandCounterIncrement(); CommandCounterIncrement();
rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex); rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex);
/* To flush the filled new heap (and the statistics about it). */ /* To flush the filled new heap (and the statistics about it). */
CommandCounterIncrement(); CommandCounterIncrement();
/* Create new index over the tuples of the new heap. */ /* Create new index over the tuples of the new heap. */
copy_index(OIDOldIndex, OIDNewHeap); copy_index(OIDOldIndex, OIDNewHeap);
sprintf(NewIndexName, "temp_%x", OIDOldIndex); sprintf(NewIndexName, "temp_%x", OIDOldIndex);
/* /*
* make this really happen. Flush all the buffers. * make this really happen. Flush all the buffers. (Believe me, it is
* (Believe me, it is necessary ... ended up in a mess without it.) * necessary ... ended up in a mess without it.)
*/ */
CommitTransactionCommand(); CommitTransactionCommand();
StartTransactionCommand(); StartTransactionCommand();
/* Destroy old heap (along with its index) and rename new. */ /* Destroy old heap (along with its index) and rename new. */
heap_destroy(oldrelname); heap_destroy(oldrelname);
renamerel(NewHeapName, saveoldrelname); renamerel(NewHeapName, saveoldrelname);
TypeRename(NewHeapName, saveoldrelname); TypeRename(NewHeapName, saveoldrelname);
renamerel(NewIndexName, saveoldindexname); renamerel(NewIndexName, saveoldindexname);
/* /*
* Again flush all the buffers. * Again flush all the buffers.
*/ */
CommitTransactionCommand(); CommitTransactionCommand();
StartTransactionCommand(); StartTransactionCommand();
} }
static Relation static Relation
copy_heap(Oid OIDOldHeap) copy_heap(Oid OIDOldHeap)
{ {
char NewName[NAMEDATALEN]; char NewName[NAMEDATALEN];
TupleDesc OldHeapDesc, tupdesc; TupleDesc OldHeapDesc,
Oid OIDNewHeap; tupdesc;
Relation NewHeap, OldHeap; Oid OIDNewHeap;
Relation NewHeap,
OldHeap;
/* /*
* Create a new heap relation with a temporary name, which has the * Create a new heap relation with a temporary name, which has the
* same tuple description as the old one. * same tuple description as the old one.
*/ */
sprintf(NewName,"temp_%x", OIDOldHeap); sprintf(NewName, "temp_%x", OIDOldHeap);
OldHeap= heap_open(OIDOldHeap); OldHeap = heap_open(OIDOldHeap);
OldHeapDesc= RelationGetTupleDescriptor(OldHeap); OldHeapDesc = RelationGetTupleDescriptor(OldHeap);
/* /*
* Need to make a copy of the tuple descriptor, heap_create modifies * Need to make a copy of the tuple descriptor, heap_create modifies
* it. * it.
*/ */
tupdesc = CreateTupleDescCopy(OldHeapDesc); tupdesc = CreateTupleDescCopy(OldHeapDesc);
OIDNewHeap=heap_create(NewName, OIDNewHeap = heap_create(NewName,
NULL, NULL,
OldHeap->rd_rel->relarch, OldHeap->rd_rel->relarch,
OldHeap->rd_rel->relsmgr, OldHeap->rd_rel->relsmgr,
tupdesc); tupdesc);
if (!OidIsValid(OIDNewHeap)) if (!OidIsValid(OIDNewHeap))
elog(WARN,"clusterheap: cannot create temporary heap relation\n"); elog(WARN, "clusterheap: cannot create temporary heap relation\n");
NewHeap=heap_open(OIDNewHeap); NewHeap = heap_open(OIDNewHeap);
heap_close(NewHeap); heap_close(NewHeap);
heap_close(OldHeap); heap_close(OldHeap);
return NewHeap; return NewHeap;
} }
static void static void
copy_index(Oid OIDOldIndex, Oid OIDNewHeap) copy_index(Oid OIDOldIndex, Oid OIDNewHeap)
{ {
Relation OldIndex, NewHeap; Relation OldIndex,
HeapTuple Old_pg_index_Tuple, Old_pg_index_relation_Tuple, pg_proc_Tuple; NewHeap;
IndexTupleForm Old_pg_index_Form; HeapTuple Old_pg_index_Tuple,
Form_pg_class Old_pg_index_relation_Form; Old_pg_index_relation_Tuple,
Form_pg_proc pg_proc_Form; pg_proc_Tuple;
char *NewIndexName; IndexTupleForm Old_pg_index_Form;
AttrNumber *attnumP; Form_pg_class Old_pg_index_relation_Form;
int natts; Form_pg_proc pg_proc_Form;
FuncIndexInfo * finfo; char *NewIndexName;
AttrNumber *attnumP;
int natts;
FuncIndexInfo *finfo;
NewHeap = heap_open(OIDNewHeap); NewHeap = heap_open(OIDNewHeap);
OldIndex = index_open(OIDOldIndex); OldIndex = index_open(OIDOldIndex);
/* /*
* OK. Create a new (temporary) index for the one that's already * OK. Create a new (temporary) index for the one that's already here.
* here. To do this I get the info from pg_index, re-build the * To do this I get the info from pg_index, re-build the FunctInfo if
* FunctInfo if I have to, and add a new index with a temporary * I have to, and add a new index with a temporary name.
* name. */
*/ Old_pg_index_Tuple =
Old_pg_index_Tuple = SearchSysCacheTuple(INDEXRELID,
SearchSysCacheTuple(INDEXRELID, ObjectIdGetDatum(OldIndex->rd_id),
ObjectIdGetDatum(OldIndex->rd_id), 0, 0, 0);
0,0,0);
Assert(Old_pg_index_Tuple); Assert(Old_pg_index_Tuple);
Old_pg_index_Form = (IndexTupleForm)GETSTRUCT(Old_pg_index_Tuple); Old_pg_index_Form = (IndexTupleForm) GETSTRUCT(Old_pg_index_Tuple);
Old_pg_index_relation_Tuple = Old_pg_index_relation_Tuple =
SearchSysCacheTuple(RELOID, SearchSysCacheTuple(RELOID,
ObjectIdGetDatum(OldIndex->rd_id), ObjectIdGetDatum(OldIndex->rd_id),
0,0,0); 0, 0, 0);
Assert(Old_pg_index_relation_Tuple); Assert(Old_pg_index_relation_Tuple);
Old_pg_index_relation_Form = Old_pg_index_relation_Form =
(Form_pg_class)GETSTRUCT(Old_pg_index_relation_Tuple); (Form_pg_class) GETSTRUCT(Old_pg_index_relation_Tuple);
NewIndexName = palloc(NAMEDATALEN); /* XXX */ NewIndexName = palloc(NAMEDATALEN); /* XXX */
sprintf(NewIndexName, "temp_%x", OIDOldIndex); /* Set the name. */ sprintf(NewIndexName, "temp_%x", OIDOldIndex); /* Set the name. */
/* /*
* Ugly as it is, the only way I have of working out the number of * Ugly as it is, the only way I have of working out the number of
* attribues is to count them. Mostly there'll be just one but * attribues is to count them. Mostly there'll be just one but I've
* I've got to be sure. * got to be sure.
*/ */
for (attnumP = &(Old_pg_index_Form->indkey[0]), natts = 0; for (attnumP = &(Old_pg_index_Form->indkey[0]), natts = 0;
*attnumP != InvalidAttrNumber; *attnumP != InvalidAttrNumber;
attnumP++, natts++); attnumP++, natts++);
/* /*
* If this is a functional index, I need to rebuild the functional * If this is a functional index, I need to rebuild the functional
* component to pass it to the defining procedure. * component to pass it to the defining procedure.
*/ */
if (Old_pg_index_Form->indproc != InvalidOid) { if (Old_pg_index_Form->indproc != InvalidOid)
finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo)); {
FIgetnArgs(finfo) = natts; finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo));
FIgetProcOid(finfo) = Old_pg_index_Form->indproc; FIgetnArgs(finfo) = natts;
FIgetProcOid(finfo) = Old_pg_index_Form->indproc;
pg_proc_Tuple = pg_proc_Tuple =
SearchSysCacheTuple(PROOID, SearchSysCacheTuple(PROOID,
ObjectIdGetDatum(Old_pg_index_Form->indproc), ObjectIdGetDatum(Old_pg_index_Form->indproc),
0,0,0); 0, 0, 0);
Assert(pg_proc_Tuple); Assert(pg_proc_Tuple);
pg_proc_Form = (Form_pg_proc)GETSTRUCT(pg_proc_Tuple); pg_proc_Form = (Form_pg_proc) GETSTRUCT(pg_proc_Tuple);
namecpy(&(finfo->funcName), &(pg_proc_Form->proname)); namecpy(&(finfo->funcName), &(pg_proc_Form->proname));
} else { }
finfo = (FuncIndexInfo *) NULL; else
natts = 1; {
} finfo = (FuncIndexInfo *) NULL;
natts = 1;
}
index_create((NewHeap->rd_rel->relname).data, index_create((NewHeap->rd_rel->relname).data,
NewIndexName, NewIndexName,
finfo, finfo,
NULL, /* type info is in the old index */ NULL, /* type info is in the old index */
Old_pg_index_relation_Form->relam, Old_pg_index_relation_Form->relam,
natts, natts,
Old_pg_index_Form->indkey, Old_pg_index_Form->indkey,
Old_pg_index_Form->indclass, Old_pg_index_Form->indclass,
(uint16)0, (Datum) NULL, NULL, (uint16) 0, (Datum) NULL, NULL,
Old_pg_index_Form->indislossy, Old_pg_index_Form->indislossy,
Old_pg_index_Form->indisunique); Old_pg_index_Form->indisunique);
heap_close(OldIndex); heap_close(OldIndex);
heap_close(NewHeap); heap_close(NewHeap);
} }
static void static void
rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
{ {
Relation LocalNewHeap, LocalOldHeap, LocalOldIndex; Relation LocalNewHeap,
IndexScanDesc ScanDesc; LocalOldHeap,
RetrieveIndexResult ScanResult; LocalOldIndex;
ItemPointer HeapTid; IndexScanDesc ScanDesc;
HeapTuple LocalHeapTuple; RetrieveIndexResult ScanResult;
Buffer LocalBuffer; ItemPointer HeapTid;
Oid OIDNewHeapInsert; HeapTuple LocalHeapTuple;
Buffer LocalBuffer;
Oid OIDNewHeapInsert;
/* /*
* Open the relations I need. Scan through the OldHeap on the OldIndex and * Open the relations I need. Scan through the OldHeap on the OldIndex
* insert each tuple into the NewHeap. * and insert each tuple into the NewHeap.
*/ */
LocalNewHeap=(Relation)heap_open(OIDNewHeap); LocalNewHeap = (Relation) heap_open(OIDNewHeap);
LocalOldHeap=(Relation)heap_open(OIDOldHeap); LocalOldHeap = (Relation) heap_open(OIDOldHeap);
LocalOldIndex=(Relation)index_open(OIDOldIndex); LocalOldIndex = (Relation) index_open(OIDOldIndex);
ScanDesc=index_beginscan(LocalOldIndex, false, 0, (ScanKey) NULL); ScanDesc = index_beginscan(LocalOldIndex, false, 0, (ScanKey) NULL);
while ((ScanResult = while ((ScanResult =
index_getnext(ScanDesc, ForwardScanDirection)) != NULL) { index_getnext(ScanDesc, ForwardScanDirection)) != NULL)
{
HeapTid = &ScanResult->heap_iptr; HeapTid = &ScanResult->heap_iptr;
LocalHeapTuple = heap_fetch(LocalOldHeap, 0, HeapTid, &LocalBuffer); LocalHeapTuple = heap_fetch(LocalOldHeap, 0, HeapTid, &LocalBuffer);
OIDNewHeapInsert = OIDNewHeapInsert =
heap_insert(LocalNewHeap, LocalHeapTuple); heap_insert(LocalNewHeap, LocalHeapTuple);
pfree(ScanResult); pfree(ScanResult);
ReleaseBuffer(LocalBuffer); ReleaseBuffer(LocalBuffer);
} }
index_endscan(ScanDesc); index_endscan(ScanDesc);
index_close(LocalOldIndex); index_close(LocalOldIndex);
heap_close(LocalOldHeap); heap_close(LocalOldHeap);
heap_close(LocalNewHeap); heap_close(LocalNewHeap);
} }

View File

@ -1,22 +1,22 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* command.c-- * command.c--
* random postgres portal and utility support code * random postgres portal and utility support code
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.13 1997/08/22 14:22:07 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.14 1997/09/07 04:40:38 momjian Exp $
* *
* NOTES * NOTES
* The PortalExecutorHeapMemory crap needs to be eliminated * The PortalExecutorHeapMemory crap needs to be eliminated
* by designing a better executor / portal processing memory * by designing a better executor / portal processing memory
* interface. * interface.
* *
* The PerformAddAttribute() code, like most of the relation * The PerformAddAttribute() code, like most of the relation
* manipulating code in the commands/ directory, should go * manipulating code in the commands/ directory, should go
* someplace closer to the lib/catalog code. * someplace closer to the lib/catalog code.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -41,443 +41,468 @@
#include <utils/builtins.h> #include <utils/builtins.h>
/* ---------------- /* ----------------
* PortalExecutorHeapMemory stuff * PortalExecutorHeapMemory stuff
* *
* This is where the XXXSuperDuperHacky code was. -cim 3/15/90 * This is where the XXXSuperDuperHacky code was. -cim 3/15/90
* ---------------- * ----------------
*/ */
MemoryContext PortalExecutorHeapMemory = NULL; MemoryContext PortalExecutorHeapMemory = NULL;
/* -------------------------------- /* --------------------------------
* PortalCleanup * PortalCleanup
* -------------------------------- * --------------------------------
*/ */
void void
PortalCleanup(Portal portal) PortalCleanup(Portal portal)
{ {
MemoryContext context; MemoryContext context;
/* ---------------- /* ----------------
* sanity checks * sanity checks
* ---------------- * ----------------
*/ */
AssertArg(PortalIsValid(portal)); AssertArg(PortalIsValid(portal));
AssertArg(portal->cleanup == PortalCleanup); AssertArg(portal->cleanup == PortalCleanup);
/* ---------------- /* ----------------
* set proper portal-executor context before calling ExecMain. * set proper portal-executor context before calling ExecMain.
* ---------------- * ----------------
*/ */
context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal)); context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
PortalExecutorHeapMemory = (MemoryContext) PortalExecutorHeapMemory = (MemoryContext)
PortalGetHeapMemory(portal); PortalGetHeapMemory(portal);
/* ---------------- /* ----------------
* tell the executor to shutdown the query * tell the executor to shutdown the query
* ---------------- * ----------------
*/ */
ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal)); ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal));
/* ---------------- /* ----------------
* switch back to previous context * switch back to previous context
* ---------------- * ----------------
*/ */
MemoryContextSwitchTo(context); MemoryContextSwitchTo(context);
PortalExecutorHeapMemory = (MemoryContext) NULL; PortalExecutorHeapMemory = (MemoryContext) NULL;
} }
/* -------------------------------- /* --------------------------------
* PerformPortalFetch * PerformPortalFetch
* -------------------------------- * --------------------------------
*/ */
void void
PerformPortalFetch(char *name, PerformPortalFetch(char *name,
bool forward, bool forward,
int count, int count,
char *tag, char *tag,
CommandDest dest) CommandDest dest)
{ {
Portal portal; Portal portal;
int feature; int feature;
QueryDesc *queryDesc; QueryDesc *queryDesc;
MemoryContext context; MemoryContext context;
/* ---------------- /* ----------------
* sanity checks * sanity checks
* ---------------- * ----------------
*/ */
if (name == NULL) { if (name == NULL)
elog(NOTICE, "PerformPortalFetch: blank portal unsupported"); {
return; elog(NOTICE, "PerformPortalFetch: blank portal unsupported");
} return;
}
/* ---------------- /* ----------------
* get the portal from the portal name * get the portal from the portal name
* ---------------- * ----------------
*/ */
portal = GetPortalByName(name); portal = GetPortalByName(name);
if (! PortalIsValid(portal)) { if (!PortalIsValid(portal))
elog(NOTICE, "PerformPortalFetch: portal \"%s\" not found", {
name); elog(NOTICE, "PerformPortalFetch: portal \"%s\" not found",
return; name);
} return;
}
/* ---------------- /* ----------------
* switch into the portal context * switch into the portal context
* ---------------- * ----------------
*/ */
context= MemoryContextSwitchTo((MemoryContext)PortalGetHeapMemory(portal)); context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
AssertState(context == AssertState(context ==
(MemoryContext)PortalGetHeapMemory(GetPortalByName(NULL))); (MemoryContext) PortalGetHeapMemory(GetPortalByName(NULL)));
/* ---------------- /* ----------------
* setup "feature" to tell the executor what direction and * setup "feature" to tell the executor what direction and
* how many tuples to fetch. * how many tuples to fetch.
* ---------------- * ----------------
*/ */
if (forward) if (forward)
feature = EXEC_FOR; feature = EXEC_FOR;
else else
feature = EXEC_BACK; feature = EXEC_BACK;
/* ---------------- /* ----------------
* tell the destination to prepare to recieve some tuples * tell the destination to prepare to recieve some tuples
* ---------------- * ----------------
*/ */
queryDesc = PortalGetQueryDesc(portal); queryDesc = PortalGetQueryDesc(portal);
BeginCommand(name, BeginCommand(name,
queryDesc->operation, queryDesc->operation,
portal->attinfo,/* QueryDescGetTypeInfo(queryDesc), */ portal->attinfo, /* QueryDescGetTypeInfo(queryDesc),
false, /* portal fetches don't end up in relations */ * */
false, /* this is a portal fetch, not a "retrieve portal" */ false, /* portal fetches don't end up in
tag, * relations */
dest); false, /* this is a portal fetch, not a "retrieve
* portal" */
tag,
dest);
/* ---------------- /* ----------------
* execute the portal fetch operation * execute the portal fetch operation
* ---------------- * ----------------
*/ */
PortalExecutorHeapMemory = (MemoryContext) PortalExecutorHeapMemory = (MemoryContext)
PortalGetHeapMemory(portal); PortalGetHeapMemory(portal);
ExecutorRun(queryDesc, PortalGetState(portal), feature, count); ExecutorRun(queryDesc, PortalGetState(portal), feature, count);
/* ---------------- /* ----------------
* Note: the "end-of-command" tag is returned by higher-level * Note: the "end-of-command" tag is returned by higher-level
* utility code * utility code
* *
* Return blank portal for now. * Return blank portal for now.
* Otherwise, this named portal will be cleaned. * Otherwise, this named portal will be cleaned.
* Note: portals will only be supported within a BEGIN...END * Note: portals will only be supported within a BEGIN...END
* block in the near future. Later, someone will fix it to * block in the near future. Later, someone will fix it to
* do what is possible across transaction boundries. * do what is possible across transaction boundries.
* ---------------- * ----------------
*/ */
MemoryContextSwitchTo( MemoryContextSwitchTo(
(MemoryContext)PortalGetHeapMemory(GetPortalByName(NULL))); (MemoryContext) PortalGetHeapMemory(GetPortalByName(NULL)));
} }
/* -------------------------------- /* --------------------------------
* PerformPortalClose * PerformPortalClose
* -------------------------------- * --------------------------------
*/ */
void void
PerformPortalClose(char *name, CommandDest dest) PerformPortalClose(char *name, CommandDest dest)
{ {
Portal portal; Portal portal;
/* ---------------- /* ----------------
* sanity checks * sanity checks
* ---------------- * ----------------
*/ */
if (name == NULL) { if (name == NULL)
elog(NOTICE, "PerformPortalClose: blank portal unsupported"); {
return; elog(NOTICE, "PerformPortalClose: blank portal unsupported");
} return;
}
/* ---------------- /* ----------------
* get the portal from the portal name * get the portal from the portal name
* ---------------- * ----------------
*/ */
portal = GetPortalByName(name); portal = GetPortalByName(name);
if (! PortalIsValid(portal)) { if (!PortalIsValid(portal))
elog(NOTICE, "PerformPortalClose: portal \"%s\" not found", {
name); elog(NOTICE, "PerformPortalClose: portal \"%s\" not found",
return; name);
} return;
}
/* ---------------- /* ----------------
* Note: PortalCleanup is called as a side-effect * Note: PortalCleanup is called as a side-effect
* ---------------- * ----------------
*/ */
PortalDestroy(&portal); PortalDestroy(&portal);
} }
/* ---------------- /* ----------------
* PerformAddAttribute * PerformAddAttribute
* *
* adds an additional attribute to a relation * adds an additional attribute to a relation
* *
* Adds attribute field(s) to a relation. Each new attribute * Adds attribute field(s) to a relation. Each new attribute
* is given attnums in sequential order and is added to the * is given attnums in sequential order and is added to the
* ATTRIBUTE relation. If the AMI fails, defunct tuples will * ATTRIBUTE relation. If the AMI fails, defunct tuples will
* remain in the ATTRIBUTE relation for later vacuuming. * remain in the ATTRIBUTE relation for later vacuuming.
* Later, there may be some reserved attribute names??? * Later, there may be some reserved attribute names???
* *
* (If needed, can instead use elog to handle exceptions.) * (If needed, can instead use elog to handle exceptions.)
* *
* Note: * Note:
* Initial idea of ordering the tuple attributes so that all * Initial idea of ordering the tuple attributes so that all
* the variable length domains occured last was scratched. Doing * the variable length domains occured last was scratched. Doing
* so would not speed access too much (in general) and would create * so would not speed access too much (in general) and would create
* many complications in formtuple, amgetattr, and addattribute. * many complications in formtuple, amgetattr, and addattribute.
* *
* scan attribute catalog for name conflict (within rel) * scan attribute catalog for name conflict (within rel)
* scan type catalog for absence of data type (if not arg) * scan type catalog for absence of data type (if not arg)
* create attnum magically??? * create attnum magically???
* create attribute tuple * create attribute tuple
* insert attribute in attribute catalog * insert attribute in attribute catalog
* modify reldesc * modify reldesc
* create new relation tuple * create new relation tuple
* insert new relation in relation catalog * insert new relation in relation catalog
* delete original relation from relation catalog * delete original relation from relation catalog
* ---------------- * ----------------
*/ */
void void
PerformAddAttribute(char *relationName, PerformAddAttribute(char *relationName,
char *userName, char *userName,
bool inherits, bool inherits,
ColumnDef *colDef) ColumnDef * colDef)
{ {
Relation relrdesc, attrdesc; Relation relrdesc,
HeapScanDesc attsdesc; attrdesc;
HeapTuple reltup; HeapScanDesc attsdesc;
HeapTuple attributeTuple; HeapTuple reltup;
AttributeTupleForm attribute; HeapTuple attributeTuple;
FormData_pg_attribute attributeD; AttributeTupleForm attribute;
int i; FormData_pg_attribute attributeD;
int minattnum, maxatts; int i;
HeapTuple tup; int minattnum,
ScanKeyData key[2]; maxatts;
ItemPointerData oldTID; HeapTuple tup;
Relation idescs[Num_pg_attr_indices]; ScanKeyData key[2];
Relation ridescs[Num_pg_class_indices]; ItemPointerData oldTID;
bool hasindex; Relation idescs[Num_pg_attr_indices];
Relation ridescs[Num_pg_class_indices];
bool hasindex;
/* /*
* permissions checking. this would normally be done in utility.c, * permissions checking. this would normally be done in utility.c,
* but this particular routine is recursive. * but this particular routine is recursive.
* *
* normally, only the owner of a class can change its schema. * normally, only the owner of a class can change its schema.
*/ */
if (IsSystemRelationName(relationName)) if (IsSystemRelationName(relationName))
elog(WARN, "PerformAddAttribute: class \"%s\" is a system catalog", elog(WARN, "PerformAddAttribute: class \"%s\" is a system catalog",
relationName); relationName);
#ifndef NO_SECURITY #ifndef NO_SECURITY
if (!pg_ownercheck(userName, relationName, RELNAME)) if (!pg_ownercheck(userName, relationName, RELNAME))
elog(WARN, "PerformAddAttribute: you do not own class \"%s\"", elog(WARN, "PerformAddAttribute: you do not own class \"%s\"",
relationName); relationName);
#endif #endif
/*
* we can't add a not null attribute
*/
if (colDef->is_not_null)
elog(WARN,"Can't add a not null attribute to a existent relation");
if (colDef->defval)
elog(WARN,"ADD ATTRIBUTE: DEFAULT is not implemented, yet");
/*
* if the first element in the 'schema' list is a "*" then we are
* supposed to add this attribute to all classes that inherit from
* 'relationName' (as well as to 'relationName').
*
* any permissions or problems with duplicate attributes will cause
* the whole transaction to abort, which is what we want -- all or
* nothing.
*/
if (colDef != NULL) {
if (inherits) {
Oid myrelid, childrelid;
List *child, *children;
relrdesc = heap_openr(relationName); /*
if (!RelationIsValid(relrdesc)) { * we can't add a not null attribute
elog(WARN, "PerformAddAttribute: unknown relation: \"%s\"", */
relationName); if (colDef->is_not_null)
} elog(WARN, "Can't add a not null attribute to a existent relation");
myrelid = relrdesc->rd_id; if (colDef->defval)
heap_close(relrdesc); elog(WARN, "ADD ATTRIBUTE: DEFAULT is not implemented, yet");
/* this routine is actually in the planner */ /*
children = find_all_inheritors(lconsi(myrelid,NIL), NIL); * if the first element in the 'schema' list is a "*" then we are
* supposed to add this attribute to all classes that inherit from
* 'relationName' (as well as to 'relationName').
*
* any permissions or problems with duplicate attributes will cause the
* whole transaction to abort, which is what we want -- all or
* nothing.
*/
if (colDef != NULL)
{
if (inherits)
{
Oid myrelid,
childrelid;
List *child,
*children;
/* relrdesc = heap_openr(relationName);
* find_all_inheritors does the recursive search of the if (!RelationIsValid(relrdesc))
* inheritance hierarchy, so all we have to do is process {
* all of the relids in the list that it returns. elog(WARN, "PerformAddAttribute: unknown relation: \"%s\"",
*/ relationName);
foreach (child, children) { }
childrelid = lfirsti(child); myrelid = relrdesc->rd_id;
if (childrelid == myrelid) heap_close(relrdesc);
continue;
relrdesc = heap_open(childrelid); /* this routine is actually in the planner */
if (!RelationIsValid(relrdesc)) { children = find_all_inheritors(lconsi(myrelid, NIL), NIL);
elog(WARN, "PerformAddAttribute: can't find catalog entry for inheriting class with oid %d",
childrelid); /*
* find_all_inheritors does the recursive search of the
* inheritance hierarchy, so all we have to do is process all
* of the relids in the list that it returns.
*/
foreach(child, children)
{
childrelid = lfirsti(child);
if (childrelid == myrelid)
continue;
relrdesc = heap_open(childrelid);
if (!RelationIsValid(relrdesc))
{
elog(WARN, "PerformAddAttribute: can't find catalog entry for inheriting class with oid %d",
childrelid);
}
PerformAddAttribute((relrdesc->rd_rel->relname).data,
userName, false, colDef);
heap_close(relrdesc);
}
} }
PerformAddAttribute((relrdesc->rd_rel->relname).data, }
userName, false, colDef);
relrdesc = heap_openr(RelationRelationName);
reltup = ClassNameIndexScan(relrdesc, relationName);
if (!PointerIsValid(reltup))
{
heap_close(relrdesc); heap_close(relrdesc);
} elog(WARN, "PerformAddAttribute: relation \"%s\" not found",
relationName);
} }
}
relrdesc = heap_openr(RelationRelationName);
reltup = ClassNameIndexScan(relrdesc, relationName);
if (!PointerIsValid(reltup)) {
heap_close(relrdesc);
elog(WARN, "PerformAddAttribute: relation \"%s\" not found",
relationName);
}
/*
* XXX is the following check sufficient?
*/
if (((Form_pg_class) GETSTRUCT(reltup))->relkind == RELKIND_INDEX) {
elog(WARN, "PerformAddAttribute: index relation \"%s\" not changed",
relationName);
return;
}
minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
maxatts = minattnum + 1;
if (maxatts > MaxHeapAttributeNumber) {
pfree(reltup); /* XXX temp */
heap_close(relrdesc); /* XXX temp */
elog(WARN, "PerformAddAttribute: relations limited to %d attributes",
MaxHeapAttributeNumber);
return;
}
attrdesc = heap_openr(AttributeRelationName);
Assert(attrdesc);
Assert(RelationGetRelationTupleForm(attrdesc));
/*
* Open all (if any) pg_attribute indices
*/
hasindex = RelationGetRelationTupleForm(attrdesc)->relhasindex;
if (hasindex)
CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
ScanKeyEntryInitialize(&key[0],
(bits16) NULL,
(AttrNumber) Anum_pg_attribute_attrelid,
(RegProcedure)ObjectIdEqualRegProcedure,
(Datum) reltup->t_oid);
ScanKeyEntryInitialize(&key[1],
(bits16) NULL,
(AttrNumber) Anum_pg_attribute_attname,
(RegProcedure)NameEqualRegProcedure,
(Datum) NULL);
attributeD.attrelid = reltup->t_oid;
attributeD.attdisbursion = 0; /* XXX temporary */
attributeD.attcacheoff = -1;
attributeTuple = heap_addheader(Natts_pg_attribute,
sizeof attributeD,
(char *)&attributeD);
attribute = (AttributeTupleForm)GETSTRUCT(attributeTuple);
i = 1 + minattnum;
{
HeapTuple typeTuple;
TypeTupleForm form;
char *p;
int attnelems;
/* /*
* XXX use syscache here as an optimization * XXX is the following check sufficient?
*/ */
key[1].sk_argument = (Datum)colDef->colname; if (((Form_pg_class) GETSTRUCT(reltup))->relkind == RELKIND_INDEX)
attsdesc = heap_beginscan(attrdesc, 0, NowTimeQual, 2, key); {
elog(WARN, "PerformAddAttribute: index relation \"%s\" not changed",
relationName);
tup = heap_getnext(attsdesc, 0, (Buffer *) NULL); return;
if (HeapTupleIsValid(tup)) {
pfree(reltup); /* XXX temp */
heap_endscan(attsdesc); /* XXX temp */
heap_close(attrdesc); /* XXX temp */
heap_close(relrdesc); /* XXX temp */
elog(WARN, "PerformAddAttribute: attribute \"%s\" already exists in class \"%s\"",
key[1].sk_argument,
relationName);
return;
} }
heap_endscan(attsdesc);
minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
maxatts = minattnum + 1;
if (maxatts > MaxHeapAttributeNumber)
{
pfree(reltup); /* XXX temp */
heap_close(relrdesc); /* XXX temp */
elog(WARN, "PerformAddAttribute: relations limited to %d attributes",
MaxHeapAttributeNumber);
return;
}
attrdesc = heap_openr(AttributeRelationName);
Assert(attrdesc);
Assert(RelationGetRelationTupleForm(attrdesc));
/* /*
* check to see if it is an array attribute. * Open all (if any) pg_attribute indices
*/ */
hasindex = RelationGetRelationTupleForm(attrdesc)->relhasindex;
p = colDef->typename->name;
if (colDef->typename->arrayBounds)
{
attnelems = length(colDef->typename->arrayBounds);
p = makeArrayTypeName(colDef->typename->name);
}
else
attnelems = 0;
typeTuple = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(p),
0,0,0);
form = (TypeTupleForm)GETSTRUCT(typeTuple);
if (!HeapTupleIsValid(typeTuple)) {
elog(WARN, "Add: type \"%s\" nonexistent", p);
}
namestrcpy(&(attribute->attname), (char*) key[1].sk_argument);
attribute->atttypid = typeTuple->t_oid;
if (colDef->typename->typlen > 0)
attribute->attlen = colDef->typename->typlen;
else /* bpchar, varchar, text */
attribute->attlen = form->typlen;
attribute->attnum = i;
attribute->attbyval = form->typbyval;
attribute->attnelems = attnelems;
attribute->attcacheoff = -1;
attribute->attisset = (bool) (form->typtype == 'c');
attribute->attalign = form->typalign;
attribute->attnotnull = false;
heap_insert(attrdesc, attributeTuple);
if (hasindex) if (hasindex)
CatalogIndexInsert(idescs, CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
Num_pg_attr_indices,
attrdesc,
attributeTuple);
}
if (hasindex) ScanKeyEntryInitialize(&key[0],
CatalogCloseIndices(Num_pg_attr_indices, idescs); (bits16) NULL,
heap_close(attrdesc); (AttrNumber) Anum_pg_attribute_attrelid,
(RegProcedure) ObjectIdEqualRegProcedure,
(Datum) reltup->t_oid);
((Form_pg_class) GETSTRUCT(reltup))->relnatts = maxatts; ScanKeyEntryInitialize(&key[1],
oldTID = reltup->t_ctid; (bits16) NULL,
heap_replace(relrdesc, &oldTID, reltup); (AttrNumber) Anum_pg_attribute_attname,
(RegProcedure) NameEqualRegProcedure,
(Datum) NULL);
/* keep catalog indices current */ attributeD.attrelid = reltup->t_oid;
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); attributeD.attdisbursion = 0; /* XXX temporary */
CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, reltup); attributeD.attcacheoff = -1;
CatalogCloseIndices(Num_pg_class_indices, ridescs);
pfree(reltup); attributeTuple = heap_addheader(Natts_pg_attribute,
heap_close(relrdesc); sizeof attributeD,
(char *) &attributeD);
attribute = (AttributeTupleForm) GETSTRUCT(attributeTuple);
i = 1 + minattnum;
{
HeapTuple typeTuple;
TypeTupleForm form;
char *p;
int attnelems;
/*
* XXX use syscache here as an optimization
*/
key[1].sk_argument = (Datum) colDef->colname;
attsdesc = heap_beginscan(attrdesc, 0, NowTimeQual, 2, key);
tup = heap_getnext(attsdesc, 0, (Buffer *) NULL);
if (HeapTupleIsValid(tup))
{
pfree(reltup); /* XXX temp */
heap_endscan(attsdesc); /* XXX temp */
heap_close(attrdesc); /* XXX temp */
heap_close(relrdesc); /* XXX temp */
elog(WARN, "PerformAddAttribute: attribute \"%s\" already exists in class \"%s\"",
key[1].sk_argument,
relationName);
return;
}
heap_endscan(attsdesc);
/*
* check to see if it is an array attribute.
*/
p = colDef->typename->name;
if (colDef->typename->arrayBounds)
{
attnelems = length(colDef->typename->arrayBounds);
p = makeArrayTypeName(colDef->typename->name);
}
else
attnelems = 0;
typeTuple = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(p),
0, 0, 0);
form = (TypeTupleForm) GETSTRUCT(typeTuple);
if (!HeapTupleIsValid(typeTuple))
{
elog(WARN, "Add: type \"%s\" nonexistent", p);
}
namestrcpy(&(attribute->attname), (char *) key[1].sk_argument);
attribute->atttypid = typeTuple->t_oid;
if (colDef->typename->typlen > 0)
attribute->attlen = colDef->typename->typlen;
else
/* bpchar, varchar, text */
attribute->attlen = form->typlen;
attribute->attnum = i;
attribute->attbyval = form->typbyval;
attribute->attnelems = attnelems;
attribute->attcacheoff = -1;
attribute->attisset = (bool) (form->typtype == 'c');
attribute->attalign = form->typalign;
attribute->attnotnull = false;
heap_insert(attrdesc, attributeTuple);
if (hasindex)
CatalogIndexInsert(idescs,
Num_pg_attr_indices,
attrdesc,
attributeTuple);
}
if (hasindex)
CatalogCloseIndices(Num_pg_attr_indices, idescs);
heap_close(attrdesc);
((Form_pg_class) GETSTRUCT(reltup))->relnatts = maxatts;
oldTID = reltup->t_ctid;
heap_replace(relrdesc, &oldTID, reltup);
/* keep catalog indices current */
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, reltup);
CatalogCloseIndices(Num_pg_class_indices, ridescs);
pfree(reltup);
heap_close(relrdesc);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* defind.c-- * defind.c--
* POSTGRES define, extend and remove index code. * POSTGRES define, extend and remove index code.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/defind.c,v 1.12 1997/03/26 03:05:28 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/defind.c,v 1.13 1997/09/07 04:40:43 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -30,7 +30,7 @@
#include <utils/relcache.h> #include <utils/relcache.h>
#include <utils/lsyscache.h> #include <utils/lsyscache.h>
#include <commands/defrem.h> #include <commands/defrem.h>
#include <parser/parsetree.h> /* for getrelid() */ #include <parser/parsetree.h> /* for getrelid() */
#include <optimizer/prep.h> #include <optimizer/prep.h>
#include <optimizer/clauses.h> #include <optimizer/clauses.h>
#include <storage/lmgr.h> #include <storage/lmgr.h>
@ -39,508 +39,543 @@
#define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args!=NULL) #define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args!=NULL)
/* non-export function prototypes */ /* non-export function prototypes */
static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid); static void CheckPredicate(List * predList, List * rangeTable, Oid baseRelOid);
static void CheckPredExpr(Node *predicate, List *rangeTable, static void
CheckPredExpr(Node * predicate, List * rangeTable,
Oid baseRelOid); Oid baseRelOid);
static void static void
CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid); CheckPredClause(Expr * predicate, List * rangeTable, Oid baseRelOid);
static void FuncIndexArgs(IndexElem *funcIndex, AttrNumber *attNumP, static void
Oid *argTypes, Oid *opOidP, Oid relId); FuncIndexArgs(IndexElem * funcIndex, AttrNumber * attNumP,
static void NormIndexAttrs(List *attList, AttrNumber *attNumP, Oid * argTypes, Oid * opOidP, Oid relId);
Oid *opOidP, Oid relId); static void
static char *GetDefaultOpClass(Oid atttypid); NormIndexAttrs(List * attList, AttrNumber * attNumP,
Oid * opOidP, Oid relId);
static char *GetDefaultOpClass(Oid atttypid);
/* /*
* DefineIndex -- * DefineIndex --
* Creates a new index. * Creates a new index.
* *
* 'attributeList' is a list of IndexElem specifying either a functional * 'attributeList' is a list of IndexElem specifying either a functional
* index or a list of attributes to index on. * index or a list of attributes to index on.
* 'parameterList' is a list of ParamString specified in the with clause. * 'parameterList' is a list of ParamString specified in the with clause.
* 'predicate' is the qual specified in the where clause. * 'predicate' is the qual specified in the where clause.
* 'rangetable' is for the predicate * 'rangetable' is for the predicate
* *
* Exceptions: * Exceptions:
* XXX * XXX
*/ */
void void
DefineIndex(char *heapRelationName, DefineIndex(char *heapRelationName,
char *indexRelationName, char *indexRelationName,
char *accessMethodName, char *accessMethodName,
List *attributeList, List * attributeList,
List *parameterList, List * parameterList,
bool unique, bool unique,
Expr *predicate, Expr * predicate,
List *rangetable) List * rangetable)
{ {
Oid *classObjectId; Oid *classObjectId;
Oid accessMethodId; Oid accessMethodId;
Oid relationId; Oid relationId;
int numberOfAttributes; int numberOfAttributes;
AttrNumber *attributeNumberA; AttrNumber *attributeNumberA;
HeapTuple tuple; HeapTuple tuple;
uint16 parameterCount = 0; uint16 parameterCount = 0;
Datum *parameterA = NULL; Datum *parameterA = NULL;
FuncIndexInfo fInfo; FuncIndexInfo fInfo;
List *cnfPred = NULL; List *cnfPred = NULL;
bool lossy = FALSE; bool lossy = FALSE;
List *pl; List *pl;
/* /*
* Handle attributes * Handle attributes
*/ */
numberOfAttributes = length(attributeList); numberOfAttributes = length(attributeList);
if (numberOfAttributes <= 0) { if (numberOfAttributes <= 0)
elog(WARN, "DefineIndex: must specify at least one attribute"); {
} elog(WARN, "DefineIndex: must specify at least one attribute");
/*
* compute heap relation id
*/
tuple = SearchSysCacheTuple(RELNAME,
PointerGetDatum(heapRelationName),
0,0,0);
if (!HeapTupleIsValid(tuple)) {
elog(WARN, "DefineIndex: %s relation not found",
heapRelationName);
}
relationId = tuple->t_oid;
if (unique && strcmp(accessMethodName,"btree") != 0)
elog(WARN, "DefineIndex: unique indices are only available with the btree access method");
if (numberOfAttributes > 1 && strcmp(accessMethodName,"btree") != 0)
elog(WARN, "DefineIndex: multi-column indices are only available with the btree access method");
/*
* compute access method id
*/
tuple = SearchSysCacheTuple(AMNAME, PointerGetDatum(accessMethodName),
0,0,0);
if (!HeapTupleIsValid(tuple)) {
elog(WARN, "DefineIndex: %s access method not found",
accessMethodName);
}
accessMethodId = tuple->t_oid;
/*
* Handle parameters
* [param list is now different (NOT USED, really) - ay 10/94]
*
* WITH clause reinstated to handle lossy indices.
* -- JMH, 7/22/96
*/
foreach(pl, parameterList) {
ParamString *param = (ParamString*)lfirst(pl);
if (!strcasecmp(param->name, "islossy"))
lossy = TRUE;
}
/*
* Convert the partial-index predicate from parsetree form to plan
* form, so it can be readily evaluated during index creation.
* Note: "predicate" comes in as a list containing (1) the predicate
* itself (a where_clause), and (2) a corresponding range table.
*
* [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94]
*/
if (predicate != NULL && rangetable != NIL) {
cnfPred = cnfify((Expr*)copyObject(predicate), true);
fix_opids(cnfPred);
CheckPredicate(cnfPred, rangetable, relationId);
}
if (IsFuncIndex(attributeList)) {
IndexElem *funcIndex= lfirst(attributeList);
int nargs;
nargs = length(funcIndex->args);
if (nargs > INDEX_MAX_KEYS) {
elog(WARN,
"Too many args to function, limit of %d",
INDEX_MAX_KEYS);
} }
FIsetnArgs(&fInfo,nargs); /*
* compute heap relation id
*/
tuple = SearchSysCacheTuple(RELNAME,
PointerGetDatum(heapRelationName),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(WARN, "DefineIndex: %s relation not found",
heapRelationName);
}
relationId = tuple->t_oid;
strcpy(FIgetname(&fInfo), funcIndex->name); if (unique && strcmp(accessMethodName, "btree") != 0)
elog(WARN, "DefineIndex: unique indices are only available with the btree access method");
attributeNumberA = if (numberOfAttributes > 1 && strcmp(accessMethodName, "btree") != 0)
(AttrNumber *)palloc(nargs * sizeof attributeNumberA[0]); elog(WARN, "DefineIndex: multi-column indices are only available with the btree access method");
classObjectId = (Oid *)palloc(sizeof classObjectId[0]); /*
* compute access method id
*/
tuple = SearchSysCacheTuple(AMNAME, PointerGetDatum(accessMethodName),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(WARN, "DefineIndex: %s access method not found",
accessMethodName);
}
accessMethodId = tuple->t_oid;
FuncIndexArgs(funcIndex, attributeNumberA, /*
&(FIgetArg(&fInfo, 0)), * Handle parameters [param list is now different (NOT USED, really) -
classObjectId, relationId); * ay 10/94]
*
* WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96
*/
foreach(pl, parameterList)
{
ParamString *param = (ParamString *) lfirst(pl);
index_create(heapRelationName, if (!strcasecmp(param->name, "islossy"))
indexRelationName, lossy = TRUE;
&fInfo, NULL, accessMethodId, }
numberOfAttributes, attributeNumberA,
classObjectId, parameterCount, parameterA, (Node*)cnfPred,
lossy, unique);
}else {
attributeNumberA =
(AttrNumber *)palloc(numberOfAttributes *
sizeof attributeNumberA[0]);
classObjectId =
(Oid *)palloc(numberOfAttributes * sizeof classObjectId[0]);
NormIndexAttrs(attributeList, attributeNumberA, /*
classObjectId, relationId); * Convert the partial-index predicate from parsetree form to plan
* form, so it can be readily evaluated during index creation. Note:
* "predicate" comes in as a list containing (1) the predicate itself
* (a where_clause), and (2) a corresponding range table.
*
* [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94]
*/
if (predicate != NULL && rangetable != NIL)
{
cnfPred = cnfify((Expr *) copyObject(predicate), true);
fix_opids(cnfPred);
CheckPredicate(cnfPred, rangetable, relationId);
}
index_create(heapRelationName, indexRelationName, NULL, if (IsFuncIndex(attributeList))
attributeList, {
accessMethodId, numberOfAttributes, attributeNumberA, IndexElem *funcIndex = lfirst(attributeList);
classObjectId, parameterCount, parameterA, (Node*)cnfPred, int nargs;
lossy, unique);
} nargs = length(funcIndex->args);
if (nargs > INDEX_MAX_KEYS)
{
elog(WARN,
"Too many args to function, limit of %d",
INDEX_MAX_KEYS);
}
FIsetnArgs(&fInfo, nargs);
strcpy(FIgetname(&fInfo), funcIndex->name);
attributeNumberA =
(AttrNumber *) palloc(nargs * sizeof attributeNumberA[0]);
classObjectId = (Oid *) palloc(sizeof classObjectId[0]);
FuncIndexArgs(funcIndex, attributeNumberA,
&(FIgetArg(&fInfo, 0)),
classObjectId, relationId);
index_create(heapRelationName,
indexRelationName,
&fInfo, NULL, accessMethodId,
numberOfAttributes, attributeNumberA,
classObjectId, parameterCount, parameterA, (Node *) cnfPred,
lossy, unique);
}
else
{
attributeNumberA =
(AttrNumber *) palloc(numberOfAttributes *
sizeof attributeNumberA[0]);
classObjectId =
(Oid *) palloc(numberOfAttributes * sizeof classObjectId[0]);
NormIndexAttrs(attributeList, attributeNumberA,
classObjectId, relationId);
index_create(heapRelationName, indexRelationName, NULL,
attributeList,
accessMethodId, numberOfAttributes, attributeNumberA,
classObjectId, parameterCount, parameterA, (Node *) cnfPred,
lossy, unique);
}
} }
/* /*
* ExtendIndex -- * ExtendIndex --
* Extends a partial index. * Extends a partial index.
* *
* Exceptions: * Exceptions:
* XXX * XXX
*/ */
void void
ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable) ExtendIndex(char *indexRelationName, Expr * predicate, List * rangetable)
{ {
Oid *classObjectId; Oid *classObjectId;
Oid accessMethodId; Oid accessMethodId;
Oid indexId, relationId; Oid indexId,
Oid indproc; relationId;
int numberOfAttributes; Oid indproc;
AttrNumber *attributeNumberA; int numberOfAttributes;
HeapTuple tuple; AttrNumber *attributeNumberA;
FuncIndexInfo fInfo; HeapTuple tuple;
FuncIndexInfo *funcInfo = NULL; FuncIndexInfo fInfo;
IndexTupleForm index; FuncIndexInfo *funcInfo = NULL;
Node *oldPred = NULL; IndexTupleForm index;
List *cnfPred = NULL; Node *oldPred = NULL;
PredInfo *predInfo; List *cnfPred = NULL;
Relation heapRelation; PredInfo *predInfo;
Relation indexRelation; Relation heapRelation;
int i; Relation indexRelation;
int i;
/* /*
* compute index relation id and access method id * compute index relation id and access method id
*/ */
tuple = SearchSysCacheTuple(RELNAME, PointerGetDatum(indexRelationName), tuple = SearchSysCacheTuple(RELNAME, PointerGetDatum(indexRelationName),
0,0,0); 0, 0, 0);
if (!HeapTupleIsValid(tuple)) {
elog(WARN, "ExtendIndex: %s index not found",
indexRelationName);
}
indexId = tuple->t_oid;
accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam;
/*
* find pg_index tuple
*/
tuple = SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(indexId),
0,0,0);
if (!HeapTupleIsValid(tuple)) {
elog(WARN, "ExtendIndex: %s is not an index",
indexRelationName);
}
/*
* Extract info from the pg_index tuple
*/
index = (IndexTupleForm)GETSTRUCT(tuple);
Assert(index->indexrelid == indexId);
relationId = index->indrelid;
indproc = index->indproc;
for (i=0; i<INDEX_MAX_KEYS; i++)
if (index->indkey[i] == 0) break;
numberOfAttributes = i;
if (VARSIZE(&index->indpred) != 0) {
char *predString;
predString = fmgr(F_TEXTOUT, &index->indpred);
oldPred = stringToNode(predString);
pfree(predString);
}
if (oldPred == NULL)
elog(WARN, "ExtendIndex: %s is not a partial index",
indexRelationName);
/*
* Convert the extension predicate from parsetree form to plan
* form, so it can be readily evaluated during index creation.
* Note: "predicate" comes in as a list containing (1) the predicate
* itself (a where_clause), and (2) a corresponding range table.
*/
if (rangetable != NIL) {
cnfPred = cnfify((Expr*)copyObject(predicate), true);
fix_opids(cnfPred);
CheckPredicate(cnfPred, rangetable, relationId);
}
/* make predInfo list to pass to index_build */
predInfo = (PredInfo*)palloc(sizeof(PredInfo));
predInfo->pred = (Node*)cnfPred;
predInfo->oldPred = oldPred;
attributeNumberA =
(AttrNumber *)palloc(numberOfAttributes*
sizeof attributeNumberA[0]);
classObjectId =
(Oid *)palloc(numberOfAttributes * sizeof classObjectId[0]);
for (i=0; i<numberOfAttributes; i++) {
attributeNumberA[i] = index->indkey[i];
classObjectId[i] = index->indclass[i];
}
if (indproc != InvalidOid) {
funcInfo = &fInfo;
/* FIgetnArgs(funcInfo) = numberOfAttributes; */
FIsetnArgs(funcInfo,numberOfAttributes);
tuple = SearchSysCacheTuple(PROOID,
ObjectIdGetDatum(indproc),
0,0,0);
if (!HeapTupleIsValid(tuple)) if (!HeapTupleIsValid(tuple))
elog(WARN, "ExtendIndex: index procedure not found"); {
elog(WARN, "ExtendIndex: %s index not found",
indexRelationName);
}
indexId = tuple->t_oid;
accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam;
namecpy(&(funcInfo->funcName), /*
&(((Form_pg_proc) GETSTRUCT(tuple))->proname)); * find pg_index tuple
*/
tuple = SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(indexId),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(WARN, "ExtendIndex: %s is not an index",
indexRelationName);
}
FIsetProcOid(funcInfo,tuple->t_oid); /*
} * Extract info from the pg_index tuple
*/
index = (IndexTupleForm) GETSTRUCT(tuple);
Assert(index->indexrelid == indexId);
relationId = index->indrelid;
indproc = index->indproc;
heapRelation = heap_open(relationId); for (i = 0; i < INDEX_MAX_KEYS; i++)
indexRelation = index_open(indexId); if (index->indkey[i] == 0)
break;
numberOfAttributes = i;
RelationSetLockForWrite(heapRelation); if (VARSIZE(&index->indpred) != 0)
{
char *predString;
InitIndexStrategy(numberOfAttributes, indexRelation, accessMethodId); predString = fmgr(F_TEXTOUT, &index->indpred);
oldPred = stringToNode(predString);
pfree(predString);
}
if (oldPred == NULL)
elog(WARN, "ExtendIndex: %s is not a partial index",
indexRelationName);
index_build(heapRelation, indexRelation, numberOfAttributes, /*
attributeNumberA, 0, NULL, funcInfo, predInfo); * Convert the extension predicate from parsetree form to plan form,
* so it can be readily evaluated during index creation. Note:
* "predicate" comes in as a list containing (1) the predicate itself
* (a where_clause), and (2) a corresponding range table.
*/
if (rangetable != NIL)
{
cnfPred = cnfify((Expr *) copyObject(predicate), true);
fix_opids(cnfPred);
CheckPredicate(cnfPred, rangetable, relationId);
}
/* make predInfo list to pass to index_build */
predInfo = (PredInfo *) palloc(sizeof(PredInfo));
predInfo->pred = (Node *) cnfPred;
predInfo->oldPred = oldPred;
attributeNumberA =
(AttrNumber *) palloc(numberOfAttributes *
sizeof attributeNumberA[0]);
classObjectId =
(Oid *) palloc(numberOfAttributes * sizeof classObjectId[0]);
for (i = 0; i < numberOfAttributes; i++)
{
attributeNumberA[i] = index->indkey[i];
classObjectId[i] = index->indclass[i];
}
if (indproc != InvalidOid)
{
funcInfo = &fInfo;
/* FIgetnArgs(funcInfo) = numberOfAttributes; */
FIsetnArgs(funcInfo, numberOfAttributes);
tuple = SearchSysCacheTuple(PROOID,
ObjectIdGetDatum(indproc),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(WARN, "ExtendIndex: index procedure not found");
namecpy(&(funcInfo->funcName),
&(((Form_pg_proc) GETSTRUCT(tuple))->proname));
FIsetProcOid(funcInfo, tuple->t_oid);
}
heapRelation = heap_open(relationId);
indexRelation = index_open(indexId);
RelationSetLockForWrite(heapRelation);
InitIndexStrategy(numberOfAttributes, indexRelation, accessMethodId);
index_build(heapRelation, indexRelation, numberOfAttributes,
attributeNumberA, 0, NULL, funcInfo, predInfo);
} }
/* /*
* CheckPredicate * CheckPredicate
* Checks that the given list of partial-index predicates refer * Checks that the given list of partial-index predicates refer
* (via the given range table) only to the given base relation oid, * (via the given range table) only to the given base relation oid,
* and that they're in a form the planner can handle, i.e., * and that they're in a form the planner can handle, i.e.,
* boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR * boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR
* has to be on the left). * has to be on the left).
*/ */
static void static void
CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid) CheckPredicate(List * predList, List * rangeTable, Oid baseRelOid)
{ {
List *item; List *item;
foreach (item, predList) { foreach(item, predList)
CheckPredExpr(lfirst(item), rangeTable, baseRelOid);
}
}
static void
CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid)
{
List *clauses = NIL, *clause;
if (is_opclause(predicate)) {
CheckPredClause((Expr*)predicate, rangeTable, baseRelOid);
return;
} else if (or_clause(predicate))
clauses = ((Expr*)predicate)->args;
else if (and_clause(predicate))
clauses = ((Expr*)predicate)->args;
else
elog(WARN, "Unsupported partial-index predicate expression type");
foreach (clause, clauses) {
CheckPredExpr(lfirst(clause), rangeTable, baseRelOid);
}
}
static void
CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid)
{
Var *pred_var;
Const *pred_const;
pred_var = (Var *)get_leftop(predicate);
pred_const = (Const *)get_rightop(predicate);
if (!IsA(predicate->oper,Oper) ||
!IsA(pred_var,Var) ||
!IsA(pred_const,Const)) {
elog(WARN, "Unsupported partial-index predicate clause type");
}
if (getrelid(pred_var->varno, rangeTable) != baseRelOid)
elog(WARN,
"Partial-index predicates may refer only to the base relation");
}
static void
FuncIndexArgs(IndexElem *funcIndex,
AttrNumber *attNumP,
Oid *argTypes,
Oid *opOidP,
Oid relId)
{
List *rest;
HeapTuple tuple;
AttributeTupleForm att;
tuple = SearchSysCacheTuple(CLANAME,
PointerGetDatum(funcIndex->class),
0,0,0);
if (!HeapTupleIsValid(tuple))
{ {
elog(WARN, "DefineIndex: %s class not found", CheckPredExpr(lfirst(item), rangeTable, baseRelOid);
funcIndex->class);
} }
*opOidP = tuple->t_oid;
memset(argTypes, 0, 8 * sizeof(Oid));
/*
* process the function arguments
*/
for (rest=funcIndex->args; rest != NIL; rest = lnext(rest)) {
char *arg;
arg = strVal(lfirst(rest));
tuple = SearchSysCacheTuple(ATTNAME,
ObjectIdGetDatum(relId),
PointerGetDatum(arg),0,0);
if (!HeapTupleIsValid(tuple)) {
elog(WARN,
"DefineIndex: attribute \"%s\" not found",
arg);
}
att = (AttributeTupleForm)GETSTRUCT(tuple);
*attNumP++ = att->attnum;
*argTypes++ = att->atttypid;
}
} }
static void static void
NormIndexAttrs(List *attList, /* list of IndexElem's */ CheckPredExpr(Node * predicate, List * rangeTable, Oid baseRelOid)
AttrNumber *attNumP,
Oid *opOidP,
Oid relId)
{ {
List *rest; List *clauses = NIL,
HeapTuple tuple; *clause;
/* if (is_opclause(predicate))
* process attributeList {
*/ CheckPredClause((Expr *) predicate, rangeTable, baseRelOid);
return;
}
else if (or_clause(predicate))
clauses = ((Expr *) predicate)->args;
else if (and_clause(predicate))
clauses = ((Expr *) predicate)->args;
else
elog(WARN, "Unsupported partial-index predicate expression type");
for (rest=attList; rest != NIL; rest = lnext(rest)) { foreach(clause, clauses)
IndexElem *attribute; {
AttributeTupleForm attform; CheckPredExpr(lfirst(clause), rangeTable, baseRelOid);
}
}
attribute = lfirst(rest); static void
CheckPredClause(Expr * predicate, List * rangeTable, Oid baseRelOid)
{
Var *pred_var;
Const *pred_const;
if (attribute->name == NULL) pred_var = (Var *) get_leftop(predicate);
elog(WARN, "missing attribute for define index"); pred_const = (Const *) get_rightop(predicate);
tuple = SearchSysCacheTuple(ATTNAME, if (!IsA(predicate->oper, Oper) ||
ObjectIdGetDatum(relId), !IsA(pred_var, Var) ||
PointerGetDatum(attribute->name), !IsA(pred_const, Const))
0,0); {
if (!HeapTupleIsValid(tuple)) { elog(WARN, "Unsupported partial-index predicate clause type");
elog(WARN,
"DefineIndex: attribute \"%s\" not found",
attribute->name);
} }
attform = (AttributeTupleForm)GETSTRUCT(tuple); if (getrelid(pred_var->varno, rangeTable) != baseRelOid)
*attNumP++ = attform->attnum;
if (attribute->class == NULL) {
/* no operator class specified, so find the default */
attribute->class = GetDefaultOpClass(attform->atttypid);
if(attribute->class == NULL) {
elog(WARN, elog(WARN,
"Can't find a default operator class for type %d.", "Partial-index predicates may refer only to the base relation");
attform->atttypid); }
}
}
static void
FuncIndexArgs(IndexElem * funcIndex,
AttrNumber * attNumP,
Oid * argTypes,
Oid * opOidP,
Oid relId)
{
List *rest;
HeapTuple tuple;
AttributeTupleForm att;
tuple = SearchSysCacheTuple(CLANAME, tuple = SearchSysCacheTuple(CLANAME,
PointerGetDatum(attribute->class), PointerGetDatum(funcIndex->class),
0,0,0); 0, 0, 0);
if (!HeapTupleIsValid(tuple)) { if (!HeapTupleIsValid(tuple))
elog(WARN, "DefineIndex: %s class not found", {
attribute->class); elog(WARN, "DefineIndex: %s class not found",
funcIndex->class);
}
*opOidP = tuple->t_oid;
memset(argTypes, 0, 8 * sizeof(Oid));
/*
* process the function arguments
*/
for (rest = funcIndex->args; rest != NIL; rest = lnext(rest))
{
char *arg;
arg = strVal(lfirst(rest));
tuple = SearchSysCacheTuple(ATTNAME,
ObjectIdGetDatum(relId),
PointerGetDatum(arg), 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(WARN,
"DefineIndex: attribute \"%s\" not found",
arg);
}
att = (AttributeTupleForm) GETSTRUCT(tuple);
*attNumP++ = att->attnum;
*argTypes++ = att->atttypid;
} }
*opOidP++ = tuple->t_oid;
}
} }
static char * static void
NormIndexAttrs(List * attList, /* list of IndexElem's */
AttrNumber * attNumP,
Oid * opOidP,
Oid relId)
{
List *rest;
HeapTuple tuple;
/*
* process attributeList
*/
for (rest = attList; rest != NIL; rest = lnext(rest))
{
IndexElem *attribute;
AttributeTupleForm attform;
attribute = lfirst(rest);
if (attribute->name == NULL)
elog(WARN, "missing attribute for define index");
tuple = SearchSysCacheTuple(ATTNAME,
ObjectIdGetDatum(relId),
PointerGetDatum(attribute->name),
0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(WARN,
"DefineIndex: attribute \"%s\" not found",
attribute->name);
}
attform = (AttributeTupleForm) GETSTRUCT(tuple);
*attNumP++ = attform->attnum;
if (attribute->class == NULL)
{
/* no operator class specified, so find the default */
attribute->class = GetDefaultOpClass(attform->atttypid);
if (attribute->class == NULL)
{
elog(WARN,
"Can't find a default operator class for type %d.",
attform->atttypid);
}
}
tuple = SearchSysCacheTuple(CLANAME,
PointerGetDatum(attribute->class),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(WARN, "DefineIndex: %s class not found",
attribute->class);
}
*opOidP++ = tuple->t_oid;
}
}
static char *
GetDefaultOpClass(Oid atttypid) GetDefaultOpClass(Oid atttypid)
{ {
HeapTuple tuple; HeapTuple tuple;
tuple = SearchSysCacheTuple(CLADEFTYPE, tuple = SearchSysCacheTuple(CLADEFTYPE,
ObjectIdGetDatum(atttypid), ObjectIdGetDatum(atttypid),
0, 0, 0); 0, 0, 0);
if(!HeapTupleIsValid(tuple)) { if (!HeapTupleIsValid(tuple))
return 0; {
} return 0;
}
return nameout(&(((Form_pg_opclass)GETSTRUCT(tuple))->opcname)); return nameout(&(((Form_pg_opclass) GETSTRUCT(tuple))->opcname));
} }
/* /*
* RemoveIndex -- * RemoveIndex --
* Deletes an index. * Deletes an index.
* *
* Exceptions: * Exceptions:
* BadArg if name is invalid. * BadArg if name is invalid.
* "WARN" if index nonexistent. * "WARN" if index nonexistent.
* ... * ...
*/ */
void void
RemoveIndex(char *name) RemoveIndex(char *name)
{ {
HeapTuple tuple; HeapTuple tuple;
tuple = SearchSysCacheTuple(RELNAME, tuple = SearchSysCacheTuple(RELNAME,
PointerGetDatum(name), PointerGetDatum(name),
0,0,0); 0, 0, 0);
if (!HeapTupleIsValid(tuple)) { if (!HeapTupleIsValid(tuple))
elog(WARN, "index \"%s\" nonexistent", name); {
} elog(WARN, "index \"%s\" nonexistent", name);
}
if (((Form_pg_class)GETSTRUCT(tuple))->relkind != RELKIND_INDEX) { if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
elog(WARN, "relation \"%s\" is of type \"%c\"", {
name, elog(WARN, "relation \"%s\" is of type \"%c\"",
((Form_pg_class)GETSTRUCT(tuple))->relkind); name,
} ((Form_pg_class) GETSTRUCT(tuple))->relkind);
}
index_destroy(tuple->t_oid); index_destroy(tuple->t_oid);
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* explain.c-- * explain.c--
* Explain the query execution plan * Explain the query execution plan
* *
* Copyright (c) 1994-5, Regents of the University of California * Copyright (c) 1994-5, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.10 1997/08/18 20:52:17 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.11 1997/09/07 04:40:49 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -17,7 +17,7 @@
#include <postgres.h> #include <postgres.h>
#include <parser/catalog_utils.h> #include <parser/catalog_utils.h>
#include <parser/parse_query.h> /* for MakeTimeRange() */ #include <parser/parse_query.h> /* for MakeTimeRange() */
#include <nodes/plannodes.h> #include <nodes/plannodes.h>
#include <tcop/tcopprot.h> #include <tcop/tcopprot.h>
#include <lib/stringinfo.h> #include <lib/stringinfo.h>
@ -25,79 +25,86 @@
#include <optimizer/planner.h> #include <optimizer/planner.h>
#include <access/xact.h> #include <access/xact.h>
typedef struct ExplainState { typedef struct ExplainState
/* options */ {
bool printCost; /* print cost */ /* options */
bool printNodes; /* do nodeToString() instead */ bool printCost; /* print cost */
/* other states */ bool printNodes; /* do nodeToString() instead */
List *rtable; /* range table */ /* other states */
} ExplainState; List *rtable; /* range table */
} ExplainState;
static char *Explain_PlanToString(Plan *plan, ExplainState *es); static char *Explain_PlanToString(Plan * plan, ExplainState * es);
/* /*
* ExplainQuery - * ExplainQuery -
* print out the execution plan for a given query * print out the execution plan for a given query
* *
*/ */
void void
ExplainQuery(Query *query, bool verbose, CommandDest dest) ExplainQuery(Query * query, bool verbose, CommandDest dest)
{ {
char *s = NULL, *s2; char *s = NULL,
Plan *plan; *s2;
ExplainState *es; Plan *plan;
int len; ExplainState *es;
int len;
if (IsAbortedTransactionBlockState()) { if (IsAbortedTransactionBlockState())
char *tag = "*ABORT STATE*"; {
EndCommand(tag, dest); char *tag = "*ABORT STATE*";
elog(NOTICE, "(transaction aborted): %s", EndCommand(tag, dest);
"queries ignored until END");
return; elog(NOTICE, "(transaction aborted): %s",
} "queries ignored until END");
/* plan the queries (XXX we've ignored rewrite!!) */ return;
plan = planner(query);
/* pg_plan could have failed */
if (plan == NULL)
return;
es = (ExplainState*)malloc(sizeof(ExplainState));
memset(es, 0, sizeof(ExplainState));
es->printCost = true; /* default */
if (verbose)
es->printNodes = true;
es->rtable = query->rtable;
if (es->printNodes)
s = nodeToString(plan);
if (es->printCost) {
s2 = Explain_PlanToString(plan, es);
if (s == NULL)
s = s2;
else {
strcat(s, "\n\n");
strcat(s, s2);
} }
}
/* output the plan */ /* plan the queries (XXX we've ignored rewrite!!) */
len = strlen(s); plan = planner(query);
elog(NOTICE, "QUERY PLAN:\n\n%.*s", ELOG_MAXLEN-64, s);
len -= ELOG_MAXLEN-64; /* pg_plan could have failed */
while (len > 0) { if (plan == NULL)
s += ELOG_MAXLEN-64; return;
elog(NOTICE, "%.*s", ELOG_MAXLEN-64, s);
len -= ELOG_MAXLEN-64; es = (ExplainState *) malloc(sizeof(ExplainState));
} memset(es, 0, sizeof(ExplainState));
free(es);
es->printCost = true; /* default */
if (verbose)
es->printNodes = true;
es->rtable = query->rtable;
if (es->printNodes)
s = nodeToString(plan);
if (es->printCost)
{
s2 = Explain_PlanToString(plan, es);
if (s == NULL)
s = s2;
else
{
strcat(s, "\n\n");
strcat(s, s2);
}
}
/* output the plan */
len = strlen(s);
elog(NOTICE, "QUERY PLAN:\n\n%.*s", ELOG_MAXLEN - 64, s);
len -= ELOG_MAXLEN - 64;
while (len > 0)
{
s += ELOG_MAXLEN - 64;
elog(NOTICE, "%.*s", ELOG_MAXLEN - 64, s);
len -= ELOG_MAXLEN - 64;
}
free(es);
} }
/***************************************************************************** /*****************************************************************************
@ -106,122 +113,130 @@ ExplainQuery(Query *query, bool verbose, CommandDest dest)
/* /*
* explain_outNode - * explain_outNode -
* converts a Node into ascii string and append it to 'str' * converts a Node into ascii string and append it to 'str'
*/ */
static void static void
explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) explain_outNode(StringInfo str, Plan * plan, int indent, ExplainState * es)
{ {
char *pname; char *pname;
char buf[1000]; char buf[1000];
int i; int i;
if (plan==NULL) { if (plan == NULL)
appendStringInfo(str, "\n"); {
return; appendStringInfo(str, "\n");
} return;
switch(nodeTag(plan)) {
case T_Result:
pname = "Result";
break;
case T_Append:
pname = "Append";
break;
case T_NestLoop:
pname = "Nested Loop";
break;
case T_MergeJoin:
pname = "Merge Join";
break;
case T_HashJoin:
pname = "Hash Join";
break;
case T_SeqScan:
pname = "Seq Scan";
break;
case T_IndexScan:
pname = "Index Scan";
break;
case T_Temp:
pname = "Temp Scan";
break;
case T_Sort:
pname = "Sort";
break;
case T_Group:
pname = "Group";
break;
case T_Agg:
pname = "Aggregate";
break;
case T_Unique:
pname = "Unique";
break;
case T_Hash:
pname = "Hash";
break;
case T_Tee:
pname = "Tee";
break;
default:
pname = "";
break;
}
for(i=0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, pname);
switch(nodeTag(plan)) {
case T_SeqScan:
case T_IndexScan:
if (((Scan*)plan)->scanrelid > 0) {
RangeTblEntry *rte = nth(((Scan*)plan)->scanrelid-1, es->rtable);
sprintf(buf, " on %s", rte->refname);
appendStringInfo(str, buf);
} }
break;
default:
break;
}
if (es->printCost) {
sprintf(buf, " (cost=%.2f size=%d width=%d)",
plan->cost, plan->plan_size, plan->plan_width);
appendStringInfo(str, buf);
}
appendStringInfo(str, "\n");
/* lefttree */ switch (nodeTag(plan))
if (outerPlan(plan)) { {
for(i=0; i < indent; i++) case T_Result:
appendStringInfo(str, " "); pname = "Result";
appendStringInfo(str, " -> "); break;
explain_outNode(str, outerPlan(plan), indent+1, es); case T_Append:
} pname = "Append";
break;
case T_NestLoop:
pname = "Nested Loop";
break;
case T_MergeJoin:
pname = "Merge Join";
break;
case T_HashJoin:
pname = "Hash Join";
break;
case T_SeqScan:
pname = "Seq Scan";
break;
case T_IndexScan:
pname = "Index Scan";
break;
case T_Temp:
pname = "Temp Scan";
break;
case T_Sort:
pname = "Sort";
break;
case T_Group:
pname = "Group";
break;
case T_Agg:
pname = "Aggregate";
break;
case T_Unique:
pname = "Unique";
break;
case T_Hash:
pname = "Hash";
break;
case T_Tee:
pname = "Tee";
break;
default:
pname = "";
break;
}
/* righttree */ for (i = 0; i < indent; i++)
if (innerPlan(plan)) { appendStringInfo(str, " ");
for(i=0; i < indent; i++)
appendStringInfo(str, " "); appendStringInfo(str, pname);
appendStringInfo(str, " -> "); switch (nodeTag(plan))
explain_outNode(str, innerPlan(plan), indent+1, es); {
} case T_SeqScan:
return; case T_IndexScan:
if (((Scan *) plan)->scanrelid > 0)
{
RangeTblEntry *rte = nth(((Scan *) plan)->scanrelid - 1, es->rtable);
sprintf(buf, " on %s", rte->refname);
appendStringInfo(str, buf);
}
break;
default:
break;
}
if (es->printCost)
{
sprintf(buf, " (cost=%.2f size=%d width=%d)",
plan->cost, plan->plan_size, plan->plan_width);
appendStringInfo(str, buf);
}
appendStringInfo(str, "\n");
/* lefttree */
if (outerPlan(plan))
{
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str, outerPlan(plan), indent + 1, es);
}
/* righttree */
if (innerPlan(plan))
{
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str, innerPlan(plan), indent + 1, es);
}
return;
} }
static char * static char *
Explain_PlanToString(Plan *plan, ExplainState *es) Explain_PlanToString(Plan * plan, ExplainState * es)
{ {
StringInfo str; StringInfo str;
char *s; char *s;
if (plan==NULL) if (plan == NULL)
return ""; return "";
Assert(plan!=NULL); Assert(plan != NULL);
str = makeStringInfo(); str = makeStringInfo();
explain_outNode(str, plan, 0, es); explain_outNode(str, plan, 0, es);
s = str->data; s = str->data;
pfree(str); pfree(str);
return s; return s;
} }

View File

@ -1,17 +1,17 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* purge.c-- * purge.c--
* the POSTGRES purge command. * the POSTGRES purge command.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/purge.c,v 1.6 1997/08/12 22:52:25 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/purge.c,v 1.7 1997/09/07 04:40:51 momjian Exp $
* *
* Note: * Note:
* XXX There are many instances of int32 instead of ...Time. These * XXX There are many instances of int32 instead of ...Time. These
* should be changed once it is decided the signed'ness will be. * should be changed once it is decided the signed'ness will be.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -21,145 +21,156 @@
#include <access/heapam.h> #include <access/heapam.h>
#include <access/xact.h> #include <access/xact.h>
#include <utils/tqual.h> /* for NowTimeQual */ #include <utils/tqual.h> /* for NowTimeQual */
#include <catalog/catname.h> #include <catalog/catname.h>
#include <catalog/indexing.h> #include <catalog/indexing.h>
#include <fmgr.h> #include <fmgr.h>
#include <commands/purge.h> #include <commands/purge.h>
#include <utils/builtins.h> /* for isreltime() */ #include <utils/builtins.h> /* for isreltime() */
static char cmdname[] = "RelationPurge"; static char cmdname[] = "RelationPurge";
#define RELATIVE 01 #define RELATIVE 01
#define ABSOLUTE 02 #define ABSOLUTE 02
int32 int32
RelationPurge(char *relationName, RelationPurge(char *relationName,
char *absoluteTimeString, char *absoluteTimeString,
char *relativeTimeString) char *relativeTimeString)
{ {
register i; register i;
AbsoluteTime absoluteTime = INVALID_ABSTIME; AbsoluteTime absoluteTime = INVALID_ABSTIME;
RelativeTime relativeTime = INVALID_RELTIME; RelativeTime relativeTime = INVALID_RELTIME;
bits8 dateTag; bits8 dateTag;
Relation relation; Relation relation;
HeapScanDesc scan; HeapScanDesc scan;
static ScanKeyData key[1] = { static ScanKeyData key[1] = {
{ 0, Anum_pg_class_relname, F_NAMEEQ } {0, Anum_pg_class_relname, F_NAMEEQ}
}; };
Buffer buffer; Buffer buffer;
HeapTuple newTuple, oldTuple; HeapTuple newTuple,
AbsoluteTime currentTime; oldTuple;
char *values[Natts_pg_class]; AbsoluteTime currentTime;
char nulls[Natts_pg_class]; char *values[Natts_pg_class];
char replace[Natts_pg_class]; char nulls[Natts_pg_class];
Relation idescs[Num_pg_class_indices]; char replace[Natts_pg_class];
Relation idescs[Num_pg_class_indices];
/* /*
* XXX for some reason getmyrelids (in inval.c) barfs when * XXX for some reason getmyrelids (in inval.c) barfs when you
* you heap_replace tuples from these classes. i thought * heap_replace tuples from these classes. i thought setheapoverride
* setheapoverride would fix it but it didn't. for now, * would fix it but it didn't. for now, just disallow purge on these
* just disallow purge on these classes. * classes.
*/ */
if (strcmp(RelationRelationName, relationName) == 0 || if (strcmp(RelationRelationName, relationName) == 0 ||
strcmp(AttributeRelationName, relationName) == 0 || strcmp(AttributeRelationName, relationName) == 0 ||
strcmp(AccessMethodRelationName, relationName) == 0 || strcmp(AccessMethodRelationName, relationName) == 0 ||
strcmp(AccessMethodOperatorRelationName, relationName) == 0) { strcmp(AccessMethodOperatorRelationName, relationName) == 0)
elog(WARN, "%s: cannot purge catalog \"%s\"", {
cmdname, relationName); elog(WARN, "%s: cannot purge catalog \"%s\"",
} cmdname, relationName);
}
if (PointerIsValid(absoluteTimeString)) {
absoluteTime = (int32) nabstimein(absoluteTimeString); if (PointerIsValid(absoluteTimeString))
absoluteTimeString[0] = '\0'; {
if (absoluteTime == INVALID_ABSTIME) { absoluteTime = (int32) nabstimein(absoluteTimeString);
elog(NOTICE, "%s: bad absolute time string \"%s\"", absoluteTimeString[0] = '\0';
cmdname, absoluteTimeString); if (absoluteTime == INVALID_ABSTIME)
elog(WARN, "purge not executed"); {
elog(NOTICE, "%s: bad absolute time string \"%s\"",
cmdname, absoluteTimeString);
elog(WARN, "purge not executed");
}
} }
}
#ifdef PURGEDEBUG #ifdef PURGEDEBUG
elog(DEBUG, "%s: absolute time `%s' is %d.", elog(DEBUG, "%s: absolute time `%s' is %d.",
cmdname, absoluteTimeString, absoluteTime); cmdname, absoluteTimeString, absoluteTime);
#endif /* defined(PURGEDEBUG) */ #endif /* defined(PURGEDEBUG) */
if (PointerIsValid(relativeTimeString)) { if (PointerIsValid(relativeTimeString))
if (isreltime(relativeTimeString) != 1) { {
elog(WARN, "%s: bad relative time string \"%s\"", if (isreltime(relativeTimeString) != 1)
cmdname, relativeTimeString); {
} elog(WARN, "%s: bad relative time string \"%s\"",
relativeTime = reltimein(relativeTimeString); cmdname, relativeTimeString);
}
relativeTime = reltimein(relativeTimeString);
#ifdef PURGEDEBUG #ifdef PURGEDEBUG
elog(DEBUG, "%s: relative time `%s' is %d.", elog(DEBUG, "%s: relative time `%s' is %d.",
cmdname, relativeTimeString, relativeTime); cmdname, relativeTimeString, relativeTime);
#endif /* defined(PURGEDEBUG) */ #endif /* defined(PURGEDEBUG) */
} }
/* /*
* Find the RELATION relation tuple for the given relation. * Find the RELATION relation tuple for the given relation.
*/ */
relation = heap_openr(RelationRelationName); relation = heap_openr(RelationRelationName);
key[0].sk_argument = PointerGetDatum(relationName); key[0].sk_argument = PointerGetDatum(relationName);
fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs); fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs);
scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
oldTuple = heap_getnext(scan, 0, &buffer);
if (!HeapTupleIsValid(oldTuple))
{
heap_endscan(scan);
heap_close(relation);
elog(WARN, "%s: no such relation: %s", cmdname, relationName);
return (0);
}
/*
* Dig around in the tuple.
*/
currentTime = GetCurrentTransactionStartTime();
if (!RelativeTimeIsValid(relativeTime))
{
dateTag = ABSOLUTE;
if (!AbsoluteTimeIsValid(absoluteTime))
absoluteTime = currentTime;
}
else if (!AbsoluteTimeIsValid(absoluteTime))
dateTag = RELATIVE;
else
dateTag = ABSOLUTE | RELATIVE;
for (i = 0; i < Natts_pg_class; ++i)
{
nulls[i] = heap_attisnull(oldTuple, i + 1) ? 'n' : ' ';
values[i] = NULL;
replace[i] = ' ';
}
if (dateTag & ABSOLUTE)
{
values[Anum_pg_class_relexpires - 1] =
(char *) UInt32GetDatum(absoluteTime);
replace[Anum_pg_class_relexpires - 1] = 'r';
}
if (dateTag & RELATIVE)
{
values[Anum_pg_class_relpreserved - 1] =
(char *) UInt32GetDatum(relativeTime);
replace[Anum_pg_class_relpreserved - 1] = 'r';
}
/*
* Change the RELATION relation tuple for the given relation.
*/
newTuple = heap_modifytuple(oldTuple, buffer, relation, (Datum *) values,
nulls, replace);
/* XXX How do you detect an insertion error?? */
heap_replace(relation, &newTuple->t_ctid, newTuple);
/* keep the system catalog indices current */
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_class_indices, relation, newTuple);
CatalogCloseIndices(Num_pg_class_indices, idescs);
pfree(newTuple);
scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
oldTuple = heap_getnext(scan, 0, &buffer);
if (!HeapTupleIsValid(oldTuple)) {
heap_endscan(scan); heap_endscan(scan);
heap_close(relation); heap_close(relation);
elog(WARN, "%s: no such relation: %s", cmdname, relationName); return (1);
return(0);
}
/*
* Dig around in the tuple.
*/
currentTime = GetCurrentTransactionStartTime();
if (!RelativeTimeIsValid(relativeTime)) {
dateTag = ABSOLUTE;
if (!AbsoluteTimeIsValid(absoluteTime))
absoluteTime = currentTime;
} else if (!AbsoluteTimeIsValid(absoluteTime))
dateTag = RELATIVE;
else
dateTag = ABSOLUTE | RELATIVE;
for (i = 0; i < Natts_pg_class; ++i) {
nulls[i] = heap_attisnull(oldTuple, i+1) ? 'n' : ' ';
values[i] = NULL;
replace[i] = ' ';
}
if (dateTag & ABSOLUTE) {
values[Anum_pg_class_relexpires-1] =
(char *) UInt32GetDatum(absoluteTime);
replace[Anum_pg_class_relexpires-1] = 'r';
}
if (dateTag & RELATIVE) {
values[Anum_pg_class_relpreserved-1] =
(char *) UInt32GetDatum(relativeTime);
replace[Anum_pg_class_relpreserved-1] = 'r';
}
/*
* Change the RELATION relation tuple for the given relation.
*/
newTuple = heap_modifytuple(oldTuple, buffer, relation, (Datum*)values,
nulls, replace);
/* XXX How do you detect an insertion error?? */
heap_replace(relation, &newTuple->t_ctid, newTuple);
/* keep the system catalog indices current */
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_class_indices, relation, newTuple);
CatalogCloseIndices(Num_pg_class_indices, idescs);
pfree(newTuple);
heap_endscan(scan);
heap_close(relation);
return(1);
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* remove.c-- * remove.c--
* POSTGRES remove (function | type | operator ) utilty code. * POSTGRES remove (function | type | operator ) utilty code.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.10 1997/08/18 20:52:17 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.11 1997/09/07 04:40:54 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -28,100 +28,112 @@
#include <storage/bufmgr.h> #include <storage/bufmgr.h>
#include <fmgr.h> #include <fmgr.h>
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
/* /*
* RemoveOperator -- * RemoveOperator --
* Deletes an operator. * Deletes an operator.
* *
* Exceptions: * Exceptions:
* BadArg if name is invalid. * BadArg if name is invalid.
* BadArg if type1 is invalid. * BadArg if type1 is invalid.
* "WARN" if operator nonexistent. * "WARN" if operator nonexistent.
* ... * ...
*/ */
void void
RemoveOperator(char *operatorName, /* operator name */ RemoveOperator(char *operatorName, /* operator name */
char *typeName1, /* first type name */ char *typeName1, /* first type name */
char *typeName2) /* optional second type name */ char *typeName2) /* optional second type name */
{ {
Relation relation; Relation relation;
HeapScanDesc scan; HeapScanDesc scan;
HeapTuple tup; HeapTuple tup;
Oid typeId1 = InvalidOid; Oid typeId1 = InvalidOid;
Oid typeId2 = InvalidOid; Oid typeId2 = InvalidOid;
bool defined; bool defined;
ItemPointerData itemPointerData; ItemPointerData itemPointerData;
Buffer buffer; Buffer buffer;
ScanKeyData operatorKey[3]; ScanKeyData operatorKey[3];
char *userName; char *userName;
if (typeName1) { if (typeName1)
typeId1 = TypeGet(typeName1, &defined); {
if (!OidIsValid(typeId1)) { typeId1 = TypeGet(typeName1, &defined);
elog(WARN, "RemoveOperator: type '%s' does not exist", typeName1); if (!OidIsValid(typeId1))
return; {
elog(WARN, "RemoveOperator: type '%s' does not exist", typeName1);
return;
}
} }
}
if (typeName2) { if (typeName2)
typeId2 = TypeGet(typeName2, &defined); {
if (!OidIsValid(typeId2)) { typeId2 = TypeGet(typeName2, &defined);
elog(WARN, "RemoveOperator: type '%s' does not exist", typeName2); if (!OidIsValid(typeId2))
return; {
elog(WARN, "RemoveOperator: type '%s' does not exist", typeName2);
return;
}
} }
}
ScanKeyEntryInitialize(&operatorKey[0], 0x0, ScanKeyEntryInitialize(&operatorKey[0], 0x0,
Anum_pg_operator_oprname, Anum_pg_operator_oprname,
NameEqualRegProcedure, NameEqualRegProcedure,
PointerGetDatum(operatorName)); PointerGetDatum(operatorName));
ScanKeyEntryInitialize(&operatorKey[1], 0x0, ScanKeyEntryInitialize(&operatorKey[1], 0x0,
Anum_pg_operator_oprleft, Anum_pg_operator_oprleft,
ObjectIdEqualRegProcedure, ObjectIdEqualRegProcedure,
ObjectIdGetDatum(typeId1)); ObjectIdGetDatum(typeId1));
ScanKeyEntryInitialize(&operatorKey[2], 0x0, ScanKeyEntryInitialize(&operatorKey[2], 0x0,
Anum_pg_operator_oprright, Anum_pg_operator_oprright,
ObjectIdEqualRegProcedure, ObjectIdEqualRegProcedure,
ObjectIdGetDatum(typeId2)); ObjectIdGetDatum(typeId2));
relation = heap_openr(OperatorRelationName); relation = heap_openr(OperatorRelationName);
scan = heap_beginscan(relation, 0, NowTimeQual, 3, operatorKey); scan = heap_beginscan(relation, 0, NowTimeQual, 3, operatorKey);
tup = heap_getnext(scan, 0, &buffer); tup = heap_getnext(scan, 0, &buffer);
if (HeapTupleIsValid(tup)) { if (HeapTupleIsValid(tup))
{
#ifndef NO_SECURITY #ifndef NO_SECURITY
userName = GetPgUserName(); userName = GetPgUserName();
if (!pg_ownercheck(userName, if (!pg_ownercheck(userName,
(char *) ObjectIdGetDatum(tup->t_oid), (char *) ObjectIdGetDatum(tup->t_oid),
OPROID)) OPROID))
elog(WARN, "RemoveOperator: operator '%s': permission denied", elog(WARN, "RemoveOperator: operator '%s': permission denied",
operatorName); operatorName);
#endif #endif
ItemPointerCopy(&tup->t_ctid, &itemPointerData); ItemPointerCopy(&tup->t_ctid, &itemPointerData);
heap_delete(relation, &itemPointerData); heap_delete(relation, &itemPointerData);
} else {
if (OidIsValid(typeId1) && OidIsValid(typeId2)) {
elog(WARN, "RemoveOperator: binary operator '%s' taking '%s' and '%s' does not exist",
operatorName,
typeName1,
typeName2);
} else if (OidIsValid(typeId1)) {
elog(WARN, "RemoveOperator: right unary operator '%s' taking '%s' does not exist",
operatorName,
typeName1);
} else {
elog(WARN, "RemoveOperator: left unary operator '%s' taking '%s' does not exist",
operatorName,
typeName2);
} }
} else
heap_endscan(scan); {
heap_close(relation); if (OidIsValid(typeId1) && OidIsValid(typeId2))
{
elog(WARN, "RemoveOperator: binary operator '%s' taking '%s' and '%s' does not exist",
operatorName,
typeName1,
typeName2);
}
else if (OidIsValid(typeId1))
{
elog(WARN, "RemoveOperator: right unary operator '%s' taking '%s' does not exist",
operatorName,
typeName1);
}
else
{
elog(WARN, "RemoveOperator: left unary operator '%s' taking '%s' does not exist",
operatorName,
typeName2);
}
}
heap_endscan(scan);
heap_close(relation);
} }
#ifdef NOTYET #ifdef NOTYET
@ -130,353 +142,379 @@ RemoveOperator(char *operatorName, /* operator name */
* don't use it - pma 2/1/94 * don't use it - pma 2/1/94
*/ */
/* /*
* SingleOpOperatorRemove * SingleOpOperatorRemove
* Removes all operators that have operands or a result of type 'typeOid'. * Removes all operators that have operands or a result of type 'typeOid'.
*/ */
static void static void
SingleOpOperatorRemove(Oid typeOid) SingleOpOperatorRemove(Oid typeOid)
{ {
Relation rdesc; Relation rdesc;
ScanKeyData key[3]; ScanKeyData key[3];
HeapScanDesc sdesc; HeapScanDesc sdesc;
HeapTuple tup; HeapTuple tup;
ItemPointerData itemPointerData; ItemPointerData itemPointerData;
Buffer buffer; Buffer buffer;
static attnums[3] = { 7, 8, 9 }; /* left, right, return */ static attnums[3] = {7, 8, 9}; /* left, right, return */
register i; register i;
ScanKeyEntryInitialize(&key[0], ScanKeyEntryInitialize(&key[0],
0, 0, ObjectIdEqualRegProcedure, (Datum)typeOid); 0, 0, ObjectIdEqualRegProcedure, (Datum) typeOid);
rdesc = heap_openr(OperatorRelationName); rdesc = heap_openr(OperatorRelationName);
for (i = 0; i < 3; ++i) { for (i = 0; i < 3; ++i)
key[0].sk_attno = attnums[i]; {
sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key); key[0].sk_attno = attnums[i];
while (PointerIsValid(tup = heap_getnext(sdesc, 0, &buffer))) { sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key);
ItemPointerCopy(&tup->t_ctid, &itemPointerData); while (PointerIsValid(tup = heap_getnext(sdesc, 0, &buffer)))
/* XXX LOCK not being passed */ {
heap_delete(rdesc, &itemPointerData); ItemPointerCopy(&tup->t_ctid, &itemPointerData);
/* XXX LOCK not being passed */
heap_delete(rdesc, &itemPointerData);
}
heap_endscan(sdesc);
} }
heap_endscan(sdesc); heap_close(rdesc);
}
heap_close(rdesc);
} }
/* /*
* AttributeAndRelationRemove * AttributeAndRelationRemove
* Removes all entries in the attribute and relation relations * Removes all entries in the attribute and relation relations
* that contain entries of type 'typeOid'. * that contain entries of type 'typeOid'.
* Currently nothing calls this code, it is untested. * Currently nothing calls this code, it is untested.
*/ */
static void static void
AttributeAndRelationRemove(Oid typeOid) AttributeAndRelationRemove(Oid typeOid)
{ {
struct oidlist { struct oidlist
Oid reloid; {
struct oidlist *next; Oid reloid;
}; struct oidlist *next;
struct oidlist *oidptr, *optr; };
Relation rdesc; struct oidlist *oidptr,
ScanKeyData key[1]; *optr;
HeapScanDesc sdesc; Relation rdesc;
HeapTuple tup; ScanKeyData key[1];
ItemPointerData itemPointerData; HeapScanDesc sdesc;
Buffer buffer; HeapTuple tup;
ItemPointerData itemPointerData;
Buffer buffer;
/* /*
* Get the oid's of the relations to be removed by scanning the * Get the oid's of the relations to be removed by scanning the entire
* entire attribute relation. * attribute relation. We don't need to remove the attributes here,
* We don't need to remove the attributes here, * because amdestroy will remove all attributes of the relation. XXX
* because amdestroy will remove all attributes of the relation. * should check for duplicate relations
* XXX should check for duplicate relations */
*/
ScanKeyEntryInitialize(&key[0], ScanKeyEntryInitialize(&key[0],
0, 3, ObjectIdEqualRegProcedure, (Datum)typeOid); 0, 3, ObjectIdEqualRegProcedure, (Datum) typeOid);
oidptr = (struct oidlist *) palloc(sizeof(*oidptr)); oidptr = (struct oidlist *) palloc(sizeof(*oidptr));
oidptr->next = NULL; oidptr->next = NULL;
optr = oidptr; optr = oidptr;
rdesc = heap_openr(AttributeRelationName); rdesc = heap_openr(AttributeRelationName);
sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key);
while (PointerIsValid(tup = heap_getnext(sdesc, 0, &buffer))) {
ItemPointerCopy(&tup->t_ctid, &itemPointerData);
optr->reloid = ((AttributeTupleForm)GETSTRUCT(tup))->attrelid;
optr->next = (struct oidlist *) palloc(sizeof(*oidptr));
optr = optr->next;
}
optr->next = NULL;
heap_endscan(sdesc);
heap_close(rdesc);
ScanKeyEntryInitialize(&key[0], 0,
ObjectIdAttributeNumber,
ObjectIdEqualRegProcedure, (Datum)0);
optr = oidptr;
rdesc = heap_openr(RelationRelationName);
while (PointerIsValid((char *) optr->next)) {
key[0].sk_argument = (Datum) (optr++)->reloid;
sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key); sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key);
tup = heap_getnext(sdesc, 0, &buffer); while (PointerIsValid(tup = heap_getnext(sdesc, 0, &buffer)))
if (PointerIsValid(tup)) { {
char *name; ItemPointerCopy(&tup->t_ctid, &itemPointerData);
optr->reloid = ((AttributeTupleForm) GETSTRUCT(tup))->attrelid;
name = (((Form_pg_class)GETSTRUCT(tup))->relname).data; optr->next = (struct oidlist *) palloc(sizeof(*oidptr));
heap_destroy(name); optr = optr->next;
} }
} optr->next = NULL;
heap_endscan(sdesc); heap_endscan(sdesc);
heap_close(rdesc); heap_close(rdesc);
ScanKeyEntryInitialize(&key[0], 0,
ObjectIdAttributeNumber,
ObjectIdEqualRegProcedure, (Datum) 0);
optr = oidptr;
rdesc = heap_openr(RelationRelationName);
while (PointerIsValid((char *) optr->next))
{
key[0].sk_argument = (Datum) (optr++)->reloid;
sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key);
tup = heap_getnext(sdesc, 0, &buffer);
if (PointerIsValid(tup))
{
char *name;
name = (((Form_pg_class) GETSTRUCT(tup))->relname).data;
heap_destroy(name);
}
}
heap_endscan(sdesc);
heap_close(rdesc);
} }
#endif /* NOTYET */
#endif /* NOTYET */
/* /*
* TypeRemove * TypeRemove
* Removes the type 'typeName' and all attributes and relations that * Removes the type 'typeName' and all attributes and relations that
* use it. * use it.
*/ */
void void
RemoveType(char *typeName) /* type name to be removed */ RemoveType(char *typeName) /* type name to be removed */
{ {
Relation relation; Relation relation;
HeapScanDesc scan; HeapScanDesc scan;
HeapTuple tup; HeapTuple tup;
Oid typeOid; Oid typeOid;
ItemPointerData itemPointerData; ItemPointerData itemPointerData;
static ScanKeyData typeKey[1] = { static ScanKeyData typeKey[1] = {
{ 0, Anum_pg_type_typname, NameEqualRegProcedure } {0, Anum_pg_type_typname, NameEqualRegProcedure}
}; };
char *shadow_type; char *shadow_type;
char *userName; char *userName;
#ifndef NO_SECURITY #ifndef NO_SECURITY
userName = GetPgUserName(); userName = GetPgUserName();
if (!pg_ownercheck(userName, typeName, TYPNAME)) if (!pg_ownercheck(userName, typeName, TYPNAME))
elog(WARN, "RemoveType: type '%s': permission denied", elog(WARN, "RemoveType: type '%s': permission denied",
typeName); typeName);
#endif #endif
relation = heap_openr(TypeRelationName); relation = heap_openr(TypeRelationName);
fmgr_info(typeKey[0].sk_procedure, &typeKey[0].sk_func, fmgr_info(typeKey[0].sk_procedure, &typeKey[0].sk_func,
&typeKey[0].sk_nargs); &typeKey[0].sk_nargs);
/* Delete the primary type */ /* Delete the primary type */
typeKey[0].sk_argument = PointerGetDatum(typeName); typeKey[0].sk_argument = PointerGetDatum(typeName);
scan = heap_beginscan(relation, 0, NowTimeQual, 1, typeKey); scan = heap_beginscan(relation, 0, NowTimeQual, 1, typeKey);
tup = heap_getnext(scan, 0, (Buffer *) 0); tup = heap_getnext(scan, 0, (Buffer *) 0);
if (!HeapTupleIsValid(tup)) { if (!HeapTupleIsValid(tup))
heap_endscan(scan);
heap_close(relation);
elog(WARN, "RemoveType: type '%s' does not exist",
typeName);
}
typeOid = tup->t_oid;
ItemPointerCopy(&tup->t_ctid, &itemPointerData);
heap_delete(relation, &itemPointerData);
heap_endscan(scan);
/* Now, Delete the "array of" that type */
shadow_type = makeArrayTypeName(typeName);
typeKey[0].sk_argument = NameGetDatum(shadow_type);
scan = heap_beginscan(relation, 0, NowTimeQual,
1, (ScanKey) typeKey);
tup = heap_getnext(scan, 0, (Buffer *) 0);
if (!HeapTupleIsValid(tup))
{ {
elog(WARN, "RemoveType: type '%s': array stub not found", heap_endscan(scan);
typeName); heap_close(relation);
elog(WARN, "RemoveType: type '%s' does not exist",
typeName);
} }
typeOid = tup->t_oid; typeOid = tup->t_oid;
ItemPointerCopy(&tup->t_ctid, &itemPointerData); ItemPointerCopy(&tup->t_ctid, &itemPointerData);
heap_delete(relation, &itemPointerData); heap_delete(relation, &itemPointerData);
heap_endscan(scan); heap_endscan(scan);
heap_close(relation); /* Now, Delete the "array of" that type */
shadow_type = makeArrayTypeName(typeName);
typeKey[0].sk_argument = NameGetDatum(shadow_type);
scan = heap_beginscan(relation, 0, NowTimeQual,
1, (ScanKey) typeKey);
tup = heap_getnext(scan, 0, (Buffer *) 0);
if (!HeapTupleIsValid(tup))
{
elog(WARN, "RemoveType: type '%s': array stub not found",
typeName);
}
typeOid = tup->t_oid;
ItemPointerCopy(&tup->t_ctid, &itemPointerData);
heap_delete(relation, &itemPointerData);
heap_endscan(scan);
heap_close(relation);
} }
/* /*
* RemoveFunction -- * RemoveFunction --
* Deletes a function. * Deletes a function.
* *
* Exceptions: * Exceptions:
* BadArg if name is invalid. * BadArg if name is invalid.
* "WARN" if function nonexistent. * "WARN" if function nonexistent.
* ... * ...
*/ */
void void
RemoveFunction(char *functionName, /* function name to be removed */ RemoveFunction(char *functionName, /* function name to be removed */
int nargs, int nargs,
List *argNameList /* list of TypeNames */) List * argNameList /* list of TypeNames */ )
{ {
Relation relation; Relation relation;
HeapScanDesc scan; HeapScanDesc scan;
HeapTuple tup; HeapTuple tup;
Buffer buffer = InvalidBuffer; Buffer buffer = InvalidBuffer;
bool bufferUsed = FALSE; bool bufferUsed = FALSE;
Oid argList[8]; Oid argList[8];
Form_pg_proc the_proc = NULL; Form_pg_proc the_proc = NULL;
ItemPointerData itemPointerData; ItemPointerData itemPointerData;
static ScanKeyData key[3] = { static ScanKeyData key[3] = {
{ 0, Anum_pg_proc_proname, NameEqualRegProcedure } {0, Anum_pg_proc_proname, NameEqualRegProcedure}
}; };
char *userName; char *userName;
char *typename; char *typename;
int i; int i;
memset(argList, 0, 8 * sizeof(Oid)); memset(argList, 0, 8 * sizeof(Oid));
for (i=0; i<nargs; i++) { for (i = 0; i < nargs; i++)
/* typename = ((TypeName*)(lfirst(argNameList)))->name; */ {
typename = strVal(lfirst(argNameList)); /* typename = ((TypeName*)(lfirst(argNameList)))->name; */
argNameList = lnext(argNameList); typename = strVal(lfirst(argNameList));
argNameList = lnext(argNameList);
if (strcmp(typename, "opaque") == 0) if (strcmp(typename, "opaque") == 0)
argList[i] = 0; argList[i] = 0;
else { else
tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typename), {
0,0,0); tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typename),
0, 0, 0);
if (!HeapTupleIsValid(tup)) { if (!HeapTupleIsValid(tup))
elog(WARN, "RemoveFunction: type '%s' not found",typename); {
} elog(WARN, "RemoveFunction: type '%s' not found", typename);
argList[i] = tup->t_oid; }
argList[i] = tup->t_oid;
}
} }
}
tup = SearchSysCacheTuple(PRONAME, PointerGetDatum(functionName), tup = SearchSysCacheTuple(PRONAME, PointerGetDatum(functionName),
Int32GetDatum(nargs), Int32GetDatum(nargs),
PointerGetDatum(argList),0); PointerGetDatum(argList), 0);
if (!HeapTupleIsValid(tup)) if (!HeapTupleIsValid(tup))
func_error("RemoveFunction", functionName, nargs, argList); func_error("RemoveFunction", functionName, nargs, argList);
#ifndef NO_SECURITY #ifndef NO_SECURITY
userName = GetPgUserName(); userName = GetPgUserName();
if (!pg_func_ownercheck(userName, functionName, nargs, argList)) { if (!pg_func_ownercheck(userName, functionName, nargs, argList))
elog(WARN, "RemoveFunction: function '%s': permission denied", {
functionName); elog(WARN, "RemoveFunction: function '%s': permission denied",
} functionName);
}
#endif #endif
key[0].sk_argument = PointerGetDatum(functionName); key[0].sk_argument = PointerGetDatum(functionName);
fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs); fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs);
relation = heap_openr(ProcedureRelationName); relation = heap_openr(ProcedureRelationName);
scan = heap_beginscan(relation, 0, NowTimeQual, 1, key); scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
do { /* hope this is ok because it's indexed */ do
if (bufferUsed) { { /* hope this is ok because it's indexed */
ReleaseBuffer(buffer); if (bufferUsed)
bufferUsed = FALSE; {
} ReleaseBuffer(buffer);
tup = heap_getnext(scan, 0, (Buffer *) &buffer); bufferUsed = FALSE;
if (!HeapTupleIsValid(tup)) }
break; tup = heap_getnext(scan, 0, (Buffer *) & buffer);
bufferUsed = TRUE; if (!HeapTupleIsValid(tup))
the_proc = (Form_pg_proc) GETSTRUCT(tup); break;
} while ( (namestrcmp(&(the_proc->proname), functionName) == 0) && bufferUsed = TRUE;
(the_proc->pronargs != nargs || the_proc = (Form_pg_proc) GETSTRUCT(tup);
!oid8eq(&(the_proc->proargtypes[0]), &argList[0]))); } while ((namestrcmp(&(the_proc->proname), functionName) == 0) &&
(the_proc->pronargs != nargs ||
!oid8eq(&(the_proc->proargtypes[0]), &argList[0])));
if (!HeapTupleIsValid(tup) || namestrcmp(&(the_proc->proname), if (!HeapTupleIsValid(tup) || namestrcmp(&(the_proc->proname),
functionName) != 0) functionName) != 0)
{ {
heap_endscan(scan); heap_endscan(scan);
heap_close(relation); heap_close(relation);
func_error("RemoveFunction", functionName,nargs, argList); func_error("RemoveFunction", functionName, nargs, argList);
} }
/* ok, function has been found */ /* ok, function has been found */
if (the_proc->prolang == INTERNALlanguageId) if (the_proc->prolang == INTERNALlanguageId)
elog(WARN, "RemoveFunction: function \"%s\" is built-in", elog(WARN, "RemoveFunction: function \"%s\" is built-in",
functionName); functionName);
ItemPointerCopy(&tup->t_ctid, &itemPointerData); ItemPointerCopy(&tup->t_ctid, &itemPointerData);
heap_delete(relation, &itemPointerData); heap_delete(relation, &itemPointerData);
heap_endscan(scan); heap_endscan(scan);
heap_close(relation); heap_close(relation);
} }
void void
RemoveAggregate(char *aggName, char *aggType) RemoveAggregate(char *aggName, char *aggType)
{ {
Relation relation; Relation relation;
HeapScanDesc scan; HeapScanDesc scan;
HeapTuple tup; HeapTuple tup;
ItemPointerData itemPointerData; ItemPointerData itemPointerData;
char *userName; char *userName;
Oid basetypeID = InvalidOid; Oid basetypeID = InvalidOid;
bool defined; bool defined;
ScanKeyData aggregateKey[3]; ScanKeyData aggregateKey[3];
/* /*
* if a basetype is passed in, then attempt to find an aggregate for that * if a basetype is passed in, then attempt to find an aggregate for
* specific type. * that specific type.
* *
* else if the basetype is blank, then attempt to find an aggregate with a * else if the basetype is blank, then attempt to find an aggregate with
* basetype of zero. This is valid. It means that the aggregate is to apply * a basetype of zero. This is valid. It means that the aggregate is
* to all basetypes. ie, a counter of some sort. * to apply to all basetypes. ie, a counter of some sort.
* *
*/ */
if (aggType) { if (aggType)
basetypeID = TypeGet(aggType, &defined); {
if (!OidIsValid(basetypeID)) { basetypeID = TypeGet(aggType, &defined);
elog(WARN, "RemoveAggregate: type '%s' does not exist", aggType); if (!OidIsValid(basetypeID))
} {
} else { elog(WARN, "RemoveAggregate: type '%s' does not exist", aggType);
basetypeID = 0; }
} }
else
{
basetypeID = 0;
}
/* /*
#ifndef NO_SECURITY #ifndef NO_SECURITY
*/ */
userName = GetPgUserName(); userName = GetPgUserName();
if (!pg_aggr_ownercheck(userName, aggName, basetypeID)) { if (!pg_aggr_ownercheck(userName, aggName, basetypeID))
if (aggType) { {
elog(WARN, "RemoveAggregate: aggregate '%s' on type '%s': permission denied", if (aggType)
aggName, aggType); {
} else { elog(WARN, "RemoveAggregate: aggregate '%s' on type '%s': permission denied",
elog(WARN, "RemoveAggregate: aggregate '%s': permission denied", aggName, aggType);
aggName); }
} else
} {
elog(WARN, "RemoveAggregate: aggregate '%s': permission denied",
aggName);
}
}
/* /*
#endif #endif
*/ */
ScanKeyEntryInitialize(&aggregateKey[0], 0x0, ScanKeyEntryInitialize(&aggregateKey[0], 0x0,
Anum_pg_aggregate_aggname, Anum_pg_aggregate_aggname,
NameEqualRegProcedure, NameEqualRegProcedure,
PointerGetDatum(aggName)); PointerGetDatum(aggName));
ScanKeyEntryInitialize(&aggregateKey[1], 0x0, ScanKeyEntryInitialize(&aggregateKey[1], 0x0,
Anum_pg_aggregate_aggbasetype, Anum_pg_aggregate_aggbasetype,
ObjectIdEqualRegProcedure, ObjectIdEqualRegProcedure,
ObjectIdGetDatum(basetypeID)); ObjectIdGetDatum(basetypeID));
relation = heap_openr(AggregateRelationName); relation = heap_openr(AggregateRelationName);
scan = heap_beginscan(relation, 0, NowTimeQual, 2, aggregateKey); scan = heap_beginscan(relation, 0, NowTimeQual, 2, aggregateKey);
tup = heap_getnext(scan, 0, (Buffer *) 0); tup = heap_getnext(scan, 0, (Buffer *) 0);
if (!HeapTupleIsValid(tup)) { if (!HeapTupleIsValid(tup))
heap_endscan(scan); {
heap_close(relation); heap_endscan(scan);
if (aggType) { heap_close(relation);
elog(WARN, "RemoveAggregate: aggregate '%s' for '%s' does not exist", if (aggType)
aggName, aggType); {
} else { elog(WARN, "RemoveAggregate: aggregate '%s' for '%s' does not exist",
elog(WARN, "RemoveAggregate: aggregate '%s' for all types does not exist", aggName, aggType);
aggName); }
} else
} {
ItemPointerCopy(&tup->t_ctid, &itemPointerData); elog(WARN, "RemoveAggregate: aggregate '%s' for all types does not exist",
heap_delete(relation, &itemPointerData); aggName);
heap_endscan(scan); }
heap_close(relation); }
ItemPointerCopy(&tup->t_ctid, &itemPointerData);
heap_delete(relation, &itemPointerData);
heap_endscan(scan);
heap_close(relation);
} }

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* rename.c-- * rename.c--
* renameatt() and renamerel() reside here. * renameatt() and renamerel() reside here.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.7 1997/08/18 20:52:18 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.8 1997/09/07 04:40:55 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -32,227 +32,246 @@
#include <catalog/pg_proc.h> #include <catalog/pg_proc.h>
#include <catalog/pg_class.h> #include <catalog/pg_class.h>
#include <optimizer/internal.h> #include <optimizer/internal.h>
#include <optimizer/prep.h> /* for find_all_inheritors */ #include <optimizer/prep.h> /* for find_all_inheritors */
#ifndef NO_SECURITY #ifndef NO_SECURITY
# include <utils/acl.h> #include <utils/acl.h>
#endif /* !NO_SECURITY */ #endif /* !NO_SECURITY */
#ifndef HAVE_MEMMOVE #ifndef HAVE_MEMMOVE
# include <regex/utils.h> #include <regex/utils.h>
#else #else
# include <string.h> #include <string.h>
#endif #endif
/* /*
* renameatt - changes the name of a attribute in a relation * renameatt - changes the name of a attribute in a relation
* *
* Attname attribute is changed in attribute catalog. * Attname attribute is changed in attribute catalog.
* No record of the previous attname is kept (correct?). * No record of the previous attname is kept (correct?).
* *
* get proper reldesc from relation catalog (if not arg) * get proper reldesc from relation catalog (if not arg)
* scan attribute catalog * scan attribute catalog
* for name conflict (within rel) * for name conflict (within rel)
* for original attribute (if not arg) * for original attribute (if not arg)
* modify attname in attribute tuple * modify attname in attribute tuple
* insert modified attribute in attribute catalog * insert modified attribute in attribute catalog
* delete original attribute from attribute catalog * delete original attribute from attribute catalog
* *
* XXX Renaming an indexed attribute must (eventually) also change * XXX Renaming an indexed attribute must (eventually) also change
* the attribute name in the associated indexes. * the attribute name in the associated indexes.
*/ */
void void
renameatt(char *relname, renameatt(char *relname,
char *oldattname, char *oldattname,
char *newattname, char *newattname,
char *userName, char *userName,
int recurse) int recurse)
{ {
Relation relrdesc, attrdesc; Relation relrdesc,
HeapTuple reltup, oldatttup, newatttup; attrdesc;
ItemPointerData oldTID; HeapTuple reltup,
Relation idescs[Num_pg_attr_indices]; oldatttup,
newatttup;
/* ItemPointerData oldTID;
* permissions checking. this would normally be done in utility.c, Relation idescs[Num_pg_attr_indices];
* but this particular routine is recursive.
*
* normally, only the owner of a class can change its schema.
*/
if (IsSystemRelationName(relname))
elog(WARN, "renameatt: class \"%s\" is a system catalog",
relname);
#ifndef NO_SECURITY
if (!IsBootstrapProcessingMode() &&
!pg_ownercheck(userName, relname, RELNAME))
elog(WARN, "renameatt: you do not own class \"%s\"",
relname);
#endif
/*
* if the 'recurse' flag is set then we are supposed to rename this
* attribute in all classes that inherit from 'relname' (as well as
* in 'relname').
*
* any permissions or problems with duplicate attributes will cause
* the whole transaction to abort, which is what we want -- all or
* nothing.
*/
if (recurse) {
Oid myrelid, childrelid;
List *child, *children;
relrdesc = heap_openr(relname);
if (!RelationIsValid(relrdesc)) {
elog(WARN, "renameatt: unknown relation: \"%s\"",
relname);
}
myrelid = relrdesc->rd_id;
heap_close(relrdesc);
/* this routine is actually in the planner */
children = find_all_inheritors(lconsi(myrelid, NIL), NIL);
/* /*
* find_all_inheritors does the recursive search of the * permissions checking. this would normally be done in utility.c,
* inheritance hierarchy, so all we have to do is process * but this particular routine is recursive.
* all of the relids in the list that it returns. *
* normally, only the owner of a class can change its schema.
*/ */
foreach (child, children) { if (IsSystemRelationName(relname))
char *childname; elog(WARN, "renameatt: class \"%s\" is a system catalog",
relname);
#ifndef NO_SECURITY
if (!IsBootstrapProcessingMode() &&
!pg_ownercheck(userName, relname, RELNAME))
elog(WARN, "renameatt: you do not own class \"%s\"",
relname);
#endif
childrelid = lfirsti(child); /*
if (childrelid == myrelid) * if the 'recurse' flag is set then we are supposed to rename this
continue; * attribute in all classes that inherit from 'relname' (as well as in
relrdesc = heap_open(childrelid); * 'relname').
if (!RelationIsValid(relrdesc)) { *
elog(WARN, "renameatt: can't find catalog entry for inheriting class with oid %d", * any permissions or problems with duplicate attributes will cause the
childrelid); * whole transaction to abort, which is what we want -- all or
} * nothing.
childname = (relrdesc->rd_rel->relname).data; */
heap_close(relrdesc); if (recurse)
renameatt(childname, oldattname, newattname, {
userName, 0); /* no more recursion! */ Oid myrelid,
childrelid;
List *child,
*children;
relrdesc = heap_openr(relname);
if (!RelationIsValid(relrdesc))
{
elog(WARN, "renameatt: unknown relation: \"%s\"",
relname);
}
myrelid = relrdesc->rd_id;
heap_close(relrdesc);
/* this routine is actually in the planner */
children = find_all_inheritors(lconsi(myrelid, NIL), NIL);
/*
* find_all_inheritors does the recursive search of the
* inheritance hierarchy, so all we have to do is process all of
* the relids in the list that it returns.
*/
foreach(child, children)
{
char *childname;
childrelid = lfirsti(child);
if (childrelid == myrelid)
continue;
relrdesc = heap_open(childrelid);
if (!RelationIsValid(relrdesc))
{
elog(WARN, "renameatt: can't find catalog entry for inheriting class with oid %d",
childrelid);
}
childname = (relrdesc->rd_rel->relname).data;
heap_close(relrdesc);
renameatt(childname, oldattname, newattname,
userName, 0); /* no more recursion! */
}
} }
}
relrdesc = heap_openr(RelationRelationName); relrdesc = heap_openr(RelationRelationName);
reltup = ClassNameIndexScan(relrdesc, relname); reltup = ClassNameIndexScan(relrdesc, relname);
if (!PointerIsValid(reltup)) { if (!PointerIsValid(reltup))
{
heap_close(relrdesc);
elog(WARN, "renameatt: relation \"%s\" nonexistent",
relname);
return;
}
heap_close(relrdesc); heap_close(relrdesc);
elog(WARN, "renameatt: relation \"%s\" nonexistent",
relname);
return;
}
heap_close(relrdesc);
attrdesc = heap_openr(AttributeRelationName); attrdesc = heap_openr(AttributeRelationName);
oldatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, oldattname); oldatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, oldattname);
if (!PointerIsValid(oldatttup)) { if (!PointerIsValid(oldatttup))
{
heap_close(attrdesc);
elog(WARN, "renameatt: attribute \"%s\" nonexistent",
oldattname);
}
if (((AttributeTupleForm) GETSTRUCT(oldatttup))->attnum < 0)
{
elog(WARN, "renameatt: system attribute \"%s\" not renamed",
oldattname);
}
newatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, newattname);
if (PointerIsValid(newatttup))
{
pfree(oldatttup);
heap_close(attrdesc);
elog(WARN, "renameatt: attribute \"%s\" exists",
newattname);
}
namestrcpy(&(((AttributeTupleForm) (GETSTRUCT(oldatttup)))->attname),
newattname);
oldTID = oldatttup->t_ctid;
/* insert "fixed" tuple */
heap_replace(attrdesc, &oldTID, oldatttup);
/* keep system catalog indices current */
CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_attr_indices, attrdesc, oldatttup);
CatalogCloseIndices(Num_pg_attr_indices, idescs);
heap_close(attrdesc); heap_close(attrdesc);
elog(WARN, "renameatt: attribute \"%s\" nonexistent",
oldattname);
}
if (((AttributeTupleForm ) GETSTRUCT(oldatttup))->attnum < 0) {
elog(WARN, "renameatt: system attribute \"%s\" not renamed",
oldattname);
}
newatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, newattname);
if (PointerIsValid(newatttup)) {
pfree(oldatttup); pfree(oldatttup);
heap_close(attrdesc);
elog(WARN, "renameatt: attribute \"%s\" exists",
newattname);
}
namestrcpy(&(((AttributeTupleForm)(GETSTRUCT(oldatttup)))->attname),
newattname);
oldTID = oldatttup->t_ctid;
/* insert "fixed" tuple */
heap_replace(attrdesc, &oldTID, oldatttup);
/* keep system catalog indices current */
CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_attr_indices, attrdesc, oldatttup);
CatalogCloseIndices(Num_pg_attr_indices, idescs);
heap_close(attrdesc);
pfree(oldatttup);
} }
/* /*
* renamerel - change the name of a relation * renamerel - change the name of a relation
* *
* Relname attribute is changed in relation catalog. * Relname attribute is changed in relation catalog.
* No record of the previous relname is kept (correct?). * No record of the previous relname is kept (correct?).
* *
* scan relation catalog * scan relation catalog
* for name conflict * for name conflict
* for original relation (if not arg) * for original relation (if not arg)
* modify relname in relation tuple * modify relname in relation tuple
* insert modified relation in relation catalog * insert modified relation in relation catalog
* delete original relation from relation catalog * delete original relation from relation catalog
* *
* XXX Will currently lose track of a relation if it is unable to * XXX Will currently lose track of a relation if it is unable to
* properly replace the new relation tuple. * properly replace the new relation tuple.
*/ */
void void
renamerel(char oldrelname[], char newrelname[]) renamerel(char oldrelname[], char newrelname[])
{ {
Relation relrdesc; /* for RELATION relation */ Relation relrdesc; /* for RELATION relation */
HeapTuple oldreltup, newreltup; HeapTuple oldreltup,
ItemPointerData oldTID; newreltup;
char oldpath[MAXPGPATH], newpath[MAXPGPATH]; ItemPointerData oldTID;
Relation idescs[Num_pg_class_indices]; char oldpath[MAXPGPATH],
newpath[MAXPGPATH];
Relation idescs[Num_pg_class_indices];
if (IsSystemRelationName(oldrelname)) { if (IsSystemRelationName(oldrelname))
elog(WARN, "renamerel: system relation \"%s\" not renamed", {
oldrelname); elog(WARN, "renamerel: system relation \"%s\" not renamed",
return; oldrelname);
} return;
if (IsSystemRelationName(newrelname)) { }
elog(WARN, "renamerel: Illegal class name: \"%s\" -- pg_ is reserved for system catalogs", if (IsSystemRelationName(newrelname))
newrelname); {
return; elog(WARN, "renamerel: Illegal class name: \"%s\" -- pg_ is reserved for system catalogs",
} newrelname);
return;
}
relrdesc = heap_openr(RelationRelationName); relrdesc = heap_openr(RelationRelationName);
oldreltup = ClassNameIndexScan(relrdesc, oldrelname); oldreltup = ClassNameIndexScan(relrdesc, oldrelname);
if (!PointerIsValid(oldreltup)) { if (!PointerIsValid(oldreltup))
heap_close(relrdesc); {
elog(WARN, "renamerel: relation \"%s\" does not exist", heap_close(relrdesc);
oldrelname); elog(WARN, "renamerel: relation \"%s\" does not exist",
} oldrelname);
}
newreltup = ClassNameIndexScan(relrdesc, newrelname);
if (PointerIsValid(newreltup))
{
pfree(oldreltup);
heap_close(relrdesc);
elog(WARN, "renamerel: relation \"%s\" exists",
newrelname);
}
/* rename the directory first, so if this fails the rename's not done */
strcpy(oldpath, relpath(oldrelname));
strcpy(newpath, relpath(newrelname));
if (rename(oldpath, newpath) < 0)
elog(WARN, "renamerel: unable to rename file: %m");
memmove((char *) (((Form_pg_class) GETSTRUCT(oldreltup))->relname.data),
newrelname,
NAMEDATALEN);
oldTID = oldreltup->t_ctid;
/* insert fixed rel tuple */
heap_replace(relrdesc, &oldTID, oldreltup);
/* keep the system catalog indices current */
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_class_indices, relrdesc, oldreltup);
CatalogCloseIndices(Num_pg_class_indices, idescs);
newreltup = ClassNameIndexScan(relrdesc, newrelname);
if (PointerIsValid(newreltup)) {
pfree(oldreltup); pfree(oldreltup);
heap_close(relrdesc); heap_close(relrdesc);
elog(WARN, "renamerel: relation \"%s\" exists",
newrelname);
}
/* rename the directory first, so if this fails the rename's not done */
strcpy(oldpath, relpath(oldrelname));
strcpy(newpath, relpath(newrelname));
if (rename(oldpath, newpath) < 0)
elog(WARN, "renamerel: unable to rename file: %m");
memmove((char *) (((Form_pg_class) GETSTRUCT(oldreltup))->relname.data),
newrelname,
NAMEDATALEN);
oldTID = oldreltup->t_ctid;
/* insert fixed rel tuple */
heap_replace(relrdesc, &oldTID, oldreltup);
/* keep the system catalog indices current */
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_class_indices, relrdesc, oldreltup);
CatalogCloseIndices(Num_pg_class_indices, idescs);
pfree(oldreltup);
heap_close(relrdesc);
} }

View File

@ -1,7 +1,7 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* sequence.c-- * sequence.c--
* PostgreSQL sequences support code. * PostgreSQL sequences support code.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -19,523 +19,539 @@
#include <commands/sequence.h> #include <commands/sequence.h>
#include <utils/builtins.h> #include <utils/builtins.h>
#define SEQ_MAGIC 0x1717 #define SEQ_MAGIC 0x1717
#define SEQ_MAXVALUE ((int4)0x7FFFFFFF) #define SEQ_MAXVALUE ((int4)0x7FFFFFFF)
#define SEQ_MINVALUE -(SEQ_MAXVALUE) #define SEQ_MINVALUE -(SEQ_MAXVALUE)
bool ItsSequenceCreation = false; bool ItsSequenceCreation = false;
typedef struct FormData_pg_sequence { typedef struct FormData_pg_sequence
NameData sequence_name; {
int4 last_value; NameData sequence_name;
int4 increment_by; int4 last_value;
int4 max_value; int4 increment_by;
int4 min_value; int4 max_value;
int4 cache_value; int4 min_value;
char is_cycled; int4 cache_value;
char is_called; char is_cycled;
} FormData_pg_sequence; char is_called;
} FormData_pg_sequence;
typedef FormData_pg_sequence *SequenceTupleForm; typedef FormData_pg_sequence *SequenceTupleForm;
typedef struct sequence_magic { typedef struct sequence_magic
uint32 magic; {
} sequence_magic; uint32 magic;
} sequence_magic;
typedef struct SeqTableData { typedef struct SeqTableData
char *name; {
Oid relid; char *name;
Oid relid;
Relation rel; Relation rel;
int4 cached; int4 cached;
int4 last; int4 last;
int4 increment; int4 increment;
struct SeqTableData *next; struct SeqTableData *next;
} SeqTableData; } SeqTableData;
typedef SeqTableData *SeqTable; typedef SeqTableData *SeqTable;
static SeqTable seqtab = NULL; static SeqTable seqtab = NULL;
static SeqTable init_sequence (char *caller, char *name); static SeqTable init_sequence(char *caller, char *name);
static SequenceTupleForm read_info (char * caller, SeqTable elm, Buffer * buf); static SequenceTupleForm read_info(char *caller, SeqTable elm, Buffer * buf);
static void init_params (CreateSeqStmt *seq, SequenceTupleForm new); static void init_params(CreateSeqStmt * seq, SequenceTupleForm new);
static int get_param (DefElem *def); static int get_param(DefElem * def);
/* /*
* DefineSequence -- * DefineSequence --
* Creates a new sequence relation * Creates a new sequence relation
*/ */
void void
DefineSequence (CreateSeqStmt *seq) DefineSequence(CreateSeqStmt * seq)
{ {
FormData_pg_sequence new; FormData_pg_sequence new;
CreateStmt *stmt = makeNode (CreateStmt); CreateStmt *stmt = makeNode(CreateStmt);
ColumnDef *coldef; ColumnDef *coldef;
TypeName *typnam; TypeName *typnam;
Relation rel; Relation rel;
Buffer buf; Buffer buf;
PageHeader page; PageHeader page;
sequence_magic *sm; sequence_magic *sm;
HeapTuple tuple; HeapTuple tuple;
TupleDesc tupDesc; TupleDesc tupDesc;
Datum value[SEQ_COL_LASTCOL]; Datum value[SEQ_COL_LASTCOL];
char null[SEQ_COL_LASTCOL]; char null[SEQ_COL_LASTCOL];
int i; int i;
/* Check and set values */ /* Check and set values */
init_params (seq, &new); init_params(seq, &new);
/* /*
* Create relation (and fill null[] & value[]) * Create relation (and fill null[] & value[])
*/ */
stmt->tableElts = NIL; stmt->tableElts = NIL;
for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++) for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
{
typnam = makeNode(TypeName);
typnam->setof = FALSE;
typnam->arrayBounds = NULL;
coldef = makeNode(ColumnDef);
coldef->typename = typnam;
coldef->defval = NULL;
coldef->is_not_null = false;
null[i-1] = ' ';
switch (i)
{ {
case SEQ_COL_NAME: typnam = makeNode(TypeName);
typnam->name = "name"; typnam->setof = FALSE;
coldef->colname = "sequence_name"; typnam->arrayBounds = NULL;
value[i-1] = PointerGetDatum (seq->seqname); coldef = makeNode(ColumnDef);
break; coldef->typename = typnam;
case SEQ_COL_LASTVAL: coldef->defval = NULL;
typnam->name = "int4"; coldef->is_not_null = false;
coldef->colname = "last_value"; null[i - 1] = ' ';
value[i-1] = Int32GetDatum (new.last_value);
break;
case SEQ_COL_INCBY:
typnam->name = "int4";
coldef->colname = "increment_by";
value[i-1] = Int32GetDatum (new.increment_by);
break;
case SEQ_COL_MAXVALUE:
typnam->name = "int4";
coldef->colname = "max_value";
value[i-1] = Int32GetDatum (new.max_value);
break;
case SEQ_COL_MINVALUE:
typnam->name = "int4";
coldef->colname = "min_value";
value[i-1] = Int32GetDatum (new.min_value);
break;
case SEQ_COL_CACHE:
typnam->name = "int4";
coldef->colname = "cache_value";
value[i-1] = Int32GetDatum (new.cache_value);
break;
case SEQ_COL_CYCLE:
typnam->name = "char";
coldef->colname = "is_cycled";
value[i-1] = CharGetDatum (new.is_cycled);
break;
case SEQ_COL_CALLED:
typnam->name = "char";
coldef->colname = "is_called";
value[i-1] = CharGetDatum ('f');
break;
}
stmt->tableElts = lappend (stmt->tableElts, coldef);
}
stmt->relname = seq->seqname; switch (i)
stmt->archiveLoc = -1; /* default */ {
stmt->archiveType = ARCH_NONE; case SEQ_COL_NAME:
stmt->inhRelnames = NIL; typnam->name = "name";
stmt->constraints = NIL; coldef->colname = "sequence_name";
value[i - 1] = PointerGetDatum(seq->seqname);
ItsSequenceCreation = true; /* hack */ break;
case SEQ_COL_LASTVAL:
DefineRelation (stmt); typnam->name = "int4";
coldef->colname = "last_value";
/* Xact abort calls CloseSequences, which turns ItsSequenceCreation off */ value[i - 1] = Int32GetDatum(new.last_value);
ItsSequenceCreation = false; /* hack */ break;
case SEQ_COL_INCBY:
rel = heap_openr (seq->seqname); typnam->name = "int4";
Assert ( RelationIsValid (rel) ); coldef->colname = "increment_by";
value[i - 1] = Int32GetDatum(new.increment_by);
RelationSetLockForWrite (rel); break;
case SEQ_COL_MAXVALUE:
tupDesc = RelationGetTupleDescriptor(rel); typnam->name = "int4";
coldef->colname = "max_value";
Assert ( RelationGetNumberOfBlocks (rel) == 0 ); value[i - 1] = Int32GetDatum(new.max_value);
buf = ReadBuffer (rel, P_NEW); break;
case SEQ_COL_MINVALUE:
if ( !BufferIsValid (buf) ) typnam->name = "int4";
elog (WARN, "DefineSequence: ReadBuffer failed"); coldef->colname = "min_value";
value[i - 1] = Int32GetDatum(new.min_value);
page = (PageHeader) BufferGetPage (buf); break;
case SEQ_COL_CACHE:
PageInit((Page)page, BufferGetPageSize(buf), sizeof(sequence_magic)); typnam->name = "int4";
sm = (sequence_magic *) PageGetSpecialPointer (page); coldef->colname = "cache_value";
sm->magic = SEQ_MAGIC; value[i - 1] = Int32GetDatum(new.cache_value);
break;
/* Now - form & insert sequence tuple */ case SEQ_COL_CYCLE:
tuple = heap_formtuple (tupDesc, value, null); typnam->name = "char";
heap_insert (rel, tuple); coldef->colname = "is_cycled";
value[i - 1] = CharGetDatum(new.is_cycled);
if ( WriteBuffer (buf) == STATUS_ERROR ) break;
elog (WARN, "DefineSequence: WriteBuffer failed"); case SEQ_COL_CALLED:
typnam->name = "char";
RelationUnsetLockForWrite (rel); coldef->colname = "is_called";
heap_close (rel); value[i - 1] = CharGetDatum('f');
break;
return; }
stmt->tableElts = lappend(stmt->tableElts, coldef);
}
int4
nextval (struct varlena * seqin)
{
char *seqname = textout(seqin);
SeqTable elm;
Buffer buf;
SequenceTupleForm seq;
ItemPointerData iptr;
int4 incby, maxv, minv, cache;
int4 result, next, rescnt = 0;
/* open and WIntentLock sequence */
elm = init_sequence ("nextval", seqname);
pfree (seqname);
if ( elm->last != elm->cached ) /* some numbers were cached */
{
elm->last += elm->increment;
return (elm->last);
}
seq = read_info ("nextval", elm, &buf); /* lock page and read tuple */
next = result = seq->last_value;
incby = seq->increment_by;
maxv = seq->max_value;
minv = seq->min_value;
cache = seq->cache_value;
if ( seq->is_called != 't' )
rescnt++; /* last_value if not called */
while ( rescnt < cache ) /* try to fetch cache numbers */
{
/*
* Check MAXVALUE for ascending sequences
* and MINVALUE for descending sequences
*/
if ( incby > 0 ) /* ascending sequence */
{
if ( ( maxv >= 0 && next > maxv - incby) ||
( maxv < 0 && next + incby > maxv ) )
{
if ( rescnt > 0 )
break; /* stop caching */
if ( seq->is_cycled != 't' )
elog (WARN, "%s.nextval: got MAXVALUE (%d)",
elm->name, maxv);
next = minv;
}
else
next += incby;
} }
else /* descending sequence */
{
if ( ( minv < 0 && next < minv - incby ) ||
( minv >= 0 && next + incby < minv ) )
{
if ( rescnt > 0 )
break; /* stop caching */
if ( seq->is_cycled != 't' )
elog (WARN, "%s.nextval: got MINVALUE (%d)",
elm->name, minv);
next = maxv;
}
else
next += incby;
}
rescnt++; /* got result */
if ( rescnt == 1 ) /* if it's first one - */
result = next; /* it's what to return */
}
/* save info in local cache */ stmt->relname = seq->seqname;
elm->last = result; /* last returned number */ stmt->archiveLoc = -1; /* default */
elm->cached = next; /* last cached number */ stmt->archiveType = ARCH_NONE;
stmt->inhRelnames = NIL;
stmt->constraints = NIL;
/* save info in sequence relation */ ItsSequenceCreation = true; /* hack */
seq->last_value = next; /* last fetched number */
seq->is_called = 't';
if ( WriteBuffer (buf) == STATUS_ERROR ) DefineRelation(stmt);
elog (WARN, "%s.nextval: WriteBuffer failed", elm->name);
ItemPointerSet(&iptr, 0, FirstOffsetNumber); /*
RelationUnsetSingleWLockPage (elm->rel, &iptr); * Xact abort calls CloseSequences, which turns ItsSequenceCreation
* off
*/
ItsSequenceCreation = false;/* hack */
return (result); rel = heap_openr(seq->seqname);
Assert(RelationIsValid(rel));
RelationSetLockForWrite(rel);
tupDesc = RelationGetTupleDescriptor(rel);
Assert(RelationGetNumberOfBlocks(rel) == 0);
buf = ReadBuffer(rel, P_NEW);
if (!BufferIsValid(buf))
elog(WARN, "DefineSequence: ReadBuffer failed");
page = (PageHeader) BufferGetPage(buf);
PageInit((Page) page, BufferGetPageSize(buf), sizeof(sequence_magic));
sm = (sequence_magic *) PageGetSpecialPointer(page);
sm->magic = SEQ_MAGIC;
/* Now - form & insert sequence tuple */
tuple = heap_formtuple(tupDesc, value, null);
heap_insert(rel, tuple);
if (WriteBuffer(buf) == STATUS_ERROR)
elog(WARN, "DefineSequence: WriteBuffer failed");
RelationUnsetLockForWrite(rel);
heap_close(rel);
return;
} }
int4 int4
currval (struct varlena * seqin) nextval(struct varlena * seqin)
{ {
char *seqname = textout(seqin); char *seqname = textout(seqin);
SeqTable elm; SeqTable elm;
int4 result; Buffer buf;
SequenceTupleForm seq;
ItemPointerData iptr;
int4 incby,
maxv,
minv,
cache;
int4 result,
next,
rescnt = 0;
/* open and WIntentLock sequence */ /* open and WIntentLock sequence */
elm = init_sequence ("currval", seqname); elm = init_sequence("nextval", seqname);
pfree (seqname); pfree(seqname);
if ( elm->increment == 0 ) /* nextval/read_info were not called */ if (elm->last != elm->cached) /* some numbers were cached */
{ {
elog (WARN, "%s.currval is not yet defined in this session", elm->name); elm->last += elm->increment;
} return (elm->last);
}
result = elm->last; seq = read_info("nextval", elm, &buf); /* lock page and read
* tuple */
return (result); next = result = seq->last_value;
incby = seq->increment_by;
maxv = seq->max_value;
minv = seq->min_value;
cache = seq->cache_value;
} if (seq->is_called != 't')
rescnt++; /* last_value if not called */
static SequenceTupleForm while (rescnt < cache) /* try to fetch cache numbers */
read_info (char * caller, SeqTable elm, Buffer * buf) {
{
ItemPointerData iptr;
PageHeader page;
ItemId lp;
HeapTuple tuple;
sequence_magic *sm;
SequenceTupleForm seq;
ItemPointerSet(&iptr, 0, FirstOffsetNumber); /*
RelationSetSingleWLockPage (elm->rel, &iptr); * Check MAXVALUE for ascending sequences and MINVALUE for
* descending sequences
*/
if (incby > 0) /* ascending sequence */
{
if ((maxv >= 0 && next > maxv - incby) ||
(maxv < 0 && next + incby > maxv))
{
if (rescnt > 0)
break; /* stop caching */
if (seq->is_cycled != 't')
elog(WARN, "%s.nextval: got MAXVALUE (%d)",
elm->name, maxv);
next = minv;
}
else
next += incby;
}
else
/* descending sequence */
{
if ((minv < 0 && next < minv - incby) ||
(minv >= 0 && next + incby < minv))
{
if (rescnt > 0)
break; /* stop caching */
if (seq->is_cycled != 't')
elog(WARN, "%s.nextval: got MINVALUE (%d)",
elm->name, minv);
next = maxv;
}
else
next += incby;
}
rescnt++; /* got result */
if (rescnt == 1) /* if it's first one - */
result = next; /* it's what to return */
}
if ( RelationGetNumberOfBlocks (elm->rel) != 1 ) /* save info in local cache */
elog (WARN, "%s.%s: invalid number of blocks in sequence", elm->last = result; /* last returned number */
elm->name, caller); elm->cached = next; /* last cached number */
*buf = ReadBuffer (elm->rel, 0); /* save info in sequence relation */
if ( !BufferIsValid (*buf) ) seq->last_value = next; /* last fetched number */
elog (WARN, "%s.%s: ReadBuffer failed", elm->name, caller); seq->is_called = 't';
page = (PageHeader) BufferGetPage (*buf); if (WriteBuffer(buf) == STATUS_ERROR)
sm = (sequence_magic *) PageGetSpecialPointer (page); elog(WARN, "%s.nextval: WriteBuffer failed", elm->name);
if ( sm->magic != SEQ_MAGIC ) ItemPointerSet(&iptr, 0, FirstOffsetNumber);
elog (WARN, "%s.%s: bad magic (%08X)", elm->name, caller, sm->magic); RelationUnsetSingleWLockPage(elm->rel, &iptr);
lp = PageGetItemId (page, FirstOffsetNumber); return (result);
Assert (ItemIdIsUsed (lp));
tuple = (HeapTuple) PageGetItem ((Page) page, lp);
seq = (SequenceTupleForm) GETSTRUCT(tuple);
elm->increment = seq->increment_by;
return (seq);
} }
static SeqTable int4
init_sequence (char * caller, char * name) currval(struct varlena * seqin)
{ {
SeqTable elm, priv = (SeqTable) NULL; char *seqname = textout(seqin);
SeqTable temp; SeqTable elm;
int4 result;
for (elm = seqtab; elm != (SeqTable) NULL; ) /* open and WIntentLock sequence */
{ elm = init_sequence("currval", seqname);
if ( strcmp (elm->name, name) == 0 ) pfree(seqname);
break;
priv = elm;
elm = elm->next;
}
if ( elm == (SeqTable) NULL ) /* not found */ if (elm->increment == 0) /* nextval/read_info were not called */
{ {
temp = (SeqTable) malloc (sizeof(SeqTableData)); elog(WARN, "%s.currval is not yet defined in this session", elm->name);
temp->name = malloc (strlen(name) + 1); }
strcpy (temp->name, name);
temp->rel = (Relation) NULL;
temp->cached = temp->last = temp->increment = 0;
temp->next = (SeqTable) NULL;
}
else /* found */
{
if ( elm->rel != (Relation) NULL) /* already opened */
return (elm);
temp = elm;
}
temp->rel = heap_openr (name); result = elm->last;
if ( ! RelationIsValid (temp->rel) ) return (result);
elog (WARN, "%s.%s: sequence does not exist", name, caller);
RelationSetWIntentLock (temp->rel); }
if ( temp->rel->rd_rel->relkind != RELKIND_SEQUENCE ) static SequenceTupleForm
elog (WARN, "%s.%s: %s is not sequence !", name, caller, name); read_info(char *caller, SeqTable elm, Buffer * buf)
{
ItemPointerData iptr;
PageHeader page;
ItemId lp;
HeapTuple tuple;
sequence_magic *sm;
SequenceTupleForm seq;
if ( elm != (SeqTable) NULL ) /* we opened sequence from our */ ItemPointerSet(&iptr, 0, FirstOffsetNumber);
{ /* SeqTable - check relid ! */ RelationSetSingleWLockPage(elm->rel, &iptr);
if ( RelationGetRelationId (elm->rel) != elm->relid )
{
elog (NOTICE, "%s.%s: sequence was re-created",
name, caller, name);
elm->cached = elm->last = elm->increment = 0;
elm->relid = RelationGetRelationId (elm->rel);
}
}
else
{
elm = temp;
elm->relid = RelationGetRelationId (elm->rel);
if ( seqtab == (SeqTable) NULL )
seqtab = elm;
else
priv->next = elm;
}
return (elm); if (RelationGetNumberOfBlocks(elm->rel) != 1)
elog(WARN, "%s.%s: invalid number of blocks in sequence",
elm->name, caller);
*buf = ReadBuffer(elm->rel, 0);
if (!BufferIsValid(*buf))
elog(WARN, "%s.%s: ReadBuffer failed", elm->name, caller);
page = (PageHeader) BufferGetPage(*buf);
sm = (sequence_magic *) PageGetSpecialPointer(page);
if (sm->magic != SEQ_MAGIC)
elog(WARN, "%s.%s: bad magic (%08X)", elm->name, caller, sm->magic);
lp = PageGetItemId(page, FirstOffsetNumber);
Assert(ItemIdIsUsed(lp));
tuple = (HeapTuple) PageGetItem((Page) page, lp);
seq = (SequenceTupleForm) GETSTRUCT(tuple);
elm->increment = seq->increment_by;
return (seq);
}
static SeqTable
init_sequence(char *caller, char *name)
{
SeqTable elm,
priv = (SeqTable) NULL;
SeqTable temp;
for (elm = seqtab; elm != (SeqTable) NULL;)
{
if (strcmp(elm->name, name) == 0)
break;
priv = elm;
elm = elm->next;
}
if (elm == (SeqTable) NULL) /* not found */
{
temp = (SeqTable) malloc(sizeof(SeqTableData));
temp->name = malloc(strlen(name) + 1);
strcpy(temp->name, name);
temp->rel = (Relation) NULL;
temp->cached = temp->last = temp->increment = 0;
temp->next = (SeqTable) NULL;
}
else
/* found */
{
if (elm->rel != (Relation) NULL) /* already opened */
return (elm);
temp = elm;
}
temp->rel = heap_openr(name);
if (!RelationIsValid(temp->rel))
elog(WARN, "%s.%s: sequence does not exist", name, caller);
RelationSetWIntentLock(temp->rel);
if (temp->rel->rd_rel->relkind != RELKIND_SEQUENCE)
elog(WARN, "%s.%s: %s is not sequence !", name, caller, name);
if (elm != (SeqTable) NULL) /* we opened sequence from our */
{ /* SeqTable - check relid ! */
if (RelationGetRelationId(elm->rel) != elm->relid)
{
elog(NOTICE, "%s.%s: sequence was re-created",
name, caller, name);
elm->cached = elm->last = elm->increment = 0;
elm->relid = RelationGetRelationId(elm->rel);
}
}
else
{
elm = temp;
elm->relid = RelationGetRelationId(elm->rel);
if (seqtab == (SeqTable) NULL)
seqtab = elm;
else
priv->next = elm;
}
return (elm);
} }
/* /*
* CloseSequences -- * CloseSequences --
* is calling by xact mgr at commit/abort. * is calling by xact mgr at commit/abort.
*/ */
void void
CloseSequences (void) CloseSequences(void)
{ {
SeqTable elm; SeqTable elm;
Relation rel; Relation rel;
ItsSequenceCreation = false; ItsSequenceCreation = false;
for (elm = seqtab; elm != (SeqTable) NULL; ) for (elm = seqtab; elm != (SeqTable) NULL;)
{
if ( elm->rel != (Relation) NULL ) /* opened in current xact */
{ {
rel = elm->rel; if (elm->rel != (Relation) NULL) /* opened in current xact */
elm->rel = (Relation) NULL; {
RelationUnsetWIntentLock (rel); rel = elm->rel;
heap_close (rel); elm->rel = (Relation) NULL;
} RelationUnsetWIntentLock(rel);
elm = elm->next; heap_close(rel);
} }
elm = elm->next;
}
return; return;
} }
static void static void
init_params (CreateSeqStmt *seq, SequenceTupleForm new) init_params(CreateSeqStmt * seq, SequenceTupleForm new)
{ {
DefElem *last_value = NULL; DefElem *last_value = NULL;
DefElem *increment_by = NULL; DefElem *increment_by = NULL;
DefElem *max_value = NULL; DefElem *max_value = NULL;
DefElem *min_value = NULL; DefElem *min_value = NULL;
DefElem *cache_value = NULL; DefElem *cache_value = NULL;
List *option; List *option;
new->is_cycled = 'f'; new->is_cycled = 'f';
foreach (option, seq->options) foreach(option, seq->options)
{ {
DefElem *defel = (DefElem *)lfirst(option); DefElem *defel = (DefElem *) lfirst(option);
if ( !strcasecmp(defel->defname, "increment") ) if (!strcasecmp(defel->defname, "increment"))
increment_by = defel; increment_by = defel;
else if ( !strcasecmp(defel->defname, "start") ) else if (!strcasecmp(defel->defname, "start"))
last_value = defel; last_value = defel;
else if ( !strcasecmp(defel->defname, "maxvalue") ) else if (!strcasecmp(defel->defname, "maxvalue"))
max_value = defel; max_value = defel;
else if ( !strcasecmp(defel->defname, "minvalue") ) else if (!strcasecmp(defel->defname, "minvalue"))
min_value = defel; min_value = defel;
else if ( !strcasecmp(defel->defname, "cache") ) else if (!strcasecmp(defel->defname, "cache"))
cache_value = defel; cache_value = defel;
else if ( !strcasecmp(defel->defname, "cycle") ) else if (!strcasecmp(defel->defname, "cycle"))
{ {
if ( defel->arg != (Node*) NULL ) if (defel->arg != (Node *) NULL)
elog (WARN, "DefineSequence: CYCLE ??"); elog(WARN, "DefineSequence: CYCLE ??");
new->is_cycled = 't'; new->is_cycled = 't';
} }
else else
elog (WARN, "DefineSequence: option \"%s\" not recognized", elog(WARN, "DefineSequence: option \"%s\" not recognized",
defel->defname); defel->defname);
} }
if ( increment_by == (DefElem*) NULL ) /* INCREMENT BY */ if (increment_by == (DefElem *) NULL) /* INCREMENT BY */
new->increment_by = 1; new->increment_by = 1;
else if ( ( new->increment_by = get_param (increment_by) ) == 0 ) else if ((new->increment_by = get_param(increment_by)) == 0)
elog (WARN, "DefineSequence: can't INCREMENT by 0"); elog(WARN, "DefineSequence: can't INCREMENT by 0");
if ( max_value == (DefElem*) NULL ) /* MAXVALUE */ if (max_value == (DefElem *) NULL) /* MAXVALUE */
if ( new->increment_by > 0 ) if (new->increment_by > 0)
new->max_value = SEQ_MAXVALUE; /* ascending seq */ new->max_value = SEQ_MAXVALUE; /* ascending seq */
else
new->max_value = -1;/* descending seq */
else else
new->max_value = -1; /* descending seq */ new->max_value = get_param(max_value);
else
new->max_value = get_param (max_value);
if ( min_value == (DefElem*) NULL ) /* MINVALUE */ if (min_value == (DefElem *) NULL) /* MINVALUE */
if ( new->increment_by > 0 ) if (new->increment_by > 0)
new->min_value = 1; /* ascending seq */ new->min_value = 1; /* ascending seq */
else
new->min_value = SEQ_MINVALUE; /* descending seq */
else else
new->min_value = SEQ_MINVALUE; /* descending seq */ new->min_value = get_param(min_value);
else
new->min_value = get_param (min_value);
if ( new->min_value >= new->max_value ) if (new->min_value >= new->max_value)
elog (WARN, "DefineSequence: MINVALUE (%d) can't be >= MAXVALUE (%d)", elog(WARN, "DefineSequence: MINVALUE (%d) can't be >= MAXVALUE (%d)",
new->min_value, new->max_value); new->min_value, new->max_value);
if ( last_value == (DefElem*) NULL ) /* START WITH */ if (last_value == (DefElem *) NULL) /* START WITH */
if ( new->increment_by > 0 ) if (new->increment_by > 0)
new->last_value = new->min_value; /* ascending seq */ new->last_value = new->min_value; /* ascending seq */
else
new->last_value = new->max_value; /* descending seq */
else else
new->last_value = new->max_value; /* descending seq */ new->last_value = get_param(last_value);
else
new->last_value = get_param (last_value);
if ( new->last_value < new->min_value ) if (new->last_value < new->min_value)
elog (WARN, "DefineSequence: START value (%d) can't be < MINVALUE (%d)", elog(WARN, "DefineSequence: START value (%d) can't be < MINVALUE (%d)",
new->last_value, new->min_value); new->last_value, new->min_value);
if ( new->last_value > new->max_value ) if (new->last_value > new->max_value)
elog (WARN, "DefineSequence: START value (%d) can't be > MAXVALUE (%d)", elog(WARN, "DefineSequence: START value (%d) can't be > MAXVALUE (%d)",
new->last_value, new->max_value); new->last_value, new->max_value);
if ( cache_value == (DefElem*) NULL ) /* CACHE */ if (cache_value == (DefElem *) NULL) /* CACHE */
new->cache_value = 1; new->cache_value = 1;
else if ( ( new->cache_value = get_param (cache_value) ) <= 0 ) else if ((new->cache_value = get_param(cache_value)) <= 0)
elog (WARN, "DefineSequence: CACHE (%d) can't be <= 0", elog(WARN, "DefineSequence: CACHE (%d) can't be <= 0",
new->cache_value); new->cache_value);
} }
static int static int
get_param (DefElem *def) get_param(DefElem * def)
{ {
if ( def->arg == (Node*) NULL ) if (def->arg == (Node *) NULL)
elog (WARN, "DefineSequence: \"%s\" value unspecified", def->defname); elog(WARN, "DefineSequence: \"%s\" value unspecified", def->defname);
if ( nodeTag (def->arg) == T_Integer ) if (nodeTag(def->arg) == T_Integer)
return (intVal (def->arg)); return (intVal(def->arg));
elog (WARN, "DefineSequence: \"%s\" is to be integer", def->defname); elog(WARN, "DefineSequence: \"%s\" is to be integer", def->defname);
return (-1); return (-1);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,17 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* view.c-- * view.c--
* use rewrite rules to construct views * use rewrite rules to construct views
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.8 1997/08/22 14:22:14 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.9 1997/09/07 04:41:06 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include <stdio.h> /* for sprintf() */ #include <stdio.h> /* for sprintf() */
#include <string.h> #include <string.h>
#include <postgres.h> #include <postgres.h>
@ -42,68 +42,73 @@
*--------------------------------------------------------------------- *---------------------------------------------------------------------
*/ */
static void static void
DefineVirtualRelation(char *relname, List *tlist) DefineVirtualRelation(char *relname, List * tlist)
{ {
CreateStmt createStmt; CreateStmt createStmt;
List *attrList, *t; List *attrList,
TargetEntry *entry; *t;
Resdom *res; TargetEntry *entry;
char *resname; Resdom *res;
char *restypename; char *resname;
char *restypename;
/* /*
* create a list with one entry per attribute of this relation. * create a list with one entry per attribute of this relation. Each
* Each entry is a two element list. The first element is the * entry is a two element list. The first element is the name of the
* name of the attribute (a string) and the second the name of the type * attribute (a string) and the second the name of the type (NOTE: a
* (NOTE: a string, not a type id!). * string, not a type id!).
*/ */
attrList = NIL; attrList = NIL;
if (tlist!=NIL) { if (tlist != NIL)
foreach (t, tlist ) { {
ColumnDef *def = makeNode(ColumnDef); foreach(t, tlist)
TypeName *typename; {
ColumnDef *def = makeNode(ColumnDef);
TypeName *typename;
/* /*
* find the names of the attribute & its type * find the names of the attribute & its type
*/ */
entry = lfirst(t); entry = lfirst(t);
res = entry->resdom; res = entry->resdom;
resname = res->resname; resname = res->resname;
restypename = tname(get_id_type(res->restype)); restypename = tname(get_id_type(res->restype));
typename = makeNode(TypeName); typename = makeNode(TypeName);
typename->name = pstrdup(restypename); typename->name = pstrdup(restypename);
def->colname = pstrdup(resname); def->colname = pstrdup(resname);
def->typename = typename; def->typename = typename;
def->is_not_null = false; def->is_not_null = false;
def->defval = (char*) NULL; def->defval = (char *) NULL;
attrList = lappend(attrList, def); attrList = lappend(attrList, def);
}
}
else
{
elog(WARN, "attempted to define virtual relation with no attrs");
} }
} else {
elog ( WARN, "attempted to define virtual relation with no attrs");
}
/* /*
* now create the parametesr for keys/inheritance etc. * now create the parametesr for keys/inheritance etc. All of them are
* All of them are nil... * nil...
*/ */
createStmt.relname = relname; createStmt.relname = relname;
createStmt.tableElts = attrList; createStmt.tableElts = attrList;
/* createStmt.tableType = NULL;*/ /* createStmt.tableType = NULL;*/
createStmt.inhRelnames = NIL; createStmt.inhRelnames = NIL;
createStmt.archiveType = ARCH_NONE; createStmt.archiveType = ARCH_NONE;
createStmt.location = -1; createStmt.location = -1;
createStmt.archiveLoc = -1; createStmt.archiveLoc = -1;
createStmt.constraints = NIL; createStmt.constraints = NIL;
/* /*
* finally create the relation... * finally create the relation...
*/ */
DefineRelation(&createStmt); DefineRelation(&createStmt);
} }
/*------------------------------------------------------------------ /*------------------------------------------------------------------
@ -118,81 +123,84 @@ DefineVirtualRelation(char *relname, List *tlist)
* XXX it also means viewName cannot be 16 chars long! - ay 11/94 * XXX it also means viewName cannot be 16 chars long! - ay 11/94
*------------------------------------------------------------------ *------------------------------------------------------------------
*/ */
char * char *
MakeRetrieveViewRuleName(char *viewName) MakeRetrieveViewRuleName(char *viewName)
{ {
/* /*
char buf[100]; char buf[100];
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
sprintf(buf, "_RET%.*s", NAMEDATALEN, viewName->data); sprintf(buf, "_RET%.*s", NAMEDATALEN, viewName->data);
buf[15] = '\0'; buf[15] = '\0';
namestrcpy(rule_name, buf); namestrcpy(rule_name, buf);
*/ */
char *buf; char *buf;
buf = palloc(strlen(viewName) + 5);
sprintf(buf, "_RET%s",viewName); buf = palloc(strlen(viewName) + 5);
return buf; sprintf(buf, "_RET%s", viewName);
return buf;
} }
static RuleStmt * static RuleStmt *
FormViewRetrieveRule(char *viewName, Query *viewParse) FormViewRetrieveRule(char *viewName, Query * viewParse)
{ {
RuleStmt *rule; RuleStmt *rule;
char *rname; char *rname;
Attr *attr; Attr *attr;
/* /*
* Create a RuleStmt that corresponds to the suitable * Create a RuleStmt that corresponds to the suitable rewrite rule
* rewrite rule args for DefineQueryRewrite(); * args for DefineQueryRewrite();
*/ */
rule = makeNode(RuleStmt); rule = makeNode(RuleStmt);
rname = MakeRetrieveViewRuleName(viewName); rname = MakeRetrieveViewRuleName(viewName);
attr = makeNode(Attr); attr = makeNode(Attr);
attr->relname = pstrdup(viewName); attr->relname = pstrdup(viewName);
/* attr->refname = pstrdup(viewName);*/ /* attr->refname = pstrdup(viewName);*/
rule->rulename = pstrdup(rname); rule->rulename = pstrdup(rname);
rule->whereClause = NULL; rule->whereClause = NULL;
rule->event = CMD_SELECT; rule->event = CMD_SELECT;
rule->object = attr; rule->object = attr;
rule->instead = true; rule->instead = true;
rule->actions = lcons(viewParse, NIL); rule->actions = lcons(viewParse, NIL);
return rule; return rule;
} }
static void static void
DefineViewRules(char *viewName, Query *viewParse) DefineViewRules(char *viewName, Query * viewParse)
{ {
RuleStmt *retrieve_rule = NULL; RuleStmt *retrieve_rule = NULL;
#ifdef NOTYET
RuleStmt *replace_rule = NULL;
RuleStmt *append_rule = NULL;
RuleStmt *delete_rule = NULL;
#endif
retrieve_rule =
FormViewRetrieveRule(viewName, viewParse);
#ifdef NOTYET #ifdef NOTYET
RuleStmt *replace_rule = NULL;
replace_rule = RuleStmt *append_rule = NULL;
FormViewReplaceRule(viewName, viewParse); RuleStmt *delete_rule = NULL;
append_rule =
FormViewAppendRule(viewName, viewParse);
delete_rule =
FormViewDeleteRule(viewName, viewParse);
#endif #endif
DefineQueryRewrite(retrieve_rule); retrieve_rule =
FormViewRetrieveRule(viewName, viewParse);
#ifdef NOTYET #ifdef NOTYET
DefineQueryRewrite(replace_rule);
DefineQueryRewrite(append_rule); replace_rule =
DefineQueryRewrite(delete_rule); FormViewReplaceRule(viewName, viewParse);
append_rule =
FormViewAppendRule(viewName, viewParse);
delete_rule =
FormViewDeleteRule(viewName, viewParse);
#endif
DefineQueryRewrite(retrieve_rule);
#ifdef NOTYET
DefineQueryRewrite(replace_rule);
DefineQueryRewrite(append_rule);
DefineQueryRewrite(delete_rule);
#endif #endif
} }
@ -216,88 +224,84 @@ DefineViewRules(char *viewName, Query *viewParse)
*--------------------------------------------------------------- *---------------------------------------------------------------
*/ */
static void static void
UpdateRangeTableOfViewParse(char *viewName, Query *viewParse) UpdateRangeTableOfViewParse(char *viewName, Query * viewParse)
{ {
List *old_rt; List *old_rt;
List *new_rt; List *new_rt;
RangeTblEntry *rt_entry1, *rt_entry2; RangeTblEntry *rt_entry1,
*rt_entry2;
/* /*
* first offset all var nodes by 2 * first offset all var nodes by 2
*/ */
OffsetVarNodes((Node*)viewParse->targetList, 2); OffsetVarNodes((Node *) viewParse->targetList, 2);
OffsetVarNodes(viewParse->qual, 2); OffsetVarNodes(viewParse->qual, 2);
/* /*
* find the old range table... * find the old range table...
*/ */
old_rt = viewParse->rtable; old_rt = viewParse->rtable;
/* /*
* create the 2 new range table entries and form the new * create the 2 new range table entries and form the new range
* range table... * table... CURRENT first, then NEW....
* CURRENT first, then NEW.... */
*/ rt_entry1 =
rt_entry1 = addRangeTableEntry(NULL, (char *) viewName, "*CURRENT*",
addRangeTableEntry(NULL, (char*)viewName, "*CURRENT*", FALSE, FALSE, NULL);
FALSE, FALSE, NULL); rt_entry2 =
rt_entry2 = addRangeTableEntry(NULL, (char *) viewName, "*NEW*",
addRangeTableEntry(NULL, (char*)viewName, "*NEW*", FALSE, FALSE, NULL);
FALSE, FALSE, NULL); new_rt = lcons(rt_entry2, old_rt);
new_rt = lcons(rt_entry2, old_rt); new_rt = lcons(rt_entry1, new_rt);
new_rt = lcons(rt_entry1, new_rt);
/* /*
* Now the tricky part.... * Now the tricky part.... Update the range table in place... Be
* Update the range table in place... Be careful here, or * careful here, or hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE!
* hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE! */
*/ viewParse->rtable = new_rt;
viewParse->rtable = new_rt;
} }
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
* DefineView * DefineView
* *
* - takes a "viewname", "parsetree" pair and then * - takes a "viewname", "parsetree" pair and then
* 1) construct the "virtual" relation * 1) construct the "virtual" relation
* 2) commit the command but NOT the transaction, * 2) commit the command but NOT the transaction,
* so that the relation exists * so that the relation exists
* before the rules are defined. * before the rules are defined.
* 2) define the "n" rules specified in the PRS2 paper * 2) define the "n" rules specified in the PRS2 paper
* over the "virtual" relation * over the "virtual" relation
*------------------------------------------------------------------- *-------------------------------------------------------------------
*/ */
void void
DefineView(char *viewName, Query *viewParse) DefineView(char *viewName, Query * viewParse)
{ {
List *viewTlist; List *viewTlist;
viewTlist = viewParse->targetList; viewTlist = viewParse->targetList;
/* /*
* Create the "view" relation * Create the "view" relation NOTE: if it already exists, the xaxt
* NOTE: if it already exists, the xaxt will be aborted. * will be aborted.
*/ */
DefineVirtualRelation(viewName, viewTlist); DefineVirtualRelation(viewName, viewTlist);
/* /*
* The relation we have just created is not visible * The relation we have just created is not visible to any other
* to any other commands running with the same transaction & * commands running with the same transaction & command id. So,
* command id. * increment the command id counter (but do NOT pfree any memory!!!!)
* So, increment the command id counter (but do NOT pfree any */
* memory!!!!) CommandCounterIncrement();
*/
CommandCounterIncrement();
/* /*
* The range table of 'viewParse' does not contain entries * The range table of 'viewParse' does not contain entries for the
* for the "CURRENT" and "NEW" relations. * "CURRENT" and "NEW" relations. So... add them! NOTE: we make the
* So... add them! * update in place! After this call 'viewParse' will never be what it
* NOTE: we make the update in place! After this call 'viewParse' * used to be...
* will never be what it used to be... */
*/ UpdateRangeTableOfViewParse(viewName, viewParse);
UpdateRangeTableOfViewParse(viewName, viewParse); DefineViewRules(viewName, viewParse);
DefineViewRules(viewName, viewParse);
} }
/*------------------------------------------------------------------ /*------------------------------------------------------------------
@ -309,23 +313,22 @@ DefineView(char *viewName, Query *viewParse)
void void
RemoveView(char *viewName) RemoveView(char *viewName)
{ {
char* rname; char *rname;
/* /*
* first remove all the "view" rules... * first remove all the "view" rules... Currently we only have one!
* Currently we only have one! */
*/ rname = MakeRetrieveViewRuleName(viewName);
rname = MakeRetrieveViewRuleName(viewName); RemoveRewriteRule(rname);
RemoveRewriteRule(rname);
/* /*
* we don't really need that, but just in case... * we don't really need that, but just in case...
*/ */
CommandCounterIncrement(); CommandCounterIncrement();
/* /*
* now remove the relation. * now remove the relation.
*/ */
heap_destroy(viewName); heap_destroy(viewName);
pfree(rname); pfree(rname);
} }

View File

@ -1,32 +1,32 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* execAmi.c-- * execAmi.c--
* miscellanious executor access method routines * miscellanious executor access method routines
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.5 1997/08/19 21:30:51 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.6 1997/09/07 04:41:09 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
/* /*
* INTERFACE ROUTINES * INTERFACE ROUTINES
* *
* ExecOpenScanR \ / amopen * ExecOpenScanR \ / amopen
* ExecBeginScan \ / ambeginscan * ExecBeginScan \ / ambeginscan
* ExecCloseR \ / amclose * ExecCloseR \ / amclose
* ExecInsert \ executor interface / aminsert * ExecInsert \ executor interface / aminsert
* ExecReScanNode / to access methods \ amrescan * ExecReScanNode / to access methods \ amrescan
* ExecReScanR / \ amrescan * ExecReScanR / \ amrescan
* ExecMarkPos / \ ammarkpos * ExecMarkPos / \ ammarkpos
* ExecRestrPos / \ amrestpos * ExecRestrPos / \ amrestpos
* *
* ExecCreatR function to create temporary relations * ExecCreatR function to create temporary relations
* *
*/ */
#include <stdio.h> /* for sprintf() */ #include <stdio.h> /* for sprintf() */
#include "postgres.h" #include "postgres.h"
@ -43,409 +43,430 @@
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/heap.h" #include "catalog/heap.h"
static Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys, static Pointer
bool isindex, ScanDirection dir, TimeQual time_range); ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
bool isindex, ScanDirection dir, TimeQual time_range);
static Relation ExecOpenR(Oid relationOid, bool isindex); static Relation ExecOpenR(Oid relationOid, bool isindex);
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecOpenScanR * ExecOpenScanR
* *
* old comments: * old comments:
* Parameters: * Parameters:
* relation -- relation to be opened and scanned. * relation -- relation to be opened and scanned.
* nkeys -- number of keys * nkeys -- number of keys
* skeys -- keys to restrict scanning * skeys -- keys to restrict scanning
* isindex -- if this is true, the relation is the relid of * isindex -- if this is true, the relation is the relid of
* an index relation, else it is an index into the * an index relation, else it is an index into the
* range table. * range table.
* Returns the relation as(relDesc scanDesc) * Returns the relation as(relDesc scanDesc)
* If this structure is changed, need to modify the access macros * If this structure is changed, need to modify the access macros
* defined in execInt.h. * defined in execInt.h.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
ExecOpenScanR(Oid relOid, ExecOpenScanR(Oid relOid,
int nkeys, int nkeys,
ScanKey skeys, ScanKey skeys,
bool isindex, bool isindex,
ScanDirection dir, ScanDirection dir,
TimeQual timeRange, TimeQual timeRange,
Relation *returnRelation, /* return */ Relation * returnRelation, /* return */
Pointer *returnScanDesc) /* return */ Pointer * returnScanDesc) /* return */
{ {
Relation relation; Relation relation;
Pointer scanDesc; Pointer scanDesc;
/* ---------------- /* ----------------
* note: scanDesc returned by ExecBeginScan can be either * note: scanDesc returned by ExecBeginScan can be either
* a HeapScanDesc or an IndexScanDesc so for now we * a HeapScanDesc or an IndexScanDesc so for now we
* make it a Pointer. There should be a better scan * make it a Pointer. There should be a better scan
* abstraction someday -cim 9/9/89 * abstraction someday -cim 9/9/89
* ---------------- * ----------------
*/ */
relation = ExecOpenR(relOid, isindex); relation = ExecOpenR(relOid, isindex);
scanDesc = ExecBeginScan(relation, scanDesc = ExecBeginScan(relation,
nkeys, nkeys,
skeys, skeys,
isindex, isindex,
dir, dir,
timeRange); timeRange);
if (returnRelation != NULL) if (returnRelation != NULL)
*returnRelation = relation; *returnRelation = relation;
if (scanDesc != NULL) if (scanDesc != NULL)
*returnScanDesc = scanDesc; *returnScanDesc = scanDesc;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecOpenR * ExecOpenR
* *
* returns a relation descriptor given an object id. * returns a relation descriptor given an object id.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
static Relation static Relation
ExecOpenR(Oid relationOid, bool isindex) ExecOpenR(Oid relationOid, bool isindex)
{ {
Relation relation; Relation relation;
relation = (Relation) NULL;
/* ---------------- relation = (Relation) NULL;
* open the relation with the correct call depending
* on whether this is a heap relation or an index relation.
* ----------------
*/
if (isindex) {
relation = index_open(relationOid);
} else
relation = heap_open(relationOid);
if (relation == NULL) /* ----------------
elog(DEBUG, "ExecOpenR: relation == NULL, heap_open failed."); * open the relation with the correct call depending
* on whether this is a heap relation or an index relation.
* ----------------
*/
if (isindex)
{
relation = index_open(relationOid);
}
else
relation = heap_open(relationOid);
return relation; if (relation == NULL)
elog(DEBUG, "ExecOpenR: relation == NULL, heap_open failed.");
return relation;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecBeginScan * ExecBeginScan
* *
* beginscans a relation in current direction. * beginscans a relation in current direction.
* *
* XXX fix parameters to AMbeginscan (and btbeginscan) * XXX fix parameters to AMbeginscan (and btbeginscan)
* currently we need to pass a flag stating whether * currently we need to pass a flag stating whether
* or not the scan should begin at an endpoint of * or not the scan should begin at an endpoint of
* the relation.. Right now we always pass false * the relation.. Right now we always pass false
* -cim 9/14/89 * -cim 9/14/89
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
static Pointer static Pointer
ExecBeginScan(Relation relation, ExecBeginScan(Relation relation,
int nkeys, int nkeys,
ScanKey skeys, ScanKey skeys,
bool isindex, bool isindex,
ScanDirection dir, ScanDirection dir,
TimeQual time_range) TimeQual time_range)
{ {
Pointer scanDesc; Pointer scanDesc;
scanDesc = NULL; scanDesc = NULL;
/* ---------------- /* ----------------
* open the appropriate type of scan. * open the appropriate type of scan.
* *
* Note: ambeginscan()'s second arg is a boolean indicating * Note: ambeginscan()'s second arg is a boolean indicating
* that the scan should be done in reverse.. That is, * that the scan should be done in reverse.. That is,
* if you pass it true, then the scan is backward. * if you pass it true, then the scan is backward.
* ---------------- * ----------------
*/ */
if (isindex) { if (isindex)
scanDesc = (Pointer) index_beginscan(relation, {
false, /* see above comment */ scanDesc = (Pointer) index_beginscan(relation,
nkeys, false, /* see above comment */
skeys); nkeys,
} else { skeys);
scanDesc = (Pointer) heap_beginscan(relation, }
ScanDirectionIsBackward(dir), else
time_range, {
nkeys, scanDesc = (Pointer) heap_beginscan(relation,
skeys); ScanDirectionIsBackward(dir),
} time_range,
nkeys,
skeys);
}
if (scanDesc == NULL) if (scanDesc == NULL)
elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed."); elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed.");
return scanDesc; return scanDesc;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecCloseR * ExecCloseR
* *
* closes the relation and scan descriptor for a scan or sort * closes the relation and scan descriptor for a scan or sort
* node. Also closes index relations and scans for index scans. * node. Also closes index relations and scans for index scans.
* *
* old comments * old comments
* closes the relation indicated in 'relID' * closes the relation indicated in 'relID'
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
ExecCloseR(Plan *node) ExecCloseR(Plan * node)
{ {
CommonScanState *state; CommonScanState *state;
Relation relation; Relation relation;
HeapScanDesc scanDesc; HeapScanDesc scanDesc;
/* ---------------- /* ----------------
* shut down the heap scan and close the heap relation * shut down the heap scan and close the heap relation
* ---------------- * ----------------
*/
switch (nodeTag(node)) {
case T_SeqScan:
state = ((SeqScan *)node)->scanstate;
break;
case T_IndexScan:
state = ((IndexScan *)node)->scan.scanstate;
break;
case T_Material:
state = &(((Material *)node)->matstate->csstate);
break;
case T_Sort:
state = &(((Sort *)node)->sortstate->csstate);
break;
case T_Agg:
state = &(((Agg *)node)->aggstate->csstate);
break;
default:
elog(DEBUG, "ExecCloseR: not a scan, material, or sort node!");
return;
}
relation = state->css_currentRelation;
scanDesc = state->css_currentScanDesc;
if (scanDesc != NULL)
heap_endscan(scanDesc);
if (relation != NULL)
heap_close(relation);
/* ----------------
* if this is an index scan then we have to take care
* of the index relations as well..
* ----------------
*/
if (nodeTag(node) == T_IndexScan) {
IndexScan *iscan= (IndexScan *)node;
IndexScanState *indexstate;
int numIndices;
RelationPtr indexRelationDescs;
IndexScanDescPtr indexScanDescs;
int i;
indexstate = iscan->indxstate;
numIndices = indexstate->iss_NumIndices;
indexRelationDescs = indexstate->iss_RelationDescs;
indexScanDescs = indexstate->iss_ScanDescs;
for (i = 0; i<numIndices; i++) {
/* ----------------
* shut down each of the scans and
* close each of the index relations
* ----------------
*/
if (indexScanDescs[i] != NULL)
index_endscan(indexScanDescs[i]);
if (indexRelationDescs[i] != NULL)
index_close(indexRelationDescs[i]);
}
}
}
/* ----------------------------------------------------------------
* ExecReScan
*
* XXX this should be extended to cope with all the node types..
*
* takes the new expression context as an argument, so that
* index scans needn't have their scan keys updated separately
* - marcel 09/20/94
* ----------------------------------------------------------------
*/
void
ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
{
switch(nodeTag(node)) {
case T_SeqScan:
ExecSeqReScan((SeqScan *) node, exprCtxt, parent);
return;
case T_IndexScan:
ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
return;
case T_Material:
/* the first call to ExecReScan should have no effect because
* everything is initialized properly already. the following
* calls will be handled by ExecSeqReScan() because the nodes
* below the Material node have already been materialized into
* a temp relation.
*/ */
return; switch (nodeTag(node))
{
case T_Tee: case T_SeqScan:
ExecTeeReScan((Tee*) node, exprCtxt, parent); state = ((SeqScan *) node)->scanstate;
break; break;
default: case T_IndexScan:
elog(WARN, "ExecReScan: not a seqscan or indexscan node."); state = ((IndexScan *) node)->scan.scanstate;
return; break;
}
case T_Material:
state = &(((Material *) node)->matstate->csstate);
break;
case T_Sort:
state = &(((Sort *) node)->sortstate->csstate);
break;
case T_Agg:
state = &(((Agg *) node)->aggstate->csstate);
break;
default:
elog(DEBUG, "ExecCloseR: not a scan, material, or sort node!");
return;
}
relation = state->css_currentRelation;
scanDesc = state->css_currentScanDesc;
if (scanDesc != NULL)
heap_endscan(scanDesc);
if (relation != NULL)
heap_close(relation);
/* ----------------
* if this is an index scan then we have to take care
* of the index relations as well..
* ----------------
*/
if (nodeTag(node) == T_IndexScan)
{
IndexScan *iscan = (IndexScan *) node;
IndexScanState *indexstate;
int numIndices;
RelationPtr indexRelationDescs;
IndexScanDescPtr indexScanDescs;
int i;
indexstate = iscan->indxstate;
numIndices = indexstate->iss_NumIndices;
indexRelationDescs = indexstate->iss_RelationDescs;
indexScanDescs = indexstate->iss_ScanDescs;
for (i = 0; i < numIndices; i++)
{
/* ----------------
* shut down each of the scans and
* close each of the index relations
* ----------------
*/
if (indexScanDescs[i] != NULL)
index_endscan(indexScanDescs[i]);
if (indexRelationDescs[i] != NULL)
index_close(indexRelationDescs[i]);
}
}
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecReScanR * ExecReScan
* *
* XXX this does not do the right thing with indices yet. * XXX this should be extended to cope with all the node types..
*
* takes the new expression context as an argument, so that
* index scans needn't have their scan keys updated separately
* - marcel 09/20/94
* ----------------------------------------------------------------
*/
void
ExecReScan(Plan * node, ExprContext * exprCtxt, Plan * parent)
{
switch (nodeTag(node))
{
case T_SeqScan:
ExecSeqReScan((SeqScan *) node, exprCtxt, parent);
return;
case T_IndexScan:
ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
return;
case T_Material:
/*
* the first call to ExecReScan should have no effect because
* everything is initialized properly already. the following
* calls will be handled by ExecSeqReScan() because the nodes
* below the Material node have already been materialized into a
* temp relation.
*/
return;
case T_Tee:
ExecTeeReScan((Tee *) node, exprCtxt, parent);
break;
default:
elog(WARN, "ExecReScan: not a seqscan or indexscan node.");
return;
}
}
/* ----------------------------------------------------------------
* ExecReScanR
*
* XXX this does not do the right thing with indices yet.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
HeapScanDesc HeapScanDesc
ExecReScanR(Relation relDesc, /* LLL relDesc unused */ ExecReScanR(Relation relDesc, /* LLL relDesc unused */
HeapScanDesc scanDesc, HeapScanDesc scanDesc,
ScanDirection direction, ScanDirection direction,
int nkeys, /* LLL nkeys unused */ int nkeys, /* LLL nkeys unused */
ScanKey skeys) ScanKey skeys)
{ {
if (scanDesc != NULL) if (scanDesc != NULL)
heap_rescan(scanDesc, /* scan desc */ heap_rescan(scanDesc, /* scan desc */
ScanDirectionIsBackward(direction), /* backward flag */ ScanDirectionIsBackward(direction), /* backward flag */
skeys); /* scan keys */ skeys); /* scan keys */
return scanDesc; return scanDesc;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecMarkPos * ExecMarkPos
* *
* Marks the current scan position. * Marks the current scan position.
* *
* XXX Needs to be extended to include all the node types. * XXX Needs to be extended to include all the node types.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
ExecMarkPos(Plan *node) ExecMarkPos(Plan * node)
{ {
switch(nodeTag(node)) { switch (nodeTag(node))
case T_SeqScan: {
ExecSeqMarkPos((SeqScan *) node); case T_SeqScan:
break; ExecSeqMarkPos((SeqScan *) node);
break;
case T_IndexScan: case T_IndexScan:
ExecIndexMarkPos((IndexScan *) node); ExecIndexMarkPos((IndexScan *) node);
break; break;
case T_Sort: case T_Sort:
ExecSortMarkPos((Sort *) node); ExecSortMarkPos((Sort *) node);
break; break;
default: default:
/* elog(DEBUG, "ExecMarkPos: unsupported node type"); */ /* elog(DEBUG, "ExecMarkPos: unsupported node type"); */
break; break;
} }
return; return;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecRestrPos * ExecRestrPos
* *
* restores the scan position previously saved with ExecMarkPos() * restores the scan position previously saved with ExecMarkPos()
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
ExecRestrPos(Plan *node) ExecRestrPos(Plan * node)
{ {
switch(nodeTag(node)) { switch (nodeTag(node))
case T_SeqScan: {
ExecSeqRestrPos((SeqScan *) node); case T_SeqScan:
return; ExecSeqRestrPos((SeqScan *) node);
return;
case T_IndexScan: case T_IndexScan:
ExecIndexRestrPos((IndexScan *) node); ExecIndexRestrPos((IndexScan *) node);
return; return;
case T_Sort: case T_Sort:
ExecSortRestrPos((Sort *) node); ExecSortRestrPos((Sort *) node);
return; return;
default: default:
/* elog(DEBUG, "ExecRestrPos: node type not supported"); */ /* elog(DEBUG, "ExecRestrPos: node type not supported"); */
return; return;
} }
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecCreatR * ExecCreatR
* *
* old comments * old comments
* Creates a relation. * Creates a relation.
* *
* Parameters: * Parameters:
* attrType -- type information on the attributes. * attrType -- type information on the attributes.
* accessMtd -- access methods used to access the created relation. * accessMtd -- access methods used to access the created relation.
* relation -- optional. Either an index to the range table or * relation -- optional. Either an index to the range table or
* negative number indicating a temporary relation. * negative number indicating a temporary relation.
* A temporary relation is assume is this field is absent. * A temporary relation is assume is this field is absent.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
Relation Relation
ExecCreatR(TupleDesc tupType, ExecCreatR(TupleDesc tupType,
Oid relationOid) Oid relationOid)
{ {
Relation relDesc; Relation relDesc;
EU3_printf("ExecCreatR: %s type=%d oid=%d\n", EU3_printf("ExecCreatR: %s type=%d oid=%d\n",
"entering: ", tupType, relationOid); "entering: ", tupType, relationOid);
CXT1_printf("ExecCreatR: context is %d\n", CurrentMemoryContext); CXT1_printf("ExecCreatR: context is %d\n", CurrentMemoryContext);
relDesc = NULL; relDesc = NULL;
if (relationOid == _TEMP_RELATION_ID_ ) { if (relationOid == _TEMP_RELATION_ID_)
/* ---------------- {
* create a temporary relation /* ----------------
* (currently the planner always puts a _TEMP_RELATION_ID * create a temporary relation
* in the relation argument so we expect this to be the case although * (currently the planner always puts a _TEMP_RELATION_ID
* it's possible that someday we'll get the name from * in the relation argument so we expect this to be the case although
* from the range table.. -cim 10/12/89) * it's possible that someday we'll get the name from
* ---------------- * from the range table.. -cim 10/12/89)
*/ * ----------------
*/
/* /*
sprintf(tempname, "temp_%d.%d", getpid(), tmpcnt++); sprintf(tempname, "temp_%d.%d", getpid(), tmpcnt++);
EU1_printf("ExecCreatR: attempting to create %s\n", tempname); EU1_printf("ExecCreatR: attempting to create %s\n", tempname);
*/ */
/* heap_creatr creates a name if the argument to heap_creatr is '\0 ' */
relDesc = heap_creatr("",
DEFAULT_SMGR,
tupType);
} else {
/* ----------------
* use a relation from the range table
* ----------------
*/
elog(DEBUG, "ExecCreatR: %s",
"stuff using range table id's is not functional");
}
if (relDesc == NULL) /*
elog(DEBUG, "ExecCreatR: failed to create relation."); * heap_creatr creates a name if the argument to heap_creatr is
* '\0 '
*/
relDesc = heap_creatr("",
DEFAULT_SMGR,
tupType);
}
else
{
/* ----------------
* use a relation from the range table
* ----------------
*/
elog(DEBUG, "ExecCreatR: %s",
"stuff using range table id's is not functional");
}
EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc); if (relDesc == NULL)
elog(DEBUG, "ExecCreatR: failed to create relation.");
return relDesc; EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc);
return relDesc;
} }

View File

@ -1,29 +1,29 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* execFlatten.c-- * execFlatten.c--
* This file handles the nodes associated with flattening sets in the * This file handles the nodes associated with flattening sets in the
* target list of queries containing functions returning sets. * target list of queries containing functions returning sets.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.2 1997/08/19 21:30:56 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.3 1997/09/07 04:41:12 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
/* /*
* ExecEvalIter() - * ExecEvalIter() -
* Iterate through the all return tuples/base types from a function one * Iterate through the all return tuples/base types from a function one
* at time (i.e. one per ExecEvalIter call). Not really needed for * at time (i.e. one per ExecEvalIter call). Not really needed for
* postquel functions, but for reasons of orthogonality, these nodes * postquel functions, but for reasons of orthogonality, these nodes
* exist above pq functions as well as c functions. * exist above pq functions as well as c functions.
* *
* ExecEvalFjoin() - * ExecEvalFjoin() -
* Given N Iter nodes return a vector of all combinations of results * Given N Iter nodes return a vector of all combinations of results
* one at a time (i.e. one result vector per ExecEvalFjoin call). This * one at a time (i.e. one result vector per ExecEvalFjoin call). This
* node does the actual flattening work. * node does the actual flattening work.
*/ */
#include "postgres.h" #include "postgres.h"
#include "nodes/primnodes.h" #include "nodes/primnodes.h"
@ -33,208 +33,216 @@
#include "executor/execFlatten.h" #include "executor/execFlatten.h"
#ifdef SETS_FIXED #ifdef SETS_FIXED
static bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext, static bool
DatumPtr results, char *nulls); FjoinBumpOuterNodes(TargetEntry * tlist, ExprContext * econtext,
DatumPtr results, char *nulls);
#endif #endif
Datum Datum
ExecEvalIter(Iter *iterNode, ExecEvalIter(Iter * iterNode,
ExprContext *econtext, ExprContext * econtext,
bool *resultIsNull, bool * resultIsNull,
bool *iterIsDone) bool * iterIsDone)
{ {
Node *expression; Node *expression;
expression = iterNode->iterexpr; expression = iterNode->iterexpr;
/* /*
* Really Iter nodes are only needed for C functions, postquel function * Really Iter nodes are only needed for C functions, postquel
* by their nature return 1 result at a time. For now we are only worrying * function by their nature return 1 result at a time. For now we are
* about postquel functions, c functions will come later. * only worrying about postquel functions, c functions will come
*/ * later.
return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone); */
return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone);
} }
void void
ExecEvalFjoin(TargetEntry *tlist, ExecEvalFjoin(TargetEntry * tlist,
ExprContext *econtext, ExprContext * econtext,
bool *isNullVect, bool * isNullVect,
bool *fj_isDone) bool * fj_isDone)
{ {
#ifdef SETS_FIXED #ifdef SETS_FIXED
bool isDone; bool isDone;
int curNode; int curNode;
List *tlistP; List *tlistP;
Fjoin *fjNode = tlist->fjoin; Fjoin *fjNode = tlist->fjoin;
DatumPtr resVect = fjNode->fj_results; DatumPtr resVect = fjNode->fj_results;
BoolPtr alwaysDone = fjNode->fj_alwaysDone; BoolPtr alwaysDone = fjNode->fj_alwaysDone;
if (fj_isDone) *fj_isDone = false; if (fj_isDone)
/* *fj_isDone = false;
* For the next tuple produced by the plan, we need to re-initialize
* the Fjoin node. /*
*/ * For the next tuple produced by the plan, we need to re-initialize
if (!fjNode->fj_initialized) * the Fjoin node.
*/
if (!fjNode->fj_initialized)
{ {
/*
* Initialize all of the Outer nodes /*
*/ * Initialize all of the Outer nodes
curNode = 1; */
foreach(tlistP, lnext(tlist)) curNode = 1;
foreach(tlistP, lnext(tlist))
{ {
TargetEntry *tle = lfirst(tlistP); TargetEntry *tle = lfirst(tlistP);
resVect[curNode] = ExecEvalIter((Iter*)tle->expr, resVect[curNode] = ExecEvalIter((Iter *) tle->expr,
econtext, econtext,
&isNullVect[curNode], &isNullVect[curNode],
&isDone); &isDone);
if (isDone) if (isDone)
isNullVect[curNode] = alwaysDone[curNode] = true; isNullVect[curNode] = alwaysDone[curNode] = true;
else else
alwaysDone[curNode] = false; alwaysDone[curNode] = false;
curNode++; curNode++;
} }
/* /*
* Initialize the inner node * Initialize the inner node
*/ */
resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr, resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
econtext, econtext,
&isNullVect[0], &isNullVect[0],
&isDone); &isDone);
if (isDone) if (isDone)
isNullVect[0] = alwaysDone[0] = true; isNullVect[0] = alwaysDone[0] = true;
else else
alwaysDone[0] = false; alwaysDone[0] = false;
/* /*
* Mark the Fjoin as initialized now. * Mark the Fjoin as initialized now.
*/ */
fjNode->fj_initialized = TRUE; fjNode->fj_initialized = TRUE;
/* /*
* If the inner node is always done, then we are done for now * If the inner node is always done, then we are done for now
*/ */
if (isDone) if (isDone)
return; return;
} }
else else
{ {
/*
* If we're already initialized, all we need to do is get the
* next inner result and pair it up with the existing outer node
* result vector. Watch out for the degenerate case, where the
* inner node never returns results.
*/
/* /*
* Fill in nulls for every function that is always done. * If we're already initialized, all we need to do is get the next
*/ * inner result and pair it up with the existing outer node result
for (curNode=fjNode->fj_nNodes-1; curNode >= 0; curNode--) * vector. Watch out for the degenerate case, where the inner
isNullVect[curNode] = alwaysDone[curNode]; * node never returns results.
*/
if (alwaysDone[0] == true) /*
* Fill in nulls for every function that is always done.
*/
for (curNode = fjNode->fj_nNodes - 1; curNode >= 0; curNode--)
isNullVect[curNode] = alwaysDone[curNode];
if (alwaysDone[0] == true)
{ {
*fj_isDone = FjoinBumpOuterNodes(tlist, *fj_isDone = FjoinBumpOuterNodes(tlist,
econtext, econtext,
resVect, resVect,
isNullVect); isNullVect);
return; return;
} }
else else
resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr, resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
econtext, econtext,
&isNullVect[0], &isNullVect[0],
&isDone); &isDone);
} }
/* /*
* if the inner node is done * if the inner node is done
*/ */
if (isDone) if (isDone)
{ {
*fj_isDone = FjoinBumpOuterNodes(tlist, *fj_isDone = FjoinBumpOuterNodes(tlist,
econtext, econtext,
resVect, resVect,
isNullVect); isNullVect);
if (*fj_isDone) if (*fj_isDone)
return; return;
resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr, resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
econtext, econtext,
&isNullVect[0], &isNullVect[0],
&isDone); &isDone);
} }
#endif #endif
return; return;
} }
#ifdef SETS_FIXED #ifdef SETS_FIXED
static bool static bool
FjoinBumpOuterNodes(TargetEntry *tlist, FjoinBumpOuterNodes(TargetEntry * tlist,
ExprContext *econtext, ExprContext * econtext,
DatumPtr results, DatumPtr results,
char *nulls) char *nulls)
{ {
bool funcIsDone = true; bool funcIsDone = true;
Fjoin *fjNode = tlist->fjoin; Fjoin *fjNode = tlist->fjoin;
char *alwaysDone = fjNode->fj_alwaysDone; char *alwaysDone = fjNode->fj_alwaysDone;
List *outerList = lnext(tlist); List *outerList = lnext(tlist);
List *trailers = lnext(tlist); List *trailers = lnext(tlist);
int trailNode = 1; int trailNode = 1;
int curNode = 1; int curNode = 1;
/* /*
* Run through list of functions until we get to one that isn't yet * Run through list of functions until we get to one that isn't yet
* done returning values. Watch out for funcs that are always done. * done returning values. Watch out for funcs that are always done.
*/ */
while ((funcIsDone == true) && (outerList != NIL)) while ((funcIsDone == true) && (outerList != NIL))
{ {
TargetEntry *tle = lfirst(outerList); TargetEntry *tle = lfirst(outerList);
if (alwaysDone[curNode] == true) if (alwaysDone[curNode] == true)
nulls[curNode] = 'n'; nulls[curNode] = 'n';
else else
results[curNode] = ExecEvalIter((Iter)tle->expr, results[curNode] = ExecEvalIter((Iter) tle->expr,
econtext, econtext,
&nulls[curNode], &nulls[curNode],
&funcIsDone); &funcIsDone);
curNode++; curNode++;
outerList = lnext(outerList); outerList = lnext(outerList);
} }
/* /*
* If every function is done, then we are done flattening. * If every function is done, then we are done flattening. Mark the
* Mark the Fjoin node unitialized, it is time to get the * Fjoin node unitialized, it is time to get the next tuple from the
* next tuple from the plan and redo all of the flattening. * plan and redo all of the flattening.
*/ */
if (funcIsDone) if (funcIsDone)
{ {
set_fj_initialized(fjNode, false); set_fj_initialized(fjNode, false);
return (true); return (true);
} }
/* /*
* We found a function that wasn't done. Now re-run every function * We found a function that wasn't done. Now re-run every function
* before it. As usual watch out for functions that are always done. * before it. As usual watch out for functions that are always done.
*/ */
trailNode = 1; trailNode = 1;
while (trailNode != curNode-1) while (trailNode != curNode - 1)
{ {
TargetEntry *tle = lfirst(trailers); TargetEntry *tle = lfirst(trailers);
if (alwaysDone[trailNode] != true) if (alwaysDone[trailNode] != true)
results[trailNode] = ExecEvalIter((Iter)tle->expr, results[trailNode] = ExecEvalIter((Iter) tle->expr,
econtext, econtext,
&nulls[trailNode], &nulls[trailNode],
&funcIsDone); &funcIsDone);
trailNode++; trailNode++;
trailers = lnext(trailers); trailers = lnext(trailers);
} }
return false; return false;
} }
#endif #endif

View File

@ -1,13 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* junk.c-- * junk.c--
* Junk attribute support stuff.... * Junk attribute support stuff....
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.5 1997/08/26 23:31:37 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.6 1997/09/07 04:41:14 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -20,12 +20,12 @@
#include "access/heapam.h" #include "access/heapam.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "nodes/relation.h" #include "nodes/relation.h"
#include "optimizer/tlist.h" /* for MakeTLE */ #include "optimizer/tlist.h" /* for MakeTLE */
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* XXX this stuff should be rewritten to take advantage * XXX this stuff should be rewritten to take advantage
* of ExecProject() and the ProjectionInfo node. * of ExecProject() and the ProjectionInfo node.
* -cim 6/3/91 * -cim 6/3/91
* *
* An attribute of a tuple living inside the executor, can be * An attribute of a tuple living inside the executor, can be
* either a normal attribute or a "junk" attribute. "junk" attributes * either a normal attribute or a "junk" attribute. "junk" attributes
@ -60,173 +60,195 @@
* Initialize the Junk filter. * Initialize the Junk filter.
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
JunkFilter * JunkFilter *
ExecInitJunkFilter(List *targetList) ExecInitJunkFilter(List * targetList)
{ {
JunkFilter *junkfilter; JunkFilter *junkfilter;
List *cleanTargetList; List *cleanTargetList;
int len, cleanLength; int len,
TupleDesc tupType, cleanTupType; cleanLength;
List *t; TupleDesc tupType,
TargetEntry *tle; cleanTupType;
Resdom *resdom, *cleanResdom; List *t;
int resjunk; TargetEntry *tle;
AttrNumber cleanResno; Resdom *resdom,
AttrNumber *cleanMap; *cleanResdom;
Size size; int resjunk;
Node *expr; AttrNumber cleanResno;
AttrNumber *cleanMap;
Size size;
Node *expr;
/* --------------------- /* ---------------------
* First find the "clean" target list, i.e. all the entries * First find the "clean" target list, i.e. all the entries
* in the original target list which have a zero 'resjunk' * in the original target list which have a zero 'resjunk'
* NOTE: make copy of the Resdom nodes, because we have * NOTE: make copy of the Resdom nodes, because we have
* to change the 'resno's... * to change the 'resno's...
* --------------------- * ---------------------
*/ */
cleanTargetList = NIL; cleanTargetList = NIL;
cleanResno = 1;
foreach (t, targetList) {
TargetEntry *rtarget = lfirst(t);
if (rtarget->resdom != NULL) {
resdom = rtarget->resdom;
expr = rtarget->expr;
resjunk = resdom->resjunk;
if (resjunk == 0) {
/*
* make a copy of the resdom node, changing its resno.
*/
cleanResdom = (Resdom *) copyObject(resdom);
cleanResdom->resno = cleanResno;
cleanResno ++;
/*
* create a new target list entry
*/
tle = makeNode(TargetEntry);
tle->resdom = cleanResdom;
tle->expr = expr;
cleanTargetList = lappend(cleanTargetList, tle);
}
}
else {
#ifdef SETS_FIXED
List *fjListP;
Fjoin *cleanFjoin;
List *cleanFjList;
List *fjList = lfirst(t);
Fjoin *fjNode = (Fjoin *)tl_node(fjList);
cleanFjoin = (Fjoin)copyObject((Node) fjNode);
cleanFjList = lcons(cleanFjoin, NIL);
resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
expr = lsecond(get_fj_innerNode(fjNode));
cleanResdom = (Resdom) copyObject((Node) resdom);
set_resno(cleanResdom, cleanResno);
cleanResno++;
tle = (List) MakeTLE(cleanResdom, (Expr) expr);
set_fj_innerNode(cleanFjoin, tle);
foreach(fjListP, lnext(fjList)) {
TargetEntry *tle = lfirst(fjListP);
resdom = tle->resdom;
expr = tle->expr;
cleanResdom = (Resdom*) copyObject((Node) resdom);
cleanResno++;
cleanResdom->Resno = cleanResno;
/*
* create a new target list entry
*/
tle = (List) MakeTLE(cleanResdom, (Expr) expr);
cleanFjList = lappend(cleanFjList, tle);
}
lappend(cleanTargetList, cleanFjList);
#endif
}
}
/* ---------------------
* Now calculate the tuple types for the original and the clean tuple
*
* XXX ExecTypeFromTL should be used sparingly. Don't we already
* have the tupType corresponding to the targetlist we are passed?
* -cim 5/31/91
* ---------------------
*/
tupType = (TupleDesc) ExecTypeFromTL(targetList);
cleanTupType = (TupleDesc) ExecTypeFromTL(cleanTargetList);
len = ExecTargetListLength(targetList);
cleanLength = ExecTargetListLength(cleanTargetList);
/* ---------------------
* Now calculate the "map" between the original tuples attributes
* and the "clean" tuple's attributes.
*
* The "map" is an array of "cleanLength" attribute numbers, i.e.
* one entry for every attribute of the "clean" tuple.
* The value of this entry is the attribute number of the corresponding
* attribute of the "original" tuple.
* ---------------------
*/
if (cleanLength > 0) {
size = cleanLength * sizeof(AttrNumber);
cleanMap = (AttrNumber*) palloc(size);
cleanResno = 1; cleanResno = 1;
foreach (t, targetList) {
TargetEntry *tle = lfirst(t); foreach(t, targetList)
if (tle->resdom != NULL) { {
resdom = tle->resdom; TargetEntry *rtarget = lfirst(t);
expr = tle->expr;
resjunk = resdom->resjunk; if (rtarget->resdom != NULL)
if (resjunk == 0) { {
cleanMap[cleanResno-1] = resdom->resno; resdom = rtarget->resdom;
cleanResno ++; expr = rtarget->expr;
resjunk = resdom->resjunk;
if (resjunk == 0)
{
/*
* make a copy of the resdom node, changing its resno.
*/
cleanResdom = (Resdom *) copyObject(resdom);
cleanResdom->resno = cleanResno;
cleanResno++;
/*
* create a new target list entry
*/
tle = makeNode(TargetEntry);
tle->resdom = cleanResdom;
tle->expr = expr;
cleanTargetList = lappend(cleanTargetList, tle);
}
} }
} else { else
{
#ifdef SETS_FIXED #ifdef SETS_FIXED
List fjListP; List *fjListP;
List fjList = lfirst(t); Fjoin *cleanFjoin;
Fjoin fjNode = (Fjoin)lfirst(fjList); List *cleanFjList;
List *fjList = lfirst(t);
Fjoin *fjNode = (Fjoin *) tl_node(fjList);
/* what the hell is this????? */ cleanFjoin = (Fjoin) copyObject((Node) fjNode);
resdom = (Resdom) lfirst(get_fj_innerNode(fjNode)); cleanFjList = lcons(cleanFjoin, NIL);
resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
expr = lsecond(get_fj_innerNode(fjNode));
cleanResdom = (Resdom) copyObject((Node) resdom);
set_resno(cleanResdom, cleanResno);
cleanResno++;
tle = (List) MakeTLE(cleanResdom, (Expr) expr);
set_fj_innerNode(cleanFjoin, tle);
foreach(fjListP, lnext(fjList))
{
TargetEntry *tle = lfirst(fjListP);
resdom = tle->resdom;
expr = tle->expr;
cleanResdom = (Resdom *) copyObject((Node) resdom);
cleanResno++;
cleanResdom->Resno = cleanResno;
/*
* create a new target list entry
*/
tle = (List) MakeTLE(cleanResdom, (Expr) expr);
cleanFjList = lappend(cleanFjList, tle);
}
lappend(cleanTargetList, cleanFjList);
#endif #endif
cleanMap[cleanResno-1] = tle->resdom->resno;
cleanResno++;
#ifdef SETS_FIXED
foreach(fjListP, lnext(fjList)) {
TargetEntry *tle = lfirst(fjListP);
resdom = tle->resdom;
cleanMap[cleanResno-1] = resdom->resno;
cleanResno++;
} }
#endif
}
} }
} else {
cleanMap = NULL;
}
/* --------------------- /* ---------------------
* Finally create and initialize the JunkFilter. * Now calculate the tuple types for the original and the clean tuple
* --------------------- *
*/ * XXX ExecTypeFromTL should be used sparingly. Don't we already
junkfilter = makeNode(JunkFilter); * have the tupType corresponding to the targetlist we are passed?
* -cim 5/31/91
* ---------------------
*/
tupType = (TupleDesc) ExecTypeFromTL(targetList);
cleanTupType = (TupleDesc) ExecTypeFromTL(cleanTargetList);
junkfilter->jf_targetList = targetList; len = ExecTargetListLength(targetList);
junkfilter->jf_length = len; cleanLength = ExecTargetListLength(cleanTargetList);
junkfilter->jf_tupType = tupType;
junkfilter->jf_cleanTargetList = cleanTargetList;
junkfilter->jf_cleanLength = cleanLength;
junkfilter->jf_cleanTupType = cleanTupType;
junkfilter->jf_cleanMap = cleanMap;
return(junkfilter); /* ---------------------
* Now calculate the "map" between the original tuples attributes
* and the "clean" tuple's attributes.
*
* The "map" is an array of "cleanLength" attribute numbers, i.e.
* one entry for every attribute of the "clean" tuple.
* The value of this entry is the attribute number of the corresponding
* attribute of the "original" tuple.
* ---------------------
*/
if (cleanLength > 0)
{
size = cleanLength * sizeof(AttrNumber);
cleanMap = (AttrNumber *) palloc(size);
cleanResno = 1;
foreach(t, targetList)
{
TargetEntry *tle = lfirst(t);
if (tle->resdom != NULL)
{
resdom = tle->resdom;
expr = tle->expr;
resjunk = resdom->resjunk;
if (resjunk == 0)
{
cleanMap[cleanResno - 1] = resdom->resno;
cleanResno++;
}
}
else
{
#ifdef SETS_FIXED
List fjListP;
List fjList = lfirst(t);
Fjoin fjNode = (Fjoin) lfirst(fjList);
/* what the hell is this????? */
resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
#endif
cleanMap[cleanResno - 1] = tle->resdom->resno;
cleanResno++;
#ifdef SETS_FIXED
foreach(fjListP, lnext(fjList))
{
TargetEntry *tle = lfirst(fjListP);
resdom = tle->resdom;
cleanMap[cleanResno - 1] = resdom->resno;
cleanResno++;
}
#endif
}
}
}
else
{
cleanMap = NULL;
}
/* ---------------------
* Finally create and initialize the JunkFilter.
* ---------------------
*/
junkfilter = makeNode(JunkFilter);
junkfilter->jf_targetList = targetList;
junkfilter->jf_length = len;
junkfilter->jf_tupType = tupType;
junkfilter->jf_cleanTargetList = cleanTargetList;
junkfilter->jf_cleanLength = cleanLength;
junkfilter->jf_cleanTupType = cleanTupType;
junkfilter->jf_cleanMap = cleanMap;
return (junkfilter);
} }
@ -242,57 +264,61 @@ ExecInitJunkFilter(List *targetList)
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
bool bool
ExecGetJunkAttribute(JunkFilter *junkfilter, ExecGetJunkAttribute(JunkFilter * junkfilter,
TupleTableSlot *slot, TupleTableSlot * slot,
char *attrName, char *attrName,
Datum *value, Datum * value,
bool *isNull) bool * isNull)
{ {
List *targetList; List *targetList;
List *t; List *t;
Resdom *resdom; Resdom *resdom;
AttrNumber resno; AttrNumber resno;
char *resname; char *resname;
int resjunk; int resjunk;
TupleDesc tupType; TupleDesc tupType;
HeapTuple tuple; HeapTuple tuple;
/* --------------------- /* ---------------------
* first look in the junkfilter's target list for * first look in the junkfilter's target list for
* an attribute with the given name * an attribute with the given name
* --------------------- * ---------------------
*/ */
resno = InvalidAttrNumber; resno = InvalidAttrNumber;
targetList = junkfilter->jf_targetList; targetList = junkfilter->jf_targetList;
foreach (t, targetList) { foreach(t, targetList)
TargetEntry *tle = lfirst(t); {
resdom = tle->resdom; TargetEntry *tle = lfirst(t);
resname = resdom->resname;
resjunk = resdom->resjunk; resdom = tle->resdom;
if (resjunk != 0 && (strcmp(resname, attrName) == 0)) { resname = resdom->resname;
/* We found it ! */ resjunk = resdom->resjunk;
resno = resdom->resno; if (resjunk != 0 && (strcmp(resname, attrName) == 0))
break; {
/* We found it ! */
resno = resdom->resno;
break;
}
} }
}
if (resno == InvalidAttrNumber) { if (resno == InvalidAttrNumber)
/* Ooops! We couldn't find this attribute... */ {
return(false); /* Ooops! We couldn't find this attribute... */
} return (false);
}
/* --------------------- /* ---------------------
* Now extract the attribute value from the tuple. * Now extract the attribute value from the tuple.
* --------------------- * ---------------------
*/ */
tuple = slot->val; tuple = slot->val;
tupType = (TupleDesc) junkfilter->jf_tupType; tupType = (TupleDesc) junkfilter->jf_tupType;
*value = (Datum) *value = (Datum)
heap_getattr(tuple, InvalidBuffer, resno, tupType, isNull); heap_getattr(tuple, InvalidBuffer, resno, tupType, isNull);
return true; return true;
} }
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
@ -302,94 +328,98 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
HeapTuple HeapTuple
ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot) ExecRemoveJunk(JunkFilter * junkfilter, TupleTableSlot * slot)
{ {
HeapTuple tuple; HeapTuple tuple;
HeapTuple cleanTuple; HeapTuple cleanTuple;
AttrNumber *cleanMap; AttrNumber *cleanMap;
TupleDesc cleanTupType; TupleDesc cleanTupType;
TupleDesc tupType; TupleDesc tupType;
int cleanLength; int cleanLength;
bool isNull; bool isNull;
int i; int i;
Size size; Size size;
Datum *values; Datum *values;
char *nulls; char *nulls;
Datum values_array[64]; Datum values_array[64];
char nulls_array[64]; char nulls_array[64];
/* ---------------- /* ----------------
* get info from the slot and the junk filter * get info from the slot and the junk filter
* ---------------- * ----------------
*/ */
tuple = slot->val; tuple = slot->val;
tupType = (TupleDesc) junkfilter->jf_tupType; tupType = (TupleDesc) junkfilter->jf_tupType;
cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType; cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType;
cleanLength = junkfilter->jf_cleanLength; cleanLength = junkfilter->jf_cleanLength;
cleanMap = junkfilter->jf_cleanMap; cleanMap = junkfilter->jf_cleanMap;
/* --------------------- /* ---------------------
* Handle the trivial case first. * Handle the trivial case first.
* --------------------- * ---------------------
*/ */
if (cleanLength == 0) if (cleanLength == 0)
return (HeapTuple) NULL; return (HeapTuple) NULL;
/* --------------------- /* ---------------------
* Create the arrays that will hold the attribute values * Create the arrays that will hold the attribute values
* and the null information for the new "clean" tuple. * and the null information for the new "clean" tuple.
* *
* Note: we use memory on the stack to optimize things when * Note: we use memory on the stack to optimize things when
* we are dealing with a small number of tuples. * we are dealing with a small number of tuples.
* for large tuples we just use palloc. * for large tuples we just use palloc.
* --------------------- * ---------------------
*/ */
if (cleanLength > 64) { if (cleanLength > 64)
size = cleanLength * sizeof(Datum); {
values = (Datum *) palloc(size); size = cleanLength * sizeof(Datum);
values = (Datum *) palloc(size);
size = cleanLength * sizeof(char); size = cleanLength * sizeof(char);
nulls = (char *) palloc(size); nulls = (char *) palloc(size);
} else { }
values = values_array;
nulls = nulls_array;
}
/* ---------------------
* Exctract one by one all the values of the "clean" tuple.
* ---------------------
*/
for (i=0; i<cleanLength; i++) {
Datum d = (Datum)
heap_getattr(tuple, InvalidBuffer, cleanMap[i], tupType, &isNull);
values[i] = d;
if (isNull)
nulls[i] = 'n';
else else
nulls[i] = ' '; {
} values = values_array;
nulls = nulls_array;
}
/* --------------------- /* ---------------------
* Now form the new tuple. * Exctract one by one all the values of the "clean" tuple.
* --------------------- * ---------------------
*/ */
cleanTuple = heap_formtuple(cleanTupType, for (i = 0; i < cleanLength; i++)
values, {
nulls); Datum d = (Datum)
heap_getattr(tuple, InvalidBuffer, cleanMap[i], tupType, &isNull);
/* --------------------- values[i] = d;
* We are done. Free any space allocated for 'values' and 'nulls'
* and return the new tuple.
* ---------------------
*/
if (cleanLength > 64) {
pfree(values);
pfree(nulls);
}
return(cleanTuple); if (isNull)
nulls[i] = 'n';
else
nulls[i] = ' ';
}
/* ---------------------
* Now form the new tuple.
* ---------------------
*/
cleanTuple = heap_formtuple(cleanTupType,
values,
nulls);
/* ---------------------
* We are done. Free any space allocated for 'values' and 'nulls'
* and return the new tuple.
* ---------------------
*/
if (cleanLength > 64)
{
pfree(values);
pfree(nulls);
}
return (cleanTuple);
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,75 +1,75 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* execProcnode.c-- * execProcnode.c--
* contains dispatch functions which call the appropriate "initialize", * contains dispatch functions which call the appropriate "initialize",
* "get a tuple", and "cleanup" routines for the given node type. * "get a tuple", and "cleanup" routines for the given node type.
* If the node has children, then it will presumably call ExecInitNode, * If the node has children, then it will presumably call ExecInitNode,
* ExecProcNode, or ExecEndNode on it's subnodes and do the appropriate * ExecProcNode, or ExecEndNode on it's subnodes and do the appropriate
* processing.. * processing..
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.2 1996/10/31 10:11:27 scrappy Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.3 1997/09/07 04:41:19 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
/* /*
* INTERFACE ROUTINES * INTERFACE ROUTINES
* ExecInitNode - initialize a plan node and it's subplans * ExecInitNode - initialize a plan node and it's subplans
* ExecProcNode - get a tuple by executing the plan node * ExecProcNode - get a tuple by executing the plan node
* ExecEndNode - shut down a plan node and it's subplans * ExecEndNode - shut down a plan node and it's subplans
* *
* NOTES * NOTES
* This used to be three files. It is now all combined into * This used to be three files. It is now all combined into
* one file so that it is easier to keep ExecInitNode, ExecProcNode, * one file so that it is easier to keep ExecInitNode, ExecProcNode,
* and ExecEndNode in sync when new nodes are added. * and ExecEndNode in sync when new nodes are added.
* *
* EXAMPLE * EXAMPLE
* suppose we want the age of the manager of the shoe department and * suppose we want the age of the manager of the shoe department and
* the number of employees in that department. so we have the query: * the number of employees in that department. so we have the query:
* *
* retrieve (DEPT.no_emps, EMP.age) * retrieve (DEPT.no_emps, EMP.age)
* where EMP.name = DEPT.mgr and * where EMP.name = DEPT.mgr and
* DEPT.name = "shoe" * DEPT.name = "shoe"
* *
* Suppose the planner gives us the following plan: * Suppose the planner gives us the following plan:
* *
* Nest Loop (DEPT.mgr = EMP.name) * Nest Loop (DEPT.mgr = EMP.name)
* / \ * / \
* / \ * / \
* Seq Scan Seq Scan * Seq Scan Seq Scan
* DEPT EMP * DEPT EMP
* (name = "shoe") * (name = "shoe")
* *
* ExecStart() is called first. * ExecStart() is called first.
* It calls InitPlan() which calls ExecInitNode() on * It calls InitPlan() which calls ExecInitNode() on
* the root of the plan -- the nest loop node. * the root of the plan -- the nest loop node.
* *
* * ExecInitNode() notices that it is looking at a nest loop and * * ExecInitNode() notices that it is looking at a nest loop and
* as the code below demonstrates, it calls ExecInitNestLoop(). * as the code below demonstrates, it calls ExecInitNestLoop().
* Eventually this calls ExecInitNode() on the right and left subplans * Eventually this calls ExecInitNode() on the right and left subplans
* and so forth until the entire plan is initialized. * and so forth until the entire plan is initialized.
* *
* * Then when ExecRun() is called, it calls ExecutePlan() which * * Then when ExecRun() is called, it calls ExecutePlan() which
* calls ExecProcNode() repeatedly on the top node of the plan. * calls ExecProcNode() repeatedly on the top node of the plan.
* Each time this happens, ExecProcNode() will end up calling * Each time this happens, ExecProcNode() will end up calling
* ExecNestLoop(), which calls ExecProcNode() on its subplans. * ExecNestLoop(), which calls ExecProcNode() on its subplans.
* Each of these subplans is a sequential scan so ExecSeqScan() is * Each of these subplans is a sequential scan so ExecSeqScan() is
* called. The slots returned by ExecSeqScan() may contain * called. The slots returned by ExecSeqScan() may contain
* tuples which contain the attributes ExecNestLoop() uses to * tuples which contain the attributes ExecNestLoop() uses to
* form the tuples it returns. * form the tuples it returns.
* *
* * Eventually ExecSeqScan() stops returning tuples and the nest * * Eventually ExecSeqScan() stops returning tuples and the nest
* loop join ends. Lastly, ExecEnd() calls ExecEndNode() which * loop join ends. Lastly, ExecEnd() calls ExecEndNode() which
* calls ExecEndNestLoop() which in turn calls ExecEndNode() on * calls ExecEndNestLoop() which in turn calls ExecEndNode() on
* its subplans which result in ExecEndSeqScan(). * its subplans which result in ExecEndSeqScan().
* *
* This should show how the executor works by having * This should show how the executor works by having
* ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch * ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch
* their work to the appopriate node support routines which may * their work to the appopriate node support routines which may
* in turn call these routines themselves on their subplans. * in turn call these routines themselves on their subplans.
* *
*/ */
#include "postgres.h" #include "postgres.h"
@ -91,389 +91,393 @@
#include "executor/nodeTee.h" #include "executor/nodeTee.h"
/* ------------------------------------------------------------------------ /* ------------------------------------------------------------------------
* ExecInitNode * ExecInitNode
* *
* Recursively initializes all the nodes in the plan rooted * Recursively initializes all the nodes in the plan rooted
* at 'node'. * at 'node'.
* *
* Initial States: * Initial States:
* 'node' is the plan produced by the query planner * 'node' is the plan produced by the query planner
* *
* returns TRUE/FALSE on whether the plan was successfully initialized * returns TRUE/FALSE on whether the plan was successfully initialized
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
bool bool
ExecInitNode(Plan *node, EState *estate, Plan *parent) ExecInitNode(Plan * node, EState * estate, Plan * parent)
{ {
bool result; bool result;
/* ----------------
* do nothing when we get to the end
* of a leaf on tree.
* ----------------
*/
if (node == NULL)
return FALSE;
switch(nodeTag(node)) {
/* ----------------
* control nodes
* ----------------
*/
case T_Result:
result = ExecInitResult((Result *)node, estate, parent);
break;
case T_Append:
result = ExecInitAppend((Append *)node, estate, parent);
break;
/* ---------------- /* ----------------
* scan nodes * do nothing when we get to the end
* of a leaf on tree.
* ---------------- * ----------------
*/ */
case T_SeqScan: if (node == NULL)
result = ExecInitSeqScan((SeqScan *)node, estate, parent); return FALSE;
break;
case T_IndexScan: switch (nodeTag(node))
result = ExecInitIndexScan((IndexScan *)node, estate, parent); {
break; /* ----------------
* control nodes
* ----------------
*/
case T_Result:
result = ExecInitResult((Result *) node, estate, parent);
break;
/* ---------------- case T_Append:
* join nodes result = ExecInitAppend((Append *) node, estate, parent);
* ---------------- break;
*/
case T_NestLoop:
result = ExecInitNestLoop((NestLoop *)node, estate, parent);
break;
case T_MergeJoin: /* ----------------
result = ExecInitMergeJoin((MergeJoin *)node, estate, parent); * scan nodes
break; * ----------------
*/
case T_SeqScan:
result = ExecInitSeqScan((SeqScan *) node, estate, parent);
break;
/* ---------------- case T_IndexScan:
* materialization nodes result = ExecInitIndexScan((IndexScan *) node, estate, parent);
* ---------------- break;
*/
case T_Material:
result = ExecInitMaterial((Material *)node, estate, parent);
break;
case T_Sort: /* ----------------
result = ExecInitSort((Sort *)node, estate, parent); * join nodes
break; * ----------------
*/
case T_NestLoop:
result = ExecInitNestLoop((NestLoop *) node, estate, parent);
break;
case T_Unique: case T_MergeJoin:
result = ExecInitUnique((Unique *)node, estate, parent); result = ExecInitMergeJoin((MergeJoin *) node, estate, parent);
break; break;
case T_Group: /* ----------------
result = ExecInitGroup((Group *)node, estate, parent); * materialization nodes
break; * ----------------
*/
case T_Material:
result = ExecInitMaterial((Material *) node, estate, parent);
break;
case T_Agg: case T_Sort:
result = ExecInitAgg((Agg *)node, estate, parent); result = ExecInitSort((Sort *) node, estate, parent);
break; break;
case T_Hash: case T_Unique:
result = ExecInitHash((Hash *)node, estate, parent); result = ExecInitUnique((Unique *) node, estate, parent);
break; break;
case T_HashJoin: case T_Group:
result = ExecInitHashJoin((HashJoin *)node, estate, parent); result = ExecInitGroup((Group *) node, estate, parent);
break; break;
case T_Tee: case T_Agg:
result = ExecInitTee((Tee*)node, estate, parent); result = ExecInitAgg((Agg *) node, estate, parent);
break; break;
default: case T_Hash:
elog(DEBUG, "ExecInitNode: node not yet supported: %d", result = ExecInitHash((Hash *) node, estate, parent);
nodeTag(node)); break;
result = FALSE;
}
return result; case T_HashJoin:
result = ExecInitHashJoin((HashJoin *) node, estate, parent);
break;
case T_Tee:
result = ExecInitTee((Tee *) node, estate, parent);
break;
default:
elog(DEBUG, "ExecInitNode: node not yet supported: %d",
nodeTag(node));
result = FALSE;
}
return result;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecProcNode * ExecProcNode
* *
* Initial States: * Initial States:
* the query tree must be initialized once by calling ExecInit. * the query tree must be initialized once by calling ExecInit.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
TupleTableSlot * TupleTableSlot *
ExecProcNode(Plan *node, Plan *parent) ExecProcNode(Plan * node, Plan * parent)
{ {
TupleTableSlot *result; TupleTableSlot *result;
/* ----------------
* deal with NULL nodes..
* ----------------
*/
if (node == NULL)
return NULL;
switch(nodeTag(node)) {
/* ----------------
* control nodes
* ----------------
*/
case T_Result:
result = ExecResult((Result *)node);
break;
case T_Append:
result = ExecProcAppend((Append *)node);
break;
/* ---------------- /* ----------------
* scan nodes * deal with NULL nodes..
* ---------------- * ----------------
*/ */
case T_SeqScan: if (node == NULL)
result = ExecSeqScan((SeqScan *)node); return NULL;
break;
case T_IndexScan: switch (nodeTag(node))
result = ExecIndexScan((IndexScan *)node); {
break; /* ----------------
* control nodes
* ----------------
*/
case T_Result:
result = ExecResult((Result *) node);
break;
/* ---------------- case T_Append:
* join nodes result = ExecProcAppend((Append *) node);
* ---------------- break;
*/
case T_NestLoop:
result = ExecNestLoop((NestLoop *)node, parent);
break;
case T_MergeJoin: /* ----------------
result = ExecMergeJoin((MergeJoin *)node); * scan nodes
break; * ----------------
*/
case T_SeqScan:
result = ExecSeqScan((SeqScan *) node);
break;
/* ---------------- case T_IndexScan:
* materialization nodes result = ExecIndexScan((IndexScan *) node);
* ---------------- break;
*/
case T_Material:
result = ExecMaterial((Material *)node);
break;
case T_Sort: /* ----------------
result = ExecSort((Sort *)node); * join nodes
break; * ----------------
*/
case T_NestLoop:
result = ExecNestLoop((NestLoop *) node, parent);
break;
case T_Unique: case T_MergeJoin:
result = ExecUnique((Unique *)node); result = ExecMergeJoin((MergeJoin *) node);
break; break;
case T_Group: /* ----------------
result = ExecGroup((Group *)node); * materialization nodes
break; * ----------------
*/
case T_Material:
result = ExecMaterial((Material *) node);
break;
case T_Agg: case T_Sort:
result = ExecAgg((Agg *)node); result = ExecSort((Sort *) node);
break; break;
case T_Hash: case T_Unique:
result = ExecHash((Hash *)node); result = ExecUnique((Unique *) node);
break; break;
case T_HashJoin: case T_Group:
result = ExecHashJoin((HashJoin *)node); result = ExecGroup((Group *) node);
break; break;
case T_Tee: case T_Agg:
result = ExecTee((Tee*)node, parent); result = ExecAgg((Agg *) node);
break; break;
default: case T_Hash:
elog(DEBUG, "ExecProcNode: node not yet supported: %d", result = ExecHash((Hash *) node);
nodeTag(node)); break;
result = FALSE;
}
return result; case T_HashJoin:
result = ExecHashJoin((HashJoin *) node);
break;
case T_Tee:
result = ExecTee((Tee *) node, parent);
break;
default:
elog(DEBUG, "ExecProcNode: node not yet supported: %d",
nodeTag(node));
result = FALSE;
}
return result;
} }
int int
ExecCountSlotsNode(Plan *node) ExecCountSlotsNode(Plan * node)
{ {
if (node == (Plan *)NULL) if (node == (Plan *) NULL)
return 0;
switch (nodeTag(node))
{
/* ----------------
* control nodes
* ----------------
*/
case T_Result:
return ExecCountSlotsResult((Result *) node);
case T_Append:
return ExecCountSlotsAppend((Append *) node);
/* ----------------
* scan nodes
* ----------------
*/
case T_SeqScan:
return ExecCountSlotsSeqScan((SeqScan *) node);
case T_IndexScan:
return ExecCountSlotsIndexScan((IndexScan *) node);
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
return ExecCountSlotsNestLoop((NestLoop *) node);
case T_MergeJoin:
return ExecCountSlotsMergeJoin((MergeJoin *) node);
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
return ExecCountSlotsMaterial((Material *) node);
case T_Sort:
return ExecCountSlotsSort((Sort *) node);
case T_Unique:
return ExecCountSlotsUnique((Unique *) node);
case T_Group:
return ExecCountSlotsGroup((Group *) node);
case T_Agg:
return ExecCountSlotsAgg((Agg *) node);
case T_Hash:
return ExecCountSlotsHash((Hash *) node);
case T_HashJoin:
return ExecCountSlotsHashJoin((HashJoin *) node);
case T_Tee:
return ExecCountSlotsTee((Tee *) node);
default:
elog(WARN, "ExecCountSlotsNode: node not yet supported: %d",
nodeTag(node));
break;
}
return 0; return 0;
switch(nodeTag(node)) {
/* ----------------
* control nodes
* ----------------
*/
case T_Result:
return ExecCountSlotsResult((Result *)node);
case T_Append:
return ExecCountSlotsAppend((Append *)node);
/* ----------------
* scan nodes
* ----------------
*/
case T_SeqScan:
return ExecCountSlotsSeqScan((SeqScan *)node);
case T_IndexScan:
return ExecCountSlotsIndexScan((IndexScan *)node);
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
return ExecCountSlotsNestLoop((NestLoop *)node);
case T_MergeJoin:
return ExecCountSlotsMergeJoin((MergeJoin *)node);
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
return ExecCountSlotsMaterial((Material *)node);
case T_Sort:
return ExecCountSlotsSort((Sort *)node);
case T_Unique:
return ExecCountSlotsUnique((Unique *)node);
case T_Group:
return ExecCountSlotsGroup((Group *)node);
case T_Agg:
return ExecCountSlotsAgg((Agg *)node);
case T_Hash:
return ExecCountSlotsHash((Hash *)node);
case T_HashJoin:
return ExecCountSlotsHashJoin((HashJoin *)node);
case T_Tee:
return ExecCountSlotsTee((Tee*)node);
default:
elog(WARN, "ExecCountSlotsNode: node not yet supported: %d",
nodeTag(node));
break;
}
return 0;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecEndNode * ExecEndNode
* *
* Recursively cleans up all the nodes in the plan rooted * Recursively cleans up all the nodes in the plan rooted
* at 'node'. * at 'node'.
* *
* After this operation, the query plan will not be able to * After this operation, the query plan will not be able to
* processed any further. This should be called only after * processed any further. This should be called only after
* the query plan has been fully executed. * the query plan has been fully executed.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
ExecEndNode(Plan *node, Plan *parent) ExecEndNode(Plan * node, Plan * parent)
{ {
/* ----------------
* do nothing when we get to the end
* of a leaf on tree.
* ----------------
*/
if (node == NULL)
return;
switch(nodeTag(node)) {
/* ---------------- /* ----------------
* control nodes * do nothing when we get to the end
* of a leaf on tree.
* ---------------- * ----------------
*/ */
case T_Result: if (node == NULL)
ExecEndResult((Result *)node); return;
break;
case T_Append: switch (nodeTag(node))
ExecEndAppend((Append *)node); {
break; /* ----------------
* control nodes
* ----------------
*/
case T_Result:
ExecEndResult((Result *) node);
break;
/* ---------------- case T_Append:
* scan nodes ExecEndAppend((Append *) node);
* ---------------- break;
*/
case T_SeqScan:
ExecEndSeqScan((SeqScan *)node);
break;
case T_IndexScan: /* ----------------
ExecEndIndexScan((IndexScan *)node); * scan nodes
break; * ----------------
*/
case T_SeqScan:
ExecEndSeqScan((SeqScan *) node);
break;
/* ---------------- case T_IndexScan:
* join nodes ExecEndIndexScan((IndexScan *) node);
* ---------------- break;
*/
case T_NestLoop:
ExecEndNestLoop((NestLoop *)node);
break;
case T_MergeJoin: /* ----------------
ExecEndMergeJoin((MergeJoin *)node); * join nodes
break; * ----------------
*/
case T_NestLoop:
ExecEndNestLoop((NestLoop *) node);
break;
/* ---------------- case T_MergeJoin:
* materialization nodes ExecEndMergeJoin((MergeJoin *) node);
* ---------------- break;
*/
case T_Material:
ExecEndMaterial((Material *)node);
break;
case T_Sort: /* ----------------
ExecEndSort((Sort *)node); * materialization nodes
break; * ----------------
*/
case T_Material:
ExecEndMaterial((Material *) node);
break;
case T_Unique: case T_Sort:
ExecEndUnique((Unique *)node); ExecEndSort((Sort *) node);
break; break;
case T_Group: case T_Unique:
ExecEndGroup((Group *)node); ExecEndUnique((Unique *) node);
break; break;
case T_Agg: case T_Group:
ExecEndAgg((Agg *)node); ExecEndGroup((Group *) node);
break; break;
/* ---------------- case T_Agg:
* XXX add hooks to these ExecEndAgg((Agg *) node);
* ---------------- break;
*/
case T_Hash:
ExecEndHash((Hash *) node);
break;
case T_HashJoin: /* ----------------
ExecEndHashJoin((HashJoin *) node); * XXX add hooks to these
break; * ----------------
*/
case T_Hash:
ExecEndHash((Hash *) node);
break;
case T_Tee: case T_HashJoin:
ExecEndTee((Tee*) node, parent); ExecEndHashJoin((HashJoin *) node);
break; break;
default: case T_Tee:
elog(DEBUG, "ExecEndNode: node not yet supported", ExecEndTee((Tee *) node, parent);
nodeTag(node)); break;
break;
} default:
elog(DEBUG, "ExecEndNode: node not yet supported",
nodeTag(node));
break;
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,17 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* execScan.c-- * execScan.c--
* This code provides support for generalized relation scans. ExecScan * This code provides support for generalized relation scans. ExecScan
* is passed a node and a pointer to a function to "do the right thing" * is passed a node and a pointer to a function to "do the right thing"
* and return a tuple from the relation. ExecScan then does the tedious * and return a tuple from the relation. ExecScan then does the tedious
* stuff - checking the qualification and projecting the tuple * stuff - checking the qualification and projecting the tuple
* appropriately. * appropriately.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.3 1997/07/28 00:53:51 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.4 1997/09/07 04:41:23 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -23,117 +23,123 @@
#include "executor/executor.h" #include "executor/executor.h"
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecScan * ExecScan
* *
* Scans the relation using the 'access method' indicated and * Scans the relation using the 'access method' indicated and
* returns the next qualifying tuple in the direction specified * returns the next qualifying tuple in the direction specified
* in the global variable ExecDirection. * in the global variable ExecDirection.
* The access method returns the next tuple and execScan() is * The access method returns the next tuple and execScan() is
* responisble for checking the tuple returned against the qual-clause. * responisble for checking the tuple returned against the qual-clause.
* *
* Conditions: * Conditions:
* -- the "cursor" maintained by the AMI is positioned at the tuple * -- the "cursor" maintained by the AMI is positioned at the tuple
* returned previously. * returned previously.
* *
* Initial States: * Initial States:
* -- the relation indicated is opened for scanning so that the * -- the relation indicated is opened for scanning so that the
* "cursor" is positioned before the first qualifying tuple. * "cursor" is positioned before the first qualifying tuple.
* *
* May need to put startmmgr and endmmgr in here. * May need to put startmmgr and endmmgr in here.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
TupleTableSlot * TupleTableSlot *
ExecScan(Scan *node, ExecScan(Scan * node,
TupleTableSlot* (*accessMtd)()) /* function returning a tuple */ TupleTableSlot * (*accessMtd) ()) /* function returning a
* tuple */
{ {
CommonScanState *scanstate; CommonScanState *scanstate;
EState *estate; EState *estate;
List *qual; List *qual;
bool isDone; bool isDone;
TupleTableSlot *slot; TupleTableSlot *slot;
TupleTableSlot *resultSlot; TupleTableSlot *resultSlot;
HeapTuple newTuple; HeapTuple newTuple;
ExprContext *econtext; ExprContext *econtext;
ProjectionInfo *projInfo; ProjectionInfo *projInfo;
/* ---------------- /* ----------------
* initialize misc variables * initialize misc variables
* ---------------- * ----------------
*/ */
newTuple = NULL; newTuple = NULL;
slot = NULL; slot = NULL;
estate = node->plan.state; estate = node->plan.state;
scanstate = node->scanstate; scanstate = node->scanstate;
/* ---------------- /* ----------------
* get the expression context * get the expression context
* ---------------- * ----------------
*/ */
econtext = scanstate->cstate.cs_ExprContext; econtext = scanstate->cstate.cs_ExprContext;
/* ---------------- /* ----------------
* initialize fields in ExprContext which don't change * initialize fields in ExprContext which don't change
* in the course of the scan.. * in the course of the scan..
* ---------------- * ----------------
*/ */
qual = node->plan.qual; qual = node->plan.qual;
econtext->ecxt_relation = scanstate->css_currentRelation; econtext->ecxt_relation = scanstate->css_currentRelation;
econtext->ecxt_relid = node->scanrelid; econtext->ecxt_relid = node->scanrelid;
if (scanstate->cstate.cs_TupFromTlist) { if (scanstate->cstate.cs_TupFromTlist)
{
projInfo = scanstate->cstate.cs_ProjInfo;
resultSlot = ExecProject(projInfo, &isDone);
if (!isDone)
return resultSlot;
}
/*
* get a tuple from the access method loop until we obtain a tuple
* which passes the qualification.
*/
for (;;)
{
slot = (TupleTableSlot *) (*accessMtd) (node);
/* ----------------
* if the slot returned by the accessMtd contains
* NULL, then it means there is nothing more to scan
* so we just return the empty slot.
* ----------------
*/
if (TupIsNull(slot))
return slot;
/* ----------------
* place the current tuple into the expr context
* ----------------
*/
econtext->ecxt_scantuple = slot;
/* ----------------
* check that the current tuple satisfies the qual-clause
* if our qualification succeeds then we
* leave the loop.
* ----------------
*/
/*
* add a check for non-nil qual here to avoid a function call to
* ExecQual() when the qual is nil
*/
if (!qual || ExecQual(qual, econtext) == true)
break;
}
/* ----------------
* form a projection tuple, store it in the result tuple
* slot and return it.
* ----------------
*/
projInfo = scanstate->cstate.cs_ProjInfo; projInfo = scanstate->cstate.cs_ProjInfo;
resultSlot = ExecProject(projInfo, &isDone); resultSlot = ExecProject(projInfo, &isDone);
if (!isDone) scanstate->cstate.cs_TupFromTlist = !isDone;
return resultSlot;
}
/*
* get a tuple from the access method
* loop until we obtain a tuple which passes the qualification.
*/
for(;;) {
slot = (TupleTableSlot *) (*accessMtd)(node);
/* ---------------- return resultSlot;
* if the slot returned by the accessMtd contains
* NULL, then it means there is nothing more to scan
* so we just return the empty slot.
* ----------------
*/
if (TupIsNull(slot)) return slot;
/* ----------------
* place the current tuple into the expr context
* ----------------
*/
econtext->ecxt_scantuple = slot;
/* ----------------
* check that the current tuple satisfies the qual-clause
* if our qualification succeeds then we
* leave the loop.
* ----------------
*/
/* add a check for non-nil qual here to avoid a
function call to ExecQual() when the qual is nil */
if (!qual || ExecQual(qual, econtext) == true)
break;
}
/* ----------------
* form a projection tuple, store it in the result tuple
* slot and return it.
* ----------------
*/
projInfo = scanstate->cstate.cs_ProjInfo;
resultSlot = ExecProject(projInfo, &isDone);
scanstate->cstate.cs_TupFromTlist = !isDone;
return resultSlot;
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,14 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* functions.c-- * functions.c--
* Routines to handle functions called from the executor * Routines to handle functions called from the executor
* Putting this stuff in fmgr makes the postmaster a mess.... * Putting this stuff in fmgr makes the postmaster a mess....
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.7 1997/08/29 09:02:50 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.8 1997/09/07 04:41:27 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -41,401 +41,438 @@
#undef new #undef new
typedef enum {F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE} ExecStatus; typedef enum
{
F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE
} ExecStatus;
typedef struct local_es { typedef struct local_es
QueryDesc *qd; {
EState *estate; QueryDesc *qd;
struct local_es *next; EState *estate;
ExecStatus status; struct local_es *next;
} execution_state; ExecStatus status;
} execution_state;
#define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL) #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL)
/* non-export function prototypes */ /* non-export function prototypes */
static TupleDesc postquel_start(execution_state *es); static TupleDesc postquel_start(execution_state * es);
static execution_state *init_execution_state(FunctionCachePtr fcache, static execution_state *
char *args[]); init_execution_state(FunctionCachePtr fcache,
static TupleTableSlot *postquel_getnext(execution_state *es); char *args[]);
static void postquel_end(execution_state *es); static TupleTableSlot *postquel_getnext(execution_state * es);
static void postquel_sub_params(execution_state *es, int nargs, static void postquel_end(execution_state * es);
char *args[], bool *nullV); static void
static Datum postquel_execute(execution_state *es, FunctionCachePtr fcache, postquel_sub_params(execution_state * es, int nargs,
List *fTlist, char **args, bool *isNull); char *args[], bool * nullV);
static Datum
postquel_execute(execution_state * es, FunctionCachePtr fcache,
List * fTlist, char **args, bool * isNull);
Datum Datum
ProjectAttribute(TupleDesc TD, ProjectAttribute(TupleDesc TD,
TargetEntry *tlist, TargetEntry * tlist,
HeapTuple tup, HeapTuple tup,
bool *isnullP) bool * isnullP)
{ {
Datum val,valueP; Datum val,
Var *attrVar = (Var *)tlist->expr; valueP;
AttrNumber attrno = attrVar->varattno; Var *attrVar = (Var *) tlist->expr;
AttrNumber attrno = attrVar->varattno;
val = PointerGetDatum(heap_getattr(tup, val = PointerGetDatum(heap_getattr(tup,
InvalidBuffer, InvalidBuffer,
attrno, attrno,
TD, TD,
isnullP)); isnullP));
if (*isnullP) if (*isnullP)
return (Datum) NULL; return (Datum) NULL;
valueP = datumCopy(val, valueP = datumCopy(val,
TD->attrs[attrno-1]->atttypid, TD->attrs[attrno - 1]->atttypid,
TD->attrs[attrno-1]->attbyval, TD->attrs[attrno - 1]->attbyval,
(Size) TD->attrs[attrno-1]->attlen); (Size) TD->attrs[attrno - 1]->attlen);
return valueP; return valueP;
} }
static execution_state * static execution_state *
init_execution_state(FunctionCachePtr fcache, init_execution_state(FunctionCachePtr fcache,
char *args[]) char *args[])
{ {
execution_state *newes; execution_state *newes;
execution_state *nextes; execution_state *nextes;
execution_state *preves; execution_state *preves;
QueryTreeList *queryTree_list; QueryTreeList *queryTree_list;
int i; int i;
List *planTree_list; List *planTree_list;
int nargs; int nargs;
nargs = fcache->nargs; nargs = fcache->nargs;
newes = (execution_state *) palloc(sizeof(execution_state)); newes = (execution_state *) palloc(sizeof(execution_state));
nextes = newes; nextes = newes;
preves = (execution_state *)NULL; preves = (execution_state *) NULL;
planTree_list = (List *) planTree_list = (List *)
pg_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None); pg_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None);
for (i=0; i < queryTree_list->len; i++) { for (i = 0; i < queryTree_list->len; i++)
EState *estate; {
Query *queryTree = (Query*) (queryTree_list->qtrees[i]); EState *estate;
Plan *planTree = lfirst(planTree_list); Query *queryTree = (Query *) (queryTree_list->qtrees[i]);
Plan *planTree = lfirst(planTree_list);
if (!nextes) if (!nextes)
nextes = (execution_state *) palloc(sizeof(execution_state)); nextes = (execution_state *) palloc(sizeof(execution_state));
if (preves) if (preves)
preves->next = nextes; preves->next = nextes;
nextes->next = NULL; nextes->next = NULL;
nextes->status = F_EXEC_START; nextes->status = F_EXEC_START;
nextes->qd = CreateQueryDesc(queryTree, nextes->qd = CreateQueryDesc(queryTree,
planTree, planTree,
None); None);
estate = CreateExecutorState(); estate = CreateExecutorState();
if (nargs > 0) { if (nargs > 0)
int i; {
ParamListInfo paramLI; int i;
ParamListInfo paramLI;
paramLI = paramLI =
(ParamListInfo)palloc((nargs+1)*sizeof(ParamListInfoData)); (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
memset(paramLI, 0, nargs*sizeof(ParamListInfoData)); memset(paramLI, 0, nargs * sizeof(ParamListInfoData));
estate->es_param_list_info = paramLI; estate->es_param_list_info = paramLI;
for (i=0; i<nargs; paramLI++, i++) { for (i = 0; i < nargs; paramLI++, i++)
paramLI->kind = PARAM_NUM; {
paramLI->id = i+1; paramLI->kind = PARAM_NUM;
paramLI->isnull = false; paramLI->id = i + 1;
paramLI->value = (Datum) NULL; paramLI->isnull = false;
} paramLI->value = (Datum) NULL;
paramLI->kind = PARAM_INVALID; }
paramLI->kind = PARAM_INVALID;
}
else
estate->es_param_list_info = (ParamListInfo) NULL;
nextes->estate = estate;
preves = nextes;
nextes = (execution_state *) NULL;
planTree_list = lnext(planTree_list);
} }
else
estate->es_param_list_info = (ParamListInfo)NULL;
nextes->estate = estate;
preves = nextes;
nextes = (execution_state *)NULL;
planTree_list = lnext(planTree_list); return newes;
}
return newes;
} }
static TupleDesc static TupleDesc
postquel_start(execution_state *es) postquel_start(execution_state * es)
{ {
#ifdef FUNC_UTIL_PATCH #ifdef FUNC_UTIL_PATCH
/*
* Do nothing for utility commands. (create, destroy...) DZ - 30-8-1996 /*
*/ * Do nothing for utility commands. (create, destroy...) DZ -
if (es->qd->operation == CMD_UTILITY) { * 30-8-1996
return (TupleDesc) NULL; */
} if (es->qd->operation == CMD_UTILITY)
{
return (TupleDesc) NULL;
}
#endif #endif
return ExecutorStart(es->qd, es->estate); return ExecutorStart(es->qd, es->estate);
} }
static TupleTableSlot * static TupleTableSlot *
postquel_getnext(execution_state *es) postquel_getnext(execution_state * es)
{ {
int feature; int feature;
#ifdef FUNC_UTIL_PATCH #ifdef FUNC_UTIL_PATCH
if (es->qd->operation == CMD_UTILITY) { if (es->qd->operation == CMD_UTILITY)
/* {
* Process an utility command. (create, destroy...) DZ - 30-8-1996
*/
ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement();
return (TupleTableSlot*) NULL;
}
#endif
feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN; /*
* Process an utility command. (create, destroy...) DZ -
return ExecutorRun(es->qd, es->estate, feature, 0); * 30-8-1996
} */
ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
static void if (!LAST_POSTQUEL_COMMAND(es))
postquel_end(execution_state *es) CommandCounterIncrement();
{ return (TupleTableSlot *) NULL;
#ifdef FUNC_UTIL_PATCH }
/* #endif
* Do nothing for utility commands. (create, destroy...) DZ - 30-8-1996
*/ feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
if (es->qd->operation == CMD_UTILITY) {
return; return ExecutorRun(es->qd, es->estate, feature, 0);
} }
#endif
ExecutorEnd(es->qd, es->estate); static void
} postquel_end(execution_state * es)
{
static void #ifdef FUNC_UTIL_PATCH
postquel_sub_params(execution_state *es,
int nargs, /*
char *args[], * Do nothing for utility commands. (create, destroy...) DZ -
bool *nullV) * 30-8-1996
{ */
ParamListInfo paramLI; if (es->qd->operation == CMD_UTILITY)
EState *estate; {
return;
estate = es->estate; }
paramLI = estate->es_param_list_info; #endif
ExecutorEnd(es->qd, es->estate);
while (paramLI->kind != PARAM_INVALID) { }
if (paramLI->kind == PARAM_NUM) {
Assert(paramLI->id <= nargs); static void
paramLI->value = (Datum)args[(paramLI->id - 1)]; postquel_sub_params(execution_state * es,
paramLI->isnull = nullV[(paramLI->id - 1)]; int nargs,
char *args[],
bool * nullV)
{
ParamListInfo paramLI;
EState *estate;
estate = es->estate;
paramLI = estate->es_param_list_info;
while (paramLI->kind != PARAM_INVALID)
{
if (paramLI->kind == PARAM_NUM)
{
Assert(paramLI->id <= nargs);
paramLI->value = (Datum) args[(paramLI->id - 1)];
paramLI->isnull = nullV[(paramLI->id - 1)];
}
paramLI++;
} }
paramLI++;
}
} }
static TupleTableSlot * static TupleTableSlot *
copy_function_result(FunctionCachePtr fcache, copy_function_result(FunctionCachePtr fcache,
TupleTableSlot *resultSlot) TupleTableSlot * resultSlot)
{ {
TupleTableSlot *funcSlot; TupleTableSlot *funcSlot;
TupleDesc resultTd; TupleDesc resultTd;
HeapTuple newTuple; HeapTuple newTuple;
HeapTuple oldTuple; HeapTuple oldTuple;
Assert(! TupIsNull(resultSlot)); Assert(!TupIsNull(resultSlot));
oldTuple = resultSlot->val; oldTuple = resultSlot->val;
funcSlot = (TupleTableSlot*)fcache->funcSlot; funcSlot = (TupleTableSlot *) fcache->funcSlot;
if (funcSlot == (TupleTableSlot*)NULL) if (funcSlot == (TupleTableSlot *) NULL)
return resultSlot; return resultSlot;
resultTd = resultSlot->ttc_tupleDescriptor; resultTd = resultSlot->ttc_tupleDescriptor;
/*
* When the funcSlot is NULL we have to initialize the funcSlot's
* tuple descriptor.
*/
if (TupIsNull(funcSlot)) {
int i= 0;
TupleDesc funcTd = funcSlot->ttc_tupleDescriptor;
while (i < oldTuple->t_natts) { /*
funcTd->attrs[i] = * When the funcSlot is NULL we have to initialize the funcSlot's
(AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE); * tuple descriptor.
memmove(funcTd->attrs[i], */
resultTd->attrs[i], if (TupIsNull(funcSlot))
ATTRIBUTE_TUPLE_SIZE); {
i++; int i = 0;
TupleDesc funcTd = funcSlot->ttc_tupleDescriptor;
while (i < oldTuple->t_natts)
{
funcTd->attrs[i] =
(AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
memmove(funcTd->attrs[i],
resultTd->attrs[i],
ATTRIBUTE_TUPLE_SIZE);
i++;
}
} }
}
newTuple = heap_copytuple(oldTuple); newTuple = heap_copytuple(oldTuple);
return ExecStoreTuple(newTuple,funcSlot,InvalidBuffer,true); return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
} }
static Datum static Datum
postquel_execute(execution_state *es, postquel_execute(execution_state * es,
FunctionCachePtr fcache, FunctionCachePtr fcache,
List *fTlist, List * fTlist,
char **args, char **args,
bool *isNull) bool * isNull)
{ {
TupleTableSlot *slot; TupleTableSlot *slot;
Datum value; Datum value;
#ifdef INDEXSCAN_PATCH #ifdef INDEXSCAN_PATCH
/*
* It's more right place to do it (before postquel_start->ExecutorStart). /*
* Now ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. * It's more right place to do it (before
* (But note: I HOPE we can do it here). - vadim 01/22/97 * postquel_start->ExecutorStart). Now
*/ * ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But
if (fcache->nargs > 0) * note: I HOPE we can do it here). - vadim 01/22/97
postquel_sub_params(es, fcache->nargs, args, fcache->nullVect); */
if (fcache->nargs > 0)
postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
#endif #endif
if (es->status == F_EXEC_START) if (es->status == F_EXEC_START)
{ {
postquel_start(es); postquel_start(es);
es->status = F_EXEC_RUN; es->status = F_EXEC_RUN;
} }
#ifndef INDEXSCAN_PATCH #ifndef INDEXSCAN_PATCH
if (fcache->nargs > 0) if (fcache->nargs > 0)
postquel_sub_params(es, fcache->nargs, args, fcache->nullVect); postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
#endif #endif
slot = postquel_getnext(es); slot = postquel_getnext(es);
if (TupIsNull(slot)) { if (TupIsNull(slot))
postquel_end(es); {
es->status = F_EXEC_DONE; postquel_end(es);
*isNull = true; es->status = F_EXEC_DONE;
/* *isNull = true;
* If this isn't the last command for the function
* we have to increment the command
* counter so that subsequent commands can see changes made
* by previous ones.
*/
if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement();
return (Datum)NULL;
}
if (LAST_POSTQUEL_COMMAND(es)) { /*
TupleTableSlot *resSlot; * If this isn't the last command for the function we have to
* increment the command counter so that subsequent commands can
* see changes made by previous ones.
*/
if (!LAST_POSTQUEL_COMMAND(es))
CommandCounterIncrement();
return (Datum) NULL;
}
/* if (LAST_POSTQUEL_COMMAND(es))
* Copy the result. copy_function_result is smart enough {
* to do nothing when no action is called for. This helps TupleTableSlot *resSlot;
* reduce the logic and code redundancy here.
*/
resSlot = copy_function_result(fcache, slot);
if (fTlist != NIL) {
HeapTuple tup;
TargetEntry *tle = lfirst(fTlist);
tup = resSlot->val; /*
value = ProjectAttribute(resSlot->ttc_tupleDescriptor, * Copy the result. copy_function_result is smart enough to do
tle, * nothing when no action is called for. This helps reduce the
tup, * logic and code redundancy here.
isNull); */
}else { resSlot = copy_function_result(fcache, slot);
value = (Datum)resSlot; if (fTlist != NIL)
*isNull = false; {
HeapTuple tup;
TargetEntry *tle = lfirst(fTlist);
tup = resSlot->val;
value = ProjectAttribute(resSlot->ttc_tupleDescriptor,
tle,
tup,
isNull);
}
else
{
value = (Datum) resSlot;
*isNull = false;
}
/*
* If this is a single valued function we have to end the function
* execution now.
*/
if (fcache->oneResult)
{
postquel_end(es);
es->status = F_EXEC_DONE;
}
return value;
} }
/* /*
* If this is a single valued function we have to end the * If this isn't the last command for the function, we don't return
* function execution now. * any results, but we have to increment the command counter so that
* subsequent commands can see changes made by previous ones.
*/ */
if (fcache->oneResult) { CommandCounterIncrement();
postquel_end(es); return (Datum) NULL;
es->status = F_EXEC_DONE;
}
return value;
}
/*
* If this isn't the last command for the function, we don't
* return any results, but we have to increment the command
* counter so that subsequent commands can see changes made
* by previous ones.
*/
CommandCounterIncrement();
return (Datum)NULL;
} }
Datum Datum
postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone) postquel_function(Func * funcNode, char **args, bool * isNull, bool * isDone)
{ {
execution_state *es; execution_state *es;
Datum result = 0; Datum result = 0;
FunctionCachePtr fcache = funcNode->func_fcache; FunctionCachePtr fcache = funcNode->func_fcache;
CommandId savedId; CommandId savedId;
/*
* Before we start do anything we must save CurrentScanCommandId
* to restore it before return to upper Executor. Also, we have to
* set CurrentScanCommandId equal to CurrentCommandId.
* - vadim 08/29/97
*/
savedId = GetScanCommandId ();
SetScanCommandId (GetCurrentCommandId ());
es = (execution_state *) fcache->func_state;
if (es == NULL)
{
es = init_execution_state(fcache, args);
fcache->func_state = (char *) es;
}
while (es && es->status == F_EXEC_DONE)
es = es->next;
Assert(es);
/*
* Execute each command in the function one after another until we're
* executing the final command and get a result or we run out of
* commands.
*/
while (es != (execution_state *)NULL)
{
result = postquel_execute(es,
fcache,
funcNode->func_tlist,
args,
isNull);
if (es->status != F_EXEC_DONE)
break;
es = es->next;
}
/*
* If we've gone through every command in this function, we are done.
*/
if (es == (execution_state *)NULL)
{
/* /*
* Reset the execution states to start over again * Before we start do anything we must save CurrentScanCommandId to
* restore it before return to upper Executor. Also, we have to set
* CurrentScanCommandId equal to CurrentCommandId. - vadim 08/29/97
*/ */
es = (execution_state *)fcache->func_state; savedId = GetScanCommandId();
while (es) SetScanCommandId(GetCurrentCommandId());
{
es->status = F_EXEC_START;
es = es->next;
}
/*
* Let caller know we're finished.
*/
*isDone = true;
SetScanCommandId (savedId);
return (fcache->oneResult) ? result : (Datum)NULL;
}
/*
* If we got a result from a command within the function it has
* to be the final command. All others shouldn't be returing
* anything.
*/
Assert ( LAST_POSTQUEL_COMMAND(es) );
*isDone = false;
SetScanCommandId (savedId); es = (execution_state *) fcache->func_state;
return result; if (es == NULL)
{
es = init_execution_state(fcache, args);
fcache->func_state = (char *) es;
}
while (es && es->status == F_EXEC_DONE)
es = es->next;
Assert(es);
/*
* Execute each command in the function one after another until we're
* executing the final command and get a result or we run out of
* commands.
*/
while (es != (execution_state *) NULL)
{
result = postquel_execute(es,
fcache,
funcNode->func_tlist,
args,
isNull);
if (es->status != F_EXEC_DONE)
break;
es = es->next;
}
/*
* If we've gone through every command in this function, we are done.
*/
if (es == (execution_state *) NULL)
{
/*
* Reset the execution states to start over again
*/
es = (execution_state *) fcache->func_state;
while (es)
{
es->status = F_EXEC_START;
es = es->next;
}
/*
* Let caller know we're finished.
*/
*isDone = true;
SetScanCommandId(savedId);
return (fcache->oneResult) ? result : (Datum) NULL;
}
/*
* If we got a result from a command within the function it has to be
* the final command. All others shouldn't be returing anything.
*/
Assert(LAST_POSTQUEL_COMMAND(es));
*isDone = false;
SetScanCommandId(savedId);
return result;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,56 +1,56 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* nodeAppend.c-- * nodeAppend.c--
* routines to handle append nodes. * routines to handle append nodes.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.5 1997/08/19 21:31:07 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.6 1997/09/07 04:41:30 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
/* INTERFACE ROUTINES /* INTERFACE ROUTINES
* ExecInitAppend - initialize the append node * ExecInitAppend - initialize the append node
* ExecProcAppend - retrieve the next tuple from the node * ExecProcAppend - retrieve the next tuple from the node
* ExecEndAppend - shut down the append node * ExecEndAppend - shut down the append node
* *
* NOTES * NOTES
* Each append node contains a list of one or more subplans which * Each append node contains a list of one or more subplans which
* must be iteratively processed (forwards or backwards). * must be iteratively processed (forwards or backwards).
* Tuples are retrieved by executing the 'whichplan'th subplan * Tuples are retrieved by executing the 'whichplan'th subplan
* until the subplan stops returning tuples, at which point that * until the subplan stops returning tuples, at which point that
* plan is shut down and the next started up. * plan is shut down and the next started up.
* *
* Append nodes don't make use of their left and right * Append nodes don't make use of their left and right
* subtrees, rather they maintain a list of subplans so * subtrees, rather they maintain a list of subplans so
* a typical append node looks like this in the plan tree: * a typical append node looks like this in the plan tree:
* *
* ... * ...
* / * /
* Append -------+------+------+--- nil * Append -------+------+------+--- nil
* / \ | | | * / \ | | |
* nil nil ... ... ... * nil nil ... ... ...
* subplans * subplans
* *
* Append nodes are currently used to support inheritance * Append nodes are currently used to support inheritance
* queries, where several relations need to be scanned. * queries, where several relations need to be scanned.
* For example, in our standard person/student/employee/student-emp * For example, in our standard person/student/employee/student-emp
* example, where student and employee inherit from person * example, where student and employee inherit from person
* and student-emp inherits from student and employee, the * and student-emp inherits from student and employee, the
* query: * query:
* *
* retrieve (e.name) from e in person* * retrieve (e.name) from e in person*
* *
* generates the plan: * generates the plan:
* *
* | * |
* Append -------+-------+--------+--------+ * Append -------+-------+--------+--------+
* / \ | | | | * / \ | | | |
* nil nil Scan Scan Scan Scan * nil nil Scan Scan Scan Scan
* | | | | * | | | |
* person employee student student-emp * person employee student student-emp
*/ */
#include "postgres.h" #include "postgres.h"
@ -62,429 +62,451 @@
#include "executor/nodeIndexscan.h" #include "executor/nodeIndexscan.h"
#include "utils/palloc.h" #include "utils/palloc.h"
#include "utils/mcxt.h" #include "utils/mcxt.h"
#include "parser/parsetree.h" /* for rt_store() macro */ #include "parser/parsetree.h" /* for rt_store() macro */
static bool exec_append_initialize_next(Append *node); static bool exec_append_initialize_next(Append * node);
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* exec-append-initialize-next * exec-append-initialize-next
* *
* Sets up the append node state (i.e. the append state node) * Sets up the append node state (i.e. the append state node)
* for the "next" scan. * for the "next" scan.
* *
* Returns t iff there is a "next" scan to process. * Returns t iff there is a "next" scan to process.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
static bool static bool
exec_append_initialize_next(Append *node) exec_append_initialize_next(Append * node)
{ {
EState *estate; EState *estate;
AppendState *unionstate; AppendState *unionstate;
TupleTableSlot *result_slot; TupleTableSlot *result_slot;
List *rangeTable; List *rangeTable;
int whichplan; int whichplan;
int nplans; int nplans;
List *rtentries; List *rtentries;
ResTarget *rtentry; ResTarget *rtentry;
Index unionrelid; Index unionrelid;
/* ---------------- /* ----------------
* get information from the append node * get information from the append node
* ---------------- * ----------------
*/ */
estate = node->plan.state; estate = node->plan.state;
unionstate = node->unionstate; unionstate = node->unionstate;
result_slot = unionstate->cstate.cs_ResultTupleSlot; result_slot = unionstate->cstate.cs_ResultTupleSlot;
rangeTable = estate->es_range_table; rangeTable = estate->es_range_table;
whichplan = unionstate->as_whichplan; whichplan = unionstate->as_whichplan;
nplans = unionstate->as_nplans; nplans = unionstate->as_nplans;
rtentries = node->unionrtentries; rtentries = node->unionrtentries;
if (whichplan < 0) { if (whichplan < 0)
/* ---------------- {
* if scanning in reverse, we start at /* ----------------
* the last scan in the list and then * if scanning in reverse, we start at
* proceed back to the first.. in any case * the last scan in the list and then
* we inform ExecProcAppend that we are * proceed back to the first.. in any case
* at the end of the line by returning FALSE * we inform ExecProcAppend that we are
* ---------------- * at the end of the line by returning FALSE
*/ * ----------------
unionstate->as_whichplan = 0; */
return FALSE; unionstate->as_whichplan = 0;
return FALSE;
} else if (whichplan >= nplans) {
/* ----------------
* as above, end the scan if we go beyond
* the last scan in our list..
* ----------------
*/
unionstate->as_whichplan = nplans - 1;
return FALSE;
} else {
/* ----------------
* initialize the scan
* (and update the range table appropriately)
* (doesn't this leave the range table hosed for anybody upstream
* of the Append node??? - jolly )
* ----------------
*/
if (node->unionrelid > 0) {
rtentry = nth(whichplan, rtentries);
if (rtentry == NULL)
elog(DEBUG, "exec_append_initialize_next: rtentry is nil");
unionrelid = node->unionrelid;
rt_store(unionrelid, rangeTable, rtentry);
if (unionstate->as_junkFilter_list) {
estate->es_junkFilter =
(JunkFilter*)nth(whichplan,
unionstate->as_junkFilter_list);
}
if (unionstate->as_result_relation_info_list) {
estate->es_result_relation_info =
(RelationInfo*) nth(whichplan,
unionstate->as_result_relation_info_list);
}
result_slot->ttc_whichplan = whichplan;
} }
else if (whichplan >= nplans)
{
/* ----------------
* as above, end the scan if we go beyond
* the last scan in our list..
* ----------------
*/
unionstate->as_whichplan = nplans - 1;
return FALSE;
return TRUE; }
} else
{
/* ----------------
* initialize the scan
* (and update the range table appropriately)
* (doesn't this leave the range table hosed for anybody upstream
* of the Append node??? - jolly )
* ----------------
*/
if (node->unionrelid > 0)
{
rtentry = nth(whichplan, rtentries);
if (rtentry == NULL)
elog(DEBUG, "exec_append_initialize_next: rtentry is nil");
unionrelid = node->unionrelid;
rt_store(unionrelid, rangeTable, rtentry);
if (unionstate->as_junkFilter_list)
{
estate->es_junkFilter =
(JunkFilter *) nth(whichplan,
unionstate->as_junkFilter_list);
}
if (unionstate->as_result_relation_info_list)
{
estate->es_result_relation_info =
(RelationInfo *) nth(whichplan,
unionstate->as_result_relation_info_list);
}
result_slot->ttc_whichplan = whichplan;
}
return TRUE;
}
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecInitAppend * ExecInitAppend
* *
* Begins all of the subscans of the append node, storing the * Begins all of the subscans of the append node, storing the
* scan structures in the 'initialized' vector of the append-state * scan structures in the 'initialized' vector of the append-state
* structure. * structure.
* *
* (This is potentially wasteful, since the entire result of the * (This is potentially wasteful, since the entire result of the
* append node may not be scanned, but this way all of the * append node may not be scanned, but this way all of the
* structures get allocated in the executor's top level memory * structures get allocated in the executor's top level memory
* block instead of that of the call to ExecProcAppend.) * block instead of that of the call to ExecProcAppend.)
* *
* Returns the scan result of the first scan. * Returns the scan result of the first scan.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
bool bool
ExecInitAppend(Append *node, EState *estate, Plan *parent) ExecInitAppend(Append * node, EState * estate, Plan * parent)
{ {
AppendState *unionstate; AppendState *unionstate;
int nplans; int nplans;
List *resultList = NULL; List *resultList = NULL;
List *rtentries; List *rtentries;
List *unionplans; List *unionplans;
bool *initialized; bool *initialized;
int i; int i;
Plan *initNode; Plan *initNode;
List *junkList; List *junkList;
RelationInfo *es_rri = estate->es_result_relation_info; RelationInfo *es_rri = estate->es_result_relation_info;
/* ---------------- /* ----------------
* assign execution state to node and get information * assign execution state to node and get information
* for append state * for append state
* ---------------- * ----------------
*/ */
node->plan.state = estate; node->plan.state = estate;
unionplans = node->unionplans; unionplans = node->unionplans;
nplans = length(unionplans); nplans = length(unionplans);
rtentries = node->unionrtentries; rtentries = node->unionrtentries;
CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext); CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
initialized = (bool *)palloc(nplans * sizeof(bool)); initialized = (bool *) palloc(nplans * sizeof(bool));
/* ---------------- /* ----------------
* create new AppendState for our append node * create new AppendState for our append node
* ---------------- * ----------------
*/ */
unionstate = makeNode(AppendState); unionstate = makeNode(AppendState);
unionstate->as_whichplan = 0; unionstate->as_whichplan = 0;
unionstate->as_nplans = nplans; unionstate->as_nplans = nplans;
unionstate->as_initialized = initialized; unionstate->as_initialized = initialized;
unionstate->as_rtentries = rtentries; unionstate->as_rtentries = rtentries;
node->unionstate = unionstate; node->unionstate = unionstate;
/* ---------------- /* ----------------
* Miscellanious initialization * Miscellanious initialization
* *
* + assign node's base_id * + assign node's base_id
* + assign debugging hooks * + assign debugging hooks
* *
* Append plans don't have expression contexts because they * Append plans don't have expression contexts because they
* never call ExecQual or ExecTargetList. * never call ExecQual or ExecTargetList.
* ---------------- * ----------------
*/ */
ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent); ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent);
#define APPEND_NSLOTS 1 #define APPEND_NSLOTS 1
/* ---------------- /* ----------------
* append nodes still have Result slots, which hold pointers * append nodes still have Result slots, which hold pointers
* to tuples, so we have to initialize them.. * to tuples, so we have to initialize them..
* ---------------- * ----------------
*/
ExecInitResultTupleSlot(estate, &unionstate->cstate);
/*
* If the inherits rtentry is the result relation, we have to make
* a result relation info list for all inheritors so we can update
* their indices and put the result tuples in the right place etc.
*
* e.g. replace p (age = p.age + 1) from p in person*
*/
if ((es_rri != (RelationInfo*)NULL) &&
(node->unionrelid == es_rri->ri_RangeTableIndex))
{
RelationInfo *rri;
List *rtentryP;
foreach(rtentryP,rtentries)
{
Oid reloid;
RangeTblEntry *rtentry = lfirst(rtentryP);
reloid = rtentry->relid;
rri = makeNode(RelationInfo);
rri->ri_RangeTableIndex = es_rri->ri_RangeTableIndex;
rri->ri_RelationDesc = heap_open(reloid);
rri->ri_NumIndices = 0;
rri->ri_IndexRelationDescs = NULL; /* index descs */
rri->ri_IndexRelationInfo = NULL; /* index key info */
resultList = lcons(rri,resultList);
ExecOpenIndices(reloid, rri);
}
unionstate->as_result_relation_info_list = resultList;
}
/* ----------------
* call ExecInitNode on each of the plans in our list
* and save the results into the array "initialized"
* ----------------
*/
junkList = NIL;
for(i = 0; i < nplans ; i++ ) {
JunkFilter *j;
List *targetList;
/* ----------------
* NOTE: we first modify range table in
* exec_append_initialize_next() and
* then initialize the subnode,
* since it may use the range table.
* ----------------
*/
unionstate->as_whichplan = i;
exec_append_initialize_next(node);
initNode = (Plan *) nth(i, unionplans);
initialized[i] = ExecInitNode(initNode, estate, (Plan*) node);
/* ---------------
* Each targetlist in the subplan may need its own junk filter
*
* This is true only when the reln being replaced/deleted is
* the one that we're looking at the subclasses of
* ---------------
*/ */
if ((es_rri != (RelationInfo*)NULL) && ExecInitResultTupleSlot(estate, &unionstate->cstate);
(node->unionrelid == es_rri->ri_RangeTableIndex)) {
targetList = initNode->targetlist; /*
j = (JunkFilter *) ExecInitJunkFilter(targetList); * If the inherits rtentry is the result relation, we have to make a
junkList = lappend(junkList, j); * result relation info list for all inheritors so we can update their
* indices and put the result tuples in the right place etc.
*
* e.g. replace p (age = p.age + 1) from p in person*
*/
if ((es_rri != (RelationInfo *) NULL) &&
(node->unionrelid == es_rri->ri_RangeTableIndex))
{
RelationInfo *rri;
List *rtentryP;
foreach(rtentryP, rtentries)
{
Oid reloid;
RangeTblEntry *rtentry = lfirst(rtentryP);
reloid = rtentry->relid;
rri = makeNode(RelationInfo);
rri->ri_RangeTableIndex = es_rri->ri_RangeTableIndex;
rri->ri_RelationDesc = heap_open(reloid);
rri->ri_NumIndices = 0;
rri->ri_IndexRelationDescs = NULL; /* index descs */
rri->ri_IndexRelationInfo = NULL; /* index key info */
resultList = lcons(rri, resultList);
ExecOpenIndices(reloid, rri);
}
unionstate->as_result_relation_info_list = resultList;
} }
/* ----------------
* call ExecInitNode on each of the plans in our list
* and save the results into the array "initialized"
* ----------------
*/
junkList = NIL;
} for (i = 0; i < nplans; i++)
unionstate->as_junkFilter_list = junkList; {
if (junkList != NIL) JunkFilter *j;
estate->es_junkFilter = (JunkFilter *)lfirst(junkList); List *targetList;
/* ---------------- /* ----------------
* initialize the return type from the appropriate subplan. * NOTE: we first modify range table in
* ---------------- * exec_append_initialize_next() and
*/ * then initialize the subnode,
initNode = (Plan *) nth(0, unionplans); * since it may use the range table.
ExecAssignResultType(&unionstate->cstate, * ----------------
/* ExecGetExecTupDesc(initNode), */ */
ExecGetTupType(initNode)); unionstate->as_whichplan = i;
unionstate->cstate.cs_ProjInfo = NULL; exec_append_initialize_next(node);
/* ---------------- initNode = (Plan *) nth(i, unionplans);
* return the result from the first subplan's initialization initialized[i] = ExecInitNode(initNode, estate, (Plan *) node);
* ----------------
*/ /* ---------------
unionstate->as_whichplan = 0; * Each targetlist in the subplan may need its own junk filter
exec_append_initialize_next(node); *
* This is true only when the reln being replaced/deleted is
* the one that we're looking at the subclasses of
* ---------------
*/
if ((es_rri != (RelationInfo *) NULL) &&
(node->unionrelid == es_rri->ri_RangeTableIndex))
{
targetList = initNode->targetlist;
j = (JunkFilter *) ExecInitJunkFilter(targetList);
junkList = lappend(junkList, j);
}
}
unionstate->as_junkFilter_list = junkList;
if (junkList != NIL)
estate->es_junkFilter = (JunkFilter *) lfirst(junkList);
/* ----------------
* initialize the return type from the appropriate subplan.
* ----------------
*/
initNode = (Plan *) nth(0, unionplans);
ExecAssignResultType(&unionstate->cstate,
/* ExecGetExecTupDesc(initNode), */
ExecGetTupType(initNode));
unionstate->cstate.cs_ProjInfo = NULL;
/* ----------------
* return the result from the first subplan's initialization
* ----------------
*/
unionstate->as_whichplan = 0;
exec_append_initialize_next(node);
#if 0 #if 0
result = (List *) initialized[0]; result = (List *) initialized[0];
#endif #endif
return TRUE; return TRUE;
} }
int int
ExecCountSlotsAppend(Append *node) ExecCountSlotsAppend(Append * node)
{ {
List *plan; List *plan;
List *unionplans = node->unionplans; List *unionplans = node->unionplans;
int nSlots = 0; int nSlots = 0;
foreach (plan,unionplans) { foreach(plan, unionplans)
nSlots += ExecCountSlotsNode((Plan *)lfirst(plan)); {
} nSlots += ExecCountSlotsNode((Plan *) lfirst(plan));
return nSlots + APPEND_NSLOTS; }
return nSlots + APPEND_NSLOTS;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecProcAppend * ExecProcAppend
* *
* Handles the iteration over the multiple scans. * Handles the iteration over the multiple scans.
* *
* NOTE: Can't call this ExecAppend, that name is used in execMain.l * NOTE: Can't call this ExecAppend, that name is used in execMain.l
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
TupleTableSlot * TupleTableSlot *
ExecProcAppend(Append *node) ExecProcAppend(Append * node)
{ {
EState *estate; EState *estate;
AppendState *unionstate; AppendState *unionstate;
int whichplan; int whichplan;
List *unionplans; List *unionplans;
Plan *subnode; Plan *subnode;
TupleTableSlot *result; TupleTableSlot *result;
TupleTableSlot *result_slot; TupleTableSlot *result_slot;
ScanDirection direction; ScanDirection direction;
/* ----------------
* get information from the node
* ----------------
*/
unionstate = node->unionstate;
estate = node->plan.state;
direction = estate->es_direction;
unionplans = node->unionplans;
whichplan = unionstate->as_whichplan;
result_slot = unionstate->cstate.cs_ResultTupleSlot;
/* ----------------
* figure out which subplan we are currently processing
* ----------------
*/
subnode = (Plan *) nth(whichplan, unionplans);
if (subnode == NULL)
elog(DEBUG, "ExecProcAppend: subnode is NULL");
/* ----------------
* get a tuple from the subplan
* ----------------
*/
result = ExecProcNode(subnode, (Plan*)node);
if (! TupIsNull(result)) {
/* ----------------
* if the subplan gave us something then place a copy of
* whatever we get into our result slot and return it, else..
* ----------------
*/
return ExecStoreTuple(result->val,
result_slot, result->ttc_buffer, false);
} else {
/* ----------------
* .. go on to the "next" subplan in the appropriate
* direction and try processing again (recursively)
* ----------------
*/
whichplan = unionstate->as_whichplan;
if (ScanDirectionIsForward(direction))
{
unionstate->as_whichplan = whichplan + 1;
}
else
{
unionstate->as_whichplan = whichplan - 1;
}
/* ---------------- /* ----------------
* return something from next node or an empty slot * get information from the node
* all of our subplans have been exhausted.
* ---------------- * ----------------
*/ */
if (exec_append_initialize_next(node)) { unionstate = node->unionstate;
ExecSetSlotDescriptorIsNew(result_slot, true); estate = node->plan.state;
return direction = estate->es_direction;
ExecProcAppend(node);
} else unionplans = node->unionplans;
return ExecClearTuple(result_slot); whichplan = unionstate->as_whichplan;
} result_slot = unionstate->cstate.cs_ResultTupleSlot;
/* ----------------
* figure out which subplan we are currently processing
* ----------------
*/
subnode = (Plan *) nth(whichplan, unionplans);
if (subnode == NULL)
elog(DEBUG, "ExecProcAppend: subnode is NULL");
/* ----------------
* get a tuple from the subplan
* ----------------
*/
result = ExecProcNode(subnode, (Plan *) node);
if (!TupIsNull(result))
{
/* ----------------
* if the subplan gave us something then place a copy of
* whatever we get into our result slot and return it, else..
* ----------------
*/
return ExecStoreTuple(result->val,
result_slot, result->ttc_buffer, false);
}
else
{
/* ----------------
* .. go on to the "next" subplan in the appropriate
* direction and try processing again (recursively)
* ----------------
*/
whichplan = unionstate->as_whichplan;
if (ScanDirectionIsForward(direction))
{
unionstate->as_whichplan = whichplan + 1;
}
else
{
unionstate->as_whichplan = whichplan - 1;
}
/* ----------------
* return something from next node or an empty slot
* all of our subplans have been exhausted.
* ----------------
*/
if (exec_append_initialize_next(node))
{
ExecSetSlotDescriptorIsNew(result_slot, true);
return
ExecProcAppend(node);
}
else
return ExecClearTuple(result_slot);
}
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecEndAppend * ExecEndAppend
* *
* Shuts down the subscans of the append node. * Shuts down the subscans of the append node.
* *
* Returns nothing of interest. * Returns nothing of interest.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
ExecEndAppend(Append *node) ExecEndAppend(Append * node)
{ {
AppendState *unionstate; AppendState *unionstate;
int nplans; int nplans;
List *unionplans; List *unionplans;
bool *initialized; bool *initialized;
int i; int i;
List *resultRelationInfoList; List *resultRelationInfoList;
RelationInfo *resultRelationInfo; RelationInfo *resultRelationInfo;
/* ---------------- /* ----------------
* get information from the node * get information from the node
* ---------------- * ----------------
*/ */
unionstate = node->unionstate; unionstate = node->unionstate;
unionplans = node->unionplans; unionplans = node->unionplans;
nplans = unionstate->as_nplans; nplans = unionstate->as_nplans;
initialized = unionstate->as_initialized; initialized = unionstate->as_initialized;
/* ---------------- /* ----------------
* shut down each of the subscans * shut down each of the subscans
* ---------------- * ----------------
*/ */
for(i = 0; i < nplans; i++) { for (i = 0; i < nplans; i++)
if (initialized[i]==TRUE) { {
ExecEndNode( (Plan *) nth(i, unionplans), (Plan*)node ); if (initialized[i] == TRUE)
} {
} ExecEndNode((Plan *) nth(i, unionplans), (Plan *) node);
}
}
/* ---------------- /* ----------------
* close out the different result relations * close out the different result relations
* ---------------- * ----------------
*/ */
resultRelationInfoList = unionstate->as_result_relation_info_list; resultRelationInfoList = unionstate->as_result_relation_info_list;
while (resultRelationInfoList != NIL) { while (resultRelationInfoList != NIL)
Relation resultRelationDesc; {
Relation resultRelationDesc;
resultRelationInfo = (RelationInfo*) lfirst(resultRelationInfoList); resultRelationInfo = (RelationInfo *) lfirst(resultRelationInfoList);
resultRelationDesc = resultRelationInfo->ri_RelationDesc; resultRelationDesc = resultRelationInfo->ri_RelationDesc;
heap_close(resultRelationDesc); heap_close(resultRelationDesc);
pfree(resultRelationInfo); pfree(resultRelationInfo);
resultRelationInfoList = lnext(resultRelationInfoList); resultRelationInfoList = lnext(resultRelationInfoList);
} }
if (unionstate->as_result_relation_info_list) if (unionstate->as_result_relation_info_list)
pfree(unionstate->as_result_relation_info_list); pfree(unionstate->as_result_relation_info_list);
/* XXX should free unionstate->as_rtentries and unionstate->as_junkfilter_list here */ /*
* XXX should free unionstate->as_rtentries and
* unionstate->as_junkfilter_list here
*/
} }

View File

@ -1,19 +1,19 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* nodeGroup.c-- * nodeGroup.c--
* Routines to handle group nodes (used for queries with GROUP BY clause). * Routines to handle group nodes (used for queries with GROUP BY clause).
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* DESCRIPTION * DESCRIPTION
* The Group node is designed for handling queries with a GROUP BY clause. * The Group node is designed for handling queries with a GROUP BY clause.
* It's outer plan must be a sort node. It assumes that the tuples it gets * It's outer plan must be a sort node. It assumes that the tuples it gets
* back from the outer plan is sorted in the order specified by the group * back from the outer plan is sorted in the order specified by the group
* columns. (ie. tuples from the same group are consecutive) * columns. (ie. tuples from the same group are consecutive)
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.5 1997/01/10 20:17:35 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.6 1997/09/07 04:41:31 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -28,329 +28,348 @@
#include "executor/executor.h" #include "executor/executor.h"
#include "executor/nodeGroup.h" #include "executor/nodeGroup.h"
static TupleTableSlot *ExecGroupEveryTuple(Group *node); static TupleTableSlot *ExecGroupEveryTuple(Group * node);
static TupleTableSlot *ExecGroupOneTuple(Group *node); static TupleTableSlot *ExecGroupOneTuple(Group * node);
static bool sameGroup(TupleTableSlot *oldslot, TupleTableSlot *newslot, static bool
int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc); sameGroup(TupleTableSlot * oldslot, TupleTableSlot * newslot,
int numCols, AttrNumber * grpColIdx, TupleDesc tupdesc);
/* --------------------------------------- /* ---------------------------------------
* ExecGroup - * ExecGroup -
* *
* There are two modes in which tuples are returned by ExecGroup. If * There are two modes in which tuples are returned by ExecGroup. If
* tuplePerGroup is TRUE, every tuple from the same group will be * tuplePerGroup is TRUE, every tuple from the same group will be
* returned, followed by a NULL at the end of each group. This is * returned, followed by a NULL at the end of each group. This is
* useful for Agg node which needs to aggregate over tuples of the same * useful for Agg node which needs to aggregate over tuples of the same
* group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary) * group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary)
* *
* If tuplePerGroup is FALSE, only one tuple per group is returned. The * If tuplePerGroup is FALSE, only one tuple per group is returned. The
* tuple returned contains only the group columns. NULL is returned only * tuple returned contains only the group columns. NULL is returned only
* at the end when no more groups is present. This is useful when * at the end when no more groups is present. This is useful when
* the query does not involve aggregates. (eg. SELECT salary FROM emp * the query does not involve aggregates. (eg. SELECT salary FROM emp
* GROUP BY salary) * GROUP BY salary)
* ------------------------------------------ * ------------------------------------------
*/ */
TupleTableSlot * TupleTableSlot *
ExecGroup(Group *node) ExecGroup(Group * node)
{ {
if (node->tuplePerGroup) if (node->tuplePerGroup)
return ExecGroupEveryTuple(node); return ExecGroupEveryTuple(node);
else else
return ExecGroupOneTuple(node); return ExecGroupOneTuple(node);
} }
/* /*
* ExecGroupEveryTuple - * ExecGroupEveryTuple -
* return every tuple with a NULL between each group * return every tuple with a NULL between each group
*/ */
static TupleTableSlot * static TupleTableSlot *
ExecGroupEveryTuple(Group *node) ExecGroupEveryTuple(Group * node)
{ {
GroupState *grpstate; GroupState *grpstate;
EState *estate; EState *estate;
ExprContext *econtext; ExprContext *econtext;
HeapTuple outerTuple = NULL; HeapTuple outerTuple = NULL;
TupleTableSlot *outerslot, *lastslot; TupleTableSlot *outerslot,
ProjectionInfo *projInfo; *lastslot;
TupleTableSlot *resultSlot; ProjectionInfo *projInfo;
TupleTableSlot *resultSlot;
bool isDone; bool isDone;
/* --------------------- /* ---------------------
* get state info from node * get state info from node
* --------------------- * ---------------------
*/
grpstate = node->grpstate;
if (grpstate->grp_done)
return NULL;
estate = node->plan.state;
econtext = grpstate->csstate.cstate.cs_ExprContext;
if (grpstate->grp_useLastTuple) {
/*
* we haven't returned last tuple yet because it is not of the
* same group
*/ */
grpstate->grp_useLastTuple = FALSE; grpstate = node->grpstate;
if (grpstate->grp_done)
return NULL;
ExecStoreTuple(grpstate->grp_lastSlot->val, estate = node->plan.state;
grpstate->csstate.css_ScanTupleSlot,
grpstate->grp_lastSlot->ttc_buffer, econtext = grpstate->csstate.cstate.cs_ExprContext;
false);
} else { if (grpstate->grp_useLastTuple)
outerslot = ExecProcNode(outerPlan(node), (Plan*)node); {
if (outerslot)
outerTuple = outerslot->val; /*
if (!HeapTupleIsValid(outerTuple)) { * we haven't returned last tuple yet because it is not of the
grpstate->grp_done = TRUE; * same group
return NULL; */
grpstate->grp_useLastTuple = FALSE;
ExecStoreTuple(grpstate->grp_lastSlot->val,
grpstate->csstate.css_ScanTupleSlot,
grpstate->grp_lastSlot->ttc_buffer,
false);
}
else
{
outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
if (outerslot)
outerTuple = outerslot->val;
if (!HeapTupleIsValid(outerTuple))
{
grpstate->grp_done = TRUE;
return NULL;
}
/* ----------------
* Compare with last tuple and see if this tuple is of
* the same group.
* ----------------
*/
lastslot = grpstate->csstate.css_ScanTupleSlot;
if (lastslot->val != NULL &&
(!sameGroup(lastslot, outerslot,
node->numCols, node->grpColIdx,
ExecGetScanType(&grpstate->csstate))))
{
/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
grpstate->grp_useLastTuple = TRUE;
/* save it for next time */
grpstate->grp_lastSlot = outerslot;
/*
* signifies the end of the group
*/
return NULL;
}
ExecStoreTuple(outerTuple,
grpstate->csstate.css_ScanTupleSlot,
outerslot->ttc_buffer,
false);
} }
/* ---------------- /* ----------------
* Compare with last tuple and see if this tuple is of * form a projection tuple, store it in the result tuple
* the same group. * slot and return it.
* ---------------- * ----------------
*/ */
lastslot = grpstate->csstate.css_ScanTupleSlot; projInfo = grpstate->csstate.cstate.cs_ProjInfo;
if (lastslot->val != NULL && econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
(!sameGroup(lastslot, outerslot, resultSlot = ExecProject(projInfo, &isDone);
node->numCols, node->grpColIdx,
ExecGetScanType(&grpstate->csstate)))) {
/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
grpstate->grp_useLastTuple = TRUE; return resultSlot;
/* save it for next time */
grpstate->grp_lastSlot = outerslot;
/*
* signifies the end of the group
*/
return NULL;
}
ExecStoreTuple(outerTuple,
grpstate->csstate.css_ScanTupleSlot,
outerslot->ttc_buffer,
false);
}
/* ----------------
* form a projection tuple, store it in the result tuple
* slot and return it.
* ----------------
*/
projInfo = grpstate->csstate.cstate.cs_ProjInfo;
econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
resultSlot = ExecProject(projInfo, &isDone);
return resultSlot;
} }
/* /*
* ExecGroupOneTuple - * ExecGroupOneTuple -
* returns one tuple per group, a NULL at the end when there are no more * returns one tuple per group, a NULL at the end when there are no more
* tuples. * tuples.
*/ */
static TupleTableSlot * static TupleTableSlot *
ExecGroupOneTuple(Group *node) ExecGroupOneTuple(Group * node)
{ {
GroupState *grpstate; GroupState *grpstate;
EState *estate; EState *estate;
ExprContext *econtext; ExprContext *econtext;
HeapTuple outerTuple = NULL; HeapTuple outerTuple = NULL;
TupleTableSlot *outerslot, *lastslot; TupleTableSlot *outerslot,
ProjectionInfo *projInfo; *lastslot;
TupleTableSlot *resultSlot; ProjectionInfo *projInfo;
TupleTableSlot *resultSlot;
bool isDone; bool isDone;
/* --------------------- /* ---------------------
* get state info from node * get state info from node
* --------------------- * ---------------------
*/ */
grpstate = node->grpstate; grpstate = node->grpstate;
if (grpstate->grp_done) if (grpstate->grp_done)
return NULL; return NULL;
estate = node->plan.state; estate = node->plan.state;
econtext = node->grpstate->csstate.cstate.cs_ExprContext; econtext = node->grpstate->csstate.cstate.cs_ExprContext;
if (grpstate->grp_useLastTuple) { if (grpstate->grp_useLastTuple)
grpstate->grp_useLastTuple = FALSE; {
ExecStoreTuple(grpstate->grp_lastSlot->val, grpstate->grp_useLastTuple = FALSE;
grpstate->csstate.css_ScanTupleSlot, ExecStoreTuple(grpstate->grp_lastSlot->val,
grpstate->grp_lastSlot->ttc_buffer, grpstate->csstate.css_ScanTupleSlot,
false); grpstate->grp_lastSlot->ttc_buffer,
} else { false);
outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
if (outerslot) outerTuple = outerslot->val;
if (!HeapTupleIsValid(outerTuple)) {
grpstate->grp_done = TRUE;
return NULL;
} }
ExecStoreTuple(outerTuple, else
grpstate->csstate.css_ScanTupleSlot, {
outerslot->ttc_buffer, outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
false); if (outerslot)
} outerTuple = outerslot->val;
lastslot = grpstate->csstate.css_ScanTupleSlot; if (!HeapTupleIsValid(outerTuple))
{
grpstate->grp_done = TRUE;
return NULL;
}
ExecStoreTuple(outerTuple,
grpstate->csstate.css_ScanTupleSlot,
outerslot->ttc_buffer,
false);
}
lastslot = grpstate->csstate.css_ScanTupleSlot;
/* /*
* find all tuples that belong to a group * find all tuples that belong to a group
*/ */
for(;;) { for (;;)
outerslot = ExecProcNode(outerPlan(node), (Plan*)node); {
outerTuple = (outerslot) ? outerslot->val : NULL; outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
if (!HeapTupleIsValid(outerTuple)) { outerTuple = (outerslot) ? outerslot->val : NULL;
/* if (!HeapTupleIsValid(outerTuple))
* we have at least one tuple (lastslot) if we reach here {
*/
grpstate->grp_done = TRUE;
/* return lastslot */ /*
break; * we have at least one tuple (lastslot) if we reach here
*/
grpstate->grp_done = TRUE;
/* return lastslot */
break;
}
/* ----------------
* Compare with last tuple and see if this tuple is of
* the same group.
* ----------------
*/
if ((!sameGroup(lastslot, outerslot,
node->numCols, node->grpColIdx,
ExecGetScanType(&grpstate->csstate))))
{
/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
grpstate->grp_useLastTuple = TRUE;
/* save it for next time */
grpstate->grp_lastSlot = outerslot;
/* return lastslot */
break;
}
ExecStoreTuple(outerTuple,
grpstate->csstate.css_ScanTupleSlot,
outerslot->ttc_buffer,
false);
lastslot = grpstate->csstate.css_ScanTupleSlot;
} }
/* ---------------- ExecStoreTuple(lastslot->val,
* Compare with last tuple and see if this tuple is of grpstate->csstate.css_ScanTupleSlot,
* the same group. lastslot->ttc_buffer,
false);
/* ----------------
* form a projection tuple, store it in the result tuple
* slot and return it.
* ---------------- * ----------------
*/ */
if ((!sameGroup(lastslot, outerslot, projInfo = grpstate->csstate.cstate.cs_ProjInfo;
node->numCols, node->grpColIdx,
ExecGetScanType(&grpstate->csstate)))) {
/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
grpstate->grp_useLastTuple = TRUE; econtext->ecxt_scantuple = lastslot;
resultSlot = ExecProject(projInfo, &isDone);
/* save it for next time */ return resultSlot;
grpstate->grp_lastSlot = outerslot;
/* return lastslot */
break;
}
ExecStoreTuple(outerTuple,
grpstate->csstate.css_ScanTupleSlot,
outerslot->ttc_buffer,
false);
lastslot = grpstate->csstate.css_ScanTupleSlot;
}
ExecStoreTuple(lastslot->val,
grpstate->csstate.css_ScanTupleSlot,
lastslot->ttc_buffer,
false);
/* ----------------
* form a projection tuple, store it in the result tuple
* slot and return it.
* ----------------
*/
projInfo = grpstate->csstate.cstate.cs_ProjInfo;
econtext->ecxt_scantuple = lastslot;
resultSlot = ExecProject(projInfo, &isDone);
return resultSlot;
} }
/* ----------------- /* -----------------
* ExecInitGroup * ExecInitGroup
* *
* Creates the run-time information for the group node produced by the * Creates the run-time information for the group node produced by the
* planner and initializes its outer subtree * planner and initializes its outer subtree
* ----------------- * -----------------
*/ */
bool bool
ExecInitGroup(Group *node, EState *estate, Plan *parent) ExecInitGroup(Group * node, EState * estate, Plan * parent)
{ {
GroupState *grpstate; GroupState *grpstate;
Plan *outerPlan; Plan *outerPlan;
/* /*
* assign the node's execution state * assign the node's execution state
*/ */
node->plan.state = estate; node->plan.state = estate;
/* /*
* create state structure * create state structure
*/ */
grpstate = makeNode(GroupState); grpstate = makeNode(GroupState);
node->grpstate = grpstate; node->grpstate = grpstate;
grpstate->grp_useLastTuple = FALSE; grpstate->grp_useLastTuple = FALSE;
grpstate->grp_done = FALSE; grpstate->grp_done = FALSE;
/* /*
* assign node's base id and create expression context * assign node's base id and create expression context
*/ */
ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate, ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
(Plan*) parent); (Plan *) parent);
ExecAssignExprContext(estate, &grpstate->csstate.cstate); ExecAssignExprContext(estate, &grpstate->csstate.cstate);
#define GROUP_NSLOTS 2 #define GROUP_NSLOTS 2
/*
* tuple table initialization
*/
ExecInitScanTupleSlot(estate, &grpstate->csstate);
ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
/* /*
* initializes child nodes * tuple table initialization
*/ */
outerPlan = outerPlan(node); ExecInitScanTupleSlot(estate, &grpstate->csstate);
ExecInitNode(outerPlan, estate, (Plan *)node); ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
/* ---------------- /*
* initialize tuple type. * initializes child nodes
* ---------------- */
*/ outerPlan = outerPlan(node);
ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate); ExecInitNode(outerPlan, estate, (Plan *) node);
/* /* ----------------
* Initialize tuple type for both result and scan. * initialize tuple type.
* This node does no projection * ----------------
*/ */
ExecAssignResultTypeFromTL((Plan*) node, &grpstate->csstate.cstate); ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
ExecAssignProjectionInfo((Plan*)node, &grpstate->csstate.cstate);
return TRUE; /*
* Initialize tuple type for both result and scan. This node does no
* projection
*/
ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
return TRUE;
} }
int int
ExecCountSlotsGroup(Group *node) ExecCountSlotsGroup(Group * node)
{ {
return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS; return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
} }
/* ------------------------ /* ------------------------
* ExecEndGroup(node) * ExecEndGroup(node)
* *
* ----------------------- * -----------------------
*/ */
void void
ExecEndGroup(Group *node) ExecEndGroup(Group * node)
{ {
GroupState *grpstate; GroupState *grpstate;
Plan *outerPlan; Plan *outerPlan;
grpstate = node->grpstate; grpstate = node->grpstate;
ExecFreeProjectionInfo(&grpstate->csstate.cstate); ExecFreeProjectionInfo(&grpstate->csstate.cstate);
outerPlan = outerPlan(node); outerPlan = outerPlan(node);
ExecEndNode(outerPlan, (Plan*)node); ExecEndNode(outerPlan, (Plan *) node);
/* clean up tuple table */ /* clean up tuple table */
ExecClearTuple(grpstate->csstate.css_ScanTupleSlot); ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
} }
/***************************************************************************** /*****************************************************************************
@ -360,54 +379,63 @@ ExecEndGroup(Group *node)
/* /*
* code swiped from nodeUnique.c * code swiped from nodeUnique.c
*/ */
static bool static bool
sameGroup(TupleTableSlot *oldslot, sameGroup(TupleTableSlot * oldslot,
TupleTableSlot *newslot, TupleTableSlot * newslot,
int numCols, int numCols,
AttrNumber *grpColIdx, AttrNumber * grpColIdx,
TupleDesc tupdesc) TupleDesc tupdesc)
{ {
bool isNull1,isNull2; bool isNull1,
char *attr1, *attr2; isNull2;
char *val1, *val2; char *attr1,
int i; *attr2;
AttrNumber att; char *val1,
Oid typoutput; *val2;
int i;
AttrNumber att;
Oid typoutput;
for(i = 0; i < numCols; i++) { for (i = 0; i < numCols; i++)
att = grpColIdx[i]; {
typoutput = typtoout((Oid)tupdesc->attrs[att-1]->atttypid); att = grpColIdx[i];
typoutput = typtoout((Oid) tupdesc->attrs[att - 1]->atttypid);
attr1 = heap_getattr(oldslot->val, attr1 = heap_getattr(oldslot->val,
InvalidBuffer, InvalidBuffer,
att, att,
tupdesc, tupdesc,
&isNull1); &isNull1);
attr2 = heap_getattr(newslot->val, attr2 = heap_getattr(newslot->val,
InvalidBuffer, InvalidBuffer,
att, att,
tupdesc, tupdesc,
&isNull2); &isNull2);
if (isNull1 == isNull2) { if (isNull1 == isNull2)
if (isNull1) /* both are null, they are equal */ {
continue; if (isNull1) /* both are null, they are equal */
continue;
val1 = fmgr(typoutput, attr1, val1 = fmgr(typoutput, attr1,
gettypelem(tupdesc->attrs[att-1]->atttypid)); gettypelem(tupdesc->attrs[att - 1]->atttypid));
val2 = fmgr(typoutput, attr2, val2 = fmgr(typoutput, attr2,
gettypelem(tupdesc->attrs[att-1]->atttypid)); gettypelem(tupdesc->attrs[att - 1]->atttypid));
/* now, val1 and val2 are ascii representations so we can /*
use strcmp for comparison */ * now, val1 and val2 are ascii representations so we can use
if (strcmp(val1,val2) != 0) * strcmp for comparison
return FALSE; */
} else { if (strcmp(val1, val2) != 0)
/* one is null and the other isn't, they aren't equal */ return FALSE;
return FALSE; }
else
{
/* one is null and the other isn't, they aren't equal */
return FALSE;
}
} }
}
return TRUE; return TRUE;
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,21 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* nodeMaterial.c-- * nodeMaterial.c--
* Routines to handle materialization nodes. * Routines to handle materialization nodes.
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.6 1997/08/20 14:53:24 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.7 1997/09/07 04:41:36 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
/* /*
* INTERFACE ROUTINES * INTERFACE ROUTINES
* ExecMaterial - generate a temporary relation * ExecMaterial - generate a temporary relation
* ExecInitMaterial - initialize node and subnodes.. * ExecInitMaterial - initialize node and subnodes..
* ExecEndMaterial - shutdown node and subnodes * ExecEndMaterial - shutdown node and subnodes
* *
*/ */
#include "postgres.h" #include "postgres.h"
@ -29,368 +29,373 @@
#include "access/heapam.h" #include "access/heapam.h"
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecMaterial * ExecMaterial
* *
* The first time this is called, ExecMaterial retrieves tuples * The first time this is called, ExecMaterial retrieves tuples
* this node's outer subplan and inserts them into a temporary * this node's outer subplan and inserts them into a temporary
* relation. After this is done, a flag is set indicating that * relation. After this is done, a flag is set indicating that
* the subplan has been materialized. Once the relation is * the subplan has been materialized. Once the relation is
* materialized, the first tuple is then returned. Successive * materialized, the first tuple is then returned. Successive
* calls to ExecMaterial return successive tuples from the temp * calls to ExecMaterial return successive tuples from the temp
* relation. * relation.
* *
* Initial State: * Initial State:
* *
* ExecMaterial assumes the temporary relation has been * ExecMaterial assumes the temporary relation has been
* created and openend by ExecInitMaterial during the prior * created and openend by ExecInitMaterial during the prior
* InitPlan() phase. * InitPlan() phase.
* *
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
TupleTableSlot * /* result tuple from subplan */ TupleTableSlot * /* result tuple from subplan */
ExecMaterial(Material *node) ExecMaterial(Material * node)
{ {
EState *estate; EState *estate;
MaterialState *matstate; MaterialState *matstate;
Plan *outerNode; Plan *outerNode;
ScanDirection dir; ScanDirection dir;
Relation tempRelation; Relation tempRelation;
Relation currentRelation; Relation currentRelation;
HeapScanDesc currentScanDesc; HeapScanDesc currentScanDesc;
HeapTuple heapTuple; HeapTuple heapTuple;
TupleTableSlot *slot; TupleTableSlot *slot;
Buffer buffer; Buffer buffer;
/* ----------------
* get state info from node
* ----------------
*/
matstate = node->matstate;
estate = node->plan.state;
dir = estate->es_direction;
/* ----------------
* the first time we call this, we retrieve all tuples
* from the subplan into a temporary relation and then
* we sort the relation. Subsequent calls return tuples
* from the temporary relation.
* ----------------
*/
if (matstate->mat_Flag == false) {
/* ----------------
* set all relations to be scanned in the forward direction
* while creating the temporary relation.
* ----------------
*/
estate->es_direction = ForwardScanDirection;
/* ---------------- /* ----------------
* if we couldn't create the temp or current relations then * get state info from node
* we print a warning and return NULL.
* ---------------- * ----------------
*/ */
tempRelation = matstate->mat_TempRelation; matstate = node->matstate;
if (tempRelation == NULL) { estate = node->plan.state;
elog(DEBUG, "ExecMaterial: temp relation is NULL! aborting..."); dir = estate->es_direction;
return NULL;
}
currentRelation = matstate->csstate.css_currentRelation; /* ----------------
if (currentRelation == NULL) { * the first time we call this, we retrieve all tuples
elog(DEBUG, "ExecMaterial: current relation is NULL! aborting..."); * from the subplan into a temporary relation and then
return NULL; * we sort the relation. Subsequent calls return tuples
* from the temporary relation.
* ----------------
*/
if (matstate->mat_Flag == false)
{
/* ----------------
* set all relations to be scanned in the forward direction
* while creating the temporary relation.
* ----------------
*/
estate->es_direction = ForwardScanDirection;
/* ----------------
* if we couldn't create the temp or current relations then
* we print a warning and return NULL.
* ----------------
*/
tempRelation = matstate->mat_TempRelation;
if (tempRelation == NULL)
{
elog(DEBUG, "ExecMaterial: temp relation is NULL! aborting...");
return NULL;
}
currentRelation = matstate->csstate.css_currentRelation;
if (currentRelation == NULL)
{
elog(DEBUG, "ExecMaterial: current relation is NULL! aborting...");
return NULL;
}
/* ----------------
* retrieve tuples from the subplan and
* insert them in the temporary relation
* ----------------
*/
outerNode = outerPlan((Plan *) node);
for (;;)
{
slot = ExecProcNode(outerNode, (Plan *) node);
heapTuple = slot->val;
if (heapTuple == NULL)
break;
heap_insert(tempRelation, /* relation desc */
heapTuple); /* heap tuple to insert */
ExecClearTuple(slot);
}
currentRelation = tempRelation;
/* ----------------
* restore to user specified direction
* ----------------
*/
estate->es_direction = dir;
/* ----------------
* now initialize the scan descriptor to scan the
* sorted relation and update the sortstate information
* ----------------
*/
currentScanDesc = heap_beginscan(currentRelation, /* relation */
ScanDirectionIsBackward(dir),
/* bkwd flag */
NowTimeQual, /* time qual */
0, /* num scan keys */
NULL); /* scan keys */
matstate->csstate.css_currentRelation = currentRelation;
matstate->csstate.css_currentScanDesc = currentScanDesc;
ExecAssignScanType(&matstate->csstate,
RelationGetTupleDescriptor(currentRelation));
/* ----------------
* finally set the sorted flag to true
* ----------------
*/
matstate->mat_Flag = true;
} }
/* ---------------- /* ----------------
* retrieve tuples from the subplan and * at this point we know we have a sorted relation so
* insert them in the temporary relation * we preform a simple scan on it with amgetnext()..
* ---------------- * ----------------
*/ */
outerNode = outerPlan((Plan *) node); currentScanDesc = matstate->csstate.css_currentScanDesc;
for (;;) {
slot = ExecProcNode(outerNode, (Plan*) node);
heapTuple = slot->val; heapTuple = heap_getnext(currentScanDesc, /* scan desc */
if (heapTuple == NULL) ScanDirectionIsBackward(dir),
break; /* bkwd flag */
&buffer); /* return: buffer */
heap_insert(tempRelation, /* relation desc */
heapTuple); /* heap tuple to insert */
ExecClearTuple( slot);
}
currentRelation = tempRelation;
/* ---------------- /* ----------------
* restore to user specified direction * put the tuple into the scan tuple slot and return the slot.
* Note: since the tuple is really a pointer to a page, we don't want
* to call pfree() on it..
* ---------------- * ----------------
*/ */
estate->es_direction = dir; slot = (TupleTableSlot *) matstate->csstate.css_ScanTupleSlot;
/* ---------------- return ExecStoreTuple(heapTuple, /* tuple to store */
* now initialize the scan descriptor to scan the slot, /* slot to store in */
* sorted relation and update the sortstate information buffer, /* buffer for this tuple */
* ---------------- false); /* don't pfree this pointer */
*/
currentScanDesc = heap_beginscan(currentRelation, /* relation */
ScanDirectionIsBackward(dir),
/* bkwd flag */
NowTimeQual, /* time qual */
0, /* num scan keys */
NULL); /* scan keys */
matstate->csstate.css_currentRelation = currentRelation;
matstate->csstate.css_currentScanDesc = currentScanDesc;
ExecAssignScanType(&matstate->csstate,
RelationGetTupleDescriptor(currentRelation));
/* ----------------
* finally set the sorted flag to true
* ----------------
*/
matstate->mat_Flag = true;
}
/* ----------------
* at this point we know we have a sorted relation so
* we preform a simple scan on it with amgetnext()..
* ----------------
*/
currentScanDesc = matstate->csstate.css_currentScanDesc;
heapTuple = heap_getnext(currentScanDesc, /* scan desc */
ScanDirectionIsBackward(dir),
/* bkwd flag */
&buffer); /* return: buffer */
/* ----------------
* put the tuple into the scan tuple slot and return the slot.
* Note: since the tuple is really a pointer to a page, we don't want
* to call pfree() on it..
* ----------------
*/
slot = (TupleTableSlot *)matstate->csstate.css_ScanTupleSlot;
return ExecStoreTuple(heapTuple, /* tuple to store */
slot, /* slot to store in */
buffer, /* buffer for this tuple */
false); /* don't pfree this pointer */
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecInitMaterial * ExecInitMaterial
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
bool /* initialization status */ bool /* initialization status */
ExecInitMaterial(Material *node, EState *estate, Plan *parent) ExecInitMaterial(Material * node, EState * estate, Plan * parent)
{ {
MaterialState *matstate; MaterialState *matstate;
Plan *outerPlan; Plan *outerPlan;
TupleDesc tupType; TupleDesc tupType;
Relation tempDesc; Relation tempDesc;
/* int len; */
/* ---------------- /* int len; */
* assign the node's execution state
* ----------------
*/
node->plan.state = estate;
/* ---------------- /* ----------------
* create state structure * assign the node's execution state
* ---------------- * ----------------
*/ */
matstate = makeNode(MaterialState); node->plan.state = estate;
matstate->mat_Flag = false;
matstate->mat_TempRelation = NULL;
node->matstate = matstate;
/* ---------------- /* ----------------
* Miscellanious initialization * create state structure
* * ----------------
* + assign node's base_id */
* + assign debugging hooks and matstate = makeNode(MaterialState);
* + assign result tuple slot matstate->mat_Flag = false;
* matstate->mat_TempRelation = NULL;
* Materialization nodes don't need ExprContexts because node->matstate = matstate;
* they never call ExecQual or ExecTargetList.
* ---------------- /* ----------------
*/ * Miscellanious initialization
ExecAssignNodeBaseInfo(estate, &matstate->csstate.cstate, parent); *
* + assign node's base_id
* + assign debugging hooks and
* + assign result tuple slot
*
* Materialization nodes don't need ExprContexts because
* they never call ExecQual or ExecTargetList.
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &matstate->csstate.cstate, parent);
#define MATERIAL_NSLOTS 1 #define MATERIAL_NSLOTS 1
/* ---------------- /* ----------------
* tuple table initialization * tuple table initialization
* ---------------- * ----------------
*/ */
ExecInitScanTupleSlot(estate, &matstate->csstate); ExecInitScanTupleSlot(estate, &matstate->csstate);
/* ---------------- /* ----------------
* initializes child nodes * initializes child nodes
* ---------------- * ----------------
*/ */
outerPlan = outerPlan((Plan *) node); outerPlan = outerPlan((Plan *) node);
ExecInitNode(outerPlan, estate, (Plan *) node); ExecInitNode(outerPlan, estate, (Plan *) node);
/* ---------------- /* ----------------
* initialize matstate information * initialize matstate information
* ---------------- * ----------------
*/ */
matstate->mat_Flag = false; matstate->mat_Flag = false;
/* ---------------- /* ----------------
* initialize tuple type. no need to initialize projection * initialize tuple type. no need to initialize projection
* info because this node doesn't do projections. * info because this node doesn't do projections.
* ---------------- * ----------------
*/ */
ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate); ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate);
matstate->csstate.cstate.cs_ProjInfo = NULL; matstate->csstate.cstate.cs_ProjInfo = NULL;
/* ---------------- /* ----------------
* get type information needed for ExecCreatR * get type information needed for ExecCreatR
* ---------------- * ----------------
*/ */
tupType = ExecGetScanType(&matstate->csstate); tupType = ExecGetScanType(&matstate->csstate);
/* ---------------- /* ----------------
* ExecCreatR wants it's second argument to be an object id of * ExecCreatR wants it's second argument to be an object id of
* a relation in the range table or a _TEMP_RELATION_ID * a relation in the range table or a _TEMP_RELATION_ID
* indicating that the relation is not in the range table. * indicating that the relation is not in the range table.
* *
* In the second case ExecCreatR creates a temp relation. * In the second case ExecCreatR creates a temp relation.
* (currently this is the only case we support -cim 10/16/89) * (currently this is the only case we support -cim 10/16/89)
* ---------------- * ----------------
*/ */
/* ---------------- /* ----------------
* create the temporary relation * create the temporary relation
* ---------------- * ----------------
*/ */
/* len = ExecTargetListLength(node->plan.targetlist); */ /* len = ExecTargetListLength(node->plan.targetlist); */
tempDesc = ExecCreatR(tupType, _TEMP_RELATION_ID_); tempDesc = ExecCreatR(tupType, _TEMP_RELATION_ID_);
/* ---------------- /* ----------------
* save the relation descriptor in the sortstate * save the relation descriptor in the sortstate
* ---------------- * ----------------
*/ */
matstate->mat_TempRelation = tempDesc; matstate->mat_TempRelation = tempDesc;
matstate->csstate.css_currentRelation = tempDesc; matstate->csstate.css_currentRelation = tempDesc;
/* ---------------- /* ----------------
* return relation oid of temporary relation in a list * return relation oid of temporary relation in a list
* (someday -- for now we return LispTrue... cim 10/12/89) * (someday -- for now we return LispTrue... cim 10/12/89)
* ---------------- * ----------------
*/ */
return TRUE; return TRUE;
} }
int int
ExecCountSlotsMaterial(Material *node) ExecCountSlotsMaterial(Material * node)
{ {
return ExecCountSlotsNode(outerPlan((Plan *)node)) + return ExecCountSlotsNode(outerPlan((Plan *) node)) +
ExecCountSlotsNode(innerPlan((Plan *)node)) + ExecCountSlotsNode(innerPlan((Plan *) node)) +
MATERIAL_NSLOTS; MATERIAL_NSLOTS;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecEndMaterial * ExecEndMaterial
* *
* old comments * old comments
* destroys the temporary relation. * destroys the temporary relation.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
ExecEndMaterial(Material *node) ExecEndMaterial(Material * node)
{ {
MaterialState *matstate; MaterialState *matstate;
Relation tempRelation; Relation tempRelation;
Plan *outerPlan; Plan *outerPlan;
/* ---------------- /* ----------------
* get info from the material state * get info from the material state
* ---------------- * ----------------
*/ */
matstate = node->matstate; matstate = node->matstate;
tempRelation = matstate->mat_TempRelation; tempRelation = matstate->mat_TempRelation;
heap_destroyr(tempRelation); heap_destroyr(tempRelation);
/* ---------------- /* ----------------
* close the temp relation and shut down the scan. * close the temp relation and shut down the scan.
* ---------------- * ----------------
*/ */
ExecCloseR((Plan *) node); ExecCloseR((Plan *) node);
/* ---------------- /* ----------------
* shut down the subplan * shut down the subplan
* ---------------- * ----------------
*/ */
outerPlan = outerPlan((Plan *) node); outerPlan = outerPlan((Plan *) node);
ExecEndNode(outerPlan, (Plan*) node); ExecEndNode(outerPlan, (Plan *) node);
/* ---------------- /* ----------------
* clean out the tuple table * clean out the tuple table
* ---------------- * ----------------
*/ */
ExecClearTuple(matstate->csstate.css_ScanTupleSlot); ExecClearTuple(matstate->csstate.css_ScanTupleSlot);
} }
#ifdef NOT_USED /* not used */ #ifdef NOT_USED /* not used */
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecMaterialMarkPos * ExecMaterialMarkPos
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
List /* nothing of interest */ List /* nothing of interest */
ExecMaterialMarkPos(Material node) ExecMaterialMarkPos(Material node)
{ {
MaterialState matstate; MaterialState matstate;
HeapScanDesc sdesc; HeapScanDesc sdesc;
/* ----------------
* if we haven't materialized yet, just return NIL.
* ----------------
*/
matstate = get_matstate(node);
if (get_mat_Flag(matstate) == false)
return NIL;
/* ----------------
* XXX access methods don't return positions yet so
* for now we return NIL. It's possible that
* they will never return positions for all I know -cim 10/16/89
* ----------------
*/
sdesc = get_css_currentScanDesc((CommonScanState) matstate);
heap_markpos(sdesc);
/* ----------------
* if we haven't materialized yet, just return NIL.
* ----------------
*/
matstate = get_matstate(node);
if (get_mat_Flag(matstate) == false)
return NIL; return NIL;
/* ----------------
* XXX access methods don't return positions yet so
* for now we return NIL. It's possible that
* they will never return positions for all I know -cim 10/16/89
* ----------------
*/
sdesc = get_css_currentScanDesc((CommonScanState)matstate);
heap_markpos(sdesc);
return NIL;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecMaterialRestrPos * ExecMaterialRestrPos
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
ExecMaterialRestrPos(Material node) ExecMaterialRestrPos(Material node)
{ {
MaterialState matstate; MaterialState matstate;
HeapScanDesc sdesc; HeapScanDesc sdesc;
/* ---------------- /* ----------------
* if we haven't materialized yet, just return. * if we haven't materialized yet, just return.
* ---------------- * ----------------
*/ */
matstate = get_matstate(node); matstate = get_matstate(node);
if (get_mat_Flag(matstate) == false) if (get_mat_Flag(matstate) == false)
return; return;
/* ---------------- /* ----------------
* restore the scan to the previously marked position * restore the scan to the previously marked position
* ---------------- * ----------------
*/ */
sdesc = get_css_currentScanDesc((CommonScanState)matstate); sdesc = get_css_currentScanDesc((CommonScanState) matstate);
heap_restrpos(sdesc); heap_restrpos(sdesc);
} }
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More